Skip to content

Commit

Permalink
libshell: Line wrapping uses unspecified "width" units
Browse files Browse the repository at this point in the history
This simplifies the code elsewhere, too.
  • Loading branch information
skyjake committed Sep 1, 2019
1 parent 0007490 commit 6f26d8a
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 64 deletions.
32 changes: 18 additions & 14 deletions doomsday/libs/shell/include/de/shell/libshell.h
Expand Up @@ -21,7 +21,7 @@

#include <de/Address>
#include <de/Range>
#include <de/String>
#include <de/CString>

/** @defgroup shell Shell Access */

Expand Down Expand Up @@ -62,16 +62,18 @@ inline Address checkPort(Address const &address)
return address;
}

using WrapWidth = duint;

/**
* Line of word-wrapped text.
*/
struct LIBSHELL_PUBLIC WrappedLine
{
String::ByteRange range;
String::CharPos width;
bool isFinal;
CString range;
WrapWidth width;
bool isFinal;

WrappedLine(const String::ByteRange &range, const String::CharPos &width, bool final = false)
WrappedLine(const CString &range, WrapWidth width, bool final = false)
: range(range)
, width(width)
, isFinal(final)
Expand All @@ -83,28 +85,30 @@ class LIBSHELL_PUBLIC ILineWrapping
public:
virtual ~ILineWrapping() {}

virtual bool isEmpty() const = 0;
virtual void clear() = 0;
virtual void wrapTextToWidth(String const &text, String::CharPos maxWidth) = 0;
virtual WrappedLine line(int index) const = 0;
virtual bool isEmpty() const = 0;
virtual void clear() = 0;
virtual void wrapTextToWidth(String const &text, WrapWidth maxWidth) = 0;
virtual WrappedLine line(int index) const = 0;

/// Determines the visible maximum width of the wrapped content.
virtual String::CharPos width() const = 0;
virtual WrapWidth width() const = 0;

/// Determines the number of lines in the wrapped content.
virtual int height() const = 0;

/// Returns the advance width of the range.
virtual String::CharPos rangeWidth(const String::ByteRange &range) const = 0;
virtual WrapWidth rangeWidth(const CString &range) const = 0;

/**
* Calculates which index in the provided content range occupies a
* character at a given width.
* Calculates which index in the text content occupies a character at a given width.
*
* @param range Range within the content.
* @param width Advance width to check.
*
* @return Index from the beginning of the content (note: @em NOT the beginning
* of @a range).
*/
virtual BytePos indexAtWidth(const String::ByteRange &range, String::CharPos width) const = 0;
virtual BytePos indexAtWidth(const CString &range, WrapWidth width) const = 0;
};

} // namespace shell
Expand Down
17 changes: 8 additions & 9 deletions doomsday/libs/shell/include/de/shell/monospacelinewrapping.h
Expand Up @@ -34,9 +34,8 @@ class LIBSHELL_PUBLIC MonospaceLineWrapping : public ILineWrapping
public:
MonospaceLineWrapping();

bool isEmpty() const;

void clear();
bool isEmpty() const override;
void clear() override;

/**
* Determines word wrapping for a line of text given a maximum line width.
Expand All @@ -47,13 +46,13 @@ class LIBSHELL_PUBLIC MonospaceLineWrapping : public ILineWrapping
* @return List of positions in @a text where to break the lines. Total number
* of word-wrapped lines is equal to the size of the returned list.
*/
void wrapTextToWidth(String const &text, String::CharPos maxWidth);
void wrapTextToWidth(String const &text, WrapWidth maxWidth) override;

WrappedLine line(int index) const { return _lines[index]; }
String::CharPos width() const;
int height() const;
String::CharPos rangeWidth(const String::ByteRange &range) const;
BytePos indexAtWidth(const String::ByteRange &range, String::CharPos width) const;
WrappedLine line(int index) const override { return _lines[index]; }
WrapWidth width() const override;
int height() const override;
WrapWidth rangeWidth(const CString &range) const override;
BytePos indexAtWidth(const CString &range, WrapWidth width) const override;

private:
String _text;
Expand Down
35 changes: 22 additions & 13 deletions doomsday/libs/shell/src/abstractlineeditor.cpp
Expand Up @@ -88,7 +88,7 @@ DE_PIMPL(AbstractLineEditor)
*/
void updateWraps()
{
wraps->wrapTextToWidth(text, String::CharPos(max(1, self().maximumWidth())));
wraps->wrapTextToWidth(text, max(1, self().maximumWidth()));

if (wraps->height() > 0)
{
Expand All @@ -113,20 +113,24 @@ DE_PIMPL(AbstractLineEditor)
WrappedLine span = lineSpan(pos.line);
if (!span.isFinal)
{
span.range.end = (iterator(span.range.end) - 1).pos();
span.range.setEnd(span.range.end() - 1);
}
if (mark >= span.range.start && mark <= span.range.end)
if (mark >= span.range.begin().pos(text) && mark <= span.range.end().pos(text))
{
// Stop here. Mark is on this line.
break;
}
pos.x -= span.range.size().index;
pos.x = (iterator(pos.x) - 1).pos();
//pos.x -= (span.range.end - span.range.start + 1).index;
pos.x -= span.range.size();
pos.x = (iterator(pos.x) - 1).pos(text);
}
return pos;
}

const char *cursorPtr() const
{
return text.data() + cursor;
}

/**
* Attemps to move the cursor up or down by a line.
*
Expand All @@ -140,7 +144,7 @@ DE_PIMPL(AbstractLineEditor)
DE_ASSERT(lineOff == 1 || lineOff == -1);

const LineBytePos linePos = lineCursorPos();
const auto destWidth = wraps->rangeWidth({lineSpan(linePos.line).range.start, cursor});
const auto destWidth = wraps->rangeWidth({lineSpan(linePos.line).range.begin(), cursorPtr()});

// Check for no room.
if (!linePos.line && lineOff < 0) return false;
Expand All @@ -152,9 +156,12 @@ DE_PIMPL(AbstractLineEditor)
if (!span.isFinal)
{
// Step back by one character.
span.range.end = (iterator(span.range.end) - 1).pos();
span.range.setEnd(span.range.end() - 1);
}
if (cursorPtr() > span.range.end())
{
cursor = span.range.end().pos(text);
}
if (cursor > span.range.end) cursor = span.range.end;

self().cursorMoved();
return true;
Expand Down Expand Up @@ -281,7 +288,7 @@ DE_PIMPL(AbstractLineEditor)
{
acceptCompletion();

cursor = lineSpan(lineCursorPos().line).range.start;
cursor = lineSpan(lineCursorPos().line).range.begin().pos(text);
self().cursorMoved();
}

Expand All @@ -290,17 +297,19 @@ DE_PIMPL(AbstractLineEditor)
acceptCompletion();

WrappedLine const span = lineSpan(lineCursorPos().line);
cursor = span.range.end; // - (span.isFinal? 0 : 1);
mb_iterator p = span.range.end();
if (!span.isFinal)
{
cursor = (iterator(cursor) - 1).pos();
--p;
}
cursor = p.pos(text);
self().cursorMoved();
}

void killEndOfLine()
{
text.remove(cursor, lineSpan(lineCursorPos().line).range.end - cursor);
text.remove(cursor,
CString(cursorPtr(), lineSpan(lineCursorPos().line).range.end()).size());
rewrapNow();
}

Expand Down
4 changes: 2 additions & 2 deletions doomsday/libs/shell/src/labelwidget.cpp
Expand Up @@ -49,7 +49,7 @@ DE_PIMPL_NOREF(LabelWidget)

void updateWraps(int width)
{
wraps.wrapTextToWidth(label, String::CharPos(width));
wraps.wrapTextToWidth(label, width);
if (vertExpand) height->set(wraps.height());
}
};
Expand Down Expand Up @@ -125,7 +125,7 @@ void LabelWidget::draw()

// Use the wrapped lines to determine width and height.
DE_ASSERT(!d->wraps.isEmpty());
Vec2i labelSize(d->wraps.width().index, d->wraps.height());
Vec2i labelSize(d->wraps.width(), d->wraps.height());

// Determine position of the label based on alignment.
Vec2i labelPos;
Expand Down
10 changes: 5 additions & 5 deletions doomsday/libs/shell/src/lineeditwidget.cpp
Expand Up @@ -66,11 +66,11 @@ Vec2i LineEditWidget::cursorPosition() const
{
de::Rectanglei pos = rule().recti();
// Calculate CharPos on the cursor's line.
const auto linePos = lineCursorPos();
const auto curLineSpan = lineWraps().line(linePos.line);
String::CharPos x = lineWraps().rangeWidth({curLineSpan.range.start, linePos.x});
int y = linePos.line;
return pos.topLeft + Vec2i(prompt().sizei(), 0) + Vec2i(x.index, y);
const auto linePos = lineCursorPos();
const auto curLineSpan = lineWraps().line(linePos.line);
WrapWidth x = lineWraps().rangeWidth(curLineSpan.range.left(linePos.x));
int y = linePos.line;
return pos.topLeft + Vec2i(prompt().sizei(), 0) + Vec2i(x, y);
}

void LineEditWidget::viewResized()
Expand Down
33 changes: 17 additions & 16 deletions doomsday/libs/shell/src/monospacelinewrapping.cpp
Expand Up @@ -35,21 +35,22 @@ void MonospaceLineWrapping::clear()
_text.clear();
}

void MonospaceLineWrapping::wrapTextToWidth(const String &text, String::CharPos maxWidth)
void MonospaceLineWrapping::wrapTextToWidth(const String &text, WrapWidth maxWidth)
{
clear();
_text = text;

if (maxWidth < 1) return; // No room to wrap.

const Char newline = '\n';
const String::CharPos lineWidth = maxWidth;
const Char newline = '\n';
const WrapWidth lineWidth = maxWidth;

mb_iterator begin = text.data();
mb_iterator begin = text.data();
const mb_iterator textEnd = text.data() + text.size();

for (;;)
{
String::CharPos curLineWidth{0};
WrapWidth curLineWidth = 0;

// Newlines always cause a wrap.
mb_iterator end = begin;
Expand All @@ -62,7 +63,7 @@ void MonospaceLineWrapping::wrapTextToWidth(const String &text, String::CharPos
if (end == textEnd)
{
// Time to stop.
_lines << WrappedLine({begin.pos(), text.sizeb()}, curLineWidth);
_lines << WrappedLine(CString(begin, textEnd), curLineWidth);
break;
}

Expand All @@ -83,13 +84,13 @@ void MonospaceLineWrapping::wrapTextToWidth(const String &text, String::CharPos
if (*end == newline)
{
// The newline will be omitted from the wrapped lines.
_lines << WrappedLine({begin.pos(), end.pos()}, curLineWidth);
_lines << WrappedLine(CString(begin, end), curLineWidth);
begin = end + 1;
}
else
{
if (iswspace(*end)) ++end;
_lines << WrappedLine({begin.pos(), end.pos()}, curLineWidth);
_lines << WrappedLine(CString(begin, end), curLineWidth);
begin = end;
}
}
Expand All @@ -98,9 +99,9 @@ void MonospaceLineWrapping::wrapTextToWidth(const String &text, String::CharPos
_lines.last().isFinal = true;
}

String::CharPos MonospaceLineWrapping::width() const
WrapWidth MonospaceLineWrapping::width() const
{
String::CharPos w{0};
WrapWidth w = 0;
for (const auto &line : _lines)
{
w = de::max(w, line.width);
Expand All @@ -113,24 +114,24 @@ int MonospaceLineWrapping::height() const
return _lines.sizei();
}

String::CharPos MonospaceLineWrapping::rangeWidth(const String::ByteRange &range) const
WrapWidth MonospaceLineWrapping::rangeWidth(const CString &range) const
{
String::CharPos w{0};
for (mb_iterator i = _text.data() + range.start, end = _text.data() + range.end; i < end; ++i)
WrapWidth w = 0;
for (mb_iterator i = range.begin(), end = range.end(); i != end; ++i)
{
++w;
}
return w;
}

BytePos MonospaceLineWrapping::indexAtWidth(const String::ByteRange &range, String::CharPos width) const
BytePos MonospaceLineWrapping::indexAtWidth(const CString &range, WrapWidth width) const
{
mb_iterator i = _text.data() + range.start;
mb_iterator i = range.begin();
while (width-- > 0)
{
++i;
}
return i.pos();
return range.begin().pos(_text) + i.pos();
}

}} // namespace de::shell
10 changes: 5 additions & 5 deletions doomsday/libs/shell/src/textcanvas.cpp
Expand Up @@ -230,22 +230,22 @@ void TextCanvas::drawWrappedText(const Vec2i & pos,
const AttribChar::Attribs &attribs,
const Alignment & lineAlignment)
{
const int width = wraps.width().index;
const int width = wraps.width();

for (int y = 0; y < wraps.height(); ++y)
{
const WrappedLine span = wraps.line(y);
String part = text.substr(span.range);
const auto part = span.range;
int x = 0;
if (lineAlignment.testFlag(AlignRight))
{
x = width - part.sizei();
x = width - span.width; //part.sizei();
}
else if (!lineAlignment.testFlag(AlignLeft))
{
x = width/2 - part.sizei()/2;
x = width/2 - span.width/2;
}
drawText(pos + Vec2i(x, y), part, attribs, span.range.start);
drawText(pos + Vec2i(x, y), part, attribs, span.range.begin().pos(text));
}
}

Expand Down

0 comments on commit 6f26d8a

Please sign in to comment.