#include "MathRow.h"
-#include "InsetMath.h"
-#include "MathClass.h"
#include "MathData.h"
#include "MathSupport.h"
#include "BufferView.h"
+#include "ColorSet.h"
#include "CoordCache.h"
#include "MetricsInfo.h"
+#include "mathed/InsetMath.h"
+
#include "frontends/FontMetrics.h"
#include "frontends/Painter.h"
#include "support/docstring.h"
#include "support/lassert.h"
+#include <algorithm>
#include <ostream>
using namespace std;
MathRow::Element::Element(MetricsInfo const & mi, Type t, MathClass mc)
: type(t), mclass(mc), before(0), after(0), macro_nesting(mi.base.macro_nesting),
- marker(InsetMath::NO_MARKER), inset(0), compl_unique_to(0), ar(0),
+ marker(marker_type::NO_MARKER), inset(nullptr), compl_unique_to(0), ar(nullptr),
color(Color_red)
{}
+namespace {
+
+// Helper functions for markers
+
+int markerMargin(MathRow::Element const & e)
+{
+ switch(e.marker) {
+ case marker_type::MARKER:
+ case marker_type::MARKER2:
+ case marker_type::BOX_MARKER:
+ return 2;
+ case marker_type::NO_MARKER:
+ return 0;
+ }
+ // should not happen
+ return 0;
+}
+
+
+void afterMetricsMarkers(MetricsInfo const & , MathRow::Element & e,
+ Dimension & dim)
+{
+ // handle vertical space for markers
+ switch(e.marker) {
+ case marker_type::NO_MARKER:
+ break;
+ case marker_type::MARKER:
+ ++dim.des;
+ break;
+ case marker_type::MARKER2:
+ ++dim.asc;
+ ++dim.des;
+ break;
+ case marker_type::BOX_MARKER:
+ FontInfo font;
+ font.setSize(TINY_SIZE);
+ Dimension namedim;
+ mathed_string_dim(font, e.inset->name(), namedim);
+ int const namewid = 1 + namedim.wid + 1;
+
+ if (namewid > dim.wid)
+ e.after += namewid - dim.wid;
+ ++dim.asc;
+ dim.des += 3 + namedim.height();
+ }
+}
+
+
+void drawMarkers(PainterInfo const & pi, MathRow::Element const & e,
+ int const x, int const y)
+{
+ if (e.marker == marker_type::NO_MARKER)
+ return;
+
+ CoordCache const & coords = pi.base.bv->coordCache();
+ Dimension const dim = coords.getInsets().dim(e.inset);
+
+ // the marker is before/after the inset. Necessary space has been reserved already.
+ int const l = x + e.before - (markerMargin(e) > 0 ? 1 : 0);
+ int const r = x + dim.width() - e.after;
+
+ // Grey lower box
+ if (e.marker == marker_type::BOX_MARKER) {
+ // draw header and rectangle around
+ FontInfo font;
+ font.setSize(TINY_SIZE);
+ font.setColor(Color_mathmacrolabel);
+ Dimension namedim;
+ mathed_string_dim(font, e.inset->name(), namedim);
+ pi.pain.fillRectangle(l, y + dim.des - namedim.height() - 2,
+ dim.wid, namedim.height() + 2, Color_mathmacrobg);
+ pi.pain.text(l, y + dim.des - namedim.des - 1, e.inset->name(), font);
+ }
+
+ // Color for corners
+ bool const highlight = e.inset->mouseHovered(pi.base.bv)
+ || e.inset->editing(pi.base.bv);
+ ColorCode const pen_color = highlight ? Color_mathframe : Color_mathcorners;
+ // If the corners have the same color as the background, do not paint them.
+ if (lcolor.getX11HexName(Color_mathbg) == lcolor.getX11HexName(pen_color))
+ return;
+
+ // Lower corners in all cases
+ int const d = y + dim.descent();
+ pi.pain.line(l, d - 3, l, d, pen_color);
+ pi.pain.line(r, d - 3, r, d, pen_color);
+ pi.pain.line(l, d, l + 3, d, pen_color);
+ pi.pain.line(r - 3, d, r, d, pen_color);
+
+ // Upper corners
+ if (e.marker == marker_type::BOX_MARKER
+ || e.marker == marker_type::MARKER2) {
+ int const a = y - dim.ascent();
+ pi.pain.line(l, a + 3, l, a, pen_color);
+ pi.pain.line(r, a + 3, r, a, pen_color);
+ pi.pain.line(l, a, l + 3, a, pen_color);
+ pi.pain.line(r - 3, a, r, a, pen_color);
+ }
+}
+
+} // namespace
+
+
MathRow::MathRow(MetricsInfo & mi, MathData const * ar)
{
// First there is a dummy element of type "open"
for (int i = 1 ; i != static_cast<int>(elements_.size()) ; ++i) {
Element & e = elements_[i];
- if (e.mclass == MC_UNKNOWN)
- continue;
-
Element & bef = elements_[before(i)];
- if (dospacing) {
+ if (dospacing && e.mclass != MC_UNKNOWN) {
int spc = class_spacing(bef.mclass, e.mclass, mi.base);
bef.after += spc / 2;
// this is better than spc / 2 to avoid rounding problems
}
// finally reserve space for markers
- if (bef.marker != Inset::NO_MARKER)
- bef.after = max(bef.after, 1);
- if (e.marker != Inset::NO_MARKER)
- e.before = max(e.before, 1);
+ bef.after = max(bef.after, markerMargin(bef));
+ if (e.mclass != MC_UNKNOWN)
+ e.before = max(e.before, markerMargin(e));
+ // for linearized insets (macros...) too
+ if (e.type == BEGIN)
+ bef.after = max(bef.after, markerMargin(e));
+ if (e.type == END && e.marker != marker_type::NO_MARKER) {
+ Element & aft = elements_[after(i)];
+ aft.before = max(aft.before, markerMargin(e));
+ }
}
// Do not lose spacing allocated to extremities
}
-void MathRow::metrics(MetricsInfo & mi, Dimension & dim) const
+void MathRow::metrics(MetricsInfo & mi, Dimension & dim)
{
- dim.asc = 0;
dim.wid = 0;
// In order to compute the dimension of macros and their
// arguments, it is necessary to keep track of them.
vector<pair<InsetMath const *, Dimension>> dim_insets;
vector<pair<MathData const *, Dimension>> dim_arrays;
CoordCache & coords = mi.base.bv->coordCache();
- for (Element const & e : elements_) {
+ for (Element & e : elements_) {
mi.base.macro_nesting = e.macro_nesting;
Dimension d;
switch (e.type) {
case BEGIN:
if (e.inset) {
dim_insets.push_back(make_pair(e.inset, Dimension()));
+ dim_insets.back().second.wid += e.before + e.after;
+ d.wid = e.before + e.after;
e.inset->beforeMetrics();
}
if (e.ar)
if (e.inset) {
e.inset->afterMetrics();
LATTEST(dim_insets.back().first == e.inset);
- coords.insets().add(e.inset, dim_insets.back().second);
+ d = dim_insets.back().second;
+ afterMetricsMarkers(mi, e, d);
+ d.wid += e.before + e.after;
+ coords.insets().add(e.inset, d);
dim_insets.pop_back();
+ // We do not want to count the width again, but the
+ // padding and the vertical dimension are meaningful.
+ d.wid = e.before + e.after;
}
if (e.ar) {
LATTEST(dim_arrays.back().first == e.ar);
// allow for one pixel before/after the box.
d.wid += e.before + e.after + 2;
} else {
- // hide the box, but give it some height
+ // hide the box, but keep its height
d.wid = 0;
}
break;
}
- // handle vertical space for markers
- switch(e.marker) {
- case InsetMath::NO_MARKER:
- break;
- case InsetMath::MARKER:
- ++d.des;
- break;
- case InsetMath::MARKER2:
- ++d.asc;
- ++d.des;
- }
-
if (!d.empty()) {
dim += d;
// Now add the dimension to current macros and arguments.
}
-namespace {
-
-void drawMarkers(PainterInfo const & pi, MathRow::Element const & e, int const x, int const y)
-{
- if (e.marker == InsetMath::NO_MARKER)
- return;
-
- CoordCache const & coords = pi.base.bv->coordCache();
- Dimension const dim = coords.getInsets().dim(e.inset);
-
- // the marker is before/after the inset. Normally some space has been reserved already.
- int const l = x + e.before - 1;
- int const r = x + dim.width() - e.after;
-
- // Duplicated from Inset.cpp and adapted. It is believed that the
- // Inset version should die eventually
- ColorCode pen_color = e.inset->mouseHovered(pi.base.bv) || e.inset->editing(pi.base.bv)?
- Color_mathframe : Color_mathcorners;
-
- int const d = y + dim.descent();
- pi.pain.line(l, d - 3, l, d, pen_color);
- pi.pain.line(r, d - 3, r, d, pen_color);
- pi.pain.line(l, d, l + 3, d, pen_color);
- pi.pain.line(r - 3, d, r, d, pen_color);
-
- if (e.marker == InsetMath::MARKER)
- return;
-
- int const a = y - dim.ascent();
- pi.pain.line(l, a + 3, l, a, pen_color);
- pi.pain.line(r, a + 3, r, a, pen_color);
- pi.pain.line(l, a, l + 3, a, pen_color);
- pi.pain.line(r - 3, a, r, a, pen_color);
-}
-
-}
-
void MathRow::draw(PainterInfo & pi, int x, int const y) const
{
CoordCache & coords = pi.base.bv->coordCache();
Dimension d2 = d;
d2.wid -= e.before + e.after;
coords.insets().add(e.inset, d2);
- e.inset->drawSelection(pi, x + e.before, y);
+ if (pi.pain.develMode() && !e.inset->isBufferValid())
+ pi.pain.fillRectangle(x + e.before, y - d2.ascent(),
+ d2.width(), d2.height(), Color_error);
e.inset->draw(pi, x + e.before, y);
coords.insets().add(e.inset, x, y);
coords.insets().add(e.inset, d);
break;
}
case BEGIN:
+ if (e.ar) {
+ coords.arrays().add(e.ar, x, y);
+ e.ar->drawSelection(pi, x, y);
+ }
if (e.inset) {
coords.insets().add(e.inset, x, y);
drawMarkers(pi, e, x, y);
e.inset->beforeDraw(pi);
}
- if (e.ar)
- coords.arrays().add(e.ar, x, y);
+ x += e.before + e.after;
break;
case END:
if (e.inset)
e.inset->afterDraw(pi);
+ x += e.before + e.after;
break;
case BOX: {
if (e.color == Color_none)
if (!s1.empty()) {
f.setColor(Color_inlinecompletion);
- pi.pain.text(x, y, s1, f);
+ // offset the text by e.after to make sure that the
+ // spacing is after the completion, not before.
+ pi.pain.text(x - e.after, y, s1, f);
x += mathed_string_width(f, s1);
}
if (!s2.empty()) {
f.setColor(Color_nonunique_inlinecompletion);
- pi.pain.text(x, y, s2, f);
+ pi.pain.text(x - e.after, y, s2, f);
x += mathed_string_width(f, s2);
}
}