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-2021 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 WIDGET_HXX
22 #define WIDGET_HXX
23 
24 class Dialog;
25 
26 #include <cassert>
27 
28 #include "bspf.hxx"
29 #include "Rect.hxx"
30 #include "Event.hxx"
31 #include "EventHandlerConstants.hxx"
32 #include "FrameBufferConstants.hxx"
33 #include "StellaKeys.hxx"
34 #include "GuiObject.hxx"
35 #include "Font.hxx"
36 
37 /**
38   This is the base class for all widgets.
39 
40   @author  Stephen Anthony
41 */
42 class Widget : public GuiObject
43 {
44   friend class Dialog;
45 
46   public:
47     Widget(GuiObject* boss, const GUI::Font& font, int x, int y, int w, int h);
48     ~Widget() override;
49 
getAbsX() const50     virtual int getAbsX() const override { return _x + _boss->getChildX(); }
getAbsY() const51     virtual int getAbsY() const override { return _y + _boss->getChildY(); }
getLeft() const52     virtual int getLeft() const { return _x; }
getTop() const53     virtual int getTop() const { return _y; }
getRight() const54     virtual int getRight() const { return _x + getWidth(); }
getBottom() const55     virtual int getBottom() const { return _y + getHeight(); }
56     virtual void setPosX(int x);
57     virtual void setPosY(int y);
58     virtual void setPos(int x, int y);
59     virtual void setPos(const Common::Point& pos);
60     void setWidth(int w) override;
61     void setHeight(int h) override;
62     virtual void setSize(int w, int h);
63     virtual void setSize(const Common::Point& pos);
64 
handleText(char text)65     virtual bool handleText(char text)                        { return false; }
handleKeyDown(StellaKey key,StellaMod mod)66     virtual bool handleKeyDown(StellaKey key, StellaMod mod)  { return false; }
handleKeyUp(StellaKey key,StellaMod mod)67     virtual bool handleKeyUp(StellaKey key, StellaMod mod)    { return false; }
handleMouseDown(int x,int y,MouseButton b,int clickCount)68     virtual void handleMouseDown(int x, int y, MouseButton b, int clickCount) { }
handleMouseUp(int x,int y,MouseButton b,int clickCount)69     virtual void handleMouseUp(int x, int y, MouseButton b, int clickCount) { }
70     virtual void handleMouseEntered();
71     virtual void handleMouseLeft();
handleMouseMoved(int x,int y)72     virtual void handleMouseMoved(int x, int y) { }
handleMouseWheel(int x,int y,int direction)73     virtual void handleMouseWheel(int x, int y, int direction) { }
handleMouseClicks(int x,int y,MouseButton b)74     virtual bool handleMouseClicks(int x, int y, MouseButton b) { return false; }
handleJoyDown(int stick,int button,bool longPress=false)75     virtual void handleJoyDown(int stick, int button, bool longPress = false) { }
handleJoyUp(int stick,int button)76     virtual void handleJoyUp(int stick, int button) { }
handleJoyAxis(int stick,JoyAxis axis,JoyDir adir,int button=JOY_CTRL_NONE)77     virtual void handleJoyAxis(int stick, JoyAxis axis, JoyDir adir, int button = JOY_CTRL_NONE) { }
handleJoyHat(int stick,int hat,JoyHatDir hdir,int button=JOY_CTRL_NONE)78     virtual bool handleJoyHat(int stick, int hat, JoyHatDir hdir, int button = JOY_CTRL_NONE) { return false; }
handleEvent(Event::Type event)79     virtual bool handleEvent(Event::Type event) { return false; }
80 
81     void tick() override;
82 
83     void setDirty() override;
84     void setDirtyChain() override;
85     void draw() override;
86     void drawChain() override;
87     void receivedFocus();
88     void lostFocus();
addFocusWidget(Widget * w)89     void addFocusWidget(Widget* w) override { _focusList.push_back(w); }
addToFocusList(const WidgetArray & list)90     void addToFocusList(const WidgetArray& list) override {
91       Vec::append(_focusList, list);
92     }
93 
94     /** Set/clear FLAG_ENABLED */
95     void setEnabled(bool e);
96 
isEnabled() const97     bool isEnabled() const          { return _flags & FLAG_ENABLED;         }
isVisible() const98     bool isVisible() const override { return !(_flags & FLAG_INVISIBLE);    }
isHighlighted() const99     bool isHighlighted() const      { return _flags & FLAG_HILITED; }
hasMouseFocus() const100     bool hasMouseFocus() const      { return _flags & FLAG_MOUSE_FOCUS; }
wantsFocus() const101     virtual bool wantsFocus() const { return _flags & FLAG_RETAIN_FOCUS;    }
wantsTab() const102     bool wantsTab() const           { return _flags & FLAG_WANTS_TAB;       }
wantsRaw() const103     bool wantsRaw() const           { return _flags & FLAG_WANTS_RAWDATA;   }
104 
setID(uInt32 id)105     void setID(uInt32 id) { _id = id;   }
getID() const106     uInt32 getID() const  { return _id; }
107 
font() const108     virtual const GUI::Font& font() const { return _font; }
109 
setTextColor(ColorId color)110     void setTextColor(ColorId color)   { _textcolor = color;   setDirty(); }
setTextColorHi(ColorId color)111     void setTextColorHi(ColorId color) { _textcolorhi = color; setDirty(); }
setBGColor(ColorId color)112     void setBGColor(ColorId color)     { _bgcolor = color;     setDirty(); }
setBGColorHi(ColorId color)113     void setBGColorHi(ColorId color)   { _bgcolorhi = color;   setDirty(); }
setShadowColor(ColorId color)114     void setShadowColor(ColorId color) { _shadowcolor = color; setDirty(); }
115 
116     void setToolTip(const string& text);
getToolTip(const Common::Point & pos) const117     virtual string getToolTip(const Common::Point& pos) const { return _toolTipText; }
changedToolTip(const Common::Point & oldPos,const Common::Point & newPos) const118     virtual bool changedToolTip(const Common::Point& oldPos,
119                                 const Common::Point& newPos) const { return false; }
120 
121     void setHelpAnchor(const string& helpAnchor, bool debugger = false);
122     void setHelpURL(const string& helpURL);
123 
loadConfig()124     virtual void loadConfig() { }
125 
126   protected:
drawWidget(bool hilite)127     virtual void drawWidget(bool hilite) { }
128 
receivedFocusWidget()129     virtual void receivedFocusWidget() { }
lostFocusWidget()130     virtual void lostFocusWidget() { }
131 
findWidget(int x,int y)132     virtual Widget* findWidget(int x, int y) { return this; }
133 
releaseFocus()134     void releaseFocus() override { assert(_boss); _boss->releaseFocus(); }
135 
wantsToolTip() const136     virtual bool wantsToolTip() const { return hasMouseFocus() && hasToolTip(); }
hasToolTip() const137     virtual bool hasToolTip() const { return !_toolTipText.empty(); }
138 
139     // By default, delegate unhandled commands to the boss
handleCommand(CommandSender * sender,int cmd,int data,int id)140     void handleCommand(CommandSender* sender, int cmd, int data, int id) override
141          { assert(_boss); _boss->handleCommand(sender, cmd, data, id); }
142 
143     const string getHelpURL() const override;
hasHelp() const144     bool hasHelp() const override { return !getHelpURL().empty(); }
145 
146   protected:
147     GuiObject* _boss{nullptr};
148     const GUI::Font& _font;
149     Widget*    _next{nullptr};
150     uInt32     _id{0};
151     bool       _hasFocus{false};
152     int        _fontWidth{0};
153     int        _lineHeight{0};
154     ColorId    _bgcolor{kWidColor};
155     ColorId    _bgcolorhi{kWidColor};
156     ColorId    _bgcolorlo{kBGColorLo};
157     ColorId    _textcolor{kTextColor};
158     ColorId    _textcolorhi{kTextColorHi};
159     ColorId    _textcolorlo{kBGColorLo};
160     ColorId    _shadowcolor{kShadowColor};
161     string     _toolTipText;
162     string     _helpAnchor;
163     string     _helpURL;
164     bool       _debuggerHelp{false};
165 
166   public:
167     static Widget* findWidgetInChain(Widget* start, int x, int y);
168 
169     /** Determine if 'find' is in the chain pointed to by 'start' */
170     static bool isWidgetInChain(Widget* start, Widget* find);
171 
172     /** Determine if 'find' is in the widget array */
173     static bool isWidgetInChain(const WidgetArray& list, Widget* find);
174 
175     /** Select either previous, current, or next widget in chain to have
176         focus, and deselects all others */
177     static Widget* setFocusForChain(GuiObject* boss, WidgetArray& arr,
178                                     Widget* w, int direction,
179                                     bool emitFocusEvents = true);
180 
181     /** Sets all widgets in this chain to be dirty (must be redrawn) */
182     static void setDirtyInChain(Widget* start);
183 
184   private:
185     // Following constructors and assignment operators not supported
186     Widget() = delete;
187     Widget(const Widget&) = delete;
188     Widget(Widget&&) = delete;
189     Widget& operator=(const Widget&) = delete;
190     Widget& operator=(Widget&&) = delete;
191 };
192 
193 /* StaticTextWidget */
194 class StaticTextWidget : public Widget, public CommandSender
195 {
196   public:
197     enum {
198       kClickedCmd = 'STcl',
199       kOpenUrlCmd = 'STou'
200     };
201 
202   public:
203     StaticTextWidget(GuiObject* boss, const GUI::Font& font,
204                      int x, int y, int w, int h,
205                      const string& text = "", TextAlign align = TextAlign::Left,
206                      ColorId shadowColor = kNone);
207     StaticTextWidget(GuiObject* boss, const GUI::Font& font,
208                      int x, int y,
209                      const string& text = "", TextAlign align = TextAlign::Left,
210                      ColorId shadowColor = kNone);
211     ~StaticTextWidget() override = default;
212 
setCmd(int cmd)213     void setCmd(int cmd) { _cmd = cmd; }
214 
215     void setValue(int value);
216     void setLabel(const string& label);
setAlign(TextAlign align)217     void setAlign(TextAlign align) { _align = align; setDirty(); }
getLabel() const218     const string& getLabel() const { return _label; }
isEditable() const219     bool isEditable() const { return _editable; }
220 
221     void setLink(size_t start = string::npos, int len = 0, bool underline = false);
222     bool setUrl(const string& url = EmptyString, const string& label = EmptyString,
223                 const string& placeHolder = EmptyString);
getUrl() const224     const string& getUrl() const { return _url; }
225 
226   protected:
227     void handleMouseEntered() override;
228     void handleMouseLeft() override;
229     void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
230 
231     void drawWidget(bool hilite) override;
232 
233   protected:
234     string    _label;
235     bool      _editable{false};
236     TextAlign _align{TextAlign::Left};
237     int       _cmd{0};
238     size_t    _linkStart{string::npos};
239     int       _linkLen{0};
240     bool      _linkUnderline{false};
241     string    _url;
242 
243   private:
244     // Following constructors and assignment operators not supported
245     StaticTextWidget() = delete;
246     StaticTextWidget(const StaticTextWidget&) = delete;
247     StaticTextWidget(StaticTextWidget&&) = delete;
248     StaticTextWidget& operator=(const StaticTextWidget&) = delete;
249     StaticTextWidget& operator=(StaticTextWidget&&) = delete;
250 };
251 
252 /* ButtonWidget */
253 class ButtonWidget : public StaticTextWidget
254 {
255   public:
256     ButtonWidget(GuiObject* boss, const GUI::Font& font,
257                  int x, int y, int w, int h,
258                  const string& label, int cmd = 0, bool repeat = false);
259     ButtonWidget(GuiObject* boss, const GUI::Font& font,
260                  int x, int y, int dw,
261                  const string& label, int cmd = 0, bool repeat = false);
262     ButtonWidget(GuiObject* boss, const GUI::Font& font,
263                  int x, int y,
264                  const string& label, int cmd = 0, bool repeat = false);
265     ButtonWidget(GuiObject* boss, const GUI::Font& font,
266                  int x, int y, int dw, int dh,
267                  const uInt32* bitmap, int bmw, int bmh,
268                  int cmd = 0, bool repeat = false);
269     ~ButtonWidget() override = default;
270 
271     bool handleEvent(Event::Type event) override;
272 
setCmd(int cmd)273     void setCmd(int cmd)  { _cmd = cmd; }
getCmd() const274     int getCmd() const    { return _cmd; }
275     /* Sets/changes the button's bitmap **/
276     void setBitmap(const uInt32* bitmap, int bmw, int bmh);
277 
278   protected:
279     bool handleMouseClicks(int x, int y, MouseButton b) override;
280     void handleMouseDown(int x, int y, MouseButton b, int clickCount) override;
281     void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
282     void handleMouseEntered() override;
283     void handleMouseLeft() override;
284 
285     void drawWidget(bool hilite) override;
286 
287   protected:
288     bool _repeat{false}; // button repeats
289     bool _useBitmap{false};
290     const uInt32* _bitmap{nullptr};
291     int  _bmw{0}, _bmh{0};
292 
293   private:
294     // Following constructors and assignment operators not supported
295     ButtonWidget() = delete;
296     ButtonWidget(const ButtonWidget&) = delete;
297     ButtonWidget(ButtonWidget&&) = delete;
298     ButtonWidget& operator=(const ButtonWidget&) = delete;
299     ButtonWidget& operator=(ButtonWidget&&) = delete;
300 };
301 
302 /* CheckboxWidget */
303 class CheckboxWidget : public ButtonWidget
304 {
305   public:
306     enum { kCheckActionCmd  = 'CBAC' };
307     enum class FillType { Normal, Inactive, Circle };
308 
309   public:
310     CheckboxWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
311                    const string& label, int cmd = 0);
312     ~CheckboxWidget() override = default;
313 
314     void setEditable(bool editable);
315     void setFill(FillType type);
316 
317     void setState(bool state, bool changed = false);
toggleState()318     void toggleState()     { setState(!_state); }
getState() const319     bool getState() const  { return _state;     }
320 
321     void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
322 
boxSize(const GUI::Font & font)323     static int boxSize(const GUI::Font& font)
324     {
325       return font.getFontHeight() < 24 ? 14 : 22; // box is square
326     }
prefixSize(const GUI::Font & font)327     static int prefixSize(const GUI::Font& font)
328     {
329       return boxSize(font) + font.getMaxCharWidth() * 0.75;
330     }
331 
332   protected:
333     void drawWidget(bool hilite) override;
334 
335   protected:
336     bool _state{false};
337     bool _holdFocus{true};
338     bool _drawBox{true};
339     bool _changed{false};
340 
341     const uInt32* _outerCircle{nullptr};
342     const uInt32* _innerCircle{nullptr};
343     const uInt32* _img{nullptr};
344     ColorId _fillColor{kColor};
345     int _boxY{0};
346     int _textY{0};
347     int _boxSize{14};
348 
349   private:
350     // Following constructors and assignment operators not supported
351     CheckboxWidget() = delete;
352     CheckboxWidget(const CheckboxWidget&) = delete;
353     CheckboxWidget(CheckboxWidget&&) = delete;
354     CheckboxWidget& operator=(const CheckboxWidget&) = delete;
355     CheckboxWidget& operator=(CheckboxWidget&&) = delete;
356 };
357 
358 /* SliderWidget */
359 class SliderWidget : public ButtonWidget
360 {
361   public:
362     SliderWidget(GuiObject* boss, const GUI::Font& font,
363                  int x, int y, int w, int h,
364                  const string& label = "", int labelWidth = 0, int cmd = 0,
365                  int valueLabelWidth = 0, const string& valueUnit = "",
366                  int valueLabelGap = 0, bool forceLabelSign = false);
367     SliderWidget(GuiObject* boss, const GUI::Font& font,
368                  int x, int y,
369                  const string& label = "", int labelWidth = 0, int cmd = 0,
370                  int valueLabelWidth = 0, const string& valueUnit = "",
371                  int valueLabelGap = 0, bool forceLabelSign = false);
372     ~SliderWidget() override = default;
373 
374     void setValue(int value);
getValue() const375     int getValue() const { return BSPF::clamp(_value, _valueMin, _valueMax); }
376 
377     void setMinValue(int value);
getMinValue() const378     int  getMinValue() const { return _valueMin; }
379     void setMaxValue(int value);
getMaxValue() const380     int  getMaxValue() const { return _valueMax; }
381     void setStepValue(int value);
getStepValue() const382     int  getStepValue() const { return _stepValue; }
383     void setValueLabel(const string& valueLabel);
384     void setValueLabel(int value);
getValueLabel() const385     const string& getValueLabel() const { return _valueLabel; }
386     void setValueUnit(const string& valueUnit);
387 
388     void setTickmarkIntervals(int numIntervals);
389 
390   protected:
391     void handleMouseMoved(int x, int y) override;
392     void handleMouseDown(int x, int y, MouseButton b, int clickCount) override;
393     void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
394     void handleMouseWheel(int x, int y, int direction) override;
395     bool handleEvent(Event::Type event) override;
396 
397     void drawWidget(bool hilite) override;
398 
399     int valueToPos(int value) const;
400     int posToValue(int pos) const;
401 
402   protected:
403     int    _value{-INT_MAX}, _stepValue{1};
404     int    _valueMin{0}, _valueMax{100};
405     bool   _isDragging{false};
406     int    _labelWidth{0};
407     string _valueLabel;
408     string _valueUnit;
409     int    _valueLabelGap{0};
410     int    _valueLabelWidth{0};
411     bool   _forceLabelSign{false};
412     int    _numIntervals{0};
413 
414   private:
415     // Following constructors and assignment operators not supported
416     SliderWidget() = delete;
417     SliderWidget(const SliderWidget&) = delete;
418     SliderWidget(SliderWidget&&) = delete;
419     SliderWidget& operator=(const SliderWidget&) = delete;
420     SliderWidget& operator=(SliderWidget&&) = delete;
421 };
422 
423 #endif
424