mirror of https://github.com/stella-emu/stella.git
added multi-line tooltip support
This commit is contained in:
parent
3690d83c7f
commit
3433a6f013
|
@ -40,7 +40,7 @@ RomListWidget::RomListWidget(GuiObject* boss, const GUI::Font& lfont,
|
|||
_textcolorhi = kTextColor;
|
||||
|
||||
_editMode = false;
|
||||
_dyCaret = 1;
|
||||
_dyText = -1; // fixes the vertical position of selected text
|
||||
|
||||
_cols = w / _fontWidth;
|
||||
_rows = h / _lineHeight;
|
||||
|
@ -631,8 +631,8 @@ Common::Rect RomListWidget::getEditRect() const
|
|||
{
|
||||
const int yoffset = std::max(0, (_selectedItem - _currentPos) * _lineHeight);
|
||||
|
||||
return Common::Rect(2 + _w - _bytesWidth, 1 + yoffset,
|
||||
_w, _lineHeight + yoffset);
|
||||
return Common::Rect(2 + _w - _bytesWidth, 1 + yoffset + 1,
|
||||
_w, _lineHeight + yoffset + 1);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -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
|
||||
{
|
||||
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)
|
||||
{
|
||||
int charWidth = font.getCharWidth(s[pos]);
|
||||
if(w2 + charWidth > w)
|
||||
{
|
||||
split = true;
|
||||
break;
|
||||
}
|
||||
w2 += charWidth;
|
||||
}
|
||||
|
||||
if(split)
|
||||
for(int i = pos; i > 0; --i)
|
||||
{
|
||||
if(isWhiteSpace(inStr[i]))
|
||||
if(isWhiteSpace(s[i]))
|
||||
{
|
||||
leftStr = inStr.substr(0, i);
|
||||
if(inStr[i] == ' ') // skip leading space after line break
|
||||
left = s.substr(0, i);
|
||||
if(s[i] == ' ') // skip leading space after line break
|
||||
i++;
|
||||
rightStr = inStr.substr(i);
|
||||
right = s.substr(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
leftStr = inStr.substr(0, pos);
|
||||
rightStr = inStr.substr(pos);
|
||||
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)
|
||||
{
|
||||
// String is too wide.
|
||||
uInt32 i;
|
||||
string leftStr, rightStr;
|
||||
int w2 = 0;
|
||||
|
||||
// SLOW algorithm to find the acceptable length. But it is good enough for now.
|
||||
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);
|
||||
splitString(font, inStr, w, leftStr, rightStr);
|
||||
drawString(font, leftStr, x, y, w, color, align, deltax, false, shadowColor);
|
||||
h -= font.getFontHeight();
|
||||
y += font.getFontHeight();
|
||||
|
|
|
@ -247,6 +247,18 @@ class FBSurface
|
|||
ColorId color, TextAlign align = TextAlign::Left,
|
||||
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.
|
||||
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;
|
||||
|
||||
void wrapString(const string& inStr, int pos,
|
||||
string& leftStr, string& rightStr) const;
|
||||
|
||||
/**
|
||||
Check if the given character is a whitespace.
|
||||
@param s Character to check
|
||||
|
|
|
@ -379,8 +379,8 @@ void EditableWidget::drawCaretSelection()
|
|||
x += _x;
|
||||
y += _y;
|
||||
|
||||
s.fillRect(x - 1, y + 1 + _dyCaret, w + 1, h - 3, kTextColorHi);
|
||||
s.drawString(_font, text, x, y + 1, w, h,
|
||||
s.fillRect(x - 1, y + 1, w + 1, h - 3, kTextColorHi);
|
||||
s.drawString(_font, text, x, y + 1 + _dyText, w, h,
|
||||
kTextColorInv, TextAlign::Left, 0, false);
|
||||
}
|
||||
|
||||
|
@ -397,8 +397,8 @@ void EditableWidget::drawCaretSelection()
|
|||
x += _x;
|
||||
y += _y;
|
||||
|
||||
s.vLine(x, y + 1 + _dyCaret, y + editRect.h() - 3, color);
|
||||
s.vLine(x - 1, y + 1 + _dyCaret, y + editRect.h() - 3, color);
|
||||
s.vLine(x, y + 1, y + editRect.h() - 3, color);
|
||||
s.vLine(x - 1, y + 1, y + editRect.h() - 3, color);
|
||||
clearDirty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ class EditableWidget : public Widget, public CommandSender
|
|||
protected:
|
||||
int _editScrollOffset{0};
|
||||
bool _editMode{true};
|
||||
int _dyCaret{0};
|
||||
int _dyText{0};
|
||||
|
||||
private:
|
||||
TextFilter _filter;
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
|
||||
// TODOs:
|
||||
// - option to disable tips
|
||||
// - multi line tips
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
ToolTip::ToolTip(Dialog& dialog, const GUI::Font& font)
|
||||
|
@ -47,8 +46,8 @@ void ToolTip::setFont(const GUI::Font& font)
|
|||
|
||||
myTextXOfs = fontHeight < 24 ? 5 : 8;
|
||||
myTextYOfs = fontHeight < 24 ? 2 : 3;
|
||||
myWidth = fontWidth * MAX_LEN + myTextXOfs * 2;
|
||||
myHeight = fontHeight + myTextYOfs * 2;
|
||||
myWidth = fontWidth * MAX_COLUMNS + myTextXOfs * 2;
|
||||
myHeight = fontHeight * MAX_ROWS + myTextYOfs * 2;
|
||||
|
||||
mySurface = myDialog.instance().frameBuffer().allocateSurface(myWidth, myHeight);
|
||||
}
|
||||
|
@ -129,9 +128,30 @@ void ToolTip::show(const string& tip)
|
|||
{
|
||||
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 H_CURSOR = 18;
|
||||
uInt32 width = std::min(myWidth, myFont->getStringWidth(tip) + myTextXOfs * 2);
|
||||
// Note: The rects include HiDPI scaling
|
||||
const Common::Rect imageRect = myDialog.instance().frameBuffer().imageRect();
|
||||
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 uInt32 yAbs = myTipPos.y + dialogRect.y() / myScale;
|
||||
Int32 x = std::min(xAbs, Int32(imageRect.w() / myScale - width));
|
||||
const uInt32 y = (yAbs + myHeight + H_CURSOR > imageRect.h() / myScale)
|
||||
? yAbs - myHeight - V_GAP
|
||||
const uInt32 y = (yAbs + height + H_CURSOR > imageRect.h() / myScale)
|
||||
? yAbs - height - V_GAP
|
||||
: yAbs + H_CURSOR / myScale + V_GAP;
|
||||
|
||||
if(x < 0)
|
||||
{
|
||||
x = 0;
|
||||
width = imageRect.w() / myScale;
|
||||
width = std::min(width, imageRect.w() / myScale);
|
||||
}
|
||||
|
||||
mySurface->setSrcSize(width, myHeight);
|
||||
mySurface->setDstSize(width * myScale, myHeight * myScale);
|
||||
mySurface->setSrcSize(width, height);
|
||||
mySurface->setDstSize(width * myScale, height * myScale);
|
||||
mySurface->setDstPos(x * myScale, y * myScale);
|
||||
|
||||
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);
|
||||
mySurface->frameRect(0, 0, width, height, kColor);
|
||||
|
||||
myTipShown = true;
|
||||
myDialog.instance().frameBuffer().setPendingRender();
|
||||
|
|
|
@ -33,9 +33,13 @@ class FBSurface;
|
|||
|
||||
class ToolTip
|
||||
{
|
||||
private:
|
||||
static constexpr uInt32 MAX_COLUMNS = 60;
|
||||
static constexpr uInt32 MAX_ROWS = 5;
|
||||
|
||||
public:
|
||||
// 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() = default;
|
||||
|
|
Loading…
Reference in New Issue