Browse Source

ui: new `item_chooser()` function

The MMGen Project 1 week ago
parent
commit
a75518e78f
2 changed files with 23 additions and 20 deletions
  1. 7 19
      mmgen/tw/addresses.py
  2. 16 1
      mmgen/ui.py

+ 7 - 19
mmgen/tw/addresses.py

@@ -12,7 +12,7 @@
 tw.addresses: Tracking wallet listaddresses class for the MMGen suite
 tw.addresses: Tracking wallet listaddresses class for the MMGen suite
 """
 """
 
 
-from ..util import msg, is_int, die
+from ..util import msg, die
 from ..obj import MMGenListItem, ImmutableAttr, ListItemAttr, TwComment, NonNegativeInt
 from ..obj import MMGenListItem, ImmutableAttr, ListItemAttr, TwComment, NonNegativeInt
 from ..addr import CoinAddr, MMGenID, MMGenAddrType
 from ..addr import CoinAddr, MMGenID, MMGenAddrType
 from ..amt import CoinAmtChk
 from ..amt import CoinAmtChk
@@ -403,24 +403,12 @@ class TwAddresses(TwView):
 		"""
 		"""
 
 
 		def choose_address(addrs):
 		def choose_address(addrs):
-
-			def format_line(n, d):
-				return '{a:3}) {b}{c}'.format(
-					a = n,
-					b = d.twmmid.hl(),
-					c = yellow(' <== has a label!') if d.comment else ''
-				)
-
-			prompt = '\nChoose a {desc}:\n\n{items}\n\nEnter a number> '.format(
-				desc = desc,
-				items = '\n'.join(format_line(n, d) for n, d in enumerate(addrs, 1)))
-
-			from ..ui import line_input
-			while True:
-				res = line_input(self.cfg, prompt)
-				if is_int(res) and 0 < int(res) <= len(addrs):
-					return addrs[int(res)-1]
-				msg(f'{res}: invalid entry')
+			def format_addr(d):
+				return '{a}{b}'.format(
+					a = d.twmmid.hl(),
+					b = yellow(' <== has a label!') if d.comment else '')
+			from ..ui import item_chooser
+			return item_chooser(self.cfg, f'Choose a {desc}', addrs, format_addr).item
 
 
 		def get_addr(mmtype):
 		def get_addr(mmtype):
 			return [self.get_change_address(
 			return [self.get_change_address(

+ 16 - 1
mmgen/ui.py

@@ -14,7 +14,7 @@ ui: Interactive user interface functions for the MMGen suite
 
 
 import sys, os
 import sys, os
 
 
-from .util import msg, msg_r, Msg, die
+from .util import msg, msg_r, Msg, die, is_int
 
 
 def confirm_or_raise(cfg, message, action, *, expect='YES', exit_msg='Exiting at user request'):
 def confirm_or_raise(cfg, message, action, *, expect='YES', exit_msg='Exiting at user request'):
 	if message:
 	if message:
@@ -117,6 +117,21 @@ def keypress_confirm(
 			case _:
 			case _:
 				msg_r('\nInvalid reply\n' if verbose else '\r')
 				msg_r('\nInvalid reply\n' if verbose else '\r')
 
 
+def item_chooser(cfg, hdr, items, item_formatter, indent='  '):
+	from collections import namedtuple
+	col1_w = len(str(len(items)))
+	prompt = '{i}{a}:\n{i}{b}\n{i}{c}'.format(
+		a = hdr,
+		b = ('\n' + indent).join(f'  {n:{col1_w}}) {item_formatter(d)}' for n, d in enumerate(items, 1)),
+		c = 'Enter a number> ',
+		i = indent)
+	while True:
+		res = line_input(cfg, prompt)
+		if is_int(res) and 0 < int(res) <= len(items):
+			num = int(res)
+			return namedtuple('user_choice', 'num idx item')(num, num - 1, items[num - 1])
+		msg(f'{indent}{res}: invalid entry\n')
+
 def do_pager(text):
 def do_pager(text):
 
 
 	pagers = ['less', 'more']
 	pagers = ['less', 'more']