This file tries to describe the state of the metrics/painting
mechanism, and identify the improvements that could be made. The first
-section can be read alone, although the context for them is really
+sections can be read alone, although the context for them is really
given in the following ones.
Please keep this file up to date as the code evolves!!!
Abbreviations:
bv: BufferView
pm: ParagraphMetrics
-tm::TextMetrics
+tm: TextMetrics
* Questions / Ideas
** SinglePar update
-The flag Update::SinglePar is set in many places but never acted on.
-Instead, metrics update is skipped whenever the recorded height of
-current paragraph did not change and Update::Force was not specified.
-Is it better to keep that (which incurs extra work) or to condition it
-on Update::SinglePar? If the first solution is kept, the flag
-SingleParUpdate shall be removed.
+This flag only has an effect in the current BufferView, but I think it
+is useful in other views too. Doing this will require some work on the
+update pipeline, though.
-Moreover, I fail to see (yet) where the 'single' part of the program
-is acted on.
** Buffer::change issues
On a related note, what is the semantics of a call to
Buffer::changed(false)? What does the caller mean?
+
** How to avoid redraw with FitCursor when the cursor is already OK?
In this case, we invoke Buffer::change(false) with drawing disabled
cursor.
-* Clean-up of drawing code
-
-The goal is to make painting with drawing disable fast enough that it
-can be used after every metrics computation. Then we can separate real
-drawing from metrics.
-
-Other changes are only clean-ups.
-
-** DONE RowPainter
+* TODO Clean-up of drawing code
-Inset position is set in paintInset, paintOnlyInsets, and paintText.
-This should be done only once in paintInset
+** Set Row::changed() in a finer way
-** DONE TextMetrics::drawParagraph
+*** singleParUpdate
-We can really simplify the code when drawing is disabled only
-paintInset needs to be called.
- + do right at the start when drawing is already disabled
- + do it in the loop for rows that are not visible on screen.
+When the height of the current paragraph changes, there is no need for
+a full screen update (at top level, at least). Only the rows after the
+current one need to have their position recomputed.
-The only thing we want to do here is to set inset positions (for
-text). The other insets still use the painter with drawing disabled.
-** DONE Painter::text
+*** redoParagraph
-We cannot remove (or make private) the version that uses a
-FontInfo because it is used by PainterInfo::draw. Document this and
-remove unused arguments rtl and double spacing. This would become a specialized helper.
-Proposed solution: keep the existing function, but private and without
-optional arguments.
+It should be possible to check whether the new row is the same as the
+old one and keep its changed() status in this case. This would reduce
+a lot the amount of stuff to redraw.
-Avoid to return (and thus compute) the width of strings?
- + used by InsetSpecialChar (fixable)
- + used by textDecoration() in text(): more difficult to fix
-Idea: add a version of text where wordspacing and textwidth (giving
-the width of strings) are required parameters and remove optional
-version.
+** Put labels and friends in the Row as elements
-==> more versions, no optional parameters.
+It should not be necessary to access the Paragraph object to draw.
+Adding the static elements to Row is a lot of work, but worth it IMO.
-** DONE When a document ends with a newline, add the bottom margin anyway
-
-The code that tests for a newline was added at 6bb98d07 in 2007.
** When a paragraph ends with a newline, compute correctly the height of the extra row.
-** Rewrite TextMetrics::completionPosAndDim using row information
-
-Currently it uses setRowHeight in a very weird way. In particular the
-topBottomSpace parameter should be removed after that.
-
-** Rewrite TextMetrics::editXY, checkInsetHit using row information (getPosNearX)?
-
- The helper version should return a Row::Element instead of an InsetTable.
-
-** Do not make RowPainter operations update x_
-
-It is better to make them const and update x_ separately.
-
-Then it will be possible to reorder the painting of the different
-elements. In particular, if text is painted last, it will be more
-visible in the presence of underlines (foreign language, change
-tracking, spell check).
-
-** 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?
-
-** Use Row for MathData
-
-It may not be so difficult. Implement x2pos and pos2x from
-the TM:cursorX and TM::getPosNearX, and use them for both text and
-math.
-
-Will the strings display OK if drawing string-wise?
-
-Then it would be possible to streamline drawing with disabled painter.
-
-** 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
+ transfer all the logic of bv::updateMetrics to tm.
+ Main InsetText should not be special.
-The difficuly for a tall table cell for example, is that it may be
+The difficulty for a tall table cell for example, is that it may be
necessary to break the whole contents to know the width of the cell.
+Also, the anchor is relative to the outer paragraph, which means that
+for a very long inset it is necessary to rebreak until the contents
+that needs to be shown (to compute the heights).
+
+All in all, this is difficult to get right. This is less important now
+that SinglePar updates work in nested insets.
* 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.
+ normal and math) in bv::coordCache and the vertical position of the
+ top-level paragraphs.
- + 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 positions of
+ visible insets.
+
+ + 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
Depending on the Update::flags passed to the method, it sets an update
strategy in (NoScreenUpdate, SingleParUpdate, FullScreenUpdate,
-DecorationUpdate). It triggers a recomputation of the metrics when either:
+DecorationUpdate). It triggers a call to updateMetrics when either:
+ Update::Force has been specified
+ Update::FitCursor has been specified and there is a need to scroll
the display.
- + the current paragraph, after rebreak, does not have the same height as in
- existing metrics. Note that the Update::SinglePar flag is *never*
- taken into account.
+ + Update::SinglePar has been specified and the current paragraph has
+ changed height.
+
+If a computation of metrics has taken place, Force is removed from the
+flags and ForceDraw is added instead.
+
+It is OK to call processUpdateFlags several times before an update. In
+this case, the effects are cumulative. processUpdateFlags executes the
+metrics-related actions, but defers the actual drawing to the next
+paint event.
The screen is drawn (with appropriate update strategy), except when
update flag is Update::None.
-** Metrics computation
+** Metrics computation (and nodraw drawing phase)
+
+This is done bv::updateMetrics. When the parameter 'force' of this
+method is true, that first thing that is done is to clear the metrics
+caches to start from a clean slate.
-This is triggered by bv::updateMetrics, which calls tm::redoParagraph for
-all visible paragraphs. Paragraphs above or below the screen (needed
-for page up/down) and computed as needed.
+Then, starting upwards then downwards from the anchor paragraph
+(anchor_pit_) and its vertical position (anchor_ypos_),
+tm::updateMetrics every visible paragraph whose metrics is not know
+(all of them when force==true) is recomputed using tm::redoParagraph.
tm::redoParagraph will call Inset::metrics for each inset. In the case
of text insets, this will invoke recursively tm::metrics, which redoes
-all the paragraphs of the inset.
+all the paragraphs of the inset. Then, a single big row is created in
+tm::tokenizeParagraph, which is later broken in multiple rows by
+tm::breakParagraph.
+If it turns out that the top or bottom margin are incorrect (paragraphs
+are too high/low), tm::updateMetrics will be called again with fixed
+values of anchor_ypos_ (this does not incur much extra work).
+
+At the end of bv::updateMetrics, bv::updatePosCache is called. It triggers
+a repaint of the document with a NullPainter (a painter that does
+nothing). This has the effect of caching all insets positions.
+
+This way of working means that scrolling can be achieved by just
+updating anchor_ypos_ and calling bv::processUpdateFlags(Update::ForceDraw).
** 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
+ 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().