12#ifdef USE_LVGL_COLOR_PICKER
17static_assert(LV_GRADIENT_MAX_STOPS >= 7,
"color_picker needs LV_GRADIENT_MAX_STOPS >= 7");
48 lv_color32_t c32 = lv_color_to_32(color, LV_OPA_COVER);
50 if (this->
obj !=
nullptr)
67 lv_obj_add_event_cb(outer,
self_size_cb, LV_EVENT_GET_SELF_SIZE,
nullptr);
68 lv_obj_add_event_cb(outer,
size_changed_cb, LV_EVENT_SIZE_CHANGED,
this);
71 int32_t left_width = 0;
72 lv_obj_t *left =
nullptr;
78#ifdef USE_LVGL_COLOR_PICKER_WHEEL
85 if (left !=
nullptr) {
86 lv_obj_set_style_align(left, LV_ALIGN_LEFT_MID, LV_PART_MAIN);
88 lv_obj_set_flag(left, LV_OBJ_FLAG_OVERFLOW_VISIBLE,
true);
93 auto *middle =
create_flex(outer, LV_FLEX_FLOW_COLUMN, LV_FLEX_ALIGN_START);
94 lv_obj_set_style_align(middle, LV_ALIGN_RIGHT_MID, LV_PART_MAIN);
95 lv_obj_set_style_pad_row(middle,
KNOB_OVERHANG, LV_PART_MAIN);
96 lv_obj_set_size(middle, lv_pct(100 - left_width), lv_pct(100));
102 auto *inner =
create_flex(middle, LV_FLEX_FLOW_ROW, LV_FLEX_ALIGN_START);
103 lv_obj_set_width(inner, lv_pct(100));
104 lv_obj_set_flex_grow(inner, 1);
108 auto *indicator_container =
create_flex(inner, LV_FLEX_FLOW_COLUMN, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER);
109 lv_obj_set_size(indicator_container, lv_pct(
CONTENT_WIDTH_PCT - channels_width), lv_pct(100));
110 lv_obj_set_style_pad_ver(indicator_container,
KNOB_OVERHANG, LV_PART_MAIN);
111 lv_obj_set_style_pad_row(indicator_container,
KNOB_OVERHANG, LV_PART_MAIN);
113 this->
color_text_ = lv_label_create(indicator_container);
115 lv_obj_set_style_border_color(this->
color_indicator_, lv_color_hex(0x808080), LV_PART_MAIN);
122 auto *rgb_container =
create_flex(inner, LV_FLEX_FLOW_ROW, LV_FLEX_ALIGN_SPACE_BETWEEN);
123 lv_obj_set_size(rgb_container, lv_pct(channels_width), lv_pct(100));
127 const lv_grad_dsc_t *grad;
129 const std::array<ChannelInfo, 3> channel_info{{
134 for (
const auto &channel : channel_info) {
137 auto *container = this->
create_slider_(rgb_container, channel.index, channel.name,
true, channel.grad, 255);
138 lv_obj_set_size(container, lv_pct(100 / channels), lv_pct(100));
149 if (this->
sliders_[index] ==
nullptr)
152 LV_EVENT_VALUE_CHANGED,
this);
166 std::array<lv_color_t, 7> hues{lv_color_hex(0xFF0000), lv_color_hex(0xFFFF00), lv_color_hex(0x00FF00),
167 lv_color_hex(0x00FFFF), lv_color_hex(0x0000FF), lv_color_hex(0xFF00FF),
168 lv_color_hex(0xFF0000)};
169#ifdef USE_LVGL_COLOR_PICKER_WHEEL
170 lv_grad_conical_init(&
wheel_hue_grad, lv_pct(50), lv_pct(50), 0, 360, LV_GRAD_EXTEND_PAD);
171 lv_grad_init_stops(&
wheel_hue_grad, hues.data(),
nullptr,
nullptr,
static_cast<int>(hues.size()));
173 const lv_color_t whites[] = {lv_color_white(), lv_color_white()};
174 const lv_opa_t white_opas[] = {LV_OPA_COVER, LV_OPA_TRANSP};
175 lv_grad_radial_init(&
wheel_white_grad, lv_pct(50), lv_pct(50), lv_pct(100), lv_pct(50), LV_GRAD_EXTEND_PAD);
179 std::reverse(hues.begin(), hues.end());
180 lv_grad_vertical_init(&
hue_bar);
181 lv_grad_init_stops(&
hue_bar, hues.data(),
nullptr,
nullptr,
static_cast<int>(hues.size()));
183 init_bar(&
red_bar, LV_GRAD_DIR_VER, lv_color_hex(0xFF0000), lv_color_white());
185 init_bar(&
blue_bar, LV_GRAD_DIR_VER, lv_color_hex(0x0000FF), lv_color_white());
188 lv_style_set_bg_opa(&
plain_box, LV_OPA_TRANSP);
189 lv_style_set_border_width(&
plain_box, 0);
198 static void init_bar(lv_grad_dsc_t *grad, lv_grad_dir_t dir, lv_color_t from, lv_color_t to) {
199 const lv_color_t colors[] = {from, to};
201 lv_grad_init_stops(grad, colors,
nullptr,
nullptr, 2);
206 lv_style_set_align(bar, LV_ALIGN_CENTER);
207 lv_style_set_bg_opa(bar, LV_OPA_COVER);
208 lv_style_set_pad_all(bar, 0);
209 lv_style_set_radius(bar, 0);
212 lv_style_set_width(bar, lv_pct(50));
213 lv_style_set_height(bar, lv_pct(90));
215 lv_style_set_width(bar, lv_pct(90));
219 lv_style_set_bg_color(knob, lv_color_white());
220 lv_style_set_border_width(knob, 1);
221 lv_style_set_outline_color(knob, lv_color_black());
222 lv_style_set_radius(knob, 0);
226 lv_style_set_pad_top(knob, vertical ? along : across);
227 lv_style_set_pad_bottom(knob, vertical ? along : across);
228 lv_style_set_pad_left(knob, vertical ? across : along);
229 lv_style_set_pad_right(knob, vertical ? across : along);
234 auto *
obj = lv_obj_create(parent);
236 lv_obj_set_flag(
obj, LV_OBJ_FLAG_SCROLLABLE,
false);
240 static lv_obj_t *
create_flex(lv_obj_t *parent, lv_flex_flow_t flow, lv_flex_align_t main_place,
241 lv_flex_align_t track_place = LV_FLEX_ALIGN_START) {
243 lv_obj_set_layout(
obj, LV_LAYOUT_FLEX);
244 lv_obj_set_flex_flow(
obj, flow);
245 lv_obj_set_flex_align(
obj, main_place, LV_FLEX_ALIGN_CENTER, track_place);
277 return this->
has_(index) ? lv_slider_get_value(this->
sliders_[index]) : fallback;
281 if (this->
has_(index))
282 lv_slider_set_value(this->
sliders_[index], value, LV_ANIM_OFF);
287 const lv_grad_dsc_t *grad, int32_t max) {
289 auto *container =
create_flex(parent, LV_FLEX_FLOW_COLUMN, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER);
293 lv_obj_add_event_cb(container,
knob_clearance_cb, LV_EVENT_STYLE_CHANGED,
nullptr);
295 lv_obj_set_style_pad_bottom(container,
KNOB_OVERHANG, LV_PART_MAIN);
296 lv_obj_set_style_pad_row(container,
KNOB_OVERHANG, LV_PART_MAIN);
298 lv_label_set_text(lv_label_create(container), name);
300 auto *slider = lv_slider_create(container);
303 lv_obj_set_style_bg_grad(slider, grad, LV_PART_MAIN);
304 lv_obj_set_style_bg_opa(slider, LV_OPA_TRANSP, LV_PART_INDICATOR);
307 lv_obj_set_flex_grow(slider, 1);
308 lv_slider_set_range(slider, 0, max);
309 lv_slider_set_mode(slider, LV_SLIDER_MODE_NORMAL);
314 lv_color_t text_color) {
315 auto *container = this->
create_slider_(parent, index, name,
false, grad, 100);
316 lv_obj_set_size(container, lv_pct(100), LV_SIZE_CONTENT);
320#ifdef USE_LVGL_COLOR_PICKER_WHEEL
322 static lv_obj_t *
create_disc(lv_obj_t *parent,
const lv_grad_dsc_t *grad) {
324 lv_obj_set_size(disc, lv_pct(100), lv_pct(100));
325 lv_obj_set_style_align(disc, LV_ALIGN_CENTER, LV_PART_MAIN);
326 lv_obj_set_style_radius(disc, LV_RADIUS_CIRCLE, LV_PART_MAIN);
327 lv_obj_set_style_bg_opa(disc, LV_OPA_COVER, LV_PART_MAIN);
328 lv_obj_set_style_bg_grad(disc, grad, LV_PART_MAIN);
329 lv_obj_set_flag(disc, LV_OBJ_FLAG_SCROLLABLE,
false);
339 lv_obj_set_flag(overlay, LV_OBJ_FLAG_CLICKABLE,
false);
343 lv_obj_set_style_align(this->
wheel_handle_, LV_ALIGN_CENTER, LV_PART_MAIN);
344 lv_obj_set_style_radius(this->
wheel_handle_, LV_RADIUS_CIRCLE, LV_PART_MAIN);
345 lv_obj_set_style_bg_opa(this->
wheel_handle_, LV_OPA_TRANSP, LV_PART_MAIN);
346 lv_obj_set_style_border_width(this->
wheel_handle_, 2, LV_PART_MAIN);
347 lv_obj_set_style_border_color(this->
wheel_handle_, lv_color_white(), LV_PART_MAIN);
348 lv_obj_set_flag(this->
wheel_handle_, LV_OBJ_FLAG_CLICKABLE,
false);
349 lv_obj_set_flag(this->
wheel_handle_, LV_OBJ_FLAG_SCROLLABLE,
false);
361 auto *slider = this->
sliders_[index];
362 lv_obj_set_flag(slider, LV_OBJ_FLAG_OVERFLOW_VISIBLE,
true);
363 auto *label = lv_label_create(slider);
365 lv_obj_set_style_align(label, LV_ALIGN_CENTER, LV_PART_MAIN);
366 lv_obj_set_style_text_color(label, color, LV_PART_MAIN);
371 const auto *font = lv_obj_get_style_text_font(container, LV_PART_MAIN);
372 auto clearance =
static_cast<int32_t
>(lv_font_get_line_height(font)) / 2;
373 if (clearance == lv_obj_get_style_pad_bottom(container, LV_PART_MAIN))
375 lv_obj_set_style_pad_bottom(container, clearance, LV_PART_MAIN);
376 lv_obj_set_style_pad_row(container, clearance, LV_PART_MAIN);
386#ifdef USE_LVGL_COLOR_PICKER_WHEEL
395 auto thickness = lv_obj_get_content_width(this->
obj) * HUE_WIDTH_PCT / 200;
397 if (this->
has_(index) && lv_obj_get_style_height(this->
sliders_[index], LV_PART_MAIN) != thickness)
398 lv_obj_set_style_height(this->
sliders_[index], thickness, LV_PART_MAIN);
405 auto *
obj = lv_event_get_current_target_obj(event);
406 if (lv_obj_get_style_width(
obj, LV_PART_MAIN) != LV_SIZE_CONTENT)
408 const auto *font = lv_obj_get_style_text_font(
obj, LV_PART_MAIN);
410 auto *point =
static_cast<lv_point_t *
>(lv_event_get_param(event));
411 point->x = self_size;
412 point->y = self_size;
418 snprintf(buf,
sizeof(buf),
"#%02X%02X%02X",
static_cast<unsigned>(this->
state_.
r),
419 static_cast<unsigned>(this->state_.g),
static_cast<unsigned>(this->state_.b));
424 if (!this->
has_(index))
426 grad->stops[1].color = color;
427 lv_obj_invalidate(this->
sliders_[index]);
432 if (this->
values_[index] ==
nullptr)
436 snprintf(buf,
sizeof(buf),
"%d",
static_cast<int>(lv_slider_get_value(this->
sliders_[index])));
437 lv_label_set_text(this->
values_[index], buf);
442 if (this->
has_(index))
443 lv_obj_set_style_bg_color(this->
sliders_[index], color, LV_PART_KNOB);
459#ifdef USE_LVGL_COLOR_PICKER_WHEEL
462 lv_obj_get_coords(this->
wheel_, area);
463 return static_cast<double>(area->x2 - area->x1) / 2.0;
473 auto angle_rad =
static_cast<double>(this->
hue_) * (std::numbers::pi / 180.0);
474 auto distance = (
static_cast<double>(this->
saturation_) / 100.0) * radius;
475 lv_obj_set_style_translate_x(this->
wheel_handle_,
static_cast<int32_t
>(std::lround(distance * std::cos(angle_rad))),
477 lv_obj_set_style_translate_y(this->
wheel_handle_,
static_cast<int32_t
>(std::lround(distance * std::sin(angle_rad))),
483 lv_indev_t *indev = lv_indev_active();
484 if (indev ==
nullptr)
487 lv_indev_get_point(indev, &point);
492 auto dx = point.x - (area.x1 + area.x2) / 2.0;
493 auto dy = point.y - (area.y1 + area.y2) / 2.0;
494 auto angle = std::atan2(dy, dx) * (180.0 / std::numbers::pi);
497 this->
hue_ =
static_cast<uint16_t
>(std::lround(angle)) % 360;
498 auto distance = std::sqrt(dx * dx + dy * dy);
499 this->
saturation_ =
static_cast<uint8_t
>(std::lround(std::min(100.0, distance * 100.0 / radius)));
503 lv_obj_send_event(this->
wheel_, LV_EVENT_VALUE_CHANGED,
nullptr);
510 for (
uint32_t i = 0; i < lv_obj_get_child_count(
obj); i++) {
511 auto *child = lv_obj_get_child(
obj, i);
512 lv_obj_set_flag(child, LV_OBJ_FLAG_EVENT_BUBBLE,
true);
519 auto full_hue = lv_color_hsv_to_rgb(this->
hue_, 100, 100);
525#ifdef USE_LVGL_COLOR_PICKER_WHEEL
534 this->
state_ =
Color(c32.red, c32.green, c32.blue);
550 static_cast<uint8_t
>(this->slider_value_(
SLIDER_GREEN, this->state_.g)),
551 static_cast<uint8_t
>(this->slider_value_(
SLIDER_BLUE, this->state_.b)));
552 auto hsv = lv_color_rgb_to_hsv(this->
state_.
r, this->state_.g, this->state_.b);
582 lv_obj_invalidate(this->
obj);
591#ifdef USE_LVGL_COLOR_PICKER_WHEEL
606 std::array<lv_obj_t *, SLIDER_COUNT>
values_{};
616#ifdef USE_LVGL_COLOR_PICKER_WHEEL
LvColorPickerType(uint8_t sliders, bool use_wheel)
void update_hue_bar_(SliderIndex index, lv_grad_dsc_t *grad, lv_color_t color)
bool can_change_saturation_() const
static constexpr int KNOB_TRIM
static void init_shared()
static void bubble_events(lv_obj_t *obj)
static constexpr int32_t WHEEL_WIDTH_PCT
bool has_(SliderIndex index) const
std::array< lv_obj_t *, SLIDER_COUNT > sliders_
void update_values_() const
int32_t slider_value_(SliderIndex index, int32_t fallback) const
static lv_style_t slider_horz_knob
lv_obj_t * create_wheel_(lv_obj_t *parent)
void update_from_wheel_touch_()
lv_grad_dsc_t saturation_bar_
static constexpr int32_t WHEEL_HANDLE_SIZE
lv_obj_t * create_slider_(lv_obj_t *parent, SliderIndex index, const char *name, bool vertical, const lv_grad_dsc_t *grad, int32_t max)
static lv_obj_t * create_disc(lv_obj_t *parent, const lv_grad_dsc_t *grad)
static lv_grad_dsc_t wheel_hue_grad
static void hsv_changed_cb(lv_event_t *event)
static constexpr int SELF_SIZE_FONT_MULTIPLE
std::array< lv_obj_t *, SLIDER_COUNT > values_
lv_obj_t * get_slider(size_t index) const
static lv_grad_dsc_t hue_bar
static lv_obj_t * create_box(lv_obj_t *parent)
static constexpr int32_t CONTENT_WIDTH_PCT
static void knob_clearance_cb(lv_event_t *event)
double wheel_radius_(lv_area_t *area) const
static lv_obj_t * create_flex(lv_obj_t *parent, lv_flex_flow_t flow, lv_flex_align_t main_place, lv_flex_align_t track_place=LV_FLEX_ALIGN_START)
static constexpr int32_t CHANNEL_WIDTH_PCT
void tint_knob_(SliderIndex index, lv_color_t color) const
static void wheel_touch_cb(lv_event_t *event)
static constexpr int32_t HUE_WIDTH_PCT
bool enabled_(SliderIndex index) const
void set_color(lv_color_t color)
void add_value_label_(SliderIndex index, lv_color_t color)
static void update_knob_clearance(lv_obj_t *container)
void update_knobs_(lv_color_t full_hue) const
static lv_grad_dsc_t wheel_white_grad
void set_tint_knobs(bool tint_knobs)
static lv_grad_dsc_t blue_bar
static void size_changed_cb(lv_event_t *event)
static lv_grad_dsc_t green_bar
static LvColorPickerType * from_event(lv_event_t *event)
static lv_grad_dsc_t red_bar
void set_slider_value_(SliderIndex index, int32_t value)
static void init_bar(lv_grad_dsc_t *grad, lv_grad_dir_t dir, lv_color_t from, lv_color_t to)
void set_obj(lv_obj_t *outer) override
static void wheel_size_cb(lv_event_t *event)
static lv_style_t slider_vert
static lv_style_t plain_box
void create_row_slider_(lv_obj_t *parent, SliderIndex index, const char *name, const lv_grad_dsc_t *grad, lv_color_t text_color)
lv_grad_dsc_t brightness_bar_
void update_wheel_handle_()
static lv_style_t slider_horz
static void rgb_changed_cb(lv_event_t *event)
static void init_slider_styles(lv_style_t *bar, lv_style_t *knob, bool vertical)
bool can_change_brightness_() const
const uint8_t sliders_mask_
void update_text_() const
void update_bar_thickness_()
lv_obj_t * color_indicator_
static void self_size_cb(lv_event_t *event)
static lv_style_t slider_vert_knob
static constexpr int KNOB_OVERHANG
virtual void set_obj(lv_obj_t *lv_obj)