Skip to content

Commit

Permalink
libgui|Font|RichFormat: Added escape sequences for tab positions
Browse files Browse the repository at this point in the history
These will be used for aligning text on a line.
  • Loading branch information
skyjake committed Jun 7, 2013
1 parent fed9a73 commit 16050e7
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 13 deletions.
23 changes: 20 additions & 3 deletions doomsday/libgui/include/de/gui/font.h
Expand Up @@ -40,6 +40,8 @@ namespace de {
class LIBGUI_PUBLIC Font
{
public:
typedef QVector<int> TabStops;

/**
* Rich formatting instructions for a string of plain text.
*
Expand Down Expand Up @@ -88,6 +90,10 @@ class LIBGUI_PUBLIC Font
AccentColor = 3,
DimAccentColor = 4
};
enum {
NoTabStop = -2,
NextTabStop = -1
};

/**
* Interface for an object providing style information: fonts and
Expand Down Expand Up @@ -152,6 +158,10 @@ class LIBGUI_PUBLIC Font
*/
RichFormat subRange(Rangei const &range) const;

TabStops const &tabStops() const { return _tabs; }

int tabStopXWidth(int stop) const;

/**
* Iterates the rich format ranges of a RichFormat.
*
Expand All @@ -176,6 +186,7 @@ class LIBGUI_PUBLIC Font
int colorIndex() const;
IStyle::Color color() const;
bool markIndent() const;
int tabStop() const;
};

private:
Expand All @@ -189,12 +200,18 @@ class LIBGUI_PUBLIC Font
Style style;
int colorIndex;
bool markIndent;
int tabStop;

FormatRange() : sizeFactor(1.f), weight(OriginalWeight),
style(OriginalStyle), colorIndex(-1), markIndent(false) {}
style(OriginalStyle), colorIndex(-1), markIndent(false),
tabStop(NoTabStop) {}
};
typedef QList<FormatRange> Ranges;
Ranges _ranges;

/// Tab stops are only applicable on the first line of a set of wrapped
/// lines. Subsequent lines use the latest accessed tab stop as the indent.
TabStops _tabs;
};

public:
Expand All @@ -221,8 +238,8 @@ class LIBGUI_PUBLIC Font
* area is covered by the glyphs. (0,0) is at the baseline, left edge of
* the line. The rectangle may extend into negative coordinates.
*
* @param textLine Text to measure.
* @param format Rich formatting for @a textLine.
* @param textLine Text to measure.
* @param format Rich formatting for @a textLine.
*
* @return Rectangle covered by the text.
*/
Expand Down
97 changes: 87 additions & 10 deletions doomsday/libgui/src/font.cpp
Expand Up @@ -36,12 +36,13 @@ Font::RichFormat::RichFormat(IStyle const &style) : _style(&style)
{}

Font::RichFormat::RichFormat(RichFormat const &other)
: _style(other._style), _ranges(other._ranges)
: _style(other._style), _ranges(other._ranges), _tabs(other._tabs)
{}

void Font::RichFormat::clear()
{
_ranges.clear();
_tabs.clear();
}

void Font::RichFormat::setStyle(IStyle const &style)
Expand Down Expand Up @@ -81,6 +82,8 @@ String Font::RichFormat::initFromStyledText(String const &styledText)
_ranges << FormatRange();
format = &_ranges.back();

_tabs.clear();

forever
{
range.end = styledText.indexOf(QChar('\x1b'), range.start);
Expand All @@ -103,34 +106,63 @@ String Font::RichFormat::initFromStyledText(String const &styledText)
_ranges << FormatRange(*format);
format = &_ranges.back();
format->range = Rangei(range.end - offset, range.end - offset);

// Some properties do not copy over.
format->markIndent = false;
format->tabStop = NoTabStop;
}

// Check the escape sequence.
int escLen = 2;
char ch = styledText[range.end + 1].toLatin1();
switch(ch)
{
case '.': // pop a format off the stack
stack.takeLast(); // ignore the one just added
if(!stack.isEmpty())
{
FormatRange prev = stack.takeLast();
format->sizeFactor = prev.sizeFactor;
format->colorIndex = prev.colorIndex;
format->weight = prev.weight;
format->style = prev.style;
format->markIndent = false;
format->tabStop = NoTabStop;
}
else
{
format->sizeFactor = 1.f;
format->colorIndex = OriginalColor;
format->weight = OriginalWeight;
format->style = OriginalStyle;
format->markIndent = false;
format->tabStop = NoTabStop;
}
break;

case '>':
format->markIndent = true;
break;

case '\t':
format->tabStop = NextTabStop;
break;

case 'T':
escLen = 3;
format->tabStop = styledText[range.end + 2].toLatin1() - 'a';
break;

case '(':
// Sequence of tab stops effective in the entire content.
_tabs.clear();
for(int i = range.end + 2; styledText[i] != ')' && i < styledText.size(); ++i, ++escLen)
{
_tabs.append(styledText[i].toLatin1() - 'a' + 1);
}
escLen++;
break;

case 'b':
format->weight = Bold;
break;
Expand Down Expand Up @@ -187,8 +219,8 @@ String Font::RichFormat::initFromStyledText(String const &styledText)
}

// Advance the scanner.
range.start = range.end + 2;
offset += 2; // skipped chars
range.start = range.end + escLen;
offset += escLen; // skipped chars
}
else
{
Expand Down Expand Up @@ -216,7 +248,13 @@ String Font::RichFormat::initFromStyledText(String const &styledText)
<< "size:" << r.sizeFactor
<< "weight:" << r.weight
<< "style:" << r.style
<< "color:" << r.colorIndex;
<< "color:" << r.colorIndex
<< "tab:" << r.tabStop;
}
qDebug() << "Tabs:" << _tabs.size();
foreach(int i, _tabs)
{
qDebug() << i;
}*/

return plain;
Expand Down Expand Up @@ -249,6 +287,23 @@ Font::RichFormat Font::RichFormat::subRange(Rangei const &range) const
return sub;
}

int Font::RichFormat::tabStopXWidth(int stop) const
{
if(stop < 0 || _tabs.isEmpty()) return 0;

DENG2_ASSERT(stop < 50);

int x = 0;
for(int i = 0; i <= stop; ++i)
{
if(i >= _tabs.size())
x += _tabs.last();
else
x += _tabs[i];
}
return x;
}

Font::RichFormat::Iterator::Iterator(RichFormat const &f) : format(f), index(-1) {}

bool Font::RichFormat::Iterator::hasNext() const
Expand Down Expand Up @@ -310,6 +365,11 @@ bool Font::RichFormat::Iterator::markIndent() const
return format._ranges[index].markIndent;
}

int Font::RichFormat::Iterator::tabStop() const
{
return format._ranges[index].tabStop;
}

DENG2_PIMPL(Font)
{
QFont font;
Expand Down Expand Up @@ -381,6 +441,28 @@ DENG2_PIMPL(Font)
}
return *metrics;
}

/*
int jumpToTabStop(RichFormat::Iterator const &rich, int pos)
{
if(rich.format.tabStops().isEmpty() || rich.tabStop() == RichFormat::NoTabStop)
{
return pos;
}
if(rich.tabStop() == RichFormat::NextTabStop)
{
int stop = 0;
forever
{
int x = rich.format.tabStopXWidth(stop++) * metrics->xHeight();
if(x >= pos) return x;
}
}
return de::max(pos, rich.format.tabStopXWidth(rich.tabStop()) * metrics->xHeight());
}
*/
};

Font::Font() : d(new Instance(this))
Expand Down Expand Up @@ -419,7 +501,7 @@ Rectanglei Font::measure(String const &textLine, RichFormat const &format) const

if(rect.height() == 0)
{
// It seems measuring the bounds of a Tab character produce
// It seems measuring the bounds of a Tab character produces
// strange results (position 100000?).
rect = Rectanglei(0, 0, rect.width(), 0);
}
Expand Down Expand Up @@ -489,11 +571,6 @@ QImage Font::rasterize(String const &textLine,
QPainter painter(&img);
painter.setCompositionMode(QPainter::CompositionMode_Source);

/*#ifdef WIN32
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::TextAntialiasing);
#endif*/

int advance = 0;
RichFormat::Iterator iter(format);
while(iter.hasNext())
Expand Down

0 comments on commit 16050e7

Please sign in to comment.