mirror of https://github.com/stella-emu/stella.git
improved tooltip handling (better delays, rerender instead of redraw)
added tooltip to StringListWidget for shortened texts (e.g. ROM names in launcher) added code for StaticTextWidget tooltip (without setting widget dirty)
This commit is contained in:
parent
1e4f3563b6
commit
3690d83c7f
|
@ -40,6 +40,7 @@ RomListWidget::RomListWidget(GuiObject* boss, const GUI::Font& lfont,
|
|||
_textcolorhi = kTextColor;
|
||||
|
||||
_editMode = false;
|
||||
_dyCaret = 1;
|
||||
|
||||
_cols = w / _fontWidth;
|
||||
_rows = h / _lineHeight;
|
||||
|
@ -485,7 +486,6 @@ string RomListWidget::getToolTip(Common::Point pos) const
|
|||
const string valStr = bytes.substr((idx.x / 3) * 3, 2);
|
||||
|
||||
val = static_cast<Int32>(stol(valStr, nullptr, 16));
|
||||
|
||||
}
|
||||
ostringstream buf;
|
||||
|
||||
|
|
|
@ -432,9 +432,9 @@ void Dialog::drawDialog()
|
|||
|
||||
FBSurface& s = surface();
|
||||
|
||||
cerr << endl << "d";
|
||||
if(isDirty())
|
||||
{
|
||||
cerr << endl << "d";
|
||||
//cerr << "*** draw dialog " << typeid(*this).name() << " ***" << endl;
|
||||
|
||||
if(clearsBackground())
|
||||
|
@ -464,6 +464,8 @@ void Dialog::drawDialog()
|
|||
|
||||
clearDirty();
|
||||
}
|
||||
else
|
||||
cerr << endl;
|
||||
|
||||
// Draw all children
|
||||
drawChain();
|
||||
|
|
|
@ -107,7 +107,7 @@ void EditableWidget::receivedFocusWidget()
|
|||
{
|
||||
_caretTimer = 0;
|
||||
_caretEnabled = true;
|
||||
dialog().tooltip().release();
|
||||
dialog().tooltip().hide();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -379,7 +379,7 @@ void EditableWidget::drawCaretSelection()
|
|||
x += _x;
|
||||
y += _y;
|
||||
|
||||
s.fillRect(x - 1, y + 1, w + 1, h - 3, kTextColorHi);
|
||||
s.fillRect(x - 1, y + 1 + _dyCaret, w + 1, h - 3, kTextColorHi);
|
||||
s.drawString(_font, text, x, y + 1, w, h,
|
||||
kTextColorInv, TextAlign::Left, 0, false);
|
||||
}
|
||||
|
@ -397,8 +397,8 @@ void EditableWidget::drawCaretSelection()
|
|||
x += _x;
|
||||
y += _y;
|
||||
|
||||
s.vLine(x, y + 1, y + editRect.h() - 3, color);
|
||||
s.vLine(x - 1, y + 1, y + editRect.h() - 3, color);
|
||||
s.vLine(x, y + 1 + _dyCaret, y + editRect.h() - 3, color);
|
||||
s.vLine(x - 1, y + 1 + _dyCaret, y + editRect.h() - 3, color);
|
||||
clearDirty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,6 +125,7 @@ class EditableWidget : public Widget, public CommandSender
|
|||
protected:
|
||||
int _editScrollOffset{0};
|
||||
bool _editMode{true};
|
||||
int _dyCaret{0};
|
||||
|
||||
private:
|
||||
TextFilter _filter;
|
||||
|
|
|
@ -106,20 +106,20 @@ class GuiObject : public CommandReceiver
|
|||
|
||||
virtual void tick() = 0;
|
||||
|
||||
void setFlags(uInt32 flags)
|
||||
void setFlags(uInt32 flags, bool updateDirty = true)
|
||||
{
|
||||
uInt32 oldFlags = _flags;
|
||||
|
||||
_flags |= flags;
|
||||
if(oldFlags != _flags)
|
||||
if(updateDirty && oldFlags != _flags)
|
||||
setDirty();
|
||||
}
|
||||
void clearFlags(uInt32 flags)
|
||||
void clearFlags(uInt32 flags, bool updateDirty = true)
|
||||
{
|
||||
uInt32 oldFlags = _flags;
|
||||
|
||||
_flags &= ~flags;
|
||||
if(oldFlags != _flags)
|
||||
if(updateDirty && oldFlags != _flags)
|
||||
setDirty();
|
||||
}
|
||||
uInt32 getFlags() const { return _flags; }
|
||||
|
|
|
@ -50,6 +50,36 @@ void StringListWidget::setList(const StringList& list)
|
|||
ListWidget::recalc();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
int StringListWidget::getToolTipIndex(Common::Point pos) const
|
||||
{
|
||||
return (pos.y - getAbsY()) / _lineHeight + _currentPos;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string StringListWidget::getToolTip(Common::Point pos) const
|
||||
{
|
||||
Common::Rect rect = getEditRect();
|
||||
const string value = _list[getToolTipIndex(pos)];
|
||||
|
||||
if(uInt32(_font.getStringWidth(value)) > rect.w())
|
||||
return _toolTipText + value;
|
||||
else
|
||||
return _toolTipText;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool StringListWidget::changedToolTip(Common::Point oldPos, Common::Point newPos) const
|
||||
{
|
||||
bool ch = getToolTipIndex(oldPos) != getToolTipIndex(newPos)
|
||||
&& getToolTip(oldPos) != getToolTip(newPos);
|
||||
|
||||
if(ch)
|
||||
cerr << "changed" << endl;
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void StringListWidget::drawWidget(bool hilite)
|
||||
{
|
||||
|
|
|
@ -32,10 +32,16 @@ class StringListWidget : public ListWidget
|
|||
void setList(const StringList& list);
|
||||
bool wantsFocus() const override { return true; }
|
||||
|
||||
string getToolTip(Common::Point pos) const override;
|
||||
bool changedToolTip(Common::Point oldPos, Common::Point newPos) const override;
|
||||
|
||||
protected:
|
||||
// display depends on _hasFocus so we have to redraw when focus changes
|
||||
void receivedFocusWidget() override { setDirty(); }
|
||||
void lostFocusWidget() override { setDirty(); }
|
||||
|
||||
bool hasToolTip() const override { return true; }
|
||||
|
||||
void drawWidget(bool hilite) override;
|
||||
Common::Rect getEditRect() const override;
|
||||
|
||||
|
@ -43,6 +49,9 @@ class StringListWidget : public ListWidget
|
|||
bool _hilite{false};
|
||||
int _textOfs{0};
|
||||
|
||||
private:
|
||||
int getToolTipIndex(Common::Point pos) const;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
StringListWidget() = delete;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "OSystem.hxx"
|
||||
#include "Dialog.hxx"
|
||||
#include "DialogContainer.hxx"
|
||||
#include "Font.hxx"
|
||||
#include "FrameBuffer.hxx"
|
||||
#include "FBSurface.hxx"
|
||||
|
@ -25,17 +26,8 @@
|
|||
#include "ToolTip.hxx"
|
||||
|
||||
// TODOs:
|
||||
// + keep enabled when mouse moves over same widget
|
||||
// + static position and text for normal widgets
|
||||
// + moving position and text over e.g. DataGridWidget
|
||||
// + reenable tip faster
|
||||
// + disable when in edit mode
|
||||
// - option to disable tips
|
||||
// - multi line tips
|
||||
// + nicer formating of DataDridWidget tip
|
||||
// + allow reversing ToogleWidget (TooglePixelWidget)
|
||||
// + shift checkbox bits
|
||||
// - RomListWidget (hex codes, maybe disassembly operands)
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
ToolTip::ToolTip(Dialog& dialog, const GUI::Font& font)
|
||||
|
@ -61,21 +53,48 @@ void ToolTip::setFont(const GUI::Font& font)
|
|||
mySurface = myDialog.instance().frameBuffer().allocateSurface(myWidth, myHeight);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void ToolTip::request()
|
||||
{
|
||||
// Called each frame when a tooltip is wanted
|
||||
if(myFocusWidget && myTimer < DELAY_TIME * RELEASE_SPEED)
|
||||
{
|
||||
const string tip = myFocusWidget->getToolTip(myMousePos);
|
||||
|
||||
if(!tip.empty())
|
||||
{
|
||||
myTipWidget = myFocusWidget;
|
||||
myTimer += RELEASE_SPEED;
|
||||
if(myTimer >= DELAY_TIME * RELEASE_SPEED)
|
||||
show(tip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void ToolTip::update(const Widget* widget, const Common::Point& pos)
|
||||
{
|
||||
// Called each mouse move
|
||||
myMousePos = pos;
|
||||
myFocusWidget = widget;
|
||||
|
||||
if(myTipWidget != widget)
|
||||
{
|
||||
myFocusWidget = widget;
|
||||
release();
|
||||
}
|
||||
if(myTipShown && myTipWidget->changedToolTip(myPos, pos))
|
||||
{
|
||||
myPos = pos;
|
||||
show();
|
||||
}
|
||||
release(false);
|
||||
|
||||
if(!myTipShown)
|
||||
release(true);
|
||||
else
|
||||
myPos = pos;
|
||||
{
|
||||
if(myTipWidget->changedToolTip(myTipPos, myMousePos))
|
||||
{
|
||||
const string tip = myTipWidget->getToolTip(myMousePos);
|
||||
|
||||
if(!tip.empty())
|
||||
show(tip);
|
||||
else
|
||||
release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -85,65 +104,40 @@ void ToolTip::hide()
|
|||
{
|
||||
myTimer = 0;
|
||||
myTipWidget = myFocusWidget = nullptr;
|
||||
|
||||
myTipShown = false;
|
||||
myDialog.setDirtyChain();
|
||||
myDialog.instance().frameBuffer().setPendingRender();
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void ToolTip::release()
|
||||
void ToolTip::release(bool emptyTip)
|
||||
{
|
||||
if(myTipShown)
|
||||
{
|
||||
myTimer = DELAY_TIME - 1;
|
||||
|
||||
myTipShown = false;
|
||||
myDialog.setDirtyChain();
|
||||
myDialog.instance().frameBuffer().setPendingRender();
|
||||
}
|
||||
|
||||
// After displaying a tip, slowly reset the timer to 0
|
||||
// until a new tip is requested
|
||||
if(myTipWidget != myFocusWidget && myTimer)
|
||||
if((emptyTip || myTipWidget != myFocusWidget) && myTimer)
|
||||
myTimer--;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void ToolTip::request()
|
||||
void ToolTip::show(const string& tip)
|
||||
{
|
||||
myTipWidget = myFocusWidget;
|
||||
|
||||
if(myTimer == DELAY_TIME)
|
||||
show();
|
||||
|
||||
myTimer++;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void ToolTip::show()
|
||||
{
|
||||
if(myTipWidget == nullptr)
|
||||
return;
|
||||
myTipPos = myMousePos;
|
||||
|
||||
const uInt32 V_GAP = 1;
|
||||
const uInt32 H_CURSOR = 18;
|
||||
string text = myTipWidget->getToolTip(myPos);
|
||||
|
||||
myTipShown = true;
|
||||
if(text.empty())
|
||||
{
|
||||
release();
|
||||
return;
|
||||
}
|
||||
|
||||
uInt32 width = std::min(myWidth, myFont->getStringWidth(text) + myTextXOfs * 2);
|
||||
//uInt32 height = std::min(myHeight, font.getFontHeight() + myTextYOfs * 2);
|
||||
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();
|
||||
// Limit position to app size and adjust accordingly
|
||||
const Int32 xAbs = myPos.x + dialogRect.x() / myScale;
|
||||
const uInt32 yAbs = myPos.y + dialogRect.y() / myScale;
|
||||
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
|
||||
|
@ -161,11 +155,11 @@ void ToolTip::show()
|
|||
|
||||
mySurface->frameRect(0, 0, width, myHeight, kColor);
|
||||
mySurface->fillRect(1, 1, width - 2, myHeight - 2, kWidColor);
|
||||
mySurface->drawString(*myFont, text, myTextXOfs, myTextYOfs,
|
||||
mySurface->drawString(*myFont, tip, myTextXOfs, myTextYOfs,
|
||||
width - myTextXOfs * 2, myHeight - myTextYOfs * 2, kTextColor);
|
||||
|
||||
myTipShown = true;
|
||||
myDialog.setDirtyChain();
|
||||
myDialog.instance().frameBuffer().setPendingRender();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -56,7 +56,7 @@ class ToolTip
|
|||
Hide a displayed tooltip and reset the timer slowly.
|
||||
This allows faster tip display of the next tip.
|
||||
*/
|
||||
void release();
|
||||
void release(bool emptyTip);
|
||||
|
||||
/**
|
||||
Update focused widget and current mouse position.
|
||||
|
@ -69,10 +69,13 @@ class ToolTip
|
|||
void render();
|
||||
|
||||
private:
|
||||
void show();
|
||||
void show(const string& tip);
|
||||
|
||||
private:
|
||||
static constexpr uInt32 DELAY_TIME = 45; // display delay
|
||||
static constexpr uInt32 DELAY_TIME = 45; // display delay [frames]
|
||||
// Tips are slower released than requested, so that repeated tips are shown
|
||||
// faster. This constant defines how much faster.
|
||||
static constexpr uInt32 RELEASE_SPEED = 2;
|
||||
|
||||
Dialog& myDialog;
|
||||
const GUI::Font* myFont{nullptr};
|
||||
|
@ -80,7 +83,8 @@ class ToolTip
|
|||
const Widget* myFocusWidget{nullptr};
|
||||
|
||||
uInt32 myTimer{0};
|
||||
Common::Point myPos;
|
||||
Common::Point myMousePos;
|
||||
Common::Point myTipPos;
|
||||
uInt32 myWidth{0};
|
||||
uInt32 myHeight{0};
|
||||
uInt32 myTextXOfs{0};
|
||||
|
|
|
@ -97,8 +97,8 @@ void Widget::draw()
|
|||
|
||||
if(isDirty())
|
||||
{
|
||||
//cerr << " *** draw widget " << typeid(*this).name() << " ***" << endl;
|
||||
cerr << "w";
|
||||
cerr << " *** draw widget " << typeid(*this).name() << " ***" << endl;
|
||||
//cerr << "w";
|
||||
|
||||
FBSurface& s = _boss->dialog().surface();
|
||||
|
||||
|
@ -418,6 +418,23 @@ void StaticTextWidget::setLabel(const string& label)
|
|||
setDirty();
|
||||
}
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void StaticTextWidget::handleMouseEntered()
|
||||
{
|
||||
if(isEnabled())
|
||||
// Mouse focus for tooltips must not change dirty status
|
||||
setFlags(Widget::FLAG_MOUSE_FOCUS, false);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void StaticTextWidget::handleMouseLeft()
|
||||
{
|
||||
if(isEnabled())
|
||||
// Mouse focus for tooltips must not change dirty status
|
||||
clearFlags(Widget::FLAG_MOUSE_FOCUS, false);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void StaticTextWidget::drawWidget(bool hilite)
|
||||
{
|
||||
|
|
|
@ -186,8 +186,8 @@ class StaticTextWidget : public Widget
|
|||
ColorId shadowColor = kNone);
|
||||
~StaticTextWidget() override = default;
|
||||
|
||||
void handleMouseEntered() override {}
|
||||
void handleMouseLeft() override {}
|
||||
void handleMouseEntered() override;
|
||||
void handleMouseLeft() override;
|
||||
|
||||
void setValue(int value);
|
||||
void setLabel(const string& label);
|
||||
|
|
Loading…
Reference in New Issue