From f3f9b083d180412a62a50bdef06ab236dca5fc9d Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Wed, 5 Oct 2016 00:25:38 +0200 Subject: [PATCH] Only display a blue rectangle for editable empty insets Empty insets should use a minimal amount of space, especially when they are part of a built-in macro in lib/symbols. With this change, blue rectangles signal actually editable places. Empty macros in editable data are shown as grey boxes, but they do not appear when further nested. This is done by adding a new type BOX of MathRow::Element object and a MetricsInfo::macro_nesting that keeps track of macros (and is reset to 0 in editable macro arguments). --- src/Dimension.h | 2 + src/MetricsInfo.cpp | 2 +- src/MetricsInfo.h | 2 + src/mathed/InsetMath.cpp | 4 +- src/mathed/InsetMath.h | 2 +- src/mathed/MathData.cpp | 14 +----- src/mathed/MathData.h | 2 +- src/mathed/MathMacro.cpp | 102 +++++++++++++++++++++++++++------------ src/mathed/MathMacro.h | 2 +- src/mathed/MathRow.cpp | 64 ++++++++++++++++-------- src/mathed/MathRow.h | 12 ++++- 11 files changed, 137 insertions(+), 71 deletions(-) diff --git a/src/Dimension.h b/src/Dimension.h index bd8f10d8f2..0607be6eba 100644 --- a/src/Dimension.h +++ b/src/Dimension.h @@ -32,6 +32,8 @@ public: void operator+=(Dimension const & dim); /// set to empty box void clear() { wid = asc = des = 0; } + /// check if box is empty + bool empty() const { return wid == 0 && asc == 0 && wid == 0; } /// get height int height() const { return asc + des; } /// get ascent diff --git a/src/MetricsInfo.cpp b/src/MetricsInfo.cpp index ea3bf0247e..02bbab3fd0 100644 --- a/src/MetricsInfo.cpp +++ b/src/MetricsInfo.cpp @@ -88,7 +88,7 @@ Changer MetricsBase::changeFontSet(string const & name, bool cond) MetricsInfo::MetricsInfo(BufferView * bv, FontInfo font, int textwidth, MacroContext const & mc) - : base(bv, font, textwidth), macrocontext(mc) + : base(bv, font, textwidth), macro_nesting(0), macrocontext(mc) {} diff --git a/src/MetricsInfo.h b/src/MetricsInfo.h index d2dd8b7bcf..415fe25076 100644 --- a/src/MetricsInfo.h +++ b/src/MetricsInfo.h @@ -101,6 +101,8 @@ public: /// MetricsBase base; + /// count wether the current mathdata is nested in macro(s) + int macro_nesting; /// The context to resolve macros MacroContext const & macrocontext; }; diff --git a/src/mathed/InsetMath.cpp b/src/mathed/InsetMath.cpp index a8642c14ac..d2182a5652 100644 --- a/src/mathed/InsetMath.cpp +++ b/src/mathed/InsetMath.cpp @@ -56,9 +56,9 @@ MathClass InsetMath::mathClass() const } -bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo const &) const +bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo & mi) const { - MathRow::Element e; + MathRow::Element e(MathRow::INSET, mi); e.inset = this; e.mclass = mathClass(); mrow.push_back(e); diff --git a/src/mathed/InsetMath.h b/src/mathed/InsetMath.h index 39490106b0..f8e70c7293 100644 --- a/src/mathed/InsetMath.h +++ b/src/mathed/InsetMath.h @@ -167,7 +167,7 @@ public: /// The class of the math object (used primarily for spacing) virtual MathClass mathClass() const; /// Add this inset to a math row. Return true if contents got added - virtual bool addToMathRow(MathRow &, MetricsInfo const & mi) const; + virtual bool addToMathRow(MathRow &, MetricsInfo & mi) const; /// identifies things that can get scripts virtual bool isScriptable() const { return false; } diff --git a/src/mathed/MathData.cpp b/src/mathed/MathData.cpp index 8b7a89dfc6..f2100afb34 100644 --- a/src/mathed/MathData.cpp +++ b/src/mathed/MathData.cpp @@ -31,7 +31,6 @@ #include "mathed/InsetMathUnknown.h" #include "frontends/FontMetrics.h" -#include "frontends/Painter.h" #include "support/debug.h" #include "support/docstream.h" @@ -217,7 +216,7 @@ void MathData::touch() const } -bool MathData::addToMathRow(MathRow & mrow, MetricsInfo const & mi) const +bool MathData::addToMathRow(MathRow & mrow, MetricsInfo & mi) const { bool has_contents = false; BufferView * bv = mi.base.bv; @@ -275,12 +274,6 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) const slevel_ = (4 * xascent) / 5; sshift_ = xascent / 4; - if (empty()) { - // Cache the dimension. - mi.base.bv->coordCache().arrays().add(this, dim); - return; - } - MathRow mrow(mi, this); mrow_cache_[mi.base.bv] = mrow; mrow.metrics(mi, dim); @@ -299,11 +292,6 @@ void MathData::draw(PainterInfo & pi, int const x, int const y) const Dimension const & dim = bv.coordCache().getArrays().dim(this); - if (empty()) { - pi.pain.rectangle(x, y - dim.ascent(), dim.width(), dim.height(), Color_mathline); - return; - } - // don't draw outside the workarea if (y + dim.descent() <= 0 || y - dim.ascent() >= bv.workHeight() diff --git a/src/mathed/MathData.h b/src/mathed/MathData.h index fa82ee95e6..9eae46671a 100644 --- a/src/mathed/MathData.h +++ b/src/mathed/MathData.h @@ -122,7 +122,7 @@ public: MathAtom const & operator[](pos_type) const; /// Add this array to a math row. Return true if contents got added - bool addToMathRow(MathRow &, MetricsInfo const & mi) const; + bool addToMathRow(MathRow &, MetricsInfo & mi) const; /// rebuild cached metrics information void metrics(MetricsInfo & mi, Dimension & dim) const; diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp index c4401d2b69..a748821113 100644 --- a/src/mathed/MathMacro.cpp +++ b/src/mathed/MathMacro.cpp @@ -68,28 +68,49 @@ public: /// InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; } /// - bool addToMathRow(MathRow & mrow, MetricsInfo const & mi) const + bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const { - MathRow::Element e(MathRow::BEG_ARG); - e.macro = mathMacro_; - e.ar = &mathMacro_->cell(idx_); - mrow.push_back(e); + // macro arguments are in macros + LATTEST(mi.macro_nesting > 0); + if (mi.macro_nesting == 1) + mi.macro_nesting = 0; + + MathRow::Element e_beg(MathRow::BEG_ARG, mi); + e_beg.macro = mathMacro_; + e_beg.ar = &mathMacro_->cell(idx_); + mrow.push_back(e_beg); mathMacro_->macro()->unlock(); - bool const has_contents = mathMacro_->cell(idx_).addToMathRow(mrow, mi); + bool has_contents = mathMacro_->cell(idx_).addToMathRow(mrow, mi); mathMacro_->macro()->lock(); - e.type = MathRow::END_ARG; - mrow.push_back(e); + // if there was no contents, and the contents is editable, + // then we insert a box instead. + if (!has_contents && mi.macro_nesting == 0) { + MathRow::Element e(MathRow::BOX, mi); + e.color = Color_mathline; + mrow.push_back(e); + has_contents = true; + } - if (has_contents) - return true; - // if there was no contents, then we insert the empty macro inset - // instead. - return InsetMath::addToMathRow(mrow, mi); + if (mi.macro_nesting == 0) + mi.macro_nesting = 1; + + MathRow::Element e_end(MathRow::END_ARG, mi); + e_end.macro = mathMacro_; + e_end.ar = &mathMacro_->cell(idx_); + + mrow.push_back(e_end); + + return has_contents; } /// void metrics(MetricsInfo & mi, Dimension & dim) const { + // macro arguments are in macros + LATTEST(mi.macro_nesting > 0); + if (mi.macro_nesting == 1) + mi.macro_nesting = 0; + mathMacro_->macro()->unlock(); mathMacro_->cell(idx_).metrics(mi, dim); @@ -98,6 +119,8 @@ public: def_.metrics(mi, dim); mathMacro_->macro()->lock(); + if (mi.macro_nesting == 0) + mi.macro_nesting = 1; } // write(), normalize(), infoize() and infoize2() are not needed since // MathMacro uses the definition and not the expanded cells. @@ -287,31 +310,42 @@ MathMacro::~MathMacro() } -bool MathMacro::addToMathRow(MathRow & mrow, MetricsInfo const & mi) const +bool MathMacro::addToMathRow(MathRow & mrow, MetricsInfo & mi) const { // set edit mode for which we will have calculated row. // This is the same as what is done in metrics(). d->editing_[mi.base.bv] = editMode(mi.base.bv); - if (displayMode() == MathMacro::DISPLAY_NORMAL - && !d->editing_[mi.base.bv]) { - MathRow::Element e(MathRow::BEG_MACRO); - e.macro = this; + if (displayMode() != MathMacro::DISPLAY_NORMAL + || d->editing_[mi.base.bv]) + return InsetMath::addToMathRow(mrow, mi); + + MathRow::Element e_beg(MathRow::BEG_MACRO, mi); + e_beg.macro = this; + mrow.push_back(e_beg); + + ++mi.macro_nesting; + + d->macro_->lock(); + bool has_contents = d->expanded_.addToMathRow(mrow, mi); + d->macro_->unlock(); + + // if there was no contents and the array is editable, then we + // insert a grey box instead. + if (!has_contents && mi.macro_nesting == 1) { + MathRow::Element e(MathRow::BOX, mi); + e.color = Color_mathmacroblend; mrow.push_back(e); - - d->macro_->lock(); - bool const has_contents = d->expanded_.addToMathRow(mrow, mi); - d->macro_->unlock(); - - e.type = MathRow::END_MACRO; - mrow.push_back(e); - - if (has_contents) - return true; - // if there was no contents, then we insert the empty macro inset - // instead. + has_contents = true; } - return InsetMath::addToMathRow(mrow, mi); + + --mi.macro_nesting; + + MathRow::Element e_end(MathRow::END_MACRO, mi); + e_end.macro = this; + mrow.push_back(e_end); + + return has_contents; } @@ -407,6 +441,9 @@ bool MathMacro::editMetrics(BufferView const * bv) const void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const { + // the macro contents is not editable (except the arguments) + ++mi.macro_nesting; + // set edit mode for which we will have calculated metrics. But only d->editing_[mi.base.bv] = editMode(mi.base.bv); @@ -495,6 +532,9 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const dim.des += 2; } } + + // restore macro nesting + --mi.macro_nesting; } diff --git a/src/mathed/MathMacro.h b/src/mathed/MathMacro.h index 9c6fdb1ce9..4eb93ba534 100644 --- a/src/mathed/MathMacro.h +++ b/src/mathed/MathMacro.h @@ -39,7 +39,7 @@ public: /// /// If the macro is in normal edit mode, dissolve its contents in /// the row. Otherwise, just insert the inset. - bool addToMathRow(MathRow &, MetricsInfo const & mi) const; + bool addToMathRow(MathRow &, MetricsInfo & mi) const; /// void draw(PainterInfo & pi, int x, int y) const; /// draw selection background diff --git a/src/mathed/MathRow.cpp b/src/mathed/MathRow.cpp index 55513bb923..8b9378bbc6 100644 --- a/src/mathed/MathRow.cpp +++ b/src/mathed/MathRow.cpp @@ -22,6 +22,7 @@ #include "CoordCache.h" #include "MetricsInfo.h" +#include "frontends/FontMetrics.h" #include "frontends/Painter.h" #include "support/debug.h" @@ -35,26 +36,33 @@ using namespace std; namespace lyx { -MathRow::Element::Element(Type t, MathClass const mc) - : type(t), - inset(0), mclass(mc), before(0), after(0), compl_unique_to(0), - macro(0) +MathRow::Element::Element(Type t, MetricsInfo &mi) + : type(t), macro_nesting(mi.macro_nesting), + inset(0), mclass(MC_ORD), before(0), after(0), compl_unique_to(0), + macro(0), color(Color_red) {} -MathRow::MathRow(MetricsInfo const & mi, MathData const * ar) +MathRow::MathRow(MetricsInfo & mi, MathData const * ar) { - if (ar->empty()) - return; - // First there is a dummy element of type "open" - push_back(Element(BEGIN, MC_OPEN)); + push_back(Element(BEGIN, mi)); + back().mclass = MC_OPEN; // Then insert the MathData argument - ar->addToMathRow(*this, mi); + bool const has_contents = ar->addToMathRow(*this, mi); + + // empty arrays are visible when they are editable + // we reserve the necessary space anyway (even if nothing gets drawn) + if (!has_contents) { + Element e(BOX, mi); + e.color = Color_mathline; + push_back(e); + } // Finally there is a dummy element of type "close" - push_back(Element(END, MC_CLOSE)); + push_back(Element(END, mi)); + back().mclass = MC_CLOSE; /* Do spacing only in math mode. This test is a bit clumsy, * but it is used in other places for guessing the current mode. @@ -120,9 +128,9 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) const map dim_macros; map dim_arrays; CoordCache & coords = mi.base.bv->coordCache(); - for (Element const & e : elements_) { Dimension d; + mi.macro_nesting = e.macro_nesting; switch (e.type) { case BEGIN: case END: @@ -131,12 +139,6 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) const e.inset->metrics(mi, d); d.wid += e.before + e.after; coords.insets().add(e.inset, d); - dim += d; - // Now add the dimension to current macros and arguments. - for (auto & dim_macro : dim_macros) - dim_macro.second += d; - for (auto & dim_array : dim_arrays) - dim_array.second += d; break; case BEG_MACRO: e.macro->macro()->lock(); @@ -164,6 +166,19 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) const coords.arrays().add(e.ar, dim_arrays[e.ar]); dim_arrays.erase(e.ar); break; + case BOX: + d = theFontMetrics(mi.base.font).dimension('I'); + d.wid += e.before + e.after; + break; + } + + if (!d.empty()) { + dim += d; + // Now add the dimension to current macros and arguments. + for (auto & dim_macro : dim_macros) + dim_macro.second += d; + for (auto & dim_array : dim_arrays) + dim_array.second += d; } if (e.compl_text.empty()) @@ -180,7 +195,6 @@ void MathRow::draw(PainterInfo & pi, int x, int const y) const { CoordCache & coords = pi.base.bv->coordCache(); for (Element const & e : elements_) { - Dimension d; switch (e.type) { case INSET: { // This is hackish: the math inset does not know that space @@ -211,6 +225,15 @@ void MathRow::draw(PainterInfo & pi, int x, int const y) const if (e.macro->editMetrics(pi.base.bv)) pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend); break; + case BOX: { + Dimension const d = theFontMetrics(pi.base.font).dimension('I'); + // the box is not visible in non-editable context (except for grey macro boxes). + if (e.macro_nesting == 0 || e.color == Color_mathmacroblend) + pi.pain.rectangle(x + e.before, y - d.ascent(), + d.width(), d.height(), e.color); + x += d.wid; + break; + } case BEGIN: case END: case END_MACRO: @@ -277,6 +300,9 @@ ostream & operator<<(ostream & os, MathRow::Element const & e) case MathRow::END_ARG: os << ")"; break; + case MathRow::BOX: + os << "@"; + break; } return os; } diff --git a/src/mathed/MathRow.h b/src/mathed/MathRow.h index d0260e2e10..3d31e1cbcb 100644 --- a/src/mathed/MathRow.h +++ b/src/mathed/MathRow.h @@ -14,6 +14,8 @@ #include "MathClass.h" +#include "ColorCode.h" + #include "support/docstring.h" #include @@ -51,16 +53,19 @@ public: END_ARG, // a macro argument ends here BEGIN, // dummy element before row END, // dummy element after row + BOX // an empty box }; // An elements, together with its spacing struct Element { /// - Element(Type t = INSET, MathClass const mc = MC_ORD); + Element(Type t, MetricsInfo & mi); /// Classifies the contents of the object Type type; + /// count wether the current mathdata is nested in macro(s) + int macro_nesting; /// When type is INSET /// the math inset @@ -80,6 +85,9 @@ public: // type is BEG_ARG, END_ARG MathData const * ar; + + // type is BOX + ColorCode color; }; /// @@ -105,7 +113,7 @@ public: // create the math row by unwinding all macros in the MathData and // compute the spacings. - MathRow(MetricsInfo const & mi, MathData const * ar); + MathRow(MetricsInfo & mi, MathData const * ar); // void metrics(MetricsInfo & mi, Dimension & dim) const;