stella/src/gui/Dialog.hxx

285 lines
10 KiB
C++

//============================================================================
//
// 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-2021 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.
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
//============================================================================
#ifndef DIALOG_HXX
#define DIALOG_HXX
class FBSurface;
class OSystem;
class DialogContainer;
class TabWidget;
class CommandSender;
class ToolTip;
#include "Widget.hxx"
#include "GuiObject.hxx"
#include "StellaKeys.hxx"
#include "EventHandlerConstants.hxx"
#include "bspf.hxx"
/**
This is the base class for all dialog boxes.
@author Stephen Anthony
*/
class Dialog : public GuiObject
{
friend class DialogContainer;
public:
using RenderCallback = std::function<void()>;
Dialog(OSystem& instance, DialogContainer& parent,
int x = 0, int y = 0, int w = 0, int h = 0);
Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font,
const string& title = "", int x = 0, int y = 0, int w = 0, int h = 0);
~Dialog() override;
void open();
void close();
bool isVisible() const override { return _visible; }
virtual void setPosition();
virtual void drawDialog();
virtual void loadConfig() { }
virtual void saveConfig() { }
virtual void setDefaults() { }
virtual void resetSurfaces();
void setDirty() override;
void setDirtyChain() override;
void redraw(bool force = false);
void drawChain() override;
void render();
void tick() override;
void addFocusWidget(Widget* w) override;
void addToFocusList(const WidgetArray& list) override;
void addToFocusList(const WidgetArray& list, TabWidget* w, int tabId);
void addBGroupToFocusList(const WidgetArray& list) { _buttonGroup = list; }
void addTabWidget(TabWidget* w);
void addDefaultWidget(ButtonWidget* w) { _defaultWidget = w; }
void addExtraWidget(ButtonWidget* w) { _extraWidget = w; }
void addOKWidget(ButtonWidget* w) { _okWidget = w; }
void addCancelWidget(ButtonWidget* w) { _cancelWidget = w; }
void setFocus(Widget* w);
/** Returns the base surface associated with this dialog. */
FBSurface& surface() const { return *_surface; }
/**
This method is called each time the main Dialog::render is called.
It is called *after* the dialog has been rendered, so it can be
used to render another surface on top of it, among other things.
*/
void addRenderCallback(const RenderCallback& callback);
void setTitle(const string& title);
bool hasTitle() { return !_title.empty(); }
void initHelp();
void setHelpAnchor(const string& helpAnchor, bool debugger = false);
void setHelpURL(const string& helpURL);
virtual bool isShading() const { return true; }
/**
Determine the maximum width/height of a dialog based on the minimum
allowable bounds, also taking into account the current window size.
Currently scales the width/height to 95% of allowable area when possible.
NOTE: This method is meant to be used for dynamic, resizeable dialogs.
That is, those that can change size during a program run, and
*have* to take the current window size into account.
@param w The resulting width to use for the dialog
@param h The resulting height to use for the dialog
@return True if the dialog fits in the current window (scaled to 90%)
False if the dialog is smaller than the current window, and
has to be scaled down
*/
bool getDynamicBounds(uInt32& w, uInt32& h) const;
/**
Checks if the dialogs fits into the actual sizes.
@param w The resulting width to use for the dialog
@param h The resulting height to use for the dialog
@return True if the dialog should be resized
*/
bool shouldResize(uInt32& w, uInt32& h) const;
ToolTip& tooltip() { return *_toolTip; }
int lineHeight() const { return _font.getLineHeight(); }
int fontHeight() const { return _font.getFontHeight(); }
int fontWidth() const { return _font.getMaxCharWidth(); }
int buttonHeight() const { return lineHeight() * 1.25; }
int buttonWidth(const string& label) const {
return _font.getStringWidth(label) + fontWidth() * 2.5;
}
int buttonGap() const { return fontWidth(); }
int hBorder() const { return fontWidth() * 1.25; }
int vBorder() const { return fontHeight() / 2; }
int vGap() const { return fontHeight() / 4; }
int indent() const { return fontWidth() * 2; }
protected:
void draw() override { }
void releaseFocus() override;
virtual void handleText(char text);
virtual void handleKeyDown(StellaKey key, StellaMod modifiers, bool repeated = false);
virtual void handleKeyUp(StellaKey key, StellaMod modifiers);
virtual void handleMouseDown(int x, int y, MouseButton b, int clickCount);
virtual void handleMouseUp(int x, int y, MouseButton b, int clickCount);
virtual void handleMouseWheel(int x, int y, int direction);
virtual void handleMouseMoved(int x, int y);
virtual bool handleMouseClicks(int x, int y, MouseButton b);
virtual void handleJoyDown(int stick, int button, bool longPress = false);
virtual void handleJoyUp(int stick, int button);
virtual void handleJoyAxis(int stick, JoyAxis axis, JoyDir adir, int button = JOY_CTRL_NONE);
virtual bool handleJoyHat(int stick, int hat, JoyHatDir hdir, int button = JOY_CTRL_NONE);
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
virtual Event::Type getJoyAxisEvent(int stick, JoyAxis axis, JoyDir adir, int button);
Widget* findWidget(int x, int y) const; // Find the widget at pos x,y if any
void addOKBGroup(WidgetArray& wid, const GUI::Font& font,
const string& okText = "OK",
int buttonWidth = 0);
void addOKCancelBGroup(WidgetArray& wid, const GUI::Font& font,
const string& okText = "OK",
const string& cancelText = "Cancel",
bool focusOKButton = true,
int buttonWidth = 0);
void addDefaultsOKCancelBGroup(WidgetArray& wid, const GUI::Font& font,
const string& okText = "OK",
const string& cancelText = "Cancel",
const string& defaultsText = "Defaults",
bool focusOKButton = true);
// NOTE: This method, and the three above it, are due to be refactored at some
// point, since the parameter list is kind of getting ridiculous
void addDefaultsExtraOKCancelBGroup(WidgetArray& wid, const GUI::Font& font,
const string& extraText, int extraCmd,
const string& okText = "OK",
const string& cancelText = "Cancel",
const string& defaultsText = "Defaults",
bool focusOKButton = true);
void processCancelWithoutWidget(bool state = true) { _processCancel = state; }
virtual void processCancel() { close(); }
/** Define the size (allowed) for the dialog. */
void setSize(uInt32 w, uInt32 h, uInt32 max_w, uInt32 max_h);
void positionAt(uInt32 pos);
virtual bool repeatEnabled() { return true; }
private:
enum {
kHelpCmd = 'DlHp'
};
void buildCurrentFocusList(int tabID = -1);
bool handleNavEvent(Event::Type e, bool repeated = false);
void getTabIdForWidget(Widget* w);
bool cycleTab(int direction);
const string getHelpURL() const override;
bool hasHelp() const override { return !getHelpURL().empty(); }
void openHelp();
protected:
const GUI::Font& _font;
Widget* _mouseWidget{nullptr};
Widget* _focusedWidget{nullptr};
Widget* _dragWidget{nullptr};
ButtonWidget* _defaultWidget{nullptr};
ButtonWidget* _extraWidget{nullptr};
ButtonWidget* _okWidget{nullptr};
ButtonWidget* _cancelWidget{nullptr};
bool _visible{false};
bool _processCancel{false};
string _title;
int _th{0};
int _layer{0};
unique_ptr<ToolTip> _toolTip;
string _helpAnchor;
string _helpURL;
bool _debuggerHelp{false};
ButtonWidget* _helpWidget{nullptr};
private:
struct Focus {
Widget* widget{nullptr};
WidgetArray list;
explicit Focus(Widget* w = nullptr) : widget{w} { }
};
using FocusList = vector<Focus>;
struct TabFocus {
TabWidget* widget{nullptr};
FocusList focus;
uInt32 currentTab{0};
explicit TabFocus(TabWidget* w = nullptr) : widget{w} { }
void appendFocusList(WidgetArray& list);
void saveCurrentFocus(Widget* w);
Widget* getNewFocus();
};
using TabFocusList = vector<TabFocus>;
Focus _myFocus; // focus for base dialog
TabFocusList _myTabList; // focus for each tab (if any)
WidgetArray _buttonGroup;
unique_ptr<FBSurface> _surface;
unique_ptr<FBSurface> _shadeSurface;
int _tabID{0};
uInt32 _max_w{0}; // maximum wanted width
uInt32 _max_h{0}; // maximum wanted height
RenderCallback _renderCallback;
private:
// Following constructors and assignment operators not supported
Dialog() = delete;
Dialog(const Dialog&) = delete;
Dialog(Dialog&&) = delete;
Dialog& operator=(const Dialog&) = delete;
Dialog& operator=(Dialog&&) = delete;
};
#endif