Merge remote-tracking branch 'remotes/origin/feature/improve_ui_redraws'

This commit is contained in:
thrust26 2020-11-19 18:16:36 +01:00
commit 39b94d74e1
99 changed files with 2088 additions and 850 deletions

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++14
endif
ifdef HAVE_CLANG
CXXFLAGS+= -Wno-multichar -Wunused -fno-rtti -Woverloaded-virtual -Wnon-virtual-dtor -std=c++14
CXXFLAGS+= -Wno-multichar -Wunused -Woverloaded-virtual -Wnon-virtual-dtor -std=c++14
endif
ifdef CLANG_WARNINGS
@ -81,8 +81,8 @@ else
endif
ifdef RELEASE
CXXFLAGS += -flto
LDFLAGS += -flto
CXXFLAGS += -flto -fno-rtti
LDFLAGS += -flto -fno-rtti
endif
#######################################################################

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

@ -181,7 +181,7 @@ uInt32 RewindManager::rewindStates(uInt32 numStates)
if(myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE
&& myOSystem.eventHandler().state() != EventHandlerState::PLAYBACK)
myOSystem.frameBuffer().showMessage(message);
myOSystem.frameBuffer().showTextMessage(message);
return i;
}
@ -216,7 +216,7 @@ uInt32 RewindManager::unwindStates(uInt32 numStates)
if(myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE
&& myOSystem.eventHandler().state() != EventHandlerState::PLAYBACK)
myOSystem.frameBuffer().showMessage(message);
myOSystem.frameBuffer().showTextMessage(message);
return i;
}

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

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

@ -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 load into " + 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 store.");
myCpuDataDest->setEditable(false, true);
_h = ypos + myPSRegister->getHeight() - y;
}

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 << _ram.getLabel(addr) << '\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();
@ -239,20 +241,6 @@ void DataGridWidget::setRange(int lower, int upper)
_upperBound = std::min(1 << _bits, upper);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DataGridWidget::handleMouseEntered()
{
setFlags(Widget::FLAG_HILITED);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DataGridWidget::handleMouseLeft()
{
clearFlags(Widget::FLAG_HILITED);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DataGridWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount)
{
@ -307,6 +295,7 @@ void DataGridWidget::handleMouseWheel(int x, int y, int direction)
else if(direction < 0)
incrementCell();
}
dialog().tooltip().hide();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -504,6 +493,7 @@ bool DataGridWidget::handleKeyDown(StellaKey key, StellaMod mod)
sendCommand(DataGridWidget::kSelectionChangedCmd, _selectedItem, _id);
setDirty();
dialog().tooltip().hide();
}
_currentKeyDown = key;
@ -583,6 +573,49 @@ void DataGridWidget::handleCommand(CommandSender* sender, int cmd,
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int DataGridWidget::getToolTipIndex(const Common::Point& pos) const
{
const int col = (pos.x - getAbsX()) / _colWidth;
const int row = (pos.y - getAbsY()) / _rowHeight;
if(row >= 0 && row < _rows && col >= 0 && col < _cols)
return row * _cols + col;
else
return -1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string DataGridWidget::getToolTip(const Common::Point& pos) const
{
const int idx = getToolTipIndex(pos);
if(idx < 0)
return EmptyString;
const Int32 val = _valueList[idx];
ostringstream buf;
buf << _toolTipText
<< "$" << Common::Base::toString(val, Common::Base::Fmt::_16)
<< " = #" << val;
if(val < 0x100)
{
if(val >= 0x80)
buf << '/' << -(0x100 - val);
buf << " = %" << Common::Base::toString(val, Common::Base::Fmt::_2);
}
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool DataGridWidget::changedToolTip(const Common::Point& oldPos,
const Common::Point& newPos) const
{
return getToolTipIndex(oldPos) != getToolTipIndex(newPos);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DataGridWidget::drawWidget(bool hilite)
{

View File

@ -84,6 +84,9 @@ class DataGridWidget : public EditableWidget
void setCrossed(bool enable) { _crossGrid = enable; }
string getToolTip(const Common::Point& pos) const override;
bool changedToolTip(const Common::Point& oldPos, const Common::Point& newPos) const override;
protected:
void drawWidget(bool hilite) override;
@ -98,11 +101,12 @@ class DataGridWidget : public EditableWidget
void receivedFocusWidget() override;
void lostFocusWidget() override;
bool hasToolTip() const override { return true; }
int getToolTipIndex(const Common::Point& pos) const;
void handleMouseDown(int x, int y, MouseButton b, int clickCount) override;
void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
void handleMouseWheel(int x, int y, int direction) override;
void handleMouseEntered() override;
void handleMouseLeft() override;
bool handleText(char text) override;
bool handleKeyDown(StellaKey key, StellaMod mod) override;
bool handleKeyUp(StellaKey key, StellaMod mod) override;
@ -128,7 +132,6 @@ class DataGridWidget : public EditableWidget
BoolArray _changedList;
BoolArray _hiliteList;
bool _editMode{false};
int _selectedItem{0};
StellaKey _currentKeyDown{KBDK_UNKNOWN};
string _backupString;
@ -148,6 +151,7 @@ class DataGridWidget : public EditableWidget
void enableEditMode(bool state) { _editMode = state; }
private:
// Following constructors and assignment operators not supported
DataGridWidget() = delete;

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"
@ -406,6 +407,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

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

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,6 +446,79 @@ void RomListWidget::lostFocusWidget()
abortEditMode();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Common::Point RomListWidget::getToolTipIndex(const Common::Point& pos) const
{
const Common::Rect& r = getEditRect();
const int col = (pos.x - r.x() - getAbsX()) / _font.getMaxCharWidth();
const int row = (pos.y - getAbsY()) / _lineHeight;
if(col < 0 || col >= 8
|| row < 0 || row + _currentPos >= int(myDisasm->list.size()))
return Common::Point(-1, -1);
else
return Common::Point(col, row + _currentPos);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string RomListWidget::getToolTip(const Common::Point& pos) const
{
const Common::Point& idx = getToolTipIndex(pos);
if(idx.y < 0)
return EmptyString;
const string bytes = myDisasm->list[idx.y].bytes;
if(static_cast<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)
{
@ -480,20 +545,20 @@ void RomListWidget::drawWidget(bool hilite)
codeDisasmW = actualWidth;
xpos = _x + CheckboxWidget::boxSize(_font) + 10; ypos = _y + 2;
for (i = 0, pos = _currentPos; i < _rows && pos < len; i++, pos++, ypos += _lineHeight)
for(i = 0, pos = _currentPos; i < _rows && pos < len; i++, pos++, ypos += _lineHeight)
{
ColorId bytesColor = textColor;
// Draw checkboxes for correct lines (takes scrolling into account)
// Mark checkboxes dirty for correct lines (takes scrolling into account)
myCheckList[i]->setState(instance().debugger().
checkBreakPoint(dlist[pos].address,
instance().debugger().cartDebug().getBank(dlist[pos].address)));
myCheckList[i]->setDirty();
// draw immediately, because chain order is not deterministic
myCheckList[i]->draw();
// Draw highlighted item in a frame
if (_highlightedItem == pos)
if(_highlightedItem == pos)
s.frameRect(_x + l.x() - 3, ypos - 1, _w - l.x(), _lineHeight, onTop ? kWidColorHi : kBGColorLo);
// Draw the selected item inverted, on a highlighted background.
@ -510,31 +575,31 @@ void RomListWidget::drawWidget(bool hilite)
// Draw labels
s.drawString(_font, dlist[pos].label, xpos, ypos, _labelWidth,
dlist[pos].hllabel ? textColor : kColor);
dlist[pos].hllabel ? textColor : kColor);
// Bytes are only editable if they represent code, graphics, or accessible data
// Otherwise, the disassembly should get all remaining space
if(dlist[pos].type & (Device::CODE|Device::GFX|Device::PGFX|
Device::COL|Device::PCOL|Device::BCOL|Device::DATA))
if(dlist[pos].type & (Device::CODE | Device::GFX | Device::PGFX |
Device::COL | Device::PCOL | Device::BCOL | Device::DATA))
{
if(dlist[pos].type == Device::CODE)
{
// Draw mnemonic
s.drawString(_font, dlist[pos].disasm.substr(0, 7), xpos + _labelWidth, ypos,
7 * _fontWidth, textColor);
7 * _fontWidth, textColor);
// Draw operand
if (dlist[pos].disasm.length() > 8)
if(dlist[pos].disasm.length() > 8)
s.drawString(_font, dlist[pos].disasm.substr(8), xpos + _labelWidth + 7 * _fontWidth, ypos,
codeDisasmW - 7 * _fontWidth, textColor);
codeDisasmW - 7 * _fontWidth, textColor);
// Draw cycle count
s.drawString(_font, dlist[pos].ccount, xpos + _labelWidth + codeDisasmW, ypos,
cycleCountW, textColor);
cycleCountW, textColor);
}
else
{
// Draw disassembly only
s.drawString(_font, dlist[pos].disasm, xpos + _labelWidth, ypos,
noCodeDisasmW - 4, kTextColor);
noCodeDisasmW - 4, kTextColor);
}
// Draw separator
@ -542,11 +607,11 @@ void RomListWidget::drawWidget(bool hilite)
// Draw bytes
{
if (_selectedItem == pos && _editMode)
if(_selectedItem == pos && _editMode)
{
adjustOffset();
s.drawString(_font, editString(), _x + r.x(), ypos, r.w(), textColor,
TextAlign::Left, -_editScrollOffset, false);
TextAlign::Left, -_editScrollOffset, false);
drawCaretSelection();
}
@ -560,7 +625,7 @@ void RomListWidget::drawWidget(bool hilite)
{
// Draw disassembly, giving it all remaining horizontal space
s.drawString(_font, dlist[pos].disasm, xpos + _labelWidth, ypos,
noTypeDisasmW, textColor);
noTypeDisasmW, textColor);
}
}
}
@ -580,8 +645,8 @@ Common::Rect RomListWidget::getEditRect() const
{
const int yoffset = std::max(0, (_selectedItem - _currentPos) * _lineHeight);
return Common::Rect(2 + _w - _bytesWidth, 1 + yoffset,
_w, _lineHeight + yoffset);
return Common::Rect(2 + _w - _bytesWidth, 1 + yoffset + 1,
_w, _lineHeight + yoffset + 1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -594,6 +659,7 @@ void RomListWidget::startEditMode()
return;
_editMode = true;
dialog().tooltip().hide();
switch(myDisasm->list[_selectedItem].type)
{
case Device::GFX:

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,30 +60,35 @@ TiaInfoWidget::TiaInfoWidget(GuiObject* boss, const GUI::Font& lfont,
xpos = x;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Frame Cycls" : "F. Cycls");
myFrameCycles = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myFrameCycles->setToolTip("CPU cycles executed this frame.");
myFrameCycles->setEditable(false, true);
// Left: WSync Cycles
ypos += lineHeight + VGAP;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "WSync Cycls" : "WSync C.");
myWSyncCylces = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myWSyncCylces->setToolTip("CPU cycles used for WSYNC this frame.");
myWSyncCylces->setEditable(false, true);
// Left: Timer Cycles
ypos += lineHeight + VGAP;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Timer Cycls" : "Timer C.");
myTimerCylces = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myTimerCylces->setToolTip("CPU cycles roughly used for INTIM reads this frame.");
myTimerCylces->setEditable(false, true);
// Left: Total Cycles
ypos += lineHeight + VGAP;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, "Total");
myTotalCycles = new EditTextWidget(boss, nfont, xpos + lwidth8, ypos - 1, twidth, lineHeight);
myTotalCycles->setToolTip("Total CPU cycles executed for this session (E notation).");
myTotalCycles->setEditable(false, true);
// Left: Delta Cycles
ypos += lineHeight + VGAP;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, "Delta");
myDeltaCycles = new EditTextWidget(boss, nfont, xpos + lwidth8, ypos - 1, twidth, lineHeight);
myDeltaCycles->setToolTip("CPU cycles executed since last debug break.");
myDeltaCycles->setEditable(false, true);
// Right column
@ -93,6 +98,7 @@ TiaInfoWidget::TiaInfoWidget(GuiObject* boss, const GUI::Font& lfont,
// Right: Frame Count
new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Frame Cnt." : "Frame");
myFrameCount = new EditTextWidget(boss, nfont, xpos + lwidthR, ypos - 1, fwidth, lineHeight);
myFrameCount->setToolTip("Total number of frames executed this session.");
myFrameCount->setEditable(false, true);
lwidth = lfont.getStringWidth(longstr ? "Color Clock " : "Pixel Pos ") + LGAP;
@ -102,28 +108,33 @@ TiaInfoWidget::TiaInfoWidget(GuiObject* boss, const GUI::Font& lfont,
ypos += lineHeight + VGAP;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Scanline" : "Scn Ln");
myScanlineCountLast = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myScanlineCountLast->setToolTip("Number of scanlines of last frame.");
myScanlineCountLast->setEditable(false, true);
myScanlineCount = new EditTextWidget(boss, nfont,
xpos + lwidth - myScanlineCountLast->getWidth() - 2, ypos - 1,
fwidth, lineHeight);
myScanlineCount->setToolTip("Current scanline of this frame.");
myScanlineCount->setEditable(false, true);
// Right: Scan Cycle
ypos += lineHeight + VGAP;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Scan Cycle" : "Scn Cycle");
myScanlineCycles = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myScanlineCycles->setToolTip("CPU cycles in current scanline.");
myScanlineCycles->setEditable(false, true);
// Right: Pixel Pos
ypos += lineHeight + VGAP;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, "Pixel Pos");
myPixelPosition = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myPixelPosition->setToolTip("Pixel position in current scanline.");
myPixelPosition->setEditable(false, true);
// Right: Color Clock
ypos += lineHeight + VGAP;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Color Clock" : "Color Clk");
myColorClocks = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myColorClocks->setToolTip("Color clocks in current scanline.");
myColorClocks->setEditable(false, true);
// Calculate actual dimensions

View File

@ -22,6 +22,8 @@
#include "FBSurface.hxx"
#include "Widget.hxx"
#include "GuiObject.hxx"
#include "Dialog.hxx"
#include "ToolTip.hxx"
#include "ContextMenu.hxx"
#include "TiaZoomWidget.hxx"
#include "Debugger.hxx"
@ -55,6 +57,7 @@ TiaOutputWidget::TiaOutputWidget(GuiObject* boss, const GUI::Font& font,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TiaOutputWidget::loadConfig()
{
setEnabled(true);
setDirty();
}
@ -92,10 +95,10 @@ void TiaOutputWidget::saveSnapshot(int execDepth, const string& execPrefix)
message = e.what();
}
if (execDepth == 0) {
instance().frameBuffer().showMessage(message);
instance().frameBuffer().showTextMessage(message);
}
#else
instance().frameBuffer().showMessage("PNG image saving not supported");
instance().frameBuffer().showTextMessage("PNG image saving not supported");
#endif
}
@ -110,6 +113,7 @@ void TiaOutputWidget::handleMouseDown(int x, int y, MouseButton b, int clickCoun
myClickX = x;
myClickY = y;
dialog().tooltip().hide();
// Add menu at current x,y mouse location
myMenu->show(x + getAbsX(), y + getAbsY(), dialog().surface().dstRect());
}
@ -135,7 +139,7 @@ void TiaOutputWidget::handleCommand(CommandSender* sender, int cmd, int data, in
{
command << "scanline #" << lines;
string message = instance().debugger().parser().run(command.str());
instance().frameBuffer().showMessage(message);
instance().frameBuffer().showTextMessage(message);
}
}
else if(rmb == "bp")
@ -144,7 +148,7 @@ void TiaOutputWidget::handleCommand(CommandSender* sender, int cmd, int data, in
int scanline = myClickY + startLine;
command << "breakif _scan==#" << scanline;
string message = instance().debugger().parser().run(command.str());
instance().frameBuffer().showMessage(message);
instance().frameBuffer().showTextMessage(message);
}
else if(rmb == "zoom")
{
@ -158,6 +162,51 @@ void TiaOutputWidget::handleCommand(CommandSender* sender, int cmd, int data, in
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Common::Point TiaOutputWidget::getToolTipIndex(const Common::Point& pos) const
{
const Int32 width = instance().console().tia().width();
const Int32 height = instance().console().tia().height();
const int col = (pos.x - 1 - getAbsX()) >> 1;
const int row = pos.y - 1 - getAbsY();
if(col < 0 || col >= width || row < 0 || row >= height)
return Common::Point(-1, -1);
else
return Common::Point(col, row);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string TiaOutputWidget::getToolTip(const Common::Point& pos) const
{
const Common::Point& idx = getToolTipIndex(pos);
if(idx.x < 0)
return EmptyString;
const uInt32 height = instance().console().tia().height();
// limit to 274 lines (PAL default without scaling)
const uInt32 yStart = height <= FrameManager::Metrics::baseHeightPAL
? 0 : (height - FrameManager::Metrics::baseHeightPAL) >> 1;
const Int32 i = idx.x + (yStart + idx.y) * instance().console().tia().width();
uInt8* tiaOutputBuffer = instance().console().tia().outputBuffer();
ostringstream buf;
buf << _toolTipText
<< "X: #" << idx.x
<< "\nY: #" << idx.y
<< "\nC: $" << Common::Base::toString(tiaOutputBuffer[i], Common::Base::Fmt::_16);
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TiaOutputWidget::changedToolTip(const Common::Point& oldPos,
const Common::Point& newPos) const
{
return getToolTipIndex(oldPos) != getToolTipIndex(newPos);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TiaOutputWidget::drawWidget(bool hilite)
{

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]);
@ -919,10 +919,14 @@ void TiaWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
case kRefP0ID:
tia.refP0(myRefP0->getState() ? 1 : 0);
myGRP0->setIntState(myGRP0->getIntState(), !myRefP0->getState());
myGRP0Old->setIntState(myGRP0Old->getIntState(), !myRefP0->getState());
break;
case kRefP1ID:
tia.refP1(myRefP1->getState() ? 1 : 0);
myGRP1->setIntState(myGRP1->getIntState(), !myRefP1->getState());
myGRP1Old->setIntState(myGRP1Old->getIntState(), !myRefP1->getState());
break;
case kDelP0ID:
@ -1043,8 +1047,8 @@ void TiaWidget::loadConfig()
myGRP0Old->setColor(kBGColorLo);
myGRP0Old->setCrossed(true);
}
myGRP0->setIntState(state.gr[TiaState::P0], false);
myGRP0Old->setIntState(state.gr[TiaState::P0+2], false);
myGRP0->setIntState(state.gr[TiaState::P0], state.ref[TiaState::P0]);
myGRP0Old->setIntState(state.gr[TiaState::P0+2], state.ref[TiaState::P0]);
// posP0
myPosP0->setList(0, state.pos[TiaState::P0],
@ -1079,8 +1083,8 @@ void TiaWidget::loadConfig()
myGRP1Old->setColor(kBGColorLo);
myGRP1Old->setCrossed(true);
}
myGRP1->setIntState(state.gr[TiaState::P1], false);
myGRP1Old->setIntState(state.gr[TiaState::P1+2], false);
myGRP1->setIntState(state.gr[TiaState::P1], state.ref[TiaState::P1]);
myGRP1Old->setIntState(state.gr[TiaState::P1+2], state.ref[TiaState::P1]);
// posP1
myPosP1->setList(0, state.pos[TiaState::P1],

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"
@ -127,6 +129,7 @@ void TiaZoomWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount)
}
else if(b == MouseButton::RIGHT)
{
dialog().tooltip().hide();
// Add menu at current x,y mouse location
myMenu->show(x + getAbsX(), y + getAbsY(), dialog().surface().dstRect());
}
@ -141,6 +144,8 @@ void TiaZoomWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TiaZoomWidget::handleMouseWheel(int x, int y, int direction)
{
dialog().tooltip().hide();
// zoom towards mouse position
myClickX = x;
myClickY = y;
@ -178,19 +183,11 @@ void TiaZoomWidget::handleMouseMoved(int x, int y)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TiaZoomWidget::handleMouseEntered()
{
setFlags(Widget::FLAG_HILITED);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TiaZoomWidget::handleMouseLeft()
{
clearFlags(Widget::FLAG_HILITED);
setDirty();
myMouseMoving = false;
Widget::handleMouseLeft();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -262,7 +259,7 @@ void TiaZoomWidget::handleCommand(CommandSender* sender, int cmd, int data, int
{
command << "scanline #" << lines;
string message = instance().debugger().parser().run(command.str());
instance().frameBuffer().showMessage(message);
instance().frameBuffer().showTextMessage(message);
}
}
else if(rmb == "bp")
@ -271,7 +268,7 @@ void TiaZoomWidget::handleCommand(CommandSender* sender, int cmd, int data, int
int scanline = myClickY / myZoomLevel + myOffY + startLine;
command << "breakif _scan==#" << scanline;
string message = instance().debugger().parser().run(command.str());
instance().frameBuffer().showMessage(message);
instance().frameBuffer().showTextMessage(message);
}
else
{
@ -282,6 +279,47 @@ void TiaZoomWidget::handleCommand(CommandSender* sender, int cmd, int data, int
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Common::Point TiaZoomWidget::getToolTipIndex(const Common::Point& pos) const
{
const Int32 width = instance().console().tia().width() * 2;
const Int32 height = instance().console().tia().height();
const int col = (pos.x - 1 - getAbsX()) / (myZoomLevel << 1) + (myOffX >> 1);
const int row = (pos.y - 1 - getAbsY()) / myZoomLevel + myOffY;
if(col < 0 || col >= width || row < 0 || row >= height)
return Common::Point(-1, -1);
else
return Common::Point(col, row);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string TiaZoomWidget::getToolTip(const Common::Point& pos) const
{
const Common::Point& idx = getToolTipIndex(pos);
if(idx.x < 0)
return EmptyString;
const Int32 i = idx.x + idx.y * instance().console().tia().width();
uInt8* tiaOutputBuffer = instance().console().tia().outputBuffer();
ostringstream buf;
buf << _toolTipText
<< "X: #" << idx.x
<< "\nY: #" << idx.y
<< "\nC: $" << Common::Base::toString(tiaOutputBuffer[i], Common::Base::Fmt::_16);
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TiaZoomWidget::changedToolTip(const Common::Point& oldPos,
const Common::Point& newPos) const
{
return getToolTipIndex(oldPos) != getToolTipIndex(newPos);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TiaZoomWidget::drawWidget(bool hilite)
{

View File

@ -34,8 +34,12 @@ class TiaZoomWidget : public Widget, public CommandSender
void loadConfig() override;
void setPos(int x, int y);
string getToolTip(const Common::Point& pos) const override;
bool changedToolTip(const Common::Point& oldPos, const Common::Point& newPos) const override;
protected:
void handleMouseEntered() override;
bool hasToolTip() const override { return true; }
Common::Point getToolTipIndex(const Common::Point& pos) const;
private:
void zoom(int level);

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)
{
@ -69,6 +78,27 @@ void ToggleBitWidget::setState(const BoolArray& state, const BoolArray& changed)
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string ToggleBitWidget::getToolTip(const Common::Point& pos) const
{
const Common::Point& idx = getToolTipIndex(pos);
if(idx.y < 0)
return EmptyString;
const string tip = ToggleWidget::getToolTip(pos);
if(idx.x < static_cast<int>(_labelList.size()))
{
const string label = _labelList[idx.x];
if(!label.empty())
return tip + "\n" + label;
}
return tip;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ToggleBitWidget::drawWidget(bool hilite)
{

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

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

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

@ -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,45 @@ void FBSurface::frameRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurface::wrapString(const string& inStr, int pos, string& leftStr, string& rightStr) const
void FBSurface::splitString(const GUI::Font& font, const string& s, int w,
string& left, string& right) const
{
for(int i = pos; i > 0; --i)
uInt32 pos;
int w2 = 0;
bool split = false;
// SLOW algorithm to find the acceptable length. But it is good enough for now.
for(pos = 0; pos < s.size(); ++pos)
{
if(isWhiteSpace(inStr[i]))
int charWidth = font.getCharWidth(s[pos]);
if(w2 + charWidth > w || s[pos] == '\n')
{
leftStr = inStr.substr(0, i);
if(inStr[i] == ' ') // skip leading space after line break
i++;
rightStr = inStr.substr(i);
return;
split = true;
break;
}
w2 += charWidth;
}
leftStr = inStr.substr(0, pos);
rightStr = inStr.substr(pos);
if(split)
for(int i = pos; i > 0; --i)
{
if(isWhiteSpace(s[i]))
{
left = s.substr(0, i);
if(s[i] == ' ' || s[pos] == '\n') // skip leading space after line break
i++;
right = s.substr(i);
return;
}
}
left = s.substr(0, pos);
right = s.substr(pos);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBSurface::isWhiteSpace(const char s) const
{
const string WHITESPACES = " ,.;:+-";
const string WHITESPACES = " ,.;:+-*/'([\n";
for(size_t i = 0; i < WHITESPACES.length(); ++i)
if(s == WHITESPACES[i])
@ -331,37 +349,30 @@ int FBSurface::drawString(const GUI::Font& font, const string& s,
ColorId color, TextAlign align,
int deltax, bool useEllipsis, ColorId shadowColor)
{
int lines = 1;
int lines = 0;
#ifdef GUI_SUPPORT
string inStr = s;
// draw multiline string
while (font.getStringWidth(inStr) > w && h >= font.getFontHeight() * 2)
//while (font.getStringWidth(inStr) > w && h >= font.getFontHeight() * 2)
while(inStr.length() && h >= font.getFontHeight() * 2)
{
// String is too wide.
uInt32 i;
string leftStr, rightStr;
int w2 = 0;
// SLOW algorithm to find the acceptable length. But it is good enough for now.
for(i = 0; i < inStr.size(); ++i)
{
int charWidth = font.getCharWidth(inStr[i]);
if(w2 + charWidth > w)
break;
w2 += charWidth;
//str += inStr[i];
}
wrapString(inStr, i, leftStr, rightStr);
splitString(font, inStr, w, leftStr, rightStr);
drawString(font, leftStr, x, y, w, color, align, deltax, false, shadowColor);
h -= font.getFontHeight();
y += font.getFontHeight();
inStr = rightStr;
lines++;
}
drawString(font, inStr, x, y, w, color, align, deltax, useEllipsis, shadowColor);
if(inStr.length())
{
drawString(font, inStr, x, y, w, color, align, deltax, useEllipsis, shadowColor);
lines++;
}
#endif
return lines;
}

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

@ -270,7 +270,7 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type,
#ifdef GUI_SUPPORT
// Erase any messages from a previous run
myMsg.counter = 0;
myMsg.enabled = false;
// Create surfaces for TIA statistics and general messages
const GUI::Font& f = hidpiEnabled() ? infoFont() : font();
@ -311,7 +311,7 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::update(bool force)
void FrameBuffer::update(UpdateMode mode)
{
// Onscreen messages are a special case and require different handling than
// other objects; they aren't UI dialogs in the normal sense nor are they
@ -322,13 +322,14 @@ void FrameBuffer::update(bool force)
// - at the bottom of ::update(), to actually draw them (this must come
// last, since they are always drawn on top of everything else).
// Full rendering is required when messages are enabled
force = force || myMsg.counter >= 0;
bool forceRedraw = mode & UpdateMode::REDRAW;
bool redraw = forceRedraw;
// Detect when a message has been turned off; one last redraw is required
// in this case, to draw over the area that the message occupied
if(myMsg.counter == 0)
myMsg.counter = -1;
// Forced render without draw required if messages or dialogs were closed
// Note: For dialogs only relevant when two or more dialogs were stacked
bool rerender = (mode & (UpdateMode::REDRAW | UpdateMode::RERENDER))
|| myPendingRender;
myPendingRender = false;
switch(myOSystem.eventHandler().state())
{
@ -343,59 +344,69 @@ void FrameBuffer::update(bool force)
if(myPausedCount-- <= 0)
{
myPausedCount = uInt32(7 * myOSystem.frameRate());
showMessage("Paused", MessagePosition::MiddleCenter);
}
if(force)
showTextMessage("Paused", MessagePosition::MiddleCenter);
myTIASurface->render();
}
if(rerender)
myTIASurface->render();
break; // EventHandlerState::PAUSE
}
#ifdef GUI_SUPPORT
case EventHandlerState::OPTIONSMENU:
{
force = force || myOSystem.menu().needsRedraw();
if(force)
myOSystem.menu().tick();
redraw |= myOSystem.menu().needsRedraw();
if(redraw)
{
clear();
myTIASurface->render();
myOSystem.menu().draw(force);
myOSystem.menu().draw(forceRedraw);
}
else if(rerender)
{
clear();
myTIASurface->render();
myOSystem.menu().render();
}
break; // EventHandlerState::OPTIONSMENU
}
case EventHandlerState::CMDMENU:
{
force = force || myOSystem.commandMenu().needsRedraw();
if(force)
myOSystem.commandMenu().tick();
redraw |= myOSystem.commandMenu().needsRedraw();
if(redraw)
{
clear();
myTIASurface->render();
myOSystem.commandMenu().draw(force);
myOSystem.commandMenu().draw(forceRedraw);
}
break; // EventHandlerState::CMDMENU
}
case EventHandlerState::MESSAGEMENU:
{
force = force || myOSystem.messageMenu().needsRedraw();
if (force)
myOSystem.messageMenu().tick();
redraw |= myOSystem.messageMenu().needsRedraw();
if(redraw)
{
clear();
myTIASurface->render();
myOSystem.messageMenu().draw(force);
myOSystem.messageMenu().draw(forceRedraw);
}
break; // EventHandlerState::MESSAGEMENU
}
case EventHandlerState::TIMEMACHINE:
{
force = force || myOSystem.timeMachine().needsRedraw();
if(force)
myOSystem.timeMachine().tick();
redraw |= myOSystem.timeMachine().needsRedraw();
if(redraw)
{
clear();
myTIASurface->render();
myOSystem.timeMachine().draw(force);
myOSystem.timeMachine().draw(forceRedraw);
}
break; // EventHandlerState::TIMEMACHINE
}
@ -420,13 +431,13 @@ void FrameBuffer::update(bool force)
r.rewindStates(1);
}
force = force || success;
if (force)
redraw |= success;
if(redraw)
myTIASurface->render();
// Stop playback mode at the end of the state buffer
// and switch to Time Machine or Pause mode
if (!success)
if(!success)
{
frames = 0;
myOSystem.eventHandler().enterMenuMode(EventHandlerState::TIMEMACHINE);
@ -436,12 +447,12 @@ void FrameBuffer::update(bool force)
case EventHandlerState::LAUNCHER:
{
force = force || myOSystem.launcher().needsRedraw();
if(force)
{
clear();
myOSystem.launcher().draw(force);
}
myOSystem.launcher().tick();
redraw |= myOSystem.launcher().needsRedraw();
if(redraw)
myOSystem.launcher().draw(forceRedraw);
else if(rerender)
myOSystem.launcher().render();
break; // EventHandlerState::LAUNCHER
}
#endif
@ -449,12 +460,12 @@ void FrameBuffer::update(bool force)
#ifdef DEBUGGER_SUPPORT
case EventHandlerState::DEBUGGER:
{
force = force || myOSystem.debugger().needsRedraw();
if(force)
{
clear();
myOSystem.debugger().draw(force);
}
myOSystem.debugger().tick();
redraw |= myOSystem.debugger().needsRedraw();
if(redraw)
myOSystem.debugger().draw(forceRedraw);
else if(rerender)
myOSystem.debugger().render();
break; // EventHandlerState::DEBUGGER
}
#endif
@ -468,10 +479,10 @@ void FrameBuffer::update(bool force)
// indicates that, and then the code at the top of this method sees
// the change and redraws everything
if(myMsg.enabled)
drawMessage();
redraw |= drawMessage();
// Push buffers to screen only when necessary
if(force)
if(redraw || rerender)
myBackend->renderToScreen();
}
@ -501,19 +512,15 @@ void FrameBuffer::updateInEmulationMode(float framesPerSecond)
myBackend->renderToScreen();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::showMessage(const string& message, MessagePosition position,
bool force)
{
#ifdef GUI_SUPPORT
void FrameBuffer::createMessage(const string& message, MessagePosition position, bool force)
{
// Only show messages if they've been enabled
if(myMsg.surface == nullptr || !(force || myOSystem.settings().getBool("uimessages")))
return;
const int fontWidth = font().getMaxCharWidth(),
fontHeight = font().getFontHeight();
const int fontHeight = font().getFontHeight();
const int VBORDER = fontHeight / 4;
const int HBORDER = fontWidth * 1.25 / 2.0;
myMsg.counter = uInt32(myOSystem.frameRate()) * 2; // Show message for 2 seconds
if(myMsg.counter == 0)
@ -522,39 +529,40 @@ void FrameBuffer::showMessage(const string& message, MessagePosition position,
// Precompute the message coordinates
myMsg.text = message;
myMsg.color = kBtnTextColor;
myMsg.showGauge = false;
myMsg.w = std::min(fontWidth * (MESSAGE_WIDTH) - HBORDER * 2,
font().getStringWidth(myMsg.text) + HBORDER * 2);
myMsg.h = fontHeight + VBORDER * 2;
myMsg.position = position;
myMsg.enabled = true;
myMsg.dirty = true;
myMsg.surface->setSrcSize(myMsg.w, myMsg.h);
myMsg.surface->setDstSize(myMsg.w * hidpiScaleFactor(), myMsg.h * hidpiScaleFactor());
}
#endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::showTextMessage(const string& message, MessagePosition position,
bool force)
{
#ifdef GUI_SUPPORT
const int fontWidth = font().getMaxCharWidth();
const int HBORDER = fontWidth * 1.25 / 2.0;
myMsg.showGauge = false;
myMsg.w = std::min(fontWidth * (MESSAGE_WIDTH) - HBORDER * 2,
font().getStringWidth(message) + HBORDER * 2);
createMessage(message, position, force);
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::showMessage(const string& message, const string& valueText,
float value, float minValue, float maxValue)
void FrameBuffer::showGaugeMessage(const string& message, const string& valueText,
float value, float minValue, float maxValue)
{
#ifdef GUI_SUPPORT
// Only show messages if they've been enabled
if(myMsg.surface == nullptr || !myOSystem.settings().getBool("uimessages"))
return;
const int fontWidth = font().getMaxCharWidth(),
fontHeight = font().getFontHeight();
const int VBORDER = fontHeight / 4;
const int fontWidth = font().getMaxCharWidth();
const int HBORDER = fontWidth * 1.25 / 2.0;
myMsg.counter = uInt32(myOSystem.frameRate()) * 2; // Show message for 2 seconds
if(myMsg.counter == 0)
myMsg.counter = 120;
// Precompute the message coordinates
myMsg.text = message;
myMsg.color = kBtnTextColor;
myMsg.showGauge = true;
if(maxValue - minValue != 0)
myMsg.value = (value - minValue) / (maxValue - minValue) * 100.F;
@ -562,16 +570,12 @@ void FrameBuffer::showMessage(const string& message, const string& valueText,
myMsg.value = 100.F;
myMsg.valueText = valueText;
myMsg.w = std::min(fontWidth * MESSAGE_WIDTH,
font().getStringWidth(myMsg.text)
font().getStringWidth(message)
+ fontWidth * (GAUGEBAR_WIDTH + 2)
+ font().getStringWidth(myMsg.valueText))
+ HBORDER * 2;
myMsg.h = fontHeight + VBORDER * 2;
myMsg.position = MessagePosition::BottomCenter;
myMsg.enabled = true;
+ font().getStringWidth(valueText))
+ HBORDER * 2;
myMsg.surface->setSrcSize(myMsg.w, myMsg.h);
myMsg.surface->setDstSize(myMsg.w * hidpiScaleFactor(), myMsg.h * hidpiScaleFactor());
createMessage(message, MessagePosition::BottomCenter);
#endif
}
@ -652,8 +656,8 @@ void FrameBuffer::toggleFrameStats(bool toggle)
myOSystem.settings().setValue(
myOSystem.settings().getBool("dev.settings") ? "dev.stats" : "plr.stats", myStatsEnabled);
myOSystem.frameBuffer().showMessage(string("Console info ") +
(myStatsEnabled ? "enabled" : "disabled"));
myOSystem.frameBuffer().showTextMessage(string("Console info ") +
(myStatsEnabled ? "enabled" : "disabled"));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -676,12 +680,19 @@ void FrameBuffer::enableMessages(bool enable)
myStatsMsg.enabled = false;
// Erase old messages on the screen
myMsg.enabled = false;
myMsg.counter = 0;
update(true); // Force update immediately
hideMessage();
update(); // update immediately
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::hideMessage()
{
myPendingRender = myMsg.enabled;
myMsg.enabled = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline bool FrameBuffer::drawMessage()
{
@ -690,115 +701,119 @@ inline bool FrameBuffer::drawMessage()
// or show again this frame
if(myMsg.counter == 0)
{
myMsg.enabled = false;
return true;
}
else if(myMsg.counter < 0)
{
myMsg.enabled = false;
hideMessage();
return false;
}
// Draw the bounded box and text
const Common::Rect& dst = myMsg.surface->dstRect();
const int fontWidth = font().getMaxCharWidth(),
fontHeight = font().getFontHeight();
const int VBORDER = fontHeight / 4;
const int HBORDER = fontWidth * 1.25 / 2.0;
constexpr int BORDER = 1;
switch(myMsg.position)
if(myMsg.dirty)
{
case MessagePosition::TopLeft:
myMsg.x = 5;
myMsg.y = 5;
break;
cerr << "--- draw message ---" << endl;
case MessagePosition::TopCenter:
myMsg.x = (imageRect().w() - dst.w()) >> 1;
myMsg.y = 5;
break;
// Draw the bounded box and text
const Common::Rect& dst = myMsg.surface->dstRect();
const int fontWidth = font().getMaxCharWidth(),
fontHeight = font().getFontHeight();
const int VBORDER = fontHeight / 4;
const int HBORDER = fontWidth * 1.25 / 2.0;
constexpr int BORDER = 1;
case MessagePosition::TopRight:
myMsg.x = imageRect().w() - dst.w() - 5;
myMsg.y = 5;
break;
case MessagePosition::MiddleLeft:
myMsg.x = 5;
myMsg.y = (imageRect().h() - dst.h()) >> 1;
break;
case MessagePosition::MiddleCenter:
myMsg.x = (imageRect().w() - dst.w()) >> 1;
myMsg.y = (imageRect().h() - dst.h()) >> 1;
break;
case MessagePosition::MiddleRight:
myMsg.x = imageRect().w() - dst.w() - 5;
myMsg.y = (imageRect().h() - dst.h()) >> 1;
break;
case MessagePosition::BottomLeft:
myMsg.x = 5;
myMsg.y = imageRect().h() - dst.h() - 5;
break;
case MessagePosition::BottomCenter:
myMsg.x = (imageRect().w() - dst.w()) >> 1;
myMsg.y = imageRect().h() - dst.h() - 5;
break;
case MessagePosition::BottomRight:
myMsg.x = imageRect().w() - dst.w() - 5;
myMsg.y = imageRect().h() - dst.h() - 5;
break;
}
myMsg.surface->setDstPos(myMsg.x + imageRect().x(), myMsg.y + imageRect().y());
myMsg.surface->fillRect(0, 0, myMsg.w, myMsg.h, kColor);
myMsg.surface->fillRect(BORDER, BORDER, myMsg.w - BORDER * 2, myMsg.h - BORDER * 2, kBtnColor);
myMsg.surface->drawString(font(), myMsg.text, HBORDER, VBORDER,
myMsg.w, myMsg.color);
if(myMsg.showGauge)
{
constexpr int NUM_TICKMARKS = 4;
// limit gauge bar width if texts are too long
const int swidth = std::min(fontWidth * GAUGEBAR_WIDTH,
fontWidth * (MESSAGE_WIDTH - 2)
- font().getStringWidth(myMsg.text)
- font().getStringWidth(myMsg.valueText));
const int bwidth = swidth * myMsg.value / 100.F;
const int bheight = fontHeight >> 1;
const int x = HBORDER + font().getStringWidth(myMsg.text) + fontWidth;
// align bar with bottom of text
const int y = VBORDER + font().desc().ascent - bheight;
// draw gauge bar
myMsg.surface->fillRect(x - BORDER, y, swidth + BORDER * 2, bheight, kSliderBGColor);
myMsg.surface->fillRect(x, y + BORDER, bwidth, bheight - BORDER * 2, kSliderColor);
// draw tickmark in the middle of the bar
for(int i = 1; i < NUM_TICKMARKS; ++i)
switch(myMsg.position)
{
ColorId color;
int xt = x + swidth * i / NUM_TICKMARKS;
if(bwidth < xt - x)
color = kCheckColor; // kSliderColor;
else
color = kSliderBGColor;
myMsg.surface->vLine(xt, y + bheight / 2, y + bheight - 1, color);
case MessagePosition::TopLeft:
myMsg.x = 5;
myMsg.y = 5;
break;
case MessagePosition::TopCenter:
myMsg.x = (imageRect().w() - dst.w()) >> 1;
myMsg.y = 5;
break;
case MessagePosition::TopRight:
myMsg.x = imageRect().w() - dst.w() - 5;
myMsg.y = 5;
break;
case MessagePosition::MiddleLeft:
myMsg.x = 5;
myMsg.y = (imageRect().h() - dst.h()) >> 1;
break;
case MessagePosition::MiddleCenter:
myMsg.x = (imageRect().w() - dst.w()) >> 1;
myMsg.y = (imageRect().h() - dst.h()) >> 1;
break;
case MessagePosition::MiddleRight:
myMsg.x = imageRect().w() - dst.w() - 5;
myMsg.y = (imageRect().h() - dst.h()) >> 1;
break;
case MessagePosition::BottomLeft:
myMsg.x = 5;
myMsg.y = imageRect().h() - dst.h() - 5;
break;
case MessagePosition::BottomCenter:
myMsg.x = (imageRect().w() - dst.w()) >> 1;
myMsg.y = imageRect().h() - dst.h() - 5;
break;
case MessagePosition::BottomRight:
myMsg.x = imageRect().w() - dst.w() - 5;
myMsg.y = imageRect().h() - dst.h() - 5;
break;
}
// draw value text
myMsg.surface->drawString(font(), myMsg.valueText,
x + swidth + fontWidth, VBORDER,
myMsg.surface->setDstPos(myMsg.x + imageRect().x(), myMsg.y + imageRect().y());
myMsg.surface->fillRect(0, 0, myMsg.w, myMsg.h, kColor);
myMsg.surface->fillRect(BORDER, BORDER, myMsg.w - BORDER * 2, myMsg.h - BORDER * 2, kBtnColor);
myMsg.surface->drawString(font(), myMsg.text, HBORDER, VBORDER,
myMsg.w, myMsg.color);
if(myMsg.showGauge)
{
constexpr int NUM_TICKMARKS = 4;
// limit gauge bar width if texts are too long
const int swidth = std::min(fontWidth * GAUGEBAR_WIDTH,
fontWidth * (MESSAGE_WIDTH - 2)
- font().getStringWidth(myMsg.text)
- font().getStringWidth(myMsg.valueText));
const int bwidth = swidth * myMsg.value / 100.F;
const int bheight = fontHeight >> 1;
const int x = HBORDER + font().getStringWidth(myMsg.text) + fontWidth;
// align bar with bottom of text
const int y = VBORDER + font().desc().ascent - bheight;
// draw gauge bar
myMsg.surface->fillRect(x - BORDER, y, swidth + BORDER * 2, bheight, kSliderBGColor);
myMsg.surface->fillRect(x, y + BORDER, bwidth, bheight - BORDER * 2, kSliderColor);
// draw tickmark in the middle of the bar
for(int i = 1; i < NUM_TICKMARKS; ++i)
{
ColorId color;
int xt = x + swidth * i / NUM_TICKMARKS;
if(bwidth < xt - x)
color = kCheckColor; // kSliderColor;
else
color = kSliderBGColor;
myMsg.surface->vLine(xt, y + bheight / 2, y + bheight - 1, color);
}
// draw value text
myMsg.surface->drawString(font(), myMsg.valueText,
x + swidth + fontWidth, VBORDER,
myMsg.w, myMsg.color);
}
myMsg.dirty = false;
myMsg.surface->render();
return true;
}
myMsg.surface->render();
myMsg.counter--;
myMsg.surface->render();
#endif
return true;
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -845,7 +860,7 @@ void FrameBuffer::resetSurfaces()
freeSurfaces();
reloadSurfaces();
update(true); // force full update
update(UpdateMode::REDRAW); // force full update
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -898,10 +913,9 @@ void FrameBuffer::setUIPalette()
void FrameBuffer::stateChanged(EventHandlerState state)
{
// Make sure any onscreen messages are removed
myMsg.enabled = false;
myMsg.counter = 0;
hideMessage();
update(true); // force full update
update(); // update immediately
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -916,10 +930,10 @@ string FrameBuffer::getDisplayKey()
case BufferType::Emulator:
return "display";
#ifdef DEBUGGER_SUPPORT
#ifdef DEBUGGER_SUPPORT
case BufferType::Debugger:
return "dbg.display";
#endif
#endif
default:
return "";
@ -938,10 +952,10 @@ string FrameBuffer::getPositionKey()
case BufferType::Emulator:
return "windowedpos";
#ifdef DEBUGGER_SUPPORT
#ifdef DEBUGGER_SUPPORT
case BufferType::Debugger:
return "dbg.pos";
#endif
#endif
default:
return "";
@ -952,10 +966,10 @@ string FrameBuffer::getPositionKey()
void FrameBuffer::saveCurrentWindowPosition()
{
myOSystem.settings().setValue(
getDisplayKey(), myBackend->getCurrentDisplayIndex());
getDisplayKey(), myBackend->getCurrentDisplayIndex());
if(myBackend->isCurrentWindowPositioned())
myOSystem.settings().setValue(
getPositionKey(), myBackend->getCurrentWindowPos());
getPositionKey(), myBackend->getCurrentWindowPos());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -990,7 +1004,9 @@ void FrameBuffer::setFullscreen(bool enable)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::toggleFullscreen(bool toggle)
{
switch(myOSystem.eventHandler().state())
EventHandlerState state = myOSystem.eventHandler().state();
switch(state)
{
case EventHandlerState::LAUNCHER:
case EventHandlerState::EMULATION:
@ -1000,17 +1016,27 @@ void FrameBuffer::toggleFullscreen(bool toggle)
const bool isFullscreen = toggle ? !fullScreen() : fullScreen();
setFullscreen(isFullscreen);
if(myBufferType != BufferType::Launcher)
if(state != EventHandlerState::LAUNCHER)
{
ostringstream msg;
msg << "Fullscreen ";
if(isFullscreen)
msg << "enabled (" << myBackend->refreshRate() << " Hz, ";
else
msg << "disabled (";
msg << "Zoom " << myActiveVidMode.zoom * 100 << "%)";
showMessage(msg.str());
if(state != EventHandlerState::DEBUGGER)
{
if(isFullscreen)
msg << "enabled (" << myBackend->refreshRate() << " Hz, ";
else
msg << "disabled (";
msg << "Zoom " << myActiveVidMode.zoom * 100 << "%)";
}
else
{
if(isFullscreen)
msg << "enabled";
else
msg << "disabled";
}
showTextMessage(msg.str());
}
break;
}
@ -1043,7 +1069,7 @@ void FrameBuffer::toggleAdaptRefresh(bool toggle)
msg << (isAdaptRefresh ? "enabled" : "disabled");
msg << " (" << myBackend->refreshRate() << " Hz)";
showMessage(msg.str());
showTextMessage(msg.str());
}
}
#endif
@ -1069,7 +1095,7 @@ void FrameBuffer::changeOverscan(int direction)
val << (overscan > 0 ? "+" : "" ) << overscan << "%";
else
val << "Off";
myOSystem.frameBuffer().showMessage("Overscan", val.str(), overscan, 0, 10);
myOSystem.frameBuffer().showGaugeMessage("Overscan", val.str(), overscan, 0, 10);
}
}
@ -1106,9 +1132,9 @@ void FrameBuffer::switchVideoMode(int direction)
if(applyVideoMode() == FBInitStatus::Success)
{
if(fullScreen())
showMessage(myActiveVidMode.description);
showTextMessage(myActiveVidMode.description);
else
showMessage("Zoom", myActiveVidMode.description, myActiveVidMode.zoom,
showGaugeMessage("Zoom", myActiveVidMode.description, myActiveVidMode.zoom,
supportedTIAMinZoom(), myTIAMaxZoom);
}
}
@ -1163,6 +1189,7 @@ FBInitStatus FrameBuffer::applyVideoMode()
resetSurfaces();
setCursorState();
myPendingRender = true;
}
else
Logger::error("ERROR: Couldn't initialize video subsystem");
@ -1248,9 +1275,9 @@ void FrameBuffer::toggleGrabMouse()
myGrabMouse = !myGrabMouse;
setCursorState();
myOSystem.settings().setValue("grabmouse", myGrabMouse);
myOSystem.frameBuffer().showMessage(oldState != myGrabMouse ? myGrabMouse
? "Grab mouse enabled" : "Grab mouse disabled"
: "Grab mouse not allowed while cursor shown");
myOSystem.frameBuffer().showTextMessage(oldState != myGrabMouse ? myGrabMouse
? "Grab mouse enabled" : "Grab mouse disabled"
: "Grab mouse not allowed while cursor shown");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1299,8 +1326,6 @@ void FrameBuffer::toggleGrabMouse()
kColorInfo TIA output position color
kColorTitleBar Title bar color
kColorTitleText Title text color
kColorTitleBarLo Disabled title bar color
kColorTitleTextLo Disabled title text color
*/
UIPaletteArray FrameBuffer::ourStandardUIPalette = {
{ 0x686868, 0x000000, 0xa38c61, 0xdccfa5, 0x404040, // base
@ -1311,7 +1336,7 @@ UIPaletteArray FrameBuffer::ourStandardUIPalette = {
0xac3410, 0xd55941, // scrollbar
0xc80000, 0xffff80, 0xc8c8ff, 0xc80000, // debugger
0xac3410, 0xd55941, 0xdccfa5, 0xf0f0cf, 0xa38c61, // slider
0xffffff, 0xac3410, 0xf0f0cf, 0x686868, 0xdccfa5 // other
0xffffff, 0xac3410, 0xf0f0cf // other
}
};
@ -1324,7 +1349,7 @@ UIPaletteArray FrameBuffer::ourClassicUIPalette = {
0x20a020, 0x00ff00, // scrollbar
0xc80000, 0x00ff00, 0xc8c8ff, 0xc80000, // debugger
0x20a020, 0x00ff00, 0x404040, 0x686868, 0x404040, // slider
0x00ff00, 0x20a020, 0x000000, 0x686868, 0x404040 // other
0x00ff00, 0x20a020, 0x000000 // other
}
};
@ -1337,7 +1362,7 @@ UIPaletteArray FrameBuffer::ourLightUIPalette = {
0xc0c0c0, 0x808080, // scrollbar
0xffc0c0, 0x000000, 0xe00000, 0xc00000, // debugger
0x333333, 0x0078d7, 0xc0c0c0, 0xffffff, 0xc0c0c0, // slider 0xBDDEF9| 0xe1e1e1 | 0xffffff
0xffffff, 0x333333, 0xf0f0f0, 0x808080, 0xc0c0c0 // other
0xffffff, 0x333333, 0xf0f0f0 // other
}
};
@ -1350,6 +1375,6 @@ UIPaletteArray FrameBuffer::ourDarkUIPalette = {
0x3c3c3c, 0x646464, // scrollbar
0x7f2020, 0xc0c0c0, 0xe00000, 0xc00000, // debugger
0x989898, 0x0059a3, 0x3c3c3c, 0x000000, 0x3c3c3c, // slider
0x000000, 0x989898, 0x202020, 0x646464, 0x3c3c3c // other
0x000000, 0x989898, 0x202020 // other
}
};

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

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

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

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

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

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

@ -71,8 +71,8 @@ class ContextMenu : public Dialog, public CommandSender
const string& getSelectedName() const;
const Variant& getSelectedTag() const;
/** This dialog uses its own positioning, so we override Dialog::center() */
void center() override;
/** This dialog uses its own positioning, so we override Dialog::setPosition() */
void setPosition() override;
/** The following methods are used when we want to select *and*
send a command for the new selection. They are only to be used

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,50 @@ void Dialog::positionAt(uInt32 pos)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Dialog::render()
void Dialog::redraw(bool force)
{
if(!_dirty || !isVisible())
return false;
if(!isVisible())
return;
if(force)
setDirty();
// Draw this dialog
center();
setPosition();
drawDialog();
// full rendering is caused in dialog container
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::render()
{
cerr << " render " << typeid(*this).name() << endl;
// Update dialog surface; also render any extra surfaces
// Extra surfaces must be rendered afterwards, so they are drawn on top
if(_surface->render())
{
mySurfaceStack.applyAll([](shared_ptr<FBSurface>& surface){
mySurfaceStack.applyAll([](shared_ptr<FBSurface>& surface) {
surface->render();
});
}
_dirty = false;
return true;
// Dialog is still on top if e.g a dialog without title is opened
// (e.g. ContextMenu)
bool onTop = parent().myDialogStack.top() == this
|| (parent().myDialogStack.get(parent().myDialogStack.size() - 2) == this
&& !parent().myDialogStack.top()->hasTitle());
//&& typeid(*parent().myDialogStack.top()) == typeid(ContextMenu))
if(!onTop)
{
cerr << " shade " << typeid(*this).name() << endl;
_shadeSurface->setDstRect(_surface->dstRect());
_shadeSurface->render();
}
_toolTip->render();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -305,7 +366,7 @@ void Dialog::setFocus(Widget* w)
{
// If the click occured inside a widget which is not the currently
// focused one, change the focus to that widget.
if(w && w != _focusedWidget && w->wantsFocus())
if(w && w != _focusedWidget && w->wantsFocus() && w->isEnabled())
{
// Redraw widgets for new focus
_focusedWidget = Widget::setFocusForChain(this, getFocusList(), w, 0);
@ -371,39 +432,43 @@ void Dialog::drawDialog()
FBSurface& s = surface();
// Dialog is still on top if e.g a ContextMenu is opened
_onTop = parent().myDialogStack.top() == this
|| (parent().myDialogStack.get(parent().myDialogStack.size() - 2) == this
&& !parent().myDialogStack.top()->hasTitle());
if(_flags & Widget::FLAG_CLEARBG)
if(isDirty())
{
// cerr << "Dialog::drawDialog(): w = " << _w << ", h = " << _h << " @ " << &s << endl << endl;
s.fillRect(_x, _y + _th, _w, _h - _th, _onTop ? kDlgColor : kBGColorLo);
if(_th)
cerr << endl << "d";
//cerr << "*** draw dialog " << typeid(*this).name() << " ***" << endl;
if(clearsBackground())
{
s.fillRect(_x, _y, _w, _th, _onTop ? kColorTitleBar : kColorTitleBarLo);
s.drawString(_font, _title, _x + _font.getMaxCharWidth() * 1.25, _y + _font.getFontHeight() / 6,
_font.getStringWidth(_title),
_onTop ? kColorTitleText : kColorTitleTextLo);
// cerr << "Dialog::drawDialog(): w = " << _w << ", h = " << _h << " @ " << &s << endl << endl;
if(hasBackground())
s.fillRect(_x, _y + _th, _w, _h - _th, kDlgColor);
else
s.invalidateRect(_x, _y + _th, _w, _h - _th);
if(_th)
{
s.fillRect(_x, _y, _w, _th, kColorTitleBar);
s.drawString(_font, _title, _x + _font.getMaxCharWidth() * 1.25, _y + _font.getFontHeight() / 6,
_font.getStringWidth(_title), kColorTitleText);
}
}
else {
s.invalidate();
//cerr << "invalidate " << typeid(*this).name() << endl;
}
if(hasBorder()) // currently only used by Dialog itself
s.frameRect(_x, _y, _w, _h, kColor);
// Make all child widgets dirty
Widget::setDirtyInChain(_firstWidget);
clearDirty();
}
else
s.invalidate();
if(_flags & Widget::FLAG_BORDER) // currently only used by Dialog itself
s.frameRect(_x, _y, _w, _h, _onTop ? kColor : kShadowColor);
// Make all child widget dirty
Widget* w = _firstWidget;
Widget::setDirtyInChain(w);
cerr << endl;
// Draw all children
w = _firstWidget;
while(w)
{
w->draw();
w = w->_next;
}
drawChain();
// Draw outlines for focused widgets
// Don't change focus, since this will trigger lost and received
@ -411,9 +476,26 @@ void Dialog::drawDialog()
if(_focusedWidget)
{
_focusedWidget = Widget::setFocusForChain(this, getFocusList(),
_focusedWidget, 0, false);
if(_focusedWidget)
_focusedWidget->draw(); // make sure the highlight color is drawn initially
_focusedWidget, 0, false);
//if(_focusedWidget)
// _focusedWidget->draw(); // make sure the highlight color is drawn initially
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::drawChain()
{
// Clear chain *before* drawing, because some widgets may set it again when
// being drawn (e.g. RomListWidget)
clearDirtyChain();
Widget* w = _firstWidget;
while(w)
{
if(w->needsRedraw())
w->draw();
w = w->_next;
}
}
@ -556,6 +638,9 @@ void Dialog::handleMouseMoved(int x, int y)
if (w && (w->getFlags() & Widget::FLAG_TRACK_MOUSE))
w->handleMouseMoved(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y));
// Update mouse coordinates for tooltips
_toolTip->update(_mouseWidget, Common::Point(x, y));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

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,21 @@ class Dialog : public GuiObject
void close();
bool isVisible() const override { return _visible; }
bool isOnTop() const { return _onTop; }
bool isOnTop() const { return true; } // TODO: remove
virtual void center();
virtual void setPosition();
virtual void drawDialog();
virtual void loadConfig() { }
virtual void saveConfig() { }
virtual void setDefaults() { }
// A dialog being dirty indicates that its underlying surface needs to be
// redrawn and then re-rendered; this is taken care of in ::render()
void setDirty() override { _dirty = true; }
bool isDirty() const { return _dirty; }
bool render();
void setDirty() override;
void setDirtyChain() override;
void redraw(bool force = false);
void drawChain() override;
void render();
void tick() override;
void addFocusWidget(Widget* w) override;
void addToFocusList(WidgetArray& list) override;
@ -89,10 +92,6 @@ class Dialog : public GuiObject
*/
void addSurface(const shared_ptr<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(); }
@ -124,6 +123,8 @@ class Dialog : public GuiObject
*/
bool shouldResize(uInt32& w, uInt32& h) const;
ToolTip& tooltip() { return *_toolTip; }
protected:
void draw() override { }
void releaseFocus() override;
@ -197,11 +198,11 @@ class Dialog : public GuiObject
Widget* _cancelWidget{nullptr};
bool _visible{false};
bool _onTop{true};
bool _processCancel{false};
string _title;
int _th{0};
int _layer{0};
unique_ptr<ToolTip> _toolTip;
Common::FixedStack<shared_ptr<FBSurface>> mySurfaceStack;
@ -232,10 +233,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

@ -89,31 +89,57 @@ void DialogContainer::updateTime(uInt64 time)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool DialogContainer::draw(bool full)
void DialogContainer::draw(bool full)
{
if(myDialogStack.empty())
return false;
return;
cerr << "draw " << full << " " << typeid(*this).name() << endl;
// Make the top dialog dirty if a full redraw is requested
if(full)
myDialogStack.top()->setDirty();
//if(full)
// myDialogStack.top()->setDirty();
// If the top dialog is dirty, then all below it must be redrawn too
const bool dirty = needsRedraw();
myDialogStack.applyAll([&](Dialog*& d){
if(dirty)
d->setDirty();
full |= d->render();
// Draw and render all dirty dialogs
myDialogStack.applyAll([&](Dialog*& d) {
if(full || d->needsRedraw())
d->redraw(full);
});
// Always render all surfaces, bottom to top
render();
}
return full;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DialogContainer::tick()
{
if(!myDialogStack.empty())
myDialogStack.top()->tick();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DialogContainer::render()
{
if(myDialogStack.empty())
return;
cerr << "full re-render " << typeid(*this).name() << endl;
// Make sure we start in a clean state (with zero'ed buffers)
if(!myOSystem.eventHandler().inTIAMode())
myOSystem.frameBuffer().clear();
// Render all dialogs
myDialogStack.applyAll([&](Dialog*& d) {
d->render();
});
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool DialogContainer::needsRedraw() const
{
return !myDialogStack.empty() ? myDialogStack.top()->isDirty() : false;
return !myDialogStack.empty()
? myDialogStack.top()->needsRedraw()
: false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -129,8 +155,8 @@ int DialogContainer::addDialog(Dialog* d)
const uInt32 scale = myOSystem.frameBuffer().hidpiScaleFactor();
if(uInt32(d->getWidth() * scale) > r.w() || uInt32(d->getHeight() * scale) > r.h())
myOSystem.frameBuffer().showMessage(
"Unable to show dialog box; FIX THE CODE");
myOSystem.frameBuffer().showTextMessage(
"Unable to show dialog box; FIX THE CODE", MessagePosition::BottomCenter, true);
else
{
d->setDirty();
@ -144,9 +170,11 @@ void DialogContainer::removeDialog()
{
if(!myDialogStack.empty())
{
cerr << "remove dialog " << typeid(*myDialogStack.top()).name() << endl;
myDialogStack.pop();
if(!myDialogStack.empty())
myDialogStack.top()->setDirty();
// Inform the frame buffer that it has to render all surfaces
myOSystem.frameBuffer().setPendingRender();
}
}
@ -157,6 +185,9 @@ void DialogContainer::reStack()
while(!myDialogStack.empty())
myDialogStack.top()->close();
// Make sure that all surfaces are cleared
myOSystem.frameBuffer().clear();
baseDialog()->open();
// Reset all continuous events

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"
@ -48,20 +49,6 @@ void EditTextWidget::setText(const string& str, bool changed)
_changed = changed;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EditTextWidget::handleMouseEntered()
{
setFlags(Widget::FLAG_HILITED);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EditTextWidget::handleMouseLeft()
{
clearFlags(Widget::FLAG_HILITED);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EditTextWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount)
{

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"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -63,6 +64,28 @@ void EditableWidget::setText(const string& str, bool)
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EditableWidget::tick()
{
if(_hasFocus && isEditable() && _editMode && isVisible() && _boss->isVisible())
{
_caretTimer++;
if(_caretTimer > 40) // switch every 2/3rd seconds
{
_caretTimer = 0;
_caretEnabled = !_caretEnabled;
setDirty();
}
}
Widget::tick();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool EditableWidget::wantsToolTip() const
{
return !(_hasFocus && isEditable() && _editMode) && Widget::wantsToolTip();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EditableWidget::setEditable(bool editable, bool hiliteBG)
{
@ -79,6 +102,14 @@ void EditableWidget::setEditable(bool editable, bool hiliteBG)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EditableWidget::receivedFocusWidget()
{
_caretTimer = 0;
_caretEnabled = true;
dialog().tooltip().hide();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EditableWidget::lostFocusWidget()
{
@ -107,7 +138,7 @@ bool EditableWidget::handleText(char text)
if(tryInsertChar(text, _caretPos))
{
_caretPos++;
setCaretPos(_caretPos + 1);
sendCommand(EditableWidget::kChangedCmd, 0, _id);
setDirty();
return true;
@ -265,7 +296,7 @@ bool EditableWidget::handleKeyDown(StellaKey key, StellaMod mod)
{
// Put caret at last difference
myUndoHandler->lastDiff(_editString, oldString);
_caretPos = myUndoHandler->lastDiff(_editString, oldString);
setCaretPos(myUndoHandler->lastDiff(_editString, oldString));
_selectSize = 0;
sendCommand(EditableWidget::kChangedCmd, key, _id);
}
@ -316,22 +347,16 @@ void EditableWidget::drawCaretSelection()
if (!_editable || !isVisible() || !_boss->isVisible() || !_hasFocus)
return;
const Common::Rect& editRect = getEditRect();
int x = editRect.x();
int y = editRect.y();
x += getCaretOffset();
x += _x;
y += _y;
FBSurface& s = _boss->dialog().surface();
s.vLine(x, y + 2, y + editRect.h() - 2, kTextColorHi);
s.vLine(x-1, y + 2, y + editRect.h() - 2, kTextColorHi);
// Draw the selection
if(_selectSize)
{
FBSurface& s = _boss->dialog().surface();
const Common::Rect& editRect = getEditRect();
int x = editRect.x();
int y = editRect.y();
string text = selectString();
x = editRect.x();
y = editRect.y();
int w = editRect.w();
@ -355,9 +380,27 @@ void EditableWidget::drawCaretSelection()
y += _y;
s.fillRect(x - 1, y + 1, w + 1, h - 3, kTextColorHi);
s.drawString(_font, text, x, y + 1, w, h,
s.drawString(_font, text, x, y + 1 + _dyText, w, h,
kTextColorInv, TextAlign::Left, 0, false);
}
// Draw the caret
if(_caretEnabled ^ (_selectSize != 0))
{
FBSurface& s = _boss->dialog().surface();
const Common::Rect& editRect = getEditRect();
int x = editRect.x();
int y = editRect.y();
ColorId color = _caretEnabled ? kTextColorHi : kTextColorInv;
x += getCaretOffset();
x += _x;
y += _y;
s.vLine(x, y + 1, y + editRect.h() - 3, color);
s.vLine(x - 1, y + 1, y + editRect.h() - 3, color);
clearDirty();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -366,6 +409,9 @@ bool EditableWidget::setCaretPos(int newPos)
assert(newPos >= 0 && newPos <= int(_editString.size()));
_caretPos = newPos;
_caretTimer = 0;
_caretEnabled = true;
return adjustOffset();
}
@ -375,6 +421,8 @@ bool EditableWidget::moveCaretPos(int direction)
if(setCaretPos(_caretPos + direction))
{
_selectSize -= direction;
_caretTimer = 0;
_caretEnabled = true;
return true;
}
return false;
@ -452,6 +500,7 @@ bool EditableWidget::killChar(int direction, bool addEdit)
{
myUndoHandler->endChars(_editString);
_editString.erase(_caretPos, 1);
setCaretPos(_caretPos);
if(addEdit)
myUndoHandler->doo(_editString);
@ -556,7 +605,7 @@ bool EditableWidget::moveWord(int direction, bool select)
if(select)
_selectSize++;
}
_caretPos = currentPos;
setCaretPos(currentPos);
handled = true;
}
else if(direction == +1) // move to first character of next word
@ -575,7 +624,7 @@ bool EditableWidget::moveWord(int direction, bool select)
if(select)
_selectSize--;
}
_caretPos = currentPos;
setCaretPos(currentPos);
handled = true;
}
@ -630,6 +679,7 @@ bool EditableWidget::killSelectedText(bool addEdit)
_selectSize = -_selectSize;
}
_editString.erase(_caretPos, _selectSize);
setCaretPos(_caretPos);
_selectSize = 0;
if(addEdit)
myUndoHandler->doo(_editString);
@ -689,7 +739,7 @@ bool EditableWidget::pasteSelectedText()
_editString.insert(_caretPos, buf.str());
// position cursor at the end of pasted text
_caretPos += int(buf.str().length());
setCaretPos(_caretPos + int(buf.str().length()));
if(selected || !pasted.empty())
{

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

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

View File

@ -130,7 +130,7 @@ void InputTextDialog::show(uInt32 x, uInt32 y, const Common::Rect& bossRect)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void InputTextDialog::center()
void InputTextDialog::setPosition()
{
if(!myEnableCenter)
{
@ -144,7 +144,7 @@ void InputTextDialog::center()
surface().setDstPos(myXOrig, myYOrig);
}
else
Dialog::center();
Dialog::setPosition();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -58,7 +58,7 @@ class InputTextDialog : public Dialog, public CommandSender
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
/** This dialog uses its own positioning, so we override Dialog::center() */
void center() override;
void setPosition() override;
private:
vector<EditTextWidget*> myInput;

View File

@ -30,6 +30,7 @@
#include "StellaSettingsDialog.hxx"
#include "WhatsNewDialog.hxx"
#include "MessageBox.hxx"
#include "ToolTip.hxx"
#include "OSystem.hxx"
#include "FrameBuffer.hxx"
#include "FBSurface.hxx"
@ -79,6 +80,8 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
const string& lblAllFiles = "Show all files";
const string& lblFound = "XXXX items found";
tooltip().setFont(font);
lwidth = font.getStringWidth(lblRom);
lwidth2 = font.getStringWidth(lblAllFiles) + CheckboxWidget::boxSize(font);
int lwidth3 = font.getStringWidth(lblFilter);
@ -120,12 +123,14 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
// Show the filter input field
xpos -= fwidth + LBL_GAP;
myPattern = new EditTextWidget(this, font, xpos, ypos - 2, fwidth, lineHeight, "");
myPattern->setToolTip("Enter filter text to reduce file list.");
// Show the "Filter" label
xpos -= lwidth3 + LBL_GAP;
new StaticTextWidget(this, font, xpos, ypos, lblFilter);
// Show the checkbox for all files
xpos -= lwidth2 + LBL_GAP * 3;
myAllFiles = new CheckboxWidget(this, font, xpos, ypos, lblAllFiles, kAllfilesCmd);
myAllFiles->setToolTip("Uncheck to show ROM files only.");
wid.push_back(myAllFiles);
wid.push_back(myPattern);
}
@ -178,6 +183,7 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
#ifndef BSPF_MACOS
myStartButton = new ButtonWidget(this, font, xpos, ypos, (buttonWidth + 0) / 4, buttonHeight,
"Select", kLoadROMCmd);
myStartButton->setToolTip("Start emulation of selected ROM.");
wid.push_back(myStartButton);
xpos += (buttonWidth + 0) / 4 + BUTTON_GAP;
@ -562,6 +568,7 @@ void LauncherDialog::handleMouseDown(int x, int y, MouseButton b, int clickCount
// Grab right mouse button for context menu, send left to base class
if(b == MouseButton::RIGHT)
{
dialog().tooltip().hide();
// Dynamically create context menu for ROM list options
VariantList items;
@ -662,7 +669,7 @@ void LauncherDialog::loadRom()
instance().settings().setValue("romdir", currentNode().getParent().getShortPath());
}
else
instance().frameBuffer().showMessage(result, MessagePosition::MiddleCenter, true);
instance().frameBuffer().showTextMessage(result, MessagePosition::MiddleCenter, true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -101,7 +101,7 @@ class LauncherDialog : public Dialog
static constexpr int MIN_ROMINFO_ROWS = 7; // full lines
static constexpr int MIN_ROMINFO_LINES = 4; // extra lines
void center() override { positionAt(0); }
void setPosition() override { positionAt(0); }
void handleKeyDown(StellaKey key, StellaMod mod, bool repeated) override;
void handleMouseDown(int x, int y, MouseButton b, int clickCount) override;
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;

View File

@ -36,6 +36,8 @@ ListWidget::ListWidget(GuiObject* boss, const GUI::Font& font,
_textcolor = kTextColor;
_textcolorhi = kTextColor;
_editMode = false;
_cols = w / _fontWidth;
_rows = h / _lineHeight;

View File

@ -99,7 +99,6 @@ class ListWidget : public EditableWidget
int _currentPos{0};
int _selectedItem{-1};
int _highlightedItem{-1};
bool _editMode{false};
bool _useScrollbar{true};
ScrollBarWidget* _scrollBar{nullptr};

View File

@ -123,12 +123,12 @@ void LoggerDialog::saveLogFile()
{
stringstream out;
out << Logger::instance().logMessages();
instance().frameBuffer().showMessage("Saving log file to " + node.getShortPath());
instance().frameBuffer().showTextMessage("Saving log file to " + node.getShortPath());
node.write(out);
}
catch(...)
{
instance().frameBuffer().showMessage("Error saving log file to " + node.getShortPath());
instance().frameBuffer().showTextMessage("Error saving log file to " + node.getShortPath());
}
}

View File

@ -52,7 +52,7 @@ class OptionsDialog : public Dialog
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
private:
unique_ptr<VideoAudioDialog> myVideoDialog;
unique_ptr<VideoAudioDialog> myVideoDialog;
unique_ptr<EmulationDialog> myEmulationDialog;
unique_ptr<InputDialog> myInputDialog;
unique_ptr<UIDialog> myUIDialog;

View File

@ -20,6 +20,8 @@
#include "FBSurface.hxx"
#include "Font.hxx"
#include "ContextMenu.hxx"
#include "Dialog.hxx"
#include "ToolTip.hxx"
#include "DialogContainer.hxx"
#include "PopUpWidget.hxx"
@ -122,6 +124,7 @@ void PopUpWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount)
{
if(isEnabled() && !myMenu->isVisible())
{
dialog().tooltip().hide();
// Add menu just underneath parent widget
myMenu->show(getAbsX() + _labelWidth, getAbsY() + getHeight(),
dialog().surface().dstRect(), myMenu->getSelected());
@ -158,20 +161,6 @@ void PopUpWidget::handleMouseWheel(int x, int y, int direction)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::handleMouseEntered()
{
setFlags(Widget::FLAG_HILITED);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::handleMouseLeft()
{
clearFlags(Widget::FLAG_HILITED);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PopUpWidget::handleEvent(Event::Type e)
{
@ -277,7 +266,7 @@ void PopUpWidget::drawWidget(bool hilite)
// Fill the background
ColorId bgCol = isEditable() ? kWidColor : kDlgColor;
s.fillRect(x + 1, _y + 1, w - (_arrowWidth * 2 - 0), _h - 2,
s.fillRect(x + 1, _y + 1, w - (_arrowWidth * 2 - 1), _h - 2,
onTop ? _changed ? kDbgChangedColor : bgCol : kDlgColor);
s.fillRect(x + w - (_arrowWidth * 2 - 2), _y + 1, (_arrowWidth * 2 - 3), _h - 2,
onTop ? isEnabled() && hilite ? kBtnColorHi : bgCol : kBGColorLo);

View File

@ -70,8 +70,6 @@ class PopUpWidget : public EditableWidget
protected:
void handleMouseDown(int x, int y, MouseButton b, int clickCount) override;
void handleMouseWheel(int x, int y, int direction) override;
void handleMouseEntered() override;
void handleMouseLeft() override;
bool handleEvent(Event::Type e) override;
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;

View File

@ -172,6 +172,8 @@ void RomInfoWidget::parseProperties(const FilesystemNode& node)
myRomInfo.push_back("Controllers: " + (left + " (left), " + right + " (right)"));
if (bsDetected != "")
myRomInfo.push_back("Type: " + Bankswitch::typeToDesc(Bankswitch::nameToType(bsDetected)));
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -227,4 +229,5 @@ void RomInfoWidget::drawWidget(bool hilite)
onTop ? _textcolor : _shadowcolor);
ypos += _font.getLineHeight() + (lines - 1) * _font.getFontHeight();
}
clearDirty();
}

View File

@ -240,19 +240,11 @@ void ScrollBarWidget::checkBounds(int old_pos)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ScrollBarWidget::handleMouseEntered()
{
setFlags(Widget::FLAG_HILITED);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ScrollBarWidget::handleMouseLeft()
{
_part = Part::None;
clearFlags(Widget::FLAG_HILITED);
setDirty();
Widget::handleMouseLeft();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -315,6 +307,7 @@ void ScrollBarWidget::drawWidget(bool hilite)
s.fillRect(_x + 1, _y + _sliderPos - 1, _w - 2, _sliderHeight + 2,
onTop ? (hilite && _part == Part::Slider) ? kScrollColorHi : kScrollColor : kColor);
}
clearDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -49,7 +49,6 @@ class ScrollBarWidget : public Widget, public CommandSender
void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
void handleMouseMoved(int x, int y) override;
bool handleMouseClicks(int x, int y, MouseButton b) override;
void handleMouseEntered() override;
void handleMouseLeft() override;
void setArrows();

View File

@ -268,6 +268,7 @@ void StellaSettingsDialog::saveConfig()
settings.setValue("uipalette",
myThemePopup->getSelectedTag().toString());
instance().frameBuffer().setUIPalette();
instance().frameBuffer().update(FrameBuffer::UpdateMode::REDRAW);
// Dialog position
settings.setValue("dialogpos", myPositionPopup->getSelectedTag().toString());

View File

@ -51,17 +51,44 @@ void StringListWidget::setList(const StringList& list)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StringListWidget::handleMouseEntered()
int StringListWidget::getToolTipIndex(const Common::Point& pos) const
{
setFlags(Widget::FLAG_HILITED);
setDirty();
int idx = (pos.y - getAbsY()) / _lineHeight + _currentPos;
if(idx >= int(_list.size()))
return -1;
else
return idx;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StringListWidget::handleMouseLeft()
string StringListWidget::getToolTip(const Common::Point& pos) const
{
clearFlags(Widget::FLAG_HILITED);
setDirty();
Common::Rect rect = getEditRect();
int idx = getToolTipIndex(pos);
if(idx < 0)
return EmptyString;
const string value = _list[idx];
if(uInt32(_font.getStringWidth(value)) > rect.w())
return _toolTipText + value;
else
return _toolTipText;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool StringListWidget::changedToolTip(const Common::Point& oldPos,
const Common::Point& newPos) const
{
bool ch = getToolTipIndex(oldPos) != getToolTipIndex(newPos)
&& getToolTip(oldPos) != getToolTip(newPos);
if(ch)
cerr << "changed" << endl;
return ch;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -32,9 +32,16 @@ class StringListWidget : public ListWidget
void setList(const StringList& list);
bool wantsFocus() const override { return true; }
string getToolTip(const Common::Point& pos) const override;
bool changedToolTip(const Common::Point& oldPos, const Common::Point& newPos) const override;
protected:
void handleMouseEntered() override;
void handleMouseLeft() override;
// display depends on _hasFocus so we have to redraw when focus changes
void receivedFocusWidget() override { setDirty(); }
void lostFocusWidget() override { setDirty(); }
bool hasToolTip() const override { return true; }
void drawWidget(bool hilite) override;
Common::Rect getEditRect() const override;
@ -42,6 +49,9 @@ class StringListWidget : public ListWidget
bool _hilite{false};
int _textOfs{0};
private:
int getToolTipIndex(const Common::Point& pos) const;
private:
// Following constructors and assignment operators not supported
StringListWidget() = delete;

View File

@ -213,20 +213,6 @@ void TabWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TabWidget::handleMouseEntered()
{
setFlags(Widget::FLAG_HILITED);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TabWidget::handleMouseLeft()
{
clearFlags(Widget::FLAG_HILITED);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TabWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
{
@ -275,39 +261,45 @@ void TabWidget::drawWidget(bool hilite)
// The tab widget is strange in that it acts as both a widget (obviously)
// and a dialog (it contains other widgets). Because of the latter,
// it must assume responsibility for refreshing all its children.
Widget::setDirtyInChain(_tabs[_activeTab].firstWidget);
FBSurface& s = dialog().surface();
bool onTop = _boss->dialog().isOnTop();
// Iterate over all tabs and draw them
int i, x = _x + kTabLeftOffset;
for (i = 0; i < int(_tabs.size()); ++i)
if(isDirty())
{
int tabWidth = _tabs[i].tabWidth ? _tabs[i].tabWidth : _tabWidth;
ColorId fontcolor = _tabs[i].enabled && onTop? kTextColor : kColor;
int yOffset = (i == _activeTab) ? 0 : 1;
s.fillRect(x, _y + 1, tabWidth, _tabHeight - 1,
(i == _activeTab)
? onTop ? kDlgColor : kBGColorLo
: onTop ? kBGColorHi : kDlgColor); // ? kWidColor : kDlgColor
s.drawString(_font, _tabs[i].title, x + kTabPadding + yOffset,
_y + yOffset + (_tabHeight - _lineHeight - 1),
tabWidth - 2 * kTabPadding, fontcolor, TextAlign::Center);
if(i == _activeTab)
FBSurface& s = dialog().surface();
bool onTop = _boss->dialog().isOnTop();
// Iterate over all tabs and draw them
int i, x = _x + kTabLeftOffset;
for(i = 0; i < int(_tabs.size()); ++i)
{
s.hLine(x, _y, x + tabWidth - 1, onTop ? kWidColor : kDlgColor);
s.vLine(x + tabWidth, _y + 1, _y + _tabHeight - 1, onTop ? kBGColorLo : kColor);
int tabWidth = _tabs[i].tabWidth ? _tabs[i].tabWidth : _tabWidth;
ColorId fontcolor = _tabs[i].enabled && onTop ? kTextColor : kColor;
int yOffset = (i == _activeTab) ? 0 : 1;
s.fillRect(x, _y + 1, tabWidth, _tabHeight - 1,
(i == _activeTab)
? onTop ? kDlgColor : kBGColorLo
: onTop ? kBGColorHi : kDlgColor); // ? kWidColor : kDlgColor
s.drawString(_font, _tabs[i].title, x + kTabPadding + yOffset,
_y + yOffset + (_tabHeight - _lineHeight - 1),
tabWidth - 2 * kTabPadding, fontcolor, TextAlign::Center);
if(i == _activeTab)
{
s.hLine(x, _y, x + tabWidth - 1, onTop ? kWidColor : kDlgColor);
s.vLine(x + tabWidth, _y + 1, _y + _tabHeight - 1, onTop ? kBGColorLo : kColor);
}
else
s.hLine(x, _y + _tabHeight, x + tabWidth, onTop ? kWidColor : kDlgColor);
x += tabWidth + kTabSpacing;
}
else
s.hLine(x, _y + _tabHeight, x + tabWidth, onTop ? kWidColor : kDlgColor);
x += tabWidth + kTabSpacing;
// fill empty right space
s.hLine(x - kTabSpacing + 1, _y + _tabHeight, _x + _w - 1, onTop ? kWidColor : kDlgColor);
s.hLine(_x, _y + _h - 1, _x + _w - 1, onTop ? kBGColorLo : kColor);
clearDirty();
// Make all child widgets of currently active tab dirty
Widget::setDirtyInChain(_tabs[_activeTab].firstWidget);
}
// fill empty right space
s.hLine(x - kTabSpacing + 1, _y + _tabHeight, _x + _w - 1, onTop ? kWidColor : kDlgColor);
s.hLine(_x, _y + _h - 1, _x + _w - 1, onTop ? kBGColorLo : kColor);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -63,8 +63,8 @@ class TabWidget : public Widget, public CommandSender
protected:
void handleMouseDown(int x, int y, MouseButton b, int clickCount) override;
void handleMouseEntered() override;
void handleMouseLeft() override;
void handleMouseEntered() override {}
void handleMouseLeft() override {}
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
bool handleEvent(Event::Type event) override;

View File

@ -35,7 +35,9 @@ TimeLineWidget::TimeLineWidget(GuiObject* boss, const GUI::Font& font,
: ButtonWidget(boss, font, x, y, w, h, label, cmd),
_labelWidth(labelWidth)
{
_flags = Widget::FLAG_ENABLED | Widget::FLAG_TRACK_MOUSE;
_flags = Widget::FLAG_ENABLED | Widget::FLAG_TRACK_MOUSE
| Widget::FLAG_CLEARBG | Widget::FLAG_NOBG;
_bgcolor = kDlgColor;
_bgcolorhi = kDlgColor;
@ -84,7 +86,7 @@ void TimeLineWidget::setStepValues(const IntArray& steps)
if(steps.size() > _stepValue.capacity())
_stepValue.reserve(2 * steps.size());
double scale = (_w - _labelWidth - 2 - HANDLE_W*0) / double(steps.back());
double scale = (_w - _labelWidth - 2 - HANDLE_W) / double(steps.back());
// Skip the very last value; we take care of it outside the end of the loop
for(uInt32 i = 0; i < steps.size() - 1; ++i)
@ -92,7 +94,7 @@ void TimeLineWidget::setStepValues(const IntArray& steps)
// Due to integer <-> double conversion, the last value is sometimes
// slightly less than the maximum value; we assign it manually to fix this
_stepValue.push_back(_w - _labelWidth - 2 - HANDLE_W*0);
_stepValue.push_back(_w - _labelWidth - 2 - HANDLE_W);
}
else
_stepValue.push_back(0);
@ -141,17 +143,18 @@ void TimeLineWidget::drawWidget(bool hilite)
{
FBSurface& s = _boss->dialog().surface();
cerr << "TimeLineWidget::drawWidget " << typeid(s).name() << endl;
// Draw the label, if any
if(_labelWidth > 0)
s.drawString(_font, _label, _x, _y + 2, _labelWidth,
isEnabled() ? kTextColor : kColor, TextAlign::Left);
int p = valueToPos(_value),
x = _x + _labelWidth,
w = _w - _labelWidth;
// Frame the handle
const int HANDLE_W2 = (HANDLE_W + 1) / 2;
int p = valueToPos(_value),
x = _x + _labelWidth + HANDLE_W2,
w = _w - _labelWidth - HANDLE_W;
s.hLine(x + p - HANDLE_W2, _y + 0, x + p - HANDLE_W2 + HANDLE_W, kColorInfo);
s.vLine(x + p - HANDLE_W2, _y + 1, _y + _h - 2, kColorInfo);
s.hLine(x + p - HANDLE_W2 + 1, _y + _h - 1, x + p - HANDLE_W2 + 1 + HANDLE_W, kBGColor);

View File

@ -218,6 +218,7 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent,
this->clearFlags(Widget::FLAG_CLEARBG); // does only work combined with blending (0..100)!
this->clearFlags(Widget::FLAG_BORDER);
this->setFlags(Widget::FLAG_NOBG);
xpos = H_BORDER;
ypos = V_BORDER;
@ -225,8 +226,10 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent,
// Add index info
myCurrentIdxWidget = new StaticTextWidget(this, font, xpos, ypos, "1000", TextAlign::Left, kBGColor);
myCurrentIdxWidget->setTextColor(kColorInfo);
myCurrentIdxWidget->setFlags(Widget::FLAG_CLEARBG | Widget::FLAG_NOBG);
myLastIdxWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("1000"), ypos,
"1000", TextAlign::Right, kBGColor);
myLastIdxWidget->setFlags(Widget::FLAG_CLEARBG | Widget::FLAG_NOBG);
myLastIdxWidget->setTextColor(kColorInfo);
// Add timeline
@ -241,9 +244,11 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent,
// Add time info
int ypos_s = ypos + (buttonHeight - font.getFontHeight() + 1) / 2; // align to button vertical center
myCurrentTimeWidget = new StaticTextWidget(this, font, xpos, ypos_s, "00:00.00", TextAlign::Left, kBGColor);
myCurrentTimeWidget->setFlags(Widget::FLAG_CLEARBG | Widget::FLAG_NOBG);
myCurrentTimeWidget->setTextColor(kColorInfo);
myLastTimeWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("00:00.00"), ypos_s,
"00:00.00", TextAlign::Right, kBGColor);
myLastTimeWidget->setFlags(Widget::FLAG_CLEARBG | Widget::FLAG_NOBG);
myLastTimeWidget->setTextColor(kColorInfo);
xpos = myCurrentTimeWidget->getRight() + BUTTON_GAP * 4;
@ -287,11 +292,12 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent,
// Add message
myMessageWidget = new StaticTextWidget(this, font, xpos, ypos_s,
" ", TextAlign::Left, kBGColor);
myMessageWidget->setFlags(Widget::FLAG_CLEARBG | Widget::FLAG_NOBG);
myMessageWidget->setTextColor(kColorInfo);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TimeMachineDialog::center()
void TimeMachineDialog::setPosition()
{
// Place on the bottom of the screen, centered horizontally
const Common::Size& screen = instance().frameBuffer().screenSize();
@ -434,11 +440,11 @@ void TimeMachineDialog::handleCommand(CommandSender* sender, int cmd,
break;
case kSaveAll:
instance().frameBuffer().showMessage(instance().state().rewindManager().saveAllStates());
instance().frameBuffer().showTextMessage(instance().state().rewindManager().saveAllStates());
break;
case kLoadAll:
instance().frameBuffer().showMessage(instance().state().rewindManager().loadAllStates());
instance().frameBuffer().showTextMessage(instance().state().rewindManager().loadAllStates());
initBar();
break;

View File

@ -44,8 +44,8 @@ class TimeMachineDialog : public Dialog
/** initialize timeline bar */
void initBar();
/** This dialog uses its own positioning, so we override Dialog::center() */
void center() override;
/** This dialog uses its own positioning, so we override Dialog::setPosition() */
void setPosition() override;
/** convert cycles into time */
string getTimeString(uInt64 cycles);

183
src/gui/ToolTip.cxx Normal file
View File

@ -0,0 +1,183 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#include "OSystem.hxx"
#include "Dialog.hxx"
#include "DialogContainer.hxx"
#include "Font.hxx"
#include "FrameBuffer.hxx"
#include "FBSurface.hxx"
#include "Widget.hxx"
#include "ToolTip.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ToolTip::ToolTip(Dialog& dialog, const GUI::Font& font)
: myDialog(dialog)
{
myScale = myDialog.instance().frameBuffer().hidpiScaleFactor();
setFont(font);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ToolTip::setFont(const GUI::Font& font)
{
myFont = &font;
const int fontWidth = font.getMaxCharWidth(),
fontHeight = font.getFontHeight();
myTextXOfs = fontHeight < 24 ? 5 : 8;
myTextYOfs = fontHeight < 24 ? 2 : 3;
myWidth = fontWidth * MAX_COLUMNS + myTextXOfs * 2;
myHeight = fontHeight * MAX_ROWS + myTextYOfs * 2;
mySurface = myDialog.instance().frameBuffer().allocateSurface(myWidth, myHeight);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ToolTip::request()
{
// Called each frame when a tooltip is wanted
if(myFocusWidget && myTimer < DELAY_TIME * RELEASE_SPEED)
{
const string tip = myFocusWidget->getToolTip(myMousePos);
if(!tip.empty())
{
myTipWidget = myFocusWidget;
myTimer += RELEASE_SPEED;
if(myTimer >= DELAY_TIME * RELEASE_SPEED)
show(tip);
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ToolTip::update(const Widget* widget, const Common::Point& pos)
{
// Called each mouse move
myMousePos = pos;
myFocusWidget = widget;
if(myTipWidget != widget)
release(false);
if(!myTipShown)
release(true);
else
{
if(myTipWidget->changedToolTip(myTipPos, myMousePos))
{
const string tip = myTipWidget->getToolTip(myMousePos);
if(!tip.empty())
show(tip);
else
release(true);
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ToolTip::hide()
{
if(myTipShown)
{
myTimer = 0;
myTipWidget = myFocusWidget = nullptr;
myTipShown = false;
myDialog.instance().frameBuffer().setPendingRender();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ToolTip::release(bool emptyTip)
{
if(myTipShown)
{
myTipShown = false;
myDialog.instance().frameBuffer().setPendingRender();
}
// After displaying a tip, slowly reset the timer to 0
// until a new tip is requested
if((emptyTip || myTipWidget != myFocusWidget) && myTimer)
myTimer--;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ToolTip::show(const string& tip)
{
myTipPos = myMousePos;
uInt32 maxWidth = std::min(myWidth - myTextXOfs * 2, uInt32(myFont->getStringWidth(tip)));
mySurface->fillRect(1, 1, maxWidth + myTextXOfs * 2 - 2, myHeight - 2, kWidColor);
int lines = std::min(MAX_ROWS,
uInt32(mySurface->drawString(*myFont, tip, myTextXOfs, myTextYOfs,
maxWidth, myHeight - myTextYOfs * 2,
kTextColor)));
// Calculate maximum width of drawn string lines
uInt32 width = 0;
string inStr = tip;
for(int i = 0; i < lines; ++i)
{
string leftStr, rightStr;
mySurface->splitString(*myFont, inStr, maxWidth, leftStr, rightStr);
width = std::max(width, uInt32(myFont->getStringWidth(leftStr)));
inStr = rightStr;
}
width += myTextXOfs * 2;
// Calculate and set surface size and position
const uInt32 height = std::min(myHeight, myFont->getFontHeight() * lines + myTextYOfs * 2);
const uInt32 V_GAP = 1;
const uInt32 H_CURSOR = 18;
// Note: The rects include HiDPI scaling
const Common::Rect imageRect = myDialog.instance().frameBuffer().imageRect();
const Common::Rect dialogRect = myDialog.surface().dstRect();
// Limit position to app size and adjust accordingly
const Int32 xAbs = myTipPos.x + dialogRect.x() / myScale;
const uInt32 yAbs = myTipPos.y + dialogRect.y() / myScale;
Int32 x = std::min(xAbs, Int32(imageRect.w() / myScale - width));
const uInt32 y = (yAbs + height + H_CURSOR > imageRect.h() / myScale)
? yAbs - height - V_GAP
: yAbs + H_CURSOR / myScale + V_GAP;
if(x < 0)
{
x = 0;
width = std::min(width, imageRect.w() / myScale);
}
mySurface->setSrcSize(width, height);
mySurface->setDstSize(width * myScale, height * myScale);
mySurface->setDstPos(x * myScale, y * myScale);
mySurface->frameRect(0, 0, width, height, kColor);
myTipShown = true;
myDialog.instance().frameBuffer().setPendingRender();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ToolTip::render()
{
if(myTipShown)
mySurface->render(), cerr << " render tooltip" << endl;
}

101
src/gui/ToolTip.hxx Normal file
View File

@ -0,0 +1,101 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#ifndef TOOL_TIP_HXX
#define TOOL_TIP_HXX
/**
* Class for providing tooltip functionality
*
* @author Thomas Jentzsch
*/
class OSystem;
class Dialog;
class Widget;
class FBSurface;
#include "Rect.hxx"
class ToolTip
{
private:
static constexpr uInt32 MAX_COLUMNS = 60;
static constexpr uInt32 MAX_ROWS = 5;
public:
// Maximum tooltip length
static constexpr uInt32 MAX_LEN = MAX_COLUMNS * MAX_ROWS;
ToolTip(Dialog& dialog, const GUI::Font& font);
~ToolTip() = default;
void setFont(const GUI::Font& font);
/**
Request a tooltip display.
*/
void request();
/**
Hide a displayed tooltip and reset the timer.
*/
void hide();
/**
Hide a displayed tooltip and reset the timer slowly.
This allows faster tip display of the next tip.
*/
void release(bool emptyTip);
/**
Update focused widget and current mouse position.
*/
void update(const Widget* widget, const Common::Point& pos);
/*
Render the tooltip
*/
void render();
private:
void show(const string& tip);
private:
static constexpr uInt32 DELAY_TIME = 45; // display delay [frames]
// Tips are slower released than requested, so that repeated tips are shown
// faster. This constant defines how much faster.
static constexpr uInt32 RELEASE_SPEED = 2;
Dialog& myDialog;
const GUI::Font* myFont{nullptr};
const Widget* myTipWidget{nullptr};
const Widget* myFocusWidget{nullptr};
uInt32 myTimer{0};
Common::Point myMousePos;
Common::Point myTipPos;
uInt32 myWidth{0};
uInt32 myHeight{0};
uInt32 myTextXOfs{0};
uInt32 myTextYOfs{0};
bool myTipShown{false};
uInt32 myScale{1};
shared_ptr<FBSurface> mySurface;
};
#endif

View File

@ -441,6 +441,7 @@ void UIDialog::saveConfig()
settings.setValue("uipalette",
myPalettePopup->getSelectedTag().toString());
instance().frameBuffer().setUIPalette();
instance().frameBuffer().update(FrameBuffer::UpdateMode::REDRAW);
// Dialog font
settings.setValue("dialogfont",

View File

@ -122,12 +122,14 @@ void VideoAudioDialog::addDisplayTab()
myRenderer = new PopUpWidget(myTab, _font, xpos, ypos, pwidth, lineHeight,
instance().frameBuffer().supportedRenderers(),
"Renderer ", lwidth);
myRenderer->setToolTip("Select renderer used for displaying screen.");
wid.push_back(myRenderer);
const int swidth = myRenderer->getWidth() - lwidth;
ypos += lineHeight + VGAP;
// TIA interpolation
myTIAInterpolate = new CheckboxWidget(myTab, _font, xpos, ypos + 1, "Interpolation ");
myTIAInterpolate->setToolTip("Blur emulated display.");
wid.push_back(myTIAInterpolate);
ypos += lineHeight + VGAP * 4;
@ -145,12 +147,14 @@ void VideoAudioDialog::addDisplayTab()
// FS stretch
myUseStretch = new CheckboxWidget(myTab, _font, xpos + INDENT, ypos + 1, "Stretch");
myUseStretch->setToolTip("Stretch emulated display to fill whole screen.");
wid.push_back(myUseStretch);
#ifdef ADAPTABLE_REFRESH_SUPPORT
// Adapt refresh rate
ypos += lineHeight + VGAP;
myRefreshAdapt = new CheckboxWidget(myTab, _font, xpos + INDENT, ypos + 1, "Adapt display refresh rate");
myRefreshAdapt->setToolTip("Select optimal display refresh rate for each ROM.");
wid.push_back(myRefreshAdapt);
#else
myRefreshAdapt = nullptr;
@ -167,6 +171,7 @@ void VideoAudioDialog::addDisplayTab()
// Aspect ratio correction
ypos += lineHeight + VGAP * 4;
myCorrectAspect = new CheckboxWidget(myTab, _font, xpos, ypos + 1, "Correct aspect ratio (*)");
myCorrectAspect->setToolTip("Uncheck to disable real world aspect ratio correction.");
wid.push_back(myCorrectAspect);
// Vertical size
@ -176,6 +181,7 @@ void VideoAudioDialog::addDisplayTab()
"V-Size adjust", lwidth, kVSizeChanged, fontWidth * 7, "%", 0, true);
myVSizeAdjust->setMinValue(-5); myVSizeAdjust->setMaxValue(5);
myVSizeAdjust->setTickmarkIntervals(2);
myVSizeAdjust->setToolTip("Adjust vertical size to match emulated TV display.");
wid.push_back(myVSizeAdjust);
@ -231,6 +237,7 @@ void VideoAudioDialog::addPaletteTab()
myPhaseShiftNtsc->setMinValue((PaletteHandler::DEF_NTSC_SHIFT - PaletteHandler::MAX_PHASE_SHIFT) * 10);
myPhaseShiftNtsc->setMaxValue((PaletteHandler::DEF_NTSC_SHIFT + PaletteHandler::MAX_PHASE_SHIFT) * 10);
myPhaseShiftNtsc->setTickmarkIntervals(4);
myPhaseShiftNtsc->setToolTip("Adjust NTSC phase shift of 'Custom' palette.");
wid.push_back(myPhaseShiftNtsc);
ypos += lineHeight + VGAP;
@ -240,6 +247,7 @@ void VideoAudioDialog::addPaletteTab()
myPhaseShiftPal->setMinValue((PaletteHandler::DEF_PAL_SHIFT - PaletteHandler::MAX_PHASE_SHIFT) * 10);
myPhaseShiftPal->setMaxValue((PaletteHandler::DEF_PAL_SHIFT + PaletteHandler::MAX_PHASE_SHIFT) * 10);
myPhaseShiftPal->setTickmarkIntervals(4);
myPhaseShiftPal->setToolTip("Adjust PAL phase shift of 'Custom' palette.");
wid.push_back(myPhaseShiftPal);
ypos += lineHeight + VGAP;
@ -252,6 +260,7 @@ void VideoAudioDialog::addPaletteTab()
myTVRedScale->setMinValue(0);
myTVRedScale->setMaxValue(100);
myTVRedScale->setTickmarkIntervals(2);
myTVRedScale->setToolTip("Adjust red saturation of 'Custom' palette.");
wid.push_back(myTVRedScale);
const int xposr = myTIAPalette->getRight() - rgbsWidth;
@ -261,6 +270,7 @@ void VideoAudioDialog::addPaletteTab()
myTVRedShift->setMinValue((PaletteHandler::DEF_RGB_SHIFT - PaletteHandler::MAX_RGB_SHIFT) * 10);
myTVRedShift->setMaxValue((PaletteHandler::DEF_RGB_SHIFT + PaletteHandler::MAX_RGB_SHIFT) * 10);
myTVRedShift->setTickmarkIntervals(2);
myTVRedShift->setToolTip("Adjust red shift of 'Custom' palette.");
wid.push_back(myTVRedShift);
ypos += lineHeight + VGAP;
@ -270,6 +280,7 @@ void VideoAudioDialog::addPaletteTab()
myTVGreenScale->setMinValue(0);
myTVGreenScale->setMaxValue(100);
myTVGreenScale->setTickmarkIntervals(2);
myTVGreenScale->setToolTip("Adjust green saturation of 'Custom' palette.");
wid.push_back(myTVGreenScale);
myTVGreenShift =
@ -278,6 +289,7 @@ void VideoAudioDialog::addPaletteTab()
myTVGreenShift->setMinValue((PaletteHandler::DEF_RGB_SHIFT - PaletteHandler::MAX_RGB_SHIFT) * 10);
myTVGreenShift->setMaxValue((PaletteHandler::DEF_RGB_SHIFT + PaletteHandler::MAX_RGB_SHIFT) * 10);
myTVGreenShift->setTickmarkIntervals(2);
myTVGreenShift->setToolTip("Adjust green shift of 'Custom' palette.");
wid.push_back(myTVGreenShift);
ypos += lineHeight + VGAP;
@ -287,6 +299,7 @@ void VideoAudioDialog::addPaletteTab()
myTVBlueScale->setMinValue(0);
myTVBlueScale->setMaxValue(100);
myTVBlueScale->setTickmarkIntervals(2);
myTVBlueScale->setToolTip("Adjust blue saturation of 'Custom' palette.");
wid.push_back(myTVBlueScale);
myTVBlueShift =
@ -295,6 +308,7 @@ void VideoAudioDialog::addPaletteTab()
myTVBlueShift->setMinValue((PaletteHandler::DEF_RGB_SHIFT - PaletteHandler::MAX_RGB_SHIFT) * 10);
myTVBlueShift->setMaxValue((PaletteHandler::DEF_RGB_SHIFT + PaletteHandler::MAX_RGB_SHIFT) * 10);
myTVBlueShift->setTickmarkIntervals(2);
myTVBlueShift->setToolTip("Adjust blue shift of 'Custom' palette.");
wid.push_back(myTVBlueShift);
ypos += lineHeight + VGAP;
xpos -= INDENT;
@ -959,7 +973,16 @@ void VideoAudioDialog::handlePaletteUpdate()
instance().frameBuffer().tiaSurface().paletteHandler().setAdjustables(paletteAdj);
if(instance().hasConsole())
{
instance().frameBuffer().tiaSurface().paletteHandler().setPalette();
constexpr int NUM_LUMA = 8;
constexpr int NUM_CHROMA = 16;
for(int idx = 0; idx < NUM_CHROMA; ++idx)
for(int lum = 0; lum < NUM_LUMA; ++lum)
myColor[idx][lum]->setDirty();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1157,10 +1180,11 @@ void VideoAudioDialog::addPalette(int x, int y, int w, int h)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void VideoAudioDialog::colorPalette()
{
constexpr int NUM_LUMA = 8;
constexpr int NUM_CHROMA = 16;
if(instance().hasConsole())
{
constexpr int NUM_LUMA = 8;
constexpr int NUM_CHROMA = 16;
const int order[2][NUM_CHROMA] =
{
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
@ -1176,11 +1200,14 @@ void VideoAudioDialog::colorPalette()
ss << Common::Base::HEX1 << std::uppercase << color;
myColorLbl[idx]->setLabel(ss.str());
for(int lum = 0; lum < NUM_LUMA; ++lum)
{
myColor[idx][lum]->setColor(color * NUM_CHROMA + lum * 2); // skip grayscale colors
}
}
}
else
// disable palette
for(int idx = 0; idx < NUM_CHROMA; ++idx)
for(int lum = 0; lum < NUM_LUMA; ++lum)
myColor[idx][lum]->setEnabled(false);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -38,7 +38,7 @@ class VideoAudioDialog : public Dialog
{
public:
VideoAudioDialog(OSystem& osystem, DialogContainer& parent, const GUI::Font& font,
int max_w, int max_h);
int max_w, int max_h);
~VideoAudioDialog() override = default;
private:

View File

@ -21,6 +21,7 @@
#include "bspf.hxx"
#include "Command.hxx"
#include "Dialog.hxx"
#include "ToolTip.hxx"
#include "FBSurface.hxx"
#include "GuiObject.hxx"
#include "OSystem.hxx"
@ -54,9 +55,38 @@ Widget::~Widget()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Widget::setDirty()
{
// A widget being dirty indicates that its parent dialog is dirty
// So we inform the parent about it
_boss->dialog().setDirty();
_dirty = true;
// Inform the parent object that its children chain is dirty
_boss->setDirtyChain();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Widget::setDirtyChain()
{
_dirtyChain = true;
// Inform the parent object that its children chain is dirty
_boss->setDirtyChain();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Widget::tick()
{
if(isEnabled())
{
if(wantsToolTip())
dialog().tooltip().request();
// Recursively tick widget and all child dialogs and widgets
Widget* w = _firstWidget;
while(w)
{
w->tick();
w = w->_next;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -65,62 +95,102 @@ void Widget::draw()
if(!isVisible() || !_boss->isVisible())
return;
FBSurface& s = _boss->dialog().surface();
bool onTop = _boss->dialog().isOnTop();
bool hasBorder = _flags & Widget::FLAG_BORDER; // currently only used by Dialog widget
int oldX = _x, oldY = _y;
// Account for our relative position in the dialog
_x = getAbsX();
_y = getAbsY();
// Clear background (unless alpha blending is enabled)
if(_flags & Widget::FLAG_CLEARBG)
if(isDirty())
{
int x = _x, y = _y, w = _w, h = _h;
if(hasBorder)
cerr << " *** draw widget " << typeid(*this).name() << " ***" << endl;
//cerr << "w";
FBSurface& s = _boss->dialog().surface();
bool onTop = _boss->dialog().isOnTop();
int oldX = _x, oldY = _y;
// Account for our relative position in the dialog
_x = getAbsX();
_y = getAbsY();
// Clear background (unless alpha blending is enabled)
if(clearsBackground())
{
x++; y++; w-=2; h-=2;
int x = _x, y = _y, w = _w, h = _h;
if(hasBorder())
{
x++; y++; w -= 2; h -= 2;
}
if(hasBackground())
s.fillRect(x, y, w, h, !onTop
? _bgcolorlo
: (_flags & Widget::FLAG_HILITED) && isEnabled()
? _bgcolorhi : _bgcolor);
else
s.invalidateRect(x, y, w, h);
}
s.fillRect(x, y, w, h, !onTop ? _bgcolorlo : (_flags & Widget::FLAG_HILITED) && isEnabled() ? _bgcolorhi : _bgcolor);
// Draw border
if(hasBorder())
{
s.frameRect(_x, _y, _w, _h, !onTop
? kColor
: (_flags & Widget::FLAG_HILITED) && isEnabled()
? kWidColorHi : kColor);
_x += 4;
_y += 4;
_w -= 8;
_h -= 8;
}
// Now perform the actual widget draw
drawWidget((_flags & Widget::FLAG_HILITED) ? true : false);
// Restore x/y
if(hasBorder())
{
_x -= 4;
_y -= 4;
_w += 8;
_h += 8;
}
_x = oldX;
_y = oldY;
}
// Draw border
if(hasBorder)
{
s.frameRect(_x, _y, _w, _h, !onTop ? kColor : (_flags & Widget::FLAG_HILITED) && isEnabled() ? kWidColorHi : kColor);
_x += 4;
_y += 4;
_w -= 8;
_h -= 8;
}
// Now perform the actual widget draw
drawWidget((_flags & Widget::FLAG_HILITED) ? true : false);
// Restore x/y
if (hasBorder)
{
_x -= 4;
_y -= 4;
_w += 8;
_h += 8;
}
_x = oldX;
_y = oldY;
clearDirty();
// Draw all children
drawChain();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Widget::drawChain()
{
// Clear chain *before* drawing, because some widgets may set it again when
// being drawn (e.g. RomListWidget)
clearDirtyChain();
Widget* w = _firstWidget;
while(w)
{
w->draw();
if(w->needsRedraw())
w->draw();
w = w->_next;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Widget::handleMouseEntered()
{
if(isEnabled())
setFlags(Widget::FLAG_HILITED | Widget::FLAG_MOUSE_FOCUS);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Widget::handleMouseLeft()
{
if(isEnabled())
clearFlags(Widget::FLAG_HILITED | Widget::FLAG_MOUSE_FOCUS);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Widget::receivedFocus()
{
@ -150,6 +220,14 @@ void Widget::setEnabled(bool e)
else clearFlags(Widget::FLAG_ENABLED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Widget::setToolTip(const string& text)
{
assert(text.length() <= ToolTip::MAX_LEN);
_toolTipText = text;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Widget* Widget::findWidgetInChain(Widget* w, int x, int y)
{
@ -224,7 +302,7 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr,
s.frameRect(x, y, w, h, onTop ? kDlgColor : kBGColorLo);
tmp->setDirty();
//tmp->setDirty();
}
}
@ -280,7 +358,7 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr,
if (onTop)
s.frameRect(x, y, w, h, kWidFrameColor, FrameStyle::Dashed);
tmp->setDirty();
//tmp->setDirty();
return tmp;
}
@ -290,6 +368,7 @@ void Widget::setDirtyInChain(Widget* start)
{
while(start)
{
//cerr << "setDirtyInChain " << typeid(*start).name() << endl;
start->setDirty();
start = start->_next;
}
@ -304,7 +383,8 @@ StaticTextWidget::StaticTextWidget(GuiObject* boss, const GUI::Font& font,
_label(text),
_align(align)
{
_flags = Widget::FLAG_ENABLED;
_flags = Widget::FLAG_ENABLED | FLAG_CLEARBG;
_bgcolor = kDlgColor;
_bgcolorhi = kDlgColor;
_textcolor = kTextColor;
@ -338,6 +418,23 @@ void StaticTextWidget::setLabel(const string& label)
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StaticTextWidget::handleMouseEntered()
{
if(isEnabled())
// Mouse focus for tooltips must not change dirty status
setFlags(Widget::FLAG_MOUSE_FOCUS, false);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StaticTextWidget::handleMouseLeft()
{
if(isEnabled())
// Mouse focus for tooltips must not change dirty status
clearFlags(Widget::FLAG_MOUSE_FOCUS, false);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StaticTextWidget::drawWidget(bool hilite)
{
@ -345,8 +442,6 @@ void StaticTextWidget::drawWidget(bool hilite)
bool onTop = _boss->dialog().isOnTop();
s.drawString(_font, _label, _x, _y, _w,
isEnabled() && onTop ? _textcolor : kColor, _align, 0, true, _shadowcolor);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -400,13 +495,15 @@ ButtonWidget::ButtonWidget(GuiObject* boss, const GUI::Font& font,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ButtonWidget::handleMouseEntered()
{
setFlags(Widget::FLAG_HILITED);
if(isEnabled())
setFlags(Widget::FLAG_HILITED | Widget::FLAG_MOUSE_FOCUS);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ButtonWidget::handleMouseLeft()
{
clearFlags(Widget::FLAG_HILITED);
if(isEnabled())
clearFlags(Widget::FLAG_HILITED | Widget::FLAG_MOUSE_FOCUS);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -474,8 +571,6 @@ void ButtonWidget::drawWidget(bool hilite)
!(isEnabled() && onTop) ? _textcolorlo :
hilite ? _textcolorhi : _textcolor,
_bmw, _bmh);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -508,18 +603,6 @@ CheckboxWidget::CheckboxWidget(GuiObject* boss, const GUI::Font& font,
setFill(CheckboxWidget::FillType::Normal);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheckboxWidget::handleMouseEntered()
{
setFlags(Widget::FLAG_HILITED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheckboxWidget::handleMouseLeft()
{
clearFlags(Widget::FLAG_HILITED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CheckboxWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount)
{
@ -635,8 +718,6 @@ void CheckboxWidget::drawWidget(bool hilite)
// Finally draw the label
s.drawString(_font, _label, _x + prefixSize(_font), _y + _textY, _w,
onTop && isEnabled() ? kTextColor : kColor);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -652,7 +733,7 @@ SliderWidget::SliderWidget(GuiObject* boss, const GUI::Font& font,
_valueLabelWidth(valueLabelWidth),
_forceLabelSign(forceLabelSign)
{
_flags = Widget::FLAG_ENABLED | Widget::FLAG_TRACK_MOUSE;
_flags = Widget::FLAG_ENABLED | Widget::FLAG_TRACK_MOUSE | Widget::FLAG_CLEARBG;
_bgcolor = kDlgColor;
_bgcolorhi = kDlgColor;
@ -870,8 +951,6 @@ void SliderWidget::drawWidget(bool hilite)
if(_valueLabelWidth > 0)
s.drawString(_font, _valueLabel + _valueUnit, _x + _w - _valueLabelWidth, _y + 2,
_valueLabelWidth, isEnabled() ? kTextColor : kColor);
setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -26,6 +26,7 @@ class Dialog;
#include <cassert>
#include "bspf.hxx"
#include "Rect.hxx"
#include "Event.hxx"
#include "EventHandlerConstants.hxx"
#include "FrameBufferConstants.hxx"
@ -42,19 +43,6 @@ class Widget : public GuiObject
{
friend class Dialog;
public:
enum : uInt32 {
FLAG_ENABLED = 1 << 0,
FLAG_INVISIBLE = 1 << 1,
FLAG_HILITED = 1 << 2,
FLAG_BORDER = 1 << 3,
FLAG_CLEARBG = 1 << 4,
FLAG_TRACK_MOUSE = 1 << 5,
FLAG_RETAIN_FOCUS = 1 << 6,
FLAG_WANTS_TAB = 1 << 7,
FLAG_WANTS_RAWDATA = 1 << 8
};
public:
Widget(GuiObject* boss, const GUI::Font& font, int x, int y, int w, int h);
~Widget() override;
@ -71,8 +59,8 @@ class Widget : public GuiObject
virtual bool handleKeyUp(StellaKey key, StellaMod mod) { return false; }
virtual void handleMouseDown(int x, int y, MouseButton b, int clickCount) { }
virtual void handleMouseUp(int x, int y, MouseButton b, int clickCount) { }
virtual void handleMouseEntered() { }
virtual void handleMouseLeft() { }
virtual void handleMouseEntered();
virtual void handleMouseLeft();
virtual void handleMouseMoved(int x, int y) { }
virtual void handleMouseWheel(int x, int y, int direction) { }
virtual bool handleMouseClicks(int x, int y, MouseButton b) { return false; }
@ -82,8 +70,12 @@ class Widget : public GuiObject
virtual bool handleJoyHat(int stick, int hat, JoyHatDir hdir, int button = JOY_CTRL_NONE) { return false; }
virtual bool handleEvent(Event::Type event) { return false; }
void tick() override;
void setDirty() override;
void setDirtyChain() override;
void draw() override;
void drawChain() override;
void receivedFocus();
void lostFocus();
void addFocusWidget(Widget* w) override { _focusList.push_back(w); }
@ -94,12 +86,10 @@ class Widget : public GuiObject
/** Set/clear FLAG_ENABLED */
void setEnabled(bool e);
void setFlags(uInt32 flags) { _flags |= flags; setDirty(); }
void clearFlags(uInt32 flags) { _flags &= ~flags; setDirty(); }
uInt32 getFlags() const { return _flags; }
bool isEnabled() const { return _flags & FLAG_ENABLED; }
bool isVisible() const override { return !(_flags & FLAG_INVISIBLE); }
bool isHighlighted() const { return _flags & FLAG_HILITED; }
bool hasMouseFocus() const { return _flags & FLAG_MOUSE_FOCUS; }
virtual bool wantsFocus() const { return _flags & FLAG_RETAIN_FOCUS; }
bool wantsTab() const { return _flags & FLAG_WANTS_TAB; }
bool wantsRaw() const { return _flags & FLAG_WANTS_RAWDATA; }
@ -115,6 +105,11 @@ class Widget : public GuiObject
void setBGColorHi(ColorId color) { _bgcolorhi = color; setDirty(); }
void setShadowColor(ColorId color) { _shadowcolor = color; setDirty(); }
void setToolTip(const string& text);
virtual string getToolTip(const Common::Point& pos) const { return _toolTipText; }
virtual bool changedToolTip(const Common::Point& oldPos,
const Common::Point& newPos) const { return false; }
virtual void loadConfig() { }
protected:
@ -127,6 +122,9 @@ class Widget : public GuiObject
void releaseFocus() override { assert(_boss); _boss->releaseFocus(); }
virtual bool wantsToolTip() const { return hasMouseFocus() && hasToolTip(); }
virtual bool hasToolTip() const { return !_toolTipText.empty(); }
// By default, delegate unhandled commands to the boss
void handleCommand(CommandSender* sender, int cmd, int data, int id) override
{ assert(_boss); _boss->handleCommand(sender, cmd, data, id); }
@ -136,7 +134,6 @@ class Widget : public GuiObject
const GUI::Font& _font;
Widget* _next{nullptr};
uInt32 _id{0};
uInt32 _flags{0};
bool _hasFocus{false};
int _fontWidth{0};
int _lineHeight{0};
@ -147,6 +144,7 @@ class Widget : public GuiObject
ColorId _textcolorhi{kTextColorHi};
ColorId _textcolorlo{kBGColorLo};
ColorId _shadowcolor{kShadowColor};
string _toolTipText;
public:
static Widget* findWidgetInChain(Widget* start, int x, int y);
@ -188,6 +186,10 @@ class StaticTextWidget : public Widget
const string& text = "", TextAlign align = TextAlign::Left,
ColorId shadowColor = kNone);
~StaticTextWidget() override = default;
void handleMouseEntered() override;
void handleMouseLeft() override;
void setValue(int value);
void setLabel(const string& label);
void setAlign(TextAlign align) { _align = align; setDirty(); }
@ -281,8 +283,6 @@ class CheckboxWidget : public ButtonWidget
bool getState() const { return _state; }
void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
void handleMouseEntered() override;
void handleMouseLeft() override;
static int boxSize(const GUI::Font& font)
{

View File

@ -49,6 +49,7 @@ MODULE_OBJS := \
src/gui/TimeLineWidget.o \
src/gui/TimeMachineDialog.o \
src/gui/TimeMachine.o \
src/gui/ToolTip.o \
src/gui/UndoHandler.o \
src/gui/UIDialog.o \
src/gui/VideoAudioDialog.o \

View File

@ -44,13 +44,17 @@ class FBSurfaceLIBRETRO : public FBSurface
const Common::Rect& dstRect() const override { return myDstGUIR; }
void setSrcPos(uInt32 x, uInt32 y) override { }
void setSrcSize(uInt32 w, uInt32 h) override { }
void setSrcRect(const Common::Rect& r) override { }
void setDstPos(uInt32 x, uInt32 y) override { }
void setDstSize(uInt32 w, uInt32 h) override { }
void setDstRect(const Common::Rect& r) override { }
void setVisible(bool visible) override { }
void translateCoords(Int32& x, Int32& y) const override { }
bool render() override { return true; }
void invalidate() override { }
void invalidateRect(uInt32, uInt32, uInt32, uInt32) override { }
void free() override { }
void reload() override { }
void resize(uInt32 width, uInt32 height) override { }

View File

@ -531,6 +531,10 @@
DCBDDE9B1D6A5F0E009DF1E9 /* Cart3EPlusWidget.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCBDDE991D6A5F0E009DF1E9 /* Cart3EPlusWidget.hxx */; };
DCBDDE9E1D6A5F2F009DF1E9 /* Cart3EPlus.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCBDDE9C1D6A5F2F009DF1E9 /* Cart3EPlus.cxx */; };
DCBDDE9F1D6A5F2F009DF1E9 /* Cart3EPlus.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCBDDE9D1D6A5F2F009DF1E9 /* Cart3EPlus.hxx */; };
DCC2FDF5255EB82500FA5E81 /* ToolTip.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCC2FDF3255EB82500FA5E81 /* ToolTip.hxx */; };
DCC2FDF6255EB82500FA5E81 /* ToolTip.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCC2FDF4255EB82500FA5E81 /* ToolTip.cxx */; };
DCC2FDF92566AD8800FA5E81 /* DataGridRamWidget.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCC2FDF72566AD8800FA5E81 /* DataGridRamWidget.cxx */; };
DCC2FDFA2566AD8800FA5E81 /* DataGridRamWidget.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCC2FDF82566AD8800FA5E81 /* DataGridRamWidget.hxx */; };
DCC527D110B9DA19005E1287 /* Device.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCC527C910B9DA19005E1287 /* Device.hxx */; };
DCC527D210B9DA19005E1287 /* M6502.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCC527CA10B9DA19005E1287 /* M6502.cxx */; };
DCC527D310B9DA19005E1287 /* M6502.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCC527CB10B9DA19005E1287 /* M6502.hxx */; };
@ -1298,6 +1302,10 @@
DCBDDE991D6A5F0E009DF1E9 /* Cart3EPlusWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cart3EPlusWidget.hxx; sourceTree = "<group>"; };
DCBDDE9C1D6A5F2F009DF1E9 /* Cart3EPlus.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cart3EPlus.cxx; sourceTree = "<group>"; };
DCBDDE9D1D6A5F2F009DF1E9 /* Cart3EPlus.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cart3EPlus.hxx; sourceTree = "<group>"; };
DCC2FDF3255EB82500FA5E81 /* ToolTip.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ToolTip.hxx; sourceTree = "<group>"; };
DCC2FDF4255EB82500FA5E81 /* ToolTip.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ToolTip.cxx; sourceTree = "<group>"; };
DCC2FDF72566AD8800FA5E81 /* DataGridRamWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataGridRamWidget.cxx; sourceTree = "<group>"; };
DCC2FDF82566AD8800FA5E81 /* DataGridRamWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DataGridRamWidget.hxx; sourceTree = "<group>"; };
DCC527C910B9DA19005E1287 /* Device.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Device.hxx; sourceTree = "<group>"; };
DCC527CA10B9DA19005E1287 /* M6502.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = M6502.cxx; sourceTree = "<group>"; };
DCC527CB10B9DA19005E1287 /* M6502.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = M6502.hxx; sourceTree = "<group>"; };
@ -1702,6 +1710,8 @@
2D20F9E708C603EC00A73076 /* CpuWidget.hxx */,
2D20F9E808C603EC00A73076 /* DataGridOpsWidget.cxx */,
2D20F9E908C603EC00A73076 /* DataGridOpsWidget.hxx */,
DCC2FDF72566AD8800FA5E81 /* DataGridRamWidget.cxx */,
DCC2FDF82566AD8800FA5E81 /* DataGridRamWidget.hxx */,
2D20F9EA08C603EC00A73076 /* DataGridWidget.cxx */,
2D20F9EB08C603EC00A73076 /* DataGridWidget.hxx */,
2D20F9EC08C603EC00A73076 /* DebuggerDialog.cxx */,
@ -2167,6 +2177,8 @@
DCA82C6E1FEB4E780059340F /* TimeMachine.hxx */,
DCA82C6F1FEB4E780059340F /* TimeMachineDialog.cxx */,
DCA82C701FEB4E780059340F /* TimeMachineDialog.hxx */,
DCC2FDF4255EB82500FA5E81 /* ToolTip.cxx */,
DCC2FDF3255EB82500FA5E81 /* ToolTip.hxx */,
DC8078E60B4BD697005E9305 /* UIDialog.cxx */,
DC8078E70B4BD697005E9305 /* UIDialog.hxx */,
DCBA539825557E2800087DD7 /* UndoHandler.cxx */,
@ -2572,6 +2584,7 @@
E08FCD5823A037EB0051F59B /* QisBlitter.hxx in Headers */,
DCA078351F8C1B04008EFEE5 /* SDL_lib.hxx in Headers */,
DCDA03B11A2009BB00711920 /* CartWD.hxx in Headers */,
DCC2FDFA2566AD8800FA5E81 /* DataGridRamWidget.hxx in Headers */,
2D91745909BA90380026E9FF /* PromptWidget.hxx in Headers */,
DC3C9BC62469C8F700CF2D47 /* PaletteHandler.hxx in Headers */,
2D91745A09BA90380026E9FF /* RamWidget.hxx in Headers */,
@ -2651,6 +2664,7 @@
DC5D1AA7102C6FC900E59AC1 /* Stack.hxx in Headers */,
DCF7B0DE10A762FC007A2870 /* CartF0.hxx in Headers */,
DCF7B0E010A762FC007A2870 /* CartFA.hxx in Headers */,
DCC2FDF5255EB82500FA5E81 /* ToolTip.hxx in Headers */,
DCC527D110B9DA19005E1287 /* Device.hxx in Headers */,
DC6F394E21B897F300897AD8 /* ThreadDebugging.hxx in Headers */,
DCC527D310B9DA19005E1287 /* M6502.hxx in Headers */,
@ -3191,6 +3205,7 @@
DC676A591729A0B000E4E73D /* CartSBWidget.cxx in Sources */,
DC3C9BCC2469C93D00CF2D47 /* EmulationDialog.cxx in Sources */,
DC676A5B1729A0B000E4E73D /* CartX07Widget.cxx in Sources */,
DCC2FDF92566AD8800FA5E81 /* DataGridRamWidget.cxx in Sources */,
DC7A24DF173B1DBC00B20FE9 /* FileListWidget.cxx in Sources */,
DC13B53F176FF2F500B8B4BB /* RomListSettings.cxx in Sources */,
DC3EE8581E2C0E6D00905161 /* crc32.c in Sources */,
@ -3205,6 +3220,7 @@
DCAACB0E188D636F00A4D282 /* Cart4KSCWidget.cxx in Sources */,
DCB60AD02543100900A5C1D2 /* FBBackendSDL2.cxx in Sources */,
DCAACB10188D636F00A4D282 /* CartBFSCWidget.cxx in Sources */,
DCC2FDF6255EB82500FA5E81 /* ToolTip.cxx in Sources */,
DCAACB12188D636F00A4D282 /* CartBFWidget.cxx in Sources */,
DCAACB14188D636F00A4D282 /* CartDFSCWidget.cxx in Sources */,
DCAACB16188D636F00A4D282 /* CartDFWidget.cxx in Sources */,
@ -3389,7 +3405,7 @@
.,
"$(HOME)/Library/Frameworks",
);
GCC_ENABLE_CPP_RTTI = NO;
GCC_ENABLE_CPP_RTTI = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_VERSION = "";
@ -3464,7 +3480,7 @@
.,
"$(HOME)/Library/Frameworks",
);
GCC_ENABLE_CPP_RTTI = NO;
GCC_ENABLE_CPP_RTTI = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 3;
GCC_VERSION = "";

View File

@ -681,6 +681,7 @@
<ClCompile Include="..\debugger\gui\CartX07Widget.cxx">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-NoDebugger|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\debugger\gui\DataGridRamWidget.cxx" />
<ClCompile Include="..\debugger\gui\DelayQueueWidget.cxx">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-NoDebugger|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -786,6 +787,7 @@
<ClCompile Include="..\gui\TimeLineWidget.cxx" />
<ClCompile Include="..\gui\TimeMachine.cxx" />
<ClCompile Include="..\gui\TimeMachineDialog.cxx" />
<ClCompile Include="..\gui\ToolTip.cxx" />
<ClCompile Include="..\gui\UndoHandler.cxx" />
<ClCompile Include="..\gui\WhatsNewDialog.cxx" />
<ClCompile Include="FSNodeWINDOWS.cxx" />
@ -1705,6 +1707,7 @@
<ClInclude Include="..\debugger\gui\ControllerWidget.hxx">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-NoDebugger|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="..\debugger\gui\DataGridRamWidget.hxx" />
<ClInclude Include="..\debugger\gui\DelayQueueWidget.hxx">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-NoDebugger|x64'">true</ExcludedFromBuild>
</ClInclude>
@ -1838,6 +1841,7 @@
<ClInclude Include="..\gui\TimeLineWidget.hxx" />
<ClInclude Include="..\gui\TimeMachine.hxx" />
<ClInclude Include="..\gui\TimeMachineDialog.hxx" />
<ClInclude Include="..\gui\ToolTip.hxx" />
<ClInclude Include="..\gui\UndoHandler.hxx" />
<ClInclude Include="..\gui\WhatsNewDialog.hxx" />
<ClInclude Include="..\libpng\pngdebug.h" />

View File

@ -1032,6 +1032,12 @@
<ClCompile Include="..\gui\UndoHandler.cxx">
<Filter>Source Files\gui</Filter>
</ClCompile>
<ClCompile Include="..\gui\ToolTip.cxx">
<Filter>Source Files\gui</Filter>
</ClCompile>
<ClCompile Include="..\debugger\gui\DataGridRamWidget.cxx">
<Filter>Source Files\debugger</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\common\bspf.hxx">
@ -2123,6 +2129,12 @@
<ClInclude Include="..\gui\UndoHandler.hxx">
<Filter>Header Files\gui</Filter>
</ClInclude>
<ClInclude Include="..\gui\ToolTip.hxx">
<Filter>Header Files\gui</Filter>
</ClInclude>
<ClInclude Include="..\debugger\gui\DataGridRamWidget.hxx">
<Filter>Header Files\debugger</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="stella.ico">