From efc735901523752a26440b2d3da390d1c787bc56 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Sun, 16 Jul 2017 01:25:03 +0200 Subject: [PATCH 01/10] three-stage drawing: add a nodraw stage Normally the two stages of drawing are 1/ compute metrics of insets/rows/paragraphs/mathrow... 2/ draw the elements and cache their positions Now the three stages are 1/ metrics 2/ nodraw: do not draw the elements, but cache their position 3/ draw the elements (and store again their position; it does not seems to hurt performance). Revive the NullPainter: this replaces the setDrawingEnabled mechanism with a painter that does nothing. The advantage is that updatePosCache (renamed from setPosCache) does not need anymore to be invoked from the frontend. updatePosCache (the nodraw stage) is called at the end of BufferView::updateMetrics. --- src/BufferView.cpp | 27 +++++-- src/BufferView.h | 5 +- src/RowPainter.cpp | 2 +- src/TextMetrics.cpp | 18 ++--- src/frontends/Makefile.am | 1 + src/frontends/NullPainter.h | 109 ++++++++++++++++++++++++++ src/frontends/Painter.h | 7 +- src/frontends/qt4/GuiPainter.cpp | 26 +----- src/frontends/qt4/GuiPainter.h | 3 + src/frontends/qt4/GuiWorkArea.cpp | 14 ++-- src/mathed/InsetMathMacroTemplate.cpp | 18 ++--- 11 files changed, 159 insertions(+), 71 deletions(-) create mode 100644 src/frontends/NullPainter.h diff --git a/src/BufferView.cpp b/src/BufferView.cpp index d2215627cf..00de06080a 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -70,6 +70,7 @@ #include "frontends/Application.h" #include "frontends/Delegates.h" #include "frontends/FontMetrics.h" +#include "frontends/NullPainter.h" #include "frontends/Painter.h" #include "frontends/Selection.h" @@ -2745,6 +2746,9 @@ void BufferView::updateMetrics() d->update_strategy_ = FullScreenUpdate; + // Now update the positions of insets in the cache. + updatePosCache(); + if (lyxerr.debugging(Debug::WORKAREA)) { LYXERR(Debug::WORKAREA, "BufferView::updateMetrics"); d->coord_cache_.dump(); @@ -2752,6 +2756,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); +} + + void BufferView::insertLyXFile(FileName const & fname) { LASSERT(d->cursor_.inTexted(), return); @@ -2997,12 +3010,11 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi) * at this point. */ // Force the recomputation of inset positions - bool const drawing = pi.pain.isDrawingEnabled(); - pi.pain.setDrawingEnabled(false); + frontend::NullPainter np; + PainterInfo(this, np); // 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 @@ -3066,12 +3078,13 @@ void BufferView::draw(frontend::Painter & pain) switch (d->update_strategy_) { case NoScreenUpdate: - // If no screen painting is actually needed, only some the different - // coordinates of insets and paragraphs needs to be updated. + // no screen painting is actually needed. In nodraw stage + // however, the different coordinates of insets and paragraphs + // needs to be updated. LYXERR(Debug::PAINTING, "Strategy: NoScreenUpdate"); pi.full_repaint = true; - pi.pain.setDrawingEnabled(false); - tm.draw(pi, 0, y); + if (pain.isNull()) + tm.draw(pi, 0, y); break; case SingleParUpdate: diff --git a/src/BufferView.h b/src/BufferView.h index 92e8987350..83b4d39e37 100644 --- a/src/BufferView.h +++ b/src/BufferView.h @@ -283,6 +283,10 @@ public: /// update the internal \c ViewMetricsInfo. 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 & textMetrics(Text const * t); @@ -303,7 +307,6 @@ public: /// get the position and height of the cursor void cursorPosAndHeight(Point & p, int & h) const; - /// void draw(frontend::Painter & pain); diff --git a/src/RowPainter.cpp b/src/RowPainter.cpp index 2d7e12c445..2dc76664a9 100644 --- a/src/RowPainter.cpp +++ b/src/RowPainter.cpp @@ -576,7 +576,7 @@ void RowPainter::paintText() paintStringAndSel(e); // 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); break; diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index f94accae67..c5784070d2 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -1802,8 +1802,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const return; size_t const nrows = pm.rows().size(); - // Use fast lane when drawing is disabled. - if (!pi.pain.isDrawingEnabled()) { + // Use fast lane in nodraw stage. + if (pi.pain.isNull()) { for (size_t i = 0; i != nrows; ++i) { Row const & row = pm.rows()[i]; @@ -1855,17 +1855,11 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const if (i) y += row.ascent(); - RowPainter rp(pi, *text_, row, row_x, y); - // It is not needed to draw on screen if we are not inside. bool const inside = (y + row.descent() >= 0 && y - row.ascent() < ww); - pi.pain.setDrawingEnabled(inside); if (!inside) { - // Paint only the insets to set inset cache correctly - // FIXME: remove paintOnlyInsets when we know that positions - // have already been set. - rp.paintOnlyInsets(); + // Inset positions have already been set in nodraw stage. y += row.descent(); continue; } @@ -1894,6 +1888,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const 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 // and if it has not changed. if (!pi.full_repaint && !row_has_changed) { @@ -1924,7 +1920,7 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const << " row_selection=" << row.selection() << " full_repaint=" << pi.full_repaint << " row_has_changed=" << row_has_changed - << " drawingEnabled=" << pi.pain.isDrawingEnabled()); + << " null painter=" << pi.pain.isNull()); } // Backup full_repaint status and force full repaint @@ -1952,8 +1948,6 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const // Restore full_repaint status. pi.full_repaint = tmp; } - // Re-enable screen drawing for future use of the painter. - pi.pain.setDrawingEnabled(true); //LYXERR(Debug::PAINTING, "."); } diff --git a/src/frontends/Makefile.am b/src/frontends/Makefile.am index 862613a67f..22b7508a7a 100644 --- a/src/frontends/Makefile.am +++ b/src/frontends/Makefile.am @@ -17,6 +17,7 @@ liblyxfrontends_a_SOURCES = \ Delegates.h \ KeyModifier.h \ KeySymbol.h \ + NullPainter.h \ Painter.h \ Clipboard.h \ Selection.h \ diff --git a/src/frontends/NullPainter.h b/src/frontends/NullPainter.h new file mode 100644 index 0000000000..19ac8b66a2 --- /dev/null +++ b/src/frontends/NullPainter.h @@ -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 diff --git a/src/frontends/Painter.h b/src/frontends/Painter.h index d03ebf40e8..79c907d23f 100644 --- a/src/frontends/Painter.h +++ b/src/frontends/Painter.h @@ -147,11 +147,8 @@ public: Color other, size_type from, size_type to, double wordspacing, double textwidth) = 0; - void setDrawingEnabled(bool drawing_enabled) - { drawing_enabled_ = drawing_enabled; } - - /// Indicate wether real screen drawing shall be done or not. - bool isDrawingEnabled() const { return drawing_enabled_; } + // Returns true if the painter does not actually paint. + virtual bool isNull() const = 0; double pixelRatio() const { return pixel_ratio_; } diff --git a/src/frontends/qt4/GuiPainter.cpp b/src/frontends/qt4/GuiPainter.cpp index a3cb089e78..56e9cc4aec 100644 --- a/src/frontends/qt4/GuiPainter.cpp +++ b/src/frontends/qt4/GuiPainter.cpp @@ -171,9 +171,6 @@ void GuiPainter::leaveMonochromeMode() void GuiPainter::point(int x, int y, Color col) { - if (!isDrawingEnabled()) - return; - setQPainterPen(computeColor(col)); drawPoint(x, y); } @@ -184,9 +181,6 @@ void GuiPainter::line(int x1, int y1, int x2, int y2, line_style ls, int lw) { - if (!isDrawingEnabled()) - return; - setQPainterPen(computeColor(col), ls, lw); bool const do_antialiasing = renderHints() & TextAntialiasing && 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, int lw) { - if (!isDrawingEnabled()) - return; - // double the size if needed // FIXME THREAD static QVector points(32); @@ -247,9 +238,6 @@ void GuiPainter::path(int const * xp, int const * yp, line_style ls, int lw) { - if (!isDrawingEnabled()) - return; - QPainterPath bpath; // This is the starting point, so its control points are meaningless bpath.moveTo(xp[0], yp[0]); @@ -278,9 +266,6 @@ void GuiPainter::rectangle(int x, int y, int w, int h, line_style ls, int lw) { - if (!isDrawingEnabled()) - return; - setQPainterPen(computeColor(col), ls, lw); 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) { - if (!isDrawingEnabled()) - return; - 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, int a1, int a2, Color col) { - if (!isDrawingEnabled()) - return; - // LyX usings 1/64ths degree, Qt usings 1/16th setQPainterPen(computeColor(col)); 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); - if (!isDrawingEnabled()) - return; - QImage const image = qlimage.image(); QRectF const drect = QRectF(x, y, w, h); 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) { //LYXERR0("text: x=" << x << ", s=" << s); - if (s.empty() || !isDrawingEnabled()) + if (s.empty()) return; /* Caution: The following ucs4 to QString conversions work for symbol fonts diff --git a/src/frontends/qt4/GuiPainter.h b/src/frontends/qt4/GuiPainter.h index 9657ed9770..7513965ea6 100644 --- a/src/frontends/qt4/GuiPainter.h +++ b/src/frontends/qt4/GuiPainter.h @@ -37,6 +37,9 @@ public: GuiPainter(QPaintDevice *, double pixel_ratio); virtual ~GuiPainter(); + /// This painter paints + virtual bool isNull() const { return false; } + /// draw a line from point to point virtual void line( int x1, int y1, diff --git a/src/frontends/qt4/GuiWorkArea.cpp b/src/frontends/qt4/GuiWorkArea.cpp index fe7082da64..1218061741 100644 --- a/src/frontends/qt4/GuiWorkArea.cpp +++ b/src/frontends/qt4/GuiWorkArea.cpp @@ -16,6 +16,11 @@ #include "ColorCache.h" #include "FontLoader.h" +#include "GuiApplication.h" +#include "GuiCompleter.h" +#include "GuiKeySymbol.h" +#include "GuiPainter.h" +#include "GuiView.h" #include "Menus.h" #include "Buffer.h" @@ -26,11 +31,6 @@ #include "Cursor.h" #include "Font.h" #include "FuncRequest.h" -#include "GuiApplication.h" -#include "GuiCompleter.h" -#include "GuiKeySymbol.h" -#include "GuiPainter.h" -#include "GuiView.h" #include "KeySymbol.h" #include "Language.h" #include "LyX.h" @@ -1271,9 +1271,8 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) return; } - GuiPainter pain(d->screen_, pixelRatio()); d->buffer_view_->updateMetrics(); - d->buffer_view_->draw(pain); + d->updateScreen(); // FIXME: shall we use real_current_font here? (see #10478) FontInfo font = d->buffer_view_->cursor().getFont().fontInfo(); FontMetrics const & fm = theFontMetrics(font); @@ -1365,6 +1364,7 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) ps = Painter::preedit_cursor; // draw one character and update cur_x. + GuiPainter pain(d->screen_, pixelRatio()); cur_x += pain.preeditText(cur_x, cur_y, typed_char, font, ps); } diff --git a/src/mathed/InsetMathMacroTemplate.cpp b/src/mathed/InsetMathMacroTemplate.cpp index 17fc2d04a2..a2fc064c00 100644 --- a/src/mathed/InsetMathMacroTemplate.cpp +++ b/src/mathed/InsetMathMacroTemplate.cpp @@ -231,25 +231,17 @@ void InsetDisplayLabelBox::metrics(MetricsInfo & mi, Dimension & dim) const { InsetLabelBox::metrics(mi, dim); if (!parent_.editing(mi.base.bv) - && parent_.cell(parent_.displayIdx()).empty()) { - dim.wid = 0; - dim.asc = 0; - dim.des = 0; - } + && parent_.cell(parent_.displayIdx()).empty()) + dim.clear(); } void InsetDisplayLabelBox::draw(PainterInfo & pi, int x, int y) const { if (parent_.editing(pi.base.bv) - || !parent_.cell(parent_.displayIdx()).empty()) { - InsetLabelBox::draw(pi, x, y); - } else { - bool enabled = pi.pain.isDrawingEnabled(); - pi.pain.setDrawingEnabled(false); - InsetLabelBox::draw(pi, x, y); - pi.pain.setDrawingEnabled(enabled); - } + || !parent_.cell(parent_.displayIdx()).empty() + || pi.pain.isNull()) + InsetLabelBox::draw(pi, x, y); } From 24c29908bd68ac36199bf413d9fed078f6f1f224 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Fri, 8 Sep 2017 16:55:11 +0200 Subject: [PATCH 02/10] Do the actual drawing in the paint event Historically, because of two-stage drawing, LyX has been painting on a Pixmap, and this pixmap is copied to screen at paint event time. Now that we have three-stage drawing, it is possible to delay the painting to actual paint event and avoid the intermediate Pixmap. Known bug: the cursor is never erased. --- src/frontends/qt4/GuiWorkArea.cpp | 55 ++++++++----------------- src/frontends/qt4/GuiWorkArea_Private.h | 29 ------------- 2 files changed, 17 insertions(+), 67 deletions(-) diff --git a/src/frontends/qt4/GuiWorkArea.cpp b/src/frontends/qt4/GuiWorkArea.cpp index 1218061741..0013670955 100644 --- a/src/frontends/qt4/GuiWorkArea.cpp +++ b/src/frontends/qt4/GuiWorkArea.cpp @@ -246,7 +246,7 @@ SyntheticMouseEvent::SyntheticMouseEvent() 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), need_resize_(false), schedule_redraw_(false), preedit_lines_(1), pixel_ratio_(1.0), @@ -297,7 +297,6 @@ void GuiWorkArea::init() d->cursor_timeout_.setInterval(500); } - d->resetScreen(); // 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. d->buffer_view_->resize(viewport()->width(), viewport()->height()); @@ -311,10 +310,11 @@ void GuiWorkArea::init() setFrameStyle(QFrame::NoFrame); updateWindowTitle(); - viewport()->setAutoFillBackground(false); + //viewport()->setAutoFillBackground(false); // We don't need double-buffering nor SystemBackground on // the viewport because we have our own backing pixmap. - viewport()->setAttribute(Qt::WA_NoSystemBackground); + //viewport()->setAttribute(Qt::WA_NoSystemBackground); + viewport()->setAttribute(Qt::WA_OpaquePaintEvent); setFocusPolicy(Qt::StrongFocus); @@ -342,7 +342,6 @@ GuiWorkArea::~GuiWorkArea() 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. @@ -489,8 +488,7 @@ void GuiWorkArea::redraw(bool update_metrics) } LYXERR(Debug::WORKAREA, "WorkArea::redraw screen"); - d->updateScreen(); - update(0, 0, viewport()->width(), viewport()->height()); + viewport()->update(); /// \warning: scrollbar updating *must* be done after the BufferView is drawn /// because \c BufferView::updateScrollbar() is called in \c BufferView::draw(). @@ -589,7 +587,7 @@ void GuiWorkArea::Private::resizeBufferView() buffer_view_->resize(p->viewport()->width(), p->viewport()->height()); if (cursor_in_view) buffer_view_->scrollToCursor(); - updateScreen(); + p->viewport()->update(); // Update scrollbars which might have changed due different // BufferView dimension. This is especially important when the @@ -1158,20 +1156,12 @@ void GuiWorkArea::resizeEvent(QResizeEvent * ev) } -void GuiWorkArea::Private::update(int x, int y, int w, int h) -{ - p->viewport()->update(x, y, w, h); -} - - 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(); @@ -1179,29 +1169,14 @@ void GuiWorkArea::paintEvent(QPaintEvent * ev) } } - QPainter pain(viewport()); - double const pr = pixelRatio(); - QRectF const rcs = QRectF(rc.x() * pr, rc.y() * pr, rc.width() * pr, rc.height() * pr); + GuiPainter pain(viewport(), pixelRatio()); + d->buffer_view_->draw(pain); - if (lyxrc.use_qimage) { - QImage const & image = static_cast(*d->screen_); - pain.drawImage(rc, image, rcs); - } else { - QPixmap const & pixmap = static_cast(*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) { @@ -1211,9 +1186,8 @@ void GuiWorkArea::Private::showCursor(int x, int y, int h, // 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(); + p->viewport()->update(); updateScrollbar(); - p->viewport()->update(QRect(0, 0, p->viewport()->width(), p->viewport()->height())); schedule_redraw_ = false; // Show the cursor immediately after the update. hideCursor(); @@ -1237,6 +1211,10 @@ void GuiWorkArea::Private::removeCursor() void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) { +//FIXME Broken Feature !! +// I do not think that we are supposed to paint inside this event. Shall we +// just let TextMetrics::breakRow add this to the relevant Row object? +#if 0 QString const & commit_string = e->commitString(); docstring const & preedit_string = qstring_to_ucs4(e->preeditString()); @@ -1272,7 +1250,7 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) } d->buffer_view_->updateMetrics(); - d->updateScreen(); + viewport()->update(); // FIXME: shall we use real_current_font here? (see #10478) FontInfo font = d->buffer_view_->cursor().getFont().fontInfo(); FontMetrics const & fm = theFontMetrics(font); @@ -1281,7 +1259,7 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) int cur_y = d->cursor_->rect().bottom(); // redraw area of preedit string. - update(0, cur_y - height, viewport()->width(), + viewport()->update(0, cur_y - height, viewport()->width(), (height + 1) * d->preedit_lines_); if (preedit_string.empty()) { @@ -1369,8 +1347,9 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) } // update the preedit string screen area. - update(0, cur_y - d->preedit_lines_*height, viewport()->width(), + viewport()->update(0, cur_y - d->preedit_lines_*height, viewport()->width(), (height + 1) * d->preedit_lines_); +#endif // Don't forget to accept the event! e->accept(); diff --git a/src/frontends/qt4/GuiWorkArea_Private.h b/src/frontends/qt4/GuiWorkArea_Private.h index 76b05c0d36..42597172b1 100644 --- a/src/frontends/qt4/GuiWorkArea_Private.h +++ b/src/frontends/qt4/GuiWorkArea_Private.h @@ -92,10 +92,6 @@ struct GuiWorkArea::Private { Private(GuiWorkArea *); - /// update the passed area. - void update(int x, int y, int w, int h); - /// - void updateScreen(); /// void resizeBufferView(); @@ -123,34 +119,9 @@ struct GuiWorkArea::Private 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(pixel_ratio_ * p->viewport()->width()), - static_cast(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(pixel_ratio_ * p->viewport()->width()), - static_cast(pixel_ratio_ * p->viewport()->height())); -#if QT_VERSION >= 0x050000 - x->setDevicePixelRatio(pixel_ratio_); -#endif - screen_ = x; - } - } /// GuiWorkArea * p; /// - QPaintDevice * screen_; - /// BufferView * buffer_view_; /// GuiView * lyx_view_; From e7fdce0b5a8dd2bd1b7dea548248cb8cc815c338 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Thu, 20 Jul 2017 23:31:05 +0200 Subject: [PATCH 03/10] 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 00de06080a..4b78006f63 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_; }; @@ -2761,7 +2767,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); } @@ -2972,6 +2978,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(); @@ -3060,7 +3083,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; @@ -3071,6 +3094,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); @@ -3082,9 +3115,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: @@ -3147,6 +3185,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 83b4d39e37..c0f0b90063 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 2a18cf8cff..ff0b1c6989 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 c5784070d2..c6f6bbf62c 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -1881,7 +1881,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 0013670955..9858d2929e 100644 --- a/src/frontends/qt4/GuiWorkArea.cpp +++ b/src/frontends/qt4/GuiWorkArea.cpp @@ -1170,9 +1170,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(); } From 1a7e342652fd0aaff44793675b86807a0f511abd Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Sat, 22 Jul 2017 01:19:45 +0200 Subject: [PATCH 04/10] Cleanup and simplify WorkArea code Rename cursor to caret to in order to avoid ambiguity. The caret is now the blinking thing only. Remove unused header contents, and some not so useful methods. No intended change of behavior. --- src/frontends/qt4/FindAndReplace.cpp | 4 +- src/frontends/qt4/GuiApplication.cpp | 11 +- src/frontends/qt4/GuiView.cpp | 8 +- src/frontends/qt4/GuiView.h | 2 +- src/frontends/qt4/GuiWorkArea.cpp | 292 ++++++++++-------------- src/frontends/qt4/GuiWorkArea.h | 12 +- src/frontends/qt4/GuiWorkArea_Private.h | 46 ++-- 7 files changed, 152 insertions(+), 223 deletions(-) diff --git a/src/frontends/qt4/FindAndReplace.cpp b/src/frontends/qt4/FindAndReplace.cpp index 4411074de7..607f1653aa 100644 --- a/src/frontends/qt4/FindAndReplace.cpp +++ b/src/frontends/qt4/FindAndReplace.cpp @@ -65,8 +65,8 @@ FindAndReplaceWidget::FindAndReplaceWidget(GuiView & view) replace_work_area_->setFrameStyle(QFrame::StyledPanel); // We don't want two cursors blinking. - find_work_area_->stopBlinkingCursor(); - replace_work_area_->stopBlinkingCursor(); + find_work_area_->stopBlinkingCaret(); + replace_work_area_->stopBlinkingCaret(); } diff --git a/src/frontends/qt4/GuiApplication.cpp b/src/frontends/qt4/GuiApplication.cpp index 1e847464d8..65668d3841 100644 --- a/src/frontends/qt4/GuiApplication.cpp +++ b/src/frontends/qt4/GuiApplication.cpp @@ -122,7 +122,6 @@ #include #include #include -#undef CursorShape #undef None #elif defined(QPA_XCB) #include @@ -1439,7 +1438,7 @@ void GuiApplication::updateCurrentView(FuncRequest const & cmd, DispatchResult & theSelection().haveSelection(bv->cursor().selection()); // update gui - current_view_->restartCursor(); + current_view_->restartCaret(); } if (dr.needMessageUpdate()) { // 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()) LYXERR(Debug::KEY, "Empty kbd action (probably composing)"); if (current_view_) - current_view_->restartCursor(); + current_view_->restartCaret(); return; } @@ -2218,7 +2217,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state) if (!isPrintable(encoded_last_key)) { LYXERR(Debug::KEY, "Non-printable character! Omitting."); if (current_view_) - current_view_->restartCursor(); + current_view_->restartCaret(); return; } // The following modifier check is not needed on Mac. @@ -2240,7 +2239,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state) { if (current_view_) { current_view_->message(_("Unknown function.")); - current_view_->restartCursor(); + current_view_->restartCaret(); } return; } @@ -2255,7 +2254,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state) LYXERR(Debug::KEY, "Unknown Action and not isText() -- giving up"); if (current_view_) { current_view_->message(_("Unknown function.")); - current_view_->restartCursor(); + current_view_->restartCaret(); } return; } diff --git a/src/frontends/qt4/GuiView.cpp b/src/frontends/qt4/GuiView.cpp index b325e4b225..d7e889b694 100644 --- a/src/frontends/qt4/GuiView.cpp +++ b/src/frontends/qt4/GuiView.cpp @@ -4345,13 +4345,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 - * the cursor immediately after the keypress. + * the caret immediately after the keypress. */ if (d.current_work_area_) - d.current_work_area_->startBlinkingCursor(); + d.current_work_area_->startBlinkingCaret(); // Take this occasion to update the other GUI elements. updateDialogs(); @@ -4420,7 +4420,7 @@ void GuiView::resetDialogs() // Now update controls with current buffer. guiApp->setCurrentView(this); restoreLayout(); - restartCursor(); + restartCaret(); } diff --git a/src/frontends/qt4/GuiView.h b/src/frontends/qt4/GuiView.h index dd6fb9bd6e..11f7ead1e1 100644 --- a/src/frontends/qt4/GuiView.h +++ b/src/frontends/qt4/GuiView.h @@ -116,7 +116,7 @@ public: /// \return true if the \c FuncRequest has been dispatched. void dispatch(FuncRequest const & cmd, DispatchResult & dr); - void restartCursor(); + void restartCaret(); /// Update the completion popup and the inline completion state. /// If \c start is true, then a new completion might be started. /// If \c keep is true, an active completion will be kept active diff --git a/src/frontends/qt4/GuiWorkArea.cpp b/src/frontends/qt4/GuiWorkArea.cpp index 9858d2929e..4a4898c859 100644 --- a/src/frontends/qt4/GuiWorkArea.cpp +++ b/src/frontends/qt4/GuiWorkArea.cpp @@ -22,6 +22,7 @@ #include "GuiPainter.h" #include "GuiView.h" #include "Menus.h" +#include "qt_helpers.h" #include "Buffer.h" #include "BufferList.h" @@ -36,7 +37,6 @@ #include "LyX.h" #include "LyXRC.h" #include "LyXVC.h" -#include "qt_helpers.h" #include "Text.h" #include "TextMetrics.h" #include "version.h" @@ -46,7 +46,6 @@ #include "support/convert.h" #include "support/debug.h" -#include "support/gettext.h" #include "support/lassert.h" #include "support/TempFile.h" @@ -68,7 +67,6 @@ #include #include #include -#include #include #include #include @@ -77,8 +75,6 @@ #include #include -#include "support/bind.h" - #include #include @@ -130,17 +126,15 @@ mouse_button::state q_motion_state(Qt::MouseButtons state) namespace frontend { -class CursorWidget { +class CaretWidget { public: - CursorWidget() : rtl_(false), l_shape_(false), completable_(false), - show_(false), x_(0), cursor_width_(0) - { - recomputeWidth(); - } + CaretWidget() : rtl_(false), l_shape_(false), completable_(false), + x_(0), caret_width_(0) + {} void draw(QPainter & painter) { - if (!show_ || !rect_.isValid()) + if (!rect_.isValid()) return; int y = rect_.top(); @@ -149,7 +143,7 @@ public: int bot = rect_.bottom(); // 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 painter.setPen(color_); @@ -157,7 +151,7 @@ public: if (rtl_) painter.drawLine(x_, bot, x_ - l, bot); else - painter.drawLine(x_, bot, x_ + cursor_width_ + r, bot); + painter.drawLine(x_, bot, x_ + caret_width_ + r, bot); } // draw completion triangle @@ -168,8 +162,8 @@ public: painter.drawLine(x_ - 1, m - d, x_ - 1 - d, m); painter.drawLine(x_ - 1, m + d, x_ - 1 - d, m); } else { - painter.drawLine(x_ + cursor_width_, m - d, x_ + cursor_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); + painter.drawLine(x_ + caret_width_, m + d, x_ + caret_width_ + d, m); } } } @@ -203,17 +197,12 @@ public: r = max(r, TabIndicatorWidth); } - // compute overall rectangle - rect_ = QRect(x - l, y, cursor_width_ + r + l, h); - } - - 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 + 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_; } @@ -226,15 +215,13 @@ private: /// triangle to show that a completion is available bool completable_; /// - bool show_; - /// QColor color_; /// rectangle, possibly with l_shape and completion triangle QRect rect_; /// x position (were the vertical line is drawn) int x_; - - int cursor_width_; + /// the width of the vertical blinking bar + int caret_width_; }; @@ -247,12 +234,34 @@ SyntheticMouseEvent::SyntheticMouseEvent() GuiWorkArea::Private::Private(GuiWorkArea * parent) : 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), pixel_ratio_(1.0), completer_(new GuiCompleter(p, p)), dialog_mode_(false), shell_escape_(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,23 +294,18 @@ double GuiWorkArea::pixelRatio() const void GuiWorkArea::init() { // Setup the signals - connect(&d->cursor_timeout_, SIGNAL(timeout()), - this, SLOT(toggleCursor())); + connect(&d->caret_timeout_, SIGNAL(timeout()), + this, SLOT(toggleCaret())); - int const time = QApplication::cursorFlashTime() / 2; - if (time > 0) { - d->cursor_timeout_.setInterval(time); - d->cursor_timeout_.start(); - } else { - // let's initialize this just to be safe - d->cursor_timeout_.setInterval(500); - } + // This connection is closed at the same time as this is destroyed. + d->synthetic_mouse_event_.timeout.timeout.connect([this](){ + generateSyntheticMouseEvent(); + }); // 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. d->buffer_view_->resize(viewport()->width(), viewport()->height()); - d->cursor_ = new frontend::CursorWidget(); - d->cursor_->hide(); + d->caret_ = new frontend::CaretWidget(); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setAcceptDrops(true); @@ -310,63 +314,33 @@ void GuiWorkArea::init() setFrameStyle(QFrame::NoFrame); updateWindowTitle(); - //viewport()->setAutoFillBackground(false); - // We don't need double-buffering nor SystemBackground on - // the viewport because we have our own backing pixmap. - //viewport()->setAttribute(Qt::WA_NoSystemBackground); + d->updateCursorShape(); + + // we paint our own background viewport()->setAttribute(Qt::WA_OpaquePaintEvent); 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() << " viewport height: " << viewport()->height()); // Enables input methods for asian languages. // Must be set when creating custom text editing widgets. setAttribute(Qt::WA_InputMethodEnabled, true); - - d->dialog_mode_ = false; } 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->buffer_view_; - delete d->cursor_; - // Completer has a QObject parent and is thus automatically destroyed. - // See #4758. - // delete completer_; 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() { - setCursorShape(buffer_view_->clickableInset() - ? Qt::PointingHandCursor : Qt::IBeamCursor); + bool const clickable = buffer_view_ && buffer_view_->clickableInset(); + p->viewport()->setCursor(clickable ? Qt::PointingHandCursor + : Qt::IBeamCursor); } @@ -433,14 +407,14 @@ BufferView const & GuiWorkArea::bufferView() const } -void GuiWorkArea::stopBlinkingCursor() +void GuiWorkArea::stopBlinkingCaret() { - d->cursor_timeout_.stop(); - d->hideCursor(); + d->caret_timeout_.stop(); + d->hideCaret(); } -void GuiWorkArea::startBlinkingCursor() +void GuiWorkArea::startBlinkingCaret() { // do not show the cursor if the view is busy if (view().busy()) @@ -453,14 +427,23 @@ void GuiWorkArea::startBlinkingCursor() if (!d->buffer_view_->cursorInView(p, h)) return; - d->showCursor(); + d->showCaret(); //we're not supposed to cache this value. int const time = QApplication::cursorFlashTime() / 2; if (time <= 0) return; - d->cursor_timeout_.setInterval(time); - d->cursor_timeout_.start(); + d->caret_timeout_.setInterval(time); + d->caret_timeout_.start(); +} + + +void GuiWorkArea::toggleCaret() +{ + if (d->caret_visible_) + d->hideCaret(); + else + d->showCaret(); } @@ -482,9 +465,9 @@ void GuiWorkArea::redraw(bool update_metrics) // update cursor position, because otherwise it has to wait until // the blinking interval is over - if (d->cursor_visible_) { - d->hideCursor(); - d->showCursor(); + if (d->caret_visible_) { + d->hideCaret(); + d->showCaret(); } LYXERR(Debug::WORKAREA, "WorkArea::redraw screen"); @@ -522,9 +505,9 @@ void GuiWorkArea::processKeySym(KeySymbol const & key, KeyModifier mod) } // In order to avoid bad surprise in the middle of an operation, - // we better stop the blinking cursor... - // the cursor gets restarted in GuiView::restartCursor() - stopBlinkingCursor(); + // we better stop the blinking caret... + // the cursor gets restarted in GuiView::restartCaret() + stopBlinkingCaret(); guiApp->processKeySym(key, mod); } @@ -544,7 +527,7 @@ void GuiWorkArea::Private::dispatch(FuncRequest const & cmd) // In order to avoid bad surprise in the middle of an operation, we better stop // the blinking cursor. if (notJustMovingTheMouse) - p->stopBlinkingCursor(); + p->stopBlinkingCaret(); buffer_view_->mouseEventDispatch(cmd); @@ -565,7 +548,7 @@ void GuiWorkArea::Private::dispatch(FuncRequest const & cmd) lyx_view_->clearMessage(); // Show the cursor immediately after any operation - p->startBlinkingCursor(); + p->startBlinkingCaret(); } updateCursorShape(); @@ -576,16 +559,16 @@ void GuiWorkArea::Private::resizeBufferView() { // WARNING: Please don't put any code that will trigger a repaint here! // We are already inside a paint event. - p->stopBlinkingCursor(); + p->stopBlinkingCaret(); // Warn our container (GuiView). p->busy(true); Point point; int h = 0; buffer_view_->cursorPosAndHeight(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()); - if (cursor_in_view) + if (caret_in_view) buffer_view_->scrollToCursor(); p->viewport()->update(); @@ -601,19 +584,19 @@ void GuiWorkArea::Private::resizeBufferView() // We might be resizing even if the focus is on another widget so we only // restart the cursor if we have the focus. 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; - Point p; + Point point; int h = 0; - buffer_view_->cursorPosAndHeight(p, h); - if (!buffer_view_->cursorInView(p, h)) + buffer_view_->cursorPosAndHeight(point, h); + if (!buffer_view_->cursorInView(point, h)) return; // RTL or not RTL @@ -636,34 +619,39 @@ void GuiWorkArea::Private::showCursor() && completer_->completionAvailable() && !completer_->popupVisible() && !completer_->inlineVisible(); - cursor_visible_ = true; - cursor_->recomputeWidth(); + caret_visible_ = true; //int cur_x = buffer_view_->getPos(cur).x_; // We may have decided to slide the cursor row so that cursor // 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; - cursor_visible_ = false; - removeCursor(); -} - - -void GuiWorkArea::toggleCursor() -{ - if (d->cursor_visible_) - d->hideCursor(); - else - d->showCursor(); + caret_visible_ = false; + //if (!qApp->focusWidget()) + p->viewport()->update(caret_->rect()); } @@ -686,7 +674,7 @@ void GuiWorkArea::Private::updateScrollbar() void GuiWorkArea::scrollTo(int value) { - stopBlinkingCursor(); + stopBlinkingCaret(); d->buffer_view_->scrollDocView(value, true); if (lyxrc.cursor_follows_scrollbar) { @@ -695,7 +683,7 @@ void GuiWorkArea::scrollTo(int value) d->lyx_view_->updateLayoutList(); } // Show the cursor immediately after any operation. - startBlinkingCursor(); + startBlinkingCaret(); // FIXME QT5 #ifdef Q_WS_X11 QApplication::syncX(); @@ -801,7 +789,7 @@ void GuiWorkArea::focusInEvent(QFocusEvent * e) d->lyx_view_->currentWorkArea()->bufferView().buffer().updateBuffer(); } - startBlinkingCursor(); + startBlinkingCaret(); QAbstractScrollArea::focusInEvent(e); } @@ -809,7 +797,7 @@ void GuiWorkArea::focusInEvent(QFocusEvent * e) void GuiWorkArea::focusOutEvent(QFocusEvent * e) { LYXERR(Debug::DEBUG, "GuiWorkArea::focusOutEvent(): " << this << endl); - stopBlinkingCursor(); + stopBlinkingCaret(); QAbstractScrollArea::focusOutEvent(e); } @@ -1163,53 +1151,21 @@ void GuiWorkArea::paintEvent(QPaintEvent * ev) if (d->needResize()) { d->resizeBufferView(); - if (d->cursor_visible_) { - d->hideCursor(); - d->showCursor(); + if (d->caret_visible_) { + d->hideCaret(); + d->showCaret(); } } GuiPainter pain(viewport(), pixelRatio()); - d->buffer_view_->draw(pain, d->cursor_visible_); + d->buffer_view_->draw(pain, d->caret_visible_); - if (d->cursor_visible_) - d->cursor_->draw(pain); + if (d->caret_visible_) + d->caret_->draw(pain); ev->accept(); } -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()); - p->viewport()->update(); - updateScrollbar(); - schedule_redraw_ = false; - // Show the cursor immediately after the update. - hideCursor(); - p->toggleCursor(); - 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) { //FIXME Broken Feature !! @@ -1236,9 +1192,9 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) // Hide the cursor during the kana-kanji transformation. if (preedit_string.empty()) - startBlinkingCursor(); + startBlinkingCaret(); else - stopBlinkingCursor(); + stopBlinkingCaret(); // last_width : for checking if last preedit string was/wasn't empty. // FIXME THREAD && FIXME @@ -1256,8 +1212,8 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) FontInfo font = d->buffer_view_->cursor().getFont().fontInfo(); FontMetrics const & fm = theFontMetrics(font); int height = fm.maxHeight(); - int cur_x = d->cursor_->rect().left(); - int cur_y = d->cursor_->rect().bottom(); + int cur_x = d->caret_->rect().left(); + int cur_y = d->caret_->rect().bottom(); // redraw area of preedit string. viewport()->update(0, cur_y - height, viewport()->width(), @@ -1277,7 +1233,7 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) // get attributes of input method cursor. // cursor_pos : cursor position in preedit string. size_t cursor_pos = 0; - bool cursor_is_visible = false; + bool caret_is_visible = false; for (int i = 0; i != att.size(); ++i) { if (att.at(i).type == QInputMethodEvent::Cursor) { cursor_pos = att.at(i).start; @@ -1364,7 +1320,7 @@ QVariant GuiWorkArea::inputMethodQuery(Qt::InputMethodQuery query) const // this is the CJK-specific composition window position and // the context menu position when the menu key is pressed. case Qt::ImMicroFocus: - cur_r = d->cursor_->rect(); + cur_r = d->caret_->rect(); if (d->preedit_lines_ != 1) cur_r.moveLeft(10); cur_r.moveBottom(cur_r.bottom() @@ -1491,7 +1447,7 @@ QSize EmbeddedWorkArea::sizeHint () const void EmbeddedWorkArea::disable() { - stopBlinkingCursor(); + stopBlinkingCaret(); if (view().currentWorkArea() != this) return; // No problem if currentMainWorkArea() is 0 (setCurrentWorkArea() diff --git a/src/frontends/qt4/GuiWorkArea.h b/src/frontends/qt4/GuiWorkArea.h index b22d2a5662..4742757907 100644 --- a/src/frontends/qt4/GuiWorkArea.h +++ b/src/frontends/qt4/GuiWorkArea.h @@ -26,10 +26,6 @@ class QDropEvent; class QToolButton; class QWidget; -#ifdef CursorShape -#undef CursorShape -#endif - namespace lyx { class Buffer; @@ -83,8 +79,6 @@ public: /// GuiCompleter & completer(); - Qt::CursorShape cursorShape() const; - /// Return the GuiView this workArea belongs to GuiView const & view() const; GuiView & view(); @@ -94,9 +88,9 @@ public: public Q_SLOTS: /// - void stopBlinkingCursor(); + void stopBlinkingCaret(); /// - void startBlinkingCursor(); + void startBlinkingCaret(); Q_SIGNALS: /// @@ -116,7 +110,7 @@ private Q_SLOTS: /// timer to limit triple clicks void doubleClickTimeout(); /// toggle the cursor's visibility - void toggleCursor(); + void toggleCaret(); /// close this work area. /// Slot for Buffer::closing signal. void close(); diff --git a/src/frontends/qt4/GuiWorkArea_Private.h b/src/frontends/qt4/GuiWorkArea_Private.h index 42597172b1..cae768a689 100644 --- a/src/frontends/qt4/GuiWorkArea_Private.h +++ b/src/frontends/qt4/GuiWorkArea_Private.h @@ -13,30 +13,13 @@ #define WORKAREA_PRIVATE_H #include "FuncRequest.h" -#include "LyXRC.h" #include "support/FileName.h" #include "support/Timeout.h" #include -#include -#include #include -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 { class Buffer; @@ -86,34 +69,30 @@ public: /** * Implementation of the work area (buffer view GUI) */ -class CursorWidget; +class CaretWidget; struct GuiWorkArea::Private { + /// Private(GuiWorkArea *); + /// + ~Private(); + /// 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); /// hide the visible cursor, if it is visible - void hideCursor(); + void hideCaret(); /// show the cursor if it is not visible - void showCursor(); + void showCaret(); /// Set the range and value of the scrollbar and connect to its valueChanged /// signal. void updateScrollbar(); /// Change the cursor when the mouse hovers over a clickable inset void updateCursorShape(); - /// - void setCursorShape(Qt::CursorShape shape); bool needResize() const { return need_resize_ || p->pixelRatio() != pixel_ratio_; @@ -125,18 +104,19 @@ struct GuiWorkArea::Private BufferView * buffer_view_; /// GuiView * lyx_view_; - /// is the cursor currently displayed - bool cursor_visible_; /// - QTimer cursor_timeout_; + CaretWidget * caret_; + /// is the cursor currently displayed + bool caret_visible_; + /// + QTimer caret_timeout_; + /// SyntheticMouseEvent synthetic_mouse_event_; /// DoubleClick dc_event_; - /// - CursorWidget * cursor_; /// bool need_resize_; /// From 14320e5b9ab9e9acfa7a76f0999ebacc9f0c9b89 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Sun, 23 Jul 2017 00:56:27 +0200 Subject: [PATCH 05/10] Make input methods support great again This unbreaks input methods by splitting the part of the code that does the actual drawing to a separate paintPreeditText() method which is called from paintEvent(). The proper solution would have been to introduce the preedit string in the Row object, like is done for completion, but this is too complex to do at this point. The only change in behavior is that now the commit string is inserted in one fell swoop, intead of emulating a number of key events. --- src/frontends/qt4/GuiWorkArea.cpp | 194 ++++++++++++------------ src/frontends/qt4/GuiWorkArea_Private.h | 11 +- 2 files changed, 103 insertions(+), 102 deletions(-) diff --git a/src/frontends/qt4/GuiWorkArea.cpp b/src/frontends/qt4/GuiWorkArea.cpp index 4a4898c859..9459340b54 100644 --- a/src/frontends/qt4/GuiWorkArea.cpp +++ b/src/frontends/qt4/GuiWorkArea.cpp @@ -39,6 +39,7 @@ #include "LyXVC.h" #include "Text.h" #include "TextMetrics.h" +#include "Undo.h" #include "version.h" #include "graphics/GraphicsImage.h" @@ -1144,105 +1145,31 @@ void GuiWorkArea::resizeEvent(QResizeEvent * ev) } -void GuiWorkArea::paintEvent(QPaintEvent * ev) +void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain) { - // 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_); - - if (d->caret_visible_) - d->caret_->draw(pain); - ev->accept(); -} - - -void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) -{ -//FIXME Broken Feature !! -// I do not think that we are supposed to paint inside this event. Shall we -// just let TextMetrics::breakRow add this to the relevant Row object? -#if 0 - 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()) - startBlinkingCaret(); - else - stopBlinkingCaret(); - - // 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(); + if (preedit_string_.empty()) return; - } - d->buffer_view_->updateMetrics(); - viewport()->update(); // 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); - int height = fm.maxHeight(); - int cur_x = d->caret_->rect().left(); - int cur_y = d->caret_->rect().bottom(); - - // redraw area of preedit string. - viewport()->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 const & att = e->attributes(); + int const height = fm.maxHeight(); + int cur_x = caret_->rect().left(); + int cur_y = caret_->rect().bottom(); // get attributes of input method cursor. // cursor_pos : cursor position in preedit string. size_t cursor_pos = 0; - bool caret_is_visible = false; - for (int i = 0; i != att.size(); ++i) { - if (att.at(i).type == QInputMethodEvent::Cursor) { - cursor_pos = att.at(i).start; - cursor_is_visible = att.at(i).length != 0; + bool cursor_is_visible = false; + for (auto const & attr : preedit_attr_) { + if (attr.type == QInputMethodEvent::Cursor) { + cursor_pos = attr.start; + cursor_is_visible = attr.length != 0; break; } } - size_t preedit_length = preedit_string.length(); + size_t const preedit_length = preedit_string_.length(); // get position of selection in input method. // FIXME: isn't there a way to do this simplier? @@ -1251,12 +1178,12 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) // rLength : selected string length in IM. size_t rLength = 0; if (cursor_pos < preedit_length) { - for (int i = 0; i != att.size(); ++i) { - if (att.at(i).type == QInputMethodEvent::TextFormat) { - if (att.at(i).start <= int(cursor_pos) - && int(cursor_pos) < att.at(i).start + att.at(i).length) { - rStart = att.at(i).start; - rLength = att.at(i).length; + for (auto const & attr : preedit_attr_) { + if (attr.type == QInputMethodEvent::TextFormat) { + if (attr.start <= int(cursor_pos) + && int(cursor_pos) < attr.start + attr.length) { + rStart = attr.start; + rLength = attr.length; if (!cursor_is_visible) cursor_pos += rLength; break; @@ -1269,20 +1196,20 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) rLength = 0; } - int const right_margin = d->buffer_view_->rightMargin(); + int const right_margin = buffer_view_->rightMargin(); Painter::preedit_style ps; // Most often there would be only one line: - d->preedit_lines_ = 1; + preedit_lines_ = 1; 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 ps = Painter::preedit_default; // 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_y += height + 1; - ++d->preedit_lines_; + ++preedit_lines_; } // preedit strings are displayed with dashed underline // and partial strings are displayed white on black indicating @@ -1299,14 +1226,79 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) ps = Painter::preedit_cursor; // draw one character and update cur_x. - GuiPainter pain(d->screen_, pixelRatio()); cur_x += pain.preeditText(cur_x, cur_y, typed_char, font, ps); } +} - // update the preedit string screen area. - viewport()->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 cursor 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_); -#endif + + if (d->preedit_string_.empty()) { + d->preedit_lines_ = 1; + e->accept(); + return; + } // Don't forget to accept the event! e->accept(); diff --git a/src/frontends/qt4/GuiWorkArea_Private.h b/src/frontends/qt4/GuiWorkArea_Private.h index cae768a689..7198c569fe 100644 --- a/src/frontends/qt4/GuiWorkArea_Private.h +++ b/src/frontends/qt4/GuiWorkArea_Private.h @@ -27,6 +27,7 @@ class Buffer; namespace frontend { class GuiCompleter; +class GuiPainter; class GuiView; class GuiWorkArea; @@ -94,6 +95,8 @@ struct GuiWorkArea::Private /// Change the cursor when the mouse hovers over a clickable inset void updateCursorShape(); + void paintPreeditText(GuiPainter & pain); + bool needResize() const { return need_resize_ || p->pixelRatio() != pixel_ratio_; } @@ -121,8 +124,14 @@ struct GuiWorkArea::Private bool need_resize_; /// bool schedule_redraw_; - /// + + /// the current preedit text of the input method + docstring preedit_string_; + /// Number of lines used by preedit text int preedit_lines_; + /// the attributes of the preedit text + QList preedit_attr_; + /// Ratio between physical pixels and device-independent pixels /// We save the last used value to detect changes of the /// current pixel_ratio of the viewport. From 8edadee1458192f4fc34ffc2a9aaff878b179257 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Sun, 23 Jul 2017 12:39:26 +0200 Subject: [PATCH 06/10] Update the painting process documentation --- development/PAINTING_ANALYSIS | 44 +++++++++++------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/development/PAINTING_ANALYSIS b/development/PAINTING_ANALYSIS index e53b4b23df..9d625934bb 100644 --- a/development/PAINTING_ANALYSIS +++ b/development/PAINTING_ANALYSIS @@ -67,31 +67,11 @@ drawing from metrics. Other changes are only clean-ups. ** 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 While the full metrics computation tries hard to limit the number of @@ -107,19 +87,19 @@ necessary to break the whole contents to know the width of the cell. * 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 paragraphs into rows. It stores the dimension of insets (both normal and math) in bv::coordCache. - + the drawing phase draws the contents and caches the inset - positions. Since the caching of positions is useful in itself, - there is a provision for drawing "without" drawing when the only - thing we want is to cache inset positions - (Painter::setDrawingEnabled). + + the nodraw drawing phase paints the screen (see below) with a null + painter. The only useful effect is to store the inset positions. + + + an update() signal is sent. This in turn will trigger a paint + event, and the actual screen painting will happen then. The machinery is controlled via bv::processUpdateFlags. This method is called at the end of bv::mouseEventDispatch and in @@ -145,7 +125,7 @@ update flag is Update::None. ** Metrics computation 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. tm::redoParagraph will call Inset::metrics for each inset. In the case @@ -155,8 +135,9 @@ all the paragraphs of the inset. ** Drawing the work area. -This is done in bv::draw. This method is triggered mainly by -Buffer::changed, which draws all the work areas that show the given buffer. +This is done in bv::draw. This method is triggered by a paint event, +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 bv::processUpdateFlags, it is not clear whether the update strategy @@ -186,3 +167,6 @@ The action depends on the update strategy: + SingleParUpdate: only tries to repaint current paragraph in a way that is not yet very clear to me. + +BufferView::draw can also be called with a null painter from +BufferView::updateMetrics(). From fb655725b729dc164adeb21ea2b15ba95174ef26 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Sun, 23 Jul 2017 15:50:35 +0200 Subject: [PATCH 07/10] Remove workaround that is not necessary anymore. This code was necessary to handle cases where the insets positions were not yet in cache. This cannot happen anymore thanks to the nodraw stage. --- development/PAINTING_ANALYSIS | 5 ----- src/BufferView.cpp | 32 ++------------------------------ src/BufferView.h | 3 +-- src/Cursor.cpp | 13 ------------- src/Cursor.h | 2 -- 5 files changed, 3 insertions(+), 52 deletions(-) diff --git a/development/PAINTING_ANALYSIS b/development/PAINTING_ANALYSIS index 9d625934bb..f734edb3b0 100644 --- a/development/PAINTING_ANALYSIS +++ b/development/PAINTING_ANALYSIS @@ -67,11 +67,6 @@ drawing from metrics. Other changes are only clean-ups. ** When a paragraph ends with a newline, compute correctly the height of the extra row. -** Cleanup after complete metrics - Then the following can be done: - + remove hack in InsetMathNest::drawSelection - + remove Cursor::inCoordCache? - ** Merging bv::updateMetrics and tm::metrics While the full metrics computation tries hard to limit the number of diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 4b78006f63..9e8faf93a6 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -2995,7 +2995,7 @@ bool BufferView::needRepaint(Text const * text, Row const & row) const } -void BufferView::checkCursorScrollOffset(PainterInfo & pi) +void BufferView::checkCursorScrollOffset() { CursorSlice rowSlice = d->cursor_.bottom(); TextMetrics const & tm = textMetrics(rowSlice.text()); @@ -3012,34 +3012,6 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi) // Set the row on which the cursor lives. 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 - frontend::NullPainter np; - PainterInfo(this, np); - // No need to care about vertical position. - RowPainter rp(pi, buffer().text(), row, -d->horiz_scroll_offset_, 0); - rp.paintText(); - } - // Current x position of the cursor in pixels int cur_x = getPos(d->cursor_).x_; @@ -3106,7 +3078,7 @@ void BufferView::draw(frontend::Painter & pain, bool paint_caret) // Check whether the row where the cursor lives needs to be scrolled. // Update the drawing strategy if needed. - checkCursorScrollOffset(pi); + checkCursorScrollOffset(); switch (d->update_strategy_) { diff --git a/src/BufferView.h b/src/BufferView.h index c0f0b90063..4b9f6be788 100644 --- a/src/BufferView.h +++ b/src/BufferView.h @@ -42,7 +42,6 @@ class FuncStatus; class Intl; class Inset; class Length; -class PainterInfo; class ParIterator; class ParagraphMetrics; class Point; @@ -374,7 +373,7 @@ private: // Check whether the row where the cursor lives needs to be scrolled. // Update the drawing strategy if needed. - void checkCursorScrollOffset(PainterInfo & pi); + void checkCursorScrollOffset(); /// The minimal size of the document that is visible. Used /// when it is allowed to scroll below the document. diff --git a/src/Cursor.cpp b/src/Cursor.cpp index a139a2b310..0d68648685 100644 --- a/src/Cursor.cpp +++ b/src/Cursor.cpp @@ -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 { Point p = bv().getPos(*this); diff --git a/src/Cursor.h b/src/Cursor.h index 5c675c493a..4c9d41d0cf 100644 --- a/src/Cursor.h +++ b/src/Cursor.h @@ -216,8 +216,6 @@ public: /// are we entering a macro name? bool & macromode() { return macromode_; } - /// returns true when all insets in cursor stack are in cache - bool inCoordCache() const; /// returns x,y position void getPos(int & x, int & y) const; /// return logical positions between which the cursor is situated From b0737d258903ca293a72447f144776c6e073bdc6 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Thu, 24 Aug 2017 17:37:56 +0200 Subject: [PATCH 08/10] Rename more instances of "cursor" to "caret" Thanks to Pavel for the hint. --- src/BufferView.cpp | 2 +- src/BufferView.h | 4 ++-- src/frontends/qt4/GuiWorkArea.cpp | 31 +++++++++++++------------ src/frontends/qt4/GuiWorkArea.h | 2 +- src/frontends/qt4/GuiWorkArea_Private.h | 6 ++--- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 9e8faf93a6..1673de1817 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -2905,7 +2905,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(); Font const font = cur.real_current_font; diff --git a/src/BufferView.h b/src/BufferView.h index 4b9f6be788..7de6985d44 100644 --- a/src/BufferView.h +++ b/src/BufferView.h @@ -307,8 +307,8 @@ public: bool paragraphVisible(DocIterator const & dit) const; /// is the cursor currently visible in the view bool cursorInView(Point const & p, int h) const; - /// get the position and height of the cursor - void cursorPosAndHeight(Point & p, int & h) const; + /// get the position and height of the caret + void caretPosAndHeight(Point & p, int & h) const; /// void draw(frontend::Painter & pain, bool paint_caret); diff --git a/src/frontends/qt4/GuiWorkArea.cpp b/src/frontends/qt4/GuiWorkArea.cpp index 9459340b54..bd3104c250 100644 --- a/src/frontends/qt4/GuiWorkArea.cpp +++ b/src/frontends/qt4/GuiWorkArea.cpp @@ -198,6 +198,7 @@ public: 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); @@ -209,7 +210,7 @@ public: QRect const & rect() { return rect_; } private: - /// cursor is in RTL or LTR text + /// caret is in RTL or LTR text bool rtl_; /// indication for RTL or LTR bool l_shape_; @@ -423,7 +424,7 @@ void GuiWorkArea::startBlinkingCaret() Point p; 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. if (!d->buffer_view_->cursorInView(p, h)) return; @@ -464,7 +465,7 @@ void GuiWorkArea::redraw(bool update_metrics) 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 if (d->caret_visible_) { d->hideCaret(); @@ -507,7 +508,7 @@ void GuiWorkArea::processKeySym(KeySymbol const & key, KeyModifier mod) // In order to avoid bad surprise in the middle of an operation, // we better stop the blinking caret... - // the cursor gets restarted in GuiView::restartCaret() + // the caret gets restarted in GuiView::restartCaret() stopBlinkingCaret(); guiApp->processKeySym(key, mod); } @@ -526,7 +527,7 @@ void GuiWorkArea::Private::dispatch(FuncRequest const & cmd) 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 - // the blinking cursor. + // the blinking caret. if (notJustMovingTheMouse) p->stopBlinkingCaret(); @@ -548,7 +549,7 @@ void GuiWorkArea::Private::dispatch(FuncRequest const & cmd) // FIXME: let GuiView take care of those. lyx_view_->clearMessage(); - // Show the cursor immediately after any operation + // Show the caret immediately after any operation p->startBlinkingCaret(); } @@ -566,7 +567,7 @@ void GuiWorkArea::Private::resizeBufferView() Point point; int h = 0; - buffer_view_->cursorPosAndHeight(point, h); + buffer_view_->caretPosAndHeight(point, h); bool const caret_in_view = buffer_view_->cursorInView(point, h); buffer_view_->resize(p->viewport()->width(), p->viewport()->height()); if (caret_in_view) @@ -581,9 +582,9 @@ void GuiWorkArea::Private::resizeBufferView() need_resize_ = 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 - // restart the cursor if we have the focus. + // restart the caret if we have the focus. if (p->hasFocus()) QTimer::singleShot(50, p, SLOT(startBlinkingCaret())); } @@ -596,7 +597,7 @@ void GuiWorkArea::Private::showCaret() Point point; int h = 0; - buffer_view_->cursorPosAndHeight(point, h); + buffer_view_->caretPosAndHeight(point, h); if (!buffer_view_->cursorInView(point, h)) return; @@ -614,7 +615,7 @@ void GuiWorkArea::Private::showCaret() if (realfont.language() == latex_language) l_shape = false; - // show cursor on screen + // show caret on screen Cursor & cur = buffer_view_->cursor(); bool completable = cur.inset().showCompletionCursor() && completer_->completionAvailable() @@ -623,7 +624,7 @@ void GuiWorkArea::Private::showCaret() caret_visible_ = true; //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. point.x_ -= buffer_view_->horizScrollOffset(); @@ -683,7 +684,7 @@ void GuiWorkArea::scrollTo(int value) // FIXME: let GuiView take care of those. d->lyx_view_->updateLayoutList(); } - // Show the cursor immediately after any operation. + // Show the caret immediately after any operation. startBlinkingCaret(); // FIXME QT5 #ifdef Q_WS_X11 @@ -1271,7 +1272,7 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e) viewport()->update(); } - // Hide the cursor during the test transformation. + // Hide the caret during the test transformation. if (e->preeditString().isEmpty()) startBlinkingCaret(); else @@ -1317,7 +1318,7 @@ QVariant GuiWorkArea::inputMethodQuery(Qt::InputMethodQuery query) const cur_r.moveLeft(10); cur_r.moveBottom(cur_r.bottom() + cur_r.height() * (d->preedit_lines_ - 1)); - // return lower right of cursor in LyX. + // return lower right of caret in LyX. return cur_r; default: return QWidget::inputMethodQuery(query); diff --git a/src/frontends/qt4/GuiWorkArea.h b/src/frontends/qt4/GuiWorkArea.h index 4742757907..2a875ad4ce 100644 --- a/src/frontends/qt4/GuiWorkArea.h +++ b/src/frontends/qt4/GuiWorkArea.h @@ -109,7 +109,7 @@ private Q_SLOTS: void scrollTo(int value); /// timer to limit triple clicks void doubleClickTimeout(); - /// toggle the cursor's visibility + /// toggle the caret's visibility void toggleCaret(); /// close this work area. /// Slot for Buffer::closing signal. diff --git a/src/frontends/qt4/GuiWorkArea_Private.h b/src/frontends/qt4/GuiWorkArea_Private.h index 7198c569fe..199ce253d6 100644 --- a/src/frontends/qt4/GuiWorkArea_Private.h +++ b/src/frontends/qt4/GuiWorkArea_Private.h @@ -85,9 +85,9 @@ struct GuiWorkArea::Private /// void dispatch(FuncRequest const & cmd0); - /// hide the visible cursor, if it is visible + /// hide the visible caret, if it is visible void hideCaret(); - /// show the cursor if it is not visible + /// show the caret if it is not visible void showCaret(); /// Set the range and value of the scrollbar and connect to its valueChanged /// signal. @@ -110,7 +110,7 @@ struct GuiWorkArea::Private /// CaretWidget * caret_; - /// is the cursor currently displayed + /// is the caret currently displayed bool caret_visible_; /// QTimer caret_timeout_; From 3b404b0023e611bfbdb64fa723c5d56d7477fcf7 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Wed, 30 Aug 2017 18:05:16 +0200 Subject: [PATCH 09/10] Update insets position in cache in more cases This patch makes sure that, every time a ParagraphMetrics has its position set, the inset positions for the insets held by this paragraph are remembered too. This is complementary to BufferView::updatePosCache, but I do not have hard evidence that this is required other than to increase robustness. It may help in some cases when scrolling the document (scrollbar, cursor up/down, page up/down). --- src/BufferView.cpp | 4 ++++ src/TextMetrics.cpp | 11 +++++++++++ src/TextMetrics.h | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 1673de1817..ab264baf7b 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -2658,6 +2658,7 @@ bool BufferView::singleParUpdate() // the singlePar optimisation. return false; + tm.updatePosCache(bottom_pit); d->update_strategy_ = SingleParUpdate; LYXERR(Debug::PAINTING, "\ny1: " << pm.position() - pm.ascent() @@ -2711,6 +2712,7 @@ void BufferView::updateMetrics() // in the first line. } anchor_pm.setPosition(d->anchor_ypos_); + tm.updatePosCache(d->anchor_pit_); LYXERR(Debug::PAINTING, "metrics: " << " anchor pit = " << d->anchor_pit_ @@ -2726,6 +2728,7 @@ void BufferView::updateMetrics() y1 -= pm.descent(); // Save the paragraph position in the cache. pm.setPosition(y1); + tm.updatePosCache(pit1); y1 -= pm.ascent(); } @@ -2739,6 +2742,7 @@ void BufferView::updateMetrics() y2 += pm.ascent(); // Save the paragraph position in the cache. pm.setPosition(y2); + tm.updatePosCache(pit2); y2 += pm.descent(); } diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index c6f6bbf62c..c88e5c6917 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -43,6 +43,7 @@ #include "frontends/FontMetrics.h" #include "frontends/Painter.h" +#include "frontends/NullPainter.h" #include "support/debug.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 { return text_->isMainText() ? pm.rightMargin(*bv_) : 0; @@ -1219,6 +1228,7 @@ void TextMetrics::newParMetricsDown() redoParagraph(pit); par_metrics_[pit].setPosition(last.second.position() + last.second.descent() + par_metrics_[pit].ascent()); + updatePosCache(pit); } @@ -1233,6 +1243,7 @@ void TextMetrics::newParMetricsUp() redoParagraph(pit); par_metrics_[pit].setPosition(first.second.position() - first.second.ascent() - par_metrics_[pit].descent()); + updatePosCache(pit); } // y is screen coordinate diff --git a/src/TextMetrics.h b/src/TextMetrics.h index 3000b218bf..ae99490955 100644 --- a/src/TextMetrics.h +++ b/src/TextMetrics.h @@ -62,6 +62,11 @@ public: /// 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 /// Basically the same routine as Paragraph::getFont() in Paragraph.cpp. /// The difference is that this one is used for displaying, and thus we From 4f4383cac59f52decba400f2c78e267f7bb162e3 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Fri, 8 Sep 2017 16:56:13 +0200 Subject: [PATCH 10/10] remove LyXRC::use_qimage This is not needed anymore now that we do not need a backup pixmap. Update LyXRC format to 23 and add conversion. --- lib/configure.py | 2 +- lib/scripts/prefs2prefs_prefs.py | 20 +++++++++++++++++++- src/LyXRC.cpp | 17 +---------------- src/LyXRC.h | 3 --- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/configure.py b/lib/configure.py index a9f161ef7e..bb8f927e3a 100644 --- a/lib/configure.py +++ b/lib/configure.py @@ -1764,7 +1764,7 @@ if __name__ == '__main__': lyx_check_config = True lyx_kpsewhich = True outfile = 'lyxrc.defaults' - lyxrc_fileformat = 22 + lyxrc_fileformat = 23 rc_entries = '' lyx_keep_temps = False version_suffix = '' diff --git a/lib/scripts/prefs2prefs_prefs.py b/lib/scripts/prefs2prefs_prefs.py index 68b4d837dc..ff24273192 100644 --- a/lib/scripts/prefs2prefs_prefs.py +++ b/lib/scripts/prefs2prefs_prefs.py @@ -94,6 +94,9 @@ # Add pygmentize_command for the python pygments syntax highlighter # No conversion necessary. +# Incremented to format 23, by lasgouttes +# Remove use_qimage preference + # NOTE: The format should also be updated in LYXRC.cpp and # in configure.py. @@ -364,6 +367,20 @@ def remove_print_support(line): # 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 = [ [ 1, [ # there were several conversions for format 1 export_menu, @@ -392,5 +409,6 @@ conversions = [ [ 19, [remove_print_support]], [ 20, []], [ 21, []], - [ 22, []] + [ 22, []], + [ 23, [remove_use_qimage]] ] diff --git a/src/LyXRC.cpp b/src/LyXRC.cpp index 315495f85c..ada43581a2 100644 --- a/src/LyXRC.cpp +++ b/src/LyXRC.cpp @@ -59,7 +59,7 @@ namespace { // The format should also be updated in configure.py, and conversion code // 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! LexerKeyword lyxrcTags[] = { @@ -195,7 +195,6 @@ LexerKeyword lyxrcTags[] = { { "\\use_converter_needauth_forbidden", LyXRC::RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN }, { "\\use_lastfilepos", LyXRC::RC_USELASTFILEPOS }, { "\\use_pixmap_cache", LyXRC::RC_USE_PIXMAP_CACHE }, - { "\\use_qimage", LyXRC::RC_USE_QIMAGE }, // compatibility with versions older than 1.4.0 only { "\\use_system_colors", LyXRC::RC_USE_SYSTEM_COLORS }, { "\\use_system_theme_icons", LyXRC::RC_USE_SYSTEM_THEME_ICONS }, @@ -327,7 +326,6 @@ void LyXRC::setDefaults() use_system_colors = false; use_tooltip = true; use_pixmap_cache = false; - use_qimage = true; converter_cache_maxage = 6 * 30 * 24 * 3600; // 6 months user_name = to_utf8(support::user_name()); 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: lexrc >> use_pixmap_cache; break; - case RC_USE_QIMAGE: - lexrc >> use_qimage; - break; case RC_SPELLCHECKER: lexrc >> spellchecker; break; @@ -2434,15 +2429,6 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c if (tag != RC_LAST) break; // fall through - case RC_USE_QIMAGE: - if (ignore_system_lyxrc || - use_qimage != system_lyxrc.use_qimage) { - os << "\\use_qimage " - << convert(use_qimage) - << '\n'; - } - if (tag != RC_LAST) - break; os << "\n#\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_TOOLTIP: case LyXRC::RC_USE_PIXMAP_CACHE: - case LyXRC::RC_USE_QIMAGE: case LyXRC::RC_USE_SYSTEM_THEME_ICONS: case LyXRC::RC_VIEWDVI_PAPEROPTION: case LyXRC::RC_SINGLE_CLOSE_TAB_BUTTON: diff --git a/src/LyXRC.h b/src/LyXRC.h index 38fcd37394..9b4b09fac3 100644 --- a/src/LyXRC.h +++ b/src/LyXRC.h @@ -174,7 +174,6 @@ public: RC_USE_SYSTEM_COLORS, RC_USE_TOOLTIP, RC_USE_PIXMAP_CACHE, - RC_USE_QIMAGE, RC_USE_SYSTEM_THEME_ICONS, RC_VIEWDVI_PAPEROPTION, RC_VIEWER, @@ -333,8 +332,6 @@ public: bool use_system_colors; /// Use pixmap cache? bool use_pixmap_cache; - /// Use QImage backend? - bool use_qimage; /// Spellchecker engine: aspell, hunspell, etc std::string spellchecker; /// Alternate language for spellchecker