refactor(src/window): migrate control center windows to AppletWindow and simplify UI
Replace Window base with AppletWindow for consistency, switch to FlatScale, and streamline mouse capture handling.
This commit is contained in:
11 files changed
+168
-199
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import subprocess
|
||||
|
||||
from fabric.bluetooth import BluetoothClient, BluetoothDevice
|
||||
from fabric.utils import Gdk, GLib, Gtk, exec_shell_command, logger
|
||||
from fabric.utils import Gdk, GLib, exec_shell_command, logger
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.centerbox import CenterBox
|
||||
@@ -12,6 +12,7 @@ from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from fabric.widgets.separator import Separator
|
||||
|
||||
from utils.utils import svg_file
|
||||
from shared.widgets.smooth_switch import SmoothSwitch
|
||||
|
||||
|
||||
def get_battery_icon_file(
|
||||
@@ -246,14 +247,11 @@ class BluetoothConnections(Box):
|
||||
children=title_children,
|
||||
)
|
||||
|
||||
self.toggle_button = Gtk.Switch(visible=True, name="toggle-button")
|
||||
|
||||
# Safely set initial state
|
||||
self.toggle_button.set_active(self.client.enabled)
|
||||
self.toggle_button.connect(
|
||||
"notify::active",
|
||||
lambda *_: set_bluetooth_enabled_with_fallback(
|
||||
self.client, self.toggle_button.get_active()
|
||||
self.toggle_button = SmoothSwitch(
|
||||
name="toggle-button",
|
||||
active=self.client.enabled,
|
||||
on_user_toggle=lambda active: set_bluetooth_enabled_with_fallback(
|
||||
self.client, active
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -1437,9 +1437,6 @@ class ExpandedPlayer(Window):
|
||||
|
||||
super().destroy()
|
||||
|
||||
def _init_mousecapture(self, mousecapture):
|
||||
self._mousecapture_parent = mousecapture
|
||||
|
||||
def hide_controlcenter(self, *_):
|
||||
# self._mousecapture_parent.toggle_mousecapture()
|
||||
self.set_visible(False)
|
||||
@@ -5,8 +5,7 @@ from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.centerbox import CenterBox
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.scale import Scale
|
||||
from fabric.widgets.wayland import WaylandWindow as Window
|
||||
from shared.widgets.flat_scale import FlatScale
|
||||
|
||||
from services.brightness import Brightness
|
||||
from services.network import NetworkClient
|
||||
@@ -22,18 +21,21 @@ from window.controlcenter.per_app_volume import PerAppVolumeControl
|
||||
from window.controlcenter.player import PlayerBoxStack, get_shared_mpris_manager
|
||||
from window.controlcenter.wifi import WifiConnections
|
||||
|
||||
brightness_service = Brightness.get_initial()
|
||||
from shared.window.applet_window import AppletWindow
|
||||
|
||||
brightness_service = Brightness()
|
||||
|
||||
|
||||
class ModusControlCenter(Window):
|
||||
def __init__(self, **kwargs):
|
||||
class ModusControlCenter(AppletWindow):
|
||||
def __init__(self, parent=None, pointing_to=None, **kwargs):
|
||||
super().__init__(
|
||||
parent=parent,
|
||||
pointing_to=pointing_to,
|
||||
layer="top",
|
||||
title="modus",
|
||||
anchor="top right",
|
||||
margin="2px 10px 0px 0px",
|
||||
exclusivity="auto",
|
||||
keyboard_mode="on-demand",
|
||||
name="control-center-menu",
|
||||
visible=False,
|
||||
**kwargs,
|
||||
@@ -98,11 +100,11 @@ class ModusControlCenter(Window):
|
||||
ellipsization="end",
|
||||
h_align="start",
|
||||
)
|
||||
self.volume_scale = Scale(
|
||||
self.volume_scale = FlatScale(
|
||||
value=volume,
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
increments=(5, 5),
|
||||
step=5,
|
||||
name="volume-widget-slider",
|
||||
size=30,
|
||||
h_expand=True,
|
||||
@@ -115,11 +117,11 @@ class ModusControlCenter(Window):
|
||||
else 50
|
||||
)
|
||||
|
||||
self.brightness_scale = Scale(
|
||||
self.brightness_scale = FlatScale(
|
||||
value=brightness_percentage,
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
increments=(5, 5),
|
||||
step=5,
|
||||
name="brightness-widget-slider",
|
||||
size=30,
|
||||
h_expand=True,
|
||||
@@ -555,7 +557,7 @@ class ModusControlCenter(Window):
|
||||
|
||||
# Connect brightness controls if brightness service is available
|
||||
if brightness_service.max_screen > 0:
|
||||
self.brightness_scale.connect("change-value", self.set_brightness)
|
||||
self.brightness_scale.connect("value-changed", self.set_brightness)
|
||||
self.brightness_scale.connect("scroll-event", self.on_brightness_scroll)
|
||||
self._signal_connections.append(
|
||||
(
|
||||
@@ -565,7 +567,7 @@ class ModusControlCenter(Window):
|
||||
)
|
||||
|
||||
# Connect volume scale signals
|
||||
self.volume_scale.connect("change-value", self.set_volume)
|
||||
self.volume_scale.connect("value-changed", self.set_volume)
|
||||
self.volume_scale.connect("scroll-event", self.on_volume_scroll)
|
||||
|
||||
# Mark signals as connected
|
||||
@@ -762,14 +764,14 @@ class ModusControlCenter(Window):
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to toggle caffeine: {e}")
|
||||
|
||||
def set_volume(self, _, __, volume):
|
||||
def set_volume(self, _, volume):
|
||||
if not self._signals_connected:
|
||||
return
|
||||
self._updating_volume = True
|
||||
audio_service.speaker.volume = round(volume)
|
||||
self._updating_volume = False
|
||||
|
||||
def set_brightness(self, _, __, brightness):
|
||||
def set_brightness(self, _, brightness):
|
||||
if not self._signals_connected:
|
||||
return
|
||||
self._updating_brightness = True
|
||||
@@ -1023,9 +1025,6 @@ class ModusControlCenter(Window):
|
||||
)
|
||||
self.focus_status_label.set_label("On" if self.focus_mode else "Off")
|
||||
|
||||
def _init_mousecapture(self, mousecapture):
|
||||
self._mousecapture_parent = mousecapture
|
||||
|
||||
def _disconnect_all_signals(self):
|
||||
"""Disconnect all signal connections to prevent memory leaks"""
|
||||
try:
|
||||
@@ -1206,8 +1205,7 @@ class ModusControlCenter(Window):
|
||||
self._disconnect_signals_when_hidden()
|
||||
|
||||
# Hide the control center
|
||||
if hasattr(self, "_mousecapture_parent"):
|
||||
self._mousecapture_parent.toggle_mousecapture()
|
||||
self.set_visible(False)
|
||||
self.set_visible(False)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -3,7 +3,7 @@ from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.image import Image
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.scale import Scale
|
||||
from shared.widgets.flat_scale import FlatScale
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from fabric.widgets.separator import Separator
|
||||
|
||||
@@ -249,11 +249,11 @@ class PerAppVolumeControl(Box):
|
||||
)
|
||||
|
||||
# Volume scale (smaller and horizontal)
|
||||
volume_scale = Scale(
|
||||
volume_scale = FlatScale(
|
||||
value=volume_percent,
|
||||
min_value=0,
|
||||
max_value=max_vol,
|
||||
increments=(1, 5),
|
||||
step=5,
|
||||
name="compact-volume-slider",
|
||||
size=20,
|
||||
h_expand=True,
|
||||
@@ -261,8 +261,8 @@ class PerAppVolumeControl(Box):
|
||||
|
||||
# Connect volume change handler
|
||||
volume_scale.connect(
|
||||
"change-value",
|
||||
lambda scale, scroll_type, value, app=app: self._set_app_volume(app, value),
|
||||
"value-changed",
|
||||
lambda scale, value, app=app: self._set_app_volume(app, value),
|
||||
)
|
||||
|
||||
# Create horizontal compact layout
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import subprocess
|
||||
|
||||
from fabric.utils import Gdk, GLib, Gtk
|
||||
from fabric.utils import Gdk, GLib
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.centerbox import CenterBox
|
||||
@@ -13,6 +13,7 @@ from fabric.widgets.separator import Separator
|
||||
from services.network import NetworkClient
|
||||
from utils.functions import get_wifi_connecting_icon, get_wifi_icon_for_strength
|
||||
from utils.utils import svg_file
|
||||
from shared.widgets.smooth_switch import SmoothSwitch
|
||||
from shared.dialogs.wifi_password_dialog import WiFiPasswordDialog
|
||||
from gi.repository import NM
|
||||
|
||||
@@ -299,7 +300,12 @@ class WifiConnections(Box):
|
||||
children=title_children,
|
||||
)
|
||||
|
||||
self.toggle_button = Gtk.Switch(visible=True, name="toggle-button")
|
||||
self.toggle_button = SmoothSwitch(
|
||||
name="toggle-button",
|
||||
on_user_toggle=lambda active: self.on_toggle_changed(
|
||||
self.toggle_button, active
|
||||
),
|
||||
)
|
||||
|
||||
# Create Known Network section
|
||||
self.known_networks_label = Label(
|
||||
@@ -393,7 +399,6 @@ class WifiConnections(Box):
|
||||
if self.wifi_service:
|
||||
# Set up WiFi toggle
|
||||
self.toggle_button.set_active(self.wifi_service.wireless_enabled)
|
||||
self.toggle_button.connect("notify::active", self.on_toggle_changed)
|
||||
|
||||
# Connect to WiFi service signals — track IDs for cleanup
|
||||
self._signal_ids.append(
|
||||
|
||||
@@ -9,11 +9,11 @@ from fabric.widgets.image import Image
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.revealer import Revealer
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from fabric.widgets.wayland import WaylandWindow as Window
|
||||
|
||||
import shared.data as data
|
||||
from services.modus import notification_service
|
||||
from shared.widgets.clipping_box import ClippingBox
|
||||
from shared.window.applet_window import AppletWindow
|
||||
from shared.widgets.custom_image import CustomImage
|
||||
from utils.functions import escape_markup_text
|
||||
from window.notification.notification import (
|
||||
@@ -591,13 +591,14 @@ class NotificationCenterWidget(NotificationWidget):
|
||||
return False
|
||||
|
||||
|
||||
class NotificationCenter(Window):
|
||||
def __init__(self):
|
||||
class NotificationCenter(AppletWindow):
|
||||
def __init__(self, parent=None, pointing_to=None):
|
||||
super().__init__(
|
||||
parent=parent,
|
||||
pointing_to=pointing_to,
|
||||
layer="overlay",
|
||||
anchor="top right",
|
||||
visible=False,
|
||||
keyboard_mode="on-demand",
|
||||
title="modus",
|
||||
)
|
||||
|
||||
@@ -840,8 +841,8 @@ class NotificationCenter(Window):
|
||||
self.scrolled.set_visible(current_count > 0)
|
||||
|
||||
# Auto-close notification center when no notifications remain
|
||||
if current_count == 0 and hasattr(self, "mousecapture"):
|
||||
self.mousecapture.hide_child_window()
|
||||
if current_count == 0:
|
||||
self.hide()
|
||||
|
||||
def clear_all_notifications(self, *_):
|
||||
# Clear all groups
|
||||
@@ -851,18 +852,14 @@ class NotificationCenter(Window):
|
||||
# Clear all remaining cached notification images AND icons when clear all is clicked
|
||||
cleanup_all_notification_caches() # Clear ALL caches (icons + images)
|
||||
notification_service.clear_all_cached_notifications()
|
||||
if hasattr(self, "mousecapture"):
|
||||
self.mousecapture.hide_child_window()
|
||||
self.hide()
|
||||
|
||||
def _on_escape_pressed(self, *_):
|
||||
if hasattr(self, "mousecapture"):
|
||||
self.mousecapture.hide_child_window()
|
||||
self.hide()
|
||||
|
||||
def _init_mousecapture(self, mousecapture):
|
||||
self.mousecapture = mousecapture
|
||||
|
||||
def _set_mousecapture(self, visible):
|
||||
def set_visible(self, visible: bool):
|
||||
"""Control notification center visibility with slide-left animation"""
|
||||
super().set_visible(visible)
|
||||
if visible:
|
||||
self.main_revealer.set_reveal_child(True)
|
||||
else:
|
||||
|
||||
@@ -7,7 +7,6 @@ from fabric.widgets.label import Label
|
||||
|
||||
from shared.dialogs.about import AboutApp, get_about_window
|
||||
from shared.window.dropdown import ModusDropdown, dropdown_divider, dropdowns
|
||||
from shared.window.mousecapture import DropDownMouseCapture
|
||||
from utils.app_name_resolver import format_window
|
||||
from utils.roam import modus_service
|
||||
from utils.utils import setup_cursor_hover
|
||||
@@ -41,8 +40,7 @@ def create_dropdown_with_capture(dropdown_id, parent, dropdown_children, layer="
|
||||
parent=parent,
|
||||
dropdown_children=dropdown_children,
|
||||
)
|
||||
mouse_capture = DropDownMouseCapture(layer=layer, child_window=dropdown)
|
||||
return mouse_capture
|
||||
return dropdown
|
||||
|
||||
|
||||
def manage_button_style_classes(buttons, active_button=None, style_class="active"):
|
||||
@@ -72,7 +70,7 @@ def on_click_subthread(button, on_clicked, on_click):
|
||||
|
||||
for dropdown in dropdowns:
|
||||
if dropdown.is_visible():
|
||||
dropdown.hide_via_mousecapture()
|
||||
dropdown.hide()
|
||||
break
|
||||
|
||||
|
||||
@@ -137,15 +135,13 @@ class GlobalMenuDropdowns:
|
||||
self.parent = parent
|
||||
|
||||
self.system_dropdown = SystemDropdown(parent=parent)
|
||||
self.menu_button_dropdown = DropDownMouseCapture(
|
||||
layer="top", child_window=self.system_dropdown
|
||||
)
|
||||
self.menu_button_dropdown = self.system_dropdown
|
||||
self.menu_button = Button(
|
||||
label="Modus",
|
||||
name="global-menu",
|
||||
on_clicked=lambda _: self.menu_button_dropdown.toggle_mousecapture(),
|
||||
on_clicked=lambda _: self.menu_button_dropdown.toggle(),
|
||||
)
|
||||
self.menu_button_dropdown.child_window.set_pointing_to(self.menu_button)
|
||||
self.menu_button_dropdown.set_pointing_to(self.menu_button)
|
||||
|
||||
self.global_title_menu_about = dropdown_option(
|
||||
f"About {modus_service.current_active_app_name}",
|
||||
@@ -162,8 +158,8 @@ class GlobalMenuDropdowns:
|
||||
parent,
|
||||
[
|
||||
dropdown_option(
|
||||
"Enter Full Screen",
|
||||
on_click="hyprctl dispatch 'hl.dsp.fullscreen()'",
|
||||
"Fullscreen",
|
||||
on_click="hyprctl dispatch 'hl.dsp.window.fullscreen()'",
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -183,35 +179,35 @@ class GlobalMenuDropdowns:
|
||||
),
|
||||
dropdown_divider("---------------------"),
|
||||
dropdown_option(
|
||||
"Move Window to Left",
|
||||
on_click="hyprctl dispatch 'hl.dsp.movewindow([[l]])'",
|
||||
"Left",
|
||||
on_click="hyprctl dispatch 'hl.dsp.window.move([[l]])'",
|
||||
),
|
||||
dropdown_option(
|
||||
"Move Window to Right",
|
||||
on_click="hyprctl dispatch 'hl.dsp.movewindow([[r]])'",
|
||||
"Right",
|
||||
on_click="hyprctl dispatch 'hl.dsp.window.move([[r]])'",
|
||||
),
|
||||
dropdown_option(
|
||||
"Cycle Through Windows",
|
||||
on_click="hyprctl dispatch 'hl.dsp.cyclenext()'",
|
||||
"Cycle Next",
|
||||
on_click="hyprctl dispatch 'hl.dsp.window.cycle_next()'",
|
||||
),
|
||||
dropdown_divider("---------------------"),
|
||||
dropdown_option(
|
||||
"Float", on_click="hyprctl dispatch 'hl.dsp.togglefloating()'"
|
||||
"Float", on_click="hyprctl dispatch 'hl.dsp.window.float()'"
|
||||
),
|
||||
dropdown_option(
|
||||
"Quit", on_click="hyprctl dispatch 'hl.dsp.killactive()'"
|
||||
"Quit", on_click="hyprctl dispatch 'hl.dsp.window.close()'"
|
||||
),
|
||||
dropdown_option(
|
||||
"Pseudo", on_click="hyprctl dispatch 'hl.dsp.pseudo()'"
|
||||
"Pseudo", on_click="hyprctl dispatch 'hl.dsp.window.pseudo()'"
|
||||
),
|
||||
dropdown_option(
|
||||
"Toggle Split", on_click="hyprctl dispatch 'hl.dsp.togglesplit()'"
|
||||
),
|
||||
dropdown_option(
|
||||
"Center", on_click="hyprctl dispatch 'hl.dsp.centerwindow()'"
|
||||
"Center", on_click="hyprctl dispatch 'hl.dsp.window.center()'"
|
||||
),
|
||||
dropdown_option(
|
||||
"Group", on_click="hyprctl dispatch 'hl.dsp.togglegroup()'"
|
||||
"Group", on_click="hyprctl dispatch 'hl.dsp.group.toggle()'"
|
||||
),
|
||||
dropdown_option(
|
||||
"Pin",
|
||||
@@ -252,31 +248,24 @@ class GlobalMenuDropdowns:
|
||||
on_clicked=self._on_title_button_clicked,
|
||||
)
|
||||
|
||||
self.global_menu_title.child_window.set_pointing_to(
|
||||
self.global_menu_button_title
|
||||
)
|
||||
self.global_menu_title.set_pointing_to(self.global_menu_button_title)
|
||||
# File, Edit and Go buttons are placeholders - no dropdowns implemented yet
|
||||
self.global_menu_button_file = create_menu_button("File")
|
||||
self.global_menu_button_edit = create_menu_button("Edit")
|
||||
self.global_menu_button_go = create_menu_button("Go")
|
||||
|
||||
self.global_menu_button_view = create_menu_button(
|
||||
"View",
|
||||
lambda _: self.global_menu_view.toggle_mousecapture(),
|
||||
"View", lambda _: self.global_menu_view.toggle()
|
||||
)
|
||||
self.global_menu_view.child_window.set_pointing_to(self.global_menu_button_view)
|
||||
self.global_menu_view.set_pointing_to(self.global_menu_button_view)
|
||||
self.global_menu_button_window = create_menu_button(
|
||||
"Window",
|
||||
lambda _: self.global_menu_window.toggle_mousecapture(),
|
||||
)
|
||||
self.global_menu_window.child_window.set_pointing_to(
|
||||
self.global_menu_button_window
|
||||
"Window", lambda _: self.global_menu_window.toggle()
|
||||
)
|
||||
self.global_menu_window.set_pointing_to(self.global_menu_button_window)
|
||||
self.global_menu_button_help = create_menu_button(
|
||||
"Help",
|
||||
lambda _: self.global_menu_help.toggle_mousecapture(),
|
||||
"Help", lambda _: self.global_menu_help.toggle()
|
||||
)
|
||||
self.global_menu_help.child_window.set_pointing_to(self.global_menu_button_help)
|
||||
self.global_menu_help.set_pointing_to(self.global_menu_button_help)
|
||||
|
||||
self.all_menu_buttons = [
|
||||
self.menu_button,
|
||||
@@ -305,7 +294,7 @@ class GlobalMenuDropdowns:
|
||||
|
||||
def _on_title_button_clicked(self, _):
|
||||
if has_active_window():
|
||||
self.global_menu_title.toggle_mousecapture()
|
||||
self.global_menu_title.set_visible(not self.global_menu_title.get_visible())
|
||||
|
||||
def _on_active_app_changed(self, _, value):
|
||||
self.global_title_menu_about.set_property("label", f"About {value}")
|
||||
@@ -366,11 +355,9 @@ class GlobalMenu(Box):
|
||||
]
|
||||
|
||||
def show_system_dropdown(self, imac_button):
|
||||
self.dropdown_system.menu_button_dropdown.child_window.set_pointing_to(
|
||||
imac_button
|
||||
)
|
||||
self.dropdown_system.menu_button_dropdown.set_pointing_to(imac_button)
|
||||
mouse_capture = self.dropdown_system.menu_button_dropdown
|
||||
mouse_capture.set_child_window_visible(not mouse_capture.is_visible())
|
||||
mouse_capture.toggle()
|
||||
|
||||
def destroy(self):
|
||||
"""Clean up the global menu and its dropdowns"""
|
||||
|
||||
@@ -2,12 +2,11 @@ from fabric.bluetooth import BluetoothClient
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.wayland import WaylandWindow as Window
|
||||
|
||||
from services.battery import BatteryService, DeviceState
|
||||
from services.battery import Battery
|
||||
from services.network import NetworkClient
|
||||
from shared.window.battery_widget import BatteryControl
|
||||
from shared.window.mousecapture import DropDownMouseCapture
|
||||
from shared.window.applet_window import AppletWindow
|
||||
from utils.functions import format_duration, get_wifi_icon_for_strength
|
||||
from utils.roam import modus_service
|
||||
from utils.utils import setup_cursor_hover, svg_file
|
||||
@@ -15,45 +14,41 @@ from window.controlcenter.bluetooth import BluetoothConnections
|
||||
from window.controlcenter.wifi import WifiConnections
|
||||
|
||||
|
||||
def create_control_window(name_prefix):
|
||||
return Window(
|
||||
def create_control_window(name_prefix, parent=None, pointing_to=None):
|
||||
return AppletWindow(
|
||||
parent=parent,
|
||||
pointing_to=pointing_to,
|
||||
layer="overlay",
|
||||
title="modus",
|
||||
anchor="top right",
|
||||
margin="2px 10px 0px 0px",
|
||||
exclusivity="auto",
|
||||
keyboard_mode="on-demand",
|
||||
name=f"{name_prefix}-window",
|
||||
visible=False,
|
||||
)
|
||||
|
||||
|
||||
def create_mouse_capture(window):
|
||||
return DropDownMouseCapture(layer="top", child_window=window)
|
||||
|
||||
|
||||
def setup_control_center(
|
||||
show_window, name_prefix, widget_class, parent, **widget_kwargs
|
||||
show_window, name_prefix, widget_class, parent, pointing_to=None, **widget_kwargs
|
||||
):
|
||||
if not show_window:
|
||||
return None, None, None
|
||||
return None, None
|
||||
|
||||
window = create_control_window(name_prefix)
|
||||
window = create_control_window(name_prefix, parent=parent, pointing_to=pointing_to)
|
||||
widget = widget_class(parent, **widget_kwargs)
|
||||
window.children = [widget]
|
||||
mouse_capture = create_mouse_capture(window)
|
||||
|
||||
return window, widget, mouse_capture
|
||||
return window, widget
|
||||
|
||||
|
||||
def handle_indicator_click(mouse_capture):
|
||||
if mouse_capture:
|
||||
mouse_capture.toggle_mousecapture()
|
||||
def handle_indicator_click(window):
|
||||
if window:
|
||||
window.toggle()
|
||||
|
||||
|
||||
def hide_control_center(mouse_capture):
|
||||
if mouse_capture:
|
||||
mouse_capture.hide_child_window()
|
||||
def hide_control_center(window):
|
||||
if window:
|
||||
window.hide()
|
||||
|
||||
|
||||
class BluetoothIndicator(Box):
|
||||
@@ -77,16 +72,14 @@ class BluetoothIndicator(Box):
|
||||
(
|
||||
self.bluetooth_window,
|
||||
self.bluetooth_widget,
|
||||
self.bluetooth_mousecapture,
|
||||
) = setup_control_center(
|
||||
self.show_window,
|
||||
"bluetooth",
|
||||
BluetoothConnections,
|
||||
self,
|
||||
pointing_to=self.bt_button,
|
||||
show_back_button=False,
|
||||
)
|
||||
if self.bluetooth_window:
|
||||
self.bluetooth_window._pointing_widget = self.bt_button
|
||||
|
||||
modus_service.connect("bluetooth-changed", self.on_bluetooth_changed)
|
||||
self.bluetooth.connect("changed", self.on_bluetooth_direct_changed)
|
||||
@@ -155,13 +148,13 @@ class BluetoothIndicator(Box):
|
||||
modus_service.bluetooth = bluetooth_state
|
||||
|
||||
def on_bluetooth_clicked(self, *args):
|
||||
handle_indicator_click(self.bluetooth_mousecapture)
|
||||
handle_indicator_click(self.bluetooth_window)
|
||||
|
||||
def close_bluetooth(self, *args):
|
||||
hide_control_center(self.bluetooth_mousecapture)
|
||||
hide_control_center(self.bluetooth_window)
|
||||
|
||||
def hide_controlcenter(self, *args):
|
||||
hide_control_center(self.bluetooth_mousecapture)
|
||||
hide_control_center(self.bluetooth_window)
|
||||
|
||||
|
||||
class NetworkIndicator(Box):
|
||||
@@ -188,12 +181,12 @@ class NetworkIndicator(Box):
|
||||
(
|
||||
self.wifi_window,
|
||||
self.wifi_widget,
|
||||
self.wifi_mousecapture,
|
||||
) = setup_control_center(
|
||||
self.show_window,
|
||||
"wifi",
|
||||
WifiConnections,
|
||||
self,
|
||||
pointing_to=self.network_button,
|
||||
show_back_button=False,
|
||||
)
|
||||
if self.wifi_window:
|
||||
@@ -232,6 +225,7 @@ class NetworkIndicator(Box):
|
||||
def on_network_direct_changed(self, *args):
|
||||
self.update_modus_service_wlan_state()
|
||||
self.update_state()
|
||||
hide_control_center(self.wifi_window)
|
||||
|
||||
def on_network_changed(self, *args):
|
||||
self.update_modus_service_wlan_state()
|
||||
@@ -309,13 +303,19 @@ class NetworkIndicator(Box):
|
||||
self.network_button.set_tooltip_text(tooltip)
|
||||
|
||||
def on_wifi_clicked(self, *args):
|
||||
handle_indicator_click(self.wifi_mousecapture)
|
||||
handle_indicator_click(self.wifi_window)
|
||||
|
||||
def close_wifi(self, *args):
|
||||
hide_control_center(self.wifi_mousecapture)
|
||||
hide_control_center(self.wifi_window)
|
||||
|
||||
def on_wifi_direct_changed(self, *args):
|
||||
hide_control_center(self.wifi_window)
|
||||
|
||||
def on_network_clicked(self, *args):
|
||||
handle_indicator_click(self.wifi_window)
|
||||
|
||||
def hide_controlcenter(self, *args):
|
||||
hide_control_center(self.wifi_mousecapture)
|
||||
hide_control_center(self.wifi_window)
|
||||
|
||||
|
||||
class BatteryIndicator(Box):
|
||||
@@ -323,7 +323,7 @@ class BatteryIndicator(Box):
|
||||
super().__init__(name="battery-indicator", orientation="h", **kwargs)
|
||||
self.show_window = show_window
|
||||
|
||||
self.battery_service = BatteryService()
|
||||
self.battery_service = Battery()
|
||||
|
||||
self.battery_icon = svg_file("battery/battery-100.svg", size=23)
|
||||
|
||||
@@ -344,12 +344,12 @@ class BatteryIndicator(Box):
|
||||
(
|
||||
self.battery_window,
|
||||
self.battery_widget,
|
||||
self.battery_mousecapture,
|
||||
) = setup_control_center(
|
||||
self.show_window,
|
||||
"battery",
|
||||
BatteryControl,
|
||||
self,
|
||||
pointing_to=self.battery_button,
|
||||
show_back_button=False,
|
||||
)
|
||||
if self.battery_window:
|
||||
@@ -367,22 +367,28 @@ class BatteryIndicator(Box):
|
||||
def on_battery_direct_changed(self, *args):
|
||||
self.update_modus_service_battery_state()
|
||||
self.update_state()
|
||||
hide_control_center(self.battery_window)
|
||||
|
||||
def _get_percentage(self):
|
||||
return int(self.battery_service.get_property("Percentage") or 0)
|
||||
return int(self.battery_service.percent)
|
||||
|
||||
def _get_state(self):
|
||||
state_value = self.battery_service.get_property("State")
|
||||
return DeviceState.get(state_value, "UNKNOWN")
|
||||
if self.battery_service.charging:
|
||||
return "CHARGING"
|
||||
if self.battery_service.discharging:
|
||||
return "DISCHARGING"
|
||||
if self.battery_service.charged:
|
||||
return "FULLY_CHARGED"
|
||||
return "UNKNOWN"
|
||||
|
||||
def _is_present(self):
|
||||
return bool(self.battery_service.get_property("IsPresent"))
|
||||
return self.battery_service.available
|
||||
|
||||
def _get_time_to_empty(self):
|
||||
return int(self.battery_service.get_property("TimeToEmpty") or 0)
|
||||
return self.battery_service.time_remaining
|
||||
|
||||
def _get_time_to_full(self):
|
||||
return int(self.battery_service.get_property("TimeToFull") or 0)
|
||||
return self.battery_service.time_to_full
|
||||
|
||||
def _format_time(self, seconds: int) -> str:
|
||||
return format_duration(seconds)
|
||||
@@ -433,10 +439,10 @@ class BatteryIndicator(Box):
|
||||
def update_state(self):
|
||||
if not self._is_present():
|
||||
print("[Battery] No battery detected, hiding indicator")
|
||||
self.set_visible(False)
|
||||
self.hide()
|
||||
return
|
||||
else:
|
||||
self.set_visible(True)
|
||||
self.show()
|
||||
|
||||
percentage = self._get_percentage()
|
||||
state = self._get_state()
|
||||
@@ -451,13 +457,13 @@ class BatteryIndicator(Box):
|
||||
self.battery_label.set_label(percentage_text)
|
||||
|
||||
def on_battery_clicked(self, *args):
|
||||
handle_indicator_click(self.battery_mousecapture)
|
||||
handle_indicator_click(self.battery_window)
|
||||
|
||||
def close_battery(self, *args):
|
||||
hide_control_center(self.battery_mousecapture)
|
||||
hide_control_center(self.battery_window)
|
||||
|
||||
def hide_controlcenter(self, *args):
|
||||
hide_control_center(self.battery_mousecapture)
|
||||
hide_control_center(self.battery_window)
|
||||
|
||||
def destroy(self):
|
||||
"""Clean up resources when the indicator is destroyed."""
|
||||
@@ -466,8 +472,8 @@ class BatteryIndicator(Box):
|
||||
modus_service.disconnect_by_func(self.on_battery_changed)
|
||||
if hasattr(self, "battery_service") and self.battery_service:
|
||||
self.battery_service.disconnect_by_func(self.on_battery_direct_changed)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Error disconnecting battery indicator signals: {e}")
|
||||
|
||||
# Destroy window and widget
|
||||
try:
|
||||
@@ -475,8 +481,6 @@ class BatteryIndicator(Box):
|
||||
self.battery_widget.destroy()
|
||||
if hasattr(self, "battery_window") and self.battery_window:
|
||||
self.battery_window.destroy()
|
||||
if hasattr(self, "battery_mousecapture") and self.battery_mousecapture:
|
||||
self.battery_mousecapture.destroy()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -494,15 +498,13 @@ def add_destroy_to_indicators():
|
||||
self.bluetooth.disconnect_by_func(self.on_bluetooth_direct_changed)
|
||||
self.bluetooth.disconnect_by_func(self.on_device_added)
|
||||
self.bluetooth.disconnect_by_func(self.on_device_removed)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Error disconnecting bluetooth indicator signals: {e}")
|
||||
try:
|
||||
if hasattr(self, "bluetooth_widget") and self.bluetooth_widget:
|
||||
self.bluetooth_widget.destroy()
|
||||
if hasattr(self, "bluetooth_window") and self.bluetooth_window:
|
||||
self.bluetooth_window.destroy()
|
||||
if hasattr(self, "bluetooth_mousecapture") and self.bluetooth_mousecapture:
|
||||
self.bluetooth_mousecapture.destroy()
|
||||
except Exception:
|
||||
pass
|
||||
Box.destroy(self)
|
||||
@@ -539,15 +541,13 @@ def add_destroy_to_indicators():
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Error disconnecting network indicator signals: {e}")
|
||||
try:
|
||||
if hasattr(self, "wifi_widget") and self.wifi_widget:
|
||||
self.wifi_widget.destroy()
|
||||
if hasattr(self, "wifi_window") and self.wifi_window:
|
||||
self.wifi_window.destroy()
|
||||
if hasattr(self, "wifi_mousecapture") and self.wifi_mousecapture:
|
||||
self.wifi_mousecapture.destroy()
|
||||
except Exception:
|
||||
pass
|
||||
Box.destroy(self)
|
||||
|
||||
+11
-20
@@ -8,7 +8,7 @@ from fabric.widgets.wayland import WaylandWindow as Window
|
||||
|
||||
from services.config import on_config_change, get_config_all
|
||||
from services.modus import notification_service
|
||||
from shared.window.mousecapture import MouseCapture
|
||||
|
||||
from utils.roam import modus_service
|
||||
from utils.utils import setup_cursor_hover, svg_file
|
||||
from window.controlcenter.main import ModusControlCenter
|
||||
@@ -78,20 +78,18 @@ class Panel(Window):
|
||||
)
|
||||
setup_cursor_hover(self.search, "pointer")
|
||||
|
||||
self.control_center = MouseCapture(
|
||||
layer="top", child_window=ModusControlCenter()
|
||||
)
|
||||
|
||||
self.control_center_btn = Button(
|
||||
name="panel-button",
|
||||
child=svg_file("misc/control-center.svg", size=22),
|
||||
on_clicked=self.control_center.toggle_mousecapture,
|
||||
)
|
||||
setup_cursor_hover(self.control_center_btn, "pointer")
|
||||
self.control_center.child_window._pointing_widget = self.control_center_btn
|
||||
|
||||
self.notification_center = MouseCapture(
|
||||
layer="overlay", child_window=NotificationCenter()
|
||||
self.control_center = ModusControlCenter(
|
||||
parent=self, pointing_to=self.control_center_btn
|
||||
)
|
||||
self.control_center_btn.connect(
|
||||
"clicked",
|
||||
lambda *_: self.control_center.toggle(),
|
||||
)
|
||||
|
||||
self.notification_icon = svg_file(
|
||||
@@ -104,8 +102,9 @@ class Panel(Window):
|
||||
on_clicked=self.on_notification_icon_clicked,
|
||||
)
|
||||
setup_cursor_hover(self.notification_center_btn, "pointer")
|
||||
self.notification_center.child_window._pointing_widget = (
|
||||
self.notification_center_btn
|
||||
|
||||
self.notification_center = NotificationCenter(
|
||||
parent=self, pointing_to=self.notification_center_btn
|
||||
)
|
||||
|
||||
self.datetime_btn = Button(
|
||||
@@ -174,9 +173,7 @@ class Panel(Window):
|
||||
def on_notification_icon_clicked(self, *args):
|
||||
count = notification_service.count
|
||||
if count > 0:
|
||||
# Only open notification center if there are notifications
|
||||
self.notification_center.toggle_mousecapture()
|
||||
# Do nothing if no notifications
|
||||
self.notification_center.toggle()
|
||||
|
||||
def update_notification_icon(self):
|
||||
count = notification_service.count
|
||||
@@ -198,8 +195,6 @@ class Panel(Window):
|
||||
if config_data is None:
|
||||
config_data = get_config_all()
|
||||
|
||||
print(f"[Panel] Rebuilding layout with config: {config_data}")
|
||||
|
||||
# Update indicator visibility directly
|
||||
battery_visible = config_data.get("battery", True)
|
||||
network_visible = config_data.get("network", True)
|
||||
@@ -258,10 +253,6 @@ class Panel(Window):
|
||||
# Force a layout recalculation
|
||||
self.queue_resize()
|
||||
|
||||
print(
|
||||
f"[Panel] Battery visibility state: {self.battery_indicator.get_visible()}"
|
||||
)
|
||||
|
||||
def _on_config_changed(self, new_config, old_config):
|
||||
print("[Panel] Config changed, checking keys...")
|
||||
keys = {
|
||||
|
||||
+14
-3
@@ -1,7 +1,7 @@
|
||||
import json
|
||||
|
||||
from fabric.hyprland.widgets import get_hyprland_connection
|
||||
from fabric.utils import Gdk
|
||||
from fabric.utils import Gdk, GLib
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.eventbox import EventBox
|
||||
from fabric.widgets.image import Image
|
||||
@@ -198,6 +198,10 @@ class ApplicationSwitcher(Window):
|
||||
name="window-button",
|
||||
style_classes=["active"] if i == self.current_index else None,
|
||||
child=button_content,
|
||||
events=["button-press-event"],
|
||||
)
|
||||
event_box.connect(
|
||||
"button-press-event", lambda w, e, idx=i: self._on_item_clicked(idx)
|
||||
)
|
||||
current_row.add(event_box)
|
||||
|
||||
@@ -311,11 +315,18 @@ class ApplicationSwitcher(Window):
|
||||
address = window.get("address")
|
||||
if address:
|
||||
try:
|
||||
command = f"/dispatch focuswindow address:{address}"
|
||||
self.conn.send_command(command)
|
||||
GLib.spawn_command_line_async(
|
||||
f"hyprctl dispatch 'hl.dsp.focus({{ window = \"address:{address}\" }})'"
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Failed to focus window: {e}")
|
||||
|
||||
def _on_item_clicked(self, index):
|
||||
self.current_index = index
|
||||
self.update_selection()
|
||||
self.activate_selected()
|
||||
self.hide_switcher()
|
||||
|
||||
def grab_keyboard(self):
|
||||
try:
|
||||
display = Gdk.Display.get_default()
|
||||
|
||||
@@ -6,10 +6,9 @@ from fabric.widgets.button import Button
|
||||
from fabric.widgets.entry import Entry
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.scrolledwindow import ScrolledWindow
|
||||
from fabric.widgets.wayland import WaylandWindow as Window
|
||||
|
||||
from services.todo import get_todo_service
|
||||
from shared.window.mousecapture import MouseCapture
|
||||
from shared.window.applet_window import AppletWindow
|
||||
from utils.utils import svg_file
|
||||
|
||||
|
||||
@@ -224,18 +223,20 @@ class TodoItem(Box):
|
||||
self.date_label.set_label(date_text)
|
||||
|
||||
|
||||
class TodoListWidget(Window):
|
||||
"""Main todo list widget window"""
|
||||
class TodoListWidget(AppletWindow):
|
||||
"""Main todo list window"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, parent=None, pointing_to=None, **kwargs):
|
||||
super().__init__(
|
||||
parent=parent,
|
||||
pointing_to=pointing_to,
|
||||
layer="top",
|
||||
title="modus-todo",
|
||||
anchor="top right",
|
||||
margin="2px 10px 0px 0px",
|
||||
exclusivity="auto",
|
||||
keyboard_mode="on-demand",
|
||||
name="todo-list-window",
|
||||
visible=False, # Back to hidden by default
|
||||
name="todo-list-menu",
|
||||
visible=False,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@@ -416,13 +417,8 @@ class TodoListWidget(Window):
|
||||
|
||||
def hide_todo_list(self, *_):
|
||||
"""Hide the todo list"""
|
||||
if hasattr(self, "_mousecapture_parent"):
|
||||
self._mousecapture_parent.toggle_mousecapture()
|
||||
self.set_visible(False)
|
||||
|
||||
def _init_mousecapture(self, mousecapture):
|
||||
"""Initialize mousecapture parent reference"""
|
||||
self._mousecapture_parent = mousecapture
|
||||
self.hide()
|
||||
self.hide()
|
||||
|
||||
def destroy(self):
|
||||
"""Clean up when destroyed"""
|
||||
@@ -430,14 +426,3 @@ class TodoListWidget(Window):
|
||||
get_todo_service().remove_callback(self._on_todo_event)
|
||||
|
||||
super().destroy()
|
||||
|
||||
|
||||
class TodoListCapture(MouseCapture):
|
||||
"""MouseCapture wrapper for the todo list"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
layer="top",
|
||||
child_window=TodoListWidget(),
|
||||
**kwargs,
|
||||
)
|
||||
Reference in new issue
Block a user