quick text entry code

This commit is contained in:
Peter D. Gray 2023-05-30 13:21:30 -04:00 committed by scgbckbone
parent 8324a1fefa
commit 0e2be61276
3 changed files with 54 additions and 214 deletions

View File

@ -1078,16 +1078,25 @@ class PassphraseMenu(MenuSystem):
global pp_sofar
pp_sofar = ''
items = [
# xxxxxxxxxxxxxxxx
MenuItem('Edit Phrase', f=self.view_edit_phrase),
MenuItem('Add Word', menu=self.word_menu),
MenuItem('Add Numbers', f=self.add_numbers),
MenuItem('Clear All', f=self.empty_phrase),
MenuItem('APPLY', f=self.done_apply),
MenuItem('CANCEL', f=self.done_cancel),
]
if version.has_qwerty:
items = [
MenuItem('Edit Phrase', f=self.view_edit_phrase),
MenuItem('Clear Phrase', f=self.empty_phrase),
MenuItem('APPLY', f=self.done_apply),
MenuItem('CANCEL', f=self.done_cancel),
]
else:
items = [
# xxxxxxxxxxxxxxxx
MenuItem('Edit Phrase', f=self.view_edit_phrase),
MenuItem('Add Word', menu=self.word_menu),
MenuItem('Add Numbers', f=self.add_numbers),
MenuItem('Clear All', f=self.empty_phrase),
MenuItem('APPLY', f=self.done_apply),
MenuItem('CANCEL', f=self.done_cancel),
]
# quick SD card check
# TODO this needs to handle 2 SD cards now ?
if pyb.SDCard().present():
try:
with CardSlot() as card:

View File

@ -335,3 +335,5 @@ Add more characters by moving past end (right side).'''
if confirm_exit:
help_msg += '\nTo quit without changes, delete everything.'
await ux_show_story(help_msg)
# EOF

View File

@ -6,6 +6,8 @@ from uasyncio import sleep_ms
import utime, gc
from charcodes import *
CURSOR = ''
class PressRelease:
def __init__(self, need_release=KEY_SELECT+KEY_CANCEL):
# Manage key-repeat: track last key, measure time it's held down, etc.
@ -77,7 +79,7 @@ async def ux_enter_number(prompt, max_value, can_cancel=False):
while 1:
# TODO: check width, go to two lines if needed?
bx = dis.text(2, 4, prompt + ' ' + value + '')
bx = dis.text(2, 4, prompt + ' ' + value + CURSOR)
ch = await press.wait()
if ch == KEY_SELECT:
@ -103,220 +105,47 @@ async def ux_enter_number(prompt, max_value, can_cancel=False):
async def ux_input_numbers(val, validate_func):
# collect a series of digits
from glob import dis
# - not wanted on Q1; just get the digits w/ the text.
pass
press = PressRelease()
footer = "CANCEL or SELECT when done."
lx = 6
y = 16
here = ''
dis.clear()
dis.text(None, -1, footer, FontTiny)
dis.save()
while 1:
dis.restore()
# text centered
msg = here
by = y
bx = dis.text(lx, y, msg[0:16])
dis.text(lx, y - 9, str(val, 'ascii').replace(' ', '_'), FontTiny)
if len(msg) > 16:
# second line when needed (left just)
by += 15
bx = dis.text(lx, by, msg[16:])
if len(here) < 32:
dis.icon(bx, by - 2, 'sm_box')
dis.show()
ch = await press.wait()
if ch == 'y':
val += here
validate_func()
return val
elif ch == 'x':
if here:
here = here[0:-1]
else:
# quit if they press X on empty screen
return
else:
if len(here) < 32:
here += ch
async def ux_input_text(pw, confirm_exit=True, hex_only=False, max_len=100):
async def ux_input_text(value, confirm_exit=True, hex_only=False, max_len=100):
# Allow them to pick each digit using "D-pad"
# - Should allow full unicode, NKDN
# - but our font is mostly just ascii
# - no control chars allowed either
from glob import dis
from display import FontTiny, FontSmall
from lcd_display import CHARS_W
from ux import ux_show_story
# Should allow full unicode, NKDN
# - but limited to what we can show in FontSmall
# - so really just ascii; not even latin-1
# - 8-bit codepoints only
my_rng = range(32, 127) # FontSmall.code_range
if hex_only:
new_expand = "0"
symbols = b"0123456789abcdef"
else:
new_expand = " "
symbols = b' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
letters = b'abcdefghijklmnopqrstuvwxyz'
Letters = b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
numbers = b'1234567890'
# assert len(set(symbols+letters+Letters+numbers)) == len(my_rng)
if hex_only:
footer1 = "Enter Hexidecimal Number"
footer2 = "58=Change 9=Next 7=Back"
else:
footer1 = "1=Letters 2=Numbers 3=Symbols"
footer2 = "4=SwapCase 0=HELP"
y = 20
pw = bytearray(pw or ('0' if hex_only else 'A'))
pos = len(pw) - 1 # which part being changed
n_visible = const(9)
scroll_x = max(pos - n_visible, 0)
def cycle_set(which, direction=1):
# pick next item in set of choices
for n, s in enumerate(which):
if pw[pos] == s:
try:
pw[pos] = which[n + direction]
except IndexError:
pw[pos] = which[0 if direction == 1 else -1]
return
pw[pos] = which[0]
def change(dx):
# next/prev within the same subset of related chars
ch = pw[pos]
if hex_only:
return cycle_set(symbols, dx)
for subset in [symbols, letters, Letters, numbers]:
if ch in subset:
return cycle_set(subset, dx)
# probably unreachable code: numeric up/down
ch = pw[pos] + dx
if ch not in my_rng:
ch = (my_rng.stop - 1) if dx < 0 else my_rng.start
assert ch in my_rng
pw[pos] = ch
# pre-render the fixed stuff
dis.clear()
dis.text(None, -10, footer1, FontTiny)
dis.text(None, -1, footer2, FontTiny)
dis.save()
dis.text(None, -2, "Use |-> to auto-complete words.")
dis.text(None, -1, "CANCEL or SELECT when done.")
# TODO:
# - left/right to edit in middle
# - multi line support
# - add prompt text?
# no key-repeat on certain keys
press = PressRelease('4xy')
press = PressRelease()
while 1:
dis.restore()
lr = pos - scroll_x # left/right distance of cursor
if lr < 4 and scroll_x:
scroll_x -= 1
elif lr < 0:
scroll_x = pos
elif lr >= (n_visible - 1):
# past right edge
scroll_x += 1
for i in range(n_visible):
# calc abs position in string
ax = scroll_x + i
x = 4 + (13 * i)
try:
ch = pw[ax]
except IndexError:
continue
if ax == pos:
# draw cursor
if not hex_only and (len(pw) < 2 * n_visible):
dis.text(x - 4, y - 19, '0x%02X' % ch, FontTiny)
dis.icon(x - 2, y - 10, 'spin')
if ch == 0x20:
dis.icon(x, y + 11, 'space')
else:
dis.text(x, y, chr(ch) if ch in my_rng else chr(215), FontSmall)
if scroll_x > 0:
dis.text(2, y - 14, str(pw, 'ascii')[0:scroll_x].replace(' ', '_'), FontTiny)
if scroll_x + n_visible < len(pw):
dis.text(-1, 1, "MORE>", FontTiny)
dis.show()
dis.text(1, 1, value + CURSOR)
ch = await press.wait()
if ch == 'y':
return str(pw, 'ascii')
elif ch == 'x':
if len(pw) > 1:
if ch == KEY_SELECT:
return str(value, 'ascii')
elif ch == KEY_DELETE:
if len(value) > 0:
# delete current char
pw = pw[0:pos] + pw[pos + 1:]
if pos >= len(pw):
pos = len(pw) - 1
else:
if confirm_exit:
pp = await ux_show_story(
"OK to leave without any changes? Or X to cancel leaving.")
if pp == 'x': continue
return None
elif ch == '7': # left
pos -= 1
if pos < 0: pos = 0
elif ch == '9': # right
pos += 1
if pos >= len(pw):
if len(pw) < max_len and pw[-3:] != b' ':
# expands with space in normal mode
# expands with 0 in hex_only mode
pw += new_expand
else:
pos -= 1 # abort addition
elif ch == '5': # up
change(1)
elif ch == '8': # down
change(-1)
elif hex_only:
# just got back at the beginning of the loop
# below branches are unreachable for hex_only mode
pass
elif ch == '1': # alpha
cycle_set(b'Aa')
elif ch == '4': # toggle case
if (pw[pos] & ~0x20) in range(65, 91):
pw[pos] ^= 0x20
elif ch == '2': # numbers
cycle_set(numbers)
elif ch == '3': # symbols (all of them)
cycle_set(symbols)
elif ch == '0': # help
help_msg = '''\
Use arrow keys (5789) to select letter and move around.
1=Letters (Aa..)
2=Numbers (12..)
3=Symbols (!@#&*)
4=Swap Case (q/Q)
X=Delete char
Add more characters by moving past end (right side).'''
value = value[:-1]
elif ch == KEY_CANCEL:
if confirm_exit:
help_msg += '\nTo quit without changes, delete everything.'
await ux_show_story(help_msg)
pp = await ux_show_story(
"OK to leave without any changes? Or CANCEL to avoid leaving.")
if pp == KEY_CANCEL: continue
return None
elif ' ' <= ch < chr(127):
value += ch
# EOF