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