added multi-line tooltip support

This commit is contained in:
thrust26 2020-11-18 20:07:25 +01:00
parent 3690d83c7f
commit 3433a6f013
7 changed files with 84 additions and 49 deletions

View File

@ -40,7 +40,7 @@ RomListWidget::RomListWidget(GuiObject* boss, const GUI::Font& lfont,
_textcolorhi = kTextColor; _textcolorhi = kTextColor;
_editMode = false; _editMode = false;
_dyCaret = 1; _dyText = -1; // fixes the vertical position of selected text
_cols = w / _fontWidth; _cols = w / _fontWidth;
_rows = h / _lineHeight; _rows = h / _lineHeight;
@ -631,8 +631,8 @@ Common::Rect RomListWidget::getEditRect() const
{ {
const int yoffset = std::max(0, (_selectedItem - _currentPos) * _lineHeight); const int yoffset = std::max(0, (_selectedItem - _currentPos) * _lineHeight);
return Common::Rect(2 + _w - _bytesWidth, 1 + yoffset, return Common::Rect(2 + _w - _bytesWidth, 1 + yoffset + 1,
_w, _lineHeight + yoffset); _w, _lineHeight + yoffset + 1);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -296,21 +296,39 @@ void FBSurface::frameRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurface::wrapString(const string& inStr, int pos, string& leftStr, string& rightStr) const void FBSurface::splitString(const GUI::Font& font, const string& s, int w,
string& left, string& right) const
{ {
for(int i = pos; i > 0; --i) uInt32 pos;
int w2 = 0;
bool split = false;
// SLOW algorithm to find the acceptable length. But it is good enough for now.
for(pos = 0; pos < s.size(); ++pos)
{ {
if(isWhiteSpace(inStr[i])) int charWidth = font.getCharWidth(s[pos]);
if(w2 + charWidth > w)
{ {
leftStr = inStr.substr(0, i); split = true;
if(inStr[i] == ' ') // skip leading space after line break break;
i++;
rightStr = inStr.substr(i);
return;
} }
w2 += charWidth;
} }
leftStr = inStr.substr(0, pos);
rightStr = inStr.substr(pos); if(split)
for(int i = pos; i > 0; --i)
{
if(isWhiteSpace(s[i]))
{
left = s.substr(0, i);
if(s[i] == ' ') // skip leading space after line break
i++;
right = s.substr(i);
return;
}
}
left = s.substr(0, pos);
right = s.substr(pos);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -340,21 +358,9 @@ int FBSurface::drawString(const GUI::Font& font, const string& s,
while (font.getStringWidth(inStr) > w && h >= font.getFontHeight() * 2) while (font.getStringWidth(inStr) > w && h >= font.getFontHeight() * 2)
{ {
// String is too wide. // String is too wide.
uInt32 i;
string leftStr, rightStr; string leftStr, rightStr;
int w2 = 0;
// SLOW algorithm to find the acceptable length. But it is good enough for now. splitString(font, inStr, w, leftStr, rightStr);
for(i = 0; i < inStr.size(); ++i)
{
int charWidth = font.getCharWidth(inStr[i]);
if(w2 + charWidth > w)
break;
w2 += charWidth;
//str += inStr[i];
}
wrapString(inStr, i, leftStr, rightStr);
drawString(font, leftStr, x, y, w, color, align, deltax, false, shadowColor); drawString(font, leftStr, x, y, w, color, align, deltax, false, shadowColor);
h -= font.getFontHeight(); h -= font.getFontHeight();
y += font.getFontHeight(); y += font.getFontHeight();

View File

@ -247,6 +247,18 @@ class FBSurface
ColorId color, TextAlign align = TextAlign::Left, ColorId color, TextAlign align = TextAlign::Left,
int deltax = 0, bool useEllipsis = true, ColorId shadowColor = kNone); int deltax = 0, bool useEllipsis = true, ColorId shadowColor = kNone);
/**
Splits a given string to a given width considering whitespaces.
@param font The font to draw the string with
@param s The string to split
@param w The width of the string area
@param left The left part of the split string
@param right The right part of the split string
*/
void splitString(const GUI::Font& font, const string& s, int w,
string& left, string& right) const;
/** /**
The rendering attributes that can be modified for this texture. The rendering attributes that can be modified for this texture.
These probably can only be implemented in child FBSurfaces where These probably can only be implemented in child FBSurfaces where
@ -383,9 +395,6 @@ class FBSurface
*/ */
bool checkBounds(const uInt32 x, const uInt32 y) const; bool checkBounds(const uInt32 x, const uInt32 y) const;
void wrapString(const string& inStr, int pos,
string& leftStr, string& rightStr) const;
/** /**
Check if the given character is a whitespace. Check if the given character is a whitespace.
@param s Character to check @param s Character to check

View File

@ -379,8 +379,8 @@ void EditableWidget::drawCaretSelection()
x += _x; x += _x;
y += _y; y += _y;
s.fillRect(x - 1, y + 1 + _dyCaret, w + 1, h - 3, kTextColorHi); s.fillRect(x - 1, y + 1, w + 1, h - 3, kTextColorHi);
s.drawString(_font, text, x, y + 1, w, h, s.drawString(_font, text, x, y + 1 + _dyText, w, h,
kTextColorInv, TextAlign::Left, 0, false); kTextColorInv, TextAlign::Left, 0, false);
} }
@ -397,8 +397,8 @@ void EditableWidget::drawCaretSelection()
x += _x; x += _x;
y += _y; y += _y;
s.vLine(x, y + 1 + _dyCaret, y + editRect.h() - 3, color); s.vLine(x, y + 1, y + editRect.h() - 3, color);
s.vLine(x - 1, y + 1 + _dyCaret, y + editRect.h() - 3, color); s.vLine(x - 1, y + 1, y + editRect.h() - 3, color);
clearDirty(); clearDirty();
} }
} }

View File

@ -125,7 +125,7 @@ class EditableWidget : public Widget, public CommandSender
protected: protected:
int _editScrollOffset{0}; int _editScrollOffset{0};
bool _editMode{true}; bool _editMode{true};
int _dyCaret{0}; int _dyText{0};
private: private:
TextFilter _filter; TextFilter _filter;

View File

@ -27,7 +27,6 @@
// TODOs: // TODOs:
// - option to disable tips // - option to disable tips
// - multi line tips
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ToolTip::ToolTip(Dialog& dialog, const GUI::Font& font) ToolTip::ToolTip(Dialog& dialog, const GUI::Font& font)
@ -47,8 +46,8 @@ void ToolTip::setFont(const GUI::Font& font)
myTextXOfs = fontHeight < 24 ? 5 : 8; myTextXOfs = fontHeight < 24 ? 5 : 8;
myTextYOfs = fontHeight < 24 ? 2 : 3; myTextYOfs = fontHeight < 24 ? 2 : 3;
myWidth = fontWidth * MAX_LEN + myTextXOfs * 2; myWidth = fontWidth * MAX_COLUMNS + myTextXOfs * 2;
myHeight = fontHeight + myTextYOfs * 2; myHeight = fontHeight * MAX_ROWS + myTextYOfs * 2;
mySurface = myDialog.instance().frameBuffer().allocateSurface(myWidth, myHeight); mySurface = myDialog.instance().frameBuffer().allocateSurface(myWidth, myHeight);
} }
@ -129,9 +128,30 @@ void ToolTip::show(const string& tip)
{ {
myTipPos = myMousePos; myTipPos = myMousePos;
uInt32 maxWidth = std::min(myWidth - myTextXOfs * 2, uInt32(myFont->getStringWidth(tip)));
mySurface->fillRect(1, 1, maxWidth + myTextXOfs * 2 - 2, myHeight - 2, kWidColor);
int lines = std::min(MAX_ROWS,
uInt32(mySurface->drawString(*myFont, tip, myTextXOfs, myTextYOfs,
maxWidth, myHeight - myTextYOfs * 2,
kTextColor)));
// Calculate maximum width of drawn string lines
uInt32 width = 0;
string inStr = tip;
for(int i = 0; i < lines; ++i)
{
string leftStr, rightStr;
mySurface->splitString(*myFont, inStr, maxWidth, leftStr, rightStr);
width = std::max(width, uInt32(myFont->getStringWidth(leftStr)));
inStr = rightStr;
}
width += myTextXOfs * 2;
// Calculate and set surface size and position
const uInt32 height = std::min(myHeight, myFont->getFontHeight() * lines + myTextYOfs * 2);
const uInt32 V_GAP = 1; const uInt32 V_GAP = 1;
const uInt32 H_CURSOR = 18; const uInt32 H_CURSOR = 18;
uInt32 width = std::min(myWidth, myFont->getStringWidth(tip) + myTextXOfs * 2);
// Note: The rects include HiDPI scaling // Note: The rects include HiDPI scaling
const Common::Rect imageRect = myDialog.instance().frameBuffer().imageRect(); const Common::Rect imageRect = myDialog.instance().frameBuffer().imageRect();
const Common::Rect dialogRect = myDialog.surface().dstRect(); const Common::Rect dialogRect = myDialog.surface().dstRect();
@ -139,24 +159,20 @@ void ToolTip::show(const string& tip)
const Int32 xAbs = myTipPos.x + dialogRect.x() / myScale; const Int32 xAbs = myTipPos.x + dialogRect.x() / myScale;
const uInt32 yAbs = myTipPos.y + dialogRect.y() / myScale; const uInt32 yAbs = myTipPos.y + dialogRect.y() / myScale;
Int32 x = std::min(xAbs, Int32(imageRect.w() / myScale - width)); Int32 x = std::min(xAbs, Int32(imageRect.w() / myScale - width));
const uInt32 y = (yAbs + myHeight + H_CURSOR > imageRect.h() / myScale) const uInt32 y = (yAbs + height + H_CURSOR > imageRect.h() / myScale)
? yAbs - myHeight - V_GAP ? yAbs - height - V_GAP
: yAbs + H_CURSOR / myScale + V_GAP; : yAbs + H_CURSOR / myScale + V_GAP;
if(x < 0) if(x < 0)
{ {
x = 0; x = 0;
width = imageRect.w() / myScale; width = std::min(width, imageRect.w() / myScale);
} }
mySurface->setSrcSize(width, myHeight); mySurface->setSrcSize(width, height);
mySurface->setDstSize(width * myScale, myHeight * myScale); mySurface->setDstSize(width * myScale, height * myScale);
mySurface->setDstPos(x * myScale, y * myScale); mySurface->setDstPos(x * myScale, y * myScale);
mySurface->frameRect(0, 0, width, height, kColor);
mySurface->frameRect(0, 0, width, myHeight, kColor);
mySurface->fillRect(1, 1, width - 2, myHeight - 2, kWidColor);
mySurface->drawString(*myFont, tip, myTextXOfs, myTextYOfs,
width - myTextXOfs * 2, myHeight - myTextYOfs * 2, kTextColor);
myTipShown = true; myTipShown = true;
myDialog.instance().frameBuffer().setPendingRender(); myDialog.instance().frameBuffer().setPendingRender();

View File

@ -33,9 +33,13 @@ class FBSurface;
class ToolTip class ToolTip
{ {
private:
static constexpr uInt32 MAX_COLUMNS = 60;
static constexpr uInt32 MAX_ROWS = 5;
public: public:
// Maximum tooltip length // Maximum tooltip length
static constexpr uInt32 MAX_LEN = 80; static constexpr uInt32 MAX_LEN = MAX_COLUMNS * MAX_ROWS;
ToolTip(Dialog& dialog, const GUI::Font& font); ToolTip(Dialog& dialog, const GUI::Font& font);
~ToolTip() = default; ~ToolTip() = default;