diff --git a/src/config/theme.tcss b/src/config/theme.tcss index 225a5e3..88be2fb 100644 --- a/src/config/theme.tcss +++ b/src/config/theme.tcss @@ -1246,3 +1246,106 @@ SpotifyView { height: auto; margin-left: 1; } + + + +SettingsView { + width: 60%; + height: 90%; + padding: 1; + align: center middle; + offset: 35% 5%; /* Adjust these values to move it more center */ + layout: grid; /* Add this to help with centering */ +} +.settings-container { + width: 90%; + height: 80%; + margin: 2; + border: round $accent; + padding: 1 2; + align: center middle; + layout: grid; /* Add this as well */ +} + +.settings-layout { + height: 100%; + layout: horizontal; + align: center middle; +} + +.settings-sidebar { + width: 30; + height: 100%; + border-right: solid $accent; + padding: 1; +} + +.settings-content { + width: 100%; + height: 100%; + padding: 1 2; +} + +.setting-button { + width: 100%; + height: 3; + margin-bottom: 1; + background: $surface; + color: $text; + border: none; +} + +.setting-button:hover { + background: $accent; +} + +.setting-button.active { + text-style: none; + border-right: thick $secondary; +} + +.settings-title { + text-style: italic; + margin-bottom: 4; + margin-left: 1; +} + +ThemeButton { + background: transparent; + + width: 30; + height: 3; /* Reduced height for better compactness */ + content-align: center middle; + color: $text; + padding: 0 0; /* Add minimal horizontal padding */ +} + +ThemeButton:hover { + background: transparent; + color: $accent; + text-style: none; +} + +ThemeButton.active { + border-top: transparent; + border-bottom: transparent; + color: $text; + text-style: none; +} + +ThemeButton:focus { + background: $accent; + color: $text; + text-style: none; +} + +.theme-buttons-grid { + layout: grid; + grid-size: 2; + grid-columns: 1fr 1fr; + grid-rows: auto; + grid-gutter: 2 1; /* Reduced gutter, horizontal spacing only */ + padding: 0; /* Remove padding */ + align: center middle; /* Center the entire grid */ + margin-right: 44; /* Add right margin to center the grid */ +} \ No newline at end of file diff --git a/src/ui/screens/home.py b/src/ui/screens/home.py index d1766e5..f4818cf 100644 --- a/src/ui/screens/home.py +++ b/src/ui/screens/home.py @@ -8,6 +8,7 @@ from src.ui.views.calendar import CalendarView from src.utils.system_stats import SystemStatsHeader from src.ui.views.nest import NestView +from src.ui.views.settings import SettingsView from src.ui.mixins.focus_mixin import InitialFocusMixin from textual.widget import Widget from typing import Optional @@ -181,7 +182,8 @@ def on_button_pressed(self, event: Button.Pressed) -> None: content_container.mount(spotify_view) elif button_id == "menu_settings": menu.add_class("hidden") - self.notify("Coming Soon!", severity="warning") + settings_view = SettingsView() + content_container.mount(settings_view) elif button_id == "menu_exit": self.action_quit_app() except Exception as e: diff --git a/src/ui/views/settings.py b/src/ui/views/settings.py new file mode 100644 index 0000000..6f99402 --- /dev/null +++ b/src/ui/views/settings.py @@ -0,0 +1,140 @@ +from textual.containers import Container, Horizontal, Vertical +from textual.widgets import Static, Button +from textual.app import ComposeResult +from textual.widget import Widget +from textual.binding import Binding +from typing import Optional + +class SettingsButton(Button): + def __init__(self, label: str, setting_id: str): + super().__init__(label, id=f"setting_{setting_id}") + self.setting_id = setting_id + self.add_class("setting-button") + + def toggle_active(self, is_active: bool): + if is_active: + self.add_class("active") + else: + self.remove_class("active") + +class ThemeButton(Button): + def __init__(self, theme: str): + super().__init__(theme, id=f"theme_{theme}") + self.theme_name = theme + + def on_button_pressed(self, event: Button.Pressed) -> None: + event.stop() + self.app.theme = self.theme_name + +class PersonalizationContent(Container): + def compose(self) -> ComposeResult: + yield Static("Personalization Settings", classes="settings-title") + with Container(classes="theme-buttons-grid"): + themes = [ + "textual-dark", "textual-light", "nord", "gruvbox", + "catppuccin-mocha", "dracula", "tokyo-night", "monokai", + "flexoki", "catppuccin-latte", "solarized-light" + ] + for theme in themes: + yield ThemeButton(theme) + +class SpotifyContent(Container): + def compose(self) -> ComposeResult: + yield Static("Spotify Settings", classes="settings-title") + +class WidgetsContent(Container): + def compose(self) -> ComposeResult: + yield Static("Widgets Settings", classes="settings-title") + +class AboutContent(Container): + def compose(self) -> ComposeResult: + yield Static("About", classes="settings-title") + +class SettingsView(Container): + BINDINGS = [ + Binding("up", "move_up", "Up", show=True), + Binding("down", "move_down", "Down", show=True), + Binding("enter", "select_setting", "Select", show=True), + ] + + def compose(self) -> ComposeResult: + with Container(classes="settings-container"): + with Horizontal(classes="settings-layout"): + with Vertical(classes="settings-sidebar"): + yield SettingsButton("Personalization", "personalization") + yield SettingsButton("Spotify", "spotify") + yield SettingsButton("Widgets", "widgets") + yield SettingsButton("About", "about") + + with Container(classes="settings-content"): + yield PersonalizationContent() + yield SpotifyContent() + yield WidgetsContent() + yield AboutContent() + + def on_mount(self) -> None: + personalization_btn = self.query_one("SettingsButton#setting_personalization") + personalization_btn.toggle_active(True) + personalization_btn.focus() + + spotify_content = self.query_one(SpotifyContent) + widgets_content = self.query_one(WidgetsContent) + about_content = self.query_one(AboutContent) + + spotify_content.styles.display = "none" + widgets_content.styles.display = "none" + about_content.styles.display = "none" + + def get_initial_focus(self) -> Optional[Widget]: + return self.query_one(SettingsButton, id="setting_personalization") + + def on_button_pressed(self, event: Button.Pressed) -> None: + if not isinstance(event.button, SettingsButton): + return + + event.stop() + + setting_buttons = self.query(SettingsButton) + + personalization_content = self.query_one(PersonalizationContent) + spotify_content = self.query_one(SpotifyContent) + widgets_content = self.query_one(WidgetsContent) + about_content = self.query_one(AboutContent) + + for button in setting_buttons: + event.stop() + button.toggle_active(button.id == event.button.id) + + all_content = [personalization_content, spotify_content, widgets_content, about_content] + for content in all_content: + content.styles.display = "none" + + if event.button.id == "setting_personalization": + personalization_content.styles.display = "block" + elif event.button.id == "setting_spotify": + spotify_content.styles.display = "block" + elif event.button.id == "setting_widgets": + widgets_content.styles.display = "block" + elif event.button.id == "setting_about": + about_content.styles.display = "block" + + async def action_move_up(self) -> None: + buttons = list(self.query(SettingsButton)) + current = self.app.focused + if current in buttons: + current_idx = buttons.index(current) + prev_idx = (current_idx - 1) % len(buttons) + buttons[prev_idx].focus() + + async def action_move_down(self) -> None: + buttons = list(self.query(SettingsButton)) + current = self.app.focused + if current in buttons: + current_idx = buttons.index(current) + next_idx = (current_idx + 1) % len(buttons) + buttons[next_idx].focus() + + async def action_select_setting(self) -> None: + current = self.app.focused + if isinstance(current, SettingsButton): + current.press() \ No newline at end of file diff --git a/src/ui/views/spotify.py b/src/ui/views/spotify.py index 03dac40..02c18b9 100644 --- a/src/ui/views/spotify.py +++ b/src/ui/views/spotify.py @@ -421,8 +421,7 @@ def on_mount(self) -> None: def compose(self) -> ComposeResult: yield SpotifyPlayer() yield Container( - Static("Spotify", id="status-bar-title", classes="status-item"), - Static("Connected 🟢", id="status-bar-connection", classes="status-item-right"), + Static("Spotify - Connected 🟢", id="status-bar-title", classes="status-item"), classes="status-bar" ) yield Container( @@ -433,7 +432,7 @@ def compose(self) -> ComposeResult: ), Container( Static("Instructions", classes="section-title"), - Static("⌘+R: Refresh", classes="instruction-item"), + Static("CTRL+R: Refresh", classes="instruction-item"), Static("Space: Play/Pause", classes="instruction-item"), Button("Authenticate Spotify", id="auth-btn", variant="primary", classes="connect-spotify-button"), classes="instructions-section"