diff --git a/Changes.txt b/Changes.txt
index 5ae5b0460..6140d6f59 100644
--- a/Changes.txt
+++ b/Changes.txt
@@ -14,7 +14,23 @@
6.4 to 6.5 (December XX, 2020)
- * Enhanced cut/copy/paste to allow selecting text (TODO: PromptWidget, doc)
+ * Enhanced cut/copy/paste for text editing. (TODO: PromptWidget)
+
+ * Added undo and redo to text editing. (TODO: PromptWidget)
+
+ * Added wildcard support to launcher dialog filter.
+
+ * Added option to search subdirectories in launcher.
+
+ * Added static tooltips to some UI items.
+
+ * Added dynamic tooltips to most debugger items.
+
+ * Increased sample size for CDFJ+.
+
+ * Fixed autofire bug for trackball controllers.
+
+ * Codebase now uses C++17 features.
-Have fun!
diff --git a/Makefile b/Makefile
index 168ae178d..d9dc607c9 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++17
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++17
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/configure b/configure
index 237fb21b9..fbc3c1b39 100755
--- a/configure
+++ b/configure
@@ -385,7 +385,7 @@ else
fi
for compiler in $compilers; do
- if test_compiler "$compiler -std=c++14"; then
+ if test_compiler "$compiler -std=c++17"; then
CXX=$compiler
echo $CXX
break
diff --git a/docs/graphics/rominfo_1x_large.png b/docs/graphics/rominfo_1x_large.png
index e5b08d857..5a0133404 100644
Binary files a/docs/graphics/rominfo_1x_large.png and b/docs/graphics/rominfo_1x_large.png differ
diff --git a/docs/graphics/rominfo_1x_small.png b/docs/graphics/rominfo_1x_small.png
index 6ee356745..99d23e135 100644
Binary files a/docs/graphics/rominfo_1x_small.png and b/docs/graphics/rominfo_1x_small.png differ
diff --git a/docs/graphics/rominfo_2x_small.png b/docs/graphics/rominfo_2x_small.png
index da610bba0..7c53384b2 100644
Binary files a/docs/graphics/rominfo_2x_small.png and b/docs/graphics/rominfo_2x_small.png differ
diff --git a/docs/index.html b/docs/index.html
index 0d2a8fd48..369598e23 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -260,7 +260,7 @@
- - High speed emulation using optimized C++14 code
+ - High speed emulation using optimized C++17 code
- Supports high quality TIA emulation using the cycle-exact TIA core from
6502.ts by
Christian Speckner
@@ -350,7 +350,7 @@
- OpenGL capable video card
- Other architectures (MIPS, PPC, PPC64, etc.) have been confirmed to work,
but aren't as well tested as i386/x86_64
- - GNU g++ v/6 or Clang v/3.9 (with C++14 support) and the make utility are required for compiling the Stella source code
+ - GNU g++ v/7 or Clang v/5 (with C++17 support) and the make utility are required for compiling the Stella source code
@@ -3689,15 +3689,29 @@
ROM Info Viewer width at 50% , UI sized 1280x900, large launcher font:
- The text box in the upper right corner can be used to narrow down the
- results in the ROM listing. When this box is empty, all files are shown
- (subject to the restrictions from the filtering option, explained below).
- Typing characters here will show only those files that match that
- pattern. For example, typing 'Activision' will show only files that
- contain the word 'Activision' in their name. This is very useful for
- quickly finding a group of related ROMs. Note that the search is not
- case sensitive, so you don't need to worry about capital or lower-case
- letters.
+ The dialog items at the top can be used to define the listed files:
+
+
+ -
+ The 'Show all files' checkbox allows displaying files which do not
+ have a valid ROM extension.
+
-
+ If 'Incl. subdirectories' is checked, Stella will list matching files
+ from all subdirectories too.
+
-
+ The 'Filter' text box can be used to narrow down the results in the
+ ROM listing. When this box is empty, all files are shown. Typing
+ characters here will show only those files that match that
+ pattern. For example, typing 'Activision' will show only files that
+ contain the word 'Activision' in their name. This is very useful for
+ quickly finding a group of related ROMs.
+ Note that the search is not case sensitive, so you don't need to worry
+ about capital or lower-case letters. You also can use '*' and '?' as
+ wildcards. E.g. for '(198?)*atari' only ROMs from the 1980s made by
+ Atari will be listed.
+
+
+
@@ -3735,12 +3749,6 @@
-
Show only ROM files: Selecting this reloads the current listing,
- showing only files that have a valid ROM extension.
-
-
Show all files: Selecting this reloads the current listing,
- showing all files (with no restriction on file name).
-
Reload listing: Selecting this performs a reload of the
current listing. It is an alternative to pressing the 'Control + r'
key combo.
diff --git a/src/cheat/CheatCodeDialog.cxx b/src/cheat/CheatCodeDialog.cxx
index 09ac1cd3c..481dcbcd5 100644
--- a/src/cheat/CheatCodeDialog.cxx
+++ b/src/cheat/CheatCodeDialog.cxx
@@ -100,6 +100,7 @@ CheatCodeDialog::CheatCodeDialog(OSystem& osystem, DialogContainer& parent,
return (c >= 'a' && c <= 'f') || (c >= '0' && c <= '9');
};
myCheatInput->setTextFilter(f1, 1);
+ myCheatInput->setToolTip("See Stella documentation for details.", 1);
addToFocusList(wid);
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 70fa481d7..044bc57e9 100644
--- a/src/common/RewindManager.cxx
+++ b/src/common/RewindManager.cxx
@@ -37,7 +37,6 @@ RewindManager::RewindManager(OSystem& system, StateManager& statemgr)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RewindManager::setup()
{
- myStateSize = 0;
myLastTimeMachineAdd = false;
const string& prefix = myOSystem.settings().getBool("dev.settings") ? "dev." : "plr.";
@@ -138,7 +137,6 @@ bool RewindManager::addState(const string& message, bool timeMachine)
s.rewind(); // rewind Serializer internal buffers
if(myStateManager.saveState(s) && myOSystem.console().tia().saveDisplay(s))
{
- myStateSize = std::max(myStateSize, uInt32(s.size()));
state.message = message;
state.cycles = myOSystem.console().tia().cycles();
myLastTimeMachineAdd = timeMachine;
@@ -183,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;
}
@@ -218,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;
}
@@ -256,18 +254,22 @@ string RewindManager::saveAllStates()
buf.str("");
out.putString(STATE_HEADER);
out.putShort(numStates);
- out.putInt(myStateSize);
- unique_ptr buffer = make_unique(myStateSize);
for (uInt32 i = 0; i < numStates; ++i)
{
RewindState& state = myStateList.current();
Serializer& s = state.data;
+ uInt32 stateSize = uInt32(s.size());
+ unique_ptr buffer = make_unique(stateSize);
+
+ out.putInt(stateSize);
+
// Rewind Serializer internal buffers
s.rewind();
+
// Save state
- s.getByteArray(buffer.get(), myStateSize);
- out.putByteArray(buffer.get(), myStateSize);
+ s.getByteArray(buffer.get(), stateSize);
+ out.putByteArray(buffer.get(), stateSize);
out.putString(state.message);
out.putLong(state.cycles);
@@ -310,25 +312,27 @@ string RewindManager::loadAllStates()
if (in.getString() != STATE_HEADER)
return "Incompatible all states file";
numStates = in.getShort();
- myStateSize = in.getInt();
- unique_ptr buffer = make_unique(myStateSize);
for (uInt32 i = 0; i < numStates; ++i)
{
if (myStateList.full())
compressStates();
+ uInt32 stateSize = in.getInt();
+ unique_ptr buffer = make_unique(stateSize);
+
// Add new state at the end of the list (queue adds at end)
// This updates the 'current' iterator inside the list
myStateList.addLast();
RewindState& state = myStateList.current();
Serializer& s = state.data;
+
// Rewind Serializer internal buffers
s.rewind();
// Fill new state with saved values
- in.getByteArray(buffer.get(), myStateSize);
- s.putByteArray(buffer.get(), myStateSize);
+ in.getByteArray(buffer.get(), stateSize);
+ s.putByteArray(buffer.get(), stateSize);
state.message = in.getString();
state.cycles = in.getLong();
}
diff --git a/src/common/RewindManager.hxx b/src/common/RewindManager.hxx
index 619af7264..5f53083f7 100644
--- a/src/common/RewindManager.hxx
+++ b/src/common/RewindManager.hxx
@@ -144,7 +144,6 @@ class RewindManager
bool atLast() const { return myStateList.atLast(); }
void resize(uInt32 size) { myStateList.resize(size); }
void clear() {
- myStateSize = 0;
myStateList.clear();
}
@@ -176,7 +175,6 @@ class RewindManager
uInt64 myHorizon{0};
double myFactor{0.0};
bool myLastTimeMachineAdd{false};
- uInt32 myStateSize{0};
struct RewindState {
Serializer data; // actual save state
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/CartDebug.cxx b/src/debugger/CartDebug.cxx
index d62b16b66..ab988e000 100644
--- a/src/debugger/CartDebug.cxx
+++ b/src/debugger/CartDebug.cxx
@@ -605,7 +605,8 @@ bool CartDebug::removeLabel(const string& label)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-bool CartDebug::getLabel(ostream& buf, uInt16 addr, bool isRead, int places) const
+bool CartDebug::getLabel(ostream& buf, uInt16 addr, bool isRead,
+ int places, bool isRam) const
{
switch(addressType(addr))
{
@@ -662,21 +663,24 @@ bool CartDebug::getLabel(ostream& buf, uInt16 addr, bool isRead, int places) con
{
// RAM can use user-defined labels; otherwise we default to
// standard mnemonics
- auto iter = myUserLabels.find(addr);
- if(iter != myUserLabels.end())
- {
- buf << iter->second;
- }
- else
- {
- uInt16 a = addr & 0xFF, offset = addr & 0xFF00;
- if((iter = myUserLabels.find(a)) != myUserLabels.end())
+ AddrToLabel::const_iterator iter;
+ uInt16 a = addr & 0xFF, offset = addr & 0xFF00;
+ bool found = false;
+
+ // Search for nearest label
+ for(uInt16 i = a; i >= 0x80; --i)
+ if((iter = myUserLabels.find(i)) != myUserLabels.end())
+ {
buf << iter->second;
- else
- buf << ourZPMnemonic[a - 0x80];
- if(offset > 0)
- buf << "|$" << Base::HEX2 << offset;
- }
+ if(a != i)
+ buf << "+$" << Base::HEX1 << (a - i);
+ found = true;
+ break;
+ }
+ if(!found)
+ buf << ourZPMnemonic[a - 0x80];
+ if(offset > 0)
+ buf << "|$" << Base::HEX2 << offset;
return true;
}
@@ -684,11 +688,28 @@ bool CartDebug::getLabel(ostream& buf, uInt16 addr, bool isRead, int places) con
case AddrType::ROM:
{
// These addresses can never be in the system labels list
- const auto& iter = myUserLabels.find(addr);
- if(iter != myUserLabels.end())
+ if(isRam) // cartridge RAM
{
- buf << iter->second;
- return true;
+ AddrToLabel::const_iterator iter;
+
+ // Search for nearest label
+ for(uInt16 i = addr; i >= (addr & 0xf000); --i)
+ if((iter = myUserLabels.find(i)) != myUserLabels.end())
+ {
+ buf << iter->second;
+ if(addr != i)
+ buf << "+$" << Base::HEX1 << (addr - i);
+ return true;
+ }
+ }
+ else
+ {
+ const auto& iter = myUserLabels.find(addr);
+ if(iter != myUserLabels.end())
+ {
+ buf << iter->second;
+ return true;
+ }
}
break;
}
@@ -713,10 +734,10 @@ bool CartDebug::getLabel(ostream& buf, uInt16 addr, bool isRead, int places) con
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-string CartDebug::getLabel(uInt16 addr, bool isRead, int places) const
+string CartDebug::getLabel(uInt16 addr, bool isRead, int places, bool isRam) const
{
ostringstream buf;
- getLabel(buf, addr, isRead, places);
+ getLabel(buf, addr, isRead, places, isRam);
return buf.str();
}
diff --git a/src/debugger/CartDebug.hxx b/src/debugger/CartDebug.hxx
index 9d71de51b..25dd59e63 100644
--- a/src/debugger/CartDebug.hxx
+++ b/src/debugger/CartDebug.hxx
@@ -211,8 +211,10 @@ class CartDebug : public DebuggerSystem
If places is not -1 and a label hasn't been defined, return a
formatted hexidecimal address
*/
- bool getLabel(ostream& buf, uInt16 addr, bool isRead, int places = -1) const;
- string getLabel(uInt16 addr, bool isRead, int places = -1) const;
+ bool getLabel(ostream& buf, uInt16 addr, bool isRead,
+ int places = -1, bool isRam = false) const;
+ string getLabel(uInt16 addr, bool isRead,
+ int places = -1, bool isRam = false) const;
int getAddress(const string& label) const;
/**
diff --git a/src/debugger/Debugger.cxx b/src/debugger/Debugger.cxx
index e363a0b82..abfc63ac1 100644
--- a/src/debugger/Debugger.cxx
+++ b/src/debugger/Debugger.cxx
@@ -118,7 +118,8 @@ FBInitStatus Debugger::initializeVideo()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-bool Debugger::start(const string& message, int address, bool read)
+bool Debugger::start(const string& message, int address, bool read,
+ const string& toolTip)
{
if(myOSystem.eventHandler().enterDebugMode())
{
@@ -129,6 +130,7 @@ bool Debugger::start(const string& message, int address, bool read)
if(address > -1)
buf << cartDebug().getLabel(address, read, 4);
myDialog->message().setText(buf.str());
+ myDialog->message().setToolTip(toolTip);
return true;
}
return false;
diff --git a/src/debugger/Debugger.hxx b/src/debugger/Debugger.hxx
index e7096cd4d..6a53238bb 100644
--- a/src/debugger/Debugger.hxx
+++ b/src/debugger/Debugger.hxx
@@ -97,7 +97,8 @@ class Debugger : public DialogContainer
@param message Message to display when entering debugger
@param address An address associated with the message
*/
- bool start(const string& message = "", int address = -1, bool read = true);
+ bool start(const string& message = "", int address = -1, bool read = true,
+ const string& toolTip = "");
bool startWithFatalError(const string& message = "");
/**
diff --git a/src/debugger/DebuggerParser.cxx b/src/debugger/DebuggerParser.cxx
index 73f09268f..cd631bc7e 100644
--- a/src/debugger/DebuggerParser.cxx
+++ b/src/debugger/DebuggerParser.cxx
@@ -1747,9 +1747,13 @@ void DebuggerParser::executeRunTo()
// Create a progress dialog box to show the progress searching through the
// disassembly, since this may be a time-consuming operation
ostringstream buf;
- buf << "RunTo searching through " << max_iterations << " disassembled instructions";
- ProgressDialog progress(debugger.baseDialog(), debugger.lfont(), buf.str());
+ ProgressDialog progress(debugger.baseDialog(), debugger.lfont());
+
+ buf << "RunTo searching through " << max_iterations << " disassembled instructions"
+ << progress.ELLIPSIS;
+ progress.setMessage(buf.str());
progress.setRange(0, max_iterations, 5);
+ progress.open();
bool done = false;
do {
@@ -1763,8 +1767,8 @@ void DebuggerParser::executeRunTo()
done = (BSPF::findIgnoreCase(next, argStrings[0]) != string::npos);
}
// Update the progress bar
- progress.setProgress(count);
- } while(!done && ++count < max_iterations);
+ progress.incProgress();
+ } while(!done && ++count < max_iterations && !progress.isCancelled());
progress.close();
@@ -1789,13 +1793,15 @@ void DebuggerParser::executeRunToPc()
uInt32 count = 0;
bool done = false;
- constexpr uInt32 max_iterations = 1000000;
// Create a progress dialog box to show the progress searching through the
// disassembly, since this may be a time-consuming operation
ostringstream buf;
- buf << "RunTo PC searching through " << max_iterations << " instructions";
- ProgressDialog progress(debugger.baseDialog(), debugger.lfont(), buf.str());
- progress.setRange(0, max_iterations, 5);
+ ProgressDialog progress(debugger.baseDialog(), debugger.lfont());
+
+ buf << " RunTo PC running" << progress.ELLIPSIS << " ";
+ progress.setMessage(buf.str());
+ progress.setRange(0, 100000, 5);
+ progress.open();
do {
debugger.step(false);
@@ -1803,8 +1809,9 @@ void DebuggerParser::executeRunToPc()
// Update romlist to point to current PC
int pcline = cartdbg.addressToLine(debugger.cpuDebug().pc());
done = (pcline >= 0) && (list[pcline].address == args[0]);
- progress.setProgress(count);
- } while(!done && ++count < max_iterations/*list.size()*/);
+ progress.incProgress();
+ ++count;
+ } while(!done && !progress.isCancelled());
progress.close();
if(done)
@@ -1953,20 +1960,24 @@ void DebuggerParser::executeStepwhile()
Expression* expr = YaccParser::getResult();
int ncycles = 0;
uInt32 count = 0;
- constexpr uInt32 max_iterations = 1000000;
// Create a progress dialog box to show the progress searching through the
// disassembly, since this may be a time-consuming operation
ostringstream buf;
- buf << "stepwhile running through " << max_iterations << " disassembled instructions";
- ProgressDialog progress(debugger.baseDialog(), debugger.lfont(), buf.str());
- progress.setRange(0, max_iterations, 5);
+ ProgressDialog progress(debugger.baseDialog(), debugger.lfont());
+
+ buf << "stepwhile running through disassembled instructions"
+ << progress.ELLIPSIS;
+ progress.setMessage(buf.str());
+ progress.setRange(0, 100000, 5);
+ progress.open();
do {
ncycles += debugger.step(false);
- progress.setProgress(count);
- } while (expr->evaluate() && ++count < max_iterations);
+ progress.incProgress();
+ ++count;
+ } while (expr->evaluate() && !progress.isCancelled());
progress.close();
commandResult << "executed " << ncycles << " cycles";
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/CartEnhancedWidget.cxx b/src/debugger/gui/CartEnhancedWidget.cxx
index 97beb1f6b..80da02ffd 100644
--- a/src/debugger/gui/CartEnhancedWidget.cxx
+++ b/src/debugger/gui/CartEnhancedWidget.cxx
@@ -392,5 +392,5 @@ uInt8 CartridgeEnhancedWidget::internalRamGetValue(int addr)
string CartridgeEnhancedWidget::internalRamLabel(int addr)
{
CartDebug& dbg = instance().debugger().cartDebug();
- return dbg.getLabel(addr + ADDR_BASE + myCart.myReadOffset, false);
+ return dbg.getLabel(addr + ADDR_BASE + myCart.myReadOffset, false, -1, true);
}
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..1069e8001 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 read for " + 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 write.");
myCpuDataDest->setEditable(false, true);
-
-
_h = ypos + myPSRegister->getHeight() - y;
}
diff --git a/src/debugger/gui/DataGridOpsWidget.cxx b/src/debugger/gui/DataGridOpsWidget.cxx
index 102d7a94d..2a62ec60c 100644
--- a/src/debugger/gui/DataGridOpsWidget.cxx
+++ b/src/debugger/gui/DataGridOpsWidget.cxx
@@ -33,31 +33,38 @@ DataGridOpsWidget::DataGridOpsWidget(GuiObject* boss, const GUI::Font& font,
xpos = x; ypos = y;
_zeroButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"0", kDGZeroCmd);
-
+ _zeroButton->setToolTip("Zero currently selected value");
+
ypos += bheight + space;
_invButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Inv", kDGInvertCmd);
+ _invButton->setToolTip("Invert currently selected value");
ypos += bheight + space;
_incButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"++", kDGIncCmd);
+ _incButton->setToolTip("Increase currently selected value.");
ypos += bheight + space;
_shiftLeftButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"<<", kDGShiftLCmd);
+ _shiftLeftButton->setToolTip("Shift currently selected value left");
// Move to next column, skip a row
xpos = x + bwidth + space; ypos = y + bheight + space;
_negButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Neg", kDGNegateCmd);
+ _negButton->setToolTip("Negate currently selected value");
ypos += bheight + space;
_decButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"--", kDGDecCmd);
+ _decButton->setToolTip("Decrease currently selected value");
ypos += bheight + space;
_shiftRightButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
">>", kDGShiftRCmd);
+ _shiftRightButton->setToolTip("Shift currently selected value right");
// Calculate real dimensions
_w = 2 * (bwidth+space);
diff --git a/src/debugger/gui/DataGridRamWidget.cxx b/src/debugger/gui/DataGridRamWidget.cxx
new file mode 100644
index 000000000..7445f1d90
--- /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 << label << '\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..9fa82bcdc 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();
@@ -105,15 +107,21 @@ DataGridWidget::DataGridWidget(GuiObject* boss, const GUI::Font& font,
void DataGridWidget::setList(const IntArray& alist, const IntArray& vlist,
const BoolArray& changed)
{
-/*
-cerr << "alist.size() = " << alist.size()
- << ", vlist.size() = " << vlist.size()
- << ", changed.size() = " << changed.size()
- << ", _rows*_cols = " << _rows * _cols << endl << endl;
-*/
+ /*
+ cerr << "alist.size() = " << alist.size()
+ << ", vlist.size() = " << vlist.size()
+ << ", changed.size() = " << changed.size()
+ << ", _rows*_cols = " << _rows * _cols << endl << endl;
+ */
int size = int(vlist.size()); // assume the alist is the same size
assert(size == _rows * _cols);
+ bool dirty = _editMode
+ || !std::equal(_valueList.begin(), _valueList.end(),
+ vlist.begin(), vlist.end())
+ || !std::equal(_changedList.begin(), _changedList.end(),
+ changed.begin(), changed.end());
+
_addrList.clear();
_valueList.clear();
_valueStringList.clear();
@@ -128,19 +136,22 @@ cerr << "alist.size() = " << alist.size()
for(int i = 0; i < size; ++i)
_valueStringList.push_back(Common::Base::toString(_valueList[i], _base));
-/*
-cerr << "_addrList.size() = " << _addrList.size()
- << ", _valueList.size() = " << _valueList.size()
- << ", _changedList.size() = " << _changedList.size()
- << ", _valueStringList.size() = " << _valueStringList.size()
- << ", _rows*_cols = " << _rows * _cols << endl << endl;
-*/
+ /*
+ cerr << "_addrList.size() = " << _addrList.size()
+ << ", _valueList.size() = " << _valueList.size()
+ << ", _changedList.size() = " << _changedList.size()
+ << ", _valueStringList.size() = " << _valueStringList.size()
+ << ", _rows*_cols = " << _rows * _cols << endl << endl;
+ */
enableEditMode(false);
- // Send item selected signal for starting with cell 0
- sendCommand(DataGridWidget::kSelectionChangedCmd, _selectedItem, _id);
+ if(dirty)
+ {
+ // Send item selected signal for starting with cell 0
+ sendCommand(DataGridWidget::kSelectionChangedCmd, _selectedItem, _id);
- setDirty();
+ setDirty();
+ }
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -239,20 +250,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 +304,7 @@ void DataGridWidget::handleMouseWheel(int x, int y, int direction)
else if(direction < 0)
incrementCell();
}
+ dialog().tooltip().hide();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -504,6 +502,7 @@ bool DataGridWidget::handleKeyDown(StellaKey key, StellaMod mod)
sendCommand(DataGridWidget::kSelectionChangedCmd, _selectedItem, _id);
setDirty();
+ dialog().tooltip().hide();
}
_currentKeyDown = key;
@@ -583,14 +582,56 @@ 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)
{
FBSurface& s = _boss->dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
int row, col;
- s.fillRect(_x, _y, _w, _h, hilite && isEnabled() && isEditable() ? _bgcolorhi : onTop ? _bgcolor : kBGColorHi);
+ s.fillRect(_x, _y, _w, _h, hilite && isEnabled() && isEditable() ? _bgcolorhi : _bgcolor);
// Draw the internal grid and labels
int linewidth = _cols * _colWidth;
s.frameRect(_x, _y, _w, _h, hilite && isEnabled() && isEditable() ? kWidColorHi : kColor);
@@ -609,7 +650,7 @@ void DataGridWidget::drawWidget(bool hilite)
int x = _x + 4 + (col * _colWidth);
int y = _y + 2 + (row * _rowHeight);
int pos = row*_cols + col;
- ColorId textColor = onTop ? kTextColor : kColor;
+ ColorId textColor = kTextColor;
// Draw the selected item inverted, on a highlighted background.
if (_currentRow == row && _currentCol == col &&
@@ -629,13 +670,12 @@ void DataGridWidget::drawWidget(bool hilite)
{
if(_changedList[pos])
{
- s.fillRect(x - 3, y - 1, _colWidth-1, _rowHeight-1,
- onTop ? kDbgChangedColor : _bgcolorlo);
+ s.fillRect(x - 3, y - 1, _colWidth-1, _rowHeight-1, kDbgChangedColor);
if(_hiliteList[pos])
textColor = kDbgColorHi;
else
- textColor = onTop ? kDbgChangedTextColor : textColor;
+ textColor = kDbgChangedTextColor;
}
else if(_hiliteList[pos])
textColor = kDbgColorHi;
@@ -677,11 +717,22 @@ int DataGridWidget::getWidth() const
return _w + (_scrollBar ? ScrollBarWidget::scrollBarWidth(_font) : 0);
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void DataGridWidget::setCrossed(bool enable)
+{
+ if(_crossGrid != enable)
+ {
+ _crossGrid = enable;
+ setDirty();
+ }
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DataGridWidget::startEditMode()
{
if (isEditable() && !_editMode && _selectedItem >= 0)
{
+ dialog().tooltip().hide();
enableEditMode(true);
setText("", true); // Erase current entry when starting editing
}
diff --git a/src/debugger/gui/DataGridWidget.hxx b/src/debugger/gui/DataGridWidget.hxx
index 114097b01..ed1b6be02 100644
--- a/src/debugger/gui/DataGridWidget.hxx
+++ b/src/debugger/gui/DataGridWidget.hxx
@@ -82,7 +82,10 @@ class DataGridWidget : public EditableWidget
void setOpsWidget(DataGridOpsWidget* w) { _opsWidget = w; }
- void setCrossed(bool enable) { _crossGrid = enable; }
+ void setCrossed(bool 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..f2ac5c230 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"
@@ -93,6 +94,7 @@ void DebuggerDialog::loadConfig()
myRomTab->loadConfig();
myMessageBox->setText("");
+ myMessageBox->setToolTip("");
}
void DebuggerDialog::saveConfig()
@@ -270,84 +272,72 @@ void DebuggerDialog::handleCommand(CommandSender* sender, int cmd,
void DebuggerDialog::doStep()
{
instance().debugger().parser().run("step");
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doTrace()
{
instance().debugger().parser().run("trace");
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doAdvance()
{
instance().debugger().parser().run("frame #1");
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doScanlineAdvance()
{
instance().debugger().parser().run("scanline #1");
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doRewind()
{
instance().debugger().parser().run("rewind");
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doUnwind()
{
instance().debugger().parser().run("unwind");
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doRewind10()
{
instance().debugger().parser().run("rewind #10");
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doUnwind10()
{
instance().debugger().parser().run("unwind #10");
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doRewindAll()
{
instance().debugger().parser().run("rewind #1000");
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doUnwindAll()
{
instance().debugger().parser().run("unwind #1000");
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doExitDebugger()
{
instance().debugger().parser().run("run");
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doExitRom()
{
instance().debugger().parser().run("exitrom");
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -406,6 +396,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/DelayQueueWidget.cxx b/src/debugger/gui/DelayQueueWidget.cxx
index 6dbd613be..d0ad50351 100644
--- a/src/debugger/gui/DelayQueueWidget.cxx
+++ b/src/debugger/gui/DelayQueueWidget.cxx
@@ -50,7 +50,11 @@ void DelayQueueWidget::loadConfig() {
using Common::Base;
for (auto&& line : myLines) {
if (!delayQueueIterator->isValid()) {
- line = "";
+ if(line != "")
+ {
+ setDirty();
+ line = "";
+ }
continue;
}
@@ -81,7 +85,11 @@ void DelayQueueWidget::loadConfig() {
break;
}
- line = ss.str();
+ if(line != ss.str())
+ {
+ setDirty();
+ line = ss.str();
+ }
delayQueueIterator->next();
}
}
@@ -90,7 +98,6 @@ void DelayQueueWidget::loadConfig() {
void DelayQueueWidget::drawWidget(bool hilite)
{
FBSurface& surface = _boss->dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
int y = _y,
x = _x,
@@ -102,14 +109,14 @@ void DelayQueueWidget::drawWidget(bool hilite)
y += 1;
x += 1;
w -= 1;
- surface.fillRect(x, y, w - 1, _h - 2, onTop ? kDlgColor : _bgcolorlo);
+ surface.fillRect(x, y, w - 1, _h - 2, kDlgColor);
y += 2;
x += 2;
w -= 3;
for (const auto& line : myLines) {
- surface.drawString(_font, line, x, y, w, onTop ? _textcolor : kColor);
+ surface.drawString(_font, line, x, y, w, _textcolor);
y += lineHeight;
}
}
diff --git a/src/debugger/gui/PromptWidget.cxx b/src/debugger/gui/PromptWidget.cxx
index 490f7b8a6..116080bf7 100644
--- a/src/debugger/gui/PromptWidget.cxx
+++ b/src/debugger/gui/PromptWidget.cxx
@@ -81,9 +81,7 @@ void PromptWidget::drawWidget(bool hilite)
{
//cerr << "PromptWidget::drawWidget\n";
ColorId fgcolor, bgcolor;
-
FBSurface& s = _boss->dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
// Draw text
int start = _scrollLine - _linesPerPage + 1;
@@ -104,7 +102,7 @@ void PromptWidget::drawWidget(bool hilite)
else
fgcolor = ColorId(c >> 8);
- s.drawChar(_font, c & 0x7f, x, y, onTop ? fgcolor : kColor);
+ s.drawChar(_font, c & 0x7f, x, y, fgcolor);
x += _kConsoleCharWidth;
}
y += _kConsoleLineHeight;
@@ -938,8 +936,6 @@ void PromptWidget::drawCaret()
{
//cerr << "PromptWidget::drawCaret()\n";
FBSurface& s = _boss->dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
-
int line = _currentPos / _lineWidth;
// Don't draw the cursor if it's not in the current view
@@ -951,7 +947,7 @@ void PromptWidget::drawCaret()
int y = _y + displayLine * _kConsoleLineHeight;
char c = buffer(_currentPos); //FIXME: int to char??
- s.fillRect(x, y, _kConsoleCharWidth, _kConsoleLineHeight, onTop ? kTextColor : kColor);
+ s.fillRect(x, y, _kConsoleCharWidth, _kConsoleLineHeight, kTextColor);
s.drawChar(_font, c, x, y + 2, kBGColor);
}
diff --git a/src/debugger/gui/RamWidget.cxx b/src/debugger/gui/RamWidget.cxx
index 77456f810..4e097d9be 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);
@@ -78,18 +78,21 @@ RamWidget::RamWidget(GuiObject* boss, const GUI::Font& lfont, const GUI::Font& n
by += bheight + VGAP * 6;
mySearchButton = new ButtonWidget(boss, lfont, bx, by, bwidth, bheight,
"Search" + ELLIPSIS, kSearchCmd);
+ mySearchButton->setToolTip("Search and highlight found values.");
wid.push_back(mySearchButton);
mySearchButton->setTarget(this);
by += bheight + VGAP;
myCompareButton = new ButtonWidget(boss, lfont, bx, by, bwidth, bheight,
"Compare" + ELLIPSIS, kCmpCmd);
+ myCompareButton->setToolTip("Compare highlighted values.");
wid.push_back(myCompareButton);
myCompareButton->setTarget(this);
by += bheight + VGAP;
myRestartButton = new ButtonWidget(boss, lfont, bx, by, bwidth, bheight,
"Reset", kRestartCmd);
+ myRestartButton->setToolTip("Reset search/compare mode.");
wid.push_back(myRestartButton);
myRestartButton->setTarget(this);
@@ -366,6 +369,9 @@ void RamWidget::showInputBox(int cmd)
myInputBox->show(x, y, dialog().surface().dstRect());
myInputBox->setText("");
myInputBox->setMessage("");
+ myInputBox->setToolTip(cmd == kSValEntered
+ ? "Enter search value (leave blank for all)."
+ : "Enter relative or absolute value\nto compare with searched values.");
myInputBox->setFocus(0);
myInputBox->setEmitSignal(cmd);
myInputBox->setTitle(cmd == kSValEntered ? "Search" : "Compare");
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..9f8b9d589 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,14 +446,86 @@ 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)
{
FBSurface& s = _boss->dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
const CartDebug::DisassemblyList& dlist = myDisasm->list;
int i, pos, xpos, ypos, len = int(dlist.size());
- ColorId textColor = onTop ? kTextColor : kColor;
+ ColorId textColor = kTextColor;
const Common::Rect& r = getEditRect();
const Common::Rect& l = getLineRect();
@@ -480,21 +544,22 @@ 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();
+ // All checkboxes have to be redrawn because RomListWidget clears its whole area
+ // Also draw immediately, because chain order is not deterministic
myCheckList[i]->draw();
// Draw highlighted item in a frame
- if (_highlightedItem == pos)
- s.frameRect(_x + l.x() - 3, ypos - 1, _w - l.x(), _lineHeight, onTop ? kWidColorHi : kBGColorLo);
+ if(_highlightedItem == pos)
+ s.frameRect(_x + l.x() - 3, ypos - 1, _w - l.x(), _lineHeight, kWidColorHi);
// Draw the selected item inverted, on a highlighted background.
if(_selectedItem == pos && _hasFocus)
@@ -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..fe2ce6d9b 100644
--- a/src/debugger/gui/TiaInfoWidget.cxx
+++ b/src/debugger/gui/TiaInfoWidget.cxx
@@ -60,18 +60,21 @@ 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
@@ -84,6 +87,7 @@ TiaInfoWidget::TiaInfoWidget(GuiObject* boss, const GUI::Font& lfont,
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 +97,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 +107,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
@@ -159,7 +169,10 @@ void TiaInfoWidget::loadConfig()
uInt64 total = tia.cyclesLo() + (uInt64(tia.cyclesHi()) << 32);
uInt64 totalOld = oldTia.info[2] + (uInt64(oldTia.info[3]) << 32);
myTotalCycles->setText(Common::Base::toString(uInt32(total) / 1000000, Common::Base::Fmt::_10_6) + "e6",
- total != totalOld);
+ total / 1000000 != totalOld / 1000000);
+ myTotalCycles->setToolTip("Total CPU cycles (E notation) executed for this session ("
+ + std::to_string(total) + ").");
+
uInt64 delta = total - totalOld;
myDeltaCycles->setText(Common::Base::toString(uInt32(delta), Common::Base::Fmt::_10_8)); // no coloring
diff --git a/src/debugger/gui/TiaOutputWidget.cxx b/src/debugger/gui/TiaOutputWidget.cxx
index 01d6d094b..d101fa674 100644
--- a/src/debugger/gui/TiaOutputWidget.cxx
+++ b/src/debugger/gui/TiaOutputWidget.cxx
@@ -22,6 +22,7 @@
#include "FBSurface.hxx"
#include "Widget.hxx"
#include "GuiObject.hxx"
+#include "Dialog.hxx"
#include "ContextMenu.hxx"
#include "TiaZoomWidget.hxx"
#include "Debugger.hxx"
@@ -55,6 +56,7 @@ TiaOutputWidget::TiaOutputWidget(GuiObject* boss, const GUI::Font& font,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TiaOutputWidget::loadConfig()
{
+ setEnabled(true);
setDirty();
}
@@ -92,10 +94,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
}
@@ -108,7 +110,7 @@ void TiaOutputWidget::handleMouseDown(int x, int y, MouseButton b, int clickCoun
else if(b == MouseButton::RIGHT)
{
myClickX = x;
- myClickY = y;
+ myClickY = y - 1;
// Add menu at current x,y mouse location
myMenu->show(x + getAbsX(), y + getAbsY(), dialog().surface().dstRect());
@@ -135,7 +137,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 +146,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 +160,52 @@ 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 startLine = instance().console().tia().startLine();
+ 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 + startLine
+ << "\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..f6bc43e6e 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]);
@@ -1043,8 +1043,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 +1079,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..af8e468dd 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"
@@ -115,7 +117,7 @@ void TiaZoomWidget::recalc()
void TiaZoomWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount)
{
myClickX = x;
- myClickY = y;
+ myClickY = y - 1;
// Button 1 is for 'drag'/movement of the image
// Button 2 is for context menu
@@ -141,9 +143,11 @@ 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;
+ myClickY = y - 1;
if(direction > 0)
{
@@ -162,6 +166,7 @@ void TiaZoomWidget::handleMouseMoved(int x, int y)
{
if(myMouseMoving)
{
+ y--;
int diffx = x + myOffXLo - myClickX;
int diffy = y + myOffYLo - myClickY;
@@ -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,48 @@ 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();
+ const uInt32 startLine = instance().console().tia().startLine();
+ uInt8* tiaOutputBuffer = instance().console().tia().outputBuffer();
+ ostringstream buf;
+
+ buf << _toolTipText
+ << "X: #" << idx.x
+ << "\nY: #" << idx.y + startLine
+ << "\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..1cc53068f 100644
--- a/src/debugger/gui/TiaZoomWidget.hxx
+++ b/src/debugger/gui/TiaZoomWidget.hxx
@@ -27,15 +27,21 @@ class ContextMenu;
class TiaZoomWidget : public Widget, public CommandSender
{
public:
+ using Widget::setPos;
+
TiaZoomWidget(GuiObject *boss, const GUI::Font& font,
int x, int y, int w, int h);
~TiaZoomWidget() override = default;
void loadConfig() override;
- void setPos(int x, int y);
+ void setPos(int x, int y) override;
+
+ 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..629e58c57 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)
{
@@ -61,12 +70,35 @@ void ToggleBitWidget::setList(const StringList& off, const StringList& on)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ToggleBitWidget::setState(const BoolArray& state, const BoolArray& changed)
{
+ if(!std::equal(_changedList.begin(), _changedList.end(),
+ changed.begin(), changed.end()))
+ setDirty();
+
_stateList.clear();
_stateList = state;
_changedList.clear();
_changedList = 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;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -74,7 +106,6 @@ void ToggleBitWidget::drawWidget(bool hilite)
{
//cerr << "ToggleBitWidget::drawWidget\n";
FBSurface& s = dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
int row, col;
string buffer;
@@ -116,18 +147,16 @@ void ToggleBitWidget::drawWidget(bool hilite)
// Highlight changes
if(_changedList[pos])
{
- s.fillRect(x - 3, y - 1, _colWidth-1, _rowHeight-1,
- onTop ? kDbgChangedColor : _bgcolorlo);
- s.drawString(_font, buffer, x, y, _colWidth, onTop ? kDbgChangedTextColor : kColor);
+ s.fillRect(x - 3, y - 1, _colWidth-1, _rowHeight-1, kDbgChangedColor);
+ s.drawString(_font, buffer, x, y, _colWidth, kDbgChangedTextColor);
}
else
- s.drawString(_font, buffer, x, y, _colWidth,
- onTop ? textColor : kColor);
+ s.drawString(_font, buffer, x, y, _colWidth, textColor);
}
else
{
- s.fillRect(x - 3, y - 1, _colWidth-1, _rowHeight-1, onTop ? kBGColorHi : kDlgColor);
- s.drawString(_font, buffer, x, y, _colWidth, onTop ? kTextColor : kColor);
+ s.fillRect(x - 3, y - 1, _colWidth-1, _rowHeight-1, kBGColorHi);
+ s.drawString(_font, buffer, x, y, _colWidth, kTextColor);
}
}
}
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..c26600130 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();
@@ -50,15 +51,18 @@ void TogglePixelWidget::setState(const BoolArray& state)
for(int col = 0; col < _cols; col++)
{
int pos = row * _cols + col;
+ bool changed = _stateList[pos] != state[pos];
- _changedList[pos] = _stateList[pos] != state[pos];
+ if(_changedList[pos] != changed)
+ {
+ _changedList[pos] = changed;
+ setDirty();
+ }
}
}
_stateList.clear();
_stateList = state;
-
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -112,12 +116,21 @@ int TogglePixelWidget::getIntState()
return value;
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void TogglePixelWidget::setCrossed(bool enable)
+{
+ if(_crossBits != enable)
+ {
+ _crossBits = enable;
+ setDirty();
+ }
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TogglePixelWidget::drawWidget(bool hilite)
{
//cerr << "TogglePixelWidget::drawWidget\n";
FBSurface& s = dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
int row, col;
s.frameRect(_x, _y, _w, _h, hilite && isEnabled() && isEditable() ? kWidColorHi : kColor);
@@ -145,7 +158,7 @@ void TogglePixelWidget::drawWidget(bool hilite)
// Either draw the pixel in given color, or erase (show background)
s.fillRect(x - 3, y - 1, _colWidth-1, _rowHeight-1,
- _stateList[pos] ? onTop ? _pixelColor : kColor : onTop ? _backgroundColor : kBGColorLo);
+ _stateList[pos] ? _pixelColor : _backgroundColor);
if (_changedList[pos])
s.frameRect(x - 3, y - 1, _colWidth - 1, _rowHeight - 1, kDbgChangedColor);
}
diff --git a/src/debugger/gui/TogglePixelWidget.hxx b/src/debugger/gui/TogglePixelWidget.hxx
index 6177cac45..503404c6f 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; }
+ void setCrossed(bool 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/CartCDF.cxx b/src/emucore/CartCDF.cxx
index 93e330c39..ae06bdc05 100644
--- a/src/emucore/CartCDF.cxx
+++ b/src/emucore/CartCDF.cxx
@@ -277,7 +277,8 @@ uInt8 CartridgeCDF::peek(uInt16 address)
if DIGITAL_AUDIO_ON
{
// retrieve packed sample (max size is 2K, or 4K of unpacked data)
- uInt32 sampleaddress = getSample() + (myMusicCounters[0] >> 21);
+
+ uInt32 sampleaddress = getSample() + (myMusicCounters[0] >> (isCDFJplus() ? 13 : 21));
// get sample value from ROM or RAM
if (sampleaddress < 0x00080000)
@@ -288,7 +289,7 @@ uInt8 CartridgeCDF::peek(uInt16 address)
peekvalue = 0;
// make sure current volume value is in the lower nybble
- if ((myMusicCounters[0] & (1<<20)) == 0)
+ if ((myMusicCounters[0] & (1<<(isCDFJplus() ? 12 : 20))) == 0)
peekvalue >>= 4;
peekvalue &= 0x0f;
}
diff --git a/src/emucore/CartEnhanced.cxx b/src/emucore/CartEnhanced.cxx
index 22eb63ee7..487a22741 100644
--- a/src/emucore/CartEnhanced.cxx
+++ b/src/emucore/CartEnhanced.cxx
@@ -60,7 +60,7 @@ CartridgeEnhanced::CartridgeEnhanced(const ByteBuffer& image, size_t size,
void CartridgeEnhanced::install(System& system)
{
// limit banked RAM size to the size of one RAM bank
- const uInt32 ramSize = myRamBankCount > 0 ? 1 << (myBankShift - 1) : uInt32(myRamSize);
+ const uInt16 ramSize = myRamBankCount > 0 ? 1 << (myBankShift - 1) : uInt16(myRamSize);
// calculate bank switching and RAM sizes and masks
myBankSize = 1 << myBankShift; // e.g. = 2 ^ 12 = 4K = 0x1000
@@ -93,7 +93,7 @@ void CartridgeEnhanced::install(System& system)
// Set the page accessing method for the RAM writing pages
// Note: Writes are mapped to poke() (NOT using direcPokeBase) to check for read from write port (RWP)
access.type = System::PageAccessType::WRITE;
- for(size_t addr = ROM_OFFSET + myWriteOffset; addr < ROM_OFFSET + myWriteOffset + myRamSize; addr += System::PAGE_SIZE)
+ for(uInt16 addr = ROM_OFFSET + myWriteOffset; addr < ROM_OFFSET + myWriteOffset + myRamSize; addr += System::PAGE_SIZE)
{
const uInt16 offset = addr & myRamMask;
@@ -105,7 +105,7 @@ void CartridgeEnhanced::install(System& system)
// Set the page accessing method for the RAM reading pages
access.type = System::PageAccessType::READ;
- for(size_t addr = ROM_OFFSET + myReadOffset; addr < ROM_OFFSET + myReadOffset + myRamSize; addr += System::PAGE_SIZE)
+ for(uInt16 addr = ROM_OFFSET + myReadOffset; addr < ROM_OFFSET + myReadOffset + myRamSize; addr += System::PAGE_SIZE)
{
const uInt16 offset = addr & myRamMask;
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/DispatchResult.cxx b/src/emucore/DispatchResult.cxx
index 20a208a56..9ba5326ca 100644
--- a/src/emucore/DispatchResult.cxx
+++ b/src/emucore/DispatchResult.cxx
@@ -31,11 +31,13 @@ void DispatchResult::setOk(uInt64 cycles)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void DispatchResult::setDebugger(uInt64 cycles, const string& message, int address, bool wasReadTrap)
+void DispatchResult::setDebugger(uInt64 cycles, const string& message,
+ const string& tooltip, int address, bool wasReadTrap)
{
myStatus = Status::debugger;
myCycles = cycles;
myMessage = message;
+ myToolTip = tooltip;
myAddress = address;
myWasReadTrap = wasReadTrap;
}
diff --git a/src/emucore/DispatchResult.hxx b/src/emucore/DispatchResult.hxx
index 6d76cf5ce..38e80a9c5 100644
--- a/src/emucore/DispatchResult.hxx
+++ b/src/emucore/DispatchResult.hxx
@@ -37,12 +37,14 @@ class DispatchResult
bool wasReadTrap() const { assertStatus(Status::debugger); return myWasReadTrap; }
+ const string& getToolTip() const { assertStatus(Status::debugger, Status::fatal); return myToolTip; }
+
bool isSuccess() const;
void setOk(uInt64 cycles);
- void setDebugger(uInt64 cycles, const string& message = "", int address = -1,
- bool wasReadTrap = true);
+ void setDebugger(uInt64 cycles, const string& message = "",
+ const string& tooltip = "", int address = -1, bool wasReadTrap = true);
void setFatal(uInt64 cycles);
@@ -73,6 +75,8 @@ class DispatchResult
int myAddress{0};
bool myWasReadTrap{false};
+
+ string myToolTip;
};
#endif // DISPATCH_RESULT_HXX
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..16030b579 100644
--- a/src/emucore/FBSurface.cxx
+++ b/src/emucore/FBSurface.cxx
@@ -296,27 +296,47 @@ 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)
+#ifdef GUI_SUPPORT
+ 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);
+#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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 +351,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/FSNode.cxx b/src/emucore/FSNode.cxx
index f2e5e477a..6ff0b6482 100644
--- a/src/emucore/FSNode.cxx
+++ b/src/emucore/FSNode.cxx
@@ -76,10 +76,64 @@ bool FilesystemNode::exists() const
return _realNode ? _realNode->exists() : false;
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+bool FilesystemNode::getAllChildren(FSList& fslist, ListMode mode,
+ const NameFilter& filter,
+ bool includeParentDirectory,
+ const CancelCheck& isCancelled) const
+{
+ if(getChildren(fslist, mode, filter, includeParentDirectory, true, isCancelled))
+ {
+ // Sort only once at the end
+ #if defined(ZIP_SUPPORT)
+ // before sorting, replace single file ZIP archive names with contained file names
+ // because they are displayed using their contained file names
+ for(auto& i : fslist)
+ {
+ if(BSPF::endsWithIgnoreCase(i.getPath(), ".zip"))
+ {
+ FilesystemNodeZIP zipNode(i.getPath());
+
+ i.setName(zipNode.getName());
+ }
+ }
+ #endif
+
+ std::sort(fslist.begin(), fslist.end(),
+ [](const FilesystemNode& node1, const FilesystemNode& node2)
+ {
+ if(node1.isDirectory() != node2.isDirectory())
+ return node1.isDirectory();
+ else
+ return BSPF::compareIgnoreCase(node1.getName(), node2.getName()) < 0;
+ }
+ );
+
+ #if defined(ZIP_SUPPORT)
+ // After sorting replace zip files with zip nodes
+ for(auto& i : fslist)
+ {
+ if(BSPF::endsWithIgnoreCase(i.getPath(), ".zip"))
+ {
+ // Force ZIP c'tor to be called
+ AbstractFSNodePtr ptr = FilesystemNodeFactory::create(i.getPath(),
+ FilesystemNodeFactory::Type::ZIP);
+ FilesystemNode zipNode(ptr);
+ i = zipNode;
+ }
+ }
+ #endif
+ return true;
+ }
+ return false;
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
const NameFilter& filter,
- bool includeParentDirectory) const
+ bool includeChildDirectories,
+ bool includeParentDirectory,
+ const CancelCheck& isCancelled) const
{
if (!_realNode || !_realNode->isDirectory())
return false;
@@ -90,12 +144,18 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
if (!_realNode->getChildren(tmp, mode))
return false;
+ // when incuding child directories, everything must be sorted once at the end
+ if(!includeChildDirectories)
+ {
+ if(isCancelled())
+ return false;
+
#if defined(ZIP_SUPPORT)
// before sorting, replace single file ZIP archive names with contained file names
// because they are displayed using their contained file names
- for (auto& i : tmp)
+ for(auto& i : tmp)
{
- if (BSPF::endsWithIgnoreCase(i->getPath(), ".zip"))
+ if(BSPF::endsWithIgnoreCase(i->getPath(), ".zip"))
{
FilesystemNodeZIP node(i->getPath());
@@ -104,15 +164,16 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
}
#endif
- std::sort(tmp.begin(), tmp.end(),
- [](const AbstractFSNodePtr& node1, const AbstractFSNodePtr& node2)
+ std::sort(tmp.begin(), tmp.end(),
+ [](const AbstractFSNodePtr& node1, const AbstractFSNodePtr& node2)
{
- if (node1->isDirectory() != node2->isDirectory())
+ if(node1->isDirectory() != node2->isDirectory())
return node1->isDirectory();
else
return BSPF::compareIgnoreCase(node1->getName(), node2->getName()) < 0;
}
- );
+ );
+ }
// Add parent node, if it is valid to do so
if (includeParentDirectory && hasParent())
@@ -125,29 +186,54 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode,
// And now add the rest of the entries
for (const auto& i: tmp)
{
+ if(isCancelled())
+ return false;
+
#if defined(ZIP_SUPPORT)
if (BSPF::endsWithIgnoreCase(i->getPath(), ".zip"))
{
// Force ZIP c'tor to be called
AbstractFSNodePtr ptr = FilesystemNodeFactory::create(i->getPath(),
- FilesystemNodeFactory::Type::ZIP);
- FilesystemNode node(ptr);
- if (filter(node))
- fslist.emplace_back(node);
+ FilesystemNodeFactory::Type::ZIP);
+ FilesystemNode zipNode(ptr);
+
+ if(filter(zipNode))
+ {
+ if(!includeChildDirectories)
+ fslist.emplace_back(zipNode);
+ else
+ {
+ // filter by zip node but add file node
+ FilesystemNode node(i);
+ fslist.emplace_back(node);
+ }
+ }
}
else
#endif
{
// Make directories stand out
- if (i->isDirectory())
+ if(i->isDirectory())
i->setName(" [" + i->getName() + "]");
FilesystemNode node(i);
- if (filter(node))
- fslist.emplace_back(node);
+
+ if(includeChildDirectories)
+ {
+ if(i->isDirectory())
+ node.getChildren(fslist, mode, filter, includeChildDirectories, false, isCancelled);
+ else
+ // do not add directories in this mode
+ if(filter(node))
+ fslist.emplace_back(node);
+ }
+ else
+ {
+ if(filter(node))
+ fslist.emplace_back(node);
+ }
}
}
-
return true;
}
diff --git a/src/emucore/FSNode.hxx b/src/emucore/FSNode.hxx
index cf76f9ac1..05378d185 100644
--- a/src/emucore/FSNode.hxx
+++ b/src/emucore/FSNode.hxx
@@ -57,6 +57,7 @@ class FilesystemNode
/** Function used to filter the file listing. Returns true if the filename
should be included, else false.*/
using NameFilter = std::function;
+ using CancelCheck = std::function const;
/**
* Create a new pathless FilesystemNode. Since there's no path associated
@@ -114,6 +115,18 @@ class FilesystemNode
*/
bool exists() const;
+ /**
+ * Return a list of child nodes of this and all sub-directories. If called on a node
+ * that does not represent a directory, false is returned.
+ *
+ * @return true if successful, false otherwise (e.g. when the directory
+ * does not exist).
+ */
+ bool getAllChildren(FSList& fslist, ListMode mode = ListMode::DirectoriesOnly,
+ const NameFilter& filter = [](const FilesystemNode&) { return true; },
+ bool includeParentDirectory = true,
+ const CancelCheck& isCancelled = []() { return false; }) const;
+
/**
* Return a list of child nodes of this directory node. If called on a node
* that does not represent a directory, false is returned.
@@ -123,7 +136,9 @@ class FilesystemNode
*/
bool getChildren(FSList& fslist, ListMode mode = ListMode::DirectoriesOnly,
const NameFilter& filter = [](const FilesystemNode&){ return true; },
- bool includeParentDirectory = true) const;
+ bool includeChildDirectories = false,
+ bool includeParentDirectory = true,
+ const CancelCheck& isCancelled = []() { return false; }) const;
/**
* Set/get a string representation of the name of the file. This is can be
@@ -273,8 +288,8 @@ class FilesystemNode
string getPathWithExt(const string& ext) const;
private:
- AbstractFSNodePtr _realNode;
explicit FilesystemNode(const AbstractFSNodePtr& realNode);
+ AbstractFSNodePtr _realNode;
void setPath(const string& path);
};
diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx
index 97bbb2153..2171382c5 100644
--- a/src/emucore/FrameBuffer.cxx
+++ b/src/emucore/FrameBuffer.cxx
@@ -198,7 +198,7 @@ void FrameBuffer::setupFonts()
FontDesc FrameBuffer::getFontDesc(const string& name) const
{
if(name == "small")
- return GUI::consoleBDesc; // 8x13
+ return GUI::consoleDesc; // 8x13
else if(name == "low_medium")
return GUI::consoleMediumBDesc; // 9x15
else if(name == "medium")
@@ -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,120 @@ 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 << "m";
+ //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 +861,7 @@ void FrameBuffer::resetSurfaces()
freeSurfaces();
reloadSurfaces();
- update(true); // force full update
+ update(UpdateMode::REDRAW); // force full update
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -898,10 +914,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 +931,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 +953,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 +967,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 +1005,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 +1017,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 +1070,7 @@ void FrameBuffer::toggleAdaptRefresh(bool toggle)
msg << (isAdaptRefresh ? "enabled" : "disabled");
msg << " (" << myBackend->refreshRate() << " Hz)";
- showMessage(msg.str());
+ showTextMessage(msg.str());
}
}
#endif
@@ -1069,7 +1096,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 +1133,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 +1190,7 @@ FBInitStatus FrameBuffer::applyVideoMode()
resetSurfaces();
setCursorState();
+ myPendingRender = true;
}
else
Logger::error("ERROR: Couldn't initialize video subsystem");
@@ -1248,9 +1276,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 +1327,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 +1337,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 +1350,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 +1363,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 +1376,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/M6502.cxx b/src/emucore/M6502.cxx
index 86b050dfb..295697af2 100644
--- a/src/emucore/M6502.cxx
+++ b/src/emucore/M6502.cxx
@@ -243,7 +243,9 @@ inline void M6502::_execute(uInt64 cycles, DispatchResult& result)
myJustHitReadTrapFlag = myJustHitWriteTrapFlag = false;
myLastBreakCycle = mySystem->cycles();
- result.setDebugger(currentCycles, myHitTrapInfo.message, myHitTrapInfo.address, read);
+ result.setDebugger(currentCycles, myHitTrapInfo.message,
+ read ? "Read trap" : "Write trap",
+ myHitTrapInfo.address, read);
return;
}
@@ -264,7 +266,7 @@ inline void M6502::_execute(uInt64 cycles, DispatchResult& result)
ostringstream msg;
msg << "BP: $" << Common::Base::HEX4 << PC << ", bank #" << std::dec << int(bank);
- result.setDebugger(currentCycles, msg.str());
+ result.setDebugger(currentCycles, msg.str(), "Breakpoint");
}
return;
}
@@ -278,7 +280,7 @@ inline void M6502::_execute(uInt64 cycles, DispatchResult& result)
msg << "CBP[" << Common::Base::HEX2 << cond << "]: " << myCondBreakNames[cond];
myLastBreakCycle = mySystem->cycles();
- result.setDebugger(currentCycles, msg.str());
+ result.setDebugger(currentCycles, msg.str(), "Conditional breakpoint");
return;
}
}
@@ -327,7 +329,7 @@ inline void M6502::_execute(uInt64 cycles, DispatchResult& result)
{
ostringstream msg;
msg << "RWP[@ $" << Common::Base::HEX4 << rwpAddr << "]: ";
- result.setDebugger(currentCycles, msg.str(), oldPC);
+ result.setDebugger(currentCycles, msg.str(), "Read from write port", oldPC);
return;
}
}
@@ -339,7 +341,7 @@ inline void M6502::_execute(uInt64 cycles, DispatchResult& result)
{
ostringstream msg;
msg << "WRP[@ $" << Common::Base::HEX4 << wrpAddr << "]: ";
- result.setDebugger(currentCycles, msg.str(), oldPC);
+ result.setDebugger(currentCycles, msg.str(), "Write to read port", oldPC);
return;
}
}
@@ -348,7 +350,7 @@ inline void M6502::_execute(uInt64 cycles, DispatchResult& result)
myExecutionStatus |= FatalErrorBit;
result.setMessage(e.what());
} catch (const EmulationWarning& e) {
- result.setDebugger(currentCycles, e.what(), PC);
+ result.setDebugger(currentCycles, e.what(), "Emulation exception", PC);
return;
}
diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx
index 611439e45..b8f8842e6 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());
}
}
@@ -775,7 +775,8 @@ double OSystem::dispatchEmulation(EmulationWorker& emulationWorker)
myDebugger->start(
dispatchResult.getMessage(),
dispatchResult.getAddress(),
- dispatchResult.wasReadTrap()
+ dispatchResult.wasReadTrap(),
+ dispatchResult.getToolTip()
);
#endif
diff --git a/src/emucore/PointingDevice.cxx b/src/emucore/PointingDevice.cxx
index cbd7f89c9..b48c372a3 100644
--- a/src/emucore/PointingDevice.cxx
+++ b/src/emucore/PointingDevice.cxx
@@ -81,7 +81,7 @@ void PointingDevice::update()
return;
// Update horizontal direction
- cerr << myEvent.get(Event::MouseAxisXMove) << ", " << myHCounterRemainder << endl;
+ //cerr << myEvent.get(Event::MouseAxisXMove) << ", " << myHCounterRemainder << endl;
updateDirection( myEvent.get(Event::MouseAxisXMove), myHCounterRemainder,
myTrackBallLeft, myTrackBallLinesH, myScanCountH, myFirstScanOffsetH);
@@ -90,7 +90,7 @@ void PointingDevice::update()
myTrackBallDown, myTrackBallLinesV, myScanCountV, myFirstScanOffsetV);
// We allow left and right mouse buttons for fire button
- setPin(DigitalPin::Six, !getAutoFireState(myEvent.get(Event::JoystickZeroFire) == 0 ||
+ setPin(DigitalPin::Six, !getAutoFireState(myEvent.get(Event::JoystickZeroFire) ||
myEvent.get(Event::MouseButtonLeftValue) || myEvent.get(Event::MouseButtonRightValue)));
}
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/Serializer.cxx b/src/emucore/Serializer.cxx
index 80282bdc2..7f94dced2 100644
--- a/src/emucore/Serializer.cxx
+++ b/src/emucore/Serializer.cxx
@@ -91,6 +91,8 @@ void Serializer::rewind()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
size_t Serializer::size() const
{
+ myStream->seekp(0, std::ios::end);
+
return myStream->tellp();
}
diff --git a/src/emucore/Settings.cxx b/src/emucore/Settings.cxx
index f7fa27ae8..a074f5697 100644
--- a/src/emucore/Settings.cxx
+++ b/src/emucore/Settings.cxx
@@ -145,6 +145,7 @@ Settings::Settings()
setPermanent("launcherres", Common::Size(900, 600));
setPermanent("launcherfont", "medium");
setPermanent("launcherroms", "true");
+ setPermanent("launchersubdirs", "false");
setPermanent("romviewer", "1");
setPermanent("lastrom", "");
@@ -542,6 +543,7 @@ void Settings::usage() const
<< " large12|large14|\n"
<< " large16>\n"
<< " -launcherroms <1|0> Show only ROMs in the launcher (vs. all files)\n"
+ << " -launchersubdirs <1|0> Show files from subdirectories too\n"
<< " -romviewer Show ROM info viewer at given zoom level in ROM\n"
<< " launcher (use 0 for off)\n"
<< " -followlauncher <0|1> Default ROM path follows launcher navigation\n"
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..13afb335a 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)
{
@@ -95,7 +81,6 @@ void CheckListWidget::drawWidget(bool hilite)
{
//cerr << "CheckListWidget::drawWidget\n";
FBSurface& s = _boss->dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
int i, pos, len = int(_list.size());
// Draw a thin frame around the list and to separate columns
@@ -126,18 +111,17 @@ void CheckListWidget::drawWidget(bool hilite)
}
else
s.frameRect(_x + r.x() - 3, _y + 1 + _lineHeight * i,
- _w - r.x(), _lineHeight, onTop ? kTextColorHi : kColor);
+ _w - r.x(), _lineHeight, kTextColorHi);
}
if (_selectedItem == pos && _editMode)
{
adjustOffset();
- s.drawString(_font, editString(), _x + r.x(), y, r.w(), onTop ? kTextColor : kColor,
+ s.drawString(_font, editString(), _x + r.x(), y, r.w(), kTextColor,
TextAlign::Left, -_editScrollOffset, false);
}
else
- s.drawString(_font, _list[pos], _x + r.x(), y, r.w(),
- onTop ? textColor : kColor);
+ s.drawString(_font, _list[pos], _x + r.x(), y, r.w(), textColor);
}
// Only draw the caret while editing, and if it's in the current viewport
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/ColorWidget.cxx b/src/gui/ColorWidget.cxx
index 27f4bc3cf..94ef4bbf4 100644
--- a/src/gui/ColorWidget.cxx
+++ b/src/gui/ColorWidget.cxx
@@ -37,15 +37,27 @@ ColorWidget::ColorWidget(GuiObject* boss, const GUI::Font& font,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ColorWidget::setColor(ColorId color)
{
- _color = color;
- setDirty();
+ if(_color != color)
+ {
+ _color = color;
+ setDirty();
+ }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void ColorWidget::setCrossed(bool enable)
+{
+ if(_crossGrid != enable)
+ {
+ _crossGrid = enable;
+ setDirty();
+ }
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ColorWidget::drawWidget(bool hilite)
{
FBSurface& s = dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
if(_framed)
{
@@ -53,11 +65,11 @@ void ColorWidget::drawWidget(bool hilite)
s.frameRect(_x, _y, _w, _h + 1, kColor);
// Show the currently selected color
- s.fillRect(_x + 1, _y + 1, _w - 2, _h - 1, onTop ? isEnabled() ? _color : kWidColor : kBGColorLo);
+ s.fillRect(_x + 1, _y + 1, _w - 2, _h - 1, isEnabled() ? _color : kWidColor);
}
else
{
- s.fillRect(_x, _y, _w, _h, onTop ? isEnabled() ? _color : kWidColor : kBGColorLo);
+ s.fillRect(_x, _y, _w, _h, isEnabled() ? _color : kWidColor);
}
// Cross out the grid?
diff --git a/src/gui/ColorWidget.hxx b/src/gui/ColorWidget.hxx
index 4d39871fa..0ec099a95 100644
--- a/src/gui/ColorWidget.hxx
+++ b/src/gui/ColorWidget.hxx
@@ -42,7 +42,7 @@ class ColorWidget : public Widget, public CommandSender
void setColor(ColorId color);
ColorId getColor() const { return _color; }
- void setCrossed(bool enable) { _crossGrid = enable; }
+ void setCrossed(bool enable);
protected:
void drawWidget(bool hilite) 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..6807635ba 100644
--- a/src/gui/ContextMenu.hxx
+++ b/src/gui/ContextMenu.hxx
@@ -45,6 +45,8 @@ class ContextMenu : public Dialog, public CommandSender
const VariantList& items, int cmd = 0, int width = 0);
~ContextMenu() override = default;
+ bool isShading() const override { return false; }
+
/** Set the parent widget's ID */
void setID(uInt32 id) { _id = id; }
@@ -71,8 +73,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/DeveloperDialog.cxx b/src/gui/DeveloperDialog.cxx
index 6c877351a..31443e1bf 100644
--- a/src/gui/DeveloperDialog.cxx
+++ b/src/gui/DeveloperDialog.cxx
@@ -115,10 +115,11 @@ void DeveloperDialog::addEmulationTab(const GUI::Font& font)
"Console info overlay");
wid.push_back(myFrameStatsWidget);
-
myDetectedInfoWidget = new CheckboxWidget(myTab, font,
myFrameStatsWidget->getRight() + fontWidth * 2.5, ypos + 1,
"Detected settings info");
+ myDetectedInfoWidget->setToolTip("Display detected controllers, bankswitching\n"
+ "and TV types at ROM start.");
wid.push_back(myDetectedInfoWidget);
ypos += lineHeight + VGAP;
@@ -131,6 +132,8 @@ void DeveloperDialog::addEmulationTab(const GUI::Font& font)
myConsoleWidget = new PopUpWidget(myTab, font, HBORDER + INDENT * 1, ypos, pwidth, lineHeight, items,
"Console ", lwidth, kConsole);
+ myConsoleWidget->setToolTip("Emulate Color/B&W/Pause key and zero\n"
+ "page RAM initialization differenly.");
wid.push_back(myConsoleWidget);
ypos += lineHeight + VGAP;
@@ -141,6 +144,8 @@ void DeveloperDialog::addEmulationTab(const GUI::Font& font)
myRandomBankWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1,
"Random startup bank");
+ myRandomBankWidget->setToolTip("Randomize the startup bank for\n"
+ "most classic bankswitching types.");
wid.push_back(myRandomBankWidget);
ypos += lineHeight + VGAP;
@@ -168,17 +173,23 @@ void DeveloperDialog::addEmulationTab(const GUI::Font& font)
// How to handle undriven TIA pins
myUndrivenPinsWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
"Drive unused TIA pins randomly on a read/peek");
+ myUndrivenPinsWidget->setToolTip("Read TIA pins random instead of last databus values.\n"
+ "Helps detecting missing '#' for immediate loads.");
wid.push_back(myUndrivenPinsWidget);
ypos += lineHeight + VGAP;
#ifdef DEBUGGER_SUPPORT
myRWPortBreakWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
"Break on reads from write ports");
+ myRWPortBreakWidget->setToolTip("Cause reads from write ports to interrupt\n"
+ "emulation and enter debugger.");
wid.push_back(myRWPortBreakWidget);
ypos += lineHeight + VGAP;
myWRPortBreakWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
"Break on writes to read ports");
+ myWRPortBreakWidget->setToolTip("Cause writes to read ports to interrupt\n"
+ "emulation and enter debugger.");
wid.push_back(myWRPortBreakWidget);
ypos += lineHeight + VGAP;
#endif
@@ -186,12 +197,16 @@ void DeveloperDialog::addEmulationTab(const GUI::Font& font)
// Thumb ARM emulation exception
myThumbExceptionWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
"Fatal ARM emulation error throws exception");
+ myThumbExceptionWidget->setToolTip("Cause Thumb ARM emulation to throw exceptions\n"
+ "on fatal errors and enter the debugger.");
wid.push_back(myThumbExceptionWidget);
ypos += lineHeight + VGAP;
// AtariVox/SaveKey EEPROM access
myEEPROMAccessWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
"Display AtariVox/SaveKey EEPROM R/W access");
+ myEEPROMAccessWidget->setToolTip("Cause message display when AtariVox/\n"
+ "SaveKey EEPROM is read or written.");
wid.push_back(myEEPROMAccessWidget);
// Add items for tab 0
@@ -239,11 +254,15 @@ void DeveloperDialog::addTiaTab(const GUI::Font& font)
VarList::push_back(items, "Custom", "custom");
myTIATypeWidget = new PopUpWidget(myTab, font, HBORDER + INDENT, ypos - 1,
pwidth, lineHeight, items, "Chip type ", 0, kTIAType);
+ myTIATypeWidget->setToolTip("Select which TIA chip type to emulate.\n"
+ "Some types cause defined glitches.");
wid.push_back(myTIATypeWidget);
ypos += lineHeight + VGAP * 1;
myInvPhaseLabel = new StaticTextWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1,
"Inverted HMOVE clock phase for");
+ myInvPhaseLabel->setToolTip("Objects react different to too\n"
+ "early HM" + ELLIPSIS + " after HMOVE changes.");
wid.push_back(myInvPhaseLabel);
ypos += lineHeight + VGAP * 1;
@@ -262,6 +281,7 @@ void DeveloperDialog::addTiaTab(const GUI::Font& font)
myPlayfieldLabel = new StaticTextWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1,
"Delayed playfield");
+ myPlayfieldLabel->setToolTip("Playfield reacts one color clock slower to updates.");
wid.push_back(myPlayfieldLabel);
ypos += lineHeight + VGAP * 1;
@@ -275,6 +295,7 @@ void DeveloperDialog::addTiaTab(const GUI::Font& font)
myBackgroundLabel = new StaticTextWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1,
"Delayed background");
+ myBackgroundLabel->setToolTip("Background color reacts one color clock slower to updates.");
wid.push_back(myBackgroundLabel);
ypos += lineHeight + VGAP * 1;
@@ -285,6 +306,7 @@ void DeveloperDialog::addTiaTab(const GUI::Font& font)
ostringstream ss;
ss << "Delayed VDEL" << ELLIPSIS << " swap for";
mySwapLabel = new StaticTextWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1, ss.str());
+ mySwapLabel->setToolTip("VDELed objects react one color clock slower to updates.");
wid.push_back(mySwapLabel);
ypos += lineHeight + VGAP * 1;
@@ -332,12 +354,14 @@ void DeveloperDialog::addVideoTab(const GUI::Font& font)
// TV jitter effect
myTVJitterWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
"Jitter/roll effect", kTVJitter);
+ myTVJitterWidget->setToolTip("Enable to emulate TV loss of sync.");
wid.push_back(myTVJitterWidget);
myTVJitterRecWidget = new SliderWidget(myTab, font,
myTVJitterWidget->getRight() + fontWidth * 3, ypos - 1,
"Recovery ", 0, kTVJitterChanged);
myTVJitterRecWidget->setMinValue(1); myTVJitterRecWidget->setMaxValue(20);
myTVJitterRecWidget->setTickmarkIntervals(5);
+ myTVJitterRecWidget->setToolTip("Define speed of sync recovery.");
wid.push_back(myTVJitterRecWidget);
myTVJitterRecLabelWidget = new StaticTextWidget(myTab, font,
myTVJitterRecWidget->getRight() + 4,
@@ -347,6 +371,8 @@ void DeveloperDialog::addVideoTab(const GUI::Font& font)
myColorLossWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
"PAL color-loss");
+ myColorLossWidget->setToolTip("PAL games with odd scanline count\n"
+ "will be displayed without color.");
wid.push_back(myColorLossWidget);
ypos += lineHeight + VGAP;
@@ -485,6 +511,7 @@ void DeveloperDialog::addTimeMachineTab(const GUI::Font& font)
#endif
myStateSizeWidget->setStepValue(20);
myStateSizeWidget->setTickmarkIntervals(5);
+ myStateSizeWidget->setToolTip("Define the total Time Machine buffer size.");
wid.push_back(myStateSizeWidget);
ypos += lineHeight + VGAP;
@@ -498,6 +525,9 @@ void DeveloperDialog::addTimeMachineTab(const GUI::Font& font)
#endif
myUncompressedWidget->setStepValue(20);
myUncompressedWidget->setTickmarkIntervals(5);
+ myUncompressedWidget->setToolTip("Define the number of completely kept states.\n"
+ "States beyond this number will be slowly removed\n"
+ "to fit the requested horizon into the buffer.");
wid.push_back(myUncompressedWidget);
ypos += lineHeight + VGAP;
@@ -507,6 +537,7 @@ void DeveloperDialog::addTimeMachineTab(const GUI::Font& font)
int pwidth = font.getStringWidth("10 seconds");
myStateIntervalWidget = new PopUpWidget(myTab, font, xpos, ypos, pwidth,
lineHeight, items, "Interval ", 0, kIntervalChanged);
+ myStateIntervalWidget->setToolTip("Define the interval between each saved state.");
wid.push_back(myStateIntervalWidget);
ypos += lineHeight + VGAP;
@@ -515,6 +546,8 @@ void DeveloperDialog::addTimeMachineTab(const GUI::Font& font)
VarList::push_back(items, HORIZONS[i], HOR_SETTINGS[i]);
myStateHorizonWidget = new PopUpWidget(myTab, font, xpos, ypos, pwidth,
lineHeight, items, "Horizon ~ ", 0, kHorizonChanged);
+ myStateHorizonWidget->setToolTip("Define how far the Time Machine\n"
+ "will allow moving back in time.");
wid.push_back(myStateHorizonWidget);
// Add message concerning usage
@@ -598,6 +631,7 @@ void DeveloperDialog::addDebuggerTab(const GUI::Font& font)
myGhostReadsTrapWidget = new CheckboxWidget(myTab, font, HBORDER, ypos + 1,
"Trap on 'ghost' reads");
+ myGhostReadsTrapWidget->setToolTip("Traps will consider CPU 'ghost' reads too.");
wid.push_back(myGhostReadsTrapWidget);
// Add message concerning usage
diff --git a/src/gui/Dialog.cxx b/src/gui/Dialog.cxx
index 85c95349f..50fcbd295 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,49 @@ 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;
+ // A dialog is still on top if a non-shading dialog (e.g. ContextMenu)
+ // is opended above it.
+ bool onTop = parent().myDialogStack.top() == this
+ || (parent().myDialogStack.get(parent().myDialogStack.size() - 2) == this
+ && !parent().myDialogStack.top()->isShading());
+
+ if(!onTop)
+ {
+ //cerr << " shade " << typeid(*this).name() << endl;
+
+ _shadeSurface->setDstRect(_surface->dstRect());
+ _shadeSurface->render();
+ }
+
+ _toolTip->render();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -305,7 +365,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 +431,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 +475,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;
}
}
@@ -430,6 +511,8 @@ void Dialog::handleKeyDown(StellaKey key, StellaMod mod, bool repeated)
{
Event::Type e = Event::NoType;
+ tooltip().hide();
+
// FIXME - I don't think this will compile!
#if defined(RETRON77)
// special keys used for R77
@@ -556,6 +639,11 @@ 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));
+
+#ifndef RETRON77
+ // Update mouse coordinates for tooltips
+ _toolTip->update(_mouseWidget, Common::Point(x, y));
+#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/src/gui/Dialog.hxx b/src/gui/Dialog.hxx
index 1f6bbec60..a6fd7259b 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,20 @@ class Dialog : public GuiObject
void close();
bool isVisible() const override { return _visible; }
- bool isOnTop() const { return _onTop; }
- 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,13 +91,11 @@ 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(); }
+ virtual bool isShading() const { return true; }
+
/**
Determine the maximum width/height of a dialog based on the minimum
allowable bounds, also taking into account the current window size.
@@ -124,6 +124,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 +199,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 +234,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..16f32536a 100644
--- a/src/gui/DialogContainer.cxx
+++ b/src/gui/DialogContainer.cxx
@@ -17,6 +17,7 @@
#include "OSystem.hxx"
#include "Dialog.hxx"
+#include "ToolTip.hxx"
#include "Stack.hxx"
#include "EventHandler.hxx"
#include "FrameBuffer.hxx"
@@ -89,31 +90,53 @@ void DialogContainer::updateTime(uInt64 time)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-bool DialogContainer::draw(bool full)
+void DialogContainer::draw(bool full)
{
if(myDialogStack.empty())
- return false;
+ return;
- // Make the top dialog dirty if a full redraw is requested
- if(full)
- myDialogStack.top()->setDirty();
+ //cerr << "draw " << full << " " << typeid(*this).name() << endl;
- // 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,10 +152,14 @@ 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
{
+ // Close all open tooltips
+ if(!myDialogStack.empty())
+ myDialogStack.top()->tooltip().hide();
+
d->setDirty();
myDialogStack.push(d);
}
@@ -144,9 +171,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 +186,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..dde104002 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"
@@ -45,21 +46,11 @@ void EditTextWidget::setText(const string& str, bool changed)
{
EditableWidget::setText(str, changed);
_backupString = str;
- _changed = changed;
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void EditTextWidget::handleMouseEntered()
-{
- setFlags(Widget::FLAG_HILITED);
- setDirty();
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void EditTextWidget::handleMouseLeft()
-{
- clearFlags(Widget::FLAG_HILITED);
- setDirty();
+ if(_changed != changed)
+ {
+ _changed = changed;
+ setDirty();
+ }
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -89,13 +80,12 @@ void EditTextWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount
void EditTextWidget::drawWidget(bool hilite)
{
FBSurface& s = _boss->dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
// Highlight changes
- if(_changed && onTop)
+ if(_changed)
s.fillRect(_x, _y, _w, _h, kDbgChangedColor);
else if(!isEditable() || !isEnabled())
- s.fillRect(_x, _y, _w, _h, onTop ? kDlgColor : kBGColorLo);
+ s.fillRect(_x, _y, _w, _h, kDlgColor);
// Draw a thin frame around us.
s.frameRect(_x, _y, _w, _h, hilite && isEditable() && isEnabled() ? kWidColorHi : kColor);
@@ -103,9 +93,9 @@ void EditTextWidget::drawWidget(bool hilite)
// Draw the text
adjustOffset();
s.drawString(_font, editString(), _x + _textOfs, _y + 2, getEditRect().w(), getEditRect().h(),
- _changed && onTop && isEnabled()
+ _changed && isEnabled()
? kDbgChangedTextColor
- : onTop && isEnabled() ? _textcolor : kColor,
+ : isEnabled() ? _textcolor : kColor,
TextAlign::Left, scrollOffset(), !isEditable());
// Draw the caret and selection
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..eaadbaeeb 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"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -42,14 +43,18 @@ EditableWidget::EditableWidget(GuiObject* boss, const GUI::Font& font,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void EditableWidget::setText(const string& str, bool)
+void EditableWidget::setText(const string& str, bool changed)
{
+ const string oldEditString = _editString;
// Filter input string
_editString = "";
for(char c: str)
if(_filter(tolower(c)))
_editString.push_back(c);
+ if(oldEditString != _editString)
+ setDirty();
+
myUndoHandler->reset();
myUndoHandler->doo(_editString);
@@ -59,8 +64,28 @@ void EditableWidget::setText(const string& str, bool)
_editScrollOffset = (_font.getStringWidth(_editString) - (getEditRect().w()));
if (_editScrollOffset < 0)
_editScrollOffset = 0;
+}
- 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();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -79,6 +104,14 @@ void EditableWidget::setEditable(bool editable, bool hiliteBG)
}
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void EditableWidget::receivedFocusWidget()
+{
+ _caretTimer = 0;
+ _caretEnabled = true;
+ dialog().tooltip().hide();
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EditableWidget::lostFocusWidget()
{
@@ -107,7 +140,7 @@ bool EditableWidget::handleText(char text)
if(tryInsertChar(text, _caretPos))
{
- _caretPos++;
+ setCaretPos(_caretPos + 1);
sendCommand(EditableWidget::kChangedCmd, 0, _id);
setDirty();
return true;
@@ -265,7 +298,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 +349,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 +382,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 +411,9 @@ bool EditableWidget::setCaretPos(int newPos)
assert(newPos >= 0 && newPos <= int(_editString.size()));
_caretPos = newPos;
+ _caretTimer = 0;
+ _caretEnabled = true;
+
return adjustOffset();
}
@@ -375,6 +423,8 @@ bool EditableWidget::moveCaretPos(int direction)
if(setCaretPos(_caretPos + direction))
{
_selectSize -= direction;
+ _caretTimer = 0;
+ _caretEnabled = true;
return true;
}
return false;
@@ -452,6 +502,7 @@ bool EditableWidget::killChar(int direction, bool addEdit)
{
myUndoHandler->endChars(_editString);
_editString.erase(_caretPos, 1);
+ setCaretPos(_caretPos);
if(addEdit)
myUndoHandler->doo(_editString);
@@ -556,7 +607,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 +626,7 @@ bool EditableWidget::moveWord(int direction, bool select)
if(select)
_selectSize--;
}
- _caretPos = currentPos;
+ setCaretPos(currentPos);
handled = true;
}
@@ -630,6 +681,7 @@ bool EditableWidget::killSelectedText(bool addEdit)
_selectSize = -_selectSize;
}
_editString.erase(_caretPos, _selectSize);
+ setCaretPos(_caretPos);
_selectSize = 0;
if(addEdit)
myUndoHandler->doo(_editString);
@@ -689,7 +741,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/EmulationDialog.cxx b/src/gui/EmulationDialog.cxx
index dcbb42859..a2c0bee9c 100644
--- a/src/gui/EmulationDialog.cxx
+++ b/src/gui/EmulationDialog.cxx
@@ -99,6 +99,7 @@ EmulationDialog::EmulationDialog(OSystem& osystem, DialogContainer& parent,
// Use sync to vblank
myUseVSync = new CheckboxWidget(this, _font, xpos, ypos + 1, "VSync");
+ myUseVSync->setToolTip("Check to enable vertical synced display updates.");
wid.push_back(myUseVSync);
ypos += lineHeight + VGAP;
diff --git a/src/gui/EventMappingWidget.cxx b/src/gui/EventMappingWidget.cxx
index 8051e4a56..7b3ae540b 100644
--- a/src/gui/EventMappingWidget.cxx
+++ b/src/gui/EventMappingWidget.cxx
@@ -97,6 +97,7 @@ EventMappingWidget::EventMappingWidget(GuiObject* boss, const GUI::Font& font,
myCancelMapButton = new ButtonWidget(boss, font, xpos, ypos,
buttonWidth, buttonHeight,
"Cancel", kStopMapCmd);
+ myCancelMapButton->setToolTip("Cancel current mapping.");
myCancelMapButton->setTarget(this);
myCancelMapButton->clearFlags(Widget::FLAG_ENABLED);
addFocusWidget(myCancelMapButton);
@@ -106,12 +107,14 @@ EventMappingWidget::EventMappingWidget(GuiObject* boss, const GUI::Font& font,
buttonWidth, buttonHeight,
"Erase", kEraseCmd);
myEraseButton->setTarget(this);
+ myEraseButton->setToolTip("Erase any mapping for selected event.");
addFocusWidget(myEraseButton);
ypos += buttonHeight + VGAP;
myResetButton = new ButtonWidget(boss, font, xpos, ypos,
buttonWidth, buttonHeight,
"Reset", kResetCmd);
+ myResetButton->setToolTip("Reset mapping for selected event to defaults.");
myResetButton->setTarget(this);
addFocusWidget(myResetButton);
diff --git a/src/gui/FileListWidget.cxx b/src/gui/FileListWidget.cxx
index cb3b0f5c9..613b05811 100644
--- a/src/gui/FileListWidget.cxx
+++ b/src/gui/FileListWidget.cxx
@@ -20,6 +20,7 @@
#include "ScrollBarWidget.hxx"
#include "FileListWidget.hxx"
#include "TimerManager.hxx"
+#include "ProgressDialog.hxx"
#include "bspf.hxx"
@@ -50,7 +51,7 @@ void FileListWidget::setDirectory(const FilesystemNode& node,
// Initialize history
FilesystemNode tmp = _node;
- while(tmp.hasParent())
+ while(tmp.hasParent() && !_history.full())
{
string name = tmp.getName();
if(name.back() == '/' || name.back() == '\\')
@@ -72,22 +73,52 @@ void FileListWidget::setDirectory(const FilesystemNode& node,
void FileListWidget::setLocation(const FilesystemNode& node,
const string& select)
{
+ progress().resetProgress();
+ progress().open();
+ FilesystemNode::CancelCheck isCancelled = []() {
+ return myProgressDialog->isCancelled();
+ };
+
_node = node;
// Read in the data from the file system (start with an empty list)
_fileList.clear();
- _fileList.reserve(512);
- _node.getChildren(_fileList, _fsmode, _filter);
- // Now fill the list widget with the names from the file list
+ if(_includeSubDirs)
+ {
+ // Actually this could become HUGE
+ _fileList.reserve(0x2000);
+ _node.getAllChildren(_fileList, _fsmode, _filter, true, isCancelled);
+ }
+ else
+ {
+ _fileList.reserve(0x200);
+ _node.getChildren(_fileList, _fsmode, _filter, false, true, isCancelled);
+ }
+
+ // Now fill the list widget with the names from the file list,
+ // even if cancelled
StringList l;
- for(const auto& file: _fileList)
+ size_t orgLen = node.getShortPath().length();
+
+ _dirList.clear();
+ for(const auto& file : _fileList)
+ {
+ const string path = file.getShortPath();
+
l.push_back(file.getName());
+ // display only relative path in tooltip
+ if(path.length() >= orgLen)
+ _dirList.push_back(path.substr(orgLen));
+ else
+ _dirList.push_back(path);
+ }
setList(l);
setSelected(select);
-
ListWidget::recalc();
+
+ progress().close();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -114,6 +145,21 @@ void FileListWidget::reload()
}
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ProgressDialog& FileListWidget::progress()
+{
+ if(myProgressDialog == nullptr)
+ myProgressDialog = make_unique(this, _font, "");
+
+ return *myProgressDialog;
+}
+
+void FileListWidget::incProgress()
+{
+ if(_includeSubDirs)
+ progress().incProgress();
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FileListWidget::handleText(char text)
{
@@ -200,5 +246,28 @@ void FileListWidget::handleCommand(CommandSender* sender, int cmd, int data, int
setTarget(this);
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+string FileListWidget::getToolTip(const Common::Point& pos) const
+{
+ Common::Rect rect = getEditRect();
+ int idx = getToolTipIndex(pos);
+
+ if(idx < 0)
+ return EmptyString;
+
+ if(_includeSubDirs && static_cast(_dirList.size()) > idx)
+ return _toolTipText + _dirList[idx];
+
+ const string value = _list[idx];
+
+ if(uInt32(_font.getStringWidth(value)) > rect.w())
+ return _toolTipText + value;
+ else
+ return _toolTipText;
+}
+
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt64 FileListWidget::_QUICK_SELECT_DELAY = 300;
+
+unique_ptr FileListWidget::myProgressDialog{nullptr};
diff --git a/src/gui/FileListWidget.hxx b/src/gui/FileListWidget.hxx
index 8b60fac44..32f6517f7 100644
--- a/src/gui/FileListWidget.hxx
+++ b/src/gui/FileListWidget.hxx
@@ -19,6 +19,7 @@
#define FILE_LIST_WIDGET_HXX
class CommandSender;
+class ProgressDialog;
#include "FSNode.hxx"
#include "Stack.hxx"
@@ -52,6 +53,8 @@ class FileListWidget : public StringListWidget
int x, int y, int w, int h);
~FileListWidget() override = default;
+ string getToolTip(const Common::Point& pos) const override;
+
/** Determines how to display files/folders; either setDirectory or reload
must be called after any of these are called. */
void setListMode(FilesystemNode::ListMode mode) { _fsmode = mode; }
@@ -59,12 +62,15 @@ class FileListWidget : public StringListWidget
_filter = filter;
}
+ // When enabled, all subdirectories will be searched too.
+ void setIncludeSubDirs(bool enable) { _includeSubDirs = enable; }
+
/**
Set initial directory, and optionally select the given item.
- @param node The directory to display. If this is a file, its parent
- will instead be used, and the file will be selected
- @param select An optional entry to select (if applicable)
+ @param node The directory to display. If this is a file, its parent
+ will instead be used, and the file will be selected
+ @param select An optional entry to select (if applicable)
*/
void setDirectory(const FilesystemNode& node,
const string& select = EmptyString);
@@ -83,6 +89,13 @@ class FileListWidget : public StringListWidget
const FilesystemNode& currentDir() const { return _node; }
static void setQuickSelectDelay(uInt64 time) { _QUICK_SELECT_DELAY = time; }
+ uInt64 getQuickSelectDelay() { return _QUICK_SELECT_DELAY; }
+
+ ProgressDialog& progress();
+ void incProgress();
+
+ protected:
+ static unique_ptr myProgressDialog;
private:
/** Very similar to setDirectory(), but also updates the history */
@@ -99,6 +112,9 @@ class FileListWidget : public StringListWidget
FilesystemNode::NameFilter _filter;
FilesystemNode _node;
FSList _fileList;
+ bool _includeSubDirs{false};
+
+ StringList _dirList;
Common::FixedStack _history;
uInt32 _selected{0};
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..4c34de23e 100644
--- a/src/gui/InputTextDialog.cxx
+++ b/src/gui/InputTextDialog.cxx
@@ -81,9 +81,10 @@ void InputTextDialog::initialize(const GUI::Font& lfont, const GUI::Font& nfont,
for(i = 0; i < labels.size(); ++i)
{
xpos = HBORDER;
- new StaticTextWidget(this, lfont, xpos, ypos + 2,
- lwidth, fontHeight,
- labels[i], TextAlign::Left);
+ StaticTextWidget* s = new StaticTextWidget(this, lfont, xpos, ypos + 2,
+ lwidth, fontHeight,
+ labels[i]);
+ myLabel.push_back(s);
xpos += lwidth + fontWidth;
EditTextWidget* w = new EditTextWidget(this, nfont, xpos, ypos,
@@ -130,7 +131,7 @@ void InputTextDialog::show(uInt32 x, uInt32 y, const Common::Rect& bossRect)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void InputTextDialog::center()
+void InputTextDialog::setPosition()
{
if(!myEnableCenter)
{
@@ -144,7 +145,7 @@ void InputTextDialog::center()
surface().setDstPos(myXOrig, myYOrig);
}
else
- Dialog::center();
+ Dialog::setPosition();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -177,6 +178,13 @@ void InputTextDialog::setTextFilter(const EditableWidget::TextFilter& f, int idx
myInput[idx]->setTextFilter(f);
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void InputTextDialog::setToolTip(const string& str, int idx)
+{
+ if(uInt32(idx) < myLabel.size())
+ myLabel[idx]->setToolTip(str);
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void InputTextDialog::setFocus(int idx)
{
diff --git a/src/gui/InputTextDialog.hxx b/src/gui/InputTextDialog.hxx
index e89e00651..059c7ada7 100644
--- a/src/gui/InputTextDialog.hxx
+++ b/src/gui/InputTextDialog.hxx
@@ -46,6 +46,7 @@ class InputTextDialog : public Dialog, public CommandSender
void setText(const string& str, int idx = 0);
void setTextFilter(const EditableWidget::TextFilter& f, int idx = 0);
+ void setToolTip(const string& str, int idx = 0);
void setEmitSignal(int cmd) { myCmd = cmd; }
void setMessage(const string& title);
@@ -58,9 +59,10 @@ 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 myLabel;
vector myInput;
StaticTextWidget* myMessage{nullptr};
diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx
index 3f6ddcec8..4a8f437d4 100644
--- a/src/gui/LauncherDialog.cxx
+++ b/src/gui/LauncherDialog.cxx
@@ -29,7 +29,10 @@
#include "GlobalPropsDialog.hxx"
#include "StellaSettingsDialog.hxx"
#include "WhatsNewDialog.hxx"
+#include "ProgressDialog.hxx"
#include "MessageBox.hxx"
+#include "ToolTip.hxx"
+#include "TimerManager.hxx"
#include "OSystem.hxx"
#include "FrameBuffer.hxx"
#include "FBSurface.hxx"
@@ -72,23 +75,71 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
buttonHeight = myUseMinimalUI ? lineHeight - VGAP * 2: lineHeight * 1.25,
buttonWidth = (_w - 2 * HBORDER - BUTTON_GAP * (4 - 1));
- int xpos = HBORDER, ypos = VBORDER, lwidth = 0, lwidth2 = 0;
+ int xpos = HBORDER, ypos = VBORDER;
WidgetArray wid;
- string lblRom = "Select a ROM from the list" + ELLIPSIS;
+ string lblSelect = "Select a ROM from the list" + ELLIPSIS;
+ string lblAllFiles = "Show all files";
const string& lblFilter = "Filter";
- const string& lblAllFiles = "Show all files";
- const string& lblFound = "XXXX items found";
+ string lblSubDirs = "Incl. subdirectories";
+ string lblFound = "12345 items found";
- lwidth = font.getStringWidth(lblRom);
- lwidth2 = font.getStringWidth(lblAllFiles) + CheckboxWidget::boxSize(font);
- int lwidth3 = font.getStringWidth(lblFilter);
- int lwidth4 = font.getStringWidth(lblFound);
+ tooltip().setFont(font);
- if(w < HBORDER * 2 + lwidth + lwidth2 + lwidth3 + lwidth4 + fontWidth * 6 + LBL_GAP * 8)
+ int lwSelect = font.getStringWidth(lblSelect);
+ int cwAllFiles = font.getStringWidth(lblAllFiles) + CheckboxWidget::prefixSize(font);
+ int cwSubDirs = font.getStringWidth(lblSubDirs) + CheckboxWidget::prefixSize(font);
+ int lwFilter = font.getStringWidth(lblFilter);
+ int lwFound = font.getStringWidth(lblFound);
+ int wTotal = HBORDER * 2 + lwSelect + cwAllFiles + cwSubDirs + lwFilter + lwFound
+ + EditTextWidget::calcWidth(font, "123456") + LBL_GAP * 7;
+ bool noSelect = false;
+
+ if(w < wTotal)
{
// make sure there is space for at least 6 characters in the filter field
- lblRom = "Select a ROM" + ELLIPSIS;
- lwidth = font.getStringWidth(lblRom);
+ lblSelect = "Select a ROM" + ELLIPSIS;
+ int lwSelectShort = font.getStringWidth(lblSelect);
+
+ wTotal -= lwSelect - lwSelectShort;
+ lwSelect = lwSelectShort;
+ }
+ if(w < wTotal)
+ {
+ // make sure there is space for at least 6 characters in the filter field
+ lblSubDirs = "Subdir.";
+ int cwSubDirsShort = font.getStringWidth(lblSubDirs) + CheckboxWidget::prefixSize(font);
+
+ wTotal -= cwSubDirs - cwSubDirsShort;
+ cwSubDirs = cwSubDirsShort;
+ }
+ if(w < wTotal)
+ {
+ // make sure there is space for at least 6 characters in the filter field
+ lblAllFiles = "All files";
+ int cwAllFilesShort = font.getStringWidth(lblAllFiles) + CheckboxWidget::prefixSize(font);
+
+ wTotal -= cwAllFiles - cwAllFilesShort;
+ cwAllFiles = cwAllFilesShort;
+ }
+ if(w < wTotal)
+ {
+ // make sure there is space for at least 6 characters in the filter field
+ lblFound = "12345 found";
+ int lwFoundShort = font.getStringWidth(lblFound);
+
+ wTotal -= lwFound - lwFoundShort;
+ lwFound = lwFoundShort;
+ myShortCount = true;
+ }
+ if(w < wTotal)
+ {
+ // make sure there is space for at least 6 characters in the filter field
+ lblSelect = "";
+ int lwSelectShort = font.getStringWidth(lblSelect);
+
+ wTotal -= lwSelect - lwSelectShort;
+ lwSelect = lwSelectShort;
+ noSelect = true;
}
if(myUseMinimalUI)
@@ -105,29 +156,49 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
}
// Show the header
- new StaticTextWidget(this, font, xpos, ypos, lblRom);
+ new StaticTextWidget(this, font, xpos, ypos, lblSelect);
// Shop the files counter
- xpos = _w - HBORDER - lwidth4;
+ xpos = _w - HBORDER - lwFound;
myRomCount = new StaticTextWidget(this, font, xpos, ypos,
- lwidth4, fontHeight,
+ lwFound, fontHeight,
"", TextAlign::Right);
// Add filter that can narrow the results shown in the listing
// It has to fit between both labels
if(!myUseMinimalUI && w >= 640)
{
- int fwidth = std::min(15 * fontWidth, xpos - lwidth3 - lwidth2 - lwidth - HBORDER - LBL_GAP * 8);
+ int fwFilter = std::min(EditTextWidget::calcWidth(font, "123456789012345"),
+ xpos - cwSubDirs - lwFilter - cwAllFiles
+ - lwSelect - HBORDER - LBL_GAP * (noSelect ? 5 : 7));
+
// Show the filter input field
- xpos -= fwidth + LBL_GAP;
- myPattern = new EditTextWidget(this, font, xpos, ypos - 2, fwidth, lineHeight, "");
+ xpos -= fwFilter + LBL_GAP;
+ myPattern = new EditTextWidget(this, font, xpos, ypos - 2, fwFilter, lineHeight, "");
+ myPattern->setToolTip("Enter filter text to reduce file list.\n"
+ "Use '*' and '?' as wildcards.");
+
// Show the "Filter" label
- xpos -= lwidth3 + LBL_GAP;
+ xpos -= lwFilter + LBL_GAP;
new StaticTextWidget(this, font, xpos, ypos, lblFilter);
+
+ // Show the subdirectories checkbox
+ xpos -= cwSubDirs + LBL_GAP * 2;
+ mySubDirs = new CheckboxWidget(this, font, xpos, ypos, lblSubDirs, kSubDirsCmd);
+ ostringstream tip;
+ tip << "Search files in subdirectories too.";
+ mySubDirs->setToolTip(tip.str());
+
// Show the checkbox for all files
- xpos -= lwidth2 + LBL_GAP * 3;
+ if(noSelect)
+ xpos = HBORDER;
+ else
+ xpos -= cwAllFiles + LBL_GAP;
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);
+ wid.push_back(mySubDirs);
}
// Add list with game titles
@@ -162,11 +233,11 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
// Add textfield to show current directory
xpos = HBORDER;
- ypos += myList->getHeight() + VGAP * 2;
- lwidth = font.getStringWidth("Path") + LBL_GAP;
- myDirLabel = new StaticTextWidget(this, font, xpos, ypos+2, lwidth, fontHeight,
+ ypos += myList->getHeight() + VGAP;
+ lwSelect = font.getStringWidth("Path") + LBL_GAP;
+ myDirLabel = new StaticTextWidget(this, font, xpos, ypos+2, lwSelect, fontHeight,
"Path", TextAlign::Left);
- xpos += lwidth;
+ xpos += lwSelect;
myDir = new EditTextWidget(this, font, xpos, ypos, _w - xpos - HBORDER, lineHeight, "");
myDir->setEditable(false, true);
myDir->clearFlags(Widget::FLAG_RETAIN_FOCUS);
@@ -214,11 +285,12 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
"Select", kLoadROMCmd);
wid.push_back(myStartButton);
#endif
+ myStartButton->setToolTip("Start emulation of selected ROM.");
}
if(myUseMinimalUI) // Highlight 'Rom Listing'
mySelectedItem = 0;
else
- mySelectedItem = 2;
+ mySelectedItem = 3;
addToFocusList(wid);
@@ -230,6 +302,10 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
myGlobalProps = make_unique(this,
myUseMinimalUI ? osystem.frameBuffer().launcherFont() : osystem.frameBuffer().font());
+ // since we cannot know how many files there are, use are really high value here
+ myList->progress().setRange(0, 50000, 5);
+ myList->progress().setMessage(" Filtering files" + ELLIPSIS + " ");
+
// Do we show only ROMs or all files?
bool onlyROMs = instance().settings().getBool("launcherroms");
showOnlyROMs(onlyROMs);
@@ -278,6 +354,16 @@ void LauncherDialog::reload()
{
myMD5List.clear();
myList->reload();
+ myPendingReload = false;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void LauncherDialog::tick()
+{
+ if(myPendingReload && myReloadTime < TimerManager::getTicks() / 1000)
+ reload();
+
+ Dialog::tick();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -297,6 +383,10 @@ void LauncherDialog::loadConfig()
instance().settings().setValue("stella.version", STELLA_VERSION);
}
+ bool subDirs = instance().settings().getBool("launchersubdirs");
+ if (mySubDirs) mySubDirs->setState(subDirs);
+ myList->setIncludeSubDirs(subDirs);
+
// Assume that if the list is empty, this is the first time that loadConfig()
// has been called (and we should reload the list)
if(myList->getList().empty())
@@ -319,6 +409,7 @@ void LauncherDialog::loadConfig()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::saveConfig()
{
+ instance().settings().setValue("launchersubdirs", mySubDirs->getState());
if(instance().settings().getBool("followlauncher"))
instance().settings().setValue("romdir", myList->currentDir().getShortPath());
}
@@ -335,18 +426,88 @@ void LauncherDialog::updateUI()
// Indicate how many files were found
ostringstream buf;
- buf << (myList->getList().size() - 1) << " items found";
+ buf << (myList->getList().size() - 1) << (myShortCount ? " found" : " items found");
myRomCount->setLabel(buf.str());
// Update ROM info UI item
loadRomInfo();
}
+size_t LauncherDialog::matchWithJoker(const string& str, const string& pattern)
+{
+ if(str.length() >= pattern.length())
+ {
+ // optimize a bit
+ if(pattern.find('?') != string::npos)
+ {
+ for(size_t pos = 0; pos < str.length() - pattern.length() + 1; ++pos)
+ {
+ bool found = true;
+
+ for(size_t i = 0; found && i < pattern.length(); ++i)
+ if(pattern[i] != str[pos + i] && pattern[i] != '?')
+ found = false;
+
+ if(found)
+ return pos;
+ }
+ }
+ else
+ return str.find(pattern);
+ }
+ return string::npos;
+}
+
+bool LauncherDialog::matchWithWildcards(const string& str, const string& pattern)
+{
+ string pat = pattern;
+
+ // remove leading and trailing '*'
+ size_t i = 0;
+ while(pat[i++] == '*');
+ pat = pat.substr(i - 1);
+
+ i = pat.length();
+ while(pat[--i] == '*');
+ pat.erase(i + 1);
+
+ // Search for first '*'
+ size_t pos = pat.find('*');
+
+ if(pos != string::npos)
+ {
+ // '*' found, split pattern into left and right part, search recursively
+ const string leftPat = pat.substr(0, pos);
+ const string rightPat = pat.substr(pos + 1);
+ size_t posLeft = matchWithJoker(str, leftPat);
+
+ if(posLeft != string::npos)
+ return matchWithWildcards(str.substr(pos + posLeft), rightPat);
+ else
+ return false;
+ }
+ // no further '*' found
+ return matchWithJoker(str, pat) != string::npos;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+bool LauncherDialog::matchWithWildcardsIgnoreCase(const string& str, const string& pattern)
+{
+ string in = str;
+ string pat = pattern;
+
+ BSPF::toUpperCase(in);
+ BSPF::toUpperCase(pat);
+
+ return matchWithWildcards(in, pat);
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::applyFiltering()
{
myList->setNameFilter(
[&](const FilesystemNode& node) {
+ myList->incProgress();
if(!node.isDirectory())
{
// Do we want to show only ROMs or all files?
@@ -355,7 +516,7 @@ void LauncherDialog::applyFiltering()
// Skip over files that don't match the pattern in the 'pattern' textbox
if(myPattern && myPattern->getText() != "" &&
- !BSPF::containsIgnoreCase(node.getName(), myPattern->getText()))
+ !matchWithWildcardsIgnoreCase(node.getName(), myPattern->getText()))
return false;
}
return true;
@@ -589,6 +750,11 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
reload();
break;
+ case kSubDirsCmd:
+ myList->setIncludeSubDirs(mySubDirs->getState());
+ reload();
+ break;
+
case kLoadROMCmd:
case FileListWidget::ItemActivated:
saveConfig();
@@ -614,9 +780,23 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
break;
case EditableWidget::kChangedCmd:
+ case EditableWidget::kAcceptCmd:
+ {
+ bool subDirs = mySubDirs->getState();
+
+ myList->setIncludeSubDirs(subDirs);
applyFiltering(); // pattern matching taken care of directly in this method
- reload();
+
+ if(subDirs && cmd == EditableWidget::kChangedCmd)
+ {
+ // delay (potentially slow) subdirectories reloads until user stops typing
+ myReloadTime = TimerManager::getTicks() / 1000 + myList->getQuickSelectDelay();
+ myPendingReload = true;
+ }
+ else
+ reload();
break;
+ }
case kQuitCmd:
saveConfig();
@@ -662,7 +842,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..a5acc262f 100644
--- a/src/gui/LauncherDialog.hxx
+++ b/src/gui/LauncherDialog.hxx
@@ -95,13 +95,15 @@ class LauncherDialog : public Dialog
*/
void reload();
+ void tick() override;
+
private:
static constexpr int MIN_LAUNCHER_CHARS = 24;
static constexpr int MIN_ROMINFO_CHARS = 30;
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;
@@ -112,6 +114,39 @@ class LauncherDialog : public Dialog
void loadConfig() override;
void saveConfig() override;
void updateUI();
+
+ /**
+ Search if string contains pattern including wildcard '*'
+ and '?' as joker, ignoring case.
+
+ @param str The searched string
+ @param pattern The pattern to search for
+
+ @return True if pattern was found.
+ */
+ bool matchWithWildcardsIgnoreCase(const string& str, const string& pattern);
+
+ /**
+ Search if string contains pattern including wildcard '*'
+ and '?' as joker.
+
+ @param str The searched string
+ @param pattern The pattern to search for
+
+ @return True if pattern was found.
+ */
+ bool matchWithWildcards(const string& str, const string& pattern);
+
+ /**
+ Search if string contains pattern including '?' as joker.
+
+ @param str The searched string
+ @param pattern The pattern to search for
+
+ @return Position of pattern in string.
+ */
+ size_t matchWithJoker(const string& str, const string& pattern);
+
void applyFiltering();
float getRomInfoZoom(int listHeight) const;
@@ -136,19 +171,22 @@ class LauncherDialog : public Dialog
// automatically sized font for ROM info viewer
unique_ptr myROMInfoFont;
- ButtonWidget* myStartButton{nullptr};
- ButtonWidget* myPrevDirButton{nullptr};
- ButtonWidget* myOptionsButton{nullptr};
- ButtonWidget* myQuitButton{nullptr};
+ CheckboxWidget* myAllFiles{nullptr};
+ EditTextWidget* myPattern{nullptr};
+ CheckboxWidget* mySubDirs{nullptr};
+ StaticTextWidget* myRomCount{nullptr};
FileListWidget* myList{nullptr};
+
StaticTextWidget* myDirLabel{nullptr};
EditTextWidget* myDir{nullptr};
- StaticTextWidget* myRomCount{nullptr};
- EditTextWidget* myPattern{nullptr};
- CheckboxWidget* myAllFiles{nullptr};
- RomInfoWidget* myRomInfoWidget{nullptr};
+ ButtonWidget* myStartButton{nullptr};
+ ButtonWidget* myPrevDirButton{nullptr};
+ ButtonWidget* myOptionsButton{nullptr};
+ ButtonWidget* myQuitButton{nullptr};
+
+ RomInfoWidget* myRomInfoWidget{nullptr};
std::unordered_map myMD5List;
int mySelectedItem{0};
@@ -156,9 +194,13 @@ class LauncherDialog : public Dialog
bool myShowOnlyROMs{false};
bool myUseMinimalUI{false};
bool myEventHandled{false};
+ bool myShortCount{false};
+ bool myPendingReload{false};
+ uInt64 myReloadTime{0};
enum {
kAllfilesCmd = 'lalf', // show all files (or ROMs only)
+ kSubDirsCmd = 'lred',
kPrevDirCmd = 'PRVD',
kOptionsCmd = 'OPTI',
kQuitCmd = 'QUIT'
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..b4dd27b84 100644
--- a/src/gui/PopUpWidget.cxx
+++ b/src/gui/PopUpWidget.cxx
@@ -20,6 +20,7 @@
#include "FBSurface.hxx"
#include "Font.hxx"
#include "ContextMenu.hxx"
+#include "Dialog.hxx"
#include "DialogContainer.hxx"
#include "PopUpWidget.hxx"
@@ -77,7 +78,11 @@ void PopUpWidget::setSelected(const Variant& tag, const Variant& def)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::setSelectedIndex(int idx, bool changed)
{
- _changed = changed;
+ if(_changed != changed)
+ {
+ _changed = changed;
+ setDirty();
+ }
myMenu->setSelectedIndex(idx);
setText(myMenu->getSelectedName());
}
@@ -85,6 +90,11 @@ void PopUpWidget::setSelectedIndex(int idx, bool changed)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::setSelectedMax(bool changed)
{
+ if(_changed != changed)
+ {
+ _changed = changed;
+ setDirty();
+ }
_changed = changed;
myMenu->setSelectedMax();
setText(myMenu->getSelectedName());
@@ -158,20 +168,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)
{
@@ -260,7 +256,6 @@ void PopUpWidget::drawWidget(bool hilite)
{
//cerr << "PopUpWidget::drawWidget\n";
FBSurface& s = dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
int x = _x + _labelWidth;
int w = _w - _labelWidth;
@@ -268,7 +263,7 @@ void PopUpWidget::drawWidget(bool hilite)
// Draw the label, if any
if(_labelWidth > 0)
s.drawString(_font, _label, _x, _y + myTextY, _labelWidth,
- isEnabled() && onTop ? _textcolor : kColor, TextAlign::Left);
+ isEnabled() ? _textcolor : kColor, TextAlign::Left);
// Draw a thin frame around us.
s.frameRect(x, _y, w, _h, isEnabled() && hilite ? kWidColorHi : kColor);
@@ -277,13 +272,13 @@ 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,
- onTop ? _changed ? kDbgChangedColor : bgCol : kDlgColor);
+ s.fillRect(x + 1, _y + 1, w - (_arrowWidth * 2 - 1), _h - 2,
+ _changed ? kDbgChangedColor : bgCol);
s.fillRect(x + w - (_arrowWidth * 2 - 2), _y + 1, (_arrowWidth * 2 - 3), _h - 2,
- onTop ? isEnabled() && hilite ? kBtnColorHi : bgCol : kBGColorLo);
+ isEnabled() && hilite ? kBtnColorHi : bgCol);
// Draw an arrow pointing down at the right end to signal this is a dropdown/popup
s.drawBitmap(_arrowImg, x + w - (_arrowWidth * 1.5 - 1), _y + myArrowsY + 1,
- !(isEnabled() && onTop) ? kColor : kTextColor, _arrowWidth, _arrowHeight);
+ !isEnabled() ? kColor : kTextColor, _arrowWidth, _arrowHeight);
// Draw the selected entry, if any
const string& name = editString();
@@ -294,7 +289,7 @@ void PopUpWidget::drawWidget(bool hilite)
TextAlign::Right : TextAlign::Left;
adjustOffset();
s.drawString(_font, name, x + _textOfs, _y + myTextY, w,
- !(isEnabled() && onTop) ? kColor : _changed ? kDbgChangedTextColor : kTextColor,
+ !isEnabled() ? kColor : _changed ? kDbgChangedTextColor : kTextColor,
align, editable ? -_editScrollOffset : 0, !editable);
if(editable)
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/ProgressDialog.cxx b/src/gui/ProgressDialog.cxx
index 04bc2adae..80aafc8c4 100644
--- a/src/gui/ProgressDialog.cxx
+++ b/src/gui/ProgressDialog.cxx
@@ -18,6 +18,8 @@
#include "bspf.hxx"
#include "OSystem.hxx"
#include "FrameBuffer.hxx"
+#include "EventHandler.hxx"
+#include "TimerManager.hxx"
#include "Widget.hxx"
#include "Dialog.hxx"
#include "Font.hxx"
@@ -27,38 +29,64 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProgressDialog::ProgressDialog(GuiObject* boss, const GUI::Font& font,
const string& message)
- : Dialog(boss->instance(), boss->parent())
+ : Dialog(boss->instance(), boss->parent()),
+ myFont(font)
{
const int fontWidth = font.getMaxCharWidth(),
fontHeight = font.getFontHeight(),
lineHeight = font.getLineHeight(),
VBORDER = fontHeight / 2,
HBORDER = fontWidth * 1.25,
- VGAP = fontHeight / 4;
- int xpos, ypos, lwidth;
+ VGAP = fontHeight / 4,
+ buttonHeight = font.getLineHeight() * 1.25,
+ BTN_BORDER = fontWidth * 2.5,
+ buttonWidth = font.getStringWidth("Cancel") + BTN_BORDER,
+ lwidth = font.getStringWidth(message);
+
+ int xpos, ypos;
+ WidgetArray wid;
// Calculate real dimensions
- lwidth = font.getStringWidth(message);
- _w = HBORDER * 2 + lwidth;
- _h = VBORDER * 2 + lineHeight * 2 + VGAP * 2;
+ _w = HBORDER * 2 + std::max(lwidth, buttonWidth);
+ _h = VBORDER * 2 + lineHeight * 2 + buttonHeight + VGAP * 6;
xpos = HBORDER; ypos = VBORDER;
myMessage = new StaticTextWidget(this, font, xpos, ypos, lwidth, fontHeight,
message, TextAlign::Center);
myMessage->setTextColor(kTextColorEm);
- xpos = HBORDER; ypos += lineHeight + VGAP * 2;
- mySlider = new SliderWidget(this, font, xpos, ypos, lwidth, lineHeight, "", 0, 0);
+ ypos += lineHeight + VGAP * 2;
+ mySlider = new SliderWidget(this, font, xpos, ypos, lwidth, lineHeight,
+ "", 0, 0);
mySlider->setMinValue(1);
mySlider->setMaxValue(100);
- open();
+ ypos += lineHeight + VGAP * 4;
+ ButtonWidget* b = new ButtonWidget(this, font, (_w - buttonWidth) / 2, ypos,
+ buttonWidth, buttonHeight, "Cancel",
+ Event::UICancel);
+ wid.push_back(b);
+ addCancelWidget(b);
+ addToFocusList(wid);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ProgressDialog::setMessage(const string& message)
{
+ const int fontWidth = myFont.getMaxCharWidth(),
+ HBORDER = fontWidth * 1.25,
+ lwidth = myFont.getStringWidth(message),
+ BTN_BORDER = fontWidth * 2.5,
+ buttonWidth = myFont.getStringWidth("Cancel") + BTN_BORDER;
+
+ // Recalculate real dimensions
+ _w = HBORDER * 2 + std::max(lwidth, buttonWidth);
+
+ myMessage->setWidth(lwidth);
myMessage->setLabel(message);
+ mySlider->setWidth(lwidth);
+
+ _cancelWidget->setPosX((_w - buttonWidth) / 2);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -68,23 +96,57 @@ void ProgressDialog::setRange(int start, int finish, int step)
myFinish = finish;
myStep = int((step / 100.0) * (myFinish - myStart + 1));
- mySlider->setMinValue(myStart);
+ mySlider->setMinValue(myStart + myStep);
mySlider->setMaxValue(myFinish);
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void ProgressDialog::resetProgress()
+{
+ myProgress = myStepProgress = 0;
+ mySlider->setValue(0);
+ myIsCancelled = false;
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ProgressDialog::setProgress(int progress)
{
// Only increase the progress bar if we have arrived at a new step
- if(progress - mySlider->getValue() > myStep)
+ if(progress - myStepProgress >= myStep)
{
- mySlider->setValue(progress);
+ myStepProgress = progress;
+ mySlider->setValue(progress % (myFinish - myStart + 1));
// Since this dialog is usually called in a tight loop that doesn't
- // yield, we need to manually tell the framebuffer that a redraw is
- // necessary
+ // yield, we need to manually:
+ // - tell the framebuffer that a redraw is necessary
+ // - poll the events
// This isn't really an ideal solution, since all redrawing and
// event handling is suspended until the dialog is closed
instance().frameBuffer().update();
+ instance().eventHandler().poll(TimerManager::getTicks());
}
}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void ProgressDialog::incProgress()
+{
+ setProgress(++myProgress);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void ProgressDialog::handleCommand(CommandSender* sender, int cmd,
+ int data, int id)
+{
+ switch(cmd)
+ {
+ case Event::UICancel:
+ myIsCancelled = true;
+ break;
+
+ default:
+ Dialog::handleCommand(sender, cmd, data, 0);
+ break;
+ }
+}
+
diff --git a/src/gui/ProgressDialog.hxx b/src/gui/ProgressDialog.hxx
index 0ec8731b5..a62fb8cbf 100644
--- a/src/gui/ProgressDialog.hxx
+++ b/src/gui/ProgressDialog.hxx
@@ -21,25 +21,37 @@
class GuiObject;
class StaticTextWidget;
class SliderWidget;
+class ButtonWidget;
#include "bspf.hxx"
+#include "Dialog.hxx"
class ProgressDialog : public Dialog
{
public:
ProgressDialog(GuiObject* boss, const GUI::Font& font,
- const string& message);
+ const string& message = "");
~ProgressDialog() override = default;
void setMessage(const string& message);
void setRange(int begin, int end, int step);
+ void resetProgress();
void setProgress(int progress);
+ void incProgress();
+ bool isCancelled() const { return myIsCancelled; }
private:
+ const GUI::Font& myFont;
StaticTextWidget* myMessage{nullptr};
SliderWidget* mySlider{nullptr};
int myStart{0}, myFinish{0}, myStep{0};
+ int myProgress{0};
+ int myStepProgress{0};
+ bool myIsCancelled{false};
+
+ private:
+ void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
private:
// Following constructors and assignment operators not supported
diff --git a/src/gui/RomAuditDialog.cxx b/src/gui/RomAuditDialog.cxx
index 09c264025..a95e4fce5 100644
--- a/src/gui/RomAuditDialog.cxx
+++ b/src/gui/RomAuditDialog.cxx
@@ -119,13 +119,17 @@ void RomAuditDialog::auditRoms()
// Create a progress dialog box to show the progress of processing
// the ROMs, since this is usually a time-consuming operation
- ProgressDialog progress(this, instance().frameBuffer().font(),
- "Auditing ROM files ...");
+ ostringstream buf;
+ ProgressDialog progress(this, instance().frameBuffer().font());
+
+ buf << "Auditing ROM files" << ELLIPSIS;
+ progress.setMessage(buf.str());
progress.setRange(0, int(files.size()) - 1, 5);
+ progress.open();
Properties props;
uInt32 renamed = 0, notfound = 0;
- for(uInt32 idx = 0; idx < files.size(); ++idx)
+ for(uInt32 idx = 0; idx < files.size() && !progress.isCancelled(); ++idx)
{
string extension;
if(files[idx].isFile() &&
@@ -156,7 +160,7 @@ void RomAuditDialog::auditRoms()
}
// Update the progress bar, indicating one more ROM has been processed
- progress.setProgress(idx);
+ progress.incProgress();
}
progress.close();
diff --git a/src/gui/RomInfoWidget.cxx b/src/gui/RomInfoWidget.cxx
index 01b30015b..325cadfda 100644
--- a/src/gui/RomInfoWidget.cxx
+++ b/src/gui/RomInfoWidget.cxx
@@ -172,17 +172,17 @@ 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();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomInfoWidget::drawWidget(bool hilite)
{
FBSurface& s = dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
-
const int yoff = myAvail.h + 10;
- s.fillRect(_x+2, _y+2, _w-4, _h-4, onTop ? _bgcolor : _bgcolorlo);
+ s.fillRect(_x+2, _y+2, _w-4, _h-4, _bgcolor);
s.frameRect(_x, _y, _w, _h, kColor);
s.frameRect(_x, _y+yoff, _w, _h-yoff, kColor);
@@ -204,7 +204,7 @@ void RomInfoWidget::drawWidget(bool hilite)
{
uInt32 x = _x + ((_w - _font.getStringWidth(mySurfaceErrorMsg)) >> 1);
uInt32 y = _y + ((yoff - _font.getLineHeight()) >> 1);
- s.drawString(_font, mySurfaceErrorMsg, x, y, _w - 10, onTop ? _textcolor : _shadowcolor);
+ s.drawString(_font, mySurfaceErrorMsg, x, y, _w - 10, _textcolor);
}
int xpos = _x + 8, ypos = _y + yoff + 5;
@@ -224,7 +224,8 @@ void RomInfoWidget::drawWidget(bool hilite)
break;
}
int lines = s.drawString(_font, info, xpos, ypos, _w - 16, _font.getFontHeight() * 3,
- onTop ? _textcolor : _shadowcolor);
+ _textcolor);
ypos += _font.getLineHeight() + (lines - 1) * _font.getFontHeight();
}
+ clearDirty();
}
diff --git a/src/gui/ScrollBarWidget.cxx b/src/gui/ScrollBarWidget.cxx
index 335b716de..c9d68cb82 100644
--- a/src/gui/ScrollBarWidget.cxx
+++ b/src/gui/ScrollBarWidget.cxx
@@ -240,25 +240,20 @@ 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();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ScrollBarWidget::recalc()
{
//cerr << "ScrollBarWidget::recalc()\n";
+ int oldSliderHeight = _sliderHeight,
+ oldSliderPos = _sliderPos;
+
if(_numEntries > _entriesPerPage)
{
_sliderHeight = (_h - 2 * _upDownBoxHeight) * _entriesPerPage / _numEntries;
@@ -276,15 +271,14 @@ void ScrollBarWidget::recalc()
_sliderPos = _upDownBoxHeight;
}
- setDirty();
+ if(oldSliderHeight != _sliderHeight || oldSliderPos != _sliderPos)
+ setDirty(); // only set dirty when something changed
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ScrollBarWidget::drawWidget(bool hilite)
{
-//cerr << "ScrollBarWidget::drawWidget\n";
FBSurface& s = _boss->dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
int bottomY = _y + _h;
bool isSinglePage = (_numEntries <= _entriesPerPage);
@@ -298,23 +292,26 @@ void ScrollBarWidget::drawWidget(bool hilite)
s.fillRect(_x + 1, _y + 1, _w - 2, _upDownBoxHeight - 2, kScrollColor);
s.drawBitmap(_upImg, _x + (_scrollBarWidth - _upDownWidth) / 2,
_y + (_upDownBoxHeight - _upDownHeight) / 2,
- onTop ? isSinglePage ? kColor : (hilite && _part == Part::UpArrow) ? kWidColor
- : kTextColor : kColor, _upDownWidth, _upDownHeight);
+ isSinglePage ? kColor
+ : (hilite && _part == Part::UpArrow) ? kWidColor : kTextColor,
+ _upDownWidth, _upDownHeight);
// Down arrow
if(hilite && _part == Part::DownArrow)
s.fillRect(_x + 1, bottomY - _upDownBoxHeight + 1, _w - 2, _upDownBoxHeight - 2, kScrollColor);
s.drawBitmap(_downImg, _x + (_scrollBarWidth - _upDownWidth) / 2,
bottomY - _upDownBoxHeight + (_upDownBoxHeight - _upDownHeight) / 2,
- onTop ? isSinglePage ? kColor : (hilite && _part == Part::DownArrow) ?
- kWidColor : kTextColor : kColor, _upDownWidth, _upDownHeight);
+ isSinglePage ? kColor
+ : (hilite && _part == Part::DownArrow) ? kWidColor : kTextColor,
+ _upDownWidth, _upDownHeight);
// Slider
if(!isSinglePage)
{
s.fillRect(_x + 1, _y + _sliderPos - 1, _w - 2, _sliderHeight + 2,
- onTop ? (hilite && _part == Part::Slider) ? kScrollColorHi : kScrollColor : kColor);
+ (hilite && _part == Part::Slider) ? kScrollColorHi : kScrollColor);
}
+ 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..b94d1b5c4 100644
--- a/src/gui/StringListWidget.cxx
+++ b/src/gui/StringListWidget.cxx
@@ -51,40 +51,61 @@ 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
+{
+ return getToolTipIndex(oldPos) != getToolTipIndex(newPos)
+ && getToolTip(oldPos) != getToolTip(newPos);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StringListWidget::drawWidget(bool hilite)
{
FBSurface& s = _boss->dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
int i, pos, len = int(_list.size());
// Draw a thin frame around the list.
- s.frameRect(_x, _y, _w + 1, _h, onTop && hilite && _hilite ? kWidColorHi : kColor);
+ s.frameRect(_x, _y, _w + 1, _h, hilite && _hilite ? kWidColorHi : kColor);
if (!isEnabled())
- s.fillRect(_x + 1, _y + 1, _w - 1, _h - 2, onTop ? kDlgColor : kBGColorLo);
+ s.fillRect(_x + 1, _y + 1, _w - 1, _h - 2, kDlgColor);
// Draw the list items
for (i = 0, pos = _currentPos; i < _rows && pos < len; i++, pos++)
{
const int y = _y + 2 + _lineHeight * i;
- ColorId textColor = onTop ? kTextColor : kShadowColor;
+ ColorId textColor = kTextColor;
// Draw the selected item inverted, on a highlighted background.
- if (onTop && _selectedItem == pos && _hilite)
+ if (_selectedItem == pos && _hilite)
{
if(_hasFocus && !_editMode)
{
diff --git a/src/gui/StringListWidget.hxx b/src/gui/StringListWidget.hxx
index eed279aeb..668414c4c 100644
--- a/src/gui/StringListWidget.hxx
+++ b/src/gui/StringListWidget.hxx
@@ -32,9 +32,17 @@ 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; }
+ int getToolTipIndex(const Common::Point& pos) const;
+
void drawWidget(bool hilite) override;
Common::Rect getEditRect() const override;
diff --git a/src/gui/TabWidget.cxx b/src/gui/TabWidget.cxx
index f1b92b876..89967d041 100644
--- a/src/gui/TabWidget.cxx
+++ b/src/gui/TabWidget.cxx
@@ -110,6 +110,9 @@ void TabWidget::setActiveTab(int tabID, bool show)
_tabs[_activeTab].firstWidget = _firstWidget;
}
+ if(_activeTab != tabID)
+ setDirty();
+
_activeTab = tabID;
_firstWidget = _tabs[tabID].firstWidget;
@@ -138,8 +141,6 @@ void TabWidget::updateActiveTab()
if(_tabs[_activeTab].parentWidget)
_tabs[_activeTab].parentWidget->loadConfig();
- setDirty();
-
// Redraw focused areas
_boss->redrawFocus(); // TJ: Does nothing!
}
@@ -213,20 +214,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 +262,43 @@ 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();
+
+ // 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 ? kTextColor : kColor;
+ int yOffset = (i == _activeTab) ? 0 : 1;
+ s.fillRect(x, _y + 1, tabWidth, _tabHeight - 1,
+ (i == _activeTab)
+ ? kDlgColor : kBGColorHi); // ? 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, kWidColor);
+ s.vLine(x + tabWidth, _y + 1, _y + _tabHeight - 1, kBGColorLo);
+ }
+ else
+ s.hLine(x, _y + _tabHeight, x + tabWidth, kWidColor);
+
+ 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, kWidColor);
+ s.hLine(_x, _y + _h - 1, _x + _w - 1, kBGColorLo);
+
+ 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..5f6e0e687 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..a887e0096
--- /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..2d773b888 100644
--- a/src/gui/UIDialog.cxx
+++ b/src/gui/UIDialog.cxx
@@ -109,6 +109,8 @@ UIDialog::UIDialog(OSystem& osystem, DialogContainer& parent,
xpos = myDialogFontPopup->getRight() + fontWidth * 5;
myHidpiWidget = new CheckboxWidget(myTab, font, xpos,
ypos + 1, "HiDPI mode (*)");
+ myHidpiWidget->setToolTip("Scale the UI by a factor of two when enabled.");
+
wid.push_back(myHidpiWidget);
// Dialog position
@@ -126,6 +128,7 @@ UIDialog::UIDialog(OSystem& osystem, DialogContainer& parent,
// Center window (in windowed mode)
xpos = myHidpiWidget->getLeft();
myCenter = new CheckboxWidget(myTab, _font, xpos, ypos + 1, "Center windows");
+ myCenter->setToolTip("Check to center all windows, else remember last position.");
wid.push_back(myCenter);
// Delay between quick-selecting characters in ListWidget
@@ -138,6 +141,8 @@ UIDialog::UIDialog(OSystem& osystem, DialogContainer& parent,
myListDelaySlider->setMaxValue(1000);
myListDelaySlider->setStepValue(50);
myListDelaySlider->setTickmarkIntervals(5);
+ myListDelaySlider->setToolTip("Set delay between key presses in file lists"
+ " before a search string resets.");
wid.push_back(myListDelaySlider);
ypos += lineHeight + VGAP;
@@ -441,6 +446,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/WhatsNewDialog.cxx b/src/gui/WhatsNewDialog.cxx
index 825ed43fe..4baa36101 100644
--- a/src/gui/WhatsNewDialog.cxx
+++ b/src/gui/WhatsNewDialog.cxx
@@ -43,18 +43,16 @@ WhatsNewDialog::WhatsNewDialog(OSystem& osystem, DialogContainer& parent, const
max_w, max_h);
#if defined(RETRON77)
- add(ypos, "fixed CDF cartridges crash");
- add(ypos, "fixed crash with SaveKey ROMs (EEPROM file issues)");
- add(ypos, "fixed bug with launcher not remembering last selected ROM");
+ add(ypos, "increased sample size for CDFJ+");
+ add(ypos, "fixed navigation bug in Video & Audio settings dialog");
+ add(ypos, "fixed autofire bug for trackball controllers");
#else
- add(ypos, "added basic text cut/copy/paste from/to UI");
- add(ypos, "added color parameters to 'Custom' palette");
- add(ypos, "improved AtariVox-USB adaptor autodetection");
- add(ypos, "fixed fullscreen mode, aspect correction and pixel-exact snapshots");
- add(ypos, "fixed reduced ARM emulation performance for CDF ROMs");
- add(ypos, "fixed crash with SaveKey ROMs (EEPROM file issues)");
- add(ypos, "fixed Atari mouse autodetection");
- add(ypos, "fixed bug with launcher not remembering last selected ROM");
+ add(ypos, "enhanced cut/copy/paste for text editing");
+ add(ypos, "added undo and redo to text editing");
+ add(ypos, "added wildcard support to launcher dialog filter");
+ add(ypos, "added option to search subdirectories in launcher");
+ add(ypos, "added tooltips to many UI items");
+ add(ypos, "increased sample size for CDFJ+");
add(ypos, ELLIPSIS + " (for a complete list see 'docs/Changes.txt')");
#endif
diff --git a/src/gui/Widget.cxx b/src/gui/Widget.cxx
index a54d9e45c..38c46bdbe 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,40 @@ 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())
+ {
+ #ifndef RETRON77
+ if(wantsToolTip())
+ dialog().tooltip().request();
+ #endif
+
+ // Recursively tick widget and all child dialogs and widgets
+ Widget* w = _firstWidget;
+
+ while(w)
+ {
+ w->tick();
+ w = w->_next;
+ }
+ }
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -65,62 +97,126 @@ 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();
+ 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, (_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, (_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::setPosX(int x)
+{
+ setPos(x, _y);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void Widget::setPosY(int y)
+{
+ setPos(_x, y);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void Widget::setPos(int x, int y)
+{
+ setPos(Common::Point(x, y));
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void Widget::setPos(const Common::Point& pos)
+{
+ if(pos != Common::Point(_x, _y))
+ {
+ _x = pos.x;
+ _y = pos.y;
+ // we have to redraw the whole dialog!
+ dialog().setDirty();
+ }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+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 +246,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)
{
@@ -197,7 +301,6 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr,
FBSurface& s = boss->dialog().surface();
int size = int(arr.size()), pos = -1;
Widget* tmp;
- bool onTop = boss->dialog().isOnTop();
for(int i = 0; i < size; ++i)
{
@@ -222,9 +325,7 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr,
else
tmp->_hasFocus = false;
- s.frameRect(x, y, w, h, onTop ? kDlgColor : kBGColorLo);
-
- tmp->setDirty();
+ s.frameRect(x, y, w, h, kDlgColor);
}
}
@@ -277,10 +378,7 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr,
tmp->setFlags(Widget::FLAG_HILITED);
}
- if (onTop)
- s.frameRect(x, y, w, h, kWidFrameColor, FrameStyle::Dashed);
-
- tmp->setDirty();
+ s.frameRect(x, y, w, h, kWidFrameColor, FrameStyle::Dashed);
return tmp;
}
@@ -290,6 +388,7 @@ void Widget::setDirtyInChain(Widget* start)
{
while(start)
{
+ //cerr << "setDirtyInChain " << typeid(*start).name() << endl;
start->setDirty();
start = start->_next;
}
@@ -304,7 +403,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;
@@ -325,28 +425,42 @@ StaticTextWidget::StaticTextWidget(GuiObject* boss, const GUI::Font& font,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StaticTextWidget::setValue(int value)
{
- _label = std::to_string(value);
-
- setDirty();
+ setLabel(std::to_string(value));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StaticTextWidget::setLabel(const string& label)
{
- _label = label;
+ if(_label != label)
+ {
+ _label = label;
+ setDirty();
+ }
+}
- 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)
{
FBSurface& s = _boss->dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
- s.drawString(_font, _label, _x, _y, _w,
- isEnabled() && onTop ? _textcolor : kColor, _align, 0, true, _shadowcolor);
- setDirty();
+ s.drawString(_font, _label, _x, _y, _w,
+ isEnabled() ? _textcolor : kColor, _align, 0, true, _shadowcolor);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -400,13 +514,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);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -461,21 +577,18 @@ void ButtonWidget::setBitmap(const uInt32* bitmap, int bmw, int bmh)
void ButtonWidget::drawWidget(bool hilite)
{
FBSurface& s = _boss->dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
- s.frameRect(_x, _y, _w, _h, !onTop ? kShadowColor : hilite && isEnabled() ? kBtnBorderColorHi : kBtnBorderColor);
+ s.frameRect(_x, _y, _w, _h, hilite && isEnabled() ? kBtnBorderColorHi : kBtnBorderColor);
if (!_useBitmap)
s.drawString(_font, _label, _x, _y + (_h - _lineHeight)/2 + 1, _w,
- !(isEnabled() && onTop) ? _textcolorlo :
+ !isEnabled() ? _textcolorlo :
hilite ? _textcolorhi : _textcolor, _align);
else
s.drawBitmap(_bitmap, _x + (_w - _bmw) / 2, _y + (_h - _bmh) / 2,
- !(isEnabled() && onTop) ? _textcolorlo :
+ !isEnabled() ? _textcolorlo :
hilite ? _textcolorhi : _textcolor,
_bmw, _bmh);
-
- setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -508,18 +621,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)
{
@@ -608,35 +709,32 @@ void CheckboxWidget::setFill(FillType type)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheckboxWidget::setState(bool state, bool changed)
{
- if(_state != state)
+ if(_state != state || _changed != changed)
{
- _state = state;
setDirty();
+
+ _state = state;
+ _changed = changed;
}
- _changed = changed;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheckboxWidget::drawWidget(bool hilite)
{
FBSurface& s = _boss->dialog().surface();
- bool onTop = _boss->dialog().isOnTop();
if(_drawBox)
- s.frameRect(_x, _y + _boxY, _boxSize, _boxSize, onTop && hilite && isEnabled() && isEditable() ? kWidColorHi : kColor);
+ s.frameRect(_x, _y + _boxY, _boxSize, _boxSize, hilite && isEnabled() && isEditable() ? kWidColorHi : kColor);
// Do we draw a square or cross?
s.fillRect(_x + 1, _y + _boxY + 1, _boxSize - 2, _boxSize - 2,
- _changed ? onTop ? kDbgChangedColor : kDlgColor :
- isEnabled() && onTop ? _bgcolor : kDlgColor);
+ _changed ? kDbgChangedColor : isEnabled() ? _bgcolor : kDlgColor);
if(_state)
- s.drawBitmap(_img, _x + 2, _y + _boxY + 2, onTop && isEnabled() ? hilite && isEditable() ? kWidColorHi : kCheckColor
+ s.drawBitmap(_img, _x + 2, _y + _boxY + 2, isEnabled() ? hilite && isEditable() ? kWidColorHi : kCheckColor
: kColor, _boxSize - 4);
// Finally draw the label
s.drawString(_font, _label, _x + prefixSize(_font), _y + _textY, _w,
- onTop && isEnabled() ? kTextColor : kColor);
-
- setDirty();
+ isEnabled() ? kTextColor : kColor);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -652,7 +750,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 +968,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..cf4637ea1 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;
@@ -65,14 +53,18 @@ class Widget : public GuiObject
virtual int getTop() const { return _y; }
virtual int getRight() const { return _x + getWidth(); }
virtual int getBottom() const { return _y + getHeight(); }
+ virtual void setPosX(int x);
+ virtual void setPosY(int y);
+ virtual void setPos(int x, int y);
+ virtual void setPos(const Common::Point& pos);
virtual bool handleText(char text) { return false; }
virtual bool handleKeyDown(StellaKey key, StellaMod mod) { return false; }
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 +74,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 +90,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 +109,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 +126,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 +138,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 +148,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 +190,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 +287,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/libretro/Makefile b/src/libretro/Makefile
index 8d17db9db..6f8eb6599 100644
--- a/src/libretro/Makefile
+++ b/src/libretro/Makefile
@@ -43,7 +43,7 @@ TARGET_NAME = stella
ifeq (,$(findstring msvc,$(platform)))
- CXXFLAGS += -std=c++14 -fno-rtti
+ CXXFLAGS += -std=c++17 -fno-rtti
LIBS := -lm
else
LIBS :=
@@ -282,8 +282,8 @@ else ifneq (,$(filter $(platform), ngc wii wiiu))
else ifeq ($(platform), emscripten)
TARGET := $(TARGET_NAME)_libretro_$(platform).bc
STATIC_LINKING = 1
- CXXFLAGS += $(PTHREAD_FLAGS) -std=c++14
- LDFLAGS += $(PTHREAD_FLAGS) -std=c++14
+ CXXFLAGS += $(PTHREAD_FLAGS) -std=c++17
+ LDFLAGS += $(PTHREAD_FLAGS) -std=c++17
# Genode
else ifeq ($(platform), genode)
diff --git a/src/libretro/jni/Android.mk b/src/libretro/jni/Android.mk
index 3115baa53..644f983ae 100644
--- a/src/libretro/jni/Android.mk
+++ b/src/libretro/jni/Android.mk
@@ -14,6 +14,6 @@ endif
include $(CLEAR_VARS)
LOCAL_MODULE := retro
LOCAL_SRC_FILES := $(SOURCES_CXX) $(SOURCES_C)
-LOCAL_CXXFLAGS := $(COREFLAGS) -std=c++14
+LOCAL_CXXFLAGS := $(COREFLAGS) -std=c++17
LOCAL_LDFLAGS := -Wl,-version-script=$(CORE_DIR)/libretro/link.T
include $(BUILD_SHARED_LIBRARY)
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
+