1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-14 06:42:58 +08:00

feat(btnmatrix/keyboard): add option to show popovers on button press (#2537)

This adds a new option that, when enabled, shows popovers when pressing
buttons, similar to how the system keyboards on Android and iOS behave.
This commit is contained in:
Johannes Marbach 2021-09-28 11:28:59 +02:00 committed by GitHub
parent 480ee77911
commit d57eb7614d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 191 additions and 34 deletions

View File

@ -35,6 +35,7 @@
- feat(msgbox): add function to get selected button index
- fix(btnmatrix): make ORed values work correctly with lv_btnmatrix_has_btn_ctrl
- fix(snapshot): snapshot is affected by parent's style because of wrong coordinates.
- feat(btnmatrix/keyboard): add option to show popovers on button press
## v8.0.2 (16.07.2021)
- fix(theme) improve button focus of keyboard

View File

@ -36,8 +36,9 @@ In addition to the width, each button can be customized with the following param
- `LV_BTNMATRIX_CTRL_NO_REPEAT` Disable repeating when the button is long pressed
- `LV_BTNMATRIX_CTRL_DISABLED` Makes a button disabled Like `LV_STATE_DISABLED` on normal objects
- `LV_BTNMATRIX_CTRL_CHECKABLE` Enable toggling of a button. I.e. `LV_STATE_CHECHED` will be added/removed as the button is clicked
- `LV_BTNMATRIX_CTRL_CHECKED` MAke the button checked. It will use the `LV_STATE_CHECHKED` styles.
- `LV_BTNMATRIX_CTRL_CLICK_TRIG` Enabled: send LV_EVENT_VALUE_CHANGE on CLICK, Disabled: send LV_EVENT_VALUE_CHANGE on PRESS*/
- `LV_BTNMATRIX_CTRL_CHECKED` Make the button checked. It will use the `LV_STATE_CHECHKED` styles.
- `LV_BTNMATRIX_CTRL_CLICK_TRIG` Enabled: send LV_EVENT_VALUE_CHANGE on CLICK, Disabled: send LV_EVENT_VALUE_CHANGE on PRESS
- `LV_BTNMATRIX_CTRL_POPOVER` Show the button label in a popover when pressing this key
- `LV_BTNMATRIX_CTRL_RECOLOR` Enable recoloring of button texts with `#`. E.g. `"It's #ff0000 red#"`
- `LV_BTNMATRIX_CTRL_CUSTOM_1` Custom free to use flag
- `LV_BTNMATRIX_CTRL_CUSTOM_2` Custom free to use flag

View File

@ -32,6 +32,12 @@ To set the mode manually, use `lv_keyboard_set_mode(kb, mode)`. The default mode
You can assign a [Text area](/widgets/core/textarea) to the Keyboard to automatically put the clicked characters there.
To assign the text area, use `lv_keyboard_set_textarea(kb, ta)`.
### Key Popovers
To enable key popovers on press, like on common Android and iOS keyboards, use `lv_keyboard_set_popovers(kb, true)`. The default control maps are preconfigured to only show the popovers on keys that produce a symbol and not on e.g. space. If you use a custom keymap, set the `LV_BTNMATRIX_CTRL_POPOVER` flag for all keys that you want to show a popover.
Note that popovers for keys in the top row will draw outside the widget boundaries. To account for this, reserve extra free space on top of the keyboard or ensure that the keyboard is added _after_ any widgets adjacent to its top boundary so that the popovers can draw over those.
The popovers currently are merely a visual effect and don't allow selecting additional characters such as accents yet.
### New Keymap
You can specify a new map (layout) for the keyboard with `lv_keyboard_set_map(kb, map)` and `lv_keyboard_set_ctrl_map(kb, ctrl_map)`.

View File

@ -13,10 +13,13 @@
#include "../../../widgets/lv_textarea.h"
#include "../../../misc/lv_assert.h"
#include <stdlib.h>
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_keyboard_class
#define LV_KB_BTN(width) LV_BTNMATRIX_CTRL_POPOVER | width
/**********************
* TYPEDEFS
@ -29,6 +32,8 @@ static void lv_keyboard_constructor(const lv_obj_class_t * class_p, lv_obj_t * o
static void lv_keyboard_update_map(lv_obj_t * obj);
static void lv_keyboard_update_ctrl_map(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
@ -48,9 +53,9 @@ static const char * const default_kb_map_lc[] = {"1#", "q", "w", "e", "r", "t",
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_lc_map[] = {
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, 1, 1, 1, 1, 1, 1, 1, LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1,
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1),
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
@ -61,9 +66,9 @@ static const char * const default_kb_map_uc[] = {"1#", "Q", "W", "E", "R", "T",
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_uc_map[] = {
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, 1, 1, 1, 1, 1, 1, 1, LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1,
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1),
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
@ -74,9 +79,9 @@ static const char * const default_kb_map_spec[] = {"1", "2", "3", "4", "5", "6",
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_spec_map[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, LV_BTNMATRIX_CTRL_CHECKED | 2,
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | 2,
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1),
LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1),
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
@ -172,8 +177,24 @@ void lv_keyboard_set_mode(lv_obj_t * obj, lv_keyboard_mode_t mode)
if(keyboard->mode == mode) return;
keyboard->mode = mode;
lv_btnmatrix_set_map(obj, kb_map[mode]);
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[mode]);
lv_keyboard_update_map(obj);
}
/**
* Show the button title in a popover when pressed.
* @param kb pointer to a Keyboard object
* @param en whether "popovers" mode is enabled
*/
void lv_keyboard_set_popovers(lv_obj_t * obj, bool en)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
if (keyboard->popovers == en) {
return;
}
keyboard->popovers = en;
lv_keyboard_update_ctrl_map(obj);
}
/**
@ -219,6 +240,17 @@ lv_keyboard_mode_t lv_keyboard_get_mode(const lv_obj_t * obj)
return keyboard->mode;
}
/**
* Tell whether "popovers" mode is enabled or not.
* @param kb pointer to a Keyboard object
* @return true: "popovers" mode is enabled; false: disabled
*/
bool lv_btnmatrix_get_popovers(const lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
return keyboard->popovers;
}
/*=====================
* Other functions
*====================*/
@ -338,24 +370,52 @@ static void lv_keyboard_constructor(const lv_obj_class_t * class_p, lv_obj_t * o
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
keyboard->ta = NULL;
keyboard->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
keyboard->popovers = 0;
lv_obj_align(obj, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_add_event_cb(obj, lv_keyboard_def_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_set_style_base_dir(obj, LV_BASE_DIR_LTR, 0);
lv_btnmatrix_set_map(obj, kb_map[keyboard->mode]);
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[keyboard->mode]);
lv_keyboard_update_map(obj);
}
/**
* Update the key map for the current mode
* @param kb pointer to a keyboard object
* Update the key and control map for the current mode
* @param obj pointer to a keyboard object
*/
static void lv_keyboard_update_map(lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
lv_btnmatrix_set_map(obj, kb_map[keyboard->mode]);
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[keyboard->mode]);
lv_keyboard_update_ctrl_map(obj);
}
/**
* Update the control map for the current mode
* @param obj pointer to a keyboard object
*/
static void lv_keyboard_update_ctrl_map(lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
if (keyboard->popovers) {
/*Apply the current control map (already includes LV_BTNMATRIX_CTRL_POPOVER flags)*/
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[keyboard->mode]);
} else {
/*Make a copy of the current control map*/
lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;
lv_btnmatrix_ctrl_t * ctrl_map = malloc(btnm->btn_cnt * sizeof(lv_btnmatrix_ctrl_t));
lv_memcpy(ctrl_map, kb_ctrl[keyboard->mode], sizeof(lv_btnmatrix_ctrl_t) * btnm->btn_cnt);
/*Remove all LV_BTNMATRIX_CTRL_POPOVER flags*/
for(uint16_t i = 0; i < btnm->btn_cnt; i++) {
ctrl_map[i] &= (~LV_BTNMATRIX_CTRL_POPOVER);
}
/*Apply new control map and clean up*/
lv_btnmatrix_set_ctrl_map(obj, ctrl_map);
free(ctrl_map);
}
}
#endif /*LV_USE_KEYBOARD*/

View File

@ -49,6 +49,7 @@ typedef struct {
lv_btnmatrix_t btnm;
lv_obj_t * ta; /*Pointer to the assigned text area*/
lv_keyboard_mode_t mode; /*Key map type*/
uint8_t popovers : 1; /*Show button titles in popovers on press*/
} lv_keyboard_t;
extern const lv_obj_class_t lv_keyboard_class;
@ -82,6 +83,13 @@ void lv_keyboard_set_textarea(lv_obj_t * kb, lv_obj_t * ta);
*/
void lv_keyboard_set_mode(lv_obj_t * kb, lv_keyboard_mode_t mode);
/**
* Show the button title in a popover when pressed.
* @param kb pointer to a Keyboard object
* @param en whether "popovers" mode is enabled
*/
void lv_keyboard_set_popovers(lv_obj_t * kb, bool en);
/**
* Set a new map for the keyboard
* @param kb pointer to a Keyboard object
@ -110,6 +118,13 @@ lv_obj_t * lv_keyboard_get_textarea(const lv_obj_t * kb);
*/
lv_keyboard_mode_t lv_keyboard_get_mode(const lv_obj_t * kb);
/**
* Tell whether "popovers" mode is enabled or not.
* @param kb pointer to a Keyboard object
* @return true: "popovers" mode is enabled; false: disabled
*/
bool lv_btnmatrix_get_popovers(const lv_obj_t * obj);
/**
* Get the current map of a keyboard
* @param kb pointer to a keyboard object

View File

@ -43,6 +43,7 @@ static bool button_is_checked(lv_btnmatrix_ctrl_t ctrl_bits);
static bool button_is_repeat_disabled(lv_btnmatrix_ctrl_t ctrl_bits);
static bool button_is_inactive(lv_btnmatrix_ctrl_t ctrl_bits);
static bool button_is_click_trig(lv_btnmatrix_ctrl_t ctrl_bits);
static bool button_is_popover(lv_btnmatrix_ctrl_t ctrl_bits);
static bool button_is_checkable(lv_btnmatrix_ctrl_t ctrl_bits);
static bool button_is_recolor(lv_btnmatrix_ctrl_t ctrl_bits);
static bool button_get_checked(lv_btnmatrix_ctrl_t ctrl_bits);
@ -51,6 +52,7 @@ static uint16_t get_button_from_point(lv_obj_t * obj, lv_point_t * p);
static void allocate_btn_areas_and_controls(const lv_obj_t * obj, const char ** map);
static void invalidate_button_area(const lv_obj_t * obj, uint16_t btn_idx);
static void make_one_button_checked(lv_obj_t * obj, uint16_t btn_idx);
static bool has_popovers_in_top_row(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
@ -111,15 +113,8 @@ void lv_btnmatrix_set_map(lv_obj_t * obj, const char * map[])
lv_coord_t max_w = lv_obj_get_content_width(obj);
lv_coord_t max_h = lv_obj_get_content_height(obj);
/*Count the lines to calculate button height*/
uint8_t row_cnt = 1;
uint32_t i;
for(i = 0; map[i] && map[i][0] != '\0'; i++) {
if(strcmp(map[i], "\n") == 0) row_cnt++;
}
/*Calculate the position of each row*/
lv_coord_t max_h_no_gap = max_h - (prow * (row_cnt - 1));
lv_coord_t max_h_no_gap = max_h - (prow * (btnm->row_cnt - 1));
/*Count the units and the buttons in a line
*(A button can be 1,2,3... unit wide)*/
@ -129,7 +124,7 @@ void lv_btnmatrix_set_map(lv_obj_t * obj, const char * map[])
/*Count the units and the buttons in a line*/
uint32_t row;
for(row = 0; row < row_cnt; row++) {
for(row = 0; row < btnm->row_cnt; row++) {
uint16_t unit_cnt = 0; /*Number of units in a row*/
uint16_t btn_cnt = 0; /*Number of buttons in a row*/
/*Count the buttons and units in this row*/
@ -144,8 +139,8 @@ void lv_btnmatrix_set_map(lv_obj_t * obj, const char * map[])
continue;
}
lv_coord_t row_y1 = ptop + (max_h_no_gap * row) / row_cnt + row * prow;
lv_coord_t row_y2 = ptop + (max_h_no_gap * (row + 1)) / row_cnt + row * prow - 1;
lv_coord_t row_y1 = ptop + (max_h_no_gap * row) / btnm->row_cnt + row * prow;
lv_coord_t row_y2 = ptop + (max_h_no_gap * (row + 1)) / btnm->row_cnt + row * prow - 1;
/*Set the button size and positions*/
lv_coord_t max_w_no_gap = max_w - (pcol * (btn_cnt - 1));
@ -180,6 +175,10 @@ void lv_btnmatrix_set_map(lv_obj_t * obj, const char * map[])
map_row = &map_row[btn_cnt + 1]; /*Set the map to the next line*/
}
/*Popovers in the top row will draw outside the widget and the extended draw size depends on
*the row height which may have changed when setting the new map*/
lv_obj_refresh_ext_draw_size(obj);
lv_obj_invalidate(obj);
}
@ -220,9 +219,13 @@ void lv_btnmatrix_set_btn_ctrl(lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctr
btnm->ctrl_bits[btn_id] |= ctrl;
invalidate_button_area(obj, btn_id);
if (ctrl & LV_BTNMATRIX_CTRL_POPOVER) {
lv_obj_refresh_ext_draw_size(obj);
}
}
void lv_btnmatrix_clear_btn_ctrl(const lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctrl_t ctrl)
void lv_btnmatrix_clear_btn_ctrl(lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctrl_t ctrl)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
@ -232,6 +235,10 @@ void lv_btnmatrix_clear_btn_ctrl(const lv_obj_t * obj, uint16_t btn_id, lv_btnma
btnm->ctrl_bits[btn_id] &= (~ctrl);
invalidate_button_area(obj, btn_id);
if (ctrl & LV_BTNMATRIX_CTRL_POPOVER) {
lv_obj_refresh_ext_draw_size(obj);
}
}
void lv_btnmatrix_set_btn_ctrl_all(lv_obj_t * obj, lv_btnmatrix_ctrl_t ctrl)
@ -353,6 +360,7 @@ static void lv_btnmatrix_constructor(const lv_obj_class_t * class_p, lv_obj_t *
LV_TRACE_OBJ_CREATE("begin");
lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;
btnm->btn_cnt = 0;
btnm->row_cnt = 0;
btnm->btn_id_sel = LV_BTNMATRIX_BTN_NONE;
btnm->button_areas = NULL;
btnm->ctrl_bits = NULL;
@ -391,6 +399,15 @@ static void lv_btnmatrix_event(const lv_obj_class_t * class_p, lv_event_t * e)
lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;
lv_point_t p;
if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t * s = lv_event_get_param(e);
if (has_popovers_in_top_row(obj)) {
/*reserve one row worth of extra space to account for popovers in the top row*/
*s = btnm->row_cnt > 0 ? lv_obj_get_content_height(obj) / btnm->row_cnt : 0;
} else {
*s = 0;
}
}
if(code == LV_EVENT_STYLE_CHANGED) {
lv_btnmatrix_set_map(obj, btnm->map_p);
}
@ -419,6 +436,7 @@ static void lv_btnmatrix_event(const lv_obj_class_t * class_p, lv_event_t * e)
if(btnm->btn_id_sel != LV_BTNMATRIX_BTN_NONE) {
if(button_is_click_trig(btnm->ctrl_bits[btnm->btn_id_sel]) == false &&
button_is_popover(btnm->ctrl_bits[btnm->btn_id_sel]) == false &&
button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel]) == false &&
button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) == false) {
uint32_t b = btnm->btn_id_sel;
@ -451,7 +469,8 @@ static void lv_btnmatrix_event(const lv_obj_class_t * class_p, lv_event_t * e)
button_is_hidden(btnm->ctrl_bits[btn_pr]) == false) {
invalidate_button_area(obj, btn_pr);
/*Send VALUE_CHANGED for the newly pressed button*/
if(button_is_click_trig(btnm->ctrl_bits[btn_pr]) == false) {
if(button_is_click_trig(btnm->ctrl_bits[btn_pr]) == false &&
button_is_popover(btnm->ctrl_bits[btnm->btn_id_sel]) == false) {
uint32_t b = btn_pr;
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, &b);
if(res != LV_RES_OK) return;
@ -474,7 +493,8 @@ static void lv_btnmatrix_event(const lv_obj_class_t * class_p, lv_event_t * e)
}
if(button_is_click_trig(btnm->ctrl_bits[btnm->btn_id_sel]) == true &&
if((button_is_click_trig(btnm->ctrl_bits[btnm->btn_id_sel]) == true ||
button_is_popover(btnm->ctrl_bits[btnm->btn_id_sel]) == true) &&
button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel]) == false &&
button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) == false) {
uint32_t b = btnm->btn_id_sel;
@ -744,6 +764,13 @@ static void draw_main(lv_event_t * e)
if(btn_area.y2 == obj->coords.y2 - pbottom) draw_rect_dsc_act.border_side &= ~LV_BORDER_SIDE_BOTTOM;
}
lv_coord_t btn_height = lv_area_get_height(&btn_area);
if ((btn_state & LV_STATE_PRESSED) && (btnm->ctrl_bits[btn_i] & LV_BTNMATRIX_CTRL_POPOVER)) {
/*Push up the upper boundary of the btn area to create the popover*/
btn_area.y1 -= btn_height;
}
/*Draw the background*/
lv_draw_rect(&btn_area, clip_area, &draw_rect_dsc_act);
@ -770,6 +797,12 @@ static void draw_main(lv_event_t * e)
btn_area.x2 = btn_area.x1 + txt_size.x;
btn_area.y2 = btn_area.y1 + txt_size.y;
if ((btn_state & LV_STATE_PRESSED) && (btnm->ctrl_bits[btn_i] & LV_BTNMATRIX_CTRL_POPOVER)) {
/*Push up the button text into the popover*/
btn_area.y1 -= btn_height / 2;
btn_area.y2 -= btn_height / 2;
}
/*Draw the text*/
lv_draw_label(&btn_area, clip_area, &draw_label_dsc_act, txt, NULL);
@ -790,10 +823,13 @@ static void allocate_btn_areas_and_controls(const lv_obj_t * obj, const char **
{
/*Count the buttons in the map*/
uint16_t btn_cnt = 0;
uint16_t row_cnt = 1;
uint16_t i = 0;
while(map[i] && map[i][0] != '\0') {
if(strcmp(map[i], "\n") != 0) { /*Do not count line breaks*/
btn_cnt++;
} else {
row_cnt++;
}
i++;
}
@ -821,6 +857,7 @@ static void allocate_btn_areas_and_controls(const lv_obj_t * obj, const char **
lv_memset_00(btnm->ctrl_bits, sizeof(lv_btnmatrix_ctrl_t) * btn_cnt);
btnm->btn_cnt = btn_cnt;
btnm->row_cnt = row_cnt;
}
/**
@ -859,6 +896,11 @@ static bool button_is_click_trig(lv_btnmatrix_ctrl_t ctrl_bits)
return (ctrl_bits & LV_BTNMATRIX_CTRL_CLICK_TRIG) ? true : false;
}
static bool button_is_popover(lv_btnmatrix_ctrl_t ctrl_bits)
{
return (ctrl_bits & LV_BTNMATRIX_CTRL_POPOVER) ? true : false;
}
static bool button_is_checkable(lv_btnmatrix_ctrl_t ctrl_bits)
{
return (ctrl_bits & LV_BTNMATRIX_CTRL_CHECKABLE) ? true : false;
@ -959,6 +1001,11 @@ static void invalidate_button_area(const lv_obj_t * obj, uint16_t btn_idx)
btn_area.x2 += obj_area.x1 + row_gap;
btn_area.y2 += obj_area.y1 + col_gap;
if ((btn_idx == btnm->btn_id_sel) && (btnm->ctrl_bits[btn_idx] & LV_BTNMATRIX_CTRL_POPOVER)) {
/*Push up the upper boundary of the btn area to also invalidate the popover*/
btn_area.y1 -= lv_area_get_height(&btn_area);
}
lv_obj_invalidate_area(obj, &btn_area);
}
@ -978,4 +1025,30 @@ static void make_one_button_checked(lv_obj_t * obj, uint16_t btn_idx)
if(was_toggled) lv_btnmatrix_set_btn_ctrl(obj, btn_idx, LV_BTNMATRIX_CTRL_CHECKED);
}
/**
* Check if any of the buttons in the first row has the LV_BTNMATRIX_CTRL_POPOVER control flag set.
* @param obj Button matrix object
* @return true if at least one button has the flag, false otherwise
*/
static bool has_popovers_in_top_row(lv_obj_t * obj)
{
lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;
if (btnm->row_cnt <= 0) {
return false;
}
const char ** map_row = btnm->map_p;
uint16_t btn_cnt = 0;
while (map_row[btn_cnt] && strcmp(map_row[btn_cnt], "\n") != 0 && map_row[btn_cnt][0] != '\0') {
if (button_is_popover(btnm->ctrl_bits[btn_cnt])) {
return true;
}
btn_cnt++;
}
return false;
}
#endif

View File

@ -39,6 +39,7 @@ enum {
LV_BTNMATRIX_CTRL_CHECKABLE = 0x0040, /**< The button can be toggled.*/
LV_BTNMATRIX_CTRL_CHECKED = 0x0080, /**< Button is currently toggled (e.g. checked).*/
LV_BTNMATRIX_CTRL_CLICK_TRIG = 0x0100, /**< 1: Send LV_EVENT_VALUE_CHANGE on CLICK, 0: Send LV_EVENT_VALUE_CHANGE on PRESS*/
LV_BTNMATRIX_CTRL_POPOVER = 0x0200, /**< Show a popover when pressing this key*/
LV_BTNMATRIX_CTRL_RECOLOR = 0x1000, /**< Enable text recoloring with `#color`*/
_LV_BTNMATRIX_CTRL_RESERVED = 0x2000, /**< Reserved for later use*/
LV_BTNMATRIX_CTRL_CUSTOM_1 = 0x4000, /**< Custom free to use flag*/
@ -57,6 +58,7 @@ typedef struct {
lv_area_t * button_areas; /*Array of areas of buttons*/
lv_btnmatrix_ctrl_t * ctrl_bits; /*Array of control bytes*/
uint16_t btn_cnt; /*Number of button in 'map_p'(Handled by the library)*/
uint16_t row_cnt; /*Number of rows in 'map_p'(Handled by the library)*/
uint16_t btn_id_sel; /*Index of the active button (being pressed/released etc) or LV_BTNMATRIX_BTN_NONE*/
uint8_t one_check : 1; /*Single button toggled at once*/
} lv_btnmatrix_t;
@ -130,7 +132,7 @@ void lv_btnmatrix_set_btn_ctrl(lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctr
* @param btn_id 0 based index of the button to modify. (Not counting new lines)
* @param ctrl OR-ed attributs. E.g. `LV_BTNMATRIX_CTRL_NO_REPEAT | LV_BTNMATRIX_CTRL_CHECKABLE`
*/
void lv_btnmatrix_clear_btn_ctrl(const lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctrl_t ctrl);
void lv_btnmatrix_clear_btn_ctrl(lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctrl_t ctrl);
/**
* Set attributes of all buttons of a button matrix
@ -210,7 +212,6 @@ bool lv_btnmatrix_has_btn_ctrl(lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctr
*/
bool lv_btnmatrix_get_one_checked(const lv_obj_t * obj);
/**********************
* MACROS
**********************/