1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//
17// Based on code from ScummVM - Scumm Interpreter
18// Copyright (C) 2002-2004 The ScummVM project
19//============================================================================
20
21#ifndef DIALOG_HXX
22#define DIALOG_HXX
23
24class FBSurface;
25class OSystem;
26class DialogContainer;
27class TabWidget;
28class CommandSender;
29
30#include "Stack.hxx"
31#include "Widget.hxx"
32#include "GuiObject.hxx"
33#include "StellaKeys.hxx"
34#include "EventHandlerConstants.hxx"
35#include "bspf.hxx"
36
37/**
38 This is the base class for all dialog boxes.
39
40 @author Stephen Anthony
41*/
42class Dialog : public GuiObject
43{
44 friend class DialogContainer;
45
46 public:
47 Dialog(OSystem& instance, DialogContainer& parent,
48 int x = 0, int y = 0, int w = 0, int h = 0);
49 Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font,
50 const string& title = "", int x = 0, int y = 0, int w = 0, int h = 0);
51
52 virtual ~Dialog();
53
54 void open();
55 void close();
56
57 bool isVisible() const override { return _visible; }
58 bool isOnTop() const { return _onTop; }
59
60 virtual void center();
61 virtual void drawDialog();
62 virtual void loadConfig() { }
63 virtual void saveConfig() { }
64 virtual void setDefaults() { }
65
66 // A dialog being dirty indicates that its underlying surface needs to be
67 // redrawn and then re-rendered; this is taken care of in ::render()
68 void setDirty() override { _dirty = true; }
69 bool isDirty() const { return _dirty; }
70 bool render();
71
72 void addFocusWidget(Widget* w) override;
73 void addToFocusList(WidgetArray& list) override;
74 void addToFocusList(WidgetArray& list, TabWidget* w, int tabId);
75 void addBGroupToFocusList(WidgetArray& list) { _buttonGroup = list; }
76 void addTabWidget(TabWidget* w);
77 void addDefaultWidget(Widget* w) { _defaultWidget = w; }
78 void addOKWidget(Widget* w) { _okWidget = w; }
79 void addCancelWidget(Widget* w) { _cancelWidget = w; }
80 void setFocus(Widget* w);
81
82 /** Returns the base surface associated with this dialog. */
83 FBSurface& surface() const { return *_surface; }
84
85 /**
86 Adds a surface to this dialog, which is rendered on top of the
87 base surface whenever the base surface is re-rendered. Since
88 the surface render() call will always occur in such a case, the
89 surface should call setVisible() to enable/disable its output.
90 */
91 void addSurface(shared_ptr<FBSurface> surface);
92
93 void setFlags(int flags) { _flags |= flags; setDirty(); }
94 void clearFlags(int flags) { _flags &= ~flags; setDirty(); }
95 int getFlags() const { return _flags; }
96
97 void setTitle(const string& title);
98 bool hasTitle() { return !_title.empty(); }
99
100 /**
101 Determine the maximum width/height of a dialog based on the minimum
102 allowable bounds, also taking into account the current window size.
103 Currently scales the width/height to 95% of allowable area when possible.
104
105 NOTE: This method is meant to be used for dynamic, resizeable dialogs.
106 That is, those that can change size during a program run, and
107 *have* to take the current window size into account.
108
109 @param w The resulting width to use for the dialog
110 @param h The resulting height to use for the dialog
111
112 @return True if the dialog fits in the current window (scaled to 90%)
113 False if the dialog is smaller than the current window, and
114 has to be scaled down
115 */
116 bool getDynamicBounds(uInt32& w, uInt32& h) const;
117
118 /**
119 Checks if the dialogs fits into the actual sizes.
120
121 @param w The resulting width to use for the dialog
122 @param h The resulting height to use for the dialog
123
124 @return True if the dialog should be resized
125 */
126 bool shouldResize(uInt32& w, uInt32& h) const;
127
128 protected:
129 virtual void draw() override { }
130 void releaseFocus() override;
131
132 virtual void handleText(char text);
133 virtual void handleKeyDown(StellaKey key, StellaMod modifiers, bool repeated = false);
134 virtual void handleKeyUp(StellaKey key, StellaMod modifiers);
135 virtual void handleMouseDown(int x, int y, MouseButton b, int clickCount);
136 virtual void handleMouseUp(int x, int y, MouseButton b, int clickCount);
137 virtual void handleMouseWheel(int x, int y, int direction);
138 virtual void handleMouseMoved(int x, int y);
139 virtual bool handleMouseClicks(int x, int y, MouseButton b);
140 virtual void handleJoyDown(int stick, int button, bool longPress = false);
141 virtual void handleJoyUp(int stick, int button);
142 virtual void handleJoyAxis(int stick, JoyAxis axis, JoyDir adir, int button = JOY_CTRL_NONE);
143 virtual bool handleJoyHat(int stick, int hat, JoyHatDir hdir, int button = JOY_CTRL_NONE);
144 virtual void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
145 virtual Event::Type getJoyAxisEvent(int stick, JoyAxis axis, JoyDir adir, int button);
146
147 Widget* findWidget(int x, int y) const; // Find the widget at pos x,y if any
148
149 void addOKCancelBGroup(WidgetArray& wid, const GUI::Font& font,
150 const string& okText = "OK",
151 const string& cancelText = "Cancel",
152 bool focusOKButton = true,
153 int buttonWidth = 0);
154
155 void addDefaultsOKCancelBGroup(WidgetArray& wid, const GUI::Font& font,
156 const string& okText = "OK",
157 const string& cancelText = "Cancel",
158 const string& defaultsText = "Defaults",
159 bool focusOKButton = true);
160
161 void processCancelWithoutWidget(bool state) { _processCancel = state; }
162 virtual void processCancel() { close(); }
163
164 /** Define the size (allowed) for the dialog. */
165 void setSize(uInt32 w, uInt32 h, uInt32 max_w, uInt32 max_h);
166 void positionAt(uInt32 pos);
167
168 virtual bool repeatEnabled() { return true; }
169
170 private:
171 void buildCurrentFocusList(int tabID = -1);
172 bool handleNavEvent(Event::Type e, bool repeated = false);
173 void getTabIdForWidget(Widget* w);
174 bool cycleTab(int direction);
175
176 protected:
177 const GUI::Font& _font;
178
179 Widget* _mouseWidget;
180 Widget* _focusedWidget;
181 Widget* _dragWidget;
182 Widget* _defaultWidget;
183 Widget* _okWidget;
184 Widget* _cancelWidget;
185
186 bool _visible;
187 bool _onTop;
188 bool _processCancel;
189 string _title;
190 int _th;
191 int _layer;
192
193 Common::FixedStack<shared_ptr<FBSurface>> mySurfaceStack;
194
195 private:
196 struct Focus {
197 Widget* widget;
198 WidgetArray list;
199
200 explicit Focus(Widget* w = nullptr) : widget(w) { }
201 virtual ~Focus() = default;
202
203 Focus(const Focus&) = default;
204 Focus& operator=(const Focus&) = default;
205 };
206 using FocusList = vector<Focus>;
207
208 struct TabFocus {
209 TabWidget* widget;
210 FocusList focus;
211 uInt32 currentTab;
212
213 explicit TabFocus(TabWidget* w = nullptr) : widget(w), currentTab(0) { }
214 virtual ~TabFocus() = default;
215
216 TabFocus(const TabFocus&) = default;
217 TabFocus& operator=(const TabFocus&) = default;
218
219 void appendFocusList(WidgetArray& list);
220 void saveCurrentFocus(Widget* w);
221 Widget* getNewFocus();
222 };
223 using TabFocusList = vector<TabFocus>;
224
225 Focus _myFocus; // focus for base dialog
226 TabFocusList _myTabList; // focus for each tab (if any)
227
228 WidgetArray _buttonGroup;
229 shared_ptr<FBSurface> _surface;
230
231 int _tabID;
232 int _flags;
233 bool _dirty;
234 uInt32 _max_w; // maximum wanted width
235 uInt32 _max_h; // maximum wanted height
236
237 private:
238 // Following constructors and assignment operators not supported
239 Dialog() = delete;
240 Dialog(const Dialog&) = delete;
241 Dialog(Dialog&&) = delete;
242 Dialog& operator=(const Dialog&) = delete;
243 Dialog& operator=(Dialog&&) = delete;
244};
245
246#endif
247