feat: add Continue and Cancel buttons to Certificate Details form

Tab (or Down/Enter on a field) now cycles through the three text fields
and then onto [ Continue ] and [ Cancel ] buttons at the bottom of the
form.  Enter or Space activates the focused button.  Ctrl-G still
confirms immediately from any position.  Escape still cancels.

The form window is trimmed from h=16 to h=13 since the buttons replace
the now-redundant empty space below the password field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vlad Doloman
2026-06-24 13:53:20 +03:00
parent 567c3e9759
commit 6ed7f9feef
2 changed files with 65 additions and 28 deletions

View File

@@ -567,15 +567,22 @@ def show_cert_form(
cn_readonly: bool = False,
) -> CertFormResult:
"""Modal cert-detail form.
Tab / Shift-Tab / Up / Down cycle fields. F5 regenerates password.
Enter on last field or Ctrl-S confirms. Escape cancels."""
Tab / Shift-Tab / Up / Down cycle fields and buttons. F5 regenerates password.
Enter or Space activates the focused button. Ctrl-G confirms immediately. Escape cancels."""
sh, sw = stdscr.getmaxyx()
h, w = 16, 62
h, w = 13, 62
win = curses.newwin(h, w, (sh - h) // 2, (sw - w) // 2)
win.keypad(True)
fw = w - 6 # field width
active = [n for n in _FORM_FIELDS if not (n == "cn" and cn_readonly)]
_BTN_CONTINUE = "_continue"
_BTN_CANCEL = "_cancel"
_BTN_Y = 9
_btn_x_cont = (w - 26) // 2 # "[ Continue ]"=12 + gap=4 + "[ Cancel ]"=10 = 26
_btn_x_canc = _btn_x_cont + 16
active = ([n for n in _FORM_FIELDS if not (n == "cn" and cn_readonly)]
+ [_BTN_CONTINUE, _BTN_CANCEL])
fields: dict[str, InputField] = {
"cn": InputField(win, _FORM_FIELD_Y["cn"], 3, fw, initial=cn),
"email": InputField(win, _FORM_FIELD_Y["email"], 3, fw, initial=email),
@@ -616,8 +623,33 @@ def show_cert_form(
else:
fields[name].draw()
fields[active[focus]].place_cursor()
hint = "Tab=next F5=regen password Ctrl-G=confirm Esc=cancel"
current = active[focus]
for btn_label, btn_id, bx in (
("Continue", _BTN_CONTINUE, _btn_x_cont),
("Cancel", _BTN_CANCEL, _btn_x_canc),
):
btn_attr = (curses.color_pair(COLOR_SELECTED)
if current == btn_id
else curses.color_pair(COLOR_NORMAL))
try:
win.addstr(_BTN_Y, bx, f"[ {btn_label} ]", btn_attr | curses.A_BOLD)
except curses.error:
pass
if current == _BTN_CONTINUE:
try:
win.move(_BTN_Y, _btn_x_cont)
except curses.error:
pass
elif current == _BTN_CANCEL:
try:
win.move(_BTN_Y, _btn_x_canc)
except curses.error:
pass
else:
fields[current].place_cursor()
hint = "Tab=next F5=regen pwd Ctrl-G=confirm Esc=cancel"
try:
win.addstr(h - 2, 2, hint[:w - 4], curses.color_pair(COLOR_DISABLED))
except curses.error:
@@ -632,16 +664,10 @@ def show_cert_form(
if key == 0x07: # Ctrl-G: immediate submit
curses.curs_set(0)
return _submit()
if key == 9: # Tab
if key in (9, curses.KEY_DOWN): # Tab / Down: next
focus = (focus + 1) % len(active)
continue
if key == curses.KEY_BTAB: # Shift-Tab
focus = (focus - 1) % len(active)
continue
if key == curses.KEY_DOWN:
focus = (focus + 1) % len(active)
continue
if key == curses.KEY_UP:
if key in (curses.KEY_BTAB, curses.KEY_UP): # Shift-Tab / Up: prev
focus = (focus - 1) % len(active)
continue
if key == curses.KEY_F5:
@@ -650,13 +676,17 @@ def show_cert_form(
initial=generate_password(), mask=True,
)
continue
if key in (10, 13, curses.KEY_ENTER):
if focus < len(active) - 1:
focus += 1
if current in (_BTN_CONTINUE, _BTN_CANCEL):
if key in (10, 13, curses.KEY_ENTER, ord(" ")):
curses.curs_set(0)
if current == _BTN_CONTINUE:
return _submit()
return CertFormResult(cn=cn, email="", password="", confirmed=False)
else:
if key in (10, 13, curses.KEY_ENTER):
focus = (focus + 1) % len(active)
continue
curses.curs_set(0)
return _submit()
fields[active[focus]].handle_key(key)
fields[current].handle_key(key)
# ============================================================