stella/src/gui/Widget.hxx

424 lines
15 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 WIDGET_HXX
#define WIDGET_HXX
class Dialog;
#include <cassert>
#include "bspf.hxx"
#include "Rect.hxx"
#include "Event.hxx"
#include "EventHandlerConstants.hxx"
#include "FrameBufferConstants.hxx"
#include "StellaKeys.hxx"
#include "GuiObject.hxx"
#include "Font.hxx"
/**
This is the base class for all widgets.
@author Stephen Anthony
*/
class Widget : public GuiObject
{
friend class Dialog;
public:
Widget(GuiObject* boss, const GUI::Font& font, int x, int y, int w, int h);
~Widget() override;
virtual int getAbsX() const override { return _x + _boss->getChildX(); }
virtual int getAbsY() const override { return _y + _boss->getChildY(); }
virtual int getLeft() const { return _x; }
virtual int getTop() const { return _y; }
virtual int getRight() const { return _x + getWidth(); }
virtual int getBottom() const { return _y + getHeight(); }
virtual void setPosX(int x);
virtual void setPosY(int y);
virtual void setPos(int x, int y);
virtual void setPos(const Common::Point& pos);
void setWidth(int w) override;
void setHeight(int h) override;
virtual void setSize(int w, int h);
virtual void setSize(const Common::Point& pos);
virtual bool handleText(char text) { return false; }
virtual bool handleKeyDown(StellaKey key, StellaMod mod) { return false; }
virtual bool handleKeyUp(StellaKey key, StellaMod mod) { return false; }
virtual void handleMouseDown(int x, int y, MouseButton b, int clickCount) { }
virtual void handleMouseUp(int x, int y, MouseButton b, int clickCount) { }
virtual void handleMouseEntered();
virtual void handleMouseLeft();
virtual void handleMouseMoved(int x, int y) { }
virtual void handleMouseWheel(int x, int y, int direction) { }
virtual bool handleMouseClicks(int x, int y, MouseButton b) { return false; }
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) { return false; }
virtual bool handleEvent(Event::Type event) { return false; }
void tick() override;
void setDirty() override;
void setDirtyChain() override;
void draw() override;
void drawChain() override;
void receivedFocus();
void lostFocus();
void addFocusWidget(Widget* w) override { _focusList.push_back(w); }
void addToFocusList(const WidgetArray& list) override {
Vec::append(_focusList, list);
}
/** Set/clear FLAG_ENABLED */
void setEnabled(bool e);
bool isEnabled() const { return _flags & FLAG_ENABLED; }
bool isVisible() const override { return !(_flags & FLAG_INVISIBLE); }
bool isHighlighted() const { return _flags & FLAG_HILITED; }
bool hasMouseFocus() const { return _flags & FLAG_MOUSE_FOCUS; }
virtual bool wantsFocus() const { return _flags & FLAG_RETAIN_FOCUS; }
bool wantsTab() const { return _flags & FLAG_WANTS_TAB; }
bool wantsRaw() const { return _flags & FLAG_WANTS_RAWDATA; }
void setID(uInt32 id) { _id = id; }
uInt32 getID() const { return _id; }
virtual const GUI::Font& font() const { return _font; }
void setTextColor(ColorId color) { _textcolor = color; setDirty(); }
void setTextColorHi(ColorId color) { _textcolorhi = color; setDirty(); }
void setBGColor(ColorId color) { _bgcolor = color; setDirty(); }
void setBGColorHi(ColorId color) { _bgcolorhi = color; setDirty(); }
void setShadowColor(ColorId color) { _shadowcolor = color; setDirty(); }
void setToolTip(const string& text);
virtual string getToolTip(const Common::Point& pos) const { return _toolTipText; }
virtual bool changedToolTip(const Common::Point& oldPos,
const Common::Point& newPos) const { return false; }
void setHelpAnchor(const string& helpAnchor, bool debugger = false);
void setHelpURL(const string& helpURL);
virtual void loadConfig() { }
protected:
virtual void drawWidget(bool hilite) { }
virtual void receivedFocusWidget() { }
virtual void lostFocusWidget() { }
virtual Widget* findWidget(int x, int y) { return this; }
void releaseFocus() override { assert(_boss); _boss->releaseFocus(); }
virtual bool wantsToolTip() const { return hasMouseFocus() && hasToolTip(); }
virtual bool hasToolTip() const { return !_toolTipText.empty(); }
// By default, delegate unhandled commands to the boss
void handleCommand(CommandSender* sender, int cmd, int data, int id) override
{ assert(_boss); _boss->handleCommand(sender, cmd, data, id); }
const string getHelpURL() const override;
bool hasHelp() const override { return !getHelpURL().empty(); }
protected:
GuiObject* _boss{nullptr};
const GUI::Font& _font;
Widget* _next{nullptr};
uInt32 _id{0};
bool _hasFocus{false};
int _fontWidth{0};
int _lineHeight{0};
ColorId _bgcolor{kWidColor};
ColorId _bgcolorhi{kWidColor};
ColorId _bgcolorlo{kBGColorLo};
ColorId _textcolor{kTextColor};
ColorId _textcolorhi{kTextColorHi};
ColorId _textcolorlo{kBGColorLo};
ColorId _shadowcolor{kShadowColor};
string _toolTipText;
string _helpAnchor;
string _helpURL;
bool _debuggerHelp{false};
public:
static Widget* findWidgetInChain(Widget* start, int x, int y);
/** Determine if 'find' is in the chain pointed to by 'start' */
static bool isWidgetInChain(Widget* start, Widget* find);
/** Determine if 'find' is in the widget array */
static bool isWidgetInChain(const WidgetArray& list, Widget* find);
/** Select either previous, current, or next widget in chain to have
focus, and deselects all others */
static Widget* setFocusForChain(GuiObject* boss, WidgetArray& arr,
Widget* w, int direction,
bool emitFocusEvents = true);
/** Sets all widgets in this chain to be dirty (must be redrawn) */
static void setDirtyInChain(Widget* start);
private:
// Following constructors and assignment operators not supported
Widget() = delete;
Widget(const Widget&) = delete;
Widget(Widget&&) = delete;
Widget& operator=(const Widget&) = delete;
Widget& operator=(Widget&&) = delete;
};
/* StaticTextWidget */
class StaticTextWidget : public Widget, public CommandSender
{
public:
enum {
kClickedCmd = 'STcl',
kOpenUrlCmd = 'STou'
};
public:
StaticTextWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h,
const string& text = "", TextAlign align = TextAlign::Left,
ColorId shadowColor = kNone);
StaticTextWidget(GuiObject* boss, const GUI::Font& font,
int x, int y,
const string& text = "", TextAlign align = TextAlign::Left,
ColorId shadowColor = kNone);
~StaticTextWidget() override = default;
void setCmd(int cmd) { _cmd = cmd; }
void setValue(int value);
void setLabel(const string& label);
void setAlign(TextAlign align) { _align = align; setDirty(); }
const string& getLabel() const { return _label; }
bool isEditable() const { return _editable; }
void setLink(size_t start = string::npos, int len = 0, bool underline = false);
bool setUrl(const string& url = EmptyString, const string& label = EmptyString,
const string& placeHolder = EmptyString);
const string& getUrl() const { return _url; }
protected:
void handleMouseEntered() override;
void handleMouseLeft() override;
void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
void drawWidget(bool hilite) override;
protected:
string _label;
bool _editable{false};
TextAlign _align{TextAlign::Left};
int _cmd{0};
size_t _linkStart{string::npos};
int _linkLen{0};
bool _linkUnderline{false};
string _url;
private:
// Following constructors and assignment operators not supported
StaticTextWidget() = delete;
StaticTextWidget(const StaticTextWidget&) = delete;
StaticTextWidget(StaticTextWidget&&) = delete;
StaticTextWidget& operator=(const StaticTextWidget&) = delete;
StaticTextWidget& operator=(StaticTextWidget&&) = delete;
};
/* ButtonWidget */
class ButtonWidget : public StaticTextWidget
{
public:
ButtonWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h,
const string& label, int cmd = 0, bool repeat = false);
ButtonWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int dw,
const string& label, int cmd = 0, bool repeat = false);
ButtonWidget(GuiObject* boss, const GUI::Font& font,
int x, int y,
const string& label, int cmd = 0, bool repeat = false);
ButtonWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int dw, int dh,
const uInt32* bitmap, int bmw, int bmh,
int cmd = 0, bool repeat = false);
~ButtonWidget() override = default;
bool handleEvent(Event::Type event) override;
void setCmd(int cmd) { _cmd = cmd; }
int getCmd() const { return _cmd; }
/* Sets/changes the button's bitmap **/
void setBitmap(const uInt32* bitmap, int bmw, int bmh);
protected:
bool handleMouseClicks(int x, int y, MouseButton b) override;
void handleMouseDown(int x, int y, MouseButton b, int clickCount) override;
void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
void handleMouseEntered() override;
void handleMouseLeft() override;
void drawWidget(bool hilite) override;
protected:
bool _repeat{false}; // button repeats
bool _useBitmap{false};
const uInt32* _bitmap{nullptr};
int _bmw{0}, _bmh{0};
private:
// Following constructors and assignment operators not supported
ButtonWidget() = delete;
ButtonWidget(const ButtonWidget&) = delete;
ButtonWidget(ButtonWidget&&) = delete;
ButtonWidget& operator=(const ButtonWidget&) = delete;
ButtonWidget& operator=(ButtonWidget&&) = delete;
};
/* CheckboxWidget */
class CheckboxWidget : public ButtonWidget
{
public:
enum { kCheckActionCmd = 'CBAC' };
enum class FillType { Normal, Inactive, Circle };
public:
CheckboxWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
const string& label, int cmd = 0);
~CheckboxWidget() override = default;
void setEditable(bool editable);
void setFill(FillType type);
void setState(bool state, bool changed = false);
void toggleState() { setState(!_state); }
bool getState() const { return _state; }
void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
static int boxSize(const GUI::Font& font)
{
return font.getFontHeight() < 24 ? 14 : 22; // box is square
}
static int prefixSize(const GUI::Font& font)
{
return boxSize(font) + font.getMaxCharWidth() * 0.75;
}
protected:
void drawWidget(bool hilite) override;
protected:
bool _state{false};
bool _holdFocus{true};
bool _drawBox{true};
bool _changed{false};
const uInt32* _outerCircle{nullptr};
const uInt32* _innerCircle{nullptr};
const uInt32* _img{nullptr};
ColorId _fillColor{kColor};
int _boxY{0};
int _textY{0};
int _boxSize{14};
private:
// Following constructors and assignment operators not supported
CheckboxWidget() = delete;
CheckboxWidget(const CheckboxWidget&) = delete;
CheckboxWidget(CheckboxWidget&&) = delete;
CheckboxWidget& operator=(const CheckboxWidget&) = delete;
CheckboxWidget& operator=(CheckboxWidget&&) = delete;
};
/* SliderWidget */
class SliderWidget : public ButtonWidget
{
public:
SliderWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h,
const string& label = "", int labelWidth = 0, int cmd = 0,
int valueLabelWidth = 0, const string& valueUnit = "",
int valueLabelGap = 0, bool forceLabelSign = false);
SliderWidget(GuiObject* boss, const GUI::Font& font,
int x, int y,
const string& label = "", int labelWidth = 0, int cmd = 0,
int valueLabelWidth = 0, const string& valueUnit = "",
int valueLabelGap = 0, bool forceLabelSign = false);
~SliderWidget() override = default;
void setValue(int value);
int getValue() const { return BSPF::clamp(_value, _valueMin, _valueMax); }
void setMinValue(int value);
int getMinValue() const { return _valueMin; }
void setMaxValue(int value);
int getMaxValue() const { return _valueMax; }
void setStepValue(int value);
int getStepValue() const { return _stepValue; }
void setValueLabel(const string& valueLabel);
void setValueLabel(int value);
const string& getValueLabel() const { return _valueLabel; }
void setValueUnit(const string& valueUnit);
void setTickmarkIntervals(int numIntervals);
protected:
void handleMouseMoved(int x, int y) override;
void handleMouseDown(int x, int y, MouseButton b, int clickCount) override;
void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
void handleMouseWheel(int x, int y, int direction) override;
bool handleEvent(Event::Type event) override;
void drawWidget(bool hilite) override;
int valueToPos(int value) const;
int posToValue(int pos) const;
protected:
int _value{-INT_MAX}, _stepValue{1};
int _valueMin{0}, _valueMax{100};
bool _isDragging{false};
int _labelWidth{0};
string _valueLabel;
string _valueUnit;
int _valueLabelGap{0};
int _valueLabelWidth{0};
bool _forceLabelSign{false};
int _numIntervals{0};
private:
// Following constructors and assignment operators not supported
SliderWidget() = delete;
SliderWidget(const SliderWidget&) = delete;
SliderWidget(SliderWidget&&) = delete;
SliderWidget& operator=(const SliderWidget&) = delete;
SliderWidget& operator=(SliderWidget&&) = delete;
};
#endif