choice toolbar: new implementation based on LVGL group

This commit is contained in:
Raphael Coeffic
2022-06-11 15:13:11 +02:00
committed by Raphael Coeffic
parent 25715ed648
commit 6177a94486
6 changed files with 213 additions and 224 deletions
+53 -61
View File
@@ -79,10 +79,10 @@ std::string Choice::getLabelText()
{
std::string text;
if (textHandler != nullptr)
text = textHandler(getValue());
else if (getValue) {
int val = getValue();
if (textHandler != nullptr) {
if (_getValue) { text = textHandler(_getValue()); }
} else if (_getValue) {
int val = _getValue();
val -= vmin;
if (val >= 0 && val < (int)values.size()) {
text = values[val];
@@ -92,25 +92,26 @@ std::string Choice::getLabelText()
return text;
}
Choice::Choice(Window* parent, const rect_t & rect, int vmin, int vmax,
std::function<int()> getValue, std::function<void(int)> setValue, WindowFlags windowFlags) :
ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags),
vmin(vmin),
vmax(vmax),
getValue(std::move(getValue)),
setValue(std::move(setValue))
Choice::Choice(Window* parent, const rect_t& rect, int vmin, int vmax,
std::function<int()> _getValue,
std::function<void(int)> _setValue, WindowFlags windowFlags) :
ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags),
vmin(vmin),
vmax(vmax),
_getValue(std::move(_getValue)),
_setValue(std::move(_setValue))
{
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
}
Choice::Choice(Window* parent, const rect_t& rect, const char* const values[],
int vmin, int vmax, std::function<int()> getValue,
std::function<void(int)> setValue, WindowFlags windowFlags) :
int vmin, int vmax, std::function<int()> _getValue,
std::function<void(int)> _setValue, WindowFlags windowFlags) :
ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags),
vmin(vmin),
vmax(vmax),
getValue(std::move(getValue)),
setValue(std::move(setValue))
_getValue(std::move(_getValue)),
_setValue(std::move(_setValue))
{
setValues(values);
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
@@ -118,26 +119,26 @@ Choice::Choice(Window* parent, const rect_t& rect, const char* const values[],
Choice::Choice(Window* parent, const rect_t& rect,
std::vector<std::string> values, int vmin, int vmax,
std::function<int()> getValue, std::function<void(int)> setValue,
std::function<int()> _getValue, std::function<void(int)> _setValue,
WindowFlags windowFlags) :
ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags),
values(std::move(values)),
vmin(vmin),
vmax(vmax),
getValue(std::move(getValue)),
setValue(std::move(setValue))
_getValue(std::move(_getValue)),
_setValue(std::move(_setValue))
{
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
}
Choice::Choice(Window* parent, const rect_t& rect, const char* values, int vmin,
int vmax, std::function<int()> getValue,
std::function<void(int)> setValue, WindowFlags windowFlags) :
int vmax, std::function<int()> _getValue,
std::function<void(int)> _setValue, WindowFlags windowFlags) :
ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags),
vmin(vmin),
vmax(vmax),
getValue(std::move(getValue)),
setValue(std::move(setValue))
_getValue(std::move(_getValue)),
_setValue(std::move(_setValue))
{
if (values) {
uint8_t len = values[0];
@@ -181,60 +182,51 @@ void Choice::setValues(const char * const values[])
}
}
void Choice::setValue(int val)
{
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
if (_setValue) _setValue(val);
}
void Choice::onClicked()
{
onKeyPress();
openMenu();
}
void Choice::fillMenu(Menu *menu, const FilterFct& filter)
{
menu->removeLines();
auto value = _getValue();
int count = 0;
for (int i = vmin; i <= vmax; ++i) {
if (filter && !filter(i)) continue;
if (isValueAvailable && !isValueAvailable(i)) continue;
if (textHandler) {
menu->addLine(textHandler(i), [=]() { setValue(i); });
} else if (unsigned(i - vmin) < values.size()) {
menu->addLine(values[i - vmin], [=]() { setValue(i); });
} else {
menu->addLine(std::to_string(i), [=]() { setValue(i); });
}
if (value == i) { menu->select(count); }
++count;
}
}
void Choice::openMenu()
{
setEditMode(true); // this needs to be done first before menu is created.
auto menu = new Menu(this);
if (!menuTitle.empty())
menu->setTitle(menuTitle);
if (!menuTitle.empty()) menu->setTitle(menuTitle);
auto value = getValue();
int count = 0;
int current = -1;
for (int i = vmin; i <= vmax; ++i) {
if (isValueAvailable && !isValueAvailable(i))
continue;
if (textHandler) {
menu->addLine(textHandler(i), [=]() {
setValue(i);
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
});
}
else if (unsigned(i - vmin) < values.size()) {
menu->addLine(values[i - vmin], [=]() {
setValue(i);
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
});
}
else {
menu->addLine(std::to_string(i), [=]() {
setValue(i);
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
});
}
if (value == i) {
current = count;
}
++count;
}
if (current >= 0) {
menu->select(current);
}
fillMenu(menu);
if (beforeDisplayMenuHandler) {
beforeDisplayMenuHandler(menu);
}
menu->setCloseHandler([=]() {
setEditMode(false);
});
menu->setCloseHandler([=]() { setEditMode(false); });
}
+30 -27
View File
@@ -31,39 +31,39 @@ enum ChoiceType {
class ChoiceBase : public FormField
{
public:
ChoiceBase(Window* parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN, WindowFlags windowFlags = 0);
public:
ChoiceBase(Window *parent, const rect_t &rect,
ChoiceType type = CHOICE_TYPE_DROPOWN,
WindowFlags windowFlags = 0);
inline ChoiceType getType() const
{
return type;
}
inline ChoiceType getType() const { return type; }
protected:
ChoiceType type;
lv_obj_t* label;
protected:
friend void choice_changed_cb(lv_event_t *e);
ChoiceType type;
lv_obj_t *label;
virtual std::string getLabelText() { return ""; };
friend void choice_changed_cb(lv_event_t *e);
virtual std::string getLabelText() { return ""; };
};
class Choice: public ChoiceBase {
public:
Choice(Window* parent, const rect_t &rect, int vmin, int vmax,
std::function<int()> getValue,
std::function<void(int)> setValue = nullptr,
std::function<int()> _getValue,
std::function<void(int)> _setValue = nullptr,
WindowFlags windowFlags = 0);
Choice(Window* parent, const rect_t &rect,
std::vector<std::string> values, int vmin, int vmax,
std::function<int()> getValue,
std::function<void(int)> setValue = nullptr,
std::function<int()> _getValue,
std::function<void(int)> _setValue = nullptr,
WindowFlags windowFlags = 0);
Choice(Window* parent, const rect_t &rect, const char *const values[],
int vmin, int vmax, std::function<int()> getValue,
std::function<void(int)> setValue = nullptr,
int vmin, int vmax, std::function<int()> _getValue,
std::function<void(int)> _setValue = nullptr,
WindowFlags windowFlags = 0);
Choice(Window* parent, const rect_t &rect, const char *values, int vmin,
int vmax, std::function<int()> getValue,
std::function<void(int)> setValue = nullptr,
int vmax, std::function<int()> _getValue,
std::function<void(int)> _setValue = nullptr,
WindowFlags windowFlags = 0);
void addValue(const char *value);
@@ -90,12 +90,12 @@ class Choice: public ChoiceBase {
void setSetValueHandler(std::function<void(int)> handler)
{
setValue = std::move(handler);
_setValue = std::move(handler);
}
void setGetValueHandler(std::function<int()> handler)
{
getValue = std::move(handler);
_getValue = std::move(handler);
}
void setAvailableHandler(std::function<bool(int)> handler)
@@ -135,10 +135,8 @@ class Choice: public ChoiceBase {
return value;
}
int getIntValue() const
{
return getValue();
}
void setValue(int val);
int getIntValue() const { return _getValue(); }
unsigned getValuesCount() const
{
@@ -169,16 +167,21 @@ class Choice: public ChoiceBase {
}
protected:
friend class MenuToolbar;
std::string getLabelText() override;
std::vector<std::string> values;
int vmin = 0;
int vmax = 0;
std::string menuTitle;
std::function<int()> getValue;
std::function<void(int)> setValue;
std::function<int()> _getValue;
std::function<void(int)> _setValue;
std::function<bool(int)> isValueAvailable;
std::function<std::string(int)> textHandler;
std::function <void(Menu *)> beforeDisplayMenuHandler;
typedef std::function<bool(int16_t)> FilterFct;
void fillMenu(Menu *menu, const FilterFct& filter = nullptr);
virtual void openMenu();
};
+3 -2
View File
@@ -279,8 +279,9 @@ void Menu::removeLines()
void Menu::onEvent(event_t event)
{
#if defined(HARDWARE_KEYS)
if (toolbar && (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) ||
event == EVT_KEY_BREAK(KEY_PGUP))) {
if (toolbar &&
(event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) ||
event == EVT_KEY_BREAK(KEY_PGUP))) {
toolbar->onEvent(event);
}
#endif
+2 -8
View File
@@ -174,12 +174,7 @@ class Menu: public ModalWindow
waitHandler = std::move(handler);
}
// void setFocusBody(uint8_t flag = SET_FOCUS_DEFAULT)
// {
// content->body.setFocus(flag);
// }
void setToolbar(Window * window);
void setToolbar(Window* window);
void setTitle(std::string text);
@@ -221,8 +216,7 @@ class Menu: public ModalWindow
}
protected:
friend void menuBodyEventCallback(lv_event_t *);
MenuWindowContent * content;
MenuWindowContent* content;
bool multiple;
Window * toolbar = nullptr;
std::function<void()> waitHandler;
+104
View File
@@ -18,8 +18,17 @@
*/
#include "menutoolbar.h"
#include "choice.h"
#include "font.h"
MenuToolbarButton::MenuToolbarButton(Window* parent, const rect_t& rect,
const char* picto) :
Button(parent, rect, nullptr, 0, 0, window_create), picto(picto)
{
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
}
void MenuToolbarButton::paint(BitmapBuffer * dc)
{
if (checked()) {
@@ -35,3 +44,98 @@ void MenuToolbarButton::paint(BitmapBuffer * dc)
picto, CENTERED | COLOR_THEME_PRIMARY1);
}
}
MenuToolbar::MenuToolbar(Choice* choice, Menu* menu) :
Window(menu, MENUS_TOOLBAR_RECT, NO_SCROLLBAR),
choice(choice),
menu(menu),
group(lv_group_create())
{
lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN);
}
MenuToolbar::~MenuToolbar()
{
lv_group_del(group);
}
void MenuToolbar::onEvent(event_t event)
{
lv_obj_t* obj = lv_group_get_focused(group);
if (obj) {
auto btn = (MenuToolbarButton*)lv_obj_get_user_data(obj);
if (btn) { btn->check(false); }
}
if (event == EVT_KEY_BREAK(KEY_PGDN)) {
lv_group_focus_next(group);
} else if (event == EVT_KEY_LONG(KEY_PGDN) ||
event == EVT_KEY_BREAK(KEY_PGUP)) {
lv_group_focus_prev(group);
}
obj = lv_group_get_focused(group);
if (!obj) {
choice->fillMenu(menu);
} else {
auto btn = (MenuToolbarButton*)lv_obj_get_user_data(obj);
if (btn) btn->onClicked();
}
}
static rect_t _get_button_rect(size_t buttons)
{
coord_t y = buttons * MENUS_TOOLBAR_BUTTON_WIDTH;
return {0, y, MENUS_TOOLBAR_BUTTON_WIDTH, MENUS_TOOLBAR_BUTTON_WIDTH};
}
bool MenuToolbar::filterMenu(MenuToolbarButton* btn, int16_t filtermin,
int16_t filtermax)
{
btn->check(!btn->checked());
Choice::FilterFct filter = nullptr;
if (btn->checked()) {
filter = [=](int16_t index) {
return index >= filtermin && index <= filtermax;
};
}
choice->fillMenu(menu, filter);
return btn->checked();
}
typedef std::function<bool(int)> IsValueAvailable;
static int getFirstAvailable(int min, int max, IsValueAvailable isValueAvailable)
{
int retval = 0;
for (int i = min; i <= max; i++) {
if (isValueAvailable(i)) {
retval = i;
break;
}
}
return retval;
}
void MenuToolbar::addButton(const char* picto, int16_t filtermin,
int16_t filtermax)
{
int vmin = choice->vmin;
int vmax = choice->vmax;
if (vmin > filtermin || vmax < filtermin) return;
if (choice->isValueAvailable &&
getFirstAvailable(filtermin, filtermax, choice->isValueAvailable) == 0)
return;
rect_t r = _get_button_rect(children.size());
auto button = new MenuToolbarButton(this, r, picto);
button->setPressHandler(
std::bind(&MenuToolbar::filterMenu, this, button, filtermin, filtermax));
lv_group_add_obj(group, button->getLvObj());
}
+21 -126
View File
@@ -21,140 +21,35 @@
#include "button.h"
#include "menu.h"
#include <algorithm>
int getFirstAvailable(int min, int max, std::function<bool(int)>isValueAvailable);
class Choice;
class MenuToolbarButton: public Button {
public:
MenuToolbarButton(FormGroup * window, const rect_t & rect, const char* picto):
Button(window, rect, nullptr),
picto(picto)
{
}
class MenuToolbarButton : public Button
{
public:
MenuToolbarButton(Window* parent, const rect_t& rect, const char* picto);
void paint(BitmapBuffer* dc) override;
void paint(BitmapBuffer * dc) override;
protected:
const char* picto;
protected:
const char* picto;
};
template <class T>
class MenuToolbar: public FormGroup {
friend T;
class MenuToolbar : public Window
{
// friend Menu;
public:
MenuToolbar(T * choice, Menu * menu):
FormGroup(menu, MENUS_TOOLBAR_RECT, NO_SCROLLBAR),
choice(choice),
menu(menu)
{
lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN);
}
public:
MenuToolbar(Choice* choice, Menu* menu);
~MenuToolbar();
void paint(BitmapBuffer * dc) override
{
// dc->clear(COLOR_THEME_SECONDARY3);
}
void onEvent(event_t event) override;
#if defined(HARDWARE_KEYS)
void onEvent(event_t event) override
{
// if (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) || event == EVT_KEY_BREAK(KEY_PGUP)) {
// #if defined(HARDWARE_TOUCH)
// if (current != children.end()) {
// static_cast<MenuToolbarButton *>(*current)->onTouchEnd(0,0);
// }
// #endif
// current = IS_KEY_LONG(event) || event == EVT_KEY_BREAK(KEY_PGUP) ?
// current == children.end() ?
// --current:
// current == children.begin() ?
// children.end() :
// --current :
// current == children.end() ?
// children.begin() :
// ++current;
protected:
Choice* choice;
Menu* menu;
// if (IS_KEY_LONG(event))
// killEvents(event);
lv_group_t* group;
// if (current != children.end()) {
// auto button = static_cast<MenuToolbarButton *>(*current);
// #if defined(HARDWARE_TOUCH)
// button->onTouchEnd(0,0);
// #endif
// scrollTo(button);
// }
// else {
// lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF);
// }
// }
}
#endif
#if defined(HARDWARE_TOUCH)
bool onTouchEnd(coord_t x, coord_t y) override
{
Window::onTouchEnd(x, y);
return true; // = don't close the menu (inverted so that click outside the menu closes it)
}
#endif
protected:
std::list<Window *>::iterator current = children.end();
T * choice;
Menu * menu;
coord_t y = 0;
std::vector<MenuToolbarButton *> menuButtons;
void addButton(const char* picto, int16_t filtermin, int16_t filtermax)
{
int vmin = choice->vmin;
int vmax = choice->vmax;
if (vmin > filtermin || vmax < filtermin)
return;
if (choice->isValueAvailable &&
getFirstAvailable(filtermin, filtermax, choice->isValueAvailable) ==
0)
return;
auto button = new MenuToolbarButton(
this, {0, y, MENUS_TOOLBAR_BUTTON_WIDTH, MENUS_TOOLBAR_BUTTON_WIDTH},
picto);
button->setPressHandler([=]() {
auto val = choice->getValue();
button->check(!button->checked());
if (button->checked()) {
choice->fillMenu(menu, val, [=](int16_t index) {
return index >= filtermin && index <= filtermax;
});
} else {
choice->fillMenu(menu, val);
}
// menu->setFocusBody();
// set current for page key procesing
current = std::find_if(children.begin(), children.end(), [button](Window *o) { return o == button; });
// clear all checked but the current button
for (auto b : menuButtons) {
if (b != button)
b->check(false);
}
return button->checked();
});
menuButtons.emplace_back(button);
y += MENUS_TOOLBAR_BUTTON_WIDTH;
}
void addButton(const char* picto, int16_t filtermin, int16_t filtermax);
bool filterMenu(MenuToolbarButton* btn, int16_t filtermin, int16_t filtermax);
};