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).
This commit is contained in:
Jean-Marc Lasgouttes 2016-10-05 00:25:38 +02:00
parent 0b5c2a8507
commit f3f9b083d1
11 changed files with 137 additions and 71 deletions

View File

@ -32,6 +32,8 @@ public:
void operator+=(Dimension const & dim); void operator+=(Dimension const & dim);
/// set to empty box /// set to empty box
void clear() { wid = asc = des = 0; } void clear() { wid = asc = des = 0; }
/// check if box is empty
bool empty() const { return wid == 0 && asc == 0 && wid == 0; }
/// get height /// get height
int height() const { return asc + des; } int height() const { return asc + des; }
/// get ascent /// get ascent

View File

@ -88,7 +88,7 @@ Changer MetricsBase::changeFontSet(string const & name, bool cond)
MetricsInfo::MetricsInfo(BufferView * bv, FontInfo font, int textwidth, MetricsInfo::MetricsInfo(BufferView * bv, FontInfo font, int textwidth,
MacroContext const & mc) MacroContext const & mc)
: base(bv, font, textwidth), macrocontext(mc) : base(bv, font, textwidth), macro_nesting(0), macrocontext(mc)
{} {}

View File

@ -101,6 +101,8 @@ public:
/// ///
MetricsBase base; MetricsBase base;
/// count wether the current mathdata is nested in macro(s)
int macro_nesting;
/// The context to resolve macros /// The context to resolve macros
MacroContext const & macrocontext; MacroContext const & macrocontext;
}; };

View File

@ -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.inset = this;
e.mclass = mathClass(); e.mclass = mathClass();
mrow.push_back(e); mrow.push_back(e);

View File

@ -167,7 +167,7 @@ public:
/// The class of the math object (used primarily for spacing) /// The class of the math object (used primarily for spacing)
virtual MathClass mathClass() const; virtual MathClass mathClass() const;
/// Add this inset to a math row. Return true if contents got added /// 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 /// identifies things that can get scripts
virtual bool isScriptable() const { return false; } virtual bool isScriptable() const { return false; }

View File

@ -31,7 +31,6 @@
#include "mathed/InsetMathUnknown.h" #include "mathed/InsetMathUnknown.h"
#include "frontends/FontMetrics.h" #include "frontends/FontMetrics.h"
#include "frontends/Painter.h"
#include "support/debug.h" #include "support/debug.h"
#include "support/docstream.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; bool has_contents = false;
BufferView * bv = mi.base.bv; BufferView * bv = mi.base.bv;
@ -275,12 +274,6 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) const
slevel_ = (4 * xascent) / 5; slevel_ = (4 * xascent) / 5;
sshift_ = xascent / 4; sshift_ = xascent / 4;
if (empty()) {
// Cache the dimension.
mi.base.bv->coordCache().arrays().add(this, dim);
return;
}
MathRow mrow(mi, this); MathRow mrow(mi, this);
mrow_cache_[mi.base.bv] = mrow; mrow_cache_[mi.base.bv] = mrow;
mrow.metrics(mi, dim); 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); 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 // don't draw outside the workarea
if (y + dim.descent() <= 0 if (y + dim.descent() <= 0
|| y - dim.ascent() >= bv.workHeight() || y - dim.ascent() >= bv.workHeight()

View File

@ -122,7 +122,7 @@ public:
MathAtom const & operator[](pos_type) const; MathAtom const & operator[](pos_type) const;
/// Add this array to a math row. Return true if contents got added /// 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 /// rebuild cached metrics information
void metrics(MetricsInfo & mi, Dimension & dim) const; void metrics(MetricsInfo & mi, Dimension & dim) const;

View File

@ -68,28 +68,49 @@ public:
/// ///
InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; } 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); // macro arguments are in macros
e.macro = mathMacro_; LATTEST(mi.macro_nesting > 0);
e.ar = &mathMacro_->cell(idx_); if (mi.macro_nesting == 1)
mrow.push_back(e); 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(); 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(); mathMacro_->macro()->lock();
e.type = MathRow::END_ARG; // if there was no contents, and the contents is editable,
mrow.push_back(e); // 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) if (mi.macro_nesting == 0)
return true; mi.macro_nesting = 1;
// if there was no contents, then we insert the empty macro inset
// instead. MathRow::Element e_end(MathRow::END_ARG, mi);
return InsetMath::addToMathRow(mrow, 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 { 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_->macro()->unlock();
mathMacro_->cell(idx_).metrics(mi, dim); mathMacro_->cell(idx_).metrics(mi, dim);
@ -98,6 +119,8 @@ public:
def_.metrics(mi, dim); def_.metrics(mi, dim);
mathMacro_->macro()->lock(); mathMacro_->macro()->lock();
if (mi.macro_nesting == 0)
mi.macro_nesting = 1;
} }
// write(), normalize(), infoize() and infoize2() are not needed since // write(), normalize(), infoize() and infoize2() are not needed since
// MathMacro uses the definition and not the expanded cells. // 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. // set edit mode for which we will have calculated row.
// This is the same as what is done in metrics(). // This is the same as what is done in metrics().
d->editing_[mi.base.bv] = editMode(mi.base.bv); d->editing_[mi.base.bv] = editMode(mi.base.bv);
if (displayMode() == MathMacro::DISPLAY_NORMAL if (displayMode() != MathMacro::DISPLAY_NORMAL
&& !d->editing_[mi.base.bv]) { || d->editing_[mi.base.bv])
MathRow::Element e(MathRow::BEG_MACRO); return InsetMath::addToMathRow(mrow, mi);
e.macro = this;
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); mrow.push_back(e);
has_contents = true;
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.
} }
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 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 // set edit mode for which we will have calculated metrics. But only
d->editing_[mi.base.bv] = editMode(mi.base.bv); d->editing_[mi.base.bv] = editMode(mi.base.bv);
@ -495,6 +532,9 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
dim.des += 2; dim.des += 2;
} }
} }
// restore macro nesting
--mi.macro_nesting;
} }

View File

@ -39,7 +39,7 @@ public:
/// ///
/// If the macro is in normal edit mode, dissolve its contents in /// If the macro is in normal edit mode, dissolve its contents in
/// the row. Otherwise, just insert the inset. /// 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; void draw(PainterInfo & pi, int x, int y) const;
/// draw selection background /// draw selection background

View File

@ -22,6 +22,7 @@
#include "CoordCache.h" #include "CoordCache.h"
#include "MetricsInfo.h" #include "MetricsInfo.h"
#include "frontends/FontMetrics.h"
#include "frontends/Painter.h" #include "frontends/Painter.h"
#include "support/debug.h" #include "support/debug.h"
@ -35,26 +36,33 @@ using namespace std;
namespace lyx { namespace lyx {
MathRow::Element::Element(Type t, MathClass const mc) MathRow::Element::Element(Type t, MetricsInfo &mi)
: type(t), : type(t), macro_nesting(mi.macro_nesting),
inset(0), mclass(mc), before(0), after(0), compl_unique_to(0), inset(0), mclass(MC_ORD), before(0), after(0), compl_unique_to(0),
macro(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" // 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 // 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" // 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, /* Do spacing only in math mode. This test is a bit clumsy,
* but it is used in other places for guessing the current mode. * 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<MathMacro const *, Dimension> dim_macros; map<MathMacro const *, Dimension> dim_macros;
map<MathData const *, Dimension> dim_arrays; map<MathData const *, Dimension> dim_arrays;
CoordCache & coords = mi.base.bv->coordCache(); CoordCache & coords = mi.base.bv->coordCache();
for (Element const & e : elements_) { for (Element const & e : elements_) {
Dimension d; Dimension d;
mi.macro_nesting = e.macro_nesting;
switch (e.type) { switch (e.type) {
case BEGIN: case BEGIN:
case END: case END:
@ -131,12 +139,6 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) const
e.inset->metrics(mi, d); e.inset->metrics(mi, d);
d.wid += e.before + e.after; d.wid += e.before + e.after;
coords.insets().add(e.inset, d); 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; break;
case BEG_MACRO: case BEG_MACRO:
e.macro->macro()->lock(); 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]); coords.arrays().add(e.ar, dim_arrays[e.ar]);
dim_arrays.erase(e.ar); dim_arrays.erase(e.ar);
break; 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()) 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(); CoordCache & coords = pi.base.bv->coordCache();
for (Element const & e : elements_) { for (Element const & e : elements_) {
Dimension d;
switch (e.type) { switch (e.type) {
case INSET: { case INSET: {
// This is hackish: the math inset does not know that space // 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)) if (e.macro->editMetrics(pi.base.bv))
pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend); pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
break; 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 BEGIN:
case END: case END:
case END_MACRO: case END_MACRO:
@ -277,6 +300,9 @@ ostream & operator<<(ostream & os, MathRow::Element const & e)
case MathRow::END_ARG: case MathRow::END_ARG:
os << ")"; os << ")";
break; break;
case MathRow::BOX:
os << "@";
break;
} }
return os; return os;
} }

View File

@ -14,6 +14,8 @@
#include "MathClass.h" #include "MathClass.h"
#include "ColorCode.h"
#include "support/docstring.h" #include "support/docstring.h"
#include <vector> #include <vector>
@ -51,16 +53,19 @@ public:
END_ARG, // a macro argument ends here END_ARG, // a macro argument ends here
BEGIN, // dummy element before row BEGIN, // dummy element before row
END, // dummy element after row END, // dummy element after row
BOX // an empty box
}; };
// An elements, together with its spacing // An elements, together with its spacing
struct Element struct Element
{ {
/// ///
Element(Type t = INSET, MathClass const mc = MC_ORD); Element(Type t, MetricsInfo & mi);
/// Classifies the contents of the object /// Classifies the contents of the object
Type type; Type type;
/// count wether the current mathdata is nested in macro(s)
int macro_nesting;
/// When type is INSET /// When type is INSET
/// the math inset /// the math inset
@ -80,6 +85,9 @@ public:
// type is BEG_ARG, END_ARG // type is BEG_ARG, END_ARG
MathData const * ar; 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 // create the math row by unwinding all macros in the MathData and
// compute the spacings. // compute the spacings.
MathRow(MetricsInfo const & mi, MathData const * ar); MathRow(MetricsInfo & mi, MathData const * ar);
// //
void metrics(MetricsInfo & mi, Dimension & dim) const; void metrics(MetricsInfo & mi, Dimension & dim) const;