From 490f8823a56d95dad130ce09482de362a175ab31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D0=B8=D0=B8=D0=BB=20=D0=93=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=B0=D1=80=D1=8C?= Date: Sat, 23 May 2026 12:14:07 +0300 Subject: [PATCH] feat: refactor MultiPickScreen to use SelectionList for multi-choice selection --- cobot/tui.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/cobot/tui.py b/cobot/tui.py index 86cab36..fdc2cf9 100644 --- a/cobot/tui.py +++ b/cobot/tui.py @@ -8,7 +8,8 @@ from textual import on from textual.app import ComposeResult from textual.binding import Binding from textual.screen import Screen -from textual.widgets import Checkbox, Footer, Input, LoadingIndicator, ProgressBar, RadioButton, RadioSet, RichLog, Static +from textual.widgets import Footer, Input, LoadingIndicator, ProgressBar, RadioButton, RadioSet, RichLog, SelectionList, Static +from textual.widgets.selection_list import Selection # Shared CSS applied to every screen in the app. # Общий CSS, применяемый ко всем экранам приложения. @@ -78,13 +79,12 @@ RunScreen #hint { color: $text-muted; text-style: dim; } -MultiPickScreen #choices { +MultiPickScreen SelectionList { height: auto; + border: none; + padding: 0; margin-bottom: 1; } -MultiPickScreen Checkbox { - margin: 0; -} """ @@ -306,8 +306,10 @@ class LogScreen(Screen[bool]): # Экран с чекбоксами для множественного выбора. # Пользователь переключает пункты пробелом, подтверждает Enter, отменяет Escape. class MultiPickScreen(Screen[Optional[List[str]]]): - """Multi-choice checkbox screen. Returns the list of selected option strings, or None on Escape. - Экран множественного выбора с чекбоксами. Возвращает список выбранных строк или None при Escape. + """Multi-choice screen using SelectionList. Navigate with arrows, toggle with Space, + confirm with Enter, cancel with Escape. Returns selected option strings or None. + Экран множественного выбора через SelectionList. Стрелки — навигация, пробел — выбор, + Enter — подтверждение, Escape — отмена. Возвращает выбранные строки или None. """ BINDINGS = [ Binding("enter", "submit", "Confirm", priority=True), @@ -320,8 +322,8 @@ class MultiPickScreen(Screen[Optional[List[str]]]): self._step = step self._question = question self._options = options - # All options are checked by default when defaults is None. - # Все пункты отмечены по умолчанию если defaults не передан. + # All options are selected by default when defaults is None. + # Все пункты выбраны по умолчанию если defaults не передан. self._defaults = set(defaults) if defaults is not None else set(options) self._note = note @@ -330,19 +332,16 @@ class MultiPickScreen(Screen[Optional[List[str]]]): yield Static(self._question, id="question") if self._note: yield Static(self._note, id="note") - with Static(id="choices"): - for opt in self._options: - yield Checkbox(opt, value=(opt in self._defaults)) + yield SelectionList( + *[Selection(opt, opt, opt in self._defaults) for opt in self._options] + ) yield Footer() def on_mount(self) -> None: - checkboxes = list(self.query(Checkbox)) - if checkboxes: - checkboxes[0].focus() + self.query_one(SelectionList).focus() def action_submit(self) -> None: - selected = [str(cb.label) for cb in self.query(Checkbox) if cb.value] - self.dismiss(selected) + self.dismiss(list(self.query_one(SelectionList).selected)) def action_abort(self) -> None: self.app.exit(None)