From da5d056257bb9c1b072b0cbd2f1c744ad3b72645 Mon Sep 17 00:00:00 2001 From: philmoz Date: Thu, 22 Dec 2022 19:47:26 +1100 Subject: [PATCH] feat(ui): Improvements to color picker (#2909) * Revised color picker: - add title to popup window - add Save and Cancel buttons - Source value only updated when 'Save'' button pressed - Add editor to select from system theme colors * Center 'Cancel' and 'Save' buttons, make them both the same size and fill available space. Reduce width of dialog box on portrait screens. --- radio/src/gui/colorlcd/color_editor.cpp | 147 ++++++++++++++++-------- radio/src/gui/colorlcd/color_editor.h | 66 +++++++++-- radio/src/gui/colorlcd/color_picker.cpp | 59 +++++++--- 3 files changed, 196 insertions(+), 76 deletions(-) diff --git a/radio/src/gui/colorlcd/color_editor.cpp b/radio/src/gui/colorlcd/color_editor.cpp index 146a0816c..77af96adf 100644 --- a/radio/src/gui/colorlcd/color_editor.cpp +++ b/radio/src/gui/colorlcd/color_editor.cpp @@ -29,15 +29,6 @@ constexpr int BAR_HEIGHT_OFFSET = BAR_TOP_MARGIN + 25; static const char *RGBChars[MAX_BARS] = { "R", "G", "B" }; static const char *HSVChars[MAX_BARS] = { "H", "S", "V" }; -// ColorTypes() -// A ColorType implements a three bar color editor. Currently we support HSV and RGB -enum HSV_BAR_TYPE -{ - HUE = 0, - SATURATION = 1, - BRIGHTNESS = 2 -}; - void ColorBar::pressing(lv_event_t* e) { lv_obj_t* target = lv_event_get_target(e); @@ -186,33 +177,68 @@ uint32_t ColorBar::screenToValue(int pos) return scaledValue; } -ColorType::ColorType(Window* parent, coord_t screenHeight) : - screenHeight(screenHeight) +BarColorType::BarColorType(Window* parent) { auto spacePerBar = (parent->width() / MAX_BARS); int leftPos = 0; + rect_t r; + r.y = BAR_TOP_MARGIN; + r.w = spacePerBar - BAR_MARGIN - 5; + r.h = parent->height() - BAR_HEIGHT_OFFSET; + for ( int i=0; i < MAX_BARS; i++){ - rect_t r; r.x = leftPos + BAR_MARGIN; - r.y = BAR_TOP_MARGIN; - r.w = spacePerBar - BAR_MARGIN - 5; - r.h = screenHeight; - + bars[i] = new ColorBar(parent, r); leftPos += spacePerBar; + + // bar labels + auto bar = bars[i]; + auto x = bar->left(); + auto y = bar->bottom(); + + barLabels[i] = create_bar_label(parent->getLvObj(), x, y + 9); + barValLabels[i] = create_bar_value_label(parent->getLvObj(), x + 10, y + 3); } } -ColorType::~ColorType() +BarColorType::~BarColorType() { for ( int i=0; i < MAX_BARS; i++) { bars[i]->deleteLater(); } }; +lv_obj_t* BarColorType::create_bar_label(lv_obj_t* parent, lv_coord_t x, lv_coord_t y) +{ + lv_obj_t* obj = lv_label_create(parent); + lv_obj_set_pos(obj, x, y); + lv_obj_set_style_text_color(obj, makeLvColor(COLOR_THEME_PRIMARY1), 0); + lv_obj_set_style_text_font(obj, getFont(FONT(XXS)), 0); + return obj; +} + +lv_obj_t* BarColorType::create_bar_value_label(lv_obj_t* parent, lv_coord_t x, lv_coord_t y) +{ + lv_obj_t* obj = lv_label_create(parent); + lv_obj_set_pos(obj, x, y); + lv_obj_set_style_text_color(obj, makeLvColor(COLOR_THEME_PRIMARY1), 0); + return obj; +} + +void BarColorType::setText() +{ + for (int i = 0; i < MAX_BARS; i++) { + auto bar = bars[i]; + lv_label_set_text_static(barLabels[i], getLabelChars()[i]); + lv_label_set_text_fmt(barValLabels[i], "%" PRIu32, bar->value); + bar->invalidate(); + } +} + HSVColorType::HSVColorType(Window* parent, uint32_t color) : - ColorType(parent, parent->height() - BAR_HEIGHT_OFFSET) + BarColorType(parent) { auto r = GET_RED(color), g = GET_GREEN(color), b = GET_BLUE(color); float values[MAX_BARS]; @@ -256,7 +282,7 @@ const char** HSVColorType::getLabelChars() } RGBColorType::RGBColorType(Window* parent, uint32_t color) : - ColorType(parent, parent->height() - BAR_HEIGHT_OFFSET) + BarColorType(parent) { auto r = GET_RED(color), g = GET_GREEN(color), b = GET_BLUE(color); float values[MAX_BARS]; @@ -285,21 +311,54 @@ const char** RGBColorType::getLabelChars() return RGBChars; } -static lv_obj_t* create_bar_label(lv_obj_t* parent, lv_coord_t x, lv_coord_t y) +void ThemeColorType::makeButton(Window* parent, uint32_t color) { - lv_obj_t* obj = lv_label_create(parent); - lv_obj_set_pos(obj, x, y); - lv_obj_set_style_text_color(obj, makeLvColor(COLOR_THEME_PRIMARY1), 0); - lv_obj_set_style_text_font(obj, getFont(FONT(XXS)), 0); - return obj; + auto btn = new TextButton(parent, rect_t{}, " "); + btn->setPressHandler([=]() { + auto cv = COLOR_VAL(color); + m_color = RGB(GET_RED(cv), GET_GREEN(cv), GET_BLUE(cv)); + lv_event_send(parent->getParent()->getParent()->getLvObj(), LV_EVENT_VALUE_CHANGED, nullptr); + return 0; + }); + btn->padAll(lv_dpx(7)); + lv_obj_set_style_bg_color(btn->getLvObj(), makeLvColor(color), LV_PART_MAIN); + lv_obj_add_style(btn->getLvObj(), &style, 0); } -static lv_obj_t* create_bar_value_label(lv_obj_t* parent, lv_coord_t x, lv_coord_t y) +void ThemeColorType::makeButtonsRow(Window* parent, uint32_t c1, uint32_t c2, uint32_t c3) { - lv_obj_t* obj = lv_label_create(parent); - lv_obj_set_pos(obj, x, y); - lv_obj_set_style_text_color(obj, makeLvColor(COLOR_THEME_PRIMARY1), 0); - return obj; + auto hbox = new FormGroup(parent, rect_t{}); + hbox->setFlexLayout(LV_FLEX_FLOW_ROW, lv_dpx(8)); + lv_obj_set_flex_align(hbox->getLvObj(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_AROUND); + + makeButton(hbox, c1); + makeButton(hbox, c2); + if (c3 != c2) + makeButton(hbox, c3); +} + +ThemeColorType::ThemeColorType(Window* parent, uint32_t color) : + ColorType() +{ + m_color = color; + + lv_style_init(&style); + lv_style_set_border_opa(&style, LV_OPA_100); + lv_style_set_border_width(&style, 2); + lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_GREY)); + + auto vbox = new FormGroup(parent, rect_t{}); + vbox->setFlexLayout(LV_FLEX_FLOW_COLUMN, lv_dpx(8)); + + makeButtonsRow(vbox, COLOR_THEME_PRIMARY1, COLOR_THEME_PRIMARY2, COLOR_THEME_PRIMARY3); + makeButtonsRow(vbox, COLOR_THEME_SECONDARY1, COLOR_THEME_SECONDARY2, COLOR_THEME_SECONDARY3); + makeButtonsRow(vbox, COLOR_THEME_FOCUS, COLOR_THEME_EDIT, COLOR_THEME_ACTIVE); + makeButtonsRow(vbox, COLOR_THEME_WARNING, COLOR_THEME_DISABLED, COLOR_THEME_DISABLED); +} + +uint32_t ThemeColorType::getRGB() +{ + return m_color; } ///////////////////////////////////////////////////////////////////////// @@ -312,19 +371,7 @@ ColorEditor::ColorEditor(Window *parent, const rect_t& rect, uint32_t color, _color(color) { _colorType = new HSVColorType(this, color); - - // bar labels - for (int i = 0; i < MAX_BARS; i++) { - auto bar = _colorType->bars[i]; - auto x = bar->left(); - auto y = bar->bottom(); - - barLabels[i] = create_bar_label(lvobj, x, y + 9); - lv_label_set_text_static(barLabels[i], _colorType->getLabelChars()[i]); - - barValLabels[i] = create_bar_value_label(lvobj, x + 10, y + 3); - lv_label_set_text_fmt(barValLabels[i], "%" PRIu32, bar->value); - } + _colorType->setText(); lv_obj_add_event_cb(lvobj, ColorEditor::value_changed, LV_EVENT_VALUE_CHANGED, nullptr); } @@ -332,27 +379,25 @@ ColorEditor::ColorEditor(Window *parent, const rect_t& rect, uint32_t color, void ColorEditor::setColorEditorType(COLOR_EDITOR_TYPE colorType) { if (_colorType != nullptr) { + clear(); delete _colorType; } if (colorType == RGB_COLOR_EDITOR) { _colorType = new RGBColorType(this, _color); setRGB(); - } else { + } else if (colorType == HSV_COLOR_EDITOR) { _colorType = new HSVColorType(this, _color); setHSV(); + } else { + _colorType = new ThemeColorType(this, _color); + setText(); } invalidate(); } void ColorEditor::setText() { - for (int i = 0; i < MAX_BARS; i++) { - auto bar = _colorType->bars[i]; - lv_label_set_text_static(barLabels[i], _colorType->getLabelChars()[i]); - lv_label_set_text_fmt(barValLabels[i], "%" PRIu32, bar->value); - bar->invalidate(); - } - + _colorType->setText(); if (_setValue != nullptr) _setValue(_color); } diff --git a/radio/src/gui/colorlcd/color_editor.h b/radio/src/gui/colorlcd/color_editor.h index 978a167dd..88e4bdc7e 100644 --- a/radio/src/gui/colorlcd/color_editor.h +++ b/radio/src/gui/colorlcd/color_editor.h @@ -47,34 +47,78 @@ struct ColorBar : public FormField { static void draw_end(lv_event_t* e); }; -struct ColorType +// ColorTypes() +// A ColorType implements an editor for selecting a color. Currently we support HSV, RGB and SYS (choose from system defined colors) +class ColorType { - ColorBar* bars[MAX_BARS]; - uint32_t screenHeight; +public: + ColorType() {} + virtual ~ColorType() {} - ColorType(Window* parent, coord_t screenHeight); - virtual ~ColorType(); + virtual void setText() {}; + virtual uint32_t getRGB() { return 0; }; - virtual uint32_t getRGB() = 0; - virtual const char** getLabelChars() = 0; +protected: }; -struct HSVColorType : public ColorType { +// Color editor with three bars for selecting color value. Base class for HSV and RGB color types. +class BarColorType : public ColorType +{ +public: + BarColorType(Window* parent); + ~BarColorType() override; + + void setText() override; + +protected: + ColorBar* bars[MAX_BARS]; + lv_obj_t* barLabels[MAX_BARS]; + lv_obj_t* barValLabels[MAX_BARS]; + + virtual const char** getLabelChars() { return nullptr; }; + + lv_obj_t* create_bar_label(lv_obj_t* parent, lv_coord_t x, lv_coord_t y); + lv_obj_t* create_bar_value_label(lv_obj_t* parent, lv_coord_t x, lv_coord_t y); +}; + +// Color editor for HSV color input +class HSVColorType : public BarColorType { +public: HSVColorType(Window* parent, uint32_t color); uint32_t getRGB() override; + +protected: const char** getLabelChars() override; }; -struct RGBColorType : public ColorType { +// Color editor for RGB color input +class RGBColorType : public BarColorType { +public: RGBColorType(Window* parent, uint32_t color); uint32_t getRGB() override; + +protected: const char** getLabelChars() override; }; +// Color editor that shows the system theme colors as buttons +class ThemeColorType : public ColorType { +public: + ThemeColorType(Window* parent, uint32_t color); + uint32_t getRGB() override; + +protected: + uint32_t m_color; + void makeButton(Window* parent, uint32_t color); + void makeButtonsRow(Window* parent, uint32_t c1, uint32_t c2, uint32_t c3); + lv_style_t style; +}; + enum COLOR_EDITOR_TYPE { RGB_COLOR_EDITOR = 0, - HSV_COLOR_EDITOR + HSV_COLOR_EDITOR, + THM_COLOR_EDITOR }; // the ColorEditor() control is a group of other controls @@ -92,8 +136,6 @@ class ColorEditor : public FormGroup protected: ColorType* _colorType = nullptr; - lv_obj_t* barLabels[MAX_BARS]; - lv_obj_t* barValLabels[MAX_BARS]; std::function _setValue; uint32_t _color; diff --git a/radio/src/gui/colorlcd/color_picker.cpp b/radio/src/gui/colorlcd/color_picker.cpp index 4909b5480..93e6cbc6e 100644 --- a/radio/src/gui/colorlcd/color_picker.cpp +++ b/radio/src/gui/colorlcd/color_picker.cpp @@ -40,12 +40,13 @@ static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, class ColorEditorPopup : public Dialog { - std::function _setValue; lv_obj_t* colorPad = nullptr; lv_obj_t* hexStr = nullptr; + uint32_t m_color; void updateColor(uint32_t c) { + m_color = c; auto r = GET_RED(c), g = GET_GREEN(c), b = GET_BLUE(c); auto lvcolor = lv_color_make(r, g, b); @@ -53,39 +54,33 @@ class ColorEditorPopup : public Dialog lv_label_set_text_fmt(hexStr, "%02X%02X%02X", (uint16_t)r, (uint16_t)g, (uint16_t)b); } - - void setValue(uint32_t c) - { - if (_setValue) _setValue(c); - updateColor(c); - } public: ColorEditorPopup(Window* parent, uint32_t color, std::function _setValue) : - Dialog(parent, std::string(), rect_t{}), - _setValue(std::move(_setValue)) + Dialog(parent, STR_COLOR_PICKER, rect_t{}) { lv_obj_set_style_bg_color(content->getLvObj(), makeLvColor(COLOR_THEME_SECONDARY3), 0); - setCloseWhenClickOutside(true); auto form = &content->form; FlexGridLayout grid(col_dsc, row_dsc); auto line = form->newLine(&grid); - rect_t r{ 0, 0, 6 * LV_DPI_DEF / 5, LV_DPI_DEF }; - auto cedit = new ColorEditor(line, r, color, [=](uint32_t c) { setValue(c); }); + rect_t r{ 0, 0, 7 * LV_DPI_DEF / 5, 7 * LV_DPI_DEF / 5 }; + auto cedit = new ColorEditor(line, r, color, [=](uint32_t c) { updateColor(c); }); lv_obj_set_style_grid_cell_x_align(cedit->getLvObj(), LV_GRID_ALIGN_CENTER, 0); auto vbox = new FormGroup(line, rect_t{}); lv_obj_set_style_grid_cell_x_align(vbox->getLvObj(), LV_GRID_ALIGN_CENTER, 0); vbox->setFlexLayout(LV_FLEX_FLOW_COLUMN, lv_dpx(8)); vbox->setWidth(r.w); + vbox->setHeight(r.h); auto hbox = new FormGroup(vbox, rect_t{}); hbox->setFlexLayout(LV_FLEX_FLOW_ROW, lv_dpx(8)); auto hbox_obj = hbox->getLvObj(); + lv_obj_set_flex_align(hbox_obj, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_SPACE_AROUND); colorPad = window_create(hbox_obj); lv_obj_set_style_bg_opa(colorPad, LV_OPA_100, LV_PART_MAIN); @@ -99,13 +94,16 @@ class ColorEditorPopup : public Dialog hbox = new FormGroup(vbox, rect_t{}); hbox->setFlexLayout(LV_FLEX_FLOW_ROW, lv_dpx(8)); + lv_obj_set_flex_align(hbox->getLvObj(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_AROUND); auto rgbBtn = new TextButton(hbox, rect_t{}, "RGB"); auto hsvBtn = new TextButton(hbox, rect_t{}, "HSV"); - + auto thmBtn = new TextButton(hbox, rect_t{}, "SYS"); + rgbBtn->setPressHandler([=]() { cedit->setColorEditorType(RGB_COLOR_EDITOR); hsvBtn->check(false); + thmBtn->check(false); return 1; }); rgbBtn->padAll(lv_dpx(8)); @@ -113,14 +111,49 @@ class ColorEditorPopup : public Dialog hsvBtn->setPressHandler([=]() { cedit->setColorEditorType(HSV_COLOR_EDITOR); rgbBtn->check(false); + thmBtn->check(false); return 1; }); hsvBtn->padAll(lv_dpx(8)); + thmBtn->setPressHandler([=]() { + cedit->setColorEditorType(THM_COLOR_EDITOR); + rgbBtn->check(false); + hsvBtn->check(false); + return 1; + }); + thmBtn->padAll(lv_dpx(8)); + // color editor defaults to HSV hsvBtn->check(true); + + hbox = new FormGroup(vbox, rect_t{}); + hbox->setFlexLayout(LV_FLEX_FLOW_ROW, lv_dpx(8)); + lv_obj_set_flex_align(hbox->getLvObj(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_END, LV_FLEX_ALIGN_SPACE_BETWEEN); + lv_obj_set_flex_grow(hbox->getLvObj(), 1); + auto cancelBtn = new TextButton(hbox, rect_t{}, STR_CANCEL, [=]() -> int8_t { + this->deleteLater(); + return 0; + }); + cancelBtn->padTop(lv_dpx(12)); + cancelBtn->padBottom(lv_dpx(12)); + lv_obj_set_flex_grow(cancelBtn->getLvObj(), 1); + + auto okBtn = new TextButton(hbox, rect_t{}, STR_SAVE, [=]() -> int8_t { + if (_setValue) _setValue(m_color); + this->deleteLater(); + return 0; + }); + okBtn->padTop(lv_dpx(12)); + okBtn->padBottom(lv_dpx(12)); + lv_obj_set_flex_grow(okBtn->getLvObj(), 1); + +#if LCD_W > LCD_H + content->setWidth(LCD_W * 0.9); +#else content->setWidth(LCD_W * 0.8); +#endif content->updateSize(); } };