First pass at fixing superfluous re-draws in the UI.

- This addresses issue 158, and reduces CPU usage to near 0% when no changes are happening
- This returns the code to the same performance levels as version 3.x.
This commit is contained in:
Stephen Anthony 2018-07-25 08:48:21 -02:30
parent a20bb6e95d
commit e691853f0e
30 changed files with 318 additions and 254 deletions

View File

@ -125,20 +125,13 @@ void FBSurfaceSDL2::translateCoords(Int32& x, Int32& y) const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBSurfaceSDL2::render() bool FBSurfaceSDL2::render()
{ {
if(mySurfaceIsDirty && myIsVisible) if(myIsVisible)
{ {
//cerr << "src: x=" << mySrcR.x << ", y=" << mySrcR.y << ", w=" << mySrcR.w << ", h=" << mySrcR.h << endl;
//cerr << "dst: x=" << myDstR.x << ", y=" << myDstR.y << ", w=" << myDstR.w << ", h=" << myDstR.h << endl;
//cerr << "render()\n";
if(myTexAccess == SDL_TEXTUREACCESS_STREAMING) if(myTexAccess == SDL_TEXTUREACCESS_STREAMING)
SDL_UpdateTexture(myTexture, &mySrcR, mySurface->pixels, mySurface->pitch); SDL_UpdateTexture(myTexture, &mySrcR, mySurface->pixels, mySurface->pitch);
SDL_RenderCopy(myFB.myRenderer, myTexture, &mySrcR, &myDstR); SDL_RenderCopy(myFB.myRenderer, myTexture, &mySrcR, &myDstR);
mySurfaceIsDirty = false; return true;
// Let postFrameUpdate() know that a change has been made
return myFB.myDirtyFlag = true;
} }
return false; return false;
} }

View File

@ -32,8 +32,7 @@
FrameBufferSDL2::FrameBufferSDL2(OSystem& osystem) FrameBufferSDL2::FrameBufferSDL2(OSystem& osystem)
: FrameBuffer(osystem), : FrameBuffer(osystem),
myWindow(nullptr), myWindow(nullptr),
myRenderer(nullptr), myRenderer(nullptr)
myDirtyFlag(true)
{ {
// Initialize SDL2 context // Initialize SDL2 context
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK) < 0) if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK) < 0)
@ -275,7 +274,6 @@ string FrameBufferSDL2::about() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL2::invalidate() void FrameBufferSDL2::invalidate()
{ {
myDirtyFlag = true;
SDL_RenderClear(myRenderer); SDL_RenderClear(myRenderer);
} }
@ -302,14 +300,10 @@ bool FrameBufferSDL2::fullScreen() const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL2::postFrameUpdate() void FrameBufferSDL2::renderToScreen()
{ {
if(myDirtyFlag) // Show all changes made to the renderer
{ SDL_RenderPresent(myRenderer);
// Now show all changes made to the renderer
SDL_RenderPresent(myRenderer);
myDirtyFlag = false;
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -347,5 +341,5 @@ void FrameBufferSDL2::readPixels(uInt8* pixels, uInt32 pitch,
void FrameBufferSDL2::clear() void FrameBufferSDL2::clear()
{ {
invalidate(); invalidate();
postFrameUpdate(); renderToScreen();
} }

View File

@ -164,9 +164,10 @@ class FrameBufferSDL2 : public FrameBuffer
string about() const override; string about() const override;
/** /**
This method is called after any drawing is done (per-frame). This method must be called after all drawing is done, and indicates
that the buffers should be pushed to the physical screen.
*/ */
void postFrameUpdate() override; void renderToScreen() override;
private: private:
// The SDL video buffer // The SDL video buffer
@ -176,9 +177,6 @@ class FrameBufferSDL2 : public FrameBuffer
// Used by mapRGB (when palettes are created) // Used by mapRGB (when palettes are created)
SDL_PixelFormat* myPixelFormat; SDL_PixelFormat* myPixelFormat;
// Indicates that the renderer has been modified, and should be redrawn
bool myDirtyFlag;
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
FrameBufferSDL2() = delete; FrameBufferSDL2() = delete;

View File

@ -793,7 +793,7 @@ void Debugger::unlockSystem()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::canExit() const bool Debugger::canExit() const
{ {
return myDialogStack.top() == baseDialog(); return !myDialogStack.empty() && myDialogStack.top() == baseDialog();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -221,72 +221,84 @@ void DebuggerDialog::handleCommand(CommandSender* sender, int cmd,
void DebuggerDialog::doStep() void DebuggerDialog::doStep()
{ {
instance().debugger().parser().run("step"); instance().debugger().parser().run("step");
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doTrace() void DebuggerDialog::doTrace()
{ {
instance().debugger().parser().run("trace"); instance().debugger().parser().run("trace");
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doAdvance() void DebuggerDialog::doAdvance()
{ {
instance().debugger().parser().run("frame #1"); instance().debugger().parser().run("frame #1");
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doScanlineAdvance() void DebuggerDialog::doScanlineAdvance()
{ {
instance().debugger().parser().run("scanline #1"); instance().debugger().parser().run("scanline #1");
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doRewind() void DebuggerDialog::doRewind()
{ {
instance().debugger().parser().run("rewind"); instance().debugger().parser().run("rewind");
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doUnwind() void DebuggerDialog::doUnwind()
{ {
instance().debugger().parser().run("unwind"); instance().debugger().parser().run("unwind");
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doRewind10() void DebuggerDialog::doRewind10()
{ {
instance().debugger().parser().run("rewind #10"); instance().debugger().parser().run("rewind #10");
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doUnwind10() void DebuggerDialog::doUnwind10()
{ {
instance().debugger().parser().run("unwind #10"); instance().debugger().parser().run("unwind #10");
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doRewindAll() void DebuggerDialog::doRewindAll()
{ {
instance().debugger().parser().run("rewind #1000"); instance().debugger().parser().run("rewind #1000");
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doUnwindAll() void DebuggerDialog::doUnwindAll()
{ {
instance().debugger().parser().run("unwind #1000"); instance().debugger().parser().run("unwind #1000");
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doExitDebugger() void DebuggerDialog::doExitDebugger()
{ {
instance().debugger().parser().run("run"); instance().debugger().parser().run("run");
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doExitRom() void DebuggerDialog::doExitRom()
{ {
instance().debugger().parser().run("exitrom"); instance().debugger().parser().run("exitrom");
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -130,8 +130,6 @@ class DebuggerDialog : public Dialog
ButtonWidget* myRewindButton; ButtonWidget* myRewindButton;
ButtonWidget* myUnwindButton; ButtonWidget* myUnwindButton;
//ButtonWidget* myOptionsButton;
unique_ptr<GUI::MessageBox> myFatalError; unique_ptr<GUI::MessageBox> myFatalError;
unique_ptr<OptionsDialog> myOptions; unique_ptr<OptionsDialog> myOptions;

View File

@ -283,7 +283,8 @@ void EventHandler::handleSystemEvent(SystemEvent e, int, int)
switch(e) switch(e)
{ {
case SystemEvent::WINDOW_EXPOSED: case SystemEvent::WINDOW_EXPOSED:
myOSystem.frameBuffer().update(); case SystemEvent::WINDOW_RESIZED:
myOSystem.frameBuffer().update(true); // force full update
break; break;
case SystemEvent::WINDOW_FOCUS_GAINED: case SystemEvent::WINDOW_FOCUS_GAINED:

View File

@ -219,12 +219,6 @@ class FBSurface
uInt32 color, TextAlign align = TextAlign::Left, uInt32 color, TextAlign align = TextAlign::Left,
int deltax = 0, bool useEllipsis = true, uInt32 shadowColor = 0); int deltax = 0, bool useEllipsis = true, uInt32 shadowColor = 0);
/**
This method should be called to indicate that the surface has been
modified, and should be redrawn at the next interval.
*/
virtual void setDirty() { }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Note: The following methods are FBSurface-specific, and must be // Note: The following methods are FBSurface-specific, and must be
// implemented in child classes. // implemented in child classes.
@ -331,6 +325,13 @@ class FBSurface
static void setPalette(const uInt32* palette) { myPalette = palette; } static void setPalette(const uInt32* palette) { myPalette = palette; }
protected:
/**
This method must be called to indicate that the surface has been
modified, and should be redrawn at the next interval.
*/
virtual void setDirty() = 0;
protected: protected:
static const uInt32* myPalette; static const uInt32* myPalette;
uInt32* myPixels; uInt32* myPixels;

View File

@ -50,7 +50,8 @@ FrameBuffer::FrameBuffer(OSystem& osystem)
myLastScanlines(0), myLastScanlines(0),
myGrabMouse(false), myGrabMouse(false),
myCurrentModeList(nullptr) myCurrentModeList(nullptr)
{} {
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBuffer::~FrameBuffer() FrameBuffer::~FrameBuffer()
@ -258,85 +259,116 @@ FBInitStatus FrameBuffer::createDisplay(const string& title,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::update() void FrameBuffer::update(bool force)
{ {
// Determine which mode we are in (from the EventHandler) // Onscreen messages are a special case and require different handling than
// Take care of S_EMULATE mode here, otherwise let the GUI // other objects; they aren't UI dialogs in the normal sense nor are they
// figure out what to draw // TIA images, and they need to be rendered on top of everything
// The logic is split in two pieces:
// - at the top of ::update(), to determine whether underlying dialogs
// need to be force-redrawn
// - 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 |= myMsg.counter >= 0;
// 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;
invalidate();
switch(myOSystem.eventHandler().state()) switch(myOSystem.eventHandler().state())
{ {
case EventHandlerState::NONE:
case EventHandlerState::EMULATION: case EventHandlerState::EMULATION:
// Do nothing; emulation mode is handled separately (see below) // Do nothing; emulation mode is handled separately (see below)
break; return;
case EventHandlerState::PAUSE: case EventHandlerState::PAUSE:
{ {
myTIASurface->render();
// Show a pause message immediately and then every 7 seconds // Show a pause message immediately and then every 7 seconds
if (myPausedCount-- <= 0) if(myPausedCount-- <= 0)
{ {
myPausedCount = uInt32(7 * myOSystem.frameRate()); myPausedCount = uInt32(7 * myOSystem.frameRate());
showMessage("Paused", MessagePosition::MiddleCenter); showMessage("Paused", MessagePosition::MiddleCenter);
} }
if(force)
myTIASurface->render();
break; // EventHandlerState::PAUSE break; // EventHandlerState::PAUSE
} }
case EventHandlerState::OPTIONSMENU: case EventHandlerState::OPTIONSMENU:
{ {
myTIASurface->render(); force |= myOSystem.menu().needsRedraw();
myOSystem.menu().draw(true); if(force)
{
myTIASurface->render();
myOSystem.menu().draw(force);
}
break; // EventHandlerState::OPTIONSMENU break; // EventHandlerState::OPTIONSMENU
} }
case EventHandlerState::CMDMENU: case EventHandlerState::CMDMENU:
{ {
myTIASurface->render(); force |= myOSystem.commandMenu().needsRedraw();
myOSystem.commandMenu().draw(true); if(force)
{
myTIASurface->render();
myOSystem.commandMenu().draw(force);
}
break; // EventHandlerState::CMDMENU break; // EventHandlerState::CMDMENU
} }
case EventHandlerState::TIMEMACHINE: case EventHandlerState::TIMEMACHINE:
{ {
myTIASurface->render(); force |= myOSystem.timeMachine().needsRedraw();
myOSystem.timeMachine().draw(true); if(force)
{
myTIASurface->render();
myOSystem.timeMachine().draw(force);
}
break; // EventHandlerState::TIMEMACHINE break; // EventHandlerState::TIMEMACHINE
} }
case EventHandlerState::LAUNCHER: case EventHandlerState::LAUNCHER:
{ {
myOSystem.launcher().draw(true); force |= myOSystem.launcher().draw(force);
break; // EventHandlerState::LAUNCHER break; // EventHandlerState::LAUNCHER
} }
case EventHandlerState::DEBUGGER: case EventHandlerState::DEBUGGER:
{ {
#ifdef DEBUGGER_SUPPORT #ifdef DEBUGGER_SUPPORT
myOSystem.debugger().draw(true); force |= myOSystem.debugger().draw(force);
#endif #endif
break; // EventHandlerState::DEBUGGER break; // EventHandlerState::DEBUGGER
} }
case EventHandlerState::NONE:
return;
} }
// Draw any pending messages // Draw any pending messages
// The logic here determines whether to draw the message
// If the message is to be disabled, logic inside the draw method
// indicates that, and then the code at the top of this method sees
// the change and redraws everything
if(myMsg.enabled) if(myMsg.enabled)
drawMessage(); force |= drawMessage();
// Do any post-frame stuff // Push buffers to screen only when necessary
postFrameUpdate(); if(force)
renderToScreen();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::updateInEmulationMode() void FrameBuffer::updateInEmulationMode()
{ {
// Determine which mode we are in (from the EventHandler) // Update method that is specifically tailored to emulation mode
// Take care of S_EMULATE mode here, otherwise let the GUI // Typically called from a thread, so it needs to be separate from
// figure out what to draw // the normal update() method
//
// We don't worry about selective rendering here; the rendering
// always happens at the full framerate
myTIASurface->render(); myTIASurface->render();
@ -351,8 +383,8 @@ void FrameBuffer::updateInEmulationMode()
if(myMsg.enabled) if(myMsg.enabled)
drawMessage(); drawMessage();
// Do any post-frame stuff // Push buffers to screen
postFrameUpdate(); renderToScreen();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -413,7 +445,6 @@ void FrameBuffer::drawFrameStats()
myStatsMsg.surface->drawString(font(), bsinfo, XPOS, YPOS + font().getFontHeight(), myStatsMsg.surface->drawString(font(), bsinfo, XPOS, YPOS + font().getFontHeight(),
myStatsMsg.w, myStatsMsg.color, TextAlign::Left, 0, true, kBGColor); myStatsMsg.w, myStatsMsg.color, TextAlign::Left, 0, true, kBGColor);
myStatsMsg.surface->setDirty();
myStatsMsg.surface->setDstPos(myImageRect.x() + 10, myImageRect.y() + 8); myStatsMsg.surface->setDstPos(myImageRect.x() + 10, myImageRect.y() + 8);
myStatsMsg.surface->render(); myStatsMsg.surface->render();
} }
@ -448,13 +479,26 @@ void FrameBuffer::enableMessages(bool enable)
// Erase old messages on the screen // Erase old messages on the screen
myMsg.enabled = false; myMsg.enabled = false;
myMsg.counter = 0; myMsg.counter = 0;
update(); // Force update immediately update(true); // Force update immediately
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline void FrameBuffer::drawMessage() inline bool FrameBuffer::drawMessage()
{ {
// Either erase the entire message (when time is reached),
// or show again this frame
if(myMsg.counter == 0)
{
myMsg.enabled = false;
return true;
}
else if(myMsg.counter < 0)
{
myMsg.enabled = false;
return false;
}
// Draw the bounded box and text // Draw the bounded box and text
const GUI::Rect& dst = myMsg.surface->dstRect(); const GUI::Rect& dst = myMsg.surface->dstRect();
@ -511,16 +555,10 @@ inline void FrameBuffer::drawMessage()
myMsg.surface->frameRect(0, 0, myMsg.w, myMsg.h, kColor); myMsg.surface->frameRect(0, 0, myMsg.w, myMsg.h, kColor);
myMsg.surface->drawString(font(), myMsg.text, 5, 4, myMsg.surface->drawString(font(), myMsg.text, 5, 4,
myMsg.w, myMsg.color, TextAlign::Left); myMsg.w, myMsg.color, TextAlign::Left);
myMsg.surface->render();
myMsg.counter--;
// Either erase the entire message (when time is reached), return true;
// or show again this frame
if(myMsg.counter-- > 0)
{
myMsg.surface->setDirty();
myMsg.surface->render();
}
else
myMsg.enabled = false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -564,6 +602,8 @@ void FrameBuffer::resetSurfaces()
freeSurfaces(); freeSurfaces();
reloadSurfaces(); reloadSurfaces();
update(true); // force full update
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -589,6 +629,8 @@ void FrameBuffer::stateChanged(EventHandlerState state)
// Make sure any onscreen messages are removed // Make sure any onscreen messages are removed
myMsg.enabled = false; myMsg.enabled = false;
myMsg.counter = 0; myMsg.counter = 0;
update(true); // force full update
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -113,10 +113,9 @@ class FrameBuffer
/** /**
Updates the display, which depending on the current mode could mean Updates the display, which depending on the current mode could mean
drawing the TIA, any pending menus, etc. Returns the numbers of CPU cycles drawing the TIA, any pending menus, etc.
spent during emulation, or -1 if not applicable.
*/ */
void update(); void update(bool force = false);
/** /**
There is a dedicated update method for emulation mode. There is a dedicated update method for emulation mode.
@ -379,9 +378,10 @@ class FrameBuffer
virtual void setWindowIcon() = 0; virtual void setWindowIcon() = 0;
/** /**
This method is called after any drawing is done (per-frame). This method must be called after all drawing is done, and indicates
that the buffers should be pushed to the physical screen.
*/ */
virtual void postFrameUpdate() = 0; virtual void renderToScreen() = 0;
/** /**
This method is called to provide information about the FrameBuffer. This method is called to provide information about the FrameBuffer.
@ -398,8 +398,10 @@ class FrameBuffer
private: private:
/** /**
Draw pending messages. Draw pending messages.
@return Indicates whether any changes actually occurred.
*/ */
void drawMessage(); bool drawMessage();
/** /**
Frees and reloads all surfaces that the framebuffer knows about. Frees and reloads all surfaces that the framebuffer knows about.
@ -521,7 +523,7 @@ class FrameBuffer
bool enabled; bool enabled;
Message() Message()
: counter(0), x(0), y(0), w(0), h(0), position(MessagePosition::BottomCenter), : counter(-1), x(0), y(0), w(0), h(0), position(MessagePosition::BottomCenter),
color(0), enabled(false) { } color(0), enabled(false) { }
}; };
Message myMsg; Message myMsg;

View File

@ -704,8 +704,8 @@ void OSystem::mainLoop()
// Dispatch emulation and render frame (if applicable) // Dispatch emulation and render frame (if applicable)
timesliceSeconds = dispatchEmulation(emulationWorker); timesliceSeconds = dispatchEmulation(emulationWorker);
else { else {
// Render the GUI with 30 Hz in all other modes // Render the GUI with 60 Hz in all other modes
timesliceSeconds = 1. / 30.; timesliceSeconds = 1. / 60.;
myFrameBuffer->update(); myFrameBuffer->update();
} }

View File

@ -206,7 +206,6 @@ uInt32 TIASurface::enableScanlines(int relative, int absolute)
attr.blendalpha = std::min(100u, attr.blendalpha); attr.blendalpha = std::min(100u, attr.blendalpha);
mySLineSurface->applyAttributes(); mySLineSurface->applyAttributes();
mySLineSurface->setDirty();
return attr.blendalpha; return attr.blendalpha;
} }
@ -217,7 +216,6 @@ void TIASurface::enableScanlineInterpolation(bool enable)
FBSurface::Attributes& attr = mySLineSurface->attributes(); FBSurface::Attributes& attr = mySLineSurface->attributes();
attr.smoothing = enable; attr.smoothing = enable;
mySLineSurface->applyAttributes(); mySLineSurface->applyAttributes();
mySLineSurface->setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -231,8 +229,6 @@ void TIASurface::enablePhosphor(bool enable, int blend)
myPhosphorPercent = blend / 100.0; myPhosphorPercent = blend / 100.0;
myFilter = Filter(enable ? uInt8(myFilter) | 0x01 : uInt8(myFilter) & 0x10); myFilter = Filter(enable ? uInt8(myFilter) | 0x01 : uInt8(myFilter) & 0x10);
myTiaSurface->setDirty();
mySLineSurface->setDirty();
memset(myRGBFramebuffer, 0, sizeof(myRGBFramebuffer)); memset(myRGBFramebuffer, 0, sizeof(myRGBFramebuffer));
// Precalculate the average colors for the 'phosphor' effect // Precalculate the average colors for the 'phosphor' effect
@ -283,8 +279,6 @@ void TIASurface::enableNTSC(bool enable)
sl_attr.blendalpha = myOSystem.settings().getInt("tv.scanlines"); sl_attr.blendalpha = myOSystem.settings().getInt("tv.scanlines");
mySLineSurface->applyAttributes(); mySLineSurface->applyAttributes();
myTiaSurface->setDirty();
mySLineSurface->setDirty();
memset(myRGBFramebuffer, 0, sizeof(myRGBFramebuffer)); memset(myRGBFramebuffer, 0, sizeof(myRGBFramebuffer));
} }
@ -379,15 +373,11 @@ void TIASurface::render()
} }
// Draw TIA image // Draw TIA image
myTiaSurface->setDirty();
myTiaSurface->render(); myTiaSurface->render();
// Draw overlaying scanlines // Draw overlaying scanlines
if(myScanlinesEnabled) if(myScanlinesEnabled)
{
mySLineSurface->setDirty();
mySLineSurface->render(); mySLineSurface->render();
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -424,14 +414,10 @@ void TIASurface::reRender()
if (myUsePhosphor) if (myUsePhosphor)
{ {
// Draw TIA image // Draw TIA image
myTiaSurface->setDirty();
myTiaSurface->render(); myTiaSurface->render();
// Draw overlaying scanlines // Draw overlaying scanlines
if (myScanlinesEnabled) if (myScanlinesEnabled)
{
mySLineSurface->setDirty();
mySLineSurface->render(); mySLineSurface->render();
}
} }
} }

View File

@ -226,7 +226,7 @@ void AboutDialog::displayInfo()
} }
// Redraw entire dialog // Redraw entire dialog
_dirty = true; setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -186,7 +186,6 @@ void AudioDialog::updateSettingsWithPreset(AudioSettings& audioSettings)
myResamplingPopup->setSelected(static_cast<int>(audioSettings.resamplingQuality())); myResamplingPopup->setSelected(static_cast<int>(audioSettings.resamplingQuality()));
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AudioDialog::saveConfig() void AudioDialog::saveConfig()
{ {
@ -235,8 +234,6 @@ void AudioDialog::setDefaults()
else updatePreset(); else updatePreset();
updateEnabledState(); updateEnabledState();
_dirty = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -118,7 +118,7 @@ void ComboDialog::setDefaults()
for(int i = 0; i < 8; ++i) for(int i = 0; i < 8; ++i)
myEvents[i]->setSelected("None", "-1"); myEvents[i]->setSelected("None", "-1");
_dirty = true; setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -546,45 +546,38 @@ void ContextMenu::drawDialog()
// by the ScummVM guys, so I'm not going to mess with it. // by the ScummVM guys, so I'm not going to mess with it.
FBSurface& s = surface(); FBSurface& s = surface();
if(_dirty) // Draw menu border and background
s.fillRect(_x+1, _y+1, _w-2, _h-2, kWidColor);
s.frameRect(_x, _y, _w, _h, kTextColor);
// Draw the entries, taking scroll buttons into account
int x = _x + 1, y = _y + 1, w = _w - 2;
// Show top scroll area
int offset = _selectedOffset;
if(_showScroll)
{ {
// Draw menu border and background s.hLine(x, y+_rowHeight-1, w+2, kColor);
s.fillRect(_x+1, _y+1, _w-2, _h-2, kWidColor); s.drawBitmap(up_arrow, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, _scrollUpColor, 8);
s.frameRect(_x, _y, _w, _h, kTextColor); y += _rowHeight;
offset--;
// Draw the entries, taking scroll buttons into account
int x = _x + 1, y = _y + 1, w = _w - 2;
// Show top scroll area
int offset = _selectedOffset;
if(_showScroll)
{
s.hLine(x, y+_rowHeight-1, w+2, kColor);
s.drawBitmap(up_arrow, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, _scrollUpColor, 8);
y += _rowHeight;
offset--;
}
for(int i = _firstEntry, current = 0; i < _firstEntry + _numEntries; ++i, ++current)
{
bool hilite = offset == current;
if(hilite) s.fillRect(x, y, w, _rowHeight, kTextColorHi);
s.drawString(_font, _entries[i].first, x + 1, y + 2, w,
!hilite ? kTextColor : kTextColorInv);
y += _rowHeight;
}
// Show bottom scroll area
if(_showScroll)
{
s.hLine(x, y, w+2, kColor);
s.drawBitmap(down_arrow, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, _scrollDnColor, 8);
}
s.setDirty();
_dirty = false;
} }
// Commit surface changes to screen for(int i = _firstEntry, current = 0; i < _firstEntry + _numEntries; ++i, ++current)
s.render(); {
bool hilite = offset == current;
if(hilite) s.fillRect(x, y, w, _rowHeight, kTextColorHi);
s.drawString(_font, _entries[i].first, x + 1, y + 2, w,
!hilite ? kTextColor : kTextColorInv);
y += _rowHeight;
}
// Show bottom scroll area
if(_showScroll)
{
s.hLine(x, y, w+2, kColor);
s.drawBitmap(down_arrow, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, _scrollDnColor, 8);
}
setDirty();
} }

View File

@ -64,6 +64,7 @@ Dialog::Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font
_flags(WIDGET_ENABLED | WIDGET_BORDER | WIDGET_CLEARBG) _flags(WIDGET_ENABLED | WIDGET_BORDER | WIDGET_CLEARBG)
{ {
setTitle(title); setTitle(title);
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -86,7 +87,7 @@ Dialog::~Dialog()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::open(bool refresh) void Dialog::open()
{ {
// Make sure we have a valid surface to draw into // Make sure we have a valid surface to draw into
// Technically, this shouldn't be needed until drawDialog(), but some // Technically, this shouldn't be needed until drawDialog(), but some
@ -103,10 +104,12 @@ void Dialog::open(bool refresh)
buildCurrentFocusList(); buildCurrentFocusList();
_visible = true; _visible = true;
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::close(bool refresh) void Dialog::close()
{ {
if (_mouseWidget) if (_mouseWidget)
{ {
@ -119,6 +122,7 @@ void Dialog::close(bool refresh)
_visible = false; _visible = false;
parent().removeDialog(); parent().removeDialog();
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -131,6 +135,8 @@ void Dialog::setTitle(const string& title)
else else
_th = _font.getLineHeight() + 4; _th = _font.getLineHeight() + 4;
_h += _th; _h += _th;
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -144,6 +150,29 @@ void Dialog::center()
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Dialog::render()
{
if(!_dirty)
return false;
// Draw this dialog
center();
drawDialog();
// 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){
surface->render();
});
}
_dirty = false;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::releaseFocus() void Dialog::releaseFocus()
{ {
@ -291,6 +320,7 @@ void Dialog::addSurface(shared_ptr<FBSurface> surface)
mySurfaceStack.push(surface); mySurfaceStack.push(surface);
} }
static int COUNT = 1;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::drawDialog() void Dialog::drawDialog()
{ {
@ -299,63 +329,46 @@ void Dialog::drawDialog()
FBSurface& s = surface(); FBSurface& s = surface();
if(_dirty) cerr << COUNT++ << " Dialog::drawDialog()\n";
// Dialog is still on top if e.g a ContextMenu is opened
bool onTop = parent().myDialogStack.top() == this
|| (parent().myDialogStack.get(parent().myDialogStack.size() - 2) == this
&& !parent().myDialogStack.top()->hasTitle());
if(_flags & WIDGET_CLEARBG)
{ {
// dialog is still on top if e.g a ContextMenu is opened // cerr << "Dialog::drawDialog(): w = " << _w << ", h = " << _h << " @ " << &s << endl << endl;
bool onTop = parent().myDialogStack.top() == this s.fillRect(_x, _y + _th, _w, _h - _th, onTop ? kDlgColor : kBGColorLo);
|| (parent().myDialogStack.get(parent().myDialogStack.size() - 2) == this if(_th)
&& !parent().myDialogStack.top()->hasTitle());
if(_flags & WIDGET_CLEARBG)
{ {
// cerr << "Dialog::drawDialog(): w = " << _w << ", h = " << _h << " @ " << &s << endl << endl; s.fillRect(_x, _y, _w, _th, onTop ? kColorTitleBar : kColorTitleBarLo);
s.fillRect(_x, _y + _th, _w, _h - _th, onTop ? kDlgColor : kBGColorLo); s.drawString(_font, _title, _x + 10, _y + 2 + 1, _font.getStringWidth(_title),
if(_th) onTop ? kColorTitleText : kColorTitleTextLo);
{
s.fillRect(_x, _y, _w, _th, onTop ? kColorTitleBar : kColorTitleBarLo);
s.drawString(_font, _title, _x + 10, _y + 2 + 1, _font.getStringWidth(_title),
onTop ? kColorTitleText : kColorTitleTextLo);
}
} }
else }
s.invalidate(); else
if(_flags & WIDGET_BORDER) s.invalidate();
s.frameRect(_x, _y, _w, _h, onTop ? kColor : kShadowColor); if(_flags & WIDGET_BORDER)
s.frameRect(_x, _y, _w, _h, onTop ? kColor : kShadowColor);
// Make all child widget dirty // Make all child widget dirty
Widget* w = _firstWidget; Widget* w = _firstWidget;
Widget::setDirtyInChain(w); Widget::setDirtyInChain(w);
// Draw all children // Draw all children
w = _firstWidget; w = _firstWidget;
while(w) while(w)
{ {
w->draw(); w->draw();
w = w->_next; w = w->_next;
}
// Draw outlines for focused widgets
// Don't change focus, since this will trigger lost and received
// focus events
if(_focusedWidget)
_focusedWidget = Widget::setFocusForChain(this, getFocusList(),
_focusedWidget, 0, false);
// Tell the surface(s) this area is dirty
s.setDirty();
_dirty = false;
} }
// Commit surface changes to screen; also render any extra surfaces // Draw outlines for focused widgets
// Extra surfaces must be rendered afterwards, so they are drawn on top // Don't change focus, since this will trigger lost and received
if(s.render()) // focus events
{ if(_focusedWidget)
mySurfaceStack.applyAll([](shared_ptr<FBSurface>& surface){ _focusedWidget = Widget::setFocusForChain(this, getFocusList(),
surface->setDirty(); _focusedWidget, 0, false);
surface->render();
});
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -51,8 +51,8 @@ class Dialog : public GuiObject
virtual ~Dialog(); virtual ~Dialog();
void open(bool refresh = true); void open();
void close(bool refresh = true); void close();
bool isVisible() const override { return _visible; } bool isVisible() const override { return _visible; }
@ -62,6 +62,12 @@ class Dialog : public GuiObject
virtual void saveConfig() { } virtual void saveConfig() { }
virtual void setDefaults() { } 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 addFocusWidget(Widget* w) override; void addFocusWidget(Widget* w) override;
void addToFocusList(WidgetArray& list) override; void addToFocusList(WidgetArray& list) override;
void addToFocusList(WidgetArray& list, TabWidget* w, int tabId); void addToFocusList(WidgetArray& list, TabWidget* w, int tabId);
@ -190,6 +196,7 @@ class Dialog : public GuiObject
int _tabID; int _tabID;
int _flags; int _flags;
bool _dirty;
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported

View File

@ -48,12 +48,12 @@ DialogContainer::~DialogContainer()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DialogContainer::updateTime(uInt64 time) void DialogContainer::updateTime(uInt64 time)
{ {
// We only need millisecond precision
myTime = time / 1000;
if(myDialogStack.empty()) if(myDialogStack.empty())
return; return;
// We only need millisecond precision
myTime = time / 1000;
// Check for pending continuous events and send them to the active dialog box // Check for pending continuous events and send them to the active dialog box
Dialog* activeDialog = myDialogStack.top(); Dialog* activeDialog = myDialogStack.top();
@ -98,19 +98,31 @@ void DialogContainer::updateTime(uInt64 time)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DialogContainer::draw(bool full) bool DialogContainer::draw(bool full)
{ {
// Draw all the dialogs on the stack when we want a full refresh if(myDialogStack.empty())
return false;
// Make the top dialog dirty if a full redraw is requested
if(full) if(full)
{ myDialogStack.top()->setDirty();
myDialogStack.applyAll([](Dialog*& d){
d->center(); // If the top dialog is dirty, then all below it must be redrawn too
bool dirty = needsRedraw();
myDialogStack.applyAll([&](Dialog*& d){
if(dirty)
d->setDirty(); d->setDirty();
d->drawDialog(); full |= d->render();
}); });
}
else if(!myDialogStack.empty()) return full;
myDialogStack.top()->drawDialog(); }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool DialogContainer::needsRedraw() const
{
return !myDialogStack.empty() ? myDialogStack.top()->isDirty() : false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -121,14 +133,21 @@ void DialogContainer::addDialog(Dialog* d)
myOSystem.frameBuffer().showMessage( myOSystem.frameBuffer().showMessage(
"Unable to show dialog box; FIX THE CODE"); "Unable to show dialog box; FIX THE CODE");
else else
{
d->setDirty();
myDialogStack.push(d); myDialogStack.push(d);
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DialogContainer::removeDialog() void DialogContainer::removeDialog()
{ {
if(!myDialogStack.empty()) if(!myDialogStack.empty())
{
myDialogStack.pop(); myDialogStack.pop();
if(!myDialogStack.empty())
myDialogStack.top()->setDirty();
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -136,9 +155,9 @@ void DialogContainer::reStack()
{ {
// Pop all items from the stack, and then add the base menu // Pop all items from the stack, and then add the base menu
while(!myDialogStack.empty()) while(!myDialogStack.empty())
myDialogStack.top()->close(false); // don't force a refresh myDialogStack.top()->close();
myBaseDialog->open(false); // don't force a refresh myBaseDialog->open();
// Reset all continuous events // Reset all continuous events
reset(); reset();

View File

@ -121,8 +121,15 @@ class DialogContainer
/** /**
Draw the stack of menus (full indicates to redraw all items). Draw the stack of menus (full indicates to redraw all items).
@return Answers whether any drawing actually occurred.
*/ */
void draw(bool full = false); bool draw(bool full = false);
/**
Answers whether a full redraw is required.
*/
bool needsRedraw() const;
/** /**
Reset dialog stack to the main configuration menu. Reset dialog stack to the main configuration menu.

View File

@ -274,8 +274,6 @@ void GlobalPropsDialog::setDefaults()
myHoldSelect->setState(false); myHoldSelect->setState(false);
myHoldReset->setState(false); myHoldReset->setState(false);
_dirty = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -60,7 +60,6 @@ class GuiObject : public CommandReceiver
myParent(parent), myParent(parent),
myDialog(dialog), myDialog(dialog),
_x(x), _y(y), _w(w), _h(h), _x(x), _y(y), _w(w), _h(h),
_dirty(false),
_firstWidget(nullptr) { } _firstWidget(nullptr) { }
virtual ~GuiObject() = default; virtual ~GuiObject() = default;
@ -69,8 +68,6 @@ class GuiObject : public CommandReceiver
DialogContainer& parent() const { return myParent; } DialogContainer& parent() const { return myParent; }
Dialog& dialog() const { return myDialog; } Dialog& dialog() const { return myDialog; }
void setDirty() { _dirty = true; }
virtual int getAbsX() const { return _x; } virtual int getAbsX() const { return _x; }
virtual int getAbsY() const { return _y; } virtual int getAbsY() const { return _y; }
virtual int getChildX() const { return getAbsX(); } virtual int getChildX() const { return getAbsX(); }
@ -82,6 +79,7 @@ class GuiObject : public CommandReceiver
virtual void setHeight(int h) { _h = h; } virtual void setHeight(int h) { _h = h; }
virtual bool isVisible() const = 0; virtual bool isVisible() const = 0;
virtual void setDirty() = 0;
/** Add given widget(s) to the focus list */ /** Add given widget(s) to the focus list */
virtual void addFocusWidget(Widget* w) = 0; virtual void addFocusWidget(Widget* w) = 0;
@ -107,7 +105,6 @@ class GuiObject : public CommandReceiver
protected: protected:
int _x, _y, _w, _h; int _x, _y, _w, _h;
bool _dirty;
Widget* _firstWidget; Widget* _firstWidget;
WidgetArray _focusList; WidgetArray _focusList;

View File

@ -185,8 +185,6 @@ void HelpDialog::displayInfo()
myKey[i]->setLabel(myKeyStr[i]); myKey[i]->setLabel(myKeyStr[i]);
myDesc[i]->setLabel(myDescStr[i]); myDesc[i]->setLabel(myDescStr[i]);
} }
_dirty = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -399,8 +399,6 @@ void InputDialog::setDefaults()
default: default:
break; break;
} }
_dirty = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -178,8 +178,6 @@ void LauncherFilterDialog::saveConfig()
void LauncherFilterDialog::setDefaults() void LauncherFilterDialog::setDefaults()
{ {
handleFileTypeChange("allroms"); handleFileTypeChange("allroms");
_dirty = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -62,11 +62,10 @@ ListWidget::ListWidget(GuiObject* boss, const GUI::Font& font,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ListWidget::setSelected(int item) void ListWidget::setSelected(int item)
{ {
setDirty();
if(item < 0 || item >= int(_list.size())) if(item < 0 || item >= int(_list.size()))
{
setDirty(); // Simply redraw and exit
return; return;
}
if(isEnabled()) if(isEnabled())
{ {
@ -180,6 +179,8 @@ void ListWidget::recalc()
// Reset to normal data entry // Reset to normal data entry
abortEditMode(); abortEditMode();
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -279,8 +279,6 @@ void UIDialog::setDefaults()
default: default:
break; break;
} }
_dirty = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -551,8 +551,6 @@ void VideoDialog::setDefaults()
break; break;
} }
} }
_dirty = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -580,8 +578,6 @@ void VideoDialog::handleTVModeChange(NTSCFilter::Preset preset)
myTVScanLabel->setEnabled(scanenable); myTVScanLabel->setEnabled(scanenable);
myTVScanIntense->setEnabled(scanenable); myTVScanIntense->setEnabled(scanenable);
myTVScanInterpolate->setEnabled(scanenable); myTVScanInterpolate->setEnabled(scanenable);
_dirty = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -48,6 +48,8 @@ Widget::Widget(GuiObject* boss, const GUI::Font& font,
_fontWidth = _font.getMaxCharWidth(); _fontWidth = _font.getMaxCharWidth();
_fontHeight = _font.getLineHeight(); _fontHeight = _font.getLineHeight();
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -59,14 +61,20 @@ Widget::~Widget()
_focusList.clear(); _focusList.clear();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Widget::setDirty()
{
// A widget being dirty indicates that its parent dialog is dirty
// So we inform the parent about it
_boss->dialog().setDirty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Widget::draw() void Widget::draw()
{ {
if(!_dirty || !isVisible() || !_boss->isVisible()) if(!isVisible() || !_boss->isVisible())
return; return;
_dirty = false;
FBSurface& s = _boss->dialog().surface(); FBSurface& s = _boss->dialog().surface();
bool hasBorder = _flags & WIDGET_BORDER; bool hasBorder = _flags & WIDGET_BORDER;
@ -119,9 +127,6 @@ void Widget::draw()
w->draw(); w->draw();
w = w->_next; w = w->_next;
} }
// Tell the framebuffer this area is dirty
s.setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -224,7 +229,6 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr,
s.frameRect(x, y, w, h, kDlgColor); s.frameRect(x, y, w, h, kDlgColor);
tmp->setDirty(); tmp->setDirty();
s.setDirty();
} }
} }
@ -278,7 +282,6 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr,
s.frameRect(x, y, w, h, kWidFrameColor, FrameStyle::Dashed); s.frameRect(x, y, w, h, kWidFrameColor, FrameStyle::Dashed);
tmp->setDirty(); tmp->setDirty();
s.setDirty();
return tmp; return tmp;
} }
@ -346,6 +349,8 @@ void StaticTextWidget::drawWidget(bool hilite)
FBSurface& s = _boss->dialog().surface(); FBSurface& s = _boss->dialog().surface();
s.drawString(_font, _label, _x, _y, _w, s.drawString(_font, _label, _x, _y, _w,
isEnabled() ? _textcolor : uInt32(kColor), _align, 0, true, _shadowcolor); isEnabled() ? _textcolor : uInt32(kColor), _align, 0, true, _shadowcolor);
_boss->dialog().setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -402,14 +407,12 @@ ButtonWidget::ButtonWidget(GuiObject* boss, const GUI::Font& font,
void ButtonWidget::handleMouseEntered() void ButtonWidget::handleMouseEntered()
{ {
setFlags(WIDGET_HILITED); setFlags(WIDGET_HILITED);
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ButtonWidget::handleMouseLeft() void ButtonWidget::handleMouseLeft()
{ {
clearFlags(WIDGET_HILITED); clearFlags(WIDGET_HILITED);
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -446,6 +449,8 @@ void ButtonWidget::setBitmap(uInt32* bitmap, int bmw, int bmh)
_bmh = bmh; _bmh = bmh;
_bmw = bmw; _bmw = bmw;
_useBitmap = true; _useBitmap = true;
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -464,6 +469,8 @@ void ButtonWidget::drawWidget(bool hilite)
!isEnabled() ? /*hilite ? uInt32(kColor) :*/ uInt32(kBGColorLo) : !isEnabled() ? /*hilite ? uInt32(kColor) :*/ uInt32(kBGColorLo) :
hilite ? _textcolorhi : _textcolor, hilite ? _textcolorhi : _textcolor,
_bmw, _bmh); _bmw, _bmh);
_boss->dialog().setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -584,6 +591,7 @@ void CheckboxWidget::setEditable(bool editable)
_bgcolor = kBGColorHi; _bgcolor = kBGColorHi;
setFill(CheckboxWidget::Inactive); setFill(CheckboxWidget::Inactive);
} }
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -604,6 +612,7 @@ void CheckboxWidget::setFill(FillType type)
_drawBox = false; _drawBox = false;
break; break;
} }
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -634,6 +643,8 @@ void CheckboxWidget::drawWidget(bool hilite)
// Finally draw the label // Finally draw the label
s.drawString(_font, _label, _x + 20, _y + _textY, _w, s.drawString(_font, _label, _x + 20, _y + _textY, _w,
isEnabled() ? kTextColor : kColor); isEnabled() ? kTextColor : kColor);
_boss->dialog().setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -697,18 +708,21 @@ void SliderWidget::setValue(int value)
void SliderWidget::setMinValue(int value) void SliderWidget::setMinValue(int value)
{ {
_valueMin = value; _valueMin = value;
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SliderWidget::setMaxValue(int value) void SliderWidget::setMaxValue(int value)
{ {
_valueMax = value; _valueMax = value;
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SliderWidget::setStepValue(int value) void SliderWidget::setStepValue(int value)
{ {
_stepValue = value; _stepValue = value;
setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -868,10 +882,12 @@ void SliderWidget::drawWidget(bool hilite)
if(_valueLabelWidth > 0) if(_valueLabelWidth > 0)
s.drawString(_font, _valueLabel + _valueUnit, _x + _w - _valueLabelWidth, _y + 2, s.drawString(_font, _valueLabel + _valueUnit, _x + _w - _valueLabelWidth, _y + 2,
_valueLabelWidth, isEnabled() ? kTextColor : kColor); _valueLabelWidth, isEnabled() ? kTextColor : kColor);
_boss->dialog().setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int SliderWidget::valueToPos(int value) int SliderWidget::valueToPos(int value) const
{ {
if(value < _valueMin) value = _valueMin; if(value < _valueMin) value = _valueMin;
else if(value > _valueMax) value = _valueMax; else if(value > _valueMax) value = _valueMax;
@ -881,7 +897,7 @@ int SliderWidget::valueToPos(int value)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int SliderWidget::posToValue(int pos) int SliderWidget::posToValue(int pos) const
{ {
int value = (pos) * (_valueMax - _valueMin) / (_w - _labelWidth - _valueLabelGap - _valueLabelWidth - 4) + _valueMin; int value = (pos) * (_valueMax - _valueMin) / (_w - _labelWidth - _valueLabelGap - _valueLabelWidth - 4) + _valueMin;

View File

@ -82,6 +82,7 @@ class Widget : public GuiObject
virtual bool handleJoyHat(int stick, int hat, JoyHat value) { return false; } virtual bool handleJoyHat(int stick, int hat, JoyHat value) { return false; }
virtual bool handleEvent(Event::Type event) { return false; } virtual bool handleEvent(Event::Type event) { return false; }
void setDirty() override;
void draw() override; void draw() override;
void receivedFocus(); void receivedFocus();
void lostFocus(); void lostFocus();
@ -108,11 +109,11 @@ class Widget : public GuiObject
virtual const GUI::Font& font() const { return _font; } virtual const GUI::Font& font() const { return _font; }
void setTextColor(uInt32 color) { _textcolor = color; } void setTextColor(uInt32 color) { _textcolor = color; setDirty(); }
void setTextColorHi(uInt32 color) { _textcolorhi = color; } void setTextColorHi(uInt32 color) { _textcolorhi = color; setDirty(); }
void setBGColor(uInt32 color) { _bgcolor = color; } void setBGColor(uInt32 color) { _bgcolor = color; setDirty(); }
void setBGColorHi(uInt32 color) { _bgcolorhi = color; } void setBGColorHi(uInt32 color) { _bgcolorhi = color; setDirty(); }
void setShadowColor(uInt32 color) { _shadowcolor = color; } void setShadowColor(uInt32 color) { _shadowcolor = color; setDirty(); }
virtual void loadConfig() { } virtual void loadConfig() { }
@ -187,7 +188,7 @@ class StaticTextWidget : public Widget
uInt32 shadowColor = 0); uInt32 shadowColor = 0);
void setValue(int value); void setValue(int value);
void setLabel(const string& label); void setLabel(const string& label);
void setAlign(TextAlign align) { _align = align; } void setAlign(TextAlign align) { _align = align; setDirty(); }
const string& getLabel() const { return _label; } const string& getLabel() const { return _label; }
bool isEditable() const { return _editable; } bool isEditable() const { return _editable; }
@ -341,8 +342,8 @@ class SliderWidget : public ButtonWidget
void drawWidget(bool hilite) override; void drawWidget(bool hilite) override;
int valueToPos(int value); int valueToPos(int value) const;
int posToValue(int pos); int posToValue(int pos) const;
protected: protected:
int _value, _stepValue; int _value, _stepValue;