v0.3.1: q/Escape beenden wieder, Rename-Crash und ".."-Aktionen gefixt

- Screen-Bindings lösen Actions im Screen-Namespace auf. "quit" traf daher
  ins Leere, weil Screen.action_quit nicht existiert – die Taste wurde still
  verworfen. Bindings auf "app.quit" umgestellt (q im Browser, Escape im
  Verbindungsdialog).
- RenameScreen._ok rief do_rename() auf sich selbst statt auf den darunter
  liegenden BrowserScreen (AttributeError). Reihenfolge korrigiert: erst
  pop_screen(), dann do_rename().
- RenameScreen mit Escape abbrechbar.
- Neuer Helfer _selected_entry() blendet den ".."-Navigationseintrag aus.
  Download, Upload, Löschen und Umbenennen operierten sonst auf "↑ ..".
This commit is contained in:
x3
2026-08-31 17:07:45 +00:00
parent 7a1bf948f1
commit db84a6aaf8
3 changed files with 21 additions and 9 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
"""ftptui - Interaktiver FTP/SFTP-Client als Terminal-User-Interface.""" """ftptui - Interaktiver FTP/SFTP-Client als Terminal-User-Interface."""
__version__ = "0.3.0" __version__ = "0.3.1"
+19 -7
View File
@@ -92,7 +92,7 @@ class FileTable(DataTable):
class ConnectionScreen(Screen): class ConnectionScreen(Screen):
"""Startbildschirm zur Eingabe der Verbindungsdaten (Terminal-Look).""" """Startbildschirm zur Eingabe der Verbindungsdaten (Terminal-Look)."""
BINDINGS = [Binding("escape", "quit", "Beenden")] BINDINGS = [Binding("escape", "app.quit", "Beenden")]
def compose(self) -> ComposeResult: def compose(self) -> ComposeResult:
yield Header(show_clock=True) yield Header(show_clock=True)
@@ -205,7 +205,7 @@ class BrowserScreen(Screen):
"""Total-Commander-artiger Datei-Browser zwischen lokal & entfernt.""" """Total-Commander-artiger Datei-Browser zwischen lokal & entfernt."""
BINDINGS = [ BINDINGS = [
Binding("q", "quit", "Beenden"), Binding("q", "app.quit", "Beenden"),
Binding("enter", "open", "Öffnen"), Binding("enter", "open", "Öffnen"),
Binding("tab", "focus_next", "Panele wechseln"), Binding("tab", "focus_next", "Panele wechseln"),
Binding("l", "download", "Herunterladen"), Binding("l", "download", "Herunterladen"),
@@ -356,6 +356,13 @@ class BrowserScreen(Screen):
cell = table.get_row_at(row)[0] cell = table.get_row_at(row)[0]
return cell.plain if isinstance(cell, Text) else str(cell) return cell.plain if isinstance(cell, Text) else str(cell)
def _selected_entry(self, table: FileTable) -> str | None:
"""Wie `_selected`, ignoriert aber den ``..``-Navigationseintrag."""
sel = self._selected(table)
if not sel or sel.startswith(""):
return None
return sel
@on(DataTable.RowSelected, "#local_table") @on(DataTable.RowSelected, "#local_table")
def _local_selected(self, event: DataTable.RowSelected) -> None: def _local_selected(self, event: DataTable.RowSelected) -> None:
self._open("local") self._open("local")
@@ -404,7 +411,7 @@ class BrowserScreen(Screen):
self._update_info() self._update_info()
def action_download(self) -> None: def action_download(self) -> None:
sel = self._selected(self.table_remote) sel = self._selected_entry(self.table_remote)
if not sel: if not sel:
return return
name = sel[:-1] if sel.endswith("/") else sel name = sel[:-1] if sel.endswith("/") else sel
@@ -420,7 +427,7 @@ class BrowserScreen(Screen):
self._refresh_local() self._refresh_local()
def action_upload(self) -> None: def action_upload(self) -> None:
sel = self._selected(self.table_local) sel = self._selected_entry(self.table_local)
if not sel: if not sel:
return return
name = sel[:-1] if sel.endswith("/") else sel name = sel[:-1] if sel.endswith("/") else sel
@@ -479,7 +486,7 @@ class BrowserScreen(Screen):
def action_delete(self) -> None: def action_delete(self) -> None:
table = self._focused_table() table = self._focused_table()
sel = self._selected(table) sel = self._selected_entry(table)
if not sel: if not sel:
return return
remote = table is not self.table_local remote = table is not self.table_local
@@ -504,7 +511,7 @@ class BrowserScreen(Screen):
def action_rename(self) -> None: def action_rename(self) -> None:
table = self._focused_table() table = self._focused_table()
sel = self._selected(table) sel = self._selected_entry(table)
if not sel: if not sel:
return return
self._rename_target = sel self._rename_target = sel
@@ -535,6 +542,11 @@ class BrowserScreen(Screen):
class RenameScreen(Screen): class RenameScreen(Screen):
"""Eingabebildschirm zum Umbenennen.""" """Eingabebildschirm zum Umbenennen."""
BINDINGS = [Binding("escape", "cancel", "Abbrechen")]
def action_cancel(self) -> None:
self.app.pop_screen()
def compose(self) -> ComposeResult: def compose(self) -> ComposeResult:
yield Static("Neuer Name:", classes="field") yield Static("Neuer Name:", classes="field")
yield Input(id="newname", placeholder="neuer-name", classes="input") yield Input(id="newname", placeholder="neuer-name", classes="input")
@@ -547,8 +559,8 @@ class RenameScreen(Screen):
@on(Button.Pressed, "#ok") @on(Button.Pressed, "#ok")
def _ok(self) -> None: def _ok(self) -> None:
name = self.query_one("#newname", Input).value.strip() name = self.query_one("#newname", Input).value.strip()
self.app.screen.do_rename(name if name else "unbenannt")
self.app.pop_screen() self.app.pop_screen()
self.app.screen.do_rename(name if name else "unbenannt")
@on(Button.Pressed, "#cancel") @on(Button.Pressed, "#cancel")
def _cancel(self) -> None: def _cancel(self) -> None:
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "ftptui" name = "ftptui"
version = "0.3.0" version = "0.3.1"
description = "Interaktiver FTP/SFTP-Client als Terminal-User-Interface in Python" description = "Interaktiver FTP/SFTP-Client als Terminal-User-Interface in Python"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"