[LyX/master] Insert a real empty row before display math at start of paragraph
Jean-Marc Lasgouttes
lasgouttes at lyx.org
Fri Nov 22 15:40:22 UTC 2024
commit 33442b17ee15da37c1087d6a4f1f9884ff8531f7
Author: Jean-Marc Lasgouttes <lasgouttes at lyx.org>
Date: Fri Nov 22 14:09:39 2024 +0100
Insert a real empty row before display math at start of paragraph
In LaTeX, when a displayed equation is at the start of a paragraph,
there is an empty row in front of it. Up to now, this was mimicked in
LyX by increasing the metrics on top of the inset. This commit creates
a real empty row, accessible by the cursor.
To make this work, many small unrelated changes are needed.
* Introduce new AlwaysBreakBefore inset row flag that means "I want a
break before myself, even if that means creating an empty row".
* Let InsetMathHull use that for display math.
* Remove the workaround that was added for InsetMathHull metrics. This
means that MetricsInfo::vmode is not used anymore. I decided to keep it,
since it may prove useful later.
* Handle the flag in TextMetrics::breakParagraph. This requires to add
a new flag 'ignore_contents' to TextMetrics::leftMargin, because we
want the empty row to have a normal left indentation, not the one of
display math (which is also at pos==0).
* In the initial empty row, do not inherit from the centered alignment
of the math inset, although both are at position 0.
* Concerning cursor positioning with mouse, two methods need fixing:
For the vertical part, handle in TextMetrics::getRowIndex the cursor
boundary at position 0 when it is set. Basically, with cursor
boundary true, the cursor will be in the empty row, whereas it will
be in font of the math inset otherwise.
For the horizontal part, handle empty row in TextMetrics::getPosNearX.
Fixes bugs 11593 and 11093.
---
src/MetricsInfo.h | 1 +
src/ParagraphMetrics.cpp | 4 ++++
src/RowFlags.h | 26 ++++++++++++++------------
src/TextMetrics.cpp | 25 ++++++++++++++++++-------
src/TextMetrics.h | 4 +++-
src/mathed/InsetMathHull.cpp | 7 ++-----
6 files changed, 42 insertions(+), 25 deletions(-)
diff --git a/src/MetricsInfo.h b/src/MetricsInfo.h
index 48d313ba71..3732e17481 100644
--- a/src/MetricsInfo.h
+++ b/src/MetricsInfo.h
@@ -105,6 +105,7 @@ public:
/// The context to resolve macros
MacroContext const & macrocontext;
/// Are we at the start of a paragraph (vertical mode)?
+ /// This is not used anymore, but could be useful
bool vmode;
/// if true, do not expand insets to max width artificially
bool tight_insets;
diff --git a/src/ParagraphMetrics.cpp b/src/ParagraphMetrics.cpp
index 470c765350..b6058a897e 100644
--- a/src/ParagraphMetrics.cpp
+++ b/src/ParagraphMetrics.cpp
@@ -95,6 +95,10 @@ size_t ParagraphMetrics::getRowIndex(pos_type pos, bool boundary) const
{
LBUFERR(!rows().empty());
+ // This makes a difference when the first row is empty (e.g. before display math)
+ if (pos == 0 && boundary)
+ return 0;
+
// If boundary is set we should return the row on which
// the character before is inside.
if (pos > 0 && boundary)
diff --git a/src/RowFlags.h b/src/RowFlags.h
index 953d7f92ee..ccc7f5b46c 100644
--- a/src/RowFlags.h
+++ b/src/RowFlags.h
@@ -27,30 +27,32 @@ enum RowFlags {
// Do not break before or after this element, except if really
// needed (between NoBreak* and CanBreak*).
Inline = 0,
+ // force (maybe empty) row before this element
+ AlwaysBreakBefore = 1 << 0,
// break row before this element if the row is not empty
- BreakBefore = 1 << 0,
+ BreakBefore = 1 << 1,
// break row whenever needed before this element
- CanBreakBefore = 1 << 1,
+ CanBreakBefore = 1 << 2,
// Avoid breaking row before this element
- NoBreakBefore = 1 << 2,
+ NoBreakBefore = 1 << 3,
// flush the row before this element (useful with BreakBefore)
- FlushBefore = 1 << 3,
+ FlushBefore = 1 << 4,
// force new (maybe empty) row after this element
- AlwaysBreakAfter = 1 << 4,
+ AlwaysBreakAfter = 1 << 5,
// break row after this element if there are more elements
- BreakAfter = 1 << 5,
+ BreakAfter = 1 << 6,
// break row whenever needed after this element
- CanBreakAfter = 1 << 6,
+ CanBreakAfter = 1 << 7,
// Avoid breaking row after this element
- NoBreakAfter = 1 << 7,
+ NoBreakAfter = 1 << 8,
// The contents of the row may be broken in two (e.g. string)
- CanBreakInside = 1 << 8,
+ CanBreakInside = 1 << 9,
// Flush the row that ends with this element
- Flush = 1 << 9,
+ Flush = 1 << 10,
// specify an alignment (left, right) for a display element
// (default is center)
- AlignLeft = 1 << 10,
- AlignRight = 1 << 11,
+ AlignLeft = 1 << 11,
+ AlignRight = 1 << 12,
// A display element breaks row at both ends
Display = FlushBefore | BreakBefore | BreakAfter,
// Flags that concern breaking after element
diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp
index c5f2bf498a..84e43c1d7d 100644
--- a/src/TextMetrics.cpp
+++ b/src/TextMetrics.cpp
@@ -706,7 +706,8 @@ LyXAlignment TextMetrics::getAlign(Paragraph const & par, Row const & row) const
// Display-style insets should always be on a centered row
if (Inset const * inset = par.getInset(row.pos())) {
- if (inset->rowFlags() & Display) {
+ // If we are in empty row, alignment of inset does not apply (it is in next row)
+ if (!row.empty() && inset->rowFlags() & Display) {
if (inset->rowFlags() & AlignLeft)
align = LYX_ALIGN_LEFT;
else if (inset->rowFlags() & AlignRight)
@@ -1155,7 +1156,7 @@ void cleanupRow(Row & row, bool at_end)
// Implement the priorities described in RowFlags.h.
bool needsRowBreak(int f1, int f2)
{
- if (f1 & AlwaysBreakAfter /*|| f2 & AlwaysBreakBefore*/)
+ if (f1 & AlwaysBreakAfter || f2 & AlwaysBreakBefore)
return true;
if (f1 & NoBreakAfter || f2 & NoBreakBefore)
return false;
@@ -1183,13 +1184,21 @@ RowList TextMetrics::breakParagraph(Row const & bigrow) const
bool const row_empty = rows.empty() || rows.back().empty();
// The row flags of previous element, if there is one.
// Otherwise we use NoBreakAfter to avoid an empty row before
- // e.g. a displayed equation.
+ // e.g. a displayed inset.
int const f1 = row_empty ? NoBreakAfter : rows.back().back().row_flags;
// The row flags of next element, if there is one.
// Otherwise we use NoBreakBefore (see above), unless the
// paragraph has an end label (for which an empty row is OK).
int const f2 = (fcit == end) ? (end_label ? Inline : NoBreakBefore)
: fcit->row_flags;
+ if (rows.empty() && needsRowBreak(f1, f2)) {
+ // Create an empty row before element
+ rows.push_back(newRow(*this, bigrow.pit(), 0, is_rtl));
+ Row & newrow = rows.back();
+ cleanupRow(newrow, false);
+ newrow.end_boundary(true);
+ newrow.left_margin = leftMargin(newrow.pit(), 0, true);
+ }
if (rows.empty() || needsRowBreak(f1, f2)) {
if (!rows.empty()) {
// Flush row as requested by row flags
@@ -1468,14 +1477,15 @@ pos_type TextMetrics::getPosNearX(Row const & row, int & x,
boundary = true;
}
+ if (row.empty())
+ boundary = row.end_boundary();
/** This tests for the case where the cursor is set at the end
* of a row which has been broken due something else than a
* separator (a display inset or a forced breaking of the
* row). We know that there is a separator when the end of the
* row is larger than the end of its last element.
*/
- if (!row.empty() && pos == row.back().endpos
- && row.back().endpos == row.endpos()) {
+ else if (pos == row.back().endpos && row.back().endpos == row.endpos()) {
Inset const * inset = row.back().inset;
if (inset && (inset->lyxCode() == NEWLINE_CODE
|| inset->lyxCode() == SEPARATOR_CODE))
@@ -1838,7 +1848,7 @@ int TextMetrics::leftMargin(pit_type pit) const
}
-int TextMetrics::leftMargin(pit_type const pit, pos_type const pos) const
+int TextMetrics::leftMargin(pit_type const pit, pos_type const pos, bool ignore_contents) const
{
ParagraphList const & pars = text_->paragraphs();
@@ -1998,7 +2008,8 @@ int TextMetrics::leftMargin(pit_type const pit, pos_type const pos) const
// in some insets, paragraphs are never indented
&& !text_->inset().neverIndent()
// display style insets do not need indentation
- && !(!par.empty()
+ && !(!ignore_contents
+ && !par.empty()
&& par.isInset(0)
&& par.getInset(0)->rowFlags() & Display)
&& (!(tclass.isDefaultLayout(par.layout())
diff --git a/src/TextMetrics.h b/src/TextMetrics.h
index d5606b610a..02b27cf067 100644
--- a/src/TextMetrics.h
+++ b/src/TextMetrics.h
@@ -138,8 +138,10 @@ public:
* This information cannot be taken from the layout object, because
* in LaTeX the beginning of the text fits in some cases
* (for example sections) exactly the label-width.
+ * When \c ignore_contents is true, alignment properties related
+ * to insets in paragraph are not taken into account.
*/
- int leftMargin(pit_type pit, pos_type pos) const;
+ int leftMargin(pit_type pit, pos_type pos, bool ignore_contents = false) const;
/// Return the left beginning of a row which is not the first one.
/// This is the left margin when there is no indentation.
int leftMargin(pit_type pit) const;
diff --git a/src/mathed/InsetMathHull.cpp b/src/mathed/InsetMathHull.cpp
index de20771e38..32a0d81bf0 100644
--- a/src/mathed/InsetMathHull.cpp
+++ b/src/mathed/InsetMathHull.cpp
@@ -525,9 +525,6 @@ void InsetMathHull::metrics(MetricsInfo & mi, Dimension & dim) const
*/
int const bottom_display_margin = mi.base.bv->zoomedPixels(6);
int top_display_margin = bottom_display_margin;
- // at start of paragraph, add an empty line
- if (mi.vmode)
- top_display_margin += theFontMetrics(mi.base.font).maxHeight() + 2;
int const ind = indent(*mi.base.bv);
mi.extrawidth = ind;
@@ -1029,9 +1026,9 @@ int InsetMathHull::rowFlags() const
case hullMultline:
case hullGather:
if (buffer().params().is_math_indent)
- return Display | AlignLeft;
+ return AlwaysBreakBefore | Display | AlignLeft;
else
- return Display;
+ return AlwaysBreakBefore | Display;
}
// avoid warning
return Display;
More information about the lyx-cvs
mailing list