mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-22 10:00:33 +00:00
Fix line breaking algorithm
Break words longer than the screen width. The code is more complicated than I would like, but I have no better idea right now. Implement properly the notion of a row broken by a display inset. This is useful in different places. Also fix a bug with last line of a paragraph spotted by Kornel.
This commit is contained in:
parent
ff608f46fd
commit
6258cebb77
@ -1,35 +1,41 @@
|
||||
This branch is where I (jmarc) try to implement string_wise metrics
|
||||
computation. This is done through a series of cleanups. The expected
|
||||
speed improvement will only be visible at the end of the road: indeed
|
||||
for now we intend to keep unchanged behavior for testing purposes.
|
||||
computation. This is done through a series of cleanups. The goal is to
|
||||
have both good metrics computation (and font with proper kerning and
|
||||
ligatures) and better performance than what we have with
|
||||
force_paint_single_char.
|
||||
|
||||
Currently everything is supposed to work for LTR text, and RTL text
|
||||
should work too except possibly metrics with Arabic and Hebrew fonts.
|
||||
We'll see what to do after some feedback.
|
||||
|
||||
What is done:
|
||||
|
||||
* Make TextMetrics methods operate on Row objects: breakRow and
|
||||
setRowHeight instead of rowBreakPoint and rowHeight.
|
||||
|
||||
* change breakRow operation to operate on text strings on which
|
||||
* Change breakRow operation to operate on text strings on which
|
||||
metrics are computed. The list of elements is stored in the row
|
||||
object in visual ordering, not logical.
|
||||
|
||||
* re-implement cursorX using row elements
|
||||
|
||||
* re-implement getColumnNearX using row elements.
|
||||
* Re-implement cursorX and getColumnNearX using row elements
|
||||
|
||||
* Implement proper string metrics computation (with cache), when
|
||||
lyxrc.force_paint_single_char is false. In this case, remove also
|
||||
useless workarounds which disable kerning and ligatures.
|
||||
|
||||
Next steps:
|
||||
|
||||
* get rid of old code of cursorX and getColumnNearX (which have been
|
||||
Next possible steps:
|
||||
|
||||
* Get rid of old code of cursorX and getColumnNearX (which have been
|
||||
kept for comparison purpose, guarded with KEEP_OLD_METRICS_CODE in
|
||||
order to check computations).
|
||||
|
||||
* re-implement row painting using row elements (can it be done?)
|
||||
* Re-implement row painting using row elements. This is not difficult
|
||||
in principle, but the code is intricate and needs some careful
|
||||
analysis.
|
||||
|
||||
* profile and see how performance can be improved.
|
||||
* Profile and see how performance can be improved.
|
||||
|
||||
* Document the code
|
||||
|
||||
Difference in behavior (aka bug fixes)
|
||||
|
||||
@ -39,7 +45,7 @@ Difference in behavior (aka bug fixes)
|
||||
* When cursor is after a LTR separator just before a RTL chunk, the
|
||||
cursor posiiton is computed better with the new code.
|
||||
|
||||
|
||||
Other differences (aka real bugs)
|
||||
* words longer than the screen are no monger broken at an arbitrary
|
||||
point. This is a problem for languages like chinese that do not use
|
||||
separators.
|
||||
|
||||
You tell me.
|
||||
|
60
src/Row.cpp
60
src/Row.cpp
@ -54,7 +54,7 @@ double Row::Element::pos2x(pos_type const i) const
|
||||
}
|
||||
|
||||
|
||||
pos_type Row::Element::x2pos(double &x, bool const low) const
|
||||
pos_type Row::Element::x2pos(double &x, bool const low) const
|
||||
{
|
||||
//lyxerr << "x2pos: x=" << x << " w=" << width() << " " << *this;
|
||||
// if element is rtl, flip x value
|
||||
@ -104,9 +104,9 @@ double Row::Element::pos2x(pos_type const i) const
|
||||
|
||||
Row::Row()
|
||||
: separator(0), label_hfill(0), x(0), right_margin(0),
|
||||
sel_beg(-1), sel_end(-1),
|
||||
begin_margin_sel(false), end_margin_sel(false),
|
||||
changed_(false), crc_(0), pos_(0), end_(0)
|
||||
sel_beg(-1), sel_end(-1),
|
||||
begin_margin_sel(false), end_margin_sel(false),
|
||||
changed_(false), crc_(0), pos_(0), end_(0), right_boundary_(false)
|
||||
{}
|
||||
|
||||
|
||||
@ -117,18 +117,6 @@ void Row::setCrc(size_type crc) const
|
||||
}
|
||||
|
||||
|
||||
void Row::pos(pos_type p)
|
||||
{
|
||||
pos_ = p;
|
||||
}
|
||||
|
||||
|
||||
void Row::endpos(pos_type p)
|
||||
{
|
||||
end_ = p;
|
||||
}
|
||||
|
||||
|
||||
bool Row::isMarginSelected(bool left_margin, DocIterator const & beg,
|
||||
DocIterator const & end) const
|
||||
{
|
||||
@ -337,6 +325,10 @@ void Row::shorten_if_needed(pos_type const keep, int const w)
|
||||
{
|
||||
if (empty() || width() < w)
|
||||
return;
|
||||
|
||||
/** First, we try to remove elements one by one from the end
|
||||
* until a separator is found.
|
||||
*/
|
||||
int i = elements_.size();
|
||||
int new_end = end_;
|
||||
int new_wid = dim_.wid;
|
||||
@ -352,23 +344,35 @@ void Row::shorten_if_needed(pos_type const keep, int const w)
|
||||
new_wid -= elements_[i].dim.wid;
|
||||
}
|
||||
if (i == 0) {
|
||||
if (elements_.size() != 1) {
|
||||
LYXERR0("Row is too large but has more than one element. " << *this);
|
||||
/* If we are here, it means that we have not found a
|
||||
* separator to shorten the row. There is one case
|
||||
* where we can do something: when we have one big
|
||||
* string, maybe with a paragraph marker after it.
|
||||
*/
|
||||
Element & front = elements_.front();
|
||||
if (!(front.type == STRING
|
||||
&& (elements_.size() == 1
|
||||
|| (elements_.size() == 2
|
||||
&& back().type == VIRTUAL))))
|
||||
return;
|
||||
}
|
||||
#if 1
|
||||
return;
|
||||
#else
|
||||
// does not work yet
|
||||
if (back().type != STRING)
|
||||
|
||||
// If this is a string element, we can try to split it.
|
||||
if (front.type != STRING)
|
||||
return;
|
||||
double xstr = w - x;
|
||||
pos_type new_pos = back().x2pos(xstr, true);
|
||||
back().str = back().str.substr(0, new_pos);
|
||||
back().endpos = new_pos;
|
||||
// If there is a paragraph marker, it should be taken in account
|
||||
if (elements_.size() == 2)
|
||||
xstr -= back().width();
|
||||
pos_type new_pos = front.x2pos(xstr, true);
|
||||
front.str = front.str.substr(0, new_pos - pos_);
|
||||
front.dim.wid = xstr;
|
||||
front.endpos = new_pos;
|
||||
end_ = new_pos;
|
||||
dim_.wid = x + xstr;
|
||||
#endif
|
||||
// If there is a paragraph marker, it should be removed.
|
||||
if (elements_.size() == 2)
|
||||
elements_.pop_back();
|
||||
return;
|
||||
}
|
||||
end_ = new_end;
|
||||
dim_.wid = new_wid;
|
||||
|
11
src/Row.h
11
src/Row.h
@ -128,13 +128,18 @@ public:
|
||||
DocIterator const & end) const;
|
||||
|
||||
///
|
||||
void pos(pos_type p);
|
||||
void pos(pos_type p) { pos_ = p; }
|
||||
///
|
||||
pos_type pos() const { return pos_; }
|
||||
///
|
||||
void endpos(pos_type p);
|
||||
void endpos(pos_type p) { end_ = p; }
|
||||
///
|
||||
pos_type endpos() const { return end_; }
|
||||
///
|
||||
void right_boundary(bool b) { right_boundary_ = b; }
|
||||
///
|
||||
bool right_boundary() const { return right_boundary_; }
|
||||
|
||||
///
|
||||
Dimension const & dimension() const { return dim_; }
|
||||
///
|
||||
@ -256,6 +261,8 @@ private:
|
||||
pos_type pos_;
|
||||
/// one behind last pos covered by this row
|
||||
pos_type end_;
|
||||
// Is there is a boundary at the end of the row (display inset...)
|
||||
bool right_boundary_;
|
||||
/// Row dimension.
|
||||
Dimension dim_;
|
||||
};
|
||||
|
@ -619,18 +619,11 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
|
||||
switch (align) {
|
||||
case LYX_ALIGN_BLOCK: {
|
||||
int const ns = numberOfSeparators(row);
|
||||
bool disp_inset = false;
|
||||
if (row.endpos() < par.size()) {
|
||||
Inset const * in = par.getInset(row.endpos());
|
||||
if (in)
|
||||
disp_inset = in->display();
|
||||
}
|
||||
// If we have separators, this is not the last row of a
|
||||
// par, does not end in newline, and is not row above a
|
||||
// display inset... then stretch it
|
||||
if (ns && row.endpos() < par.size()
|
||||
&& !par.isNewline(row.endpos() - 1)
|
||||
&& !disp_inset) {
|
||||
/** 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()
|
||||
&& row.endpos() != par.size()) {
|
||||
setSeparatorWidth(row, w / ns);
|
||||
row.dimension().wid = width;
|
||||
//lyxerr << "row.separator " << row.separator << endl;
|
||||
@ -803,6 +796,10 @@ private:
|
||||
|
||||
} // anon namespace
|
||||
|
||||
/** This is the function where the hard work is done. The code here is
|
||||
* very sensitive to small changes :) Note that part of the
|
||||
* intelligence is also in Row::shorten_if_needed
|
||||
*/
|
||||
void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit) const
|
||||
{
|
||||
Paragraph const & par = text_->getPar(pit);
|
||||
@ -812,6 +809,7 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
|
||||
pos_type const body_pos = par.beginOfBody();
|
||||
row.clear();
|
||||
row.dimension().wid = leftMargin(max_width_, pit, pos);
|
||||
row.x = row.width();
|
||||
row.right_margin = right_margin;
|
||||
|
||||
if (pos >= end || row.width() > width) {
|
||||
@ -893,6 +891,7 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
|
||||
&& inset->display())
|
||||
|| (!row.empty() && row.back().inset
|
||||
&& row.back().inset->display())) {
|
||||
row.right_boundary(true);
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
@ -1153,13 +1152,11 @@ pos_type TextMetrics::getColumnNearX(pit_type const pit,
|
||||
|
||||
/** This tests for the case where the cursor is set at the end
|
||||
* of a row which has been broken due to a display inset on
|
||||
* next row. This can be recognized because the end of the
|
||||
* last element is the same as the end of the row (there is no
|
||||
* separator at the end of the row)
|
||||
* next row. This is indicated by Row::right_boundary.
|
||||
*/
|
||||
if (!row.empty() && pos == row.back().endpos
|
||||
&& row.back().endpos == row.endpos())
|
||||
boundary = true;
|
||||
boundary = row.right_boundary();
|
||||
|
||||
#if !defined(KEEP_OLD_METRICS_CODE)
|
||||
return pos - row.pos();
|
||||
|
Loading…
Reference in New Issue
Block a user