From b31a6687047dc460a9ab174f005fa8d043c498f4 Mon Sep 17 00:00:00 2001 From: Ken Carpenter Date: Tue, 16 May 2023 16:55:03 -0700 Subject: [PATCH 1/9] First pass - scrolling still needs to be added --- lib/lv_bindings/lv_conf.h | 2 +- ports/stm32/boards/Passport/manifest.py | 1 + .../modules/pages/file_picker_page.py | 149 ++++++++++------- ports/stm32/boards/Passport/modules/utils.py | 17 +- .../boards/Passport/modules/views/__init__.py | 1 + .../boards/Passport/modules/views/table.py | 157 ++++++++++++++++++ 6 files changed, 259 insertions(+), 68 deletions(-) create mode 100644 ports/stm32/boards/Passport/modules/views/table.py diff --git a/lib/lv_bindings/lv_conf.h b/lib/lv_bindings/lv_conf.h index 412145e1f..7804156fb 100644 --- a/lib/lv_bindings/lv_conf.h +++ b/lib/lv_bindings/lv_conf.h @@ -448,7 +448,7 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ # define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/ #endif -#define LV_USE_TABLE 0 +#define LV_USE_TABLE 1 #define LV_USE_QRCODE 1 diff --git a/ports/stm32/boards/Passport/manifest.py b/ports/stm32/boards/Passport/manifest.py index ba65a05bb..83603afc5 100644 --- a/ports/stm32/boards/Passport/manifest.py +++ b/ports/stm32/boards/Passport/manifest.py @@ -326,6 +326,7 @@ 'views/statusbar.py', 'views/switch.py', 'views/symbol_picker.py', + 'views/table.py', 'views/text_area.py', 'views/text_input.py', 'views/view.py')) diff --git a/ports/stm32/boards/Passport/modules/pages/file_picker_page.py b/ports/stm32/boards/Passport/modules/pages/file_picker_page.py index e81113083..78ea8a0eb 100644 --- a/ports/stm32/boards/Passport/modules/pages/file_picker_page.py +++ b/ports/stm32/boards/Passport/modules/pages/file_picker_page.py @@ -6,11 +6,12 @@ import lvgl as lv import microns -from styles.colors import TEXT_GREY, SCROLLBAR_BG_COLOR +from styles.colors import TEXT_GREY, WHITE, FD_BLUE from styles import Stylize from pages import Page -from views import FileItem, View +from views import Table from micropython import const +from constants import MENU_ITEM_CORNER_RADIUS import passport MAX_FILE_DISPLAY = const(15) if passport.IS_COLOR else const(10) @@ -33,93 +34,123 @@ def __init__(self, default.flex_fill() default.flex_align(main=lv.FLEX_ALIGN.CENTER, cross=lv.FLEX_ALIGN.CENTER, track=lv.FLEX_ALIGN.CENTER) default.pad_row(0) + default.bg_color(WHITE) # Set non-style props self.set_width(lv.pct(100)) self.set_no_scroll() + self.table = Table(self.files) + self.table.set_width(lv.pct(100)) + self.table.set_height(lv.pct(100)) + self.table.set_scroll_dir(lv.DIR.VER) + + with Stylize(self.table, lv.PART.MAIN) as default: + default.bg_color(WHITE) + default.radius(4) + default.border_width(0) + + with Stylize(self.table, lv.PART.ITEMS) as items: + items.bg_color(WHITE) + items.text_color(TEXT_GREY) + items.border_width(0) + items.radius(MENU_ITEM_CORNER_RADIUS) + + with Stylize(self.table, lv.PART.ITEMS | lv.STATE.FOCUS_KEY) as focused: + focused.bg_color(FD_BLUE) + + + self.add_child(self.table) + # Add a scroll container for the list items, but disable scrollbars until attached - self.scroll_container = View(flex_flow=lv.FLEX_FLOW.COLUMN) - self.scroll_container.set_no_scroll() - self.scroll_container.set_width(lv.pct(100)) + # self.scroll_container = View(flex_flow=lv.FLEX_FLOW.COLUMN) + # self.scroll_container.set_no_scroll() + # self.scroll_container.set_width(lv.pct(100)) - with Stylize(self.scroll_container) as default: - default.flex_fill() - default.pad_row(0) + # with Stylize(self.scroll_container) as default: + # default.flex_fill() + # default.pad_row(0) - # Adjust scrollbar position - with Stylize(self.scroll_container, selector=lv.PART.SCROLLBAR) as scrollbar: - scrollbar.pad(right=0) - if not passport.IS_COLOR: - scrollbar.bg_color(SCROLLBAR_BG_COLOR) + # # Adjust scrollbar position + # with Stylize(self.scroll_container, selector=lv.PART.SCROLLBAR) as scrollbar: + # scrollbar.pad(right=0) # Add the file items to the scroll container - num_files = min(MAX_FILE_DISPLAY, len(self.files)) - for index in range(num_files): - filename, _full_path, is_folder = self.files[index] - self.scroll_container.add_child( - FileItem(filename=filename, is_folder=is_folder)) + # num_files = min(MAX_FILE_DISPLAY, len(self.files)) + # for index in range(num_files): + # filename, _full_path, is_folder = self.files[index] + # self.scroll_container.add_child( + # FileItem(filename=filename, is_folder=is_folder)) - self.add_child(self.scroll_container) - async def display(self, auto_close_timeout=None): - from pages import ErrorPage + # async def display(self, auto_close_timeout=None): + # from pages import ErrorPage - if len(self.files) > MAX_FILE_DISPLAY: - await ErrorPage(text="Unable to display all files. Displaying the first " - "{} files alphabetically.".format(MAX_FILE_DISPLAY)).show() + # if len(self.files) > MAX_FILE_DISPLAY: + # await ErrorPage(text="Unable to display all files. Displaying the first " + # "{} files alphabetically.".format(MAX_FILE_DISPLAY)).show() - await super().display() + # await super().display() def attach(self, group): super().attach(group) + group.add_obj(self.table.lvgl_root) # IMPORTANT: Add this to the group AFTER setting up gridnav - # Ensure scrollbars are enabled again - self.scroll_container.set_scroll_dir(lv.DIR.VER) + # # Ensure scrollbars are enabled again + # # self.scroll_container.set_scroll_dir(lv.DIR.VER) - # Setup gridnav for the layout - lv.gridnav_add(self.scroll_container.lvgl_root, lv.GRIDNAV_CTRL.NONE) - group.add_obj(self.scroll_container.lvgl_root) # IMPORTANT: Add this to the group AFTER setting up gridnav - def detach(self): - lv.group_remove_obj(self.scroll_container.lvgl_root) + # # Setup gridnav for the layout + # # lv.gridnav_add(self.scroll_container.lvgl_root, lv.GRIDNAV_CTRL.NONE) + # # group.add_obj(self.scroll_container.lvgl_root) # IMPORTANT: Add this to the group AFTER setting up gridnav - # Hide scrollbars during transitions - self.scroll_container.set_no_scroll() + def detach(self): + self.table.set_no_scroll() super().detach() - def get_selected_option_index_by_value(self, value): - for index in range(len(self.options)): - entry = self.options[index] - if entry.get('value') == value: - return index + # # Hide scrollbars during transitions + # self.scroll_container.set_no_scroll() + # super().detach() + + # def get_selected_option_index_by_value(self, value): + # for index in range(len(self.options)): + # entry = self.options[index] + # if entry.get('value') == value: + # return index - return 0 + # return 0 - def get_focused_item_index(self): - if self.is_mounted(): - focused_item = lv.gridnav_get_focused(self.scroll_container.lvgl_root) + # def get_focused_item_index(self): + # if self.is_mounted(): + # focused_item = lv.gridnav_get_focused(self.scroll_container.lvgl_root) - # Look through the children to find what index the selected one is at - for index in range(len(self.scroll_container.children)): - item = self.scroll_container.children[index] - if item.lvgl_root == focused_item: - return index + # # Look through the children to find what index the selected one is at + # for index in range(len(self.scroll_container.children)): + # item = self.scroll_container.children[index] + # if item.lvgl_root == focused_item: + # return index - # Should never happen - assert(False) - return None + # # Should never happen + # assert(False) + # return None def left_action(self, is_pressed): if not is_pressed: self.set_result(None) def right_action(self, is_pressed): - if not is_pressed: - try: - selected_option_idx = self.get_focused_item_index() - selected_file = self.files[selected_option_idx] - # print('Selected file: {}'.format(selected_file)) - self.set_result(selected_file) - except Exception as e: - assert(False, '{}'.format(e)) + selected_idx = self.table.get_selected_row() + + print('selected: {}'.format(selected_idx)) + + # if not is_pressed: + # self.set_result(None) + + # if not is_pressed: + # try: + # selected_option_idx = self.get_focused_item_index() + # selected_file = self.files[selected_option_idx] + # # print('Selected file: {}'.format(selected_file)) + # self.set_result(selected_file) + # except Exception as e: + # assert(False, '{}'.format(e)) diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index f3d3a7cde..71966f239 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -1184,14 +1184,15 @@ def restore_sd_cb(): prev_sd_card_cb = CardSlot.get_sd_card_change_cb() CardSlot.set_sd_card_change_cb(sd_card_cb) - try: - await page.display() - except Exception as e: - page.unmount() - restore_sd_cb() - await on_result(None) - await ErrorPage(text='Unable to display page.').show() - return + # try: + await page.display() + # except Exception as e: + # print(e) + # page.unmount() + # restore_sd_cb() + # await on_result(None) + # await ErrorPage(text='Unable to display page.').show() + # return g = page.poll_for_done() while True: diff --git a/ports/stm32/boards/Passport/modules/views/__init__.py b/ports/stm32/boards/Passport/modules/views/__init__.py index f9932094d..58a6d6acb 100644 --- a/ports/stm32/boards/Passport/modules/views/__init__.py +++ b/ports/stm32/boards/Passport/modules/views/__init__.py @@ -32,6 +32,7 @@ from .card_nav import * from .slider import * from .switch import * +from .table import * from .color_picker import * from .pin_input import * from .symbol_picker import * diff --git a/ports/stm32/boards/Passport/modules/views/table.py b/ports/stm32/boards/Passport/modules/views/table.py new file mode 100644 index 000000000..ef6843489 --- /dev/null +++ b/ports/stm32/boards/Passport/modules/views/table.py @@ -0,0 +1,157 @@ +# SPDX-FileCopyrightText: © 2022 Foundation Devices, Inc. +# SPDX-License-Identifier: GPL-3.0-or-later +# +# table.py - An LVGL table component wrapper + +import lvgl as lv +# from styles import Stylize, LocalStyle +from views import View +from styles.colors import WHITE, TEXT_GREY + +class Table(View): + def __init__(self, items=[], col_widths=[212]): + super().__init__() + self.items = items + self.col_widths = col_widths + + def set_src(self, src): + self.src = src + self.update() + + def change_event_cb(self, e): + print('change event!') + # obj = e.get_target() + row = lv.C_Pointer() + col = lv.C_Pointer() + self.lvgl_root.get_selected_cell(row, col) + print("row: ",row.uint_val) + + # chk = table.has_cell_ctrl(row.uint_val, 0, lv.table.CELL_CTRL.CUSTOM_1) + # if chk: + # table.clear_cell_ctrl(row.uint_val, 0, lv.table.CELL_CTRL.CUSTOM_1) + # else: + # table.add_cell_ctrl(row.uint_val, 0, lv.table.CELL_CTRL.CUSTOM + + def draw_part_begin_event_cb(self, e): + dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param()) + + # If the cells are drawn... + if dsc.part == lv.PART.ITEMS: + label_dsc = lv.draw_label_dsc_t.__cast__(dsc.label_dsc) + # print("draw 3: dir rect_dsc={}".format(dir(label_dsc))) + label_dsc.ofs_x += 28 + + def draw_part_end_event_cb(self, e): + print("Draw part end 1") + dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param()) + + # If the cells are drawn... + if dsc.part == lv.PART.ITEMS: + + # # If the cells are drawn... + # print("Draw part end 3") + # if dsc.part == lv.PART.ITEMS: + # Draw the icon + obj = e.get_target() + is_folder = obj.has_cell_ctrl(dsc.id, 0, lv.table.CELL_CTRL.CUSTOM_1) + + icon_area = lv.area_t() + icon = None + if is_folder: + print("FOLDER-----------------------------------------") + icon = lv.ICON_FOLDER + else: + print("FILE-------------------------------------------") + icon= lv.ICON_FILE + + # print("dir(dsc)={}".format(dir(dsc))) + print("p1={} p2={}".format(dsc.p1, dsc.p2)) + + selected_row = self.get_selected_row() + col_count = self.lvgl_root.get_row_cnt() + print("--> id={}".format(dsc.id)) + draw_row = dsc.id % col_count + draw_col = dsc.id // col_count + + print("--> selected_row={}".format(selected_row)) + print("--> draw_row={}".format(draw_row)) + print("--> draw_col={}".format(draw_col)) + selected = draw_row == selected_row + if selected: + print("Draw WHITE icon") + icon_color = WHITE + else: + print("Draw grey icon") + icon_color = TEXT_GREY + + + icon_img_dsc = lv.draw_img_dsc_t() + icon_img_dsc.init() + icon_img_dsc.recolor = icon_color + icon_img_dsc.recolor_opa=255 + + print("draw_area: x1={} x2={} y1={} y2={}".format( + dsc.draw_area.x1, + dsc.draw_area.x2, + dsc.draw_area.y1, + dsc.draw_area.y2 + )) + + # Setup the draw area + icon_area.x1 = 10 + dsc.draw_area.x1 + icon_area.y1 = dsc.draw_area.y1 + ((dsc.draw_area.y2 - dsc.draw_area.y1) - icon.header.h) // 2 + icon_area.x2 = icon_area.x1 + icon.header.w - 1 + icon_area.y2 = icon_area.y1 + icon.header.h - 1 + print("icon_area: x1={} x2={} y1={} y2={}".format( + icon_area.x1, + icon_area.x2, + icon_area.y1, + icon_area.y2 + )) + + print("clip_area: x1={} x2={} y1={} y2={}".format( + dsc.clip_area.x1, + dsc.clip_area.x2, + dsc.clip_area.y1, + dsc.clip_area.y2 + )) + # lv.draw_rect(icon_area, dsc.clip_area, rect_dsc) + lv.draw_img(icon_area, dsc.clip_area, icon, icon_img_dsc) + print("===============================================") + + + def create_lvgl_root(self, lvgl_parent): + table = lv.table(lvgl_parent) + + # root.set_row_cnt(len(self.items)) + # root.set_col_cnt(len(self.col_widths)) + + # Don't make the cell pressed, we will draw something different in the event + # table.remove_style(None, lv.PART.ITEMS | lv.STATE.PRESSED) + + for i, item in enumerate(self.items): + # print("data: {}: {}".format(i, item)) + (filename, _full_path, is_folder) = item + # root.set_cell_value(i, 0, ">" if is_folder else "F") + table.set_cell_value(i,0, filename) + if is_folder: + table.add_cell_ctrl(i, 0, lv.table.CELL_CTRL.CUSTOM_1) + else: + table.clear_cell_ctrl(i, 0, lv.table.CELL_CTRL.CUSTOM_1) + + for i, width in enumerate(self.col_widths): + # print("width: {}: {}".format(i, width)) + table.set_col_width(i, width) + + # Add an event callback to apply some custom drawing + table.add_event_cb(self.draw_part_begin_event_cb, lv.EVENT.DRAW_PART_BEGIN, None) + table.add_event_cb(self.draw_part_end_event_cb, lv.EVENT.DRAW_PART_END, None) + table.add_event_cb(self.change_event_cb, lv.EVENT.VALUE_CHANGED, None) + return table + + def get_selected_row(self): + row = lv.C_Pointer() + _col = lv.C_Pointer() + self.lvgl_root.get_selected_cell(row, _col) + print("row: {}".format(row.uint_val)) + return row.uint_val \ No newline at end of file From ee60dc30783d1358f828920278bd7318f16add82 Mon Sep 17 00:00:00 2001 From: Ken Carpenter Date: Thu, 25 May 2023 09:47:47 -0400 Subject: [PATCH 2/9] Make scrolling to selected cell work Backported the v8.3 lv_table code for scrolling to selected cell Remaining issues: - Last item is clipped by 1-2 pixels, so cell height calculation may be incorrect - Need to implement key handling to select the active cell --- lib/lv_bindings/lvgl/src/widgets/lv_table.c | 247 +++++++++++++++----- 1 file changed, 184 insertions(+), 63 deletions(-) diff --git a/lib/lv_bindings/lvgl/src/widgets/lv_table.c b/lib/lv_bindings/lvgl/src/widgets/lv_table.c index a07c3249b..cf040c84d 100644 --- a/lib/lv_bindings/lvgl/src/widgets/lv_table.c +++ b/lib/lv_bindings/lvgl/src/widgets/lv_table.c @@ -36,8 +36,16 @@ static void draw_main(lv_event_t * e); static lv_coord_t get_row_height(lv_obj_t * obj, uint16_t row_id, const lv_font_t * font, lv_coord_t letter_space, lv_coord_t line_space, lv_coord_t cell_left, lv_coord_t cell_right, lv_coord_t cell_top, lv_coord_t cell_bottom); -static void refr_size(lv_obj_t * obj, uint32_t strat_row); -static lv_res_t get_pressed_cell(lv_obj_t * obj, uint16_t * row, uint16_t * col); +static void refr_size_form_row(lv_obj_t *obj, uint32_t strat_row); +static void refr_cell_size(lv_obj_t *obj, uint32_t row, uint32_t col); +static lv_res_t get_pressed_cell(lv_obj_t *obj, uint16_t *row, uint16_t *col); +static void get_cell_area(lv_obj_t *obj, uint16_t row, uint16_t col, lv_area_t *area); +static void scroll_to_selected_cell(lv_obj_t *obj); + +static inline bool is_cell_empty(void *cell) +{ + return cell == NULL; +} /********************** * STATIC VARIABLES @@ -106,9 +114,7 @@ void lv_table_set_cell_value(lv_obj_t * obj, uint16_t row, uint16_t col, const c #endif table->cell_data[cell][0] = ctrl; - refr_size(obj, row); - - lv_obj_invalidate(obj); + refr_cell_size(obj, row, col); } void lv_table_set_cell_value_fmt(lv_obj_t * obj, uint16_t row, uint16_t col, const char * fmt, ...) @@ -235,7 +241,7 @@ void lv_table_set_row_cnt(lv_obj_t * obj, uint16_t row_cnt) lv_memset_00(&table->cell_data[old_cell_cnt], (new_cell_cnt - old_cell_cnt) * sizeof(table->cell_data[0])); } - refr_size(obj, 0) ; + refr_size_form_row(obj, 0) ; } void lv_table_set_col_cnt(lv_obj_t * obj, uint16_t col_cnt) @@ -289,8 +295,7 @@ void lv_table_set_col_cnt(lv_obj_t * obj, uint16_t col_cnt) lv_mem_free(table->cell_data); table->cell_data = new_cell_data; - - refr_size(obj, 0) ; + refr_size_form_row(obj, 0); } void lv_table_set_col_width(lv_obj_t * obj, uint16_t col_id, lv_coord_t w) @@ -303,7 +308,7 @@ void lv_table_set_col_width(lv_obj_t * obj, uint16_t col_id, lv_coord_t w) if(col_id >= table->col_cnt) lv_table_set_col_cnt(obj, col_id + 1); table->col_w[col_id] = w; - refr_size(obj, 0) ; + refr_size_form_row(obj, 0); } void lv_table_add_cell_ctrl(lv_obj_t * obj, uint16_t row, uint16_t col, lv_table_cell_ctrl_t ctrl) @@ -481,7 +486,7 @@ static void lv_table_event(const lv_obj_class_t * class_p, lv_event_t * e) lv_table_t * table = (lv_table_t *)obj; if(code == LV_EVENT_STYLE_CHANGED) { - refr_size(obj, 0); + refr_size_form_row(obj, 0); } else if(code == LV_EVENT_GET_SELF_SIZE) { lv_point_t * p = lv_event_get_param(e); @@ -531,6 +536,7 @@ static void lv_table_event(const lv_obj_class_t * class_p, lv_event_t * e) if(col == LV_TABLE_CELL_NONE || row == LV_TABLE_CELL_NONE) { table->col_act = 0; table->row_act = 0; + scroll_to_selected_cell(obj); lv_obj_invalidate(obj); return; } @@ -575,6 +581,7 @@ static void lv_table_event(const lv_obj_class_t * class_p, lv_event_t * e) table->row_act = row; lv_obj_invalidate(obj); + scroll_to_selected_cell(obj); res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL); if(res != LV_RES_OK) return; } @@ -773,85 +780,137 @@ static void draw_main(lv_event_t * e) } } -static void refr_size(lv_obj_t * obj, uint32_t strat_row) +/* Refreshes size of the table starting from @start_row row */ +static void refr_size_form_row(lv_obj_t *obj, uint32_t start_row) { - lv_table_t * table = (lv_table_t *)obj; + const lv_coord_t cell_pad_left = lv_obj_get_style_pad_left(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_right = lv_obj_get_style_pad_right(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_top = lv_obj_get_style_pad_top(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_ITEMS); + + lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_ITEMS); + lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_ITEMS); + const lv_font_t *font = lv_obj_get_style_text_font(obj, LV_PART_ITEMS); + const lv_coord_t minh = lv_obj_get_style_min_height(obj, LV_PART_ITEMS); + const lv_coord_t maxh = lv_obj_get_style_max_height(obj, LV_PART_ITEMS); + + lv_table_t *table = (lv_table_t *)obj; uint32_t i; + for (i = start_row; i < table->row_cnt; i++) + { + lv_coord_t calculated_height = get_row_height(obj, i, font, letter_space, line_space, + cell_pad_left, cell_pad_right, cell_pad_top, cell_pad_bottom); + table->row_h[i] = LV_CLAMP(minh, calculated_height, maxh); + } - lv_coord_t cell_left = lv_obj_get_style_pad_left(obj, LV_PART_ITEMS); - lv_coord_t cell_right = lv_obj_get_style_pad_right(obj, LV_PART_ITEMS); - lv_coord_t cell_top = lv_obj_get_style_pad_top(obj, LV_PART_ITEMS); - lv_coord_t cell_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_ITEMS); + lv_obj_refresh_self_size(obj); + lv_obj_invalidate(obj); +} + +static void refr_cell_size(lv_obj_t *obj, uint32_t row, uint32_t col) +{ + const lv_coord_t cell_pad_left = lv_obj_get_style_pad_left(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_right = lv_obj_get_style_pad_right(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_top = lv_obj_get_style_pad_top(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_ITEMS); lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_ITEMS); lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_ITEMS); - const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_ITEMS); + const lv_font_t *font = lv_obj_get_style_text_font(obj, LV_PART_ITEMS); - lv_coord_t minh = lv_obj_get_style_min_height(obj, LV_PART_ITEMS); - lv_coord_t maxh = lv_obj_get_style_max_height(obj, LV_PART_ITEMS); + const lv_coord_t minh = lv_obj_get_style_min_height(obj, LV_PART_ITEMS); + const lv_coord_t maxh = lv_obj_get_style_max_height(obj, LV_PART_ITEMS); - for(i = strat_row; i < table->row_cnt; i++) { - table->row_h[i] = get_row_height(obj, i, font, letter_space, line_space, - cell_left, cell_right, cell_top, cell_bottom); - table->row_h[i] = LV_CLAMP(minh, table->row_h[i], maxh); - } + lv_table_t *table = (lv_table_t *)obj; + lv_coord_t calculated_height = get_row_height(obj, row, font, letter_space, line_space, + cell_pad_left, cell_pad_right, cell_pad_top, cell_pad_bottom); - lv_obj_refresh_self_size(obj) ; + lv_coord_t prev_row_size = table->row_h[row]; + table->row_h[row] = LV_CLAMP(minh, calculated_height, maxh); + + /*If the row height havn't changed invalidate only this cell*/ + if (prev_row_size == table->row_h[row]) + { + lv_area_t cell_area; + get_cell_area(obj, row, col, &cell_area); + lv_area_move(&cell_area, obj->coords.x1, obj->coords.y1); + lv_obj_invalidate_area(obj, &cell_area); + } + else + { + lv_obj_refresh_self_size(obj); + lv_obj_invalidate(obj); + } } -static lv_coord_t get_row_height(lv_obj_t * obj, uint16_t row_id, const lv_font_t * font, +static lv_coord_t get_row_height(lv_obj_t *obj, uint16_t row_id, const lv_font_t *font, lv_coord_t letter_space, lv_coord_t line_space, lv_coord_t cell_left, lv_coord_t cell_right, lv_coord_t cell_top, lv_coord_t cell_bottom) { - lv_table_t * table = (lv_table_t *)obj; - lv_point_t txt_size; - lv_coord_t txt_w; + lv_table_t *table = (lv_table_t *)obj; + lv_coord_t h_max = lv_font_get_line_height(font) + cell_top + cell_bottom; + /* Calculate the cell_data index where to start */ uint16_t row_start = row_id * table->col_cnt; + + /* Traverse the cells in the row_id row */ uint16_t cell; uint16_t col; - lv_coord_t h_max = lv_font_get_line_height(font) + cell_top + cell_bottom; + for (cell = row_start, col = 0; cell < row_start + table->col_cnt; cell++, col++) + { + char *cell_data = table->cell_data[cell]; - for(cell = row_start, col = 0; cell < row_start + table->col_cnt; cell++, col++) { - if(table->cell_data[cell] != NULL) { - txt_w = table->col_w[col]; - uint16_t col_merge = 0; - for(col_merge = 0; col_merge + col < table->col_cnt - 1; col_merge++) { + if (is_cell_empty(cell_data)) + { + continue; + } - if(table->cell_data[cell + col_merge] != NULL) { - lv_table_cell_ctrl_t ctrl = 0; - char * next_cell_data = table->cell_data[cell + col_merge]; - if(next_cell_data) ctrl = next_cell_data[0]; - if(ctrl & LV_TABLE_CELL_CTRL_MERGE_RIGHT) - txt_w += table->col_w[col + col_merge + 1]; - else - break; - } - else { - break; - } - } + lv_coord_t txt_w = table->col_w[col]; - lv_table_cell_ctrl_t ctrl = 0; - if(table->cell_data[cell]) ctrl = table->cell_data[cell][0]; + /* Traverse the current row from the first until the penultimate column. + * Increment the text width if the cell has the LV_TABLE_CELL_CTRL_MERGE_RIGHT control, + * exit the traversal when the current cell control is not LV_TABLE_CELL_CTRL_MERGE_RIGHT */ + uint16_t col_merge = 0; + for (col_merge = 0; col_merge + col < table->col_cnt - 1; col_merge++) + { + char *next_cell_data = table->cell_data[cell + col_merge]; - /*With text crop assume 1 line*/ - if(ctrl & LV_TABLE_CELL_CTRL_TEXT_CROP) { - h_max = LV_MAX(lv_font_get_line_height(font) + cell_top + cell_bottom, - h_max); + if (is_cell_empty(next_cell_data)) + break; + + lv_table_cell_ctrl_t ctrl = (lv_table_cell_ctrl_t)next_cell_data[0]; + if (ctrl & LV_TABLE_CELL_CTRL_MERGE_RIGHT) + { + txt_w += table->col_w[col + col_merge + 1]; } - /*Without text crop calculate the height of the text in the cell*/ - else { - txt_w -= cell_left + cell_right; + else + { + break; + } + } - lv_txt_get_size(&txt_size, table->cell_data[cell] + 1, font, - letter_space, line_space, txt_w, LV_TEXT_FLAG_NONE); + lv_table_cell_ctrl_t ctrl = (lv_table_cell_ctrl_t)cell_data[0]; - h_max = LV_MAX(txt_size.y + cell_top + cell_bottom, h_max); - cell += col_merge; - col += col_merge; - } + /*When cropping the text we can assume the row height is equal to the line height*/ + if (ctrl & LV_TABLE_CELL_CTRL_TEXT_CROP) + { + h_max = LV_MAX(lv_font_get_line_height(font) + cell_top + cell_bottom, + h_max); + } + /*Else we have to calculate the height of the cell text*/ + else + { + lv_point_t txt_size; + txt_w -= cell_left + cell_right; + + lv_txt_get_size(&txt_size, table->cell_data[cell] + 1, font, + letter_space, line_space, txt_w, LV_TEXT_FLAG_NONE); + + h_max = LV_MAX(txt_size.y + cell_top + cell_bottom, h_max); + /*Skip until one element after the last merged column*/ + cell += col_merge; + col += col_merge; } } @@ -909,5 +968,67 @@ static lv_res_t get_pressed_cell(lv_obj_t * obj, uint16_t * row, uint16_t * col) return LV_RES_OK; } +static void get_cell_area(lv_obj_t *obj, uint16_t row, uint16_t col, lv_area_t *area) +{ + lv_table_t *table = (lv_table_t *)obj; + + uint32_t c; + area->x1 = 0; + for (c = 0; c < col; c++) + { + area->x1 += table->col_w[c]; + } + + bool rtl = lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL; + if (rtl) + { + area->x1 += lv_obj_get_scroll_x(obj); + lv_coord_t w = lv_obj_get_width(obj); + area->x2 = w - area->x1 - lv_obj_get_style_pad_right(obj, 0); + area->x1 = area->x2 - table->col_w[col]; + } + else + { + area->x1 -= lv_obj_get_scroll_x(obj); + area->x1 += lv_obj_get_style_pad_left(obj, 0); + area->x2 = area->x1 + table->col_w[col] - 1; + } + + uint32_t r; + area->y1 = 0; + for (r = 0; r < row; r++) + { + area->y1 += table->row_h[r]; + } + + area->y1 += lv_obj_get_style_pad_top(obj, 0); + area->y1 -= lv_obj_get_scroll_y(obj); + area->y2 = area->y1 + table->row_h[row] - 1; +} + +static void scroll_to_selected_cell(lv_obj_t *obj) +{ + lv_table_t *table = (lv_table_t *)obj; + + lv_area_t a; + get_cell_area(obj, table->row_act, table->col_act, &a); + if (a.x1 < 0) + { + lv_obj_scroll_by_bounded(obj, -a.x1, 0, LV_ANIM_ON); + } + else if (a.x2 > lv_obj_get_width(obj)) + { + lv_obj_scroll_by_bounded(obj, lv_obj_get_width(obj) - a.x2, 0, LV_ANIM_ON); + } + + if (a.y1 < 0) + { + lv_obj_scroll_by_bounded(obj, 0, -a.y1, LV_ANIM_ON); + } + else if (a.y2 > lv_obj_get_height(obj)) + { + lv_obj_scroll_by_bounded(obj, 0, lv_obj_get_height(obj) - a.y2, LV_ANIM_ON); + } +} #endif From fdfcd7a82e00a4b9e6e9dde40a64dc28f195545b Mon Sep 17 00:00:00 2001 From: Ken Carpenter Date: Tue, 6 Jun 2023 23:18:31 -0700 Subject: [PATCH 3/9] Make table code more generic Add back file/folder selection --- lib/lv_bindings/lvgl/src/widgets/lv_table.c | 4 +- .../modules/pages/file_picker_page.py | 35 ++--- .../boards/Passport/modules/views/table.py | 135 ++++++++---------- 3 files changed, 82 insertions(+), 92 deletions(-) diff --git a/lib/lv_bindings/lvgl/src/widgets/lv_table.c b/lib/lv_bindings/lvgl/src/widgets/lv_table.c index cf040c84d..3412cf152 100644 --- a/lib/lv_bindings/lvgl/src/widgets/lv_table.c +++ b/lib/lv_bindings/lvgl/src/widgets/lv_table.c @@ -745,7 +745,7 @@ static void draw_main(lv_event_t * e) if(table->cell_data[cell]) { txt_area.x1 = cell_area.x1 + cell_left; - txt_area.x2 = cell_area.x2 - cell_right; + txt_area.x2 = cell_area.x2 - cell_right - 28; // FOUNDATION txt_area.y1 = cell_area.y1 + cell_top; txt_area.y2 = cell_area.y2 - cell_bottom; @@ -1027,7 +1027,7 @@ static void scroll_to_selected_cell(lv_obj_t *obj) } else if (a.y2 > lv_obj_get_height(obj)) { - lv_obj_scroll_by_bounded(obj, 0, lv_obj_get_height(obj) - a.y2, LV_ANIM_ON); + lv_obj_scroll_by_bounded(obj, 0, lv_obj_get_height(obj) - a.y2 - 10, LV_ANIM_ON); } } diff --git a/ports/stm32/boards/Passport/modules/pages/file_picker_page.py b/ports/stm32/boards/Passport/modules/pages/file_picker_page.py index 78ea8a0eb..1af865a1e 100644 --- a/ports/stm32/boards/Passport/modules/pages/file_picker_page.py +++ b/ports/stm32/boards/Passport/modules/pages/file_picker_page.py @@ -16,6 +16,13 @@ MAX_FILE_DISPLAY = const(15) if passport.IS_COLOR else const(10) +# Returns a tuple where the first value is the string to show in the list and the second value is +# whether to show the default icon or the alt icon. +# NOTE: Doesn't support showing arbitrary icons at this time. +def get_file_info(item): + (filename, _full_path, is_folder) = item + return (filename, is_folder) + class FilePickerPage(Page): def __init__(self, @@ -40,7 +47,7 @@ def __init__(self, self.set_width(lv.pct(100)) self.set_no_scroll() - self.table = Table(self.files) + self.table = Table(items=self.files, get_cell_info=get_file_info) self.table.set_width(lv.pct(100)) self.table.set_height(lv.pct(100)) self.table.set_scroll_dir(lv.DIR.VER) @@ -139,18 +146,14 @@ def left_action(self, is_pressed): self.set_result(None) def right_action(self, is_pressed): - selected_idx = self.table.get_selected_row() - - print('selected: {}'.format(selected_idx)) - - # if not is_pressed: - # self.set_result(None) - - # if not is_pressed: - # try: - # selected_option_idx = self.get_focused_item_index() - # selected_file = self.files[selected_option_idx] - # # print('Selected file: {}'.format(selected_file)) - # self.set_result(selected_file) - # except Exception as e: - # assert(False, '{}'.format(e)) + print('Right Action!') + if not is_pressed: + try: + selected_idx = self.table.get_selected_row() + print('selected: {}'.format(selected_idx)) + selected_file = self.files[selected_idx] + print('Selected file: {}'.format(selected_file)) + self.set_result(selected_file) + except Exception as e: + print("Exception: {}".format(e)) + assert(False, '{}'.format(e)) \ No newline at end of file diff --git a/ports/stm32/boards/Passport/modules/views/table.py b/ports/stm32/boards/Passport/modules/views/table.py index ef6843489..8c8b79312 100644 --- a/ports/stm32/boards/Passport/modules/views/table.py +++ b/ports/stm32/boards/Passport/modules/views/table.py @@ -4,33 +4,33 @@ # table.py - An LVGL table component wrapper import lvgl as lv -# from styles import Stylize, LocalStyle from views import View from styles.colors import WHITE, TEXT_GREY class Table(View): - def __init__(self, items=[], col_widths=[212]): + def __init__(self, + items=[], + col_width=212, + default_icon=lv.ICON_FILE, + alt_icon=lv.ICON_FOLDER, + get_cell_info=None): super().__init__() self.items = items - self.col_widths = col_widths + self.col_width = col_width + self.default_icon = default_icon + self.alt_icon = alt_icon + self.get_cell_info = get_cell_info def set_src(self, src): self.src = src self.update() - def change_event_cb(self, e): - print('change event!') - # obj = e.get_target() - row = lv.C_Pointer() - col = lv.C_Pointer() - self.lvgl_root.get_selected_cell(row, col) - print("row: ",row.uint_val) - - # chk = table.has_cell_ctrl(row.uint_val, 0, lv.table.CELL_CTRL.CUSTOM_1) - # if chk: - # table.clear_cell_ctrl(row.uint_val, 0, lv.table.CELL_CTRL.CUSTOM_1) - # else: - # table.add_cell_ctrl(row.uint_val, 0, lv.table.CELL_CTRL.CUSTOM + # def change_event_cb(self, e): + # print('change event!') + # # obj = e.get_target() + # row = lv.C_Pointer() + # col = lv.C_Pointer() + # self.lvgl_root.get_selected_cell(row, col) def draw_part_begin_event_cb(self, e): dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param()) @@ -38,50 +38,41 @@ def draw_part_begin_event_cb(self, e): # If the cells are drawn... if dsc.part == lv.PART.ITEMS: label_dsc = lv.draw_label_dsc_t.__cast__(dsc.label_dsc) - # print("draw 3: dir rect_dsc={}".format(dir(label_dsc))) label_dsc.ofs_x += 28 def draw_part_end_event_cb(self, e): - print("Draw part end 1") + # print("Draw part end 1") dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param()) - # If the cells are drawn... + # If the cells are being drawn... if dsc.part == lv.PART.ITEMS: - # # If the cells are drawn... - # print("Draw part end 3") - # if dsc.part == lv.PART.ITEMS: # Draw the icon obj = e.get_target() - is_folder = obj.has_cell_ctrl(dsc.id, 0, lv.table.CELL_CTRL.CUSTOM_1) + is_alt_icon = obj.has_cell_ctrl(dsc.id, 0, lv.table.CELL_CTRL.CUSTOM_1) icon_area = lv.area_t() icon = None - if is_folder: - print("FOLDER-----------------------------------------") - icon = lv.ICON_FOLDER + if is_alt_icon: + icon = self.alt_icon else: - print("FILE-------------------------------------------") - icon= lv.ICON_FILE - - # print("dir(dsc)={}".format(dir(dsc))) - print("p1={} p2={}".format(dsc.p1, dsc.p2)) + icon= self.default_icon selected_row = self.get_selected_row() col_count = self.lvgl_root.get_row_cnt() - print("--> id={}".format(dsc.id)) + # print("--> id={}".format(dsc.id)) draw_row = dsc.id % col_count draw_col = dsc.id // col_count - print("--> selected_row={}".format(selected_row)) - print("--> draw_row={}".format(draw_row)) - print("--> draw_col={}".format(draw_col)) + # print("--> selected_row={}".format(selected_row)) + # print("--> draw_row={}".format(draw_row)) + # print("--> draw_col={}".format(draw_col)) selected = draw_row == selected_row if selected: - print("Draw WHITE icon") + # print("Draw WHITE icon") icon_color = WHITE else: - print("Draw grey icon") + # print("Draw grey icon") icon_color = TEXT_GREY @@ -90,68 +81,64 @@ def draw_part_end_event_cb(self, e): icon_img_dsc.recolor = icon_color icon_img_dsc.recolor_opa=255 - print("draw_area: x1={} x2={} y1={} y2={}".format( - dsc.draw_area.x1, - dsc.draw_area.x2, - dsc.draw_area.y1, - dsc.draw_area.y2 - )) + # print("draw_area: x1={} x2={} y1={} y2={}".format( + # dsc.draw_area.x1, + # dsc.draw_area.x2, + # dsc.draw_area.y1, + # dsc.draw_area.y2 + # )) # Setup the draw area icon_area.x1 = 10 + dsc.draw_area.x1 icon_area.y1 = dsc.draw_area.y1 + ((dsc.draw_area.y2 - dsc.draw_area.y1) - icon.header.h) // 2 icon_area.x2 = icon_area.x1 + icon.header.w - 1 icon_area.y2 = icon_area.y1 + icon.header.h - 1 - print("icon_area: x1={} x2={} y1={} y2={}".format( - icon_area.x1, - icon_area.x2, - icon_area.y1, - icon_area.y2 - )) - - print("clip_area: x1={} x2={} y1={} y2={}".format( - dsc.clip_area.x1, - dsc.clip_area.x2, - dsc.clip_area.y1, - dsc.clip_area.y2 - )) - # lv.draw_rect(icon_area, dsc.clip_area, rect_dsc) + + # print("icon_area: x1={} x2={} y1={} y2={}".format( + # icon_area.x1, + # icon_area.x2, + # icon_area.y1, + # icon_area.y2 + # )) + + # print("clip_area: x1={} x2={} y1={} y2={}".format( + # dsc.clip_area.x1, + # dsc.clip_area.x2, + # dsc.clip_area.y1, + # dsc.clip_area.y2 + # )) + lv.draw_img(icon_area, dsc.clip_area, icon, icon_img_dsc) - print("===============================================") + # print("===============================================") def create_lvgl_root(self, lvgl_parent): table = lv.table(lvgl_parent) - # root.set_row_cnt(len(self.items)) - # root.set_col_cnt(len(self.col_widths)) - - # Don't make the cell pressed, we will draw something different in the event - # table.remove_style(None, lv.PART.ITEMS | lv.STATE.PRESSED) - for i, item in enumerate(self.items): - # print("data: {}: {}".format(i, item)) - (filename, _full_path, is_folder) = item - # root.set_cell_value(i, 0, ">" if is_folder else "F") - table.set_cell_value(i,0, filename) - if is_folder: + if self.get_cell_info is not None: + (label, is_alt_icon) = self.get_cell_info(item) + else: + label = item + is_alt_icon = False + + table.set_cell_value(i,0, label) + if is_alt_icon: table.add_cell_ctrl(i, 0, lv.table.CELL_CTRL.CUSTOM_1) else: table.clear_cell_ctrl(i, 0, lv.table.CELL_CTRL.CUSTOM_1) - for i, width in enumerate(self.col_widths): - # print("width: {}: {}".format(i, width)) - table.set_col_width(i, width) + table.set_col_width(0, self.col_width) # Add an event callback to apply some custom drawing table.add_event_cb(self.draw_part_begin_event_cb, lv.EVENT.DRAW_PART_BEGIN, None) table.add_event_cb(self.draw_part_end_event_cb, lv.EVENT.DRAW_PART_END, None) - table.add_event_cb(self.change_event_cb, lv.EVENT.VALUE_CHANGED, None) + # table.add_event_cb(self.change_event_cb, lv.EVENT.VALUE_CHANGED, None) return table def get_selected_row(self): row = lv.C_Pointer() _col = lv.C_Pointer() self.lvgl_root.get_selected_cell(row, _col) - print("row: {}".format(row.uint_val)) + # print("row: {}".format(row.uint_val)) return row.uint_val \ No newline at end of file From 36a1c257bc77864019038b623835b1f95476b520 Mon Sep 17 00:00:00 2001 From: Ken Carpenter Date: Thu, 8 Jun 2023 18:40:38 -0700 Subject: [PATCH 4/9] Fix lint warnings --- .../modules/pages/file_picker_page.py | 76 +------------------ .../boards/Passport/modules/views/table.py | 53 ++----------- 2 files changed, 10 insertions(+), 119 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/pages/file_picker_page.py b/ports/stm32/boards/Passport/modules/pages/file_picker_page.py index 1af865a1e..bfbba207c 100644 --- a/ports/stm32/boards/Passport/modules/pages/file_picker_page.py +++ b/ports/stm32/boards/Passport/modules/pages/file_picker_page.py @@ -16,9 +16,7 @@ MAX_FILE_DISPLAY = const(15) if passport.IS_COLOR else const(10) -# Returns a tuple where the first value is the string to show in the list and the second value is -# whether to show the default icon or the alt icon. -# NOTE: Doesn't support showing arbitrary icons at this time. + def get_file_info(item): (filename, _full_path, is_folder) = item return (filename, is_folder) @@ -66,94 +64,26 @@ def __init__(self, with Stylize(self.table, lv.PART.ITEMS | lv.STATE.FOCUS_KEY) as focused: focused.bg_color(FD_BLUE) - self.add_child(self.table) - # Add a scroll container for the list items, but disable scrollbars until attached - # self.scroll_container = View(flex_flow=lv.FLEX_FLOW.COLUMN) - # self.scroll_container.set_no_scroll() - # self.scroll_container.set_width(lv.pct(100)) - - # with Stylize(self.scroll_container) as default: - # default.flex_fill() - # default.pad_row(0) - - # # Adjust scrollbar position - # with Stylize(self.scroll_container, selector=lv.PART.SCROLLBAR) as scrollbar: - # scrollbar.pad(right=0) - - # Add the file items to the scroll container - # num_files = min(MAX_FILE_DISPLAY, len(self.files)) - # for index in range(num_files): - # filename, _full_path, is_folder = self.files[index] - # self.scroll_container.add_child( - # FileItem(filename=filename, is_folder=is_folder)) - - - # async def display(self, auto_close_timeout=None): - # from pages import ErrorPage - - # if len(self.files) > MAX_FILE_DISPLAY: - # await ErrorPage(text="Unable to display all files. Displaying the first " - # "{} files alphabetically.".format(MAX_FILE_DISPLAY)).show() - - # await super().display() - def attach(self, group): super().attach(group) - group.add_obj(self.table.lvgl_root) # IMPORTANT: Add this to the group AFTER setting up gridnav - - # # Ensure scrollbars are enabled again - # # self.scroll_container.set_scroll_dir(lv.DIR.VER) - - - # # Setup gridnav for the layout - # # lv.gridnav_add(self.scroll_container.lvgl_root, lv.GRIDNAV_CTRL.NONE) - # # group.add_obj(self.scroll_container.lvgl_root) # IMPORTANT: Add this to the group AFTER setting up gridnav + group.add_obj(self.table.lvgl_root) def detach(self): self.table.set_no_scroll() super().detach() - # # Hide scrollbars during transitions - # self.scroll_container.set_no_scroll() - # super().detach() - - # def get_selected_option_index_by_value(self, value): - # for index in range(len(self.options)): - # entry = self.options[index] - # if entry.get('value') == value: - # return index - - # return 0 - - # def get_focused_item_index(self): - # if self.is_mounted(): - # focused_item = lv.gridnav_get_focused(self.scroll_container.lvgl_root) - - # # Look through the children to find what index the selected one is at - # for index in range(len(self.scroll_container.children)): - # item = self.scroll_container.children[index] - # if item.lvgl_root == focused_item: - # return index - - # # Should never happen - # assert(False) - # return None - def left_action(self, is_pressed): if not is_pressed: self.set_result(None) def right_action(self, is_pressed): - print('Right Action!') if not is_pressed: try: selected_idx = self.table.get_selected_row() - print('selected: {}'.format(selected_idx)) selected_file = self.files[selected_idx] - print('Selected file: {}'.format(selected_file)) self.set_result(selected_file) except Exception as e: print("Exception: {}".format(e)) - assert(False, '{}'.format(e)) \ No newline at end of file + assert(False, '{}'.format(e)) diff --git a/ports/stm32/boards/Passport/modules/views/table.py b/ports/stm32/boards/Passport/modules/views/table.py index 8c8b79312..7dee54f05 100644 --- a/ports/stm32/boards/Passport/modules/views/table.py +++ b/ports/stm32/boards/Passport/modules/views/table.py @@ -7,6 +7,7 @@ from views import View from styles.colors import WHITE, TEXT_GREY + class Table(View): def __init__(self, items=[], @@ -25,13 +26,6 @@ def set_src(self, src): self.src = src self.update() - # def change_event_cb(self, e): - # print('change event!') - # # obj = e.get_target() - # row = lv.C_Pointer() - # col = lv.C_Pointer() - # self.lvgl_root.get_selected_cell(row, col) - def draw_part_begin_event_cb(self, e): dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param()) @@ -39,9 +33,8 @@ def draw_part_begin_event_cb(self, e): if dsc.part == lv.PART.ITEMS: label_dsc = lv.draw_label_dsc_t.__cast__(dsc.label_dsc) label_dsc.ofs_x += 28 - + def draw_part_end_event_cb(self, e): - # print("Draw part end 1") dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param()) # If the cells are being drawn... @@ -56,37 +49,22 @@ def draw_part_end_event_cb(self, e): if is_alt_icon: icon = self.alt_icon else: - icon= self.default_icon + icon = self.default_icon - selected_row = self.get_selected_row() + selected_row = self.get_selected_row() col_count = self.lvgl_root.get_row_cnt() - # print("--> id={}".format(dsc.id)) draw_row = dsc.id % col_count - draw_col = dsc.id // col_count - # print("--> selected_row={}".format(selected_row)) - # print("--> draw_row={}".format(draw_row)) - # print("--> draw_col={}".format(draw_col)) selected = draw_row == selected_row if selected: - # print("Draw WHITE icon") icon_color = WHITE else: - # print("Draw grey icon") icon_color = TEXT_GREY - icon_img_dsc = lv.draw_img_dsc_t() icon_img_dsc.init() icon_img_dsc.recolor = icon_color - icon_img_dsc.recolor_opa=255 - - # print("draw_area: x1={} x2={} y1={} y2={}".format( - # dsc.draw_area.x1, - # dsc.draw_area.x2, - # dsc.draw_area.y1, - # dsc.draw_area.y2 - # )) + icon_img_dsc.recolor_opa = 255 # Setup the draw area icon_area.x1 = 10 + dsc.draw_area.x1 @@ -94,23 +72,7 @@ def draw_part_end_event_cb(self, e): icon_area.x2 = icon_area.x1 + icon.header.w - 1 icon_area.y2 = icon_area.y1 + icon.header.h - 1 - # print("icon_area: x1={} x2={} y1={} y2={}".format( - # icon_area.x1, - # icon_area.x2, - # icon_area.y1, - # icon_area.y2 - # )) - - # print("clip_area: x1={} x2={} y1={} y2={}".format( - # dsc.clip_area.x1, - # dsc.clip_area.x2, - # dsc.clip_area.y1, - # dsc.clip_area.y2 - # )) - lv.draw_img(icon_area, dsc.clip_area, icon, icon_img_dsc) - # print("===============================================") - def create_lvgl_root(self, lvgl_parent): table = lv.table(lvgl_parent) @@ -122,7 +84,7 @@ def create_lvgl_root(self, lvgl_parent): label = item is_alt_icon = False - table.set_cell_value(i,0, label) + table.set_cell_value(i, 0, label) if is_alt_icon: table.add_cell_ctrl(i, 0, lv.table.CELL_CTRL.CUSTOM_1) else: @@ -140,5 +102,4 @@ def get_selected_row(self): row = lv.C_Pointer() _col = lv.C_Pointer() self.lvgl_root.get_selected_cell(row, _col) - # print("row: {}".format(row.uint_val)) - return row.uint_val \ No newline at end of file + return row.uint_val From fb0b65708e430daa0881794a055f759c35e516a3 Mon Sep 17 00:00:00 2001 From: Ken Carpenter Date: Fri, 9 Jun 2023 17:27:29 -0700 Subject: [PATCH 5/9] Make the item size match normal menus --- ports/stm32/boards/Passport/modules/views/table.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/views/table.py b/ports/stm32/boards/Passport/modules/views/table.py index 7dee54f05..0263dacc0 100644 --- a/ports/stm32/boards/Passport/modules/views/table.py +++ b/ports/stm32/boards/Passport/modules/views/table.py @@ -6,7 +6,7 @@ import lvgl as lv from views import View from styles.colors import WHITE, TEXT_GREY - +from styles import Stylize class Table(View): def __init__(self, @@ -22,6 +22,9 @@ def __init__(self, self.alt_icon = alt_icon self.get_cell_info = get_cell_info + with Stylize(self, lv.PART.ITEMS) as default: + default.pad(bottom=11, top=12) + def set_src(self, src): self.src = src self.update() @@ -103,3 +106,4 @@ def get_selected_row(self): _col = lv.C_Pointer() self.lvgl_root.get_selected_cell(row, _col) return row.uint_val + From bb5a11716f8b831efa5b2473695a2dac001dd619 Mon Sep 17 00:00:00 2001 From: Ken Carpenter Date: Fri, 9 Jun 2023 17:48:11 -0700 Subject: [PATCH 6/9] Fix lint --- ports/stm32/boards/Passport/modules/views/table.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/views/table.py b/ports/stm32/boards/Passport/modules/views/table.py index 0263dacc0..6399332e8 100644 --- a/ports/stm32/boards/Passport/modules/views/table.py +++ b/ports/stm32/boards/Passport/modules/views/table.py @@ -8,6 +8,7 @@ from styles.colors import WHITE, TEXT_GREY from styles import Stylize + class Table(View): def __init__(self, items=[], @@ -106,4 +107,3 @@ def get_selected_row(self): _col = lv.C_Pointer() self.lvgl_root.get_selected_cell(row, _col) return row.uint_val - From 1222211bff7d29ad37b4594d50abdca85c4603fe Mon Sep 17 00:00:00 2001 From: Ken Carpenter Date: Fri, 9 Jun 2023 22:13:52 -0700 Subject: [PATCH 7/9] Adjust scrollbar position & padding --- .../boards/Passport/modules/pages/file_picker_page.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/stm32/boards/Passport/modules/pages/file_picker_page.py b/ports/stm32/boards/Passport/modules/pages/file_picker_page.py index bfbba207c..d78a7bb2a 100644 --- a/ports/stm32/boards/Passport/modules/pages/file_picker_page.py +++ b/ports/stm32/boards/Passport/modules/pages/file_picker_page.py @@ -50,6 +50,14 @@ def __init__(self, self.table.set_height(lv.pct(100)) self.table.set_scroll_dir(lv.DIR.VER) + with Stylize(self.table) as default: + default.flex_fill() + default.pad_row(0) + + # Adjust scrollbar position + with Stylize(self.table, selector=lv.PART.SCROLLBAR) as scrollbar: + scrollbar.pad(right=0) + with Stylize(self.table, lv.PART.MAIN) as default: default.bg_color(WHITE) default.radius(4) From 30c5b53db46ae5dd3010acc7a34afa445a4f4036 Mon Sep 17 00:00:00 2001 From: Ken Carpenter Date: Mon, 12 Jun 2023 17:36:03 -0700 Subject: [PATCH 8/9] Fix visual glitch on last table item Add back code that was temporarily commented out --- .../extra/themes/default/lv_theme_default.c | 3 ++- lib/lv_bindings/lvgl/src/widgets/lv_table.c | 5 +++-- ports/stm32/boards/Passport/modules/utils.py | 18 +++++++++--------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/lv_bindings/lvgl/src/extra/themes/default/lv_theme_default.c b/lib/lv_bindings/lvgl/src/extra/themes/default/lv_theme_default.c index df4174460..33899692b 100644 --- a/lib/lv_bindings/lvgl/src/extra/themes/default/lv_theme_default.c +++ b/lib/lv_bindings/lvgl/src/extra/themes/default/lv_theme_default.c @@ -211,7 +211,8 @@ static lv_color_t grey_filter_cb(const lv_color_filter_dsc_t *f, lv_color_t colo static void style_init(void) { static const lv_style_prop_t trans_props[] = { - // FOUNDATION: Comment out the following line to have no transitions for button background color changes + // FOUNDATION CHANGE ============================================================== + // Comment out the following line to have no transitions for button background color changes // LV_STYLE_BG_OPA, LV_STYLE_BG_COLOR, LV_STYLE_TRANSFORM_WIDTH, LV_STYLE_TRANSFORM_HEIGHT, LV_STYLE_TRANSLATE_Y, LV_STYLE_TRANSLATE_X, diff --git a/lib/lv_bindings/lvgl/src/widgets/lv_table.c b/lib/lv_bindings/lvgl/src/widgets/lv_table.c index 3412cf152..9e2f46f34 100644 --- a/lib/lv_bindings/lvgl/src/widgets/lv_table.c +++ b/lib/lv_bindings/lvgl/src/widgets/lv_table.c @@ -498,7 +498,7 @@ static void lv_table_event(const lv_obj_class_t * class_p, lv_event_t * e) for(i = 0; i < table->row_cnt; i++) h += table->row_h[i]; p->x = w - 1; - p->y = h - 1; + p->y = h; // FOUNDATION CHANGE - last pixel was being cut off on last table item } else if(code == LV_EVENT_PRESSED || code == LV_EVENT_PRESSING) { uint16_t col; @@ -745,7 +745,8 @@ static void draw_main(lv_event_t * e) if(table->cell_data[cell]) { txt_area.x1 = cell_area.x1 + cell_left; - txt_area.x2 = cell_area.x2 - cell_right - 28; // FOUNDATION + txt_area.x2 = cell_area.x2 - cell_right - 28; // FOUNDATION CHANGE =================================== + txt_area.y1 = cell_area.y1 + cell_top; txt_area.y2 = cell_area.y2 - cell_bottom; diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index 71966f239..dcc3d6e64 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -1184,15 +1184,15 @@ def restore_sd_cb(): prev_sd_card_cb = CardSlot.get_sd_card_change_cb() CardSlot.set_sd_card_change_cb(sd_card_cb) - # try: - await page.display() - # except Exception as e: - # print(e) - # page.unmount() - # restore_sd_cb() - # await on_result(None) - # await ErrorPage(text='Unable to display page.').show() - # return + try: + await page.display() + except Exception as e: + print(e) + page.unmount() + restore_sd_cb() + await on_result(None) + await ErrorPage(text='Unable to display page.').show() + return g = page.poll_for_done() while True: From df507c66a40da5367e4baea08d4ef11cd50f8d23 Mon Sep 17 00:00:00 2001 From: Ken Carpenter Date: Tue, 20 Jun 2023 12:30:19 -0700 Subject: [PATCH 9/9] SFT-932: Fixes for mono/FE devices --- lib/lv_bindings/lvgl/src/widgets/lv_table.c | 5 +++- .../modules/pages/file_picker_page.py | 5 ++-- .../boards/Passport/modules/views/table.py | 24 +++++++++++++++---- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/lv_bindings/lvgl/src/widgets/lv_table.c b/lib/lv_bindings/lvgl/src/widgets/lv_table.c index 9e2f46f34..42215a7f7 100644 --- a/lib/lv_bindings/lvgl/src/widgets/lv_table.c +++ b/lib/lv_bindings/lvgl/src/widgets/lv_table.c @@ -745,8 +745,11 @@ static void draw_main(lv_event_t * e) if(table->cell_data[cell]) { txt_area.x1 = cell_area.x1 + cell_left; +#ifdef SCREEN_MODE_COLOR txt_area.x2 = cell_area.x2 - cell_right - 28; // FOUNDATION CHANGE =================================== - +#else + txt_area.x2 = cell_area.x2 - cell_right - 18; // FOUNDATION CHANGE =================================== +#endif txt_area.y1 = cell_area.y1 + cell_top; txt_area.y2 = cell_area.y2 - cell_bottom; diff --git a/ports/stm32/boards/Passport/modules/pages/file_picker_page.py b/ports/stm32/boards/Passport/modules/pages/file_picker_page.py index d78a7bb2a..cc9afa465 100644 --- a/ports/stm32/boards/Passport/modules/pages/file_picker_page.py +++ b/ports/stm32/boards/Passport/modules/pages/file_picker_page.py @@ -6,7 +6,7 @@ import lvgl as lv import microns -from styles.colors import TEXT_GREY, WHITE, FD_BLUE +from styles.colors import TEXT_GREY, FOCUSED_LIST_ITEM_BG, FOCUSED_LIST_ITEM_TEXT, WHITE from styles import Stylize from pages import Page from views import Table @@ -70,7 +70,8 @@ def __init__(self, items.radius(MENU_ITEM_CORNER_RADIUS) with Stylize(self.table, lv.PART.ITEMS | lv.STATE.FOCUS_KEY) as focused: - focused.bg_color(FD_BLUE) + focused.bg_color(FOCUSED_LIST_ITEM_BG) + focused.text_color(FOCUSED_LIST_ITEM_TEXT) self.add_child(self.table) diff --git a/ports/stm32/boards/Passport/modules/views/table.py b/ports/stm32/boards/Passport/modules/views/table.py index 6399332e8..958db8300 100644 --- a/ports/stm32/boards/Passport/modules/views/table.py +++ b/ports/stm32/boards/Passport/modules/views/table.py @@ -5,20 +5,28 @@ import lvgl as lv from views import View -from styles.colors import WHITE, TEXT_GREY +from styles.colors import FOCUSED_LIST_ITEM_TEXT, TEXT_GREY from styles import Stylize +import passport class Table(View): def __init__(self, items=[], - col_width=212, + col_width=None, default_icon=lv.ICON_FILE, alt_icon=lv.ICON_FOLDER, get_cell_info=None): super().__init__() self.items = items + + if col_width is None: + if passport.IS_COLOR: + col_width = 212 + else: + col_width = 202 self.col_width = col_width + self.default_icon = default_icon self.alt_icon = alt_icon self.get_cell_info = get_cell_info @@ -36,7 +44,10 @@ def draw_part_begin_event_cb(self, e): # If the cells are drawn... if dsc.part == lv.PART.ITEMS: label_dsc = lv.draw_label_dsc_t.__cast__(dsc.label_dsc) - label_dsc.ofs_x += 28 + if passport.IS_COLOR: + label_dsc.ofs_x += 28 + else: + label_dsc.ofs_x += 18 def draw_part_end_event_cb(self, e): dsc = lv.obj_draw_part_dsc_t.__cast__(e.get_param()) @@ -61,7 +72,7 @@ def draw_part_end_event_cb(self, e): selected = draw_row == selected_row if selected: - icon_color = WHITE + icon_color = FOCUSED_LIST_ITEM_TEXT else: icon_color = TEXT_GREY @@ -71,7 +82,10 @@ def draw_part_end_event_cb(self, e): icon_img_dsc.recolor_opa = 255 # Setup the draw area - icon_area.x1 = 10 + dsc.draw_area.x1 + if passport.IS_COLOR: + icon_area.x1 = 10 + dsc.draw_area.x1 + else: + icon_area.x1 = 8 + dsc.draw_area.x1 icon_area.y1 = dsc.draw_area.y1 + ((dsc.draw_area.y2 - dsc.draw_area.y1) - icon.header.h) // 2 icon_area.x2 = icon_area.x1 + icon.header.w - 1 icon_area.y2 = icon_area.y1 + icon.header.h - 1