diff --git a/src/window/controlcenter/battery.py b/src/window/controlcenter/battery.py index 088e77e..f2ddda1 100644 --- a/src/window/controlcenter/battery.py +++ b/src/window/controlcenter/battery.py @@ -357,8 +357,8 @@ class BluetoothConnections(Box): # Initial device update self.update_devices() - # Start periodic device monitoring for real-time updates - self.start_device_monitoring() + self.connect("map", lambda *_: self.start_device_monitoring()) + self.connect("unmap", lambda *_: self.on_hide()) def toggle_other_devices(self, *_): """Toggle the visibility of other devices section""" @@ -490,6 +490,10 @@ class BluetoothConnections(Box): finally: self._update_in_progress = False + def on_hide(self, *_): + """Called when the widget is hidden""" + self.stop_device_monitoring() + def start_device_monitoring(self): """Start periodic monitoring for device changes""" # Monitor for device changes every 5 seconds (less aggressive) diff --git a/src/window/controlcenter/bluetooth.py b/src/window/controlcenter/bluetooth.py index eaf057d..ce95413 100644 --- a/src/window/controlcenter/bluetooth.py +++ b/src/window/controlcenter/bluetooth.py @@ -421,13 +421,12 @@ class BluetoothConnections(Box): self.connect("destroy", self.on_destroy) self.connect("unmap", self.on_hide) + self.connect("map", lambda *_: self.start_device_monitoring()) self.client.notify("scanning") self.client.notify("enabled") self.update_devices() - self.start_device_monitoring() - def handle_user_toggle(self, active: bool): self._switch_lock = True set_bluetooth_enabled_with_fallback(self.client, active) @@ -441,6 +440,7 @@ class BluetoothConnections(Box): def on_hide(self, *_): """Called when the widget is hidden (popup closed)""" + self.stop_device_monitoring() if self.other_devices.get_visible(): self.other_devices.set_visible(False) self.other_devices_scrolled.snap_to_size(0) diff --git a/src/window/controlcenter/per_app_volume.py b/src/window/controlcenter/per_app_volume.py index f3b1799..86c9146 100644 --- a/src/window/controlcenter/per_app_volume.py +++ b/src/window/controlcenter/per_app_volume.py @@ -77,8 +77,8 @@ class PerAppVolumeControl(Box): # Initial population self._populate_apps() - # Set up auto-refresh timer for audio streams - self._refresh_timer = GLib.timeout_add_seconds(2, self._auto_refresh) + self.connect("map", self._on_map) + self.connect("unmap", self._on_unmap) def _auto_refresh(self): """Auto-refresh the application list every 2 seconds""" @@ -88,6 +88,15 @@ class PerAppVolumeControl(Box): self._populate_apps() return True # Continue the timer + def _on_map(self, *_): + if self._refresh_timer is None: + self._refresh_timer = GLib.timeout_add_seconds(2, self._auto_refresh) + + def _on_unmap(self, *_): + if self._refresh_timer is not None: + GLib.source_remove(self._refresh_timer) + self._refresh_timer = None + def _go_back(self, *_): """Return to main control center view""" self.control_center.close_per_app_volume() diff --git a/src/window/controlcenter/wifi.py b/src/window/controlcenter/wifi.py index fa993bd..c7d497c 100644 --- a/src/window/controlcenter/wifi.py +++ b/src/window/controlcenter/wifi.py @@ -383,11 +383,11 @@ class WifiConnections(Box): self.connect("destroy", self.on_destroy) self.connect("unmap", self.on_hide) - - self.start_network_monitoring() + self.connect("map", lambda *_: self.start_network_monitoring()) def on_hide(self, *_): """Called when the widget is hidden (popup closed)""" + self.stop_network_monitoring() if self.other_networks.get_visible(): self.other_networks.set_visible(False) self.other_networks_scrolled.snap_to_size(0) diff --git a/src/window/dock.py b/src/window/dock.py index e74998b..442832d 100644 --- a/src/window/dock.py +++ b/src/window/dock.py @@ -73,16 +73,38 @@ class AppBar(Box): self.setup_app_monitoring() def setup_app_monitoring(self): - def update_running_apps(): - try: - self.update_dock_apps() - except Exception as e: - logger.error(f"[AppBar] Error updating apps: {e}") - return True + self._hyprland_event_handlers = [] + + events_to_monitor = [ + "event::openwindow", + "event::closewindow", + "event::activewindow", + "event::windowtitle", + "event::movewindow", + "event::workspace", + ] + + for event in events_to_monitor: + handler_id = self._hyprland_connection.connect( + event, lambda *_: self.debounced_update_dock_apps() + ) + self._hyprland_event_handlers.append(handler_id) - self._app_monitor_timer_id = GLib.timeout_add(250, update_running_apps) GLib.idle_add(self.update_dock_apps) + def debounced_update_dock_apps(self): + if hasattr(self, "_dock_update_timer") and self._dock_update_timer: + GLib.source_remove(self._dock_update_timer) + self._dock_update_timer = GLib.timeout_add(50, self._do_update_dock_apps) + + def _do_update_dock_apps(self): + self._dock_update_timer = None + try: + self.update_dock_apps() + except Exception as e: + logger.error(f"[AppBar] Error updating apps: {e}") + return False + def update_icon_size(self): """Update all icons in the dock to the current config size""" desktop_apps = get_desktop_applications(include_hidden=False) @@ -122,6 +144,18 @@ class AppBar(Box): GLib.source_remove(self._app_monitor_timer_id) self._app_monitor_timer_id = None + if hasattr(self, "_dock_update_timer") and self._dock_update_timer: + GLib.source_remove(self._dock_update_timer) + self._dock_update_timer = None + + if hasattr(self, "_hyprland_event_handlers"): + for handler_id in self._hyprland_event_handlers: + try: + self._hyprland_connection.disconnect(handler_id) + except Exception: + pass + self._hyprland_event_handlers = [] + # Destroy context menu if self.menu: self.menu.destroy() diff --git a/src/window/launcher/main.py b/src/window/launcher/main.py index d13160f..8c65aef 100644 --- a/src/window/launcher/main.py +++ b/src/window/launcher/main.py @@ -272,7 +272,17 @@ class Launcher(Window): def close_launcher(self): """Hide the launcher and clear search.""" self.hide() + + # Cancel any pending search timeout to prevent background rendering + if hasattr(self, "_search_timeout") and self._search_timeout: + GLib.source_remove(self._search_timeout) + self._search_timeout = None + + # Temporarily block search handling while clearing the entry + self._initializing = True self.search_entry.set_text("") + self._initializing = False + self._clear_results() self.triggered_plugin = None self.active_trigger = "" @@ -435,7 +445,6 @@ class Launcher(Window): for r in all_results ) - # Don't limit results for triggered plugin queries, only for global searches and trigger suggestions if self.triggered_plugin and self.active_trigger: # In trigger mode - show all results from the triggered plugin self.results = all_results @@ -448,8 +457,8 @@ class Launcher(Window): ) if is_application_search: - # Don't limit application search results - self.results = all_results + # Limit application search results to a reasonable number to prevent lag + self.results = all_results[:20] else: # Apply max_results limit for other global searches and trigger suggestions self.results = all_results[: self.max_results] diff --git a/src/window/notification/notification.py b/src/window/notification/notification.py index dd5b067..42a18cc 100644 --- a/src/window/notification/notification.py +++ b/src/window/notification/notification.py @@ -367,7 +367,7 @@ def preload_notification_assets(notification): # Cache notification image if available if hasattr(notification, "image_pixbuf") and notification.image_pixbuf: cache_notification_image( - notification.id, notification.image_pixbuf, (35, 35) + notification.id, notification.image_pixbuf, (128, 128) ) except Exception as e: diff --git a/src/window/panel/main.py b/src/window/panel/main.py index ba2be53..28d2192 100644 --- a/src/window/panel/main.py +++ b/src/window/panel/main.py @@ -85,9 +85,7 @@ class Panel(Window): ) setup_cursor_hover(self.control_center_btn, "pointer") - self.control_center = ModusControlCenter( - parent=self, pointing_to=self.control_center_btn - ) + self._control_center_instance = None self.control_center_btn.connect( "clicked", lambda *_: self.control_center.toggle(), @@ -104,9 +102,7 @@ class Panel(Window): ) setup_cursor_hover(self.notification_center_btn, "pointer") - self.notification_center = NotificationCenter( - parent=self, pointing_to=self.notification_center_btn - ) + self._notification_center_instance = None self.datetime_btn = Button( name="panel-button", @@ -171,10 +167,24 @@ class Panel(Window): def on_notification_count_changed(self, service, *args): self.update_notification_icon() - def on_notification_icon_clicked(self, *args): - count = notification_service.count - if count > 0: - self.notification_center.toggle() + def on_notification_icon_clicked(self, *_): + self.notification_center.toggle() + + @property + def control_center(self): + if self._control_center_instance is None: + self._control_center_instance = ModusControlCenter( + parent=self, pointing_to=self.control_center_btn + ) + return self._control_center_instance + + @property + def notification_center(self): + if self._notification_center_instance is None: + self._notification_center_instance = NotificationCenter( + parent=self, pointing_to=self.notification_center_btn + ) + return self._notification_center_instance def update_notification_icon(self): count = notification_service.count