Merge pull request #625 from alvroble/messaging_toasts

[Enhancement] Messaging toast refactor
This commit is contained in:
Nick Klockenga
2025-02-28 15:53:36 -05:00
committed by GitHub
2 changed files with 126 additions and 15 deletions
+119 -15
View File
@@ -13,6 +13,7 @@ logger = logging.getLogger(__name__)
class ToastOverlay(BaseComponent): class ToastOverlay(BaseComponent):
icon_name: str = None icon_name: str = None
color: str = GUIConstants.NOTIFICATION_COLOR color: str = GUIConstants.NOTIFICATION_COLOR
font_color: str = GUIConstants.NOTIFICATION_COLOR
label_text: str = None label_text: str = None
height: int = GUIConstants.ICON_TOAST_FONT_SIZE + 2*GUIConstants.EDGE_PADDING height: int = GUIConstants.ICON_TOAST_FONT_SIZE + 2*GUIConstants.EDGE_PADDING
font_size: int = 19 font_size: int = 19
@@ -21,33 +22,42 @@ class ToastOverlay(BaseComponent):
def __post_init__(self): def __post_init__(self):
super().__post_init__() super().__post_init__()
self.icon = Icon( icon_delta_x = 0
image_draw=self.image_draw, if self.icon_name:
canvas=self.canvas, self.icon = Icon(
screen_x=self.outline_thickness + 2*GUIConstants.EDGE_PADDING, # Push the icon further from the left edge than strictly necessary image_draw=self.image_draw,
icon_name=self.icon_name, canvas=self.canvas,
icon_size=GUIConstants.ICON_TOAST_FONT_SIZE, screen_x=self.outline_thickness + GUIConstants.EDGE_PADDING, # Push the icon further from the left edge than strictly necessary
icon_color=self.color icon_name=self.icon_name,
) icon_size=GUIConstants.ICON_TOAST_FONT_SIZE,
self.icon.screen_y = self.canvas_height - self.height + int((self.height - self.icon.height)/2) icon_color=self.color
)
icon_delta_x = self.icon.width + self.icon.screen_x
self.label = TextArea( self.label = TextArea(
image_draw=self.image_draw, image_draw=self.image_draw,
canvas=self.canvas, canvas=self.canvas,
text=self.label_text, text=self.label_text,
font_size=self.font_size, font_size=self.font_size,
font_color=self.color, font_color=self.font_color,
edge_padding=0, edge_padding=0,
is_text_centered=False, is_text_centered=False,
auto_line_break=True, auto_line_break=True,
width=self.canvas_width - self.icon.screen_x - self.icon.width - GUIConstants.COMPONENT_PADDING - self.outline_thickness, width=self.canvas_width - icon_delta_x - 2 * GUIConstants.COMPONENT_PADDING - 2 * self.outline_thickness,
screen_x=self.icon.screen_x + self.icon.width + GUIConstants.COMPONENT_PADDING, screen_x=icon_delta_x + GUIConstants.COMPONENT_PADDING,
allow_text_overflow=False, allow_text_overflow=False,
height_ignores_below_baseline=True,
) )
if self.label.height > GUIConstants.ICON_FONT_SIZE:
self.height = self.label.height + GUIConstants.EDGE_PADDING * 2
# Vertically center the message within the toast (for single- or multi-line # Vertically center the message and icon within the toast (for single- or multi-line
# messages). # messages).
self.label.screen_y = self.canvas_height - self.height + self.outline_thickness + int((self.height - 2*self.outline_thickness - self.label.height)/2) self.label.screen_y = self.canvas_height - self.height + self.outline_thickness + int((self.height - 2*self.outline_thickness - self.label.height)/2)
if self.icon_name:
self.icon.screen_y = self.canvas_height - self.height + int((self.height - self.icon.height)/2)
def render(self): def render(self):
@@ -61,7 +71,9 @@ class ToastOverlay(BaseComponent):
) )
# Draw the toast visual elements # Draw the toast visual elements
self.icon.render() if self.icon_name:
self.icon.render()
self.label.render() self.label.render()
self.renderer.show_image() self.renderer.show_image()
@@ -125,6 +137,7 @@ class BaseToastOverlayManagerThread(BaseThread):
def run(self): def run(self):
logger.info(f"{self.__class__.__name__}: started") logger.info(f"{self.__class__.__name__}: started")
start = time.time() start = time.time()
time.sleep(0.2)
while time.time() - start < self.activation_delay: while time.time() - start < self.activation_delay:
if self.hw_inputs.has_any_input(): if self.hw_inputs.has_any_input():
# User has pressed a button, cancel the toast # User has pressed a button, cancel the toast
@@ -237,3 +250,94 @@ class SDCardStateChangeToastManagerThread(BaseToastOverlayManagerThread):
icon_name=SeedSignerIconConstants.MICROSD, icon_name=SeedSignerIconConstants.MICROSD,
label_text=self.message, label_text=self.message,
) )
"""****************************************************************************
Messaging toasts
****************************************************************************"""
class DefaultToast(BaseToastOverlayManagerThread):
def __init__(self, label_text="This is a notification toast", activation_delay=0, duration=3):
# Note: activation_delay is configurable so the screenshot generator can get the
# toast to immediately render.
self.label_text = label_text
super().__init__(
activation_delay=activation_delay, # seconds
duration=duration # seconds
)
def instantiate_toast(self) -> ToastOverlay:
body_font_size = GUIConstants.get_body_font_size()
return ToastOverlay(
label_text=self.label_text,
color=GUIConstants.BODY_FONT_COLOR,
font_color=GUIConstants.BODY_FONT_COLOR,
font_size=body_font_size,
)
class InfoToast(DefaultToast):
def instantiate_toast(self) -> ToastOverlay:
body_font_size = GUIConstants.get_body_font_size()
return ToastOverlay(
icon_name=SeedSignerIconConstants.INFO,
label_text=self.label_text,
color=GUIConstants.INFO_COLOR,
font_color=GUIConstants.BODY_FONT_COLOR,
font_size=body_font_size,
)
class SuccessToast(DefaultToast):
def instantiate_toast(self) -> ToastOverlay:
body_font_size = GUIConstants.get_body_font_size()
return ToastOverlay(
icon_name=SeedSignerIconConstants.SUCCESS,
label_text=self.label_text,
color=GUIConstants.SUCCESS_COLOR,
font_color=GUIConstants.BODY_FONT_COLOR,
font_size=body_font_size,
)
class WarningToast(DefaultToast):
def instantiate_toast(self) -> ToastOverlay:
body_font_size = GUIConstants.get_body_font_size()
return ToastOverlay(
icon_name=SeedSignerIconConstants.WARNING,
label_text=self.label_text,
color=GUIConstants.WARNING_COLOR,
font_color=GUIConstants.BODY_FONT_COLOR,
font_size=body_font_size,
)
class DireWarningToast(DefaultToast):
def instantiate_toast(self) -> ToastOverlay:
body_font_size = GUIConstants.get_body_font_size()
return ToastOverlay(
icon_name=SeedSignerIconConstants.WARNING,
label_text=self.label_text,
color=GUIConstants.DIRE_WARNING_COLOR,
font_color=GUIConstants.BODY_FONT_COLOR,
font_size=body_font_size,
)
class ErrorToast(DefaultToast):
def instantiate_toast(self) -> ToastOverlay:
body_font_size = GUIConstants.get_body_font_size()
return ToastOverlay(
icon_name=SeedSignerIconConstants.ERROR,
label_text=self.label_text,
color=GUIConstants.ERROR_COLOR,
font_color=GUIConstants.BODY_FONT_COLOR,
font_size=body_font_size,
)
+7
View File
@@ -27,6 +27,7 @@ from seedsigner.controller import Controller
from seedsigner.gui.renderer import Renderer from seedsigner.gui.renderer import Renderer
from seedsigner.gui.screens.seed_screens import SeedAddPassphraseScreen from seedsigner.gui.screens.seed_screens import SeedAddPassphraseScreen
from seedsigner.gui.toast import RemoveSDCardToastManagerThread, SDCardStateChangeToastManagerThread from seedsigner.gui.toast import RemoveSDCardToastManagerThread, SDCardStateChangeToastManagerThread
from seedsigner.gui.toast import DefaultToast, InfoToast, SuccessToast, WarningToast, ErrorToast, DireWarningToast
from seedsigner.hardware.microsd import MicroSD from seedsigner.hardware.microsd import MicroSD
from seedsigner.helpers import embit_utils from seedsigner.helpers import embit_utils
from seedsigner.models.decode_qr import DecodeQR from seedsigner.models.decode_qr import DecodeQR
@@ -243,6 +244,12 @@ def generate_screenshots(locale):
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_SDCardStateChangeToast_removed', toast_thread=SDCardStateChangeToastManagerThread(action=MicroSD.ACTION__REMOVED, activation_delay=0, duration=0)), ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_SDCardStateChangeToast_removed', toast_thread=SDCardStateChangeToastManagerThread(action=MicroSD.ACTION__REMOVED, activation_delay=0, duration=0)),
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_SDCardStateChangeToast_inserted', toast_thread=SDCardStateChangeToastManagerThread(action=MicroSD.ACTION__INSERTED, activation_delay=0, duration=0)), ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_SDCardStateChangeToast_inserted', toast_thread=SDCardStateChangeToastManagerThread(action=MicroSD.ACTION__INSERTED, activation_delay=0, duration=0)),
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_RemoveSDCardToast', toast_thread=RemoveSDCardToastManagerThread(activation_delay=0, duration=0)), ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_RemoveSDCardToast', toast_thread=RemoveSDCardToastManagerThread(activation_delay=0, duration=0)),
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_DefaultToast', toast_thread=DefaultToast("This is a default text toast!", activation_delay=0, duration=0)),
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_InfoToast', toast_thread=InfoToast("This is an info toast!", activation_delay=0, duration=0)),
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_SuccessToast', toast_thread=SuccessToast("This is a success toast!", activation_delay=0, duration=0)),
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_WarningToast', toast_thread=WarningToast("This is a warning toast!", activation_delay=0, duration=0)),
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_DireWarningToast', toast_thread=DireWarningToast("This is a dire warning toast!", activation_delay=0, duration=0)),
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_ErrorToast', toast_thread=ErrorToast("This is an error toast!", activation_delay=0, duration=0)),
ScreenshotConfig(PowerOptionsView), ScreenshotConfig(PowerOptionsView),
ScreenshotConfig(RestartView), ScreenshotConfig(RestartView),
ScreenshotConfig(PowerOffView), ScreenshotConfig(PowerOffView),