diff --git a/doomsday/client/data/defaultstyle.pack/fonts.dei b/doomsday/client/data/defaultstyle.pack/fonts.dei index d1cde77e3e..1a5bfd80cb 100644 --- a/doomsday/client/data/defaultstyle.pack/fonts.dei +++ b/doomsday/client/data/defaultstyle.pack/fonts.dei @@ -18,7 +18,7 @@ group { font default { family: Lucida Grande - size: 18pt + size: 16pt weight: normal style: normal } diff --git a/doomsday/client/include/ui/widgets/fontlinewrapping.h b/doomsday/client/include/ui/widgets/fontlinewrapping.h index 9fed3fc78c..18c450227c 100644 --- a/doomsday/client/include/ui/widgets/fontlinewrapping.h +++ b/doomsday/client/include/ui/widgets/fontlinewrapping.h @@ -27,6 +27,8 @@ * Line wrapper that uses a particular font and calculates widths in pixels. * Height is still in lines, though. The wrapper cannot be used before the * font has been defined. + * + * Supports indentation of lines, as marked in the RichFormat. */ class FontLineWrapping : public de::shell::ILineWrapping { @@ -39,6 +41,7 @@ class FontLineWrapping : public de::shell::ILineWrapping bool isEmpty() const; void clear(); void wrapTextToWidth(de::String const &text, int maxWidth); + void wrapTextToWidth(de::String const &text, de::Font::RichFormat const &format, int maxWidth); de::String const &text() const; de::shell::WrappedLine line(int index) const; @@ -55,6 +58,8 @@ class FontLineWrapping : public de::shell::ILineWrapping de::Vector2i charTopLeftInPixels(int line, int charIndex); + int lineIndent(int index) const; + private: DENG2_PRIVATE(d) }; diff --git a/doomsday/client/include/ui/widgets/gltextcomposer.h b/doomsday/client/include/ui/widgets/gltextcomposer.h index b66e2b7964..daed374e9e 100644 --- a/doomsday/client/include/ui/widgets/gltextcomposer.h +++ b/doomsday/client/include/ui/widgets/gltextcomposer.h @@ -60,6 +60,7 @@ class GLTextComposer void setWrapping(FontLineWrapping const &wrappedLines); void setText(de::String const &text); + void setText(de::String const &text, de::Font::RichFormat const &format); /** * Makes sure all the lines are allocated on the atlas. After this all the diff --git a/doomsday/client/include/ui/widgets/styledlogsinkformatter.h b/doomsday/client/include/ui/widgets/styledlogsinkformatter.h index 3320a68f63..212c705637 100644 --- a/doomsday/client/include/ui/widgets/styledlogsinkformatter.h +++ b/doomsday/client/include/ui/widgets/styledlogsinkformatter.h @@ -32,7 +32,7 @@ class StyledLogSinkFormatter : public de::LogSink::IFormatter { // This will form a single long line. The line wrapper will // then determine how to wrap it onto the available width. - return Lines() << entry.asText(de::LogEntry::Styled); + return Lines() << entry.asText(de::LogEntry::Styled | de::LogEntry::OmitLevel); } }; diff --git a/doomsday/client/src/ui/widgets/fontlinewrapping.cpp b/doomsday/client/src/ui/widgets/fontlinewrapping.cpp index 3f28b5b525..70b224336f 100644 --- a/doomsday/client/src/ui/widgets/fontlinewrapping.cpp +++ b/doomsday/client/src/ui/widgets/fontlinewrapping.cpp @@ -27,13 +27,17 @@ DENG2_PIMPL_NOREF(FontLineWrapping) struct Line { WrappedLine line; int width; + int indent; - Line(WrappedLine const &ln = WrappedLine(Rangei()), int w = 0) : line(ln), width(w) {} + Line(WrappedLine const &ln = WrappedLine(Rangei()), int w = 0, int ind = 0) + : line(ln), width(w), indent(ind) {} }; QList lines; String text; + Font::RichFormat format; + int indent; - Instance() : font(0) {} + Instance() : font(0), indent(0) {} String rangeText(Rangei const &range) const { @@ -44,7 +48,7 @@ DENG2_PIMPL_NOREF(FontLineWrapping) { if(font) { - return font->measure(rangeText(range)).width(); + return font->measure(rangeText(range), format.subRange(range)).width(); } return 0; } @@ -53,14 +57,28 @@ DENG2_PIMPL_NOREF(FontLineWrapping) { if(font) { - return font->advanceWidth(rangeText(range)); + return font->advanceWidth(rangeText(range), format.subRange(range)); } return 0; } void appendLine(Rangei const &range) { - lines.append(Line(WrappedLine(range), rangeVisibleWidth(range))); + lines.append(Line(WrappedLine(range), rangeVisibleWidth(range), indent)); + + // Check for possible indent for following lines. + Font::RichFormat rich = format.subRange(range); + Font::RichFormat::Iterator iter(rich); + int newIndent = indent; + while(iter.hasNext()) + { + iter.next(); + if(iter.markIndent()) + { + newIndent = indent + rangeAdvanceWidth(Rangei(range.start, iter.range().start)); + } + } + indent = newIndent; } }; @@ -91,29 +109,41 @@ void FontLineWrapping::clear() void FontLineWrapping::wrapTextToWidth(String const &text, int maxWidth) { - /** - * @note This is quite similar to MonospaceLineWrapping::wrapTextToWidth(). - * Perhaps a generic method could be abstracted from these two. - */ + wrapTextToWidth(text, Font::RichFormat::fromPlainText(text), maxWidth); +} +void FontLineWrapping::wrapTextToWidth(String const &text, Font::RichFormat const &format, int maxWidth) +{ QChar const newline('\n'); clear(); + int const MIN_LINE_WIDTH = 120; + if(maxWidth <= 1 || !d->font) return; // This is the text that we will be wrapping. - d->text = text; + d->text = text; + d->format = format; int begin = 0; forever { + // How much width is available, taking indentation into account? + if(maxWidth - d->indent < MIN_LINE_WIDTH) + { + // There is no room for this indent... + d->indent = de::max(0, maxWidth - MIN_LINE_WIDTH); + } + int availWidth = maxWidth - d->indent; + // Quick check: does the remainder fit? Rangei range(begin, text.size()); int visWidth = d->rangeVisibleWidth(range); - if(visWidth <= maxWidth) + if(visWidth <= availWidth) { - d->lines.append(Instance::Line(WrappedLine(Rangei(begin, text.size())), visWidth)); + d->lines.append(Instance::Line(WrappedLine(Rangei(begin, text.size())), + visWidth, d->indent)); break; } @@ -124,7 +154,7 @@ void FontLineWrapping::wrapTextToWidth(String const &text, int maxWidth) { ++end; - if(d->rangeVisibleWidth(Rangei(begin, end)) > maxWidth) + if(d->rangeVisibleWidth(Rangei(begin, end)) > availWidth) { // Went too far. wrapPosMax = --end; @@ -134,14 +164,6 @@ void FontLineWrapping::wrapTextToWidth(String const &text, int maxWidth) DENG2_ASSERT(end != text.size()); - /* - if(end == text.size()) - { - // Out of characters; time to stop. - d->appendLine(Range(begin, text.size())); - break; - }*/ - // Find a good (whitespace) break point. while(!text.at(end).isSpace()) { @@ -161,7 +183,7 @@ void FontLineWrapping::wrapTextToWidth(String const &text, int maxWidth) } else { - while(text.at(end).isSpace()) ++end; + while(end < text.size() && text.at(end).isSpace()) ++end; d->appendLine(Rangei(begin, end)); begin = end; } @@ -257,3 +279,8 @@ Vector2i FontLineWrapping::charTopLeftInPixels(int line, int charIndex) return cp; } + +int FontLineWrapping::lineIndent(int index) const +{ + return d->lines[index].indent; +} diff --git a/doomsday/client/src/ui/widgets/gltextcomposer.cpp b/doomsday/client/src/ui/widgets/gltextcomposer.cpp index 520b45e156..e43c24e79a 100644 --- a/doomsday/client/src/ui/widgets/gltextcomposer.cpp +++ b/doomsday/client/src/ui/widgets/gltextcomposer.cpp @@ -28,6 +28,7 @@ DENG2_PIMPL(GLTextComposer) Atlas *atlas; String text; FontLineWrapping const *wraps; + Font::RichFormat format; bool needRaster; struct Line { @@ -80,7 +81,7 @@ DENG2_PIMPL(GLTextComposer) changed = true; - String const part = text.substr(span.range.start, span.range.size()); + String const part = text.substr(span.range); if(i >= lines.size()) { @@ -90,7 +91,7 @@ DENG2_PIMPL(GLTextComposer) Line &line = lines[i]; line.range = span.range; - line.id = atlas->alloc(font->rasterize(part)); + line.id = atlas->alloc(font->rasterize(part, format.subRange(span.range))); } // Remove the excess lines. @@ -127,8 +128,14 @@ void GLTextComposer::setWrapping(FontLineWrapping const &wrappedLines) } void GLTextComposer::setText(String const &text) +{ + setText(text, Font::RichFormat::fromPlainText(text)); +} + +void GLTextComposer::setText(String const &text, Font::RichFormat const &format) { d->text = text; + d->format = format; d->needRaster = true; // Force a redo of everything. } @@ -193,7 +200,7 @@ void GLTextComposer::makeVertices(Vertices &triStrip, v.rgba = Vector4f(1, 1, 1, 1); // should be a param - Vector2f linePos = p; + Vector2f linePos = p + Vector2f(d->wraps->lineIndent(i), 0); // Align the line. if(lineAlign.testFlag(AlignRight)) diff --git a/doomsday/client/src/ui/widgets/logwidget.cpp b/doomsday/client/src/ui/widgets/logwidget.cpp index f68aba307a..80c089a0cd 100644 --- a/doomsday/client/src/ui/widgets/logwidget.cpp +++ b/doomsday/client/src/ui/widgets/logwidget.cpp @@ -47,6 +47,7 @@ DENG2_PIMPL(LogWidget) /// Cache of drawable entries. struct CacheEntry { + Font::RichFormat format; FontLineWrapping wraps; GLTextComposer composer; @@ -72,10 +73,11 @@ DENG2_PIMPL(LogWidget) return wraps.isEmpty(); } - void wrap(String const &text, int width) + void wrap(String const &richText, int width) { - wraps.wrapTextToWidth(text, width); - composer.setText(wraps.text()); + String plain = format.initFromStyledText(richText); + wraps.wrapTextToWidth(plain, format, width); + composer.setText(plain, format); composer.setWrapping(wraps); } @@ -265,6 +267,10 @@ DENG2_PIMPL(LogWidget) StyledLogSinkFormatter::Lines lines = formatter.logEntryToTextLines(entry); DENG2_ASSERT(lines.size() == 1); + + Font::RichFormat format; + format.initFromStyledText(lines[0]); + return lines[0]; } @@ -447,7 +453,7 @@ DENG2_PIMPL(LogWidget) LogWidget::LogWidget(String const &name) : GuiWidget(name), d(new Instance(this)) { - rule().setInput(Rule::Width, Const(400)); // TODO -- from rule defs + rule().setInput(Rule::Width, Const(600)); // TODO -- from rule defs LogBuffer::appBuffer().addSink(d->sink); }