commit ef1d50207088bc40cd0fbbd27229df9e8400e932
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Thu Jul 20 23:31:05 2017 +0200

    Fix caret painting
    
    The trick is to remember in BufferView what has been done at the
    previous draw, so that the row that contained the caret can be
    repainted if needed.
    
    To this end, add an argument paint_caret to BufferView, although
    painting the caret is not the job of the BufferView (at this point).
    
    BufferView::needRepaint will act as an interface with
    TextMetrics::drawParagraph to know whether the painting of a given
    row should be forced.
    
    Currently everything is done at the top row level, so that, if the
    caret is in a large table, the whole table will have to be repainted.
    It is not clear yet that this is necessary.
---
 src/BufferView.cpp                |   54 +++++++++++++++++++++++++++++++++---
 src/BufferView.h                  |    6 +++-
 src/MetricsInfo.h                 |    2 +-
 src/TextMetrics.cpp               |    3 +-
 src/frontends/qt4/GuiWorkArea.cpp |    5 ++-
 5 files changed, 60 insertions(+), 10 deletions(-)

diff --git a/src/BufferView.cpp b/src/BufferView.cpp
index 94e448d..3c6adfc 100644
--- a/src/BufferView.cpp
+++ b/src/BufferView.cpp
@@ -235,7 +235,7 @@ struct BufferView::Private
                last_inset_(0), clickable_inset_(false),
                mouse_position_cache_(),
                bookmark_edit_position_(-1), gui_(0),
-               horiz_scroll_offset_(0)
+               horiz_scroll_offset_(0), repaint_caret_row_(false)
        {
                xsel_cache_.set = false;
        }
@@ -312,6 +312,12 @@ struct BufferView::Private
        /// a slice pointing to the start of the row where cursor was
        /// at previous draw event
        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_;
 };
 
 
@@ -2748,7 +2754,7 @@ void BufferView::updatePosCache()
        // this is the "nodraw" drawing stage: only set the positions of the
        // insets in metrics cache.
        frontend::NullPainter np;
-       draw(np);
+       draw(np, false);
 }
 
 
@@ -2959,6 +2965,23 @@ void BufferView::setCurrentRowSlice(CursorSlice const & 
rowSlice)
 }
 
 
+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(PainterInfo & pi)
 {
        CursorSlice rowSlice = d->cursor_.bottom();
@@ -3047,7 +3070,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)
                return;
@@ -3058,6 +3081,16 @@ void BufferView::draw(frontend::Painter & pain)
        int const y = tm.first().second->position();
        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.
        // Update the drawing strategy if needed.
        checkCursorScrollOffset(pi);
@@ -3069,9 +3102,14 @@ void BufferView::draw(frontend::Painter & pain)
                // however, the different coordinates of insets and paragraphs
                // needs to be updated.
                LYXERR(Debug::PAINTING, "Strategy: NoScreenUpdate");
-               pi.full_repaint = true;
-               if (pain.isNull())
+               pi.full_repaint = false;
+               if (pain.isNull()) {
+                       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;
 
        case SingleParUpdate:
@@ -3134,6 +3172,12 @@ void BufferView::draw(frontend::Painter & pain)
        }
        LYXERR(Debug::PAINTING, "Found new anchor pit = " << d->anchor_pit_
                << "  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();
 }
 
 
diff --git a/src/BufferView.h b/src/BufferView.h
index 40af7c6..eed408d 100644
--- a/src/BufferView.h
+++ b/src/BufferView.h
@@ -46,6 +46,7 @@ class PainterInfo;
 class ParIterator;
 class ParagraphMetrics;
 class Point;
+class Row;
 class TexRow;
 class Text;
 class TextMetrics;
@@ -132,6 +133,9 @@ public:
        /// Only to be called with good y coordinates (after a bv::metrics)
        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
        // top-level row where the cursor lies
        int horizScrollOffset() const;
@@ -308,7 +312,7 @@ public:
        void cursorPosAndHeight(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.
        Intl & getIntl();
diff --git a/src/MetricsInfo.h b/src/MetricsInfo.h
index 2a18cf8..ff0b1c6 100644
--- a/src/MetricsInfo.h
+++ b/src/MetricsInfo.h
@@ -130,7 +130,7 @@ public:
        bool selected;
        /// Whether the spell checker is enabled for the parent
        bool do_spellcheck;
-       ///
+       /// True when it can be assumed that the screen has been cleared
        bool full_repaint;
        /// Current background color
        ColorCode background_color;
diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp
index 360d83b..5271740 100644
--- a/src/TextMetrics.cpp
+++ b/src/TextMetrics.cpp
@@ -1876,7 +1876,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, 
pit_type const pit, int const
                // Row signature; has row changed since last paint?
                row.setCrc(pm.computeRowSignature(row, *bv_));
                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.
                if (row_has_changed && pi.do_spellcheck && 
lyxrc.spellcheck_continuously) {
diff --git a/src/frontends/qt4/GuiWorkArea.cpp 
b/src/frontends/qt4/GuiWorkArea.cpp
index 8ce5985..9a9e155 100644
--- a/src/frontends/qt4/GuiWorkArea.cpp
+++ b/src/frontends/qt4/GuiWorkArea.cpp
@@ -1188,9 +1188,10 @@ void GuiWorkArea::paintEvent(QPaintEvent * ev)
        }
 
        GuiPainter pain(viewport(), pixelRatio());
-       d->buffer_view_->draw(pain);
+       d->buffer_view_->draw(pain, d->cursor_visible_);
 
-       d->cursor_->draw(pain);
+       if (d->cursor_visible_)
+               d->cursor_->draw(pain);
        ev->accept();
 }
 

Reply via email to