X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FTextMetrics.cpp;h=88ea65541f044b596ff33fc63aa44020f4b7fd42;hb=89d9334e03c311a4a7585f40ad81880304d174d4;hp=a29b4dca77edf74f8cd9917886cda721ca4a484b;hpb=c2732a30f9e09208cb1010ae42d8e56ee80f2a78;p=lyx.git diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index a29b4dca77..88ea65541f 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -49,6 +49,7 @@ #include "support/debug.h" #include "support/lassert.h" #include "support/lyxlib.h" +#include "support/RefChanger.h" #include #include @@ -62,10 +63,6 @@ using frontend::FontMetrics; namespace { -// the somewhat arbitrary leading added between rows. This is 20% of -// the characters height, inluding the possible leading of the font. -// 20% is a standard value used by LaTeX and word processors. -double const extra_leading = 0.2; int numberOfLabelHfills(Paragraph const & par, Row const & row) { @@ -188,6 +185,12 @@ ParagraphMetrics const & TextMetrics::parMetrics(pit_type pit) const } +ParagraphMetrics & TextMetrics::parMetrics(pit_type pit) +{ + return parMetrics(pit, true); +} + + void TextMetrics::newParMetricsDown() { pair const & last = *par_metrics_.rbegin(); @@ -238,7 +241,7 @@ bool TextMetrics::metrics(MetricsInfo & mi, Dimension & dim, int min_width, // << " maxWidth: " << max_width_ << "\nfont: " << mi.base.font << endl; bool changed = false; - unsigned int h = 0; + int h = 0; for (pit_type pit = 0; pit != npar; ++pit) { // create rows, but do not set alignment yet changed |= redoParagraph(pit, false); @@ -436,8 +439,9 @@ bool TextMetrics::redoParagraph(pit_type const pit, bool const align_rows) // FIXME: This check ought to be done somewhere else. It is the reason // why text_ is not const. But then, where else to do it? // Well, how can you end up with either (a) a biblio environment that - // has no InsetBibitem or (b) a biblio environment with more than one - // InsetBibitem? I think the answer is: when paragraphs are merged; + // has no InsetBibitem, (b) a biblio environment with more than one + // InsetBibitem or (c) a paragraph that has a bib item but is no biblio + // environment? I think the answer is: when paragraphs are merged; // when layout is set; when material is pasted. if (par.brokenBiblio()) { Cursor & cur = const_cast(bv_->cursor()); @@ -528,10 +532,11 @@ bool TextMetrics::redoParagraph(pit_type const pit, bool const align_rows) do { if (row_index == pm.rows().size()) pm.rows().push_back(Row()); + else + pm.rows()[row_index] = Row(); Row & row = pm.rows()[row_index]; row.pit(pit); row.pos(first); - row.pit(pit); need_new_row = breakRow(row, right_margin); setRowHeight(row); row.changed(true); @@ -558,28 +563,6 @@ bool TextMetrics::redoParagraph(pit_type const pit, bool const align_rows) if (row_index < pm.rows().size()) pm.rows().resize(row_index); - // FIXME: It might be better to move this in another method - // specially tailored for the main text. - // Top and bottom margin of the document (only at top-level) - if (text_->isMainText()) { - // original value was 20px, which is 0.2in at 100dpi - int const margin = bv_->zoomedPixels(20); - if (pit == 0) { - pm.rows().front().dim().asc += margin; - /* coverity thinks that we should update pm.dim().asc - * below, but all the rows heights are actually counted as - * part of the paragraph metric descent see loop above). - */ - // coverity[copy_paste_error] - pm.dim().des += margin; - } - ParagraphList const & pars = text_->paragraphs(); - if (pit + 1 == pit_type(pars.size())) { - pm.rows().back().dim().des += margin; - pm.dim().des += margin; - } - } - // The space above and below the paragraph. int const top = parTopSpacing(pit); pm.rows().front().dim().asc += top; @@ -590,6 +573,18 @@ bool TextMetrics::redoParagraph(pit_type const pit, bool const align_rows) pm.dim().asc += pm.rows()[0].ascent(); pm.dim().des -= pm.rows()[0].ascent(); + // Top and bottom margin of the document (only at top-level) + // FIXME: It might be better to move this in another method + // specially tailored for the main text. + if (text_->isMainText()) { + if (pit == 0) + pm.dim().asc += bv_->topMargin(); + ParagraphList const & pars = text_->paragraphs(); + if (pit + 1 == pit_type(pars.size())) { + pm.dim().des += bv_->bottomMargin(); + } + } + changed |= old_dim.height() != pm.dim().height(); return changed; @@ -598,7 +593,7 @@ bool TextMetrics::redoParagraph(pit_type const pit, bool const align_rows) LyXAlignment TextMetrics::getAlign(Paragraph const & par, Row const & row) const { - LyXAlignment align = par.getAlign(); + LyXAlignment align = par.getAlign(bv_->buffer().params()); // handle alignment inside tabular cells Inset const & owner = text_->inset(); @@ -623,19 +618,13 @@ 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())) { - switch (inset->display()) { - case Inset::AlignLeft: - align = LYX_ALIGN_BLOCK; - break; - case Inset::AlignCenter: - align = LYX_ALIGN_CENTER; - break; - case Inset::Inline: - // unchanged (use align) - break; - case Inset::AlignRight: - align = LYX_ALIGN_RIGHT; - break; + if (inset->rowFlags() & Inset::Display) { + if (inset->rowFlags() & Inset::AlignLeft) + align = LYX_ALIGN_BLOCK; + else if (inset->rowFlags() & Inset::AlignRight) + align = LYX_ALIGN_RIGHT; + else + align = LYX_ALIGN_CENTER; } } @@ -885,14 +874,14 @@ private: */ bool TextMetrics::breakRow(Row & row, int const right_margin) const { + LATTEST(row.empty()); Paragraph const & par = text_->getPar(row.pit()); pos_type const end = par.size(); pos_type const pos = row.pos(); pos_type const body_pos = par.beginOfBody(); - bool const is_rtl = text_->isRTL(par); + bool const is_rtl = text_->isRTL(row.pit()); bool need_new_row = false; - row.clear(); row.left_margin = leftMargin(row.pit(), pos); row.right_margin = right_margin; if (is_rtl) @@ -977,17 +966,22 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const } // Handle some situations that abruptly terminate the row - // - A newline inset - // - Before a display inset - // - After a display inset - Inset const * inset = 0; - if (par.isNewline(i) || par.isEnvSeparator(i) - || (i + 1 < end && (inset = par.getInset(i + 1)) - && inset->display()) - || (!row.empty() && row.back().inset - && row.back().inset->display())) { + // - Before an inset with BreakBefore + // - After an inset with BreakAfter + Inset const * prevInset = !row.empty() ? row.back().inset : 0; + Inset const * nextInset = (i + 1 < end) ? par.getInset(i + 1) : 0; + if ((nextInset && nextInset->rowFlags() & Inset::BreakBefore) + || (prevInset && prevInset->rowFlags() & Inset::BreakAfter)) { row.flushed(true); - need_new_row = par.isNewline(i); + // Force a row creation after this one if it is ended by + // an inset that either + // - has row flag RowAfter that enforces that; + // - or (1) did force the row breaking, (2) is at end of + // paragraph and (3) the said paragraph has an end label. + need_new_row = prevInset && + (prevInset->rowFlags() & Inset::RowAfter + || (prevInset->rowFlags() & Inset::BreakAfter && i + 1 == end + && text_->getEndLabel(row.pit()) != END_LABEL_NO_LABEL)); ++i; break; } @@ -1148,19 +1142,35 @@ void TextMetrics::setRowHeight(Row & row) const // Initial value for ascent (useful if row is empty). Font const font = displayFont(row.pit(), row.pos()); FontMetrics const & fm = theFontMetrics(font); - int maxasc = fm.maxAscent() + fm.leading(); - int maxdes = fm.maxDescent(); + int maxasc = int(fm.maxAscent() * spacing_val); + int maxdes = int(fm.maxDescent() * spacing_val); + + // Take label string into account (useful if labelfont is large) + if (row.pos() == 0 && layout.labelIsInline()) { + FontInfo const labelfont = text_->labelFont(par); + FontMetrics const & lfm = theFontMetrics(labelfont); + maxasc = max(maxasc, int(lfm.maxAscent() * spacing_val)); + maxdes = max(maxdes, int(lfm.maxDescent() * spacing_val)); + } // Find the ascent/descent of the row contents for (Row::Element const & e : row) { - maxasc = max(maxasc, e.dim.ascent()); - maxdes = max(maxdes, e.dim.descent()); + if (e.inset) { + maxasc = max(maxasc, e.dim.ascent()); + maxdes = max(maxdes, e.dim.descent()); + } else { + FontMetrics const & fm2 = theFontMetrics(e.font); + maxasc = max(maxasc, int(fm2.maxAscent() * spacing_val)); + maxdes = max(maxdes, int(fm2.maxDescent() * spacing_val)); + } } - // Add some leading (split between before and after) - int const leading = support::iround(extra_leading * (maxasc + maxdes)); - row.dim().asc = int((maxasc + leading - leading / 2) * spacing_val); - row.dim().des = int((maxdes + leading / 2) * spacing_val); + // This is nicer with box insets + ++maxasc; + ++maxdes; + + row.dim().asc = maxasc; + row.dim().des = maxdes; } @@ -1327,7 +1337,7 @@ Row const & TextMetrics::getPitAndRowNearY(int & y, pit_type & pit, { ParagraphMetrics const & pm = par_metrics_[pit]; - int yy = pm.position() - pm.ascent(); + int yy = pm.position() - pm.rows().front().ascent(); LBUFERR(!pm.rows().empty()); RowList::const_iterator rit = pm.rows().begin(); RowList::const_iterator rlast = pm.rows().end(); @@ -1744,7 +1754,7 @@ int TextMetrics::leftMargin(pit_type const pit, pos_type const pos) const if (!par.params().leftIndent().zero()) l_margin += par.params().leftIndent().inPixels(max_width_, lfm.em()); - LyXAlignment align = par.getAlign(); + LyXAlignment align = par.getAlign(bv_->buffer().params()); // set the correct parindent if (pos == 0 @@ -1758,10 +1768,10 @@ int TextMetrics::leftMargin(pit_type const pit, pos_type const pos) const && !par.params().noindent() // in some insets, paragraphs are never indented && !text_->inset().neverIndent() - // display style insets are always centered, omit indentation + // display style insets do not need indentation && !(!par.empty() && par.isInset(pos) - && par.getInset(pos)->display()) + && par.getInset(pos)->rowFlags() & Inset::Display) && (!(tclass.isDefaultLayout(par.layout()) || tclass.isPlainLayout(par.layout())) || buffer.params().paragraph_separation @@ -1807,6 +1817,9 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const if (pm.rows().empty()) return; size_t const nrows = pm.rows().size(); + // Remember left and right margin for drawing math numbers + Changer changeleft = make_change(pi.leftx, x + leftMargin(pit)); + Changer changeright = make_change(pi.rightx, x + width() - rightMargin(pit)); // Use fast lane in nodraw stage. if (pi.pain.isNull()) { @@ -1853,6 +1866,9 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const } } + if (text_->isRTL(pit)) + swap(pi.leftx, pi.rightx); + for (size_t i = 0; i != nrows; ++i) { Row const & row = pm.rows()[i]; @@ -1908,15 +1924,15 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const LYXERR(Debug::PAINTING, "Clear rect@(" << max(row_x, 0) << ", " << y - row.ascent() << ")=" << width() << " x " << row.height()); - // FIXME: this is a hack. We know that at least this - // amount of pixels can be cleared on right and left. - // Doing so gets rid of caret ghosts when the cursor is at - // the begining/end of row. However, it will not work if - // the caret has a ridiculous width like 6. (see ticket - // #10797) - pi.pain.fillRectangle(max(row_x, 0) - Inset::TEXT_TO_INSET_OFFSET, - y - row.ascent(), - width() + 2 * Inset::TEXT_TO_INSET_OFFSET, + // FIXME: this is a hack. We clear an amount equal to + // cursor width. This will not work if the caret has a + // ridiculous width like 6. (see ticket #10797) + // This is the same formula as in GuiWorkArea. + int const caret_width = lyxrc.cursor_width + ? lyxrc.cursor_width + : 1 + int((lyxrc.currentZoom + 50) / 200.0); + pi.pain.fillRectangle(max(row_x, 0), y - row.ascent(), + width() + caret_width, row.height(), pi.background_color); } @@ -1960,7 +1976,7 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const static int count = 0; ++count; FontInfo fi(sane_font); - fi.setSize(FONT_SIZE_TINY); + fi.setSize(TINY_SIZE); fi.setColor(Color_red); pi.pain.text(row_x, y, convert(count), fi); #endif @@ -2010,8 +2026,7 @@ void TextMetrics::completionPosAndDim(Cursor const & cur, int & x, int & y, int defaultRowHeight() { - FontMetrics const & fm = theFontMetrics(sane_font); - return support::iround(fm.maxHeight() * (1 + extra_leading) + fm.leading()); + return int(theFontMetrics(sane_font).maxHeight() * 1.2); } } // namespace lyx