mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 23:18:25 +00:00
Merge pull request #445 from kdmukai/final_word_calc_screen_fix
[bugfix] Final word calc UI spacing fix; `TextArea` consistency/placement improvements
This commit is contained in:
@@ -287,6 +287,7 @@ class TextArea(BaseComponent):
|
||||
supersampling_factor: int = 1
|
||||
auto_line_break: bool = True
|
||||
allow_text_overflow: bool = False
|
||||
height_ignores_below_baseline: bool = False # If True, characters that render below the baseline (e.g. "pqgy") will not affect the final height calculation
|
||||
|
||||
|
||||
def __post_init__(self):
|
||||
@@ -316,11 +317,9 @@ class TextArea(BaseComponent):
|
||||
font = Fonts.get_font(self.font_name, self.font_size)
|
||||
|
||||
# Note: from the baseline anchor, `top` is a negative number while `bottom`
|
||||
# conveys the pixels used below the baseline (e.g. in "py").
|
||||
# For consistency, ensure we have a full-height character above baseline.
|
||||
# Also include some "below baseline" chars.
|
||||
measurement_chars = "Agjpqy"
|
||||
(left, top, right, bottom) = font.getbbox(self.text + measurement_chars, anchor="ls")
|
||||
# conveys the height of the pixels that rendered below the baseline, if any
|
||||
# (e.g. "py" in "python").
|
||||
(left, top, right, bottom) = font.getbbox(self.text, anchor="ls")
|
||||
self.text_height_above_baseline = -1 * top
|
||||
self.text_height_below_baseline = bottom
|
||||
|
||||
@@ -333,12 +332,14 @@ class TextArea(BaseComponent):
|
||||
|
||||
# Calculate the actual height
|
||||
if len(self.text_lines) == 1:
|
||||
total_text_height = self.text_height_above_baseline + self.text_height_below_baseline
|
||||
total_text_height = self.text_height_above_baseline
|
||||
if not self.height_ignores_below_baseline:
|
||||
total_text_height += self.text_height_below_baseline
|
||||
else:
|
||||
# Multiply for the number of lines plus the spacer
|
||||
total_text_height = self.text_height_above_baseline * len(self.text_lines) + self.line_spacing * (len(self.text_lines) - 1)
|
||||
|
||||
if re.findall(f"[gjpqy]", self.text_lines[-1]["text"]):
|
||||
if not self.height_ignores_below_baseline and re.findall(f"[gjpqy]", self.text_lines[-1]["text"]):
|
||||
# Last line has at least one char that dips below baseline
|
||||
total_text_height += self.text_height_below_baseline
|
||||
|
||||
@@ -356,15 +357,7 @@ class TextArea(BaseComponent):
|
||||
|
||||
else:
|
||||
# Vertically center the text's starting point
|
||||
if len(self.text_lines) == 1:
|
||||
# For consistency when used in TopNav and elsewhere, ignore the
|
||||
# text's pixels below the baseline.
|
||||
# In other words: "Home" and "Something" will get the same text_y,
|
||||
# even though the "g" dips below baseline.
|
||||
self.text_y += int(self.height - (total_text_height - self.text_height_below_baseline))/2
|
||||
else:
|
||||
# Vertically center for the full height.
|
||||
self.text_y += int(self.height - (total_text_height))/2
|
||||
self.text_y += int(self.height - total_text_height)/2
|
||||
|
||||
|
||||
def render(self):
|
||||
@@ -373,26 +366,28 @@ class TextArea(BaseComponent):
|
||||
# Add a `resample_padding` above and below when supersampling to avoid edge
|
||||
# effects (resized text that's right up against the top/bottom gets slightly
|
||||
# dimmer at the edge otherwise).
|
||||
# TODO: Store resulting super-sampled image as a member var in __post_init__ and
|
||||
# just re-paste it here.
|
||||
if self.font_size < 20 and (not self.supersampling_factor or self.supersampling_factor == 1):
|
||||
self.supersampling_factor = 2
|
||||
|
||||
actual_text_height = self.height
|
||||
if self.height_ignores_below_baseline:
|
||||
# Even though we're ignoring the pixels below the baseline for spacing
|
||||
# purposes, we have to make sure we don't crop those pixels out during the
|
||||
# supersampling operations here.
|
||||
actual_text_height += self.text_height_below_baseline
|
||||
|
||||
resample_padding = 10 if self.supersampling_factor > 1.0 else 0
|
||||
img = Image.new(
|
||||
"RGBA",
|
||||
(
|
||||
self.width * self.supersampling_factor,
|
||||
(self.height + 2*resample_padding) * self.supersampling_factor
|
||||
(actual_text_height + 2*resample_padding) * self.supersampling_factor
|
||||
),
|
||||
self.background_color
|
||||
)
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
# draw.line((0, resample_padding * self.supersampling_factor, self.width * self.supersampling_factor, resample_padding * self.supersampling_factor), fill="blue", width=1)
|
||||
# draw.line((0, (resample_padding + self.height) * self.supersampling_factor, self.width * self.supersampling_factor, (resample_padding + self.height) * self.supersampling_factor), fill="red", width=1)
|
||||
cur_y = (self.text_y + resample_padding) * self.supersampling_factor
|
||||
|
||||
supersampled_font = Fonts.get_font(self.font_name, int(self.supersampling_factor * self.font_size))
|
||||
|
||||
if self.is_text_centered:
|
||||
@@ -412,15 +407,20 @@ class TextArea(BaseComponent):
|
||||
text_x = self.min_text_x + int(line["text_width"]/2)
|
||||
|
||||
draw.text((text_x * self.supersampling_factor, cur_y), line["text"], fill=self.font_color, font=supersampled_font, anchor=anchor)
|
||||
|
||||
# Debugging: show the exact vertical extents of each line of text
|
||||
# draw.line((0, cur_y - self.text_height_above_baseline * self.supersampling_factor, self.width * self.supersampling_factor, cur_y - self.text_height_above_baseline * self.supersampling_factor), fill="red", width=int(self.supersampling_factor))
|
||||
# draw.line((0, cur_y, self.width * self.supersampling_factor, cur_y), fill="red", width=int(self.supersampling_factor))
|
||||
|
||||
cur_y += (self.text_height_above_baseline + self.line_spacing) * self.supersampling_factor
|
||||
|
||||
# Crop off the top_padding and resize the result down to onscreen size
|
||||
if self.supersampling_factor > 1.0:
|
||||
resized = img.resize((self.width, self.height + 2*resample_padding), Image.LANCZOS)
|
||||
resized = img.resize((self.width, actual_text_height + 2*resample_padding), Image.LANCZOS)
|
||||
sharpened = resized.filter(ImageFilter.SHARPEN)
|
||||
|
||||
# Crop args are actually (left, top, WIDTH, HEIGHT)
|
||||
img = sharpened.crop((0, resample_padding, self.width, self.height + resample_padding))
|
||||
img = sharpened.crop((0, resample_padding, self.width, actual_text_height + resample_padding))
|
||||
self.canvas.paste(img, (self.screen_x, self.screen_y))
|
||||
|
||||
|
||||
@@ -1218,7 +1218,7 @@ class TopNav(BaseComponent):
|
||||
icon_name=SeedSignerIconConstants.BACK,
|
||||
icon_size=GUIConstants.ICON_INLINE_FONT_SIZE,
|
||||
screen_x=GUIConstants.EDGE_PADDING,
|
||||
screen_y=GUIConstants.EDGE_PADDING,
|
||||
screen_y=GUIConstants.EDGE_PADDING - 1, # Text can't perfectly vertically center relative to the button; shifting it down 1px looks better.
|
||||
width=GUIConstants.TOP_NAV_BUTTON_SIZE,
|
||||
height=GUIConstants.TOP_NAV_BUTTON_SIZE,
|
||||
)
|
||||
@@ -1262,6 +1262,7 @@ class TopNav(BaseComponent):
|
||||
is_text_centered=True,
|
||||
font_name=self.font_name,
|
||||
font_size=self.font_size,
|
||||
height_ignores_below_baseline=True, # Consistently vertically center text, ignoring chars that render below baseline (e.g. "pqyj")
|
||||
)
|
||||
|
||||
|
||||
@@ -1289,15 +1290,6 @@ class TopNav(BaseComponent):
|
||||
self.right_button.is_selected = self.is_selected
|
||||
self.right_button.render()
|
||||
|
||||
# self.image_draw.text(
|
||||
# (self.text_x, self.text_y),
|
||||
# self.text,
|
||||
# font=self.font,
|
||||
# fill=self.font_color,
|
||||
# stroke_width=1,
|
||||
# stroke_fill=GUIConstants.BACKGROUND_COLOR,
|
||||
# )
|
||||
|
||||
|
||||
|
||||
def linear_interp(a, b, t):
|
||||
|
||||
@@ -240,12 +240,13 @@ class ToolsCalcFinalWordScreen(ButtonListScreen):
|
||||
|
||||
self.components.append(TextArea(
|
||||
text=f"""Your input: \"{selection_text}\"""",
|
||||
screen_y=self.top_nav.height,
|
||||
screen_y=self.top_nav.height + GUIConstants.COMPONENT_PADDING - 2, # Nudge to last line doesn't get too close to "Next" button
|
||||
height_ignores_below_baseline=True, # Keep the next line (bits display) snugged up, regardless of text rendering below the baseline
|
||||
))
|
||||
|
||||
# ...and that entropy's associated 11 bits
|
||||
screen_y=self.components[-1].screen_y + self.components[-1].height + GUIConstants.COMPONENT_PADDING
|
||||
self.components.append(TextArea(
|
||||
screen_y = self.components[-1].screen_y + self.components[-1].height + GUIConstants.COMPONENT_PADDING
|
||||
first_bits_line = TextArea(
|
||||
text=keeper_selected_bits,
|
||||
font_name=GUIConstants.FIXED_WIDTH_EMPHASIS_FONT_NAME,
|
||||
font_size=bit_font_size,
|
||||
@@ -253,10 +254,13 @@ class ToolsCalcFinalWordScreen(ButtonListScreen):
|
||||
screen_x=bit_display_x,
|
||||
screen_y=screen_y,
|
||||
is_text_centered=False,
|
||||
))
|
||||
)
|
||||
self.components.append(first_bits_line)
|
||||
|
||||
# Render the least significant bits that will be replaced by the checksum in a
|
||||
# de-emphasized font color.
|
||||
if "_" in discard_selected_bits:
|
||||
screen_y += int(first_bits_line.height/2) # center the underscores vertically like hypens
|
||||
self.components.append(TextArea(
|
||||
text=discard_selected_bits,
|
||||
font_name=GUIConstants.FIXED_WIDTH_EMPHASIS_FONT_NAME,
|
||||
@@ -272,7 +276,7 @@ class ToolsCalcFinalWordScreen(ButtonListScreen):
|
||||
self.components.append(TextArea(
|
||||
text="Checksum",
|
||||
edge_padding=0,
|
||||
screen_y=self.components[-1].screen_y + self.components[-1].height + 2*GUIConstants.COMPONENT_PADDING,
|
||||
screen_y=first_bits_line.screen_y + first_bits_line.height + 2*GUIConstants.COMPONENT_PADDING,
|
||||
))
|
||||
|
||||
# ...and its actual bits. Prepend spacers to keep vertical alignment
|
||||
@@ -288,7 +292,7 @@ class ToolsCalcFinalWordScreen(ButtonListScreen):
|
||||
font_size=bit_font_size,
|
||||
edge_padding=0,
|
||||
screen_x=bit_display_x,
|
||||
screen_y=screen_y,
|
||||
screen_y=screen_y + int(first_bits_line.height/2), # center the underscores vertically like hypens
|
||||
is_text_centered=False,
|
||||
))
|
||||
|
||||
@@ -308,6 +312,7 @@ class ToolsCalcFinalWordScreen(ButtonListScreen):
|
||||
self.components.append(TextArea(
|
||||
text=f"""Final Word: \"{self.actual_final_word}\"""",
|
||||
screen_y=self.components[-1].screen_y + self.components[-1].height + 2*GUIConstants.COMPONENT_PADDING,
|
||||
height_ignores_below_baseline=True, # Keep the next line (bits display) snugged up, regardless of text rendering below the baseline
|
||||
))
|
||||
|
||||
# Once again show the bits that came from the user's entropy...
|
||||
|
||||
@@ -38,15 +38,12 @@ class ToastOverlay(BaseComponent):
|
||||
auto_line_break=True,
|
||||
width=self.canvas_width - self.icon.screen_x - self.icon.width - GUIConstants.COMPONENT_PADDING - self.outline_thickness,
|
||||
screen_x=self.icon.screen_x + self.icon.width + GUIConstants.COMPONENT_PADDING,
|
||||
allow_text_overflow=False
|
||||
allow_text_overflow=False,
|
||||
)
|
||||
# Single-line toast messages need their vertical centering nudged down to account
|
||||
# for TextArea including below the baseline in its height calculation.
|
||||
below_baseline = self.label.text_height_below_baseline if len(self.label.text_lines) == 1 else 0
|
||||
|
||||
# Vertically center the message within the toast (for single- or multi-line
|
||||
# messages).
|
||||
self.label.screen_y = self.canvas_height - self.height + self.outline_thickness + int((self.height - 2*self.outline_thickness - (self.label.height - below_baseline))/2)
|
||||
self.label.screen_y = self.canvas_height - self.height + self.outline_thickness + int((self.height - 2*self.outline_thickness - self.label.height)/2)
|
||||
|
||||
|
||||
def render(self):
|
||||
@@ -67,7 +64,6 @@ class ToastOverlay(BaseComponent):
|
||||
|
||||
|
||||
|
||||
|
||||
class BaseToastOverlayManagerThread(BaseThread):
|
||||
"""
|
||||
The toast notification popup consists of a gui component (`ToastOverlay`) and this
|
||||
|
||||
@@ -63,6 +63,11 @@ class SeedStorage:
|
||||
|
||||
|
||||
def update_pending_mnemonic(self, word: str, index: int):
|
||||
"""
|
||||
Replaces the nth word in the pending mnemonic.
|
||||
|
||||
* may specify a negative `index` (e.g. -1 is the last word).
|
||||
"""
|
||||
if index >= len(self._pending_mnemonic):
|
||||
raise Exception(f"index {index} is too high")
|
||||
self._pending_mnemonic[index] = word
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from dataclasses import dataclass
|
||||
import hashlib
|
||||
import os
|
||||
import time
|
||||
@@ -313,8 +314,7 @@ class ToolsCalcFinalWordFinalizePromptView(View):
|
||||
|
||||
class ToolsCalcFinalWordCoinFlipsView(View):
|
||||
def run(self):
|
||||
mnemonic = self.controller.storage.pending_mnemonic
|
||||
mnemonic_length = len(mnemonic)
|
||||
mnemonic_length = len(self.controller.storage.pending_mnemonic)
|
||||
|
||||
if mnemonic_length == 12:
|
||||
total_flips = 7
|
||||
@@ -329,68 +329,74 @@ class ToolsCalcFinalWordCoinFlipsView(View):
|
||||
return Destination(BackStackView)
|
||||
|
||||
else:
|
||||
print(ret_val)
|
||||
binary_string = ret_val + "0" * (11 - total_flips)
|
||||
wordlist_index = int(binary_string, 2)
|
||||
wordlist = Seed.get_wordlist(self.controller.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE))
|
||||
word = wordlist[wordlist_index]
|
||||
self.controller.storage.update_pending_mnemonic(word, mnemonic_length - 1)
|
||||
|
||||
return Destination(ToolsCalcFinalWordShowFinalWordView, view_args=dict(coin_flips=ret_val))
|
||||
|
||||
|
||||
|
||||
class ToolsCalcFinalWordShowFinalWordView(View):
|
||||
def __init__(self, coin_flips=None):
|
||||
def __init__(self, coin_flips: str = None):
|
||||
super().__init__()
|
||||
self.coin_flips = coin_flips
|
||||
|
||||
|
||||
def run(self):
|
||||
# Construct the actual final word. The user's selected_final_word
|
||||
# contributes:
|
||||
# * 3 bits to a 24-word seed (plus 8-bit checksum)
|
||||
# * 7 bits to a 12-word seed (plus 4-bit checksum)
|
||||
from seedsigner.helpers import mnemonic_generation
|
||||
|
||||
mnemonic = self.controller.storage.pending_mnemonic
|
||||
mnemonic_length = len(mnemonic)
|
||||
wordlist_language_code = self.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE)
|
||||
wordlist = Seed.get_wordlist(wordlist_language_code)
|
||||
|
||||
# Prep the user's selected word / coin flips and the actual final word for
|
||||
# the display.
|
||||
if coin_flips:
|
||||
self.selected_final_word = None
|
||||
self.selected_final_bits = coin_flips
|
||||
else:
|
||||
# Convert the user's final word selection into its binary index equivalent
|
||||
self.selected_final_word = self.controller.storage.pending_mnemonic[-1]
|
||||
self.selected_final_bits = format(wordlist.index(self.selected_final_word), '011b')
|
||||
|
||||
if coin_flips:
|
||||
# fill the last bits (what will eventually be the checksum) with zeros
|
||||
binary_string = coin_flips + "0" * (11 - len(coin_flips))
|
||||
|
||||
# retrieve the matching word for the resulting index
|
||||
wordlist_index = int(binary_string, 2)
|
||||
wordlist = Seed.get_wordlist(self.controller.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE))
|
||||
word = wordlist[wordlist_index]
|
||||
|
||||
# update the pending mnemonic with our new "final" (pre-checksum) word
|
||||
self.controller.storage.update_pending_mnemonic(word, -1)
|
||||
|
||||
# Now calculate the REAL final word (has a proper checksum)
|
||||
final_mnemonic = mnemonic_generation.calculate_checksum(
|
||||
mnemonic=self.controller.storage.pending_mnemonic,
|
||||
wordlist_language_code=wordlist_language_code,
|
||||
)
|
||||
self.controller.storage.update_pending_mnemonic(final_mnemonic[-1], mnemonic_length - 1)
|
||||
|
||||
# Prep the user's selected word (if there was one) and the actual final word for
|
||||
# the display.
|
||||
if self.coin_flips:
|
||||
selected_final_word = None
|
||||
selected_final_bits = self.coin_flips
|
||||
else:
|
||||
# Convert the user's final word selection into its binary index equivalent
|
||||
selected_final_word = mnemonic[-1]
|
||||
selected_final_bits = format(wordlist.index(selected_final_word), '011b')
|
||||
# Update our pending mnemonic with the real final word
|
||||
self.controller.storage.update_pending_mnemonic(final_mnemonic[-1], -1)
|
||||
|
||||
mnemonic = self.controller.storage.pending_mnemonic
|
||||
mnemonic_length = len(mnemonic)
|
||||
|
||||
# And grab the actual final word's checksum bits
|
||||
actual_final_word = self.controller.storage.pending_mnemonic[-1]
|
||||
if mnemonic_length == 12:
|
||||
checksum_bits = format(wordlist.index(actual_final_word), '011b')[-4:]
|
||||
else:
|
||||
checksum_bits = format(wordlist.index(actual_final_word), '011b')[-8:]
|
||||
self.actual_final_word = self.controller.storage.pending_mnemonic[-1]
|
||||
num_checksum_bits = 4 if mnemonic_length == 12 else 8
|
||||
self.checksum_bits = format(wordlist.index(self.actual_final_word), '011b')[-num_checksum_bits:]
|
||||
|
||||
|
||||
def run(self):
|
||||
NEXT = "Next"
|
||||
button_data = [NEXT]
|
||||
selected_menu_num = ToolsCalcFinalWordScreen(
|
||||
selected_menu_num = self.run_screen(
|
||||
ToolsCalcFinalWordScreen,
|
||||
title="Final Word Calc",
|
||||
button_data=button_data,
|
||||
selected_final_word=selected_final_word,
|
||||
selected_final_bits=selected_final_bits,
|
||||
checksum_bits=checksum_bits,
|
||||
actual_final_word=actual_final_word,
|
||||
).display()
|
||||
selected_final_word=self.selected_final_word,
|
||||
selected_final_bits=self.selected_final_bits,
|
||||
checksum_bits=self.checksum_bits,
|
||||
actual_final_word=self.actual_final_word,
|
||||
)
|
||||
|
||||
if selected_menu_num == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
@@ -71,6 +71,12 @@ def test_generate_screenshots(target_locale):
|
||||
controller.storage.set_pending_seed(seed_24)
|
||||
UnhandledExceptionViewFood = ["IndexError", "line 1, in some_buggy_code.py", "list index out of range"]
|
||||
|
||||
# Pending mnemonic for ToolsCalcFinalWordShowFinalWordView
|
||||
controller.storage.init_pending_mnemonic(num_words=12)
|
||||
for i, word in enumerate(mnemonic_12[:11]):
|
||||
controller.storage.update_pending_mnemonic(word=word, index=i)
|
||||
controller.storage.update_pending_mnemonic(word="satoshi", index=11) # random last word; not supposed to be a valid checksum (yet)
|
||||
|
||||
# Load a PSBT into memory
|
||||
BASE64_PSBT_1 = """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"""
|
||||
decoder = DecodeQR()
|
||||
@@ -207,7 +213,8 @@ def test_generate_screenshots(target_locale):
|
||||
tools_views.ToolsCalcFinalWordNumWordsView,
|
||||
tools_views.ToolsCalcFinalWordFinalizePromptView,
|
||||
tools_views.ToolsCalcFinalWordCoinFlipsView,
|
||||
#(tools_views.ToolsCalcFinalWordShowFinalWordView, dict(coin_flips=3)),
|
||||
(tools_views.ToolsCalcFinalWordShowFinalWordView, {}, "ToolsCalcFinalWordShowFinalWordView_pick_word"),
|
||||
(tools_views.ToolsCalcFinalWordShowFinalWordView, dict(coin_flips="0010101"), "ToolsCalcFinalWordShowFinalWordView_coin_flips"),
|
||||
#tools_views.ToolsCalcFinalWordDoneView,
|
||||
tools_views.ToolsAddressExplorerSelectSourceView,
|
||||
tools_views.ToolsAddressExplorerAddressTypeView,
|
||||
|
||||
Reference in New Issue
Block a user