Merge remote-tracking branch 'features/properpaint'

Now painting the workarea is done at paint events as should be.
Explicit painting after updating metrics has been replaced by a much
lighter procedure (updatePosCache) to update the insets positions cache.

Expected benefits:
- better performance
- proper use of subpixel aliasing

The LyXRC variable use_qimage is not needed anymore and is therefore removed.
This commit is contained in:
Jean-Marc Lasgouttes 2017-09-08 17:05:13 +02:00
commit 3d590d3bd7
26 changed files with 533 additions and 559 deletions

View File

@ -67,31 +67,6 @@ drawing from metrics.
Other changes are only clean-ups. Other changes are only clean-ups.
** When a paragraph ends with a newline, compute correctly the height of the extra row. ** When a paragraph ends with a newline, compute correctly the height of the extra row.
** Rewrite TextMetrics::editXY, checkInsetHit using row information (getPosNearX)?
The helper version should return a Row::Element instead of an InsetTable.
** Set inset position during metrics phase
In order to do that, a no-paint drawing will be initiated after every
redoParagraph. This code path will need to be made as fast as possible.
Effect: avoid depending on actual drawing having taken place. In turn,
it will allow to do drawing on paint events, like any reasonable
application would do.
** Cleanup after complete metrics
Then the following can be done:
+ remove hack in InsetMathNest::drawSelection
+ remove painting when not inside in drawParagraph
+ remove Cursor::inCoordCache?
** Paint directly to screen
Instead of using an intermediary pixmap. I have no idea of how
difficult it will prove.
One benefit will be that subpixel aliasing will work again (#9972)
** Merging bv::updateMetrics and tm::metrics ** Merging bv::updateMetrics and tm::metrics
While the full metrics computation tries hard to limit the number of While the full metrics computation tries hard to limit the number of
@ -107,19 +82,19 @@ necessary to break the whole contents to know the width of the cell.
* Description of current drawing mechanism * Description of current drawing mechanism
** Two stage drawing ** Three-stage drawing
There are two parts to drawing the work area: There are three parts to drawing the work area:
+ the metrics phase computes the size of insets and breaks the + the metrics phase computes the size of insets and breaks the
paragraphs into rows. It stores the dimension of insets (both paragraphs into rows. It stores the dimension of insets (both
normal and math) in bv::coordCache. normal and math) in bv::coordCache.
+ the drawing phase draws the contents and caches the inset + the nodraw drawing phase paints the screen (see below) with a null
positions. Since the caching of positions is useful in itself, painter. The only useful effect is to store the inset positions.
there is a provision for drawing "without" drawing when the only
thing we want is to cache inset positions + an update() signal is sent. This in turn will trigger a paint
(Painter::setDrawingEnabled). event, and the actual screen painting will happen then.
The machinery is controlled via bv::processUpdateFlags. This method is The machinery is controlled via bv::processUpdateFlags. This method is
called at the end of bv::mouseEventDispatch and in called at the end of bv::mouseEventDispatch and in
@ -145,7 +120,7 @@ update flag is Update::None.
** Metrics computation ** Metrics computation
This is triggered by bv::updateMetrics, which calls tm::redoParagraph for This is triggered by bv::updateMetrics, which calls tm::redoParagraph for
all visible paragraphs. Paragraphs above or below the screen (needed all visible paragraphs. Some Paragraphs above or below the screen (needed
for page up/down) and computed as needed. for page up/down) and computed as needed.
tm::redoParagraph will call Inset::metrics for each inset. In the case tm::redoParagraph will call Inset::metrics for each inset. In the case
@ -155,8 +130,9 @@ all the paragraphs of the inset.
** Drawing the work area. ** Drawing the work area.
This is done in bv::draw. This method is triggered mainly by This is done in bv::draw. This method is triggered by a paint event,
Buffer::changed, which draws all the work areas that show the given buffer. mainly called through Buffer::changed, which draws all the work areas
that show the given buffer.
Note that, When Buffer::changed is called outside of Note that, When Buffer::changed is called outside of
bv::processUpdateFlags, it is not clear whether the update strategy bv::processUpdateFlags, it is not clear whether the update strategy
@ -186,3 +162,6 @@ The action depends on the update strategy:
+ SingleParUpdate: only tries to repaint current paragraph in a way + SingleParUpdate: only tries to repaint current paragraph in a way
that is not yet very clear to me. that is not yet very clear to me.
BufferView::draw can also be called with a null painter from
BufferView::updateMetrics().

View File

@ -1772,7 +1772,7 @@ if __name__ == '__main__':
lyx_check_config = True lyx_check_config = True
lyx_kpsewhich = True lyx_kpsewhich = True
outfile = 'lyxrc.defaults' outfile = 'lyxrc.defaults'
lyxrc_fileformat = 22 lyxrc_fileformat = 23
rc_entries = '' rc_entries = ''
lyx_keep_temps = False lyx_keep_temps = False
version_suffix = '' version_suffix = ''

View File

@ -94,6 +94,9 @@
# Add pygmentize_command for the python pygments syntax highlighter # Add pygmentize_command for the python pygments syntax highlighter
# No conversion necessary. # No conversion necessary.
# Incremented to format 23, by lasgouttes
# Remove use_qimage preference
# NOTE: The format should also be updated in LYXRC.cpp and # NOTE: The format should also be updated in LYXRC.cpp and
# in configure.py. # in configure.py.
@ -364,6 +367,20 @@ def remove_print_support(line):
# End conversions for LyX 2.1 to 2.2 # End conversions for LyX 2.1 to 2.2
#################################### ####################################
#################################
# Conversions from LyX 2.3 to 2.4
def remove_use_qimage(line):
if not line.lower().startswith("\\use_qimage "):
return no_match
return (True, "")
# End conversions for LyX 2.3 to 2.4
####################################
conversions = [ conversions = [
[ 1, [ # there were several conversions for format 1 [ 1, [ # there were several conversions for format 1
export_menu, export_menu,
@ -392,5 +409,6 @@ conversions = [
[ 19, [remove_print_support]], [ 19, [remove_print_support]],
[ 20, []], [ 20, []],
[ 21, []], [ 21, []],
[ 22, []] [ 22, []],
[ 23, [remove_use_qimage]]
] ]

View File

@ -70,6 +70,7 @@
#include "frontends/Application.h" #include "frontends/Application.h"
#include "frontends/Delegates.h" #include "frontends/Delegates.h"
#include "frontends/FontMetrics.h" #include "frontends/FontMetrics.h"
#include "frontends/NullPainter.h"
#include "frontends/Painter.h" #include "frontends/Painter.h"
#include "frontends/Selection.h" #include "frontends/Selection.h"
@ -234,7 +235,7 @@ struct BufferView::Private
last_inset_(0), clickable_inset_(false), last_inset_(0), clickable_inset_(false),
mouse_position_cache_(), mouse_position_cache_(),
bookmark_edit_position_(-1), gui_(0), bookmark_edit_position_(-1), gui_(0),
horiz_scroll_offset_(0) horiz_scroll_offset_(0), repaint_caret_row_(false)
{ {
xsel_cache_.set = false; xsel_cache_.set = false;
} }
@ -311,6 +312,12 @@ struct BufferView::Private
/// a slice pointing to the start of the row where cursor was /// a slice pointing to the start of the row where cursor was
/// at previous draw event /// at previous draw event
CursorSlice last_row_slice_; CursorSlice last_row_slice_;
/// a slice pointing to where the cursor has been drawn after the current
/// draw() call.
CursorSlice caret_slice_;
/// indicates whether the caret slice needs to be repainted in this draw() run.
bool repaint_caret_row_;
}; };
@ -2651,6 +2658,7 @@ bool BufferView::singleParUpdate()
// the singlePar optimisation. // the singlePar optimisation.
return false; return false;
tm.updatePosCache(bottom_pit);
d->update_strategy_ = SingleParUpdate; d->update_strategy_ = SingleParUpdate;
LYXERR(Debug::PAINTING, "\ny1: " << pm.position() - pm.ascent() LYXERR(Debug::PAINTING, "\ny1: " << pm.position() - pm.ascent()
@ -2704,6 +2712,7 @@ void BufferView::updateMetrics()
// in the first line. // in the first line.
} }
anchor_pm.setPosition(d->anchor_ypos_); anchor_pm.setPosition(d->anchor_ypos_);
tm.updatePosCache(d->anchor_pit_);
LYXERR(Debug::PAINTING, "metrics: " LYXERR(Debug::PAINTING, "metrics: "
<< " anchor pit = " << d->anchor_pit_ << " anchor pit = " << d->anchor_pit_
@ -2719,6 +2728,7 @@ void BufferView::updateMetrics()
y1 -= pm.descent(); y1 -= pm.descent();
// Save the paragraph position in the cache. // Save the paragraph position in the cache.
pm.setPosition(y1); pm.setPosition(y1);
tm.updatePosCache(pit1);
y1 -= pm.ascent(); y1 -= pm.ascent();
} }
@ -2732,6 +2742,7 @@ void BufferView::updateMetrics()
y2 += pm.ascent(); y2 += pm.ascent();
// Save the paragraph position in the cache. // Save the paragraph position in the cache.
pm.setPosition(y2); pm.setPosition(y2);
tm.updatePosCache(pit2);
y2 += pm.descent(); y2 += pm.descent();
} }
@ -2745,6 +2756,9 @@ void BufferView::updateMetrics()
d->update_strategy_ = FullScreenUpdate; d->update_strategy_ = FullScreenUpdate;
// Now update the positions of insets in the cache.
updatePosCache();
if (lyxerr.debugging(Debug::WORKAREA)) { if (lyxerr.debugging(Debug::WORKAREA)) {
LYXERR(Debug::WORKAREA, "BufferView::updateMetrics"); LYXERR(Debug::WORKAREA, "BufferView::updateMetrics");
d->coord_cache_.dump(); d->coord_cache_.dump();
@ -2752,6 +2766,15 @@ void BufferView::updateMetrics()
} }
void BufferView::updatePosCache()
{
// this is the "nodraw" drawing stage: only set the positions of the
// insets in metrics cache.
frontend::NullPainter np;
draw(np, false);
}
void BufferView::insertLyXFile(FileName const & fname) void BufferView::insertLyXFile(FileName const & fname)
{ {
LASSERT(d->cursor_.inTexted(), return); LASSERT(d->cursor_.inTexted(), return);
@ -2886,7 +2909,7 @@ bool BufferView::paragraphVisible(DocIterator const & dit) const
} }
void BufferView::cursorPosAndHeight(Point & p, int & h) const void BufferView::caretPosAndHeight(Point & p, int & h) const
{ {
Cursor const & cur = cursor(); Cursor const & cur = cursor();
Font const font = cur.real_current_font; Font const font = cur.real_current_font;
@ -2959,7 +2982,24 @@ void BufferView::setCurrentRowSlice(CursorSlice const & rowSlice)
} }
void BufferView::checkCursorScrollOffset(PainterInfo & pi) namespace {
bool sliceInRow(CursorSlice const & cs, Text const * text, Row const & row)
{
return !cs.empty() && cs.text() == text && cs.pit() == row.pit()
&& row.pos() <= cs.pos() && cs.pos() <= row.endpos();
}
}
bool BufferView::needRepaint(Text const * text, Row const & row) const
{
return d->repaint_caret_row_ && sliceInRow(d->caret_slice_, text, row);
}
void BufferView::checkCursorScrollOffset()
{ {
CursorSlice rowSlice = d->cursor_.bottom(); CursorSlice rowSlice = d->cursor_.bottom();
TextMetrics const & tm = textMetrics(rowSlice.text()); TextMetrics const & tm = textMetrics(rowSlice.text());
@ -2976,35 +3016,6 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi)
// Set the row on which the cursor lives. // Set the row on which the cursor lives.
setCurrentRowSlice(rowSlice); setCurrentRowSlice(rowSlice);
// If insets referred to by cursor are not all in the cache, the positions
// need to be recomputed.
if (!d->cursor_.inCoordCache()) {
/** FIXME: the code below adds an extraneous computation of
* inset positions, and can therefore be bad for performance
* (think for example about a very large tabular inset.
* Redawing the row where it is means redrawing the whole
* screen).
*
* The bug that this fixes is the following: assume that there
* is a very large math inset. Upon entering the inset, when
* pressing `End', the row is not scrolled and the cursor is
* not visible. The extra row computation makes sure that the
* inset positions are correctly computed and set in the
* cache. This would not happen if we did not have two-stage
* drawing.
*
* A proper fix would be to always have proper inset positions
* at this point.
*/
// Force the recomputation of inset positions
bool const drawing = pi.pain.isDrawingEnabled();
pi.pain.setDrawingEnabled(false);
// No need to care about vertical position.
RowPainter rp(pi, buffer().text(), row, -d->horiz_scroll_offset_, 0);
rp.paintText();
pi.pain.setDrawingEnabled(drawing);
}
// Current x position of the cursor in pixels // Current x position of the cursor in pixels
int cur_x = getPos(d->cursor_).x_; int cur_x = getPos(d->cursor_).x_;
@ -3048,7 +3059,7 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi)
} }
void BufferView::draw(frontend::Painter & pain) void BufferView::draw(frontend::Painter & pain, bool paint_caret)
{ {
if (height_ == 0 || width_ == 0) if (height_ == 0 || width_ == 0)
return; return;
@ -3059,19 +3070,35 @@ void BufferView::draw(frontend::Painter & pain)
int const y = tm.first().second->position(); int const y = tm.first().second->position();
PainterInfo pi(this, pain); PainterInfo pi(this, pain);
CursorSlice const & bottomSlice = d->cursor_.bottom();
/** A repaint of the previous cursor row is needed if
* 1/ the caret will be painted and is is not the same than the
* already painted one;
* 2/ the caret will not be painted, but there is already one on
* screen.
*/
d->repaint_caret_row_ = (paint_caret && bottomSlice != d->caret_slice_)
|| (! paint_caret && !d->caret_slice_.empty());
// Check whether the row where the cursor lives needs to be scrolled. // Check whether the row where the cursor lives needs to be scrolled.
// Update the drawing strategy if needed. // Update the drawing strategy if needed.
checkCursorScrollOffset(pi); checkCursorScrollOffset();
switch (d->update_strategy_) { switch (d->update_strategy_) {
case NoScreenUpdate: case NoScreenUpdate:
// If no screen painting is actually needed, only some the different // no screen painting is actually needed. In nodraw stage
// coordinates of insets and paragraphs needs to be updated. // however, the different coordinates of insets and paragraphs
// needs to be updated.
LYXERR(Debug::PAINTING, "Strategy: NoScreenUpdate"); LYXERR(Debug::PAINTING, "Strategy: NoScreenUpdate");
pi.full_repaint = true; pi.full_repaint = false;
pi.pain.setDrawingEnabled(false); if (pain.isNull()) {
tm.draw(pi, 0, y); pi.full_repaint = true;
tm.draw(pi, 0, y);
} else if (d->repaint_caret_row_) {
pi.full_repaint = false;
tm.draw(pi, 0, y);
}
break; break;
case SingleParUpdate: case SingleParUpdate:
@ -3134,6 +3161,12 @@ void BufferView::draw(frontend::Painter & pain)
} }
LYXERR(Debug::PAINTING, "Found new anchor pit = " << d->anchor_pit_ LYXERR(Debug::PAINTING, "Found new anchor pit = " << d->anchor_pit_
<< " anchor ypos = " << d->anchor_ypos_); << " anchor ypos = " << d->anchor_ypos_);
// Remember what has just been done for the next draw() step
if (paint_caret)
d->caret_slice_ = bottomSlice;
else
d->caret_slice_ = CursorSlice();
} }

View File

@ -42,10 +42,10 @@ class FuncStatus;
class Intl; class Intl;
class Inset; class Inset;
class Length; class Length;
class PainterInfo;
class ParIterator; class ParIterator;
class ParagraphMetrics; class ParagraphMetrics;
class Point; class Point;
class Row;
class TexRow; class TexRow;
class Text; class Text;
class TextMetrics; class TextMetrics;
@ -132,6 +132,9 @@ public:
/// Only to be called with good y coordinates (after a bv::metrics) /// Only to be called with good y coordinates (after a bv::metrics)
bool needsFitCursor() const; bool needsFitCursor() const;
/// returns true if this row needs to be repainted (to erase caret)
bool needRepaint(Text const * text, Row const & row) const;
// Returns the amount of horizontal scrolling applied to the // Returns the amount of horizontal scrolling applied to the
// top-level row where the cursor lies // top-level row where the cursor lies
int horizScrollOffset() const; int horizScrollOffset() const;
@ -283,6 +286,10 @@ public:
/// update the internal \c ViewMetricsInfo. /// update the internal \c ViewMetricsInfo.
void updateMetrics(); void updateMetrics();
// this is the "nodraw" drawing stage: only set the positions of the
// insets in metrics cache.
void updatePosCache();
/// ///
TextMetrics const & textMetrics(Text const * t) const; TextMetrics const & textMetrics(Text const * t) const;
TextMetrics & textMetrics(Text const * t); TextMetrics & textMetrics(Text const * t);
@ -300,12 +307,11 @@ public:
bool paragraphVisible(DocIterator const & dit) const; bool paragraphVisible(DocIterator const & dit) const;
/// is the cursor currently visible in the view /// is the cursor currently visible in the view
bool cursorInView(Point const & p, int h) const; bool cursorInView(Point const & p, int h) const;
/// get the position and height of the cursor /// get the position and height of the caret
void cursorPosAndHeight(Point & p, int & h) const; void caretPosAndHeight(Point & p, int & h) const;
/// ///
void draw(frontend::Painter & pain); void draw(frontend::Painter & pain, bool paint_caret);
/// get this view's keyboard map handler. /// get this view's keyboard map handler.
Intl & getIntl(); Intl & getIntl();
@ -367,7 +373,7 @@ private:
// Check whether the row where the cursor lives needs to be scrolled. // Check whether the row where the cursor lives needs to be scrolled.
// Update the drawing strategy if needed. // Update the drawing strategy if needed.
void checkCursorScrollOffset(PainterInfo & pi); void checkCursorScrollOffset();
/// The minimal size of the document that is visible. Used /// The minimal size of the document that is visible. Used
/// when it is allowed to scroll below the document. /// when it is allowed to scroll below the document.

View File

@ -454,19 +454,6 @@ int Cursor::currentMode()
} }
bool Cursor::inCoordCache() const
{
// the root inset is not in cache, but we do not need it.
if (depth() == 1)
return true;
CoordCache::Insets const & icache = bv_->coordCache().getInsets();
for (size_t i = 1 ; i < depth() ; ++i)
if (!icache.has(&(*this)[i].inset()))
return false;
return true;
}
void Cursor::getPos(int & x, int & y) const void Cursor::getPos(int & x, int & y) const
{ {
Point p = bv().getPos(*this); Point p = bv().getPos(*this);

View File

@ -216,8 +216,6 @@ public:
/// are we entering a macro name? /// are we entering a macro name?
bool & macromode() { return macromode_; } bool & macromode() { return macromode_; }
/// returns true when all insets in cursor stack are in cache
bool inCoordCache() const;
/// returns x,y position /// returns x,y position
void getPos(int & x, int & y) const; void getPos(int & x, int & y) const;
/// return logical positions between which the cursor is situated /// return logical positions between which the cursor is situated

View File

@ -59,7 +59,7 @@ namespace {
// The format should also be updated in configure.py, and conversion code // The format should also be updated in configure.py, and conversion code
// should be added to prefs2prefs_prefs.py. // should be added to prefs2prefs_prefs.py.
static unsigned int const LYXRC_FILEFORMAT = 22; // ef: pygmentize_command static unsigned int const LYXRC_FILEFORMAT = 23; // lasgouttes: remove qimage
// when adding something to this array keep it sorted! // when adding something to this array keep it sorted!
LexerKeyword lyxrcTags[] = { LexerKeyword lyxrcTags[] = {
@ -195,7 +195,6 @@ LexerKeyword lyxrcTags[] = {
{ "\\use_converter_needauth_forbidden", LyXRC::RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN }, { "\\use_converter_needauth_forbidden", LyXRC::RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN },
{ "\\use_lastfilepos", LyXRC::RC_USELASTFILEPOS }, { "\\use_lastfilepos", LyXRC::RC_USELASTFILEPOS },
{ "\\use_pixmap_cache", LyXRC::RC_USE_PIXMAP_CACHE }, { "\\use_pixmap_cache", LyXRC::RC_USE_PIXMAP_CACHE },
{ "\\use_qimage", LyXRC::RC_USE_QIMAGE },
// compatibility with versions older than 1.4.0 only // compatibility with versions older than 1.4.0 only
{ "\\use_system_colors", LyXRC::RC_USE_SYSTEM_COLORS }, { "\\use_system_colors", LyXRC::RC_USE_SYSTEM_COLORS },
{ "\\use_system_theme_icons", LyXRC::RC_USE_SYSTEM_THEME_ICONS }, { "\\use_system_theme_icons", LyXRC::RC_USE_SYSTEM_THEME_ICONS },
@ -327,7 +326,6 @@ void LyXRC::setDefaults()
use_system_colors = false; use_system_colors = false;
use_tooltip = true; use_tooltip = true;
use_pixmap_cache = false; use_pixmap_cache = false;
use_qimage = true;
converter_cache_maxage = 6 * 30 * 24 * 3600; // 6 months converter_cache_maxage = 6 * 30 * 24 * 3600; // 6 months
user_name = to_utf8(support::user_name()); user_name = to_utf8(support::user_name());
user_email = to_utf8(support::user_email()); user_email = to_utf8(support::user_email());
@ -880,9 +878,6 @@ LyXRC::ReturnValues LyXRC::read(Lexer & lexrc, bool check_format)
case RC_USE_PIXMAP_CACHE: case RC_USE_PIXMAP_CACHE:
lexrc >> use_pixmap_cache; lexrc >> use_pixmap_cache;
break; break;
case RC_USE_QIMAGE:
lexrc >> use_qimage;
break;
case RC_SPELLCHECKER: case RC_SPELLCHECKER:
lexrc >> spellchecker; lexrc >> spellchecker;
break; break;
@ -2434,15 +2429,6 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c
if (tag != RC_LAST) if (tag != RC_LAST)
break; break;
// fall through // fall through
case RC_USE_QIMAGE:
if (ignore_system_lyxrc ||
use_qimage != system_lyxrc.use_qimage) {
os << "\\use_qimage "
<< convert<string>(use_qimage)
<< '\n';
}
if (tag != RC_LAST)
break;
os << "\n#\n" os << "\n#\n"
<< "# LANGUAGE SUPPORT SECTION ##########################\n" << "# LANGUAGE SUPPORT SECTION ##########################\n"
@ -3016,7 +3002,6 @@ void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
case LyXRC::RC_USE_SYSTEM_COLORS: case LyXRC::RC_USE_SYSTEM_COLORS:
case LyXRC::RC_USE_TOOLTIP: case LyXRC::RC_USE_TOOLTIP:
case LyXRC::RC_USE_PIXMAP_CACHE: case LyXRC::RC_USE_PIXMAP_CACHE:
case LyXRC::RC_USE_QIMAGE:
case LyXRC::RC_USE_SYSTEM_THEME_ICONS: case LyXRC::RC_USE_SYSTEM_THEME_ICONS:
case LyXRC::RC_VIEWDVI_PAPEROPTION: case LyXRC::RC_VIEWDVI_PAPEROPTION:
case LyXRC::RC_SINGLE_CLOSE_TAB_BUTTON: case LyXRC::RC_SINGLE_CLOSE_TAB_BUTTON:

View File

@ -174,7 +174,6 @@ public:
RC_USE_SYSTEM_COLORS, RC_USE_SYSTEM_COLORS,
RC_USE_TOOLTIP, RC_USE_TOOLTIP,
RC_USE_PIXMAP_CACHE, RC_USE_PIXMAP_CACHE,
RC_USE_QIMAGE,
RC_USE_SYSTEM_THEME_ICONS, RC_USE_SYSTEM_THEME_ICONS,
RC_VIEWDVI_PAPEROPTION, RC_VIEWDVI_PAPEROPTION,
RC_VIEWER, RC_VIEWER,
@ -334,8 +333,6 @@ public:
bool use_system_colors; bool use_system_colors;
/// Use pixmap cache? /// Use pixmap cache?
bool use_pixmap_cache; bool use_pixmap_cache;
/// Use QImage backend?
bool use_qimage;
/// Spellchecker engine: aspell, hunspell, etc /// Spellchecker engine: aspell, hunspell, etc
std::string spellchecker; std::string spellchecker;
/// Alternate language for spellchecker /// Alternate language for spellchecker

View File

@ -130,7 +130,7 @@ public:
bool selected; bool selected;
/// Whether the spell checker is enabled for the parent /// Whether the spell checker is enabled for the parent
bool do_spellcheck; bool do_spellcheck;
/// /// True when it can be assumed that the screen has been cleared
bool full_repaint; bool full_repaint;
/// Current background color /// Current background color
ColorCode background_color; ColorCode background_color;

View File

@ -576,7 +576,7 @@ void RowPainter::paintText()
paintStringAndSel(e); paintStringAndSel(e);
// Paint the spelling marks if enabled. // Paint the spelling marks if enabled.
if (lyxrc.spellcheck_continuously && pi_.do_spellcheck && pi_.pain.isDrawingEnabled()) if (lyxrc.spellcheck_continuously && pi_.do_spellcheck && !pi_.pain.isNull())
paintMisspelledMark(e); paintMisspelledMark(e);
break; break;

View File

@ -43,6 +43,7 @@
#include "frontends/FontMetrics.h" #include "frontends/FontMetrics.h"
#include "frontends/Painter.h" #include "frontends/Painter.h"
#include "frontends/NullPainter.h"
#include "support/debug.h" #include "support/debug.h"
#include "support/lassert.h" #include "support/lassert.h"
@ -198,6 +199,14 @@ bool TextMetrics::metrics(MetricsInfo & mi, Dimension & dim, int min_width)
} }
void TextMetrics::updatePosCache(pit_type pit) const
{
frontend::NullPainter np;
PainterInfo pi(bv_, np);
drawParagraph(pi, pit, origin_.x_, par_metrics_[pit].position());
}
int TextMetrics::rightMargin(ParagraphMetrics const & pm) const int TextMetrics::rightMargin(ParagraphMetrics const & pm) const
{ {
return text_->isMainText() ? pm.rightMargin(*bv_) : 0; return text_->isMainText() ? pm.rightMargin(*bv_) : 0;
@ -1220,6 +1229,7 @@ void TextMetrics::newParMetricsDown()
redoParagraph(pit); redoParagraph(pit);
par_metrics_[pit].setPosition(last.second.position() par_metrics_[pit].setPosition(last.second.position()
+ last.second.descent() + par_metrics_[pit].ascent()); + last.second.descent() + par_metrics_[pit].ascent());
updatePosCache(pit);
} }
@ -1234,6 +1244,7 @@ void TextMetrics::newParMetricsUp()
redoParagraph(pit); redoParagraph(pit);
par_metrics_[pit].setPosition(first.second.position() par_metrics_[pit].setPosition(first.second.position()
- first.second.ascent() - par_metrics_[pit].descent()); - first.second.ascent() - par_metrics_[pit].descent());
updatePosCache(pit);
} }
// y is screen coordinate // y is screen coordinate
@ -1803,8 +1814,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
return; return;
size_t const nrows = pm.rows().size(); size_t const nrows = pm.rows().size();
// Use fast lane when drawing is disabled. // Use fast lane in nodraw stage.
if (!pi.pain.isDrawingEnabled()) { if (pi.pain.isNull()) {
for (size_t i = 0; i != nrows; ++i) { for (size_t i = 0; i != nrows; ++i) {
Row const & row = pm.rows()[i]; Row const & row = pm.rows()[i];
@ -1856,17 +1867,11 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
if (i) if (i)
y += row.ascent(); y += row.ascent();
RowPainter rp(pi, *text_, row, row_x, y);
// It is not needed to draw on screen if we are not inside. // It is not needed to draw on screen if we are not inside.
bool const inside = (y + row.descent() >= 0 bool const inside = (y + row.descent() >= 0
&& y - row.ascent() < ww); && y - row.ascent() < ww);
pi.pain.setDrawingEnabled(inside);
if (!inside) { if (!inside) {
// Paint only the insets to set inset cache correctly // Inset positions have already been set in nodraw stage.
// FIXME: remove paintOnlyInsets when we know that positions
// have already been set.
rp.paintOnlyInsets();
y += row.descent(); y += row.descent();
continue; continue;
} }
@ -1888,13 +1893,16 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
// Row signature; has row changed since last paint? // Row signature; has row changed since last paint?
row.setCrc(pm.computeRowSignature(row, *bv_)); row.setCrc(pm.computeRowSignature(row, *bv_));
bool row_has_changed = row.changed() bool row_has_changed = row.changed()
|| bv_->hadHorizScrollOffset(text_, pit, row.pos()); || bv_->hadHorizScrollOffset(text_, pit, row.pos())
|| bv_->needRepaint(text_, row);
// Take this opportunity to spellcheck the row contents. // Take this opportunity to spellcheck the row contents.
if (row_has_changed && pi.do_spellcheck && lyxrc.spellcheck_continuously) { if (row_has_changed && pi.do_spellcheck && lyxrc.spellcheck_continuously) {
text_->getPar(pit).spellCheck(); text_->getPar(pit).spellCheck();
} }
RowPainter rp(pi, *text_, row, row_x, y);
// Don't paint the row if a full repaint has not been requested // Don't paint the row if a full repaint has not been requested
// and if it has not changed. // and if it has not changed.
if (!pi.full_repaint && !row_has_changed) { if (!pi.full_repaint && !row_has_changed) {
@ -1925,7 +1933,7 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
<< " row_selection=" << row.selection() << " row_selection=" << row.selection()
<< " full_repaint=" << pi.full_repaint << " full_repaint=" << pi.full_repaint
<< " row_has_changed=" << row_has_changed << " row_has_changed=" << row_has_changed
<< " drawingEnabled=" << pi.pain.isDrawingEnabled()); << " null painter=" << pi.pain.isNull());
} }
// Backup full_repaint status and force full repaint // Backup full_repaint status and force full repaint
@ -1953,8 +1961,6 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
// Restore full_repaint status. // Restore full_repaint status.
pi.full_repaint = tmp; pi.full_repaint = tmp;
} }
// Re-enable screen drawing for future use of the painter.
pi.pain.setDrawingEnabled(true);
//LYXERR(Debug::PAINTING, "."); //LYXERR(Debug::PAINTING, ".");
} }

View File

@ -62,6 +62,11 @@ public:
/// ///
void newParMetricsUp(); void newParMetricsUp();
/// The "nodraw" drawing stage for one single paragraph: set the
/// positions of the insets contained this paragraph in metrics
/// cache. Related to BufferView::updatePosCache.
void updatePosCache(pit_type pit) const;
/// Gets the fully instantiated font at a given position in a paragraph /// Gets the fully instantiated font at a given position in a paragraph
/// Basically the same routine as Paragraph::getFont() in Paragraph.cpp. /// Basically the same routine as Paragraph::getFont() in Paragraph.cpp.
/// The difference is that this one is used for displaying, and thus we /// The difference is that this one is used for displaying, and thus we

View File

@ -17,6 +17,7 @@ liblyxfrontends_a_SOURCES = \
Delegates.h \ Delegates.h \
KeyModifier.h \ KeyModifier.h \
KeySymbol.h \ KeySymbol.h \
NullPainter.h \
Painter.h \ Painter.h \
Clipboard.h \ Clipboard.h \
Selection.h \ Selection.h \

109
src/frontends/NullPainter.h Normal file
View File

@ -0,0 +1,109 @@
// -*- C++ -*-
/**
* \file NullPainter.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author unknown
* \author John Levon
* \author Jean-Marc Lasgouttes
*
* Full author contact details are available in file CREDITS.
*/
#ifndef NULLPAINTER_H
#define NULLPAINTER_H
#include "Painter.h"
namespace lyx {
namespace frontend {
/**
* NullPainter - A painter instance that does nothing
*/
class NullPainter : public Painter {
public:
NullPainter() : Painter(1) {}
~NullPainter() {}
/// draw a line from point to point
void line(int, int, int, int, Color,
line_style = line_solid, int = thin_line) {}
///
void lines(int const *, int const *, int, Color,
fill_style = fill_none, line_style = line_solid,
int = thin_line) {}
///
void path(int const *, int const *, int const *, int const *,
int const *, int const *, int, Color,
fill_style = fill_none, line_style = line_solid, int = thin_line) {}
/// draw a rectangle
void rectangle(int, int, int, int, Color,
line_style = line_solid, int = thin_line) {}
/// draw a filled rectangle
void fillRectangle(int, int, int, int, Color) {}
/// draw an arc
void arc(int, int, unsigned int, unsigned int, int, int, Color) {}
/// draw a pixel
void point(int, int, Color) {}
/// draw an image from the image cache
void image(int, int, int, int, graphics::Image const &) {}
/// draw a string
void text(int, int, docstring const &, FontInfo const &) {}
/// draw a char
void text(int, int, char_type, FontInfo const &) {}
/// draw a string
void text(int, int, docstring const &, Font const &, double, double) {}
///
void text(int, int, docstring const &, Font const &,
Color, size_type, size_type, double, double) {}
/// This painter does not paint
bool isNull() const { return true; }
/// draw the underbar, strikeout, xout, uuline and uwave font attributes
void textDecoration(FontInfo const &, int, int, int) {}
/**
* Draw a string and enclose it inside a rectangle. If
* back color is specified, the background is cleared with
* the given color. If frame is specified, a thin frame is drawn
* around the text with the given color.
*/
void rectText(int, int, docstring const &,
FontInfo const &, Color, Color) {}
/// draw a string and enclose it inside a button frame
void buttonText(int, int, docstring const &,
FontInfo const &, Color, Color, int) {}
/// draw a character of a preedit string for cjk support.
int preeditText(int, int, char_type, FontInfo const &,
preedit_style) { return 0; }
/// start monochrome painting mode, i.e. map every color into [min,max]
void enterMonochromeMode(Color const &, Color const &) {}
/// leave monochrome painting mode
void leaveMonochromeMode() {}
/// draws a wavy line that can be used for underlining.
void wavyHorizontalLine(int, int, int, ColorCode) {}
};
} // namespace frontend
} // namespace lyx
#endif // NULLPAINTER_H

View File

@ -147,11 +147,8 @@ public:
Color other, size_type from, size_type to, Color other, size_type from, size_type to,
double wordspacing, double textwidth) = 0; double wordspacing, double textwidth) = 0;
void setDrawingEnabled(bool drawing_enabled) // Returns true if the painter does not actually paint.
{ drawing_enabled_ = drawing_enabled; } virtual bool isNull() const = 0;
/// Indicate wether real screen drawing shall be done or not.
bool isDrawingEnabled() const { return drawing_enabled_; }
double pixelRatio() const { return pixel_ratio_; } double pixelRatio() const { return pixel_ratio_; }

View File

@ -65,8 +65,8 @@ FindAndReplaceWidget::FindAndReplaceWidget(GuiView & view)
replace_work_area_->setFrameStyle(QFrame::StyledPanel); replace_work_area_->setFrameStyle(QFrame::StyledPanel);
// We don't want two cursors blinking. // We don't want two cursors blinking.
find_work_area_->stopBlinkingCursor(); find_work_area_->stopBlinkingCaret();
replace_work_area_->stopBlinkingCursor(); replace_work_area_->stopBlinkingCaret();
} }

View File

@ -122,7 +122,6 @@
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <QX11Info> #include <QX11Info>
#undef CursorShape
#undef None #undef None
#elif defined(QPA_XCB) #elif defined(QPA_XCB)
#include <xcb/xcb.h> #include <xcb/xcb.h>
@ -1439,7 +1438,7 @@ void GuiApplication::updateCurrentView(FuncRequest const & cmd, DispatchResult &
theSelection().haveSelection(bv->cursor().selection()); theSelection().haveSelection(bv->cursor().selection());
// update gui // update gui
current_view_->restartCursor(); current_view_->restartCaret();
} }
if (dr.needMessageUpdate()) { if (dr.needMessageUpdate()) {
// Some messages may already be translated, so we cannot use _() // Some messages may already be translated, so we cannot use _()
@ -2158,7 +2157,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
if (!keysym.isOK()) if (!keysym.isOK())
LYXERR(Debug::KEY, "Empty kbd action (probably composing)"); LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
if (current_view_) if (current_view_)
current_view_->restartCursor(); current_view_->restartCaret();
return; return;
} }
@ -2218,7 +2217,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
if (!isPrintable(encoded_last_key)) { if (!isPrintable(encoded_last_key)) {
LYXERR(Debug::KEY, "Non-printable character! Omitting."); LYXERR(Debug::KEY, "Non-printable character! Omitting.");
if (current_view_) if (current_view_)
current_view_->restartCursor(); current_view_->restartCaret();
return; return;
} }
// The following modifier check is not needed on Mac. // The following modifier check is not needed on Mac.
@ -2240,7 +2239,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
{ {
if (current_view_) { if (current_view_) {
current_view_->message(_("Unknown function.")); current_view_->message(_("Unknown function."));
current_view_->restartCursor(); current_view_->restartCaret();
} }
return; return;
} }
@ -2255,7 +2254,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
LYXERR(Debug::KEY, "Unknown Action and not isText() -- giving up"); LYXERR(Debug::KEY, "Unknown Action and not isText() -- giving up");
if (current_view_) { if (current_view_) {
current_view_->message(_("Unknown function.")); current_view_->message(_("Unknown function."));
current_view_->restartCursor(); current_view_->restartCaret();
} }
return; return;
} }

View File

@ -171,9 +171,6 @@ void GuiPainter::leaveMonochromeMode()
void GuiPainter::point(int x, int y, Color col) void GuiPainter::point(int x, int y, Color col)
{ {
if (!isDrawingEnabled())
return;
setQPainterPen(computeColor(col)); setQPainterPen(computeColor(col));
drawPoint(x, y); drawPoint(x, y);
} }
@ -184,9 +181,6 @@ void GuiPainter::line(int x1, int y1, int x2, int y2,
line_style ls, line_style ls,
int lw) int lw)
{ {
if (!isDrawingEnabled())
return;
setQPainterPen(computeColor(col), ls, lw); setQPainterPen(computeColor(col), ls, lw);
bool const do_antialiasing = renderHints() & TextAntialiasing bool const do_antialiasing = renderHints() & TextAntialiasing
&& x1 != x2 && y1 != y2 && ls != line_solid_aliased; && x1 != x2 && y1 != y2 && ls != line_solid_aliased;
@ -202,9 +196,6 @@ void GuiPainter::lines(int const * xp, int const * yp, int np,
line_style ls, line_style ls,
int lw) int lw)
{ {
if (!isDrawingEnabled())
return;
// double the size if needed // double the size if needed
// FIXME THREAD // FIXME THREAD
static QVector<QPoint> points(32); static QVector<QPoint> points(32);
@ -247,9 +238,6 @@ void GuiPainter::path(int const * xp, int const * yp,
line_style ls, line_style ls,
int lw) int lw)
{ {
if (!isDrawingEnabled())
return;
QPainterPath bpath; QPainterPath bpath;
// This is the starting point, so its control points are meaningless // This is the starting point, so its control points are meaningless
bpath.moveTo(xp[0], yp[0]); bpath.moveTo(xp[0], yp[0]);
@ -278,9 +266,6 @@ void GuiPainter::rectangle(int x, int y, int w, int h,
line_style ls, line_style ls,
int lw) int lw)
{ {
if (!isDrawingEnabled())
return;
setQPainterPen(computeColor(col), ls, lw); setQPainterPen(computeColor(col), ls, lw);
drawRect(x, y, w, h); drawRect(x, y, w, h);
} }
@ -288,9 +273,6 @@ void GuiPainter::rectangle(int x, int y, int w, int h,
void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col) void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
{ {
if (!isDrawingEnabled())
return;
fillRect(x, y, w, h, guiApp->colorCache().get(col)); fillRect(x, y, w, h, guiApp->colorCache().get(col));
} }
@ -298,9 +280,6 @@ void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h, void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
int a1, int a2, Color col) int a1, int a2, Color col)
{ {
if (!isDrawingEnabled())
return;
// LyX usings 1/64ths degree, Qt usings 1/16th // LyX usings 1/64ths degree, Qt usings 1/16th
setQPainterPen(computeColor(col)); setQPainterPen(computeColor(col));
bool const do_antialiasing = renderHints() & TextAntialiasing; bool const do_antialiasing = renderHints() & TextAntialiasing;
@ -317,9 +296,6 @@ void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
fillRectangle(x, y, w, h, Color_graphicsbg); fillRectangle(x, y, w, h, Color_graphicsbg);
if (!isDrawingEnabled())
return;
QImage const image = qlimage.image(); QImage const image = qlimage.image();
QRectF const drect = QRectF(x, y, w, h); QRectF const drect = QRectF(x, y, w, h);
QRectF const srect = QRectF(0, 0, image.width(), image.height()); QRectF const srect = QRectF(0, 0, image.width(), image.height());
@ -391,7 +367,7 @@ void GuiPainter::text(int x, int y, docstring const & s,
double const wordspacing, double const tw) double const wordspacing, double const tw)
{ {
//LYXERR0("text: x=" << x << ", s=" << s); //LYXERR0("text: x=" << x << ", s=" << s);
if (s.empty() || !isDrawingEnabled()) if (s.empty())
return; return;
/* Caution: The following ucs4 to QString conversions work for symbol fonts /* Caution: The following ucs4 to QString conversions work for symbol fonts

View File

@ -37,6 +37,9 @@ public:
GuiPainter(QPaintDevice *, double pixel_ratio); GuiPainter(QPaintDevice *, double pixel_ratio);
virtual ~GuiPainter(); virtual ~GuiPainter();
/// This painter paints
virtual bool isNull() const { return false; }
/// draw a line from point to point /// draw a line from point to point
virtual void line( virtual void line(
int x1, int y1, int x1, int y1,

View File

@ -4351,13 +4351,13 @@ Buffer const * GuiView::updateInset(Inset const * inset)
} }
void GuiView::restartCursor() void GuiView::restartCaret()
{ {
/* When we move around, or type, it's nice to be able to see /* When we move around, or type, it's nice to be able to see
* the cursor immediately after the keypress. * the caret immediately after the keypress.
*/ */
if (d.current_work_area_) if (d.current_work_area_)
d.current_work_area_->startBlinkingCursor(); d.current_work_area_->startBlinkingCaret();
// Take this occasion to update the other GUI elements. // Take this occasion to update the other GUI elements.
updateDialogs(); updateDialogs();
@ -4426,7 +4426,7 @@ void GuiView::resetDialogs()
// Now update controls with current buffer. // Now update controls with current buffer.
guiApp->setCurrentView(this); guiApp->setCurrentView(this);
restoreLayout(); restoreLayout();
restartCursor(); restartCaret();
} }

View File

@ -116,7 +116,7 @@ public:
/// \return true if the \c FuncRequest has been dispatched. /// \return true if the \c FuncRequest has been dispatched.
void dispatch(FuncRequest const & cmd, DispatchResult & dr); void dispatch(FuncRequest const & cmd, DispatchResult & dr);
void restartCursor(); void restartCaret();
/// Update the completion popup and the inline completion state. /// Update the completion popup and the inline completion state.
/// If \c start is true, then a new completion might be started. /// If \c start is true, then a new completion might be started.
/// If \c keep is true, an active completion will be kept active /// If \c keep is true, an active completion will be kept active

View File

@ -16,7 +16,13 @@
#include "ColorCache.h" #include "ColorCache.h"
#include "FontLoader.h" #include "FontLoader.h"
#include "GuiApplication.h"
#include "GuiCompleter.h"
#include "GuiKeySymbol.h"
#include "GuiPainter.h"
#include "GuiView.h"
#include "Menus.h" #include "Menus.h"
#include "qt_helpers.h"
#include "Buffer.h" #include "Buffer.h"
#include "BufferList.h" #include "BufferList.h"
@ -26,19 +32,14 @@
#include "Cursor.h" #include "Cursor.h"
#include "Font.h" #include "Font.h"
#include "FuncRequest.h" #include "FuncRequest.h"
#include "GuiApplication.h"
#include "GuiCompleter.h"
#include "GuiKeySymbol.h"
#include "GuiPainter.h"
#include "GuiView.h"
#include "KeySymbol.h" #include "KeySymbol.h"
#include "Language.h" #include "Language.h"
#include "LyX.h" #include "LyX.h"
#include "LyXRC.h" #include "LyXRC.h"
#include "LyXVC.h" #include "LyXVC.h"
#include "qt_helpers.h"
#include "Text.h" #include "Text.h"
#include "TextMetrics.h" #include "TextMetrics.h"
#include "Undo.h"
#include "version.h" #include "version.h"
#include "graphics/GraphicsImage.h" #include "graphics/GraphicsImage.h"
@ -46,7 +47,6 @@
#include "support/convert.h" #include "support/convert.h"
#include "support/debug.h" #include "support/debug.h"
#include "support/gettext.h"
#include "support/lassert.h" #include "support/lassert.h"
#include "support/TempFile.h" #include "support/TempFile.h"
@ -68,7 +68,6 @@
#include <QMenu> #include <QMenu>
#include <QPainter> #include <QPainter>
#include <QPalette> #include <QPalette>
#include <QPixmapCache>
#include <QScrollBar> #include <QScrollBar>
#include <QStyleOption> #include <QStyleOption>
#include <QStylePainter> #include <QStylePainter>
@ -77,8 +76,6 @@
#include <QToolTip> #include <QToolTip>
#include <QMenuBar> #include <QMenuBar>
#include "support/bind.h"
#include <cmath> #include <cmath>
#include <iostream> #include <iostream>
@ -130,17 +127,15 @@ mouse_button::state q_motion_state(Qt::MouseButtons state)
namespace frontend { namespace frontend {
class CursorWidget { class CaretWidget {
public: public:
CursorWidget() : rtl_(false), l_shape_(false), completable_(false), CaretWidget() : rtl_(false), l_shape_(false), completable_(false),
show_(false), x_(0), cursor_width_(0) x_(0), caret_width_(0)
{ {}
recomputeWidth();
}
void draw(QPainter & painter) void draw(QPainter & painter)
{ {
if (!show_ || !rect_.isValid()) if (!rect_.isValid())
return; return;
int y = rect_.top(); int y = rect_.top();
@ -149,7 +144,7 @@ public:
int bot = rect_.bottom(); int bot = rect_.bottom();
// draw vertical line // draw vertical line
painter.fillRect(x_, y, cursor_width_, rect_.height(), color_); painter.fillRect(x_, y, caret_width_, rect_.height(), color_);
// draw RTL/LTR indication // draw RTL/LTR indication
painter.setPen(color_); painter.setPen(color_);
@ -157,7 +152,7 @@ public:
if (rtl_) if (rtl_)
painter.drawLine(x_, bot, x_ - l, bot); painter.drawLine(x_, bot, x_ - l, bot);
else else
painter.drawLine(x_, bot, x_ + cursor_width_ + r, bot); painter.drawLine(x_, bot, x_ + caret_width_ + r, bot);
} }
// draw completion triangle // draw completion triangle
@ -168,8 +163,8 @@ public:
painter.drawLine(x_ - 1, m - d, x_ - 1 - d, m); painter.drawLine(x_ - 1, m - d, x_ - 1 - d, m);
painter.drawLine(x_ - 1, m + d, x_ - 1 - d, m); painter.drawLine(x_ - 1, m + d, x_ - 1 - d, m);
} else { } else {
painter.drawLine(x_ + cursor_width_, m - d, x_ + cursor_width_ + d, m); painter.drawLine(x_ + caret_width_, m - d, x_ + caret_width_ + d, m);
painter.drawLine(x_ + cursor_width_, m + d, x_ + cursor_width_ + d, m); painter.drawLine(x_ + caret_width_, m + d, x_ + caret_width_ + d, m);
} }
} }
} }
@ -203,38 +198,32 @@ public:
r = max(r, TabIndicatorWidth); r = max(r, TabIndicatorWidth);
} }
// compute overall rectangle //FIXME: LyXRC::cursor_width should be caret_width
rect_ = QRect(x - l, y, cursor_width_ + r + l, h); caret_width_ = lyxrc.cursor_width
}
void show(bool set_show = true) { show_ = set_show; }
void hide() { show_ = false; }
int cursorWidth() const { return cursor_width_; }
void recomputeWidth() {
cursor_width_ = lyxrc.cursor_width
? lyxrc.cursor_width ? lyxrc.cursor_width
: 1 + int((lyxrc.currentZoom + 50) / 200.0); : 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_; } QRect const & rect() { return rect_; }
private: private:
/// cursor is in RTL or LTR text /// caret is in RTL or LTR text
bool rtl_; bool rtl_;
/// indication for RTL or LTR /// indication for RTL or LTR
bool l_shape_; bool l_shape_;
/// triangle to show that a completion is available /// triangle to show that a completion is available
bool completable_; bool completable_;
/// ///
bool show_;
///
QColor color_; QColor color_;
/// rectangle, possibly with l_shape and completion triangle /// rectangle, possibly with l_shape and completion triangle
QRect rect_; QRect rect_;
/// 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
int cursor_width_; int caret_width_;
}; };
@ -246,13 +235,35 @@ SyntheticMouseEvent::SyntheticMouseEvent()
GuiWorkArea::Private::Private(GuiWorkArea * parent) GuiWorkArea::Private::Private(GuiWorkArea * parent)
: p(parent), screen_(0), buffer_view_(0), lyx_view_(0), : p(parent), buffer_view_(0), lyx_view_(0),
cursor_visible_(false), cursor_(0), caret_(0), caret_visible_(false),
need_resize_(false), schedule_redraw_(false), preedit_lines_(1), need_resize_(false), schedule_redraw_(false), preedit_lines_(1),
pixel_ratio_(1.0), pixel_ratio_(1.0),
completer_(new GuiCompleter(p, p)), dialog_mode_(false), shell_escape_(false), completer_(new GuiCompleter(p, p)), dialog_mode_(false), shell_escape_(false),
read_only_(false), clean_(true), externally_modified_(false) read_only_(false), clean_(true), externally_modified_(false)
{ {
int const time = QApplication::cursorFlashTime() / 2;
if (time > 0) {
caret_timeout_.setInterval(time);
caret_timeout_.start();
} else {
// let's initialize this just to be safe
caret_timeout_.setInterval(500);
}
}
GuiWorkArea::Private::~Private()
{
// If something is wrong with the buffer, we can ignore it safely
try {
buffer_view_->buffer().workAreaManager().remove(p);
} catch(...) {}
delete buffer_view_;
delete caret_;
// Completer has a QObject parent and is thus automatically destroyed.
// See #4758.
// delete completer_;
} }
@ -285,24 +296,18 @@ double GuiWorkArea::pixelRatio() const
void GuiWorkArea::init() void GuiWorkArea::init()
{ {
// Setup the signals // Setup the signals
connect(&d->cursor_timeout_, SIGNAL(timeout()), connect(&d->caret_timeout_, SIGNAL(timeout()),
this, SLOT(toggleCursor())); this, SLOT(toggleCaret()));
int const time = QApplication::cursorFlashTime() / 2; // This connection is closed at the same time as this is destroyed.
if (time > 0) { d->synthetic_mouse_event_.timeout.timeout.connect([this](){
d->cursor_timeout_.setInterval(time); generateSyntheticMouseEvent();
d->cursor_timeout_.start(); });
} else {
// let's initialize this just to be safe
d->cursor_timeout_.setInterval(500);
}
d->resetScreen();
// With Qt4.5 a mouse event will happen before the first paint event // With Qt4.5 a mouse event will happen before the first paint event
// so make sure that the buffer view has an up to date metrics. // so make sure that the buffer view has an up to date metrics.
d->buffer_view_->resize(viewport()->width(), viewport()->height()); d->buffer_view_->resize(viewport()->width(), viewport()->height());
d->cursor_ = new frontend::CursorWidget(); d->caret_ = new frontend::CaretWidget();
d->cursor_->hide();
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setAcceptDrops(true); setAcceptDrops(true);
@ -311,63 +316,33 @@ void GuiWorkArea::init()
setFrameStyle(QFrame::NoFrame); setFrameStyle(QFrame::NoFrame);
updateWindowTitle(); updateWindowTitle();
viewport()->setAutoFillBackground(false); d->updateCursorShape();
// We don't need double-buffering nor SystemBackground on
// the viewport because we have our own backing pixmap. // we paint our own background
viewport()->setAttribute(Qt::WA_NoSystemBackground); viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
setFocusPolicy(Qt::StrongFocus); setFocusPolicy(Qt::StrongFocus);
d->setCursorShape(Qt::IBeamCursor);
// This connection is closed at the same time as this is destroyed.
d->synthetic_mouse_event_.timeout.timeout.connect([this](){
generateSyntheticMouseEvent();
});
LYXERR(Debug::GUI, "viewport width: " << viewport()->width() LYXERR(Debug::GUI, "viewport width: " << viewport()->width()
<< " viewport height: " << viewport()->height()); << " viewport height: " << viewport()->height());
// Enables input methods for asian languages. // Enables input methods for asian languages.
// Must be set when creating custom text editing widgets. // Must be set when creating custom text editing widgets.
setAttribute(Qt::WA_InputMethodEnabled, true); setAttribute(Qt::WA_InputMethodEnabled, true);
d->dialog_mode_ = false;
} }
GuiWorkArea::~GuiWorkArea() GuiWorkArea::~GuiWorkArea()
{ {
// If something is wrong with the buffer, we can ignore it safely
try {
d->buffer_view_->buffer().workAreaManager().remove(this);
} catch(...) {}
delete d->screen_;
delete d->buffer_view_;
delete d->cursor_;
// Completer has a QObject parent and is thus automatically destroyed.
// See #4758.
// delete completer_;
delete d; delete d;
} }
Qt::CursorShape GuiWorkArea::cursorShape() const
{
return viewport()->cursor().shape();
}
void GuiWorkArea::Private::setCursorShape(Qt::CursorShape shape)
{
p->viewport()->setCursor(shape);
}
void GuiWorkArea::Private::updateCursorShape() void GuiWorkArea::Private::updateCursorShape()
{ {
setCursorShape(buffer_view_->clickableInset() bool const clickable = buffer_view_ && buffer_view_->clickableInset();
? Qt::PointingHandCursor : Qt::IBeamCursor); p->viewport()->setCursor(clickable ? Qt::PointingHandCursor
: Qt::IBeamCursor);
} }
@ -434,14 +409,14 @@ BufferView const & GuiWorkArea::bufferView() const
} }
void GuiWorkArea::stopBlinkingCursor() void GuiWorkArea::stopBlinkingCaret()
{ {
d->cursor_timeout_.stop(); d->caret_timeout_.stop();
d->hideCursor(); d->hideCaret();
} }
void GuiWorkArea::startBlinkingCursor() void GuiWorkArea::startBlinkingCaret()
{ {
// do not show the cursor if the view is busy // do not show the cursor if the view is busy
if (view().busy()) if (view().busy())
@ -449,19 +424,28 @@ void GuiWorkArea::startBlinkingCursor()
Point p; Point p;
int h = 0; int h = 0;
d->buffer_view_->cursorPosAndHeight(p, h); d->buffer_view_->caretPosAndHeight(p, h);
// Don't start blinking if the cursor isn't on screen. // Don't start blinking if the cursor isn't on screen.
if (!d->buffer_view_->cursorInView(p, h)) if (!d->buffer_view_->cursorInView(p, h))
return; return;
d->showCursor(); d->showCaret();
//we're not supposed to cache this value. //we're not supposed to cache this value.
int const time = QApplication::cursorFlashTime() / 2; int const time = QApplication::cursorFlashTime() / 2;
if (time <= 0) if (time <= 0)
return; return;
d->cursor_timeout_.setInterval(time); d->caret_timeout_.setInterval(time);
d->cursor_timeout_.start(); d->caret_timeout_.start();
}
void GuiWorkArea::toggleCaret()
{
if (d->caret_visible_)
d->hideCaret();
else
d->showCaret();
} }
@ -481,16 +465,15 @@ void GuiWorkArea::redraw(bool update_metrics)
d->buffer_view_->cursor().fixIfBroken(); d->buffer_view_->cursor().fixIfBroken();
} }
// update cursor position, because otherwise it has to wait until // update caret position, because otherwise it has to wait until
// the blinking interval is over // the blinking interval is over
if (d->cursor_visible_) { if (d->caret_visible_) {
d->hideCursor(); d->hideCaret();
d->showCursor(); d->showCaret();
} }
LYXERR(Debug::WORKAREA, "WorkArea::redraw screen"); LYXERR(Debug::WORKAREA, "WorkArea::redraw screen");
d->updateScreen(); viewport()->update();
update(0, 0, viewport()->width(), viewport()->height());
/// \warning: scrollbar updating *must* be done after the BufferView is drawn /// \warning: scrollbar updating *must* be done after the BufferView is drawn
/// because \c BufferView::updateScrollbar() is called in \c BufferView::draw(). /// because \c BufferView::updateScrollbar() is called in \c BufferView::draw().
@ -524,9 +507,9 @@ void GuiWorkArea::processKeySym(KeySymbol const & key, KeyModifier mod)
} }
// In order to avoid bad surprise in the middle of an operation, // In order to avoid bad surprise in the middle of an operation,
// we better stop the blinking cursor... // we better stop the blinking caret...
// the cursor gets restarted in GuiView::restartCursor() // the caret gets restarted in GuiView::restartCaret()
stopBlinkingCursor(); stopBlinkingCaret();
guiApp->processKeySym(key, mod); guiApp->processKeySym(key, mod);
} }
@ -544,9 +527,9 @@ void GuiWorkArea::Private::dispatch(FuncRequest const & cmd)
cmd.action() != LFUN_MOUSE_MOTION || cmd.button() != mouse_button::none; cmd.action() != LFUN_MOUSE_MOTION || cmd.button() != mouse_button::none;
// In order to avoid bad surprise in the middle of an operation, we better stop // In order to avoid bad surprise in the middle of an operation, we better stop
// the blinking cursor. // the blinking caret.
if (notJustMovingTheMouse) if (notJustMovingTheMouse)
p->stopBlinkingCursor(); p->stopBlinkingCaret();
buffer_view_->mouseEventDispatch(cmd); buffer_view_->mouseEventDispatch(cmd);
@ -566,8 +549,8 @@ void GuiWorkArea::Private::dispatch(FuncRequest const & cmd)
// FIXME: let GuiView take care of those. // FIXME: let GuiView take care of those.
lyx_view_->clearMessage(); lyx_view_->clearMessage();
// Show the cursor immediately after any operation // Show the caret immediately after any operation
p->startBlinkingCursor(); p->startBlinkingCaret();
} }
updateCursorShape(); updateCursorShape();
@ -578,18 +561,18 @@ void GuiWorkArea::Private::resizeBufferView()
{ {
// WARNING: Please don't put any code that will trigger a repaint here! // WARNING: Please don't put any code that will trigger a repaint here!
// We are already inside a paint event. // We are already inside a paint event.
p->stopBlinkingCursor(); p->stopBlinkingCaret();
// Warn our container (GuiView). // Warn our container (GuiView).
p->busy(true); p->busy(true);
Point point; Point point;
int h = 0; int h = 0;
buffer_view_->cursorPosAndHeight(point, h); buffer_view_->caretPosAndHeight(point, h);
bool const cursor_in_view = buffer_view_->cursorInView(point, h); bool const caret_in_view = buffer_view_->cursorInView(point, h);
buffer_view_->resize(p->viewport()->width(), p->viewport()->height()); buffer_view_->resize(p->viewport()->width(), p->viewport()->height());
if (cursor_in_view) if (caret_in_view)
buffer_view_->scrollToCursor(); buffer_view_->scrollToCursor();
updateScreen(); p->viewport()->update();
// Update scrollbars which might have changed due different // Update scrollbars which might have changed due different
// BufferView dimension. This is especially important when the // BufferView dimension. This is especially important when the
@ -599,23 +582,23 @@ void GuiWorkArea::Private::resizeBufferView()
need_resize_ = false; need_resize_ = false;
p->busy(false); p->busy(false);
// Eventually, restart the cursor after the resize event. // Eventually, restart the caret after the resize event.
// We might be resizing even if the focus is on another widget so we only // We might be resizing even if the focus is on another widget so we only
// restart the cursor if we have the focus. // restart the caret if we have the focus.
if (p->hasFocus()) if (p->hasFocus())
QTimer::singleShot(50, p, SLOT(startBlinkingCursor())); QTimer::singleShot(50, p, SLOT(startBlinkingCaret()));
} }
void GuiWorkArea::Private::showCursor() void GuiWorkArea::Private::showCaret()
{ {
if (cursor_visible_) if (caret_visible_)
return; return;
Point p; Point point;
int h = 0; int h = 0;
buffer_view_->cursorPosAndHeight(p, h); buffer_view_->caretPosAndHeight(point, h);
if (!buffer_view_->cursorInView(p, h)) if (!buffer_view_->cursorInView(point, h))
return; return;
// RTL or not RTL // RTL or not RTL
@ -632,40 +615,45 @@ void GuiWorkArea::Private::showCursor()
if (realfont.language() == latex_language) if (realfont.language() == latex_language)
l_shape = false; l_shape = false;
// show cursor on screen // show caret on screen
Cursor & cur = buffer_view_->cursor(); Cursor & cur = buffer_view_->cursor();
bool completable = cur.inset().showCompletionCursor() bool completable = cur.inset().showCompletionCursor()
&& completer_->completionAvailable() && completer_->completionAvailable()
&& !completer_->popupVisible() && !completer_->popupVisible()
&& !completer_->inlineVisible(); && !completer_->inlineVisible();
cursor_visible_ = true; caret_visible_ = true;
cursor_->recomputeWidth();
//int cur_x = buffer_view_->getPos(cur).x_; //int cur_x = buffer_view_->getPos(cur).x_;
// We may have decided to slide the cursor row so that cursor // We may have decided to slide the cursor row so that caret
// is visible. // is visible.
p.x_ -= buffer_view_->horizScrollOffset(); point.x_ -= buffer_view_->horizScrollOffset();
showCursor(p.x_, p.y_, h, l_shape, isrtl, completable); caret_->update(point.x_, point.y_, h, l_shape, isrtl, completable);
if (schedule_redraw_) {
// This happens when a graphic conversion is finished. As we don't know
// the size of the new graphics, it's better the update everything.
// We can't use redraw() here because this would trigger a infinite
// recursive loop with showCaret().
buffer_view_->resize(p->viewport()->width(), p->viewport()->height());
p->viewport()->update();
updateScrollbar();
schedule_redraw_ = false;
return;
}
p->viewport()->update(caret_->rect());
} }
void GuiWorkArea::Private::hideCursor() void GuiWorkArea::Private::hideCaret()
{ {
if (!cursor_visible_) if (!caret_visible_)
return; return;
cursor_visible_ = false; caret_visible_ = false;
removeCursor(); //if (!qApp->focusWidget())
} p->viewport()->update(caret_->rect());
void GuiWorkArea::toggleCursor()
{
if (d->cursor_visible_)
d->hideCursor();
else
d->showCursor();
} }
@ -688,7 +676,7 @@ void GuiWorkArea::Private::updateScrollbar()
void GuiWorkArea::scrollTo(int value) void GuiWorkArea::scrollTo(int value)
{ {
stopBlinkingCursor(); stopBlinkingCaret();
d->buffer_view_->scrollDocView(value, true); d->buffer_view_->scrollDocView(value, true);
if (lyxrc.cursor_follows_scrollbar) { if (lyxrc.cursor_follows_scrollbar) {
@ -696,8 +684,8 @@ void GuiWorkArea::scrollTo(int value)
// FIXME: let GuiView take care of those. // FIXME: let GuiView take care of those.
d->lyx_view_->updateLayoutList(); d->lyx_view_->updateLayoutList();
} }
// Show the cursor immediately after any operation. // Show the caret immediately after any operation.
startBlinkingCursor(); startBlinkingCaret();
// FIXME QT5 // FIXME QT5
#ifdef Q_WS_X11 #ifdef Q_WS_X11
QApplication::syncX(); QApplication::syncX();
@ -803,7 +791,7 @@ void GuiWorkArea::focusInEvent(QFocusEvent * e)
d->lyx_view_->currentWorkArea()->bufferView().buffer().updateBuffer(); d->lyx_view_->currentWorkArea()->bufferView().buffer().updateBuffer();
} }
startBlinkingCursor(); startBlinkingCaret();
QAbstractScrollArea::focusInEvent(e); QAbstractScrollArea::focusInEvent(e);
} }
@ -811,7 +799,7 @@ void GuiWorkArea::focusInEvent(QFocusEvent * e)
void GuiWorkArea::focusOutEvent(QFocusEvent * e) void GuiWorkArea::focusOutEvent(QFocusEvent * e)
{ {
LYXERR(Debug::DEBUG, "GuiWorkArea::focusOutEvent(): " << this << endl); LYXERR(Debug::DEBUG, "GuiWorkArea::focusOutEvent(): " << this << endl);
stopBlinkingCursor(); stopBlinkingCaret();
QAbstractScrollArea::focusOutEvent(e); QAbstractScrollArea::focusOutEvent(e);
} }
@ -1158,157 +1146,31 @@ void GuiWorkArea::resizeEvent(QResizeEvent * ev)
} }
void GuiWorkArea::Private::update(int x, int y, int w, int h) void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain)
{ {
p->viewport()->update(x, y, w, h); if (preedit_string_.empty())
}
void GuiWorkArea::paintEvent(QPaintEvent * ev)
{
QRectF const rc = ev->rect();
// LYXERR(Debug::PAINTING, "paintEvent begin: x: " << rc.x()
// << " y: " << rc.y() << " w: " << rc.width() << " h: " << rc.height());
if (d->needResize()) {
d->resetScreen();
d->resizeBufferView();
if (d->cursor_visible_) {
d->hideCursor();
d->showCursor();
}
}
QPainter pain(viewport());
double const pr = pixelRatio();
QRectF const rcs = QRectF(rc.x() * pr, rc.y() * pr, rc.width() * pr, rc.height() * pr);
if (lyxrc.use_qimage) {
QImage const & image = static_cast<QImage const &>(*d->screen_);
pain.drawImage(rc, image, rcs);
} else {
QPixmap const & pixmap = static_cast<QPixmap const &>(*d->screen_);
pain.drawPixmap(rc, pixmap, rcs);
}
d->cursor_->draw(pain);
ev->accept();
}
void GuiWorkArea::Private::updateScreen()
{
GuiPainter pain(screen_, p->pixelRatio());
buffer_view_->draw(pain);
}
void GuiWorkArea::Private::showCursor(int x, int y, int h,
bool l_shape, bool rtl, bool completable)
{
if (schedule_redraw_) {
// This happens when a graphic conversion is finished. As we don't know
// the size of the new graphics, it's better the update everything.
// We can't use redraw() here because this would trigger a infinite
// recursive loop with showCursor().
buffer_view_->resize(p->viewport()->width(), p->viewport()->height());
updateScreen();
updateScrollbar();
p->viewport()->update(QRect(0, 0, p->viewport()->width(), p->viewport()->height()));
schedule_redraw_ = false;
// Show the cursor immediately after the update.
hideCursor();
p->toggleCursor();
return; return;
}
cursor_->update(x, y, h, l_shape, rtl, completable);
cursor_->show();
p->viewport()->update(cursor_->rect());
}
void GuiWorkArea::Private::removeCursor()
{
cursor_->hide();
//if (!qApp->focusWidget())
p->viewport()->update(cursor_->rect());
}
void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
{
QString const & commit_string = e->commitString();
docstring const & preedit_string
= qstring_to_ucs4(e->preeditString());
if (!commit_string.isEmpty()) {
LYXERR(Debug::KEY, "preeditString: " << e->preeditString()
<< " commitString: " << e->commitString());
int key = 0;
// FIXME Iwami 04/01/07: we should take care also of UTF16 surrogates here.
for (int i = 0; i != commit_string.size(); ++i) {
QKeyEvent ev(QEvent::KeyPress, key, Qt::NoModifier, commit_string[i]);
keyPressEvent(&ev);
}
}
// Hide the cursor during the kana-kanji transformation.
if (preedit_string.empty())
startBlinkingCursor();
else
stopBlinkingCursor();
// last_width : for checking if last preedit string was/wasn't empty.
// FIXME THREAD && FIXME
// We could have more than one work area, right?
static bool last_width = false;
if (!last_width && preedit_string.empty()) {
// if last_width is last length of preedit string.
e->accept();
return;
}
GuiPainter pain(d->screen_, pixelRatio());
d->buffer_view_->updateMetrics();
d->buffer_view_->draw(pain);
// FIXME: shall we use real_current_font here? (see #10478) // FIXME: shall we use real_current_font here? (see #10478)
FontInfo font = d->buffer_view_->cursor().getFont().fontInfo(); FontInfo const font = buffer_view_->cursor().getFont().fontInfo();
FontMetrics const & fm = theFontMetrics(font); FontMetrics const & fm = theFontMetrics(font);
int height = fm.maxHeight(); int const height = fm.maxHeight();
int cur_x = d->cursor_->rect().left(); int cur_x = caret_->rect().left();
int cur_y = d->cursor_->rect().bottom(); int cur_y = caret_->rect().bottom();
// redraw area of preedit string.
update(0, cur_y - height, viewport()->width(),
(height + 1) * d->preedit_lines_);
if (preedit_string.empty()) {
last_width = false;
d->preedit_lines_ = 1;
e->accept();
return;
}
last_width = true;
// att : stores an IM attribute.
QList<QInputMethodEvent::Attribute> const & att = e->attributes();
// 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.
size_t cursor_pos = 0; size_t cursor_pos = 0;
bool cursor_is_visible = false; bool cursor_is_visible = false;
for (int i = 0; i != att.size(); ++i) { for (auto const & attr : preedit_attr_) {
if (att.at(i).type == QInputMethodEvent::Cursor) { if (attr.type == QInputMethodEvent::Cursor) {
cursor_pos = att.at(i).start; cursor_pos = attr.start;
cursor_is_visible = att.at(i).length != 0; cursor_is_visible = attr.length != 0;
break; break;
} }
} }
size_t preedit_length = preedit_string.length(); size_t const preedit_length = preedit_string_.length();
// get position of selection in input method. // get position of selection in input method.
// FIXME: isn't there a way to do this simplier? // FIXME: isn't there a way to do this simplier?
@ -1317,12 +1179,12 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
// rLength : selected string length in IM. // rLength : selected string length in IM.
size_t rLength = 0; size_t rLength = 0;
if (cursor_pos < preedit_length) { if (cursor_pos < preedit_length) {
for (int i = 0; i != att.size(); ++i) { for (auto const & attr : preedit_attr_) {
if (att.at(i).type == QInputMethodEvent::TextFormat) { if (attr.type == QInputMethodEvent::TextFormat) {
if (att.at(i).start <= int(cursor_pos) if (attr.start <= int(cursor_pos)
&& int(cursor_pos) < att.at(i).start + att.at(i).length) { && int(cursor_pos) < attr.start + attr.length) {
rStart = att.at(i).start; rStart = attr.start;
rLength = att.at(i).length; rLength = attr.length;
if (!cursor_is_visible) if (!cursor_is_visible)
cursor_pos += rLength; cursor_pos += rLength;
break; break;
@ -1335,20 +1197,20 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
rLength = 0; rLength = 0;
} }
int const right_margin = d->buffer_view_->rightMargin(); int const right_margin = buffer_view_->rightMargin();
Painter::preedit_style ps; Painter::preedit_style ps;
// Most often there would be only one line: // Most often there would be only one line:
d->preedit_lines_ = 1; preedit_lines_ = 1;
for (size_t pos = 0; pos != preedit_length; ++pos) { for (size_t pos = 0; pos != preedit_length; ++pos) {
char_type const typed_char = preedit_string[pos]; char_type const typed_char = preedit_string_[pos];
// reset preedit string style // reset preedit string style
ps = Painter::preedit_default; ps = Painter::preedit_default;
// if we reached the right extremity of the screen, go to next line. // if we reached the right extremity of the screen, go to next line.
if (cur_x + fm.width(typed_char) > viewport()->width() - right_margin) { if (cur_x + fm.width(typed_char) > p->viewport()->width() - right_margin) {
cur_x = right_margin; cur_x = right_margin;
cur_y += height + 1; cur_y += height + 1;
++d->preedit_lines_; ++preedit_lines_;
} }
// preedit strings are displayed with dashed underline // preedit strings are displayed with dashed underline
// and partial strings are displayed white on black indicating // and partial strings are displayed white on black indicating
@ -1367,11 +1229,78 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
// draw one character and update cur_x. // draw one character and update cur_x.
cur_x += pain.preeditText(cur_x, cur_y, typed_char, font, ps); cur_x += pain.preeditText(cur_x, cur_y, typed_char, font, ps);
} }
}
// update the preedit string screen area.
update(0, cur_y - d->preedit_lines_*height, viewport()->width(), void GuiWorkArea::paintEvent(QPaintEvent * ev)
{
// LYXERR(Debug::PAINTING, "paintEvent begin: x: " << rc.x()
// << " y: " << rc.y() << " w: " << rc.width() << " h: " << rc.height());
if (d->needResize()) {
d->resizeBufferView();
if (d->caret_visible_) {
d->hideCaret();
d->showCaret();
}
}
GuiPainter pain(viewport(), pixelRatio());
d->buffer_view_->draw(pain, d->caret_visible_);
// The preedit text, if needed
d->paintPreeditText(pain);
// and the caret
if (d->caret_visible_)
d->caret_->draw(pain);
ev->accept();
}
void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
{
LYXERR(Debug::KEY, "preeditString: " << e->preeditString()
<< " commitString: " << e->commitString());
// insert the processed text in the document (handles undo)
if (!e->commitString().isEmpty()) {
d->buffer_view_->cursor().beginUndoGroup();
d->buffer_view_->cursor().insert(qstring_to_ucs4(e->commitString()));
d->buffer_view_->updateMetrics();
d->buffer_view_->cursor().endUndoGroup();
viewport()->update();
}
// Hide the caret during the test transformation.
if (e->preeditString().isEmpty())
startBlinkingCaret();
else
stopBlinkingCaret();
if (d->preedit_string_.empty() && e->preeditString().isEmpty()) {
// Nothing to do
e->accept();
return;
}
// The preedit text and its attributes will be used in paintPreeditText
d->preedit_string_ = qstring_to_ucs4(e->preeditString());
d->preedit_attr_ = e->attributes();
// redraw area of preedit string.
int height = d->caret_->rect().height();
int cur_y = d->caret_->rect().bottom();
viewport()->update(0, cur_y - height, viewport()->width(),
(height + 1) * d->preedit_lines_); (height + 1) * d->preedit_lines_);
if (d->preedit_string_.empty()) {
d->preedit_lines_ = 1;
e->accept();
return;
}
// Don't forget to accept the event! // Don't forget to accept the event!
e->accept(); e->accept();
} }
@ -1384,12 +1313,12 @@ 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->cursor_->rect(); cur_r = d->caret_->rect();
if (d->preedit_lines_ != 1) if (d->preedit_lines_ != 1)
cur_r.moveLeft(10); cur_r.moveLeft(10);
cur_r.moveBottom(cur_r.bottom() cur_r.moveBottom(cur_r.bottom()
+ cur_r.height() * (d->preedit_lines_ - 1)); + cur_r.height() * (d->preedit_lines_ - 1));
// return lower right of cursor in LyX. // return lower right of caret in LyX.
return cur_r; return cur_r;
default: default:
return QWidget::inputMethodQuery(query); return QWidget::inputMethodQuery(query);
@ -1511,7 +1440,7 @@ QSize EmbeddedWorkArea::sizeHint () const
void EmbeddedWorkArea::disable() void EmbeddedWorkArea::disable()
{ {
stopBlinkingCursor(); stopBlinkingCaret();
if (view().currentWorkArea() != this) if (view().currentWorkArea() != this)
return; return;
// No problem if currentMainWorkArea() is 0 (setCurrentWorkArea() // No problem if currentMainWorkArea() is 0 (setCurrentWorkArea()

View File

@ -26,10 +26,6 @@ class QDropEvent;
class QToolButton; class QToolButton;
class QWidget; class QWidget;
#ifdef CursorShape
#undef CursorShape
#endif
namespace lyx { namespace lyx {
class Buffer; class Buffer;
@ -83,8 +79,6 @@ public:
/// ///
GuiCompleter & completer(); GuiCompleter & completer();
Qt::CursorShape cursorShape() const;
/// Return the GuiView this workArea belongs to /// Return the GuiView this workArea belongs to
GuiView const & view() const; GuiView const & view() const;
GuiView & view(); GuiView & view();
@ -94,9 +88,9 @@ public:
public Q_SLOTS: public Q_SLOTS:
/// ///
void stopBlinkingCursor(); void stopBlinkingCaret();
/// ///
void startBlinkingCursor(); void startBlinkingCaret();
Q_SIGNALS: Q_SIGNALS:
/// ///
@ -115,8 +109,8 @@ private Q_SLOTS:
void scrollTo(int value); void scrollTo(int value);
/// timer to limit triple clicks /// timer to limit triple clicks
void doubleClickTimeout(); void doubleClickTimeout();
/// toggle the cursor's visibility /// toggle the caret's visibility
void toggleCursor(); void toggleCaret();
/// close this work area. /// close this work area.
/// Slot for Buffer::closing signal. /// Slot for Buffer::closing signal.
void close(); void close();

View File

@ -13,30 +13,13 @@
#define WORKAREA_PRIVATE_H #define WORKAREA_PRIVATE_H
#include "FuncRequest.h" #include "FuncRequest.h"
#include "LyXRC.h"
#include "support/FileName.h" #include "support/FileName.h"
#include "support/Timeout.h" #include "support/Timeout.h"
#include <QMouseEvent> #include <QMouseEvent>
#include <QImage>
#include <QPixmap>
#include <QTimer> #include <QTimer>
class QContextMenuEvent;
class QDragEnterEvent;
class QDropEvent;
class QKeyEvent;
class QPaintEvent;
class QResizeEvent;
class QToolButton;
class QWheelEvent;
class QWidget;
#ifdef CursorShape
#undef CursorShape
#endif
namespace lyx { namespace lyx {
class Buffer; class Buffer;
@ -44,6 +27,7 @@ class Buffer;
namespace frontend { namespace frontend {
class GuiCompleter; class GuiCompleter;
class GuiPainter;
class GuiView; class GuiView;
class GuiWorkArea; class GuiWorkArea;
@ -86,92 +70,68 @@ public:
/** /**
* Implementation of the work area (buffer view GUI) * Implementation of the work area (buffer view GUI)
*/ */
class CursorWidget; class CaretWidget;
struct GuiWorkArea::Private struct GuiWorkArea::Private
{ {
///
Private(GuiWorkArea *); Private(GuiWorkArea *);
/// update the passed area.
void update(int x, int y, int w, int h);
/// ///
void updateScreen(); ~Private();
/// ///
void resizeBufferView(); void resizeBufferView();
/// paint the cursor and store the background
void showCursor(int x, int y, int h,
bool l_shape, bool rtl, bool completable);
/// hide the cursor
void removeCursor();
/// ///
void dispatch(FuncRequest const & cmd0); void dispatch(FuncRequest const & cmd0);
/// hide the visible cursor, if it is visible /// hide the visible caret, if it is visible
void hideCursor(); void hideCaret();
/// show the cursor if it is not visible /// show the caret if it is not visible
void showCursor(); void showCaret();
/// Set the range and value of the scrollbar and connect to its valueChanged /// Set the range and value of the scrollbar and connect to its valueChanged
/// signal. /// signal.
void updateScrollbar(); void updateScrollbar();
/// Change the cursor when the mouse hovers over a clickable inset /// Change the cursor when the mouse hovers over a clickable inset
void updateCursorShape(); void updateCursorShape();
///
void setCursorShape(Qt::CursorShape shape); void paintPreeditText(GuiPainter & pain);
bool needResize() const { bool needResize() const {
return need_resize_ || p->pixelRatio() != pixel_ratio_; return need_resize_ || p->pixelRatio() != pixel_ratio_;
} }
void resetScreen()
{
delete screen_;
pixel_ratio_ = p->pixelRatio();
if (lyxrc.use_qimage) {
QImage *x =
new QImage(static_cast<int>(pixel_ratio_ * p->viewport()->width()),
static_cast<int>(pixel_ratio_ * p->viewport()->height()),
QImage::Format_ARGB32_Premultiplied);
#if QT_VERSION >= 0x050000
x->setDevicePixelRatio(pixel_ratio_);
#endif
screen_ = x;
} else {
QPixmap *x =
new QPixmap(static_cast<int>(pixel_ratio_ * p->viewport()->width()),
static_cast<int>(pixel_ratio_ * p->viewport()->height()));
#if QT_VERSION >= 0x050000
x->setDevicePixelRatio(pixel_ratio_);
#endif
screen_ = x;
}
}
/// ///
GuiWorkArea * p; GuiWorkArea * p;
/// ///
QPaintDevice * screen_;
///
BufferView * buffer_view_; BufferView * buffer_view_;
/// ///
GuiView * lyx_view_; GuiView * lyx_view_;
/// is the cursor currently displayed
bool cursor_visible_;
/// ///
QTimer cursor_timeout_; CaretWidget * caret_;
/// is the caret currently displayed
bool caret_visible_;
///
QTimer caret_timeout_;
/// ///
SyntheticMouseEvent synthetic_mouse_event_; SyntheticMouseEvent synthetic_mouse_event_;
/// ///
DoubleClick dc_event_; DoubleClick dc_event_;
///
CursorWidget * cursor_;
/// ///
bool need_resize_; bool need_resize_;
/// ///
bool schedule_redraw_; bool schedule_redraw_;
///
/// the current preedit text of the input method
docstring preedit_string_;
/// Number of lines used by preedit text
int preedit_lines_; int preedit_lines_;
/// the attributes of the preedit text
QList<QInputMethodEvent::Attribute> preedit_attr_;
/// Ratio between physical pixels and device-independent pixels /// Ratio between physical pixels and device-independent pixels
/// We save the last used value to detect changes of the /// We save the last used value to detect changes of the
/// current pixel_ratio of the viewport. /// current pixel_ratio of the viewport.

View File

@ -231,25 +231,17 @@ void InsetDisplayLabelBox::metrics(MetricsInfo & mi, Dimension & dim) const
{ {
InsetLabelBox::metrics(mi, dim); InsetLabelBox::metrics(mi, dim);
if (!parent_.editing(mi.base.bv) if (!parent_.editing(mi.base.bv)
&& parent_.cell(parent_.displayIdx()).empty()) { && parent_.cell(parent_.displayIdx()).empty())
dim.wid = 0; dim.clear();
dim.asc = 0;
dim.des = 0;
}
} }
void InsetDisplayLabelBox::draw(PainterInfo & pi, int x, int y) const void InsetDisplayLabelBox::draw(PainterInfo & pi, int x, int y) const
{ {
if (parent_.editing(pi.base.bv) if (parent_.editing(pi.base.bv)
|| !parent_.cell(parent_.displayIdx()).empty()) { || !parent_.cell(parent_.displayIdx()).empty()
InsetLabelBox::draw(pi, x, y); || pi.pain.isNull())
} else { InsetLabelBox::draw(pi, x, y);
bool enabled = pi.pain.isDrawingEnabled();
pi.pain.setDrawingEnabled(false);
InsetLabelBox::draw(pi, x, y);
pi.pain.setDrawingEnabled(enabled);
}
} }