Make TUI responsive to terminal size
- Auto-hide docs panel when terminal < 100 cols, respects manual F1 toggle - Hide logo on main menu when terminal < 80 cols - DataTable: flexible height (auto with min/max) instead of fixed 12 - Dialogs, pickers, wizard: percentage-based widths with max-width caps - Menu list: flexible height and width constraints Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
24
tui/app.py
24
tui/app.py
@@ -1,5 +1,6 @@
|
|||||||
from textual.app import App
|
from textual.app import App
|
||||||
from textual.css.query import NoMatches
|
from textual.css.query import NoMatches
|
||||||
|
from textual.events import Resize
|
||||||
|
|
||||||
from tui.config import has_remotes, has_targets
|
from tui.config import has_remotes, has_targets
|
||||||
from tui.screens.main_menu import MainMenuScreen
|
from tui.screens.main_menu import MainMenuScreen
|
||||||
@@ -62,13 +63,36 @@ class GnizaApp(App):
|
|||||||
else:
|
else:
|
||||||
self.notify(f"{job.label} failed (exit code {message.return_code})", severity="error")
|
self.notify(f"{job.label} failed (exit code {message.return_code})", severity="error")
|
||||||
|
|
||||||
|
# Width threshold for auto-hiding the docs panel
|
||||||
|
DOCS_AUTO_HIDE_WIDTH = 100
|
||||||
|
|
||||||
def action_toggle_docs(self) -> None:
|
def action_toggle_docs(self) -> None:
|
||||||
try:
|
try:
|
||||||
panel = self.screen.query_one("#docs-panel")
|
panel = self.screen.query_one("#docs-panel")
|
||||||
panel.display = not panel.display
|
panel.display = not panel.display
|
||||||
|
panel._user_toggled = True
|
||||||
except NoMatches:
|
except NoMatches:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def on_resize(self, event: Resize) -> None:
|
||||||
|
try:
|
||||||
|
panel = self.screen.query_one("#docs-panel")
|
||||||
|
except NoMatches:
|
||||||
|
return
|
||||||
|
if getattr(panel, "_user_toggled", False):
|
||||||
|
return
|
||||||
|
panel.display = event.size.width >= self.DOCS_AUTO_HIDE_WIDTH
|
||||||
|
|
||||||
|
def on_screen_resume(self) -> None:
|
||||||
|
"""Re-evaluate docs panel visibility when switching screens."""
|
||||||
|
try:
|
||||||
|
panel = self.screen.query_one("#docs-panel")
|
||||||
|
except NoMatches:
|
||||||
|
return
|
||||||
|
if getattr(panel, "_user_toggled", False):
|
||||||
|
return
|
||||||
|
panel.display = self.size.width >= self.DOCS_AUTO_HIDE_WIDTH
|
||||||
|
|
||||||
async def action_quit(self) -> None:
|
async def action_quit(self) -> None:
|
||||||
if job_manager.running_count() > 0:
|
if job_manager.running_count() > 0:
|
||||||
from tui.widgets import ConfirmDialog
|
from tui.widgets import ConfirmDialog
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ Screen {
|
|||||||
|
|
||||||
#menu-list {
|
#menu-list {
|
||||||
width: 1fr;
|
width: 1fr;
|
||||||
max-width: 35;
|
max-width: 40;
|
||||||
height: 20;
|
min-width: 25;
|
||||||
|
height: auto;
|
||||||
|
max-height: 22;
|
||||||
margin: 1 2 0 2;
|
margin: 1 2 0 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +41,9 @@ Screen {
|
|||||||
|
|
||||||
/* Data tables */
|
/* Data tables */
|
||||||
DataTable {
|
DataTable {
|
||||||
height: 12;
|
height: auto;
|
||||||
|
min-height: 5;
|
||||||
|
max-height: 16;
|
||||||
margin: 1 0;
|
margin: 1 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +129,7 @@ SelectionList {
|
|||||||
#set-buttons {
|
#set-buttons {
|
||||||
height: auto;
|
height: auto;
|
||||||
margin: 1 0;
|
margin: 1 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#backup-buttons Button,
|
#backup-buttons Button,
|
||||||
@@ -145,7 +150,8 @@ SelectionList {
|
|||||||
|
|
||||||
/* Dialogs */
|
/* Dialogs */
|
||||||
#confirm-dialog {
|
#confirm-dialog {
|
||||||
width: 60;
|
width: 80%;
|
||||||
|
max-width: 60;
|
||||||
height: auto;
|
height: auto;
|
||||||
padding: 2;
|
padding: 2;
|
||||||
background: $panel;
|
background: $panel;
|
||||||
@@ -173,8 +179,10 @@ SelectionList {
|
|||||||
|
|
||||||
/* Folder picker */
|
/* Folder picker */
|
||||||
#folder-picker {
|
#folder-picker {
|
||||||
width: 70;
|
width: 90%;
|
||||||
height: 30;
|
max-width: 70;
|
||||||
|
height: 80%;
|
||||||
|
max-height: 30;
|
||||||
padding: 1;
|
padding: 1;
|
||||||
background: $panel;
|
background: $panel;
|
||||||
border: thick $accent;
|
border: thick $accent;
|
||||||
@@ -216,7 +224,8 @@ SelectionList {
|
|||||||
|
|
||||||
/* Operation log */
|
/* Operation log */
|
||||||
#op-log {
|
#op-log {
|
||||||
width: 80%;
|
width: 90%;
|
||||||
|
max-width: 120;
|
||||||
height: 80%;
|
height: 80%;
|
||||||
padding: 1;
|
padding: 1;
|
||||||
background: $panel;
|
background: $panel;
|
||||||
@@ -254,7 +263,8 @@ SelectionList {
|
|||||||
|
|
||||||
/* Snapshot browser */
|
/* Snapshot browser */
|
||||||
#snapshot-browser {
|
#snapshot-browser {
|
||||||
width: 80%;
|
width: 90%;
|
||||||
|
max-width: 120;
|
||||||
height: 80%;
|
height: 80%;
|
||||||
padding: 1;
|
padding: 1;
|
||||||
background: $panel;
|
background: $panel;
|
||||||
@@ -302,7 +312,8 @@ SelectionList {
|
|||||||
|
|
||||||
/* Wizard */
|
/* Wizard */
|
||||||
#wizard {
|
#wizard {
|
||||||
width: 60;
|
width: 90%;
|
||||||
|
max-width: 60;
|
||||||
padding: 2;
|
padding: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,8 +361,10 @@ Switch {
|
|||||||
|
|
||||||
/* File picker */
|
/* File picker */
|
||||||
#file-picker {
|
#file-picker {
|
||||||
width: 70;
|
width: 90%;
|
||||||
height: 30;
|
max-width: 70;
|
||||||
|
height: 80%;
|
||||||
|
max-height: 30;
|
||||||
padding: 1;
|
padding: 1;
|
||||||
background: $panel;
|
background: $panel;
|
||||||
border: thick $accent;
|
border: thick $accent;
|
||||||
|
|||||||
@@ -57,8 +57,16 @@ class MainMenuScreen(Screen):
|
|||||||
yield Footer()
|
yield Footer()
|
||||||
|
|
||||||
def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
|
self._update_logo_visibility()
|
||||||
self.query_one("#menu-list", OptionList).focus()
|
self.query_one("#menu-list", OptionList).focus()
|
||||||
|
|
||||||
|
def on_resize(self) -> None:
|
||||||
|
self._update_logo_visibility()
|
||||||
|
|
||||||
|
def _update_logo_visibility(self) -> None:
|
||||||
|
logo = self.query_one("#logo")
|
||||||
|
logo.display = self.app.size.width >= 80
|
||||||
|
|
||||||
def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
|
def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
|
||||||
option_id = event.option.id
|
option_id = event.option.id
|
||||||
if option_id == "quit":
|
if option_id == "quit":
|
||||||
|
|||||||
Reference in New Issue
Block a user