diff --git a/src/debugger/gui/RomListWidget.cxx b/src/debugger/gui/RomListWidget.cxx index 0d0d6c3fa..861a4112d 100644 --- a/src/debugger/gui/RomListWidget.cxx +++ b/src/debugger/gui/RomListWidget.cxx @@ -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(stol(valStr, nullptr, 16)); - } ostringstream buf; diff --git a/src/gui/Dialog.cxx b/src/gui/Dialog.cxx index a33b4ffa1..1b58d7729 100644 --- a/src/gui/Dialog.cxx +++ b/src/gui/Dialog.cxx @@ -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(); diff --git a/src/gui/EditableWidget.cxx b/src/gui/EditableWidget.cxx index 39a4b519f..8ff411f83 100644 --- a/src/gui/EditableWidget.cxx +++ b/src/gui/EditableWidget.cxx @@ -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(); } } diff --git a/src/gui/EditableWidget.hxx b/src/gui/EditableWidget.hxx index 6e55cf541..497c2a4e8 100644 --- a/src/gui/EditableWidget.hxx +++ b/src/gui/EditableWidget.hxx @@ -125,6 +125,7 @@ class EditableWidget : public Widget, public CommandSender protected: int _editScrollOffset{0}; bool _editMode{true}; + int _dyCaret{0}; private: TextFilter _filter; diff --git a/src/gui/GuiObject.hxx b/src/gui/GuiObject.hxx index 490df4344..85424a670 100644 --- a/src/gui/GuiObject.hxx +++ b/src/gui/GuiObject.hxx @@ -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; } diff --git a/src/gui/StringListWidget.cxx b/src/gui/StringListWidget.cxx index ad5f1e7ec..74cf23e40 100644 --- a/src/gui/StringListWidget.cxx +++ b/src/gui/StringListWidget.cxx @@ -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) { diff --git a/src/gui/StringListWidget.hxx b/src/gui/StringListWidget.hxx index ed5874b91..fb96a9e22 100644 --- a/src/gui/StringListWidget.hxx +++ b/src/gui/StringListWidget.hxx @@ -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; diff --git a/src/gui/ToolTip.cxx b/src/gui/ToolTip.cxx index 77b5d5e20..5d37478db 100644 --- a/src/gui/ToolTip.cxx +++ b/src/gui/ToolTip.cxx @@ -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(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/ToolTip.hxx b/src/gui/ToolTip.hxx index 17ff2e7ec..2c9f95037 100644 --- a/src/gui/ToolTip.hxx +++ b/src/gui/ToolTip.hxx @@ -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}; diff --git a/src/gui/Widget.cxx b/src/gui/Widget.cxx index cfb933965..23c72760d 100644 --- a/src/gui/Widget.cxx +++ b/src/gui/Widget.cxx @@ -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) { diff --git a/src/gui/Widget.hxx b/src/gui/Widget.hxx index 45c676fe2..c5eb233b3 100644 --- a/src/gui/Widget.hxx +++ b/src/gui/Widget.hxx @@ -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);