| 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 WIDGET_HXX |
| 22 | #define WIDGET_HXX |
| 23 | |
| 24 | class Dialog; |
| 25 | |
| 26 | namespace GUI { |
| 27 | class Font; |
| 28 | } |
| 29 | |
| 30 | #include <cassert> |
| 31 | |
| 32 | #include "bspf.hxx" |
| 33 | #include "Event.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 | enum : uInt32 { |
| 48 | FLAG_ENABLED = 1 << 0, |
| 49 | FLAG_INVISIBLE = 1 << 1, |
| 50 | FLAG_HILITED = 1 << 2, |
| 51 | FLAG_BORDER = 1 << 3, |
| 52 | FLAG_CLEARBG = 1 << 4, |
| 53 | FLAG_TRACK_MOUSE = 1 << 5, |
| 54 | FLAG_RETAIN_FOCUS = 1 << 6, |
| 55 | FLAG_WANTS_TAB = 1 << 7, |
| 56 | FLAG_WANTS_RAWDATA = 1 << 8 |
| 57 | }; |
| 58 | |
| 59 | public: |
| 60 | Widget(GuiObject* boss, const GUI::Font& font, int x, int y, int w, int h); |
| 61 | virtual ~Widget(); |
| 62 | |
| 63 | virtual int getAbsX() const override { return _x + _boss->getChildX(); } |
| 64 | virtual int getAbsY() const override { return _y + _boss->getChildY(); } |
| 65 | virtual int getLeft() const { return _x; } |
| 66 | virtual int getTop() const { return _y; } |
| 67 | virtual int getRight() const { return _x + getWidth(); } |
| 68 | virtual int getBottom() const { return _y + getHeight(); } |
| 69 | |
| 70 | virtual bool handleText(char text) { return false; } |
| 71 | virtual bool handleKeyDown(StellaKey key, StellaMod mod) { return false; } |
| 72 | virtual bool handleKeyUp(StellaKey key, StellaMod mod) { return false; } |
| 73 | virtual void handleMouseDown(int x, int y, MouseButton b, int clickCount) { } |
| 74 | virtual void handleMouseUp(int x, int y, MouseButton b, int clickCount) { } |
| 75 | virtual void handleMouseEntered() { } |
| 76 | virtual void handleMouseLeft() { } |
| 77 | virtual void handleMouseMoved(int x, int y) { } |
| 78 | virtual void handleMouseWheel(int x, int y, int direction) { } |
| 79 | virtual bool handleMouseClicks(int x, int y, MouseButton b) { return false; } |
| 80 | virtual void handleJoyDown(int stick, int button, bool longPress = false) { } |
| 81 | virtual void handleJoyUp(int stick, int button) { } |
| 82 | virtual void handleJoyAxis(int stick, JoyAxis axis, JoyDir adir, int button = JOY_CTRL_NONE) { } |
| 83 | virtual bool handleJoyHat(int stick, int hat, JoyHatDir hdir, int button = JOY_CTRL_NONE) { return false; } |
| 84 | virtual bool handleEvent(Event::Type event) { return false; } |
| 85 | |
| 86 | void setDirty() override; |
| 87 | void draw() override; |
| 88 | void receivedFocus(); |
| 89 | void lostFocus(); |
| 90 | void addFocusWidget(Widget* w) override { _focusList.push_back(w); } |
| 91 | void addToFocusList(WidgetArray& list) override { |
| 92 | Vec::append(_focusList, list); |
| 93 | } |
| 94 | |
| 95 | /** Set/clear FLAG_ENABLED */ |
| 96 | void setEnabled(bool e); |
| 97 | |
| 98 | void setFlags(uInt32 flags) { _flags |= flags; setDirty(); } |
| 99 | void clearFlags(uInt32 flags) { _flags &= ~flags; setDirty(); } |
| 100 | uInt32 getFlags() const { return _flags; } |
| 101 | |
| 102 | bool isEnabled() const { return _flags & FLAG_ENABLED; } |
| 103 | bool isVisible() const override { return !(_flags & FLAG_INVISIBLE); } |
| 104 | virtual bool wantsFocus() const { return _flags & FLAG_RETAIN_FOCUS; } |
| 105 | bool wantsTab() const { return _flags & FLAG_WANTS_TAB; } |
| 106 | bool wantsRaw() const { return _flags & FLAG_WANTS_RAWDATA; } |
| 107 | |
| 108 | void setID(uInt32 id) { _id = id; } |
| 109 | uInt32 getID() const { return _id; } |
| 110 | |
| 111 | virtual const GUI::Font& font() const { return _font; } |
| 112 | |
| 113 | void setTextColor(ColorId color) { _textcolor = color; setDirty(); } |
| 114 | void setTextColorHi(ColorId color) { _textcolorhi = color; setDirty(); } |
| 115 | void setBGColor(ColorId color) { _bgcolor = color; setDirty(); } |
| 116 | void setBGColorHi(ColorId color) { _bgcolorhi = color; setDirty(); } |
| 117 | void setShadowColor(ColorId color) { _shadowcolor = color; setDirty(); } |
| 118 | |
| 119 | virtual void loadConfig() { } |
| 120 | |
| 121 | protected: |
| 122 | virtual void drawWidget(bool hilite) { } |
| 123 | |
| 124 | virtual void receivedFocusWidget() { } |
| 125 | virtual void lostFocusWidget() { } |
| 126 | |
| 127 | virtual Widget* findWidget(int x, int y) { return this; } |
| 128 | |
| 129 | void releaseFocus() override { assert(_boss); _boss->releaseFocus(); } |
| 130 | |
| 131 | // By default, delegate unhandled commands to the boss |
| 132 | void handleCommand(CommandSender* sender, int cmd, int data, int id) override |
| 133 | { assert(_boss); _boss->handleCommand(sender, cmd, data, id); } |
| 134 | |
| 135 | protected: |
| 136 | GuiObject* _boss; |
| 137 | const GUI::Font& _font; |
| 138 | Widget* _next; |
| 139 | uInt32 _id; |
| 140 | uInt32 _flags; |
| 141 | bool _hasFocus; |
| 142 | int _fontWidth; |
| 143 | int _fontHeight; |
| 144 | ColorId _bgcolor; |
| 145 | ColorId _bgcolorhi; |
| 146 | ColorId _bgcolorlo; |
| 147 | ColorId _textcolor; |
| 148 | ColorId _textcolorhi; |
| 149 | ColorId _textcolorlo; |
| 150 | ColorId _shadowcolor; |
| 151 | |
| 152 | public: |
| 153 | static Widget* findWidgetInChain(Widget* start, int x, int y); |
| 154 | |
| 155 | /** Determine if 'find' is in the chain pointed to by 'start' */ |
| 156 | static bool isWidgetInChain(Widget* start, Widget* find); |
| 157 | |
| 158 | /** Determine if 'find' is in the widget array */ |
| 159 | static bool isWidgetInChain(WidgetArray& list, Widget* find); |
| 160 | |
| 161 | /** Select either previous, current, or next widget in chain to have |
| 162 | focus, and deselects all others */ |
| 163 | static Widget* setFocusForChain(GuiObject* boss, WidgetArray& arr, |
| 164 | Widget* w, int direction, |
| 165 | bool emitFocusEvents = true); |
| 166 | |
| 167 | /** Sets all widgets in this chain to be dirty (must be redrawn) */ |
| 168 | static void setDirtyInChain(Widget* start); |
| 169 | |
| 170 | private: |
| 171 | // Following constructors and assignment operators not supported |
| 172 | Widget() = delete; |
| 173 | Widget(const Widget&) = delete; |
| 174 | Widget(Widget&&) = delete; |
| 175 | Widget& operator=(const Widget&) = delete; |
| 176 | Widget& operator=(Widget&&) = delete; |
| 177 | }; |
| 178 | |
| 179 | /* StaticTextWidget */ |
| 180 | class StaticTextWidget : public Widget |
| 181 | { |
| 182 | public: |
| 183 | StaticTextWidget(GuiObject* boss, const GUI::Font& font, |
| 184 | int x, int y, int w, int h, |
| 185 | const string& text = "" , TextAlign align = TextAlign::Left, |
| 186 | ColorId shadowColor = kNone); |
| 187 | StaticTextWidget(GuiObject* boss, const GUI::Font& font, |
| 188 | int x, int y, |
| 189 | const string& text = "" , TextAlign align = TextAlign::Left, |
| 190 | ColorId shadowColor = kNone); |
| 191 | void setValue(int value); |
| 192 | void setLabel(const string& label); |
| 193 | void setAlign(TextAlign align) { _align = align; setDirty(); } |
| 194 | const string& getLabel() const { return _label; } |
| 195 | bool isEditable() const { return _editable; } |
| 196 | |
| 197 | protected: |
| 198 | void drawWidget(bool hilite) override; |
| 199 | |
| 200 | protected: |
| 201 | string _label; |
| 202 | bool _editable; |
| 203 | TextAlign _align; |
| 204 | |
| 205 | private: |
| 206 | // Following constructors and assignment operators not supported |
| 207 | StaticTextWidget() = delete; |
| 208 | StaticTextWidget(const StaticTextWidget&) = delete; |
| 209 | StaticTextWidget(StaticTextWidget&&) = delete; |
| 210 | StaticTextWidget& operator=(const StaticTextWidget&) = delete; |
| 211 | StaticTextWidget& operator=(StaticTextWidget&&) = delete; |
| 212 | }; |
| 213 | |
| 214 | /* ButtonWidget */ |
| 215 | class ButtonWidget : public StaticTextWidget, public CommandSender |
| 216 | { |
| 217 | public: |
| 218 | ButtonWidget(GuiObject* boss, const GUI::Font& font, |
| 219 | int x, int y, int w, int h, |
| 220 | const string& label, int cmd = 0, bool repeat = false); |
| 221 | ButtonWidget(GuiObject* boss, const GUI::Font& font, |
| 222 | int x, int y, int dw, |
| 223 | const string& label, int cmd = 0, bool repeat = false); |
| 224 | ButtonWidget(GuiObject* boss, const GUI::Font& font, |
| 225 | int x, int y, |
| 226 | const string& label, int cmd = 0, bool repeat = false); |
| 227 | ButtonWidget(GuiObject* boss, const GUI::Font& font, |
| 228 | int x, int y, int dw, int dh, |
| 229 | uInt32* bitmap, int bmw, int bmh, |
| 230 | int cmd = 0, bool repeat = false); |
| 231 | |
| 232 | void setCmd(int cmd) { _cmd = cmd; } |
| 233 | int getCmd() const { return _cmd; } |
| 234 | /* Sets/changes the button's bitmap **/ |
| 235 | void setBitmap(uInt32* bitmap, int bmw, int bmh); |
| 236 | |
| 237 | protected: |
| 238 | bool handleMouseClicks(int x, int y, MouseButton b) override; |
| 239 | void handleMouseDown(int x, int y, MouseButton b, int clickCount) override; |
| 240 | void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; |
| 241 | void handleMouseEntered() override; |
| 242 | void handleMouseLeft() override; |
| 243 | bool handleEvent(Event::Type event) override; |
| 244 | |
| 245 | void drawWidget(bool hilite) override; |
| 246 | |
| 247 | protected: |
| 248 | int _cmd; |
| 249 | bool _repeat; // button repeats |
| 250 | bool _useBitmap; |
| 251 | uInt32* _bitmap; |
| 252 | int _bmw, _bmh; |
| 253 | |
| 254 | private: |
| 255 | // Following constructors and assignment operators not supported |
| 256 | ButtonWidget() = delete; |
| 257 | ButtonWidget(const ButtonWidget&) = delete; |
| 258 | ButtonWidget(ButtonWidget&&) = delete; |
| 259 | ButtonWidget& operator=(const ButtonWidget&) = delete; |
| 260 | ButtonWidget& operator=(ButtonWidget&&) = delete; |
| 261 | }; |
| 262 | |
| 263 | /* CheckboxWidget */ |
| 264 | class CheckboxWidget : public ButtonWidget |
| 265 | { |
| 266 | public: |
| 267 | enum { kCheckActionCmd = 'CBAC' }; |
| 268 | enum class FillType { Normal, Inactive, Circle }; |
| 269 | |
| 270 | public: |
| 271 | CheckboxWidget(GuiObject* boss, const GUI::Font& font, int x, int y, |
| 272 | const string& label, int cmd = 0); |
| 273 | |
| 274 | void setEditable(bool editable); |
| 275 | void setFill(FillType type); |
| 276 | |
| 277 | void setState(bool state, bool changed = false); |
| 278 | void toggleState() { setState(!_state); } |
| 279 | bool getState() const { return _state; } |
| 280 | |
| 281 | void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; |
| 282 | void handleMouseEntered() override; |
| 283 | void handleMouseLeft() override; |
| 284 | |
| 285 | static int boxSize() { return 14; } // box is square |
| 286 | |
| 287 | protected: |
| 288 | void drawWidget(bool hilite) override; |
| 289 | |
| 290 | protected: |
| 291 | bool _state; |
| 292 | bool _holdFocus; |
| 293 | bool _drawBox; |
| 294 | bool _changed; |
| 295 | |
| 296 | uInt32* _img; |
| 297 | ColorId _fillColor; |
| 298 | int _boxY; |
| 299 | int _textY; |
| 300 | |
| 301 | private: |
| 302 | // Following constructors and assignment operators not supported |
| 303 | CheckboxWidget() = delete; |
| 304 | CheckboxWidget(const CheckboxWidget&) = delete; |
| 305 | CheckboxWidget(CheckboxWidget&&) = delete; |
| 306 | CheckboxWidget& operator=(const CheckboxWidget&) = delete; |
| 307 | CheckboxWidget& operator=(CheckboxWidget&&) = delete; |
| 308 | }; |
| 309 | |
| 310 | /* SliderWidget */ |
| 311 | class SliderWidget : public ButtonWidget |
| 312 | { |
| 313 | public: |
| 314 | SliderWidget(GuiObject* boss, const GUI::Font& font, |
| 315 | int x, int y, int w, int h, |
| 316 | const string& label = "" , int labelWidth = 0, int cmd = 0, |
| 317 | int valueLabelWidth = 0, const string& valueUnit = "" , int valueLabelGap = 4); |
| 318 | SliderWidget(GuiObject* boss, const GUI::Font& font, |
| 319 | int x, int y, |
| 320 | const string& label = "" , int labelWidth = 0, int cmd = 0, |
| 321 | int valueLabelWidth = 0, const string& valueUnit = "" , int valueLabelGap = 4); |
| 322 | |
| 323 | void setValue(int value); |
| 324 | int getValue() const { return _value; } |
| 325 | |
| 326 | void setMinValue(int value); |
| 327 | int getMinValue() const { return _valueMin; } |
| 328 | void setMaxValue(int value); |
| 329 | int getMaxValue() const { return _valueMax; } |
| 330 | void setStepValue(int value); |
| 331 | int getStepValue() const { return _stepValue; } |
| 332 | void setValueLabel(const string& valueLabel); |
| 333 | void setValueLabel(int value); |
| 334 | const string& getValueLabel() const { return _valueLabel; } |
| 335 | void setValueUnit(const string& valueUnit); |
| 336 | |
| 337 | void setTickmarkIntervals(int numIntervals); |
| 338 | |
| 339 | protected: |
| 340 | void handleMouseMoved(int x, int y) override; |
| 341 | void handleMouseDown(int x, int y, MouseButton b, int clickCount) override; |
| 342 | void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; |
| 343 | void handleMouseWheel(int x, int y, int direction) override; |
| 344 | bool handleEvent(Event::Type event) override; |
| 345 | |
| 346 | void drawWidget(bool hilite) override; |
| 347 | |
| 348 | int valueToPos(int value) const; |
| 349 | int posToValue(int pos) const; |
| 350 | |
| 351 | protected: |
| 352 | int _value, _stepValue; |
| 353 | int _valueMin, _valueMax; |
| 354 | bool _isDragging; |
| 355 | int _labelWidth; |
| 356 | string _valueLabel; |
| 357 | string _valueUnit; |
| 358 | int _valueLabelGap; |
| 359 | int _valueLabelWidth; |
| 360 | int _numIntervals; |
| 361 | |
| 362 | private: |
| 363 | // Following constructors and assignment operators not supported |
| 364 | SliderWidget() = delete; |
| 365 | SliderWidget(const SliderWidget&) = delete; |
| 366 | SliderWidget(SliderWidget&&) = delete; |
| 367 | SliderWidget& operator=(const SliderWidget&) = delete; |
| 368 | SliderWidget& operator=(SliderWidget&&) = delete; |
| 369 | }; |
| 370 | |
| 371 | #endif |
| 372 | |