From 8fb1aa51f8d14747bd21d9e113a8dd301a2f07dc Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Fri, 25 Jul 2014 22:35:08 +0200 Subject: [PATCH 1/6] Whitespace only --- src/BufferView.cpp | 114 ++++++++++++++++++++++---------------------- src/BufferView.h | 2 +- src/RowPainter.cpp | 2 +- src/TextMetrics.cpp | 2 +- src/TextMetrics.h | 4 +- 5 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/BufferView.cpp b/src/BufferView.cpp index bc8162b978..faa08f0320 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -118,12 +118,12 @@ bool findNextInset(DocIterator & dit, vector const & codes, while (tmpdit) { Inset const * inset = tmpdit.nextInset(); if (inset) { - bool const valid_code = std::find(codes.begin(), codes.end(), + bool const valid_code = std::find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end(); InsetCommand const * ic = inset->asInsetCommand(); bool const same_or_no_contents = contents.empty() || (ic && (ic->getFirstNonOptParam() == contents)); - + if (valid_code && same_or_no_contents) { dit = tmpdit; return true; @@ -228,7 +228,7 @@ struct BufferView::Private Private(BufferView & bv): wh_(0), cursor_(bv), anchor_pit_(0), anchor_ypos_(0), inlineCompletionUniqueChars_(0), - last_inset_(0), clickable_inset_(false), + last_inset_(0), clickable_inset_(false), mouse_position_cache_(), bookmark_edit_position_(-1), gui_(0) {} @@ -325,9 +325,9 @@ BufferView::~BufferView() fp.pit = d->cursor_.bottom().pit(); fp.pos = d->cursor_.bottom().pos(); theSession().lastFilePos().save(buffer_.fileName(), fp); - + if (d->last_inset_) - d->last_inset_->setMouseHover(this, false); + d->last_inset_->setMouseHover(this, false); delete d; } @@ -498,7 +498,7 @@ void BufferView::updateScrollbar() d->scrollbarParameters_.page_step = height_; Text & t = buffer_.text(); - TextMetrics & tm = d->text_metrics_[&t]; + TextMetrics & tm = d->text_metrics_[&t]; LYXERR(Debug::GUI, " Updating scrollbar: height: " << t.paragraphs().size() @@ -658,7 +658,7 @@ void BufferView::setCursorFromScrollbar() case CUR_INSIDE: int const y = getPos(oldcur).y_; newy = min(last, max(y, first)); - if (y == newy) + if (y == newy) return; } // We reset the cursor because cursorStatus() does not @@ -778,7 +778,7 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, // the bookmark. if (bottom_pit < int(buffer_.paragraphs().size())) { dit = doc_iterator_begin(&buffer_); - + dit.pit() = bottom_pit; dit.pos() = min(bottom_pos, dit.paragraph().size()); success = true; @@ -958,7 +958,7 @@ void BufferView::makeDocumentClass() void BufferView::updateDocumentClass(DocumentClassConstPtr olddc) { message(_("Converting document to new document class...")); - + StableDocIterator backcur(d->cursor_); ErrorList & el = buffer_.errorList("Class Switch"); cap::switchBetweenClasses( @@ -1222,7 +1222,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) Cursor & cur = d->cursor_; // Don't dispatch function that does not apply to internal buffers. - if (buffer_.isInternal() + if (buffer_.isInternal() && lyxaction.funcHasFlag(cmd.action(), LyXAction::NoInternal)) return; @@ -1246,7 +1246,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) << (unknown_tokens == 1 ? "" : "s")); } updateDocumentClass(olddc); - + // We are most certainly here because of a change in the document // It is then better to make sure that all dialogs are in sync with // current document settings. @@ -1254,7 +1254,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) dr.forceBufferUpdate(); break; } - + case LFUN_LAYOUT_MODULES_CLEAR: { cur.recordUndoFullDocument(); buffer_.params().clearLayoutModules(); @@ -1267,7 +1267,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) case LFUN_LAYOUT_MODULE_ADD: { BufferParams const & params = buffer_.params(); if (!params.layoutModuleCanBeAdded(argument)) { - LYXERR0("Module `" << argument << + LYXERR0("Module `" << argument << "' cannot be added due to failed requirements or " "conflicts with installed modules."); break; @@ -1281,9 +1281,9 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } case LFUN_TEXTCLASS_APPLY: { - // since this shortcircuits, the second call is made only if + // since this shortcircuits, the second call is made only if // the first fails - bool const success = + bool const success = LayoutFileList::get().load(argument, buffer_.temppath()) || LayoutFileList::get().load(argument, buffer_.filePath()); if (!success) { @@ -1310,12 +1310,12 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } case LFUN_TEXTCLASS_LOAD: { - // since this shortcircuits, the second call is made only if + // since this shortcircuits, the second call is made only if // the first fails - bool const success = + bool const success = LayoutFileList::get().load(argument, buffer_.temppath()) || LayoutFileList::get().load(argument, buffer_.filePath()); - if (!success) { + if (!success) { docstring s = bformat(_("The document class `%1$s' " "could not be loaded."), from_utf8(argument)); frontend::Alert::error(_("Could not load class"), s); @@ -1381,7 +1381,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } break; } - + case LFUN_PARAGRAPH_GOTO: { int const id = convert(cmd.getArg(0)); int const pos = convert(cmd.getArg(1)); @@ -1459,7 +1459,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) // FIXME: Move this LFUN to Buffer so that we don't have to do this: dr.screenUpdate(Update::Force | Update::FitCursor); break; - + case LFUN_CHANGE_PREVIOUS: findPreviousChange(this); // FIXME: Move this LFUN to Buffer so that we don't have to do this: @@ -1620,7 +1620,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) case LFUN_SCREEN_SHOW_CURSOR: showCursor(); break; - + case LFUN_SCREEN_RECENTER: recenter(); break; @@ -1648,7 +1648,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) if (inset->delDatabase(cmd.argument())) { buffer_.invalidateBibfileCache(); dr.forceBufferUpdate(); - } + } } break; } @@ -1726,13 +1726,13 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) updateHoveredInset(); d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_, - true, act == LFUN_SCREEN_UP); + true, act == LFUN_SCREEN_UP); //FIXME: what to do with cur.x_target()? bool update = in_texted && cur.bv().checkDepm(cur, old); cur.finishUndo(); if (update || cur.mark()) - dr.screenUpdate(Update::Force | Update::FitCursor); + dr.screenUpdate(Update::Force | Update::FitCursor); if (update) dr.forceBufferUpdate(); break; @@ -1898,7 +1898,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) if (decodeInsetParam(name, data, buffer_)) lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data)); else - lyxerr << "Inset type '" << name << + lyxerr << "Inset type '" << name << "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl; break; } @@ -2108,10 +2108,10 @@ void BufferView::updateHoveredInset() const need_redraw |= d->last_inset_->setMouseHover(this, false); d->last_inset_ = 0; } - + if (covering_inset && covering_inset->setMouseHover(this, true)) { need_redraw = true; - // Only the insets that accept the hover state, do + // Only the insets that accept the hover state, do // clear the last_inset_, so only set the last_inset_ // member if the hovered setting is accepted. d->last_inset_ = covering_inset; @@ -2119,9 +2119,9 @@ void BufferView::updateHoveredInset() const if (need_redraw) { LYXERR(Debug::PAINTING, "Mouse hover detected at: (" - << d->mouse_position_cache_.x_ << ", " + << d->mouse_position_cache_.x_ << ", " << d->mouse_position_cache_.y_ << ")"); - + d->update_strategy_ = DecorationUpdate; // This event (moving without mouse click) is not passed further. @@ -2197,7 +2197,7 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) if (badcursor) cursor().fixIfBroken(); } - + // Do we have a selection? theSelection().haveSelection(cursor().selection()); @@ -2431,8 +2431,8 @@ bool BufferView::checkDepm(Cursor & cur, Cursor & old) return false; d->cursor_ = cur; - - // we would rather not do this here, but it needs to be done before + + // we would rather not do this here, but it needs to be done before // the changed() signal is sent. buffer_.updateBuffer(); @@ -2460,7 +2460,7 @@ bool BufferView::mouseSetCursor(Cursor & cur, bool select) d->cursor_.fixIfBroken(); // FIXME: shift-mouse selection doesn't work well across insets. - bool const do_selection = + bool const do_selection = select && &d->cursor_.normalAnchor().inset() == &cur.inset(); // do the dEPM magic if needed @@ -2576,7 +2576,7 @@ bool BufferView::singleParUpdate() // (if this paragraph contains insets etc., rebreaking will // recursively descend) tm.redoParagraph(bottom_pit); - ParagraphMetrics const & pm = tm.parMetrics(bottom_pit); + ParagraphMetrics const & pm = tm.parMetrics(bottom_pit); if (pm.height() != old_height) // Paragraph height has changed so we cannot proceed to // the singlePar optimisation. @@ -2613,7 +2613,7 @@ void BufferView::updateMetrics() // make sure inline completion pointer is ok if (d->inlineCompletionPos_.fixIfBroken()) d->inlineCompletionPos_ = DocIterator(); - + if (d->anchor_pit_ >= npit) // The anchor pit must have been deleted... d->anchor_pit_ = npit - 1; @@ -2621,19 +2621,19 @@ void BufferView::updateMetrics() // Rebreak anchor paragraph. tm.redoParagraph(d->anchor_pit_); ParagraphMetrics & anchor_pm = tm.par_metrics_[d->anchor_pit_]; - + // position anchor if (d->anchor_pit_ == 0) { int scrollRange = d->scrollbarParameters_.max - d->scrollbarParameters_.min; - + // Complete buffer visible? Then it's easy. if (scrollRange == 0) d->anchor_ypos_ = anchor_pm.ascent(); - + // FIXME: Some clever handling needed to show // the _first_ paragraph up to the top if the cursor is // in the first line. - } + } anchor_pm.setPosition(d->anchor_ypos_); LYXERR(Debug::PAINTING, "metrics: " @@ -2727,14 +2727,14 @@ Point BufferView::coordOffset(DocIterator const & dit) const CursorSlice const & sl = dit[i]; int xx = 0; int yy = 0; - + // get relative position inside sl.inset() sl.inset().cursorPos(*this, sl, dit.boundary() && (i + 1 == dit.depth()), xx, yy); - + // Make relative position inside of the edited inset relative to sl.inset() x += xx; y += yy; - + // In case of an RTL inset, the edited inset will be positioned to the left // of xx:yy if (sl.text()) { @@ -2759,7 +2759,7 @@ Point BufferView::coordOffset(DocIterator const & dit) const Dimension const dim = sl.inset().dimension(*this); lastw = dim.wid; } - + //lyxerr << "Cursor::getPos, i: " // << i << " x: " << xx << " y: " << y << endl; } @@ -2788,20 +2788,20 @@ Point BufferView::coordOffset(DocIterator const & dit) const for (size_t rit = 0; rit != rend; ++rit) y += pm.rows()[rit].height(); y += pm.rows()[rend].ascent(); - + TextMetrics const & bottom_tm = textMetrics(dit.bottom().text()); - + // Make relative position from the nested inset now bufferview absolute. int xx = bottom_tm.cursorX(dit.bottom(), dit.boundary() && dit.depth() == 1); x += xx; - - // In the RTL case place the nested inset at the left of the cursor in + + // In the RTL case place the nested inset at the left of the cursor in // the outer paragraph bool boundary_1 = dit.boundary() && 1 == dit.depth(); bool rtl = bottom_tm.isRTL(dit.bottom(), boundary_1); if (rtl) x -= lastw; - + return Point(x, y); } @@ -2815,7 +2815,7 @@ Point BufferView::getPos(DocIterator const & dit) const TextMetrics const & tm = textMetrics(bot.text()); // offset from outer paragraph - Point p = coordOffset(dit); + Point p = coordOffset(dit); p.y_ += tm.parMetrics(bot.pit()).position(); return p; } @@ -2901,9 +2901,9 @@ void BufferView::draw(frontend::Painter & pain) // and possibly grey out below pair lastpm = tm.last(); int const y2 = lastpm.second->position() + lastpm.second->descent(); - + if (y2 < height_) { - Color color = buffer().isInternal() + Color color = buffer().isInternal() ? Color_background : Color_bottomarea; pain.fillRectangle(0, y2, width_, height_ - y2, color); } @@ -3057,7 +3057,7 @@ bool samePar(DocIterator const & a, DocIterator const & b) } -void BufferView::setInlineCompletion(Cursor const & cur, DocIterator const & pos, +void BufferView::setInlineCompletion(Cursor const & cur, DocIterator const & pos, docstring const & completion, size_t uniqueChars) { uniqueChars = min(completion.size(), uniqueChars); @@ -3066,9 +3066,9 @@ void BufferView::setInlineCompletion(Cursor const & cur, DocIterator const & pos bool singlePar = true; d->inlineCompletion_ = completion; d->inlineCompletionUniqueChars_ = min(completion.size(), uniqueChars); - + //lyxerr << "setInlineCompletion pos=" << pos << " completion=" << completion << " uniqueChars=" << uniqueChars << std::endl; - + // at new position? DocIterator const & old = d->inlineCompletionPos_; if (old != pos) { @@ -3081,7 +3081,7 @@ void BufferView::setInlineCompletion(Cursor const & cur, DocIterator const & pos } d->inlineCompletionPos_ = pos; } - + // set update flags if (changed) { if (singlePar && !(cur.result().screenUpdate() & Update::Force)) @@ -3093,8 +3093,8 @@ void BufferView::setInlineCompletion(Cursor const & cur, DocIterator const & pos bool BufferView::clickableInset() const -{ - return d->clickable_inset_; +{ + return d->clickable_inset_; } } // namespace lyx diff --git a/src/BufferView.h b/src/BufferView.h index a94ddfd56b..a144aee136 100644 --- a/src/BufferView.h +++ b/src/BufferView.h @@ -155,7 +155,7 @@ public: /// Ensure that the BufferView cursor is visible. /// This method will automatically scroll and update the BufferView /// if needed. - void showCursor(); + void showCursor(); /// Ensure the passed cursor \p dit is visible. /// This method will automatically scroll and update the BufferView /// if needed. diff --git a/src/RowPainter.cpp b/src/RowPainter.cpp index bf9f2805ba..4f7e667e88 100644 --- a/src/RowPainter.cpp +++ b/src/RowPainter.cpp @@ -239,7 +239,7 @@ void RowPainter::paintChars(pos_type & vpos, Font const & font) ++pos; ++start_pos; swap(start_pos, pos); - } + } // at least part of text selected? bool const some_sel = (pos >= row_.sel_beg && start_pos < row_.sel_end) diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index 540537b1a7..a5f03648de 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -613,7 +613,7 @@ void TextMetrics::computeRowMetrics(pit_type const pit, /** If we have separators, and this row has * not be broken abruptly by a display inset * or newline, then stretch it */ - if (ns && !row.right_boundary() + if (ns && !row.right_boundary() && row.endpos() != par.size()) { setSeparatorWidth(row, w / ns); row.dimension().wid = width; diff --git a/src/TextMetrics.h b/src/TextMetrics.h index e5b32a1904..92b1962545 100644 --- a/src/TextMetrics.h +++ b/src/TextMetrics.h @@ -225,7 +225,7 @@ public: int leftMargin(int max_width, pit_type pit) const; /// calculates the position of a completion popup - void completionPosAndDim(Cursor const & cur, int & x, int & y, + void completionPosAndDim(Cursor const & cur, int & x, int & y, Dimension & dim) const; private: @@ -252,7 +252,7 @@ public: /// our 'outermost' font. /// This is handed down from the surrounding /// inset through the pi/mi parameter (pi.base.font) - /// It is used in applyOuterFont() and setCharFont() for reasons + /// It is used in applyOuterFont() and setCharFont() for reasons /// that are not clear... to hand hand the outermost language and /// also for char style apparently. Font font_; From 5a361b35cf0c2a2f1ca9535b0748045063f20555 Mon Sep 17 00:00:00 2001 From: Hashini Senaratne Date: Sat, 26 Jul 2014 13:17:28 +0200 Subject: [PATCH 2/6] Keyboard based horizontal scrolling for wide insets [This commit is the output of the "horizontal scrolling" GSoC 2013 project, by Hashini Senaratne. The code has been cleaned up, some variables have been renamed and moved from the Cursor class to BufferView::Private. This is the base from which I (jmarc) will polish the feature for landing on master. Below is the original commit log of Hashini, updated to reflect the changes that have been done.] This feature also applicable for other insets; graphics and labels. This implementation is capable of scrolling a single row when reaching its content which is beyond the screen limits, using left and right arrow keys. The attribute 'horiz_scroll_offset_' introduced in the BufferView::Private class plays a main role in horizontal scrolling of the wide rows that grow beyond the screen limits. This attribute represents by how much pixels the current row that the text cursor lies in should be get scrolled. The main logic that is responsible for drawing the scrolled rows is within the BufferView class, BufferView::checkCursorScrollOffset. * The main logic is called via BufferView::draw. * What this does is set the horiz_scroll_offset_ attribute in in order to show the position that the text cursor lies in. * To make sure that BufferView::draw gets involved when Update flag is FitCursor, necessary changes are made in BufferView::processUpdateFlags. Basically what the logic that used to set the horiz_scroll_offset_ does is, * The row which the text cursor lies in is identified by a CursorSlice that points to the beginning of the row. This is the 'rowSlice' variable used in BufferView::checkCursorScrollOffset. Acessors are added to obtain this variable. Here row objects were not used to identify the current row, because it appears that row objects can disappear when doing a decoration update for example. This means that comparing row pointers is not a good idea, because they can change without notice. * Stop calculations of horiz_scroll_offset_ variable, if metrics have not been computed yet. Otherwise the calls to TextMetrics::parMetrics, calls redoParagraph and may change the row heigths. Therefore vertical scrolling feature may get disturbed. This is avoided. * Using BufferView::::setCurrentRowSlice resets horiz_scroll_offset_ when changing cursor row. This is done in order to prevent unwanted scrolling that happens when changing the selected row using up and down arrow keys. * Recompute inset positions before checking scoll offset of the row, by painting the row insets with drawing disabled. This is done because the position of insets is computed within the drawing procedure. * Current x position of the text cursor is compared with the horiz_scroll_offset_ value and the other variables like row.width(), bv.workWidth(). Compute the new horiz_scroll_offset_ value in order to show where the text cursor lies in. The basics conditions that we check before recomputing it are, if the text cursor lies rightward to the current right screen boundary, if the text cursor lies leftward to the current left screen boundary, if the text cursor lies within screen boundaries but the length of the row is less than the left boundary of the screen (this happens when we delete some content of the row using delete key or backspace key). * Change update strategy when scrooll offset has changed. This allows to redraw the row when no drawing was scheduled. By doing so, it was possible to redraw a wide row when moving to the leftmost position of the wide row, from the leftmost position of the row below, using the left arrow key. In TextMetrics::drawParagraph it is checked whether the current row is what is drawing now. If it is so, the value used to the x value of the row for drawing is adapted according to BufferView::horizScrollOffset. The method used to pass boundary() was fixed to get row when cursor was in a nested inset. This matter is considered in Cursor::textRow and it is modified accordingly. GuiWorkArea::Private::showCursor() is modified to show the cursor position in a scrolled row. --- src/BufferView.cpp | 119 +++++++++++++++++++++++++++++- src/BufferView.h | 20 +++++ src/CursorSlice.h | 2 + src/TextMetrics.cpp | 13 +++- src/frontends/qt4/GuiWorkArea.cpp | 6 ++ 5 files changed, 157 insertions(+), 3 deletions(-) diff --git a/src/BufferView.cpp b/src/BufferView.cpp index faa08f0320..4bd1c12e77 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -45,6 +45,7 @@ #include "Paragraph.h" #include "ParagraphParameters.h" #include "ParIterator.h" +#include "RowPainter.h" #include "Session.h" #include "Text.h" #include "TextClass.h" @@ -230,7 +231,8 @@ struct BufferView::Private inlineCompletionUniqueChars_(0), last_inset_(0), clickable_inset_(false), mouse_position_cache_(), - bookmark_edit_position_(-1), gui_(0) + bookmark_edit_position_(-1), gui_(0), + horiz_scroll_offset_(0) {} /// @@ -295,6 +297,16 @@ struct BufferView::Private /// map edited_insets_; + + /// When the row where the cursor lies is scrolled, this + /// contains the scroll offset + int horiz_scroll_offset_; + /// a slice pointing to the start of the row where the cursor + /// is (at last draw time) + CursorSlice current_row_slice_; + /// a slice pointing to the start of the row where cursor was + /// at previous draw event + CursorSlice last_row_slice_; }; @@ -456,8 +468,10 @@ void BufferView::processUpdateFlags(Update::flags flags) buffer_.changed(false); return; } - // no screen update is needed. + // no screen update is needed in principle, but this + // could change if cursor row needs scrolling. d->update_strategy_ = NoScreenUpdate; + buffer_.changed(false); return; } @@ -2853,6 +2867,103 @@ bool BufferView::cursorInView(Point const & p, int h) const } +int BufferView::horizScrollOffset() const +{ + return d->horiz_scroll_offset_; +} + + +CursorSlice const & BufferView::currentRowSlice() const +{ + return d->current_row_slice_; +} + + +CursorSlice const & BufferView::lastRowSlice() const +{ + return d->last_row_slice_; +} + + +void BufferView::setCurrentRowSlice(CursorSlice const & rowSlice) +{ + // nothing to do if the cursor was already on this row + if (d->current_row_slice_ == rowSlice) { + d->last_row_slice_ = CursorSlice(); + return; + } + + // if the (previous) current row was scrolled, we have to + // remember it in order to repaint it next time. + if (d->horiz_scroll_offset_ != 0) + d->last_row_slice_ = d->current_row_slice_; + else + d->last_row_slice_ = CursorSlice(); + + // Since we changed row, the scroll offset is not valid anymore + d->horiz_scroll_offset_ = 0; + d->current_row_slice_ = rowSlice; +} + + +void BufferView::checkCursorScrollOffset(PainterInfo & pi) +{ + CursorSlice rowSlice = d->cursor_.bottom(); + TextMetrics const & tm = textMetrics(rowSlice.text()); + + // Stop if metrics have not been computed yet, since it means + // that there is nothing to do. + if (!tm.contains(rowSlice.pit())) + return; + ParagraphMetrics const & pm = tm.parMetrics(rowSlice.pit()); + Row const & row = pm.getRow(rowSlice.pos(), + d->cursor_.boundary() + && rowSlice == d->cursor_.top()); + rowSlice.pos() = row.pos(); + + // Set the row on which the cursor lives. + setCurrentRowSlice(rowSlice); + + // Force the recomputation of inset positions + bool const drawing = pi.pain.isDrawingEnabled(); + pi.pain.setDrawingEnabled(false); + // No need to care about vertical position. + RowPainter rp(pi, buffer().text(), d->cursor_.bottom().pit(), row, 0, 0); + rp.paintText(); + pi.pain.setDrawingEnabled(drawing); + + // Current x position of the cursor in pixels + int const cur_x = getPos(d->cursor_).x_; + + // Horizontal scroll offset of the cursor row in pixels + int offset = d->horiz_scroll_offset_; + int const MARGIN = 10; + if (cur_x < offset + MARGIN) { + // scroll right + offset = cur_x - MARGIN; + } else if (cur_x > offset + workWidth() - MARGIN) { + // scroll left + offset = cur_x - workWidth() + MARGIN; + } else if(offset > 0 + && row.width() - offset < workWidth()){ + offset = row.width() - workWidth(); + } + + if (offset != d->horiz_scroll_offset_) + LYXERR0("Offset is now " << offset); + + if (d->update_strategy_ == NoScreenUpdate + && (offset != d->horiz_scroll_offset_ + || !d->last_row_slice_.empty())) { + // FIXME: if one uses SingleParUpdate, then home/end + // will not work on long rows. Why? + d->update_strategy_ = FullScreenUpdate;//DecorationUpdate; + } + + d->horiz_scroll_offset_ = offset; +} + + void BufferView::draw(frontend::Painter & pain) { if (height_ == 0 || width_ == 0) @@ -2864,6 +2975,10 @@ void BufferView::draw(frontend::Painter & pain) int const y = tm.first().second->position(); PainterInfo pi(this, pain); + // Check whether the row where the cursor lives needs to be scrolled. + // Update the drawing strategy if needed. + checkCursorScrollOffset(pi); + switch (d->update_strategy_) { case NoScreenUpdate: diff --git a/src/BufferView.h b/src/BufferView.h index a144aee136..6aa2f06777 100644 --- a/src/BufferView.h +++ b/src/BufferView.h @@ -32,6 +32,7 @@ class Buffer; class Change; class CoordCache; class Cursor; +class CursorSlice; class DispatchResult; class DocIterator; class DocumentClass; @@ -39,6 +40,7 @@ class FuncRequest; class FuncStatus; class Intl; class Inset; +class PainterInfo; class ParIterator; class ParagraphMetrics; class Point; @@ -118,6 +120,17 @@ public: /// move the screen to fit the cursor. /// Only to be called with good y coordinates (after a bv::metrics) bool fitCursor(); + + // Returns the amount of horizontal scrolling applied to the + // top-level row where the cursor lies + int horizScrollOffset() const; + + // Points to the top-level row where the cursor lies (during draw). + CursorSlice const & currentRowSlice() const; + + // Points to the top-level row where the cursor lied at last draw event. + CursorSlice const & lastRowSlice() const; + /// reset the scrollbar to reflect current view position. void updateScrollbar(); /// return the Scrollbar Parameters. @@ -331,6 +344,13 @@ private: /// \return true if no further update is needed. bool singleParUpdate(); + // Set the row on which the cursor lives. + void setCurrentRowSlice(CursorSlice const & rowSlice); + + // Check whether the row where the cursor lives needs to be scrolled. + // Update the drawing strategy if needed. + void checkCursorScrollOffset(PainterInfo & pi); + /// The minimal size of the document that is visible. Used /// when it is allowed to scroll below the document. int minVisiblePart(); diff --git a/src/CursorSlice.h b/src/CursorSlice.h index 01634bdacc..4da530d137 100644 --- a/src/CursorSlice.h +++ b/src/CursorSlice.h @@ -65,6 +65,8 @@ public: friend bool operator<=(CursorSlice const &, CursorSlice const &); //@} + /// return true if the slice has not been initialized + bool empty() const { return !inset_; } /// the current inset Inset & inset() const { return *inset_; } /// return the cell this cursor is in diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index a5f03648de..f325da7734 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -1850,10 +1850,20 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) co if (i) y += row.ascent(); + CursorSlice rowSlice(const_cast(text_->inset())); + rowSlice.pit() = pit; + rowSlice.pos() = row.pos(); + bool const inside = (y + row.descent() >= 0 && y - row.ascent() < ww); + + // Adapt to cursor row scroll offset if applicable. + if (bv_->currentRowSlice() == rowSlice) + x -= bv_->horizScrollOffset(); + // It is not needed to draw on screen if we are not inside. pi.pain.setDrawingEnabled(inside && original_drawing_state); + RowPainter rp(pi, *text_, pit, row, x, y); if (selection) @@ -1872,7 +1882,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) co // Row signature; has row changed since last paint? row.setCrc(pm.computeRowSignature(row, bparams)); - bool row_has_changed = row.changed(); + bool row_has_changed = row.changed() + || rowSlice == bv_->lastRowSlice(); // Take this opportunity to spellcheck the row contents. if (row_has_changed && lyxrc.spellcheck_continuously) { diff --git a/src/frontends/qt4/GuiWorkArea.cpp b/src/frontends/qt4/GuiWorkArea.cpp index 4e4d3d467e..b8a52f45ab 100644 --- a/src/frontends/qt4/GuiWorkArea.cpp +++ b/src/frontends/qt4/GuiWorkArea.cpp @@ -628,6 +628,12 @@ void GuiWorkArea::Private::showCursor() && !completer_->inlineVisible(); cursor_visible_ = true; cursor_->recomputeWidth(); + + //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(); + showCursor(p.x_, p.y_, h, l_shape, isrtl, completable); } From a7ba04dbaf77af889006201172dc22f02e7b2307 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Sat, 26 Jul 2014 16:25:56 +0200 Subject: [PATCH 3/6] Fine tune the scroll offset setting code The new code should feel a bit more natural. It avoids explicit pixel values for the margins and does not scroll in some cases where it is not necessary. --- src/BufferView.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 4bd1c12e77..e4e2a83b4f 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -35,6 +35,7 @@ #include "Language.h" #include "LaTeXFeatures.h" #include "LayoutFile.h" +#include "Length.h" #include "Lexer.h" #include "LyX.h" #include "LyXAction.h" @@ -2937,20 +2938,17 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi) // Horizontal scroll offset of the cursor row in pixels int offset = d->horiz_scroll_offset_; - int const MARGIN = 10; + int const MARGIN = Length(2, Length::EM).inPixels(workWidth()); if (cur_x < offset + MARGIN) { // scroll right offset = cur_x - MARGIN; } else if (cur_x > offset + workWidth() - MARGIN) { // scroll left offset = cur_x - workWidth() + MARGIN; - } else if(offset > 0 - && row.width() - offset < workWidth()){ - offset = row.width() - workWidth(); } - if (offset != d->horiz_scroll_offset_) - LYXERR0("Offset is now " << offset); + if (offset < 0 || row.width() <= workWidth()) + offset = 0; if (d->update_strategy_ == NoScreenUpdate && (offset != d->horiz_scroll_offset_ From fcaf5b5fc8a0949987d7e49949f2ce0a74792008 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Sat, 26 Jul 2014 22:25:48 +0200 Subject: [PATCH 4/6] Remove the special horizontal scrolling support in InsetTabular It is not necessary anymore now that there is a global mechanism. Also, fix the clearing of rows in SingleParUpdate mode. --- src/BufferView.cpp | 10 +++++++- src/TextMetrics.cpp | 5 +++- src/insets/InsetTabular.cpp | 49 ++++--------------------------------- src/insets/InsetTabular.h | 4 --- 4 files changed, 18 insertions(+), 50 deletions(-) diff --git a/src/BufferView.cpp b/src/BufferView.cpp index e4e2a83b4f..6c95d8df83 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -2955,7 +2955,7 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi) || !d->last_row_slice_.empty())) { // FIXME: if one uses SingleParUpdate, then home/end // will not work on long rows. Why? - d->update_strategy_ = FullScreenUpdate;//DecorationUpdate; + d->update_strategy_ = FullScreenUpdate; } d->horiz_scroll_offset_ = offset; @@ -2982,6 +2982,7 @@ void BufferView::draw(frontend::Painter & pain) case NoScreenUpdate: // If no screen painting is actually needed, only some 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); @@ -2989,6 +2990,7 @@ void BufferView::draw(frontend::Painter & pain) case SingleParUpdate: pi.full_repaint = false; + LYXERR(Debug::PAINTING, "Strategy: SingleParUpdate"); // In general, only the current row of the outermost paragraph // will be redrawn. Particular cases where selection spans // multiple paragraph are correctly detected in TextMetrics. @@ -3001,6 +3003,12 @@ void BufferView::draw(frontend::Painter & pain) // because of the single backing pixmap. case FullScreenUpdate: + + LYXERR(Debug::PAINTING, + ((d->update_strategy_ == FullScreenUpdate) + ? "Strategy: FullScreenUpdate" + : "Strategy: DecorationUpdate")); + // The whole screen, including insets, will be refreshed. pi.full_repaint = true; diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index f325da7734..e41874a98f 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -1903,7 +1903,10 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) co // Clear background of this row if paragraph background was not // already cleared because of a full repaint. if (!pi.full_repaint && row_has_changed) { - pi.pain.fillRectangle(x, y - row.ascent(), + LYXERR(Debug::PAINTING, "Clear rect@(" + << max(x, 0) << ", " << y-row.ascent() << ")=" + << width() << " x " << row.height()); + pi.pain.fillRectangle(max(x, 0), y - row.ascent(), width(), row.height(), pi.background_color); } diff --git a/src/insets/InsetTabular.cpp b/src/insets/InsetTabular.cpp index 8a8c2129f6..507f30233f 100644 --- a/src/insets/InsetTabular.cpp +++ b/src/insets/InsetTabular.cpp @@ -3456,14 +3456,14 @@ docstring InsetTableCell::xhtml(XHTMLStream & xs, OutputParams const & rp) const InsetTabular::InsetTabular(Buffer * buf, row_type rows, col_type columns) - : Inset(buf), tabular(buf, max(rows, row_type(1)), max(columns, col_type(1))), scx_(0), + : Inset(buf), tabular(buf, max(rows, row_type(1)), max(columns, col_type(1))), rowselect_(false), colselect_(false) { } InsetTabular::InsetTabular(InsetTabular const & tab) - : Inset(tab), tabular(tab.tabular), scx_(0) + : Inset(tab), tabular(tab.tabular) { } @@ -3714,11 +3714,10 @@ bool InsetTabular::isCellSelected(Cursor & cur, row_type row, col_type col) void InsetTabular::draw(PainterInfo & pi, int x, int y) const { - x += scx_ + ADD_TO_TABULAR_WIDTH; + x += ADD_TO_TABULAR_WIDTH; BufferView * bv = pi.base.bv; Cursor & cur = pi.base.bv->cursor(); - resetPos(cur); // FIXME: As the full background is painted in drawBackground(), // we have no choice but to do a full repaint for the Text cells. @@ -3766,7 +3765,7 @@ void InsetTabular::draw(PainterInfo & pi, int x, int y) const void InsetTabular::drawBackground(PainterInfo & pi, int x, int y) const { - x += scx_ + ADD_TO_TABULAR_WIDTH; + x += ADD_TO_TABULAR_WIDTH; y += offset_valign_ - tabular.rowAscent(0); pi.pain.fillRectangle(x, y, tabular.width(), tabular.height(), pi.backgroundColor(this)); @@ -3776,9 +3775,8 @@ void InsetTabular::drawBackground(PainterInfo & pi, int x, int y) const void InsetTabular::drawSelection(PainterInfo & pi, int x, int y) const { Cursor & cur = pi.base.bv->cursor(); - resetPos(cur); - x += scx_ + ADD_TO_TABULAR_WIDTH; + x += ADD_TO_TABULAR_WIDTH; if (!cur.selection()) return; @@ -3894,7 +3892,6 @@ void InsetTabular::edit(Cursor & cur, bool front, EntryDirection) } cur.setCurrentFont(); // FIXME: this accesses the position cache before it is initialized - //resetPos(cur); //cur.bv().fitCursor(); } @@ -5083,7 +5080,6 @@ void InsetTabular::cursorPos(BufferView const & bv, x += cellXPos(sl.idx()); x += tabular.textHOffset(sl.idx()); x += ADD_TO_TABULAR_WIDTH; - x += scx_; } @@ -5124,7 +5120,6 @@ Inset * InsetTabular::editXY(Cursor & cur, int x, int y) cur.setSelection(false); cur.push(*this); cur.idx() = getNearestCell(cur.bv(), x, y); - resetPos(cur); return cur.bv().textMetrics(&cell(cur.idx())->text()).editXY(cur, x, y); } @@ -5174,36 +5169,6 @@ int InsetTabular::cellXPos(idx_type const cell) const } -void InsetTabular::resetPos(Cursor & cur) const -{ - BufferView & bv = cur.bv(); - int const maxwidth = bv.workWidth(); - - int const scx_old = scx_; - int const i = cur.find(this); - if (i == -1) { - scx_ = 0; - } else { - int const X1 = 0; - int const X2 = maxwidth; - int const offset = ADD_TO_TABULAR_WIDTH + 2; - int const x1 = xo(cur.bv()) + cellXPos(cur[i].idx()) + offset; - int const x2 = x1 + tabular.cellWidth(cur[i].idx()); - - if (x1 < X1) - scx_ = X1 + 20 - x1; - else if (x2 > X2) - scx_ = X2 - 20 - x2; - else - scx_ = 0; - } - - // only update if offset changed - if (scx_ != scx_old) - cur.screenUpdateFlags(Update::Force | Update::FitCursor); -} - - void InsetTabular::moveNextCell(Cursor & cur, EntryDirection entry_from) { row_type const row = tabular.cellRow(cur.idx()); @@ -5238,7 +5203,6 @@ void InsetTabular::moveNextCell(Cursor & cur, EntryDirection entry_from) if (cur.selIsMultiCell()) { cur.pit() = cur.lastpit(); cur.pos() = cur.lastpos(); - resetPos(cur); return; } @@ -5261,7 +5225,6 @@ void InsetTabular::moveNextCell(Cursor & cur, EntryDirection entry_from) } cur.setCurrentFont(); - resetPos(cur); } @@ -5296,7 +5259,6 @@ void InsetTabular::movePrevCell(Cursor & cur, EntryDirection entry_from) if (cur.selIsMultiCell()) { cur.pit() = cur.lastpit(); cur.pos() = cur.lastpos(); - resetPos(cur); return; } @@ -5319,7 +5281,6 @@ void InsetTabular::movePrevCell(Cursor & cur, EntryDirection entry_from) } cur.setCurrentFont(); - resetPos(cur); } diff --git a/src/insets/InsetTabular.h b/src/insets/InsetTabular.h index 774949ca22..a055543eff 100644 --- a/src/insets/InsetTabular.h +++ b/src/insets/InsetTabular.h @@ -989,8 +989,6 @@ private: /// int cellYPos(idx_type cell) const; /// - void resetPos(Cursor & cur) const; - /// bool copySelection(Cursor & cur); /// bool pasteClipboard(Cursor & cur); @@ -1013,8 +1011,6 @@ private: col_type col_start, col_type col_end) const; /// mutable idx_type first_visible_cell; - /// - mutable int scx_; /// The vertical offset of the table due to the vertical /// alignment with respect to the baseline. mutable int offset_valign_; From c6e1db7682dc8d58a68147b5eee1d004829ef6d2 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Sun, 27 Jul 2014 17:30:57 +0200 Subject: [PATCH 5/6] Fix some display glitches * When doing a redraw with drawing disabled (to set inset positions properly), take horizontal scroll offset in account * reset horizontal scroll offset when it is smaller than the left margin. * when drawing a paragraph, do not modify x globally, only for the row that is offset. --- src/BufferView.cpp | 9 +++++++-- src/TextMetrics.cpp | 13 +++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 6c95d8df83..92ce6f634d 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -2929,7 +2929,8 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi) bool const drawing = pi.pain.isDrawingEnabled(); pi.pain.setDrawingEnabled(false); // No need to care about vertical position. - RowPainter rp(pi, buffer().text(), d->cursor_.bottom().pit(), row, 0, 0); + RowPainter rp(pi, buffer().text(), d->cursor_.bottom().pit(), row, + -d->horiz_scroll_offset_, 0); rp.paintText(); pi.pain.setDrawingEnabled(drawing); @@ -2947,9 +2948,13 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi) offset = cur_x - workWidth() + MARGIN; } - if (offset < 0 || row.width() <= workWidth()) + if (offset < row.x || row.width() <= workWidth()) offset = 0; + if (offset != d->horiz_scroll_offset_) + LYXERR(Debug::PAINTING, "Horiz. scroll offset changed from " + << d->horiz_scroll_offset_ << " to " << offset); + if (d->update_strategy_ == NoScreenUpdate && (offset != d->horiz_scroll_offset_ || !d->last_row_slice_.empty())) { diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index e41874a98f..ab857a5fdd 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -1807,7 +1807,7 @@ void TextMetrics::draw(PainterInfo & pi, int x, int y) const } -void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) const +void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const x, int y) const { BufferParams const & bparams = bv_->buffer().params(); ParagraphMetrics const & pm = par_metrics_[pit]; @@ -1847,6 +1847,7 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) co for (size_t i = 0; i != nrows; ++i) { Row const & row = pm.rows()[i]; + int row_x = x; if (i) y += row.ascent(); @@ -1859,12 +1860,12 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) co // Adapt to cursor row scroll offset if applicable. if (bv_->currentRowSlice() == rowSlice) - x -= bv_->horizScrollOffset(); + row_x -= bv_->horizScrollOffset(); // It is not needed to draw on screen if we are not inside. pi.pain.setDrawingEnabled(inside && original_drawing_state); - RowPainter rp(pi, *text_, pit, row, x, y); + RowPainter rp(pi, *text_, pit, row, row_x, y); if (selection) row.setSelectionAndMargins(sel_beg_par, sel_end_par); @@ -1903,10 +1904,10 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) co // Clear background of this row if paragraph background was not // already cleared because of a full repaint. if (!pi.full_repaint && row_has_changed) { - LYXERR(Debug::PAINTING, "Clear rect@(" - << max(x, 0) << ", " << y-row.ascent() << ")=" + LYXERR(Debug::PAINTING, "Clear rect@(" + << max(row_x, 0) << ", " << y-row.ascent() << ")=" << width() << " x " << row.height()); - pi.pain.fillRectangle(max(x, 0), y - row.ascent(), + pi.pain.fillRectangle(max(row_x, 0), y - row.ascent(), width(), row.height(), pi.background_color); } From 52236503d3f680a2df00a6e6539b530703066d32 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Sun, 27 Jul 2014 18:55:10 +0200 Subject: [PATCH 6/6] Add marks to indicate when a row is too large A dotted line is shown on the left or on the right to indicate that a part of the row is not visible. Add a color code for the scroll indicator --- src/Color.cpp | 1 + src/ColorCode.h | 2 ++ src/RowPainter.cpp | 17 +++++++++++++++++ src/RowPainter.h | 1 + src/TextMetrics.cpp | 2 ++ 5 files changed, 23 insertions(+) diff --git a/src/Color.cpp b/src/Color.cpp index 31d5f62697..161ebbad09 100644 --- a/src/Color.cpp +++ b/src/Color.cpp @@ -238,6 +238,7 @@ ColorSet::ColorSet() { Color_urllabel, N_("URL label"), "urllabel", "blue", "urllabel" }, { Color_urltext, N_("URL text"), "urltext", "blue", "urltext" }, { Color_depthbar, N_("depth bar"), "depthbar", "IndianRed", "depthbar" }, + { Color_scroll, N_("scroll indicator"), "scroll", "IndianRed", "scroll" }, { Color_language, N_("language"), "language", "Blue", "language" }, { Color_command, N_("command inset"), "command", "black", "command" }, { Color_commandbg, N_("command inset background"), "commandbg", "azure", "commandbg" }, diff --git a/src/ColorCode.h b/src/ColorCode.h index 2338995f7d..4d391c0081 100644 --- a/src/ColorCode.h +++ b/src/ColorCode.h @@ -91,6 +91,8 @@ enum ColorCode { /// Color for the depth bars in the margin Color_depthbar, + /// Color that indicates when a row can be scrolled + Color_scroll, /// Color for marking foreign language words Color_language, diff --git a/src/RowPainter.cpp b/src/RowPainter.cpp index 4f7e667e88..5e3444abb3 100644 --- a/src/RowPainter.cpp +++ b/src/RowPainter.cpp @@ -439,6 +439,23 @@ int RowPainter::paintAppendixStart(int y) } +void RowPainter::paintTooLargeMarks(bool const left, bool const right) +{ + if (left) + pi_.pain.line(dotted_line_thickness_, yo_ - row_.ascent(), + dotted_line_thickness_, yo_ + row_.descent(), + Color_scroll, + Painter::line_onoffdash, dotted_line_thickness_); + if (right) { + int const wwidth = pi_.base.bv->workWidth() - dotted_line_thickness_; + pi_.pain.line(wwidth, yo_ - row_.ascent(), + wwidth, yo_ + row_.descent(), + Color_scroll, + Painter::line_onoffdash, dotted_line_thickness_); + } +} + + void RowPainter::paintFirst() { BufferParams const & bparams = pi_.base.bv->buffer().params(); diff --git a/src/RowPainter.h b/src/RowPainter.h index aebd73e20e..0628f3288f 100644 --- a/src/RowPainter.h +++ b/src/RowPainter.h @@ -66,6 +66,7 @@ public: void paintAppendix(); void paintDepthBar(); void paintChangeBar(); + void paintTooLargeMarks(bool const left, bool const right); void paintFirst(); void paintLast(); void paintText(); diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index ab857a5fdd..9a6b67d28b 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -1942,6 +1942,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const rp.paintLast(); if (i == 0 && is_rtl) rp.paintFirst(); + rp.paintTooLargeMarks(row_x < 0, + row_x + row.width() > bv_->workWidth()); y += row.descent(); // Restore full_repaint status.