feat(color): Better color picker (#2217)

* Better color picker

* Portrait layout

* Remove obsolete ColorEdit

* Fix label clipping

* chore: Bump libopenui
This commit is contained in:
Raphael Coeffic
2022-08-28 19:01:55 +10:00
committed by GitHub
parent e038e4d214
commit cd211cba59
12 changed files with 212 additions and 349 deletions
+1 -1
View File
@@ -27,8 +27,8 @@ set(GUI_SRC
lz4_bitmaps.cpp
theme.cpp
theme_manager.cpp
color_editor_popup.cpp
color_editor.cpp
color_picker.cpp
color_list.cpp
preview_window.cpp
file_browser.cpp
+2 -2
View File
@@ -293,9 +293,9 @@ static lv_obj_t* create_bar_value_label(lv_obj_t* parent, lv_coord_t x, lv_coord
/////////////////////////////////////////////////////////////////////////
////// ColorEditor Base class
/////////////////////////////////////////////////////////////////////////
ColorEditor::ColorEditor(FormGroup *window, const rect_t rect, uint32_t color,
ColorEditor::ColorEditor(Window *parent, const rect_t& rect, uint32_t color,
std::function<void (uint32_t rgb)> setValue) :
FormGroup(window, rect),
FormGroup(parent, rect),
_setValue(std::move(setValue)),
_color(color)
{
+1 -1
View File
@@ -81,7 +81,7 @@ enum COLOR_EDITOR_TYPE
class ColorEditor : public FormGroup
{
public:
ColorEditor(FormGroup* window, const rect_t rect, uint32_t color,
ColorEditor(Window* parent, const rect_t& rect, uint32_t color,
std::function<void(uint32_t rgb)> setValue = nullptr);
#if defined(DEBUG_WINDOWS)
@@ -1,234 +0,0 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "color_editor_popup.h"
#define SET_DIRTY() setDirty()
////////////////////////////////////////////////////////////////////////////////////////////////
///// ColorEditorContent
////////////////////////////////////////////////////////////////////////////////////////////////
constexpr int GRID_TOP_MARGIN = 60;
constexpr int HUE_BAR_TOP_MARGIN = 35;
constexpr int HUE_BAR_HEIGHT = 20;
constexpr int COLOR_BOX_TOP = 130;
constexpr int COLOR_BOX_WIDTH = 90;
constexpr int COLOR_BOX_HEIGHT = 32;
#if (LCD_W > LCD_H)
constexpr int COLOR_BOX_LEFT = 240;
constexpr int SCREEN_LEFT_MARGIN = 10;
constexpr rect_t contentRect = { 50, 50, 360 + 20, 170 };
#else
constexpr int COLOR_BOX_LEFT = 210;
constexpr rect_t contentRect = { 5, 50, LCD_W - 10, 170 };
constexpr int SCREEN_LEFT_MARGIN = 0;
#endif
ColorEditorContent::ColorEditorContent(ModalWindow *window, const rect_t rect, uint32_t color, std::function<void (uint32_t rgb)> setValue) :
ModalWindowContent(window, rect),
setValue(std::move(setValue))
{
r = GET_RED(color); g = GET_GREEN(color); b = GET_BLUE(color);
float h, s, v;
RGBtoHSV(r, g, b, h, s, v);
hue = (int) h;
this->s = 100 * s;
this->v = 100 * v;
rect_t rText = {COLOR_BOX_LEFT, 60, 50, 20};
rect_t rNumber = {COLOR_BOX_LEFT + 60, 60, 40, 20};
new StaticText(this, rText, STR_RED, 0, COLOR_THEME_PRIMARY1);
rText.y += 23;
rEdit = new NumberEdit(this, rNumber, 0, 255, [=] () { return r; }, [=] (int value) { r = value; });
rNumber.y += 23;
new StaticText(this, rText, STR_GREEN, 0, COLOR_THEME_PRIMARY1);
rText.y += 23;
gEdit = new NumberEdit(this, rNumber, 0, 255, [=] () { return g; }, [=] (int value) { g = value; });
rNumber.y += 23;
new StaticText(this, rText, STR_BLUE, 0, COLOR_THEME_PRIMARY1);
bEdit = new NumberEdit(this, rNumber, 0, 255, [=] () { return b; }, [=] (int value) { b = value; });
invalidate();
}
void ColorEditorContent::setRGB()
{
int rgb = HSVtoRGB(hue, s, v);
r = GET_RED(rgb);
g = GET_GREEN(rgb);
b = GET_BLUE(rgb);
rEdit->setValue(r);
gEdit->setValue(g);
bEdit->setValue(b);
invalidate();
if (setValue != nullptr)
setValue(rgb);
}
#if defined(HARDWARE_TOUCH)
bool ColorEditorContent::onTouchEnd(coord_t x, coord_t y)
{
sliding = false;
colorPicking = false;
invalidate();
return true;
}
bool ColorEditorContent::onTouchStart(coord_t x, coord_t y)
{
if (!sliding &&
(y > HUE_BAR_TOP_MARGIN && y < HUE_BAR_TOP_MARGIN + HUE_BAR_HEIGHT)) {
hue = x - SCREEN_LEFT_MARGIN;
hue = max(hue, 0);
hue = min(hue, MAX_HUE);
sliding = true;
invalidate();
} else if (x >= SCREEN_LEFT_MARGIN &&
x <= SCREEN_LEFT_MARGIN + (MAX_SATURATION * 2) &&
y >= GRID_TOP_MARGIN && y <= GRID_TOP_MARGIN + MAX_BRIGHTNESS) {
colorPicking = true;
v = min(MAX_BRIGHTNESS - (y - GRID_TOP_MARGIN), MAX_BRIGHTNESS);
s = min((x - SCREEN_LEFT_MARGIN) / 2, MAX_SATURATION);
setRGB();
}
return true;
}
#endif
void ColorEditorContent::onEvent(event_t event)
{
#if defined(HARDWARE_KEYS)
int oldSliderVal = hue;
if (event == EVT_ROTARY_LEFT) {
hue -= 2;
killAllEvents();
}
else if (event == EVT_ROTARY_RIGHT) {
hue += 2;
killAllEvents();
}
hue = max(hue, 0);
hue = min(hue, MAX_HUE);
if (hue != oldSliderVal) {
invalidate();
}
#endif
}
void ColorEditorContent::drawHueBar(BitmapBuffer *dc)
{
for (int i = 0; i < MAX_HUE; i++) {
auto rgb = HSVtoRGB(i, MAX_SATURATION, MAX_BRIGHTNESS);
auto r = GET_RED(rgb);
auto g = GET_GREEN(rgb);
auto b = GET_BLUE(rgb);
dc->drawSolidVerticalLine(i + SCREEN_LEFT_MARGIN, HUE_BAR_TOP_MARGIN,
HUE_BAR_HEIGHT, COLOR2FLAGS(RGB(r, g, b)));
}
dc->drawFilledCircle(hue + SCREEN_LEFT_MARGIN,
HUE_BAR_TOP_MARGIN + (HUE_BAR_HEIGHT / 2), 5,
COLOR2FLAGS(WHITE));
if (sliding) {
dc->drawText(hue + (SCREEN_LEFT_MARGIN / 2), 25,
std::to_string(hue).c_str(), FONT(XXS) | COLOR2FLAGS(WHITE));
}
}
void ColorEditorContent::drawGrid(BitmapBuffer *dc)
{
for (int s = 0; s <= MAX_SATURATION; s++) {
for (int v = MAX_BRIGHTNESS; v >= 0; v--) {
uint32_t rgbVal = HSVtoRGB(hue, s, v);
dc->drawPixel((s * 2) + SCREEN_LEFT_MARGIN,
((MAX_BRIGHTNESS - v)) + GRID_TOP_MARGIN, rgbVal);
dc->drawPixel((s * 2) + SCREEN_LEFT_MARGIN + 1,
((MAX_BRIGHTNESS - v)) + GRID_TOP_MARGIN, rgbVal);
}
}
dc->drawFilledCircle((this->s * 2) + SCREEN_LEFT_MARGIN,
(MAX_BRIGHTNESS - this->v) + GRID_TOP_MARGIN, 3,
COLOR2FLAGS(WHITE));
}
void ColorEditorContent::drawColorBox(BitmapBuffer *dc)
{
int32_t rgb = RGB(r, g, b);
dc->drawSolidFilledRect(COLOR_BOX_LEFT, COLOR_BOX_TOP, COLOR_BOX_WIDTH,
COLOR_BOX_HEIGHT, COLOR2FLAGS(rgb));
dc->drawSolidRect(COLOR_BOX_LEFT, COLOR_BOX_TOP, COLOR_BOX_WIDTH,
COLOR_BOX_HEIGHT, 1, COLOR2FLAGS(BLACK));
}
void ColorEditorContent::paint(BitmapBuffer *dc)
{
ModalWindowContent::paint(dc);
drawHueBar(dc);
drawGrid(dc);
drawColorBox(dc);
}
///////////////////////////////////////////////////////////////////////////
///// ColorEditorPopup
///////////////////////////////////////////////////////////////////////////
ColorEditorPopup::ColorEditorPopup(
Window *window, std::function<uint32_t()> getValue,
std::function<void(uint32_t value)> setValue) :
ModalWindow(window, true), _getValue(getValue), _setValue(setValue)
{
color = _getValue();
content = new ColorEditorContent(this, contentRect, color, [=](uint32_t rgb) {
if (_setValue != nullptr) _setValue(rgb);
});
bringToTop();
content->setTitle(STR_COLOR_PICKER);
killAllEvents();
}
#if defined(HARDWARE_TOUCH)
bool ColorEditorPopup::onTouchEnd(coord_t x, coord_t y)
{
TRACE("ColorEditorPopup::onTouchEnd");
if (!Window::onTouchEnd(x, y) && closeWhenClickOutside) {
deleteLater();
}
return true;
}
#endif
void ColorEditorPopup::deleteLater(bool detach, bool trash)
{
if (_deleted) return;
Layer::pop(this);
Window::deleteLater(detach, trash);
}
@@ -1,89 +0,0 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include <algorithm>
#include <vector>
#include <iostream>
#include "bitmapbuffer.h"
#include "libopenui.h"
#include "theme_manager.h"
#include "listbox.h"
// the content page of the ColorEditorPupup
class ColorEditorContent : public ModalWindowContent
{
friend class ColorEditorPopup;
public:
ColorEditorContent(ModalWindow *window, const rect_t rect, uint32_t color,
std::function<void(uint32_t rgb)> setValue = nullptr);
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "ColorEditorContent"; }
#endif
#if defined(HARDWARE_TOUCH)
bool onTouchEnd(coord_t x, coord_t y) override;
bool onTouchStart(coord_t x, coord_t y) override;
#endif
void onEvent(event_t event) override;
void paint(BitmapBuffer *dc) override;
protected:
bool sliding = false;
bool colorPicking = false;
int r, g, b;
int hue = 0, s, v;
NumberEdit *rEdit;
NumberEdit *gEdit;
NumberEdit *bEdit;
void drawHueBar(BitmapBuffer *dc);
void drawGrid(BitmapBuffer *dc);
void drawColorBox(BitmapBuffer *dc);
void setRGB();
std::function<void(uint32_t rgb)> setValue;
};
// a color editor popup
class ColorEditorPopup : public ModalWindow
{
public:
ColorEditorPopup(Window *window, std::function<uint32_t()> getValue,
std::function<void(uint32_t value)> setValue = nullptr);
void deleteLater(bool detach = true, bool trash = true) override;
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "ColorEditorPopup"; }
#endif
#if defined(HARDWARE_TOUCH)
bool onTouchEnd(coord_t x, coord_t y) override;
#endif
protected:
uint32_t color;
ColorEditorContent *content;
std::function<uint32_t()> _getValue;
std::function<void(uint32_t value)> _setValue;
};
+163
View File
@@ -0,0 +1,163 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "color_picker.h"
#include "color_editor.h"
// based on LVGL default switch size
constexpr lv_coord_t COLOR_PAD_WIDTH = (4 * LV_DPI_DEF) / 10;
constexpr lv_coord_t COLOR_PAD_HEIGHT = (4 * LV_DPI_DEF) / 17;
#if LCD_W > LCD_H
// Landscape
static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1),
LV_GRID_TEMPLATE_LAST};
static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST};
#else
// Portrait
static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT,
LV_GRID_TEMPLATE_LAST};
#endif
class ColorEditorPopup : public Dialog
{
std::function<void(uint32_t)> _setValue;
lv_obj_t* colorPad = nullptr;
lv_obj_t* hexStr = nullptr;
void updateColor(uint32_t c)
{
auto r = GET_RED(c), g = GET_GREEN(c), b = GET_BLUE(c);
auto lvcolor = lv_color_make(r, g, b);
lv_obj_set_style_bg_color(colorPad, lvcolor, LV_PART_MAIN);
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<void(uint32_t)> _setValue) :
Dialog(parent, std::string(), rect_t{}),
_setValue(std::move(_setValue))
{
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); });
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);
auto hbox = new FormGroup(vbox, rect_t{});
hbox->setFlexLayout(LV_FLEX_FLOW_ROW, lv_dpx(8));
auto hbox_obj = hbox->getLvObj();
colorPad = window_create(hbox_obj);
lv_obj_set_style_bg_opa(colorPad, LV_OPA_100, LV_PART_MAIN);
lv_obj_set_size(colorPad, COLOR_PAD_WIDTH, COLOR_PAD_HEIGHT);
hexStr = lv_label_create(hbox_obj);
lv_obj_set_style_text_font(hexStr, getFont(FONT(L)), LV_PART_MAIN);
lv_obj_set_style_text_color(hexStr, makeLvColor(COLOR_THEME_PRIMARY1), LV_PART_MAIN);
updateColor(color);
hbox = new FormGroup(vbox, rect_t{});
hbox->setFlexLayout(LV_FLEX_FLOW_ROW, lv_dpx(8));
auto rgbBtn = new TextButton(hbox, rect_t{}, "RGB");
auto hsvBtn = new TextButton(hbox, rect_t{}, "HSV");
rgbBtn->setPressHandler([=]() {
cedit->setColorEditorType(RGB_COLOR_EDITOR);
hsvBtn->check(false);
return 1;
});
rgbBtn->padAll(lv_dpx(8));
hsvBtn->setPressHandler([=]() {
cedit->setColorEditorType(HSV_COLOR_EDITOR);
rgbBtn->check(false);
return 1;
});
hsvBtn->padAll(lv_dpx(8));
// color editor defaults to HSV
hsvBtn->check(true);
content->setWidth(LCD_W * 0.8);
content->updateSize();
}
};
static void color_editor_popup(lv_event_t* e)
{
auto picker = (ColorPicker*)lv_event_get_user_data(e);
if (picker) {
new ColorEditorPopup(picker, picker->getColor(),
[=](uint32_t c) { picker->setColor(c); });
}
}
ColorPicker::ColorPicker(Window* parent, const rect_t& rect,
std::function<uint16_t()> getValue,
std::function<void(uint16_t)> setValue) :
FormField(parent, rect, 0, 0, lv_btn_create),
setValue(std::move(setValue))
{
lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN);
lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_STATE_FOCUS_KEY);
lv_obj_set_size(lvobj, COLOR_PAD_WIDTH, COLOR_PAD_HEIGHT);
lv_obj_add_event_cb(lvobj, color_editor_popup, LV_EVENT_CLICKED, this);
updateColor(getValue());
}
void ColorPicker::setColor(uint32_t c)
{
setValue(c);
updateColor(c);
}
void ColorPicker::updateColor(uint32_t c)
{
color = c;
auto lvcolor = lv_color_make(GET_RED(color), GET_GREEN(color), GET_BLUE(color));
lv_obj_set_style_bg_color(lvobj, lvcolor, LV_PART_MAIN);
lv_obj_set_style_bg_color(lvobj, lvcolor, LV_STATE_FOCUS_KEY);
}
+40
View File
@@ -0,0 +1,40 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "form.h"
class ColorPicker : public FormField
{
uint32_t color;
std::function<void(uint16_t)> setValue;
void updateColor(uint32_t c);
public:
ColorPicker(Window* parent, const rect_t& rect,
std::function<uint16_t()> getValue,
std::function<void(uint16_t)> setValue = nullptr);
void setColor(uint32_t c);
uint32_t getColor() const { return color; }
};
-1
View File
@@ -54,7 +54,6 @@
#include "gridlayout.h"
#include "getset_helpers.h"
#include "curveedit.h"
#include "coloredit.h"
#include "draw_functions.h"
#include "textedits.h"
#include "LvglWrapper.h"
-1
View File
@@ -30,7 +30,6 @@
#include "widget_settings.h"
#include "sliders.h"
#include "color_list.h"
#include "color_editor_popup.h"
#include "color_editor.h"
#include "listbox.h"
#include "preview_window.h"
+2 -1
View File
@@ -27,6 +27,7 @@
#include "libopenui.h"
#include "layouts/layout_factory_impl.h"
#include "widgets_setup.h"
#include "color_picker.h"
#define SET_DIRTY() storageDirty(EE_MODEL)
#define BUTTON_HEIGHT 30
@@ -322,7 +323,7 @@ void ScreenSetupPage::buildLayoutOptions()
break;
case ZoneOption::Color:
new ColorEdit(line, rect_t{}, GET_SET_DEFAULT(value->unsignedValue));
new ColorPicker(line, rect_t{}, GET_SET_DEFAULT(value->unsignedValue));
break;
default:
+2 -18
View File
@@ -24,6 +24,7 @@
#include "widget_settings.h"
#include "view_main.h"
#include "color_picker.h"
#define SET_DIRTY() storageDirty(EE_MODEL)
@@ -42,13 +43,10 @@ static const lv_coord_t line_row_dsc[] = {LV_GRID_CONTENT,
WidgetSettings::WidgetSettings(Window* parent, Widget* widget) :
Dialog(ViewMain::instance(), STR_WIDGET_SETTINGS, widgetSettingsDialogRect)
{
auto lv_content = content->getLvObj();
lv_obj_set_flex_flow(lv_content, LV_FLEX_FLOW_COLUMN);
setCloseWhenClickOutside(true);
auto form = &content->form;
form->setFlexLayout();
FlexGridLayout grid(line_col_dsc, line_row_dsc);
@@ -152,7 +150,7 @@ WidgetSettings::WidgetSettings(Window* parent, Widget* widget) :
break;
case ZoneOption::Color:
new ColorEdit(
new ColorPicker(
line, rect_t{},
[=]() -> int { // getValue
return (int)widget->getOptionValue(optIdx)->unsignedValue;
@@ -171,18 +169,4 @@ WidgetSettings::WidgetSettings(Window* parent, Widget* widget) :
content->updateSize();
setCloseHandler([=]() { widget->update(); });
// form->setFocus();
}
// #if defined(HARDWARE_KEYS)
// void WidgetSettings::onEvent(event_t event)
// {
// TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event);
// if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) {
// deleteLater();
// }
// }
// #endif