From 59fe1888c4d2426a73cbb047f76a42005735e179 Mon Sep 17 00:00:00 2001 From: Thomas Jentzsch Date: Wed, 10 Aug 2022 08:19:12 +0200 Subject: [PATCH 1/4] preparing for #916 --- src/emucore/Bankswitch.cxx | 14 +- src/gui/LauncherDialog.cxx | 21 ++- src/gui/LauncherDialog.hxx | 2 + src/gui/RomImageWidget.cxx | 199 +++++++++++++++++++++++++++++ src/gui/RomImageWidget.hxx | 78 +++++++++++ src/gui/RomInfoWidget.cxx | 110 ++-------------- src/gui/RomInfoWidget.hxx | 21 +-- src/gui/module.mk | 1 + src/windows/Stella.vcxproj | 2 + src/windows/Stella.vcxproj.filters | 6 + 10 files changed, 321 insertions(+), 133 deletions(-) create mode 100644 src/gui/RomImageWidget.cxx create mode 100644 src/gui/RomImageWidget.hxx diff --git a/src/emucore/Bankswitch.cxx b/src/emucore/Bankswitch.cxx index 61d91a498..a1fb614e0 100644 --- a/src/emucore/Bankswitch.cxx +++ b/src/emucore/Bankswitch.cxx @@ -97,13 +97,13 @@ Bankswitch::BSList = {{ { "AUTO" , "Auto-detect" }, { "0840" , "0840 (8K EconoBanking)" }, { "0FA0" , "0FA0 (8K Fotomania)" }, - { "2IN1" , "2IN1 Multicart (4-64K)" }, - { "4IN1" , "4IN1 Multicart (8-64K)" }, - { "8IN1" , "8IN1 Multicart (16-64K)" }, - { "16IN1" , "16IN1 Multicart (32-128K)" }, - { "32IN1" , "32IN1 Multicart (64/128K)" }, - { "64IN1" , "64IN1 Multicart (128/256K)" }, - { "128IN1" , "128IN1 Multicart (256/512K)" }, + { "2IN1" , "2in1 Multicart (4-64K)" }, + { "4IN1" , "4in1 Multicart (8-64K)" }, + { "8IN1" , "8in1 Multicart (16-64K)" }, + { "16IN1" , "16in1 Multicart (32-128K)" }, + { "32IN1" , "32in1 Multicart (64/128K)" }, + { "64IN1" , "64in1 Multicart (128/256K)" }, + { "128IN1" , "128in1 Multicart (256/512K)" }, { "2K" , "2K (32-2048 bytes Atari)" }, { "3E" , "3E (Tigervision, 32K RAM)" }, { "3EX" , "3EX (Tigervision, 256K RAM)" }, diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx index 128f2eee2..43e31b5a4 100644 --- a/src/gui/LauncherDialog.cxx +++ b/src/gui/LauncherDialog.cxx @@ -45,10 +45,10 @@ #include "StellaKeys.hxx" #include "Props.hxx" #include "PropsSet.hxx" +#include "RomImageWidget.hxx" #include "RomInfoWidget.hxx" #include "TIAConstants.hxx" #include "Settings.hxx" -#include "Widget.hxx" #include "Font.hxx" #include "StellaFont.hxx" #include "ConsoleBFont.hxx" @@ -329,10 +329,14 @@ void LauncherDialog::addRomWidgets(int ypos) // Calculate font area, and in the process the font that can be used const Common::Size fontArea(romWidth - fontWidth * 2, myList->getHeight() - imgSize.h - VGAP * 3); - setRomInfoFont(fontArea); + + myRomImageWidget = new RomImageWidget(this, *myROMInfoFont, + xpos, ypos, romWidth, imgSize.h); + + int yofs = imgSize.h + _font.getFontHeight() / 2; myRomInfoWidget = new RomInfoWidget(this, *myROMInfoFont, - xpos, ypos, romWidth, myList->getHeight(), imgSize); + xpos, ypos + yofs, romWidth, myList->getHeight() - yofs); } addToFocusList(wid); } @@ -503,7 +507,10 @@ void LauncherDialog::loadConfig() Dialog::setFocus(getFocusList()[mySelectedItem]); if(myRomInfoWidget) + { + myRomImageWidget->reloadProperties(currentNode()); myRomInfoWidget->reloadProperties(currentNode()); + } myList->clearFlags(Widget::FLAG_WANTS_RAWDATA); // always reset this } @@ -716,14 +723,20 @@ void LauncherDialog::setRomInfoFont(const Common::Size& area) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void LauncherDialog::loadRomInfo() { - if(!myRomInfoWidget) + if(!myRomImageWidget || !myROMInfoFont) return; const string& md5 = selectedRomMD5(); if(md5 != EmptyString) + { + myRomImageWidget->setProperties(currentNode(), md5); myRomInfoWidget->setProperties(currentNode(), md5); + } else + { + myRomImageWidget->clearProperties(); myRomInfoWidget->clearProperties(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/LauncherDialog.hxx b/src/gui/LauncherDialog.hxx index 289fa0f6e..193860676 100644 --- a/src/gui/LauncherDialog.hxx +++ b/src/gui/LauncherDialog.hxx @@ -27,6 +27,7 @@ class Properties; class EditTextWidget; class NavigationWidget; class LauncherFileListWidget; +class RomImageWidget; class RomInfoWidget; class StaticTextWidget; @@ -213,6 +214,7 @@ class LauncherDialog : public Dialog, CommandSender ButtonWidget* myOptionsButton{nullptr}; ButtonWidget* myQuitButton{nullptr}; + RomImageWidget* myRomImageWidget{nullptr}; RomInfoWidget* myRomInfoWidget{nullptr}; std::unordered_map myMD5List; diff --git a/src/gui/RomImageWidget.cxx b/src/gui/RomImageWidget.cxx new file mode 100644 index 000000000..a1832304c --- /dev/null +++ b/src/gui/RomImageWidget.cxx @@ -0,0 +1,199 @@ +//============================================================================ +// +// 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-2022 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 "EventHandler.hxx" +#include "FrameBuffer.hxx" +#include "Dialog.hxx" +#include "FBSurface.hxx" +#include "Font.hxx" +#include "OSystem.hxx" +#include "PNGLibrary.hxx" +#include "Props.hxx" +#include "PropsSet.hxx" +#include "RomImageWidget.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +RomImageWidget::RomImageWidget(GuiObject* boss, const GUI::Font& font, + int x, int y, int w, int h) + : Widget(boss, font, x, y, w, h), + CommandSender(boss) +{ + _flags = Widget::FLAG_ENABLED; + _bgcolor = kDlgColor; + _bgcolorlo = kBGColorLo; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void RomImageWidget::setProperties(const FSNode& node, const string& md5) +{ + myHaveProperties = true; + + // Make sure to load a per-ROM properties entry, if one exists + instance().propSet().loadPerROM(node, md5); + + // And now get the properties for this ROM + instance().propSet().getMD5(md5, myProperties); + + // Decide whether the information should be shown immediately + if(instance().eventHandler().state() == EventHandlerState::LAUNCHER) + parseProperties(node); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void RomImageWidget::clearProperties() +{ + myHaveProperties = mySurfaceIsValid = false; + if(mySurface) + mySurface->setVisible(mySurfaceIsValid); + + // Decide whether the information should be shown immediately + if(instance().eventHandler().state() == EventHandlerState::LAUNCHER) + setDirty(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void RomImageWidget::reloadProperties(const FSNode& node) +{ + // The ROM may have changed since we were last in the browser, either + // by saving a different image or through a change in video renderer, + // so we reload the properties + if(myHaveProperties) + parseProperties(node); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void RomImageWidget::parseProperties(const FSNode& node) +{ + // Check if a surface has ever been created; if so, we use it + // The surface will always be the maximum size, but sometimes we'll + // only draw certain parts of it + if(mySurface == nullptr) + { + mySurface = instance().frameBuffer().allocateSurface( + _w, _h, ScalingInterpolation::blur); + mySurface->applyAttributes(); + + dialog().addRenderCallback([this]() { + if(mySurfaceIsValid) + mySurface->render(); + } + ); + } + + // Initialize to empty properties entry + mySurfaceErrorMsg = ""; + mySurfaceIsValid = false; + +#ifdef PNG_SUPPORT + // Get a valid filename representing a snapshot file for this rom and load the snapshot + const string& path = instance().snapshotLoadDir().getPath(); + + // 1. Try to load snapshot by property name + mySurfaceIsValid = loadPng(path + myProperties.get(PropType::Cart_Name) + ".png"); + + if(!mySurfaceIsValid) + { + // 2. If no snapshot with property name exists, try to load snapshot image by filename + mySurfaceIsValid = loadPng(path + node.getNameWithExt("") + ".png"); + + if(!mySurfaceIsValid) + { + // 3. If no ROM snapshot exists, try to load a default snapshot + mySurfaceIsValid = loadPng(path + "default_snapshot.png"); + } + } +#else + mySurfaceErrorMsg = "PNG image loading not supported"; +#endif + if(mySurface) + mySurface->setVisible(mySurfaceIsValid); + + setDirty(); +} + +#ifdef PNG_SUPPORT +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool RomImageWidget::loadPng(const string& filename) +{ + try + { + instance().png().loadImage(filename, *mySurface); + + // Scale surface to available image area + const Common::Rect& src = mySurface->srcRect(); + const float scale = std::min(float(_w) / src.w(), float(_h) / src.h()) * + instance().frameBuffer().hidpiScaleFactor(); + mySurface->setDstSize(static_cast(src.w() * scale), static_cast(src.h() * scale)); + + return true; + } + catch(const runtime_error& e) + { + mySurfaceErrorMsg = e.what(); + } + return false; +} +#endif + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//void RomImageWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) +//{ +// if(isEnabled() && isHighlighted() +// && x >= 0 && x < _w +// && y >= static_cast(_h) + _font.getFontHeight() / 2 && y < _h) +// { +// clearFlags(Widget::FLAG_HILITED); // avoid double clicks and opened URLs +// sendCommand(kClickedCmd, 0, _id); +// } +//} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void RomImageWidget::drawWidget(bool hilite) +{ + FBSurface& s = dialog().surface(); + const int yoff = _h + _font.getFontHeight() / 2; + + s.fillRect(_x+2, _y+2, _w-4, _h-4, _bgcolor); + s.frameRect(_x, _y, _w, _h, kColor); + + if(!myHaveProperties) + { + clearDirty(); + return; + } + + if(mySurfaceIsValid) + { + const Common::Rect& dst = mySurface->dstRect(); + const uInt32 scale = instance().frameBuffer().hidpiScaleFactor(); + const uInt32 x = _x * scale + ((_w * scale - dst.w()) >> 1); + const uInt32 y = _y * scale + ((_h * scale - dst.h()) >> 1); + + // Make sure when positioning the snapshot surface that we take + // the dialog surface position into account + const Common::Rect& s_dst = s.dstRect(); + mySurface->setDstPos(x + s_dst.x(), y + s_dst.y()); + } + else if(mySurfaceErrorMsg != "") + { + const uInt32 x = _x + ((_w - _font.getStringWidth(mySurfaceErrorMsg)) >> 1); + const uInt32 y = _y + ((yoff - _font.getLineHeight()) >> 1); + s.drawString(_font, mySurfaceErrorMsg, x, y, _w - 10, _textcolor); + } + + clearDirty(); +} diff --git a/src/gui/RomImageWidget.hxx b/src/gui/RomImageWidget.hxx new file mode 100644 index 000000000..bc6c8826f --- /dev/null +++ b/src/gui/RomImageWidget.hxx @@ -0,0 +1,78 @@ +//============================================================================ +// +// 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-2022 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 ROM_IMAGE_WIDGET_HXX +#define ROM_IMAGE_WIDGET_HXX + +class FBSurface; +class Properties; + +#include "Widget.hxx" +#include "bspf.hxx" + +class RomImageWidget : public Widget, public CommandSender +{ + public: + //enum { + // kClickedCmd = 'RIcl' + //}; + + public: + RomImageWidget(GuiObject *boss, const GUI::Font& font, + int x, int y, int w, int h); + ~RomImageWidget() override = default; + + void setProperties(const FSNode& node, const string& md5); + void clearProperties(); + void reloadProperties(const FSNode& node); + + protected: + void drawWidget(bool hilite) override; + //void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; + + private: + void parseProperties(const FSNode& node); + #ifdef PNG_SUPPORT + bool loadPng(const string& filename); + #endif + + private: + // Surface pointer holding the PNG image + shared_ptr mySurface; + + // Whether the surface should be redrawn by drawWidget() + bool mySurfaceIsValid{false}; + + // The properties for the currently selected ROM + Properties myProperties; + + // Indicates if the current properties should actually be used + bool myHaveProperties{false}; + + // Indicates if an error occurred in creating/displaying the surface + string mySurfaceErrorMsg; + + private: + // Following constructors and assignment operators not supported + RomImageWidget() = delete; + RomImageWidget(const RomImageWidget&) = delete; + RomImageWidget(RomImageWidget&&) = delete; + RomImageWidget& operator=(const RomImageWidget&) = delete; + RomImageWidget& operator=(RomImageWidget&&) = delete; +}; + +#endif diff --git a/src/gui/RomInfoWidget.cxx b/src/gui/RomInfoWidget.cxx index 4ead37fb3..3638b19dd 100644 --- a/src/gui/RomInfoWidget.cxx +++ b/src/gui/RomInfoWidget.cxx @@ -24,21 +24,15 @@ #include "ControllerDetector.hxx" #include "Bankswitch.hxx" #include "CartDetector.hxx" -#include "Logger.hxx" #include "Props.hxx" -#include "PNGLibrary.hxx" #include "PropsSet.hxx" -#include "Rect.hxx" -#include "Widget.hxx" #include "RomInfoWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - RomInfoWidget::RomInfoWidget(GuiObject* boss, const GUI::Font& font, - int x, int y, int w, int h, - const Common::Size& imgSize) + int x, int y, int w, int h) : Widget(boss, font, x, y, w, h), - CommandSender(boss), - myAvail{imgSize} + CommandSender(boss) { _flags = Widget::FLAG_ENABLED; _bgcolor = kDlgColor; @@ -64,9 +58,7 @@ void RomInfoWidget::setProperties(const FSNode& node, const string& md5) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RomInfoWidget::clearProperties() { - myHaveProperties = mySurfaceIsValid = false; - if(mySurface) - mySurface->setVisible(mySurfaceIsValid); + myHaveProperties = false; // Decide whether the information should be shown immediately if(instance().eventHandler().state() == EventHandlerState::LAUNCHER) @@ -87,51 +79,9 @@ void RomInfoWidget::reloadProperties(const FSNode& node) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RomInfoWidget::parseProperties(const FSNode& node) { - // Check if a surface has ever been created; if so, we use it - // The surface will always be the maximum size, but sometimes we'll - // only draw certain parts of it - if(mySurface == nullptr) - { - mySurface = instance().frameBuffer().allocateSurface( - myAvail.w, myAvail.h, ScalingInterpolation::blur); - mySurface->applyAttributes(); - - dialog().addRenderCallback([this]() { - if(mySurfaceIsValid) - mySurface->render(); - } - ); - } - // Initialize to empty properties entry - mySurfaceErrorMsg = ""; - mySurfaceIsValid = false; myRomInfo.clear(); -#ifdef PNG_SUPPORT - // Get a valid filename representing a snapshot file for this rom and load the snapshot - const string& path = instance().snapshotLoadDir().getPath(); - - // 1. Try to load snapshot by property name - mySurfaceIsValid = loadPng(path + myProperties.get(PropType::Cart_Name) + ".png"); - - if(!mySurfaceIsValid) - { - // 2. If no snapshot with property name exists, try to load snapshot image by filename - mySurfaceIsValid = loadPng(path + node.getNameWithExt("") + ".png"); - - if(!mySurfaceIsValid) - { - // 3. If no ROM snapshot exists, try to load a default snapshot - mySurfaceIsValid = loadPng(path + "default_snapshot.png"); - } - } -#else - mySurfaceErrorMsg = "PNG image loading not supported"; -#endif - if(mySurface) - mySurface->setVisible(mySurfaceIsValid); - myUrl = myProperties.get(PropType::Cart_Url); // Now add some info for the message box below the image @@ -207,36 +157,13 @@ void RomInfoWidget::parseProperties(const FSNode& node) setDirty(); } -#ifdef PNG_SUPPORT -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool RomInfoWidget::loadPng(const string& filename) -{ - try - { - instance().png().loadImage(filename, *mySurface); - - // Scale surface to available image area - const Common::Rect& src = mySurface->srcRect(); - const float scale = std::min(float(myAvail.w) / src.w(), float(myAvail.h) / src.h()) * - instance().frameBuffer().hidpiScaleFactor(); - mySurface->setDstSize(static_cast(src.w() * scale), static_cast(src.h() * scale)); - - return true; - } - catch(const runtime_error& e) - { - mySurfaceErrorMsg = e.what(); - } - return false; -} -#endif - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RomInfoWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) { - if(isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h) + if(isEnabled() && isHighlighted() + && x >= 0 && x < _w && y >= 0 && y < _h) { - clearFlags(Widget::FLAG_HILITED); + clearFlags(Widget::FLAG_HILITED); // avoid double clicks and opened URLs sendCommand(kClickedCmd, 0, _id); } } @@ -245,11 +172,9 @@ void RomInfoWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) void RomInfoWidget::drawWidget(bool hilite) { FBSurface& s = dialog().surface(); - const int yoff = myAvail.h + _font.getFontHeight() / 2; s.fillRect(_x+2, _y+2, _w-4, _h-4, _bgcolor); - s.frameRect(_x, _y, _w, myAvail.h, kColor); - s.frameRect(_x, _y+yoff, _w, _h-yoff, kColor); + s.frameRect(_x, _y, _w, _h, kColor); if(!myHaveProperties) { @@ -257,27 +182,8 @@ void RomInfoWidget::drawWidget(bool hilite) return; } - if(mySurfaceIsValid) - { - const Common::Rect& dst = mySurface->dstRect(); - const uInt32 scale = instance().frameBuffer().hidpiScaleFactor(); - const uInt32 x = _x * scale + ((_w * scale - dst.w()) >> 1); - const uInt32 y = _y * scale + ((myAvail.h * scale - dst.h()) >> 1); - - // Make sure when positioning the snapshot surface that we take - // the dialog surface position into account - const Common::Rect& s_dst = s.dstRect(); - mySurface->setDstPos(x + s_dst.x(), y + s_dst.y()); - } - else if(mySurfaceErrorMsg != "") - { - const uInt32 x = _x + ((_w - _font.getStringWidth(mySurfaceErrorMsg)) >> 1); - const uInt32 y = _y + ((yoff - _font.getLineHeight()) >> 1); - s.drawString(_font, mySurfaceErrorMsg, x, y, _w - 10, _textcolor); - } - const int xpos = _x + 8; - int ypos = _y + yoff + 5; + int ypos = _y + 5; for(const auto& info : myRomInfo) { if(info.length() * _font.getMaxCharWidth() <= static_cast(_w - 16)) diff --git a/src/gui/RomInfoWidget.hxx b/src/gui/RomInfoWidget.hxx index fc3b449e6..06cb977c9 100644 --- a/src/gui/RomInfoWidget.hxx +++ b/src/gui/RomInfoWidget.hxx @@ -20,9 +20,6 @@ class FBSurface; class Properties; -namespace Common { - struct Size; -} #include "Widget.hxx" #include "bspf.hxx" @@ -36,8 +33,7 @@ class RomInfoWidget : public Widget, public CommandSender public: RomInfoWidget(GuiObject *boss, const GUI::Font& font, - int x, int y, int w, int h, - const Common::Size& imgSize); + int x, int y, int w, int h); ~RomInfoWidget() override = default; void setProperties(const FSNode& node, const string& md5); @@ -52,17 +48,8 @@ class RomInfoWidget : public Widget, public CommandSender private: void parseProperties(const FSNode& node); - #ifdef PNG_SUPPORT - bool loadPng(const string& filename); - #endif private: - // Surface pointer holding the PNG image - shared_ptr mySurface; - - // Whether the surface should be redrawn by drawWidget() - bool mySurfaceIsValid{false}; - // Some ROM properties info, as well as 'tEXt' chunks from the PNG image StringList myRomInfo; @@ -75,12 +62,6 @@ class RomInfoWidget : public Widget, public CommandSender // Optional cart link URL string myUrl; - // Indicates if an error occurred in creating/displaying the surface - string mySurfaceErrorMsg; - - // How much space available for the PNG image - Common::Size myAvail; - private: // Following constructors and assignment operators not supported RomInfoWidget() = delete; diff --git a/src/gui/module.mk b/src/gui/module.mk index a31c859b7..f8e33c993 100644 --- a/src/gui/module.mk +++ b/src/gui/module.mk @@ -47,6 +47,7 @@ MODULE_OBJS := \ src/gui/R77HelpDialog.o \ src/gui/RadioButtonWidget.o \ src/gui/RomAuditDialog.o \ + src/gui/RomImageWidget.o \ src/gui/RomInfoWidget.o \ src/gui/ScrollBarWidget.o \ src/gui/SnapshotDialog.o \ diff --git a/src/windows/Stella.vcxproj b/src/windows/Stella.vcxproj index 1fd59827a..e889ba1d6 100755 --- a/src/windows/Stella.vcxproj +++ b/src/windows/Stella.vcxproj @@ -929,6 +929,7 @@ + @@ -2146,6 +2147,7 @@ + diff --git a/src/windows/Stella.vcxproj.filters b/src/windows/Stella.vcxproj.filters index d44dbcaa5..7e3371379 100644 --- a/src/windows/Stella.vcxproj.filters +++ b/src/windows/Stella.vcxproj.filters @@ -1146,6 +1146,9 @@ Source Files\debugger + + Source Files\gui + @@ -2372,6 +2375,9 @@ Header Files\debugger + + Header Files\gui + From 1883c5ab58d4835c49c99861aac957af2ccedea9 Mon Sep 17 00:00:00 2001 From: Thomas Jentzsch Date: Wed, 10 Aug 2022 20:59:02 +0200 Subject: [PATCH 2/4] added multiple image searching and switching --- src/common/bspf.hxx | 59 ++++++++++++++++++++++++++++ src/gui/RomImageWidget.cxx | 79 ++++++++++++++++++++++++++++++-------- src/gui/RomImageWidget.hxx | 22 ++++++++--- 3 files changed, 139 insertions(+), 21 deletions(-) diff --git a/src/common/bspf.hxx b/src/common/bspf.hxx index 1e77ae7d2..403f8a436 100644 --- a/src/common/bspf.hxx +++ b/src/common/bspf.hxx @@ -319,6 +319,65 @@ namespace BSPF return false; } + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + inline size_t matchWithJoker(const string& str, const string& pattern) + { + if(str.length() >= pattern.length()) + { + // optimize a bit + if(pattern.find('?') != string::npos) + { + for(size_t pos = 0; pos < str.length() - pattern.length() + 1; ++pos) + { + bool found = true; + + for(size_t i = 0; found && i < pattern.length(); ++i) + if(pattern[i] != str[pos + i] && pattern[i] != '?') + found = false; + + if(found) + return pos; + } + } + else + return str.find(pattern); + } + return string::npos; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + inline bool matchWithWildcards(const string& str, const string& pattern) + { + string pat = pattern; + + // remove leading and trailing '*' + size_t i = 0; + while(pat[i++] == '*'); + pat = pat.substr(i - 1); + + i = pat.length(); + while(pat[--i] == '*'); + pat.erase(i + 1); + + // Search for first '*' + const size_t pos = pat.find('*'); + + if(pos != string::npos) + { + // '*' found, split pattern into left and right part, search recursively + const string leftPat = pat.substr(0, pos); + const string rightPat = pat.substr(pos + 1); + const size_t posLeft = matchWithJoker(str, leftPat); + + if(posLeft != string::npos) + return matchWithWildcards(str.substr(pos + posLeft), rightPat); + else + return false; + } + // no further '*' found + return matchWithJoker(str, pat) != string::npos; + } + // Modify 'str', replacing all occurrences of 'from' with 'to' inline void replaceAll(string& str, const string& from, const string& to) { diff --git a/src/gui/RomImageWidget.cxx b/src/gui/RomImageWidget.cxx index a1832304c..1b383969b 100644 --- a/src/gui/RomImageWidget.cxx +++ b/src/gui/RomImageWidget.cxx @@ -24,6 +24,7 @@ #include "PNGLibrary.hxx" #include "Props.hxx" #include "PropsSet.hxx" +#include "bspf.hxx" #include "RomImageWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -99,20 +100,26 @@ void RomImageWidget::parseProperties(const FSNode& node) mySurfaceIsValid = false; #ifdef PNG_SUPPORT + // TODO: RETRON_77 + // Get a valid filename representing a snapshot file for this rom and load the snapshot const string& path = instance().snapshotLoadDir().getPath(); - // 1. Try to load snapshot by property name - mySurfaceIsValid = loadPng(path + myProperties.get(PropType::Cart_Name) + ".png"); + // 1. Try to load snapshots by property name + if(getImageList(path + myProperties.get(PropType::Cart_Name))) + mySurfaceIsValid = loadPng(myImageList[0].getPath()); + //mySurfaceIsValid = loadPng(path + myProperties.get(PropType::Cart_Name) + ".png"); if(!mySurfaceIsValid) { - // 2. If no snapshot with property name exists, try to load snapshot image by filename - mySurfaceIsValid = loadPng(path + node.getNameWithExt("") + ".png"); + // 2. If no snapshots with property name exists, try to load snapshot images by filename + if(getImageList(path + node.getNameWithExt(""))) + mySurfaceIsValid = loadPng(myImageList[0].getPath()); + //mySurfaceIsValid = loadPng(path + node.getNameWithExt("") + ".png"); if(!mySurfaceIsValid) { - // 3. If no ROM snapshot exists, try to load a default snapshot + // 3. If no ROM snapshots exist, try to load a default snapshot mySurfaceIsValid = loadPng(path + "default_snapshot.png"); } } @@ -126,6 +133,22 @@ void RomImageWidget::parseProperties(const FSNode& node) } #ifdef PNG_SUPPORT +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool RomImageWidget::getImageList(const string& filename) +{ + FSNode::NameFilter filter = ([&](const FSNode& node) { + return (!node.isDirectory() && + (node.getPath() == filename + ".png" || + BSPF::matchWithWildcards(node.getPath(), filename + "#*.png"))); + }); + + FSNode node(instance().snapshotLoadDir().getPath()); + + myImageList.clear(); + node.getChildren(myImageList, FSNode::ListMode::FilesOnly, filter, false, false); + return myImageList.size() > 0; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool RomImageWidget::loadPng(const string& filename) { @@ -139,6 +162,7 @@ bool RomImageWidget::loadPng(const string& filename) instance().frameBuffer().hidpiScaleFactor(); mySurface->setDstSize(static_cast(src.w() * scale), static_cast(src.h() * scale)); + setDirty(); return true; } catch(const runtime_error& e) @@ -149,17 +173,32 @@ bool RomImageWidget::loadPng(const string& filename) } #endif +#ifdef PNG_SUPPORT // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -//void RomImageWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) -//{ -// if(isEnabled() && isHighlighted() -// && x >= 0 && x < _w -// && y >= static_cast(_h) + _font.getFontHeight() / 2 && y < _h) -// { -// clearFlags(Widget::FLAG_HILITED); // avoid double clicks and opened URLs -// sendCommand(kClickedCmd, 0, _id); -// } -//} +void RomImageWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) +{ + if(isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h) + { + if(x < _w/2) + { + if(myImageIdx) + { + loadPng(myImageList[--myImageIdx].getPath()); + } + } + else if(myImageIdx < myImageList.size()) + { + loadPng(myImageList[++myImageIdx].getPath()); + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void RomImageWidget::handleMouseMoved(int x, int y) +{ + myMouseX = x; +} +#endif // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RomImageWidget::drawWidget(bool hilite) @@ -195,5 +234,15 @@ void RomImageWidget::drawWidget(bool hilite) s.drawString(_font, mySurfaceErrorMsg, x, y, _w - 10, _textcolor); } +#ifdef PNG_SUPPORT + if(isHighlighted()) + { + // TODO: need another surface + const int xOfs = myMouseX < _w / 2 ? 10 : _w - 50; + + s.line(xOfs, _h / 2 - 10, xOfs + 20, _h / 2, kBtnTextColorHi); + } +#endif + clearDirty(); } diff --git a/src/gui/RomImageWidget.hxx b/src/gui/RomImageWidget.hxx index bc6c8826f..0af65822e 100644 --- a/src/gui/RomImageWidget.hxx +++ b/src/gui/RomImageWidget.hxx @@ -26,11 +26,6 @@ class Properties; class RomImageWidget : public Widget, public CommandSender { - public: - //enum { - // kClickedCmd = 'RIcl' - //}; - public: RomImageWidget(GuiObject *boss, const GUI::Font& font, int x, int y, int w, int h); @@ -42,11 +37,15 @@ class RomImageWidget : public Widget, public CommandSender protected: void drawWidget(bool hilite) override; - //void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; +#ifdef PNG_SUPPORT + void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; + void handleMouseMoved(int x, int y) override; +#endif private: void parseProperties(const FSNode& node); #ifdef PNG_SUPPORT + bool getImageList(const string& filename); bool loadPng(const string& filename); #endif @@ -66,6 +65,17 @@ class RomImageWidget : public Widget, public CommandSender // Indicates if an error occurred in creating/displaying the surface string mySurfaceErrorMsg; +#ifdef PNG_SUPPORT + // Contains the list of image names for the current ROM + FSList myImageList; + + // Index of currently displayed image + int myImageIdx{0}; + + // Current x-position of the mouse + int myMouseX{0}; +#endif + private: // Following constructors and assignment operators not supported RomImageWidget() = delete; From 7590b9bad2ea0c4fb854629bcf64870bf0cf86a7 Mon Sep 17 00:00:00 2001 From: Thomas Jentzsch Date: Fri, 12 Aug 2022 11:40:21 +0200 Subject: [PATCH 3/4] added reading and displaying image labels --- src/common/PNGLibrary.cxx | 23 +++++++++++++- src/common/PNGLibrary.hxx | 8 ++++- src/gui/LauncherDialog.cxx | 22 ++++++++----- src/gui/RomImageWidget.cxx | 64 +++++++++++++++++++++++++------------- src/gui/RomImageWidget.hxx | 10 ++++++ 5 files changed, 96 insertions(+), 31 deletions(-) diff --git a/src/common/PNGLibrary.cxx b/src/common/PNGLibrary.cxx index db7b17ffc..6612000de 100644 --- a/src/common/PNGLibrary.cxx +++ b/src/common/PNGLibrary.cxx @@ -38,7 +38,7 @@ PNGLibrary::PNGLibrary(OSystem& osystem) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void PNGLibrary::loadImage(const string& filename, FBSurface& surface) +void PNGLibrary::loadImage(const string& filename, FBSurface& surface, VariantList& comments) { png_structp png_ptr{nullptr}; png_infop info_ptr{nullptr}; @@ -114,6 +114,9 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface) // We're finished reading png_read_end(png_ptr, info_ptr); + // Read the comments we got + readComments(png_ptr, info_ptr, comments); + // Load image into the surface, setting the correct dimensions loadImagetoSurface(surface); @@ -335,6 +338,7 @@ void PNGLibrary::takeSnapshot(uInt32 number) // Some text fields to add to the PNG snapshot VariantList comments; ostringstream version; + VarList::push_back(comments, "Title", "Snapshot"); version << "Stella " << STELLA_VERSION << " (Build " << STELLA_BUILD << ") [" << BSPF::ARCH << "]"; VarList::push_back(comments, "Software", version.str()); @@ -448,6 +452,23 @@ void PNGLibrary::writeComments(const png_structp png_ptr, png_infop info_ptr, png_set_text(png_ptr, info_ptr, text_ptr.data(), numComments); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PNGLibrary::readComments(const png_structp png_ptr, png_infop info_ptr, + VariantList& comments) +{ + png_textp text_ptr; + int numComments = 0; + + // TODO: currently works only if comments are *before* the image data + png_get_text(png_ptr, info_ptr, &text_ptr, &numComments); + + comments.clear(); + for(int i = 0; i < numComments; ++i) + { + VarList::push_back(comments, text_ptr[i].key, text_ptr[i].text); + } +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PNGLibrary::png_read_data(const png_structp ctx, png_bytep area, png_size_t size) { diff --git a/src/common/PNGLibrary.hxx b/src/common/PNGLibrary.hxx index 6770e7671..9d4d7f8a4 100644 --- a/src/common/PNGLibrary.hxx +++ b/src/common/PNGLibrary.hxx @@ -52,7 +52,7 @@ class PNGLibrary runtime_error is thrown containing a more detailed error message. */ - void loadImage(const string& filename, FBSurface& surface); + void loadImage(const string& filename, FBSurface& surface, VariantList& comments); /** Save the current FrameBuffer image to a PNG file. Note that in most @@ -183,6 +183,12 @@ class PNGLibrary void writeComments(const png_structp png_ptr, png_infop info_ptr, const VariantList& comments); + /** + Read PNG tEXt chunks from the image. + */ + void readComments(const png_structp png_ptr, png_infop info_ptr, + VariantList& comments); + /** PNG library callback functions */ static void png_read_data(const png_structp ctx, png_bytep area, png_size_t size); static void png_write_data(const png_structp ctx, png_bytep area, png_size_t size); diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx index 43e31b5a4..1f2233319 100644 --- a/src/gui/LauncherDialog.cxx +++ b/src/gui/LauncherDialog.cxx @@ -305,8 +305,8 @@ void LauncherDialog::addRomWidgets(int ypos) const int listHeight = _h - ypos - VBORDER - buttonHeight - VGAP * 3; const float imgZoom = getRomInfoZoom(listHeight); - const int romWidth = imgZoom * TIAConstants::viewableWidth; - const int listWidth = _w - (romWidth > 0 ? romWidth + fontWidth : 0) - HBORDER * 2; + const int imageWidth = imgZoom * TIAConstants::viewableWidth; + const int listWidth = _w - (imageWidth > 0 ? imageWidth + fontWidth : 0) - HBORDER * 2; // remember initial ROM directory for returning there via home button instance().settings().setValue("startromdir", getRomDir()); @@ -319,7 +319,7 @@ void LauncherDialog::addRomWidgets(int ypos) wid.push_back(myList); // Add ROM info area (if enabled) - if(romWidth > 0) + if(imageWidth > 0) { xpos += myList->getWidth() + fontWidth; @@ -327,16 +327,22 @@ void LauncherDialog::addRomWidgets(int ypos) const Common::Size imgSize(TIAConstants::viewableWidth * imgZoom, TIAConstants::viewableHeight * imgZoom); // Calculate font area, and in the process the font that can be used - const Common::Size fontArea(romWidth - fontWidth * 2, - myList->getHeight() - imgSize.h - VGAP * 3); + + // Infofont is unknown yet, but used in image label too. Assuming maximum font height. + int imageHeight = imgSize.h + RomImageWidget::labelHeight(_font); + + const Common::Size fontArea(imageWidth - fontWidth * 2, + myList->getHeight() - imageHeight - VGAP * 4); setRomInfoFont(fontArea); + // Now we have the correct font height + imageHeight = imgSize.h + RomImageWidget::labelHeight(*myROMInfoFont); myRomImageWidget = new RomImageWidget(this, *myROMInfoFont, - xpos, ypos, romWidth, imgSize.h); + xpos, ypos, imageWidth, imageHeight); - int yofs = imgSize.h + _font.getFontHeight() / 2; + const int yofs = imageHeight + VGAP * 2; myRomInfoWidget = new RomInfoWidget(this, *myROMInfoFont, - xpos, ypos + yofs, romWidth, myList->getHeight() - yofs); + xpos, ypos + yofs, imageWidth, myList->getHeight() - yofs); } addToFocusList(wid); } diff --git a/src/gui/RomImageWidget.cxx b/src/gui/RomImageWidget.cxx index 1b383969b..a9dce62bc 100644 --- a/src/gui/RomImageWidget.cxx +++ b/src/gui/RomImageWidget.cxx @@ -36,6 +36,7 @@ RomImageWidget::RomImageWidget(GuiObject* boss, const GUI::Font& font, _flags = Widget::FLAG_ENABLED; _bgcolor = kDlgColor; _bgcolorlo = kBGColorLo; + myImageHeight = _h - labelHeight(font); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -85,7 +86,7 @@ void RomImageWidget::parseProperties(const FSNode& node) if(mySurface == nullptr) { mySurface = instance().frameBuffer().allocateSurface( - _w, _h, ScalingInterpolation::blur); + _w, myImageHeight, ScalingInterpolation::blur); mySurface->applyAttributes(); dialog().addRenderCallback([this]() { @@ -105,24 +106,22 @@ void RomImageWidget::parseProperties(const FSNode& node) // Get a valid filename representing a snapshot file for this rom and load the snapshot const string& path = instance().snapshotLoadDir().getPath(); + myImageList.clear(); + myImageIdx = 0; // 1. Try to load snapshots by property name if(getImageList(path + myProperties.get(PropType::Cart_Name))) mySurfaceIsValid = loadPng(myImageList[0].getPath()); - //mySurfaceIsValid = loadPng(path + myProperties.get(PropType::Cart_Name) + ".png"); + + // 2. Also try to load snapshot images by filename + if(getImageList(path + node.getNameWithExt(""))) + mySurfaceIsValid = loadPng(myImageList[0].getPath()); if(!mySurfaceIsValid) { - // 2. If no snapshots with property name exists, try to load snapshot images by filename - if(getImageList(path + node.getNameWithExt(""))) - mySurfaceIsValid = loadPng(myImageList[0].getPath()); - //mySurfaceIsValid = loadPng(path + node.getNameWithExt("") + ".png"); - - if(!mySurfaceIsValid) - { - // 3. If no ROM snapshots exist, try to load a default snapshot - mySurfaceIsValid = loadPng(path + "default_snapshot.png"); - } + // 3. If no ROM snapshots exist, try to load a default snapshot + mySurfaceIsValid = loadPng(path + "default_snapshot.png"); } + #else mySurfaceErrorMsg = "PNG image loading not supported"; #endif @@ -144,7 +143,6 @@ bool RomImageWidget::getImageList(const string& filename) FSNode node(instance().snapshotLoadDir().getPath()); - myImageList.clear(); node.getChildren(myImageList, FSNode::ListMode::FilesOnly, filter, false, false); return myImageList.size() > 0; } @@ -154,13 +152,28 @@ bool RomImageWidget::loadPng(const string& filename) { try { - instance().png().loadImage(filename, *mySurface); + VariantList comments; + instance().png().loadImage(filename, *mySurface, comments); // Scale surface to available image area const Common::Rect& src = mySurface->srcRect(); - const float scale = std::min(float(_w) / src.w(), float(_h) / src.h()) * + const float scale = std::min(float(_w) / src.w(), float(myImageHeight) / src.h()) * instance().frameBuffer().hidpiScaleFactor(); mySurface->setDstSize(static_cast(src.w() * scale), static_cast(src.h() * scale)); + + // Retrieve label for loaded image + myLabel = ""; + for(auto comment = comments.begin(); comment != comments.end(); ++comment) + { + if(comment->first == "Title") + { + myLabel = comment->second.toString(); + break; + } + if(comment->first == "Software" + && comment->second.toString().find("Stella") == 0) + myLabel = "Snapshot"; // default for Stella snapshots with missing "Title" comment + } setDirty(); return true; @@ -177,7 +190,7 @@ bool RomImageWidget::loadPng(const string& filename) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RomImageWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) { - if(isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h) + if(isEnabled() && x >= 0 && x < _w && y >= 0 && y < myImageHeight) { if(x < _w/2) { @@ -186,7 +199,7 @@ void RomImageWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) loadPng(myImageList[--myImageIdx].getPath()); } } - else if(myImageIdx < myImageList.size()) + else if(myImageIdx < myImageList.size() - 1) { loadPng(myImageList[++myImageIdx].getPath()); } @@ -204,10 +217,10 @@ void RomImageWidget::handleMouseMoved(int x, int y) void RomImageWidget::drawWidget(bool hilite) { FBSurface& s = dialog().surface(); - const int yoff = _h + _font.getFontHeight() / 2; + const int yoff = myImageHeight; - s.fillRect(_x+2, _y+2, _w-4, _h-4, _bgcolor); - s.frameRect(_x, _y, _w, _h, kColor); + s.fillRect(_x+1, _y+1, _w-2, _h-1, _bgcolor); + s.frameRect(_x, _y, _w, myImageHeight, kColor); if(!myHaveProperties) { @@ -220,7 +233,7 @@ void RomImageWidget::drawWidget(bool hilite) const Common::Rect& dst = mySurface->dstRect(); const uInt32 scale = instance().frameBuffer().hidpiScaleFactor(); const uInt32 x = _x * scale + ((_w * scale - dst.w()) >> 1); - const uInt32 y = _y * scale + ((_h * scale - dst.h()) >> 1); + const uInt32 y = _y * scale + ((myImageHeight * scale - dst.h()) >> 1); // Make sure when positioning the snapshot surface that we take // the dialog surface position into account @@ -233,6 +246,15 @@ void RomImageWidget::drawWidget(bool hilite) const uInt32 y = _y + ((yoff - _font.getLineHeight()) >> 1); s.drawString(_font, mySurfaceErrorMsg, x, y, _w - 10, _textcolor); } + ostringstream buf; + buf << myImageIdx + 1 << "/" << myImageList.size(); + const int yText = _y + myImageHeight + _font.getFontHeight() / 8; + const int wText = _font.getStringWidth(buf.str()); + + if(myLabel.length()) + s.drawString(_font, myLabel, _x, yText, _w - wText - _font.getMaxCharWidth() * 2, _textcolor); + if(myImageList.size()) + s.drawString(_font, buf.str(), _x + _w - wText, yText, wText, _textcolor); #ifdef PNG_SUPPORT if(isHighlighted()) diff --git a/src/gui/RomImageWidget.hxx b/src/gui/RomImageWidget.hxx index 0af65822e..aca0d7004 100644 --- a/src/gui/RomImageWidget.hxx +++ b/src/gui/RomImageWidget.hxx @@ -31,6 +31,11 @@ class RomImageWidget : public Widget, public CommandSender int x, int y, int w, int h); ~RomImageWidget() override = default; + static int labelHeight(const GUI::Font& font) + { + return font.getFontHeight() * 9 / 8; + } + void setProperties(const FSNode& node, const string& md5); void clearProperties(); void reloadProperties(const FSNode& node); @@ -66,6 +71,8 @@ class RomImageWidget : public Widget, public CommandSender string mySurfaceErrorMsg; #ifdef PNG_SUPPORT + int myImageHeight{0}; + // Contains the list of image names for the current ROM FSList myImageList; @@ -74,6 +81,9 @@ class RomImageWidget : public Widget, public CommandSender // Current x-position of the mouse int myMouseX{0}; + + // Label for the loaded image + string myLabel; #endif private: From c43c6ad5be6b02503f21775b12878b9e2e425b64 Mon Sep 17 00:00:00 2001 From: Thomas Jentzsch Date: Fri, 12 Aug 2022 20:12:10 +0200 Subject: [PATCH 4/4] added image navigation display added keyboard image navigation (TODO: controller) --- src/gui/LauncherDialog.cxx | 8 +++ src/gui/RomImageWidget.cxx | 111 ++++++++++++++++++++++++------------- src/gui/RomImageWidget.hxx | 7 ++- 3 files changed, 87 insertions(+), 39 deletions(-) diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx index 1f2233319..10c36d4e1 100644 --- a/src/gui/LauncherDialog.cxx +++ b/src/gui/LauncherDialog.cxx @@ -879,6 +879,14 @@ void LauncherDialog::handleKeyDown(StellaKey key, StellaMod mod, bool repeated) reload(); break; + case KBDK_LEFT: + myRomImageWidget->changeImage(-1); + break; + + case KBDK_RIGHT: + myRomImageWidget->changeImage(1); + break; + default: handled = false; break; diff --git a/src/gui/RomImageWidget.cxx b/src/gui/RomImageWidget.cxx index a9dce62bc..ea8e5a4a4 100644 --- a/src/gui/RomImageWidget.cxx +++ b/src/gui/RomImageWidget.cxx @@ -33,7 +33,7 @@ RomImageWidget::RomImageWidget(GuiObject* boss, const GUI::Font& font, : Widget(boss, font, x, y, w, h), CommandSender(boss) { - _flags = Widget::FLAG_ENABLED; + _flags = Widget::FLAG_ENABLED | Widget::FLAG_TRACK_MOUSE; _bgcolor = kDlgColor; _bgcolorlo = kBGColorLo; myImageHeight = _h - labelHeight(font); @@ -80,6 +80,19 @@ void RomImageWidget::reloadProperties(const FSNode& node) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RomImageWidget::parseProperties(const FSNode& node) { + if(myNavSurface == nullptr) + { + // Create navigation surface + myNavSurface = instance().frameBuffer().allocateSurface( + _w, myImageHeight); + + FBSurface::Attributes& attr = myNavSurface->attributes(); + + attr.blending = true; + attr.blendalpha = 60; + myNavSurface->applyAttributes(); + } + // Check if a surface has ever been created; if so, we use it // The surface will always be the maximum size, but sometimes we'll // only draw certain parts of it @@ -91,9 +104,12 @@ void RomImageWidget::parseProperties(const FSNode& node) dialog().addRenderCallback([this]() { if(mySurfaceIsValid) + { mySurface->render(); + if(isHighlighted()) + myNavSurface->render(); } - ); + }); } // Initialize to empty properties entry @@ -131,6 +147,17 @@ void RomImageWidget::parseProperties(const FSNode& node) setDirty(); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool RomImageWidget::changeImage(int direction) +{ + if(direction == -1 && myImageIdx) + return loadPng(myImageList[--myImageIdx].getPath()); + else if(direction == 1 && myImageIdx < myImageList.size() - 1) + return loadPng(myImageList[++myImageIdx].getPath()); + + return false; +} + #ifdef PNG_SUPPORT // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool RomImageWidget::getImageList(const string& filename) @@ -184,31 +211,19 @@ bool RomImageWidget::loadPng(const string& filename) } return false; } -#endif -#ifdef PNG_SUPPORT // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RomImageWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) { if(isEnabled() && x >= 0 && x < _w && y >= 0 && y < myImageHeight) - { - if(x < _w/2) - { - if(myImageIdx) - { - loadPng(myImageList[--myImageIdx].getPath()); - } - } - else if(myImageIdx < myImageList.size() - 1) - { - loadPng(myImageList[++myImageIdx].getPath()); - } - } + changeImage(x < _w / 2 ? 1 : -1); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RomImageWidget::handleMouseMoved(int x, int y) { + if(x < _w / 2 != myMouseX < _w / 2) + setDirty(); myMouseX = x; } #endif @@ -239,6 +254,48 @@ void RomImageWidget::drawWidget(bool hilite) // the dialog surface position into account const Common::Rect& s_dst = s.dstRect(); mySurface->setDstPos(x + s_dst.x(), y + s_dst.y()); + + // Draw the image label and counter + ostringstream buf; + buf << myImageIdx + 1 << "/" << myImageList.size(); + const int yText = _y + myImageHeight + _font.getFontHeight() / 8; + const int wText = _font.getStringWidth(buf.str()); + + if(myLabel.length()) + s.drawString(_font, myLabel, _x, yText, _w - wText - _font.getMaxCharWidth() * 2, _textcolor); + if(myImageList.size()) + s.drawString(_font, buf.str(), _x + _w - wText, yText, wText, _textcolor); + + // Draw the navigation arrows + const bool leftArrow = myMouseX < _w / 2; + + myNavSurface->invalidate(); + if(isHighlighted() && + ((leftArrow && myImageIdx) || (!leftArrow && myImageIdx < myImageList.size() - 1)) || true) + { + const int w = _w / 64; + const int w2 = 1; // w / 2; + const int ax = leftArrow ? _w / 12 - w / 2 : _w - _w / 12 - w / 2; + const int ay = myImageHeight >> 1; + const int dx = (_w / 32) * (leftArrow ? 1 : -1); + const int dy = myImageHeight / 16; + + for(int i = 0; i < w; ++i) + { + myNavSurface->line(ax + dx + i + w2, ay - dy, ax + i + w2, ay, kBGColor); + myNavSurface->line(ax + dx + i + w2, ay + dy, ax + i + w2, ay, kBGColor); + myNavSurface->line(ax + dx + i, ay - dy + w2, ax + i, ay + w2, kBGColor); + myNavSurface->line(ax + dx + i, ay + dy + w2, ax + i, ay + w2, kBGColor); + myNavSurface->line(ax + dx + i + w2, ay - dy + w2, ax + i + w2, ay + w2, kBGColor); + myNavSurface->line(ax + dx + i + w2, ay + dy + w2, ax + i + w2, ay + w2, kBGColor); + } + for(int i = 0; i < w; ++i) + { + myNavSurface->line(ax + dx + i, ay - dy, ax + i, ay, kColorInfo); + myNavSurface->line(ax + dx + i, ay + dy, ax + i, ay, kColorInfo); + } + myNavSurface->setDstRect(mySurface->dstRect()); + } } else if(mySurfaceErrorMsg != "") { @@ -246,25 +303,5 @@ void RomImageWidget::drawWidget(bool hilite) const uInt32 y = _y + ((yoff - _font.getLineHeight()) >> 1); s.drawString(_font, mySurfaceErrorMsg, x, y, _w - 10, _textcolor); } - ostringstream buf; - buf << myImageIdx + 1 << "/" << myImageList.size(); - const int yText = _y + myImageHeight + _font.getFontHeight() / 8; - const int wText = _font.getStringWidth(buf.str()); - - if(myLabel.length()) - s.drawString(_font, myLabel, _x, yText, _w - wText - _font.getMaxCharWidth() * 2, _textcolor); - if(myImageList.size()) - s.drawString(_font, buf.str(), _x + _w - wText, yText, wText, _textcolor); - -#ifdef PNG_SUPPORT - if(isHighlighted()) - { - // TODO: need another surface - const int xOfs = myMouseX < _w / 2 ? 10 : _w - 50; - - s.line(xOfs, _h / 2 - 10, xOfs + 20, _h / 2, kBtnTextColorHi); - } -#endif - clearDirty(); } diff --git a/src/gui/RomImageWidget.hxx b/src/gui/RomImageWidget.hxx index aca0d7004..0d5b9e4dd 100644 --- a/src/gui/RomImageWidget.hxx +++ b/src/gui/RomImageWidget.hxx @@ -39,6 +39,7 @@ class RomImageWidget : public Widget, public CommandSender void setProperties(const FSNode& node, const string& md5); void clearProperties(); void reloadProperties(const FSNode& node); + bool changeImage(int direction = 1); protected: void drawWidget(bool hilite) override; @@ -58,6 +59,9 @@ class RomImageWidget : public Widget, public CommandSender // Surface pointer holding the PNG image shared_ptr mySurface; + // Surface pointer holding the navigation elements + shared_ptr myNavSurface; + // Whether the surface should be redrawn by drawWidget() bool mySurfaceIsValid{false}; @@ -70,7 +74,7 @@ class RomImageWidget : public Widget, public CommandSender // Indicates if an error occurred in creating/displaying the surface string mySurfaceErrorMsg; -#ifdef PNG_SUPPORT + // Height of the image area int myImageHeight{0}; // Contains the list of image names for the current ROM @@ -84,7 +88,6 @@ class RomImageWidget : public Widget, public CommandSender // Label for the loaded image string myLabel; -#endif private: // Following constructors and assignment operators not supported