mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-26 03:11:59 +00:00
Cleanup the caret geometry code
Intended changes: * code is shorter and cleaner * caret scales better with zoom when cursor_width=0: completion indicator, l-shaped cursor... Details: * Rename BufferView::getPosAndHeight to getPosAndDim because ascent is needed too and width could in the future be set depending on font. * Get rid of rect_ in CaretWidget and record a Dimension (and y value) instead. Remove also caret_width_ and slant_, replace rtl_ with dir. * Make CaretWidget members public and lose the trailing _. * change CaretWidget::update to read its parameters from current bufferview.
This commit is contained in:
parent
7d96531909
commit
7f1b1729b4
@ -3062,23 +3062,26 @@ bool BufferView::paragraphVisible(DocIterator const & dit) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BufferView::caretPosAndHeight(Point & p, int & h) const
|
void BufferView::caretPosAndDim(Point & p, Dimension & dim) const
|
||||||
{
|
{
|
||||||
int asc, des;
|
|
||||||
Cursor const & cur = cursor();
|
Cursor const & cur = cursor();
|
||||||
if (cur.inMathed()) {
|
if (cur.inMathed()) {
|
||||||
MathRow const & mrow = mathRow(&cur.cell());
|
MathRow const & mrow = mathRow(&cur.cell());
|
||||||
asc = mrow.caret_ascent;
|
dim.asc = mrow.caret_ascent;
|
||||||
des = mrow.caret_descent;
|
dim.des = mrow.caret_descent;
|
||||||
} else {
|
} else {
|
||||||
Font const font = cur.real_current_font;
|
Font const font = cur.real_current_font;
|
||||||
frontend::FontMetrics const & fm = theFontMetrics(font);
|
frontend::FontMetrics const & fm = theFontMetrics(font);
|
||||||
asc = fm.maxAscent();
|
dim.asc = fm.maxAscent();
|
||||||
des = fm.maxDescent();
|
dim.des = fm.maxDescent();
|
||||||
}
|
}
|
||||||
h = asc + des;
|
if (lyxrc.cursor_width > 0)
|
||||||
|
dim.wid = lyxrc.cursor_width;
|
||||||
|
else
|
||||||
|
dim.wid = 1 + int((lyxrc.currentZoom + 50) / 200.0);
|
||||||
|
|
||||||
p = getPos(cur);
|
p = getPos(cur);
|
||||||
p.y_ -= asc;
|
p.y_ -= dim.asc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3087,11 +3090,11 @@ bool BufferView::caretInView() const
|
|||||||
if (!paragraphVisible(cursor()))
|
if (!paragraphVisible(cursor()))
|
||||||
return false;
|
return false;
|
||||||
Point p;
|
Point p;
|
||||||
int h;
|
Dimension dim;
|
||||||
caretPosAndHeight(p, h);
|
caretPosAndDim(p, dim);
|
||||||
|
|
||||||
// does the cursor touch the screen ?
|
// does the cursor touch the screen ?
|
||||||
if (p.y_ + h < 0 || p.y_ >= workHeight())
|
if (p.y_ + dim.height() < 0 || p.y_ >= workHeight())
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ class Change;
|
|||||||
class CoordCache;
|
class CoordCache;
|
||||||
class Cursor;
|
class Cursor;
|
||||||
class CursorSlice;
|
class CursorSlice;
|
||||||
|
class Dimension;
|
||||||
class DispatchResult;
|
class DispatchResult;
|
||||||
class DocIterator;
|
class DocIterator;
|
||||||
class DocumentClass;
|
class DocumentClass;
|
||||||
@ -313,7 +314,7 @@ public:
|
|||||||
/// is the caret currently visible in the view
|
/// is the caret currently visible in the view
|
||||||
bool caretInView() const;
|
bool caretInView() const;
|
||||||
/// get the position and height of the caret
|
/// get the position and height of the caret
|
||||||
void caretPosAndHeight(Point & p, int & h) const;
|
void caretPosAndDim(Point & p, Dimension & dim) const;
|
||||||
|
|
||||||
///
|
///
|
||||||
void draw(frontend::Painter & pain, bool paint_caret);
|
void draw(frontend::Painter & pain, bool paint_caret);
|
||||||
|
@ -558,6 +558,7 @@ public:
|
|||||||
};
|
};
|
||||||
///
|
///
|
||||||
ScrollWheelZoom scroll_wheel_zoom = SCROLL_WHEEL_ZOOM_CTRL;
|
ScrollWheelZoom scroll_wheel_zoom = SCROLL_WHEEL_ZOOM_CTRL;
|
||||||
|
// FIXME: should be caret_width
|
||||||
///
|
///
|
||||||
int cursor_width = 1;
|
int cursor_width = 1;
|
||||||
/// One of: yes, no, ask
|
/// One of: yes, no, ask
|
||||||
|
@ -129,8 +129,8 @@ namespace frontend {
|
|||||||
|
|
||||||
class CaretWidget {
|
class CaretWidget {
|
||||||
public:
|
public:
|
||||||
CaretWidget() : rtl_(false), l_shape_(false), completable_(false),
|
CaretWidget() : dir(1), l_shape(false), completable(false),
|
||||||
x_(0), caret_width_(0), slant_(false), ascent_(0), slope_(0)
|
x(0), y(0), slope(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/* Draw the caret. Parameter \c horiz_offset is not 0 when there
|
/* Draw the caret. Parameter \c horiz_offset is not 0 when there
|
||||||
@ -138,116 +138,86 @@ public:
|
|||||||
*/
|
*/
|
||||||
void draw(QPainter & painter, int horiz_offset)
|
void draw(QPainter & painter, int horiz_offset)
|
||||||
{
|
{
|
||||||
if (!rect_.isValid())
|
if (dim.empty())
|
||||||
return;
|
return;
|
||||||
|
// correction is (1) for horizontal scrolling and (2) for
|
||||||
int const x = x_ - horiz_offset;
|
// better positionning of large cursors.
|
||||||
int const y = rect_.top();
|
int const xx = x - horiz_offset - dim.wid / 2;
|
||||||
int const lx = rtl_ ? x_ - rect_.left() : rect_.right() - x_;
|
int const lx = dim.height() / 3;
|
||||||
int const bot = rect_.bottom();
|
|
||||||
int const dir = rtl_ ? -1 : 1;
|
|
||||||
|
|
||||||
// draw caret box
|
// draw caret box
|
||||||
if (slant_ && !rtl_) {
|
painter.setPen(color);
|
||||||
// slanted
|
QPainterPath path;
|
||||||
|
path.moveTo(xx + dim.asc * slope, y);
|
||||||
QPainterPath path;
|
path.lineTo(xx - dim.des * slope, y + dim.height());
|
||||||
path.moveTo(x + ascent_ * slope_, y);
|
path.lineTo(xx + dir * dim.wid - dim.des * slope, y + dim.height());
|
||||||
path.lineTo(x - (rect_.height() - ascent_) * slope_,
|
path.lineTo(xx + dir * dim.wid + dim.asc * slope, y);
|
||||||
y + rect_.height());
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||||
path.lineTo(x + dir * caret_width_ - (rect_.height() - ascent_) * slope_,
|
painter.fillPath(path, color);
|
||||||
y + rect_.height());
|
painter.setRenderHint(QPainter::Antialiasing, false);
|
||||||
path.lineTo(x + dir * caret_width_ + ascent_ * slope_, y);
|
|
||||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
|
||||||
painter.fillPath(path, color_);
|
|
||||||
painter.setRenderHint(QPainter::Antialiasing, false);
|
|
||||||
} else
|
|
||||||
// regular
|
|
||||||
painter.fillRect(x, y, dir * caret_width_, rect_.height(), color_);
|
|
||||||
|
|
||||||
// draw RTL/LTR indication
|
// draw RTL/LTR indication
|
||||||
painter.setPen(color_);
|
if (l_shape)
|
||||||
if (l_shape_) {
|
painter.fillRect(xx - dim.des * slope,
|
||||||
painter.drawLine(x, bot, x + dir * (caret_width_ + lx - 1), bot);
|
y + dim.height() - dim.wid + 1,
|
||||||
}
|
dir * (dim.wid + lx - 1), dim.wid, color);
|
||||||
|
|
||||||
// draw completion triangle
|
// draw completion triangle
|
||||||
if (completable_) {
|
if (completable) {
|
||||||
int const m = y + rect_.height() / 2;
|
int const m = y + dim.height() / 2;
|
||||||
int const d = TabIndicatorWidth - 1;
|
int const d = TabIndicatorWidth * dim.wid - 1;
|
||||||
// offset for slanted carret
|
// offset for slanted carret
|
||||||
int const sx = (slant_ && !rtl_) ? (ascent_ - (rect_.height() / 2 - d)) * slope_ : 0;
|
int const sx = (dim.asc - (dim.height() / 2 - d)) * slope;
|
||||||
painter.drawLine(x + dir * (caret_width_ + 1) + sx, m - d,
|
painter.drawLine(xx + dir * (dim.wid + 1) + sx, m - d,
|
||||||
x + dir * (caret_width_ + d + 1) + sx, m);
|
xx + dir * (dim.wid + d + 1) + sx, m);
|
||||||
painter.drawLine(x + dir * (caret_width_ + 1) + sx, m + d,
|
painter.drawLine(xx + dir * (dim.wid + 1) + sx, m + d,
|
||||||
x + dir * (caret_width_ + d + 1) + sx, m);
|
xx + dir * (dim.wid + d + 1) + sx, m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(int x, int y, int h, bool l_shape,
|
void update(BufferView const * bv, bool complet) {
|
||||||
bool rtl, bool completable, bool slant, int ascent, double slope)
|
// Cursor size and position
|
||||||
{
|
Point point;
|
||||||
color_ = guiApp->colorCache().get(Color_cursor);
|
bv->caretPosAndDim(point, dim);
|
||||||
l_shape_ = l_shape;
|
x = point.x_;
|
||||||
rtl_ = rtl;
|
y = point.y_;
|
||||||
completable_ = completable;
|
completable = complet;
|
||||||
x_ = x;
|
|
||||||
slant_ = slant;
|
|
||||||
ascent_ = ascent;
|
|
||||||
slope_ = slope;
|
|
||||||
|
|
||||||
// extension to left and right
|
Cursor const & cur = bv->cursor();
|
||||||
int l = 0;
|
Font const & realfont = cur.real_current_font;
|
||||||
int r = 0;
|
FontMetrics const & fm = theFontMetrics(realfont.fontInfo());
|
||||||
|
BufferParams const & bp = bv->buffer().params();
|
||||||
|
bool const samelang = realfont.language() == bp.language;
|
||||||
|
bool const isrtl = realfont.isVisibleRightToLeft();
|
||||||
|
dir = isrtl ? -1 : 1;
|
||||||
|
// special shape
|
||||||
|
l_shape = (!samelang || isrtl != bp.language->rightToLeft())
|
||||||
|
&& realfont.language() != latex_language;
|
||||||
|
|
||||||
// RTL/LTR indication
|
// use slanted caret for italics in text edit mode
|
||||||
if (l_shape_) {
|
// except for selections because the selection rect does not slant
|
||||||
if (rtl)
|
bool const slant = fm.italic() && cur.inTexted() && !cur.selection();
|
||||||
l += h / 3;
|
slope = slant ? fm.italicSlope() : 0;
|
||||||
else
|
|
||||||
r += h / 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// completion triangle
|
color = guiApp->colorCache().get(Color_cursor);
|
||||||
if (completable_) {
|
|
||||||
if (rtl)
|
|
||||||
l = max(l, TabIndicatorWidth);
|
|
||||||
else
|
|
||||||
r = max(r, TabIndicatorWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
//FIXME: LyXRC::cursor_width should be caret_width
|
|
||||||
caret_width_ = lyxrc.cursor_width
|
|
||||||
? lyxrc.cursor_width
|
|
||||||
: 1 + int((lyxrc.currentZoom + 50) / 200.0);
|
|
||||||
|
|
||||||
// compute overall rectangle
|
|
||||||
rect_ = QRect(x - l, y, caret_width_ + r + l, h);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect const & rect() { return rect_; }
|
/// text direction (1 for LtR, -1 for RtL)
|
||||||
|
int dir;
|
||||||
private:
|
/// indication for language change
|
||||||
/// caret is in RTL or LTR text
|
bool l_shape;
|
||||||
bool rtl_;
|
|
||||||
/// indication for RTL or LTR
|
|
||||||
bool l_shape_;
|
|
||||||
/// triangle to show that a completion is available
|
/// triangle to show that a completion is available
|
||||||
bool completable_;
|
bool completable;
|
||||||
///
|
///
|
||||||
QColor color_;
|
QColor color;
|
||||||
/// rectangle, possibly with l_shape and completion triangle
|
/// dimension uf base caret
|
||||||
QRect rect_;
|
Dimension dim;
|
||||||
/// x position (were the vertical line is drawn)
|
/// x position (were the vertical line is drawn)
|
||||||
int x_;
|
int x;
|
||||||
/// the width of the vertical blinking bar
|
/// y position (the top of the caret)
|
||||||
int caret_width_;
|
int y;
|
||||||
/// caret is in slanted text
|
|
||||||
bool slant_;
|
|
||||||
/// the fontmetrics ascent for drawing slanted caret
|
|
||||||
int ascent_;
|
|
||||||
/// the slope for drawing slanted caret
|
/// the slope for drawing slanted caret
|
||||||
double slope_;
|
double slope;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -631,6 +601,15 @@ void GuiWorkArea::Private::resetCaret()
|
|||||||
if (!buffer_view_->caretInView())
|
if (!buffer_view_->caretInView())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// completion indicator
|
||||||
|
Cursor const & cur = buffer_view_->cursor();
|
||||||
|
bool const completable = cur.inset().showCompletionCursor()
|
||||||
|
&& completer_->completionAvailable()
|
||||||
|
&& !completer_->popupVisible()
|
||||||
|
&& !completer_->inlineVisible();
|
||||||
|
|
||||||
|
caret_->update(buffer_view_, completable);
|
||||||
|
|
||||||
needs_caret_geometry_update_ = true;
|
needs_caret_geometry_update_ = true;
|
||||||
caret_visible_ = true;
|
caret_visible_ = true;
|
||||||
}
|
}
|
||||||
@ -644,40 +623,7 @@ void GuiWorkArea::Private::updateCaretGeometry()
|
|||||||
|| !buffer_view_->caretInView())
|
|| !buffer_view_->caretInView())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Point point;
|
|
||||||
int h = 0;
|
|
||||||
buffer_view_->caretPosAndHeight(point, h);
|
|
||||||
Cursor & cur = buffer_view_->cursor();
|
|
||||||
|
|
||||||
// RTL or not RTL
|
|
||||||
bool l_shape = false;
|
|
||||||
Font const & realfont = cur.real_current_font;
|
|
||||||
FontMetrics const & fm = theFontMetrics(realfont.fontInfo());
|
|
||||||
BufferParams const & bp = buffer_view_->buffer().params();
|
|
||||||
bool const samelang = realfont.language() == bp.language;
|
|
||||||
bool const isrtl = realfont.isVisibleRightToLeft();
|
|
||||||
|
|
||||||
if (!samelang || isrtl != bp.language->rightToLeft())
|
|
||||||
l_shape = true;
|
|
||||||
|
|
||||||
// The ERT language hack needs fixing up
|
|
||||||
if (realfont.language() == latex_language)
|
|
||||||
l_shape = false;
|
|
||||||
|
|
||||||
// show caret on screen
|
|
||||||
bool completable = cur.inset().showCompletionCursor()
|
|
||||||
&& completer_->completionAvailable()
|
|
||||||
&& !completer_->popupVisible()
|
|
||||||
&& !completer_->inlineVisible();
|
|
||||||
|
|
||||||
// use slanted caret for italics in text edit mode
|
|
||||||
// except for selections because the selection rect does not slant
|
|
||||||
int slant = fm.italic() && buffer_view_->cursor().inTexted()
|
|
||||||
&& !buffer_view_->cursor().selection();
|
|
||||||
double slope = fm.italicSlope();
|
|
||||||
|
|
||||||
caret_->update(point.x_, point.y_, h, l_shape, isrtl, completable, slant,
|
|
||||||
fm.maxAscent(), slope);
|
|
||||||
needs_caret_geometry_update_ = false;
|
needs_caret_geometry_update_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1252,8 +1198,8 @@ void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain)
|
|||||||
FontInfo const font = buffer_view_->cursor().getFont().fontInfo();
|
FontInfo const font = buffer_view_->cursor().getFont().fontInfo();
|
||||||
FontMetrics const & fm = theFontMetrics(font);
|
FontMetrics const & fm = theFontMetrics(font);
|
||||||
int const height = fm.maxHeight();
|
int const height = fm.maxHeight();
|
||||||
int cur_x = caret_->rect().left();
|
int cur_x = caret_->x;
|
||||||
int cur_y = caret_->rect().bottom();
|
int cur_y = caret_->y + height;
|
||||||
|
|
||||||
// get attributes of input method cursor.
|
// get attributes of input method cursor.
|
||||||
// cursor_pos : cursor position in preedit string.
|
// cursor_pos : cursor position in preedit string.
|
||||||
@ -1447,9 +1393,9 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
|
|||||||
|
|
||||||
|
|
||||||
// redraw area of preedit string.
|
// redraw area of preedit string.
|
||||||
int height = d->caret_->rect().height();
|
int height = d->caret_->dim.height();
|
||||||
int cur_y = d->caret_->rect().bottom();
|
int cur_y = d->caret_->y;
|
||||||
viewport()->update(0, cur_y - height, viewport()->width(),
|
viewport()->update(0, cur_y, viewport()->width(),
|
||||||
(height + 1) * d->preedit_lines_);
|
(height + 1) * d->preedit_lines_);
|
||||||
|
|
||||||
if (d->preedit_string_.empty()) {
|
if (d->preedit_string_.empty()) {
|
||||||
@ -1470,13 +1416,9 @@ QVariant GuiWorkArea::inputMethodQuery(Qt::InputMethodQuery query) const
|
|||||||
// this is the CJK-specific composition window position and
|
// this is the CJK-specific composition window position and
|
||||||
// the context menu position when the menu key is pressed.
|
// the context menu position when the menu key is pressed.
|
||||||
case Qt::ImMicroFocus:
|
case Qt::ImMicroFocus:
|
||||||
cur_r = d->caret_->rect();
|
return QRect(d->caret_->x - 10 * (d->preedit_lines_ != 1),
|
||||||
if (d->preedit_lines_ != 1)
|
d->caret_->y + d->caret_->dim.height() * d->preedit_lines_,
|
||||||
cur_r.moveLeft(10);
|
d->caret_->dim.width(), d->caret_->dim.height());
|
||||||
cur_r.moveBottom(cur_r.bottom()
|
|
||||||
+ cur_r.height() * (d->preedit_lines_ - 1));
|
|
||||||
// return lower right of caret in LyX.
|
|
||||||
return cur_r;
|
|
||||||
default:
|
default:
|
||||||
return QWidget::inputMethodQuery(query);
|
return QWidget::inputMethodQuery(query);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user