feat(color): Improvements to switch and source select menu (#4104)

This commit is contained in:
philmoz
2023-10-13 20:39:28 +10:00
committed by GitHub
parent 614ff284db
commit ede1d0081d
33 changed files with 702 additions and 495 deletions
+70 -40
View File
@@ -20,74 +20,104 @@
*/
#include "sourcechoice.h"
#include "menutoolbar.h"
#include "menu.h"
#include "dataconstants.h"
#include "lcd.h"
#include "strhelpers.h"
#include "draw_functions.h"
#include "lcd.h"
#include "menu.h"
#include "menutoolbar.h"
#include "opentx.h"
#include "strhelpers.h"
#include "switches.h"
#if LCD_W > LCD_H
#define FILTER_COLUMNS 3
#else
#define FILTER_COLUMNS 2
#endif
class SourceChoiceMenuToolbar : public MenuToolbar
{
public:
SourceChoiceMenuToolbar(SourceChoice * choice, Menu * menu):
MenuToolbar(choice, menu)
{
addButton(STR_CHAR_INPUT, MIXSRC_FIRST_INPUT, MIXSRC_LAST_INPUT);
public:
SourceChoiceMenuToolbar(SourceChoice *choice, Menu *menu) :
MenuToolbar(choice, menu, FILTER_COLUMNS)
{
addButton(STR_CHAR_INPUT, MIXSRC_FIRST_INPUT, MIXSRC_LAST_INPUT, nullptr,
STR_MENU_INPUTS);
#if defined(LUA_MODEL_SCRIPTS)
if (modelCustomScriptsEnabled())
addButton(STR_CHAR_LUA, MIXSRC_FIRST_LUA, MIXSRC_LAST_LUA);
if (modelCustomScriptsEnabled())
addButton(STR_CHAR_LUA, MIXSRC_FIRST_LUA, MIXSRC_LAST_LUA, nullptr,
STR_MENU_LUA);
#endif
#if defined(PCBHORUS)
auto lastSource = MIXSRC_LAST_SPACEMOUSE;
auto lastSource = MIXSRC_LAST_SPACEMOUSE;
#elif defined(IMU)
auto lastSource = MIXSRC_TILT_Y;
auto lastSource = MIXSRC_TILT_Y;
#else
auto lastSource = MIXSRC_LAST_STICK;
auto lastSource = MIXSRC_LAST_STICK;
#endif
addButton(STR_CHAR_STICK, MIXSRC_FIRST_STICK, lastSource, [=](int16_t index) {
if (index >= MIXSRC_FIRST_POT && index <= MIXSRC_LAST_POT)
return false;
addButton(
STR_CHAR_STICK, MIXSRC_FIRST_STICK, lastSource,
[=](int16_t index) {
if (index >= MIXSRC_FIRST_POT && index <= MIXSRC_LAST_POT)
return false;
#if MAX_AXIS > 0
if (index >= MIXSRC_FIRST_AXIS && index <= MIXSRC_LAST_AXIS)
return false;
if (index >= MIXSRC_FIRST_AXIS && index <= MIXSRC_LAST_AXIS)
return false;
#endif
return index >= MIXSRC_FIRST_STICK && index <= lastSource;
});
addButton(STR_CHAR_POT, MIXSRC_FIRST_POT, MIXSRC_LAST_POT);
addButton(STR_CHAR_FUNCTION, MIXSRC_MIN, MIXSRC_LAST_TIMER, [=](int16_t index) {
return (index >= MIXSRC_MIN && index <= MIXSRC_MAX) || (index >= MIXSRC_TX_VOLTAGE && index <= MIXSRC_LAST_TIMER);
});
return index >= MIXSRC_FIRST_STICK && index <= lastSource;
},
STR_MENU_STICKS);
addButton(STR_CHAR_POT, MIXSRC_FIRST_POT, MIXSRC_LAST_POT, nullptr,
STR_MENU_POTS);
addButton(
STR_CHAR_FUNCTION, MIXSRC_MIN, MIXSRC_LAST_TIMER,
[=](int16_t index) {
return (index >= MIXSRC_MIN && index <= MIXSRC_MAX) ||
(index >= MIXSRC_TX_VOLTAGE && index <= MIXSRC_LAST_TIMER);
},
STR_MENU_OTHER);
#if defined(HELI)
if (modelHeliEnabled())
addButton(STR_CHAR_CYC, MIXSRC_FIRST_HELI, MIXSRC_LAST_HELI);
if (modelHeliEnabled())
addButton(STR_CHAR_CYC, MIXSRC_FIRST_HELI, MIXSRC_LAST_HELI, nullptr,
STR_MENU_HELI);
#endif
addButton(STR_CHAR_TRIM, MIXSRC_FIRST_TRIM, MIXSRC_LAST_TRIM);
addButton(STR_CHAR_SWITCH, MIXSRC_FIRST_SWITCH, MIXSRC_LAST_SWITCH);
if (modelLSEnabled())
addButton(STR_CHAR_SWITCH, MIXSRC_FIRST_LOGICAL_SWITCH, MIXSRC_LAST_LOGICAL_SWITCH);
addButton(STR_CHAR_TRAINER, MIXSRC_FIRST_TRAINER, MIXSRC_LAST_TRAINER);
addButton(STR_CHAR_CHANNEL, MIXSRC_FIRST_CH, MIXSRC_LAST_CH);
addButton(STR_CHAR_TRIM, MIXSRC_FIRST_TRIM, MIXSRC_LAST_TRIM, nullptr,
STR_MENU_TRIMS);
addButton(STR_CHAR_SWITCH, MIXSRC_FIRST_SWITCH, MIXSRC_LAST_SWITCH, nullptr,
STR_MENU_SWITCHES);
if (modelLSEnabled())
addButton("LS", MIXSRC_FIRST_LOGICAL_SWITCH, MIXSRC_LAST_LOGICAL_SWITCH,
nullptr, STR_MENU_LOGICAL_SWITCHES);
addButton(STR_CHAR_TRAINER, MIXSRC_FIRST_TRAINER, MIXSRC_LAST_TRAINER,
nullptr, STR_MENU_TRAINER);
addButton(STR_CHAR_CHANNEL, MIXSRC_FIRST_CH, MIXSRC_LAST_CH, nullptr,
STR_MENU_CHANNELS);
#if defined(GVARS)
if (modelGVEnabled())
addButton(STR_CHAR_SLIDER, MIXSRC_FIRST_GVAR, MIXSRC_LAST_GVAR);
if (modelGVEnabled())
addButton(STR_CHAR_SLIDER, MIXSRC_FIRST_GVAR, MIXSRC_LAST_GVAR, nullptr,
STR_MENU_GVARS);
#endif
if (modelTelemetryEnabled())
addButton(STR_CHAR_TELEMETRY, MIXSRC_FIRST_TELEM, MIXSRC_LAST_TELEM);
}
if (modelTelemetryEnabled())
addButton(STR_CHAR_TELEMETRY, MIXSRC_FIRST_TELEM, MIXSRC_LAST_TELEM,
nullptr, STR_MENU_TELEMETRY);
if ((nxtBtnPos > filterColumns) && choice->isValueAvailable &&
choice->isValueAvailable(0))
addButton(STR_SELECT_MENU_CLR, 0, 0, nullptr, nullptr, true);
}
};
// defined in gui/gui_common.cpp
uint8_t switchToMix(uint8_t source);
SourceChoice::SourceChoice(Window* parent, const rect_t &rect, int16_t vmin,
SourceChoice::SourceChoice(Window *parent, const rect_t &rect, int16_t vmin,
int16_t vmax, std::function<int16_t()> getValue,
std::function<void(int16_t)> setValue,
WindowFlags windowFlags, LcdFlags textFlags) :
Choice(parent, rect, vmin, vmax, getValue, setValue)
{
setMenuTitle(STR_SOURCE);
setBeforeDisplayMenuHandler([=](Menu *menu) {
auto tb = new SourceChoiceMenuToolbar(this, menu);
menu->setToolbar(tb);
@@ -122,5 +152,5 @@ SourceChoice::SourceChoice(Window* parent, const rect_t &rect, int16_t vmin,
return std::string(getSourceString(value));
});
setAvailableHandler([](int v){ return isSourceAvailable(v); });
setAvailableHandler([](int v) { return isSourceAvailable(v); });
}
+5
View File
@@ -25,6 +25,8 @@
#include "choice.h"
#include "form.h"
class SourceChoiceMenuToolbar;
bool isSourceAvailable(int source);
class SourceChoice : public Choice
@@ -35,6 +37,9 @@ class SourceChoice : public Choice
std::function<void(int16_t)> setValue,
WindowFlags windowFlags = 0, LcdFlags textFlags = 0);
protected:
friend SourceChoiceMenuToolbar;
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "SourceChoice"; }
#endif
+79 -9
View File
@@ -32,22 +32,69 @@ class SwitchChoiceMenuToolbar : public MenuToolbar
{
public:
SwitchChoiceMenuToolbar(SwitchChoice* choice, Menu* menu) :
MenuToolbar(choice, menu)
MenuToolbar(choice, menu, 2)
{
addButton(STR_CHAR_SWITCH, SWSRC_FIRST_SWITCH, SWSRC_LAST_MULTIPOS_SWITCH);
addButton(STR_CHAR_TRIM, SWSRC_FIRST_TRIM, SWSRC_LAST_TRIM);
addButton(STR_CHAR_SWITCH, SWSRC_FIRST_LOGICAL_SWITCH, SWSRC_LAST_LOGICAL_SWITCH);
addButton(STR_CHAR_TELEMETRY, SWSRC_FIRST_SENSOR, SWSRC_LAST_SENSOR);
addButton(STR_CHAR_SWITCH, SWSRC_FIRST_SWITCH, SWSRC_LAST_MULTIPOS_SWITCH,
nullptr, STR_MENU_SWITCHES);
addButton(STR_CHAR_TRIM, SWSRC_FIRST_TRIM, SWSRC_LAST_TRIM, nullptr,
STR_MENU_TRIMS);
addButton("LS", SWSRC_FIRST_LOGICAL_SWITCH, SWSRC_LAST_LOGICAL_SWITCH,
nullptr, STR_MENU_LOGICAL_SWITCHES);
addButton("FM", SWSRC_FIRST_FLIGHT_MODE, SWSRC_LAST_FLIGHT_MODE, nullptr,
STR_FLIGHT_MODE);
addButton(STR_CHAR_TELEMETRY, SWSRC_FIRST_SENSOR, SWSRC_LAST_SENSOR,
nullptr, STR_MENU_TELEMETRY);
#if defined(DEBUG_LATENCY)
auto lastSource = SWSRC_LATENCY_TOGGLE;
#else
auto lastSource = SWSRC_RADIO_ACTIVITY;
#endif
addButton(STR_CHAR_FUNCTION, SWSRC_TELEMETRY_STREAMING, lastSource, [=](int16_t index) {
index = abs(index);
return index == 0 || (index >= SWSRC_TELEMETRY_STREAMING && index <= lastSource && !(index >= SWSRC_FIRST_SENSOR && index <= SWSRC_LAST_SENSOR));
addButton(
STR_CHAR_FUNCTION, SWSRC_ON, lastSource,
[=](int16_t index) {
index = abs(index);
return index == 0 || index == SWSRC_ON || index == SWSRC_ONE ||
(index >= SWSRC_TELEMETRY_STREAMING && index <= lastSource &&
!(index >= SWSRC_FIRST_SENSOR && index <= SWSRC_LAST_SENSOR));
},
STR_MENU_OTHER);
if ((nxtBtnPos > filterColumns) && choice->isValueAvailable &&
choice->isValueAvailable(0))
addButton(STR_SELECT_MENU_CLR, 0, 0, nullptr, nullptr, true);
#if defined(HARDWARE_TOUCH)
coord_t y =
height() - MENUS_TOOLBAR_BUTTON_WIDTH - MENUS_TOOLBAR_BUTTON_PADDING;
coord_t w = width() - MENUS_TOOLBAR_BUTTON_PADDING * 2;
invertBtn = new MenuToolbarButton(
this, {MENUS_TOOLBAR_BUTTON_PADDING, y, w, MENUS_TOOLBAR_BUTTON_WIDTH},
STR_SELECT_MENU_INV);
invertBtn->check(choice->inverted);
invertBtn->setPressHandler([=]() {
lv_obj_clear_state(invertBtn->getLvObj(), LV_STATE_FOCUSED);
longPress();
return choice->inverted;
});
#endif
}
void longPress()
{
SwitchChoice* switchChoice = (SwitchChoice*)choice;
switchChoice->inverted = !switchChoice->inverted;
auto idx = menu->selection();
switchChoice->fillMenu(menu, filter);
menu->select(idx);
#if defined(HARDWARE_TOUCH)
invertBtn->check(switchChoice->inverted);
#endif
}
protected:
MenuToolbarButton* invertBtn = nullptr;
};
void SwitchChoice::LongPressHandler(void* data)
@@ -61,12 +108,33 @@ void SwitchChoice::LongPressHandler(void* data)
}
}
void SwitchChoice::setValue(int value)
{
if (inMenu) {
if (inverted) value = -value;
inMenu = false;
}
Choice::setValue(value);
}
int SwitchChoice::getIntValue() const
{
int value = Choice::getIntValue();
if (inMenu) value = abs(value);
return value;
}
SwitchChoice::SwitchChoice(Window* parent, const rect_t& rect, int vmin,
int vmax, std::function<int16_t()> getValue,
std::function<void(int16_t)> setValue) :
Choice(parent, rect, vmin, vmax, getValue, setValue)
Choice(parent, rect, 0, vmax, getValue, setValue)
{
setMenuTitle(STR_SWITCH);
setBeforeDisplayMenuHandler([=](Menu* menu) {
inverted = getValue() < 0;
inMenu = true;
auto tb = new SwitchChoiceMenuToolbar(this, menu);
menu->setToolbar(tb);
@@ -94,6 +162,8 @@ SwitchChoice::SwitchChoice(Window* parent, const rect_t& rect, int vmin,
});
setTextHandler([=](int value) {
if (inMenu && inverted) value = -value;
if (isValueAvailable && !isValueAvailable(value))
return std::to_string(0); // we will fix this later
+10
View File
@@ -25,6 +25,8 @@
#include "choice.h"
#include "form.h"
class SwitchChoiceMenuToolbar;
class SwitchChoice : public Choice
{
public:
@@ -35,6 +37,14 @@ class SwitchChoice : public Choice
static void LongPressHandler(void* data);
protected:
friend SwitchChoiceMenuToolbar;
bool inverted = false;
bool inMenu = false;
void setValue(int value) override;
int getIntValue() const override;
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "SwitchChoice"; }
#endif
@@ -820,6 +820,19 @@ const lv_obj_class_t etx_button_class = {
.instance_size = sizeof(lv_btn_t),
};
const lv_obj_class_t etx_menu_button_class = {
.base_class = &lv_btn_class,
.constructor_cb = etx_button_constructor,
.destructor_cb = nullptr,
.user_data = nullptr,
.event_cb = nullptr,
.width_def = LV_SIZE_CONTENT,
.height_def = 32,
.editable = LV_OBJ_CLASS_EDITABLE_INHERIT,
.group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE,
.instance_size = sizeof(lv_btn_t),
};
const lv_obj_class_t etx_choice_class = {
.base_class = &lv_obj_class,
.constructor_cb = etx_choice_constructor,
@@ -983,6 +996,11 @@ lv_obj_t* etx_button_create(lv_obj_t* parent)
return etx_create(&etx_button_class, parent);
}
lv_obj_t* etx_menu_button_create(lv_obj_t* parent)
{
return etx_create(&etx_menu_button_class, parent);
}
lv_obj_t* etx_choice_create(lv_obj_t* parent)
{
return etx_create(&etx_choice_class, parent);
+1 -5
View File
@@ -32,10 +32,6 @@ constexpr uint32_t ALERT_MESSAGE_LEFT = ALERT_TITLE_LEFT;
constexpr coord_t INPUT_EDIT_CURVE_WIDTH = 132;
constexpr coord_t INPUT_EDIT_CURVE_HEIGHT = INPUT_EDIT_CURVE_WIDTH;
constexpr coord_t MENUS_LINE_HEIGHT = 35;
constexpr coord_t MENUS_WIDTH = 200;
constexpr coord_t MENUS_MAX_HEIGHT = (MENUS_LINE_HEIGHT * 7);
constexpr rect_t MENUS_TOOLBAR_RECT = { 100, 51, 30, 209 };
constexpr coord_t MENUS_MAX_HEIGHT = (MENUS_LINE_HEIGHT * 7) + 8;
#define ROTARY_ENCODER_SPEED() rotencSpeed
@@ -32,11 +32,7 @@ constexpr uint32_t ALERT_MESSAGE_LEFT = 15;
constexpr coord_t INPUT_EDIT_CURVE_WIDTH = 176;
constexpr coord_t INPUT_EDIT_CURVE_HEIGHT = 132;
constexpr coord_t MENUS_LINE_HEIGHT = 35;
constexpr coord_t MENUS_WIDTH = 200;
constexpr coord_t MENUS_MAX_HEIGHT = (MENUS_LINE_HEIGHT * 10);
constexpr rect_t MENUS_TOOLBAR_RECT = { 35, (LCD_H - MENUS_MAX_HEIGHT) / 2, 50, MENUS_MAX_HEIGHT };
// Disable rotary encoder, as the NV14 does not have one
#define ROTARY_ENCODER_SPEED() 0
+36 -31
View File
@@ -17,17 +17,18 @@
*/
#include "choice.h"
#include "menu.h"
#include "theme.h"
void choice_changed_cb(lv_event_t *e)
void choice_changed_cb(lv_event_t* e)
{
auto code = lv_event_get_code(e);
if (code == LV_EVENT_VALUE_CHANGED) {
lv_obj_t *target = lv_event_get_target(e);
lv_obj_t* target = lv_event_get_target(e);
if (target != nullptr) {
ChoiceBase *cb = (ChoiceBase*)lv_obj_get_user_data(target);
ChoiceBase* cb = (ChoiceBase*)lv_obj_get_user_data(target);
if (cb) {
std::string text = cb->getLabelText();
lv_label_set_text(cb->label, text.c_str());
@@ -38,20 +39,19 @@ void choice_changed_cb(lv_event_t *e)
ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type,
WindowFlags windowFlags) :
FormField(parent, rect, windowFlags, 0, etx_choice_create),
type(type)
FormField(parent, rect, windowFlags, 0, etx_choice_create), type(type)
{
// lv_obj_set_height(lvobj, LV_SIZE_CONTENT);
// lv_obj_set_width(lvobj, LV_SIZE_CONTENT);
// lv_obj_set_height(lvobj, LV_SIZE_CONTENT);
// lv_obj_set_width(lvobj, LV_SIZE_CONTENT);
lv_obj_set_layout(lvobj, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(lvobj, LV_FLEX_FLOW_ROW);
lv_obj_add_event_cb(lvobj, choice_changed_cb, LV_EVENT_VALUE_CHANGED, lvobj);
label = lv_label_create(lvobj);
lv_group_t * def_group = lv_group_get_default();
if(def_group) {
lv_group_add_obj(def_group, lvobj);
lv_group_t* def_group = lv_group_get_default();
if (def_group) {
lv_group_add_obj(def_group, lvobj);
}
lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN);
@@ -60,8 +60,9 @@ ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type,
// lv_obj_set
// add the image
lv_obj_t *img = lv_img_create(lvobj);
lv_img_set_src(img, type == CHOICE_TYPE_DROPOWN ? LV_SYMBOL_DOWN : LV_SYMBOL_DIRECTORY);
lv_obj_t* img = lv_img_create(lvobj);
lv_img_set_src(
img, type == CHOICE_TYPE_DROPOWN ? LV_SYMBOL_DOWN : LV_SYMBOL_DIRECTORY);
lv_obj_set_align(img, LV_ALIGN_RIGHT_MID);
}
@@ -70,7 +71,9 @@ std::string Choice::getLabelText()
std::string text;
if (textHandler != nullptr) {
if (_getValue) { text = textHandler(_getValue()); }
if (_getValue) {
text = textHandler(_getValue());
}
} else if (_getValue) {
int val = _getValue();
val -= vmin;
@@ -78,7 +81,7 @@ std::string Choice::getLabelText()
text = values[val];
}
}
return text;
}
@@ -111,8 +114,8 @@ 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,
WindowFlags windowFlags) :
std::function<int()> _getValue,
std::function<void(int)> _setValue, WindowFlags windowFlags) :
ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags),
values(std::move(values)),
vmin(vmin),
@@ -146,17 +149,16 @@ Choice::Choice(Window* parent, const rect_t& rect, const char* values, int vmin,
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
}
void Choice::addValue(const char * value)
void Choice::addValue(const char* value)
{
values.emplace_back(value);
vmax += 1;
}
void Choice::addValues(const char * const values[], uint8_t count)
void Choice::addValues(const char* const values[], uint8_t count)
{
this->values.reserve(this->values.size() + count);
for (uint8_t i = 0; i < count; i++)
this->values.emplace_back(values[i]);
for (uint8_t i = 0; i < count; i++) this->values.emplace_back(values[i]);
vmax += count;
}
@@ -165,7 +167,7 @@ void Choice::setValues(std::vector<std::string> values)
this->values = std::move(values);
}
void Choice::setValues(const char * const values[])
void Choice::setValues(const char* const values[])
{
this->values.clear();
if (values) {
@@ -186,18 +188,17 @@ void Choice::setValue(int val)
void Choice::onClicked()
{
if (!longPressData.isLongPressed)
openMenu();
if (!longPressData.isLongPressed) openMenu();
}
void Choice::fillMenu(Menu *menu, const FilterFct& filter)
void Choice::fillMenu(Menu* menu, const FilterFct& filter)
{
menu->removeLines();
auto value = _getValue();
auto value = getIntValue();
int count = 0;
int selectedIx = -1;
int selectedIx0 = -1;
selectedIx0 = -1;
for (int i = vmin; i <= vmax; ++i) {
if (filter && !filter(i)) continue;
if (isValueAvailable && !isValueAvailable(i)) continue;
@@ -208,8 +209,12 @@ void Choice::fillMenu(Menu *menu, const FilterFct& filter)
} else {
menu->addLineBuffered(std::to_string(i), [=]() { setValue(i); });
}
if (value == i) { selectedIx = count; }
if (i == 0) { selectedIx0 = count; }
if (value == i) {
selectedIx = count;
}
if (i == 0) {
selectedIx0 = count;
}
++count;
}
if (fillMenuHandler) {
@@ -234,12 +239,12 @@ void Choice::openMenu()
auto menu = new Menu(this);
if (!menuTitle.empty()) menu->setTitle(menuTitle);
fillMenu(menu);
if (beforeDisplayMenuHandler) {
beforeDisplayMenuHandler(menu);
}
fillMenu(menu);
menu->setCloseHandler([=]() { setEditMode(false); });
}
+126 -128
View File
@@ -19,12 +19,13 @@
#pragma once
#include <vector>
#include "form.h"
typedef void (*lvHandler_t)(void*);
typedef void (*lvHandler_t)(void *);
typedef struct {
void* userData;
void *userData;
bool isLongPressed;
lvHandler_t lv_LongPressHandler;
} lv_eventData_t;
@@ -44,168 +45,165 @@ class ChoiceBase : public FormField
ChoiceBase(Window *parent, const rect_t &rect,
ChoiceType type = CHOICE_TYPE_DROPOWN,
WindowFlags windowFlags = 0);
protected:
ChoiceType type;
lv_obj_t* label;
lv_obj_t *label;
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,
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,
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,
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,
WindowFlags windowFlags = 0);
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,
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,
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,
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,
WindowFlags windowFlags = 0);
void addValue(const char *value);
void addValue(const char *value);
void addValues(const char *const values[], uint8_t count);
void addValues(const char *const values[], uint8_t count);
void setValues(std::vector<std::string> values);
void setValues(std::vector<std::string> values);
void setValues(const char *const values[]);
void setValues(const char *const values[]);
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "Choice";
}
std::string getName() const override { return "Choice"; }
#endif
void onClicked() override;
void onClicked() override;
void setFillMenuHandler(std::function<void(Menu*, int, int&)> handler)
{
fillMenuHandler = std::move(handler);
}
void setBeforeDisplayMenuHandler(std::function<void(Menu*)> handler)
{
beforeDisplayMenuHandler = std::move(handler);
void setFillMenuHandler(std::function<void(Menu*, int, int&)> handler)
{
fillMenuHandler = std::move(handler);
}
void setBeforeDisplayMenuHandler(std::function<void(Menu *)> handler)
{
beforeDisplayMenuHandler = std::move(handler);
}
void setSetValueHandler(std::function<void(int)> handler)
{
_setValue = std::move(handler);
}
void setGetValueHandler(std::function<int()> handler)
{
_getValue = std::move(handler);
}
void setAvailableHandler(std::function<bool(int)> handler)
{
isValueAvailable = std::move(handler);
}
unsigned getIndexFromValue(int value) const
{
if (!isValueAvailable) {
return value - vmin;
}
void setSetValueHandler(std::function<void(int)> handler)
{
_setValue = std::move(handler);
}
void setGetValueHandler(std::function<int()> handler)
{
_getValue = std::move(handler);
}
void setAvailableHandler(std::function<bool(int)> handler)
{
isValueAvailable = std::move(handler);
}
unsigned getIndexFromValue(int value) const
{
if (!isValueAvailable) {
return value - vmin;
unsigned index = 0;
for (int i = vmin; i < value; i++) {
if (isValueAvailable(i)) {
index++;
}
}
return index;
}
unsigned index = 0;
for (int i = vmin; i < value; i++) {
if (isValueAvailable(i)) {
index++;
}
}
return index;
int getValueFromIndex(int index) const
{
if (!isValueAvailable) {
return vmin + index;
}
int getValueFromIndex(int index) const
{
if (!isValueAvailable) {
return vmin + index;
}
int value = vmin - 1;
while (index >= 0) {
index--;
int value = vmin - 1;
while (index >= 0) {
index--;
value++;
while (value < vmax && !isValueAvailable(value)) {
value++;
while (value < vmax && !isValueAvailable(value)) {
value++;
}
}
return value;
}
return value;
}
void setValue(int val);
int getIntValue() const { return _getValue(); }
virtual void setValue(int val);
virtual int getIntValue() const { return _getValue(); }
unsigned getValuesCount() const
{
return getIndexFromValue(vmax + 1);
}
unsigned getValuesCount() const { return getIndexFromValue(vmax + 1); }
void setTextHandler(std::function<std::string(int)> handler)
{
textHandler = std::move(handler);
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
}
void setTextHandler(std::function<std::string(int)> handler)
{
textHandler = std::move(handler);
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
}
void setMenuTitle(std::string value)
{
menuTitle = std::move(value);
}
void setMenuTitle(std::string value) { menuTitle = std::move(value); }
std::string getMenuTitle() const { return menuTitle; }
void setMin(int value)
{
vmin = value;
invalidate();
}
void setMin(int value)
{
vmin = value;
invalidate();
}
void setMax(int value)
{
vmax = value;
invalidate();
}
void setMax(int value)
{
vmax = value;
invalidate();
}
void set_lv_LongPressHandler(lvHandler_t longPressHandler, void* data);
int getMin() const { return vmin; }
int getMax() const { return vmax; }
protected:
friend class MenuToolbar;
void set_lv_LongPressHandler(lvHandler_t longPressHandler, void *data);
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<bool(int)> isValueAvailable;
std::function<std::string(int)> textHandler;
std::function<void(Menu *, int, int&)> fillMenuHandler;
std::function <void(Menu *)> beforeDisplayMenuHandler;
int selectedIx0 = 0;
typedef std::function<bool(int16_t)> FilterFct;
void fillMenu(Menu *menu, const FilterFct& filter = nullptr);
protected:
friend class MenuToolbar;
virtual void openMenu();
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<bool(int)> isValueAvailable;
std::function<std::string(int)> textHandler;
std::function<void(Menu *, int, int&)> fillMenuHandler;
std::function<void(Menu *)> beforeDisplayMenuHandler;
lv_eventData_t longPressData;
std::function<void(event_t)> longPressHandler = nullptr;
typedef std::function<bool(int16_t)> FilterFct;
void fillMenu(Menu *menu, const FilterFct &filter = nullptr);
static void ClickHandler(lv_event_t* e)
{
auto ch = (Choice*)lv_event_get_user_data(e);
if (ch) ch->longPressData.isLongPressed = false;
}
virtual void openMenu();
lv_eventData_t longPressData;
std::function<void(event_t)> longPressHandler = nullptr;
static void ClickHandler(lv_event_t *e)
{
auto ch = (Choice *)lv_event_get_user_data(e);
if (ch) ch->longPressData.isLongPressed = false;
}
};
+5 -2
View File
@@ -41,8 +41,8 @@ enum FontIndex
constexpr uint32_t MENU_HEADER_BUTTON_WIDTH = 33;
constexpr uint32_t MENU_HEADER_BUTTONS_LEFT = 47;
constexpr uint32_t MENUS_TOOLBAR_BUTTON_WIDTH = 30;
constexpr uint32_t MENUS_TOOLBAR_BUTTON_PADDING = 3;
constexpr uint32_t MENUS_TOOLBAR_BUTTON_WIDTH = 32;
constexpr uint32_t MENUS_TOOLBAR_BUTTON_PADDING = 4;
constexpr uint32_t MENU_HEADER_HEIGHT = 45;
constexpr uint32_t MENU_TITLE_TOP = 48;
@@ -108,5 +108,8 @@ constexpr coord_t MODEL_SELECT_FOOTER_HEIGHT = 24;
#define LV_OBJ_FLAG_ENCODER_ACCEL LV_OBJ_FLAG_USER_1
constexpr coord_t MENUS_LINE_HEIGHT = 35;
constexpr coord_t MENUS_WIDTH = 200;
#include "libopenui_config.h"
+77 -69
View File
@@ -17,12 +17,15 @@
*/
#include "menu.h"
#include "font.h"
#include "theme.h"
#include <lvgl/lvgl.h>
void MenuBody::onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc)
#include "font.h"
#include "menutoolbar.h"
#include "theme.h"
void MenuBody::onDrawBegin(uint16_t row, uint16_t col,
lv_obj_draw_part_dsc_t* dsc)
{
lv_canvas_t* icon = (lv_canvas_t*)lines[row].getIcon();
if (!icon) return;
@@ -32,15 +35,16 @@ void MenuBody::onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* d
dsc->label_dsc->ofs_x = img->w + cell_left;
}
void MenuBody::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc)
void MenuBody::onDrawEnd(uint16_t row, uint16_t col,
lv_obj_draw_part_dsc_t* dsc)
{
if(row >= lines.size()) return;
if (row >= lines.size()) return;
lv_obj_t* icon = lines[row].getIcon();
if (icon) {
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
lv_img_dsc_t* img = lv_canvas_get_img(icon);
lv_area_t coords;
@@ -51,12 +55,11 @@ void MenuBody::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc
coords.x2 = coords.x1 + img->header.w - 1;
coords.y1 = dsc->draw_area->y1 + (area_h - img->header.h) / 2;
coords.y2 = coords.y1 + img->header.h - 1;
lv_draw_img(dsc->draw_ctx, &img_dsc, &coords, img);
}
if(lines[row].isChecked != nullptr &&
lines[row].isChecked()) {
if (lines[row].isChecked != nullptr && lines[row].isChecked()) {
lv_area_t coords;
lv_coord_t area_h = lv_area_get_height(dsc->draw_area);
lv_coord_t cell_right = lv_obj_get_style_pad_right(lvobj, LV_PART_ITEMS);
@@ -65,17 +68,28 @@ void MenuBody::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc
coords.x2 = coords.x1 + font_h;
coords.y1 = dsc->draw_area->y1 + (area_h - font_h) / 2;
coords.y2 = coords.y1 + font_h - 1;
lv_draw_label(dsc->draw_ctx, dsc->label_dsc, &coords, LV_SYMBOL_OK, nullptr);
lv_draw_label(dsc->draw_ctx, dsc->label_dsc, &coords, LV_SYMBOL_OK,
nullptr);
}
}
static void _force_editing(lv_group_t* g)
static void _force_editing(lv_group_t* g) { lv_group_set_editing(g, true); }
static void localLongPressHandler(lv_event_t* e)
{
lv_group_set_editing(g, true);
MenuBody* menuBody = (MenuBody*)lv_event_get_user_data(e);
if (menuBody) menuBody->longPress();
}
MenuBody::MenuBody(Window * parent, const rect_t & rect):
TableField(parent, rect)
void MenuBody::longPress()
{
isLongPressed = true;
Menu* menu = getParentMenu();
if (menu) menu->longPress();
}
MenuBody::MenuBody(Window* parent, const rect_t& rect) :
TableField(parent, rect)
{
// Allow encoder acceleration
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_ENCODER_ACCEL);
@@ -96,9 +110,12 @@ MenuBody::MenuBody(Window * parent, const rect_t & rect):
});
lv_group_set_editing(g, true);
}
lv_obj_add_event_cb(lvobj, localLongPressHandler, LV_EVENT_LONG_PRESSED,
this);
}
void MenuBody::addLine(const std::string &text, std::function<void()> onPress,
void MenuBody::addLine(const std::string& text, std::function<void()> onPress,
std::function<bool()> isChecked, bool update)
{
lines.emplace_back(text, std::move(onPress), std::move(isChecked), nullptr);
@@ -109,16 +126,15 @@ void MenuBody::addLine(const std::string &text, std::function<void()> onPress,
}
}
void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text,
void MenuBody::addLine(const uint8_t* icon_mask, const std::string& text,
std::function<void()> onPress,
std::function<bool()> isChecked,
bool update)
std::function<bool()> isChecked, bool update)
{
lv_obj_t* canvas = lv_canvas_create(nullptr);
lines.emplace_back(text, std::move(onPress), std::move(isChecked), canvas);
lv_coord_t w = *((uint16_t *)icon_mask);
lv_coord_t h = *(((uint16_t *)icon_mask)+1);
lv_coord_t w = *((uint16_t*)icon_mask);
lv_coord_t h = *(((uint16_t*)icon_mask) + 1);
void* buf = (void*)(icon_mask + 4);
lv_canvas_set_buffer(canvas, buf, w, h, LV_IMG_CF_ALPHA_8BIT);
@@ -131,7 +147,7 @@ void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text,
void MenuBody::updateLines()
{
setRowCount(lines.size());
for(unsigned int idx = 0; idx < lines.size(); idx++) {
for (unsigned int idx = 0; idx < lines.size(); idx++) {
lv_table_set_cell_value(lvobj, idx, 0, lines[idx].text.c_str());
}
}
@@ -146,14 +162,11 @@ void MenuBody::removeLines()
lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF);
}
coord_t MenuBody::getContentHeight()
{
return lv_obj_get_self_height(lvobj);
}
coord_t MenuBody::getContentHeight() { return lv_obj_get_self_height(lvobj); }
void MenuBody::onPress(size_t index)
{
Menu *menu = getParentMenu();
Menu* menu = getParentMenu();
if (index < lines.size()) {
if (menu->multiple) {
if (selectedIndex == (int)index)
@@ -196,15 +209,14 @@ void MenuBody::setIndex(int index)
table->col_act = LV_TABLE_CELL_NONE;
return;
}
table->row_act = index;
table->col_act = 0;
lv_coord_t h_before = 0;
for (uint16_t i = 0; i < table->row_act; i++)
h_before += table->row_h[i];
for (uint16_t i = 0; i < table->row_act; i++) h_before += table->row_h[i];
lv_coord_t row_h = table->row_h[table->row_act];
lv_coord_t row_h = table->row_h[table->row_act];
lv_coord_t scroll_y = lv_obj_get_scroll_y(lvobj);
lv_obj_update_layout(lvobj);
@@ -225,24 +237,16 @@ void MenuBody::setIndex(int index)
void MenuBody::onPress(uint16_t row, uint16_t col)
{
onPress(row);
if (!isLongPressed) onPress(row);
isLongPressed = false;
}
void MenuBody::selectNext(MENU_DIRECTION direction)
{
// look for the next non separator line
int index = selectedIndex + direction;
setIndex(rangeCheck(index));
}
void MenuBody::onEvent(event_t event)
{
#if defined(HARDWARE_KEYS)
if (event == EVT_KEY_BREAK(KEY_EXIT)) {
onCancel();
}
else {
} else {
TableField::onEvent(event);
}
#endif
@@ -250,7 +254,7 @@ void MenuBody::onEvent(event_t event)
void MenuBody::onCancel()
{
if(_onCancel) _onCancel();
if (_onCancel) _onCancel();
TableField::onCancel();
}
@@ -274,14 +278,14 @@ void MenuWindowContent::deleteLater(bool detach, bool trash)
ModalWindowContent::deleteLater(detach, trash);
}
Menu::Menu(Window * parent, bool multiple):
ModalWindow(parent, true),
content(new MenuWindowContent(this)),
multiple(multiple)
Menu::Menu(Window* parent, bool multiple) :
ModalWindow(parent, true),
content(new MenuWindowContent(this)),
multiple(multiple)
{
}
void Menu::setToolbar(Window * window)
void Menu::setToolbar(MenuToolbar* window)
{
toolbar = window;
updatePosition();
@@ -293,10 +297,10 @@ void Menu::setToolbar(Window * window)
void Menu::setOutline(Window* obj)
{
lv_obj_set_style_outline_width(obj->getLvObj(), 2, LV_PART_MAIN);
lv_obj_set_style_outline_color(obj->getLvObj(), makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN);
lv_obj_set_style_outline_opa(obj->getLvObj(), LV_OPA_100, LV_PART_MAIN);
lv_obj_set_style_outline_pad(obj->getLvObj(), 1, LV_PART_MAIN);
lv_obj_set_style_outline_color(
obj->getLvObj(), makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN);
lv_obj_set_style_outline_opa(obj->getLvObj(), LV_OPA_100, LV_PART_MAIN);
lv_obj_set_style_outline_pad(obj->getLvObj(), 1, LV_PART_MAIN);
}
void Menu::updatePosition()
@@ -304,10 +308,10 @@ void Menu::updatePosition()
coord_t height = content->body.getContentHeight();
if (toolbar) {
content->setLeft((LCD_W - content->width() + toolbar->width())/2);
content->setLeft((LCD_W - content->width() + toolbar->width()) / 2);
setOutline(toolbar);
} else {
content->setLeft((LCD_W - content->width())/2);
content->setLeft((LCD_W - content->width()) / 2);
auto headerHeight = content->getHeaderHeight();
auto bodyHeight = min<coord_t>(height, MENUS_MAX_HEIGHT - headerHeight);
content->setTop((LCD_H - headerHeight - bodyHeight) / 2);
@@ -324,32 +328,35 @@ void Menu::setTitle(std::string text)
updatePosition();
}
void Menu::addLine(const std::string &text, std::function<void()> onPress,
void Menu::addLine(const std::string& text, std::function<void()> onPress,
std::function<bool()> isChecked)
{
content->body.addLine(text, std::move(onPress), std::move(isChecked));
updatePosition();
}
void Menu::addLine(const uint8_t *icon_mask, const std::string &text,
void Menu::addLine(const uint8_t* icon_mask, const std::string& text,
std::function<void()> onPress,
std::function<bool()> isChecked)
{
content->body.addLine(icon_mask, text, std::move(onPress), std::move(isChecked));
content->body.addLine(icon_mask, text, std::move(onPress),
std::move(isChecked));
updatePosition();
}
void Menu::addLineBuffered(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked)
void Menu::addLineBuffered(const std::string& text,
std::function<void()> onPress,
std::function<bool()> isChecked)
{
content->body.addLine(text, std::move(onPress), std::move(isChecked), false);
}
void Menu::addLineBuffered(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress,
std::function<bool()> isChecked)
void Menu::addLineBuffered(const uint8_t* icon_mask, const std::string& text,
std::function<void()> onPress,
std::function<bool()> isChecked)
{
content->body.addLine(icon_mask, text, std::move(onPress), std::move(isChecked), false);
content->body.addLine(icon_mask, text, std::move(onPress),
std::move(isChecked), false);
}
void Menu::updateLines()
@@ -367,20 +374,21 @@ void Menu::removeLines()
void Menu::onEvent(event_t event)
{
#if defined(HARDWARE_KEYS)
if (toolbar &&
(event == EVT_KEY_BREAK(KEY_PAGEDN) ||
if (toolbar && (event == EVT_KEY_BREAK(KEY_PAGEDN) ||
#if defined(KEYS_GPIO_REG_PAGEUP)
event == EVT_KEY_BREAK(KEY_PAGEUP)
event == EVT_KEY_BREAK(KEY_PAGEUP)
#else
event == EVT_KEY_LONG(KEY_PAGEDN)
event == EVT_KEY_LONG(KEY_PAGEDN)
#endif
)) {
)) {
toolbar->onEvent(event);
}
#endif
}
void Menu::onCancel()
void Menu::onCancel() { deleteLater(); }
void Menu::longPress()
{
deleteLater();
if (toolbar) toolbar->longPress();
}
+123 -143
View File
@@ -18,51 +18,49 @@
#pragma once
#include <vector>
#include <functional>
#include <utility>
#include <memory>
#include <utility>
#include <vector>
#include "modal_window.h"
#include "table.h"
class Menu;
class MenuWindowContent;
class MenuToolbar;
struct lvobj_delete {
constexpr lvobj_delete() = default;
void operator()(lv_obj_t* obj) const {
lv_obj_del(obj);
}
void operator()(lv_obj_t *obj) const { lv_obj_del(obj); }
};
class MenuBody: public TableField
class MenuBody : public TableField
{
friend class MenuWindowContent;
friend class Menu;
enum MENU_DIRECTION
{
DIRECTION_UP = 1,
DIRECTION_DOWN = -1
};
enum MENU_DIRECTION { DIRECTION_UP = 1, DIRECTION_DOWN = -1 };
class MenuLine
{
friend class MenuBody;
public:
MenuLine(const std::string& text, std::function<void()> onPress, std::function<bool()> isChecked,
lv_obj_t *icon) :
text(text), onPress(std::move(onPress)), isChecked(std::move(isChecked)), icon(icon)
MenuLine(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked, lv_obj_t *icon) :
text(text),
onPress(std::move(onPress)),
isChecked(std::move(isChecked)),
icon(icon)
{
}
MenuLine(MenuLine &) = delete;
MenuLine(MenuLine &&) = default;
lv_obj_t* getIcon() { return icon.get(); }
lv_obj_t *getIcon() { return icon.get(); }
protected:
std::string text;
std::function<void()> onPress;
@@ -70,175 +68,157 @@ class MenuBody: public TableField
std::unique_ptr<lv_obj_t, lvobj_delete> icon;
};
public:
MenuBody(Window * parent, const rect_t & rect);
public:
MenuBody(Window *parent, const rect_t &rect);
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "MenuBody";
}
std::string getName() const override { return "MenuBody"; }
#endif
void setIndex(int index);
void setIndex(int index);
int selection() const
{
return selectedIndex;
}
int selection() const { return selectedIndex; }
int count() const
{
return lines.size();
}
int count() const { return lines.size(); }
void onEvent(event_t event) override;
void onCancel() override;
void onEvent(event_t event) override;
void onCancel() override;
void addLine(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked, bool update = true);
void addLine(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked, bool update = true);
void addLine(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress,
std::function<bool()> isChecked,
bool update = true);
void addLine(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress, std::function<bool()> isChecked,
bool update = true);
void updateLines();
void updateLines();
void removeLines();
void removeLines();
void setCancelHandler(std::function<void()> handler)
{
_onCancel = std::move(handler);
}
void setCancelHandler(std::function<void()> handler)
{
_onCancel = std::move(handler);
}
coord_t getContentHeight();
coord_t getContentHeight();
void onPress(size_t index);
protected:
void onPress(uint16_t row, uint16_t col) override;
void onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) override;
void onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) override;
void onPress(size_t index);
void longPress();
void selectNext(MENU_DIRECTION direction);
int rangeCheck(int);
protected:
bool isLongPressed = false;
std::vector<MenuLine> lines;
int selectedIndex = 0;
std::function<void()> _onCancel;
void onPress(uint16_t row, uint16_t col) override;
void onDrawBegin(uint16_t row, uint16_t col,
lv_obj_draw_part_dsc_t *dsc) override;
void onDrawEnd(uint16_t row, uint16_t col,
lv_obj_draw_part_dsc_t *dsc) override;
inline Menu * getParentMenu();
int rangeCheck(int);
std::vector<MenuLine> lines;
int selectedIndex = 0;
std::function<void()> _onCancel;
inline Menu *getParentMenu();
void onSelected(uint16_t row, uint16_t col) override { selectedIndex = row; }
};
class MenuWindowContent: public ModalWindowContent
class MenuWindowContent : public ModalWindowContent
{
friend class Menu;
public:
explicit MenuWindowContent(Menu * parent);
public:
explicit MenuWindowContent(Menu *parent);
coord_t getHeaderHeight() const;
coord_t getHeaderHeight() const;
void deleteLater(bool detach = true, bool trash = true) override;
void deleteLater(bool detach = true, bool trash = true) override;
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "MenuWindowContent";
}
std::string getName() const override { return "MenuWindowContent"; }
#endif
protected:
MenuBody body;
protected:
MenuBody body;
};
class Menu: public ModalWindow
class Menu : public ModalWindow
{
friend class MenuBody;
public:
explicit Menu(Window * parent, bool multiple = false);
public:
explicit Menu(Window *parent, bool multiple = false);
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "Menu";
}
std::string getName() const override { return "Menu"; }
#endif
void setCancelHandler(std::function<void()> handler)
{
content->body.setCancelHandler(std::move(handler));
void setCancelHandler(std::function<void()> handler)
{
content->body.setCancelHandler(std::move(handler));
}
void setWaitHandler(std::function<void()> handler)
{
waitHandler = std::move(handler);
}
void setToolbar(MenuToolbar *window);
void setTitle(std::string text);
void addLine(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked = nullptr);
void addLine(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress,
std::function<bool()> isChecked = nullptr);
void addLineBuffered(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked = nullptr);
void addLineBuffered(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress,
std::function<bool()> isChecked = nullptr);
void updateLines();
void addSeparator();
void removeLines();
unsigned count() const { return content->body.count(); }
int selection() const { return content->body.selection(); }
void select(int index) { content->body.setIndex(index); }
void onEvent(event_t event) override;
void onCancel() override;
void checkEvents() override
{
ModalWindow::checkEvents();
if (waitHandler) {
waitHandler();
}
}
void setWaitHandler(std::function<void()> handler)
{
waitHandler = std::move(handler);
}
void longPress();
void setToolbar(Window* window);
protected:
MenuWindowContent *content;
bool multiple;
MenuToolbar *toolbar = nullptr;
std::function<void()> waitHandler;
void updatePosition();
void setTitle(std::string text);
void addLine(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked = nullptr);
void addLine(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress,
std::function<bool()> isChecked = nullptr);
void addLineBuffered(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked = nullptr);
void addLineBuffered(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress,
std::function<bool()> isChecked = nullptr);
void updateLines();
void addSeparator();
void removeLines();
unsigned count() const
{
return content->body.count();
}
int selection() const
{
return content->body.selection();
}
void select(int index)
{
content->body.setIndex(index);
}
void onEvent(event_t event) override;
void onCancel() override;
void checkEvents() override
{
ModalWindow::checkEvents();
if (waitHandler) {
waitHandler();
}
}
protected:
MenuWindowContent* content;
bool multiple;
Window * toolbar = nullptr;
std::function<void()> waitHandler;
void updatePosition();
void setOutline(Window* obj);
void setOutline(Window *obj);
};
Menu * MenuBody::getParentMenu()
Menu *MenuBody::getParentMenu()
{
return static_cast<Menu *>(getParent()->getParent());
}
+65 -55
View File
@@ -17,8 +17,9 @@
*/
#include "menutoolbar.h"
#include "choice.h"
#include "font.h"
#include "translations.h"
static void toolbar_btn_defocus(lv_event_t* event)
{
@@ -29,56 +30,49 @@ static void toolbar_btn_defocus(lv_event_t* event)
MenuToolbarButton::MenuToolbarButton(Window* parent, const rect_t& rect,
const char* picto) :
Button(parent, rect, nullptr, 0, 0, window_create), picto(picto)
Button(parent, rect, nullptr, 0, 0, etx_menu_button_create)
{
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
lv_obj_add_event_cb(lvobj, toolbar_btn_defocus, LV_EVENT_DEFOCUSED, nullptr);
auto label = lv_label_create(lvobj);
lv_label_set_text(label, picto);
lv_obj_center(label);
}
void MenuToolbarButton::paint(BitmapBuffer * dc)
{
lv_coord_t width = lv_obj_get_content_width(lvobj);
if (checked()) {
dc->drawSolidFilledRect(
MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING,
width - 2 * MENUS_TOOLBAR_BUTTON_PADDING,
MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING,
COLOR_THEME_FOCUS);
dc->drawText(width / 2, (rect.h - getFontHeight(FONT(L))) / 2 + 2,
picto, FONT(L) | CENTERED | COLOR_THEME_PRIMARY2);
} else {
dc->drawText(width / 2, (rect.h - getFontHeight(FONT(L))) / 2 + 2,
picto, FONT(L) | CENTERED | COLOR_THEME_PRIMARY1);
}
}
MenuToolbar::MenuToolbar(Choice* choice, Menu* menu) :
Window(menu, MENUS_TOOLBAR_RECT, OPAQUE),
MenuToolbar::MenuToolbar(Choice* choice, Menu* menu, const int columns) :
Window(menu, {0, (LCD_H - MENUS_MAX_HEIGHT) / 2, 76, MENUS_MAX_HEIGHT},
OPAQUE),
choice(choice),
menu(menu),
filterColumns(columns),
group(lv_group_create())
{
lv_group_add_obj(group, lvobj);
lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY3), 0);
setWidth((MENUS_TOOLBAR_BUTTON_WIDTH + MENUS_TOOLBAR_BUTTON_PADDING) *
columns +
MENUS_TOOLBAR_BUTTON_PADDING);
addButton(STR_SELECT_MENU_ALL, choice->getMin(), choice->getMax(), nullptr,
nullptr, true);
}
MenuToolbar::~MenuToolbar()
{
lv_group_del(group);
}
MenuToolbar::~MenuToolbar() { lv_group_del(group); }
void MenuToolbar::resetFilter()
{
if (lv_group_get_focused(group) != lvobj) {
lv_group_focus_obj(lvobj);
choice->fillMenu(menu);
menu->setTitle(choice->menuTitle);
}
}
void MenuToolbar::onEvent(event_t event)
{
if (event == EVT_KEY_BREAK(KEY_PAGEDN)) {
lv_group_focus_next(group);
}
@@ -90,54 +84,70 @@ void MenuToolbar::onEvent(event_t event)
#endif
lv_group_focus_prev(group);
}
auto obj = lv_group_get_focused(group);
if (!obj) {
choice->fillMenu(menu);
} else {
lv_event_send(obj, LV_EVENT_CLICKED, nullptr);
}
lv_event_send(obj, LV_EVENT_CLICKED, nullptr);
}
rect_t MenuToolbar::getButtonRect(size_t buttons)
rect_t MenuToolbar::getButtonRect(bool wideButton)
{
coord_t y = buttons * (MENUS_TOOLBAR_BUTTON_WIDTH + MENUS_TOOLBAR_BUTTON_PADDING);
return {0, y, LV_PCT(100), MENUS_TOOLBAR_BUTTON_WIDTH};
if (wideButton && (nxtBtnPos % filterColumns))
nxtBtnPos = nxtBtnPos - (nxtBtnPos % filterColumns) + filterColumns;
coord_t x = (nxtBtnPos % filterColumns) *
(MENUS_TOOLBAR_BUTTON_WIDTH + MENUS_TOOLBAR_BUTTON_PADDING) +
MENUS_TOOLBAR_BUTTON_PADDING;
coord_t y = (nxtBtnPos / filterColumns) *
(MENUS_TOOLBAR_BUTTON_WIDTH + MENUS_TOOLBAR_BUTTON_PADDING) +
MENUS_TOOLBAR_BUTTON_PADDING;
coord_t w =
wideButton ? (MENUS_TOOLBAR_BUTTON_WIDTH + MENUS_TOOLBAR_BUTTON_PADDING) *
(filterColumns - 1) +
MENUS_TOOLBAR_BUTTON_WIDTH
: MENUS_TOOLBAR_BUTTON_WIDTH;
nxtBtnPos += wideButton ? filterColumns : 1;
return {x, y, w, MENUS_TOOLBAR_BUTTON_WIDTH};
}
bool MenuToolbar::filterMenu(MenuToolbarButton* btn, int16_t filtermin,
int16_t filtermax, const FilterFct& filterFunc)
int16_t filtermax, const Choice::FilterFct& filterFunc,
const char* title)
{
btn->check(!btn->checked());
Choice::FilterFct filter = nullptr;
filter = nullptr;
if (btn->checked()) {
if (title)
menu->setTitle(title);
else
menu->setTitle(choice->menuTitle);
filter = [=](int16_t index) {
if (filterFunc)
return filterFunc(index);
if (filterFunc) return filterFunc(index);
return index == 0 || (abs(index) >= filtermin && abs(index) <= filtermax);
};
lv_group_focus_obj(btn->getLvObj());
choice->fillMenu(menu, filter);
} else {
lv_event_send(allBtn->getLvObj(), LV_EVENT_CLICKED, nullptr);
}
choice->fillMenu(menu, filter);
return btn->checked();
}
typedef std::function<bool(int)> IsValueAvailable;
static int getFirstAvailable(int min, int max, IsValueAvailable isValueAvailable)
static bool checkFirstAvailable(int min, int max,
IsValueAvailable isValueAvailable)
{
int retval = 0;
for (int i = min; i <= max; i++) {
if (isValueAvailable(i)) {
retval = i;
break;
return true;
}
}
return retval;
return false;
}
void MenuToolbar::addButton(const char* picto, int16_t filtermin,
int16_t filtermax, const FilterFct& filterFunc)
int16_t filtermax, const Choice::FilterFct& filterFunc,
const char* title, bool wideButton)
{
int vmin = choice->vmin;
int vmax = choice->vmax;
@@ -145,19 +155,19 @@ void MenuToolbar::addButton(const char* picto, int16_t filtermin,
if (vmin > filtermin || vmax < filtermin) return;
if (choice->isValueAvailable &&
getFirstAvailable(filtermin, filtermax, choice->isValueAvailable) == 0)
!checkFirstAvailable(filtermin, filtermax, choice->isValueAvailable))
return;
rect_t r = getButtonRect(children.size());
rect_t r = getButtonRect(wideButton);
auto button = new MenuToolbarButton(this, r, picto);
button->setPressHandler(
std::bind(&MenuToolbar::filterMenu, this, button, filtermin, filtermax, filterFunc));
button->setPressHandler(std::bind(&MenuToolbar::filterMenu, this, button,
filtermin, filtermax, filterFunc, title));
lv_group_add_obj(group, button->getLvObj());
}
void MenuToolbar::onClicked()
{
choice->fillMenu(menu);
if (children.size() == 1) {
allBtn = button;
lv_event_send(allBtn->getLvObj(), LV_EVENT_CLICKED, nullptr);
}
}
+14 -9
View File
@@ -19,6 +19,7 @@
#pragma once
#include "button.h"
#include "choice.h"
#include "menu.h"
class Choice;
@@ -27,10 +28,8 @@ class MenuToolbarButton : public Button
{
public:
MenuToolbarButton(Window* parent, const rect_t& rect, const char* picto);
void paint(BitmapBuffer* dc) override;
protected:
const char* picto;
};
class MenuToolbar : public Window
@@ -38,23 +37,29 @@ class MenuToolbar : public Window
// friend Menu;
public:
MenuToolbar(Choice* choice, Menu* menu);
MenuToolbar(Choice* choice, Menu* menu, const int columns);
~MenuToolbar();
void resetFilter();
void onEvent(event_t event) override;
virtual void longPress() {}
protected:
Choice* choice;
Choice::FilterFct filter;
Menu* menu;
typedef std::function<bool(int16_t)> FilterFct;
int nxtBtnPos = 0;
int filterColumns = 0;
MenuToolbarButton* allBtn = nullptr;
lv_group_t* group;
void addButton(const char* picto, int16_t filtermin, int16_t filtermax, const FilterFct& filterFunc = nullptr);
bool filterMenu(MenuToolbarButton* btn, int16_t filtermin, int16_t filtermax, const FilterFct& filterFunc = nullptr);
void addButton(const char* picto, int16_t filtermin, int16_t filtermax,
const Choice::FilterFct& filterFunc = nullptr,
const char* title = nullptr, bool wideButton = false);
bool filterMenu(MenuToolbarButton* btn, int16_t filtermin, int16_t filtermax,
const Choice::FilterFct& filterFunc, const char* title);
rect_t getButtonRect(size_t buttons);
void onClicked() override;
rect_t getButtonRect(bool wideButton);
};
@@ -35,6 +35,7 @@ lv_obj_t* etx_switch_create(lv_obj_t* parent);
lv_obj_t* etx_slider_create(lv_obj_t* parent);
lv_obj_t* etx_btnmatrix_create(lv_obj_t* parent);
lv_obj_t* etx_button_create(lv_obj_t* parent);
lv_obj_t* etx_menu_button_create(lv_obj_t* parent);
lv_obj_t* etx_choice_create(lv_obj_t* parent);
lv_obj_t* etx_modal_create(lv_obj_t* parent);
lv_obj_t* etx_modal_content_create(lv_obj_t* parent);
+4
View File
@@ -1083,3 +1083,7 @@ const char STR_STICK_NAMES2[] = TR_STICK_NAMES2;
const char STR_STICK_NAMES3[] = TR_STICK_NAMES3;
const char STR_SURFACE_NAMES0[] = TR_SURFACE_NAMES0;
const char STR_SURFACE_NAMES1[] = TR_SURFACE_NAMES1;
const char STR_SELECT_MENU_ALL[] = TR_SELECT_MENU_ALL;
const char STR_SELECT_MENU_CLR[] = TR_SELECT_MENU_CLR;
const char STR_SELECT_MENU_INV[] = TR_SELECT_MENU_INV;
+4
View File
@@ -1262,5 +1262,9 @@ extern const char* const STR_VUSBJOYSTICK_CIRC_COUTOUT[];
extern const char STR_USBJOYSTICK_APPLY_CHANGES[];
#endif
extern const char STR_SELECT_MENU_ALL[];
extern const char STR_SELECT_MENU_CLR[];
extern const char STR_SELECT_MENU_INV[];
#endif // _TRANSLATIONS_H_
+4
View File
@@ -1240,3 +1240,7 @@
#define TR_ENABLED_FEATURES "启用的功能"
#define TR_RADIO_MENU_TABS "系统功能选项卡"
#define TR_MODEL_MENU_TABS "模型功能选项卡"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1261,3 +1261,7 @@
#define TR_ENABLED_FEATURES "Aktivní funkce"
#define TR_RADIO_MENU_TABS "Rádiové menu záložky"
#define TR_MODEL_MENU_TABS "Model menu záložky"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1266,3 +1266,7 @@
#define TR_ENABLED_FEATURES "Aktiverede funktioner"
#define TR_RADIO_MENU_TABS "Radio menu tab"
#define TR_MODEL_MENU_TABS "Model menu tab"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1252,3 +1252,7 @@
#define TR_ENABLED_FEATURES "Menüpunkte"
#define TR_RADIO_MENU_TABS "Sender Menüpunkte"
#define TR_MODEL_MENU_TABS "Modell Menüpunkte"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1249,3 +1249,7 @@
#define TR_ENABLED_FEATURES "Enabled Features"
#define TR_RADIO_MENU_TABS "Radio Menu Tabs"
#define TR_MODEL_MENU_TABS "Model Menu Tabs"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1254,3 +1254,7 @@
#define TR_ENABLED_FEATURES "Enabled Features"
#define TR_RADIO_MENU_TABS "Radio Menu Tabs"
#define TR_MODEL_MENU_TABS "Model Menu Tabs"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1270,3 +1270,7 @@
#define TR_ENABLED_FEATURES "Enabled Features"
#define TR_RADIO_MENU_TABS "Radio Menu Tabs"
#define TR_MODEL_MENU_TABS "Model Menu Tabs"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1274,3 +1274,7 @@
#define TR_ENABLED_FEATURES TR("Fonctions activées", "Fonctionnalités activées")
#define TR_RADIO_MENU_TABS "Onglets Menu Radio"
#define TR_MODEL_MENU_TABS "Onglets Menu Modèles"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1258,3 +1258,7 @@
#define TR_ENABLED_FEATURES "אפשר יכולות"
#define TR_RADIO_MENU_TABS "לשוניות תפריט רדיו"
#define TR_MODEL_MENU_TABS "לשוניות תפריט מודל"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1249,3 +1249,7 @@
#define TR_RADIO_MENU_TABS "Schede del Menu Radio"
#define TR_MODEL_MENU_TABS "Schede del Menu Modello"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1244,3 +1244,7 @@
#define TR_ENABLED_FEATURES "有効機能"
#define TR_RADIO_MENU_TABS "送信機メニュータブ"
#define TR_MODEL_MENU_TABS "モデルメニュータブ"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1261,3 +1261,7 @@
#define TR_ENABLED_FEATURES "Enabled Features"
#define TR_RADIO_MENU_TABS "Radio Menu Tabs"
#define TR_MODEL_MENU_TABS "Model Menu Tabs"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1248,3 +1248,7 @@
#define TR_ENABLED_FEATURES "Włączone opcje"
#define TR_RADIO_MENU_TABS "Zakładki menu radia"
#define TR_MODEL_MENU_TABS "Zakładki menu modelu"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1252,3 +1252,7 @@
#define TR_ENABLED_FEATURES "Recursos ativos"
#define TR_RADIO_MENU_TABS "Abas opcoes radio"
#define TR_MODEL_MENU_TABS "Abas opcoes modelo"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1287,3 +1287,7 @@
#define TR_ENABLED_FEATURES "Aktiverade funktioner"
#define TR_RADIO_MENU_TABS "Radiomenyflikar"
#define TR_MODEL_MENU_TABS "Modellmenyflikar"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"
+4
View File
@@ -1246,3 +1246,7 @@
#define TR_ENABLED_FEATURES "啟用的功能"
#define TR_RADIO_MENU_TABS "系統功能選項卡"
#define TR_MODEL_MENU_TABS "模型功能選項卡"
#define TR_SELECT_MENU_ALL "All"
#define TR_SELECT_MENU_CLR "Clear"
#define TR_SELECT_MENU_INV "Invert"