Fix back button: use Button without can_focus for Textual 2.x compat

This commit is contained in:
shuki
2026-03-07 07:13:15 +02:00
parent 45231fef90
commit a66cab8304
10 changed files with 58 additions and 34 deletions

View File

@@ -3,6 +3,7 @@ from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button, Select from textual.widgets import Header, Footer, Static, Button, Select
from tui.widgets.header import GnizaHeader as Header # noqa: F811 from tui.widgets.header import GnizaHeader as Header # noqa: F811
from textual.containers import Vertical, Horizontal from textual.containers import Vertical, Horizontal
from textual.events import Click
from tui.config import list_conf_dir, has_targets, has_remotes from tui.config import list_conf_dir, has_targets, has_remotes
from tui.jobs import job_manager from tui.jobs import job_manager
from tui.widgets import ConfirmDialog, DocsPanel from tui.widgets import ConfirmDialog, DocsPanel
@@ -19,7 +20,7 @@ class BackupScreen(Screen):
with Horizontal(classes="screen-with-docs"): with Horizontal(classes="screen-with-docs"):
with Vertical(id="backup-screen"): with Vertical(id="backup-screen"):
with Horizontal(id="title-bar"): with Horizontal(id="title-bar"):
yield Button("← Back", id="btn-back", classes="back-btn", can_focus=False) yield Button("← Back", id="btn-back", classes="back-btn")
yield Static("Backup", id="screen-title") yield Static("Backup", id="screen-title")
if not targets: if not targets:
yield Static("No sources configured. Add a source first.") yield Static("No sources configured. Add a source first.")
@@ -67,9 +68,7 @@ class BackupScreen(Screen):
pass pass
def on_button_pressed(self, event: Button.Pressed) -> None: def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "btn-back": if event.button.id == "btn-backup":
self.app.pop_screen()
elif event.button.id == "btn-backup":
target_sel = self.query_one("#backup-target", Select) target_sel = self.query_one("#backup-target", Select)
if not isinstance(target_sel.value, str): if not isinstance(target_sel.value, str):
self.notify("Please select a source", severity="error") self.notify("Please select a source", severity="error")
@@ -103,5 +102,9 @@ class BackupScreen(Screen):
job_manager.start_job(self.app, job, "backup", "--all") job_manager.start_job(self.app, job, "backup", "--all")
self.app.switch_screen("running_tasks") self.app.switch_screen("running_tasks")
def on_click(self, event: Click) -> None:
if event.widget.id == "btn-back":
self.app.pop_screen()
def action_go_back(self) -> None: def action_go_back(self) -> None:
self.app.pop_screen() self.app.pop_screen()

View File

@@ -91,7 +91,7 @@ class LogsScreen(Screen):
with Horizontal(classes="screen-with-docs"): with Horizontal(classes="screen-with-docs"):
with Vertical(id="logs-screen"): with Vertical(id="logs-screen"):
with Horizontal(id="title-bar"): with Horizontal(id="title-bar"):
yield Button("← Back", id="btn-back", classes="back-btn", can_focus=False) yield Button("← Back", id="btn-back", classes="back-btn")
yield Static("Logs", id="screen-title") yield Static("Logs", id="screen-title")
yield DataTable(id="logs-table") yield DataTable(id="logs-table")
with Horizontal(id="logs-buttons"): with Horizontal(id="logs-buttons"):

View File

@@ -3,6 +3,7 @@ from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button, DataTable from textual.widgets import Header, Footer, Static, Button, DataTable
from tui.widgets.header import GnizaHeader as Header # noqa: F811 from tui.widgets.header import GnizaHeader as Header # noqa: F811
from textual.containers import Vertical, Horizontal from textual.containers import Vertical, Horizontal
from textual.events import Click
from textual import work from textual import work
from tui.config import list_conf_dir, parse_conf, CONFIG_DIR from tui.config import list_conf_dir, parse_conf, CONFIG_DIR
@@ -19,7 +20,7 @@ class RemotesScreen(Screen):
with Horizontal(classes="screen-with-docs"): with Horizontal(classes="screen-with-docs"):
with Vertical(id="remotes-screen"): with Vertical(id="remotes-screen"):
with Horizontal(id="title-bar"): with Horizontal(id="title-bar"):
yield Button("← Back", id="btn-back", classes="back-btn", can_focus=False) yield Button("← Back", id="btn-back", classes="back-btn")
yield Static("Destinations", id="screen-title") yield Static("Destinations", id="screen-title")
yield DataTable(id="remotes-table") yield DataTable(id="remotes-table")
with Horizontal(id="remotes-buttons"): with Horizontal(id="remotes-buttons"):
@@ -60,9 +61,7 @@ class RemotesScreen(Screen):
return None return None
def on_button_pressed(self, event: Button.Pressed) -> None: def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "btn-back": if event.button.id == "btn-add":
self.app.pop_screen()
elif event.button.id == "btn-add":
self.app.push_screen("remote_edit", callback=lambda _: self._refresh_table()) self.app.push_screen("remote_edit", callback=lambda _: self._refresh_table())
elif event.button.id == "btn-edit": elif event.button.id == "btn-edit":
name = self._selected_remote() name = self._selected_remote()
@@ -122,5 +121,9 @@ class RemotesScreen(Screen):
self.notify(f"Destination '{name}' deleted.") self.notify(f"Destination '{name}' deleted.")
self._refresh_table() self._refresh_table()
def on_click(self, event: Click) -> None:
if event.widget.id == "btn-back":
self.app.pop_screen()
def action_go_back(self) -> None: def action_go_back(self) -> None:
self.app.pop_screen() self.app.pop_screen()

View File

@@ -3,6 +3,7 @@ from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button, Select, Input, RadioSet, RadioButton, Switch from textual.widgets import Header, Footer, Static, Button, Select, Input, RadioSet, RadioButton, Switch
from tui.widgets.header import GnizaHeader as Header # noqa: F811 from tui.widgets.header import GnizaHeader as Header # noqa: F811
from textual.containers import Vertical, Horizontal from textual.containers import Vertical, Horizontal
from textual.events import Click
from textual import work, on from textual import work, on
from tui.config import list_conf_dir, parse_conf, CONFIG_DIR from tui.config import list_conf_dir, parse_conf, CONFIG_DIR
@@ -22,7 +23,7 @@ class RestoreScreen(Screen):
with Horizontal(classes="screen-with-docs"): with Horizontal(classes="screen-with-docs"):
with Vertical(id="restore-screen"): with Vertical(id="restore-screen"):
with Horizontal(id="title-bar"): with Horizontal(id="title-bar"):
yield Button("← Back", id="btn-back", classes="back-btn", can_focus=False) yield Button("← Back", id="btn-back", classes="back-btn")
yield Static("Restore", id="screen-title") yield Static("Restore", id="screen-title")
if not targets or not remotes: if not targets or not remotes:
yield Static("Both sources and destinations must be configured for restore.") yield Static("Both sources and destinations must be configured for restore.")
@@ -97,9 +98,7 @@ class RestoreScreen(Screen):
self.notify("No snapshots found", severity="warning") self.notify("No snapshots found", severity="warning")
def on_button_pressed(self, event: Button.Pressed) -> None: def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "btn-back": if event.button.id == "btn-browse-dest":
self.app.pop_screen()
elif event.button.id == "btn-browse-dest":
self.app.push_screen( self.app.push_screen(
FolderPicker("Select destination directory"), FolderPicker("Select destination directory"),
callback=self._dest_selected, callback=self._dest_selected,
@@ -157,5 +156,9 @@ class RestoreScreen(Screen):
job_manager.start_job(self.app, job, *args) job_manager.start_job(self.app, job, *args)
self.app.switch_screen("running_tasks") self.app.switch_screen("running_tasks")
def on_click(self, event: Click) -> None:
if event.widget.id == "btn-back":
self.app.pop_screen()
def action_go_back(self) -> None: def action_go_back(self) -> None:
self.app.pop_screen() self.app.pop_screen()

View File

@@ -3,6 +3,7 @@ from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button, Select, Input from textual.widgets import Header, Footer, Static, Button, Select, Input
from tui.widgets.header import GnizaHeader as Header # noqa: F811 from tui.widgets.header import GnizaHeader as Header # noqa: F811
from textual.containers import Vertical, Horizontal from textual.containers import Vertical, Horizontal
from textual.events import Click
from textual import work from textual import work
from tui.config import list_conf_dir, parse_conf, update_conf_key, CONFIG_DIR from tui.config import list_conf_dir, parse_conf, update_conf_key, CONFIG_DIR
@@ -22,7 +23,7 @@ class RetentionScreen(Screen):
with Horizontal(classes="screen-with-docs"): with Horizontal(classes="screen-with-docs"):
with Vertical(id="retention-screen"): with Vertical(id="retention-screen"):
with Horizontal(id="title-bar"): with Horizontal(id="title-bar"):
yield Button("← Back", id="btn-back", classes="back-btn", can_focus=False) yield Button("← Back", id="btn-back", classes="back-btn")
yield Static("Retention Cleanup", id="screen-title") yield Static("Retention Cleanup", id="screen-title")
if not targets: if not targets:
yield Static("No sources configured.") yield Static("No sources configured.")
@@ -45,9 +46,7 @@ class RetentionScreen(Screen):
yield Footer() yield Footer()
def on_button_pressed(self, event: Button.Pressed) -> None: def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "btn-back": if event.button.id == "btn-cleanup":
self.app.pop_screen()
elif event.button.id == "btn-cleanup":
target_sel = self.query_one("#ret-target", Select) target_sel = self.query_one("#ret-target", Select)
if not isinstance(target_sel.value, str): if not isinstance(target_sel.value, str):
self.notify("Select a source first", severity="error") self.notify("Select a source first", severity="error")
@@ -92,5 +91,9 @@ class RetentionScreen(Screen):
log_screen.write(f"\n[red]Cleanup failed (exit code {rc}).[/red]") log_screen.write(f"\n[red]Cleanup failed (exit code {rc}).[/red]")
log_screen.finish() log_screen.finish()
def on_click(self, event: Click) -> None:
if event.widget.id == "btn-back":
self.app.pop_screen()
def action_go_back(self) -> None: def action_go_back(self) -> None:
self.app.pop_screen() self.app.pop_screen()

View File

@@ -35,7 +35,7 @@ class RunningTasksScreen(Screen):
with Horizontal(classes="screen-with-docs"): with Horizontal(classes="screen-with-docs"):
with Vertical(id="running-tasks-screen"): with Vertical(id="running-tasks-screen"):
with Horizontal(id="title-bar"): with Horizontal(id="title-bar"):
yield Button("← Back", id="btn-back", classes="back-btn", can_focus=False) yield Button("← Back", id="btn-back", classes="back-btn")
yield Static("Running Tasks", id="screen-title") yield Static("Running Tasks", id="screen-title")
yield DataTable(id="rt-table") yield DataTable(id="rt-table")
with Horizontal(id="rt-buttons"): with Horizontal(id="rt-buttons"):

View File

@@ -5,6 +5,7 @@ from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button, DataTable from textual.widgets import Header, Footer, Static, Button, DataTable
from tui.widgets.header import GnizaHeader as Header # noqa: F811 from tui.widgets.header import GnizaHeader as Header # noqa: F811
from textual.containers import Vertical, Horizontal from textual.containers import Vertical, Horizontal
from textual.events import Click
from textual import work from textual import work
from tui.config import list_conf_dir, parse_conf, update_conf_key, CONFIG_DIR from tui.config import list_conf_dir, parse_conf, update_conf_key, CONFIG_DIR
@@ -22,7 +23,7 @@ class ScheduleScreen(Screen):
with Horizontal(classes="screen-with-docs"): with Horizontal(classes="screen-with-docs"):
with Vertical(id="schedule-screen"): with Vertical(id="schedule-screen"):
with Horizontal(id="title-bar"): with Horizontal(id="title-bar"):
yield Button("← Back", id="btn-back", classes="back-btn", can_focus=False) yield Button("← Back", id="btn-back", classes="back-btn")
yield Static("Schedules", id="screen-title") yield Static("Schedules", id="screen-title")
yield DataTable(id="sched-table") yield DataTable(id="sched-table")
with Horizontal(id="sched-buttons"): with Horizontal(id="sched-buttons"):
@@ -98,9 +99,7 @@ class ScheduleScreen(Screen):
return None return None
def on_button_pressed(self, event: Button.Pressed) -> None: def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "btn-back": if event.button.id == "btn-add":
self.app.pop_screen()
elif event.button.id == "btn-add":
from tui.screens.schedule_edit import ScheduleEditScreen from tui.screens.schedule_edit import ScheduleEditScreen
self.app.push_screen(ScheduleEditScreen(), callback=self._on_schedule_saved) self.app.push_screen(ScheduleEditScreen(), callback=self._on_schedule_saved)
elif event.button.id == "btn-edit": elif event.button.id == "btn-edit":
@@ -224,5 +223,9 @@ class ScheduleScreen(Screen):
log_screen.write(stderr) log_screen.write(stderr)
log_screen.finish() log_screen.finish()
def on_click(self, event: Click) -> None:
if event.widget.id == "btn-back":
self.app.pop_screen()
def action_go_back(self) -> None: def action_go_back(self) -> None:
self.app.pop_screen() self.app.pop_screen()

View File

@@ -3,6 +3,7 @@ from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button, Input, Select from textual.widgets import Header, Footer, Static, Button, Input, Select
from tui.widgets.header import GnizaHeader as Header # noqa: F811 from tui.widgets.header import GnizaHeader as Header # noqa: F811
from textual.containers import Vertical, Horizontal from textual.containers import Vertical, Horizontal
from textual.events import Click
from textual import work from textual import work
from tui.config import parse_conf, write_conf, CONFIG_DIR from tui.config import parse_conf, write_conf, CONFIG_DIR
@@ -22,7 +23,7 @@ class SettingsScreen(Screen):
with Horizontal(classes="screen-with-docs"): with Horizontal(classes="screen-with-docs"):
with Vertical(id="settings-screen"): with Vertical(id="settings-screen"):
with Horizontal(id="title-bar"): with Horizontal(id="title-bar"):
yield Button("← Back", id="btn-back", classes="back-btn", can_focus=False) yield Button("← Back", id="btn-back", classes="back-btn")
yield Static("Settings", id="screen-title") yield Static("Settings", id="screen-title")
with Vertical(classes="settings-section", id="section-general"): with Vertical(classes="settings-section", id="section-general"):
yield Static("Log Level:") yield Static("Log Level:")
@@ -91,9 +92,7 @@ class SettingsScreen(Screen):
self.query_one("#section-web").border_title = "Web Dashboard" self.query_one("#section-web").border_title = "Web Dashboard"
def on_button_pressed(self, event: Button.Pressed) -> None: def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "btn-back": if event.button.id == "btn-save":
self.app.pop_screen()
elif event.button.id == "btn-save":
self._save() self._save()
elif event.button.id == "btn-test-email": elif event.button.id == "btn-test-email":
self._save() self._save()
@@ -141,5 +140,9 @@ class SettingsScreen(Screen):
log_screen.write(stderr) log_screen.write(stderr)
log_screen.finish() log_screen.finish()
def on_click(self, event: Click) -> None:
if event.widget.id == "btn-back":
self.app.pop_screen()
def action_go_back(self) -> None: def action_go_back(self) -> None:
self.app.pop_screen() self.app.pop_screen()

View File

@@ -6,6 +6,7 @@ from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button, Select, DataTable from textual.widgets import Header, Footer, Static, Button, Select, DataTable
from tui.widgets.header import GnizaHeader as Header # noqa: F811 from tui.widgets.header import GnizaHeader as Header # noqa: F811
from textual.containers import Vertical, Horizontal from textual.containers import Vertical, Horizontal
from textual.events import Click
from textual import work from textual import work
from tui.config import list_conf_dir from tui.config import list_conf_dir
@@ -33,7 +34,7 @@ class SnapshotsScreen(Screen):
with Horizontal(classes="screen-with-docs"): with Horizontal(classes="screen-with-docs"):
with Vertical(id="snapshots-screen"): with Vertical(id="snapshots-screen"):
with Horizontal(id="title-bar"): with Horizontal(id="title-bar"):
yield Button("← Back", id="btn-back", classes="back-btn", can_focus=False) yield Button("← Back", id="btn-back", classes="back-btn")
yield Static("Snapshots Browser", id="screen-title") yield Static("Snapshots Browser", id="screen-title")
if not targets or not remotes: if not targets or not remotes:
yield Static("Sources and destinations must be configured to browse snapshots.") yield Static("Sources and destinations must be configured to browse snapshots.")
@@ -57,9 +58,7 @@ class SnapshotsScreen(Screen):
pass pass
def on_button_pressed(self, event: Button.Pressed) -> None: def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "btn-back": if event.button.id == "btn-load":
self.app.pop_screen()
elif event.button.id == "btn-load":
self._load_snapshots() self._load_snapshots()
elif event.button.id == "btn-browse": elif event.button.id == "btn-browse":
self._browse_snapshot() self._browse_snapshot()
@@ -135,5 +134,9 @@ class SnapshotsScreen(Screen):
browser = SnapshotBrowser(f"{target} / {snapshot}", files) browser = SnapshotBrowser(f"{target} / {snapshot}", files)
self.app.push_screen(browser) self.app.push_screen(browser)
def on_click(self, event: Click) -> None:
if event.widget.id == "btn-back":
self.app.pop_screen()
def action_go_back(self) -> None: def action_go_back(self) -> None:
self.app.pop_screen() self.app.pop_screen()

View File

@@ -3,6 +3,7 @@ from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button, DataTable from textual.widgets import Header, Footer, Static, Button, DataTable
from tui.widgets.header import GnizaHeader as Header # noqa: F811 from tui.widgets.header import GnizaHeader as Header # noqa: F811
from textual.containers import Vertical, Horizontal from textual.containers import Vertical, Horizontal
from textual.events import Click
from tui.config import list_conf_dir, parse_conf, CONFIG_DIR from tui.config import list_conf_dir, parse_conf, CONFIG_DIR
from tui.widgets import ConfirmDialog, DocsPanel from tui.widgets import ConfirmDialog, DocsPanel
@@ -17,7 +18,7 @@ class TargetsScreen(Screen):
with Horizontal(classes="screen-with-docs"): with Horizontal(classes="screen-with-docs"):
with Vertical(id="targets-screen"): with Vertical(id="targets-screen"):
with Horizontal(id="title-bar"): with Horizontal(id="title-bar"):
yield Button("← Back", id="btn-back", classes="back-btn", can_focus=False) yield Button("← Back", id="btn-back", classes="back-btn")
yield Static("Sources", id="screen-title") yield Static("Sources", id="screen-title")
yield DataTable(id="targets-table") yield DataTable(id="targets-table")
with Horizontal(id="targets-buttons"): with Horizontal(id="targets-buttons"):
@@ -52,9 +53,7 @@ class TargetsScreen(Screen):
return None return None
def on_button_pressed(self, event: Button.Pressed) -> None: def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "btn-back": if event.button.id == "btn-add":
self.app.pop_screen()
elif event.button.id == "btn-add":
self.app.push_screen("target_edit", callback=lambda _: self._refresh_table()) self.app.push_screen("target_edit", callback=lambda _: self._refresh_table())
elif event.button.id == "btn-edit": elif event.button.id == "btn-edit":
name = self._selected_target() name = self._selected_target()
@@ -80,5 +79,9 @@ class TargetsScreen(Screen):
self.notify(f"Source '{name}' deleted.") self.notify(f"Source '{name}' deleted.")
self._refresh_table() self._refresh_table()
def on_click(self, event: Click) -> None:
if event.widget.id == "btn-back":
self.app.pop_screen()
def action_go_back(self) -> None: def action_go_back(self) -> None:
self.app.pop_screen() self.app.pop_screen()