1 #pragma once
2
3 // More traditional UI framework than ui/ui.h.
4
5 // Still very simple to use.
6
7 // Works very similarly to Android, there's a Measure pass and a Layout pass which you don't
8 // really need to care about if you just use the standard containers and widgets.
9
10 #include <cmath>
11 #include <cstdio>
12 #include <functional>
13 #include <map>
14 #include <memory>
15 #include <string>
16 #include <vector>
17
18 #include "Common/Render/TextureAtlas.h"
19 #include "Common/Math/lin/matrix4x4.h"
20 #include "Common/Math/math_util.h"
21 #include "Common/Math/geom2d.h"
22
23 #include "Common/Common.h"
24
25 #undef small
26
27 struct KeyInput;
28 struct TouchInput;
29 struct AxisInput;
30
31 struct ImageID;
32
33 class DrawBuffer;
34 class Texture;
35 class UIContext;
36
37 namespace Draw {
38 class DrawContext;
39 class Texture;
40 }
41
42
43 // I don't generally like namespaces but I think we do need one for UI, so many potentially-clashing names.
44 namespace UI {
45
46 class View;
47
48 enum DrawableType {
49 DRAW_NOTHING,
50 DRAW_SOLID_COLOR,
51 DRAW_4GRID,
52 DRAW_STRETCH_IMAGE,
53 };
54
55 enum Visibility {
56 V_VISIBLE,
57 V_INVISIBLE, // Keeps position, not drawn or interacted with
58 V_GONE, // Does not participate in layout
59 };
60
61 struct Drawable {
DrawableDrawable62 Drawable() : type(DRAW_NOTHING), image(ImageID::invalid()), color(0xFFFFFFFF) {}
DrawableDrawable63 explicit Drawable(uint32_t col) : type(DRAW_SOLID_COLOR), image(ImageID::invalid()), color(col) {}
typeDrawable64 Drawable(DrawableType t, ImageID img, uint32_t col = 0xFFFFFFFF) : type(t), image(img), color(col) {}
65
66 DrawableType type;
67 ImageID image;
68 uint32_t color;
69 };
70
71 struct Style {
StyleStyle72 Style() : fgColor(0xFFFFFFFF), background(0xFF303030), image(ImageID::invalid()) {}
73
74 uint32_t fgColor;
75 Drawable background;
76 ImageID image; // where applicable.
77 };
78
79 struct FontStyle {
FontStyleFontStyle80 FontStyle() : atlasFont(0), sizePts(0), flags(0) {}
FontStyleFontStyle81 FontStyle(const char *name, int size) : atlasFont(0), fontName(name), sizePts(size), flags(0) {}
FontStyleFontStyle82 FontStyle(FontID atlasFnt, const char *name, int size) : atlasFont(atlasFnt), fontName(name), sizePts(size), flags(0) {}
83
84 FontID atlasFont;
85 // For native fonts:
86 std::string fontName;
87 int sizePts;
88 int flags;
89 };
90
91
92 // To use with an UI atlas.
93 struct Theme {
94 FontStyle uiFont;
95 FontStyle uiFontSmall;
96 FontStyle uiFontSmaller;
97
98 ImageID checkOn;
99 ImageID checkOff;
100 ImageID sliderKnob;
101 ImageID whiteImage;
102 ImageID dropShadow4Grid;
103
104 Style buttonStyle;
105 Style buttonFocusedStyle;
106 Style buttonDownStyle;
107 Style buttonDisabledStyle;
108 Style buttonHighlightedStyle;
109
110 Style itemStyle;
111 Style itemDownStyle;
112 Style itemFocusedStyle;
113 Style itemDisabledStyle;
114 Style itemHighlightedStyle;
115
116 Style headerStyle;
117 Style infoStyle;
118
119 Style popupTitle;
120 Style popupStyle;
121 };
122
123 // The four cardinal directions should be enough, plus Prev/Next in "element order".
124 enum FocusDirection {
125 FOCUS_UP,
126 FOCUS_DOWN,
127 FOCUS_LEFT,
128 FOCUS_RIGHT,
129 FOCUS_NEXT,
130 FOCUS_PREV,
131 FOCUS_FIRST,
132 FOCUS_LAST,
133 FOCUS_PREV_PAGE,
134 FOCUS_NEXT_PAGE,
135 };
136
137 enum {
138 WRAP_CONTENT = -1,
139 FILL_PARENT = -2,
140 };
141
142 // Gravity
143 enum Gravity {
144 G_LEFT = 0,
145 G_RIGHT = 1,
146 G_HCENTER = 2,
147
148 G_HORIZMASK = 3,
149
150 G_TOP = 0,
151 G_BOTTOM = 4,
152 G_VCENTER = 8,
153
154 G_TOPLEFT = G_TOP | G_LEFT,
155 G_TOPRIGHT = G_TOP | G_RIGHT,
156
157 G_BOTTOMLEFT = G_BOTTOM | G_LEFT,
158 G_BOTTOMRIGHT = G_BOTTOM | G_RIGHT,
159
160 G_CENTER = G_HCENTER | G_VCENTER,
161
162 G_VERTMASK = 3 << 2,
163 };
164
165 enum Borders {
166 BORDER_NONE = 0,
167
168 BORDER_TOP = 0x0001,
169 BORDER_LEFT = 0x0002,
170 BORDER_BOTTOM = 0x0004,
171 BORDER_RIGHT = 0x0008,
172
173 BORDER_HORIZ = BORDER_LEFT | BORDER_RIGHT,
174 BORDER_VERT = BORDER_TOP | BORDER_BOTTOM,
175 BORDER_ALL = BORDER_TOP | BORDER_LEFT | BORDER_BOTTOM | BORDER_RIGHT,
176 };
177
178 enum class BorderStyle {
179 HEADER_FG,
180 ITEM_DOWN_BG,
181 };
182
183 typedef float Size; // can also be WRAP_CONTENT or FILL_PARENT.
184
185 enum Orientation {
186 ORIENT_HORIZONTAL,
187 ORIENT_VERTICAL,
188 };
189
Opposite(Orientation o)190 inline Orientation Opposite(Orientation o) {
191 if (o == ORIENT_HORIZONTAL) return ORIENT_VERTICAL; else return ORIENT_HORIZONTAL;
192 }
193
Opposite(FocusDirection d)194 inline FocusDirection Opposite(FocusDirection d) {
195 switch (d) {
196 case FOCUS_UP: return FOCUS_DOWN;
197 case FOCUS_DOWN: return FOCUS_UP;
198 case FOCUS_LEFT: return FOCUS_RIGHT;
199 case FOCUS_RIGHT: return FOCUS_LEFT;
200 case FOCUS_PREV: return FOCUS_NEXT;
201 case FOCUS_NEXT: return FOCUS_PREV;
202 case FOCUS_FIRST: return FOCUS_LAST;
203 case FOCUS_LAST: return FOCUS_FIRST;
204 case FOCUS_PREV_PAGE: return FOCUS_NEXT_PAGE;
205 case FOCUS_NEXT_PAGE: return FOCUS_PREV_PAGE;
206 }
207 return d;
208 }
209
210 enum MeasureSpecType {
211 UNSPECIFIED,
212 EXACTLY,
213 AT_MOST,
214 };
215
216 // I hope I can find a way to simplify this one day.
217 enum EventReturn {
218 EVENT_DONE, // Return this when no other view may process this event, for example if you changed the view hierarchy
219 EVENT_SKIPPED, // Return this if you ignored an event
220 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.
221 };
222
223 enum FocusFlags {
224 FF_LOSTFOCUS = 1,
225 FF_GOTFOCUS = 2
226 };
227
228 enum PersistStatus {
229 PERSIST_SAVE,
230 PERSIST_RESTORE,
231 };
232
233 typedef std::vector<int> PersistBuffer;
234 typedef std::map<std::string, UI::PersistBuffer> PersistMap;
235
236 class ViewGroup;
237
238 struct MeasureSpec {
typeMeasureSpec239 MeasureSpec(MeasureSpecType t, float s = 0.0f) : type(t), size(s) {}
MeasureSpecMeasureSpec240 MeasureSpec() : type(UNSPECIFIED), size(0) {}
241
242 MeasureSpec operator -(float amount) {
243 // TODO: Check type
244 return MeasureSpec(type, size - amount);
245 }
246 MeasureSpecType type;
247 float size;
248 };
249
250 // Should cover all bases.
251 struct EventParams {
252 View *v;
253 uint32_t a, b, x, y;
254 float f;
255 std::string s;
256 };
257
258 struct HandlerRegistration {
259 std::function<EventReturn(EventParams&)> func;
260 };
261
262 class Event {
263 public:
Event()264 Event() {}
265 ~Event();
266 // Call this from input thread or whatever, it doesn't matter
267 void Trigger(EventParams &e);
268 // Call this from UI thread
269 EventReturn Dispatch(EventParams &e);
270
271 // This is suggested for use in most cases. Autobinds, allowing for neat syntax.
272 template<class T>
Handle(T * thiz,EventReturn (T::* theCallback)(EventParams & e))273 T *Handle(T *thiz, EventReturn (T::* theCallback)(EventParams &e)) {
274 Add(std::bind(theCallback, thiz, std::placeholders::_1));
275 return thiz;
276 }
277
278 // Sometimes you have an already-bound function<>, just use this then.
279 void Add(std::function<EventReturn(EventParams&)> func);
280
281 private:
282 std::vector<HandlerRegistration> handlers_;
283 DISALLOW_COPY_AND_ASSIGN(Event);
284 };
285
286 struct Margins {
MarginsMargins287 Margins() : top(0), bottom(0), left(0), right(0) {}
MarginsMargins288 explicit Margins(int8_t all) : top(all), bottom(all), left(all), right(all) {}
MarginsMargins289 Margins(int8_t horiz, int8_t vert) : top(vert), bottom(vert), left(horiz), right(horiz) {}
MarginsMargins290 Margins(int8_t l, int8_t t, int8_t r, int8_t b) : top(t), bottom(b), left(l), right(r) {}
291
horizMargins292 int horiz() const {
293 return left + right;
294 }
vertMargins295 int vert() const {
296 return top + bottom;
297 }
298
299 int8_t top;
300 int8_t bottom;
301 int8_t left;
302 int8_t right;
303 };
304
305 struct Padding {
PaddingPadding306 Padding() : top(0), bottom(0), left(0), right(0) {}
PaddingPadding307 explicit Padding(float all) : top(all), bottom(all), left(all), right(all) {}
PaddingPadding308 Padding(float horiz, float vert) : top(vert), bottom(vert), left(horiz), right(horiz) {}
PaddingPadding309 Padding(float l, float t, float r, float b) : top(t), bottom(b), left(l), right(r) {}
310
horizPadding311 float horiz() const {
312 return left + right;
313 }
vertPadding314 float vert() const {
315 return top + bottom;
316 }
317
318 float top;
319 float bottom;
320 float left;
321 float right;
322 };
323
324 enum LayoutParamsType {
325 LP_PLAIN = 0,
326 LP_LINEAR = 1,
327 LP_ANCHOR = 2,
328 LP_GRID = 3,
329 };
330
331 // Need a virtual destructor so vtables are created, otherwise RTTI can't work
332 class LayoutParams {
333 public:
334 LayoutParams(LayoutParamsType type = LP_PLAIN)
width(WRAP_CONTENT)335 : width(WRAP_CONTENT), height(WRAP_CONTENT), type_(type) {}
336 LayoutParams(Size w, Size h, LayoutParamsType type = LP_PLAIN)
width(w)337 : width(w), height(h), type_(type) {}
~LayoutParams()338 virtual ~LayoutParams() {}
339 Size width;
340 Size height;
341
342 // Fake RTTI
Is(LayoutParamsType type)343 bool Is(LayoutParamsType type) const { return type_ == type; }
344
345 template <typename T>
As()346 T *As() {
347 if (Is(T::StaticType())) {
348 return static_cast<T *>(this);
349 }
350 return nullptr;
351 }
352
353 template <typename T>
As()354 const T *As() const {
355 if (Is(T::StaticType())) {
356 return static_cast<const T *>(this);
357 }
358 return nullptr;
359 }
360
StaticType()361 static LayoutParamsType StaticType() {
362 return LP_PLAIN;
363 }
364
365 private:
366 LayoutParamsType type_;
367 };
368
369 View *GetFocusedView();
370
371 class Tween;
372 class CallbackColorTween;
373
374 class View {
375 public:
layoutParams_(layoutParams)376 View(LayoutParams *layoutParams = 0) : layoutParams_(layoutParams), visibility_(V_VISIBLE), measuredWidth_(0), measuredHeight_(0), enabledPtr_(0), enabled_(true), enabledMeansDisabled_(false) {
377 if (!layoutParams)
378 layoutParams_.reset(new LayoutParams());
379 }
380 virtual ~View();
381
382 // Please note that Touch is called ENTIRELY asynchronously from drawing!
383 // Can even be called on a different thread! This is to really minimize latency, and decouple
384 // touch response from the frame rate. Same with Key and Axis.
Key(const KeyInput & input)385 virtual bool Key(const KeyInput &input) { return false; }
Touch(const TouchInput & input)386 virtual void Touch(const TouchInput &input) {}
Axis(const AxisInput & input)387 virtual void Axis(const AxisInput &input) {}
388 virtual void Update();
389
DeviceLost()390 virtual void DeviceLost() {}
DeviceRestored(Draw::DrawContext * draw)391 virtual void DeviceRestored(Draw::DrawContext *draw) {}
392
393 // If this view covers these coordinates, it should add itself and its children to the list.
394 virtual void Query(float x, float y, std::vector<View *> &list);
395 virtual std::string DescribeLog() const;
396 // Accessible/searchable description.
DescribeText()397 virtual std::string DescribeText() const { return ""; }
398
FocusChanged(int focusFlags)399 virtual void FocusChanged(int focusFlags) {}
400 virtual void PersistData(PersistStatus status, std::string anonId, PersistMap &storage);
401
Move(Bounds bounds)402 void Move(Bounds bounds) {
403 bounds_ = bounds;
404 }
405
406 // Views don't do anything here in Layout, only containers implement this.
407 virtual void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert);
Layout()408 virtual void Layout() {}
Draw(UIContext & dc)409 virtual void Draw(UIContext &dc) {}
410
GetMeasuredWidth()411 virtual float GetMeasuredWidth() const { return measuredWidth_; }
GetMeasuredHeight()412 virtual float GetMeasuredHeight() const { return measuredHeight_; }
413
414 // Override this for easy standard behaviour. No need to override Measure.
415 virtual void GetContentDimensions(const UIContext &dc, float &w, float &h) const;
416 virtual void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const;
417
418 // Called when the layout is done.
SetBounds(Bounds bounds)419 void SetBounds(Bounds bounds) { bounds_ = bounds; }
GetLayoutParams()420 virtual const LayoutParams *GetLayoutParams() const { return layoutParams_.get(); }
ReplaceLayoutParams(LayoutParams * newLayoutParams)421 virtual void ReplaceLayoutParams(LayoutParams *newLayoutParams) { layoutParams_.reset(newLayoutParams); }
GetBounds()422 const Bounds &GetBounds() const { return bounds_; }
423
424 virtual bool SetFocus();
425
CanBeFocused()426 virtual bool CanBeFocused() const { return true; }
SubviewFocused(View * view)427 virtual bool SubviewFocused(View *view) { return false; }
428
HasFocus()429 bool HasFocus() const {
430 return GetFocusedView() == this;
431 }
432
SetEnabled(bool enabled)433 void SetEnabled(bool enabled) {
434 enabledFunc_ = nullptr;
435 enabledPtr_ = nullptr;
436 enabled_ = enabled;
437 enabledMeansDisabled_ = false;
438 }
IsEnabled()439 bool IsEnabled() const {
440 if (enabledFunc_)
441 return enabledFunc_() != enabledMeansDisabled_;
442 if (enabledPtr_)
443 return *enabledPtr_ != enabledMeansDisabled_;
444 return enabled_ != enabledMeansDisabled_;
445 }
SetEnabledFunc(std::function<bool ()> func)446 void SetEnabledFunc(std::function<bool()> func) {
447 enabledFunc_ = func;
448 enabledPtr_ = nullptr;
449 enabledMeansDisabled_ = false;
450 }
SetEnabledPtr(bool * enabled)451 void SetEnabledPtr(bool *enabled) {
452 enabledFunc_ = nullptr;
453 enabledPtr_ = enabled;
454 enabledMeansDisabled_ = false;
455 }
SetDisabledPtr(bool * disabled)456 void SetDisabledPtr(bool *disabled) {
457 enabledFunc_ = nullptr;
458 enabledPtr_ = disabled;
459 enabledMeansDisabled_ = true;
460 }
461
SetVisibility(Visibility visibility)462 virtual void SetVisibility(Visibility visibility) { visibility_ = visibility; }
GetVisibility()463 Visibility GetVisibility() const { return visibility_; }
464
Tag()465 const std::string &Tag() const { return tag_; }
SetTag(const std::string & str)466 void SetTag(const std::string &str) { tag_ = str; }
467
468 // Fake RTTI
IsViewGroup()469 virtual bool IsViewGroup() const { return false; }
ContainsSubview(const View * view)470 virtual bool ContainsSubview(const View *view) const { return false; }
471
472 Point GetFocusPosition(FocusDirection dir);
473
474 template <class T>
AddTween(T * t)475 T *AddTween(T *t) {
476 tweens_.push_back(t);
477 return t;
478 }
479
480 protected:
481 // Inputs to layout
482 std::unique_ptr<LayoutParams> layoutParams_;
483
484 std::string tag_;
485 Visibility visibility_;
486
487 // Results of measure pass. Set these in Measure.
488 float measuredWidth_;
489 float measuredHeight_;
490
491 // Outputs of layout. X/Y are absolute screen coordinates, hierarchy is "gone" here.
492 Bounds bounds_;
493
494 std::vector<Tween *> tweens_;
495
496 private:
497 std::function<bool()> enabledFunc_;
498 bool *enabledPtr_;
499 bool enabled_;
500 bool enabledMeansDisabled_;
501
502 DISALLOW_COPY_AND_ASSIGN(View);
503 };
504
505 // These don't do anything when touched.
506 class InertView : public View {
507 public:
InertView(LayoutParams * layoutParams)508 InertView(LayoutParams *layoutParams)
509 : View(layoutParams) {}
510
Key(const KeyInput & input)511 bool Key(const KeyInput &input) override { return false; }
Touch(const TouchInput & input)512 void Touch(const TouchInput &input) override {}
CanBeFocused()513 bool CanBeFocused() const override { return false; }
514 };
515
516
517 // All these light up their background when touched, or have focus.
518 class Clickable : public View {
519 public:
520 Clickable(LayoutParams *layoutParams);
521
522 bool Key(const KeyInput &input) override;
523 void Touch(const TouchInput &input) override;
524
525 void FocusChanged(int focusFlags) override;
526
527 Event OnClick;
528
529 protected:
530 // Internal method that fires on a click. Default behaviour is to trigger
531 // the event.
532 // Use it for checking/unchecking checkboxes, etc.
533 virtual void Click();
534 void DrawBG(UIContext &dc, const Style &style);
535
536 CallbackColorTween *bgColor_ = nullptr;
537 float bgColorLast_ = 0.0f;
538 int downCountDown_ = 0;
539 bool dragging_ = false;
540 bool down_ = false;
541 };
542
543 // TODO: Very similar to Choice, should probably merge them.
544 // Right now more flexible image support though.
545 class Button : public Clickable {
546 public:
547 Button(const std::string &text, LayoutParams *layoutParams = 0)
Clickable(layoutParams)548 : Clickable(layoutParams), text_(text), imageID_(ImageID::invalid()) {}
549 Button(const std::string &text, ImageID imageID, LayoutParams *layoutParams = 0)
Clickable(layoutParams)550 : Clickable(layoutParams), text_(text), imageID_(imageID) {}
551
552 void Click() override;
553 void Draw(UIContext &dc) override;
554 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
GetText()555 const std::string &GetText() const { return text_; }
556 std::string DescribeText() const override;
SetPadding(int w,int h)557 void SetPadding(int w, int h) {
558 paddingW_ = w;
559 paddingH_ = h;
560 }
SetImageID(ImageID imageID)561 void SetImageID(ImageID imageID) {
562 imageID_ = imageID;
563 }
SetIgnoreText(bool ignore)564 void SetIgnoreText(bool ignore) {
565 ignoreText_ = ignore;
566 }
567 // Needed an extra small button...
SetScale(float f)568 void SetScale(float f) {
569 scale_ = f;
570 }
571 private:
572 Style style_;
573 std::string text_;
574 ImageID imageID_;
575 int paddingW_ = 16;
576 int paddingH_ = 8;
577 float scale_ = 1.0f;
578 bool ignoreText_ = false;
579 };
580
581 class RadioButton : public Clickable {
582 public:
583 RadioButton(int *value, int thisButtonValue, const std::string &text, LayoutParams *layoutParams = 0)
Clickable(layoutParams)584 : Clickable(layoutParams), value_(value), thisButtonValue_(thisButtonValue), text_(text) {}
585 void Click() override;
586 void Draw(UIContext &dc) override;
587 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
588 std::string DescribeText() const override;
589
590 private:
591 int *value_;
592 int thisButtonValue_;
593 std::string text_;
594 const float paddingW_ = 8;
595 const float paddingH_ = 4;
596
597 const float radioRadius_ = 16.0f;
598 const float radioInnerRadius_ = 8.0f;
599 };
600
601 class Slider : public Clickable {
602 public:
603 Slider(int *value, int minValue, int maxValue, LayoutParams *layoutParams = 0)
Clickable(layoutParams)604 : Clickable(layoutParams), value_(value), showPercent_(false), minValue_(minValue), maxValue_(maxValue), paddingLeft_(5), paddingRight_(70), step_(1), repeat_(-1) {}
605
606 Slider(int *value, int minValue, int maxValue, int step = 1, LayoutParams *layoutParams = 0)
Clickable(layoutParams)607 : Clickable(layoutParams), value_(value), showPercent_(false), minValue_(minValue), maxValue_(maxValue), paddingLeft_(5), paddingRight_(70), repeat_(-1) {
608 step_ = step <= 0 ? 1 : step;
609 }
610 void Draw(UIContext &dc) override;
611 std::string DescribeText() const override;
612 bool Key(const KeyInput &input) override;
613 void Touch(const TouchInput &input) override;
614 void Update() override;
615 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
SetShowPercent(bool s)616 void SetShowPercent(bool s) { showPercent_ = s; }
617
618 // OK to call this from the outside after having modified *value_
619 void Clamp();
620
621 Event OnChange;
622
623 private:
624 bool ApplyKey(int keyCode);
625
626 int *value_;
627 bool showPercent_;
628 int minValue_;
629 int maxValue_;
630 float paddingLeft_;
631 float paddingRight_;
632 int step_;
633 int repeat_ = 0;
634 int repeatCode_ = 0;
635 };
636
637 class SliderFloat : public Clickable {
638 public:
639 SliderFloat(float *value, float minValue, float maxValue, LayoutParams *layoutParams = 0)
Clickable(layoutParams)640 : Clickable(layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), paddingLeft_(5), paddingRight_(70), repeat_(-1) {}
641 void Draw(UIContext &dc) override;
642 std::string DescribeText() const override;
643 bool Key(const KeyInput &input) override;
644 void Touch(const TouchInput &input) override;
645 void Update() override;
646 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
647
648 // OK to call this from the outside after having modified *value_
649 void Clamp();
650
651 Event OnChange;
652
653 private:
654 bool ApplyKey(int keyCode);
655
656 float *value_;
657 float minValue_;
658 float maxValue_;
659 float paddingLeft_;
660 float paddingRight_;
661 int repeat_;
662 int repeatCode_ = 0;
663 };
664
665 // Basic button that modifies a bitfield based on the pressed status. Supports multitouch.
666 // Suitable for controller simulation (ABXY etc).
667 class TriggerButton : public View {
668 public:
TriggerButton(uint32_t * bitField,uint32_t bit,ImageID imageBackground,ImageID imageForeground,LayoutParams * layoutParams)669 TriggerButton(uint32_t *bitField, uint32_t bit, ImageID imageBackground, ImageID imageForeground, LayoutParams *layoutParams)
670 : View(layoutParams), down_(0.0), bitField_(bitField), bit_(bit), imageBackground_(imageBackground), imageForeground_(imageForeground) {}
671
672 void Touch(const TouchInput &input) override;
673 void Draw(UIContext &dc) override;
674 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
675
676 private:
677 int down_; // bitfield of pressed fingers, translates into bitField
678
679 uint32_t *bitField_;
680 uint32_t bit_;
681
682 ImageID imageBackground_;
683 ImageID imageForeground_;
684 };
685
686
687 // The following classes are mostly suitable as items in ListView which
688 // really is just a LinearLayout in a ScrollView, possibly with some special optimizations.
689
690 class Item : public InertView {
691 public:
692 Item(LayoutParams *layoutParams);
693 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
694 };
695
696 class ClickableItem : public Clickable {
697 public:
698 ClickableItem(LayoutParams *layoutParams);
699 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
700
701 // Draws the item background.
702 void Draw(UIContext &dc) override;
703 };
704
705 // Use to trigger something or open a submenu screen.
706 class Choice : public ClickableItem {
707 public:
708 Choice(const std::string &text, LayoutParams *layoutParams = nullptr)
Choice(text,std::string (),false,layoutParams)709 : Choice(text, std::string(), false, layoutParams) {}
710 Choice(const std::string &text, ImageID image, LayoutParams *layoutParams = nullptr)
ClickableItem(layoutParams)711 : ClickableItem(layoutParams), text_(text), image_(image) {}
712 Choice(const std::string &text, const std::string &smallText, bool selected = false, LayoutParams *layoutParams = nullptr)
ClickableItem(layoutParams)713 : ClickableItem(layoutParams), text_(text), smallText_(smallText), image_(ImageID::invalid()) {}
714 Choice(ImageID image, LayoutParams *layoutParams = nullptr)
ClickableItem(layoutParams)715 : ClickableItem(layoutParams), image_(image), rightIconImage_(ImageID::invalid()) {}
716 Choice(ImageID image, float imgScale, float imgRot, bool imgFlipH = false, LayoutParams *layoutParams = nullptr)
ClickableItem(layoutParams)717 : ClickableItem(layoutParams), image_(image), rightIconImage_(ImageID::invalid()), imgScale_(imgScale), imgRot_(imgRot), imgFlipH_(imgFlipH) {}
718
719 void Click() override;
720 virtual void HighlightChanged(bool highlighted);
721 void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
722 void Draw(UIContext &dc) override;
723 std::string DescribeText() const override;
SetCentered(bool c)724 virtual void SetCentered(bool c) {
725 centered_ = c;
726 }
727 virtual void SetIcon(ImageID iconImage, float scale = 1.0f, float rot = 0.0f, bool flipH = false) {
728 rightIconScale_ = scale;
729 rightIconRot_ = rot;
730 rightIconFlipH_ = flipH;
731 rightIconImage_ = iconImage;
732 }
733
734 protected:
735 // hackery
IsSticky()736 virtual bool IsSticky() const { return false; }
737 virtual float CalculateTextScale(const UIContext &dc, float availWidth) const;
738
739 std::string text_;
740 std::string smallText_;
741 ImageID image_; // Centered if no text, on the left if text.
742 ImageID rightIconImage_ = ImageID::invalid(); // Shows in the right.
743 float rightIconScale_;
744 float rightIconRot_;
745 bool rightIconFlipH_;
746 Padding textPadding_;
747 bool centered_ = false;
748 bool highlighted_ = false;
749 float imgScale_ = 1.0f;
750 float imgRot_ = 0.0f;
751 bool imgFlipH_ = false;
752
753 private:
754 bool selected_ = false;
755 };
756
757 // Different key handling.
758 class StickyChoice : public Choice {
759 public:
760 StickyChoice(const std::string &text, const std::string &smallText = "", LayoutParams *layoutParams = 0)
Choice(text,smallText,false,layoutParams)761 : Choice(text, smallText, false, layoutParams) {}
762 StickyChoice(ImageID buttonImage, LayoutParams *layoutParams = 0)
Choice(buttonImage,layoutParams)763 : Choice(buttonImage, layoutParams) {}
764
765 bool Key(const KeyInput &key) override;
766 void Touch(const TouchInput &touch) override;
767 void FocusChanged(int focusFlags) override;
768
Press()769 void Press() { down_ = true; dragging_ = false; }
Release()770 void Release() { down_ = false; dragging_ = false; }
IsDown()771 bool IsDown() { return down_; }
772
773 protected:
774 // hackery
IsSticky()775 bool IsSticky() const override { return true; }
776 };
777
778 class InfoItem : public Item {
779 public:
780 InfoItem(const std::string &text, const std::string &rightText, LayoutParams *layoutParams = nullptr);
781
782 void Draw(UIContext &dc) override;
783 std::string DescribeText() const override;
784
785 // These are focusable so that long lists of them can be keyboard scrolled.
CanBeFocused()786 bool CanBeFocused() const override { return true; }
787
SetText(const std::string & text)788 void SetText(const std::string &text) {
789 text_ = text;
790 }
GetText()791 const std::string &GetText() const {
792 return text_;
793 }
SetRightText(const std::string & text)794 void SetRightText(const std::string &text) {
795 rightText_ = text;
796 }
SetChoiceStyle(bool choiceStyle)797 void SetChoiceStyle(bool choiceStyle) {
798 choiceStyle_ = choiceStyle;
799 }
800
801 private:
802 CallbackColorTween *bgColor_ = nullptr;
803 CallbackColorTween *fgColor_ = nullptr;
804
805 std::string text_;
806 std::string rightText_;
807
808 bool choiceStyle_ = false;
809 };
810
811 class ItemHeader : public Item {
812 public:
813 ItemHeader(const std::string &text, LayoutParams *layoutParams = 0);
814 void Draw(UIContext &dc) override;
815 std::string DescribeText() const override;
816 void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
817
818 private:
819 std::string text_;
820 };
821
822 class PopupHeader : public Item {
823 public:
824 PopupHeader(const std::string &text, LayoutParams *layoutParams = 0)
Item(layoutParams)825 : Item(layoutParams), text_(text) {
826 layoutParams_->width = FILL_PARENT;
827 layoutParams_->height = 64;
828 }
829 void Draw(UIContext &dc) override;
830 std::string DescribeText() const override;
831
832 private:
833 std::string text_;
834 };
835
836 class CheckBox : public ClickableItem {
837 public:
838 CheckBox(bool *toggle, const std::string &text, const std::string &smallText = "", LayoutParams *layoutParams = 0)
ClickableItem(layoutParams)839 : ClickableItem(layoutParams), toggle_(toggle), text_(text), smallText_(smallText) {
840 OnClick.Handle(this, &CheckBox::OnClicked);
841 }
842
843 void Draw(UIContext &dc) override;
844 std::string DescribeText() const override;
845 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
846
847 EventReturn OnClicked(EventParams &e);
848 //allow external agents to toggle the checkbox
849 virtual void Toggle();
850 virtual bool Toggled() const;
851 private:
852 float CalculateTextScale(const UIContext &dc, float availWidth) const;
853
854 bool *toggle_;
855 std::string text_;
856 std::string smallText_;
857 };
858
859 class BitCheckBox : public CheckBox {
860 public:
861 BitCheckBox(uint32_t *bitfield, uint32_t bit, const std::string &text, const std::string &smallText = "", LayoutParams *layoutParams = nullptr)
CheckBox(nullptr,text,smallText,layoutParams)862 : CheckBox(nullptr, text, smallText, layoutParams), bitfield_(bitfield), bit_(bit) {
863 }
864
865 void Toggle() override;
866 bool Toggled() const override;
867
868 private:
869 uint32_t *bitfield_;
870 uint32_t bit_;
871 };
872
873 // These are for generic use.
874
875 class Spacer : public InertView {
876 public:
877 Spacer(LayoutParams *layoutParams = 0)
InertView(layoutParams)878 : InertView(layoutParams), size_(0.0f) {}
879 Spacer(float size, LayoutParams *layoutParams = 0)
InertView(layoutParams)880 : InertView(layoutParams), size_(size) {}
GetContentDimensions(const UIContext & dc,float & w,float & h)881 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override {
882 w = size_; h = size_;
883 }
Draw(UIContext & dc)884 void Draw(UIContext &dc) override {}
DescribeText()885 std::string DescribeText() const override { return ""; }
886
887 private:
888 float size_ = 0.0f;
889 };
890
891 class BorderView : public InertView {
892 public:
893 BorderView(Borders borderFlags, BorderStyle style, float size = 2.0f, LayoutParams *layoutParams = nullptr)
InertView(layoutParams)894 : InertView(layoutParams), borderFlags_(borderFlags), style_(style), size_(size) {
895 }
896 void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
897 void Draw(UIContext &dc) override;
DescribeText()898 std::string DescribeText() const override { return ""; }
899
900 private:
901 Borders borderFlags_;
902 BorderStyle style_;
903 float size_;
904 };
905
906 class TextView : public InertView {
907 public:
908 TextView(const std::string &text, LayoutParams *layoutParams = 0)
InertView(layoutParams)909 : InertView(layoutParams), text_(text), textAlign_(0), textColor_(0xFFFFFFFF), small_(false), shadow_(false), focusable_(false), clip_(true) {}
910
911 TextView(const std::string &text, int textAlign, bool small, LayoutParams *layoutParams = 0)
InertView(layoutParams)912 : InertView(layoutParams), text_(text), textAlign_(textAlign), textColor_(0xFFFFFFFF), small_(small), shadow_(false), focusable_(false), clip_(true) {}
913
914 void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
915 void Draw(UIContext &dc) override;
916
SetText(const std::string & text)917 void SetText(const std::string &text) { text_ = text; }
GetText()918 const std::string &GetText() const { return text_; }
DescribeText()919 std::string DescribeText() const override { return GetText(); }
SetSmall(bool small)920 void SetSmall(bool small) { small_ = small; }
SetTextColor(uint32_t color)921 void SetTextColor(uint32_t color) { textColor_ = color; hasTextColor_ = true; }
SetShadow(bool shadow)922 void SetShadow(bool shadow) { shadow_ = shadow; }
SetFocusable(bool focusable)923 void SetFocusable(bool focusable) { focusable_ = focusable; }
SetClip(bool clip)924 void SetClip(bool clip) { clip_ = clip; }
SetBullet(bool bullet)925 void SetBullet(bool bullet) { bullet_ = bullet; }
926
CanBeFocused()927 bool CanBeFocused() const override { return focusable_; }
928
929 private:
930 std::string text_;
931 int textAlign_;
932 uint32_t textColor_;
933 bool hasTextColor_ = false;
934 bool small_;
935 bool shadow_;
936 bool focusable_;
937 bool clip_;
938 bool bullet_ = false;
939 };
940
941 class TextEdit : public View {
942 public:
943 TextEdit(const std::string &text, const std::string &title, const std::string &placeholderText, LayoutParams *layoutParams = nullptr);
SetText(const std::string & text)944 void SetText(const std::string &text) { text_ = text; scrollPos_ = 0; caret_ = (int)text_.size(); }
SetTextColor(uint32_t color)945 void SetTextColor(uint32_t color) { textColor_ = color; hasTextColor_ = true; }
GetText()946 const std::string &GetText() const { return text_; }
SetMaxLen(size_t maxLen)947 void SetMaxLen(size_t maxLen) { maxLen_ = maxLen; }
SetTextAlign(int align)948 void SetTextAlign(int align) { align_ = align; } // Only really useful for setting FLAG_DYNAMIC_ASCII
949
950 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
951 void Draw(UIContext &dc) override;
952 std::string DescribeText() const override;
953 bool Key(const KeyInput &key) override;
954 void Touch(const TouchInput &touch) override;
955
956 Event OnTextChange;
957 Event OnEnter;
958
959 private:
960 void InsertAtCaret(const char *text);
961
962 std::string text_;
963 std::string title_;
964 std::string undo_;
965 std::string placeholderText_;
966 uint32_t textColor_;
967 bool hasTextColor_ = false;
968 int caret_;
969 int scrollPos_ = 0;
970 size_t maxLen_;
971 bool ctrlDown_ = false; // TODO: Make some global mechanism for this.
972 int align_ = 0;
973 // TODO: Selections
974 };
975
976 enum ImageSizeMode {
977 IS_DEFAULT,
978 IS_FIXED,
979 IS_KEEP_ASPECT,
980 };
981
982 class ImageView : public InertView {
983 public:
984 ImageView(ImageID atlasImage, const std::string &text, ImageSizeMode sizeMode, LayoutParams *layoutParams = 0)
InertView(layoutParams)985 : InertView(layoutParams), text_(text), atlasImage_(atlasImage), sizeMode_(sizeMode) {}
986
987 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
988 void Draw(UIContext &dc) override;
DescribeText()989 std::string DescribeText() const override { return text_; }
990
991 private:
992 std::string text_;
993 ImageID atlasImage_;
994 ImageSizeMode sizeMode_;
995 };
996
997 class ProgressBar : public InertView {
998 public:
999 ProgressBar(LayoutParams *layoutParams = 0)
InertView(layoutParams)1000 : InertView(layoutParams), progress_(0.0) {}
1001
1002 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
1003 void Draw(UIContext &dc) override;
1004 std::string DescribeText() const override;
1005
SetProgress(float progress)1006 void SetProgress(float progress) {
1007 if (progress > 1.0f) {
1008 progress_ = 1.0f;
1009 } else if (progress < 0.0f) {
1010 progress_ = 0.0f;
1011 } else {
1012 progress_ = progress;
1013 }
1014 }
GetProgress()1015 float GetProgress() const { return progress_; }
1016
1017 private:
1018 float progress_;
1019 };
1020
1021 class Spinner : public InertView {
1022 public:
1023 Spinner(const ImageID *images, int numImages, LayoutParams *layoutParams = 0)
InertView(layoutParams)1024 : InertView(layoutParams), images_(images), numImages_(numImages) {
1025 }
1026
1027 void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
1028 void Draw(UIContext &dc) override;
DescribeText()1029 std::string DescribeText() const override { return ""; }
SetColor(uint32_t color)1030 void SetColor(uint32_t color) { color_ = color; }
1031
1032 private:
1033 const ImageID *images_;
1034 int numImages_;
1035 uint32_t color_ = 0xFFFFFFFF;
1036 };
1037
1038 void MeasureBySpec(Size sz, float contentWidth, MeasureSpec spec, float *measured);
1039
1040 bool IsDPadKey(const KeyInput &key);
1041 bool IsAcceptKey(const KeyInput &key);
1042 bool IsEscapeKey(const KeyInput &key);
1043 bool IsTabLeftKey(const KeyInput &key);
1044 bool IsTabRightKey(const KeyInput &key);
1045
1046 } // namespace
1047