Merge branch 'feature/fix-surfaces'

This commit is contained in:
Stephen Anthony 2021-01-23 13:10:40 -03:30
commit 982e7d18a6
23 changed files with 206 additions and 143 deletions

View File

@ -200,17 +200,10 @@ void FBSurfaceSDL2::invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
SDL_FillRect(mySurface, &tmp, 0); SDL_FillRect(mySurface, &tmp, 0);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::free()
{
myBlitter.reset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::reload() void FBSurfaceSDL2::reload()
{ {
free(); reinitializeBlitter(true);
reinitializeBlitter();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -221,8 +214,6 @@ void FBSurfaceSDL2::resize(uInt32 width, uInt32 height)
if(mySurface) if(mySurface)
SDL_FreeSurface(mySurface); SDL_FreeSurface(mySurface);
free();
// NOTE: Currently, a resize changes a 'static' surface to 'streaming' // NOTE: Currently, a resize changes a 'static' surface to 'streaming'
// No code currently does this, but we should at least check for it // No code currently does this, but we should at least check for it
if(myIsStatic) if(myIsStatic)
@ -260,12 +251,15 @@ void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height,
if(myIsStatic) if(myIsStatic)
SDL_memcpy(mySurface->pixels, data, mySurface->w * mySurface->h * 4); SDL_memcpy(mySurface->pixels, data, mySurface->w * mySurface->h * 4);
reinitializeBlitter(); reload();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::reinitializeBlitter() void FBSurfaceSDL2::reinitializeBlitter(bool force)
{ {
if (force)
myBlitter.reset();
if (!myBlitter && myBackend.isInitialized()) if (!myBlitter && myBackend.isInitialized())
myBlitter = BlitterFactory::createBlitter( myBlitter = BlitterFactory::createBlitter(
myBackend, scalingAlgorithm(myInterpolationMode)); myBackend, scalingAlgorithm(myInterpolationMode));

View File

@ -60,7 +60,6 @@ class FBSurfaceSDL2 : public FBSurface
void invalidate() override; void invalidate() override;
void invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) override; void invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) override;
void free() override;
void reload() override; void reload() override;
void resize(uInt32 width, uInt32 height) override; void resize(uInt32 width, uInt32 height) override;
@ -109,7 +108,7 @@ class FBSurfaceSDL2 : public FBSurface
void createSurface(uInt32 width, uInt32 height, const uInt32* data); void createSurface(uInt32 width, uInt32 height, const uInt32* data);
void reinitializeBlitter(); void reinitializeBlitter(bool force = false);
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
FBSurfaceSDL2() = delete; FBSurfaceSDL2() = delete;

View File

@ -350,16 +350,8 @@ class FBSurface
*/ */
virtual void invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) = 0; virtual void invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) = 0;
/**
This method should be called to free any resources being used by
the surface.
*/
virtual void free() = 0;
/** /**
This method should be called to reload the surface data/state. This method should be called to reload the surface data/state.
It will normally be called after free().
*/ */
virtual void reload() = 0; virtual void reload() = 0;

View File

@ -67,11 +67,6 @@ FrameBuffer::FrameBuffer(OSystem& osystem)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBuffer::~FrameBuffer() FrameBuffer::~FrameBuffer()
{ {
// Make sure to free surfaces/textures before destroying the backend itself
// Most platforms are fine with doing this in either order, but it seems
// that OpenBSD in particular crashes when attempting to destroy textures
// *after* the renderer is already destroyed
freeSurfaces();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -256,21 +251,7 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type,
return FBInitStatus::FailTooLarge; return FBInitStatus::FailTooLarge;
#endif #endif
// Initialize video mode handler, so it can know what video modes are #ifdef GUI_SUPPORT // TODO: put message stuff in its own class
// appropriate for the requested image size
myVidModeHandler.setImageSize(size);
// Always save, maybe only the mode of the window has changed
saveCurrentWindowPosition();
myBufferType = type;
// Initialize video subsystem
string pre_about = myBackend->about();
FBInitStatus status = applyVideoMode();
if(status != FBInitStatus::Success)
return status;
#ifdef GUI_SUPPORT
// Erase any messages from a previous run // Erase any messages from a previous run
myMsg.enabled = false; myMsg.enabled = false;
@ -297,6 +278,20 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type,
} }
#endif #endif
// Initialize video mode handler, so it can know what video modes are
// appropriate for the requested image size
myVidModeHandler.setImageSize(size);
// Always save, maybe only the mode of the window has changed
saveCurrentWindowPosition();
myBufferType = type;
// Initialize video subsystem
string pre_about = myBackend->about();
FBInitStatus status = applyVideoMode();
if(status != FBInitStatus::Success)
return status;
// Print initial usage message, but only print it later if the status has changed // Print initial usage message, but only print it later if the status has changed
if(myInitializedCount == 1) if(myInitializedCount == 1)
{ {
@ -553,6 +548,7 @@ void FrameBuffer::updateInEmulationMode(float framesPerSecond)
} }
#ifdef GUI_SUPPORT #ifdef GUI_SUPPORT
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::createMessage(const string& message, MessagePosition position, bool force) void FrameBuffer::createMessage(const string& message, MessagePosition position, bool force)
{ {
// Only show messages if they've been enabled // Only show messages if they've been enabled
@ -866,42 +862,63 @@ void FrameBuffer::setPauseDelay()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
shared_ptr<FBSurface> FrameBuffer::allocateSurface( unique_ptr<FBSurface> FrameBuffer::allocateSurface(
int w, int h, ScalingInterpolation inter, const uInt32* data int w, int h, ScalingInterpolation inter, const uInt32* data)
)
{ {
// Add new surface to the list return myBackend->createSurface(w, h, inter, data);
mySurfaceList.push_back(myBackend->createSurface(w, h, inter, data));
// And return a pointer to it (pointer should be treated read-only)
return mySurfaceList.at(mySurfaceList.size() - 1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::freeSurfaces()
{
for(auto& s: mySurfaceList)
s->free();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::reloadSurfaces()
{
for(auto& s: mySurfaceList)
s->reload();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::resetSurfaces() void FrameBuffer::resetSurfaces()
{ {
// Free all resources for each surface, then reload them switch(myOSystem.eventHandler().state())
// Due to possible timing and/or synchronization issues, all free()'s {
// are done first, then all reload()'s case EventHandlerState::NONE:
// Any derived FrameBuffer classes that call this method should be case EventHandlerState::EMULATION:
// aware of these restrictions, and act accordingly case EventHandlerState::PAUSE:
case EventHandlerState::PLAYBACK:
#ifdef GUI_SUPPORT
myMsg.surface->reload();
myStatsMsg.surface->reload();
#endif
myTIASurface->resetSurfaces();
break;
freeSurfaces(); #ifdef GUI_SUPPORT
reloadSurfaces(); case EventHandlerState::OPTIONSMENU:
myOSystem.menu().resetSurfaces();
break;
case EventHandlerState::CMDMENU:
myOSystem.commandMenu().resetSurfaces();
break;
case EventHandlerState::HIGHSCORESMENU:
myOSystem.highscoresMenu().resetSurfaces();
break;
case EventHandlerState::MESSAGEMENU:
myOSystem.messageMenu().resetSurfaces();
break;
case EventHandlerState::TIMEMACHINE:
myOSystem.timeMachine().resetSurfaces();
break;
case EventHandlerState::LAUNCHER:
myOSystem.launcher().resetSurfaces();
break;
#endif
#ifdef DEBUGGER_SUPPORT
case EventHandlerState::DEBUGGER:
myOSystem.debugger().resetSurfaces();
break;
#endif
default:
break;
}
update(UpdateMode::REDRAW); // force full update update(UpdateMode::REDRAW); // force full update
} }

View File

@ -158,7 +158,7 @@ class FrameBuffer
@return A pointer to a valid surface object, or nullptr @return A pointer to a valid surface object, or nullptr
*/ */
shared_ptr<FBSurface> allocateSurface( unique_ptr<FBSurface> allocateSurface(
int w, int w,
int h, int h,
ScalingInterpolation inter = ScalingInterpolation::none, ScalingInterpolation inter = ScalingInterpolation::none,
@ -384,16 +384,6 @@ class FrameBuffer
string getDisplayKey() const; string getDisplayKey() const;
void saveCurrentWindowPosition() const; void saveCurrentWindowPosition() const;
/**
Calls 'free()' on all surfaces that the framebuffer knows about.
*/
void freeSurfaces();
/**
Calls 'reload()' on all surfaces that the framebuffer knows about.
*/
void reloadSurfaces();
/** /**
Frees and reloads all surfaces that the framebuffer knows about. Frees and reloads all surfaces that the framebuffer knows about.
*/ */
@ -520,7 +510,7 @@ class FrameBuffer
int x{0}, y{0}, w{0}, h{0}; int x{0}, y{0}, w{0}, h{0};
MessagePosition position{MessagePosition::BottomCenter}; MessagePosition position{MessagePosition::BottomCenter};
ColorId color{kNone}; ColorId color{kNone};
shared_ptr<FBSurface> surface; unique_ptr<FBSurface> surface;
bool enabled{false}; bool enabled{false};
bool dirty{false}; bool dirty{false};
bool showGauge{false}; bool showGauge{false};
@ -541,9 +531,6 @@ class FrameBuffer
// Maximum TIA zoom level that can be used for this framebuffer // Maximum TIA zoom level that can be used for this framebuffer
float myTIAMaxZoom{1.F}; float myTIAMaxZoom{1.F};
// Holds a reference to all the surfaces that have been created
vector<shared_ptr<FBSurface>> mySurfaceList;
// Maximum message width [chars] // Maximum message width [chars]
static constexpr int MESSAGE_WIDTH = 56; static constexpr int MESSAGE_WIDTH = 56;
// Maximum gauge bar width [chars] // Maximum gauge bar width [chars]

View File

@ -541,3 +541,12 @@ bool TIASurface::correctAspect() const
{ {
return myOSystem.settings().getBool("tia.correct_aspect"); return myOSystem.settings().getBool("tia.correct_aspect");
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIASurface::resetSurfaces()
{
myTiaSurface->reload();
mySLineSurface->reload();
myBaseTiaSurface->reload();
myShadeSurface->reload();
}

View File

@ -183,6 +183,11 @@ class TIASurface
*/ */
void updateSurfaceSettings(); void updateSurfaceSettings();
/**
Issue a 'reload' to each surface.
*/
void resetSurfaces();
private: private:
/** /**
Average current calculated buffer's pixel with previous calculated buffer's pixel (50:50). Average current calculated buffer's pixel with previous calculated buffer's pixel (50:50).
@ -208,7 +213,7 @@ class TIASurface
FrameBuffer& myFB; FrameBuffer& myFB;
TIA* myTIA{nullptr}; TIA* myTIA{nullptr};
shared_ptr<FBSurface> myTiaSurface, mySLineSurface, myBaseTiaSurface, myShadeSurface; unique_ptr<FBSurface> myTiaSurface, mySLineSurface, myBaseTiaSurface, myShadeSurface;
// NTSC object to use in TIA rendering mode // NTSC object to use in TIA rendering mode
NTSCFilter myNTSCFilter; NTSCFilter myNTSCFilter;

View File

@ -583,7 +583,6 @@ void ContextMenu::setArrows()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ContextMenu::drawDialog() void ContextMenu::drawDialog()
{ {
// Normally we add widgets and let Dialog::draw() take care of this // Normally we add widgets and let Dialog::draw() take care of this
// logic. But for some reason, this Dialog was written differently // logic. But for some reason, this Dialog was written differently
// 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.

View File

@ -50,23 +50,12 @@ Dialog::Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font
const string& title, int x, int y, int w, int h) const string& title, int x, int y, int w, int h)
: GuiObject(instance, parent, *this, x, y, w, h), : GuiObject(instance, parent, *this, x, y, w, h),
_font{font}, _font{font},
_title{title} _title{title},
_renderCallback{[]() { return; }}
{ {
_flags = Widget::FLAG_ENABLED | Widget::FLAG_BORDER | Widget::FLAG_CLEARBG; _flags = Widget::FLAG_ENABLED | Widget::FLAG_BORDER | Widget::FLAG_CLEARBG;
setTitle(title); 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); _toolTip = make_unique<ToolTip>(*this, font);
} }
@ -173,6 +162,12 @@ void Dialog::setDirtyChain()
_dirtyChain = true; _dirtyChain = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::resetSurfaces()
{
_surface->reload();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::tick() void Dialog::tick()
{ {
@ -251,11 +246,7 @@ void Dialog::render()
// Update dialog surface; also render any extra surfaces // Update dialog surface; also render any extra surfaces
// Extra surfaces must be rendered afterwards, so they are drawn on top // Extra surfaces must be rendered afterwards, so they are drawn on top
if(_surface->render()) if(_surface->render())
{ _renderCallback();
mySurfaceStack.applyAll([](shared_ptr<FBSurface>& surface) {
surface->render();
});
}
// A dialog is still on top if a non-shading dialog (e.g. ContextMenu) // A dialog is still on top if a non-shading dialog (e.g. ContextMenu)
// is opended above it. // is opended above it.
@ -265,6 +256,20 @@ void Dialog::render()
if(!onTop) if(!onTop)
{ {
if(_shadeSurface == nullptr)
{
// 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();
}
_shadeSurface->setDstRect(_surface->dstRect()); _shadeSurface->setDstRect(_surface->dstRect());
_shadeSurface->render(); _shadeSurface->render();
} }
@ -418,9 +423,9 @@ void Dialog::buildCurrentFocusList(int tabID)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::addSurface(const shared_ptr<FBSurface>& surface) void Dialog::addRenderCallback(const std::function<void()>& callback)
{ {
mySurfaceStack.push(surface); _renderCallback = callback;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -28,7 +28,6 @@ class TabWidget;
class CommandSender; class CommandSender;
class ToolTip; class ToolTip;
#include "Stack.hxx"
#include "Widget.hxx" #include "Widget.hxx"
#include "GuiObject.hxx" #include "GuiObject.hxx"
#include "StellaKeys.hxx" #include "StellaKeys.hxx"
@ -45,6 +44,8 @@ class Dialog : public GuiObject
friend class DialogContainer; friend class DialogContainer;
public: public:
using RenderCallback = std::function<void()>;
Dialog(OSystem& instance, DialogContainer& parent, Dialog(OSystem& instance, DialogContainer& parent,
int x = 0, int y = 0, int w = 0, int h = 0); int x = 0, int y = 0, int w = 0, int h = 0);
Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font, Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font,
@ -62,6 +63,8 @@ class Dialog : public GuiObject
virtual void saveConfig() { } virtual void saveConfig() { }
virtual void setDefaults() { } virtual void setDefaults() { }
virtual void resetSurfaces();
void setDirty() override; void setDirty() override;
void setDirtyChain() override; void setDirtyChain() override;
void redraw(bool force = false); void redraw(bool force = false);
@ -85,12 +88,11 @@ class Dialog : public GuiObject
FBSurface& surface() const { return *_surface; } FBSurface& surface() const { return *_surface; }
/** /**
Adds a surface to this dialog, which is rendered on top of the This method is called each time the main Dialog::render is called.
base surface whenever the base surface is re-rendered. Since It is called *after* the dialog has been rendered, so it can be
the surface render() call will always occur in such a case, the used to render another surface on top of it, among other things.
surface should call setVisible() to enable/disable its output.
*/ */
void addSurface(const shared_ptr<FBSurface>& surface); void addRenderCallback(const RenderCallback& callback);
void setTitle(const string& title); void setTitle(const string& title);
bool hasTitle() { return !_title.empty(); } bool hasTitle() { return !_title.empty(); }
@ -220,8 +222,6 @@ class Dialog : public GuiObject
int _layer{0}; int _layer{0};
unique_ptr<ToolTip> _toolTip; unique_ptr<ToolTip> _toolTip;
Common::FixedStack<shared_ptr<FBSurface>> mySurfaceStack;
private: private:
struct Focus { struct Focus {
Widget* widget{nullptr}; Widget* widget{nullptr};
@ -248,13 +248,15 @@ class Dialog : public GuiObject
TabFocusList _myTabList; // focus for each tab (if any) TabFocusList _myTabList; // focus for each tab (if any)
WidgetArray _buttonGroup; WidgetArray _buttonGroup;
shared_ptr<FBSurface> _surface; unique_ptr<FBSurface> _surface;
shared_ptr<FBSurface> _shadeSurface; unique_ptr<FBSurface> _shadeSurface;
int _tabID{0}; int _tabID{0};
uInt32 _max_w{0}; // maximum wanted width uInt32 _max_w{0}; // maximum wanted width
uInt32 _max_h{0}; // maximum wanted height uInt32 _max_h{0}; // maximum wanted height
RenderCallback _renderCallback;
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
Dialog() = delete; Dialog() = delete;

View File

@ -199,6 +199,14 @@ void DialogContainer::reStack()
reset(); reset();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DialogContainer::resetSurfaces()
{
myDialogStack.applyAll([&](Dialog*& d) {
d->resetSurfaces();
});
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DialogContainer::handleTextEvent(char text) void DialogContainer::handleTextEvent(char text)
{ {

View File

@ -150,6 +150,13 @@ class DialogContainer
*/ */
void reStack(); void reStack();
/**
Issue a 'reload' event to each dialog surface in the stack. This
is typically used when interpolation or attributes for a dialog
have changed.
*/
void resetSurfaces();
/** /**
Inform the container that it should resize according to the current Inform the container that it should resize according to the current
screen dimensions. We make this virtual, since the container may or screen dimensions. We make this virtual, since the container may or

View File

@ -40,9 +40,6 @@ EditableWidget::EditableWidget(GuiObject* boss, const GUI::Font& font,
_bgcolorlo = kDlgColor; _bgcolorlo = kDlgColor;
_textcolor = kTextColor; _textcolor = kTextColor;
_textcolorhi = kTextColor; _textcolorhi = kTextColor;
// add mouse context menu
myMouseMenu = make_unique<ContextMenu>(this, font, EmptyVarList);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -143,7 +140,7 @@ int EditableWidget::toCaretPos(int x) const
void EditableWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount) void EditableWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount)
{ {
// Grab right mouse button for context menu, send left to base class // Grab right mouse button for context menu, send left to base class
if(b == MouseButton::RIGHT && isEnabled() && !myMouseMenu->isVisible()) if(b == MouseButton::RIGHT && isEnabled() && !mouseMenu().isVisible())
{ {
VariantList items; VariantList items;
#ifndef BSPF_MACOS #ifndef BSPF_MACOS
@ -159,10 +156,10 @@ void EditableWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount
if(isEditable()) if(isEditable())
VarList::push_back(items, " Paste Cmd+V ", "paste"); VarList::push_back(items, " Paste Cmd+V ", "paste");
#endif #endif
myMouseMenu->addItems(items); mouseMenu().addItems(items);
// Add menu at current x,y mouse location // Add menu at current x,y mouse location
myMouseMenu->show(x + getAbsX(), y + getAbsY(), dialog().surface().dstRect()); mouseMenu().show(x + getAbsX(), y + getAbsY(), dialog().surface().dstRect());
return; return;
} }
else if(b == MouseButton::LEFT && isEnabled()) else if(b == MouseButton::LEFT && isEnabled())
@ -179,6 +176,16 @@ void EditableWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount
Widget::handleMouseDown(x, y, b, clickCount); Widget::handleMouseDown(x, y, b, clickCount);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ContextMenu& EditableWidget::mouseMenu()
{
// add mouse context menu
if(myMouseMenu == nullptr)
myMouseMenu = make_unique<ContextMenu>(this, _font, EmptyVarList);
return *myMouseMenu;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EditableWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) void EditableWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount)
{ {
@ -205,7 +212,7 @@ void EditableWidget::handleCommand(CommandSender* sender, int cmd, int data, int
{ {
if(cmd == ContextMenu::kItemSelectedCmd) if(cmd == ContextMenu::kItemSelectedCmd)
{ {
const string& rmb = myMouseMenu->getSelectedTag().toString(); const string& rmb = mouseMenu().getSelectedTag().toString();
if(rmb == "cut") if(rmb == "cut")
{ {

View File

@ -117,6 +117,8 @@ class EditableWidget : public Widget, public CommandSender
// internal buffer // internal buffer
bool tryInsertChar(char c, int pos); bool tryInsertChar(char c, int pos);
ContextMenu& mouseMenu();
private: private:
unique_ptr<ContextMenu> myMouseMenu; unique_ptr<ContextMenu> myMouseMenu;
bool _isDragging{false}; bool _isDragging{false};

View File

@ -128,7 +128,7 @@ EventMappingWidget::EventMappingWidget(GuiObject* boss, const GUI::Font& font,
addFocusWidget(myComboButton); addFocusWidget(myComboButton);
VariantList combolist = instance().eventHandler().getComboList(mode); VariantList combolist = instance().eventHandler().getComboList(mode);
myComboDialog = new ComboDialog(boss, font, combolist); myComboDialog = make_unique<ComboDialog>(boss, font, combolist);
} }
// Show message for currently selected event // Show message for currently selected event

View File

@ -90,7 +90,7 @@ class EventMappingWidget : public Widget, public CommandSender
StringListWidget* myActionsList{nullptr}; StringListWidget* myActionsList{nullptr};
EditTextWidget* myKeyMapping{nullptr}; EditTextWidget* myKeyMapping{nullptr};
ComboDialog* myComboDialog{nullptr}; unique_ptr<ComboDialog> myComboDialog;
// Since this widget can be used for different collections of events, // Since this widget can be used for different collections of events,
// we need to specify exactly which group of events we are remapping // we need to specify exactly which group of events we are remapping

View File

@ -295,9 +295,6 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
addToFocusList(wid); addToFocusList(wid);
// Create (empty) context menu for ROM list options
myMenu = make_unique<ContextMenu>(this, _font, EmptyVarList);
// since we cannot know how many files there are, use are really high value here // since we cannot know how many files there are, use are really high value here
myList->progress().setRange(0, 50000, 5); myList->progress().setRange(0, 50000, 5);
myList->progress().setMessage(" Filtering files" + ELLIPSIS + " "); myList->progress().setMessage(" Filtering files" + ELLIPSIS + " ");
@ -353,6 +350,15 @@ void LauncherDialog::reload()
myPendingReload = false; myPendingReload = false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::resetSurfaces()
{
if(myRomInfoWidget)
myRomInfoWidget->resetSurfaces();
Dialog::resetSurfaces();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::tick() void LauncherDialog::tick()
{ {
@ -608,7 +614,7 @@ void LauncherDialog::loadRomInfo()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::handleContextMenu() void LauncherDialog::handleContextMenu()
{ {
const string& cmd = myMenu->getSelectedTag().toString(); const string& cmd = menu().getSelectedTag().toString();
if(cmd == "override") if(cmd == "override")
openGlobalProps(); openGlobalProps();
@ -618,6 +624,17 @@ void LauncherDialog::handleContextMenu()
openHighScores(); openHighScores();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ContextMenu& LauncherDialog::menu()
{
if(myMenu == nullptr)
// Create (empty) context menu for ROM list options
myMenu = make_unique<ContextMenu>(this, _font, EmptyVarList);
return *myMenu;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::showOnlyROMs(bool state) void LauncherDialog::showOnlyROMs(bool state)
{ {
@ -752,10 +769,10 @@ void LauncherDialog::handleMouseDown(int x, int y, MouseButton b, int clickCount
if(instance().highScores().enabled()) if(instance().highScores().enabled())
VarList::push_back(items, " High scores" + ELLIPSIS + " Ctrl+H", "highscores"); VarList::push_back(items, " High scores" + ELLIPSIS + " Ctrl+H", "highscores");
VarList::push_back(items, " Reload listing Ctrl+R ", "reload"); VarList::push_back(items, " Reload listing Ctrl+R ", "reload");
myMenu->addItems(items); menu().addItems(items);
// Add menu at current x,y mouse location // Add menu at current x,y mouse location
myMenu->show(x + getAbsX(), y + getAbsY(), surface().dstRect()); menu().show(x + getAbsX(), y + getAbsY(), surface().dstRect());
} }
else else
Dialog::handleMouseDown(x, y, b, clickCount); Dialog::handleMouseDown(x, y, b, clickCount);

View File

@ -108,6 +108,7 @@ class LauncherDialog : public Dialog
void loadConfig() override; void loadConfig() override;
void saveConfig() override; void saveConfig() override;
void resetSurfaces() override;
void updateUI(); void updateUI();
/** /**
@ -157,6 +158,8 @@ class LauncherDialog : public Dialog
void openHighScores(); void openHighScores();
void openWhatsNew(); void openWhatsNew();
ContextMenu& menu();
private: private:
unique_ptr<Dialog> myDialog; unique_ptr<Dialog> myDialog;
unique_ptr<ContextMenu> myMenu; unique_ptr<ContextMenu> myMenu;

View File

@ -94,7 +94,11 @@ void RomInfoWidget::parseProperties(const FilesystemNode& node)
myAvail.w, myAvail.h, ScalingInterpolation::blur); myAvail.w, myAvail.h, ScalingInterpolation::blur);
mySurface->applyAttributes(); mySurface->applyAttributes();
dialog().addSurface(mySurface); dialog().addRenderCallback([this]() {
if(mySurfaceIsValid)
mySurface->render();
}
);
} }
// Initialize to empty properties entry // Initialize to empty properties entry
@ -169,6 +173,13 @@ void RomInfoWidget::parseProperties(const FilesystemNode& node)
setDirty(); setDirty();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomInfoWidget::resetSurfaces()
{
if(mySurface)
mySurface->reload();
}
#ifdef PNG_SUPPORT #ifdef PNG_SUPPORT
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RomInfoWidget::loadPng(const string& filename) bool RomInfoWidget::loadPng(const string& filename)

View File

@ -39,6 +39,8 @@ class RomInfoWidget : public Widget
void clearProperties(); void clearProperties();
void reloadProperties(const FilesystemNode& node); void reloadProperties(const FilesystemNode& node);
void resetSurfaces();
protected: protected:
void drawWidget(bool hilite) override; void drawWidget(bool hilite) override;
@ -50,7 +52,7 @@ class RomInfoWidget : public Widget
private: private:
// Surface pointer holding the PNG image // Surface pointer holding the PNG image
shared_ptr<FBSurface> mySurface; unique_ptr<FBSurface> mySurface;
// Whether the surface should be redrawn by drawWidget() // Whether the surface should be redrawn by drawWidget()
bool mySurfaceIsValid{false}; bool mySurfaceIsValid{false};

View File

@ -47,15 +47,13 @@ void ToolTip::setFont(const GUI::Font& font)
myWidth = fontWidth * MAX_COLUMNS + myTextXOfs * 2; myWidth = fontWidth * MAX_COLUMNS + myTextXOfs * 2;
myHeight = fontHeight * MAX_ROWS + myTextYOfs * 2; myHeight = fontHeight * MAX_ROWS + myTextYOfs * 2;
// unallocate
if(mySurface != nullptr) if(mySurface != nullptr)
{ mySurface.reset();
// TODO: unallocate
mySurface = nullptr;
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
shared_ptr<FBSurface> ToolTip::surface() const unique_ptr<FBSurface>& ToolTip::surface()
{ {
if(mySurface == nullptr) if(mySurface == nullptr)
mySurface = myDialog.instance().frameBuffer().allocateSurface(myWidth, myHeight); mySurface = myDialog.instance().frameBuffer().allocateSurface(myWidth, myHeight);

View File

@ -76,7 +76,7 @@ class ToolTip
/** /**
Allocate surface if required and return it Allocate surface if required and return it
*/ */
shared_ptr<FBSurface> surface(); const unique_ptr<FBSurface>& surface();
void show(const string& tip); void show(const string& tip);
@ -100,7 +100,7 @@ class ToolTip
uInt32 myTextYOfs{0}; uInt32 myTextYOfs{0};
bool myTipShown{false}; bool myTipShown{false};
uInt32 myScale{1}; uInt32 myScale{1};
shared_ptr<FBSurface> mySurface; unique_ptr<FBSurface> mySurface;
}; };
#endif #endif

View File

@ -55,7 +55,6 @@ class FBSurfaceLIBRETRO : public FBSurface
bool render() override { return true; } bool render() override { return true; }
void invalidate() override { } void invalidate() override { }
void invalidateRect(uInt32, uInt32, uInt32, uInt32) override { } void invalidateRect(uInt32, uInt32, uInt32, uInt32) override { }
void free() override { }
void reload() override { } void reload() override { }
void resize(uInt32 width, uInt32 height) override { } void resize(uInt32 width, uInt32 height) override { }
void setScalingInterpolation(ScalingInterpolation) override { } void setScalingInterpolation(ScalingInterpolation) override { }