1.10
radial.h
1/*
2 * Copyright (C) 2018 Microchip Technology Inc. All rights reserved.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6#ifndef EGT_RADIAL_H
7#define EGT_RADIAL_H
8
9#include <algorithm>
10#include <egt/detail/math.h>
11#include <egt/detail/meta.h>
12#include <egt/flags.h>
13#include <egt/frame.h>
14#include <egt/painter.h>
15#include <egt/signal.h>
16#include <egt/text.h>
17#include <egt/value.h>
18#include <egt/widget.h>
19#include <memory>
20#include <string>
21#include <vector>
22
28namespace egt
29{
30inline namespace v1
31{
32namespace experimental
33{
34
45template<class T>
46class RadialType : public Widget
47{
48public:
49
61 enum class RadialFlag
62 {
67
72
77 };
78
81
85 explicit RadialType(const Rect& rect = {}) noexcept
86 : Widget(rect)
87 {
88 this->name("Radial" + std::to_string(m_widgetid));
89 this->grab_mouse(true);
90 }
91
96 explicit RadialType(Frame& parent, const Rect& rect = {}) noexcept
97 : RadialType<T>(rect)
98 {
99 parent.add(*this);
100 }
101
105 explicit RadialType(Serializer::Properties& props) noexcept
106 : RadialType(props, false)
107 {
108 }
109
110protected:
111
112 explicit RadialType(Serializer::Properties& props, bool is_derived) noexcept
113 : Widget(props, true)
114 {
115 this->name("Radial" + std::to_string(m_widgetid));
116 this->grab_mouse(true);
117
118 if (!is_derived)
119 deserialize_leaf(props);
120 }
121
122public:
123
133 Object::RegisterHandle add(const std::shared_ptr<RangeValue<T>>& range,
134 const Pattern& color = {},
135 DefaultDim width = 10,
136 RadialFlags flags = {})
137 {
138 // TODO: m_handle_counter can wrap, making the handle non-unique
139 auto handle = ++this->m_handle_counter;
140 this->m_values.emplace_back(range, color, width, flags, handle);
141
142 // when a value changes, damage
143 range->on_value_changed([this, flags]()
144 {
145 this->damage();
146 });
147
148 damage();
149
150 return handle;
151 }
152
159 {
160 auto i = std::find_if(this->m_values.begin(), this->m_values.end(),
161 [&handle](const auto & obj)
162 {
163 return obj->handle == handle;
164 });
165
166 if (i != this->m_values.end())
167 {
168 this->m_values.erase(i);
169 damage();
170 }
171 }
172
173 using Widget::color;
174
182 {
183 for (auto& value : this->m_values)
184 {
185 if (value.handle == handle)
186 {
187 value.color = color;
188 this->damage();
189 break;
190 }
191 }
192 }
193
197 EGT_NODISCARD const std::string& text() const { return m_text; }
198
202 void text(const std::string& text)
203 {
204 if (detail::change_if_diff<>(m_text, text))
205 this->damage();
206 }
207
208 void handle(Event& event) override
209 {
210 Widget::handle(event);
211
212 switch (event.id())
213 {
216 {
217 bool changed = false;
218 auto angle = this->touch_to_degrees(this->display_to_local(event.pointer().point));
219
220 for (auto& value : this->m_values)
221 {
222 if (value.flags.is_set(RadialFlag::input_value))
223 {
224 auto v = this->degrees_to_value(value.range->start(),
225 value.range->end(),
226 angle);
227 if (value.range->value(v))
228 changed = true;
229 }
230 }
231
232 if (changed)
233 this->on_user_input_changed.invoke();
234 }
235 default:
236 break;
237 }
238 }
239
240 void draw(Painter& painter, const Rect& rect) override
241 {
242 Drawer<RadialType<T>>::draw(*this, painter, rect);
243 }
244
246 static void default_draw(RadialType<T>& widget, Painter& painter, const Rect& rect)
247 {
249
251
252 const auto b = widget.content_area();
253 const auto c = b.center();
254 auto text = widget.text();
255 const auto smalldim = std::min(b.width(), b.height());
256
257 DefaultDim maxwidth = 0;
258 for (auto& value : widget.m_values)
259 {
260 if (value.width > maxwidth)
261 maxwidth = value.width;
262 }
263
264 for (auto& value : widget.m_values)
265 {
266 const auto radius = smalldim * 0.5f - (maxwidth * 0.5f);
267 const auto angle1 = detail::to_radians<float>(-90, widget.start_angle());
268 const auto angle2 = detail::to_radians<float>(-90,
269 widget.value_to_degrees(value.range->start(),
270 value.range->end(),
271 value.range->value()));
272
273 painter.set(value.color);
274 painter.line_width(value.width);
275 auto cr = painter.context().get();
276 if (value.flags.is_set(RadialFlag::rounded_cap))
277 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
278 else
279 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
280
281 painter.draw(Arc(c, radius, angle1, angle2));
282 painter.stroke();
283
284 if (value.flags.is_set(RadialFlag::text_value))
285 {
286 text = std::to_string(value.range->value());
287 }
288 }
289
290 if (!text.empty())
291 {
292 auto target = Rect(Size(smalldim, smalldim));
293 target.move_to_center(b.center());
294 auto font = TextWidget::scale_font(target.size(), text,
295 widget.font());
296
297 detail::draw_text(painter,
298 widget.content_area(),
299 text,
300 font,
301 {},
305 }
306 }
307
311 EGT_NODISCARD float touch_to_degrees(const Point& point) const
312 {
313 const auto b = this->content_area();
314 const Point c = b.center();
315 auto radians = c.angle_to<float>(point);
316 float angle = detail::to_degrees(radians);
317 if (angle < 0)
318 angle = angle + 360.0f;
319 return angle;
320 }
321
325 EGT_NODISCARD T value_to_degrees(T min, T max, T value) const
326 {
327 const auto n = (static_cast<float>(value) -
328 static_cast<float>(min)) /
329 (static_cast<float>(max) - static_cast<float>(min));
330 return n * 360.0f;
331 }
332
336 EGT_NODISCARD T degrees_to_value(T min, T max, T degrees) const
337 {
338 const auto n = degrees / 360.;
339 return (n * (max - min)) + min;
340 }
341
345 EGT_NODISCARD float start_angle() const
346 {
347 return m_start_angle;
348 }
349
353 void start_angle(float value)
354 {
355 m_start_angle = value;
356 }
357
358protected:
359
360 bool internal_drag() const override { return true; }
361
363 template<class T2>
364 struct ValueData
365 {
366 ValueData(std::shared_ptr<RangeValue<T2>> r,
367 Pattern c,
368 size_t w = 10,
369 // NOLINTNEXTLINE(modernize-pass-by-value)
370 RadialFlags f = {},
371 Object::RegisterHandle h = 0) noexcept
372 : range(std::move(r)),
373 color(std::move(c)),
374 width(w),
375 flags(f),
376 handle(h)
377 {}
378
379 std::shared_ptr<RangeValue<T2>> range;
380 Pattern color;
381 DefaultDim width{};
382 RadialFlags flags{};
383 Object::RegisterHandle handle{0};
384 };
385
387 std::string m_text;
388
390 std::vector<ValueData<T>> m_values;
391
394
396 float m_start_angle{0.f};
397};
398
405
412
413}
414
416template<>
417EGT_API const std::pair<experimental::Radial::RadialFlag, char const*> detail::EnumStrings<experimental::Radial::RadialFlag>::data[3];
418
419}
420}
421
422#endif
static constexpr HVBitField center
Definition widgetflags.h:297
Manager of the Drawable for each widget type.
Definition theme.h:104
Utility class for managing a set of flags with the ability to observe changes to the flags.
Definition flags.h:40
A Frame is a Widget that has children widgets.
Definition frame.h:45
uint64_t RegisterHandle
Handle type.
Definition object.h:61
EGT_NODISCARD const std::string & name() const
Get the name of the Object.
Definition object.h:42
Drawing interface for 2D graphics.
Definition painter.h:45
Painter & set(const Pattern &pattern)
Set the current color.
Painter & line_width(float width)
Set the current line width.
Painter & draw(const PointType< T, detail::Compatible::normal > &point)
Move to a point.
Definition painter.h:182
Painter & stroke()
EGT_NODISCARD shared_cairo_t context() const
Get the current underlying context the painter is using.
Definition painter.h:340
@ bg
General (Window, Frame) background color.
@ label_text
Label text color.
A Pattern which can store one or more colors at different offsets (steps) which can be used to create...
Definition pattern.h:55
EGT_NODISCARD T angle_to(const PointType &point) const noexcept
Return the angle in radians from this point to get to another.
Definition geometry.h:176
Manages a value in a range.
Definition value.h:88
EGT_NODISCARD constexpr PointType< Dim, DimCompat > center() const noexcept
Return the center point of the rectangle.
Definition geometry.h:681
std::list< std::tuple< std::string, std::string, Serializer::Attributes > > Properties
Definition serialize.h:47
Signal class used for defining a signal and dispatching events.
Definition signal.h:30
void invoke(Args... args)
Invoke all handlers with the specified args.
Definition signal.h:85
static Font scale_font(const Size &target, const std::string &text, const Font &font)
Given a Font, text, and a target Size, scale the font size so that the text will will fit and return ...
Base Widget class.
Definition widget.h:53
virtual void handle(Event &event)
Handle an event.
virtual void move(const Point &point)
Move the Widget to a new position.
WidgetId m_widgetid
Unique ID of this widget.
Definition widget.h:1800
EGT_NODISCARD DefaultDim width() const
Width of the widget's box().
Definition widget.h:755
virtual Point display_to_local(const DisplayPoint &p) const
Convert a display point to a local point.
Widget * parent()
Get a pointer to the parent Widget, or nullptr if none exists.
EGT_NODISCARD bool grab_mouse() const
Return the grab_mouse state of the widget.
EGT_NODISCARD const Widget::Flags & flags() const
Get a const ref of the flags.
Definition widget.h:1719
EGT_NODISCARD const Font & font() const
Get the widget Font.
virtual EGT_NODISCARD Rect content_area() const
Return the area that content is allowed to be positioned into.
EGT_NODISCARD const Point & point() const
Get the origin of the widget's box().
Definition widget.h:747
EGT_NODISCARD const Pattern & color(Palette::ColorId id) const
Get a Widget color.
void deserialize_leaf(Serializer::Properties &props)
Deserialize widget properties that require to call overridden methods.
virtual void damage()
Damage the box() of the widget and cause a redraw.
void draw_box(Painter &painter, Palette::ColorId bg, Palette::ColorId border) const
Helper function to draw this widget's box using the appropriate theme.
Radial widget that draws a series of RangleValues on a circle.
Definition radial.h:47
Object::RegisterHandle m_handle_counter
Counter used to generate unique handles for each handle registration.
Definition radial.h:393
RadialType(Serializer::Properties &props) noexcept
Definition radial.h:105
void draw(Painter &painter, const Rect &rect) override
Draw the widget.
Definition radial.h:240
EGT_NODISCARD T value_to_degrees(T min, T max, T value) const
Normalize a value to degrees.
Definition radial.h:325
RadialType(const Rect &rect={}) noexcept
Definition radial.h:85
RadialFlag
Radial flags.
Definition radial.h:62
@ rounded_cap
When drawing the value, use rounded ends.
@ input_value
This value is modified by user input.
@ text_value
Use the value for the center text of the widget.
Object::RegisterHandle add(const std::shared_ptr< RangeValue< T > > &range, const Pattern &color={}, DefaultDim width=10, RadialFlags flags={})
Add a range value to the radial.
Definition radial.h:133
EGT_NODISCARD const std::string & text() const
Get the current text of the radial.
Definition radial.h:197
RadialType(Serializer::Properties &props, bool is_derived) noexcept
Definition radial.h:112
bool internal_drag() const override
Definition radial.h:360
void remove(Object::RegisterHandle handle)
Remove a range value from the radial.
Definition radial.h:158
std::string m_text
Center text of the widget.
Definition radial.h:387
static void default_draw(RadialType< T > &widget, Painter &painter, const Rect &rect)
Default draw method for the widget.
Definition radial.h:246
void text(const std::string &text)
Set the center label text of the dial.
Definition radial.h:202
void handle(Event &event) override
Handle an event.
Definition radial.h:208
std::vector< ValueData< T > > m_values
The second value of the widget.
Definition radial.h:390
EGT_NODISCARD T degrees_to_value(T min, T max, T degrees) const
Normalize degrees to a value.
Definition radial.h:336
float m_start_angle
The starting angle in degrees for the min values.
Definition radial.h:396
egt::Flags< RadialFlag > RadialFlags
Radial flags.
Definition radial.h:80
void start_angle(float value)
Set the starting angle in degrees for the min values.
Definition radial.h:353
RadialType(Frame &parent, const Rect &rect={}) noexcept
Definition radial.h:96
EGT_NODISCARD float start_angle() const
The starting angle in degrees for the min values.
Definition radial.h:345
void color(Object::RegisterHandle handle, const Pattern &color)
Set the individual color of a range value.
Definition radial.h:181
Signal on_user_input_changed
Event signal.
Definition radial.h:57
EGT_NODISCARD float touch_to_degrees(const Point &point) const
Convert a touch point to degrees on the radial.
Definition radial.h:311
@ pointer_click
Pointer event.
@ pointer_drag
Pointer event.
SizeType< DefaultDim, detail::Compatible::normal > Size
Helper type alias.
Definition geometry.h:573
RectType< DefaultDim, detail::Compatible::normal > Rect
Helper type alias.
Definition geometry.h:1023
ArcType< DefaultDim > Arc
Helper type alias.
Definition geometry.h:1191
constexpr T bit(T n)
Utility to create a bit mask for the specified bit.
Definition meta.h:306
void ignoreparam(T &&)
Utility function to safely ignore a parameter to a function.
Definition meta.h:97
constexpr T to_degrees(T radians)
Convert from radians to degrees.
Definition math.h:72
int DefaultDim
Define the default dimension type used for geometry.
Definition geometry.h:34
EGT framework namespace.
Definition animation.h:24
A single event that has information about the event and state for the event.
Definition event.h:255
EGT_NODISCARD const EventId & id() const noexcept
Get the id of the event.
Definition event.h:284
Pointer & pointer()
Get the Pointer event data.
Definition event.h:309
DisplayPoint point
Mouse position in display coordinates.
Definition event.h:133
When using enum_to_string() and enum_from_string(), this type needs to be defined and specialized to ...
Definition enum.h:48