diff options
Diffstat (limited to 'app/ui')
| -rw-r--r-- | app/ui/hub.py | 9 | ||||
| -rw-r--r-- | app/ui/runview.py | 5 | ||||
| -rw-r--r-- | app/ui/taskview.py | 9 | ||||
| -rw-r--r-- | app/ui/tui.py | 27 |
4 files changed, 34 insertions, 16 deletions
diff --git a/app/ui/hub.py b/app/ui/hub.py index 1c0cafa..b41b369 100644 --- a/app/ui/hub.py +++ b/app/ui/hub.py @@ -403,7 +403,10 @@ class _Hub: self.stdscr.timeout(-1) except Exception: pass - invalidate_detect_cache() + # The run may have autostarted a server or changed on-disk + # state; every exit path (stop-and-exit, key press, cancel, + # crash) must drop the cached statuses. + invalidate_detect_cache() return False # -- settings ------------------------------------------------------- @@ -1406,8 +1409,8 @@ def _read_port(values: dict, key: str) -> int: """Parse a port field value, raising ValueError on a bad number.""" try: number = int(values[key].strip()) - except (KeyError, ValueError): - raise ValueError(f"Enter a valid port for {key}") + except (KeyError, ValueError) as exc: + raise ValueError(f"Enter a valid port for {key}") from exc if not 1 <= number <= 65535: raise ValueError("Port must be between 1 and 65535") return number diff --git a/app/ui/runview.py b/app/ui/runview.py index a954499..1da8690 100644 --- a/app/ui/runview.py +++ b/app/ui/runview.py @@ -112,10 +112,7 @@ class RunView(ScreenView): self.chunk_total = 0 self.book_results: List[tuple] = [] # (name, ok) self.error_message = "" - self.cancelled = False - self.cancelling = False - self.started_server = False - self.finished_at: Optional[float] = None + self.started_server = False # cancelled/cancelling/finished_at: base self.boot_started: Optional[float] = None self.convert_started: Optional[float] = None self.stop_started: Optional[float] = None diff --git a/app/ui/taskview.py b/app/ui/taskview.py index 0d3b445..c5c2ff7 100644 --- a/app/ui/taskview.py +++ b/app/ui/taskview.py @@ -52,15 +52,9 @@ from ui.viewkit import (TERMINAL_PHASES as _TERMINAL, ScreenView, _box, _fit, _format_elapsed, _sep, _text) -# Redraw cadence for the timed getch (milliseconds). -_DRAW_TIMEOUT_MS = 250 - # How many recent output lines the log tail keeps. _LOG_TAIL = 10 -# Terminal state: the run is over and the screen waits for a key. -_TERMINAL = ("done", "error", "cancelled") - # Progress-line matchers, in order of precedence. _PROGRESS_BYTES = re.compile(r"AUDIOCPP_PROGRESS downloaded=(\d+) total=(\d+)") _PROGRESS_PERCENT = re.compile(r"(\d{1,3})%") @@ -592,7 +586,6 @@ class _LaneState: self.finished = False - class _GetchModes: """The blocking/non-blocking getch switching shared by all views.""" @@ -836,7 +829,7 @@ class LanesView(_GetchModes): rects = [(1, 1, width - 2, top_h), (1, 2 + top_h, width - 2, inner_h - top_h - 1)] - for lane, (x, y, w, h) in zip(self._lanes, rects): + for lane, (x, y, w, h) in zip(self._lanes, rects, strict=False): self._draw_pane(curses, theme, x, y, w, h, lane, terminal) if self.phase == "done": diff --git a/app/ui/tui.py b/app/ui/tui.py index 11f5653..cc21c0b 100644 --- a/app/ui/tui.py +++ b/app/ui/tui.py @@ -883,13 +883,20 @@ def form(scr, title: str, fields: Sequence[dict], "validate": lambda s: None if s.isdigit() else "digits only"} {"key": "combine", "label": "Combine chapters", "kind": "bool", "value": False} + {"key": "voices", "label": "Voices directory", + "kind": "dir", "value": Path("./voices")} Fields render as a two-column table: each label is padded to the widest label so every value starts in the same column. KINDS: ``choice`` opens a single choice menu (its ``choices`` may be a callable of the field list, resolved when the menu opens); ``text`` opens a line editor (reusing its VALIDATE); ``bool`` shows Yes/No and - toggles in place on Enter or Space. + toggles in place on Enter or Space; ``dir`` opens the DOS-style + directory browser (browse_directory) on Enter — its VALUE is a Path + (or str path, used as the browse start), an empty value starts at + the working directory, and backing out of the browser keeps the old + value. Accepting a directory also moves focus to the first button, + so Enter right after picking continues to the next screen. A field may set ``visible`` to a bool or a callable of the field list; hidden fields are not drawn, are skipped by the cursor, and @@ -945,6 +952,9 @@ def form(scr, title: str, fields: Sequence[dict], def display_value(field: dict) -> str: if field.get("kind") == "bool": return "Yes" if field["value"] else "No" + if field.get("kind") == "dir": + value = field["value"] + return str(value) if value is not None else "" return str(field["value"]) while True: @@ -1047,6 +1057,21 @@ def form(scr, title: str, fields: Sequence[dict], if chosen is not edit_cancel: field["value"] = chosen run_on_change(field) + elif field.get("kind") == "dir": + start = field["value"] + start = Path(start) if start else Path.cwd() + picked = browse_directory( + scr, field["label"], start=start, + validate=field.get("validate"), + back_value=edit_cancel) + if picked is not edit_cancel: + field["value"] = picked + run_on_change(field) + # Picking a directory is a completed choice: + # hand focus straight to the accept button so + # Enter continues, with no extra Tab hunting. + on_buttons = True + btn_index = 0 else: edited = line_edit(scr, field["label"], field["value"], |
