feat(src/window): migrate device monitoring to map/unmap events

Ensure monitoring starts only when the widget is shown and stops on hide, preventing timer leaks and unnecessary refreshes
This commit is contained in:
sankalp committed 2026-06-23 14:11:25 +05:45
1 parent 01fed4c52f
commit 6707e1e830
8 files changed
+95 -29

No files matched your search

+6 -2
View File
@@ -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)
+2 -2
View File
@@ -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)
+11 -2
View File
@@ -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()
+2 -2
View File
@@ -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)
+41 -7
View File
@@ -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()
+12 -3
View File
@@ -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]
+1 -1
View File
@@ -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:
+20 -10
View File
@@ -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