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;
|
_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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue