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);
/// 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

View File

@ -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)
{}

View File

@ -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;
};

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

View File

@ -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; }

View File

@ -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()

View File

@ -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;

View File

@ -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;
// 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;
mrow.push_back(e);
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 const has_contents = d->expanded_.addToMathRow(mrow, mi);
bool has_contents = d->expanded_.addToMathRow(mrow, mi);
d->macro_->unlock();
e.type = MathRow::END_MACRO;
// 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);
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;
}

View File

@ -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

View File

@ -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<MathMacro const *, Dimension> dim_macros;
map<MathData const *, Dimension> 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;
}

View File

@ -14,6 +14,8 @@
#include "MathClass.h"
#include "ColorCode.h"
#include "support/docstring.h"
#include <vector>
@ -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;