HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
toggle_widget.hpp
1// Copyright Take Vos 2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "abstract_bool_toggle_button_widget.hpp"
8#include "../label.hpp"
9#include "../stencils/label_stencil.hpp"
10#include "../GUI/draw_context.hpp"
11#include "../observable.hpp"
12#include <memory>
13#include <string>
14#include <array>
15#include <optional>
16#include <future>
17
18namespace tt {
19
21public:
23
24 observable<label> on_label;
25 observable<label> off_label;
26
27 template<typename Value = observable<bool>>
31 Value &&value = observable<bool>{}) noexcept :
32 super(window, parent, std::forward<Value>(value))
33 {
34 _on_label_callback = this->on_label.subscribe([this](auto...) {
35 _request_reconstrain = true;
36 });
37 _off_label_callback = this->off_label.subscribe([this](auto...) {
38 _request_reconstrain = true;
39 });
40 }
41
43
44 [[nodiscard]] bool update_constraints(hires_utc_clock::time_point display_time_point, bool need_reconstrain) noexcept override
45 {
46 tt_axiom(gui_system_mutex.recurse_lock_count());
47
48 if (super::update_constraints(display_time_point, need_reconstrain)) {
49 _on_label_stencil = stencil::make_unique(alignment::top_left, *on_label, theme::global->labelStyle);
50 _off_label_stencil = stencil::make_unique(alignment::top_left, *off_label, theme::global->labelStyle);
51
52 ttlet minimumHeight = std::max(
53 {_on_label_stencil->preferred_extent().height(),
54 _off_label_stencil->preferred_extent().height(),
55 theme::global->smallSize});
56
57 ttlet minimumWidth =
58 std::max({_on_label_stencil->preferred_extent().width(), _off_label_stencil->preferred_extent().width()}) +
59 theme::global->smallSize * 2.0f + theme::global->margin;
60
61 _preferred_size = interval_extent2::make_minimum(minimumWidth, minimumHeight);
62
63 return true;
64 } else {
65 return false;
66 }
67 }
68
69 [[nodiscard]] void update_layout(hires_utc_clock::time_point display_time_point, bool need_layout) noexcept override
70 {
71 tt_axiom(gui_system_mutex.recurse_lock_count());
72
73 need_layout |= std::exchange(_request_relayout, false);
74 if (need_layout) {
75 _rail_rectangle = aarectangle{
76 -0.5f, // Expand horizontally due to rounded shape
77 std::round(base_line() - theme::global->smallSize * 0.5f),
78 theme::global->smallSize * 2.0f + 1.0f, // Expand horizontally due to rounded shape
79 theme::global->smallSize};
80
81 ttlet labelX = theme::global->smallSize * 2.0f + theme::global->margin;
82 _label_rectangle = aarectangle{labelX, 0.0f, rectangle().width() - labelX, rectangle().height()};
83 _on_label_stencil->set_layout_parameters(_label_rectangle, base_line());
84 _off_label_stencil->set_layout_parameters(_label_rectangle, base_line());
85
86 _slider_rectangle =
87 shrink(aarectangle{0.0f, _rail_rectangle.bottom(), _rail_rectangle.height(), _rail_rectangle.height()}, 2.5f);
88
89 ttlet sliderMoveWidth = theme::global->smallSize * 2.0f - (_slider_rectangle.left() * 2.0f);
90 _slider_move_range = sliderMoveWidth - _slider_rectangle.width();
91 }
92
93 super::update_layout(display_time_point, need_layout);
94 }
95
96 void draw(draw_context context, hires_utc_clock::time_point display_time_point) noexcept override
97 {
98 tt_axiom(gui_system_mutex.recurse_lock_count());
99
100 if (overlaps(context, _clipping_rectangle)) {
101 draw_rail(context);
102 draw_slider(context);
103 draw_label(context);
104 }
105
106 super::draw(std::move(context), display_time_point);
107 }
108
109private:
110 static constexpr hires_utc_clock::duration _animation_duration = 150ms;
111
112 aarectangle _rail_rectangle;
113
114 aarectangle _slider_rectangle;
115 float _slider_move_range;
116
117 aarectangle _label_rectangle;
118
119 std::unique_ptr<label_stencil> _on_label_stencil;
120 std::unique_ptr<label_stencil> _off_label_stencil;
121
122 decltype(value)::callback_ptr_type _value_callback;
123 decltype(on_label)::callback_ptr_type _on_label_callback;
124 decltype(off_label)::callback_ptr_type _off_label_callback;
125
126 void draw_rail(draw_context draw_context) noexcept
127 {
128 tt_axiom(gui_system_mutex.recurse_lock_count());
129
131 _rail_rectangle, background_color(), focus_color(), corner_shapes{_rail_rectangle.height() * 0.5f});
132 }
133
134 void draw_slider(draw_context draw_context) noexcept
135 {
136 tt_axiom(gui_system_mutex.recurse_lock_count());
137
138 // Prepare animation values.
139 ttlet animationProgress = value.animation_progress(_animation_duration);
140 if (animationProgress < 1.0f) {
141 request_redraw();
142 }
143
144 ttlet animatedValue = to_float(value, _animation_duration);
145 ttlet positionedSliderRectangle = translate3{_slider_move_range * animatedValue, 0.0f, 0.1f} * _slider_rectangle;
146
147 draw_context.draw_box(
148 positionedSliderRectangle, accent_color(), corner_shapes{positionedSliderRectangle.height() * 0.5f});
149 }
150
151 void draw_label(draw_context draw_context) noexcept
152 {
153 tt_axiom(gui_system_mutex.recurse_lock_count());
154
155 ttlet &label_stencil = *value ? _on_label_stencil : _off_label_stencil;
156 label_stencil->draw(draw_context, label_color());
157 }
158};
159
160} // namespace tt
Class which represents an axis-aligned rectangle.
Definition axis_aligned_rectangle.hpp:18
Definition corner_shapes.hpp:9
Draw context for drawing using the TTauri shaders.
Definition draw_context.hpp:33
void draw_box_with_border_inside(rectangle rectangle, color fill_color, color line_color, float line_width=1.0, tt::corner_shapes corner_shapes=tt::corner_shapes{}) const noexcept
Draw an axis aligned box This function will shrink to include the size of the border inside the given...
Definition draw_context.hpp:183
Definition gui_window.hpp:37
Definition observable.hpp:20
float animation_progress(duration animation_duration) const noexcept
The relative time since the start of the animation.
Definition observable.hpp:108
int recurse_lock_count() const noexcept
This function should be used in tt_axiom() to check if the lock is held by current thread.
Definition unfair_recursive_mutex.hpp:60
An abstract boolean toggle button widget.
Definition abstract_bool_toggle_button_widget.hpp:14
Definition toggle_widget.hpp:20
bool update_constraints(hires_utc_clock::time_point display_time_point, bool need_reconstrain) noexcept override
Update the constraints of the widget.
Definition toggle_widget.hpp:44
void update_layout(hires_utc_clock::time_point display_time_point, bool need_layout) noexcept override
Update the internal layout of the widget.
Definition toggle_widget.hpp:69
void draw(draw_context context, hires_utc_clock::time_point display_time_point) noexcept override
Draw the widget.
Definition toggle_widget.hpp:96
float margin() const noexcept
Get the margin around the Widget.
Definition widget.hpp:128
virtual void update_layout(hires_utc_clock::time_point display_time_point, bool need_layout) noexcept
Update the internal layout of the widget.
aarectangle rectangle() const noexcept
Get the rectangle in local coordinates.
Definition widget.hpp:342
gui_window & window
Convenient reference to the Window.
Definition widget.hpp:101
virtual float base_line() const noexcept
Return the base-line where the text should be located.
Definition widget.hpp:351
virtual void draw(draw_context context, hires_utc_clock::time_point display_time_point) noexcept
Draw the widget.
Definition widget.hpp:462
virtual bool update_constraints(hires_utc_clock::time_point display_time_point, bool need_reconstrain) noexcept
Update the constraints of the widget.
abstract_container_widget const & parent() const noexcept
Get a reference to the parent.
T max(T... args)
T move(T... args)
T round(T... args)