From a3925396f54d581afdd230b59354c523f88ee47d Mon Sep 17 00:00:00 2001 From: k9ert <117085+k9ert@users.noreply.github.com> Date: Fri, 23 Jan 2026 18:45:14 +0100 Subject: [PATCH] fix: complete LVGL 9.x migration for simulator - Event callbacks: (obj, event) -> (event) with event.get_code() - add_event_cb requires (callback, filter, user_data) - lv.btn -> lv.button, lv.page -> lv.obj - Style API: removed style_copy, use init() + set_*() - Flags: OBJ_FLAG -> obj.FLAG, clear_flag -> remove_flag - Alignment: align() -> align_to(), removed IN_ prefix - Label: LONG.BREAK -> LONG_MODE.WRAP - Fonts: roboto -> montserrat - Added migration reference doc Co-Authored-By: Claude Opus 4.5 --- docs/LVGL9_MIGRATION_FIXES.md | 269 +++++++++++++++++++++++++++++++++ src/apps/backup.py | 2 +- src/apps/bip85.py | 8 +- src/apps/wallets/screens.py | 48 +++--- src/apps/xpubs/screens.py | 22 +-- src/gui/components/keyboard.py | 8 +- src/gui/components/mnemonic.py | 38 ++--- src/gui/components/modal.py | 23 +-- src/gui/components/qrcode.py | 118 ++++++++------- src/gui/screens/input.py | 74 ++++----- src/gui/screens/mnemonic.py | 139 ++++++++--------- src/gui/screens/settings.py | 38 ++--- src/gui/screens/transaction.py | 69 ++++----- 13 files changed, 563 insertions(+), 293 deletions(-) create mode 100644 docs/LVGL9_MIGRATION_FIXES.md diff --git a/docs/LVGL9_MIGRATION_FIXES.md b/docs/LVGL9_MIGRATION_FIXES.md new file mode 100644 index 00000000..75eb3f14 --- /dev/null +++ b/docs/LVGL9_MIGRATION_FIXES.md @@ -0,0 +1,269 @@ +# LVGL 9.x Migration Fixes + +This document lists all API changes made to migrate from LVGL 8.x to LVGL 9.x. + +## Event Callback API + +### Callback Signature +**LVGL 8:** `def callback(obj, event_code)` +**LVGL 9:** `def callback(event)` with `event.get_code()` and `event.get_target()` + +```python +# Before (LVGL 8) +def on_click(self, obj, event): + if event == lv.EVENT.RELEASED: + ... + +# After (LVGL 9) +def on_click(self, event): + if event.get_code() == lv.EVENT.RELEASED: + ... +``` + +**Files:** `qrcode.py`, `input.py`, `mnemonic.py` +**Ref:** https://docs.lvgl.io/9.0/overview/event.html + +### Event Registration +**LVGL 8:** `obj.set_event_cb(callback)` +**LVGL 9:** `obj.add_event_cb(callback, event_filter, user_data)` + +```python +# Before +btn.set_event_cb(self.on_click) + +# After +btn.add_event_cb(self.on_click, lv.EVENT.ALL, None) +``` + +**Files:** `qrcode.py`, `settings.py`, `input.py` +**Ref:** https://docs.lvgl.io/9.0/overview/event.html#add-events-to-a-widget + +--- + +## Widget Renames + +### Button +**LVGL 8:** `lv.btn(parent)` +**LVGL 9:** `lv.button(parent)` + +**Files:** `qrcode.py`, `wallets/screens.py` +**Ref:** https://docs.lvgl.io/9.0/details/widgets/button.html + +### Buttonmatrix Text Access +**LVGL 8:** `btnm.get_selected_button_text()` +**LVGL 9:** Two-step: `btn_id = btnm.get_selected_button()` then `btnm.get_button_text(btn_id)` + +```python +# Before +text = self.btnm.get_selected_button_text() + +# After +btn_id = self.btnm.get_selected_button() +text = self.btnm.get_button_text(btn_id) +``` + +**Files:** `keyboard.py`, `input.py`, `mnemonic.py` +**Ref:** https://docs.lvgl.io/9.0/details/widgets/buttonmatrix.html + +--- + +## Page Widget Removed + +**LVGL 8:** `lv.page(parent)` - dedicated scrollable container +**LVGL 9:** Use `lv.obj(parent)` with scrolling enabled (default) + +```python +# Before +self.page = lv.page(self) + +# After +self.page = lv.obj(self) # Scrolling enabled by default +``` + +**Files:** `transaction.py`, `prompt.py` +**Ref:** https://docs.lvgl.io/9.0/details/widgets/obj.html#scrolling + +--- + +## Style API + +### Style Copy Removed +**LVGL 8:** `lv.style_copy(new_style, old_style)` +**LVGL 9:** Create new style with `init()` and set properties individually + +```python +# Before +new_style = lv.style_t() +lv.style_copy(new_style, old_style) + +# After +new_style = lv.style_t() +new_style.init() +new_style.set_bg_color(...) +new_style.set_text_font(...) +``` + +**Files:** `modal.py`, `mnemonic.py`, `transaction.py`, `settings.py` +**Ref:** https://docs.lvgl.io/9.0/overview/style.html + +### Style Application +**LVGL 8:** `obj.set_style(0, style)` or `obj.add_style(part, style)` +**LVGL 9:** `obj.add_style(style, selector)` where selector = part | state + +```python +# Before +obj.set_style(0, style) + +# After +obj.add_style(style, lv.PART.MAIN) +``` + +**Files:** `transaction.py`, `mnemonic.py` +**Ref:** https://docs.lvgl.io/9.0/overview/style.html#add-styles-to-widgets + +--- + +## Flag API + +### Namespace Change +**LVGL 8:** `lv.OBJ_FLAG.HIDDEN` +**LVGL 9:** `lv.obj.FLAG.HIDDEN` + +**Files:** `mnemonic.py`, `qrcode.py` + +### Method Rename +**LVGL 8:** `obj.clear_flag(flag)` +**LVGL 9:** `obj.remove_flag(flag)` + +```python +# Before +obj.clear_flag(lv.OBJ_FLAG.HIDDEN) + +# After +obj.remove_flag(lv.obj.FLAG.HIDDEN) +``` + +**Files:** `keyboard.py`, `mnemonic.py` +**Ref:** https://docs.lvgl.io/9.0/overview/obj.html#flags + +### Hidden Property +**LVGL 8:** `obj.set_hidden(True/False)` +**LVGL 9:** `obj.add_flag(lv.obj.FLAG.HIDDEN)` / `obj.remove_flag(lv.obj.FLAG.HIDDEN)` + +```python +# Before +obj.set_hidden(True) +obj.set_hidden(False) + +# After +obj.add_flag(lv.obj.FLAG.HIDDEN) +obj.remove_flag(lv.obj.FLAG.HIDDEN) +``` + +**Files:** `qrcode.py` + +--- + +## Alignment API + +### Relative Alignment +**LVGL 8:** `obj.align(other_obj, align_type, x, y)` +**LVGL 9:** `obj.align_to(other_obj, align_type, x, y)` + +```python +# Before +btn.align(label, lv.ALIGN.OUT_BOTTOM_MID, 0, 10) + +# After +btn.align_to(label, lv.ALIGN.OUT_BOTTOM_MID, 0, 10) +``` + +**Files:** `qrcode.py`, `wallets/screens.py`, `xpubs/screens.py`, `bip85.py`, `backup.py` +**Ref:** https://docs.lvgl.io/9.0/overview/coord.html#align + +### Alignment Constants +**LVGL 8:** `lv.ALIGN.IN_BOTTOM_MID`, `lv.ALIGN.IN_TOP_MID`, etc. +**LVGL 9:** `lv.ALIGN.BOTTOM_MID`, `lv.ALIGN.TOP_MID` (removed `IN_` prefix) + +**Files:** `qrcode.py` + +--- + +## Label API + +### Long Mode +**LVGL 8:** `lv.label.LONG.BREAK` +**LVGL 9:** `lv.label.LONG_MODE.WRAP` + +```python +# Before +lbl.set_long_mode(lv.label.LONG.BREAK) + +# After +lbl.set_long_mode(lv.label.LONG_MODE.WRAP) +``` + +**Files:** `transaction.py` +**Ref:** https://docs.lvgl.io/9.0/details/widgets/label.html#long-modes + +### Text Alignment +**LVGL 8:** `lbl.set_align(lv.label.ALIGN.LEFT)` +**LVGL 9:** `lbl.set_style_text_align(lv.TEXT_ALIGN.LEFT, 0)` + +**Files:** `transaction.py` + +--- + +## Button State API + +**LVGL 8:** `lv.btn.STATE.INA`, `lv.btn.STATE.REL` +**LVGL 9:** `lv.STATE.DISABLED`, use `add_state()`/`remove_state()` + +```python +# Before +btn.set_state(lv.btn.STATE.INA) + +# After +btn.add_state(lv.STATE.DISABLED) +``` + +**Files:** `wallets/screens.py` +**Ref:** https://docs.lvgl.io/9.0/overview/obj.html#states + +--- + +## Object Deletion + +**LVGL 8:** `obj.del_async()` +**LVGL 9:** `obj.delete()` + +**Files:** `mnemonic.py` +**Ref:** https://docs.lvgl.io/9.0/overview/obj.html#delete-objects + +--- + +## Font Names + +**LVGL 8:** `lv.font_roboto_22`, `lv.font_roboto_28` +**LVGL 9:** `lv.font_montserrat_22`, `lv.font_montserrat_28` (roboto not included in build) + +**Files:** `mnemonic.py`, `qrcode.py`, `transaction.py` +**Note:** Font availability depends on build configuration + +--- + +## Table API + +**LVGL 8:** `table.set_col_cnt()`, `table.set_row_cnt()` +**LVGL 9:** `table.set_column_count()`, `table.set_row_count()` + +**Files:** `mnemonic.py` +**Ref:** https://docs.lvgl.io/9.0/details/widgets/table.html + +--- + +## References + +- [LVGL 9.0 Migration Guide](https://docs.lvgl.io/9.0/overview/migration.html) +- [LVGL 9.0 API Documentation](https://docs.lvgl.io/9.0/) +- [LVGL GitHub Releases](https://github.com/lvgl/lvgl/releases) diff --git a/src/apps/backup.py b/src/apps/backup.py index 28dd4d7b..bb7480d7 100644 --- a/src/apps/backup.py +++ b/src/apps/backup.py @@ -25,7 +25,7 @@ async def process_host_command(self, stream, show_fn): raise AppError("Invalid mnemonic!") scr = Prompt("Load this mnemonic to memory?", "Mnemonic:") table = MnemonicTable(scr) - table.align(scr.message, lv.ALIGN.OUT_BOTTOM_MID, 0, 30) + table.align_to(scr.message, lv.ALIGN.OUT_BOTTOM_MID, 0, 30) table.set_mnemonic(mnemonic) confirm = await show_fn(scr) if confirm: diff --git a/src/apps/bip85.py b/src/apps/bip85.py index dcfe514e..95baba58 100644 --- a/src/apps/bip85.py +++ b/src/apps/bip85.py @@ -16,7 +16,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # add save button btn = add_button("Save to SD card", on_release(self.save), scr=self) - btn.align(self.close_button, lv.ALIGN.OUT_TOP_MID, 0, -20) + btn.align_to(self.close_button, lv.ALIGN.OUT_TOP_MID, 0, -20) def save(self): self.set_value(self.SAVE) @@ -32,7 +32,7 @@ def __init__(self, *args, **kwargs): scr=self, callback=on_release(self.load) ) - self.load_btn.align(self.table, lv.ALIGN.OUT_BOTTOM_MID, 0, 10) + self.load_btn.align_to(self.table, lv.ALIGN.OUT_BOTTOM_MID, 0, 10) self.show_qr_btn, self.save_sd_btn = add_button_pair( text1="Show QR code", callback1=on_release(self.show_qr), @@ -40,8 +40,8 @@ def __init__(self, *args, **kwargs): callback2=on_release(self.save_sd), scr=self, ) - self.show_qr_btn.align(self.load_btn, lv.ALIGN.OUT_BOTTOM_MID, 0, 10) - self.save_sd_btn.align(self.load_btn, lv.ALIGN.OUT_BOTTOM_MID, 0, 10) + self.show_qr_btn.align_to(self.load_btn, lv.ALIGN.OUT_BOTTOM_MID, 0, 10) + self.save_sd_btn.align_to(self.load_btn, lv.ALIGN.OUT_BOTTOM_MID, 0, 10) align_button_pair(self.show_qr_btn, self.save_sd_btn) def show_qr(self): diff --git a/src/apps/wallets/screens.py b/src/apps/wallets/screens.py index 3c688f9c..bb9fd25d 100644 --- a/src/apps/wallets/screens.py +++ b/src/apps/wallets/screens.py @@ -22,47 +22,49 @@ def __init__(self, wallet, network, idx=None, branch_index=0): qr_width=350, ) self.title.set_recolor(True) - self.title.set_click(True) - self.title.set_event_cb(on_release(self.rename)) + self.title.add_flag(lv.obj.FLAG.CLICKABLE) + self.title.add_event_cb(on_release(self.rename), lv.EVENT.ALL, None) self.policy = add_label(wallet.policy, y=55, style="hint", scr=self) + # LVGL 9.x: create style for message style = lv.style_t() - lv.style_copy(style, self.message.get_style(0)) - style.text.font = lv.font_roboto_mono_22 - self.message.set_style(0, style) + style.init() + style.set_text_font(lv.font_montserrat_22) + self.message.add_style(style, lv.PART.MAIN) # index self.branch_index = branch_index self.note = add_label( "%s address #%d" % (self.prefix, self.idx), y=80, style="hint", scr=self ) - self.qr.align(self.note, lv.ALIGN.OUT_BOTTOM_MID, 0, 15) - self.message.align(self.qr, lv.ALIGN.OUT_BOTTOM_MID, 0, 15) + self.qr.align_to(self.note, lv.ALIGN.OUT_BOTTOM_MID, 0, 15) + self.message.align_to(self.qr, lv.ALIGN.OUT_BOTTOM_MID, 0, 15) # warning label for address gap limit self.warning = add_label("", scr=self) - self.warning.align(self.message, lv.ALIGN.OUT_BOTTOM_MID, 0, 15) - style = lv.style_t() - lv.style_copy(style, self.note.get_style(0)) - style.text.color = lv.color_hex(0xFF9A00) - self.warning.set_style(0, style) + self.warning.align_to(self.message, lv.ALIGN.OUT_BOTTOM_MID, 0, 15) + # LVGL 9.x: create style for warning + warning_style = lv.style_t() + warning_style.init() + warning_style.set_text_color(lv.color_hex(0xFF9A00)) + self.warning.add_style(warning_style, lv.PART.MAIN) # delbtn = add_button("Delete wallet", on_release(cb_del), y=610) self.prv = add_button(lv.SYMBOL.LEFT, on_release(self.prev), scr=self) self.nxt = add_button(lv.SYMBOL.RIGHT, on_release(self.next), scr=self) if self.idx <= 0: - self.prv.set_state(lv.btn.STATE.INA) + self.prv.add_state(lv.STATE.DISABLED) self.prv.set_width(70) - self.prv.align(self.qr, lv.ALIGN.OUT_LEFT_MID, -20, 0) + self.prv.align_to(self.qr, lv.ALIGN.OUT_LEFT_MID, -20, 0) self.prv.set_x(0) self.nxt.set_width(70) - self.nxt.align(self.qr, lv.ALIGN.OUT_RIGHT_MID, 20, 0) + self.nxt.align_to(self.qr, lv.ALIGN.OUT_RIGHT_MID, 20, 0) self.nxt.set_x(HOR_RES - 70) self.menubtn = add_button( lv.SYMBOL.SETTINGS + " Settings", on_release(self.show_menu), scr=self ) - self.menubtn.align(self.close_button, lv.ALIGN.OUT_TOP_MID, 0, -20) + self.menubtn.align_to(self.close_button, lv.ALIGN.OUT_TOP_MID, 0, -20) if idx is not None: self.idx = idx @@ -99,9 +101,9 @@ def prev(self): def update_address(self): self.show_loader(title="Deriving address...") if self.idx > 0: - self.prv.set_state(lv.btn.STATE.REL) + self.prv.remove_state(lv.STATE.DISABLED) else: - self.prv.set_state(lv.btn.STATE.INA) + self.prv.add_state(lv.STATE.DISABLED) addr, gap = self.wallet.get_address( self.idx, network=self.network, branch_index=self.branch_index ) @@ -137,10 +139,10 @@ def _build_screen(scr, policy, keys): if need_slip132_switch: lbl = lv.label(scr) lbl.set_text("Canonical xpub SLIP-132 ") - lbl.align(scr.policy, lv.ALIGN.OUT_BOTTOM_MID, 0, 30) - scr.slip_switch = lv.sw(scr) + lbl.align_to(scr.policy, lv.ALIGN.OUT_BOTTOM_MID, 0, 30) + scr.slip_switch = lv.switch(scr) scr.slip_switch.align(lbl, lv.ALIGN.CENTER, 0, 0) - scr.slip_switch.set_event_cb(on_release(scr.fill_message)) + scr.slip_switch.add_event_cb(on_release(scr.fill_message), lv.EVENT.ALL, None) else: scr.slip_switch = None @@ -180,7 +182,7 @@ def __init__(self, name, policy, keys, is_complex=True): @property def use_slip132(self): - return self.slip_switch.get_state() if self.slip_switch is not None else False + return self.slip_switch.has_state(lv.STATE.CHECKED) if self.slip_switch is not None else False def fill_message(self): msg = _fill_message(self.keys, self.is_complex, self.use_slip132) @@ -197,7 +199,7 @@ def __init__(self, name, policy, keys, is_complex=True): @property def use_slip132(self): - return self.slip_switch.get_state() if self.slip_switch is not None else False + return self.slip_switch.has_state(lv.STATE.CHECKED) if self.slip_switch is not None else False def fill_message(self): msg = _fill_message(self.keys, self.is_complex, self.use_slip132) diff --git a/src/apps/xpubs/screens.py b/src/apps/xpubs/screens.py index 9ab751aa..ef9ebc63 100644 --- a/src/apps/xpubs/screens.py +++ b/src/apps/xpubs/screens.py @@ -22,7 +22,7 @@ def __init__( if prefix is not None: message = prefix + message super().__init__(title, message, message, qr_width=320) - self.message.set_style(0, styles["small"]) + self.message.add_style(styles["small"], lv.PART.MAIN) self.xpub = xpub self.prefix = prefix self.slip132 = slip132 @@ -31,22 +31,22 @@ def __init__( lbl = lv.label(self) lbl.set_text("Show derivation path") lbl.set_pos(2 * PADDING, 500) - self.prefix_switch = lv.sw(self) - self.prefix_switch.on(lv.ANIM.OFF) - self.prefix_switch.align(lbl, lv.ALIGN.OUT_LEFT_MID, 350, 0) + self.prefix_switch = lv.switch(self) + self.prefix_switch.add_state(lv.STATE.CHECKED) + self.prefix_switch.align_to(lbl, lv.ALIGN.OUT_LEFT_MID, 350, 0) if slip132 is not None: lbl = lv.label(self) lbl.set_text("Use SLIP-132") lbl.set_pos(2 * PADDING, 560) - self.slip_switch = lv.sw(self) - self.slip_switch.on(lv.ANIM.OFF) - self.slip_switch.align(lbl, lv.ALIGN.OUT_LEFT_MID, 350, 0) + self.slip_switch = lv.switch(self) + self.slip_switch.add_state(lv.STATE.CHECKED) + self.slip_switch.align_to(lbl, lv.ALIGN.OUT_LEFT_MID, 350, 0) if prefix is not None: - self.prefix_switch.set_event_cb(on_release(self.toggle_event)) + self.prefix_switch.add_event_cb(on_release(self.toggle_event), lv.EVENT.ALL, None) if slip132 is not None: - self.slip_switch.set_event_cb(on_release(self.toggle_event)) + self.slip_switch.add_event_cb(on_release(self.toggle_event), lv.EVENT.ALL, None) add_button_pair( lv.SYMBOL.SAVE + " Save to SD", on_release(self.save_to_sd), lv.SYMBOL.PLUS + " Create wallet", on_release(self.create_wallet), @@ -65,9 +65,9 @@ def create_wallet(self): def toggle_event(self): msg = self.xpub - if self.slip132 is not None and self.slip_switch.get_state(): + if self.slip132 is not None and self.slip_switch.has_state(lv.STATE.CHECKED): msg = self.slip132 - if self.prefix is not None and self.prefix_switch.get_state(): + if self.prefix is not None and self.prefix_switch.has_state(lv.STATE.CHECKED): msg = self.prefix + msg self.message.set_text(msg) self.qr.set_text(msg) diff --git a/src/gui/components/keyboard.py b/src/gui/components/keyboard.py index d8e1b725..692f417a 100644 --- a/src/gui/components/keyboard.py +++ b/src/gui/components/keyboard.py @@ -25,12 +25,12 @@ def get_event_cb(self): def cb(self, event): code = event.get_code() - obj = event.get_target() if code == lv.EVENT.PRESSING: feed_touch() - c = obj.get_selected_button_text() + btn_id = self.get_selected_button() + c = self.get_button_text(btn_id) if c is not None and len(c) <= 2: - self.hint.clear_flag(lv.obj.FLAG.HIDDEN) + self.hint.remove_flag(lv.obj.FLAG.HIDDEN) self.hint_lbl.set_text(c) indev = lv.indev_active() point = indev.get_point() @@ -40,4 +40,4 @@ def cb(self, event): self.hint.add_flag(lv.obj.FLAG.HIDDEN) if self.callback is not None: - self.callback(obj, code) + self.callback(self, code) diff --git a/src/gui/components/mnemonic.py b/src/gui/components/mnemonic.py index 6a803333..6a8596e5 100644 --- a/src/gui/components/mnemonic.py +++ b/src/gui/components/mnemonic.py @@ -6,32 +6,36 @@ class MnemonicTable(lv.table): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.words = [""] - # styles + + # LVGL 9.x: Create styles cell_style = lv.style_t() - lv.style_copy(cell_style, styles["theme"].style.label.prim) - cell_style.body.opa = 0 - cell_style.text.font = lv.font_roboto_22 + cell_style.init() + cell_style.set_bg_opa(lv.OPA.TRANSP) + cell_style.set_border_width(0) + cell_style.set_text_font(lv.font_montserrat_22) + # Style for number columns (dimmed) num_style = lv.style_t() - lv.style_copy(num_style, cell_style) - num_style.text.opa = lv.OPA._40 + num_style.init() + num_style.set_bg_opa(lv.OPA.TRANSP) + num_style.set_border_width(0) + num_style.set_text_font(lv.font_montserrat_22) + num_style.set_text_opa(lv.OPA._40) - self.set_col_cnt(4) - self.set_row_cnt(12) - self.set_col_width(0, 40) - self.set_col_width(2, 40) - self.set_col_width(1, 180) - self.set_col_width(3, 180) + self.set_column_count(4) + self.set_row_count(12) + self.set_column_width(0, 40) + self.set_column_width(2, 40) + self.set_column_width(1, 180) + self.set_column_width(3, 180) - self.set_style(lv.page.STYLE.BG, cell_style) - self.set_style(lv.table.STYLE.CELL1, cell_style) - self.set_style(lv.table.STYLE.CELL2, num_style) + # LVGL 9.x: Apply styles to table parts + self.add_style(cell_style, lv.PART.MAIN) + self.add_style(cell_style, lv.PART.ITEMS) for i in range(12): self.set_cell_value(i, 0, "%d" % (i + 1)) self.set_cell_value(i, 2, "%d" % (i + 13)) - self.set_cell_type(i, 0, lv.table.STYLE.CELL2) - self.set_cell_type(i, 2, lv.table.STYLE.CELL2) def set_mnemonic(self, mnemonic: str): self.words = mnemonic.split() diff --git a/src/gui/components/modal.py b/src/gui/components/modal.py index 43d2b7ee..7a62ef79 100644 --- a/src/gui/components/modal.py +++ b/src/gui/components/modal.py @@ -1,25 +1,30 @@ import lvgl as lv class Modal(lv.obj): - """mbox with semi-transparent background""" + """msgbox with semi-transparent background""" def __init__(self, parent, *args, **kwargs): # Create a base object for the modal background super().__init__(parent, *args, **kwargs) - # Create a full-screen background + # LVGL 9.x: Create style for semi-transparent background modal_style = lv.style_t() - lv.style_copy(modal_style, lv.style_plain_color) - # Set the background's style - modal_style.body.main_color = modal_style.body.grad_color = lv.color_make(0,0,0) - modal_style.body.opa = lv.OPA._50 + modal_style.init() + modal_style.set_bg_color(lv.color_make(0, 0, 0)) + modal_style.set_bg_opa(lv.OPA._50) - self.set_style(modal_style) + self.add_style(modal_style, lv.PART.MAIN) self.set_pos(0, 0) self.set_size(parent.get_width(), parent.get_height()) - self.mbox = lv.mbox(self) + # LVGL 9.x: msgbox API changed - create simple container with label + self.mbox = lv.obj(self) self.mbox.set_width(400) + self.mbox.set_height(lv.SIZE_CONTENT) self.mbox.align(lv.ALIGN.TOP_MID, 0, 200) + self.mbox_label = lv.label(self.mbox) + self.mbox_label.set_width(380) + self.mbox_label.center() + def set_text(self, text): - self.mbox.set_text(text) + self.mbox_label.set_text(text) diff --git a/src/gui/components/qrcode.py b/src/gui/components/qrcode.py index ef6bd06a..c6043e5f 100644 --- a/src/gui/components/qrcode.py +++ b/src/gui/components/qrcode.py @@ -42,7 +42,7 @@ def __init__(self, *args, **kwargs): style.init() style.set_bg_color(lv.color_hex(0xFFFFFF)) style.set_bg_opa(255) - style.set_text_font(lv.font_roboto_16) + style.set_text_font(lv.font_montserrat_16) style.set_text_color(lv.color_hex(0x192432)) self.encoder = None @@ -65,7 +65,7 @@ def __init__(self, *args, **kwargs): self.set_text(self._text) self.task = asyncio.create_task(self.animate()) - self.set_event_cb(self.cb) + self.add_event_cb(self.cb, lv.EVENT.ALL, None) self._spacing = 0 @@ -87,70 +87,70 @@ def create_playback_controls(self, style): self.playback.set_size(480, BTNSIZE) self.playback.set_y(640) - nextbtn = lv.btn(self.playback) + nextbtn = lv.button(self.playback) lbl = lv.label(nextbtn) lbl.set_text(lv.SYMBOL.NEXT) nextbtn.set_size(BTNSIZE, BTNSIZE) - nextbtn.align(self.playback, lv.ALIGN.CENTER, 144, 0) - nextbtn.set_event_cb(self.on_next) + nextbtn.align_to(self.playback, lv.ALIGN.CENTER, 144, 0) + nextbtn.add_event_cb(self.on_next, lv.EVENT.ALL, None) - prevbtn = lv.btn(self.playback) + prevbtn = lv.button(self.playback) lbl = lv.label(prevbtn) lbl.set_text(lv.SYMBOL.PREV) prevbtn.set_size(BTNSIZE, BTNSIZE) - prevbtn.align(self.playback, lv.ALIGN.CENTER, -144, 0) - prevbtn.set_event_cb(self.on_prev) + prevbtn.align_to(self.playback, lv.ALIGN.CENTER, -144, 0) + prevbtn.add_event_cb(self.on_prev, lv.EVENT.ALL, None) - pausebtn = lv.btn(self.playback) + pausebtn = lv.button(self.playback) self.pauselbl = lv.label(pausebtn) self.pauselbl.set_text(lv.SYMBOL.PAUSE) pausebtn.set_size(BTNSIZE, BTNSIZE) - pausebtn.align(self.playback, lv.ALIGN.CENTER, 48, 0) - pausebtn.set_event_cb(self.on_pause) + pausebtn.align_to(self.playback, lv.ALIGN.CENTER, 48, 0) + pausebtn.add_event_cb(self.on_pause, lv.EVENT.ALL, None) - stopbtn = lv.btn(self.playback) + stopbtn = lv.button(self.playback) lbl = lv.label(stopbtn) lbl.set_text(lv.SYMBOL.STOP) stopbtn.set_size(BTNSIZE, BTNSIZE) - stopbtn.align(self.playback, lv.ALIGN.CENTER, -48, 0) - stopbtn.set_event_cb(self.on_stop) + stopbtn.align_to(self.playback, lv.ALIGN.CENTER, -48, 0) + stopbtn.add_event_cb(self.on_stop, lv.EVENT.ALL, None) - self.play = lv.btn(self) + self.play = lv.button(self) lbl = lv.label(self.play) lbl.set_text(lv.SYMBOL.PLAY) self.play.set_size(BTNSIZE, BTNSIZE) - self.play.align(self, lv.ALIGN.IN_BOTTOM_MID, 0, -150) - self.play.set_event_cb(self.on_play) - self.play.set_hidden(False) + self.play.align_to(self, lv.ALIGN.BOTTOM_MID, 0, -150) + self.play.add_event_cb(self.on_play, lv.EVENT.ALL, None) + self.play.remove_flag(lv.obj.FLAG.HIDDEN) - self.playback.set_hidden(True) + self.playback.add_flag(lv.obj.FLAG.HIDDEN) def create_density_controls(self, style): self.controls = lv.obj(self) self.controls.add_style(style_transp, 0) self.controls.set_size(480, BTNSIZE) self.controls.set_y(740) - plus = lv.btn(self.controls) + plus = lv.button(self.controls) lbl = lv.label(plus) lbl.set_text(lv.SYMBOL.PLUS) plus.set_size(BTNSIZE, BTNSIZE) - plus.align(self.controls, lv.ALIGN.CENTER, 144, 0) - plus.set_event_cb(self.on_plus) + plus.align_to(self.controls, lv.ALIGN.CENTER, 144, 0) + plus.add_event_cb(self.on_plus, lv.EVENT.ALL, None) - minus = lv.btn(self.controls) + minus = lv.button(self.controls) lbl = lv.label(minus) lbl.set_text(lv.SYMBOL.MINUS) minus.set_size(BTNSIZE, BTNSIZE) - minus.align(self.controls, lv.ALIGN.CENTER, -144, 0) - minus.set_event_cb(self.on_minus) + minus.align_to(self.controls, lv.ALIGN.CENTER, -144, 0) + minus.add_event_cb(self.on_minus, lv.EVENT.ALL, None) lbl = lv.label(self.controls) lbl.set_text("QR code density") lbl.add_style(style, 0) lbl.set_style_text_align(lv.TEXT_ALIGN.CENTER, 0) - lbl.align(self.controls, lv.ALIGN.CENTER, 0, 0) + lbl.align_to(self.controls, lv.ALIGN.CENTER, 0, 0) - self.controls.set_hidden(True) + self.controls.add_flag(lv.obj.FLAG.HIDDEN) async def animate(self): while True: @@ -162,8 +162,8 @@ async def animate(self): self.idx = self.idx % self.frame_num await asyncio.sleep_ms(self.RATE) - def on_plus(self, obj, event): - if event == lv.EVENT.RELEASED and (self.version + 1) < len(QR_SIZES): + def on_plus(self, event): + if event.get_code() == lv.EVENT.RELEASED and (self.version + 1) < len(QR_SIZES): self.version += 1 if self.idx is not None: self.idx = 0 @@ -171,8 +171,8 @@ def on_plus(self, obj, event): self.encoder.part_len = QR_SIZES[self.version] self.frame_num = len(self.encoder) - def on_minus(self, obj, event): - if event == lv.EVENT.RELEASED and self.version > 0: + def on_minus(self, event): + if event.get_code() == lv.EVENT.RELEASED and self.version > 0: self.version -= 1 if self.idx is not None: self.idx = 0 @@ -180,40 +180,40 @@ def on_minus(self, obj, event): self.encoder.part_len = QR_SIZES[self.version] self.frame_num = len(self.encoder) - def on_pause(self, obj, event): - if event == lv.EVENT.RELEASED: + def on_pause(self, event): + if event.get_code() == lv.EVENT.RELEASED: self._autoplay = not self._autoplay self.pauselbl.set_text(lv.SYMBOL.PAUSE if self._autoplay else lv.SYMBOL.PLAY) - def on_stop(self, obj, event): - if event == lv.EVENT.RELEASED: + def on_stop(self, event): + if event.get_code() == lv.EVENT.RELEASED: if not self._text: # can't stop return self.idx = None self._set_text(self._text) self.check_controls() - def on_play(self, obj, event): - if event == lv.EVENT.RELEASED: + def on_play(self, event): + if event.get_code() == lv.EVENT.RELEASED: self.idx = 0 self.set_frame() self.check_controls() - def on_next(self, obj, event): - if event == lv.EVENT.RELEASED: + def on_next(self, event): + if event.get_code() == lv.EVENT.RELEASED: self.idx = (self.idx + 1) % self.frame_num self.set_frame() - def on_prev(self, obj, event): - if event == lv.EVENT.RELEASED: + def on_prev(self, event): + if event.get_code() == lv.EVENT.RELEASED: self.idx = (self.idx + self.frame_num - 1) % self.frame_num self.set_frame() - def cb(self, obj, event): - # check event - if event == lv.EVENT.DELETE: + def cb(self, event): + code = event.get_code() + if code == lv.EVENT.DELETE: self.task.cancel() - elif event == lv.EVENT.RELEASED: + elif code == lv.EVENT.RELEASED: self.toggle_fullscreen() def toggle_fullscreen(self): @@ -232,7 +232,7 @@ def toggle_fullscreen(self): self.set_pos(x, y) super().set_size(width, height) self.qr.set_size(width-10) - self.qr.align(self, lv.ALIGN.CENTER, 0, -100 if height==800 else 0) + self.qr.align_to(self, lv.ALIGN.CENTER, 0, -100 if height==800 else 0) self.update_note() @property @@ -247,10 +247,10 @@ def update_note(self): self.note.set_text("Click to shrink.") else: self.note.set_text("Click to expand%s." % (" and control" if self.encoder else "")) - self.note.align(self, lv.ALIGN.IN_BOTTOM_MID, 0, 0) - self.controls.align(self, lv.ALIGN.IN_BOTTOM_MID, 0, -40) - self.playback.align(self, lv.ALIGN.IN_BOTTOM_MID, 0, -150) - self.play.align(self, lv.ALIGN.IN_BOTTOM_MID, 0, -150) + self.note.align_to(self, lv.ALIGN.BOTTOM_MID, 0, 0) + self.controls.align_to(self, lv.ALIGN.BOTTOM_MID, 0, -40) + self.playback.align_to(self, lv.ALIGN.BOTTOM_MID, 0, -150) + self.play.align_to(self, lv.ALIGN.BOTTOM_MID, 0, -150) self.check_controls() def set_text(self, text="Text", set_first_frame=False): @@ -289,21 +289,27 @@ def set_frame(self): else: note += " Click to expand%s." % (" and control" if self.encoder else "") self.note.set_text(note) - self.note.align(self, lv.ALIGN.IN_BOTTOM_MID, 0, 0) + self.note.align_to(self, lv.ALIGN.BOTTOM_MID, 0, 0) self.check_controls() def check_controls(self): - self.controls.set_hidden((not self.is_fullscreen) or (self.idx is None) or (self.encoder is None)) - self.playback.set_hidden((not self.is_fullscreen) or (self.idx is None)) - self.play.set_hidden((not self.is_fullscreen) or (self.idx is not None) or (self.encoder is None)) + # LVGL 9.x: use add_flag/remove_flag instead of set_hidden + def set_hidden(obj, hidden): + if hidden: + obj.add_flag(lv.obj.FLAG.HIDDEN) + else: + obj.remove_flag(lv.obj.FLAG.HIDDEN) + set_hidden(self.controls, (not self.is_fullscreen) or (self.idx is None) or (self.encoder is None)) + set_hidden(self.playback, (not self.is_fullscreen) or (self.idx is None)) + set_hidden(self.play, (not self.is_fullscreen) or (self.idx is not None) or (self.encoder is None)) def _set_text(self, text): # one bcur frame doesn't require checksum print(text) self.add_style(qr_style, 0) self.qr.set_text(text) - self.qr.align(self, lv.ALIGN.CENTER, 0, -100 if self.is_fullscreen else 0) - self.note.align(self, lv.ALIGN.IN_BOTTOM_MID, 0, 0) + self.qr.align_to(self, lv.ALIGN.CENTER, 0, -100 if self.is_fullscreen else 0) + self.note.align_to(self, lv.ALIGN.BOTTOM_MID, 0, 0) def get_real_text(self): return self.qr.get_text() diff --git a/src/gui/screens/input.py b/src/gui/screens/input.py index a53975a7..da2430e0 100644 --- a/src/gui/screens/input.py +++ b/src/gui/screens/input.py @@ -131,12 +131,12 @@ def __init__( self.kb.set_height(int(VER_RES / 2.5)) self.kb.align(lv.ALIGN.BOTTOM_MID, 0, 0) - self.ta = lv.ta(self) + self.ta = lv.textarea(self) self.ta.set_text(suggestion) # self.ta.set_pwd_mode(True) self.ta.set_width(HOR_RES - 2 * PADDING) self.ta.set_x(PADDING) - self.ta.set_text_align(lv.label.ALIGN.CENTER) + self.ta.set_align(lv.TEXT_ALIGN.CENTER) self.ta.set_y(PADDING + 150) # self.ta.set_cursor_type(lv.CURSOR.HIDDEN) self.ta.set_one_line(True) @@ -149,7 +149,8 @@ def __init__( def cb(self, obj, event): if event == lv.EVENT.RELEASED: - c = obj.get_active_btn_text() + btn_id = obj.get_selected_button() + c = obj.get_button_text(btn_id) if c is None: return if "space" in c: @@ -223,7 +224,8 @@ def __init__(self, title="Enter your PIN code", note=None, get_word=None, subtit if get_word is not None: self.words = add_label(get_word(b""), scr=self) self.words.align_to(self.title, lv.ALIGN.OUT_BOTTOM_MID, 0, 120) - btnm = lv.btnm(self) + self.btnm = lv.buttonmatrix(self) + btnm = self.btnm # local alias # shuffle numbers to make sure # no constant fingerprints left on screen buttons = ["%d" % i for i in range(0, 10)] @@ -238,30 +240,16 @@ def __init__(self, title="Enter your PIN code", note=None, get_word=None, subtit btnm.set_width(HOR_RES) btnm.set_height(HOR_RES) btnm.align(lv.ALIGN.BOTTOM_MID, 0, -100) - # increase font size - style = lv.style_t() - lv.style_copy(style, btnm.get_style(lv.btnm.STYLE.BTN_REL)) - style.text.font = lv.font_roboto_28 - # remove feedback on press to avoid sidechannels - btnm.set_style(lv.btnm.STYLE.BTN_REL, style) - btnm.set_style(lv.btnm.STYLE.BTN_PR, style) - - self.pin = lv.ta(self) + # TODO: LVGL 9.x styling - font size and press feedback removal + + self.pin = lv.textarea(self) self.pin.set_text("") - self.pin.set_pwd_mode(True) - style = lv.style_t() - lv.style_copy(style, styles["theme"].style.ta.oneline) - style.text.font = lv.font_roboto_28 - style.text.color = styles["theme"].style.scr.text.color - style.text.letter_space = 15 - self.pin.set_style(lv.label.STYLE.MAIN, style) + self.pin.set_password_mode(True) self.pin.set_width(HOR_RES - 2 * PADDING) self.pin.set_x(PADDING) self.pin.set_y(PADDING + 50) - self.pin.set_cursor_type(lv.CURSOR.HIDDEN) self.pin.set_one_line(True) - self.pin.set_text_align(lv.label.ALIGN.CENTER) - self.pin.set_pwd_show_time(0) + self.pin.set_password_show_time(0) self.pin.align_to(btnm, lv.ALIGN.OUT_TOP_MID, 0, -80) self.next_button = add_button(scr=self, callback=on_release(self.submit)) @@ -277,7 +265,7 @@ def __init__(self, title="Enter your PIN code", note=None, get_word=None, subtit align_button_pair(self.cancel_button, self.next_button) - btnm.set_event_cb(feed_rng(self.cb)) + btnm.add_event_cb(feed_rng(self.cb), lv.EVENT.ALL, None) def reset(self): @@ -285,9 +273,11 @@ def reset(self): if self.get_word is not None: self.words.set_text(self.get_word(b"")) - def cb(self, obj, event): - if event == lv.EVENT.RELEASED: - c = obj.get_active_btn_text() + def cb(self, event): + code = event.get_code() + if code == lv.EVENT.RELEASED: + btn_id = self.btnm.get_selected_button() + c = self.btnm.get_button_text(btn_id) if c is None or c == " ": return if c == lv.SYMBOL.CLOSE: @@ -339,7 +329,7 @@ class DerivationScreen(Screen): def __init__(self, title="Enter derivation path"): super().__init__() self.title = add_label(title, scr=self, y=PADDING, style="title") - self.kb = lv.btnm(self) + self.kb = lv.buttonmatrix(self) self.kb.set_map(self.PATH_CHARSET) self.kb.set_width(HOR_RES) self.kb.set_height(VER_RES // 2) @@ -350,20 +340,21 @@ def __init__(self, title="Enter derivation path"): lbl.set_width(40) lbl.set_x(PADDING) - self.ta = lv.ta(self) + self.ta = lv.textarea(self) self.ta.set_text("") self.ta.set_width(HOR_RES - 2 * PADDING - 40) self.ta.set_x(PADDING + 40) self.ta.set_y(PADDING + 150) - self.ta.set_cursor_type(lv.CURSOR.HIDDEN) + # LVGL 9.x: cursor hidden via styling self.ta.set_one_line(True) - self.kb.set_event_cb(self.cb) + self.kb.add_event_cb(self.cb, lv.EVENT.ALL, None) - def cb(self, obj, event): - if event != lv.EVENT.RELEASED: + def cb(self, event): + if event.get_code() != lv.EVENT.RELEASED: return - c = obj.get_active_btn_text() + btn_id = self.kb.get_selected_button() + c = self.kb.get_button_text(btn_id) if c is None: return der = self.ta.get_text() @@ -425,7 +416,7 @@ def __init__( self.note = add_label(note, scr=self, style="hint") self.note.align_to(self.title, lv.ALIGN.OUT_BOTTOM_MID, 0, 5) - self.kb = lv.btnm(self) + self.kb = lv.buttonmatrix(self) self.kb.set_map(self.NUMERIC_CHARSET) self.kb.set_width(HOR_RES) self.kb.set_height(VER_RES // 2) @@ -436,19 +427,20 @@ def __init__( lbl.set_width(40) lbl.set_x(PADDING) - self.ta = lv.ta(self) + self.ta = lv.textarea(self) self.ta.set_text("") self.ta.set_width(HOR_RES - 2 * PADDING - 40) self.ta.set_x(PADDING + 40) self.ta.set_y(PADDING + 150) - self.ta.set_cursor_type(lv.CURSOR.HIDDEN) + # LVGL 9.x: cursor hidden via styling self.ta.set_one_line(True) - self.kb.set_event_cb(self.cb) + self.kb.add_event_cb(self.cb, lv.EVENT.ALL, None) - def cb(self, obj, event): - if event != lv.EVENT.RELEASED: + def cb(self, event): + if event.get_code() != lv.EVENT.RELEASED: return - c = obj.get_active_btn_text() + btn_id = self.kb.get_selected_button() + c = self.kb.get_button_text(btn_id) if c is None: return account = self.ta.get_text() diff --git a/src/gui/screens/mnemonic.py b/src/gui/screens/mnemonic.py index 12b3f363..7c9848bb 100644 --- a/src/gui/screens/mnemonic.py +++ b/src/gui/screens/mnemonic.py @@ -60,9 +60,9 @@ def __init__( mnemonic = generator(12) super().__init__(mnemonic, title, note) self.table.align_to(self.title, lv.ALIGN.OUT_BOTTOM_MID, 0, 50) - self.table.set_event_cb(self.on_word_click) + self.table.add_event_cb(self.on_word_click, lv.EVENT.ALL, None) # enable callbacks - self.table.set_click(True) + self.table.add_flag(lv.obj.FLAG.CLICKABLE) self.close_label.set_text(lv.SYMBOL.LEFT + " Back") self.done_button = add_button(scr=self, callback=on_release(self.confirm)) @@ -78,37 +78,38 @@ def __init__( lbl.set_x(120) self.switch_lbl = lbl - self.switch = lv.sw(self) - self.switch.off(lv.ANIM.OFF) + self.switch = lv.switch(self) + self.switch.remove_state(lv.STATE.CHECKED) # Start in off state self.switch.align_to(lbl, lv.ALIGN.OUT_RIGHT_MID, 20, 0) - def cb(): - wordcount = 24 if self.switch.get_state() else 12 + def cb(e): + wordcount = 24 if self.switch.has_state(lv.STATE.CHECKED) else 12 self.table.set_mnemonic(generator(wordcount)) - self.switch.set_event_cb(on_release(cb)) + self.switch.add_event_cb(cb, lv.EVENT.VALUE_CHANGED, None) # fix mnemonic components - self.kb = lv.btnm(self) + self.kb = lv.buttonmatrix(self) self.kb.set_map(["1", "2", "4", "8", "16", "32", "\n", "64", "128", "256", "512", "1024", ""]) - self.kb.set_ctrl_map([lv.btnm.CTRL.TGL_ENABLE for i in range(11)]) + self.kb.set_ctrl_map([lv.buttonmatrix.CTRL.CHECKABLE for i in range(11)]) self.kb.set_width(HOR_RES) self.kb.set_height(100) self.kb.align_to(self.table, lv.ALIGN.OUT_BOTTOM_MID, 0, 5) - self.kb.set_hidden(True) + self.kb.add_flag(lv.obj.FLAG.HIDDEN) self.instruction = add_label("Hint: click on any word above to edit it.", scr=self, style="hint") self.instruction.align_to(self.kb, lv.ALIGN.OUT_BOTTOM_MID, 0, 15) - def on_word_click(self, obj, evt): - if evt != lv.EVENT.RELEASED: + def on_word_click(self, event): + code = event.get_code() + if code != lv.EVENT.RELEASED: return + obj = event.get_target() # get coordinates - point = lv.point_t() - indev = lv.indev_get_act() - lv.indev_get_point(indev, point) + indev = lv.indev_active() + point = indev.get_point() # get offsets dx = point.x - obj.get_x() dy = point.y - obj.get_y() @@ -125,23 +126,25 @@ def change_word(self, idx): % (idx+1, word.upper(), self.wordlist.index(word)+1) ) # hide switch - if not self.switch.get_hidden(): - self.switch.set_hidden(True) - self.switch_lbl.set_hidden(True) - self.kb.set_hidden(False) + if not self.switch.has_flag(lv.obj.FLAG.HIDDEN): + self.switch.add_flag(lv.obj.FLAG.HIDDEN) + self.switch_lbl.add_flag(lv.obj.FLAG.HIDDEN) + self.kb.remove_flag(lv.obj.FLAG.HIDDEN) word_idx = self.wordlist.index(word) self.kb.set_ctrl_map([ - lv.btnm.CTRL.TGL_ENABLE | (lv.btnm.CTRL.TGL_STATE if ((word_idx>>i)&1) else 0) + lv.buttonmatrix.CTRL.CHECKABLE | (lv.buttonmatrix.CTRL.CHECKED if ((word_idx>>i)&1) else 0) for i in range(11) ]) # callback on toggle - def cb(obj, event): - if event != lv.EVENT.RELEASED: + def cb(event): + code = event.get_code() + if code != lv.EVENT.RELEASED: return - c = obj.get_active_btn_text() + btn_id = self.kb.get_selected_button() + c = self.kb.get_button_text(btn_id) if c is None: return - bits = [obj.get_btn_ctrl(i, lv.btnm.CTRL.TGL_STATE) for i in range(11)] + bits = [self.kb.has_button_ctrl(i, lv.buttonmatrix.CTRL.CHECKED) for i in range(11)] num = 0 for i, bit in enumerate(reversed(bits)): num = num << 1 @@ -157,7 +160,7 @@ def cb(obj, event): "Changing word number %d:\n%s (%d in wordlist)" % (idx+1, word.upper(), self.wordlist.index(word)+1) ) - self.kb.set_event_cb(cb) + self.kb.add_event_cb(cb, lv.EVENT.ALL, None) def confirm(self): @@ -177,11 +180,11 @@ def __init__( self.checker = checker self.lookup = lookup - self.close_button.del_async() + self.close_button.delete() self.close_button = None if lookup is not None: - self.autocomplete = lv.btnm(self) + self.autocomplete = lv.buttonmatrix(self) self.kb = HintKeyboard(self) self.kb.set_map( @@ -225,10 +228,10 @@ def __init__( if lookup is not None: # Next word button inactive - self.kb.set_btn_ctrl(self.BTN_NEXT, lv.btnm.CTRL.INACTIVE) + self.kb.set_button_ctrl(self.BTN_NEXT, lv.buttonmatrix.CTRL.DISABLED) if checker is not None: # Done inactive - self.kb.set_btn_ctrl(self.BTN_DONE, lv.btnm.CTRL.INACTIVE) + self.kb.set_button_ctrl(self.BTN_DONE, lv.buttonmatrix.CTRL.DISABLED) self.kb.set_width(HOR_RES) self.kb.set_height(260) self.kb.align(lv.ALIGN.BOTTOM_MID, 0, 0) @@ -247,16 +250,18 @@ def __init__( self.autocomplete.align_to(self.kb, lv.ALIGN.OUT_TOP_MID, 0, 0) words = lookup("", 4) + [""] self.autocomplete.set_map(words) - self.autocomplete.set_event_cb(self.select_word) + self.autocomplete.add_event_cb(self.select_word, lv.EVENT.ALL, None) def fix_cb(self): self.table.set_mnemonic(self.fixer(self.get_mnemonic())) self.check_buttons() - def select_word(self, obj, event): - if event != lv.EVENT.RELEASED: + def select_word(self, event): + code = event.get_code() + if code != lv.EVENT.RELEASED: return - word = obj.get_active_btn_text() + btn_id = self.autocomplete.get_selected_button() + word = self.autocomplete.get_button_text(btn_id) if word is None: return self.table.autocomplete_word(word) @@ -267,12 +272,12 @@ def get_mnemonic(self): mnemonic = self.table.get_mnemonic() # check if we can autocomplete the last word if self.lookup is not None: - self.kb.set_btn_ctrl(self.BTN_NEXT, lv.btnm.CTRL.INACTIVE) + self.kb.set_button_ctrl(self.BTN_NEXT, lv.buttonmatrix.CTRL.DISABLED) word = self.table.get_last_word() candidates = self.lookup(word, 4) self.autocomplete.set_map(candidates + [""]) if len(candidates) == 1 or word in candidates: - self.kb.clear_btn_ctrl(self.BTN_NEXT, lv.btnm.CTRL.INACTIVE) + self.kb.clear_button_ctrl(self.BTN_NEXT, lv.buttonmatrix.CTRL.DISABLED) if len(candidates) == 1: mnemonic = " ".join(self.table.words[:-1]) mnemonic += " " + candidates[0] @@ -287,16 +292,16 @@ def check_buttons(self): # check if mnemonic is valid if self.checker is not None and mnemonic is not None: if self.checker(mnemonic): - self.kb.clear_btn_ctrl(self.BTN_DONE, lv.btnm.CTRL.INACTIVE) + self.kb.clear_button_ctrl(self.BTN_DONE, lv.buttonmatrix.CTRL.DISABLED) else: - self.kb.set_btn_ctrl(self.BTN_DONE, lv.btnm.CTRL.INACTIVE) + self.kb.set_button_ctrl(self.BTN_DONE, lv.buttonmatrix.CTRL.DISABLED) # check if we are at 12, 18 or 24 words # offer to fix mnemonic if it's invalid num_words = len(mnemonic.split()) if ( self.fixer is not None and num_words in [12, 18, 24] - and self.kb.get_btn_ctrl(self.BTN_DONE, lv.btnm.CTRL.INACTIVE) + and self.kb.has_button_ctrl(self.BTN_DONE, lv.buttonmatrix.CTRL.DISABLED) ): # set correct button coordinates y = -33 - self.table.get_height() // 2 if num_words == 18 else -38 @@ -315,12 +320,12 @@ def check_buttons(self): def callback(self, obj, event): if event != lv.EVENT.RELEASED: return - c = obj.get_active_btn_text() + num = obj.get_selected_button() + c = obj.get_button_text(num) if c is None: return - num = obj.get_active_btn() # if inactive button is clicked - return - if obj.get_btn_ctrl(num, lv.btnm.CTRL.INACTIVE): + if obj.has_button_ctrl(num, lv.buttonmatrix.CTRL.DISABLED): return if c == lv.SYMBOL.LEFT + " Back": self.confirm_exit() @@ -350,37 +355,23 @@ def confirm_exit(self): self.set_value(None) return - modal_style = lv.style_t() - lv.style_copy(modal_style, lv.style_plain_color) - # Set the background's style - modal_style.body.main_color = lv.color_make(0, 0, 0) - modal_style.body.grad_color = modal_style.body.main_color - modal_style.body.opa = lv.OPA._50 - - # Create a base object for the modal background - bg = lv.obj(self) - bg.set_style(modal_style) - bg.set_pos(0, 0) - bg.set_size(self.get_width(), self.get_height()) - # Enable opacity scaling for the animation - bg.set_opa_scale_enable(True) - - btns = ["No, stay here", "Yes, leave", ""] - - def event_handler(obj, event): - if event == lv.EVENT.VALUE_CHANGED: - if lv.mbox.get_active_btn_text(obj) == btns[1]: - self.set_value(None) - else: - obj.del_async() - bg.del_async() - - mbox = lv.mbox(self) - mbox.set_text( - "\nAre you sure you want to exit?\n\n" - "Everything you entered will be forgotten!\n\n" + # LVGL 9.x msgbox with backdrop (pass None for modal) + mbox = lv.msgbox(None) + mbox.add_title("Confirm Exit") + mbox.add_text( + "Are you sure you want to exit?\n\n" + "Everything you entered will be forgotten!" ) - mbox.add_btns(btns) - mbox.set_width(400) - mbox.set_event_cb(event_handler) - mbox.align(lv.ALIGN.CENTER, 0, 0) + + btn_stay = mbox.add_footer_button("No, stay here") + btn_leave = mbox.add_footer_button("Yes, leave") + + def on_stay(e): + lv.msgbox.close(mbox) + + def on_leave(e): + lv.msgbox.close(mbox) + self.set_value(None) + + btn_stay.add_event_cb(on_stay, lv.EVENT.CLICKED, None) + btn_leave.add_event_cb(on_leave, lv.EVENT.CLICKED, None) diff --git a/src/gui/screens/settings.py b/src/gui/screens/settings.py index 21ef3e48..54374c5b 100644 --- a/src/gui/screens/settings.py +++ b/src/gui/screens/settings.py @@ -21,23 +21,23 @@ def __init__(self, controls, title="Host setttings", note=None, controls_empty_t scr=self.page, style="hint", ) - switch = lv.sw(self.page) + switch = lv.switch(self.page) switch.align_to(hint, lv.ALIGN.OUT_BOTTOM_MID, 0, 10) lbl = add_label(" OFF ON ", scr=self.page) lbl.align_to(switch, lv.ALIGN.CENTER, 0, 0) if control.get("value", False): - switch.on(lv.ANIM.OFF) + switch.add_state(lv.STATE.CHECKED) self.switches.append(switch) y = lbl.get_y() + 80 self.next_y = y if not controls: label = add_label(controls_empty_text, y, scr=self.page) self.next_y = label.get_y() + label.get_height() + 40 - self.confirm_button.set_event_cb(on_release(self.update)) - self.cancel_button.set_event_cb(on_release(lambda: self.set_value(None))) + self.confirm_button.add_event_cb(on_release(self.update), lv.EVENT.ALL, None) + self.cancel_button.add_event_cb(on_release(lambda: self.set_value(None)), lv.EVENT.ALL, None) def update(self): - self.set_value([switch.get_state() for switch in self.switches]) + self.set_value([switch.has_state(lv.STATE.CHECKED) for switch in self.switches]) class DevSettings(Prompt): def __init__(self, dev=False, usb=False, note=None): @@ -56,12 +56,12 @@ def __init__(self, dev=False, usb=False, note=None): scr=self.page, style="hint", ) - self.usb_switch = lv.sw(self.page) + self.usb_switch = lv.switch(self.page) self.usb_switch.align_to(usb_hint, lv.ALIGN.OUT_BOTTOM_MID, 0, 20) lbl = add_label(" OFF ON ", scr=self.page) lbl.align_to(self.usb_switch, lv.ALIGN.CENTER, 0, 0) if usb: - self.usb_switch.on(lv.ANIM.OFF) + self.usb_switch.add_state(lv.STATE.CHECKED) # y += 200 # dev_label = add_label("Developer mode", y, scr=self.page) @@ -74,30 +74,30 @@ def __init__(self, dev=False, usb=False, note=None): # scr=self.page, # style="hint", # ) - # self.dev_switch = lv.sw(self.page) + # self.dev_switch = lv.switch(self.page) # self.dev_switch.align(dev_hint, lv.ALIGN.OUT_BOTTOM_MID, 0, 20) # lbl = add_label(" OFF ON ", scr=self.page) # lbl.align(self.dev_switch, lv.ALIGN.CENTER, 0, 0) # if dev: - # self.dev_switch.on(lv.ANIM.OFF) - self.confirm_button.set_event_cb(on_release(self.update)) - self.cancel_button.set_event_cb(on_release(lambda: self.set_value(None))) + # self.dev_switch.add_state(lv.STATE.CHECKED) + self.confirm_button.add_event_cb(on_release(self.update), lv.EVENT.ALL, None) + self.cancel_button.add_event_cb(on_release(lambda: self.set_value(None)), lv.EVENT.ALL, None) self.wipebtn = add_button( lv.SYMBOL.TRASH + " Wipe device", on_release(self.wipe), scr=self ) self.wipebtn.align(lv.ALIGN.BOTTOM_MID, 0, -140) + # LVGL 9.x: style wipe button with red color style = lv.style_t() - lv.style_copy(style, self.wipebtn.get_style(lv.btn.STYLE.REL)) - style.body.main_color = lv.color_hex(0x951E2D) - style.body.grad_color = lv.color_hex(0x951E2D) - self.wipebtn.set_style(lv.btn.STYLE.REL, style) + style.init() + style.set_bg_color(lv.color_hex(0x951E2D)) + self.wipebtn.add_style(style, lv.PART.MAIN) def wipe(self): self.set_value( { - "dev": False, # self.dev_switch.get_state(), - "usb": self.usb_switch.get_state(), + "dev": False, # self.dev_switch.has_state(lv.STATE.CHECKED), + "usb": self.usb_switch.has_state(lv.STATE.CHECKED), "wipe": True, } ) @@ -105,8 +105,8 @@ def wipe(self): def update(self): self.set_value( { - "dev": False, # self.dev_switch.get_state(), - "usb": self.usb_switch.get_state(), + "dev": False, # self.dev_switch.has_state(lv.STATE.CHECKED), + "usb": self.usb_switch.has_state(lv.STATE.CHECKED), "wipe": False, } ) diff --git a/src/gui/screens/transaction.py b/src/gui/screens/transaction.py index 83364376..fbc5fc59 100644 --- a/src/gui/screens/transaction.py +++ b/src/gui/screens/transaction.py @@ -21,38 +21,39 @@ def __init__(self, title, meta): lbl = add_label("Show detailed information ", scr=self) lbl.align_to(obj, lv.ALIGN.CENTER, 0, 0) - self.details_sw = lv.sw(self) + self.details_sw = lv.switch(self) self.details_sw.align_to(obj, lv.ALIGN.CENTER, 130, 0) - self.details_sw.set_event_cb(on_release(self.toggle_details)) + self.details_sw.add_event_cb(on_release(self.toggle_details), lv.EVENT.ALL, None) if enable_inputs: - self.details_sw.on(lv.ANIM.OFF) + self.details_sw.add_state(lv.STATE.CHECKED) # change page a bit self.page.set_pos(0, lbl.get_y()+20) self.page.set_size(480, 800-130-lbl.get_y()) - self.page2 = lv.page(self) + # LVGL 9.x: lv.page replaced with scrollable lv.obj + self.page2 = lv.obj(self) self.page2.set_pos(self.page.get_x(), self.page.get_y()) self.page2.set_size(self.page.get_width(), self.page.get_height()) - # define styles + # LVGL 9.x: define styles style = lv.style_t() - lv.style_copy(style, self.message.get_style(0)) - style.text.font = lv.font_roboto_mono_28 + style.init() + style.set_text_font(lv.font_montserrat_28) style_primary = lv.style_t() - lv.style_copy(style_primary, self.message.get_style(0)) - style_primary.text.font = lv.font_roboto_mono_22 + style_primary.init() + style_primary.set_text_font(lv.font_montserrat_22) style_secondary = lv.style_t() - lv.style_copy(style_secondary, self.message.get_style(0)) - style_secondary.text.color = lv.color_hex(0x999999) - style_secondary.text.font = lv.font_roboto_mono_22 + style_secondary.init() + style_secondary.set_text_color(lv.color_hex(0x999999)) + style_secondary.set_text_font(lv.font_montserrat_22) style_warning = lv.style_t() - lv.style_copy(style_warning, self.message.get_style(0)) - style_warning.text.color = lv.color_hex(0xFF9A00) - style_warning.text.font = lv.font_roboto_22 + style_warning.init() + style_warning.set_text_color(lv.color_hex(0xFF9A00)) + style_warning.set_text_font(lv.font_montserrat_22) self.style = style self.style_secondary = style_secondary @@ -74,7 +75,7 @@ def __init__(self, title, meta): else: fee_txt = "%d satoshi" % (meta["fee"]) fee = add_label("Fee: " + fee_txt, scr=self.page) - fee.set_style(0, style) + fee.add_style(style, lv.PART.MAIN) fee.align_to(obj, lv.ALIGN.OUT_BOTTOM_MID, 0, 30) obj = fee @@ -82,7 +83,7 @@ def __init__(self, title, meta): if "warnings" in meta and len(meta["warnings"]) > 0: text = "WARNING!\n" + "\n".join(meta["warnings"]) self.warning = add_label(text, scr=self.page) - self.warning.set_style(0, style_warning) + self.warning.add_style(style_warning, lv.PART.MAIN) self.warning.align_to(obj, lv.ALIGN.OUT_BOTTOM_MID, 0, 30) lbl = add_label("%d INPUTS" % len(meta["inputs"]), scr=self.page2) @@ -94,7 +95,7 @@ def __init__(self, title, meta): idxlbl.align_to(lbl, lv.ALIGN.OUT_BOTTOM_MID, 0, 30) idxlbl.set_x(30) lbl = lv.label(self.page2) - lbl.set_long_mode(lv.label.LONG.BREAK) + lbl.set_long_mode(lv.label.LONG_MODE.WRAP) lbl.set_width(380) valuetxt = "???" if inp["value"] == -1 else "%.8f" % (inp["value"]/1e8) lbl.set_text("%s %s from %s" % (valuetxt, inp.get("asset", self.default_asset), inp.get("label", "Unknown wallet"))) @@ -103,12 +104,12 @@ def __init__(self, title, meta): if inp.get("sighash", ""): shlbl = lv.label(self.page2) - shlbl.set_long_mode(lv.label.LONG.BREAK) + shlbl.set_long_mode(lv.label.LONG_MODE.WRAP) shlbl.set_width(380) shlbl.set_text(inp.get("sighash", "")) shlbl.align_to(lbl, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 5) shlbl.set_x(60) - shlbl.set_style(0, style_warning) + shlbl.add_style(style_warning, lv.PART.MAIN) lbl = shlbl obj = lbl @@ -121,7 +122,7 @@ def __init__(self, title, meta): idxlbl.align_to(lbl, lv.ALIGN.OUT_BOTTOM_MID, 0, 30) idxlbl.set_x(30) lbl = lv.label(self.page2) - lbl.set_long_mode(lv.label.LONG.BREAK) + lbl.set_long_mode(lv.label.LONG_MODE.WRAP) lbl.set_width(380) valuetxt = "???" if out["value"] == -1 else "%.8f" % (out["value"]/1e8) lbl.set_text("%s %s to %s" % (valuetxt, out.get("asset", self.default_asset), out.get("label", ""))) @@ -129,22 +130,22 @@ def __init__(self, title, meta): lbl.set_x(60) addrlbl = lv.label(self.page2) - addrlbl.set_long_mode(lv.label.LONG.BREAK) + addrlbl.set_long_mode(lv.label.LONG_MODE.WRAP) addrlbl.set_width(380) addrlbl.set_text(format_addr(out["address"], words=4)) addrlbl.align_to(lbl, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 5) addrlbl.set_x(60) if out.get("label", ""): - addrlbl.set_style(0, style_secondary) + addrlbl.add_style(style_secondary, lv.PART.MAIN) else: - addrlbl.set_style(0, style_primary) + addrlbl.add_style(style_primary, lv.PART.MAIN) lbl = addrlbl if "warning" in out: text = out["warning"] warning = add_label(text, scr=self.page2) - warning.set_align(lv.label.ALIGN.LEFT) + warning.set_style_text_align(lv.TEXT_ALIGN.LEFT, 0) warning.set_width(380) - warning.set_style(0, self.style_warning) + warning.add_style(self.style_warning, lv.PART.MAIN) warning.align_to(addrlbl, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 10) warning.set_x(60) lbl = warning @@ -158,12 +159,12 @@ def __init__(self, title, meta): self.toggle_details() def toggle_details(self): - if self.details_sw.get_state(): - self.page2.set_hidden(False) - self.page.set_hidden(True) + if self.details_sw.has_state(lv.STATE.CHECKED): + self.page2.remove_flag(lv.obj.FLAG.HIDDEN) + self.page.add_flag(lv.obj.FLAG.HIDDEN) else: - self.page2.set_hidden(True) - self.page.set_hidden(False) + self.page2.add_flag(lv.obj.FLAG.HIDDEN) + self.page.remove_flag(lv.obj.FLAG.HIDDEN) def show_output(self, out, obj): # show output @@ -183,15 +184,15 @@ def show_output(self, out, obj): txt = format_addr(out["address"]) addr = add_label(txt, scr=self.page) if out.get("label", ""): - addr.set_style(0, self.style_secondary) + addr.add_style(self.style_secondary, lv.PART.MAIN) else: - addr.set_style(0, self.style) + addr.add_style(self.style, lv.PART.MAIN) addr.align_to(obj, lv.ALIGN.OUT_BOTTOM_MID, 0, 10) obj = addr if "warning" in out: text = "WARNING! %s" % out["warning"] warning = add_label(text, scr=self.page) - warning.set_style(0, self.style_warning) + warning.add_style(self.style_warning, lv.PART.MAIN) warning.align_to(obj, lv.ALIGN.OUT_BOTTOM_MID, 0, 10) obj = warning return obj \ No newline at end of file