Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
159f483
Modified layout a little bit
maggo83 Dec 10, 2025
4e3834d
Added basic support for internationalization
maggo83 Dec 10, 2025
1f8da54
Added user interface to change language
maggo83 Dec 10, 2025
5bfd00a
Updated nad loaded montserrat fonts to support German "Umlauts" Ä,Ö,Ü
maggo83 Dec 11, 2025
d58e6da
Changed icon for Language Menu
maggo83 Dec 11, 2025
28e525e
Removed not needed intermediate AI files
maggo83 Dec 11, 2025
65355a1
Merge branch 'Mock-UI-Add-framework-for-i18n' into MERGED_I18n_Design…
maggo83 Dec 11, 2025
8f8efeb
Minor fixes
maggo83 Dec 11, 2025
af79d04
PAssphrase menu closes automatically when passphrase is set
maggo83 Dec 11, 2025
df96857
Fixed logic to display "sign message" (and hence whole "process input…
maggo83 Dec 11, 2025
256c280
merge: resolve conflict with main (keep both i18n + control mode)
k9ert Dec 14, 2025
d1ff549
Removed support for "Umlauts"
maggo83 Dec 18, 2025
3cdbf28
Merge branch 'Mock-UI-Add-framework-for-i18n' of https://github.com/m…
maggo83 Dec 18, 2025
7ecd527
Fixed review findings
maggo83 Dec 18, 2025
fcf6e93
Merge branch 'Mock-UI-Add-framework-for-i18n' into MERGED_I18n_Design…
maggo83 Dec 18, 2025
91b6a5c
Added support for different size (height) menu elements in GenericMenu
maggo83 Dec 18, 2025
cbb4cb3
Split Status Bar into Wallet- and DeviceBar
maggo83 Dec 18, 2025
cfc7708
Merge branch 'Make-MenuItems-sizes-context-dependent' into MarcosPlay…
maggo83 Dec 18, 2025
2e92fe0
Merge branch 'Split-StatusBar-into-WalletBar-and-DeviceBar' into Marc…
maggo83 Dec 18, 2025
452e22c
Minor fixes to UI
maggo83 Dec 18, 2025
b15d21e
Made Menus "edgy"
maggo83 Dec 20, 2025
cff42e0
Revert "Made Menus "edgy""
maggo83 Dec 21, 2025
3ebf392
Move "ManageWallet" to first line in "wallet" section in main menu
maggo83 Dec 30, 2025
73f2d9e
Increase sizes of display elements in wallet bar
maggo83 Dec 30, 2025
bafb27d
Added PoC for "Help"-Buttons for menu items
maggo83 Jan 1, 2026
d02e4e0
Right center wallet name in wallet bar
maggo83 Jan 13, 2026
abcfeee
Remove rounded edges/corners for bars
maggo83 Jan 14, 2026
f9390ed
feat(MockUI): Add guided tour for first-time users
maggo83 Jan 14, 2026
1bbf01f
chore: Add ui_state_config.json to gitignore
maggo83 Jan 14, 2026
cec701a
improved location of explainer highlight for info symbols
maggo83 Jan 15, 2026
d954422
Made "Skip Tour" Button wider
maggo83 Jan 16, 2026
d027893
-Prep commit to make following merge easier
maggo83 Feb 23, 2026
d8422ea
Merge remote-tracking branch 'upstream/main' into MarcosPlaygroundBra…
maggo83 Feb 23, 2026
591493a
test(device): refactor i18n device tests after GUI changes — reflect …
maggo83 Feb 23, 2026
ad95be1
Added self-written modal_overlay as workaround for not working LVGL b…
maggo83 Feb 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions scenarios/MockUI/src/MockUI/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# MockUI/__init__.py
from .basic import BTN_HEIGHT, BTN_WIDTH, MENU_PCT, PAD_SIZE, SWITCH_HEIGHT, SWITCH_WIDTH, STATUS_BTN_HEIGHT, STATUS_BTN_WIDTH, ONE_LETTER_SYMBOL_WIDTH, TWO_LETTER_SYMBOL_WIDTH, THREE_LETTER_SYMBOL_WIDTH, GREEN, ORANGE, RED
from .basic import MainMenu, LockedMenu, StatusBar, ActionScreen, GenericMenu
from .basic import MainMenu, LockedMenu, DeviceBar, WalletBar, ActionScreen, GenericMenu
from .basic import NavigationController
from .tour import UIExplainer, GuidedTour

from .helpers import UIState, SpecterState, Wallet

Expand All @@ -22,8 +23,11 @@
BackupsMenu,
SecurityMenu,
StorageMenu,
SettingsMenu,
)

from .tour import GuidedTour

__all__ = [
"BTN_HEIGHT", "BTN_WIDTH",
"MENU_PCT",
Expand All @@ -39,7 +43,8 @@
"Wallet",
"ActionScreen",
"UIState",
"StatusBar",
"DeviceBar",
"WalletBar",
"SeedPhraseMenu",
"SecurityMenu",
"InterfacesMenu",
Expand All @@ -51,6 +56,9 @@
"LockedMenu",
"GenerateSeedMenu",
"StorageMenu",
"SettingsMenu",
"PassphraseMenu",
"NavigationController",
"UIExplainer",
"GuidedTour",
]
15 changes: 10 additions & 5 deletions scenarios/MockUI/src/MockUI/basic/__init__.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
from .ui_consts import BTN_HEIGHT, BTN_WIDTH, MENU_PCT, PAD_SIZE, SWITCH_HEIGHT, SWITCH_WIDTH, STATUS_BTN_HEIGHT, STATUS_BTN_WIDTH, BTC_ICON_WIDTH, ONE_LETTER_SYMBOL_WIDTH, TWO_LETTER_SYMBOL_WIDTH, THREE_LETTER_SYMBOL_WIDTH, GREEN, ORANGE, RED, GREEN_HEX, ORANGE_HEX, RED_HEX, WHITE_HEX, BLACK_HEX
from .ui_consts import BTN_HEIGHT, BTN_WIDTH, BACK_BTN_HEIGHT, BACK_BTN_WIDTH, MENU_PCT, PAD_SIZE, SWITCH_HEIGHT, SWITCH_WIDTH, STATUS_BTN_HEIGHT, STATUS_BTN_WIDTH, BTC_ICON_WIDTH, ONE_LETTER_SYMBOL_WIDTH, TWO_LETTER_SYMBOL_WIDTH, THREE_LETTER_SYMBOL_WIDTH, GREEN, ORANGE, RED, GREEN_HEX, ORANGE_HEX, RED_HEX, WHITE_HEX, BLACK_HEX, TITLE_PADDING, MODAL_WIDTH_PCT, MODAL_HEIGHT_PCT, EXPLAINER_WIDTH_PCT, EXPLAINER_HEIGHT_PCT, EXPLAINER_OVERLAY_OPA
from .main_menu import MainMenu
from .locked_menu import LockedMenu
from .status_bar import StatusBar
from .device_bar import DeviceBar
from .wallet_bar import WalletBar
from .action_screen import ActionScreen
from .menu import GenericMenu
from .modal_overlay import ModalOverlay
from .navigation_controller import NavigationController
from .symbol_lib import BTC_ICONS

__all__ = ["BTN_HEIGHT", "BTN_WIDTH",
__all__ = ["BTN_HEIGHT", "BTN_WIDTH", "BACK_BTN_HEIGHT", "BACK_BTN_WIDTH",
"MENU_PCT",
"PAD_SIZE",
"TITLE_PADDING",
"MODAL_WIDTH_PCT", "MODAL_HEIGHT_PCT",
"EXPLAINER_WIDTH_PCT", "EXPLAINER_HEIGHT_PCT", "EXPLAINER_OVERLAY_OPA",
"SWITCH_HEIGHT", "SWITCH_WIDTH",
"STATUS_BTN_HEIGHT", "STATUS_BTN_WIDTH",
"BTC_ICON_WIDTH",
"ONE_LETTER_SYMBOL_WIDTH", "TWO_LETTER_SYMBOL_WIDTH", "THREE_LETTER_SYMBOL_WIDTH",
"GREEN", "ORANGE", "RED",
"GREEN_HEX", "ORANGE_HEX", "RED_HEX", "WHITE_HEX", "BLACK_HEX",
"MainMenu", "LockedMenu", "StatusBar", "ActionScreen", "GenericMenu", "NavigationController",
"BTC_ICONS"
"MainMenu", "LockedMenu", "DeviceBar", "WalletBar", "ActionScreen", "GenericMenu", "ModalOverlay", "NavigationController",
"BTC_ICONS"
]
8 changes: 6 additions & 2 deletions scenarios/MockUI/src/MockUI/basic/action_screen.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import lvgl as lv
from .ui_consts import BTN_HEIGHT, BTN_WIDTH
from .ui_consts import BTN_HEIGHT, BTN_WIDTH, BACK_BTN_WIDTH, BACK_BTN_HEIGHT
from .symbol_lib import BTC_ICONS

class ActionScreen(lv.obj):
Expand All @@ -19,11 +19,15 @@ def __init__(self, title, parent, *args, **kwargs):
# Fill parent
self.set_width(lv.pct(100))
self.set_height(lv.pct(100))
# Remove padding from base object to allow full-width content
self.set_style_pad_all(0, 0)
# Remove border
self.set_style_border_width(0, 0)

# If ui_state has history, show back button to the left of the title
if parent.ui_state and parent.ui_state.history and len(parent.ui_state.history) > 0:
self.back_btn = lv.button(self)
self.back_btn.set_size(40, 28)
self.back_btn.set_size(BACK_BTN_HEIGHT, BACK_BTN_WIDTH)
self.back_ico = lv.image(self.back_btn)
BTC_ICONS.CARET_LEFT.add_to_parent(self.back_ico)
self.back_ico.center()
Expand Down
235 changes: 235 additions & 0 deletions scenarios/MockUI/src/MockUI/basic/device_bar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import lvgl as lv
from ..helpers import Battery
from .ui_consts import BTC_ICON_WIDTH, GREEN_HEX, ORANGE_HEX, RED_HEX, STATUS_BTN_HEIGHT, STATUS_BTN_WIDTH, THREE_LETTER_SYMBOL_WIDTH
from .symbol_lib import BTC_ICONS


class DeviceBar(lv.obj):
"""Device status bar showing system-level information. Designed to be ~5% of the screen height at the top."""

def __init__(self, parent, height_pct=5, *args, **kwargs):
super().__init__(parent, *args, **kwargs)

self.parent = parent # for callback access

self.set_width(lv.pct(100))
self.set_height(lv.pct(height_pct))

self.set_layout(lv.LAYOUT.FLEX)
self.set_flex_flow(lv.FLEX_FLOW.ROW)
self.set_flex_align(
lv.FLEX_ALIGN.SPACE_BETWEEN, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER
)
self.set_style_pad_all(0, 0)
self.set_style_radius(0, 0)
self.set_style_border_width(0, 0)

# LEFT SECTION: Lock button
self.left_container = lv.obj(self)
self.left_container.set_width(STATUS_BTN_WIDTH + 10)
self.left_container.set_height(lv.pct(100))
self.left_container.set_layout(lv.LAYOUT.FLEX)
self.left_container.set_flex_flow(lv.FLEX_FLOW.ROW)
self.left_container.set_flex_align(lv.FLEX_ALIGN.START, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER)
self.left_container.set_style_pad_all(0, 0)
self.left_container.set_style_border_width(0, 0)

self.lock_btn = lv.button(self.left_container)
self.lock_btn.set_size(STATUS_BTN_WIDTH, STATUS_BTN_HEIGHT)
self.lock_ico = lv.image(self.lock_btn)
BTC_ICONS.UNLOCK.add_to_parent(self.lock_ico)
self.lock_ico.center()
self.lock_btn.add_event_cb(self.lock_cb, lv.EVENT.CLICKED, None)

# CENTER SECTION: Peripheral indicators
self.center_container = lv.obj(self)
self.center_container.set_width(BTC_ICON_WIDTH * 4 + 40)
self.center_container.set_height(lv.pct(100))
self.center_container.set_layout(lv.LAYOUT.FLEX)
self.center_container.set_flex_flow(lv.FLEX_FLOW.ROW)
self.center_container.set_flex_align(lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER)
self.center_container.set_style_pad_all(0, 0)
self.center_container.set_style_border_width(0, 0)

# Peripheral indicators (only visible when unlocked)
self.qr_img = lv.image(self.center_container)
self.qr_img.set_width(BTC_ICON_WIDTH)

self.usb_img = lv.image(self.center_container)
self.usb_img.set_width(BTC_ICON_WIDTH)

self.sd_img = lv.image(self.center_container)
self.sd_img.set_width(BTC_ICON_WIDTH)

self.smartcard_img = lv.image(self.center_container)
self.smartcard_img.set_width(BTC_ICON_WIDTH)

# Make peripheral icons clickable to navigate to interfaces menu
peripheral_icons = [
self.qr_img,
self.usb_img,
self.sd_img,
self.smartcard_img,
]
for ico in peripheral_icons:
ico.add_flag(lv.obj.FLAG.CLICKABLE)
ico.add_event_cb(self.peripheral_ico_clicked, lv.EVENT.CLICKED, None)

# RIGHT SECTION: Battery, Language, Settings, Power (in that order)
self.right_container = lv.obj(self)
self.right_container.set_width(STATUS_BTN_WIDTH * 2 + THREE_LETTER_SYMBOL_WIDTH + 70)
self.right_container.set_height(lv.pct(100))
self.right_container.set_layout(lv.LAYOUT.FLEX)
self.right_container.set_flex_flow(lv.FLEX_FLOW.ROW)
self.right_container.set_flex_align(lv.FLEX_ALIGN.END, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER)
self.right_container.set_style_pad_all(0, 0)
self.right_container.set_style_border_width(0, 0)

# Battery icon
self.batt_icon = Battery(self.right_container)
self.batt_icon.VALUE = parent.specter_state.battery_pct
self.batt_icon.update()

# Language indicator (clickable selector) - always visible
self.lang_lbl = lv.label(self.right_container)
self.lang_lbl.set_text("")
self.lang_lbl.set_width(THREE_LETTER_SYMBOL_WIDTH)
self.lang_lbl.add_flag(lv.obj.FLAG.CLICKABLE)
self.lang_lbl.add_event_cb(self.lang_clicked, lv.EVENT.CLICKED, None)

# Settings button
self.settings_btn = lv.button(self.right_container)
self.settings_btn.set_size(STATUS_BTN_WIDTH, STATUS_BTN_HEIGHT)
self.settings_ico = lv.image(self.settings_btn)
BTC_ICONS.GEAR.add_to_parent(self.settings_ico)
self.settings_ico.center()
self.settings_btn.add_event_cb(self.settings_cb, lv.EVENT.CLICKED, None)

# Power button
self.power_btn = lv.button(self.right_container)
self.power_btn.set_size(STATUS_BTN_WIDTH, STATUS_BTN_HEIGHT)
self.power_lbl = lv.label(self.power_btn)
self.power_lbl.set_text(lv.SYMBOL.POWER)
self.power_lbl.center()
self.power_btn.add_event_cb(self.power_cb, lv.EVENT.CLICKED, None)

# Apply smaller font to labels
self.font = lv.font_montserrat_12
labels = [self.lang_lbl, self.power_lbl]
for lbl in labels:
lbl.set_style_text_font(self.font, 0)

def power_cb(self, e):
if e.get_code() == lv.EVENT.CLICKED:
if self.parent.specter_state.battery_pct is None:
self.parent.specter_state.battery_pct = 50
self.parent.refresh_ui()
else:
self.parent.specter_state.battery_pct = None
self.parent.refresh_ui()

def lock_cb(self, e):
if e.get_code() == lv.EVENT.CLICKED:
if self.parent.specter_state.is_locked:
# unlocking should be handled by the locked screen's PIN flow
return
else:
# lock the device and force NavigationController to show the locked screen
self.parent.specter_state.lock()
# show_menu will detect is_locked and show the locked screen
self.parent.show_menu(None)

def peripheral_ico_clicked(self, e):
if e.get_code() == lv.EVENT.CLICKED:
if self.parent.ui_state.current_menu_id != "interfaces":
self.parent.show_menu("interfaces")

def lang_clicked(self, e):
"""Navigate to language selection menu when language label is clicked."""
if e.get_code() == lv.EVENT.CLICKED:
if self.parent.ui_state.current_menu_id != "select_language":
self.parent.show_menu("select_language")

def settings_cb(self, e):
"""Navigate to settings menu when settings button is clicked."""
if e.get_code() == lv.EVENT.CLICKED:
if self.parent.ui_state.current_menu_id != "manage_settings":
self.parent.show_menu("manage_settings")

def refresh(self, state):
"""Update visual elements from a SpecterState-like object."""
locked = state.is_locked

# Battery (always visible)
self.batt_icon.CHARGING = state.is_charging
if state.has_battery:
perc = state.battery_pct
self.batt_icon.VALUE = perc
self.batt_icon.update()
else:
self.batt_icon.VALUE = 100
self.batt_icon.update()

# Language (always visible)
lang_code = self.parent.i18n.get_language()
self.lang_lbl.set_text(self._truncate(lang_code.upper(), 3))

# Lock icon (always visible, but changes based on state)
if locked:
BTC_ICONS.LOCK.add_to_parent(self.lock_ico)
# Hide peripheral indicators when locked
self.qr_img.set_src(None)
self.usb_img.set_src(None)
self.sd_img.set_src(None)
self.smartcard_img.set_src(None)
else:
BTC_ICONS.UNLOCK.add_to_parent(self.lock_ico)
# Show peripheral indicators when unlocked
if state.hasQR:
if state.enabledQR:
BTC_ICONS.QR_CODE(GREEN_HEX).add_to_parent(self.qr_img)
else:
BTC_ICONS.QR_CODE(ORANGE_HEX).add_to_parent(self.qr_img)
else:
self.qr_img.set_src(None)

if state.hasUSB:
if state.enabledUSB:
BTC_ICONS.USB(GREEN_HEX).add_to_parent(self.usb_img)
else:
BTC_ICONS.USB(ORANGE_HEX).add_to_parent(self.usb_img)
else:
self.usb_img.set_src(None)

if state.hasSD:
if state.enabledSD:
if state.detectedSD:
BTC_ICONS.SD_CARD(GREEN_HEX).add_to_parent(self.sd_img)
else:
BTC_ICONS.SD_CARD(ORANGE_HEX).add_to_parent(self.sd_img)
else:
BTC_ICONS.SD_CARD(RED_HEX).add_to_parent(self.sd_img)
else:
self.sd_img.set_src(None)

if state.hasSmartCard:
if state.enabledSmartCard:
if state.detectedSmartCard:
BTC_ICONS.SMARTCARD(GREEN_HEX).add_to_parent(self.smartcard_img)
else:
BTC_ICONS.SMARTCARD(ORANGE_HEX).add_to_parent(self.smartcard_img)
else:
BTC_ICONS.SMARTCARD(RED_HEX).add_to_parent(self.smartcard_img)
else:
self.smartcard_img.set_src(None)

def _truncate(self, text, max_chars):
"""Return text truncated to max_chars."""
if not text:
return ""
s = str(text)
if len(s) <= max_chars:
return s
if max_chars <= 3:
return s[:3]
return s[:max_chars]
48 changes: 32 additions & 16 deletions scenarios/MockUI/src/MockUI/basic/main_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,47 @@ def MainMenu(parent, *args, **kwargs):
#relevant input possibilities are QR Scanner, SD Card, or (to sign messages) a registered wallet
if (state and ((state.hasQR and state.enabledQR)
or (state.hasSD and state.enabledSD and state.detectedSD)
or (state.active_wallet and not state.active_wallet.isMultiSig)
or (state and state.active_wallet and not state.active_wallet.isMultiSig and
(
(state.hasQR and state.enabledQR)
or (state.hasSD and state.enabledSD and state.detectedSD)
or (state.hasUSB and state.enabledUSB)
))
or (state.active_wallet is None and state.hasSmartCard and state.enabledSmartCard and state.detectedSmartCard)
)):
menu_items.append((None, t("MAIN_MENU_PROCESS_INPUT"), None, None))
menu_items.append((None, t("MAIN_MENU_PROCESS_INPUT"), None, None, None, None))
if (state.hasQR and state.enabledQR):
menu_items.append((BTC_ICONS.SCAN, t("MAIN_MENU_SCAN_QR"), "scan_qr", None))
scan_size = 1
if not (state.active_wallet is None):
scan_size = 2
menu_items.append((BTC_ICONS.SCAN, t("MAIN_MENU_SCAN_QR"), "scan_qr", None, scan_size, "HELP_SCAN_QR"))
if (state.hasSD and state.enabledSD and state.detectedSD):
menu_items.append((BTC_ICONS.SD_CARD, t("MAIN_MENU_LOAD_SD"), "load_sd", None))
if (state and state.active_wallet and not state.active_wallet.isMultiSig):
menu_items.append((BTC_ICONS.SIGN, t("MAIN_MENU_SIGN_MESSAGE"), "sign_message", None))
menu_items.append((BTC_ICONS.SD_CARD, t("MAIN_MENU_LOAD_SD"), "load_sd", None, 2, None))
if (state and state.active_wallet and not state.active_wallet.isMultiSig and
(
(state.hasQR and state.enabledQR)
or (state.hasSD and state.enabledSD and state.detectedSD)
or (state.hasUSB and state.enabledUSB)
)):
menu_items.append((BTC_ICONS.SIGN, t("MAIN_MENU_SIGN_MESSAGE"), "sign_message", None, None, None))
if (state and state.active_wallet is None and state.hasSmartCard and state.enabledSmartCard and state.detectedSmartCard):
menu_items.append((BTC_ICONS.SEND, t("MAIN_MENU_IMPORT_SMARTCARD"), "import_from_smartcard", None))
menu_items.append((BTC_ICONS.SEND, t("MAIN_MENU_IMPORT_SMARTCARD"), "import_from_smartcard", None, 3, None))

menu_items.append((None, t("MAIN_MENU_CHOOSE_WALLET"), None, None))
if state.registered_wallets and len(state.registered_wallets) > 0:
menu_items.append((BTC_ICONS.WALLET, t("MAIN_MENU_CHANGE_ADD_WALLET"), "change_wallet", None))
menu_items.append((None, t("MAIN_MENU_CHOOSE_WALLET"), None, None, None, None))
if (state and not state.active_wallet is None):
menu_items.append((BTC_ICONS.WALLET, t("MENU_MANAGE_WALLET"), "manage_wallet", None, None, None))
if state.registered_wallets and len(state.registered_wallets) > 1:
menu_items.append((BTC_ICONS.REFRESH, t("MAIN_MENU_CHANGE_ADD_WALLET"), "change_wallet", None, None, None))
else:
menu_items.append((BTC_ICONS.PLUS, t("MENU_ADD_WALLET"), "add_wallet", None))
add_size = 2
if (state.registered_wallets and len(state.registered_wallets) > 0):
add_size = 1
menu_items.append((BTC_ICONS.PLUS, t("MENU_ADD_WALLET"), "add_wallet", None, add_size, None))

menu_items.append((None, t("MAIN_MENU_MANAGE_SETTINGS"), None, None))
if (state and not state.active_wallet is None):
menu_items.append((BTC_ICONS.WALLET, t("MENU_MANAGE_WALLET"), "manage_wallet", None))

menu_items.append((BTC_ICONS.GEAR, t("MENU_MANAGE_DEVICE"), "manage_device", None))
menu_items.append((lv.SYMBOL.DRIVE, t("MENU_MANAGE_STORAGE"), "manage_storage", None))
menu_items.append((None, t("MAIN_MENU_MANAGE_SETTINGS"), None, None, None, None))

menu_items.append((BTC_ICONS.GEAR, t("MENU_MANAGE_SETTINGS"), "manage_settings", None, None, None))


return GenericMenu("main", t("MAIN_MENU_TITLE"), menu_items, parent, *args, **kwargs)
Loading
Loading