Merge remote-tracking branch 'origin/master' into feature/json-mappings

This commit is contained in:
Christian Speckner 2020-11-28 20:45:51 +01:00
commit 730932355f
141 changed files with 3108 additions and 1183 deletions

View File

@ -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!

View File

@ -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
#######################################################################

2
configure vendored
View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

@ -260,7 +260,7 @@
<h2><b><a name="Features">Features</a></b></h2>
<ul>
<li>High speed emulation using optimized C++14 code</li>
<li>High speed emulation using optimized C++17 code</li>
<li>Supports high quality TIA emulation using the cycle-exact TIA core from
<a href="https://github.com/6502ts/6502.ts">6502.ts</a> by
Christian Speckner</li>
@ -350,7 +350,7 @@
<li>OpenGL capable video card</li>
<li>Other architectures (MIPS, PPC, PPC64, etc.) have been confirmed to work,
but aren't as well tested as i386/x86_64</li>
<li>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</li>
<li>GNU g++ v/7 or Clang v/5 (with C++17 support) and the make utility are required for compiling the Stella source code</li>
</ul>
<p>
@ -3689,15 +3689,29 @@
<p>ROM Info Viewer width at 50% , UI sized 1280x900, large launcher font:</p>
<img src="graphics/rominfo_2x_small.png">
<p>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.</p>
<p>The dialog items at the top can be used to define the listed files:</p>
<ul>
<li>
The 'Show all files' checkbox allows displaying files which do not
have a valid ROM extension.
</li><li>
If 'Incl. subdirectories' is checked, Stella will list matching files
from all subdirectories too.
</li><li>
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.</br>
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.
</li>
</ul>
</br>
<h3><b><a name="ROMLauncherContextMenu">ROM Launcher Context Menu</a></b></h3>
@ -3735,12 +3749,6 @@
</tr>
</table>
</li>
<br><li><b>Show only ROM files</b>: Selecting this reloads the current listing,
showing only files that have a valid ROM extension.</li>
<br><li><b>Show all files</b>: Selecting this reloads the current listing,
showing <i>all</i> files (with no restriction on file name).</li>
<br><li><b>Reload listing</b>: Selecting this performs a reload of the
current listing. It is an alternative to pressing the 'Control + r'
key combo.</li>

View File

@ -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);

View File

@ -45,6 +45,8 @@ EventHandlerSDL2::EventHandlerSDL2(OSystem& osystem)
}
Logger::debug("EventHandlerSDL2::EventHandlerSDL2 SDL_INIT_JOYSTICK");
#endif
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -104,41 +104,49 @@ const Common::Rect& FBSurfaceSDL2::dstRect() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setSrcPos(uInt32 x, uInt32 y)
{
if(x != static_cast<uInt32>(mySrcR.x) || y != static_cast<uInt32>(mySrcR.y))
{
setSrcPosInternal(x, y);
if(setSrcPosInternal(x, y))
reinitializeBlitter();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setSrcSize(uInt32 w, uInt32 h)
{
if(w != static_cast<uInt32>(mySrcR.w) || h != static_cast<uInt32>(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<uInt32>(myDstR.x) || y != static_cast<uInt32>(myDstR.y))
{
setDstPosInternal(x, y);
if(setDstPosInternal(x, y))
reinitializeBlitter();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setDstSize(uInt32 w, uInt32 h)
{
if(w != static_cast<uInt32>(myDstR.w) || h != static_cast<uInt32>(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()
{

View File

@ -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<uInt32>(mySrcR.x) || y != static_cast<uInt32>(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<uInt32>(mySrcR.w) || h != static_cast<uInt32>(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<uInt32>(myDstR.x) || y != static_cast<uInt32>(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<uInt32>(myDstR.w) || h != static_cast<uInt32>(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};

View File

@ -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);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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<uInt8[]> buffer = make_unique<uInt8[]>(myStateSize);
for (uInt32 i = 0; i < numStates; ++i)
{
RewindState& state = myStateList.current();
Serializer& s = state.data;
uInt32 stateSize = uInt32(s.size());
unique_ptr<uInt8[]> buffer = make_unique<uInt8[]>(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<uInt8[]> buffer = make_unique<uInt8[]>(myStateSize);
for (uInt32 i = 0; i < numStates; ++i)
{
if (myStateList.full())
compressStates();
uInt32 stateSize = in.getInt();
unique_ptr<uInt8[]> buffer = make_unique<uInt8[]>(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();
}

View File

@ -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

View File

@ -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);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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;
/**

View File

@ -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;

View File

@ -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 = "");
/**

View File

@ -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";

View File

@ -117,7 +117,7 @@ void Cartridge3EWidget::loadConfig()
myBankWidgets[1]->setSelectedIndex(bank - myCart.romBankCount(), oldBank != bank);
}
CartDebugWidget::loadConfig();
CartDebugWidget::loadConfig(); // Intentionally calling grand-parent method
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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);
}

View File

@ -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;

View File

@ -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<string, 4> 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<string, 4> 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;
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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

View File

@ -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
}

View File

@ -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;

View File

@ -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);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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");

View File

@ -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<StaticTextWidget*, 16> myRamLabels{nullptr};
DataGridWidget* myRamGrid{nullptr};
DataGridRamWidget* myRamGrid{nullptr};
DataGridWidget* myHexValue{nullptr};
DataGridWidget* myDecValue{nullptr};
DataGridWidget* myBinValue{nullptr};

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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};

View File

@ -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<Int32>(bytes.length()) < idx.x + 1)
return EmptyString;
Int32 val;
if(bytes.length() == 8 && bytes[2] != ' ')
{
// Binary value
val = static_cast<Int32>(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<Int32>(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:

View File

@ -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<RomListSettings> 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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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)
{

View File

@ -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<ContextMenu> myMenu;
TiaZoomWidget* myZoom{nullptr};

View File

@ -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],

View File

@ -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)
{

View File

@ -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);

View File

@ -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<int>(_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);
}
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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:

View File

@ -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);
}

View File

@ -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;

View File

@ -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 \

View File

@ -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;
}

View File

@ -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;

View File

@ -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<Controller> 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<AtariVox>(port, myEvent, *mySystem,
myOSystem.settings().getString("avoxport"), nvramfile, callback);
@ -933,7 +933,7 @@ unique_ptr<Controller> 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<SaveKey>(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);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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<bool(const FilesystemNode& node)>;
using CancelCheck = std::function<bool()> 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);
};

View File

@ -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
}
};

View File

@ -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<FBSurface> surface;
bool enabled{false};
bool dirty{false};
bool showGauge{false};
float value{0.0F};
string valueText;

View File

@ -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
;

View File

@ -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;
}

View File

@ -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

View File

@ -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)));
}

View File

@ -71,7 +71,7 @@ unique_ptr<Controller> 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)

View File

@ -91,6 +91,8 @@ void Serializer::rewind()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
size_t Serializer::size() const
{
myStream->seekp(0, std::ios::end);
return myStream->tellp();
}

View File

@ -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 <float> 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"

View File

@ -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);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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?

View File

@ -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;

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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<ToolTip>(*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<FBSurface>& surface){
mySurfaceStack.applyAll([](shared_ptr<FBSurface>& 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
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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<FBSurface>& 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> _toolTip;
Common::FixedStack<shared_ptr<FBSurface>> mySurfaceStack;
@ -232,10 +234,9 @@ class Dialog : public GuiObject
WidgetArray _buttonGroup;
shared_ptr<FBSurface> _surface;
shared_ptr<FBSurface> _shadeSurface;
int _tabID{0};
int _flags{0};
bool _dirty{false};
uInt32 _max_w{0}; // maximum wanted width
uInt32 _max_h{0}; // maximum wanted height

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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())
{

View File

@ -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<UndoHandler> 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;

View File

@ -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;

View File

@ -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);

View File

@ -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<ProgressDialog>(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<int>(_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<ProgressDialog> FileListWidget::myProgressDialog{nullptr};

View File

@ -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<ProgressDialog> 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<string> _history;
uInt32 _selected{0};

View File

@ -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");
}
}

View File

@ -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:

Some files were not shown because too many files have changed in this diff Show More