diff --git a/Makefile b/Makefile index 168ae178d..87a2c9847 100644 --- a/Makefile +++ b/Makefile @@ -48,11 +48,11 @@ endif CXXFLAGS+= -Wall -Wextra -Wno-unused-parameter ifdef HAVE_GCC - CXXFLAGS+= -Wno-multichar -Wunused -fno-rtti -Woverloaded-virtual -Wnon-virtual-dtor -std=c++14 + CXXFLAGS+= -Wno-multichar -Wunused -Woverloaded-virtual -Wnon-virtual-dtor -std=c++14 endif ifdef HAVE_CLANG - CXXFLAGS+= -Wno-multichar -Wunused -fno-rtti -Woverloaded-virtual -Wnon-virtual-dtor -std=c++14 + CXXFLAGS+= -Wno-multichar -Wunused -Woverloaded-virtual -Wnon-virtual-dtor -std=c++14 endif ifdef CLANG_WARNINGS @@ -81,8 +81,8 @@ else endif ifdef RELEASE - CXXFLAGS += -flto - LDFLAGS += -flto + CXXFLAGS += -flto -fno-rtti + LDFLAGS += -flto -fno-rtti endif ####################################################################### diff --git a/src/common/EventHandlerSDL2.cxx b/src/common/EventHandlerSDL2.cxx index e5900cfba..86d4dad89 100644 --- a/src/common/EventHandlerSDL2.cxx +++ b/src/common/EventHandlerSDL2.cxx @@ -45,6 +45,8 @@ EventHandlerSDL2::EventHandlerSDL2(OSystem& osystem) } Logger::debug("EventHandlerSDL2::EventHandlerSDL2 SDL_INIT_JOYSTICK"); #endif + + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/FBSurfaceSDL2.cxx b/src/common/FBSurfaceSDL2.cxx index 543195165..7f0ddb7d7 100644 --- a/src/common/FBSurfaceSDL2.cxx +++ b/src/common/FBSurfaceSDL2.cxx @@ -104,41 +104,49 @@ const Common::Rect& FBSurfaceSDL2::dstRect() const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FBSurfaceSDL2::setSrcPos(uInt32 x, uInt32 y) { - if(x != static_cast(mySrcR.x) || y != static_cast(mySrcR.y)) - { - setSrcPosInternal(x, y); + if(setSrcPosInternal(x, y)) reinitializeBlitter(); - } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FBSurfaceSDL2::setSrcSize(uInt32 w, uInt32 h) { - if(w != static_cast(mySrcR.w) || h != static_cast(mySrcR.h)) - { - setSrcSizeInternal(w, h); + if(setSrcSizeInternal(w, h)) + reinitializeBlitter(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FBSurfaceSDL2::setSrcRect(const Common::Rect& r) +{ + const bool posChanged = setSrcPosInternal(r.x(), r.y()), + sizeChanged = setSrcSizeInternal(r.w(), r.h()); + + if(posChanged || sizeChanged) reinitializeBlitter(); - } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FBSurfaceSDL2::setDstPos(uInt32 x, uInt32 y) { - if(x != static_cast(myDstR.x) || y != static_cast(myDstR.y)) - { - setDstPosInternal(x, y); + if(setDstPosInternal(x, y)) reinitializeBlitter(); - } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FBSurfaceSDL2::setDstSize(uInt32 w, uInt32 h) { - if(w != static_cast(myDstR.w) || h != static_cast(myDstR.h)) - { - setDstSizeInternal(w, h); + if(setDstSizeInternal(w, h)) + reinitializeBlitter(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FBSurfaceSDL2::setDstRect(const Common::Rect& r) +{ + const bool posChanged = setDstPosInternal(r.x(), r.y()), + sizeChanged = setDstSizeInternal(r.w(), r.h()); + + if(posChanged || sizeChanged) reinitializeBlitter(); - } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -176,6 +184,22 @@ void FBSurfaceSDL2::invalidate() SDL_FillRect(mySurface, nullptr, 0); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FBSurfaceSDL2::invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) +{ + ASSERT_MAIN_THREAD; + + // Clear the rectangle + SDL_Rect tmp; + tmp.x = x; + tmp.y = y; + tmp.w = w; + tmp.h = h; + // Note: Transparency has to be 0 to clear the rectangle foreground + // without affecting the background display. + SDL_FillRect(mySurface, &tmp, 0); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FBSurfaceSDL2::free() { diff --git a/src/common/FBSurfaceSDL2.hxx b/src/common/FBSurfaceSDL2.hxx index f446bb17b..d8bfa0029 100644 --- a/src/common/FBSurfaceSDL2.hxx +++ b/src/common/FBSurfaceSDL2.hxx @@ -48,13 +48,18 @@ class FBSurfaceSDL2 : public FBSurface const Common::Rect& dstRect() const override; void setSrcPos(uInt32 x, uInt32 y) override; void setSrcSize(uInt32 w, uInt32 h) override; + void setSrcRect(const Common::Rect& r) override; void setDstPos(uInt32 x, uInt32 y) override; void setDstSize(uInt32 w, uInt32 h) override; + void setDstRect(const Common::Rect& r) override; + void setVisible(bool visible) override; void translateCoords(Int32& x, Int32& y) const override; bool render() override; void invalidate() override; + void invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) override; + void free() override; void reload() override; void resize(uInt32 width, uInt32 height) override; @@ -65,21 +70,41 @@ class FBSurfaceSDL2 : public FBSurface void applyAttributes() override; private: - inline void setSrcPosInternal(uInt32 x, uInt32 y) { - mySrcR.x = x; mySrcR.y = y; - mySrcGUIR.moveTo(x, y); + inline bool setSrcPosInternal(uInt32 x, uInt32 y) { + if(x != static_cast(mySrcR.x) || y != static_cast(mySrcR.y)) + { + mySrcR.x = x; mySrcR.y = y; + mySrcGUIR.moveTo(x, y); + return true; + } + return false; } - inline void setSrcSizeInternal(uInt32 w, uInt32 h) { - mySrcR.w = w; mySrcR.h = h; - mySrcGUIR.setWidth(w); mySrcGUIR.setHeight(h); + inline bool setSrcSizeInternal(uInt32 w, uInt32 h) { + if(w != static_cast(mySrcR.w) || h != static_cast(mySrcR.h)) + { + mySrcR.w = w; mySrcR.h = h; + mySrcGUIR.setWidth(w); mySrcGUIR.setHeight(h); + return true; + } + return false; } - inline void setDstPosInternal(uInt32 x, uInt32 y) { - myDstR.x = x; myDstR.y = y; - myDstGUIR.moveTo(x, y); + inline bool setDstPosInternal(uInt32 x, uInt32 y) { + if(x != static_cast(myDstR.x) || y != static_cast(myDstR.y)) + { + myDstR.x = x; myDstR.y = y; + myDstGUIR.moveTo(x, y); + return true; + } + return false; } - inline void setDstSizeInternal(uInt32 w, uInt32 h) { - myDstR.w = w; myDstR.h = h; - myDstGUIR.setWidth(w); myDstGUIR.setHeight(h); + inline bool setDstSizeInternal(uInt32 w, uInt32 h) { + if(w != static_cast(myDstR.w) || h != static_cast(myDstR.h)) + { + myDstR.w = w; myDstR.h = h; + myDstGUIR.setWidth(w); myDstGUIR.setHeight(h); + return true; + } + return false; } void createSurface(uInt32 width, uInt32 height, const uInt32* data); @@ -101,7 +126,7 @@ class FBSurfaceSDL2 : public FBSurface {ScalingInterpolation::none}; SDL_Surface* mySurface{nullptr}; - SDL_Rect mySrcR{0, 0, 0, 0}, myDstR{0, 0, 0, 0}; + SDL_Rect mySrcR{-1, -1, -1, -1}, myDstR{-1, -1, -1, -1}; bool myIsVisible{true}; bool myIsStatic{false}; diff --git a/src/common/PNGLibrary.cxx b/src/common/PNGLibrary.cxx index 901d42478..2a6f84d6a 100644 --- a/src/common/PNGLibrary.cxx +++ b/src/common/PNGLibrary.cxx @@ -267,7 +267,7 @@ void PNGLibrary::toggleContinuousSnapshots(bool perFrame) buf << "Enabling snapshots in " << interval << " second intervals"; interval *= uInt32(myOSystem.frameRate()); } - myOSystem.frameBuffer().showMessage(buf.str()); + myOSystem.frameBuffer().showTextMessage(buf.str()); setContinuousSnapInterval(interval); } else @@ -276,7 +276,7 @@ void PNGLibrary::toggleContinuousSnapshots(bool perFrame) buf << "Disabling snapshots, generated " << (mySnapCounter / mySnapInterval) << " files"; - myOSystem.frameBuffer().showMessage(buf.str()); + myOSystem.frameBuffer().showTextMessage(buf.str()); setContinuousSnapInterval(0); } } @@ -378,7 +378,7 @@ void PNGLibrary::takeSnapshot(uInt32 number) // Re-enable old messages myOSystem.frameBuffer().enableMessages(true); } - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/PaletteHandler.cxx b/src/common/PaletteHandler.cxx index db6961af8..f13c18ab1 100644 --- a/src/common/PaletteHandler.cxx +++ b/src/common/PaletteHandler.cxx @@ -69,7 +69,7 @@ void PaletteHandler::cyclePalette(int direction) const string palette = toPaletteName(PaletteType(type)); const string message = MESSAGES[type] + " palette"; - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); setPalette(palette); } @@ -112,7 +112,7 @@ void PaletteHandler::showAdjustableMessage() const float value = myOSystem.console().timing() == ConsoleTiming::pal ? myPhasePAL : myPhaseNTSC; buf << std::fixed << std::setprecision(1) << value << DEGREE; - myOSystem.frameBuffer().showMessage( + myOSystem.frameBuffer().showGaugeMessage( "Palette phase shift", buf.str(), value, (isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT) - MAX_PHASE_SHIFT, (isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT) + MAX_PHASE_SHIFT); @@ -122,7 +122,7 @@ void PaletteHandler::showAdjustableMessage() const float value = *myAdjustables[myCurrentAdjustable].value; buf << std::fixed << std::setprecision(1) << value << DEGREE; - myOSystem.frameBuffer().showMessage( + myOSystem.frameBuffer().showGaugeMessage( msg.str(), buf.str(), value, -MAX_RGB_SHIFT, +MAX_RGB_SHIFT); } else @@ -131,7 +131,7 @@ void PaletteHandler::showAdjustableMessage() ? scaleRGBTo100(*myAdjustables[myCurrentAdjustable].value) : scaleTo100(*myAdjustables[myCurrentAdjustable].value); buf << value << "%"; - myOSystem.frameBuffer().showMessage( + myOSystem.frameBuffer().showGaugeMessage( msg.str(), buf.str(), value); } } diff --git a/src/common/Rect.hxx b/src/common/Rect.hxx index faf6677b6..708de9d86 100644 --- a/src/common/Rect.hxx +++ b/src/common/Rect.hxx @@ -44,8 +44,8 @@ struct Point if(c != 'x') x = y = 0; } - bool operator==(const Point & p) const { return x == p.x && y == p.y; } - bool operator!=(const Point & p) const { return x != p.x || y != p.y; } + bool operator==(const Point& p) const { return x == p.x && y == p.y; } + bool operator!=(const Point& p) const { return !(*this == p); } friend ostream& operator<<(ostream& os, const Point& p) { os << p.x << "x" << p.y; @@ -75,11 +75,11 @@ struct Size } bool operator==(const Size& s) const { return w == s.w && h == s.h; } - bool operator!=(const Size& s) const { return w != s.w || h != s.h; } - bool operator<(const Size& s) const { return w < s.w && h < s.h; } - bool operator<=(const Size& s) const { return w <= s.w && h <= s.h; } - bool operator>(const Size& s) const { return w > s.w || h > s.h; } - bool operator>=(const Size& s) const { return w >= s.w || h >= s.h; } + bool operator< (const Size& s) const { return w < s.w && h < s.h; } + bool operator> (const Size& s) const { return w > s.w || h > s.h; } + bool operator!=(const Size& s) const { return !(*this == s); } + bool operator<=(const Size& s) const { return !(*this > s); } + bool operator>=(const Size& s) const { return !(*this < s); } friend ostream& operator<<(ostream& os, const Size& s) { os << s.w << "x" << s.h; @@ -175,6 +175,11 @@ struct Rect return r.left != x || r.top != y; } + bool operator==(const Rect& r) const { + return top == r.top && left == r.left && bottom == r.bottom && right == r.right; + } + bool operator!=(const Rect& r) const { return !(*this == r); } + friend ostream& operator<<(ostream& os, const Rect& r) { os << r.point() << "," << r.size(); return os; diff --git a/src/common/RewindManager.cxx b/src/common/RewindManager.cxx index bc6b70813..044bc57e9 100644 --- a/src/common/RewindManager.cxx +++ b/src/common/RewindManager.cxx @@ -181,7 +181,7 @@ uInt32 RewindManager::rewindStates(uInt32 numStates) if(myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE && myOSystem.eventHandler().state() != EventHandlerState::PLAYBACK) - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); return i; } @@ -216,7 +216,7 @@ uInt32 RewindManager::unwindStates(uInt32 numStates) if(myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE && myOSystem.eventHandler().state() != EventHandlerState::PLAYBACK) - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); return i; } diff --git a/src/common/SoundSDL2.cxx b/src/common/SoundSDL2.cxx index 6caabc72e..40b95b0f0 100644 --- a/src/common/SoundSDL2.cxx +++ b/src/common/SoundSDL2.cxx @@ -224,7 +224,7 @@ bool SoundSDL2::toggleMute() string message = "Sound "; message += enabled ? "unmuted" : "muted"; - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); //ostringstream strval; //uInt32 volume; @@ -282,7 +282,7 @@ void SoundSDL2::adjustVolume(int direction) strval << percent << "%"; else strval << "Off"; - myOSystem.frameBuffer().showMessage("Volume", strval.str(), percent); + myOSystem.frameBuffer().showGaugeMessage("Volume", strval.str(), percent); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/StateManager.cxx b/src/common/StateManager.cxx index a4add0889..956f62409 100644 --- a/src/common/StateManager.cxx +++ b/src/common/StateManager.cxx @@ -132,9 +132,9 @@ void StateManager::toggleTimeMachine() myActiveMode = myActiveMode == Mode::TimeMachine ? Mode::Off : Mode::TimeMachine; if(myActiveMode == Mode::TimeMachine) - myOSystem.frameBuffer().showMessage("Time Machine enabled"); + myOSystem.frameBuffer().showTextMessage("Time Machine enabled"); else - myOSystem.frameBuffer().showMessage("Time Machine disabled"); + myOSystem.frameBuffer().showTextMessage("Time Machine disabled"); myOSystem.settings().setValue(devSettings ? "dev.timemachine" : "plr.timemachine", myActiveMode == Mode::TimeMachine); } @@ -215,7 +215,7 @@ void StateManager::loadState(int slot) { buf.str(""); buf << "Can't open/load from state file " << slot; - myOSystem.frameBuffer().showMessage(buf.str()); + myOSystem.frameBuffer().showTextMessage(buf.str()); return; } @@ -239,7 +239,7 @@ void StateManager::loadState(int slot) buf << "Invalid data in state " << slot << " file"; } - myOSystem.frameBuffer().showMessage(buf.str()); + myOSystem.frameBuffer().showTextMessage(buf.str()); } } @@ -261,7 +261,7 @@ void StateManager::saveState(int slot) { buf.str(""); buf << "Can't open/save to state file " << slot; - myOSystem.frameBuffer().showMessage(buf.str()); + myOSystem.frameBuffer().showTextMessage(buf.str()); return; } @@ -274,7 +274,7 @@ void StateManager::saveState(int slot) catch(...) { buf << "Error saving state " << slot; - myOSystem.frameBuffer().showMessage(buf.str()); + myOSystem.frameBuffer().showTextMessage(buf.str()); return; } @@ -292,7 +292,7 @@ void StateManager::saveState(int slot) else buf << "Error saving state " << slot; - myOSystem.frameBuffer().showMessage(buf.str()); + myOSystem.frameBuffer().showTextMessage(buf.str()); } } @@ -307,7 +307,7 @@ void StateManager::changeState(int direction) buf << "Changed to state slot " << myCurrentSlot; else buf << "State slot " << myCurrentSlot; - myOSystem.frameBuffer().showMessage(buf.str()); + myOSystem.frameBuffer().showTextMessage(buf.str()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -318,7 +318,7 @@ void StateManager::toggleAutoSlot() // Print appropriate message ostringstream buf; buf << "Automatic slot change " << (autoSlot ? "enabled" : "disabled"); - myOSystem.frameBuffer().showMessage(buf.str()); + myOSystem.frameBuffer().showTextMessage(buf.str()); myOSystem.settings().setValue("autoslot", autoSlot); } diff --git a/src/debugger/gui/Cart3EWidget.cxx b/src/debugger/gui/Cart3EWidget.cxx index 910f03368..9e0822b51 100644 --- a/src/debugger/gui/Cart3EWidget.cxx +++ b/src/debugger/gui/Cart3EWidget.cxx @@ -117,7 +117,7 @@ void Cartridge3EWidget::loadConfig() myBankWidgets[1]->setSelectedIndex(bank - myCart.romBankCount(), oldBank != bank); } - CartDebugWidget::loadConfig(); + CartDebugWidget::loadConfig(); // Intentionally calling grand-parent method } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/debugger/gui/CartRamWidget.hxx b/src/debugger/gui/CartRamWidget.hxx index bc0f4f810..d24a5d134 100644 --- a/src/debugger/gui/CartRamWidget.hxx +++ b/src/debugger/gui/CartRamWidget.hxx @@ -67,11 +67,11 @@ class CartRamWidget : public Widget, public CommandSender int x, int y, int w, int h, CartDebugWidget& cartDebug); ~InternalRamWidget() override = default; + string getLabel(int addr) const override; private: uInt8 getValue(int addr) const override; void setValue(int addr, uInt8 value) override; - string getLabel(int addr) const override; void fillList(uInt32 start, uInt32 size, IntArray& alist, IntArray& vlist, BoolArray& changed) const override; diff --git a/src/debugger/gui/CpuWidget.cxx b/src/debugger/gui/CpuWidget.cxx index 31f939f01..a8719f448 100644 --- a/src/debugger/gui/CpuWidget.cxx +++ b/src/debugger/gui/CpuWidget.cxx @@ -85,18 +85,19 @@ CpuWidget::CpuWidget(GuiObject* boss, const GUI::Font& lfont, const GUI::Font& n _w = lwidth + myPCGrid->getWidth() + myPCLabel->getWidth() + 20; // Create labels showing the source of data for SP/A/X/Y registers + const std::array labels = { "SP", "A", "X", "Y" }; xpos += myCpuGridBinValue->getWidth() + 20; int src_y = ypos, src_w = (max_w - xpos + x) - 10; for(int i = 0; i < 4; ++i) { myCpuDataSrc[i] = new EditTextWidget(boss, nfont, xpos, src_y, src_w, fontHeight + 1); + myCpuDataSrc[i]->setToolTip("Source label of last load into " + labels[i] + "."); myCpuDataSrc[i]->setEditable(false, true); src_y += fontHeight + 2; } // Add labels for other CPU registers xpos = x; - const std::array labels = { "SP ", "A ", "X ", "Y " }; for(int row = 0; row < 4; ++row) { new StaticTextWidget(boss, lfont, xpos, ypos + row*lineHeight + 2, @@ -109,10 +110,10 @@ CpuWidget::CpuWidget(GuiObject* boss, const GUI::Font& lfont, const GUI::Font& n { new StaticTextWidget(boss, lfont, myCpuGridDecValue->getLeft() - fontWidth, ypos + row * lineHeight + 2, - lwidth - 2, fontHeight, "#"); + fontWidth, fontHeight, "#"); new StaticTextWidget(boss, lfont, myCpuGridBinValue->getLeft() - fontWidth, ypos + row * lineHeight + 2, - lwidth - 2, fontHeight, "%"); + fontWidth, fontHeight, "%"); } // Create a bitfield widget for changing the processor status @@ -139,10 +140,9 @@ CpuWidget::CpuWidget(GuiObject* boss, const GUI::Font& lfont, const GUI::Font& n xpos = myCpuDataSrc[0]->getLeft(); new StaticTextWidget(boss, lfont, xpos - fontWidth * 4.5, ypos + 2, "Dest"); myCpuDataDest = new EditTextWidget(boss, nfont, xpos, ypos, src_w, fontHeight + 1); + myCpuDataDest->setToolTip("Destination label of last store."); myCpuDataDest->setEditable(false, true); - - _h = ypos + myPSRegister->getHeight() - y; } diff --git a/src/debugger/gui/DataGridRamWidget.cxx b/src/debugger/gui/DataGridRamWidget.cxx new file mode 100644 index 000000000..9be4c4f14 --- /dev/null +++ b/src/debugger/gui/DataGridRamWidget.cxx @@ -0,0 +1,54 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#include "RamWidget.hxx" +#include "DataGridRamWidget.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +DataGridRamWidget::DataGridRamWidget(GuiObject* boss, const RamWidget& ram, + const GUI::Font& font, + int x, int y, int cols, int rows, + int colchars, int bits, + Common::Base::Fmt base, + bool useScrollbar) + : DataGridWidget(boss, font, x, y, cols, rows, colchars, + bits, base, useScrollbar), + _ram(ram) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string DataGridRamWidget::getToolTip(const Common::Point& pos) const +{ + const int idx = getToolTipIndex(pos); + + if(idx < 0) + return EmptyString; + + const Int32 addr = _addrList[idx]; + const string label = _ram.getLabel(addr); + const string tip = DataGridWidget::getToolTip(pos); + + if(label.empty()) + return tip; + + ostringstream buf; + + buf << _ram.getLabel(addr) << '\n' << tip; + + return buf.str(); +} diff --git a/src/debugger/gui/DataGridRamWidget.hxx b/src/debugger/gui/DataGridRamWidget.hxx new file mode 100644 index 000000000..451c23403 --- /dev/null +++ b/src/debugger/gui/DataGridRamWidget.hxx @@ -0,0 +1,51 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef DATA_GRID_RAM_WIDGET_HXX +#define DATA_GRID_RAM_WIDGET_HXX + +class RamWidget; + +#include "DataGridWidget.hxx" +#include "Base.hxx" + +class DataGridRamWidget : public DataGridWidget +{ + public: + DataGridRamWidget(GuiObject* boss, const RamWidget& ram, + const GUI::Font& font, + int x, int y, int cols, int rows, + int colchars, int bits, + Common::Base::Fmt format = Common::Base::Fmt::_DEFAULT, + bool useScrollbar = false); + ~DataGridRamWidget() override = default; + + string getToolTip(const Common::Point& pos) const override; + + private: + const RamWidget& _ram; + + private: + // Following constructors and assignment operators not supported + DataGridRamWidget() = delete; + DataGridRamWidget(const DataGridRamWidget&) = delete; + DataGridRamWidget(DataGridRamWidget&&) = delete; + DataGridRamWidget& operator=(const DataGridRamWidget&) = delete; + DataGridRamWidget& operator=(DataGridRamWidget&&) = delete; +}; + +#endif diff --git a/src/debugger/gui/DataGridWidget.cxx b/src/debugger/gui/DataGridWidget.cxx index f3b81676c..5d0dc0623 100644 --- a/src/debugger/gui/DataGridWidget.cxx +++ b/src/debugger/gui/DataGridWidget.cxx @@ -17,6 +17,7 @@ #include "Widget.hxx" #include "Dialog.hxx" +#include "ToolTip.hxx" #include "Font.hxx" #include "OSystem.hxx" #include "Debugger.hxx" @@ -45,6 +46,7 @@ DataGridWidget::DataGridWidget(GuiObject* boss, const GUI::Font& font, _base(base) { _flags = Widget::FLAG_ENABLED | Widget::FLAG_RETAIN_FOCUS | Widget::FLAG_WANTS_RAWDATA; + _editMode = false; // Make sure all lists contain some default values _hiliteList.clear(); @@ -239,20 +241,6 @@ void DataGridWidget::setRange(int lower, int upper) _upperBound = std::min(1 << _bits, upper); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void DataGridWidget::handleMouseEntered() -{ - setFlags(Widget::FLAG_HILITED); - setDirty(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void DataGridWidget::handleMouseLeft() -{ - clearFlags(Widget::FLAG_HILITED); - setDirty(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void DataGridWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount) { @@ -307,6 +295,7 @@ void DataGridWidget::handleMouseWheel(int x, int y, int direction) else if(direction < 0) incrementCell(); } + dialog().tooltip().hide(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -504,6 +493,7 @@ bool DataGridWidget::handleKeyDown(StellaKey key, StellaMod mod) sendCommand(DataGridWidget::kSelectionChangedCmd, _selectedItem, _id); setDirty(); + dialog().tooltip().hide(); } _currentKeyDown = key; @@ -583,6 +573,49 @@ void DataGridWidget::handleCommand(CommandSender* sender, int cmd, } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int DataGridWidget::getToolTipIndex(const Common::Point& pos) const +{ + const int col = (pos.x - getAbsX()) / _colWidth; + const int row = (pos.y - getAbsY()) / _rowHeight; + + if(row >= 0 && row < _rows && col >= 0 && col < _cols) + return row * _cols + col; + else + return -1; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string DataGridWidget::getToolTip(const Common::Point& pos) const +{ + const int idx = getToolTipIndex(pos); + + if(idx < 0) + return EmptyString; + + const Int32 val = _valueList[idx]; + ostringstream buf; + + buf << _toolTipText + << "$" << Common::Base::toString(val, Common::Base::Fmt::_16) + << " = #" << val; + if(val < 0x100) + { + if(val >= 0x80) + buf << '/' << -(0x100 - val); + buf << " = %" << Common::Base::toString(val, Common::Base::Fmt::_2); + } + + return buf.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool DataGridWidget::changedToolTip(const Common::Point& oldPos, + const Common::Point& newPos) const +{ + return getToolTipIndex(oldPos) != getToolTipIndex(newPos); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void DataGridWidget::drawWidget(bool hilite) { diff --git a/src/debugger/gui/DataGridWidget.hxx b/src/debugger/gui/DataGridWidget.hxx index 114097b01..848993311 100644 --- a/src/debugger/gui/DataGridWidget.hxx +++ b/src/debugger/gui/DataGridWidget.hxx @@ -84,6 +84,9 @@ class DataGridWidget : public EditableWidget void setCrossed(bool enable) { _crossGrid = enable; } + string getToolTip(const Common::Point& pos) const override; + bool changedToolTip(const Common::Point& oldPos, const Common::Point& newPos) const override; + protected: void drawWidget(bool hilite) override; @@ -98,11 +101,12 @@ class DataGridWidget : public EditableWidget void receivedFocusWidget() override; void lostFocusWidget() override; + bool hasToolTip() const override { return true; } + int getToolTipIndex(const Common::Point& pos) const; + void handleMouseDown(int x, int y, MouseButton b, int clickCount) override; void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; void handleMouseWheel(int x, int y, int direction) override; - void handleMouseEntered() override; - void handleMouseLeft() override; bool handleText(char text) override; bool handleKeyDown(StellaKey key, StellaMod mod) override; bool handleKeyUp(StellaKey key, StellaMod mod) override; @@ -128,7 +132,6 @@ class DataGridWidget : public EditableWidget BoolArray _changedList; BoolArray _hiliteList; - bool _editMode{false}; int _selectedItem{0}; StellaKey _currentKeyDown{KBDK_UNKNOWN}; string _backupString; @@ -148,6 +151,7 @@ class DataGridWidget : public EditableWidget void enableEditMode(bool state) { _editMode = state; } + private: // Following constructors and assignment operators not supported DataGridWidget() = delete; diff --git a/src/debugger/gui/DebuggerDialog.cxx b/src/debugger/gui/DebuggerDialog.cxx index b8c0662ee..07e530ac6 100644 --- a/src/debugger/gui/DebuggerDialog.cxx +++ b/src/debugger/gui/DebuggerDialog.cxx @@ -18,6 +18,7 @@ #include "Cart.hxx" #include "Widget.hxx" #include "Dialog.hxx" +#include "ToolTip.hxx" #include "Settings.hxx" #include "StellaKeys.hxx" #include "EventHandler.hxx" @@ -406,6 +407,7 @@ void DebuggerDialog::createFont() break; } } + tooltip().setFont(*myNFont); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/debugger/gui/DebuggerDialog.hxx b/src/debugger/gui/DebuggerDialog.hxx index 0affa5160..51633c825 100644 --- a/src/debugger/gui/DebuggerDialog.hxx +++ b/src/debugger/gui/DebuggerDialog.hxx @@ -76,7 +76,7 @@ class DebuggerDialog : public Dialog void saveConfig() override; private: - void center() override { positionAt(0); } + void setPosition() override { positionAt(0); } void loadConfig() override; void handleKeyDown(StellaKey key, StellaMod mod, bool repeated) override; void handleCommand(CommandSender* sender, int cmd, int data, int id) override; diff --git a/src/debugger/gui/RamWidget.cxx b/src/debugger/gui/RamWidget.cxx index 77456f810..7f651910a 100644 --- a/src/debugger/gui/RamWidget.cxx +++ b/src/debugger/gui/RamWidget.cxx @@ -15,7 +15,7 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "DataGridWidget.hxx" +#include "DataGridRamWidget.hxx" #include "EditTextWidget.hxx" #include "GuiObject.hxx" #include "InputTextDialog.hxx" @@ -54,8 +54,8 @@ RamWidget::RamWidget(GuiObject* boss, const GUI::Font& lfont, const GUI::Font& n // Add RAM grid (with scrollbar) int xpos = x + _font.getStringWidth("xxxx"); bool useScrollbar = ramsize / numrows > 16; - myRamGrid = new DataGridWidget(_boss, _nfont, xpos, ypos, - 16, myNumRows, 2, 8, Common::Base::Fmt::_16, useScrollbar); + myRamGrid = new DataGridRamWidget(_boss, *this, _nfont, xpos, ypos, + 16, myNumRows, 2, 8, Common::Base::Fmt::_16, useScrollbar); myRamGrid->setTarget(this); myRamGrid->setID(kRamGridID); addFocusWidget(myRamGrid); diff --git a/src/debugger/gui/RamWidget.hxx b/src/debugger/gui/RamWidget.hxx index fbc7747e6..069086fef 100644 --- a/src/debugger/gui/RamWidget.hxx +++ b/src/debugger/gui/RamWidget.hxx @@ -22,6 +22,7 @@ class GuiObject; class ButtonWidget; class DataGridWidget; class DataGridOpsWidget; +class DataGridRamWidget; class EditTextWidget; class StaticTextWidget; class InputTextDialog; @@ -41,11 +42,12 @@ class RamWidget : public Widget, public CommandSender void setOpsWidget(DataGridOpsWidget* w); void handleCommand(CommandSender* sender, int cmd, int data, int id) override; + virtual string getLabel(int addr) const = 0; + private: // To be implemented by derived classes virtual uInt8 getValue(int addr) const = 0; virtual void setValue(int addr, uInt8 value) = 0; - virtual string getLabel(int addr) const = 0; virtual void fillList(uInt32 start, uInt32 size, IntArray& alist, IntArray& vlist, @@ -97,7 +99,7 @@ class RamWidget : public Widget, public CommandSender StaticTextWidget* myRamStart{nullptr}; std::array myRamLabels{nullptr}; - DataGridWidget* myRamGrid{nullptr}; + DataGridRamWidget* myRamGrid{nullptr}; DataGridWidget* myHexValue{nullptr}; DataGridWidget* myDecValue{nullptr}; DataGridWidget* myBinValue{nullptr}; diff --git a/src/debugger/gui/RiotRamWidget.hxx b/src/debugger/gui/RiotRamWidget.hxx index 53d006bb0..3e30fc3e0 100644 --- a/src/debugger/gui/RiotRamWidget.hxx +++ b/src/debugger/gui/RiotRamWidget.hxx @@ -36,10 +36,11 @@ class RiotRamWidget : public RamWidget int x, int y, int w); ~RiotRamWidget() override = default; + string getLabel(int addr) const override; + private: uInt8 getValue(int addr) const override; void setValue(int addr, uInt8 value) override; - string getLabel(int addr) const override; void fillList(uInt32 start, uInt32 size, IntArray& alist, IntArray& vlist, BoolArray& changed) const override; diff --git a/src/debugger/gui/RiotWidget.cxx b/src/debugger/gui/RiotWidget.cxx index 73a70391f..053ff1325 100644 --- a/src/debugger/gui/RiotWidget.cxx +++ b/src/debugger/gui/RiotWidget.cxx @@ -66,11 +66,13 @@ RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont, on.push_back("1"); } + StringList labels; + #define CREATE_IO_REGS(desc, bits, bitsID, editable) \ t = new StaticTextWidget(boss, lfont, xpos, ypos+2, lwidth, fontHeight,\ - desc, TextAlign::Left); \ + desc); \ xpos += t->getWidth() + 5; \ - bits = new ToggleBitWidget(boss, nfont, xpos, ypos, 8, 1); \ + bits = new ToggleBitWidget(boss, nfont, xpos, ypos, 8, 1, 1, labels); \ bits->setTarget(this); \ bits->setID(bitsID); \ if(editable) addFocusWidget(bits); else bits->setEditable(false); \ @@ -78,6 +80,7 @@ RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont, bits->setList(off, on); // SWCHA bits in 'poke' mode + labels.clear(); CREATE_IO_REGS("SWCHA(W)", mySWCHAWriteBits, kSWCHABitsID, true) col = xpos + 20; // remember this for adding widgets to the second column @@ -87,10 +90,20 @@ RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont, // SWCHA bits in 'peek' mode xpos = 10; ypos += lineHeight + 5; + labels.clear(); + labels.push_back("P0 right"); + labels.push_back("P0 left"); + labels.push_back("P0 down"); + labels.push_back("P0 up"); + labels.push_back("P1 right"); + labels.push_back("P1 left"); + labels.push_back("P1 down"); + labels.push_back("P1 up"); CREATE_IO_REGS("SWCHA(R)", mySWCHAReadBits, kSWCHARBitsID, true) // SWCHB bits in 'poke' mode xpos = 10; ypos += 2 * lineHeight; + labels.clear(); CREATE_IO_REGS("SWCHB(W)", mySWCHBWriteBits, kSWCHBBitsID, true) // SWBCNT bits @@ -99,6 +112,15 @@ RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont, // SWCHB bits in 'peek' mode xpos = 10; ypos += lineHeight + 5; + labels.clear(); + labels.push_back("P1 difficulty"); + labels.push_back("P0 difficulty"); + labels.push_back(""); + labels.push_back(""); + labels.push_back("Color/B+W"); + labels.push_back(""); + labels.push_back("Select"); + labels.push_back("Reset"); CREATE_IO_REGS("SWCHB(R)", mySWCHBReadBits, kSWCHBRBitsID, true) // Timer registers (R/W) diff --git a/src/debugger/gui/RomListSettings.cxx b/src/debugger/gui/RomListSettings.cxx index 9c3baf3d2..93b555631 100644 --- a/src/debugger/gui/RomListSettings.cxx +++ b/src/debugger/gui/RomListSettings.cxx @@ -100,7 +100,7 @@ void RomListSettings::show(uInt32 x, uInt32 y, const Common::Rect& bossRect, int } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void RomListSettings::center() +void RomListSettings::setPosition() { // First set position according to original coordinates surface().setDstPos(_xorig, _yorig); diff --git a/src/debugger/gui/RomListSettings.hxx b/src/debugger/gui/RomListSettings.hxx index 0845dd8ab..a589b0334 100644 --- a/src/debugger/gui/RomListSettings.hxx +++ b/src/debugger/gui/RomListSettings.hxx @@ -38,8 +38,8 @@ class RomListSettings : public Dialog, public CommandSender ('data' will be the currently selected line number in RomListWidget) */ void show(uInt32 x, uInt32 y, const Common::Rect& bossRect, int data = -1); - /** This dialog uses its own positioning, so we override Dialog::center() */ - void center() override; + /** This dialog uses its own positioning, so we override Dialog::setPosition() */ + void setPosition() override; private: uInt32 _xorig{0}, _yorig{0}; diff --git a/src/debugger/gui/RomListWidget.cxx b/src/debugger/gui/RomListWidget.cxx index 48885f734..d732cd328 100644 --- a/src/debugger/gui/RomListWidget.cxx +++ b/src/debugger/gui/RomListWidget.cxx @@ -20,6 +20,8 @@ #include "Debugger.hxx" #include "DiStella.hxx" #include "Widget.hxx" +#include "Dialog.hxx" +#include "ToolTip.hxx" #include "StellaKeys.hxx" #include "FBSurface.hxx" #include "Font.hxx" @@ -39,6 +41,9 @@ RomListWidget::RomListWidget(GuiObject* boss, const GUI::Font& lfont, _textcolor = kTextColor; _textcolorhi = kTextColor; + _editMode = false; + _dyText = -1; // fixes the vertical position of selected text + _cols = w / _fontWidth; _rows = h / _lineHeight; @@ -242,6 +247,7 @@ void RomListWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount) // Set selected and add menu at current x,y mouse location _selectedItem = findItem(x, y); scrollToSelected(); + dialog().tooltip().hide(); myMenu->show(x + getAbsX(), y + getAbsY(), dialog().surface().dstRect(), _selectedItem); } @@ -282,20 +288,6 @@ void RomListWidget::handleMouseWheel(int x, int y, int direction) myScrollBar->handleMouseWheel(x, y, direction); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void RomListWidget::handleMouseEntered() -{ - setFlags(Widget::FLAG_HILITED); - setDirty(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void RomListWidget::handleMouseLeft() -{ - clearFlags(Widget::FLAG_HILITED); - setDirty(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool RomListWidget::handleText(char text) { @@ -454,6 +446,79 @@ void RomListWidget::lostFocusWidget() abortEditMode(); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Common::Point RomListWidget::getToolTipIndex(const Common::Point& pos) const +{ + const Common::Rect& r = getEditRect(); + const int col = (pos.x - r.x() - getAbsX()) / _font.getMaxCharWidth(); + const int row = (pos.y - getAbsY()) / _lineHeight; + + if(col < 0 || col >= 8 + || row < 0 || row + _currentPos >= int(myDisasm->list.size())) + return Common::Point(-1, -1); + else + return Common::Point(col, row + _currentPos); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string RomListWidget::getToolTip(const Common::Point& pos) const +{ + const Common::Point& idx = getToolTipIndex(pos); + + if(idx.y < 0) + return EmptyString; + + const string bytes = myDisasm->list[idx.y].bytes; + + if(static_cast(bytes.length()) < idx.x + 1) + return EmptyString; + + Int32 val; + if(bytes.length() == 8 && bytes[2] != ' ') + { + // Binary value + val = static_cast(stol(bytes, nullptr, 2)); + } + else + { + // 1..3 hex values + if(idx.x == 2) + // Skip gap after first byte + return EmptyString; + + string valStr; + + if(idx.x < 2 || bytes.length() < 8) + // 1 or 2 hex bytes, get one hex byte + valStr = bytes.substr((idx.x / 3) * 3, 2); + else + // 3 hex bytes, get two rightmost hex bytes + valStr = bytes.substr(6, 2) + bytes.substr(3, 2); + + val = static_cast(stol(valStr, nullptr, 16)); + } + ostringstream buf; + + buf << _toolTipText + << "$" << Common::Base::toString(val, Common::Base::Fmt::_16) + << " = #" << val; + if(val < 0x100) + { + if(val >= 0x80) + buf << '/' << -(0x100 - val); + buf << " = %" << Common::Base::toString(val, Common::Base::Fmt::_2); + } + + return buf.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool RomListWidget::changedToolTip(const Common::Point& oldPos, + const Common::Point& newPos) const +{ + return getToolTipIndex(oldPos) != getToolTipIndex(newPos); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RomListWidget::drawWidget(bool hilite) { @@ -480,20 +545,20 @@ void RomListWidget::drawWidget(bool hilite) codeDisasmW = actualWidth; xpos = _x + CheckboxWidget::boxSize(_font) + 10; ypos = _y + 2; - for (i = 0, pos = _currentPos; i < _rows && pos < len; i++, pos++, ypos += _lineHeight) + for(i = 0, pos = _currentPos; i < _rows && pos < len; i++, pos++, ypos += _lineHeight) { ColorId bytesColor = textColor; - // Draw checkboxes for correct lines (takes scrolling into account) + // Mark checkboxes dirty for correct lines (takes scrolling into account) myCheckList[i]->setState(instance().debugger(). checkBreakPoint(dlist[pos].address, instance().debugger().cartDebug().getBank(dlist[pos].address))); - myCheckList[i]->setDirty(); + // draw immediately, because chain order is not deterministic myCheckList[i]->draw(); // Draw highlighted item in a frame - if (_highlightedItem == pos) + if(_highlightedItem == pos) s.frameRect(_x + l.x() - 3, ypos - 1, _w - l.x(), _lineHeight, onTop ? kWidColorHi : kBGColorLo); // Draw the selected item inverted, on a highlighted background. @@ -510,31 +575,31 @@ void RomListWidget::drawWidget(bool hilite) // Draw labels s.drawString(_font, dlist[pos].label, xpos, ypos, _labelWidth, - dlist[pos].hllabel ? textColor : kColor); + dlist[pos].hllabel ? textColor : kColor); // Bytes are only editable if they represent code, graphics, or accessible data // Otherwise, the disassembly should get all remaining space - if(dlist[pos].type & (Device::CODE|Device::GFX|Device::PGFX| - Device::COL|Device::PCOL|Device::BCOL|Device::DATA)) + if(dlist[pos].type & (Device::CODE | Device::GFX | Device::PGFX | + Device::COL | Device::PCOL | Device::BCOL | Device::DATA)) { if(dlist[pos].type == Device::CODE) { // Draw mnemonic s.drawString(_font, dlist[pos].disasm.substr(0, 7), xpos + _labelWidth, ypos, - 7 * _fontWidth, textColor); + 7 * _fontWidth, textColor); // Draw operand - if (dlist[pos].disasm.length() > 8) + if(dlist[pos].disasm.length() > 8) s.drawString(_font, dlist[pos].disasm.substr(8), xpos + _labelWidth + 7 * _fontWidth, ypos, - codeDisasmW - 7 * _fontWidth, textColor); + codeDisasmW - 7 * _fontWidth, textColor); // Draw cycle count s.drawString(_font, dlist[pos].ccount, xpos + _labelWidth + codeDisasmW, ypos, - cycleCountW, textColor); + cycleCountW, textColor); } else { // Draw disassembly only s.drawString(_font, dlist[pos].disasm, xpos + _labelWidth, ypos, - noCodeDisasmW - 4, kTextColor); + noCodeDisasmW - 4, kTextColor); } // Draw separator @@ -542,11 +607,11 @@ void RomListWidget::drawWidget(bool hilite) // Draw bytes { - if (_selectedItem == pos && _editMode) + if(_selectedItem == pos && _editMode) { adjustOffset(); s.drawString(_font, editString(), _x + r.x(), ypos, r.w(), textColor, - TextAlign::Left, -_editScrollOffset, false); + TextAlign::Left, -_editScrollOffset, false); drawCaretSelection(); } @@ -560,7 +625,7 @@ void RomListWidget::drawWidget(bool hilite) { // Draw disassembly, giving it all remaining horizontal space s.drawString(_font, dlist[pos].disasm, xpos + _labelWidth, ypos, - noTypeDisasmW, textColor); + noTypeDisasmW, textColor); } } } @@ -580,8 +645,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); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -594,6 +659,7 @@ void RomListWidget::startEditMode() return; _editMode = true; + dialog().tooltip().hide(); switch(myDisasm->list[_selectedItem].type) { case Device::GFX: diff --git a/src/debugger/gui/RomListWidget.hxx b/src/debugger/gui/RomListWidget.hxx index 40abb0adf..9baa9350a 100644 --- a/src/debugger/gui/RomListWidget.hxx +++ b/src/debugger/gui/RomListWidget.hxx @@ -56,12 +56,13 @@ class RomListWidget : public EditableWidget void setSelected(int item); void setHighlighted(int item); + string getToolTip(const Common::Point& pos) const override; + bool changedToolTip(const Common::Point& oldPos, const Common::Point& newPos) const override; + protected: void handleMouseDown(int x, int y, MouseButton b, int clickCount) override; void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; void handleMouseWheel(int x, int y, int direction) override; - void handleMouseEntered() override; - void handleMouseLeft() override; bool handleText(char text) override; bool handleKeyDown(StellaKey key, StellaMod mod) override; bool handleKeyUp(StellaKey key, StellaMod mod) override; @@ -79,11 +80,15 @@ class RomListWidget : public EditableWidget void endEditMode() override; void abortEditMode() override; void lostFocusWidget() override; + + bool hasToolTip() const override { return true; } + void scrollToSelected() { scrollToCurrent(_selectedItem); } void scrollToHighlighted() { scrollToCurrent(_highlightedItem); } private: void scrollToCurrent(int item); + Common::Point getToolTipIndex(const Common::Point& pos) const; private: unique_ptr myMenu; @@ -96,7 +101,6 @@ class RomListWidget : public EditableWidget int _currentPos{0}; // position of first line in visible window int _selectedItem{-1}; int _highlightedItem{-1}; - bool _editMode{false}; StellaKey _currentKeyDown{KBDK_UNKNOWN}; Common::Base::Fmt _base{Common::Base::Fmt::_DEFAULT}; // base used during editing diff --git a/src/debugger/gui/RomWidget.cxx b/src/debugger/gui/RomWidget.cxx index 9b387d69f..0454ef16d 100644 --- a/src/debugger/gui/RomWidget.cxx +++ b/src/debugger/gui/RomWidget.cxx @@ -90,10 +90,6 @@ void RomWidget::handleCommand(CommandSender* sender, int cmd, int data, int id) case RomListWidget::kBPointChangedCmd: // 'data' is the line in the disassemblylist to be accessed toggleBreak(data); - // Refresh the romlist, since the breakpoint may not have - // actually changed - myRomList->setDirty(); - myRomList->draw(); break; case RomListWidget::kRomChangedCmd: @@ -199,7 +195,7 @@ void RomWidget::runtoPC(int disasm_line) ostringstream command; command << "runtopc #" << address; string msg = instance().debugger().run(command.str()); - instance().frameBuffer().showMessage(msg); + instance().frameBuffer().showTextMessage(msg); } } diff --git a/src/debugger/gui/TiaInfoWidget.cxx b/src/debugger/gui/TiaInfoWidget.cxx index a02b2365d..2690f8b4f 100644 --- a/src/debugger/gui/TiaInfoWidget.cxx +++ b/src/debugger/gui/TiaInfoWidget.cxx @@ -60,30 +60,35 @@ TiaInfoWidget::TiaInfoWidget(GuiObject* boss, const GUI::Font& lfont, xpos = x; new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Frame Cycls" : "F. Cycls"); myFrameCycles = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight); + myFrameCycles->setToolTip("CPU cycles executed this frame."); myFrameCycles->setEditable(false, true); // Left: WSync Cycles ypos += lineHeight + VGAP; new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "WSync Cycls" : "WSync C."); myWSyncCylces = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight); + myWSyncCylces->setToolTip("CPU cycles used for WSYNC this frame."); myWSyncCylces->setEditable(false, true); // Left: Timer Cycles ypos += lineHeight + VGAP; new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Timer Cycls" : "Timer C."); myTimerCylces = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight); + myTimerCylces->setToolTip("CPU cycles roughly used for INTIM reads this frame."); myTimerCylces->setEditable(false, true); // Left: Total Cycles ypos += lineHeight + VGAP; new StaticTextWidget(boss, lfont, xpos, ypos + 1, "Total"); myTotalCycles = new EditTextWidget(boss, nfont, xpos + lwidth8, ypos - 1, twidth, lineHeight); + myTotalCycles->setToolTip("Total CPU cycles executed for this session (E notation)."); myTotalCycles->setEditable(false, true); // Left: Delta Cycles ypos += lineHeight + VGAP; new StaticTextWidget(boss, lfont, xpos, ypos + 1, "Delta"); myDeltaCycles = new EditTextWidget(boss, nfont, xpos + lwidth8, ypos - 1, twidth, lineHeight); + myDeltaCycles->setToolTip("CPU cycles executed since last debug break."); myDeltaCycles->setEditable(false, true); // Right column @@ -93,6 +98,7 @@ TiaInfoWidget::TiaInfoWidget(GuiObject* boss, const GUI::Font& lfont, // Right: Frame Count new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Frame Cnt." : "Frame"); myFrameCount = new EditTextWidget(boss, nfont, xpos + lwidthR, ypos - 1, fwidth, lineHeight); + myFrameCount->setToolTip("Total number of frames executed this session."); myFrameCount->setEditable(false, true); lwidth = lfont.getStringWidth(longstr ? "Color Clock " : "Pixel Pos ") + LGAP; @@ -102,28 +108,33 @@ TiaInfoWidget::TiaInfoWidget(GuiObject* boss, const GUI::Font& lfont, ypos += lineHeight + VGAP; new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Scanline" : "Scn Ln"); myScanlineCountLast = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight); + myScanlineCountLast->setToolTip("Number of scanlines of last frame."); myScanlineCountLast->setEditable(false, true); myScanlineCount = new EditTextWidget(boss, nfont, xpos + lwidth - myScanlineCountLast->getWidth() - 2, ypos - 1, fwidth, lineHeight); + myScanlineCount->setToolTip("Current scanline of this frame."); myScanlineCount->setEditable(false, true); // Right: Scan Cycle ypos += lineHeight + VGAP; new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Scan Cycle" : "Scn Cycle"); myScanlineCycles = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight); + myScanlineCycles->setToolTip("CPU cycles in current scanline."); myScanlineCycles->setEditable(false, true); // Right: Pixel Pos ypos += lineHeight + VGAP; new StaticTextWidget(boss, lfont, xpos, ypos + 1, "Pixel Pos"); myPixelPosition = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight); + myPixelPosition->setToolTip("Pixel position in current scanline."); myPixelPosition->setEditable(false, true); // Right: Color Clock ypos += lineHeight + VGAP; new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Color Clock" : "Color Clk"); myColorClocks = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight); + myColorClocks->setToolTip("Color clocks in current scanline."); myColorClocks->setEditable(false, true); // Calculate actual dimensions diff --git a/src/debugger/gui/TiaOutputWidget.cxx b/src/debugger/gui/TiaOutputWidget.cxx index 01d6d094b..6d2a1b9cb 100644 --- a/src/debugger/gui/TiaOutputWidget.cxx +++ b/src/debugger/gui/TiaOutputWidget.cxx @@ -22,6 +22,8 @@ #include "FBSurface.hxx" #include "Widget.hxx" #include "GuiObject.hxx" +#include "Dialog.hxx" +#include "ToolTip.hxx" #include "ContextMenu.hxx" #include "TiaZoomWidget.hxx" #include "Debugger.hxx" @@ -55,6 +57,7 @@ TiaOutputWidget::TiaOutputWidget(GuiObject* boss, const GUI::Font& font, // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TiaOutputWidget::loadConfig() { + setEnabled(true); setDirty(); } @@ -92,10 +95,10 @@ void TiaOutputWidget::saveSnapshot(int execDepth, const string& execPrefix) message = e.what(); } if (execDepth == 0) { - instance().frameBuffer().showMessage(message); + instance().frameBuffer().showTextMessage(message); } #else - instance().frameBuffer().showMessage("PNG image saving not supported"); + instance().frameBuffer().showTextMessage("PNG image saving not supported"); #endif } @@ -110,6 +113,7 @@ void TiaOutputWidget::handleMouseDown(int x, int y, MouseButton b, int clickCoun myClickX = x; myClickY = y; + dialog().tooltip().hide(); // Add menu at current x,y mouse location myMenu->show(x + getAbsX(), y + getAbsY(), dialog().surface().dstRect()); } @@ -135,7 +139,7 @@ void TiaOutputWidget::handleCommand(CommandSender* sender, int cmd, int data, in { command << "scanline #" << lines; string message = instance().debugger().parser().run(command.str()); - instance().frameBuffer().showMessage(message); + instance().frameBuffer().showTextMessage(message); } } else if(rmb == "bp") @@ -144,7 +148,7 @@ void TiaOutputWidget::handleCommand(CommandSender* sender, int cmd, int data, in int scanline = myClickY + startLine; command << "breakif _scan==#" << scanline; string message = instance().debugger().parser().run(command.str()); - instance().frameBuffer().showMessage(message); + instance().frameBuffer().showTextMessage(message); } else if(rmb == "zoom") { @@ -158,6 +162,51 @@ void TiaOutputWidget::handleCommand(CommandSender* sender, int cmd, int data, in } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Common::Point TiaOutputWidget::getToolTipIndex(const Common::Point& pos) const +{ + const Int32 width = instance().console().tia().width(); + const Int32 height = instance().console().tia().height(); + const int col = (pos.x - 1 - getAbsX()) >> 1; + const int row = pos.y - 1 - getAbsY(); + + if(col < 0 || col >= width || row < 0 || row >= height) + return Common::Point(-1, -1); + else + return Common::Point(col, row); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string TiaOutputWidget::getToolTip(const Common::Point& pos) const +{ + const Common::Point& idx = getToolTipIndex(pos); + + if(idx.x < 0) + return EmptyString; + + const uInt32 height = instance().console().tia().height(); + // limit to 274 lines (PAL default without scaling) + const uInt32 yStart = height <= FrameManager::Metrics::baseHeightPAL + ? 0 : (height - FrameManager::Metrics::baseHeightPAL) >> 1; + const Int32 i = idx.x + (yStart + idx.y) * instance().console().tia().width(); + uInt8* tiaOutputBuffer = instance().console().tia().outputBuffer(); + ostringstream buf; + + buf << _toolTipText + << "X: #" << idx.x + << "\nY: #" << idx.y + << "\nC: $" << Common::Base::toString(tiaOutputBuffer[i], Common::Base::Fmt::_16); + + return buf.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool TiaOutputWidget::changedToolTip(const Common::Point& oldPos, + const Common::Point& newPos) const +{ + return getToolTipIndex(oldPos) != getToolTipIndex(newPos); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TiaOutputWidget::drawWidget(bool hilite) { diff --git a/src/debugger/gui/TiaOutputWidget.hxx b/src/debugger/gui/TiaOutputWidget.hxx index c7bfd26f6..cc465266a 100644 --- a/src/debugger/gui/TiaOutputWidget.hxx +++ b/src/debugger/gui/TiaOutputWidget.hxx @@ -47,6 +47,13 @@ class TiaOutputWidget : public Widget, public CommandSender bool handleKeyDown(StellaKey key, StellaMod mod) override; bool handleKeyUp(StellaKey key, StellaMod mod) override; */ + string getToolTip(const Common::Point& pos) const override; + bool changedToolTip(const Common::Point& oldPos, const Common::Point& newPos) const override; + + protected: + bool hasToolTip() const override { return true; } + Common::Point getToolTipIndex(const Common::Point& pos) const; + private: unique_ptr myMenu; TiaZoomWidget* myZoom{nullptr}; diff --git a/src/debugger/gui/TiaWidget.cxx b/src/debugger/gui/TiaWidget.cxx index 5f6ba56ab..c330bf386 100644 --- a/src/debugger/gui/TiaWidget.cxx +++ b/src/debugger/gui/TiaWidget.cxx @@ -563,7 +563,7 @@ TiaWidget::TiaWidget(GuiObject* boss, const GUI::Font& lfont, new StaticTextWidget(boss, lfont, xpos, ypos+2, 2*fontWidth, fontHeight, "PF", TextAlign::Left); xpos += 2*fontWidth + 5; - myPF[0] = new TogglePixelWidget(boss, nfont, xpos, ypos+1, 4, 1); + myPF[0] = new TogglePixelWidget(boss, nfont, xpos, ypos+1, 4, 1, 4); myPF[0]->setTarget(this); myPF[0]->setID(kPF0ID); addFocusWidget(myPF[0]); @@ -919,10 +919,14 @@ void TiaWidget::handleCommand(CommandSender* sender, int cmd, int data, int id) case kRefP0ID: tia.refP0(myRefP0->getState() ? 1 : 0); + myGRP0->setIntState(myGRP0->getIntState(), !myRefP0->getState()); + myGRP0Old->setIntState(myGRP0Old->getIntState(), !myRefP0->getState()); break; case kRefP1ID: tia.refP1(myRefP1->getState() ? 1 : 0); + myGRP1->setIntState(myGRP1->getIntState(), !myRefP1->getState()); + myGRP1Old->setIntState(myGRP1Old->getIntState(), !myRefP1->getState()); break; case kDelP0ID: @@ -1043,8 +1047,8 @@ void TiaWidget::loadConfig() myGRP0Old->setColor(kBGColorLo); myGRP0Old->setCrossed(true); } - myGRP0->setIntState(state.gr[TiaState::P0], false); - myGRP0Old->setIntState(state.gr[TiaState::P0+2], false); + myGRP0->setIntState(state.gr[TiaState::P0], state.ref[TiaState::P0]); + myGRP0Old->setIntState(state.gr[TiaState::P0+2], state.ref[TiaState::P0]); // posP0 myPosP0->setList(0, state.pos[TiaState::P0], @@ -1079,8 +1083,8 @@ void TiaWidget::loadConfig() myGRP1Old->setColor(kBGColorLo); myGRP1Old->setCrossed(true); } - myGRP1->setIntState(state.gr[TiaState::P1], false); - myGRP1Old->setIntState(state.gr[TiaState::P1+2], false); + myGRP1->setIntState(state.gr[TiaState::P1], state.ref[TiaState::P1]); + myGRP1Old->setIntState(state.gr[TiaState::P1+2], state.ref[TiaState::P1]); // posP1 myPosP1->setList(0, state.pos[TiaState::P1], diff --git a/src/debugger/gui/TiaZoomWidget.cxx b/src/debugger/gui/TiaZoomWidget.cxx index e4553f361..3cbbad8e2 100644 --- a/src/debugger/gui/TiaZoomWidget.cxx +++ b/src/debugger/gui/TiaZoomWidget.cxx @@ -26,6 +26,8 @@ #include "FBSurface.hxx" #include "Widget.hxx" #include "GuiObject.hxx" +#include "Dialog.hxx" +#include "ToolTip.hxx" #include "ContextMenu.hxx" #include "FrameManager.hxx" #include "TiaZoomWidget.hxx" @@ -127,6 +129,7 @@ void TiaZoomWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount) } else if(b == MouseButton::RIGHT) { + dialog().tooltip().hide(); // Add menu at current x,y mouse location myMenu->show(x + getAbsX(), y + getAbsY(), dialog().surface().dstRect()); } @@ -141,6 +144,8 @@ void TiaZoomWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TiaZoomWidget::handleMouseWheel(int x, int y, int direction) { + dialog().tooltip().hide(); + // zoom towards mouse position myClickX = x; myClickY = y; @@ -178,19 +183,11 @@ void TiaZoomWidget::handleMouseMoved(int x, int y) } } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void TiaZoomWidget::handleMouseEntered() -{ - setFlags(Widget::FLAG_HILITED); - setDirty(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TiaZoomWidget::handleMouseLeft() { - clearFlags(Widget::FLAG_HILITED); - setDirty(); myMouseMoving = false; + Widget::handleMouseLeft(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -262,7 +259,7 @@ void TiaZoomWidget::handleCommand(CommandSender* sender, int cmd, int data, int { command << "scanline #" << lines; string message = instance().debugger().parser().run(command.str()); - instance().frameBuffer().showMessage(message); + instance().frameBuffer().showTextMessage(message); } } else if(rmb == "bp") @@ -271,7 +268,7 @@ void TiaZoomWidget::handleCommand(CommandSender* sender, int cmd, int data, int int scanline = myClickY / myZoomLevel + myOffY + startLine; command << "breakif _scan==#" << scanline; string message = instance().debugger().parser().run(command.str()); - instance().frameBuffer().showMessage(message); + instance().frameBuffer().showTextMessage(message); } else { @@ -282,6 +279,47 @@ void TiaZoomWidget::handleCommand(CommandSender* sender, int cmd, int data, int } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Common::Point TiaZoomWidget::getToolTipIndex(const Common::Point& pos) const +{ + const Int32 width = instance().console().tia().width() * 2; + const Int32 height = instance().console().tia().height(); + const int col = (pos.x - 1 - getAbsX()) / (myZoomLevel << 1) + (myOffX >> 1); + const int row = (pos.y - 1 - getAbsY()) / myZoomLevel + myOffY; + + if(col < 0 || col >= width || row < 0 || row >= height) + return Common::Point(-1, -1); + else + return Common::Point(col, row); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string TiaZoomWidget::getToolTip(const Common::Point& pos) const +{ + const Common::Point& idx = getToolTipIndex(pos); + + if(idx.x < 0) + return EmptyString; + + const Int32 i = idx.x + idx.y * instance().console().tia().width(); + uInt8* tiaOutputBuffer = instance().console().tia().outputBuffer(); + ostringstream buf; + + buf << _toolTipText + << "X: #" << idx.x + << "\nY: #" << idx.y + << "\nC: $" << Common::Base::toString(tiaOutputBuffer[i], Common::Base::Fmt::_16); + + return buf.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool TiaZoomWidget::changedToolTip(const Common::Point& oldPos, + const Common::Point& newPos) const +{ + return getToolTipIndex(oldPos) != getToolTipIndex(newPos); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TiaZoomWidget::drawWidget(bool hilite) { diff --git a/src/debugger/gui/TiaZoomWidget.hxx b/src/debugger/gui/TiaZoomWidget.hxx index d4d3a9835..d46787f23 100644 --- a/src/debugger/gui/TiaZoomWidget.hxx +++ b/src/debugger/gui/TiaZoomWidget.hxx @@ -34,8 +34,12 @@ class TiaZoomWidget : public Widget, public CommandSender void loadConfig() override; void setPos(int x, int y); + string getToolTip(const Common::Point& pos) const override; + bool changedToolTip(const Common::Point& oldPos, const Common::Point& newPos) const override; + protected: - void handleMouseEntered() override; + bool hasToolTip() const override { return true; } + Common::Point getToolTipIndex(const Common::Point& pos) const; private: void zoom(int level); diff --git a/src/debugger/gui/ToggleBitWidget.cxx b/src/debugger/gui/ToggleBitWidget.cxx index 479f31225..1ebe1c5e4 100644 --- a/src/debugger/gui/ToggleBitWidget.cxx +++ b/src/debugger/gui/ToggleBitWidget.cxx @@ -25,8 +25,10 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ToggleBitWidget::ToggleBitWidget(GuiObject* boss, const GUI::Font& font, - int x, int y, int cols, int rows, int colchars) - : ToggleWidget(boss, font, x, y, cols, rows, 1) + int x, int y, int cols, int rows, int colchars, + const StringList& labels) + : ToggleWidget(boss, font, x, y, cols, rows), + _labelList(labels) { _rowHeight = font.getLineHeight(); _colWidth = colchars * font.getMaxCharWidth() + 8; @@ -47,6 +49,13 @@ ToggleBitWidget::ToggleBitWidget(GuiObject* boss, const GUI::Font& font, _h = _rowHeight * rows + 1; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +ToggleBitWidget::ToggleBitWidget(GuiObject* boss, const GUI::Font& font, + int x, int y, int cols, int rows, int colchars) + : ToggleBitWidget(boss, font, x, y, cols, rows, colchars, StringList()) +{ +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ToggleBitWidget::setList(const StringList& off, const StringList& on) { @@ -69,6 +78,27 @@ void ToggleBitWidget::setState(const BoolArray& state, const BoolArray& changed) setDirty(); } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string ToggleBitWidget::getToolTip(const Common::Point& pos) const +{ + const Common::Point& idx = getToolTipIndex(pos); + + if(idx.y < 0) + return EmptyString; + + const string tip = ToggleWidget::getToolTip(pos); + + if(idx.x < static_cast(_labelList.size())) + { + const string label = _labelList[idx.x]; + + if(!label.empty()) + return tip + "\n" + label; + } + return tip; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ToggleBitWidget::drawWidget(bool hilite) { diff --git a/src/debugger/gui/ToggleBitWidget.hxx b/src/debugger/gui/ToggleBitWidget.hxx index 6d5505030..707aea1cc 100644 --- a/src/debugger/gui/ToggleBitWidget.hxx +++ b/src/debugger/gui/ToggleBitWidget.hxx @@ -26,17 +26,23 @@ class ToggleBitWidget : public ToggleWidget public: ToggleBitWidget(GuiObject* boss, const GUI::Font& font, int x, int y, int cols, int rows, int colchars = 1); + ToggleBitWidget(GuiObject* boss, const GUI::Font& font, + int x, int y, int cols, int rows, int colchars, + const StringList& labels); ~ToggleBitWidget() override = default; void setList(const StringList& off, const StringList& on); void setState(const BoolArray& state, const BoolArray& changed); + string getToolTip(const Common::Point& pos) const override; + protected: void drawWidget(bool hilite) override; protected: StringList _offList; StringList _onList; + StringList _labelList; private: // Following constructors and assignment operators not supported diff --git a/src/debugger/gui/TogglePixelWidget.cxx b/src/debugger/gui/TogglePixelWidget.cxx index fdc1609bc..b7dd5dbdf 100644 --- a/src/debugger/gui/TogglePixelWidget.cxx +++ b/src/debugger/gui/TogglePixelWidget.cxx @@ -24,8 +24,9 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TogglePixelWidget::TogglePixelWidget(GuiObject* boss, const GUI::Font& font, - int x, int y, int cols, int rows) - : ToggleWidget(boss, font, x, y, cols, rows, 1) + int x, int y, int cols, int rows, + int shiftBits) + : ToggleWidget(boss, font, x, y, cols, rows, shiftBits) { _rowHeight = _colWidth = font.getLineHeight(); diff --git a/src/debugger/gui/TogglePixelWidget.hxx b/src/debugger/gui/TogglePixelWidget.hxx index 6177cac45..fe63cd964 100644 --- a/src/debugger/gui/TogglePixelWidget.hxx +++ b/src/debugger/gui/TogglePixelWidget.hxx @@ -25,7 +25,8 @@ class TogglePixelWidget : public ToggleWidget { public: TogglePixelWidget(GuiObject* boss, const GUI::Font& font, - int x, int y, int cols, int rows); + int x, int y, int cols = 1, int rows = 1, + int shiftBits = 0); ~TogglePixelWidget() override = default; void setColor(ColorId color) { _pixelColor = color; } @@ -35,14 +36,13 @@ class TogglePixelWidget : public ToggleWidget void setState(const BoolArray& state); - void setIntState(int value, bool swap); + void setIntState(int value, bool swap = false); int getIntState(); void setCrossed(bool enable) { _crossBits = enable; } private: ColorId _pixelColor{kNone}, _backgroundColor{kDlgColor}; - bool _swapBits{false}; bool _crossBits{false}; private: diff --git a/src/debugger/gui/ToggleWidget.cxx b/src/debugger/gui/ToggleWidget.cxx index 02ba10146..adb33e118 100644 --- a/src/debugger/gui/ToggleWidget.cxx +++ b/src/debugger/gui/ToggleWidget.cxx @@ -16,44 +16,26 @@ //============================================================================ #include "OSystem.hxx" +#include "Base.hxx" #include "StellaKeys.hxx" #include "Widget.hxx" +#include "Dialog.hxx" +#include "ToolTip.hxx" #include "ToggleWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ToggleWidget::ToggleWidget(GuiObject* boss, const GUI::Font& font, - int x, int y, int cols, int rows, - int clicksToChange) + int x, int y, int cols, int rows, int shiftBits) : Widget(boss, font, x, y, 16, 16), CommandSender(boss), _rows(rows), _cols(cols), - _currentRow(0), - _currentCol(0), - _rowHeight(0), - _colWidth(0), - _selectedItem(0), - _clicksToChange(clicksToChange), - _editable(true) + _shiftBits(shiftBits) { _flags = Widget::FLAG_ENABLED | Widget::FLAG_CLEARBG | Widget::FLAG_RETAIN_FOCUS | Widget::FLAG_WANTS_RAWDATA; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ToggleWidget::handleMouseEntered() -{ - setFlags(Widget::FLAG_HILITED); - setDirty(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ToggleWidget::handleMouseLeft() -{ - clearFlags(Widget::FLAG_HILITED); - setDirty(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ToggleWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount) { @@ -71,6 +53,7 @@ void ToggleWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount) _selectedItem = newSelectedItem; _currentRow = _selectedItem / _cols; _currentCol = _selectedItem - (_currentRow * _cols); + dialog().tooltip().hide(); setDirty(); } } @@ -83,7 +66,7 @@ void ToggleWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) // If this was a double click and the mouse is still over the selected item, // send the double click command - if (clickCount == _clicksToChange && (_selectedItem == findItem(x, y))) + if (clickCount == 1 && (_selectedItem == findItem(x, y))) { _stateList[_selectedItem] = !_stateList[_selectedItem]; _changedList[_selectedItem] = !_changedList[_selectedItem]; @@ -202,6 +185,7 @@ bool ToggleWidget::handleKeyDown(StellaKey key, StellaMod mod) _stateList[_selectedItem] = !_stateList[_selectedItem]; _changedList[_selectedItem] = !_changedList[_selectedItem]; sendCommand(ToggleWidget::kItemDataChangedCmd, _selectedItem, _id); + dialog().tooltip().hide(); } setDirty(); @@ -223,3 +207,56 @@ void ToggleWidget::handleCommand(CommandSender* sender, int cmd, } } } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Common::Point ToggleWidget::getToolTipIndex(const Common::Point& pos) const +{ + const int col = (pos.x - getAbsX()) / _colWidth; + const int row = (pos.y - getAbsY()) / _rowHeight; + + if(row >= 0 && row < _rows && col >= 0 && col < _cols) + return Common::Point(col, row); + else + return Common::Point(-1, -1); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string ToggleWidget::getToolTip(const Common::Point& pos) const +{ + const int idx = getToolTipIndex(pos).y * _cols; + + if(idx < 0) + return EmptyString; + + Int32 val = 0; + ostringstream buf; + + if(_swapBits) + for(int col = _cols - 1; col >= 0; --col) + { + val <<= 1; + val += _stateList[idx + col]; + } + else + for(int col = 0; col < _cols; ++col) + { + val <<= 1; + val += _stateList[idx + col]; + } + val <<= _shiftBits; + + buf << _toolTipText + << "$" << Common::Base::toString(val, Common::Base::Fmt::_16) + << " = #" << val; + if(val < 0x100) + buf << " = %" << Common::Base::toString(val, Common::Base::Fmt::_2); + + return buf.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool ToggleWidget::changedToolTip(const Common::Point& oldPos, + const Common::Point& newPos) const +{ + return getToolTipIndex(oldPos) != getToolTipIndex(newPos); +} diff --git a/src/debugger/gui/ToggleWidget.hxx b/src/debugger/gui/ToggleWidget.hxx index 67777a13a..ee70f6233 100644 --- a/src/debugger/gui/ToggleWidget.hxx +++ b/src/debugger/gui/ToggleWidget.hxx @@ -33,8 +33,8 @@ class ToggleWidget : public Widget, public CommandSender public: ToggleWidget(GuiObject* boss, const GUI::Font& font, - int x, int y, int cols, int rows, - int clicksToChange = 2); + int x, int y, int cols = 1, int rows = 1, + int shiftBits = 0); ~ToggleWidget() override = default; const BoolArray& getState() { return _stateList; } @@ -46,18 +46,24 @@ class ToggleWidget : public Widget, public CommandSender void setEditable(bool editable) { _editable = editable; } bool isEditable() const { return _editable; } - protected: + string getToolTip(const Common::Point& pos) const override; + bool changedToolTip(const Common::Point& oldPos, const Common::Point& newPos) const override; protected: - int _rows; - int _cols; - int _currentRow; - int _currentCol; - int _rowHeight; // explicitly set in child classes - int _colWidth; // explicitly set in child classes - int _selectedItem; - int _clicksToChange; // number of clicks to register a change - bool _editable; + bool hasToolTip() const override { return true; } + Common::Point getToolTipIndex(const Common::Point& pos) const; + + protected: + int _rows{0}; + int _cols{0}; + int _currentRow{0}; + int _currentCol{0}; + int _rowHeight{0}; // explicitly set in child classes + int _colWidth{0}; // explicitly set in child classes + int _selectedItem{0}; + bool _editable{true}; + bool _swapBits{false}; + int _shiftBits{0}; // shift bits for tooltip display BoolArray _stateList; BoolArray _changedList; @@ -68,8 +74,6 @@ class ToggleWidget : public Widget, public CommandSender void handleMouseDown(int x, int y, MouseButton b, int clickCount) override; void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; - void handleMouseEntered() override; - void handleMouseLeft() override; bool handleKeyDown(StellaKey key, StellaMod mod) override; void handleCommand(CommandSender* sender, int cmd, int data, int id) override; diff --git a/src/debugger/gui/module.mk b/src/debugger/gui/module.mk index cda7666e9..0a985e71b 100644 --- a/src/debugger/gui/module.mk +++ b/src/debugger/gui/module.mk @@ -55,6 +55,7 @@ MODULE_OBJS := \ src/debugger/gui/CartDebugWidget.o \ src/debugger/gui/CpuWidget.o \ src/debugger/gui/DataGridOpsWidget.o \ + src/debugger/gui/DataGridRamWidget.o \ src/debugger/gui/DataGridWidget.o \ src/debugger/gui/DebuggerDialog.o \ src/debugger/gui/DelayQueueWidget.o \ diff --git a/src/emucore/Console.cxx b/src/emucore/Console.cxx index 099773989..bb292859e 100644 --- a/src/emucore/Console.cxx +++ b/src/emucore/Console.cxx @@ -476,7 +476,7 @@ void Console::setFormat(uInt32 format, bool force) initializeAudio(); // ensure that audio synthesis is set up to match emulation rate myOSystem.resetFps(); // Reset FPS measurement - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); // Let the other devices know about the console change mySystem->consoleChanged(myConsoleTiming); @@ -493,10 +493,10 @@ void Console::toggleColorLoss(bool toggle) string message = string("PAL color-loss ") + (colorloss ? "enabled" : "disabled"); - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); } else - myOSystem.frameBuffer().showMessage( + myOSystem.frameBuffer().showTextMessage( "PAL color-loss not available in non PAL modes"); } @@ -521,7 +521,7 @@ void Console::toggleInter(bool toggle) ostringstream ss; ss << "Interpolation " << (enabled ? "enabled" : "disabled"); - myOSystem.frameBuffer().showMessage(ss.str()); + myOSystem.frameBuffer().showTextMessage(ss.str()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -539,7 +539,7 @@ void Console::toggleTurbo() ostringstream ss; ss << "Turbo mode " << (!enabled ? "enabled" : "disabled"); - myOSystem.frameBuffer().showMessage(ss.str()); + myOSystem.frameBuffer().showTextMessage(ss.str()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -564,7 +564,7 @@ void Console::changeSpeed(int direction) ostringstream val; val << formatSpeed(speed) << "%"; - myOSystem.frameBuffer().showMessage("Emulation speed", val.str(), speed, MIN_SPEED, MAX_SPEED); + myOSystem.frameBuffer().showGaugeMessage("Emulation speed", val.str(), speed, MIN_SPEED, MAX_SPEED); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -574,13 +574,13 @@ void Console::togglePhosphor() { myProperties.set(PropType::Display_Phosphor, "NO"); myOSystem.frameBuffer().tiaSurface().enablePhosphor(false); - myOSystem.frameBuffer().showMessage("Phosphor effect disabled"); + myOSystem.frameBuffer().showTextMessage("Phosphor effect disabled"); } else { myProperties.set(PropType::Display_Phosphor, "YES"); myOSystem.frameBuffer().tiaSurface().enablePhosphor(true); - myOSystem.frameBuffer().showMessage("Phosphor effect enabled"); + myOSystem.frameBuffer().showTextMessage("Phosphor effect enabled"); } } @@ -605,7 +605,7 @@ void Console::changePhosphor(int direction) val.str(""); val << "Off"; } - myOSystem.frameBuffer().showMessage("Phosphor blend", val.str(), blend); + myOSystem.frameBuffer().showGaugeMessage("Phosphor blend", val.str(), blend); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -699,7 +699,7 @@ void Console::changeVerticalCenter(int direction) if (vcenter != myTIA->vcenter()) myTIA->setVcenter(vcenter); val << (vcenter ? vcenter > 0 ? "+" : "" : " ") << vcenter << "px"; - myOSystem.frameBuffer().showMessage("V-Center", val.str(), vcenter, + myOSystem.frameBuffer().showGaugeMessage("V-Center", val.str(), vcenter, myTIA->minVcenter(), myTIA->maxVcenter()); } @@ -729,7 +729,7 @@ void Console::changeVSizeAdjust(int direction) val << (newAdjustVSize ? newAdjustVSize > 0 ? "+" : "" : " ") << newAdjustVSize << "%"; - myOSystem.frameBuffer().showMessage("V-Size", val.str(), newAdjustVSize, -5, 5); + myOSystem.frameBuffer().showGaugeMessage("V-Size", val.str(), newAdjustVSize, -5, 5); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -746,7 +746,7 @@ void Console::toggleCorrectAspectRatio(bool toggle) const string& message = string("Correct aspect ratio ") + (enabled ? "enabled" : "disabled"); - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -920,7 +920,7 @@ unique_ptr Console::getControllerPort(const Controller::Type type, Controller::onMessageCallback callback = [&os = myOSystem](const string& msg) { bool devSettings = os.settings().getBool("dev.settings"); if(os.settings().getBool(devSettings ? "dev.eepromaccess" : "plr.eepromaccess")) - os.frameBuffer().showMessage(msg); + os.frameBuffer().showTextMessage(msg); }; controller = make_unique(port, myEvent, *mySystem, myOSystem.settings().getString("avoxport"), nvramfile, callback); @@ -933,7 +933,7 @@ unique_ptr Console::getControllerPort(const Controller::Type type, Controller::onMessageCallback callback = [&os = myOSystem](const string& msg) { bool devSettings = os.settings().getBool("dev.settings"); if(os.settings().getBool(devSettings ? "dev.eepromaccess" : "plr.eepromaccess")) - os.frameBuffer().showMessage(msg); + os.frameBuffer().showTextMessage(msg); }; controller = make_unique(port, myEvent, *mySystem, nvramfile, callback); break; @@ -987,7 +987,7 @@ void Console::changeAutoFireRate(int direction) else val << "Off"; - myOSystem.frameBuffer().showMessage("Autofire rate", val.str(), rate, 0, isNTSC ? 30 : 25); + myOSystem.frameBuffer().showGaugeMessage("Autofire rate", val.str(), rate, 0, isNTSC ? 30 : 25); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1012,7 +1012,7 @@ void Console::toggleTIABit(TIABit bit, const string& bitname, bool show, bool to bool result = myTIA->toggleBit(bit, toggle ? 2 : 3); const string message = bitname + (result ? " enabled" : " disabled"); - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1021,7 +1021,7 @@ void Console::toggleBits(bool toggle) const bool enabled = myTIA->toggleBits(toggle); const string message = string("TIA bits ") + (enabled ? "enabled" : "disabled"); - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1030,7 +1030,7 @@ void Console::toggleTIACollision(TIABit bit, const string& bitname, bool show, b bool result = myTIA->toggleCollision(bit, toggle ? 2 : 3); const string message = bitname + (result ? " collision enabled" : " collision disabled"); - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1039,7 +1039,7 @@ void Console::toggleCollisions(bool toggle) const bool enabled = myTIA->toggleCollisions(toggle); const string message = string("TIA collisions ") + (enabled ? "enabled" : "disabled"); - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1048,7 +1048,7 @@ void Console::toggleFixedColors(bool toggle) const bool enabled = toggle ? myTIA->toggleFixedColors() : myTIA->usingFixedColors(); const string message = string("Fixed debug colors ") + (enabled ? "enabled" : "disabled"); - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1057,7 +1057,7 @@ void Console::toggleJitter(bool toggle) const bool enabled = myTIA->toggleJitter(toggle ? 2 : 3); const string message = string("TV scanline jitter ") + (enabled ? "enabled" : "disabled"); - myOSystem.frameBuffer().showMessage(message); + myOSystem.frameBuffer().showTextMessage(message); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index 871d6cd73..5eced0d94 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -191,12 +191,12 @@ void EventHandler::toggleSAPortOrder() if(saport == "lr") { mapStelladaptors("rl"); - myOSystem.frameBuffer().showMessage("Stelladaptor ports right/left"); + myOSystem.frameBuffer().showTextMessage("Stelladaptor ports right/left"); } else { mapStelladaptors("lr"); - myOSystem.frameBuffer().showMessage("Stelladaptor ports left/right"); + myOSystem.frameBuffer().showTextMessage("Stelladaptor ports left/right"); } #endif } @@ -214,7 +214,7 @@ void EventHandler::set7800Mode() void EventHandler::handleMouseControl() { if(myMouseControl) - myOSystem.frameBuffer().showMessage(myMouseControl->next()); + myOSystem.frameBuffer().showTextMessage(myMouseControl->next()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -323,7 +323,8 @@ void EventHandler::handleSystemEvent(SystemEvent e, int, int) { case SystemEvent::WINDOW_EXPOSED: case SystemEvent::WINDOW_RESIZED: - myOSystem.frameBuffer().update(true); // force full update + // Force full render update + myOSystem.frameBuffer().update(FrameBuffer::UpdateMode::RERENDER); break; #ifdef BSPF_UNIX case SystemEvent::WINDOW_FOCUS_GAINED: @@ -550,7 +551,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) default: break; } - myOSystem.frameBuffer().showMessage(msg + " settings"); + myOSystem.frameBuffer().showTextMessage(msg + " settings"); myAdjustActive = false; } break; @@ -1210,7 +1211,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) case Event::SaveAllStates: if (pressed && !repeated) - myOSystem.frameBuffer().showMessage(myOSystem.state().rewindManager().saveAllStates()); + myOSystem.frameBuffer().showTextMessage(myOSystem.state().rewindManager().saveAllStates()); return; case Event::PreviousState: @@ -1243,7 +1244,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) case Event::LoadAllStates: if (pressed && !repeated) - myOSystem.frameBuffer().showMessage(myOSystem.state().rewindManager().loadAllStates()); + myOSystem.frameBuffer().showTextMessage(myOSystem.state().rewindManager().loadAllStates()); return; case Event::RewindPause: @@ -1476,7 +1477,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) { myEvent.set(Event::ConsoleBlackWhite, 0); myEvent.set(Event::ConsoleColor, 1); - myOSystem.frameBuffer().showMessage(myIs7800 ? "Pause released" : "Color Mode"); + myOSystem.frameBuffer().showTextMessage(myIs7800 ? "Pause released" : "Color Mode"); myOSystem.console().switches().update(); } return; @@ -1485,7 +1486,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) { myEvent.set(Event::ConsoleBlackWhite, 1); myEvent.set(Event::ConsoleColor, 0); - myOSystem.frameBuffer().showMessage(myIs7800 ? "Pause pushed" : "B/W Mode"); + myOSystem.frameBuffer().showTextMessage(myIs7800 ? "Pause pushed" : "B/W Mode"); myOSystem.console().switches().update(); } return; @@ -1496,13 +1497,13 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) { myEvent.set(Event::ConsoleBlackWhite, 1); myEvent.set(Event::ConsoleColor, 0); - myOSystem.frameBuffer().showMessage(myIs7800 ? "Pause pushed" : "B/W Mode"); + myOSystem.frameBuffer().showTextMessage(myIs7800 ? "Pause pushed" : "B/W Mode"); } else { myEvent.set(Event::ConsoleBlackWhite, 0); myEvent.set(Event::ConsoleColor, 1); - myOSystem.frameBuffer().showMessage(myIs7800 ? "Pause released" : "Color Mode"); + myOSystem.frameBuffer().showTextMessage(myIs7800 ? "Pause released" : "Color Mode"); } myOSystem.console().switches().update(); } @@ -1514,7 +1515,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) myEvent.set(Event::ConsoleBlackWhite, 0); myEvent.set(Event::ConsoleColor, 0); if (myIs7800) - myOSystem.frameBuffer().showMessage("Pause pressed"); + myOSystem.frameBuffer().showTextMessage("Pause pressed"); myOSystem.console().switches().update(); } return; @@ -1524,7 +1525,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) { myEvent.set(Event::ConsoleLeftDiffA, 1); myEvent.set(Event::ConsoleLeftDiffB, 0); - myOSystem.frameBuffer().showMessage(GUI::LEFT_DIFFICULTY + " A"); + myOSystem.frameBuffer().showTextMessage(GUI::LEFT_DIFFICULTY + " A"); myOSystem.console().switches().update(); } return; @@ -1533,7 +1534,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) { myEvent.set(Event::ConsoleLeftDiffA, 0); myEvent.set(Event::ConsoleLeftDiffB, 1); - myOSystem.frameBuffer().showMessage(GUI::LEFT_DIFFICULTY + " B"); + myOSystem.frameBuffer().showTextMessage(GUI::LEFT_DIFFICULTY + " B"); myOSystem.console().switches().update(); } return; @@ -1544,13 +1545,13 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) { myEvent.set(Event::ConsoleLeftDiffA, 0); myEvent.set(Event::ConsoleLeftDiffB, 1); - myOSystem.frameBuffer().showMessage(GUI::LEFT_DIFFICULTY + " B"); + myOSystem.frameBuffer().showTextMessage(GUI::LEFT_DIFFICULTY + " B"); } else { myEvent.set(Event::ConsoleLeftDiffA, 1); myEvent.set(Event::ConsoleLeftDiffB, 0); - myOSystem.frameBuffer().showMessage(GUI::LEFT_DIFFICULTY + " A"); + myOSystem.frameBuffer().showTextMessage(GUI::LEFT_DIFFICULTY + " A"); } myOSystem.console().switches().update(); } @@ -1561,7 +1562,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) { myEvent.set(Event::ConsoleRightDiffA, 1); myEvent.set(Event::ConsoleRightDiffB, 0); - myOSystem.frameBuffer().showMessage(GUI::RIGHT_DIFFICULTY + " A"); + myOSystem.frameBuffer().showTextMessage(GUI::RIGHT_DIFFICULTY + " A"); myOSystem.console().switches().update(); } return; @@ -1570,7 +1571,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) { myEvent.set(Event::ConsoleRightDiffA, 0); myEvent.set(Event::ConsoleRightDiffB, 1); - myOSystem.frameBuffer().showMessage(GUI::RIGHT_DIFFICULTY + " B"); + myOSystem.frameBuffer().showTextMessage(GUI::RIGHT_DIFFICULTY + " B"); myOSystem.console().switches().update(); } return; @@ -1581,13 +1582,13 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) { myEvent.set(Event::ConsoleRightDiffA, 0); myEvent.set(Event::ConsoleRightDiffB, 1); - myOSystem.frameBuffer().showMessage(GUI::RIGHT_DIFFICULTY + " B"); + myOSystem.frameBuffer().showTextMessage(GUI::RIGHT_DIFFICULTY + " B"); } else { myEvent.set(Event::ConsoleRightDiffA, 1); myEvent.set(Event::ConsoleRightDiffB, 0); - myOSystem.frameBuffer().showMessage(GUI::RIGHT_DIFFICULTY + " A"); + myOSystem.frameBuffer().showTextMessage(GUI::RIGHT_DIFFICULTY + " A"); } myOSystem.console().switches().update(); } @@ -2287,6 +2288,7 @@ void EventHandler::enterMenuMode(EventHandlerState state) void EventHandler::leaveMenuMode() { #ifdef GUI_SUPPORT + myOverlay->removeDialog(); // remove the base dialog from dialog stack setState(EventHandlerState::EMULATION); myOSystem.sound().mute(false); #endif @@ -2312,15 +2314,16 @@ bool EventHandler::enterDebugMode() myOSystem.debugger().setQuitState(); setState(EventHandlerState::EMULATION); if(fbstatus == FBInitStatus::FailTooLarge) - myOSystem.frameBuffer().showMessage("Debugger window too large for screen", - MessagePosition::BottomCenter, true); + myOSystem.frameBuffer().showTextMessage("Debugger window too large for screen", + MessagePosition::BottomCenter, true); return false; } myOverlay->reStack(); myOSystem.sound().mute(true); + #else - myOSystem.frameBuffer().showMessage("Debugger support not included", - MessagePosition::BottomCenter, true); + myOSystem.frameBuffer().showTextMessage("Debugger support not included", + MessagePosition::BottomCenter, true); #endif return true; diff --git a/src/emucore/FBSurface.cxx b/src/emucore/FBSurface.cxx index c489fc703..6421dd171 100644 --- a/src/emucore/FBSurface.cxx +++ b/src/emucore/FBSurface.cxx @@ -296,27 +296,45 @@ 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 || s[pos] == '\n') { - leftStr = inStr.substr(0, i); - if(inStr[i] == ' ') // skip leading space after line break - i++; - rightStr = inStr.substr(i); - return; + split = true; + break; } + 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] == ' ' || s[pos] == '\n') // skip leading space after line break + i++; + right = s.substr(i); + return; + } + } + left = s.substr(0, pos); + right = s.substr(pos); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool FBSurface::isWhiteSpace(const char s) const { - const string WHITESPACES = " ,.;:+-"; + const string WHITESPACES = " ,.;:+-*/'([\n"; for(size_t i = 0; i < WHITESPACES.length(); ++i) if(s == WHITESPACES[i]) @@ -331,37 +349,30 @@ int FBSurface::drawString(const GUI::Font& font, const string& s, ColorId color, TextAlign align, int deltax, bool useEllipsis, ColorId shadowColor) { - int lines = 1; + int lines = 0; #ifdef GUI_SUPPORT string inStr = s; // draw multiline string - while (font.getStringWidth(inStr) > w && h >= font.getFontHeight() * 2) + //while (font.getStringWidth(inStr) > w && h >= font.getFontHeight() * 2) + while(inStr.length() && 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(); inStr = rightStr; lines++; } - drawString(font, inStr, x, y, w, color, align, deltax, useEllipsis, shadowColor); + if(inStr.length()) + { + drawString(font, inStr, x, y, w, color, align, deltax, useEllipsis, shadowColor); + lines++; + } #endif return lines; } diff --git a/src/emucore/FBSurface.hxx b/src/emucore/FBSurface.hxx index a018ac2c9..de19fca28 100644 --- a/src/emucore/FBSurface.hxx +++ b/src/emucore/FBSurface.hxx @@ -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 @@ -292,11 +304,14 @@ class FBSurface These methods set the origin point and width/height for the specified service. They are defined as separate x/y and w/h methods since these items are sometimes set separately. + Other times they are set together, so we can use a Rect instead. */ virtual void setSrcPos(uInt32 x, uInt32 y) = 0; virtual void setSrcSize(uInt32 w, uInt32 h) = 0; + virtual void setSrcRect(const Common::Rect& r) = 0; virtual void setDstPos(uInt32 x, uInt32 y) = 0; virtual void setDstSize(uInt32 w, uInt32 h) = 0; + virtual void setDstRect(const Common::Rect& r) = 0; /** This method should be called to enable/disable showing the surface @@ -323,7 +338,18 @@ class FBSurface This method should be called to reset the surface to empty pixels / colour black. */ - virtual void invalidate() = 0; + virtual void invalidate() {} + + /** + This method should be called to reset a surface area to empty + + @param x The x coordinate + @param y The y coordinate + @param w The width of the area + @param h The height of the area + */ + virtual void invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) = 0; + /** This method should be called to free any resources being used by @@ -369,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 diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx index 97bbb2153..91cabfe3e 100644 --- a/src/emucore/FrameBuffer.cxx +++ b/src/emucore/FrameBuffer.cxx @@ -270,7 +270,7 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type, #ifdef GUI_SUPPORT // Erase any messages from a previous run - myMsg.counter = 0; + myMsg.enabled = false; // Create surfaces for TIA statistics and general messages const GUI::Font& f = hidpiEnabled() ? infoFont() : font(); @@ -311,7 +311,7 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBuffer::update(bool force) +void FrameBuffer::update(UpdateMode mode) { // Onscreen messages are a special case and require different handling than // other objects; they aren't UI dialogs in the normal sense nor are they @@ -322,13 +322,14 @@ void FrameBuffer::update(bool force) // - at the bottom of ::update(), to actually draw them (this must come // last, since they are always drawn on top of everything else). - // Full rendering is required when messages are enabled - force = force || myMsg.counter >= 0; + bool forceRedraw = mode & UpdateMode::REDRAW; + bool redraw = forceRedraw; - // Detect when a message has been turned off; one last redraw is required - // in this case, to draw over the area that the message occupied - if(myMsg.counter == 0) - myMsg.counter = -1; + // Forced render without draw required if messages or dialogs were closed + // Note: For dialogs only relevant when two or more dialogs were stacked + bool rerender = (mode & (UpdateMode::REDRAW | UpdateMode::RERENDER)) + || myPendingRender; + myPendingRender = false; switch(myOSystem.eventHandler().state()) { @@ -343,59 +344,69 @@ void FrameBuffer::update(bool force) if(myPausedCount-- <= 0) { myPausedCount = uInt32(7 * myOSystem.frameRate()); - showMessage("Paused", MessagePosition::MiddleCenter); - } - if(force) + showTextMessage("Paused", MessagePosition::MiddleCenter); + myTIASurface->render(); + } + if(rerender) myTIASurface->render(); - break; // EventHandlerState::PAUSE } #ifdef GUI_SUPPORT case EventHandlerState::OPTIONSMENU: { - force = force || myOSystem.menu().needsRedraw(); - if(force) + myOSystem.menu().tick(); + redraw |= myOSystem.menu().needsRedraw(); + if(redraw) { clear(); myTIASurface->render(); - myOSystem.menu().draw(force); + myOSystem.menu().draw(forceRedraw); + } + else if(rerender) + { + clear(); + myTIASurface->render(); + myOSystem.menu().render(); } break; // EventHandlerState::OPTIONSMENU } case EventHandlerState::CMDMENU: { - force = force || myOSystem.commandMenu().needsRedraw(); - if(force) + myOSystem.commandMenu().tick(); + redraw |= myOSystem.commandMenu().needsRedraw(); + if(redraw) { clear(); myTIASurface->render(); - myOSystem.commandMenu().draw(force); + myOSystem.commandMenu().draw(forceRedraw); } break; // EventHandlerState::CMDMENU } case EventHandlerState::MESSAGEMENU: { - force = force || myOSystem.messageMenu().needsRedraw(); - if (force) + myOSystem.messageMenu().tick(); + redraw |= myOSystem.messageMenu().needsRedraw(); + if(redraw) { clear(); myTIASurface->render(); - myOSystem.messageMenu().draw(force); + myOSystem.messageMenu().draw(forceRedraw); } break; // EventHandlerState::MESSAGEMENU } case EventHandlerState::TIMEMACHINE: { - force = force || myOSystem.timeMachine().needsRedraw(); - if(force) + myOSystem.timeMachine().tick(); + redraw |= myOSystem.timeMachine().needsRedraw(); + if(redraw) { clear(); myTIASurface->render(); - myOSystem.timeMachine().draw(force); + myOSystem.timeMachine().draw(forceRedraw); } break; // EventHandlerState::TIMEMACHINE } @@ -420,13 +431,13 @@ void FrameBuffer::update(bool force) r.rewindStates(1); } - force = force || success; - if (force) + redraw |= success; + if(redraw) myTIASurface->render(); // Stop playback mode at the end of the state buffer // and switch to Time Machine or Pause mode - if (!success) + if(!success) { frames = 0; myOSystem.eventHandler().enterMenuMode(EventHandlerState::TIMEMACHINE); @@ -436,12 +447,12 @@ void FrameBuffer::update(bool force) case EventHandlerState::LAUNCHER: { - force = force || myOSystem.launcher().needsRedraw(); - if(force) - { - clear(); - myOSystem.launcher().draw(force); - } + myOSystem.launcher().tick(); + redraw |= myOSystem.launcher().needsRedraw(); + if(redraw) + myOSystem.launcher().draw(forceRedraw); + else if(rerender) + myOSystem.launcher().render(); break; // EventHandlerState::LAUNCHER } #endif @@ -449,12 +460,12 @@ void FrameBuffer::update(bool force) #ifdef DEBUGGER_SUPPORT case EventHandlerState::DEBUGGER: { - force = force || myOSystem.debugger().needsRedraw(); - if(force) - { - clear(); - myOSystem.debugger().draw(force); - } + myOSystem.debugger().tick(); + redraw |= myOSystem.debugger().needsRedraw(); + if(redraw) + myOSystem.debugger().draw(forceRedraw); + else if(rerender) + myOSystem.debugger().render(); break; // EventHandlerState::DEBUGGER } #endif @@ -468,10 +479,10 @@ void FrameBuffer::update(bool force) // indicates that, and then the code at the top of this method sees // the change and redraws everything if(myMsg.enabled) - drawMessage(); + redraw |= drawMessage(); // Push buffers to screen only when necessary - if(force) + if(redraw || rerender) myBackend->renderToScreen(); } @@ -501,19 +512,15 @@ void FrameBuffer::updateInEmulationMode(float framesPerSecond) myBackend->renderToScreen(); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBuffer::showMessage(const string& message, MessagePosition position, - bool force) -{ #ifdef GUI_SUPPORT +void FrameBuffer::createMessage(const string& message, MessagePosition position, bool force) +{ // Only show messages if they've been enabled if(myMsg.surface == nullptr || !(force || myOSystem.settings().getBool("uimessages"))) return; - const int fontWidth = font().getMaxCharWidth(), - fontHeight = font().getFontHeight(); + const int fontHeight = font().getFontHeight(); const int VBORDER = fontHeight / 4; - const int HBORDER = fontWidth * 1.25 / 2.0; myMsg.counter = uInt32(myOSystem.frameRate()) * 2; // Show message for 2 seconds if(myMsg.counter == 0) @@ -522,39 +529,40 @@ void FrameBuffer::showMessage(const string& message, MessagePosition position, // Precompute the message coordinates myMsg.text = message; myMsg.color = kBtnTextColor; - myMsg.showGauge = false; - myMsg.w = std::min(fontWidth * (MESSAGE_WIDTH) - HBORDER * 2, - font().getStringWidth(myMsg.text) + HBORDER * 2); myMsg.h = fontHeight + VBORDER * 2; myMsg.position = position; myMsg.enabled = true; + myMsg.dirty = true; myMsg.surface->setSrcSize(myMsg.w, myMsg.h); myMsg.surface->setDstSize(myMsg.w * hidpiScaleFactor(), myMsg.h * hidpiScaleFactor()); +} +#endif + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FrameBuffer::showTextMessage(const string& message, MessagePosition position, + bool force) +{ +#ifdef GUI_SUPPORT + const int fontWidth = font().getMaxCharWidth(); + const int HBORDER = fontWidth * 1.25 / 2.0; + + myMsg.showGauge = false; + myMsg.w = std::min(fontWidth * (MESSAGE_WIDTH) - HBORDER * 2, + font().getStringWidth(message) + HBORDER * 2); + + createMessage(message, position, force); #endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBuffer::showMessage(const string& message, const string& valueText, - float value, float minValue, float maxValue) +void FrameBuffer::showGaugeMessage(const string& message, const string& valueText, + float value, float minValue, float maxValue) { #ifdef GUI_SUPPORT - // Only show messages if they've been enabled - if(myMsg.surface == nullptr || !myOSystem.settings().getBool("uimessages")) - return; - - const int fontWidth = font().getMaxCharWidth(), - fontHeight = font().getFontHeight(); - const int VBORDER = fontHeight / 4; + const int fontWidth = font().getMaxCharWidth(); const int HBORDER = fontWidth * 1.25 / 2.0; - myMsg.counter = uInt32(myOSystem.frameRate()) * 2; // Show message for 2 seconds - if(myMsg.counter == 0) - myMsg.counter = 120; - - // Precompute the message coordinates - myMsg.text = message; - myMsg.color = kBtnTextColor; myMsg.showGauge = true; if(maxValue - minValue != 0) myMsg.value = (value - minValue) / (maxValue - minValue) * 100.F; @@ -562,16 +570,12 @@ void FrameBuffer::showMessage(const string& message, const string& valueText, myMsg.value = 100.F; myMsg.valueText = valueText; myMsg.w = std::min(fontWidth * MESSAGE_WIDTH, - font().getStringWidth(myMsg.text) + font().getStringWidth(message) + fontWidth * (GAUGEBAR_WIDTH + 2) - + font().getStringWidth(myMsg.valueText)) - + HBORDER * 2; - myMsg.h = fontHeight + VBORDER * 2; - myMsg.position = MessagePosition::BottomCenter; - myMsg.enabled = true; + + font().getStringWidth(valueText)) + + HBORDER * 2; - myMsg.surface->setSrcSize(myMsg.w, myMsg.h); - myMsg.surface->setDstSize(myMsg.w * hidpiScaleFactor(), myMsg.h * hidpiScaleFactor()); + createMessage(message, MessagePosition::BottomCenter); #endif } @@ -652,8 +656,8 @@ void FrameBuffer::toggleFrameStats(bool toggle) myOSystem.settings().setValue( myOSystem.settings().getBool("dev.settings") ? "dev.stats" : "plr.stats", myStatsEnabled); - myOSystem.frameBuffer().showMessage(string("Console info ") + - (myStatsEnabled ? "enabled" : "disabled")); + myOSystem.frameBuffer().showTextMessage(string("Console info ") + + (myStatsEnabled ? "enabled" : "disabled")); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -676,12 +680,19 @@ void FrameBuffer::enableMessages(bool enable) myStatsMsg.enabled = false; // Erase old messages on the screen - myMsg.enabled = false; - myMsg.counter = 0; - update(true); // Force update immediately + hideMessage(); + + update(); // update immediately } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FrameBuffer::hideMessage() +{ + myPendingRender = myMsg.enabled; + myMsg.enabled = false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inline bool FrameBuffer::drawMessage() { @@ -690,115 +701,119 @@ inline bool FrameBuffer::drawMessage() // or show again this frame if(myMsg.counter == 0) { - myMsg.enabled = false; - return true; - } - else if(myMsg.counter < 0) - { - myMsg.enabled = false; + hideMessage(); return false; } - // Draw the bounded box and text - const Common::Rect& dst = myMsg.surface->dstRect(); - const int fontWidth = font().getMaxCharWidth(), - fontHeight = font().getFontHeight(); - const int VBORDER = fontHeight / 4; - const int HBORDER = fontWidth * 1.25 / 2.0; - constexpr int BORDER = 1; - - switch(myMsg.position) + if(myMsg.dirty) { - case MessagePosition::TopLeft: - myMsg.x = 5; - myMsg.y = 5; - break; + cerr << "--- draw message ---" << endl; - case MessagePosition::TopCenter: - myMsg.x = (imageRect().w() - dst.w()) >> 1; - myMsg.y = 5; - break; + // Draw the bounded box and text + const Common::Rect& dst = myMsg.surface->dstRect(); + const int fontWidth = font().getMaxCharWidth(), + fontHeight = font().getFontHeight(); + const int VBORDER = fontHeight / 4; + const int HBORDER = fontWidth * 1.25 / 2.0; + constexpr int BORDER = 1; - case MessagePosition::TopRight: - myMsg.x = imageRect().w() - dst.w() - 5; - myMsg.y = 5; - break; - - case MessagePosition::MiddleLeft: - myMsg.x = 5; - myMsg.y = (imageRect().h() - dst.h()) >> 1; - break; - - case MessagePosition::MiddleCenter: - myMsg.x = (imageRect().w() - dst.w()) >> 1; - myMsg.y = (imageRect().h() - dst.h()) >> 1; - break; - - case MessagePosition::MiddleRight: - myMsg.x = imageRect().w() - dst.w() - 5; - myMsg.y = (imageRect().h() - dst.h()) >> 1; - break; - - case MessagePosition::BottomLeft: - myMsg.x = 5; - myMsg.y = imageRect().h() - dst.h() - 5; - break; - - case MessagePosition::BottomCenter: - myMsg.x = (imageRect().w() - dst.w()) >> 1; - myMsg.y = imageRect().h() - dst.h() - 5; - break; - - case MessagePosition::BottomRight: - myMsg.x = imageRect().w() - dst.w() - 5; - myMsg.y = imageRect().h() - dst.h() - 5; - break; - } - - myMsg.surface->setDstPos(myMsg.x + imageRect().x(), myMsg.y + imageRect().y()); - myMsg.surface->fillRect(0, 0, myMsg.w, myMsg.h, kColor); - myMsg.surface->fillRect(BORDER, BORDER, myMsg.w - BORDER * 2, myMsg.h - BORDER * 2, kBtnColor); - myMsg.surface->drawString(font(), myMsg.text, HBORDER, VBORDER, - myMsg.w, myMsg.color); - - if(myMsg.showGauge) - { - constexpr int NUM_TICKMARKS = 4; - // limit gauge bar width if texts are too long - const int swidth = std::min(fontWidth * GAUGEBAR_WIDTH, - fontWidth * (MESSAGE_WIDTH - 2) - - font().getStringWidth(myMsg.text) - - font().getStringWidth(myMsg.valueText)); - const int bwidth = swidth * myMsg.value / 100.F; - const int bheight = fontHeight >> 1; - const int x = HBORDER + font().getStringWidth(myMsg.text) + fontWidth; - // align bar with bottom of text - const int y = VBORDER + font().desc().ascent - bheight; - - // draw gauge bar - myMsg.surface->fillRect(x - BORDER, y, swidth + BORDER * 2, bheight, kSliderBGColor); - myMsg.surface->fillRect(x, y + BORDER, bwidth, bheight - BORDER * 2, kSliderColor); - // draw tickmark in the middle of the bar - for(int i = 1; i < NUM_TICKMARKS; ++i) + switch(myMsg.position) { - ColorId color; - int xt = x + swidth * i / NUM_TICKMARKS; - if(bwidth < xt - x) - color = kCheckColor; // kSliderColor; - else - color = kSliderBGColor; - myMsg.surface->vLine(xt, y + bheight / 2, y + bheight - 1, color); + case MessagePosition::TopLeft: + myMsg.x = 5; + myMsg.y = 5; + break; + + case MessagePosition::TopCenter: + myMsg.x = (imageRect().w() - dst.w()) >> 1; + myMsg.y = 5; + break; + + case MessagePosition::TopRight: + myMsg.x = imageRect().w() - dst.w() - 5; + myMsg.y = 5; + break; + + case MessagePosition::MiddleLeft: + myMsg.x = 5; + myMsg.y = (imageRect().h() - dst.h()) >> 1; + break; + + case MessagePosition::MiddleCenter: + myMsg.x = (imageRect().w() - dst.w()) >> 1; + myMsg.y = (imageRect().h() - dst.h()) >> 1; + break; + + case MessagePosition::MiddleRight: + myMsg.x = imageRect().w() - dst.w() - 5; + myMsg.y = (imageRect().h() - dst.h()) >> 1; + break; + + case MessagePosition::BottomLeft: + myMsg.x = 5; + myMsg.y = imageRect().h() - dst.h() - 5; + break; + + case MessagePosition::BottomCenter: + myMsg.x = (imageRect().w() - dst.w()) >> 1; + myMsg.y = imageRect().h() - dst.h() - 5; + break; + + case MessagePosition::BottomRight: + myMsg.x = imageRect().w() - dst.w() - 5; + myMsg.y = imageRect().h() - dst.h() - 5; + break; } - // draw value text - myMsg.surface->drawString(font(), myMsg.valueText, - x + swidth + fontWidth, VBORDER, + + myMsg.surface->setDstPos(myMsg.x + imageRect().x(), myMsg.y + imageRect().y()); + myMsg.surface->fillRect(0, 0, myMsg.w, myMsg.h, kColor); + myMsg.surface->fillRect(BORDER, BORDER, myMsg.w - BORDER * 2, myMsg.h - BORDER * 2, kBtnColor); + myMsg.surface->drawString(font(), myMsg.text, HBORDER, VBORDER, myMsg.w, myMsg.color); + + if(myMsg.showGauge) + { + constexpr int NUM_TICKMARKS = 4; + // limit gauge bar width if texts are too long + const int swidth = std::min(fontWidth * GAUGEBAR_WIDTH, + fontWidth * (MESSAGE_WIDTH - 2) + - font().getStringWidth(myMsg.text) + - font().getStringWidth(myMsg.valueText)); + const int bwidth = swidth * myMsg.value / 100.F; + const int bheight = fontHeight >> 1; + const int x = HBORDER + font().getStringWidth(myMsg.text) + fontWidth; + // align bar with bottom of text + const int y = VBORDER + font().desc().ascent - bheight; + + // draw gauge bar + myMsg.surface->fillRect(x - BORDER, y, swidth + BORDER * 2, bheight, kSliderBGColor); + myMsg.surface->fillRect(x, y + BORDER, bwidth, bheight - BORDER * 2, kSliderColor); + // draw tickmark in the middle of the bar + for(int i = 1; i < NUM_TICKMARKS; ++i) + { + ColorId color; + int xt = x + swidth * i / NUM_TICKMARKS; + if(bwidth < xt - x) + color = kCheckColor; // kSliderColor; + else + color = kSliderBGColor; + myMsg.surface->vLine(xt, y + bheight / 2, y + bheight - 1, color); + } + // draw value text + myMsg.surface->drawString(font(), myMsg.valueText, + x + swidth + fontWidth, VBORDER, + myMsg.w, myMsg.color); + } + myMsg.dirty = false; + myMsg.surface->render(); + return true; } - myMsg.surface->render(); + myMsg.counter--; + myMsg.surface->render(); #endif - return true; + return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -845,7 +860,7 @@ void FrameBuffer::resetSurfaces() freeSurfaces(); reloadSurfaces(); - update(true); // force full update + update(UpdateMode::REDRAW); // force full update } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -898,10 +913,9 @@ void FrameBuffer::setUIPalette() void FrameBuffer::stateChanged(EventHandlerState state) { // Make sure any onscreen messages are removed - myMsg.enabled = false; - myMsg.counter = 0; + hideMessage(); - update(true); // force full update + update(); // update immediately } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -916,10 +930,10 @@ string FrameBuffer::getDisplayKey() case BufferType::Emulator: return "display"; - #ifdef DEBUGGER_SUPPORT + #ifdef DEBUGGER_SUPPORT case BufferType::Debugger: return "dbg.display"; - #endif + #endif default: return ""; @@ -938,10 +952,10 @@ string FrameBuffer::getPositionKey() case BufferType::Emulator: return "windowedpos"; - #ifdef DEBUGGER_SUPPORT + #ifdef DEBUGGER_SUPPORT case BufferType::Debugger: return "dbg.pos"; - #endif + #endif default: return ""; @@ -952,10 +966,10 @@ string FrameBuffer::getPositionKey() void FrameBuffer::saveCurrentWindowPosition() { myOSystem.settings().setValue( - getDisplayKey(), myBackend->getCurrentDisplayIndex()); + getDisplayKey(), myBackend->getCurrentDisplayIndex()); if(myBackend->isCurrentWindowPositioned()) myOSystem.settings().setValue( - getPositionKey(), myBackend->getCurrentWindowPos()); + getPositionKey(), myBackend->getCurrentWindowPos()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -990,7 +1004,9 @@ void FrameBuffer::setFullscreen(bool enable) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FrameBuffer::toggleFullscreen(bool toggle) { - switch(myOSystem.eventHandler().state()) + EventHandlerState state = myOSystem.eventHandler().state(); + + switch(state) { case EventHandlerState::LAUNCHER: case EventHandlerState::EMULATION: @@ -1000,17 +1016,27 @@ void FrameBuffer::toggleFullscreen(bool toggle) const bool isFullscreen = toggle ? !fullScreen() : fullScreen(); setFullscreen(isFullscreen); - if(myBufferType != BufferType::Launcher) + if(state != EventHandlerState::LAUNCHER) { ostringstream msg; msg << "Fullscreen "; - if(isFullscreen) - msg << "enabled (" << myBackend->refreshRate() << " Hz, "; - else - msg << "disabled ("; - msg << "Zoom " << myActiveVidMode.zoom * 100 << "%)"; - showMessage(msg.str()); + if(state != EventHandlerState::DEBUGGER) + { + if(isFullscreen) + msg << "enabled (" << myBackend->refreshRate() << " Hz, "; + else + msg << "disabled ("; + msg << "Zoom " << myActiveVidMode.zoom * 100 << "%)"; + } + else + { + if(isFullscreen) + msg << "enabled"; + else + msg << "disabled"; + } + showTextMessage(msg.str()); } break; } @@ -1043,7 +1069,7 @@ void FrameBuffer::toggleAdaptRefresh(bool toggle) msg << (isAdaptRefresh ? "enabled" : "disabled"); msg << " (" << myBackend->refreshRate() << " Hz)"; - showMessage(msg.str()); + showTextMessage(msg.str()); } } #endif @@ -1069,7 +1095,7 @@ void FrameBuffer::changeOverscan(int direction) val << (overscan > 0 ? "+" : "" ) << overscan << "%"; else val << "Off"; - myOSystem.frameBuffer().showMessage("Overscan", val.str(), overscan, 0, 10); + myOSystem.frameBuffer().showGaugeMessage("Overscan", val.str(), overscan, 0, 10); } } @@ -1106,9 +1132,9 @@ void FrameBuffer::switchVideoMode(int direction) if(applyVideoMode() == FBInitStatus::Success) { if(fullScreen()) - showMessage(myActiveVidMode.description); + showTextMessage(myActiveVidMode.description); else - showMessage("Zoom", myActiveVidMode.description, myActiveVidMode.zoom, + showGaugeMessage("Zoom", myActiveVidMode.description, myActiveVidMode.zoom, supportedTIAMinZoom(), myTIAMaxZoom); } } @@ -1163,6 +1189,7 @@ FBInitStatus FrameBuffer::applyVideoMode() resetSurfaces(); setCursorState(); + myPendingRender = true; } else Logger::error("ERROR: Couldn't initialize video subsystem"); @@ -1248,9 +1275,9 @@ void FrameBuffer::toggleGrabMouse() myGrabMouse = !myGrabMouse; setCursorState(); myOSystem.settings().setValue("grabmouse", myGrabMouse); - myOSystem.frameBuffer().showMessage(oldState != myGrabMouse ? myGrabMouse - ? "Grab mouse enabled" : "Grab mouse disabled" - : "Grab mouse not allowed while cursor shown"); + myOSystem.frameBuffer().showTextMessage(oldState != myGrabMouse ? myGrabMouse + ? "Grab mouse enabled" : "Grab mouse disabled" + : "Grab mouse not allowed while cursor shown"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1299,8 +1326,6 @@ void FrameBuffer::toggleGrabMouse() kColorInfo TIA output position color kColorTitleBar Title bar color kColorTitleText Title text color - kColorTitleBarLo Disabled title bar color - kColorTitleTextLo Disabled title text color */ UIPaletteArray FrameBuffer::ourStandardUIPalette = { { 0x686868, 0x000000, 0xa38c61, 0xdccfa5, 0x404040, // base @@ -1311,7 +1336,7 @@ UIPaletteArray FrameBuffer::ourStandardUIPalette = { 0xac3410, 0xd55941, // scrollbar 0xc80000, 0xffff80, 0xc8c8ff, 0xc80000, // debugger 0xac3410, 0xd55941, 0xdccfa5, 0xf0f0cf, 0xa38c61, // slider - 0xffffff, 0xac3410, 0xf0f0cf, 0x686868, 0xdccfa5 // other + 0xffffff, 0xac3410, 0xf0f0cf // other } }; @@ -1324,7 +1349,7 @@ UIPaletteArray FrameBuffer::ourClassicUIPalette = { 0x20a020, 0x00ff00, // scrollbar 0xc80000, 0x00ff00, 0xc8c8ff, 0xc80000, // debugger 0x20a020, 0x00ff00, 0x404040, 0x686868, 0x404040, // slider - 0x00ff00, 0x20a020, 0x000000, 0x686868, 0x404040 // other + 0x00ff00, 0x20a020, 0x000000 // other } }; @@ -1337,7 +1362,7 @@ UIPaletteArray FrameBuffer::ourLightUIPalette = { 0xc0c0c0, 0x808080, // scrollbar 0xffc0c0, 0x000000, 0xe00000, 0xc00000, // debugger 0x333333, 0x0078d7, 0xc0c0c0, 0xffffff, 0xc0c0c0, // slider 0xBDDEF9| 0xe1e1e1 | 0xffffff - 0xffffff, 0x333333, 0xf0f0f0, 0x808080, 0xc0c0c0 // other + 0xffffff, 0x333333, 0xf0f0f0 // other } }; @@ -1350,6 +1375,6 @@ UIPaletteArray FrameBuffer::ourDarkUIPalette = { 0x3c3c3c, 0x646464, // scrollbar 0x7f2020, 0xc0c0c0, 0xe00000, 0xc00000, // debugger 0x989898, 0x0059a3, 0x3c3c3c, 0x000000, 0x3c3c3c, // slider - 0x000000, 0x989898, 0x202020, 0x646464, 0x3c3c3c // other + 0x000000, 0x989898, 0x202020 // other } }; diff --git a/src/emucore/FrameBuffer.hxx b/src/emucore/FrameBuffer.hxx index c084c9cf6..0971873f2 100644 --- a/src/emucore/FrameBuffer.hxx +++ b/src/emucore/FrameBuffer.hxx @@ -55,6 +55,12 @@ class FrameBuffer // Zoom level step interval static constexpr float ZOOM_STEPS = 0.25; + enum UpdateMode { + NONE = 0, + REDRAW = 1, + RERENDER = 2 + }; + public: FrameBuffer(OSystem& osystem); ~FrameBuffer(); @@ -84,23 +90,28 @@ class FrameBuffer Updates the display, which depending on the current mode could mean drawing the TIA, any pending menus, etc. */ - void update(bool force = false); + void update(UpdateMode mode = UpdateMode::NONE); /** There is a dedicated update method for emulation mode. - */ + */ void updateInEmulationMode(float framesPerSecond); /** - Shows a message onscreen. + Set pending rendering flag. + */ + void setPendingRender() { myPendingRender = true; } + + /** + Shows a text message onscreen. @param message The message to be shown @param position Onscreen position for the message @param force Force showing this message, even if messages are disabled */ - void showMessage(const string& message, - MessagePosition position = MessagePosition::BottomCenter, - bool force = false); + void showTextMessage(const string& message, + MessagePosition position = MessagePosition::BottomCenter, + bool force = false); /** Shows a message with a gauge bar onscreen. @@ -110,8 +121,8 @@ class FrameBuffer @param minValue The minimal value of the gauge bar @param maxValue The maximal value of the gauge bar */ - void showMessage(const string& message, const string& valueText, - float value, float minValue = 0.F, float maxValue = 100.F); + void showGaugeMessage(const string& message, const string& valueText, + float value, float minValue = 0.F, float maxValue = 100.F); bool messageShown() const; @@ -375,6 +386,18 @@ class FrameBuffer */ void resetSurfaces(); + #ifdef GUI_SUPPORT + /** + Helps to create a basic message onscreen. + + @param message The message to be shown + @param position Onscreen position for the message + @param force Force showing this message, even if messages are disabled + */ + void createMessage(const string& message, MessagePosition position, + bool force = false); + #endif + /** Draw pending messages. @@ -382,6 +405,11 @@ class FrameBuffer */ bool drawMessage(); + /** + Hide pending messages. + */ + void hideMessage(); + /** Draws the frame stats overlay. */ @@ -443,6 +471,9 @@ class FrameBuffer // Supported renderers VariantList myRenderers; + // Flag for pending render + bool myPendingRender{false}; + // The VideoModeHandler class takes responsibility for all video // mode functionality VideoModeHandler myVidModeHandler; @@ -478,6 +509,7 @@ class FrameBuffer ColorId color{kNone}; shared_ptr surface; bool enabled{false}; + bool dirty{false}; bool showGauge{false}; float value{0.0F}; string valueText; diff --git a/src/emucore/FrameBufferConstants.hxx b/src/emucore/FrameBufferConstants.hxx index 134086a0a..7896fd04c 100644 --- a/src/emucore/FrameBufferConstants.hxx +++ b/src/emucore/FrameBufferConstants.hxx @@ -109,9 +109,7 @@ static constexpr ColorId kColorInfo = 287, kColorTitleBar = 288, kColorTitleText = 289, - kColorTitleBarLo = 290, - kColorTitleTextLo = 291, - kNumColors = 292, + kNumColors = 290, kNone = 0 // placeholder to represent default/no color ; diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx index 611439e45..e659c1e5c 100644 --- a/src/emucore/OSystem.cxx +++ b/src/emucore/OSystem.cxx @@ -475,9 +475,9 @@ string OSystem::createConsole(const FilesystemNode& rom, const string& md5sum, { const string& id = myConsole->cartridge().multiCartID(); if(id == "") - myFrameBuffer->showMessage("New console created"); + myFrameBuffer->showTextMessage("New console created"); else - myFrameBuffer->showMessage("Multicart " + + myFrameBuffer->showTextMessage("Multicart " + myConsole->cartridge().detectedType() + ", loading ROM" + id); } buf << "Game console created:" << endl @@ -506,7 +506,7 @@ string OSystem::createConsole(const FilesystemNode& rom, const string& md5sum, msg << myConsole->leftController().name() << "/" << myConsole->rightController().name() << " - " << myConsole->cartridge().detectedType() << " - " << myConsole->getFormatString(); - myFrameBuffer->showMessage(msg.str()); + myFrameBuffer->showTextMessage(msg.str()); } } diff --git a/src/emucore/QuadTari.cxx b/src/emucore/QuadTari.cxx index 64c0cf027..f3fb00cdf 100644 --- a/src/emucore/QuadTari.cxx +++ b/src/emucore/QuadTari.cxx @@ -71,7 +71,7 @@ unique_ptr QuadTari::addController(const Controller::Type type, bool Controller::onMessageCallback callback = [&os = myOSystem](const string& msg) { bool devSettings = os.settings().getBool("dev.settings"); if(os.settings().getBool(devSettings ? "dev.eepromaccess" : "plr.eepromaccess")) - os.frameBuffer().showMessage(msg); + os.frameBuffer().showTextMessage(msg); }; switch(type) diff --git a/src/emucore/TIASurface.cxx b/src/emucore/TIASurface.cxx index 3858817a7..ec9dfe020 100644 --- a/src/emucore/TIASurface.cxx +++ b/src/emucore/TIASurface.cxx @@ -184,7 +184,7 @@ void TIASurface::setNTSC(NTSCFilter::Preset preset, bool show) } myOSystem.settings().setValue("tv.filter", int(preset)); - if(show) myFB.showMessage(buf.str()); + if(show) myFB.showTextMessage(buf.str()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -221,7 +221,7 @@ void TIASurface::setNTSCAdjustable(int direction) setNTSC(NTSCFilter::Preset::CUSTOM); ntsc().selectAdjustable(direction, text, valueText, value); - myOSystem.frameBuffer().showMessage(text, valueText, value); + myOSystem.frameBuffer().showGaugeMessage(text, valueText, value); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -232,7 +232,7 @@ void TIASurface::changeNTSCAdjustable(int adjustable, int direction) setNTSC(NTSCFilter::Preset::CUSTOM); ntsc().changeAdjustable(adjustable, direction, text, valueText, newValue); - myOSystem.frameBuffer().showMessage(text, valueText, newValue); + myOSystem.frameBuffer().showGaugeMessage(text, valueText, newValue); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -243,7 +243,7 @@ void TIASurface::changeCurrentNTSCAdjustable(int direction) setNTSC(NTSCFilter::Preset::CUSTOM); ntsc().changeCurrentAdjustable(direction, text, valueText, newValue); - myOSystem.frameBuffer().showMessage(text, valueText, newValue); + myOSystem.frameBuffer().showGaugeMessage(text, valueText, newValue); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -259,7 +259,7 @@ void TIASurface::setScanlineIntensity(int direction) buf << intensity << "%"; else buf << "Off"; - myFB.showMessage("Scanline intensity", buf.str(), intensity); + myFB.showGaugeMessage("Scanline intensity", buf.str(), intensity); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/AboutDialog.cxx b/src/gui/AboutDialog.cxx index db088d923..426adbeeb 100644 --- a/src/gui/AboutDialog.cxx +++ b/src/gui/AboutDialog.cxx @@ -67,11 +67,12 @@ AboutDialog::AboutDialog(OSystem& osystem, DialogContainer& parent, addCancelWidget(b); xpos = HBORDER; ypos = _th + VBORDER + (buttonHeight - fontHeight) / 2; - myTitle = new StaticTextWidget(this, font, xpos, ypos, _w - xpos * 2, fontHeight, - "", TextAlign::Center); + int bwidth = font.getStringWidth("What's New" + ELLIPSIS) + fontWidth * 2.5; + + myTitle = new StaticTextWidget(this, font, xpos + bwidth, ypos, _w - (xpos + bwidth) * 2, + fontHeight, "", TextAlign::Center); myTitle->setTextColor(kTextColorEm); - int bwidth = font.getStringWidth("What's New" + ELLIPSIS) + fontWidth * 2.5; myWhatsNewButton = new ButtonWidget(this, font, _w - HBORDER - bwidth, ypos - (buttonHeight - fontHeight) / 2, bwidth, buttonHeight, "What's New" + ELLIPSIS, kWhatsNew); diff --git a/src/gui/CheckListWidget.cxx b/src/gui/CheckListWidget.cxx index 079989376..6d80fcc07 100644 --- a/src/gui/CheckListWidget.cxx +++ b/src/gui/CheckListWidget.cxx @@ -46,20 +46,6 @@ CheckListWidget::CheckListWidget(GuiObject* boss, const GUI::Font& font, } } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CheckListWidget::handleMouseEntered() -{ - setFlags(Widget::FLAG_HILITED); - setDirty(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CheckListWidget::handleMouseLeft() -{ - clearFlags(Widget::FLAG_HILITED); - setDirty(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CheckListWidget::setList(const StringList& list, const BoolArray& state) { diff --git a/src/gui/CheckListWidget.hxx b/src/gui/CheckListWidget.hxx index 625875a9d..2ebec0d5d 100644 --- a/src/gui/CheckListWidget.hxx +++ b/src/gui/CheckListWidget.hxx @@ -42,10 +42,6 @@ class CheckListWidget : public ListWidget bool getState(int line); bool getSelectedState() { return getState(_selectedItem); } - protected: - void handleMouseEntered() override; - void handleMouseLeft() override; - private: bool handleEvent(Event::Type e) override; void handleCommand(CommandSender* sender, int cmd, int data, int id) override; diff --git a/src/gui/ContextMenu.cxx b/src/gui/ContextMenu.cxx index 41f049ba2..ae1ee09be 100644 --- a/src/gui/ContextMenu.cxx +++ b/src/gui/ContextMenu.cxx @@ -77,7 +77,7 @@ void ContextMenu::show(uInt32 x, uInt32 y, const Common::Rect& bossRect, int ite } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ContextMenu::center() +void ContextMenu::setPosition() { // First set position according to original coordinates surface().setDstPos(_xorig, _yorig); @@ -346,8 +346,12 @@ int ContextMenu::findItem(int x, int y) const void ContextMenu::drawCurrentSelection(int item) { // Change selection - _selectedOffset = item; - setDirty(); + if(_selectedOffset != item) + { + _selectedOffset = item; + cerr << "ContextMenu" << endl; + setDirty(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -621,5 +625,5 @@ void ContextMenu::drawDialog() s.drawBitmap(_downImg, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, _scrollDnColor, _arrowSize); } - setDirty(); + clearDirty(); } diff --git a/src/gui/ContextMenu.hxx b/src/gui/ContextMenu.hxx index 423ef3074..f1736e517 100644 --- a/src/gui/ContextMenu.hxx +++ b/src/gui/ContextMenu.hxx @@ -71,8 +71,8 @@ class ContextMenu : public Dialog, public CommandSender const string& getSelectedName() const; const Variant& getSelectedTag() const; - /** This dialog uses its own positioning, so we override Dialog::center() */ - void center() override; + /** This dialog uses its own positioning, so we override Dialog::setPosition() */ + void setPosition() override; /** The following methods are used when we want to select *and* send a command for the new selection. They are only to be used diff --git a/src/gui/Dialog.cxx b/src/gui/Dialog.cxx index 85c95349f..1b58d7729 100644 --- a/src/gui/Dialog.cxx +++ b/src/gui/Dialog.cxx @@ -27,6 +27,7 @@ #include "Dialog.hxx" #include "Widget.hxx" #include "TabWidget.hxx" +#include "ToolTip.hxx" #include "ContextMenu.hxx" #include "PopUpWidget.hxx" @@ -49,10 +50,24 @@ Dialog::Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font const string& title, int x, int y, int w, int h) : GuiObject(instance, parent, *this, x, y, w, h), _font(font), - _title(title), - _flags(Widget::FLAG_ENABLED | Widget::FLAG_BORDER | Widget::FLAG_CLEARBG) + _title(title) { + _flags = Widget::FLAG_ENABLED | Widget::FLAG_BORDER | Widget::FLAG_CLEARBG; setTitle(title); + + // Create shading surface + uInt32 data = 0xff000000; + + _shadeSurface = instance.frameBuffer().allocateSurface( + 1, 1, ScalingInterpolation::sharp, &data); + + FBSurface::Attributes& attr = _shadeSurface->attributes(); + + attr.blending = true; + attr.blendalpha = 25; // darken background dialogs by 25% + _shadeSurface->applyAttributes(); + + _toolTip = make_unique(*this, font); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -91,7 +106,7 @@ void Dialog::open() const uInt32 scale = instance().frameBuffer().hidpiScaleFactor(); _surface->setDstSize(_w * scale, _h * scale); - center(); + setPosition(); if(_myTabList.size()) // (Re)-build the focus list to use for all widgets of all tabs @@ -110,8 +125,6 @@ void Dialog::open() loadConfig(); // has to be done AFTER (re)building the focus list _visible = true; - - setDirty(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -128,7 +141,6 @@ void Dialog::close() _visible = false; parent().removeDialog(); - setDirty(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -144,11 +156,36 @@ void Dialog::setTitle(const string& title) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Dialog::center() +void Dialog::setPosition() { positionAt(instance().settings().getInt("dialogpos")); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Dialog::setDirty() +{ + _dirty = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Dialog::setDirtyChain() +{ + _dirtyChain = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Dialog::tick() +{ + // Recursively tick dialog and all child dialogs and widgets + Widget* w = _firstWidget; + + while(w) + { + w->tick(); + w = w->_next; + } +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Dialog::positionAt(uInt32 pos) { @@ -190,26 +227,50 @@ void Dialog::positionAt(uInt32 pos) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Dialog::render() +void Dialog::redraw(bool force) { - if(!_dirty || !isVisible()) - return false; + if(!isVisible()) + return; + + if(force) + setDirty(); // Draw this dialog - center(); + setPosition(); drawDialog(); + // full rendering is caused in dialog container +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Dialog::render() +{ + cerr << " render " << typeid(*this).name() << endl; // Update dialog surface; also render any extra surfaces // Extra surfaces must be rendered afterwards, so they are drawn on top if(_surface->render()) { - mySurfaceStack.applyAll([](shared_ptr& surface){ + mySurfaceStack.applyAll([](shared_ptr& surface) { surface->render(); }); } - _dirty = false; - return true; + // Dialog is still on top if e.g a dialog without title is opened + // (e.g. ContextMenu) + bool onTop = parent().myDialogStack.top() == this + || (parent().myDialogStack.get(parent().myDialogStack.size() - 2) == this + && !parent().myDialogStack.top()->hasTitle()); + //&& typeid(*parent().myDialogStack.top()) == typeid(ContextMenu)) + + if(!onTop) + { + cerr << " shade " << typeid(*this).name() << endl; + + _shadeSurface->setDstRect(_surface->dstRect()); + _shadeSurface->render(); + } + + _toolTip->render(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -305,7 +366,7 @@ void Dialog::setFocus(Widget* w) { // If the click occured inside a widget which is not the currently // focused one, change the focus to that widget. - if(w && w != _focusedWidget && w->wantsFocus()) + if(w && w != _focusedWidget && w->wantsFocus() && w->isEnabled()) { // Redraw widgets for new focus _focusedWidget = Widget::setFocusForChain(this, getFocusList(), w, 0); @@ -371,39 +432,43 @@ void Dialog::drawDialog() FBSurface& s = surface(); - // Dialog is still on top if e.g a ContextMenu is opened - _onTop = parent().myDialogStack.top() == this - || (parent().myDialogStack.get(parent().myDialogStack.size() - 2) == this - && !parent().myDialogStack.top()->hasTitle()); - - if(_flags & Widget::FLAG_CLEARBG) + if(isDirty()) { - // cerr << "Dialog::drawDialog(): w = " << _w << ", h = " << _h << " @ " << &s << endl << endl; - s.fillRect(_x, _y + _th, _w, _h - _th, _onTop ? kDlgColor : kBGColorLo); - if(_th) + cerr << endl << "d"; + //cerr << "*** draw dialog " << typeid(*this).name() << " ***" << endl; + + if(clearsBackground()) { - s.fillRect(_x, _y, _w, _th, _onTop ? kColorTitleBar : kColorTitleBarLo); - s.drawString(_font, _title, _x + _font.getMaxCharWidth() * 1.25, _y + _font.getFontHeight() / 6, - _font.getStringWidth(_title), - _onTop ? kColorTitleText : kColorTitleTextLo); + // cerr << "Dialog::drawDialog(): w = " << _w << ", h = " << _h << " @ " << &s << endl << endl; + + if(hasBackground()) + s.fillRect(_x, _y + _th, _w, _h - _th, kDlgColor); + else + s.invalidateRect(_x, _y + _th, _w, _h - _th); + if(_th) + { + s.fillRect(_x, _y, _w, _th, kColorTitleBar); + s.drawString(_font, _title, _x + _font.getMaxCharWidth() * 1.25, _y + _font.getFontHeight() / 6, + _font.getStringWidth(_title), kColorTitleText); + } } + else { + s.invalidate(); + //cerr << "invalidate " << typeid(*this).name() << endl; + } + if(hasBorder()) // currently only used by Dialog itself + s.frameRect(_x, _y, _w, _h, kColor); + + // Make all child widgets dirty + Widget::setDirtyInChain(_firstWidget); + + clearDirty(); } else - s.invalidate(); - if(_flags & Widget::FLAG_BORDER) // currently only used by Dialog itself - s.frameRect(_x, _y, _w, _h, _onTop ? kColor : kShadowColor); - - // Make all child widget dirty - Widget* w = _firstWidget; - Widget::setDirtyInChain(w); + cerr << endl; // Draw all children - w = _firstWidget; - while(w) - { - w->draw(); - w = w->_next; - } + drawChain(); // Draw outlines for focused widgets // Don't change focus, since this will trigger lost and received @@ -411,9 +476,26 @@ void Dialog::drawDialog() if(_focusedWidget) { _focusedWidget = Widget::setFocusForChain(this, getFocusList(), - _focusedWidget, 0, false); - if(_focusedWidget) - _focusedWidget->draw(); // make sure the highlight color is drawn initially + _focusedWidget, 0, false); + //if(_focusedWidget) + // _focusedWidget->draw(); // make sure the highlight color is drawn initially + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Dialog::drawChain() +{ + // Clear chain *before* drawing, because some widgets may set it again when + // being drawn (e.g. RomListWidget) + clearDirtyChain(); + + Widget* w = _firstWidget; + + while(w) + { + if(w->needsRedraw()) + w->draw(); + w = w->_next; } } @@ -556,6 +638,9 @@ void Dialog::handleMouseMoved(int x, int y) if (w && (w->getFlags() & Widget::FLAG_TRACK_MOUSE)) w->handleMouseMoved(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y)); + + // Update mouse coordinates for tooltips + _toolTip->update(_mouseWidget, Common::Point(x, y)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/Dialog.hxx b/src/gui/Dialog.hxx index 1f6bbec60..f628ec792 100644 --- a/src/gui/Dialog.hxx +++ b/src/gui/Dialog.hxx @@ -26,6 +26,7 @@ class OSystem; class DialogContainer; class TabWidget; class CommandSender; +class ToolTip; #include "Stack.hxx" #include "Widget.hxx" @@ -54,19 +55,21 @@ class Dialog : public GuiObject void close(); bool isVisible() const override { return _visible; } - bool isOnTop() const { return _onTop; } + bool isOnTop() const { return true; } // TODO: remove - virtual void center(); + virtual void setPosition(); virtual void drawDialog(); virtual void loadConfig() { } virtual void saveConfig() { } virtual void setDefaults() { } - // A dialog being dirty indicates that its underlying surface needs to be - // redrawn and then re-rendered; this is taken care of in ::render() - void setDirty() override { _dirty = true; } - bool isDirty() const { return _dirty; } - bool render(); + void setDirty() override; + void setDirtyChain() override; + void redraw(bool force = false); + void drawChain() override; + void render(); + + void tick() override; void addFocusWidget(Widget* w) override; void addToFocusList(WidgetArray& list) override; @@ -89,10 +92,6 @@ class Dialog : public GuiObject */ void addSurface(const shared_ptr& surface); - void setFlags(int flags) { _flags |= flags; setDirty(); } - void clearFlags(int flags) { _flags &= ~flags; setDirty(); } - int getFlags() const { return _flags; } - void setTitle(const string& title); bool hasTitle() { return !_title.empty(); } @@ -124,6 +123,8 @@ class Dialog : public GuiObject */ bool shouldResize(uInt32& w, uInt32& h) const; + ToolTip& tooltip() { return *_toolTip; } + protected: void draw() override { } void releaseFocus() override; @@ -197,11 +198,11 @@ class Dialog : public GuiObject Widget* _cancelWidget{nullptr}; bool _visible{false}; - bool _onTop{true}; bool _processCancel{false}; string _title; int _th{0}; int _layer{0}; + unique_ptr _toolTip; Common::FixedStack> mySurfaceStack; @@ -232,10 +233,9 @@ class Dialog : public GuiObject WidgetArray _buttonGroup; shared_ptr _surface; + shared_ptr _shadeSurface; int _tabID{0}; - int _flags{0}; - bool _dirty{false}; uInt32 _max_w{0}; // maximum wanted width uInt32 _max_h{0}; // maximum wanted height diff --git a/src/gui/DialogContainer.cxx b/src/gui/DialogContainer.cxx index 950d71c06..fcdac91a9 100644 --- a/src/gui/DialogContainer.cxx +++ b/src/gui/DialogContainer.cxx @@ -89,31 +89,57 @@ void DialogContainer::updateTime(uInt64 time) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool DialogContainer::draw(bool full) +void DialogContainer::draw(bool full) { if(myDialogStack.empty()) - return false; + return; + + cerr << "draw " << full << " " << typeid(*this).name() << endl; // Make the top dialog dirty if a full redraw is requested - if(full) - myDialogStack.top()->setDirty(); + //if(full) + // myDialogStack.top()->setDirty(); - // If the top dialog is dirty, then all below it must be redrawn too - const bool dirty = needsRedraw(); - - myDialogStack.applyAll([&](Dialog*& d){ - if(dirty) - d->setDirty(); - full |= d->render(); + // Draw and render all dirty dialogs + myDialogStack.applyAll([&](Dialog*& d) { + if(full || d->needsRedraw()) + d->redraw(full); }); + // Always render all surfaces, bottom to top + render(); +} - return full; +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void DialogContainer::tick() +{ + if(!myDialogStack.empty()) + myDialogStack.top()->tick(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void DialogContainer::render() +{ + if(myDialogStack.empty()) + return; + + cerr << "full re-render " << typeid(*this).name() << endl; + + // Make sure we start in a clean state (with zero'ed buffers) + if(!myOSystem.eventHandler().inTIAMode()) + myOSystem.frameBuffer().clear(); + + // Render all dialogs + myDialogStack.applyAll([&](Dialog*& d) { + d->render(); + }); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool DialogContainer::needsRedraw() const { - return !myDialogStack.empty() ? myDialogStack.top()->isDirty() : false; + return !myDialogStack.empty() + ? myDialogStack.top()->needsRedraw() + : false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -129,8 +155,8 @@ int DialogContainer::addDialog(Dialog* d) const uInt32 scale = myOSystem.frameBuffer().hidpiScaleFactor(); if(uInt32(d->getWidth() * scale) > r.w() || uInt32(d->getHeight() * scale) > r.h()) - myOSystem.frameBuffer().showMessage( - "Unable to show dialog box; FIX THE CODE"); + myOSystem.frameBuffer().showTextMessage( + "Unable to show dialog box; FIX THE CODE", MessagePosition::BottomCenter, true); else { d->setDirty(); @@ -144,9 +170,11 @@ void DialogContainer::removeDialog() { if(!myDialogStack.empty()) { + cerr << "remove dialog " << typeid(*myDialogStack.top()).name() << endl; myDialogStack.pop(); - if(!myDialogStack.empty()) - myDialogStack.top()->setDirty(); + + // Inform the frame buffer that it has to render all surfaces + myOSystem.frameBuffer().setPendingRender(); } } @@ -157,6 +185,9 @@ void DialogContainer::reStack() while(!myDialogStack.empty()) myDialogStack.top()->close(); + // Make sure that all surfaces are cleared + myOSystem.frameBuffer().clear(); + baseDialog()->open(); // Reset all continuous events diff --git a/src/gui/DialogContainer.hxx b/src/gui/DialogContainer.hxx index cc50e23b6..134d82e55 100644 --- a/src/gui/DialogContainer.hxx +++ b/src/gui/DialogContainer.hxx @@ -120,11 +120,19 @@ class DialogContainer void handleJoyHatEvent(int stick, int hat, JoyHatDir hdir, int button); /** - Draw the stack of menus (full indicates to redraw all items). - - @return Answers whether any drawing actually occurred. + Tick the dialog and all its widgets. */ - bool draw(bool full = false); + void tick(); + + /** + Draw the stack of menus (full indicates to redraw all items). + */ + void draw(bool full = false); + + /** + Render the stack of menus. + */ + void render(); /** Answers whether a full redraw is required. diff --git a/src/gui/EditTextWidget.cxx b/src/gui/EditTextWidget.cxx index 6105e70d3..61785bd78 100644 --- a/src/gui/EditTextWidget.cxx +++ b/src/gui/EditTextWidget.cxx @@ -18,6 +18,7 @@ #include "OSystem.hxx" #include "FBSurface.hxx" #include "Dialog.hxx" +#include "ToolTip.hxx" #include "Font.hxx" #include "EditTextWidget.hxx" @@ -48,20 +49,6 @@ void EditTextWidget::setText(const string& str, bool changed) _changed = changed; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EditTextWidget::handleMouseEntered() -{ - setFlags(Widget::FLAG_HILITED); - setDirty(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EditTextWidget::handleMouseLeft() -{ - clearFlags(Widget::FLAG_HILITED); - setDirty(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EditTextWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount) { diff --git a/src/gui/EditTextWidget.hxx b/src/gui/EditTextWidget.hxx index fe063e042..e7c21beff 100644 --- a/src/gui/EditTextWidget.hxx +++ b/src/gui/EditTextWidget.hxx @@ -54,8 +54,6 @@ class EditTextWidget : public EditableWidget Common::Rect getEditRect() const override; void handleMouseDown(int x, int y, MouseButton b, int clickCount) override; - void handleMouseEntered() override; - void handleMouseLeft() override; protected: string _backupString; diff --git a/src/gui/EditableWidget.cxx b/src/gui/EditableWidget.cxx index 16b371813..e015dd899 100644 --- a/src/gui/EditableWidget.cxx +++ b/src/gui/EditableWidget.cxx @@ -22,6 +22,7 @@ #include "OSystem.hxx" #include "EventHandler.hxx" #include "UndoHandler.hxx" +#include "ToolTip.hxx" #include "EditableWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -63,6 +64,28 @@ void EditableWidget::setText(const string& str, bool) setDirty(); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void EditableWidget::tick() +{ + if(_hasFocus && isEditable() && _editMode && isVisible() && _boss->isVisible()) + { + _caretTimer++; + if(_caretTimer > 40) // switch every 2/3rd seconds + { + _caretTimer = 0; + _caretEnabled = !_caretEnabled; + setDirty(); + } + } + Widget::tick(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool EditableWidget::wantsToolTip() const +{ + return !(_hasFocus && isEditable() && _editMode) && Widget::wantsToolTip(); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EditableWidget::setEditable(bool editable, bool hiliteBG) { @@ -79,6 +102,14 @@ void EditableWidget::setEditable(bool editable, bool hiliteBG) } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void EditableWidget::receivedFocusWidget() +{ + _caretTimer = 0; + _caretEnabled = true; + dialog().tooltip().hide(); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EditableWidget::lostFocusWidget() { @@ -107,7 +138,7 @@ bool EditableWidget::handleText(char text) if(tryInsertChar(text, _caretPos)) { - _caretPos++; + setCaretPos(_caretPos + 1); sendCommand(EditableWidget::kChangedCmd, 0, _id); setDirty(); return true; @@ -265,7 +296,7 @@ bool EditableWidget::handleKeyDown(StellaKey key, StellaMod mod) { // Put caret at last difference myUndoHandler->lastDiff(_editString, oldString); - _caretPos = myUndoHandler->lastDiff(_editString, oldString); + setCaretPos(myUndoHandler->lastDiff(_editString, oldString)); _selectSize = 0; sendCommand(EditableWidget::kChangedCmd, key, _id); } @@ -316,22 +347,16 @@ void EditableWidget::drawCaretSelection() if (!_editable || !isVisible() || !_boss->isVisible() || !_hasFocus) return; - const Common::Rect& editRect = getEditRect(); - int x = editRect.x(); - int y = editRect.y(); - - x += getCaretOffset(); - - x += _x; - y += _y; - - FBSurface& s = _boss->dialog().surface(); - s.vLine(x, y + 2, y + editRect.h() - 2, kTextColorHi); - s.vLine(x-1, y + 2, y + editRect.h() - 2, kTextColorHi); - + // Draw the selection if(_selectSize) { + FBSurface& s = _boss->dialog().surface(); + const Common::Rect& editRect = getEditRect(); + int x = editRect.x(); + int y = editRect.y(); + string text = selectString(); + x = editRect.x(); y = editRect.y(); int w = editRect.w(); @@ -355,9 +380,27 @@ void EditableWidget::drawCaretSelection() y += _y; 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); } + + // Draw the caret + if(_caretEnabled ^ (_selectSize != 0)) + { + FBSurface& s = _boss->dialog().surface(); + const Common::Rect& editRect = getEditRect(); + int x = editRect.x(); + int y = editRect.y(); + ColorId color = _caretEnabled ? kTextColorHi : kTextColorInv; + + x += getCaretOffset(); + 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); + clearDirty(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -366,6 +409,9 @@ bool EditableWidget::setCaretPos(int newPos) assert(newPos >= 0 && newPos <= int(_editString.size())); _caretPos = newPos; + _caretTimer = 0; + _caretEnabled = true; + return adjustOffset(); } @@ -375,6 +421,8 @@ bool EditableWidget::moveCaretPos(int direction) if(setCaretPos(_caretPos + direction)) { _selectSize -= direction; + _caretTimer = 0; + _caretEnabled = true; return true; } return false; @@ -452,6 +500,7 @@ bool EditableWidget::killChar(int direction, bool addEdit) { myUndoHandler->endChars(_editString); _editString.erase(_caretPos, 1); + setCaretPos(_caretPos); if(addEdit) myUndoHandler->doo(_editString); @@ -556,7 +605,7 @@ bool EditableWidget::moveWord(int direction, bool select) if(select) _selectSize++; } - _caretPos = currentPos; + setCaretPos(currentPos); handled = true; } else if(direction == +1) // move to first character of next word @@ -575,7 +624,7 @@ bool EditableWidget::moveWord(int direction, bool select) if(select) _selectSize--; } - _caretPos = currentPos; + setCaretPos(currentPos); handled = true; } @@ -630,6 +679,7 @@ bool EditableWidget::killSelectedText(bool addEdit) _selectSize = -_selectSize; } _editString.erase(_caretPos, _selectSize); + setCaretPos(_caretPos); _selectSize = 0; if(addEdit) myUndoHandler->doo(_editString); @@ -689,7 +739,7 @@ bool EditableWidget::pasteSelectedText() _editString.insert(_caretPos, buf.str()); // position cursor at the end of pasted text - _caretPos += int(buf.str().length()); + setCaretPos(_caretPos + int(buf.str().length())); if(selected || !pasted.empty()) { diff --git a/src/gui/EditableWidget.hxx b/src/gui/EditableWidget.hxx index fda92ccdf..3ffe09a65 100644 --- a/src/gui/EditableWidget.hxx +++ b/src/gui/EditableWidget.hxx @@ -65,7 +65,10 @@ class EditableWidget : public Widget, public CommandSender void setTextFilter(const TextFilter& filter) { _filter = filter; } protected: + void receivedFocusWidget() override; void lostFocusWidget() override; + void tick() override; + bool wantsToolTip() const override; virtual void startEditMode() { setFlags(Widget::FLAG_WANTS_RAWDATA); } virtual void endEditMode() { clearFlags(Widget::FLAG_WANTS_RAWDATA); } @@ -110,6 +113,8 @@ class EditableWidget : public Widget, public CommandSender unique_ptr myUndoHandler; int _caretPos{0}; + int _caretTimer{0}; + bool _caretEnabled{true}; // Size of current selected text // 0 = no selection @@ -119,6 +124,8 @@ class EditableWidget : public Widget, public CommandSender protected: int _editScrollOffset{0}; + bool _editMode{true}; + int _dyText{0}; private: TextFilter _filter; diff --git a/src/gui/GameInfoDialog.cxx b/src/gui/GameInfoDialog.cxx index 5a3c4b1b5..57ad900d2 100644 --- a/src/gui/GameInfoDialog.cxx +++ b/src/gui/GameInfoDialog.cxx @@ -855,12 +855,12 @@ void GameInfoDialog::saveCurrentPropertiesToDisk() propfile /= myGameFile.getNameWithExt(".pro"); propfile.write(out); - instance().frameBuffer().showMessage("Properties saved to " + - propfile.getShortPath()); + instance().frameBuffer().showTextMessage("Properties saved to " + + propfile.getShortPath()); } catch(...) { - instance().frameBuffer().showMessage("Error saving properties"); + instance().frameBuffer().showTextMessage("Error saving properties"); } } diff --git a/src/gui/GuiObject.hxx b/src/gui/GuiObject.hxx index 77392209c..85424a670 100644 --- a/src/gui/GuiObject.hxx +++ b/src/gui/GuiObject.hxx @@ -41,6 +41,21 @@ class GuiObject : public CommandReceiver friend class Widget; friend class DialogContainer; + public: + enum : uInt32 { + FLAG_ENABLED = 1 << 0, + FLAG_INVISIBLE = 1 << 1, + FLAG_HILITED = 1 << 2, + FLAG_BORDER = 1 << 3, + FLAG_CLEARBG = 1 << 4, + FLAG_TRACK_MOUSE = 1 << 5, + FLAG_RETAIN_FOCUS = 1 << 6, + FLAG_WANTS_TAB = 1 << 7, + FLAG_WANTS_RAWDATA = 1 << 8, + FLAG_NOBG = 1 << 9, + FLAG_MOUSE_FOCUS = 1 << 10 + }; + public: // The commands generated by various widgets enum { @@ -77,7 +92,41 @@ class GuiObject : public CommandReceiver virtual void setHeight(int h) { _h = h; } virtual bool isVisible() const = 0; + virtual void setDirty() = 0; + virtual void setDirtyChain() = 0; + void clearDirty() { _dirty = false; } + void clearDirtyChain() { _dirtyChain = false; } + bool isDirty() const { return _dirty; } + bool isChainDirty() const { return _dirtyChain; } + + // The GUI indicates if its underlying surface needs to be redrawn + // and then re-rendered + virtual bool needsRedraw() { return isDirty() || isChainDirty(); } + + virtual void tick() = 0; + + void setFlags(uInt32 flags, bool updateDirty = true) + { + uInt32 oldFlags = _flags; + + _flags |= flags; + if(updateDirty && oldFlags != _flags) + setDirty(); + } + void clearFlags(uInt32 flags, bool updateDirty = true) + { + uInt32 oldFlags = _flags; + + _flags &= ~flags; + if(updateDirty && oldFlags != _flags) + setDirty(); + } + uInt32 getFlags() const { return _flags; } + + bool hasBorder() const { return _flags & FLAG_BORDER; } + bool clearsBackground() const { return _flags & FLAG_CLEARBG; } + bool hasBackground() const { return !(_flags & FLAG_NOBG); } /** Add given widget(s) to the focus list */ virtual void addFocusWidget(Widget* w) = 0; @@ -96,6 +145,7 @@ class GuiObject : public CommandReceiver protected: virtual void releaseFocus() = 0; virtual void draw() = 0; + virtual void drawChain() = 0; private: OSystem& myOSystem; @@ -103,9 +153,12 @@ class GuiObject : public CommandReceiver Dialog& myDialog; protected: - int _x{0}, _y{0}, _w{0}, _h{0}; + int _x{0}, _y{0}, _w{0}, _h{0}; + bool _dirty{false}; + bool _dirtyChain{false}; + uInt32 _flags{0}; - Widget* _firstWidget{nullptr}; + Widget* _firstWidget{nullptr}; WidgetArray _focusList; private: diff --git a/src/gui/InputTextDialog.cxx b/src/gui/InputTextDialog.cxx index 12e7a66d4..54b656a67 100644 --- a/src/gui/InputTextDialog.cxx +++ b/src/gui/InputTextDialog.cxx @@ -130,7 +130,7 @@ void InputTextDialog::show(uInt32 x, uInt32 y, const Common::Rect& bossRect) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void InputTextDialog::center() +void InputTextDialog::setPosition() { if(!myEnableCenter) { @@ -144,7 +144,7 @@ void InputTextDialog::center() surface().setDstPos(myXOrig, myYOrig); } else - Dialog::center(); + Dialog::setPosition(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/InputTextDialog.hxx b/src/gui/InputTextDialog.hxx index e89e00651..a06ca4446 100644 --- a/src/gui/InputTextDialog.hxx +++ b/src/gui/InputTextDialog.hxx @@ -58,7 +58,7 @@ class InputTextDialog : public Dialog, public CommandSender void handleCommand(CommandSender* sender, int cmd, int data, int id) override; /** This dialog uses its own positioning, so we override Dialog::center() */ - void center() override; + void setPosition() override; private: vector myInput; diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx index 3f6ddcec8..405be5745 100644 --- a/src/gui/LauncherDialog.cxx +++ b/src/gui/LauncherDialog.cxx @@ -30,6 +30,7 @@ #include "StellaSettingsDialog.hxx" #include "WhatsNewDialog.hxx" #include "MessageBox.hxx" +#include "ToolTip.hxx" #include "OSystem.hxx" #include "FrameBuffer.hxx" #include "FBSurface.hxx" @@ -79,6 +80,8 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent, const string& lblAllFiles = "Show all files"; const string& lblFound = "XXXX items found"; + tooltip().setFont(font); + lwidth = font.getStringWidth(lblRom); lwidth2 = font.getStringWidth(lblAllFiles) + CheckboxWidget::boxSize(font); int lwidth3 = font.getStringWidth(lblFilter); @@ -120,12 +123,14 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent, // Show the filter input field xpos -= fwidth + LBL_GAP; myPattern = new EditTextWidget(this, font, xpos, ypos - 2, fwidth, lineHeight, ""); + myPattern->setToolTip("Enter filter text to reduce file list."); // Show the "Filter" label xpos -= lwidth3 + LBL_GAP; new StaticTextWidget(this, font, xpos, ypos, lblFilter); // Show the checkbox for all files xpos -= lwidth2 + LBL_GAP * 3; myAllFiles = new CheckboxWidget(this, font, xpos, ypos, lblAllFiles, kAllfilesCmd); + myAllFiles->setToolTip("Uncheck to show ROM files only."); wid.push_back(myAllFiles); wid.push_back(myPattern); } @@ -178,6 +183,7 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent, #ifndef BSPF_MACOS myStartButton = new ButtonWidget(this, font, xpos, ypos, (buttonWidth + 0) / 4, buttonHeight, "Select", kLoadROMCmd); + myStartButton->setToolTip("Start emulation of selected ROM."); wid.push_back(myStartButton); xpos += (buttonWidth + 0) / 4 + BUTTON_GAP; @@ -562,6 +568,7 @@ void LauncherDialog::handleMouseDown(int x, int y, MouseButton b, int clickCount // Grab right mouse button for context menu, send left to base class if(b == MouseButton::RIGHT) { + dialog().tooltip().hide(); // Dynamically create context menu for ROM list options VariantList items; @@ -662,7 +669,7 @@ void LauncherDialog::loadRom() instance().settings().setValue("romdir", currentNode().getParent().getShortPath()); } else - instance().frameBuffer().showMessage(result, MessagePosition::MiddleCenter, true); + instance().frameBuffer().showTextMessage(result, MessagePosition::MiddleCenter, true); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/LauncherDialog.hxx b/src/gui/LauncherDialog.hxx index 0dd9b898d..cc265928b 100644 --- a/src/gui/LauncherDialog.hxx +++ b/src/gui/LauncherDialog.hxx @@ -101,7 +101,7 @@ class LauncherDialog : public Dialog static constexpr int MIN_ROMINFO_ROWS = 7; // full lines static constexpr int MIN_ROMINFO_LINES = 4; // extra lines - void center() override { positionAt(0); } + void setPosition() override { positionAt(0); } void handleKeyDown(StellaKey key, StellaMod mod, bool repeated) override; void handleMouseDown(int x, int y, MouseButton b, int clickCount) override; void handleCommand(CommandSender* sender, int cmd, int data, int id) override; diff --git a/src/gui/ListWidget.cxx b/src/gui/ListWidget.cxx index d8b0bc458..1cf6c7e0d 100644 --- a/src/gui/ListWidget.cxx +++ b/src/gui/ListWidget.cxx @@ -36,6 +36,8 @@ ListWidget::ListWidget(GuiObject* boss, const GUI::Font& font, _textcolor = kTextColor; _textcolorhi = kTextColor; + _editMode = false; + _cols = w / _fontWidth; _rows = h / _lineHeight; diff --git a/src/gui/ListWidget.hxx b/src/gui/ListWidget.hxx index 96d4f036c..1fba03f01 100644 --- a/src/gui/ListWidget.hxx +++ b/src/gui/ListWidget.hxx @@ -99,7 +99,6 @@ class ListWidget : public EditableWidget int _currentPos{0}; int _selectedItem{-1}; int _highlightedItem{-1}; - bool _editMode{false}; bool _useScrollbar{true}; ScrollBarWidget* _scrollBar{nullptr}; diff --git a/src/gui/LoggerDialog.cxx b/src/gui/LoggerDialog.cxx index bf53d80a0..3f94e093e 100644 --- a/src/gui/LoggerDialog.cxx +++ b/src/gui/LoggerDialog.cxx @@ -123,12 +123,12 @@ void LoggerDialog::saveLogFile() { stringstream out; out << Logger::instance().logMessages(); - instance().frameBuffer().showMessage("Saving log file to " + node.getShortPath()); + instance().frameBuffer().showTextMessage("Saving log file to " + node.getShortPath()); node.write(out); } catch(...) { - instance().frameBuffer().showMessage("Error saving log file to " + node.getShortPath()); + instance().frameBuffer().showTextMessage("Error saving log file to " + node.getShortPath()); } } diff --git a/src/gui/OptionsDialog.hxx b/src/gui/OptionsDialog.hxx index 55d4d4b0d..bd13c3f37 100644 --- a/src/gui/OptionsDialog.hxx +++ b/src/gui/OptionsDialog.hxx @@ -52,7 +52,7 @@ class OptionsDialog : public Dialog void handleCommand(CommandSender* sender, int cmd, int data, int id) override; private: - unique_ptr myVideoDialog; + unique_ptr myVideoDialog; unique_ptr myEmulationDialog; unique_ptr myInputDialog; unique_ptr myUIDialog; diff --git a/src/gui/PopUpWidget.cxx b/src/gui/PopUpWidget.cxx index 8540f2df3..45f79e55b 100644 --- a/src/gui/PopUpWidget.cxx +++ b/src/gui/PopUpWidget.cxx @@ -20,6 +20,8 @@ #include "FBSurface.hxx" #include "Font.hxx" #include "ContextMenu.hxx" +#include "Dialog.hxx" +#include "ToolTip.hxx" #include "DialogContainer.hxx" #include "PopUpWidget.hxx" @@ -122,6 +124,7 @@ void PopUpWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount) { if(isEnabled() && !myMenu->isVisible()) { + dialog().tooltip().hide(); // Add menu just underneath parent widget myMenu->show(getAbsX() + _labelWidth, getAbsY() + getHeight(), dialog().surface().dstRect(), myMenu->getSelected()); @@ -158,20 +161,6 @@ void PopUpWidget::handleMouseWheel(int x, int y, int direction) } } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void PopUpWidget::handleMouseEntered() -{ - setFlags(Widget::FLAG_HILITED); - setDirty(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void PopUpWidget::handleMouseLeft() -{ - clearFlags(Widget::FLAG_HILITED); - setDirty(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool PopUpWidget::handleEvent(Event::Type e) { @@ -277,7 +266,7 @@ void PopUpWidget::drawWidget(bool hilite) // Fill the background ColorId bgCol = isEditable() ? kWidColor : kDlgColor; - s.fillRect(x + 1, _y + 1, w - (_arrowWidth * 2 - 0), _h - 2, + s.fillRect(x + 1, _y + 1, w - (_arrowWidth * 2 - 1), _h - 2, onTop ? _changed ? kDbgChangedColor : bgCol : kDlgColor); s.fillRect(x + w - (_arrowWidth * 2 - 2), _y + 1, (_arrowWidth * 2 - 3), _h - 2, onTop ? isEnabled() && hilite ? kBtnColorHi : bgCol : kBGColorLo); diff --git a/src/gui/PopUpWidget.hxx b/src/gui/PopUpWidget.hxx index deb0af30c..babe28686 100644 --- a/src/gui/PopUpWidget.hxx +++ b/src/gui/PopUpWidget.hxx @@ -70,8 +70,6 @@ class PopUpWidget : public EditableWidget protected: void handleMouseDown(int x, int y, MouseButton b, int clickCount) override; void handleMouseWheel(int x, int y, int direction) override; - void handleMouseEntered() override; - void handleMouseLeft() override; bool handleEvent(Event::Type e) override; void handleCommand(CommandSender* sender, int cmd, int data, int id) override; diff --git a/src/gui/RomInfoWidget.cxx b/src/gui/RomInfoWidget.cxx index 01b30015b..c2068a4d1 100644 --- a/src/gui/RomInfoWidget.cxx +++ b/src/gui/RomInfoWidget.cxx @@ -172,6 +172,8 @@ void RomInfoWidget::parseProperties(const FilesystemNode& node) myRomInfo.push_back("Controllers: " + (left + " (left), " + right + " (right)")); if (bsDetected != "") myRomInfo.push_back("Type: " + Bankswitch::typeToDesc(Bankswitch::nameToType(bsDetected))); + + setDirty(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -227,4 +229,5 @@ void RomInfoWidget::drawWidget(bool hilite) onTop ? _textcolor : _shadowcolor); ypos += _font.getLineHeight() + (lines - 1) * _font.getFontHeight(); } + clearDirty(); } diff --git a/src/gui/ScrollBarWidget.cxx b/src/gui/ScrollBarWidget.cxx index 335b716de..73e89bc78 100644 --- a/src/gui/ScrollBarWidget.cxx +++ b/src/gui/ScrollBarWidget.cxx @@ -240,19 +240,11 @@ void ScrollBarWidget::checkBounds(int old_pos) } } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ScrollBarWidget::handleMouseEntered() -{ - setFlags(Widget::FLAG_HILITED); - setDirty(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ScrollBarWidget::handleMouseLeft() { _part = Part::None; - clearFlags(Widget::FLAG_HILITED); - setDirty(); + Widget::handleMouseLeft(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -315,6 +307,7 @@ void ScrollBarWidget::drawWidget(bool hilite) s.fillRect(_x + 1, _y + _sliderPos - 1, _w - 2, _sliderHeight + 2, onTop ? (hilite && _part == Part::Slider) ? kScrollColorHi : kScrollColor : kColor); } + clearDirty(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/ScrollBarWidget.hxx b/src/gui/ScrollBarWidget.hxx index 1d1b4c34a..0c29fde17 100644 --- a/src/gui/ScrollBarWidget.hxx +++ b/src/gui/ScrollBarWidget.hxx @@ -49,7 +49,6 @@ class ScrollBarWidget : public Widget, public CommandSender void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; void handleMouseMoved(int x, int y) override; bool handleMouseClicks(int x, int y, MouseButton b) override; - void handleMouseEntered() override; void handleMouseLeft() override; void setArrows(); diff --git a/src/gui/StellaSettingsDialog.cxx b/src/gui/StellaSettingsDialog.cxx index fc3d3e4d9..06ff03b3e 100644 --- a/src/gui/StellaSettingsDialog.cxx +++ b/src/gui/StellaSettingsDialog.cxx @@ -268,6 +268,7 @@ void StellaSettingsDialog::saveConfig() settings.setValue("uipalette", myThemePopup->getSelectedTag().toString()); instance().frameBuffer().setUIPalette(); + instance().frameBuffer().update(FrameBuffer::UpdateMode::REDRAW); // Dialog position settings.setValue("dialogpos", myPositionPopup->getSelectedTag().toString()); diff --git a/src/gui/StringListWidget.cxx b/src/gui/StringListWidget.cxx index 4171632cb..536240b8a 100644 --- a/src/gui/StringListWidget.cxx +++ b/src/gui/StringListWidget.cxx @@ -51,17 +51,44 @@ void StringListWidget::setList(const StringList& list) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void StringListWidget::handleMouseEntered() +int StringListWidget::getToolTipIndex(const Common::Point& pos) const { - setFlags(Widget::FLAG_HILITED); - setDirty(); + int idx = (pos.y - getAbsY()) / _lineHeight + _currentPos; + + if(idx >= int(_list.size())) + return -1; + else + return idx; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void StringListWidget::handleMouseLeft() +string StringListWidget::getToolTip(const Common::Point& pos) const { - clearFlags(Widget::FLAG_HILITED); - setDirty(); + Common::Rect rect = getEditRect(); + int idx = getToolTipIndex(pos); + + if(idx < 0) + return EmptyString; + + const string value = _list[idx]; + + if(uInt32(_font.getStringWidth(value)) > rect.w()) + return _toolTipText + value; + else + return _toolTipText; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool StringListWidget::changedToolTip(const Common::Point& oldPos, + const Common::Point& newPos) const +{ + bool ch = getToolTipIndex(oldPos) != getToolTipIndex(newPos) + && getToolTip(oldPos) != getToolTip(newPos); + + if(ch) + cerr << "changed" << endl; + + return ch; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/StringListWidget.hxx b/src/gui/StringListWidget.hxx index eed279aeb..f0bf8544b 100644 --- a/src/gui/StringListWidget.hxx +++ b/src/gui/StringListWidget.hxx @@ -32,9 +32,16 @@ class StringListWidget : public ListWidget void setList(const StringList& list); bool wantsFocus() const override { return true; } + string getToolTip(const Common::Point& pos) const override; + bool changedToolTip(const Common::Point& oldPos, const Common::Point& newPos) const override; + protected: - void handleMouseEntered() override; - void handleMouseLeft() override; + // 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; @@ -42,6 +49,9 @@ class StringListWidget : public ListWidget bool _hilite{false}; int _textOfs{0}; + private: + int getToolTipIndex(const Common::Point& pos) const; + private: // Following constructors and assignment operators not supported StringListWidget() = delete; diff --git a/src/gui/TabWidget.cxx b/src/gui/TabWidget.cxx index f1b92b876..28b383f6c 100644 --- a/src/gui/TabWidget.cxx +++ b/src/gui/TabWidget.cxx @@ -213,20 +213,6 @@ void TabWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount) } } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void TabWidget::handleMouseEntered() -{ - setFlags(Widget::FLAG_HILITED); - setDirty(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void TabWidget::handleMouseLeft() -{ - clearFlags(Widget::FLAG_HILITED); - setDirty(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TabWidget::handleCommand(CommandSender* sender, int cmd, int data, int id) { @@ -275,39 +261,45 @@ void TabWidget::drawWidget(bool hilite) // The tab widget is strange in that it acts as both a widget (obviously) // and a dialog (it contains other widgets). Because of the latter, // it must assume responsibility for refreshing all its children. - Widget::setDirtyInChain(_tabs[_activeTab].firstWidget); - FBSurface& s = dialog().surface(); - bool onTop = _boss->dialog().isOnTop(); - - // Iterate over all tabs and draw them - int i, x = _x + kTabLeftOffset; - for (i = 0; i < int(_tabs.size()); ++i) + if(isDirty()) { - int tabWidth = _tabs[i].tabWidth ? _tabs[i].tabWidth : _tabWidth; - ColorId fontcolor = _tabs[i].enabled && onTop? kTextColor : kColor; - int yOffset = (i == _activeTab) ? 0 : 1; - s.fillRect(x, _y + 1, tabWidth, _tabHeight - 1, - (i == _activeTab) - ? onTop ? kDlgColor : kBGColorLo - : onTop ? kBGColorHi : kDlgColor); // ? kWidColor : kDlgColor - s.drawString(_font, _tabs[i].title, x + kTabPadding + yOffset, - _y + yOffset + (_tabHeight - _lineHeight - 1), - tabWidth - 2 * kTabPadding, fontcolor, TextAlign::Center); - if(i == _activeTab) + FBSurface& s = dialog().surface(); + bool onTop = _boss->dialog().isOnTop(); + + // Iterate over all tabs and draw them + int i, x = _x + kTabLeftOffset; + for(i = 0; i < int(_tabs.size()); ++i) { - s.hLine(x, _y, x + tabWidth - 1, onTop ? kWidColor : kDlgColor); - s.vLine(x + tabWidth, _y + 1, _y + _tabHeight - 1, onTop ? kBGColorLo : kColor); + int tabWidth = _tabs[i].tabWidth ? _tabs[i].tabWidth : _tabWidth; + ColorId fontcolor = _tabs[i].enabled && onTop ? kTextColor : kColor; + int yOffset = (i == _activeTab) ? 0 : 1; + s.fillRect(x, _y + 1, tabWidth, _tabHeight - 1, + (i == _activeTab) + ? onTop ? kDlgColor : kBGColorLo + : onTop ? kBGColorHi : kDlgColor); // ? kWidColor : kDlgColor + s.drawString(_font, _tabs[i].title, x + kTabPadding + yOffset, + _y + yOffset + (_tabHeight - _lineHeight - 1), + tabWidth - 2 * kTabPadding, fontcolor, TextAlign::Center); + if(i == _activeTab) + { + s.hLine(x, _y, x + tabWidth - 1, onTop ? kWidColor : kDlgColor); + s.vLine(x + tabWidth, _y + 1, _y + _tabHeight - 1, onTop ? kBGColorLo : kColor); + } + else + s.hLine(x, _y + _tabHeight, x + tabWidth, onTop ? kWidColor : kDlgColor); + + x += tabWidth + kTabSpacing; } - else - s.hLine(x, _y + _tabHeight, x + tabWidth, onTop ? kWidColor : kDlgColor); - x += tabWidth + kTabSpacing; + // fill empty right space + s.hLine(x - kTabSpacing + 1, _y + _tabHeight, _x + _w - 1, onTop ? kWidColor : kDlgColor); + s.hLine(_x, _y + _h - 1, _x + _w - 1, onTop ? kBGColorLo : kColor); + + clearDirty(); + // Make all child widgets of currently active tab dirty + Widget::setDirtyInChain(_tabs[_activeTab].firstWidget); } - - // fill empty right space - s.hLine(x - kTabSpacing + 1, _y + _tabHeight, _x + _w - 1, onTop ? kWidColor : kDlgColor); - s.hLine(_x, _y + _h - 1, _x + _w - 1, onTop ? kBGColorLo : kColor); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/TabWidget.hxx b/src/gui/TabWidget.hxx index 1f581df5c..141b29497 100644 --- a/src/gui/TabWidget.hxx +++ b/src/gui/TabWidget.hxx @@ -63,8 +63,8 @@ class TabWidget : public Widget, public CommandSender protected: void handleMouseDown(int x, int y, MouseButton b, int clickCount) override; - void handleMouseEntered() override; - void handleMouseLeft() override; + void handleMouseEntered() override {} + void handleMouseLeft() override {} void handleCommand(CommandSender* sender, int cmd, int data, int id) override; bool handleEvent(Event::Type event) override; diff --git a/src/gui/TimeLineWidget.cxx b/src/gui/TimeLineWidget.cxx index 053d7bccf..4e6b9e9a0 100644 --- a/src/gui/TimeLineWidget.cxx +++ b/src/gui/TimeLineWidget.cxx @@ -35,7 +35,9 @@ TimeLineWidget::TimeLineWidget(GuiObject* boss, const GUI::Font& font, : ButtonWidget(boss, font, x, y, w, h, label, cmd), _labelWidth(labelWidth) { - _flags = Widget::FLAG_ENABLED | Widget::FLAG_TRACK_MOUSE; + _flags = Widget::FLAG_ENABLED | Widget::FLAG_TRACK_MOUSE + | Widget::FLAG_CLEARBG | Widget::FLAG_NOBG; + _bgcolor = kDlgColor; _bgcolorhi = kDlgColor; @@ -84,7 +86,7 @@ void TimeLineWidget::setStepValues(const IntArray& steps) if(steps.size() > _stepValue.capacity()) _stepValue.reserve(2 * steps.size()); - double scale = (_w - _labelWidth - 2 - HANDLE_W*0) / double(steps.back()); + double scale = (_w - _labelWidth - 2 - HANDLE_W) / double(steps.back()); // Skip the very last value; we take care of it outside the end of the loop for(uInt32 i = 0; i < steps.size() - 1; ++i) @@ -92,7 +94,7 @@ void TimeLineWidget::setStepValues(const IntArray& steps) // Due to integer <-> double conversion, the last value is sometimes // slightly less than the maximum value; we assign it manually to fix this - _stepValue.push_back(_w - _labelWidth - 2 - HANDLE_W*0); + _stepValue.push_back(_w - _labelWidth - 2 - HANDLE_W); } else _stepValue.push_back(0); @@ -141,17 +143,18 @@ void TimeLineWidget::drawWidget(bool hilite) { FBSurface& s = _boss->dialog().surface(); + cerr << "TimeLineWidget::drawWidget " << typeid(s).name() << endl; + // Draw the label, if any if(_labelWidth > 0) s.drawString(_font, _label, _x, _y + 2, _labelWidth, isEnabled() ? kTextColor : kColor, TextAlign::Left); - int p = valueToPos(_value), - x = _x + _labelWidth, - w = _w - _labelWidth; - // Frame the handle const int HANDLE_W2 = (HANDLE_W + 1) / 2; + int p = valueToPos(_value), + x = _x + _labelWidth + HANDLE_W2, + w = _w - _labelWidth - HANDLE_W; s.hLine(x + p - HANDLE_W2, _y + 0, x + p - HANDLE_W2 + HANDLE_W, kColorInfo); s.vLine(x + p - HANDLE_W2, _y + 1, _y + _h - 2, kColorInfo); s.hLine(x + p - HANDLE_W2 + 1, _y + _h - 1, x + p - HANDLE_W2 + 1 + HANDLE_W, kBGColor); diff --git a/src/gui/TimeMachineDialog.cxx b/src/gui/TimeMachineDialog.cxx index 3b591a62d..8e550d2a9 100644 --- a/src/gui/TimeMachineDialog.cxx +++ b/src/gui/TimeMachineDialog.cxx @@ -218,6 +218,7 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, this->clearFlags(Widget::FLAG_CLEARBG); // does only work combined with blending (0..100)! this->clearFlags(Widget::FLAG_BORDER); + this->setFlags(Widget::FLAG_NOBG); xpos = H_BORDER; ypos = V_BORDER; @@ -225,8 +226,10 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, // Add index info myCurrentIdxWidget = new StaticTextWidget(this, font, xpos, ypos, "1000", TextAlign::Left, kBGColor); myCurrentIdxWidget->setTextColor(kColorInfo); + myCurrentIdxWidget->setFlags(Widget::FLAG_CLEARBG | Widget::FLAG_NOBG); myLastIdxWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("1000"), ypos, "1000", TextAlign::Right, kBGColor); + myLastIdxWidget->setFlags(Widget::FLAG_CLEARBG | Widget::FLAG_NOBG); myLastIdxWidget->setTextColor(kColorInfo); // Add timeline @@ -241,9 +244,11 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, // Add time info int ypos_s = ypos + (buttonHeight - font.getFontHeight() + 1) / 2; // align to button vertical center myCurrentTimeWidget = new StaticTextWidget(this, font, xpos, ypos_s, "00:00.00", TextAlign::Left, kBGColor); + myCurrentTimeWidget->setFlags(Widget::FLAG_CLEARBG | Widget::FLAG_NOBG); myCurrentTimeWidget->setTextColor(kColorInfo); myLastTimeWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("00:00.00"), ypos_s, "00:00.00", TextAlign::Right, kBGColor); + myLastTimeWidget->setFlags(Widget::FLAG_CLEARBG | Widget::FLAG_NOBG); myLastTimeWidget->setTextColor(kColorInfo); xpos = myCurrentTimeWidget->getRight() + BUTTON_GAP * 4; @@ -287,11 +292,12 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, // Add message myMessageWidget = new StaticTextWidget(this, font, xpos, ypos_s, " ", TextAlign::Left, kBGColor); + myMessageWidget->setFlags(Widget::FLAG_CLEARBG | Widget::FLAG_NOBG); myMessageWidget->setTextColor(kColorInfo); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void TimeMachineDialog::center() +void TimeMachineDialog::setPosition() { // Place on the bottom of the screen, centered horizontally const Common::Size& screen = instance().frameBuffer().screenSize(); @@ -434,11 +440,11 @@ void TimeMachineDialog::handleCommand(CommandSender* sender, int cmd, break; case kSaveAll: - instance().frameBuffer().showMessage(instance().state().rewindManager().saveAllStates()); + instance().frameBuffer().showTextMessage(instance().state().rewindManager().saveAllStates()); break; case kLoadAll: - instance().frameBuffer().showMessage(instance().state().rewindManager().loadAllStates()); + instance().frameBuffer().showTextMessage(instance().state().rewindManager().loadAllStates()); initBar(); break; diff --git a/src/gui/TimeMachineDialog.hxx b/src/gui/TimeMachineDialog.hxx index a4a489b79..1d15d6b15 100644 --- a/src/gui/TimeMachineDialog.hxx +++ b/src/gui/TimeMachineDialog.hxx @@ -44,8 +44,8 @@ class TimeMachineDialog : public Dialog /** initialize timeline bar */ void initBar(); - /** This dialog uses its own positioning, so we override Dialog::center() */ - void center() override; + /** This dialog uses its own positioning, so we override Dialog::setPosition() */ + void setPosition() override; /** convert cycles into time */ string getTimeString(uInt64 cycles); diff --git a/src/gui/ToolTip.cxx b/src/gui/ToolTip.cxx new file mode 100644 index 000000000..6e6ef9fc9 --- /dev/null +++ b/src/gui/ToolTip.cxx @@ -0,0 +1,183 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#include "OSystem.hxx" +#include "Dialog.hxx" +#include "DialogContainer.hxx" +#include "Font.hxx" +#include "FrameBuffer.hxx" +#include "FBSurface.hxx" +#include "Widget.hxx" + +#include "ToolTip.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +ToolTip::ToolTip(Dialog& dialog, const GUI::Font& font) + : myDialog(dialog) +{ + myScale = myDialog.instance().frameBuffer().hidpiScaleFactor(); + + setFont(font); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ToolTip::setFont(const GUI::Font& font) +{ + myFont = &font; + const int fontWidth = font.getMaxCharWidth(), + fontHeight = font.getFontHeight(); + + myTextXOfs = fontHeight < 24 ? 5 : 8; + myTextYOfs = fontHeight < 24 ? 2 : 3; + myWidth = fontWidth * MAX_COLUMNS + myTextXOfs * 2; + myHeight = fontHeight * MAX_ROWS + myTextYOfs * 2; + + 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) + release(false); + + if(!myTipShown) + release(true); + else + { + if(myTipWidget->changedToolTip(myTipPos, myMousePos)) + { + const string tip = myTipWidget->getToolTip(myMousePos); + + if(!tip.empty()) + show(tip); + else + release(true); + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ToolTip::hide() +{ + if(myTipShown) + { + myTimer = 0; + myTipWidget = myFocusWidget = nullptr; + myTipShown = false; + myDialog.instance().frameBuffer().setPendingRender(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ToolTip::release(bool emptyTip) +{ + if(myTipShown) + { + myTipShown = false; + myDialog.instance().frameBuffer().setPendingRender(); + } + + // After displaying a tip, slowly reset the timer to 0 + // until a new tip is requested + if((emptyTip || myTipWidget != myFocusWidget) && myTimer) + myTimer--; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +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; + // 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 = 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 + height + H_CURSOR > imageRect.h() / myScale) + ? yAbs - height - V_GAP + : yAbs + H_CURSOR / myScale + V_GAP; + + if(x < 0) + { + x = 0; + width = std::min(width, imageRect.w() / myScale); + } + + mySurface->setSrcSize(width, height); + mySurface->setDstSize(width * myScale, height * myScale); + mySurface->setDstPos(x * myScale, y * myScale); + mySurface->frameRect(0, 0, width, height, kColor); + + myTipShown = true; + myDialog.instance().frameBuffer().setPendingRender(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ToolTip::render() +{ + if(myTipShown) + mySurface->render(), cerr << " render tooltip" << endl; +} diff --git a/src/gui/ToolTip.hxx b/src/gui/ToolTip.hxx new file mode 100644 index 000000000..71da6dc13 --- /dev/null +++ b/src/gui/ToolTip.hxx @@ -0,0 +1,101 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef TOOL_TIP_HXX +#define TOOL_TIP_HXX + +/** + * Class for providing tooltip functionality + * + * @author Thomas Jentzsch + */ + +class OSystem; +class Dialog; +class Widget; +class FBSurface; + +#include "Rect.hxx" + +class ToolTip +{ + private: + static constexpr uInt32 MAX_COLUMNS = 60; + static constexpr uInt32 MAX_ROWS = 5; + + public: + // Maximum tooltip length + static constexpr uInt32 MAX_LEN = MAX_COLUMNS * MAX_ROWS; + + ToolTip(Dialog& dialog, const GUI::Font& font); + ~ToolTip() = default; + + void setFont(const GUI::Font& font); + + /** + Request a tooltip display. + */ + void request(); + + /** + Hide a displayed tooltip and reset the timer. + */ + void hide(); + + /** + Hide a displayed tooltip and reset the timer slowly. + This allows faster tip display of the next tip. + */ + void release(bool emptyTip); + + /** + Update focused widget and current mouse position. + */ + void update(const Widget* widget, const Common::Point& pos); + + /* + Render the tooltip + */ + void render(); + + private: + void show(const string& tip); + + private: + 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}; + const Widget* myTipWidget{nullptr}; + const Widget* myFocusWidget{nullptr}; + + uInt32 myTimer{0}; + Common::Point myMousePos; + Common::Point myTipPos; + uInt32 myWidth{0}; + uInt32 myHeight{0}; + uInt32 myTextXOfs{0}; + uInt32 myTextYOfs{0}; + bool myTipShown{false}; + uInt32 myScale{1}; + shared_ptr mySurface; +}; + +#endif diff --git a/src/gui/UIDialog.cxx b/src/gui/UIDialog.cxx index 1b64d73f2..12f710bc0 100644 --- a/src/gui/UIDialog.cxx +++ b/src/gui/UIDialog.cxx @@ -441,6 +441,7 @@ void UIDialog::saveConfig() settings.setValue("uipalette", myPalettePopup->getSelectedTag().toString()); instance().frameBuffer().setUIPalette(); + instance().frameBuffer().update(FrameBuffer::UpdateMode::REDRAW); // Dialog font settings.setValue("dialogfont", diff --git a/src/gui/VideoAudioDialog.cxx b/src/gui/VideoAudioDialog.cxx index f68be6df8..ac2ad19bf 100644 --- a/src/gui/VideoAudioDialog.cxx +++ b/src/gui/VideoAudioDialog.cxx @@ -122,12 +122,14 @@ void VideoAudioDialog::addDisplayTab() myRenderer = new PopUpWidget(myTab, _font, xpos, ypos, pwidth, lineHeight, instance().frameBuffer().supportedRenderers(), "Renderer ", lwidth); + myRenderer->setToolTip("Select renderer used for displaying screen."); wid.push_back(myRenderer); const int swidth = myRenderer->getWidth() - lwidth; ypos += lineHeight + VGAP; // TIA interpolation myTIAInterpolate = new CheckboxWidget(myTab, _font, xpos, ypos + 1, "Interpolation "); + myTIAInterpolate->setToolTip("Blur emulated display."); wid.push_back(myTIAInterpolate); ypos += lineHeight + VGAP * 4; @@ -145,12 +147,14 @@ void VideoAudioDialog::addDisplayTab() // FS stretch myUseStretch = new CheckboxWidget(myTab, _font, xpos + INDENT, ypos + 1, "Stretch"); + myUseStretch->setToolTip("Stretch emulated display to fill whole screen."); wid.push_back(myUseStretch); #ifdef ADAPTABLE_REFRESH_SUPPORT // Adapt refresh rate ypos += lineHeight + VGAP; myRefreshAdapt = new CheckboxWidget(myTab, _font, xpos + INDENT, ypos + 1, "Adapt display refresh rate"); + myRefreshAdapt->setToolTip("Select optimal display refresh rate for each ROM."); wid.push_back(myRefreshAdapt); #else myRefreshAdapt = nullptr; @@ -167,6 +171,7 @@ void VideoAudioDialog::addDisplayTab() // Aspect ratio correction ypos += lineHeight + VGAP * 4; myCorrectAspect = new CheckboxWidget(myTab, _font, xpos, ypos + 1, "Correct aspect ratio (*)"); + myCorrectAspect->setToolTip("Uncheck to disable real world aspect ratio correction."); wid.push_back(myCorrectAspect); // Vertical size @@ -176,6 +181,7 @@ void VideoAudioDialog::addDisplayTab() "V-Size adjust", lwidth, kVSizeChanged, fontWidth * 7, "%", 0, true); myVSizeAdjust->setMinValue(-5); myVSizeAdjust->setMaxValue(5); myVSizeAdjust->setTickmarkIntervals(2); + myVSizeAdjust->setToolTip("Adjust vertical size to match emulated TV display."); wid.push_back(myVSizeAdjust); @@ -231,6 +237,7 @@ void VideoAudioDialog::addPaletteTab() myPhaseShiftNtsc->setMinValue((PaletteHandler::DEF_NTSC_SHIFT - PaletteHandler::MAX_PHASE_SHIFT) * 10); myPhaseShiftNtsc->setMaxValue((PaletteHandler::DEF_NTSC_SHIFT + PaletteHandler::MAX_PHASE_SHIFT) * 10); myPhaseShiftNtsc->setTickmarkIntervals(4); + myPhaseShiftNtsc->setToolTip("Adjust NTSC phase shift of 'Custom' palette."); wid.push_back(myPhaseShiftNtsc); ypos += lineHeight + VGAP; @@ -240,6 +247,7 @@ void VideoAudioDialog::addPaletteTab() myPhaseShiftPal->setMinValue((PaletteHandler::DEF_PAL_SHIFT - PaletteHandler::MAX_PHASE_SHIFT) * 10); myPhaseShiftPal->setMaxValue((PaletteHandler::DEF_PAL_SHIFT + PaletteHandler::MAX_PHASE_SHIFT) * 10); myPhaseShiftPal->setTickmarkIntervals(4); + myPhaseShiftPal->setToolTip("Adjust PAL phase shift of 'Custom' palette."); wid.push_back(myPhaseShiftPal); ypos += lineHeight + VGAP; @@ -252,6 +260,7 @@ void VideoAudioDialog::addPaletteTab() myTVRedScale->setMinValue(0); myTVRedScale->setMaxValue(100); myTVRedScale->setTickmarkIntervals(2); + myTVRedScale->setToolTip("Adjust red saturation of 'Custom' palette."); wid.push_back(myTVRedScale); const int xposr = myTIAPalette->getRight() - rgbsWidth; @@ -261,6 +270,7 @@ void VideoAudioDialog::addPaletteTab() myTVRedShift->setMinValue((PaletteHandler::DEF_RGB_SHIFT - PaletteHandler::MAX_RGB_SHIFT) * 10); myTVRedShift->setMaxValue((PaletteHandler::DEF_RGB_SHIFT + PaletteHandler::MAX_RGB_SHIFT) * 10); myTVRedShift->setTickmarkIntervals(2); + myTVRedShift->setToolTip("Adjust red shift of 'Custom' palette."); wid.push_back(myTVRedShift); ypos += lineHeight + VGAP; @@ -270,6 +280,7 @@ void VideoAudioDialog::addPaletteTab() myTVGreenScale->setMinValue(0); myTVGreenScale->setMaxValue(100); myTVGreenScale->setTickmarkIntervals(2); + myTVGreenScale->setToolTip("Adjust green saturation of 'Custom' palette."); wid.push_back(myTVGreenScale); myTVGreenShift = @@ -278,6 +289,7 @@ void VideoAudioDialog::addPaletteTab() myTVGreenShift->setMinValue((PaletteHandler::DEF_RGB_SHIFT - PaletteHandler::MAX_RGB_SHIFT) * 10); myTVGreenShift->setMaxValue((PaletteHandler::DEF_RGB_SHIFT + PaletteHandler::MAX_RGB_SHIFT) * 10); myTVGreenShift->setTickmarkIntervals(2); + myTVGreenShift->setToolTip("Adjust green shift of 'Custom' palette."); wid.push_back(myTVGreenShift); ypos += lineHeight + VGAP; @@ -287,6 +299,7 @@ void VideoAudioDialog::addPaletteTab() myTVBlueScale->setMinValue(0); myTVBlueScale->setMaxValue(100); myTVBlueScale->setTickmarkIntervals(2); + myTVBlueScale->setToolTip("Adjust blue saturation of 'Custom' palette."); wid.push_back(myTVBlueScale); myTVBlueShift = @@ -295,6 +308,7 @@ void VideoAudioDialog::addPaletteTab() myTVBlueShift->setMinValue((PaletteHandler::DEF_RGB_SHIFT - PaletteHandler::MAX_RGB_SHIFT) * 10); myTVBlueShift->setMaxValue((PaletteHandler::DEF_RGB_SHIFT + PaletteHandler::MAX_RGB_SHIFT) * 10); myTVBlueShift->setTickmarkIntervals(2); + myTVBlueShift->setToolTip("Adjust blue shift of 'Custom' palette."); wid.push_back(myTVBlueShift); ypos += lineHeight + VGAP; xpos -= INDENT; @@ -959,7 +973,16 @@ void VideoAudioDialog::handlePaletteUpdate() instance().frameBuffer().tiaSurface().paletteHandler().setAdjustables(paletteAdj); if(instance().hasConsole()) + { instance().frameBuffer().tiaSurface().paletteHandler().setPalette(); + + constexpr int NUM_LUMA = 8; + constexpr int NUM_CHROMA = 16; + + for(int idx = 0; idx < NUM_CHROMA; ++idx) + for(int lum = 0; lum < NUM_LUMA; ++lum) + myColor[idx][lum]->setDirty(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1157,10 +1180,11 @@ void VideoAudioDialog::addPalette(int x, int y, int w, int h) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void VideoAudioDialog::colorPalette() { + constexpr int NUM_LUMA = 8; + constexpr int NUM_CHROMA = 16; + if(instance().hasConsole()) { - constexpr int NUM_LUMA = 8; - constexpr int NUM_CHROMA = 16; const int order[2][NUM_CHROMA] = { {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, @@ -1176,11 +1200,14 @@ void VideoAudioDialog::colorPalette() ss << Common::Base::HEX1 << std::uppercase << color; myColorLbl[idx]->setLabel(ss.str()); for(int lum = 0; lum < NUM_LUMA; ++lum) - { myColor[idx][lum]->setColor(color * NUM_CHROMA + lum * 2); // skip grayscale colors - } } } + else + // disable palette + for(int idx = 0; idx < NUM_CHROMA; ++idx) + for(int lum = 0; lum < NUM_LUMA; ++lum) + myColor[idx][lum]->setEnabled(false); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/VideoAudioDialog.hxx b/src/gui/VideoAudioDialog.hxx index 1d0e8a637..550617d2d 100644 --- a/src/gui/VideoAudioDialog.hxx +++ b/src/gui/VideoAudioDialog.hxx @@ -38,7 +38,7 @@ class VideoAudioDialog : public Dialog { public: VideoAudioDialog(OSystem& osystem, DialogContainer& parent, const GUI::Font& font, - int max_w, int max_h); + int max_w, int max_h); ~VideoAudioDialog() override = default; private: diff --git a/src/gui/Widget.cxx b/src/gui/Widget.cxx index a54d9e45c..23c72760d 100644 --- a/src/gui/Widget.cxx +++ b/src/gui/Widget.cxx @@ -21,6 +21,7 @@ #include "bspf.hxx" #include "Command.hxx" #include "Dialog.hxx" +#include "ToolTip.hxx" #include "FBSurface.hxx" #include "GuiObject.hxx" #include "OSystem.hxx" @@ -54,9 +55,38 @@ Widget::~Widget() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Widget::setDirty() { - // A widget being dirty indicates that its parent dialog is dirty - // So we inform the parent about it - _boss->dialog().setDirty(); + _dirty = true; + + // Inform the parent object that its children chain is dirty + _boss->setDirtyChain(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Widget::setDirtyChain() +{ + _dirtyChain = true; + + // Inform the parent object that its children chain is dirty + _boss->setDirtyChain(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Widget::tick() +{ + if(isEnabled()) + { + if(wantsToolTip()) + dialog().tooltip().request(); + + // Recursively tick widget and all child dialogs and widgets + Widget* w = _firstWidget; + + while(w) + { + w->tick(); + w = w->_next; + } + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -65,62 +95,102 @@ void Widget::draw() if(!isVisible() || !_boss->isVisible()) return; - FBSurface& s = _boss->dialog().surface(); - - bool onTop = _boss->dialog().isOnTop(); - - bool hasBorder = _flags & Widget::FLAG_BORDER; // currently only used by Dialog widget - int oldX = _x, oldY = _y; - - // Account for our relative position in the dialog - _x = getAbsX(); - _y = getAbsY(); - - // Clear background (unless alpha blending is enabled) - if(_flags & Widget::FLAG_CLEARBG) + if(isDirty()) { - int x = _x, y = _y, w = _w, h = _h; - if(hasBorder) + cerr << " *** draw widget " << typeid(*this).name() << " ***" << endl; + //cerr << "w"; + + FBSurface& s = _boss->dialog().surface(); + + bool onTop = _boss->dialog().isOnTop(); + int oldX = _x, oldY = _y; + + // Account for our relative position in the dialog + _x = getAbsX(); + _y = getAbsY(); + + // Clear background (unless alpha blending is enabled) + if(clearsBackground()) { - x++; y++; w-=2; h-=2; + int x = _x, y = _y, w = _w, h = _h; + if(hasBorder()) + { + x++; y++; w -= 2; h -= 2; + } + if(hasBackground()) + s.fillRect(x, y, w, h, !onTop + ? _bgcolorlo + : (_flags & Widget::FLAG_HILITED) && isEnabled() + ? _bgcolorhi : _bgcolor); + else + s.invalidateRect(x, y, w, h); } - s.fillRect(x, y, w, h, !onTop ? _bgcolorlo : (_flags & Widget::FLAG_HILITED) && isEnabled() ? _bgcolorhi : _bgcolor); + + // Draw border + if(hasBorder()) + { + s.frameRect(_x, _y, _w, _h, !onTop + ? kColor + : (_flags & Widget::FLAG_HILITED) && isEnabled() + ? kWidColorHi : kColor); + _x += 4; + _y += 4; + _w -= 8; + _h -= 8; + } + + // Now perform the actual widget draw + drawWidget((_flags & Widget::FLAG_HILITED) ? true : false); + + // Restore x/y + if(hasBorder()) + { + _x -= 4; + _y -= 4; + _w += 8; + _h += 8; + } + + _x = oldX; + _y = oldY; } - - // Draw border - if(hasBorder) - { - s.frameRect(_x, _y, _w, _h, !onTop ? kColor : (_flags & Widget::FLAG_HILITED) && isEnabled() ? kWidColorHi : kColor); - _x += 4; - _y += 4; - _w -= 8; - _h -= 8; - } - - // Now perform the actual widget draw - drawWidget((_flags & Widget::FLAG_HILITED) ? true : false); - - // Restore x/y - if (hasBorder) - { - _x -= 4; - _y -= 4; - _w += 8; - _h += 8; - } - - _x = oldX; - _y = oldY; + clearDirty(); // Draw all children + drawChain(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Widget::drawChain() +{ + // Clear chain *before* drawing, because some widgets may set it again when + // being drawn (e.g. RomListWidget) + clearDirtyChain(); + Widget* w = _firstWidget; + while(w) { - w->draw(); + if(w->needsRedraw()) + w->draw(); w = w->_next; } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Widget::handleMouseEntered() +{ + if(isEnabled()) + setFlags(Widget::FLAG_HILITED | Widget::FLAG_MOUSE_FOCUS); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Widget::handleMouseLeft() +{ + if(isEnabled()) + clearFlags(Widget::FLAG_HILITED | Widget::FLAG_MOUSE_FOCUS); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Widget::receivedFocus() { @@ -150,6 +220,14 @@ void Widget::setEnabled(bool e) else clearFlags(Widget::FLAG_ENABLED); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Widget::setToolTip(const string& text) +{ + assert(text.length() <= ToolTip::MAX_LEN); + + _toolTipText = text; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Widget* Widget::findWidgetInChain(Widget* w, int x, int y) { @@ -224,7 +302,7 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr, s.frameRect(x, y, w, h, onTop ? kDlgColor : kBGColorLo); - tmp->setDirty(); + //tmp->setDirty(); } } @@ -280,7 +358,7 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr, if (onTop) s.frameRect(x, y, w, h, kWidFrameColor, FrameStyle::Dashed); - tmp->setDirty(); + //tmp->setDirty(); return tmp; } @@ -290,6 +368,7 @@ void Widget::setDirtyInChain(Widget* start) { while(start) { + //cerr << "setDirtyInChain " << typeid(*start).name() << endl; start->setDirty(); start = start->_next; } @@ -304,7 +383,8 @@ StaticTextWidget::StaticTextWidget(GuiObject* boss, const GUI::Font& font, _label(text), _align(align) { - _flags = Widget::FLAG_ENABLED; + _flags = Widget::FLAG_ENABLED | FLAG_CLEARBG; + _bgcolor = kDlgColor; _bgcolorhi = kDlgColor; _textcolor = kTextColor; @@ -338,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) { @@ -345,8 +442,6 @@ void StaticTextWidget::drawWidget(bool hilite) bool onTop = _boss->dialog().isOnTop(); s.drawString(_font, _label, _x, _y, _w, isEnabled() && onTop ? _textcolor : kColor, _align, 0, true, _shadowcolor); - - setDirty(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -400,13 +495,15 @@ ButtonWidget::ButtonWidget(GuiObject* boss, const GUI::Font& font, // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ButtonWidget::handleMouseEntered() { - setFlags(Widget::FLAG_HILITED); + if(isEnabled()) + setFlags(Widget::FLAG_HILITED | Widget::FLAG_MOUSE_FOCUS); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ButtonWidget::handleMouseLeft() { - clearFlags(Widget::FLAG_HILITED); + if(isEnabled()) + clearFlags(Widget::FLAG_HILITED | Widget::FLAG_MOUSE_FOCUS); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -474,8 +571,6 @@ void ButtonWidget::drawWidget(bool hilite) !(isEnabled() && onTop) ? _textcolorlo : hilite ? _textcolorhi : _textcolor, _bmw, _bmh); - - setDirty(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -508,18 +603,6 @@ CheckboxWidget::CheckboxWidget(GuiObject* boss, const GUI::Font& font, setFill(CheckboxWidget::FillType::Normal); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CheckboxWidget::handleMouseEntered() -{ - setFlags(Widget::FLAG_HILITED); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CheckboxWidget::handleMouseLeft() -{ - clearFlags(Widget::FLAG_HILITED); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CheckboxWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) { @@ -635,8 +718,6 @@ void CheckboxWidget::drawWidget(bool hilite) // Finally draw the label s.drawString(_font, _label, _x + prefixSize(_font), _y + _textY, _w, onTop && isEnabled() ? kTextColor : kColor); - - setDirty(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -652,7 +733,7 @@ SliderWidget::SliderWidget(GuiObject* boss, const GUI::Font& font, _valueLabelWidth(valueLabelWidth), _forceLabelSign(forceLabelSign) { - _flags = Widget::FLAG_ENABLED | Widget::FLAG_TRACK_MOUSE; + _flags = Widget::FLAG_ENABLED | Widget::FLAG_TRACK_MOUSE | Widget::FLAG_CLEARBG; _bgcolor = kDlgColor; _bgcolorhi = kDlgColor; @@ -870,8 +951,6 @@ void SliderWidget::drawWidget(bool hilite) if(_valueLabelWidth > 0) s.drawString(_font, _valueLabel + _valueUnit, _x + _w - _valueLabelWidth, _y + 2, _valueLabelWidth, isEnabled() ? kTextColor : kColor); - - setDirty(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/Widget.hxx b/src/gui/Widget.hxx index 50e71681e..64685813a 100644 --- a/src/gui/Widget.hxx +++ b/src/gui/Widget.hxx @@ -26,6 +26,7 @@ class Dialog; #include #include "bspf.hxx" +#include "Rect.hxx" #include "Event.hxx" #include "EventHandlerConstants.hxx" #include "FrameBufferConstants.hxx" @@ -42,19 +43,6 @@ class Widget : public GuiObject { friend class Dialog; - public: - enum : uInt32 { - FLAG_ENABLED = 1 << 0, - FLAG_INVISIBLE = 1 << 1, - FLAG_HILITED = 1 << 2, - FLAG_BORDER = 1 << 3, - FLAG_CLEARBG = 1 << 4, - FLAG_TRACK_MOUSE = 1 << 5, - FLAG_RETAIN_FOCUS = 1 << 6, - FLAG_WANTS_TAB = 1 << 7, - FLAG_WANTS_RAWDATA = 1 << 8 - }; - public: Widget(GuiObject* boss, const GUI::Font& font, int x, int y, int w, int h); ~Widget() override; @@ -71,8 +59,8 @@ class Widget : public GuiObject virtual bool handleKeyUp(StellaKey key, StellaMod mod) { return false; } virtual void handleMouseDown(int x, int y, MouseButton b, int clickCount) { } virtual void handleMouseUp(int x, int y, MouseButton b, int clickCount) { } - virtual void handleMouseEntered() { } - virtual void handleMouseLeft() { } + virtual void handleMouseEntered(); + virtual void handleMouseLeft(); virtual void handleMouseMoved(int x, int y) { } virtual void handleMouseWheel(int x, int y, int direction) { } virtual bool handleMouseClicks(int x, int y, MouseButton b) { return false; } @@ -82,8 +70,12 @@ class Widget : public GuiObject virtual bool handleJoyHat(int stick, int hat, JoyHatDir hdir, int button = JOY_CTRL_NONE) { return false; } virtual bool handleEvent(Event::Type event) { return false; } + void tick() override; + void setDirty() override; + void setDirtyChain() override; void draw() override; + void drawChain() override; void receivedFocus(); void lostFocus(); void addFocusWidget(Widget* w) override { _focusList.push_back(w); } @@ -94,12 +86,10 @@ class Widget : public GuiObject /** Set/clear FLAG_ENABLED */ void setEnabled(bool e); - void setFlags(uInt32 flags) { _flags |= flags; setDirty(); } - void clearFlags(uInt32 flags) { _flags &= ~flags; setDirty(); } - uInt32 getFlags() const { return _flags; } - bool isEnabled() const { return _flags & FLAG_ENABLED; } bool isVisible() const override { return !(_flags & FLAG_INVISIBLE); } + bool isHighlighted() const { return _flags & FLAG_HILITED; } + bool hasMouseFocus() const { return _flags & FLAG_MOUSE_FOCUS; } virtual bool wantsFocus() const { return _flags & FLAG_RETAIN_FOCUS; } bool wantsTab() const { return _flags & FLAG_WANTS_TAB; } bool wantsRaw() const { return _flags & FLAG_WANTS_RAWDATA; } @@ -115,6 +105,11 @@ class Widget : public GuiObject void setBGColorHi(ColorId color) { _bgcolorhi = color; setDirty(); } void setShadowColor(ColorId color) { _shadowcolor = color; setDirty(); } + void setToolTip(const string& text); + virtual string getToolTip(const Common::Point& pos) const { return _toolTipText; } + virtual bool changedToolTip(const Common::Point& oldPos, + const Common::Point& newPos) const { return false; } + virtual void loadConfig() { } protected: @@ -127,6 +122,9 @@ class Widget : public GuiObject void releaseFocus() override { assert(_boss); _boss->releaseFocus(); } + virtual bool wantsToolTip() const { return hasMouseFocus() && hasToolTip(); } + virtual bool hasToolTip() const { return !_toolTipText.empty(); } + // By default, delegate unhandled commands to the boss void handleCommand(CommandSender* sender, int cmd, int data, int id) override { assert(_boss); _boss->handleCommand(sender, cmd, data, id); } @@ -136,7 +134,6 @@ class Widget : public GuiObject const GUI::Font& _font; Widget* _next{nullptr}; uInt32 _id{0}; - uInt32 _flags{0}; bool _hasFocus{false}; int _fontWidth{0}; int _lineHeight{0}; @@ -147,6 +144,7 @@ class Widget : public GuiObject ColorId _textcolorhi{kTextColorHi}; ColorId _textcolorlo{kBGColorLo}; ColorId _shadowcolor{kShadowColor}; + string _toolTipText; public: static Widget* findWidgetInChain(Widget* start, int x, int y); @@ -188,6 +186,10 @@ class StaticTextWidget : public Widget const string& text = "", TextAlign align = TextAlign::Left, ColorId shadowColor = kNone); ~StaticTextWidget() override = default; + + void handleMouseEntered() override; + void handleMouseLeft() override; + void setValue(int value); void setLabel(const string& label); void setAlign(TextAlign align) { _align = align; setDirty(); } @@ -281,8 +283,6 @@ class CheckboxWidget : public ButtonWidget bool getState() const { return _state; } void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; - void handleMouseEntered() override; - void handleMouseLeft() override; static int boxSize(const GUI::Font& font) { diff --git a/src/gui/module.mk b/src/gui/module.mk index a2f8489fd..4be9b66ad 100644 --- a/src/gui/module.mk +++ b/src/gui/module.mk @@ -49,6 +49,7 @@ MODULE_OBJS := \ src/gui/TimeLineWidget.o \ src/gui/TimeMachineDialog.o \ src/gui/TimeMachine.o \ + src/gui/ToolTip.o \ src/gui/UndoHandler.o \ src/gui/UIDialog.o \ src/gui/VideoAudioDialog.o \ diff --git a/src/libretro/FBSurfaceLIBRETRO.hxx b/src/libretro/FBSurfaceLIBRETRO.hxx index 646d2c46c..f6bf3320b 100644 --- a/src/libretro/FBSurfaceLIBRETRO.hxx +++ b/src/libretro/FBSurfaceLIBRETRO.hxx @@ -44,13 +44,17 @@ class FBSurfaceLIBRETRO : public FBSurface const Common::Rect& dstRect() const override { return myDstGUIR; } void setSrcPos(uInt32 x, uInt32 y) override { } void setSrcSize(uInt32 w, uInt32 h) override { } + void setSrcRect(const Common::Rect& r) override { } void setDstPos(uInt32 x, uInt32 y) override { } void setDstSize(uInt32 w, uInt32 h) override { } + void setDstRect(const Common::Rect& r) override { } + void setVisible(bool visible) override { } void translateCoords(Int32& x, Int32& y) const override { } bool render() override { return true; } void invalidate() override { } + void invalidateRect(uInt32, uInt32, uInt32, uInt32) override { } void free() override { } void reload() override { } void resize(uInt32 width, uInt32 height) override { } diff --git a/src/macos/stella.xcodeproj/project.pbxproj b/src/macos/stella.xcodeproj/project.pbxproj index 4452ddce2..d0892bdce 100644 --- a/src/macos/stella.xcodeproj/project.pbxproj +++ b/src/macos/stella.xcodeproj/project.pbxproj @@ -531,6 +531,10 @@ DCBDDE9B1D6A5F0E009DF1E9 /* Cart3EPlusWidget.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCBDDE991D6A5F0E009DF1E9 /* Cart3EPlusWidget.hxx */; }; DCBDDE9E1D6A5F2F009DF1E9 /* Cart3EPlus.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCBDDE9C1D6A5F2F009DF1E9 /* Cart3EPlus.cxx */; }; DCBDDE9F1D6A5F2F009DF1E9 /* Cart3EPlus.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCBDDE9D1D6A5F2F009DF1E9 /* Cart3EPlus.hxx */; }; + DCC2FDF5255EB82500FA5E81 /* ToolTip.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCC2FDF3255EB82500FA5E81 /* ToolTip.hxx */; }; + DCC2FDF6255EB82500FA5E81 /* ToolTip.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCC2FDF4255EB82500FA5E81 /* ToolTip.cxx */; }; + DCC2FDF92566AD8800FA5E81 /* DataGridRamWidget.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCC2FDF72566AD8800FA5E81 /* DataGridRamWidget.cxx */; }; + DCC2FDFA2566AD8800FA5E81 /* DataGridRamWidget.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCC2FDF82566AD8800FA5E81 /* DataGridRamWidget.hxx */; }; DCC527D110B9DA19005E1287 /* Device.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCC527C910B9DA19005E1287 /* Device.hxx */; }; DCC527D210B9DA19005E1287 /* M6502.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCC527CA10B9DA19005E1287 /* M6502.cxx */; }; DCC527D310B9DA19005E1287 /* M6502.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCC527CB10B9DA19005E1287 /* M6502.hxx */; }; @@ -1298,6 +1302,10 @@ DCBDDE991D6A5F0E009DF1E9 /* Cart3EPlusWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cart3EPlusWidget.hxx; sourceTree = ""; }; DCBDDE9C1D6A5F2F009DF1E9 /* Cart3EPlus.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cart3EPlus.cxx; sourceTree = ""; }; DCBDDE9D1D6A5F2F009DF1E9 /* Cart3EPlus.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cart3EPlus.hxx; sourceTree = ""; }; + DCC2FDF3255EB82500FA5E81 /* ToolTip.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ToolTip.hxx; sourceTree = ""; }; + DCC2FDF4255EB82500FA5E81 /* ToolTip.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ToolTip.cxx; sourceTree = ""; }; + DCC2FDF72566AD8800FA5E81 /* DataGridRamWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataGridRamWidget.cxx; sourceTree = ""; }; + DCC2FDF82566AD8800FA5E81 /* DataGridRamWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DataGridRamWidget.hxx; sourceTree = ""; }; DCC527C910B9DA19005E1287 /* Device.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Device.hxx; sourceTree = ""; }; DCC527CA10B9DA19005E1287 /* M6502.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = M6502.cxx; sourceTree = ""; }; DCC527CB10B9DA19005E1287 /* M6502.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = M6502.hxx; sourceTree = ""; }; @@ -1702,6 +1710,8 @@ 2D20F9E708C603EC00A73076 /* CpuWidget.hxx */, 2D20F9E808C603EC00A73076 /* DataGridOpsWidget.cxx */, 2D20F9E908C603EC00A73076 /* DataGridOpsWidget.hxx */, + DCC2FDF72566AD8800FA5E81 /* DataGridRamWidget.cxx */, + DCC2FDF82566AD8800FA5E81 /* DataGridRamWidget.hxx */, 2D20F9EA08C603EC00A73076 /* DataGridWidget.cxx */, 2D20F9EB08C603EC00A73076 /* DataGridWidget.hxx */, 2D20F9EC08C603EC00A73076 /* DebuggerDialog.cxx */, @@ -2167,6 +2177,8 @@ DCA82C6E1FEB4E780059340F /* TimeMachine.hxx */, DCA82C6F1FEB4E780059340F /* TimeMachineDialog.cxx */, DCA82C701FEB4E780059340F /* TimeMachineDialog.hxx */, + DCC2FDF4255EB82500FA5E81 /* ToolTip.cxx */, + DCC2FDF3255EB82500FA5E81 /* ToolTip.hxx */, DC8078E60B4BD697005E9305 /* UIDialog.cxx */, DC8078E70B4BD697005E9305 /* UIDialog.hxx */, DCBA539825557E2800087DD7 /* UndoHandler.cxx */, @@ -2572,6 +2584,7 @@ E08FCD5823A037EB0051F59B /* QisBlitter.hxx in Headers */, DCA078351F8C1B04008EFEE5 /* SDL_lib.hxx in Headers */, DCDA03B11A2009BB00711920 /* CartWD.hxx in Headers */, + DCC2FDFA2566AD8800FA5E81 /* DataGridRamWidget.hxx in Headers */, 2D91745909BA90380026E9FF /* PromptWidget.hxx in Headers */, DC3C9BC62469C8F700CF2D47 /* PaletteHandler.hxx in Headers */, 2D91745A09BA90380026E9FF /* RamWidget.hxx in Headers */, @@ -2651,6 +2664,7 @@ DC5D1AA7102C6FC900E59AC1 /* Stack.hxx in Headers */, DCF7B0DE10A762FC007A2870 /* CartF0.hxx in Headers */, DCF7B0E010A762FC007A2870 /* CartFA.hxx in Headers */, + DCC2FDF5255EB82500FA5E81 /* ToolTip.hxx in Headers */, DCC527D110B9DA19005E1287 /* Device.hxx in Headers */, DC6F394E21B897F300897AD8 /* ThreadDebugging.hxx in Headers */, DCC527D310B9DA19005E1287 /* M6502.hxx in Headers */, @@ -3191,6 +3205,7 @@ DC676A591729A0B000E4E73D /* CartSBWidget.cxx in Sources */, DC3C9BCC2469C93D00CF2D47 /* EmulationDialog.cxx in Sources */, DC676A5B1729A0B000E4E73D /* CartX07Widget.cxx in Sources */, + DCC2FDF92566AD8800FA5E81 /* DataGridRamWidget.cxx in Sources */, DC7A24DF173B1DBC00B20FE9 /* FileListWidget.cxx in Sources */, DC13B53F176FF2F500B8B4BB /* RomListSettings.cxx in Sources */, DC3EE8581E2C0E6D00905161 /* crc32.c in Sources */, @@ -3205,6 +3220,7 @@ DCAACB0E188D636F00A4D282 /* Cart4KSCWidget.cxx in Sources */, DCB60AD02543100900A5C1D2 /* FBBackendSDL2.cxx in Sources */, DCAACB10188D636F00A4D282 /* CartBFSCWidget.cxx in Sources */, + DCC2FDF6255EB82500FA5E81 /* ToolTip.cxx in Sources */, DCAACB12188D636F00A4D282 /* CartBFWidget.cxx in Sources */, DCAACB14188D636F00A4D282 /* CartDFSCWidget.cxx in Sources */, DCAACB16188D636F00A4D282 /* CartDFWidget.cxx in Sources */, @@ -3389,7 +3405,7 @@ ., "$(HOME)/Library/Frameworks", ); - GCC_ENABLE_CPP_RTTI = NO; + GCC_ENABLE_CPP_RTTI = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_VERSION = ""; @@ -3464,7 +3480,7 @@ ., "$(HOME)/Library/Frameworks", ); - GCC_ENABLE_CPP_RTTI = NO; + GCC_ENABLE_CPP_RTTI = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 3; GCC_VERSION = ""; diff --git a/src/windows/Stella.vcxproj b/src/windows/Stella.vcxproj index 5f0bd9126..d1cf96c6b 100644 --- a/src/windows/Stella.vcxproj +++ b/src/windows/Stella.vcxproj @@ -681,6 +681,7 @@ true + true @@ -786,6 +787,7 @@ + @@ -1705,6 +1707,7 @@ true + true @@ -1838,6 +1841,7 @@ + diff --git a/src/windows/Stella.vcxproj.filters b/src/windows/Stella.vcxproj.filters index 8dca2be2e..a8cac63d6 100644 --- a/src/windows/Stella.vcxproj.filters +++ b/src/windows/Stella.vcxproj.filters @@ -1032,6 +1032,12 @@ Source Files\gui + + Source Files\gui + + + Source Files\debugger + @@ -2123,6 +2129,12 @@ Header Files\gui + + Header Files\gui + + + Header Files\debugger +