Improve cursor movement with boundaries

Introduce a new NoEndBoundary flag for insets like InsetNewline.

Indroduce Row::start_boundary() that is true when previous Row has
end_boundary() set.

Use this to improve cursor movement around row boundaries (both for
logical ad visible cursor movement). The new code remove some of the
newline/separator hardcoding.
This commit is contained in:
Jean-Marc Lasgouttes 2024-11-22 16:30:48 +01:00
parent 33442b17ee
commit ecac032a94
7 changed files with 33 additions and 46 deletions

View File

@ -1288,7 +1288,9 @@ bool Cursor::posVisToNewRow(bool movingLeft)
// if moving left in an LTR paragraph or moving right in an
// RTL one, move to previous row
if (par_is_LTR == movingLeft) {
if (row.pos() == 0) { // we're at first row in paragraph
if (row.start_boundary())
boundary(true);
else if (row.pos() == 0) { // we're at first row in paragraph
if (pit() == 0) // no previous paragraph! don't move
return false;
// move to last pos in previous par

View File

@ -212,6 +212,10 @@ public:
///
pos_type endpos() const { return end_; }
///
void start_boundary(bool b) { start_boundary_ = b; }
///
bool start_boundary() const { return start_boundary_; }
///
void end_boundary(bool b) { end_boundary_ = b; }
///
bool end_boundary() const { return end_boundary_; }
@ -373,6 +377,8 @@ private:
pos_type pos_ = 0;
/// one behind last pos covered by this row
pos_type end_ = 0;
// Is there a boundary at the start of the row (display inset...)
bool start_boundary_ = false;
// Is there a boundary at the end of the row (display inset...)
bool end_boundary_ = false;
// Shall the row be flushed when it is supposed to be justified?

View File

@ -53,6 +53,8 @@ enum RowFlags {
// (default is center)
AlignLeft = 1 << 11,
AlignRight = 1 << 12,
// Forbid boundary after this element
NoEndBoundary = 1 << 13,
// A display element breaks row at both ends
Display = FlushBefore | BreakBefore | BreakAfter,
// Flags that concern breaking after element

View File

@ -3054,29 +3054,16 @@ bool Text::cursorBackward(Cursor & cur)
// Tell BufferView to test for FitCursor in any case!
cur.screenUpdateFlags(Update::FitCursor);
// if on right side of a row boundary (at row start), skip it,
// i.e. set boundary to true, i.e. go only logically left
if (!cur.boundary()
&& cur.textRow().pos() == cur.pos()
&& cur.textRow().start_boundary()) {
return setCursor(cur, cur.pit(), cur.pos(), true, true);
}
// not at paragraph start?
if (cur.pos() > 0) {
// if on right side of boundary (i.e. not at paragraph end, but line end)
// -> skip it, i.e. set boundary to true, i.e. go only logically left
// there are some exceptions to ignore this: lineseps, newlines, spaces
#if 0
// some effectless debug code to see the values in the debugger
bool bound = cur.boundary();
int rowpos = cur.textRow().pos();
int pos = cur.pos();
bool sep = cur.paragraph().isSeparator(cur.pos() - 1);
bool newline = cur.paragraph().isNewline(cur.pos() - 1);
bool linesep = cur.paragraph().isLineSeparator(cur.pos() - 1);
#endif
if (!cur.boundary() &&
cur.textRow().pos() == cur.pos() &&
!cur.paragraph().isLineSeparator(cur.pos() - 1) &&
!cur.paragraph().isNewline(cur.pos() - 1) &&
!cur.paragraph().isEnvSeparator(cur.pos() - 1) &&
!cur.paragraph().isSeparator(cur.pos() - 1)) {
return setCursor(cur, cur.pit(), cur.pos(), true, true);
}
// go left and try to enter inset
if (checkAndActivateInset(cur, false))
return false;
@ -3143,33 +3130,14 @@ bool Text::cursorForward(Cursor & cur)
// next position is left of boundary,
// but go to next line for special cases like space, newline, linesep
#if 0
// some effectless debug code to see the values in the debugger
int endpos = cur.textRow().endpos();
int lastpos = cur.lastpos();
int pos = cur.pos();
bool linesep = cur.paragraph().isLineSeparator(cur.pos());
bool newline = cur.paragraph().isNewline(cur.pos());
bool sep = cur.paragraph().isSeparator(cur.pos());
if (cur.pos() != cur.lastpos()) {
bool linesep2 = cur.paragraph().isLineSeparator(cur.pos()+1);
bool newline2 = cur.paragraph().isNewline(cur.pos()+1);
bool sep2 = cur.paragraph().isSeparator(cur.pos()+1);
}
#endif
if (cur.textRow().endpos() == cur.pos() + 1) {
if (cur.paragraph().isEnvSeparator(cur.pos()) &&
cur.pos() + 1 == cur.lastpos() &&
cur.pit() != cur.lastpit()) {
// move to next paragraph
return setCursor(cur, cur.pit() + 1, 0, true, false);
} else if (cur.textRow().endpos() != cur.lastpos() &&
!cur.paragraph().isNewline(cur.pos()) &&
!cur.paragraph().isEnvSeparator(cur.pos()) &&
!cur.paragraph().isLineSeparator(cur.pos()) &&
!cur.paragraph().isSeparator(cur.pos())) {
} else if (cur.textRow().end_boundary())
return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
}
}
// in front of RTL boundary? Stay on this side of the boundary because:

View File

@ -1147,7 +1147,9 @@ void cleanupRow(Row & row, bool at_end)
if (!at_end && !row.flushed())
row.back().rtrim();
// boundary exists when there was no space at the end of row
row.end_boundary(!at_end && row.back().endpos == row.endpos());
row.end_boundary(!at_end
&& row.back().endpos == row.endpos()
&& !(row.back().row_flags & NoEndBoundary));
// make sure that the RTL elements are in reverse ordering
row.reverseRTL();
}
@ -1250,6 +1252,13 @@ RowList TextMetrics::breakParagraph(Row const & bigrow) const
rows.back().needsChangeBar(true);
}
// Set start_boundary to be equal to the previous row's end boundary
bool sb = false;
for (auto & row : rows) {
row.start_boundary(sb);
sb = row.end_boundary();
}
return rows;
}

View File

@ -44,9 +44,9 @@ InsetNewline::InsetNewline() : Inset(nullptr)
int InsetNewline::rowFlags() const
{
if (params_.kind == InsetNewlineParams::LINEBREAK)
return AlwaysBreakAfter;
return AlwaysBreakAfter | NoEndBoundary;
else
return AlwaysBreakAfter | Flush;
return AlwaysBreakAfter | NoEndBoundary | Flush;
}

View File

@ -65,7 +65,7 @@ public:
return docstring();
}
///
int rowFlags() const override { return BreakAfter | Flush; }
int rowFlags() const override { return BreakAfter | Flush | NoEndBoundary; }
///
bool nextnoindent() const { return params_.kind == InsetSeparatorParams::PLAIN; }
private: