From 09f792078feb822e94fb66257a2028a5066fdd0d Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Sat, 1 Aug 2026 20:03:46 +0545 Subject: [PATCH] fix: dropdown callbacks, singleton about dialog, config-driven settings sidebar, switcher cache eviction --- src/shared/window/dropdown.py | 4 +- src/window/panel/components/globalmenu.py | 20 +- src/window/settings/main.py | 238 ++++------------------ src/window/switcher/main.py | 1 + 4 files changed, 63 insertions(+), 200 deletions(-) diff --git a/src/shared/window/dropdown.py b/src/shared/window/dropdown.py index b2a3265..514f599 100644 --- a/src/shared/window/dropdown.py +++ b/src/shared/window/dropdown.py @@ -68,9 +68,9 @@ class ModusDropdown: return item @staticmethod - def _activate(callback, command, *_): + def _activate(callback, command, *args): if callback: - callback() + callback(*args) if command: thread(run_command, ["sh", "-c", command], timeout=30) diff --git a/src/window/panel/components/globalmenu.py b/src/window/panel/components/globalmenu.py index e95cf9f..338d099 100644 --- a/src/window/panel/components/globalmenu.py +++ b/src/window/panel/components/globalmenu.py @@ -23,13 +23,29 @@ def has_active_window(): return app_name and app_name != "Modus" +_about_app_window = None + + +def _reset_about_app_window(): + global _about_app_window + _about_app_window = None + + def show_about_app(_=None): + global _about_app_window if not has_active_window(): return app_name = modus_service.current_active_app_name wmclass = getattr(modus_service, "current_active_wm_class", "") - about_window = AboutApp(app_name=app_name, wmclass=wmclass) - about_window.toggle(None) + + window = _about_app_window + if window is None or getattr(window, "app_name", None) != app_name: + if window is not None: + window.destroy() + window = AboutApp(app_name=app_name, wmclass=wmclass) + window.connect("destroy", lambda *_: _reset_about_app_window()) + _about_app_window = window + window.toggle(None) # Menu factories diff --git a/src/window/settings/main.py b/src/window/settings/main.py index 61b6e51..2438041 100644 --- a/src/window/settings/main.py +++ b/src/window/settings/main.py @@ -250,6 +250,21 @@ class SettingsPage(ScrolledWindow): self._container = container +SECTION_ICONS = { + "general": "misc/logo.svg", + "dock": "misc/control.svg", + "panel": "misc/control-center.svg", + "switcher": "screencapture/window.svg", + "notification": "notifications/notification-active.svg", +} + +DEFAULT_SECTION_ICON = "misc/logo.svg" + + +def humanize_key(key): + return " ".join(word.capitalize() for word in key.replace("_", " ").split()) + + class SettingsWindow(Gtk.Window): def __init__(self, **kwargs): super().__init__(title="Modus Settings", **kwargs) @@ -275,31 +290,15 @@ class SettingsWindow(Gtk.Window): v_expand=True, ) - self.pages = { - "general": self._create_general_page(), - "dock": self._create_dock_page(), - "panel": self._create_panel_page(), - "notifications": self._create_notifications_page(), - } - - for name, page in self.pages.items(): - self.stack.add_named(page, name) - + self.pages = {} self.sidebar_buttons = {} - self.sidebar_buttons["general"] = self._create_sidebar_button( - "General", "general", "misc/logo.svg" - ) - self.sidebar_buttons["dock"] = self._create_sidebar_button( - "Dock", "dock", "misc/control.svg" - ) - self.sidebar_buttons["panel"] = self._create_sidebar_button( - "Panel", "panel", "misc/control-center.svg" - ) - self.sidebar_buttons["notifications"] = self._create_sidebar_button( - "Notifications", "notifications", "notifications/notification-active.svg" - ) - self.set_page("general") + for section, values in config().get_all().items(): + if not isinstance(values, dict): + continue + self._add_section(section, values) + + self.set_page(next(iter(self.pages))) self.sidebar = Box( name="settings-sidebar", @@ -349,182 +348,29 @@ class SettingsWindow(Gtk.Window): else: btn.remove_style_class("active") - def _create_general_page(self): - return SettingsPage( - "General Settings", - [ - SettingsRow( - "Debug Mode", - SettingsSwitch("general.debug"), - "Enable verbose logging for development", - ), - SettingsRow( - "Weather Location", - SettingsEntry("general.weather_location"), - "City name for weather updates", - ), - SettingsRow( - "Keyboard Layouts", - SettingsList("general.keyboard_layouts"), - "Manage active keyboard input languages", - ), - SettingsRow( - "Hide Special Workspace", - SettingsSwitch("panel.hide_special_workspace"), - "Don't show special workspace in indicators", - ), - SettingsRow( - "Night Light Temperature", - SettingsEntry("panel.night_light_temperature"), - "Color temperature in Kelvin (e.g. 4500)", - ), - SettingsRow( - "Live Preview", - SettingsSwitch("switcher.live_preview"), - "Show live window previews in switcher", - ), - SettingsRow( - "Preview Delay (ms)", - SettingsEntry("switcher.live_preview_delay_ms"), - "Delay before live preview appears", - ), - ], + def _add_section(self, section, values): + page = self._build_page(section, values) + self.pages[section] = page + self.stack.add_named(page, section) + self.sidebar_buttons[section] = self._create_sidebar_button( + humanize_key(section), + section, + SECTION_ICONS.get(section, DEFAULT_SECTION_ICON), ) - def _create_dock_page(self): - return SettingsPage( - "Dock Settings", - [ - SettingsRow( - "Enabled", - SettingsSwitch("dock.enabled"), - "Show the application dock", - ), - SettingsRow( - "Auto Hide", - SettingsSwitch("dock.auto_hide"), - "Hide dock when not in use", - ), - SettingsRow( - "Always Occluded", - SettingsSwitch("dock.always_occluded"), - "Keep dock behind other windows", - ), - SettingsRow( - "Icon Size", - SettingsEntry("dock.icon_size"), - "Size of dock icons in pixels", - ), - SettingsRow( - "Hide Special Apps", - SettingsSwitch("dock.hide_special_workspace_apps"), - "Hide apps from special workspace in dock", - ), - SettingsRow( - "Hover Scale", - SettingsEntry("dock.hover_scale"), - "Icon scale on hover (1.0 = off, 1.6 = default, 2.0 = max)", - ), - ], - ) + def _build_page(self, section, values): + rows = [] + for key, value in values.items(): + widget = self._infer_widget(f"{section}.{key}", value) + rows.append(SettingsRow(humanize_key(key), widget)) + return SettingsPage(f"{humanize_key(section)} Settings", rows) - def _create_panel_page(self): - rows = [ - SettingsRow( - "Global Menu", - SettingsSwitch("panel.global_menu"), - "Show application menu in the panel", - ), - SettingsRow( - "iMac Button", - SettingsSwitch("panel.imac_button"), - ), - SettingsRow( - "Systray", - SettingsSwitch("panel.systray"), - ), - SettingsRow( - "Control Center", - SettingsSwitch("panel.control_center"), - ), - SettingsRow( - "Search", - SettingsSwitch("panel.search"), - ), - SettingsRow( - "Network", - SettingsSwitch("panel.network"), - ), - SettingsRow( - "Battery", - SettingsSwitch("panel.battery"), - ), - SettingsRow( - "Bluetooth", - SettingsSwitch("panel.bluetooth"), - ), - SettingsRow( - "Date & Time", - SettingsSwitch("panel.date_time"), - ), - SettingsRow( - "Workspace Indicator", - SettingsSwitch("panel.workspace_indicator"), - ), - SettingsRow( - "Notification Center", - SettingsSwitch("panel.notification_center"), - ), - SettingsRow( - "Custom Mods", - SettingsSwitch("panel.custom_mods"), - "Enable custom Hyprland mods in panel", - ), - SettingsRow( - "Window Switcher", - SettingsSwitch("switcher.window_switcher"), - "Enable Alt+Tab window switcher", - ), - SettingsRow( - "OSD", - SettingsSwitch("panel.osd"), - "Enable on-screen display for volume/brightness", - ), - SettingsRow( - "Wallpapers Directory", - SettingsEntry("general.wallpapers_dir"), - "Path to wallpapers folder", - ), - SettingsRow( - "Systray Ignore", - SettingsList("panel.systray_ignore"), - "Icons to hide from the system tray", - ), - ] - - return SettingsPage("Panel Settings", rows) - - def _create_notifications_page(self): - return SettingsPage( - "Notification Settings", - [ - SettingsRow( - "Timeout", - SettingsEntry("notification.timeout"), - "How long notifications stay on screen (e.g. 5s)", - ), - SettingsRow( - "Ignored Apps", - SettingsList("notification.ignored_apps"), - "Apps that won't show notifications", - ), - SettingsRow( - "Limited History", - SettingsList("notification.limited_apps_history"), - "Apps with only the latest notification shown", - ), - ], - ) + def _infer_widget(self, full_key, value): + if isinstance(value, bool): + return SettingsSwitch(full_key) + if isinstance(value, list): + return SettingsList(full_key) + return SettingsEntry(full_key) def toggle(self): if getattr(self, "_destroyed", False): diff --git a/src/window/switcher/main.py b/src/window/switcher/main.py index 1778de0..d3baa1f 100644 --- a/src/window/switcher/main.py +++ b/src/window/switcher/main.py @@ -165,6 +165,7 @@ class ApplicationSwitcher(Window): def _get_pixbuf(self, class_name: str) -> GdkPixbuf.Pixbuf | None: key = (class_name, self.icon_size) if key in self._pixbuf_cache: + self._pixbuf_cache.move_to_end(key) return self._pixbuf_cache[key] if len(self._pixbuf_cache) > 128: