HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
ToggleWidget.hpp
1// Copyright 2020 Pokitec
2// All rights reserved.
3
4#pragma once
5
6#include "TTauri/Widgets/Widget.hpp"
7#include "TTauri/Cells/TextCell.hpp"
8#include "TTauri/GUI/DrawContext.hpp"
9#include "TTauri/Foundation/observable.hpp"
10#include "TTauri/Text/format10.hpp"
11#include <memory>
12#include <string>
13#include <array>
14#include <optional>
15#include <future>
16
17namespace tt {
18
19template<typename ValueType=bool>
20class ToggleWidget : public Widget {
21protected:
22 static constexpr hires_utc_clock::duration animation_duration = 150ms;
23
24 float toggle_height;
25 float toggle_width;
26 float toggle_x;
27 float toggle_y;
28 float toggle_middle;
29 aarect toggle_rectangle;
30
31 float slider_x;
32 float slider_y;
33 float slider_move;
34 float slider_width;
35 float slider_height;
36
37 aarect label_rectangle;
38 mat::T label_translate;
40
41 ValueType trueValue;
42
43public:
46
47 template<typename V>
48 ToggleWidget(Window &window, Widget *parent, V &&value, ValueType trueValue) noexcept :
50 value(std::forward<V>(value)),
51 label()
52 {
53 [[maybe_unused]] ttlet value_cbid = this->value.add_callback([this](auto...){
54 forceRedraw = true;
55 });
56 [[maybe_unused]] ttlet label_cbid = this->label.add_callback([this](auto...) {
57 forceLayout = true;
58 });
59 }
60
62 }
63
64 ToggleWidget(const ToggleWidget &) = delete;
65 ToggleWidget &operator=(const ToggleWidget &) = delete;
66 ToggleWidget(ToggleWidget&&) = delete;
67 ToggleWidget &operator=(ToggleWidget &&) = delete;
68
69 void layout(hires_utc_clock::time_point displayTimePoint) noexcept override {
70 Widget::layout(displayTimePoint);
71
72 // The label is located to the right of the toggle.
73 ttlet label_x = Theme::smallWidth + Theme::margin;
74 label_rectangle = aarect{
75 label_x, 0.0f,
76 rectangle().width() - label_x, rectangle().height()
77 };
78
79 labelCell = std::make_unique<TextCell>(*label, theme->labelStyle);
80 setFixedHeight(std::max(labelCell->heightForWidth(label_rectangle.width()), Theme::smallHeight));
81
82 toggle_height = Theme::smallHeight;
83 toggle_width = Theme::smallWidth + 1.0f; // Expand horizontally due to rounded shape
84 toggle_x = -0.5f; // Expand horizontally due to rounded shape
85 toggle_y = rectangle().height() - toggle_height;
86 toggle_rectangle = aarect{toggle_x, toggle_y, toggle_width, toggle_height};
87 toggle_middle = toggle_y + toggle_height * 0.5f;
88
89 slider_x = 1.5f;
90 slider_y = toggle_y + 1.5f;
91 slider_width = toggle_height - 3.0f;
92 slider_height = toggle_height - 3.0f;
93 ttlet slider_move_width = Theme::smallWidth - (slider_x * 2.0f);
94 slider_move = slider_move_width - slider_width;
95 }
96
97 void draw(DrawContext const &drawContext, hires_utc_clock::time_point displayTimePoint) noexcept override {
98 // Prepare animation values.
99 ttlet animation_progress = value.animation_progress(animation_duration);
100 if (animation_progress < 1.0f) {
101 forceRedraw = true;
102 }
103
104 ttlet animated_value = to_float(value, animation_duration);
105
106 // Outside oval.
107 auto context = drawContext;
108 context.cornerShapes = vec{toggle_rectangle.height() * 0.5f};
109 context.drawBoxIncludeBorder(toggle_rectangle);
110
111 // Inside circle
112 ttlet slider_rectangle = aarect{
113 slider_x + slider_move * animated_value, slider_y,
114 slider_width, slider_height
115 };
116
117 if (value == trueValue) {
118 if (*enabled && window.active) {
119 context.color = theme->accentColor;
120 }
121 } else {
122 if (*enabled && window.active) {
123 context.color = hover ?
124 theme->borderColor(nestingLevel() + 1) :
125 theme->borderColor(nestingLevel());
126 }
127 }
128 std::swap(context.color, context.fillColor);
129 context.cornerShapes = vec{slider_rectangle.height() * 0.5f};
130 context.drawBoxIncludeBorder(slider_rectangle);
131
132 labelCell->draw(context, label_rectangle, Alignment::TopLeft, toggle_middle);
133 Widget::draw(drawContext, displayTimePoint);
134 }
135
136 void handleMouseEvent(MouseEvent const &event) noexcept override {
138
139 if (*enabled) {
140 if (
141 event.type == MouseEvent::Type::ButtonUp &&
142 event.cause.leftButton &&
143 rectangle().contains(event.position)
144 ) {
145 handleCommand("gui.activate"_ltag);
146 }
147 }
148 }
149
150 void handleCommand(string_ltag command) noexcept override {
151 if (!*enabled) {
152 return;
153 }
154
155 if (command == "gui.activate"_ltag) {
156 if (assign_and_compare(value, !*value)) {
157 forceRedraw = true;
158 }
159 }
160 Widget::handleCommand(command);
161 }
162
163 HitBox hitBoxTest(vec position) const noexcept override {
164 if (rectangle().contains(position)) {
165 return HitBox{this, elevation, *enabled ? HitBox::Type::Button : HitBox::Type::Default};
166 } else {
167 return HitBox{};
168 }
169 }
170
171 [[nodiscard]] bool acceptsFocus() const noexcept override {
172 return *enabled;
173 }
174
175};
176
177}
Class which represents an axis-aligned rectangle.
Definition aarect.hpp:13
Optimized translate matrix.
Definition mat.hpp:119
Definition observable.hpp:20
float animation_progress(duration animation_duration) const noexcept
The relative time since the start of the animation.
Definition observable.hpp:160
A 4D vector.
Definition vec.hpp:37
static tt_force_inline vec color(float r, float g, float b, float a=1.0f) noexcept
Create a color out of 3 to 4 values.
Definition vec.hpp:226
Draw context for drawing using the TTauri shaders.
Definition DrawContext.hpp:30
Definition HitBox.hpp:12
Definition MouseEvent.hpp:12
static constexpr float smallHeight
The height of smaller widget like labels, toggles, checkboxes and radio buttons.
Definition Theme.hpp:47
static constexpr float smallWidth
The width of smaller widget like labels, toggles, checkboxes and radio buttons.
Definition Theme.hpp:53
static constexpr float margin
Distance between widgets and between widgets and the border of the container.
Definition Theme.hpp:33
vec borderColor(ssize_t nestingLevel) const noexcept
Get border color of elements of widgets and child widgets.
Definition Theme.hpp:122
std::atomic< bool > active
Definition Window_base.hpp:86
Definition Window_vulkan_win32.hpp:15
Definition ToggleWidget.hpp:20
void handleCommand(string_ltag command) noexcept override
Handle command.
Definition ToggleWidget.hpp:150
void draw(DrawContext const &drawContext, hires_utc_clock::time_point displayTimePoint) noexcept override
Draw widget.
Definition ToggleWidget.hpp:97
bool acceptsFocus() const noexcept override
Check if the widget will accept keyboard focus.
Definition ToggleWidget.hpp:171
HitBox hitBoxTest(vec position) const noexcept override
Find the widget that is under the mouse cursor.
Definition ToggleWidget.hpp:163
void handleMouseEvent(MouseEvent const &event) noexcept override
Definition ToggleWidget.hpp:136
void layout(hires_utc_clock::time_point displayTimePoint) noexcept override
Layout the widget.
Definition ToggleWidget.hpp:69
Definition Widget.hpp:64
virtual void handleCommand(string_ltag command) noexcept
Handle command.
Widget(Window &window, Widget *parent, vec defaultExtent) noexcept
observable< bool > enabled
The widget is enabled.
Definition Widget.hpp:150
ssize_t nestingLevel() noexcept
Get nesting level used for selecting colors for the widget.
Definition Widget.hpp:314
virtual void handleMouseEvent(MouseEvent const &event) noexcept
Definition Widget.hpp:374
virtual void layout(hires_utc_clock::time_point displayTimePoint) noexcept
Layout the widget.
virtual void draw(DrawContext const &drawContext, hires_utc_clock::time_point displayTimePoint) noexcept
Draw widget.
aarect rectangle() const noexcept
Get the rectangle in local coordinates.
Definition Widget.hpp:273
T max(T... args)
T swap(T... args)