From 64bd9053a25297f7a442ca831c7da5b44bd33f84 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Wed, 12 Jun 2024 17:54:40 +1000 Subject: Update LVGL to v9.1.0 --- .../src/widgets/buttonmatrix/lv_buttonmatrix.c | 1042 ++++++++++++++++++++ .../src/widgets/buttonmatrix/lv_buttonmatrix.h | 222 +++++ 2 files changed, 1264 insertions(+) create mode 100644 lib/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.c create mode 100644 lib/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.h (limited to 'lib/lvgl/src/widgets/buttonmatrix') diff --git a/lib/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.c b/lib/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.c new file mode 100644 index 00000000..9b67c31b --- /dev/null +++ b/lib/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.c @@ -0,0 +1,1042 @@ +/** + * @file lv_btnmatrix.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_buttonmatrix.h" +#if LV_USE_BUTTONMATRIX != 0 + +#include "../../misc/lv_assert.h" +#include "../../indev/lv_indev.h" +#include "../../core/lv_group.h" +#include "../../draw/lv_draw.h" +#include "../../core/lv_refr.h" +#include "../../misc/lv_text.h" +#include "../../misc/lv_text_ap.h" +#include "../../stdlib/lv_string.h" + +/********************* + * DEFINES + *********************/ +#define MY_CLASS (&lv_buttonmatrix_class) + +#define BTN_EXTRA_CLICK_AREA_MAX (LV_DPI_DEF / 10) +#define LV_BUTTONMATRIX_WIDTH_MASK 0x000F + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void lv_buttonmatrix_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void lv_buttonmatrix_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void lv_buttonmatrix_event(const lv_obj_class_t * class_p, lv_event_t * e); +static void draw_main(lv_event_t * e); + +static uint32_t get_button_width(lv_buttonmatrix_ctrl_t ctrl_bits); +static bool button_is_hidden(lv_buttonmatrix_ctrl_t ctrl_bits); +static bool button_is_checked(lv_buttonmatrix_ctrl_t ctrl_bits); +static bool button_is_repeat_disabled(lv_buttonmatrix_ctrl_t ctrl_bits); +static bool button_is_inactive(lv_buttonmatrix_ctrl_t ctrl_bits); +static bool button_is_click_trig(lv_buttonmatrix_ctrl_t ctrl_bits); +static bool button_is_popover(lv_buttonmatrix_ctrl_t ctrl_bits); +static bool button_is_checkable(lv_buttonmatrix_ctrl_t ctrl_bits); +static bool button_get_checked(lv_buttonmatrix_ctrl_t ctrl_bits); +static uint32_t get_button_from_point(lv_obj_t * obj, lv_point_t * p); +static void allocate_button_areas_and_controls(const lv_obj_t * obj, const char ** map); +static void invalidate_button_area(const lv_obj_t * obj, uint32_t btn_idx); +static void make_one_button_checked(lv_obj_t * obj, uint32_t btn_idx); +static bool has_popovers_in_top_row(lv_obj_t * obj); + +/********************** + * STATIC VARIABLES + **********************/ +#if LV_WIDGETS_HAS_DEFAULT_VALUE +static const char * lv_buttonmatrix_def_map[] = {"Btn1", "Btn2", "Btn3", "\n", "Btn4", "Btn5", ""}; +#endif + +const lv_obj_class_t lv_buttonmatrix_class = { + .constructor_cb = lv_buttonmatrix_constructor, + .destructor_cb = lv_buttonmatrix_destructor, + .event_cb = lv_buttonmatrix_event, + .width_def = LV_DPI_DEF * 2, + .height_def = LV_DPI_DEF, + .instance_size = sizeof(lv_buttonmatrix_t), + .editable = LV_OBJ_CLASS_EDITABLE_TRUE, + .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, + .base_class = &lv_obj_class, + .name = "btnmatrix", +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_obj_t * lv_buttonmatrix_create(lv_obj_t * parent) +{ + LV_LOG_INFO("begin"); + lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); + lv_obj_class_init_obj(obj); + return obj; +} + +/*===================== + * Setter functions + *====================*/ + +void lv_buttonmatrix_set_map(lv_obj_t * obj, const char * map[]) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + if(map == NULL) return; + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + + /*Analyze the map and create the required number of buttons*/ + allocate_button_areas_and_controls(obj, map); + btnm->map_p = map; + + lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN); + + /*Set size and positions of the buttons*/ + int32_t sleft = lv_obj_get_style_space_left(obj, LV_PART_MAIN); + int32_t stop = lv_obj_get_style_space_top(obj, LV_PART_MAIN); + int32_t prow = lv_obj_get_style_pad_row(obj, LV_PART_MAIN); + int32_t pcol = lv_obj_get_style_pad_column(obj, LV_PART_MAIN); + + int32_t max_w = lv_obj_get_content_width(obj); + int32_t max_h = lv_obj_get_content_height(obj); + + /*Calculate the position of each row*/ + int32_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)*/ + uint32_t txt_tot_i = 0; /*Act. index in the str map*/ + uint32_t btn_tot_i = 0; /*Act. index of button areas*/ + const char ** map_row = map; + + /*Count the units and the buttons in a line*/ + uint32_t row; + for(row = 0; row < btnm->row_cnt; row++) { + uint32_t unit_cnt = 0; /*Number of units in a row*/ + uint32_t btn_cnt = 0; /*Number of buttons in a row*/ + /*Count the buttons and units in this row*/ + while(map_row[btn_cnt] && strcmp(map_row[btn_cnt], "\n") != 0 && map_row[btn_cnt][0] != '\0') { + unit_cnt += get_button_width(btnm->ctrl_bits[btn_tot_i + btn_cnt]); + btn_cnt++; + } + + /*Only deal with the non empty lines*/ + if(btn_cnt == 0) { + map_row = &map_row[btn_cnt + 1]; /*Set the map to the next row*/ + continue; + } + + int32_t row_y1 = stop + (max_h_no_gap * row) / btnm->row_cnt + row * prow; + int32_t row_y2 = stop + (max_h_no_gap * (row + 1)) / btnm->row_cnt + row * prow - 1; + + /*Set the button size and positions*/ + int32_t max_w_no_gap = max_w - (pcol * (btn_cnt - 1)); + if(max_w_no_gap < 0) max_w_no_gap = 0; + + uint32_t row_unit_cnt = 0; /*The current unit position in the row*/ + uint32_t btn; + for(btn = 0; btn < btn_cnt; btn++, btn_tot_i++, txt_tot_i++) { + uint32_t btn_u = get_button_width(btnm->ctrl_bits[btn_tot_i]); + + int32_t btn_x1 = (max_w_no_gap * row_unit_cnt) / unit_cnt + btn * pcol; + int32_t btn_x2 = (max_w_no_gap * (row_unit_cnt + btn_u)) / unit_cnt + btn * pcol - 1; + + /*If RTL start from the right*/ + if(base_dir == LV_BASE_DIR_RTL) { + int32_t tmp = btn_x1; + btn_x1 = btn_x2; + btn_x2 = tmp; + + btn_x1 = max_w - btn_x1; + btn_x2 = max_w - btn_x2; + } + + btn_x1 += sleft; + btn_x2 += sleft; + + lv_area_set(&btnm->button_areas[btn_tot_i], btn_x1, row_y1, btn_x2, row_y2); + + row_unit_cnt += btn_u; + } + + 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); +} + +void lv_buttonmatrix_set_ctrl_map(lv_obj_t * obj, const lv_buttonmatrix_ctrl_t ctrl_map[]) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + lv_memcpy(btnm->ctrl_bits, ctrl_map, sizeof(lv_buttonmatrix_ctrl_t) * btnm->btn_cnt); + + lv_buttonmatrix_set_map(obj, btnm->map_p); +} + +void lv_buttonmatrix_set_selected_button(lv_obj_t * obj, uint32_t btn_id) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + + if(btn_id >= btnm->btn_cnt && btn_id != LV_BUTTONMATRIX_BUTTON_NONE) return; + + invalidate_button_area(obj, btnm->btn_id_sel); + btnm->btn_id_sel = btn_id; + invalidate_button_area(obj, btn_id); +} + +void lv_buttonmatrix_set_button_ctrl(lv_obj_t * obj, uint32_t btn_id, lv_buttonmatrix_ctrl_t ctrl) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + + if(btn_id >= btnm->btn_cnt) return; + + if(btnm->one_check && (ctrl & LV_BUTTONMATRIX_CTRL_CHECKED)) { + lv_buttonmatrix_clear_button_ctrl_all(obj, LV_BUTTONMATRIX_CTRL_CHECKED); + } + + btnm->ctrl_bits[btn_id] |= ctrl; + invalidate_button_area(obj, btn_id); + + if(ctrl & LV_BUTTONMATRIX_CTRL_POPOVER) { + lv_obj_refresh_ext_draw_size(obj); + } +} + +void lv_buttonmatrix_clear_button_ctrl(lv_obj_t * obj, uint32_t btn_id, lv_buttonmatrix_ctrl_t ctrl) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + + if(btn_id >= btnm->btn_cnt) return; + + btnm->ctrl_bits[btn_id] &= (~ctrl); + invalidate_button_area(obj, btn_id); + + if(ctrl & LV_BUTTONMATRIX_CTRL_POPOVER) { + lv_obj_refresh_ext_draw_size(obj); + } +} + +void lv_buttonmatrix_set_button_ctrl_all(lv_obj_t * obj, lv_buttonmatrix_ctrl_t ctrl) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + uint32_t i; + for(i = 0; i < btnm->btn_cnt; i++) { + lv_buttonmatrix_set_button_ctrl(obj, i, ctrl); + } +} + +void lv_buttonmatrix_clear_button_ctrl_all(lv_obj_t * obj, lv_buttonmatrix_ctrl_t ctrl) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + uint32_t i; + for(i = 0; i < btnm->btn_cnt; i++) { + lv_buttonmatrix_clear_button_ctrl(obj, i, ctrl); + } +} + +void lv_buttonmatrix_set_button_width(lv_obj_t * obj, uint32_t btn_id, uint32_t width) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + if(btn_id >= btnm->btn_cnt) return; + btnm->ctrl_bits[btn_id] &= (~LV_BUTTONMATRIX_WIDTH_MASK); + btnm->ctrl_bits[btn_id] |= (LV_BUTTONMATRIX_WIDTH_MASK & width); + + lv_buttonmatrix_set_map(obj, btnm->map_p); +} + +void lv_buttonmatrix_set_one_checked(lv_obj_t * obj, bool en) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + btnm->one_check = en; + + /*If more than one button is toggled only the first one should be*/ + make_one_button_checked(obj, 0); +} + +/*===================== + * Getter functions + *====================*/ + +const char ** lv_buttonmatrix_get_map(const lv_obj_t * obj) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + return btnm->map_p; +} + +uint32_t lv_buttonmatrix_get_selected_button(const lv_obj_t * obj) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + return btnm->btn_id_sel; +} + +const char * lv_buttonmatrix_get_button_text(const lv_obj_t * obj, uint32_t btn_id) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + if(btn_id == LV_BUTTONMATRIX_BUTTON_NONE) return NULL; + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + if(btn_id > btnm->btn_cnt) return NULL; + + uint32_t txt_i = 0; + uint32_t btn_i = 0; + + /*Search the text of btnm->btn_pr the buttons text in the map + *Skip "\n"-s*/ + while(btn_i != btn_id) { + btn_i++; + txt_i++; + if(strcmp(btnm->map_p[txt_i], "\n") == 0) txt_i++; + } + + if(btn_i == btnm->btn_cnt) return NULL; + + return btnm->map_p[txt_i]; +} + +bool lv_buttonmatrix_has_button_ctrl(lv_obj_t * obj, uint32_t btn_id, lv_buttonmatrix_ctrl_t ctrl) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + if(btn_id >= btnm->btn_cnt) return false; + + return (btnm->ctrl_bits[btn_id] & ctrl) == ctrl; +} + +bool lv_buttonmatrix_get_one_checked(const lv_obj_t * obj) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + + return btnm->one_check; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void lv_buttonmatrix_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) +{ + LV_UNUSED(class_p); + LV_TRACE_OBJ_CREATE("begin"); + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + btnm->btn_cnt = 0; + btnm->row_cnt = 0; + btnm->btn_id_sel = LV_BUTTONMATRIX_BUTTON_NONE; + btnm->button_areas = NULL; + btnm->ctrl_bits = NULL; + btnm->map_p = NULL; + btnm->one_check = 0; + +#if LV_WIDGETS_HAS_DEFAULT_VALUE + lv_buttonmatrix_set_map(obj, lv_buttonmatrix_def_map); +#endif + + LV_TRACE_OBJ_CREATE("finished"); +} + +static void lv_buttonmatrix_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) +{ + LV_TRACE_OBJ_CREATE("begin"); + LV_UNUSED(class_p); + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + lv_free(btnm->button_areas); + lv_free(btnm->ctrl_bits); + btnm->button_areas = NULL; + btnm->ctrl_bits = NULL; + LV_TRACE_OBJ_CREATE("finished"); +} + +static void lv_buttonmatrix_event(const lv_obj_class_t * class_p, lv_event_t * e) +{ + LV_UNUSED(class_p); + + lv_result_t res; + + /*Call the ancestor's event handler*/ + res = lv_obj_event_base(MY_CLASS, e); + if(res != LV_RESULT_OK) return; + + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t * obj = lv_event_get_current_target(e); + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + lv_point_t p; + + if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) { + if(has_popovers_in_top_row(obj)) { + /*reserve one row worth of extra space to account for popovers in the top row*/ + int32_t s = btnm->row_cnt > 0 ? lv_obj_get_content_height(obj) / btnm->row_cnt : 0; + lv_event_set_ext_draw_size(e, s); + } + } + if(code == LV_EVENT_STYLE_CHANGED) { + lv_buttonmatrix_set_map(obj, btnm->map_p); + } + else if(code == LV_EVENT_SIZE_CHANGED) { + lv_buttonmatrix_set_map(obj, btnm->map_p); + } + else if(code == LV_EVENT_PRESSED) { + lv_indev_t * indev = lv_event_get_indev(e); + invalidate_button_area(obj, btnm->btn_id_sel); + + lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_active()); + if(indev_type == LV_INDEV_TYPE_POINTER || indev_type == LV_INDEV_TYPE_BUTTON) { + uint32_t btn_pr; + /*Search the pressed area*/ + lv_indev_get_point(indev, &p); + btn_pr = get_button_from_point(obj, &p); + /*Handle the case where there is no button there*/ + btnm->btn_id_sel = LV_BUTTONMATRIX_BUTTON_NONE; + if(btn_pr != LV_BUTTONMATRIX_BUTTON_NONE) { + if(button_is_inactive(btnm->ctrl_bits[btn_pr]) == false && + button_is_hidden(btnm->ctrl_bits[btn_pr]) == false) { + btnm->btn_id_sel = btn_pr; + invalidate_button_area(obj, btnm->btn_id_sel); /*Invalidate the new area*/ + } + } + else { + btnm->btn_id_sel = LV_BUTTONMATRIX_BUTTON_NONE; + } + } + + if(btnm->btn_id_sel != LV_BUTTONMATRIX_BUTTON_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; + res = lv_obj_send_event(obj, LV_EVENT_VALUE_CHANGED, &b); + if(res != LV_RESULT_OK) return; + } + } + } + else if(code == LV_EVENT_PRESSING) { + /*If a slid to a new button, discard the current button and don't press any buttons*/ + if(btnm->btn_id_sel != LV_BUTTONMATRIX_BUTTON_NONE) { + lv_indev_t * indev = lv_event_get_indev(e); + lv_indev_get_point(indev, &p); + uint32_t btn_pr = get_button_from_point(obj, &p); + if(btn_pr != btnm->btn_id_sel) { + invalidate_button_area(obj, btnm->btn_id_sel); /*Invalidate the old area*/ + btnm->btn_id_sel = LV_BUTTONMATRIX_BUTTON_NONE; + } + } + } + else if(code == LV_EVENT_RELEASED) { + if(btnm->btn_id_sel != LV_BUTTONMATRIX_BUTTON_NONE) { + /*Toggle the button if enabled*/ + if(button_is_checkable(btnm->ctrl_bits[btnm->btn_id_sel]) && + !button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel])) { + if(button_get_checked(btnm->ctrl_bits[btnm->btn_id_sel]) && !btnm->one_check) { + btnm->ctrl_bits[btnm->btn_id_sel] &= (~LV_BUTTONMATRIX_CTRL_CHECKED); + } + else { + btnm->ctrl_bits[btnm->btn_id_sel] |= LV_BUTTONMATRIX_CTRL_CHECKED; + } + if(btnm->one_check) make_one_button_checked(obj, btnm->btn_id_sel); + } + + 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; + res = lv_obj_send_event(obj, LV_EVENT_VALUE_CHANGED, &b); + if(res != LV_RESULT_OK) return; + } + } + + /*Invalidate to old pressed area*/; + invalidate_button_area(obj, btnm->btn_id_sel); + + } + else if(code == LV_EVENT_LONG_PRESSED_REPEAT) { + if(btnm->btn_id_sel != LV_BUTTONMATRIX_BUTTON_NONE) { + if(button_is_repeat_disabled(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; + res = lv_obj_send_event(obj, LV_EVENT_VALUE_CHANGED, &b); + if(res != LV_RESULT_OK) return; + } + } + } + else if(code == LV_EVENT_PRESS_LOST) { + invalidate_button_area(obj, btnm->btn_id_sel); + btnm->btn_id_sel = LV_BUTTONMATRIX_BUTTON_NONE; + } + else if(code == LV_EVENT_FOCUSED) { + if(btnm->btn_cnt == 0) return; + + lv_indev_t * indev = lv_event_get_indev(e); + lv_indev_type_t indev_type = lv_indev_get_type(indev); + + /*If not focused by an input device assume the last input device*/ + if(indev == NULL) { + indev = lv_indev_get_next(NULL); + indev_type = lv_indev_get_type(indev); + } + + bool editing = lv_group_get_editing(lv_obj_get_group(obj)); + /*Focus the first button if there is not selected button*/ + if(btnm->btn_id_sel == LV_BUTTONMATRIX_BUTTON_NONE) { + if(indev_type == LV_INDEV_TYPE_KEYPAD || (indev_type == LV_INDEV_TYPE_ENCODER && editing)) { + uint32_t b = 0; + if(btnm->one_check) { + while(b < btnm->btn_cnt && + (button_is_hidden(btnm->ctrl_bits[b]) || + button_is_inactive(btnm->ctrl_bits[b]) || + button_is_checked(btnm->ctrl_bits[b]) == false)) { + b++; + } + } + else { + while(b < btnm->btn_cnt && + (button_is_hidden(btnm->ctrl_bits[b]) || + button_is_inactive(btnm->ctrl_bits[b]))) { + b++; + } + } + + btnm->btn_id_sel = b; + } + else { + btnm->btn_id_sel = LV_BUTTONMATRIX_BUTTON_NONE; + } + } + } + else if(code == LV_EVENT_DEFOCUSED || code == LV_EVENT_LEAVE) { + // TODO + // if(btnm->btn_id_sel != LV_BUTTONMATRIX_BUTTON_NONE) invalidate_button_area(obj, btnm->btn_id_sel); + // btnm->btn_id_sel = LV_BUTTONMATRIX_BUTTON_NONE; + } + else if(code == LV_EVENT_KEY) { + + invalidate_button_area(obj, btnm->btn_id_sel); + + uint32_t c = lv_event_get_key(e); + if(c == LV_KEY_RIGHT) { + if(btnm->btn_id_sel == LV_BUTTONMATRIX_BUTTON_NONE) btnm->btn_id_sel = 0; + else btnm->btn_id_sel++; + if(btnm->btn_id_sel >= btnm->btn_cnt) btnm->btn_id_sel = 0; + + uint32_t btn_id_start = btnm->btn_id_sel; + while(button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) || button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel])) { + btnm->btn_id_sel++; + if(btnm->btn_id_sel >= btnm->btn_cnt) btnm->btn_id_sel = 0; + + if(btnm->btn_id_sel == btn_id_start) { + btnm->btn_id_sel = LV_BUTTONMATRIX_BUTTON_NONE; + break; + } + } + } + else if(c == LV_KEY_LEFT) { + if(btnm->btn_id_sel == LV_BUTTONMATRIX_BUTTON_NONE) btnm->btn_id_sel = 0; + + if(btnm->btn_id_sel == 0) btnm->btn_id_sel = btnm->btn_cnt - 1; + else if(btnm->btn_id_sel > 0) btnm->btn_id_sel--; + + uint32_t btn_id_start = btnm->btn_id_sel; + while(button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) || button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel])) { + if(btnm->btn_id_sel > 0) btnm->btn_id_sel--; + else btnm->btn_id_sel = btnm->btn_cnt - 1; + + if(btnm->btn_id_sel == btn_id_start) { + btnm->btn_id_sel = LV_BUTTONMATRIX_BUTTON_NONE; + break; + } + } + } + else if(c == LV_KEY_DOWN) { + int32_t col_gap = lv_obj_get_style_pad_column(obj, LV_PART_MAIN); + /*Find the area below the current*/ + if(btnm->btn_id_sel == LV_BUTTONMATRIX_BUTTON_NONE) { + btnm->btn_id_sel = 0; + while(button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) || button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel])) { + btnm->btn_id_sel++; + if(btnm->btn_id_sel >= btnm->btn_cnt) { + btnm->btn_id_sel = LV_BUTTONMATRIX_BUTTON_NONE; + break; + } + } + } + else { + uint32_t area_below; + int32_t pr_center = + btnm->button_areas[btnm->btn_id_sel].x1 + (lv_area_get_width(&btnm->button_areas[btnm->btn_id_sel]) >> 1); + + for(area_below = btnm->btn_id_sel; area_below < btnm->btn_cnt; area_below++) { + if(btnm->button_areas[area_below].y1 > btnm->button_areas[btnm->btn_id_sel].y1 && + pr_center >= btnm->button_areas[area_below].x1 && + pr_center <= btnm->button_areas[area_below].x2 + col_gap && + button_is_inactive(btnm->ctrl_bits[area_below]) == false && + button_is_hidden(btnm->ctrl_bits[area_below]) == false) { + break; + } + } + + if(area_below < btnm->btn_cnt) btnm->btn_id_sel = area_below; + } + } + else if(c == LV_KEY_UP) { + int32_t col_gap = lv_obj_get_style_pad_column(obj, LV_PART_MAIN); + /*Find the area below the current*/ + if(btnm->btn_id_sel == LV_BUTTONMATRIX_BUTTON_NONE) { + btnm->btn_id_sel = 0; + while(button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) || button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel])) { + btnm->btn_id_sel++; + if(btnm->btn_id_sel >= btnm->btn_cnt) { + btnm->btn_id_sel = LV_BUTTONMATRIX_BUTTON_NONE; + break; + } + } + } + else { + int16_t area_above; + int32_t pr_center = + btnm->button_areas[btnm->btn_id_sel].x1 + (lv_area_get_width(&btnm->button_areas[btnm->btn_id_sel]) >> 1); + + for(area_above = btnm->btn_id_sel; area_above >= 0; area_above--) { + if(btnm->button_areas[area_above].y1 < btnm->button_areas[btnm->btn_id_sel].y1 && + pr_center >= btnm->button_areas[area_above].x1 - col_gap && + pr_center <= btnm->button_areas[area_above].x2 && + button_is_inactive(btnm->ctrl_bits[area_above]) == false && + button_is_hidden(btnm->ctrl_bits[area_above]) == false) { + break; + } + } + if(area_above >= 0) btnm->btn_id_sel = area_above; + } + } + + invalidate_button_area(obj, btnm->btn_id_sel); + } + else if(code == LV_EVENT_DRAW_MAIN) { + draw_main(e); + } + +} + +static void draw_main(lv_event_t * e) +{ + lv_obj_t * obj = lv_event_get_current_target(e); + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + if(btnm->btn_cnt == 0) return; + + lv_layer_t * layer = lv_event_get_layer(e); + obj->skip_trans = 1; + + lv_area_t area_obj; + lv_obj_get_coords(obj, &area_obj); + + lv_area_t btn_area; + + uint32_t btn_i = 0; + uint32_t txt_i = 0; + + lv_draw_rect_dsc_t draw_rect_dsc_act; + lv_draw_label_dsc_t draw_label_dsc_act; + + lv_draw_rect_dsc_t draw_rect_dsc_def; + lv_draw_label_dsc_t draw_label_dsc_def; + + lv_state_t state_ori = obj->state; + obj->state = LV_STATE_DEFAULT; + obj->skip_trans = 1; + lv_draw_rect_dsc_init(&draw_rect_dsc_def); + lv_draw_label_dsc_init(&draw_label_dsc_def); + lv_obj_init_draw_rect_dsc(obj, LV_PART_ITEMS, &draw_rect_dsc_def); + lv_obj_init_draw_label_dsc(obj, LV_PART_ITEMS, &draw_label_dsc_def); + obj->skip_trans = 0; + obj->state = state_ori; + + int32_t ptop = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); + int32_t pbottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN); + int32_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); + int32_t pright = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); + +#if LV_USE_ARABIC_PERSIAN_CHARS + char txt_ap[256]; +#endif + + for(btn_i = 0; btn_i < btnm->btn_cnt; btn_i++, txt_i++) { + /*Search the next valid text in the map*/ + while(strcmp(btnm->map_p[txt_i], "\n") == 0) { + txt_i++; + } + + /*Skip hidden buttons*/ + if(button_is_hidden(btnm->ctrl_bits[btn_i])) continue; + + /*Get the state of the button*/ + lv_state_t btn_state = LV_STATE_DEFAULT; + if(button_get_checked(btnm->ctrl_bits[btn_i])) btn_state |= LV_STATE_CHECKED; + + if(button_is_inactive(btnm->ctrl_bits[btn_i])) btn_state |= LV_STATE_DISABLED; + else if(btn_i == btnm->btn_id_sel) { + if(state_ori & LV_STATE_PRESSED) btn_state |= LV_STATE_PRESSED; + if(state_ori & LV_STATE_FOCUSED) btn_state |= LV_STATE_FOCUSED; + if(state_ori & LV_STATE_FOCUS_KEY) btn_state |= LV_STATE_FOCUS_KEY; + if(state_ori & LV_STATE_EDITED) btn_state |= LV_STATE_EDITED; + } + + /*Get the button's area*/ + lv_area_copy(&btn_area, &btnm->button_areas[btn_i]); + btn_area.x1 += area_obj.x1; + btn_area.y1 += area_obj.y1; + btn_area.x2 += area_obj.x1; + btn_area.y2 += area_obj.y1; + + /*Set up the draw descriptors*/ + if(btn_state == LV_STATE_DEFAULT) { + lv_memcpy(&draw_rect_dsc_act, &draw_rect_dsc_def, sizeof(lv_draw_rect_dsc_t)); + lv_memcpy(&draw_label_dsc_act, &draw_label_dsc_def, sizeof(lv_draw_label_dsc_t)); + } + /*In other cases get the styles directly without caching them*/ + else { + obj->state = btn_state; + obj->skip_trans = 1; + lv_draw_rect_dsc_init(&draw_rect_dsc_act); + lv_draw_label_dsc_init(&draw_label_dsc_act); + lv_obj_init_draw_rect_dsc(obj, LV_PART_ITEMS, &draw_rect_dsc_act); + lv_obj_init_draw_label_dsc(obj, LV_PART_ITEMS, &draw_label_dsc_act); + obj->state = state_ori; + obj->skip_trans = 0; + } + + draw_rect_dsc_act.base.id1 = btn_i; + + /*Remove borders on the edges if `LV_BORDER_SIDE_INTERNAL`*/ + if(draw_rect_dsc_act.border_side & LV_BORDER_SIDE_INTERNAL) { + draw_rect_dsc_act.border_side = LV_BORDER_SIDE_FULL; + if(btn_area.x1 == obj->coords.x1 + pleft) draw_rect_dsc_act.border_side &= ~LV_BORDER_SIDE_LEFT; + if(btn_area.x2 == obj->coords.x2 - pright) draw_rect_dsc_act.border_side &= ~LV_BORDER_SIDE_RIGHT; + if(btn_area.y1 == obj->coords.y1 + ptop) draw_rect_dsc_act.border_side &= ~LV_BORDER_SIDE_TOP; + if(btn_area.y2 == obj->coords.y2 - pbottom) draw_rect_dsc_act.border_side &= ~LV_BORDER_SIDE_BOTTOM; + } + + int32_t btn_height = lv_area_get_height(&btn_area); + + if((btn_state & LV_STATE_PRESSED) && (btnm->ctrl_bits[btn_i] & LV_BUTTONMATRIX_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(layer, &draw_rect_dsc_act, &btn_area); + + /*Calculate the size of the text*/ + const lv_font_t * font = draw_label_dsc_act.font; + int32_t letter_space = draw_label_dsc_act.letter_space; + int32_t line_space = draw_label_dsc_act.line_space; + const char * txt = btnm->map_p[txt_i]; + +#if LV_USE_ARABIC_PERSIAN_CHARS + /*Get the size of the Arabic text and process it*/ + size_t len_ap = _lv_text_ap_calc_bytes_count(txt); + if(len_ap < sizeof(txt_ap)) { + _lv_text_ap_proc(txt, txt_ap); + txt = txt_ap; + } +#endif + lv_point_t txt_size; + lv_text_get_size(&txt_size, txt, font, letter_space, + line_space, lv_area_get_width(&area_obj), draw_label_dsc_act.flag); + + btn_area.x1 += (lv_area_get_width(&btn_area) - txt_size.x) / 2; + btn_area.y1 += (lv_area_get_height(&btn_area) - txt_size.y) / 2; + 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_BUTTONMATRIX_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*/ + draw_label_dsc_act.text = txt; + draw_label_dsc_act.text_local = true; + draw_label_dsc_act.base.id1 = btn_i; + lv_draw_label(layer, &draw_label_dsc_act, &btn_area); + } + + obj->skip_trans = 0; +} +/** + * Create the required number of buttons and control bytes according to a map + * @param obj pointer to button matrix object + * @param map_p pointer to a string array + */ +static void allocate_button_areas_and_controls(const lv_obj_t * obj, const char ** map) +{ + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + btnm->row_cnt = 1; + /*Count the buttons in the map*/ + uint32_t btn_cnt = 0; + uint32_t i = 0; + while(map[i] && map[i][0] != '\0') { + if(strcmp(map[i], "\n") != 0) { /*Do not count line breaks*/ + btn_cnt++; + } + else { + btnm->row_cnt++; + } + i++; + } + + /*Do not allocate memory for the same amount of buttons*/ + if(btn_cnt == btnm->btn_cnt) return; + + if(btnm->button_areas != NULL) { + lv_free(btnm->button_areas); + btnm->button_areas = NULL; + } + if(btnm->ctrl_bits != NULL) { + lv_free(btnm->ctrl_bits); + btnm->ctrl_bits = NULL; + } + + btnm->button_areas = lv_malloc(sizeof(lv_area_t) * btn_cnt); + LV_ASSERT_MALLOC(btnm->button_areas); + btnm->ctrl_bits = lv_malloc(sizeof(lv_buttonmatrix_ctrl_t) * btn_cnt); + LV_ASSERT_MALLOC(btnm->ctrl_bits); + if(btnm->button_areas == NULL || btnm->ctrl_bits == NULL) btn_cnt = 0; + + lv_memzero(btnm->ctrl_bits, sizeof(lv_buttonmatrix_ctrl_t) * btn_cnt); + + btnm->btn_cnt = btn_cnt; +} + +/** + * Get the width of a button in units (default is 1). + * @param ctrl_bits least significant 3 bits used (1..7 valid values) + * @return the width of the button in units + */ +static uint32_t get_button_width(lv_buttonmatrix_ctrl_t ctrl_bits) +{ + uint32_t w = ctrl_bits & LV_BUTTONMATRIX_WIDTH_MASK; + return w != 0 ? w : 1; +} + +static bool button_is_hidden(lv_buttonmatrix_ctrl_t ctrl_bits) +{ + return ctrl_bits & LV_BUTTONMATRIX_CTRL_HIDDEN; +} + +static bool button_is_checked(lv_buttonmatrix_ctrl_t ctrl_bits) +{ + return ctrl_bits & LV_BUTTONMATRIX_CTRL_CHECKED; +} + +static bool button_is_repeat_disabled(lv_buttonmatrix_ctrl_t ctrl_bits) +{ + return ctrl_bits & LV_BUTTONMATRIX_CTRL_NO_REPEAT; +} + +static bool button_is_inactive(lv_buttonmatrix_ctrl_t ctrl_bits) +{ + return ctrl_bits & LV_BUTTONMATRIX_CTRL_DISABLED; +} + +static bool button_is_click_trig(lv_buttonmatrix_ctrl_t ctrl_bits) +{ + return ctrl_bits & LV_BUTTONMATRIX_CTRL_CLICK_TRIG; +} + +static bool button_is_popover(lv_buttonmatrix_ctrl_t ctrl_bits) +{ + return ctrl_bits & LV_BUTTONMATRIX_CTRL_POPOVER; +} + +static bool button_is_checkable(lv_buttonmatrix_ctrl_t ctrl_bits) +{ + return ctrl_bits & LV_BUTTONMATRIX_CTRL_CHECKABLE; +} + +static bool button_get_checked(lv_buttonmatrix_ctrl_t ctrl_bits) +{ + return ctrl_bits & LV_BUTTONMATRIX_CTRL_CHECKED; +} + +/** + * Gives the button id of a button under a given point + * @param obj pointer to a button matrix object + * @param p a point with absolute coordinates + * @return the id of the button or LV_BUTTONMATRIX_BUTTON_NONE. + */ +static uint32_t get_button_from_point(lv_obj_t * obj, lv_point_t * p) +{ + lv_area_t obj_cords; + lv_area_t btn_area; + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + uint32_t i; + lv_obj_get_coords(obj, &obj_cords); + + int32_t w = lv_obj_get_width(obj); + int32_t h = lv_obj_get_height(obj); + int32_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); + int32_t pright = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); + int32_t ptop = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); + int32_t pbottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN); + int32_t prow = lv_obj_get_style_pad_row(obj, LV_PART_MAIN); + int32_t pcol = lv_obj_get_style_pad_column(obj, LV_PART_MAIN); + + /*Get the half gap. Button look larger with this value. (+1 for rounding error)*/ + prow = (prow / 2) + 1 + (prow & 1); + pcol = (pcol / 2) + 1 + (pcol & 1); + + prow = LV_MIN(prow, BTN_EXTRA_CLICK_AREA_MAX); + pcol = LV_MIN(pcol, BTN_EXTRA_CLICK_AREA_MAX); + pright = LV_MIN(pright, BTN_EXTRA_CLICK_AREA_MAX); + ptop = LV_MIN(ptop, BTN_EXTRA_CLICK_AREA_MAX); + pbottom = LV_MIN(pbottom, BTN_EXTRA_CLICK_AREA_MAX); + + for(i = 0; i < btnm->btn_cnt; i++) { + lv_area_copy(&btn_area, &btnm->button_areas[i]); + if(btn_area.x1 <= pleft) btn_area.x1 += obj_cords.x1 - LV_MIN(pleft, BTN_EXTRA_CLICK_AREA_MAX); + else btn_area.x1 += obj_cords.x1 - pcol; + + if(btn_area.y1 <= ptop) btn_area.y1 += obj_cords.y1 - LV_MIN(ptop, BTN_EXTRA_CLICK_AREA_MAX); + else btn_area.y1 += obj_cords.y1 - prow; + + if(btn_area.x2 >= w - pright - 2) btn_area.x2 += obj_cords.x1 + LV_MIN(pright, + BTN_EXTRA_CLICK_AREA_MAX); /*-2 for rounding error*/ + else btn_area.x2 += obj_cords.x1 + pcol; + + if(btn_area.y2 >= h - pbottom - 2) btn_area.y2 += obj_cords.y1 + LV_MIN(pbottom, + BTN_EXTRA_CLICK_AREA_MAX); /*-2 for rounding error*/ + else btn_area.y2 += obj_cords.y1 + prow; + + if(_lv_area_is_point_on(&btn_area, p, 0) != false) { + break; + } + } + + if(i == btnm->btn_cnt) i = LV_BUTTONMATRIX_BUTTON_NONE; + + return i; +} + +static void invalidate_button_area(const lv_obj_t * obj, uint32_t btn_idx) +{ + if(btn_idx == LV_BUTTONMATRIX_BUTTON_NONE) return; + + lv_area_t btn_area; + lv_area_t obj_area; + + lv_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + if(btn_idx >= btnm->btn_cnt) return; + + lv_area_copy(&btn_area, &btnm->button_areas[btn_idx]); + lv_obj_get_coords(obj, &obj_area); + + /*The buttons might have outline and shadow so make the invalidation larger with the gaps between the buttons. + *It assumes that the outline or shadow is smaller than the gaps*/ + int32_t row_gap = lv_obj_get_style_pad_row(obj, LV_PART_MAIN); + int32_t col_gap = lv_obj_get_style_pad_column(obj, LV_PART_MAIN); + + /*Be sure to have a minimal extra space if row/col_gap is small*/ + int32_t dpi = lv_display_get_dpi(lv_obj_get_display(obj)); + row_gap = LV_MAX(row_gap, dpi / 10); + col_gap = LV_MAX(col_gap, dpi / 10); + + /*Convert relative coordinates to absolute*/ + btn_area.x1 += obj_area.x1 - row_gap; + btn_area.y1 += obj_area.y1 - col_gap; + 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_BUTTONMATRIX_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); +} + +/** + * Enforces a single button being toggled on the button matrix. + * It simply clears the toggle flag on other buttons. + * @param obj Button matrix object + * @param btn_idx Button that should remain toggled + */ +static void make_one_button_checked(lv_obj_t * obj, uint32_t btn_idx) +{ + /*Save whether the button was toggled*/ + bool was_toggled = lv_buttonmatrix_has_button_ctrl(obj, btn_idx, LV_BUTTONMATRIX_CTRL_CHECKED); + + lv_buttonmatrix_clear_button_ctrl_all(obj, LV_BUTTONMATRIX_CTRL_CHECKED); + + if(was_toggled) lv_buttonmatrix_set_button_ctrl(obj, btn_idx, LV_BUTTONMATRIX_CTRL_CHECKED); +} + +/** + * Check if any of the buttons in the first row has the LV_BUTTONMATRIX_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_buttonmatrix_t * btnm = (lv_buttonmatrix_t *)obj; + + if(btnm->row_cnt <= 0) { + return false; + } + + const char ** map_row = btnm->map_p; + uint32_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 diff --git a/lib/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.h b/lib/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.h new file mode 100644 index 00000000..64c0d1b5 --- /dev/null +++ b/lib/lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.h @@ -0,0 +1,222 @@ +/** + * @file lv_btnmatrix.h + * + */ + +#ifndef LV_BUTTONMATRIX_H +#define LV_BUTTONMATRIX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_BUTTONMATRIX != 0 + +#include "../../core/lv_obj.h" + +/********************* + * DEFINES + *********************/ +#define LV_BUTTONMATRIX_BUTTON_NONE 0xFFFF +LV_EXPORT_CONST_INT(LV_BUTTONMATRIX_BUTTON_NONE); + +/********************** + * TYPEDEFS + **********************/ + +/** Type to store button control bits (disabled, hidden etc.) + * The first 3 bits are used to store the width*/ +enum _lv_buttonmatrix_ctrl_t { + _LV_BUTTONMATRIX_WIDTH = 0x000F, /**< Reserved to store the size units*/ + LV_BUTTONMATRIX_CTRL_HIDDEN = 0x0010, /**< Button hidden*/ + LV_BUTTONMATRIX_CTRL_NO_REPEAT = 0x0020, /**< Do not repeat press this button.*/ + LV_BUTTONMATRIX_CTRL_DISABLED = 0x0040, /**< Disable this button.*/ + LV_BUTTONMATRIX_CTRL_CHECKABLE = 0x0080, /**< The button can be toggled.*/ + LV_BUTTONMATRIX_CTRL_CHECKED = 0x0100, /**< Button is currently toggled (e.g. checked).*/ + LV_BUTTONMATRIX_CTRL_CLICK_TRIG = 0x0200, /**< 1: Send LV_EVENT_VALUE_CHANGE on CLICK, 0: Send LV_EVENT_VALUE_CHANGE on PRESS*/ + LV_BUTTONMATRIX_CTRL_POPOVER = 0x0400, /**< Show a popover when pressing this key*/ + _LV_BUTTONMATRIX_CTRL_RESERVED_1 = 0x0800, /**< Reserved for later use*/ + _LV_BUTTONMATRIX_CTRL_RESERVED_2 = 0x1000, /**< Reserved for later use*/ + _LV_BUTTONMATRIX_CTRL_RESERVED_3 = 0x2000, /**< Reserved for later use*/ + LV_BUTTONMATRIX_CTRL_CUSTOM_1 = 0x4000, /**< Custom free to use flag*/ + LV_BUTTONMATRIX_CTRL_CUSTOM_2 = 0x8000, /**< Custom free to use flag*/ +}; + +#ifdef DOXYGEN +typedef _lv_buttonmatrix_ctrl_t lv_buttonmatrix_ctrl_t; +#else +typedef uint32_t lv_buttonmatrix_ctrl_t; +#endif /*DOXYGEN*/ + +typedef bool (*lv_buttonmatrix_button_draw_cb_t)(lv_obj_t * btnm, uint32_t btn_id, const lv_area_t * draw_area, + const lv_area_t * clip_area); + +/*Data of button matrix*/ +typedef struct { + lv_obj_t obj; + const char ** map_p; /*Pointer to the current map*/ + lv_area_t * button_areas; /*Array of areas of buttons*/ + lv_buttonmatrix_ctrl_t * ctrl_bits; /*Array of control bytes*/ + uint32_t btn_cnt; /*Number of button in 'map_p'(Handled by the library)*/ + uint32_t row_cnt; /*Number of rows in 'map_p'(Handled by the library)*/ + uint32_t btn_id_sel; /*Index of the active button (being pressed/released etc) or LV_BUTTONMATRIX_BUTTON_NONE*/ + uint32_t one_check : 1; /*Single button toggled at once*/ +} lv_buttonmatrix_t; + +LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_buttonmatrix_class; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a button matrix object + * @param parent pointer to an object, it will be the parent of the new button matrix + * @return pointer to the created button matrix + */ +lv_obj_t * lv_buttonmatrix_create(lv_obj_t * parent); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new map. Buttons will be created/deleted according to the map. The + * button matrix keeps a reference to the map and so the string array must not + * be deallocated during the life of the matrix. + * @param obj pointer to a button matrix object + * @param map pointer a string array. The last string has to be: "". Use "\n" to make a line break. + */ +void lv_buttonmatrix_set_map(lv_obj_t * obj, const char * map[]); + +/** + * Set the button control map (hidden, disabled etc.) for a button matrix. + * The control map array will be copied and so may be deallocated after this + * function returns. + * @param obj pointer to a button matrix object + * @param ctrl_map pointer to an array of `lv_button_ctrl_t` control bytes. The + * length of the array and position of the elements must match + * the number and order of the individual buttons (i.e. excludes + * newline entries). + * An element of the map should look like e.g.: + * `ctrl_map[0] = width | LV_BUTTONMATRIX_CTRL_NO_REPEAT | LV_BUTTONMATRIX_CTRL_TGL_ENABLE` + */ +void lv_buttonmatrix_set_ctrl_map(lv_obj_t * obj, const lv_buttonmatrix_ctrl_t ctrl_map[]); + +/** + * Set the selected buttons + * @param obj pointer to button matrix object + * @param btn_id 0 based index of the button to modify. (Not counting new lines) + */ +void lv_buttonmatrix_set_selected_button(lv_obj_t * obj, uint32_t btn_id); + +/** + * Set the attributes of a button of the button matrix + * @param obj pointer to button matrix object + * @param btn_id 0 based index of the button to modify. (Not counting new lines) + * @param ctrl OR-ed attributes. E.g. `LV_BUTTONMATRIX_CTRL_NO_REPEAT | LV_BUTTONMATRIX_CTRL_CHECKABLE` + */ +void lv_buttonmatrix_set_button_ctrl(lv_obj_t * obj, uint32_t btn_id, lv_buttonmatrix_ctrl_t ctrl); + +/** + * Clear the attributes of a button of the button matrix + * @param obj pointer to button matrix object + * @param btn_id 0 based index of the button to modify. (Not counting new lines) + * @param ctrl OR-ed attributes. E.g. `LV_BUTTONMATRIX_CTRL_NO_REPEAT | LV_BUTTONMATRIX_CTRL_CHECKABLE` + */ +void lv_buttonmatrix_clear_button_ctrl(lv_obj_t * obj, uint32_t btn_id, lv_buttonmatrix_ctrl_t ctrl); + +/** + * Set attributes of all buttons of a button matrix + * @param obj pointer to a button matrix object + * @param ctrl attribute(s) to set from `lv_buttonmatrix_ctrl_t`. Values can be ORed. + */ +void lv_buttonmatrix_set_button_ctrl_all(lv_obj_t * obj, lv_buttonmatrix_ctrl_t ctrl); + +/** + * Clear the attributes of all buttons of a button matrix + * @param obj pointer to a button matrix object + * @param ctrl attribute(s) to set from `lv_buttonmatrix_ctrl_t`. Values can be ORed. + * @param en true: set the attributes; false: clear the attributes + */ +void lv_buttonmatrix_clear_button_ctrl_all(lv_obj_t * obj, lv_buttonmatrix_ctrl_t ctrl); + +/** + * Set a single button's relative width. + * This method will cause the matrix be regenerated and is a relatively + * expensive operation. It is recommended that initial width be specified using + * `lv_buttonmatrix_set_ctrl_map` and this method only be used for dynamic changes. + * @param obj pointer to button matrix object + * @param btn_id 0 based index of the button to modify. + * @param width relative width compared to the buttons in the same row. [1..15] + */ +void lv_buttonmatrix_set_button_width(lv_obj_t * obj, uint32_t btn_id, uint32_t width); + +/** + * Make the button matrix like a selector widget (only one button may be checked at a time). + * `LV_BUTTONMATRIX_CTRL_CHECKABLE` must be enabled on the buttons to be selected using + * `lv_buttonmatrix_set_ctrl()` or `lv_buttonmatrix_set_button_ctrl_all()`. + * @param obj pointer to a button matrix object + * @param en whether "one check" mode is enabled + */ +void lv_buttonmatrix_set_one_checked(lv_obj_t * obj, bool en); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the current map of a button matrix + * @param obj pointer to a button matrix object + * @return the current map + */ +const char ** lv_buttonmatrix_get_map(const lv_obj_t * obj); + +/** + * Get the index of the lastly "activated" button by the user (pressed, released, focused etc) + * Useful in the `event_cb` to get the text of the button, check if hidden etc. + * @param obj pointer to button matrix object + * @return index of the last released button (LV_BUTTONMATRIX_BUTTON_NONE: if unset) + */ +uint32_t lv_buttonmatrix_get_selected_button(const lv_obj_t * obj); + +/** + * Get the button's text + * @param obj pointer to button matrix object + * @param btn_id the index a button not counting new line characters. + * @return text of btn_index` button + */ +const char * lv_buttonmatrix_get_button_text(const lv_obj_t * obj, uint32_t btn_id); + +/** + * Get the whether a control value is enabled or disabled for button of a button matrix + * @param obj pointer to a button matrix object + * @param btn_id the index of a button not counting new line characters. + * @param ctrl control values to check (ORed value can be used) + * @return true: the control attribute is enabled false: disabled + */ +bool lv_buttonmatrix_has_button_ctrl(lv_obj_t * obj, uint32_t btn_id, lv_buttonmatrix_ctrl_t ctrl); + +/** + * Tell whether "one check" mode is enabled or not. + * @param obj Button matrix object + * @return true: "one check" mode is enabled; false: disabled + */ +bool lv_buttonmatrix_get_one_checked(const lv_obj_t * obj); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_BUTTONMATRIX*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_BUTTONMATRIX_H*/ -- cgit v1.2.3