Skip to content

Commit a3c8f39

Browse files
authored
Merge pull request #625 from alvroble/messaging_toasts
[Enhancement] Messaging toast refactor
2 parents 25498b6 + e8ab75e commit a3c8f39

File tree

2 files changed

+126
-15
lines changed

2 files changed

+126
-15
lines changed

src/seedsigner/gui/toast.py

+119-15
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
class ToastOverlay(BaseComponent):
1414
icon_name: str = None
1515
color: str = GUIConstants.NOTIFICATION_COLOR
16+
font_color: str = GUIConstants.NOTIFICATION_COLOR
1617
label_text: str = None
1718
height: int = GUIConstants.ICON_TOAST_FONT_SIZE + 2*GUIConstants.EDGE_PADDING
1819
font_size: int = 19
@@ -21,33 +22,42 @@ class ToastOverlay(BaseComponent):
2122
def __post_init__(self):
2223
super().__post_init__()
2324

24-
self.icon = Icon(
25-
image_draw=self.image_draw,
26-
canvas=self.canvas,
27-
screen_x=self.outline_thickness + 2*GUIConstants.EDGE_PADDING, # Push the icon further from the left edge than strictly necessary
28-
icon_name=self.icon_name,
29-
icon_size=GUIConstants.ICON_TOAST_FONT_SIZE,
30-
icon_color=self.color
31-
)
32-
self.icon.screen_y = self.canvas_height - self.height + int((self.height - self.icon.height)/2)
33-
25+
icon_delta_x = 0
26+
if self.icon_name:
27+
self.icon = Icon(
28+
image_draw=self.image_draw,
29+
canvas=self.canvas,
30+
screen_x=self.outline_thickness + GUIConstants.EDGE_PADDING, # Push the icon further from the left edge than strictly necessary
31+
icon_name=self.icon_name,
32+
icon_size=GUIConstants.ICON_TOAST_FONT_SIZE,
33+
icon_color=self.color
34+
)
35+
icon_delta_x = self.icon.width + self.icon.screen_x
36+
3437
self.label = TextArea(
3538
image_draw=self.image_draw,
3639
canvas=self.canvas,
3740
text=self.label_text,
3841
font_size=self.font_size,
39-
font_color=self.color,
42+
font_color=self.font_color,
4043
edge_padding=0,
4144
is_text_centered=False,
4245
auto_line_break=True,
43-
width=self.canvas_width - self.icon.screen_x - self.icon.width - GUIConstants.COMPONENT_PADDING - self.outline_thickness,
44-
screen_x=self.icon.screen_x + self.icon.width + GUIConstants.COMPONENT_PADDING,
46+
width=self.canvas_width - icon_delta_x - 2 * GUIConstants.COMPONENT_PADDING - 2 * self.outline_thickness,
47+
screen_x=icon_delta_x + GUIConstants.COMPONENT_PADDING,
4548
allow_text_overflow=False,
49+
height_ignores_below_baseline=True,
4650
)
51+
52+
if self.label.height > GUIConstants.ICON_FONT_SIZE:
53+
self.height = self.label.height + GUIConstants.EDGE_PADDING * 2
4754

48-
# Vertically center the message within the toast (for single- or multi-line
55+
# Vertically center the message and icon within the toast (for single- or multi-line
4956
# messages).
5057
self.label.screen_y = self.canvas_height - self.height + self.outline_thickness + int((self.height - 2*self.outline_thickness - self.label.height)/2)
58+
59+
if self.icon_name:
60+
self.icon.screen_y = self.canvas_height - self.height + int((self.height - self.icon.height)/2)
5161

5262

5363
def render(self):
@@ -61,7 +71,9 @@ def render(self):
6171
)
6272

6373
# Draw the toast visual elements
64-
self.icon.render()
74+
if self.icon_name:
75+
self.icon.render()
76+
6577
self.label.render()
6678

6779
self.renderer.show_image()
@@ -125,6 +137,7 @@ def toggle_renderer_lock(self):
125137
def run(self):
126138
logger.info(f"{self.__class__.__name__}: started")
127139
start = time.time()
140+
time.sleep(0.2)
128141
while time.time() - start < self.activation_delay:
129142
if self.hw_inputs.has_any_input():
130143
# User has pressed a button, cancel the toast
@@ -237,3 +250,94 @@ def instantiate_toast(self) -> ToastOverlay:
237250
icon_name=SeedSignerIconConstants.MICROSD,
238251
label_text=self.message,
239252
)
253+
254+
255+
"""****************************************************************************
256+
Messaging toasts
257+
****************************************************************************"""
258+
259+
260+
class DefaultToast(BaseToastOverlayManagerThread):
261+
def __init__(self, label_text="This is a notification toast", activation_delay=0, duration=3):
262+
# Note: activation_delay is configurable so the screenshot generator can get the
263+
# toast to immediately render.
264+
self.label_text = label_text
265+
super().__init__(
266+
activation_delay=activation_delay, # seconds
267+
duration=duration # seconds
268+
)
269+
270+
271+
def instantiate_toast(self) -> ToastOverlay:
272+
body_font_size = GUIConstants.get_body_font_size()
273+
return ToastOverlay(
274+
label_text=self.label_text,
275+
color=GUIConstants.BODY_FONT_COLOR,
276+
font_color=GUIConstants.BODY_FONT_COLOR,
277+
font_size=body_font_size,
278+
)
279+
280+
281+
282+
class InfoToast(DefaultToast):
283+
def instantiate_toast(self) -> ToastOverlay:
284+
body_font_size = GUIConstants.get_body_font_size()
285+
return ToastOverlay(
286+
icon_name=SeedSignerIconConstants.INFO,
287+
label_text=self.label_text,
288+
color=GUIConstants.INFO_COLOR,
289+
font_color=GUIConstants.BODY_FONT_COLOR,
290+
font_size=body_font_size,
291+
)
292+
293+
294+
295+
class SuccessToast(DefaultToast):
296+
def instantiate_toast(self) -> ToastOverlay:
297+
body_font_size = GUIConstants.get_body_font_size()
298+
return ToastOverlay(
299+
icon_name=SeedSignerIconConstants.SUCCESS,
300+
label_text=self.label_text,
301+
color=GUIConstants.SUCCESS_COLOR,
302+
font_color=GUIConstants.BODY_FONT_COLOR,
303+
font_size=body_font_size,
304+
)
305+
306+
307+
308+
class WarningToast(DefaultToast):
309+
def instantiate_toast(self) -> ToastOverlay:
310+
body_font_size = GUIConstants.get_body_font_size()
311+
return ToastOverlay(
312+
icon_name=SeedSignerIconConstants.WARNING,
313+
label_text=self.label_text,
314+
color=GUIConstants.WARNING_COLOR,
315+
font_color=GUIConstants.BODY_FONT_COLOR,
316+
font_size=body_font_size,
317+
)
318+
319+
320+
321+
class DireWarningToast(DefaultToast):
322+
def instantiate_toast(self) -> ToastOverlay:
323+
body_font_size = GUIConstants.get_body_font_size()
324+
return ToastOverlay(
325+
icon_name=SeedSignerIconConstants.WARNING,
326+
label_text=self.label_text,
327+
color=GUIConstants.DIRE_WARNING_COLOR,
328+
font_color=GUIConstants.BODY_FONT_COLOR,
329+
font_size=body_font_size,
330+
)
331+
332+
333+
334+
class ErrorToast(DefaultToast):
335+
def instantiate_toast(self) -> ToastOverlay:
336+
body_font_size = GUIConstants.get_body_font_size()
337+
return ToastOverlay(
338+
icon_name=SeedSignerIconConstants.ERROR,
339+
label_text=self.label_text,
340+
color=GUIConstants.ERROR_COLOR,
341+
font_color=GUIConstants.BODY_FONT_COLOR,
342+
font_size=body_font_size,
343+
)

tests/screenshot_generator/generator.py

+7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from seedsigner.gui.renderer import Renderer
2828
from seedsigner.gui.screens.seed_screens import SeedAddPassphraseScreen
2929
from seedsigner.gui.toast import RemoveSDCardToastManagerThread, SDCardStateChangeToastManagerThread
30+
from seedsigner.gui.toast import DefaultToast, InfoToast, SuccessToast, WarningToast, ErrorToast, DireWarningToast
3031
from seedsigner.hardware.microsd import MicroSD
3132
from seedsigner.helpers import embit_utils
3233
from seedsigner.models.decode_qr import DecodeQR
@@ -243,6 +244,12 @@ def PSBTOpReturnView_raw_hex_data_cb_before():
243244
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_SDCardStateChangeToast_removed', toast_thread=SDCardStateChangeToastManagerThread(action=MicroSD.ACTION__REMOVED, activation_delay=0, duration=0)),
244245
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_SDCardStateChangeToast_inserted', toast_thread=SDCardStateChangeToastManagerThread(action=MicroSD.ACTION__INSERTED, activation_delay=0, duration=0)),
245246
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_RemoveSDCardToast', toast_thread=RemoveSDCardToastManagerThread(activation_delay=0, duration=0)),
247+
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_DefaultToast', toast_thread=DefaultToast("This is a default text toast!", activation_delay=0, duration=0)),
248+
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_InfoToast', toast_thread=InfoToast("This is an info toast!", activation_delay=0, duration=0)),
249+
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_SuccessToast', toast_thread=SuccessToast("This is a success toast!", activation_delay=0, duration=0)),
250+
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_WarningToast', toast_thread=WarningToast("This is a warning toast!", activation_delay=0, duration=0)),
251+
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_DireWarningToast', toast_thread=DireWarningToast("This is a dire warning toast!", activation_delay=0, duration=0)),
252+
ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_ErrorToast', toast_thread=ErrorToast("This is an error toast!", activation_delay=0, duration=0)),
246253
ScreenshotConfig(PowerOptionsView),
247254
ScreenshotConfig(RestartView),
248255
ScreenshotConfig(PowerOffView),

0 commit comments

Comments
 (0)