ppsspp/Common/UI/View.h

1053 lines
29 KiB
C
Raw Normal View History

2013-05-02 22:21:39 +00:00
#pragma once
// More traditional UI framework than ui/ui.h.
2013-05-02 22:21:39 +00:00
// Still very simple to use.
// Works very similarly to Android, there's a Measure pass and a Layout pass which you don't
// really need to care about if you just use the standard containers and widgets.
2013-05-25 14:52:27 +00:00
#include <cmath>
#include <cstdio>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
2013-05-02 22:21:39 +00:00
#include "Common/Render/TextureAtlas.h"
#include "Common/Math/lin/matrix4x4.h"
#include "Common/Math/math_util.h"
#include "Common/Math/geom2d.h"
2013-05-02 22:21:39 +00:00
#include "Common/Common.h"
2013-10-18 17:45:05 +00:00
#undef small
2013-07-08 10:34:39 +00:00
struct KeyInput;
2013-05-02 22:21:39 +00:00
struct TouchInput;
struct AxisInput;
2013-05-02 22:21:39 +00:00
struct ImageID;
2013-05-02 22:21:39 +00:00
class DrawBuffer;
class Texture;
2013-05-27 22:32:00 +00:00
class UIContext;
2013-05-02 22:21:39 +00:00
2016-12-25 17:18:19 +00:00
namespace Draw {
class DrawContext;
2016-12-25 19:54:37 +00:00
class Texture;
2016-12-25 17:18:19 +00:00
}
2014-08-17 19:28:34 +00:00
2013-05-02 22:21:39 +00:00
// I don't generally like namespaces but I think we do need one for UI, so many potentially-clashing names.
namespace UI {
class View;
2013-05-02 22:21:39 +00:00
enum DrawableType {
DRAW_NOTHING,
DRAW_SOLID_COLOR,
DRAW_4GRID,
DRAW_STRETCH_IMAGE,
2013-05-02 22:21:39 +00:00
};
2013-06-02 21:44:28 +00:00
enum Visibility {
V_VISIBLE,
V_INVISIBLE, // Keeps position, not drawn or interacted with
V_GONE, // Does not participate in layout
};
2013-05-02 22:21:39 +00:00
struct Drawable {
Drawable() : type(DRAW_NOTHING), image(ImageID::invalid()), color(0xFFFFFFFF) {}
explicit Drawable(uint32_t col) : type(DRAW_SOLID_COLOR), image(ImageID::invalid()), color(col) {}
Drawable(DrawableType t, ImageID img, uint32_t col = 0xFFFFFFFF) : type(t), image(img), color(col) {}
2013-05-02 22:21:39 +00:00
DrawableType type;
ImageID image;
uint32_t color;
2013-05-02 22:21:39 +00:00
};
struct Style {
Style() : fgColor(0xFFFFFFFF), background(0xFF303030), image(ImageID::invalid()) {}
2013-05-02 22:21:39 +00:00
uint32_t fgColor;
Drawable background;
ImageID image; // where applicable.
2013-05-02 22:21:39 +00:00
};
struct FontStyle {
FontStyle() {}
FontStyle(FontID atlasFnt, const char *name, int size) : atlasFont(atlasFnt), fontName(name), sizePts(size) {}
FontID atlasFont{ nullptr };
// For native fonts:
std::string fontName;
int sizePts = 0;
int flags = 0;
};
2013-05-02 22:21:39 +00:00
// To use with an UI atlas.
struct Theme {
FontStyle uiFont;
FontStyle uiFontSmall;
FontStyle uiFontSmaller;
ImageID checkOn;
ImageID checkOff;
ImageID sliderKnob;
ImageID whiteImage;
ImageID dropShadow4Grid;
Style itemStyle;
2013-05-25 14:52:27 +00:00
Style itemDownStyle;
Style itemFocusedStyle;
2013-08-16 14:47:25 +00:00
Style itemDisabledStyle;
Style headerStyle;
Style infoStyle;
2013-08-14 21:29:27 +00:00
Style popupTitle;
Style popupStyle;
uint32_t backgroundColor;
};
// The four cardinal directions should be enough, plus Prev/Next in "element order".
enum FocusDirection {
FOCUS_UP,
FOCUS_DOWN,
FOCUS_LEFT,
FOCUS_RIGHT,
FOCUS_NEXT,
FOCUS_PREV,
FOCUS_FIRST,
FOCUS_LAST,
FOCUS_PREV_PAGE,
FOCUS_NEXT_PAGE,
2013-05-02 22:21:39 +00:00
};
2022-11-12 18:27:19 +00:00
typedef float Size; // can also be WRAP_CONTENT or FILL_PARENT.
static constexpr Size WRAP_CONTENT = -1.0f;
static constexpr Size FILL_PARENT = -2.0f;
2013-05-02 22:21:39 +00:00
// Gravity
enum Gravity {
G_LEFT = 0,
G_RIGHT = 1,
G_HCENTER = 2,
G_HORIZMASK = 3,
G_TOP = 0,
G_BOTTOM = 4,
G_VCENTER = 8,
G_TOPLEFT = G_TOP | G_LEFT,
G_TOPRIGHT = G_TOP | G_RIGHT,
G_BOTTOMLEFT = G_BOTTOM | G_LEFT,
G_BOTTOMRIGHT = G_BOTTOM | G_RIGHT,
G_CENTER = G_HCENTER | G_VCENTER,
2013-05-02 22:21:39 +00:00
G_VERTMASK = 3 << 2,
};
enum Borders {
BORDER_NONE = 0,
BORDER_TOP = 0x0001,
BORDER_LEFT = 0x0002,
BORDER_BOTTOM = 0x0004,
BORDER_RIGHT = 0x0008,
BORDER_HORIZ = BORDER_LEFT | BORDER_RIGHT,
BORDER_VERT = BORDER_TOP | BORDER_BOTTOM,
BORDER_ALL = BORDER_TOP | BORDER_LEFT | BORDER_BOTTOM | BORDER_RIGHT,
};
enum class BorderStyle {
HEADER_FG,
ITEM_DOWN_BG,
};
2013-05-02 22:21:39 +00:00
enum Orientation {
ORIENT_HORIZONTAL,
ORIENT_VERTICAL,
};
inline Orientation Opposite(Orientation o) {
if (o == ORIENT_HORIZONTAL) return ORIENT_VERTICAL; else return ORIENT_HORIZONTAL;
}
inline FocusDirection Opposite(FocusDirection d) {
switch (d) {
case FOCUS_UP: return FOCUS_DOWN;
case FOCUS_DOWN: return FOCUS_UP;
case FOCUS_LEFT: return FOCUS_RIGHT;
case FOCUS_RIGHT: return FOCUS_LEFT;
case FOCUS_PREV: return FOCUS_NEXT;
case FOCUS_NEXT: return FOCUS_PREV;
case FOCUS_FIRST: return FOCUS_LAST;
case FOCUS_LAST: return FOCUS_FIRST;
case FOCUS_PREV_PAGE: return FOCUS_NEXT_PAGE;
case FOCUS_NEXT_PAGE: return FOCUS_PREV_PAGE;
}
2013-07-15 22:25:08 +00:00
return d;
}
2013-05-02 22:21:39 +00:00
enum MeasureSpecType {
UNSPECIFIED,
EXACTLY,
AT_MOST,
};
// I hope I can find a way to simplify this one day.
2013-05-02 22:21:39 +00:00
enum EventReturn {
EVENT_DONE, // Return this when no other view may process this event, for example if you changed the view hierarchy
EVENT_SKIPPED, // Return this if you ignored an event
2013-10-13 18:49:10 +00:00
EVENT_CONTINUE, // Return this if it's safe to send this event to further listeners. This should normally be the default choice but often EVENT_DONE is necessary.
2013-05-02 22:21:39 +00:00
};
2013-07-15 15:51:12 +00:00
enum FocusFlags {
FF_LOSTFOCUS = 1,
FF_GOTFOCUS = 2
};
enum PersistStatus {
PERSIST_SAVE,
PERSIST_RESTORE,
};
2013-05-02 22:21:39 +00:00
typedef std::vector<int> PersistBuffer;
typedef std::map<std::string, UI::PersistBuffer> PersistMap;
class ViewGroup;
2013-05-02 22:21:39 +00:00
struct MeasureSpec {
MeasureSpec(MeasureSpecType t, float s = 0.0f) : type(t), size(s) {}
MeasureSpec() : type(UNSPECIFIED), size(0) {}
MeasureSpec operator -(float amount) {
// TODO: Check type
return MeasureSpec(type, size - amount);
}
MeasureSpecType type;
float size;
};
// Should cover all bases.
struct EventParams {
View *v;
uint32_t a, b, x, y;
float f;
std::string s;
2013-05-02 22:21:39 +00:00
};
struct HandlerRegistration {
2013-06-02 12:25:57 +00:00
std::function<EventReturn(EventParams&)> func;
2013-05-02 22:21:39 +00:00
};
class Event {
public:
Event() {}
~Event();
2013-05-02 22:21:39 +00:00
// Call this from input thread or whatever, it doesn't matter
2013-06-01 16:59:03 +00:00
void Trigger(EventParams &e);
2013-05-02 22:21:39 +00:00
// Call this from UI thread
EventReturn Dispatch(EventParams &e);
2013-05-02 22:21:39 +00:00
// This is suggested for use in most cases. Autobinds, allowing for neat syntax.
template<class T>
2013-06-09 10:40:53 +00:00
T *Handle(T *thiz, EventReturn (T::* theCallback)(EventParams &e)) {
Add(std::bind(theCallback, thiz, std::placeholders::_1));
2013-06-09 10:40:53 +00:00
return thiz;
}
// Sometimes you have an already-bound function<>, just use this then.
void Add(std::function<EventReturn(EventParams&)> func);
private:
2013-05-02 22:21:39 +00:00
std::vector<HandlerRegistration> handlers_;
DISALLOW_COPY_AND_ASSIGN(Event);
};
struct Margins {
Margins() : top(0), bottom(0), left(0), right(0) {}
2013-08-19 22:34:54 +00:00
explicit Margins(int8_t all) : top(all), bottom(all), left(all), right(all) {}
Margins(int8_t horiz, int8_t vert) : top(vert), bottom(vert), left(horiz), right(horiz) {}
Margins(int8_t l, int8_t t, int8_t r, int8_t b) : top(t), bottom(b), left(l), right(r) {}
int horiz() const {
return left + right;
}
int vert() const {
return top + bottom;
}
void SetAll(float f) {
int8_t i = (int)f;
top = i;
bottom = i;
left = i;
right = i;
}
2013-08-19 22:34:54 +00:00
int8_t top;
int8_t bottom;
int8_t left;
int8_t right;
2013-05-02 22:21:39 +00:00
};
struct Padding {
Padding() : top(0), bottom(0), left(0), right(0) {}
explicit Padding(float all) : top(all), bottom(all), left(all), right(all) {}
Padding(float horiz, float vert) : top(vert), bottom(vert), left(horiz), right(horiz) {}
Padding(float l, float t, float r, float b) : top(t), bottom(b), left(l), right(r) {}
float horiz() const {
return left + right;
}
float vert() const {
return top + bottom;
}
float top;
float bottom;
float left;
float right;
};
2013-06-09 11:01:36 +00:00
enum LayoutParamsType {
LP_PLAIN = 0,
LP_LINEAR = 1,
LP_ANCHOR = 2,
LP_GRID = 3,
2013-06-09 11:01:36 +00:00
};
2013-05-02 22:21:39 +00:00
// Need a virtual destructor so vtables are created, otherwise RTTI can't work
class LayoutParams {
public:
2013-06-09 11:01:36 +00:00
LayoutParams(LayoutParamsType type = LP_PLAIN)
2013-06-11 18:33:41 +00:00
: width(WRAP_CONTENT), height(WRAP_CONTENT), type_(type) {}
2013-06-09 11:01:36 +00:00
LayoutParams(Size w, Size h, LayoutParamsType type = LP_PLAIN)
: width(w), height(h), type_(type) {}
2013-05-02 22:21:39 +00:00
virtual ~LayoutParams() {}
Size width;
Size height;
2013-06-09 11:01:36 +00:00
// Fake RTTI
bool Is(LayoutParamsType type) const { return type_ == type; }
template <typename T>
T *As() {
if (Is(T::StaticType())) {
return static_cast<T *>(this);
}
return nullptr;
}
template <typename T>
const T *As() const {
if (Is(T::StaticType())) {
return static_cast<const T *>(this);
}
return nullptr;
}
static LayoutParamsType StaticType() {
return LP_PLAIN;
}
2013-06-09 11:01:36 +00:00
private:
LayoutParamsType type_;
2013-05-02 22:21:39 +00:00
};
View *GetFocusedView();
2017-12-03 18:40:09 +00:00
class Tween;
class CallbackColorTween;
2017-12-03 18:40:09 +00:00
2013-05-02 22:21:39 +00:00
class View {
public:
View(LayoutParams *layoutParams = 0) : layoutParams_(layoutParams), visibility_(V_VISIBLE), measuredWidth_(0), measuredHeight_(0), enabledPtr_(0), enabled_(true), enabledMeansDisabled_(false) {
2013-05-02 22:21:39 +00:00
if (!layoutParams)
layoutParams_.reset(new LayoutParams());
}
virtual ~View();
2013-05-02 22:21:39 +00:00
// Please note that Touch is called ENTIRELY asynchronously from drawing!
// Can even be called on a different thread! This is to really minimize latency, and decouple
// touch response from the frame rate. Same with Key and Axis.
virtual bool Key(const KeyInput &input) { return false; }
virtual bool Touch(const TouchInput &input) { return true; }
virtual void Axis(const AxisInput &input) {}
2017-12-03 18:40:09 +00:00
virtual void Update();
2013-05-02 22:21:39 +00:00
virtual void DeviceLost() {}
virtual void DeviceRestored(Draw::DrawContext *draw) {}
// If this view covers these coordinates, it should add itself and its children to the list.
virtual void Query(float x, float y, std::vector<View *> &list);
virtual std::string DescribeLog() const;
// Accessible/searchable description.
virtual std::string DescribeText() const { return ""; }
2013-07-15 15:51:12 +00:00
virtual void FocusChanged(int focusFlags) {}
virtual void PersistData(PersistStatus status, std::string anonId, PersistMap &storage);
2013-07-15 15:51:12 +00:00
2013-05-02 22:21:39 +00:00
void Move(Bounds bounds) {
bounds_ = bounds;
}
2013-11-04 12:34:15 +00:00
2013-05-02 22:21:39 +00:00
// Views don't do anything here in Layout, only containers implement this.
2013-05-27 22:32:00 +00:00
virtual void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert);
2013-05-02 22:21:39 +00:00
virtual void Layout() {}
2013-05-27 22:32:00 +00:00
virtual void Draw(UIContext &dc) {}
2013-05-02 22:21:39 +00:00
virtual float GetMeasuredWidth() const { return measuredWidth_; }
virtual float GetMeasuredHeight() const { return measuredHeight_; }
// Override this for easy standard behaviour. No need to override Measure.
2013-05-27 22:32:00 +00:00
virtual void GetContentDimensions(const UIContext &dc, float &w, float &h) const;
virtual void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const;
2013-05-02 22:21:39 +00:00
// Called when the layout is done.
void SetBounds(Bounds bounds) { bounds_ = bounds; }
virtual const LayoutParams *GetLayoutParams() const { return layoutParams_.get(); }
virtual void ReplaceLayoutParams(LayoutParams *newLayoutParams) { layoutParams_.reset(newLayoutParams); }
2013-05-02 22:21:39 +00:00
const Bounds &GetBounds() const { return bounds_; }
2014-03-03 11:33:57 +00:00
virtual bool SetFocus();
2013-05-02 22:21:39 +00:00
virtual bool CanBeFocused() const { return true; }
virtual bool SubviewFocused(View *view) { return false; }
bool HasFocus() const {
2013-05-02 22:21:39 +00:00
return GetFocusedView() == this;
}
void SetEnabled(bool enabled) {
enabledFunc_ = nullptr;
enabledPtr_ = nullptr;
enabled_ = enabled;
enabledMeansDisabled_ = false;
}
bool IsEnabled() const {
if (enabledFunc_)
return enabledFunc_() != enabledMeansDisabled_;
if (enabledPtr_)
return *enabledPtr_ != enabledMeansDisabled_;
return enabled_ != enabledMeansDisabled_;
}
void SetEnabledFunc(std::function<bool()> func) {
enabledFunc_ = func;
enabledPtr_ = nullptr;
enabledMeansDisabled_ = false;
}
void SetEnabledPtr(bool *enabled) {
enabledFunc_ = nullptr;
enabledPtr_ = enabled;
enabledMeansDisabled_ = false;
}
void SetDisabledPtr(bool *disabled) {
enabledFunc_ = nullptr;
enabledPtr_ = disabled;
enabledMeansDisabled_ = true;
}
2013-06-02 21:44:28 +00:00
virtual void SetVisibility(Visibility visibility) { visibility_ = visibility; }
2013-06-02 21:44:28 +00:00
Visibility GetVisibility() const { return visibility_; }
2013-06-01 16:59:03 +00:00
const std::string &Tag() const { return tag_; }
void SetTag(const std::string &str) { tag_ = str; }
2013-06-09 11:01:36 +00:00
// Fake RTTI
virtual bool IsViewGroup() const { return false; }
virtual bool ContainsSubview(const View *view) const { return false; }
2013-06-09 11:01:36 +00:00
Point GetFocusPosition(FocusDirection dir);
2017-12-03 18:40:09 +00:00
template <class T>
T *AddTween(T *t) {
tweens_.push_back(t);
return t;
}
2013-05-02 22:21:39 +00:00
protected:
// Inputs to layout
std::unique_ptr<LayoutParams> layoutParams_;
std::string tag_;
2013-06-02 21:44:28 +00:00
Visibility visibility_;
2013-06-01 16:59:03 +00:00
2013-05-02 22:21:39 +00:00
// Results of measure pass. Set these in Measure.
float measuredWidth_;
float measuredHeight_;
// Outputs of layout. X/Y are absolute screen coordinates, hierarchy is "gone" here.
Bounds bounds_;
2017-12-03 18:40:09 +00:00
std::vector<Tween *> tweens_;
2013-05-02 22:21:39 +00:00
private:
std::function<bool()> enabledFunc_;
bool *enabledPtr_;
bool enabled_;
bool enabledMeansDisabled_;
2013-05-02 22:21:39 +00:00
DISALLOW_COPY_AND_ASSIGN(View);
};
// These don't do anything when touched.
class InertView : public View {
public:
InertView(LayoutParams *layoutParams)
: View(layoutParams) {}
bool Key(const KeyInput &input) override { return false; }
bool Touch(const TouchInput &input) override { return false; }
bool CanBeFocused() const override { return false; }
2013-05-02 22:21:39 +00:00
};
// All these light up their background when touched, or have focus.
class Clickable : public View {
public:
Clickable(LayoutParams *layoutParams);
2013-05-02 22:21:39 +00:00
bool Key(const KeyInput &input) override;
bool Touch(const TouchInput &input) override;
2013-05-02 22:21:39 +00:00
void FocusChanged(int focusFlags) override;
2013-07-15 15:51:12 +00:00
2013-05-02 22:21:39 +00:00
Event OnClick;
protected:
// Internal method that fires on a click. Default behaviour is to trigger
// the event.
// Use it for checking/unchecking checkboxes, etc.
virtual void Click();
void DrawBG(UIContext &dc, const Style &style);
2013-05-02 22:21:39 +00:00
CallbackColorTween *bgColor_ = nullptr;
float bgColorLast_ = 0.0f;
int downCountDown_ = 0;
bool dragging_ = false;
bool down_ = false;
2013-05-02 22:21:39 +00:00
};
// TODO: Very similar to Choice, should probably merge them.
// Right now more flexible image support though.
2013-05-02 22:21:39 +00:00
class Button : public Clickable {
public:
Button(const std::string &text, LayoutParams *layoutParams = 0)
: Clickable(layoutParams), text_(text), imageID_(ImageID::invalid()) {}
Button(const std::string &text, ImageID imageID, LayoutParams *layoutParams = 0)
: Clickable(layoutParams), text_(text), imageID_(imageID) {}
void Click() override;
void Draw(UIContext &dc) override;
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
const std::string &GetText() const { return text_; }
std::string DescribeText() const override;
void SetPadding(int w, int h) {
paddingW_ = w;
paddingH_ = h;
}
void SetImageID(ImageID imageID) {
imageID_ = imageID;
}
2021-02-22 02:48:01 +00:00
void SetIgnoreText(bool ignore) {
ignoreText_ = ignore;
}
// Needed an extra small button...
void SetScale(float f) {
scale_ = f;
}
2013-05-02 22:21:39 +00:00
private:
Style style_;
std::string text_;
ImageID imageID_;
int paddingW_ = 16;
int paddingH_ = 8;
float scale_ = 1.0f;
2021-02-22 02:48:01 +00:00
bool ignoreText_ = false;
2013-05-02 22:21:39 +00:00
};
class RadioButton : public Clickable {
public:
RadioButton(int *value, int thisButtonValue, const std::string &text, LayoutParams *layoutParams = 0)
: Clickable(layoutParams), value_(value), thisButtonValue_(thisButtonValue), text_(text) {}
void Click() override;
void Draw(UIContext &dc) override;
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
std::string DescribeText() const override;
private:
int *value_;
int thisButtonValue_;
std::string text_;
const float paddingW_ = 8;
const float paddingH_ = 4;
const float radioRadius_ = 16.0f;
const float radioInnerRadius_ = 8.0f;
};
2013-07-18 08:25:30 +00:00
class Slider : public Clickable {
public:
Slider(int *value, int minValue, int maxValue, LayoutParams *layoutParams = 0)
: Clickable(layoutParams), value_(value), showPercent_(false), minValue_(minValue), maxValue_(maxValue), paddingLeft_(5), paddingRight_(70), step_(1), repeat_(-1) {}
Slider(int *value, int minValue, int maxValue, int step = 1, LayoutParams *layoutParams = 0)
: Clickable(layoutParams), value_(value), showPercent_(false), minValue_(minValue), maxValue_(maxValue), paddingLeft_(5), paddingRight_(70), repeat_(-1) {
2014-05-06 04:55:37 +00:00
step_ = step <= 0 ? 1 : step;
}
void Draw(UIContext &dc) override;
std::string DescribeText() const override;
bool Key(const KeyInput &input) override;
bool Touch(const TouchInput &input) override;
2017-03-15 05:01:18 +00:00
void Update() override;
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
2013-08-20 10:48:48 +00:00
void SetShowPercent(bool s) { showPercent_ = s; }
// OK to call this from the outside after having modified *value_
2013-07-18 08:25:30 +00:00
void Clamp();
Event OnChange;
private:
bool ApplyKey(int keyCode);
2013-07-18 08:25:30 +00:00
int *value_;
2013-08-20 10:48:48 +00:00
bool showPercent_;
2013-07-18 08:25:30 +00:00
int minValue_;
int maxValue_;
2013-08-20 10:48:48 +00:00
float paddingLeft_;
float paddingRight_;
2014-05-06 04:55:37 +00:00
int step_;
int repeat_ = 0;
int repeatCode_ = 0;
2013-07-18 08:25:30 +00:00
};
2013-07-20 11:54:09 +00:00
class SliderFloat : public Clickable {
public:
SliderFloat(float *value, float minValue, float maxValue, LayoutParams *layoutParams = 0)
: Clickable(layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), paddingLeft_(5), paddingRight_(70), repeat_(-1) {}
void Draw(UIContext &dc) override;
std::string DescribeText() const override;
bool Key(const KeyInput &input) override;
bool Touch(const TouchInput &input) override;
2017-03-15 05:01:18 +00:00
void Update() override;
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
2013-07-20 11:54:09 +00:00
// OK to call this from the outside after having modified *value_
2013-07-20 11:54:09 +00:00
void Clamp();
Event OnChange;
private:
bool ApplyKey(int keyCode);
2013-07-20 11:54:09 +00:00
float *value_;
float minValue_;
float maxValue_;
2013-08-20 10:48:48 +00:00
float paddingLeft_;
float paddingRight_;
int repeat_;
int repeatCode_ = 0;
2013-07-20 11:54:09 +00:00
};
2013-07-18 08:25:30 +00:00
2013-05-27 20:22:35 +00:00
// Basic button that modifies a bitfield based on the pressed status. Supports multitouch.
// Suitable for controller simulation (ABXY etc).
class TriggerButton : public View {
public:
TriggerButton(uint32_t *bitField, uint32_t bit, ImageID imageBackground, ImageID imageForeground, LayoutParams *layoutParams)
2013-05-27 20:22:35 +00:00
: View(layoutParams), down_(0.0), bitField_(bitField), bit_(bit), imageBackground_(imageBackground), imageForeground_(imageForeground) {}
bool Touch(const TouchInput &input) override;
void Draw(UIContext &dc) override;
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
2013-05-27 20:22:35 +00:00
private:
int down_; // bitfield of pressed fingers, translates into bitField
uint32_t *bitField_;
uint32_t bit_;
ImageID imageBackground_;
ImageID imageForeground_;
2013-05-27 20:22:35 +00:00
};
// The following classes are mostly suitable as items in ListView which
// really is just a LinearLayout in a ScrollView, possibly with some special optimizations.
class Item : public InertView {
public:
2013-05-27 22:50:19 +00:00
Item(LayoutParams *layoutParams);
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
};
class ClickableItem : public Clickable {
public:
2013-05-27 22:50:19 +00:00
ClickableItem(LayoutParams *layoutParams);
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
2013-05-25 14:52:27 +00:00
// Draws the item background.
void Draw(UIContext &dc) override;
2013-05-25 14:52:27 +00:00
};
2013-05-02 22:21:39 +00:00
// Use to trigger something or open a submenu screen.
class Choice : public ClickableItem {
2013-05-02 22:21:39 +00:00
public:
Choice(const std::string &text, LayoutParams *layoutParams = nullptr)
: Choice(text, std::string(), false, layoutParams) {}
2021-09-19 16:45:15 +00:00
Choice(const std::string &text, ImageID image, LayoutParams *layoutParams = nullptr)
: ClickableItem(layoutParams), text_(text), image_(image) {}
Choice(const std::string &text, const std::string &smallText, bool selected = false, LayoutParams *layoutParams = nullptr)
2021-09-19 16:45:15 +00:00
: ClickableItem(layoutParams), text_(text), smallText_(smallText), image_(ImageID::invalid()) {}
Choice(ImageID image, LayoutParams *layoutParams = nullptr)
2021-09-19 16:45:15 +00:00
: ClickableItem(layoutParams), image_(image), rightIconImage_(ImageID::invalid()) {}
Choice(ImageID image, float imgScale, float imgRot, bool imgFlipH = false, LayoutParams *layoutParams = nullptr)
: ClickableItem(layoutParams), image_(image), rightIconImage_(ImageID::invalid()), imgScale_(imgScale), imgRot_(imgRot), imgFlipH_(imgFlipH) {}
2013-05-02 22:21:39 +00:00
2020-08-10 02:20:47 +00:00
void Click() override;
void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
void Draw(UIContext &dc) override;
std::string DescribeText() const override;
2013-08-30 20:42:02 +00:00
virtual void SetCentered(bool c) {
centered_ = c;
}
virtual void SetIcon(ImageID iconImage, float scale = 1.0f, float rot = 0.0f, bool flipH = false, bool keepColor = true) {
rightIconKeepColor_ = keepColor;
rightIconScale_ = scale;
rightIconRot_ = rot;
rightIconFlipH_ = flipH;
2021-09-19 16:45:15 +00:00
rightIconImage_ = iconImage;
}
2013-05-02 22:21:39 +00:00
2013-07-16 23:03:29 +00:00
protected:
// hackery
virtual bool IsSticky() const { return false; }
virtual float CalculateTextScale(const UIContext &dc, float availWidth) const;
2013-05-02 22:21:39 +00:00
std::string text_;
std::string smallText_;
2021-09-19 16:45:15 +00:00
ImageID image_; // Centered if no text, on the left if text.
ImageID rightIconImage_ = ImageID::invalid(); // Shows in the right.
float rightIconScale_;
float rightIconRot_;
bool rightIconFlipH_;
bool rightIconKeepColor_;
Padding textPadding_;
2021-09-19 16:45:15 +00:00
bool centered_ = false;
float imgScale_ = 1.0f;
float imgRot_ = 0.0f;
bool imgFlipH_ = false;
2013-07-16 23:03:29 +00:00
private:
2021-09-19 16:45:15 +00:00
bool selected_ = false;
2013-05-02 22:21:39 +00:00
};
// Different key handling.
class StickyChoice : public Choice {
public:
StickyChoice(const std::string &text, const std::string &smallText = "", LayoutParams *layoutParams = 0)
2013-07-15 22:25:08 +00:00
: Choice(text, smallText, false, layoutParams) {}
2013-08-19 22:34:54 +00:00
StickyChoice(ImageID buttonImage, LayoutParams *layoutParams = 0)
: Choice(buttonImage, layoutParams) {}
bool Key(const KeyInput &key) override;
bool Touch(const TouchInput &touch) override;
void FocusChanged(int focusFlags) override;
2013-08-17 10:06:08 +00:00
void Press() { down_ = true; dragging_ = false; }
void Release() { down_ = false; dragging_ = false; }
bool IsDown() { return down_; }
protected:
// hackery
bool IsSticky() const override { return true; }
};
class InfoItem : public Item {
public:
InfoItem(const std::string &text, const std::string &rightText, LayoutParams *layoutParams = nullptr);
void Draw(UIContext &dc) override;
std::string DescribeText() const override;
// These are focusable so that long lists of them can be keyboard scrolled.
bool CanBeFocused() const override { return true; }
2015-07-05 16:45:23 +00:00
void SetText(const std::string &text) {
text_ = text;
}
const std::string &GetText() const {
return text_;
}
2015-07-05 16:45:23 +00:00
void SetRightText(const std::string &text) {
rightText_ = text;
}
void SetChoiceStyle(bool choiceStyle) {
choiceStyle_ = choiceStyle;
}
2015-07-05 16:45:23 +00:00
private:
CallbackColorTween *bgColor_ = nullptr;
CallbackColorTween *fgColor_ = nullptr;
std::string text_;
std::string rightText_;
bool choiceStyle_ = false;
};
2013-05-25 14:52:27 +00:00
class ItemHeader : public Item {
public:
2013-08-19 22:34:54 +00:00
ItemHeader(const std::string &text, LayoutParams *layoutParams = 0);
void Draw(UIContext &dc) override;
std::string DescribeText() const override;
2017-12-12 08:16:05 +00:00
void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
2013-05-25 14:52:27 +00:00
private:
std::string text_;
};
2013-08-14 21:29:27 +00:00
class PopupHeader : public Item {
public:
PopupHeader(const std::string &text, LayoutParams *layoutParams = 0)
: Item(layoutParams), text_(text) {
layoutParams_->width = FILL_PARENT;
layoutParams_->height = 64;
}
void Draw(UIContext &dc) override;
std::string DescribeText() const override;
2013-08-14 21:29:27 +00:00
private:
std::string text_;
};
2013-05-25 13:12:46 +00:00
class CheckBox : public ClickableItem {
2013-05-02 22:21:39 +00:00
public:
CheckBox(bool *toggle, const std::string &text, const std::string &smallText = "", LayoutParams *layoutParams = nullptr)
: ClickableItem(layoutParams), toggle_(toggle), text_(text), smallText_(smallText) {
OnClick.Handle(this, &CheckBox::OnClicked);
}
2013-05-02 22:21:39 +00:00
// Image-only "checkbox", lights up instead of showing a checkmark.
CheckBox(bool *toggle, ImageID imageID, LayoutParams *layoutParams = nullptr)
: ClickableItem(layoutParams), toggle_(toggle), imageID_(imageID) {
OnClick.Handle(this, &CheckBox::OnClicked);
}
void Draw(UIContext &dc) override;
std::string DescribeText() const override;
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
2013-05-02 22:21:39 +00:00
EventReturn OnClicked(EventParams &e);
2013-10-27 05:28:16 +00:00
//allow external agents to toggle the checkbox
2019-02-03 22:02:47 +00:00
virtual void Toggle();
virtual bool Toggled() const;
2013-05-02 22:21:39 +00:00
private:
float CalculateTextScale(const UIContext &dc, float availWidth) const;
bool *toggle_;
2013-05-02 22:21:39 +00:00
std::string text_;
std::string smallText_;
ImageID imageID_;
2013-05-02 22:21:39 +00:00
};
2019-02-03 22:02:47 +00:00
class BitCheckBox : public CheckBox {
public:
BitCheckBox(uint32_t *bitfield, uint32_t bit, const std::string &text, const std::string &smallText = "", LayoutParams *layoutParams = nullptr)
: CheckBox(nullptr, text, smallText, layoutParams), bitfield_(bitfield), bit_(bit) {
}
void Toggle() override;
bool Toggled() const override;
private:
uint32_t *bitfield_;
uint32_t bit_;
};
// These are for generic use.
class Spacer : public InertView {
public:
Spacer(LayoutParams *layoutParams = 0)
: InertView(layoutParams), size_(0.0f) {}
Spacer(float size, LayoutParams *layoutParams = 0)
: InertView(layoutParams), size_(size) {}
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override {
w = size_; h = size_;
}
void Draw(UIContext &dc) override {}
std::string DescribeText() const override { return ""; }
private:
float size_ = 0.0f;
};
class BorderView : public InertView {
public:
BorderView(Borders borderFlags, BorderStyle style, float size = 2.0f, LayoutParams *layoutParams = nullptr)
: InertView(layoutParams), borderFlags_(borderFlags), style_(style), size_(size) {
}
void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
void Draw(UIContext &dc) override;
std::string DescribeText() const override { return ""; }
private:
Borders borderFlags_;
BorderStyle style_;
float size_;
2013-05-02 22:21:39 +00:00
};
class TextView : public InertView {
public:
TextView(const std::string &text, LayoutParams *layoutParams = 0)
: InertView(layoutParams), text_(text), textAlign_(0), textColor_(0xFFFFFFFF), small_(false), shadow_(false), focusable_(false), clip_(true) {}
2013-08-16 10:51:57 +00:00
TextView(const std::string &text, int textAlign, bool small, LayoutParams *layoutParams = 0)
2015-09-23 15:39:52 +00:00
: InertView(layoutParams), text_(text), textAlign_(textAlign), textColor_(0xFFFFFFFF), small_(small), shadow_(false), focusable_(false), clip_(true) {}
2013-05-02 22:21:39 +00:00
void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
void Draw(UIContext &dc) override;
void SetText(const std::string &text) { text_ = text; }
const std::string &GetText() const { return text_; }
std::string DescribeText() const override { return GetText(); }
void SetSmall(bool small) { small_ = small; }
void SetTextColor(uint32_t color) { textColor_ = color; hasTextColor_ = true; }
void SetShadow(bool shadow) { shadow_ = shadow; }
void SetFocusable(bool focusable) { focusable_ = focusable; }
void SetClip(bool clip) { clip_ = clip; }
void SetBullet(bool bullet) { bullet_ = bullet; }
bool CanBeFocused() const override { return focusable_; }
2013-05-02 22:21:39 +00:00
private:
2013-06-01 16:59:03 +00:00
std::string text_;
int textAlign_;
2015-01-05 00:19:49 +00:00
uint32_t textColor_;
bool hasTextColor_ = false;
bool small_;
bool shadow_;
bool focusable_;
bool clip_;
bool bullet_ = false;
2013-05-02 22:21:39 +00:00
};
class TextEdit : public View {
public:
TextEdit(const std::string &text, const std::string &title, const std::string &placeholderText, LayoutParams *layoutParams = nullptr);
void SetText(const std::string &text) { text_ = text; scrollPos_ = 0; caret_ = (int)text_.size(); }
void SetTextColor(uint32_t color) { textColor_ = color; hasTextColor_ = true; }
const std::string &GetText() const { return text_; }
2014-08-30 09:05:21 +00:00
void SetMaxLen(size_t maxLen) { maxLen_ = maxLen; }
void SetTextAlign(int align) { align_ = align; } // Only really useful for setting FLAG_DYNAMIC_ASCII
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
void Draw(UIContext &dc) override;
std::string DescribeText() const override;
bool Key(const KeyInput &key) override;
bool Touch(const TouchInput &touch) override;
Event OnTextChange;
2015-01-05 00:19:49 +00:00
Event OnEnter;
private:
void InsertAtCaret(const char *text);
std::string text_;
std::string title_;
std::string undo_;
std::string placeholderText_;
uint32_t textColor_;
bool hasTextColor_ = false;
int caret_;
int scrollPos_ = 0;
2014-08-30 09:05:21 +00:00
size_t maxLen_;
bool ctrlDown_ = false; // TODO: Make some global mechanism for this.
int align_ = 0;
// TODO: Selections
};
enum ImageSizeMode {
IS_DEFAULT,
IS_FIXED,
IS_KEEP_ASPECT,
};
2013-05-02 22:21:39 +00:00
class ImageView : public InertView {
public:
ImageView(ImageID atlasImage, const std::string &text, ImageSizeMode sizeMode, LayoutParams *layoutParams = 0)
: InertView(layoutParams), text_(text), atlasImage_(atlasImage), sizeMode_(sizeMode) {}
2013-05-02 22:21:39 +00:00
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
void Draw(UIContext &dc) override;
std::string DescribeText() const override { return text_; }
2013-05-02 22:21:39 +00:00
private:
std::string text_;
ImageID atlasImage_;
2013-05-02 22:21:39 +00:00
ImageSizeMode sizeMode_;
};
2013-06-02 21:44:28 +00:00
class ProgressBar : public InertView {
public:
ProgressBar(LayoutParams *layoutParams = 0)
: InertView(layoutParams), progress_(0.0) {}
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
void Draw(UIContext &dc) override;
std::string DescribeText() const override;
2013-06-02 21:44:28 +00:00
2013-12-06 14:28:52 +00:00
void SetProgress(float progress) {
if (progress > 1.0f) {
progress_ = 1.0f;
} else if (progress < 0.0f) {
progress_ = 0.0f;
} else {
progress_ = progress;
}
}
2013-06-02 21:44:28 +00:00
float GetProgress() const { return progress_; }
private:
float progress_;
};
class Spinner : public InertView {
public:
Spinner(const ImageID *images, int numImages, LayoutParams *layoutParams = 0)
: InertView(layoutParams), images_(images), numImages_(numImages) {
}
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
void Draw(UIContext &dc) override;
std::string DescribeText() const override { return ""; }
void SetColor(uint32_t color) { color_ = color; }
private:
const ImageID *images_;
int numImages_;
uint32_t color_ = 0xFFFFFFFF;
};
2013-05-02 22:21:39 +00:00
void MeasureBySpec(Size sz, float contentWidth, MeasureSpec spec, float *measured);
bool IsDPadKey(const KeyInput &key);
bool IsAcceptKey(const KeyInput &key);
bool IsEscapeKey(const KeyInput &key);
bool IsTabLeftKey(const KeyInput &key);
bool IsTabRightKey(const KeyInput &key);
} // namespace