Merge branch 'macos' of https://github.com/tr1xem/Modus into macos

This commit is contained in:
tr1x_em committed 2025-07-31 21:27:21 +05:30
commit d1ce3ff003
5 files changed
+112 -81

No files matched your search

+66 -61
View File
@@ -1,16 +1,13 @@
from gi.repository import Gtk, Gdk, GLib
from services.network import NetworkService
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.image import Image
from fabric.widgets.label import Label
from fabric.widgets.scrolledwindow import ScrolledWindow
from gi.repository import Gdk, GLib, Gtk
from services.network import NetworkService
# FIX: Make sure the network only scan when the pull to refresh started
# TODO: Show visual Feeadback and animation like bluetooth
from fabric.widgets.eventbox import EventBox
class WifiNetworkSlot(CenterBox):
@@ -23,16 +20,14 @@ class WifiNetworkSlot(CenterBox):
self.ssid = network_data.get("ssid", "Unknown")
self.bssid = network_data.get("bssid", "")
self.strength = network_data.get("strength", 0)
self.icon_name = network_data.get(
"icon-name", "network-wireless-signal-none-symbolic"
)
self.icon_name = network_data.get("icon-name", "network-wireless-signal-none-symbolic")
# The network service puts the same active-ap reference in all networks
# We need to check if THIS specific network's BSSID matches the active AP's BSSID
self.is_connected = False
if self.wifi_service and self.wifi_service._ap:
active_bssid = self.wifi_service._ap.get_bssid()
self.is_connected = active_bssid == self.bssid
self.is_connected = (active_bssid == self.bssid)
# Initialize styles based on connection state
self.styles = [
@@ -64,7 +59,7 @@ class WifiNetworkSlot(CenterBox):
is_currently_connected = False
if self.wifi_service and self.wifi_service._ap:
active_bssid = self.wifi_service._ap.get_bssid()
is_currently_connected = active_bssid == self.bssid
is_currently_connected = (active_bssid == self.bssid)
if is_currently_connected:
# Disconnect from network
@@ -86,7 +81,7 @@ class WifiNetworkSlot(CenterBox):
is_currently_connected = False
if self.wifi_service and self.wifi_service._ap:
active_bssid = self.wifi_service._ap.get_bssid()
is_currently_connected = active_bssid == self.bssid
is_currently_connected = (active_bssid == self.bssid)
self.is_connected = is_currently_connected
self.styles = [
@@ -108,6 +103,7 @@ class WifiConnections(Box):
self.parent = parent
self.network_service = NetworkService()
self.wifi_service = None
self.is_scanning = False # Track scanning state
# Wait for network service to be ready
self.network_service.connect("device-ready", self.on_network_ready)
@@ -118,6 +114,7 @@ class WifiConnections(Box):
label="↓ Pull to scan for networks",
h_align="center",
visible=False,
style="color: #fff; font-size: 12px; padding: 5px;"
)
self.title = Box(
@@ -138,7 +135,7 @@ class WifiConnections(Box):
self.device_box = Box(
spacing=2,
orientation="vertical",
children=[self.refresh_indicator, self.available_networks],
children=[self.refresh_indicator, self.available_networks]
)
# Create scrolled window with pull-to-refresh
@@ -173,32 +170,33 @@ class WifiConnections(Box):
self.toggle_button.set_active(self.wifi_service.enabled)
self.toggle_button.connect(
"notify::active",
lambda *_: setattr(
self.wifi_service, "enabled", self.toggle_button.get_active()
),
lambda *_: setattr(self.wifi_service, 'enabled', self.toggle_button.get_active()),
)
# Connect to WiFi service signals
self.wifi_service.connect("notify::enabled", self.on_wifi_enabled_changed)
self.wifi_service.connect("scanning", self.on_scanning_changed)
self.wifi_service.connect("notify::scanning", self.on_scanning_changed)
self.wifi_service.connect("changed", self.update_networks)
# Also connect to access point changes to update connection states
self.wifi_service.connect(
"notify::access-points", self.refresh_network_states
)
self.wifi_service.connect("notify::access-points", self.refresh_network_states)
# Initial network update
self.update_networks()
# Initialize scanning state
self.wifi_service.notify("scanning")
def on_wifi_enabled_changed(self, *_):
"""Handle WiFi enabled state changes"""
if self.wifi_service:
self.toggle_button.set_active(self.wifi_service.enabled)
def on_scanning_changed(self, service, is_scanning):
def on_scanning_changed(self, *_):
"""Handle scanning state changes"""
print(f"WiFi scanning state changed: {is_scanning}") # Debug output
self.update_scan_indicator(is_scanning)
if self.wifi_service:
is_scanning = self.wifi_service.scanning
self.is_scanning = is_scanning # Update our tracking state
self.update_scan_label()
def update_networks(self, *_):
"""Update the list of available networks"""
@@ -222,7 +220,7 @@ class WifiConnections(Box):
def refresh_network_states(self, *_):
"""Refresh connection states for all network slots"""
for child in self.available_networks.get_children():
if hasattr(child, "on_changed"):
if hasattr(child, 'on_changed'):
child.on_changed()
def setup_pull_to_refresh(self):
@@ -239,7 +237,7 @@ class WifiConnections(Box):
self.bounce_timeout_id = None
self.bounce_frame = 0
self.bounce_duration = 60 # Total frames for animation
self.bounce_amplitude = 15 # Maximum bounce height in pixels
self.bounce_amplitude = 30 # Maximum bounce height in pixels (increased for visibility)
# Connect to scroll events
self.scrolled_window.connect("scroll-event", self.on_scroll_event)
@@ -249,10 +247,10 @@ class WifiConnections(Box):
# Enable events
self.scrolled_window.set_events(
Gdk.EventMask.SCROLL_MASK
| Gdk.EventMask.BUTTON_PRESS_MASK
| Gdk.EventMask.BUTTON_RELEASE_MASK
| Gdk.EventMask.POINTER_MOTION_MASK
Gdk.EventMask.SCROLL_MASK |
Gdk.EventMask.BUTTON_PRESS_MASK |
Gdk.EventMask.BUTTON_RELEASE_MASK |
Gdk.EventMask.POINTER_MOTION_MASK
)
def on_scroll_event(self, widget, event):
@@ -260,12 +258,11 @@ class WifiConnections(Box):
# Only handle pull-to-refresh when at the top
if self.vadjustment.get_value() <= 0:
if event.direction == Gdk.ScrollDirection.UP:
# Scrolling up at the top - start scan
print("WiFi scroll scan triggered") # Debug output
if self.wifi_service:
# Scrolling up at the top - start scan (only if not already scanning)
if self.wifi_service and not self.is_scanning:
self.wifi_service.scan()
# Trigger smooth bounce animation for scroll
self.start_bounce_animation()
# Trigger smooth bounce animation for scroll
self.start_bounce_animation()
return True # Consume the event
return False # Let normal scrolling continue
@@ -281,12 +278,11 @@ class WifiConnections(Box):
if self.is_pulling:
pull_distance = event.y - self.pull_start_y
if pull_distance > self.pull_threshold:
# Trigger scan
print("WiFi pull-to-refresh scan triggered") # Debug output
if self.wifi_service:
# Start scan (only if not already scanning)
if self.wifi_service and not self.is_scanning:
self.wifi_service.scan()
# Trigger elastic animation for pull gesture (most satisfying)
self.start_elastic_animation()
# Trigger elastic animation for pull gesture (most satisfying)
self.start_elastic_animation()
else:
# Stop any ongoing animation
self.stop_bounce_animation()
@@ -304,49 +300,54 @@ class WifiConnections(Box):
# Show refresh indicator when pulling down
self.refresh_indicator.set_visible(True)
if pull_distance >= self.pull_threshold:
self.refresh_indicator.set_label("↑ Release to scan")
if self.is_scanning:
self.refresh_indicator.set_label("↑ Already scanning...")
else:
self.refresh_indicator.set_label("↑ Release to scan")
self.refresh_indicator.add_style_class("ready-to-refresh")
else:
self.refresh_indicator.set_label("↓ Pull to scan for networks")
if self.is_scanning:
self.refresh_indicator.set_label("↓ Scanning in progress...")
else:
self.refresh_indicator.set_label("↓ Pull to scan for networks")
self.refresh_indicator.remove_style_class("ready-to-refresh")
else:
self.refresh_indicator.set_visible(False)
return False
def update_scan_indicator(self, is_scanning):
def update_scan_label(self):
"""Update scanning state appearance"""
print(f"WiFi update_scan_indicator called: {is_scanning}") # Debug output
if is_scanning:
if self.wifi_service and self.wifi_service.scanning:
# Show scanning feedback in refresh indicator
self.refresh_indicator.set_label("Scanning for networks...")
self.refresh_indicator.set_visible(True)
self.refresh_indicator.add_style_class("scanning")
print("WiFi scanning indicator shown") # Debug output
self.refresh_indicator.show_all()
else:
# Hide scanning feedback
if not self.is_pulling:
# Hide scanning feedback only if not animating and not pulling
if not self.is_pulling and not self.bounce_timeout_id:
self.refresh_indicator.set_visible(False)
self.refresh_indicator.remove_style_class("scanning")
print("WiFi scanning indicator hidden") # Debug output
def ease_out_bounce(self, t):
"""Smooth bounce easing function (0 to 1)"""
if t < 1 / 2.75:
import math
if t < 1/2.75:
return 7.5625 * t * t
elif t < 2 / 2.75:
t -= 1.5 / 2.75
elif t < 2/2.75:
t -= 1.5/2.75
return 7.5625 * t * t + 0.75
elif t < 2.5 / 2.75:
t -= 2.25 / 2.75
elif t < 2.5/2.75:
t -= 2.25/2.75
return 7.5625 * t * t + 0.9375
else:
t -= 2.625 / 2.75
t -= 2.625/2.75
return 7.5625 * t * t + 0.984375
def ease_out_elastic(self, t):
"""Elastic easing function for smoother bounce"""
import math
if t == 0 or t == 1:
return t
@@ -356,7 +357,6 @@ class WifiConnections(Box):
def start_bounce_animation(self):
"""Start smooth bounce animation for the refresh indicator"""
print("WiFi bounce animation started") # Debug output
if self.bounce_timeout_id:
GLib.source_remove(self.bounce_timeout_id)
@@ -370,6 +370,9 @@ class WifiConnections(Box):
# Animation finished
self.refresh_indicator.set_margin_top(0)
self.bounce_timeout_id = None
# Hide indicator after animation if not scanning and not pulling
if not (self.wifi_service and self.wifi_service.scanning) and not self.is_pulling:
self.refresh_indicator.set_visible(False)
return False
# Calculate progress (0 to 1)
@@ -387,13 +390,12 @@ class WifiConnections(Box):
def start_elastic_animation(self):
"""Start elastic animation for more dramatic effect"""
print("WiFi elastic animation started") # Debug output
if self.bounce_timeout_id:
GLib.source_remove(self.bounce_timeout_id)
self.bounce_frame = 0
self.bounce_duration = 80 # Longer for elastic effect
self.bounce_amplitude = 20 # Higher amplitude
self.bounce_amplitude = 40 # Higher amplitude (increased for visibility)
self.bounce_timeout_id = GLib.timeout_add(16, self.animate_elastic)
def animate_elastic(self):
@@ -402,6 +404,9 @@ class WifiConnections(Box):
# Animation finished
self.refresh_indicator.set_margin_top(0)
self.bounce_timeout_id = None
# Hide indicator after animation if not scanning and not pulling
if not (self.wifi_service and self.wifi_service.scanning) and not self.is_pulling:
self.refresh_indicator.set_visible(False)
return False
# Calculate progress (0 to 1)
@@ -414,6 +419,7 @@ class WifiConnections(Box):
bounce_offset = int(self.bounce_amplitude * (1 - eased_progress))
self.refresh_indicator.set_margin_top(max(0, bounce_offset))
self.bounce_frame += 1
return True
@@ -426,5 +432,4 @@ class WifiConnections(Box):
def on_destroy(self, widget):
"""Cleanup animations when widget is destroyed"""
self.stop_bounce_animation()
self.stop_bounce_animation()
+9 -3
View File
@@ -367,6 +367,12 @@ class MenuBar(Box):
self.dropdown_system.global_menu_button_help,
]
def show_system_dropdown(self, _):
"""Show the system dropdown menu"""
self.dropdown_system.menu_button_dropdown.toggle_mousecapture()
def show_system_dropdown(self, imac_button):
self.dropdown_system.menu_button_dropdown.child_window.set_pointing_to(
imac_button
)
mouse_capture = self.dropdown_system.menu_button_dropdown
if mouse_capture.is_visible():
mouse_capture.set_child_window_visible(False)
else:
mouse_capture.set_child_window_visible(True)
-3
View File
@@ -17,12 +17,9 @@ from modules.panel.components.indicators import (
)
from modules.panel.components.menubar import MenuBar
from modules.panel.components.recording_indicator import RecordingIndicator
<<<<<<< HEAD
=======
from modules.controlcenter.main import ModusControlCenter
from widgets.mousecapture import MouseCapture
>>>>>>> 9d83d960ef466f38bc3bc426eb89534c26b0fe04
# Apply enhanced system tray icon handling
apply_enhanced_system_tray()
+37 -9
View File
@@ -31,6 +31,8 @@ class Wifi(Service):
self._device: NM.DeviceWifi = device
self._ap: NM.AccessPoint | None = None
self._ap_signal: int | None = None
self._scan_cancellable: Gio.Cancellable | None = None
self._is_scanning: bool = False
super().__init__(**kwargs)
self._client.connect(
@@ -107,18 +109,44 @@ class Wifi(Service):
def scan(self):
"""Start scanning for WiFi networks and emit scanning signal"""
if self._device:
self.emit("scanning", True) # Emit signal that scanning has started
if self._device and not self._is_scanning:
self._is_scanning = True
self._scan_cancellable = Gio.Cancellable()
self.notify("scanning") # Notify property change
self._device.request_scan_async(
None,
lambda device, result: [
device.request_scan_finish(result),
self.emit(
"scanning", False
), # Emit signal that scanning has stopped
],
self._scan_cancellable,
lambda device, result: self._scan_finished(device, result),
)
def _scan_finished(self, device, result):
"""Handle scan completion"""
try:
device.request_scan_finish(result)
except Exception as e:
logger.error(f"WiFi scan error: {e}")
finally:
self._is_scanning = False
self._scan_cancellable = None
self.notify("scanning") # Notify property change
def toggle_scan(self):
"""Toggle WiFi scanning on/off"""
if self._is_scanning:
# Stop current scan
if self._scan_cancellable:
self._scan_cancellable.cancel()
self._scan_cancellable = None
self._is_scanning = False
self.notify("scanning") # Notify property change
else:
# Start new scan
self.scan()
@Property(bool, "readable", default_value=False)
def scanning(self) -> bool:
"""Check if currently scanning"""
return self._is_scanning
def is_active_ap(self, name) -> bool:
return self._ap.get_bssid() == name if self._ap else False
-5
View File
@@ -82,11 +82,6 @@
background: radial-gradient(alpha(#aaa, 0.2) 100%, transparent, transparent);
}
/* .button.active { */
/* background: radial-gradient(alpha(#aaa, 0.2) 100%, transparent, transparent); */
/* } */
/* EnvSH Button */
#modus-button label {
font-size: 17px;