diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index 28612c6bd8..38a73853e1 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -270,17 +270,6 @@ pub async fn main() { #[cfg(target_os = "macos")] hypr_intercept::setup_force_quit_handler(); - #[cfg(target_os = "macos")] - { - let handle = app.handle().clone(); - hypr_intercept::set_close_handler(move || { - for (_, window) in handle.webview_windows() { - let _ = window.close(); - } - let _ = handle.set_activation_policy(tauri::ActivationPolicy::Accessory); - }); - } - #[allow(unused_variables)] app.run(move |app, event| match event { #[cfg(target_os = "macos")] diff --git a/crates/intercept/src/lib.rs b/crates/intercept/src/lib.rs index 0ccc3cf962..f32aa8337c 100644 --- a/crates/intercept/src/lib.rs +++ b/crates/intercept/src/lib.rs @@ -1,8 +1,5 @@ #[cfg(target_os = "macos")] -use std::sync::{ - OnceLock, - atomic::{AtomicBool, Ordering}, -}; +use std::sync::atomic::{AtomicBool, Ordering}; #[cfg(target_os = "macos")] use swift_rs::swift; @@ -22,9 +19,6 @@ static HANDLER_INITIALIZED: AtomicBool = AtomicBool::new(false); #[cfg(target_os = "macos")] static FORCE_QUIT: AtomicBool = AtomicBool::new(false); -#[cfg(target_os = "macos")] -static CLOSE_HANDLER: OnceLock> = OnceLock::new(); - #[cfg(target_os = "macos")] pub fn setup_force_quit_handler() { if !HANDLER_INITIALIZED.swap(true, Ordering::SeqCst) { @@ -34,11 +28,6 @@ pub fn setup_force_quit_handler() { } } -#[cfg(target_os = "macos")] -pub fn set_close_handler(f: impl Fn() + Send + Sync + 'static) { - let _ = CLOSE_HANDLER.set(Box::new(f)); -} - #[cfg(target_os = "macos")] pub fn should_force_quit() -> bool { FORCE_QUIT.load(Ordering::SeqCst) @@ -68,11 +57,3 @@ pub fn demo_quit_progress() { pub extern "C" fn rust_set_force_quit() { FORCE_QUIT.store(true, Ordering::SeqCst); } - -#[unsafe(no_mangle)] -#[cfg(target_os = "macos")] -pub extern "C" fn rust_perform_close() { - if let Some(handler) = CLOSE_HANDLER.get() { - handler(); - } -} diff --git a/crates/intercept/swift-lib/src/EntryPoint.swift b/crates/intercept/swift-lib/src/EntryPoint.swift index dcb6ee2ec4..0a324c7f94 100644 --- a/crates/intercept/swift-lib/src/EntryPoint.swift +++ b/crates/intercept/swift-lib/src/EntryPoint.swift @@ -3,9 +3,6 @@ import Cocoa @_silgen_name("rust_set_force_quit") func rustSetForceQuit() -@_silgen_name("rust_perform_close") -func rustPerformClose() - @_cdecl("_setup_force_quit_handler") public func _setupForceQuitHandler() { QuitInterceptor.shared.setup() @@ -21,9 +18,6 @@ public func _showQuitOverlay() { @_cdecl("_demo_quit_progress") public func _demoQuitProgress() { DispatchQueue.main.async { - let interceptor = QuitInterceptor.shared - interceptor.showOverlay() - interceptor.setHoldingAppearance() - interceptor.startProgressAnimation() + QuitInterceptor.shared.showOverlay() } } diff --git a/crates/intercept/swift-lib/src/QuitInterceptor.swift b/crates/intercept/swift-lib/src/QuitInterceptor.swift index 9c024b62f6..11eac54dc8 100644 --- a/crates/intercept/swift-lib/src/QuitInterceptor.swift +++ b/crates/intercept/swift-lib/src/QuitInterceptor.swift @@ -7,18 +7,13 @@ final class QuitInterceptor { case idle case firstPress case awaiting - case holding } var keyMonitor: Any? var panel: NSPanel? - var progressLayer: CALayer? - var pressLabel: NSTextField? - var holdLabel: NSTextField? + var messageLabel: NSTextField? var state: State = .idle var dismissTimer: DispatchWorkItem? - var holdThresholdTimer: DispatchWorkItem? - var quitTimer: DispatchWorkItem? // MARK: - Setup @@ -45,8 +40,6 @@ final class QuitInterceptor { // MARK: - Actions func performQuit() { - resetProgress() - setDefaultAppearance() rustSetForceQuit() hidePanel() DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { @@ -54,11 +47,6 @@ final class QuitInterceptor { } } - func performClose() { - hidePanel() - rustPerformClose() - } - // MARK: - State Machine func onCmdQPressed() { @@ -66,23 +54,14 @@ final class QuitInterceptor { case .idle: state = .firstPress showOverlay() - scheduleTimer(&holdThresholdTimer, delay: QuitOverlay.holdThreshold) { [weak self] in - guard let self, self.state == .firstPress else { return } - self.state = .holding - self.setHoldingAppearance() - self.startProgressAnimation() - self.scheduleTimer(&self.quitTimer, delay: QuitOverlay.holdDuration) { [weak self] in - self?.performQuit() - } - } - case .firstPress, .holding: + case .firstPress: break case .awaiting: state = .idle cancelTimer(&dismissTimer) - performClose() + performQuit() } } @@ -93,18 +72,6 @@ final class QuitInterceptor { case .firstPress: state = .awaiting - cancelTimer(&holdThresholdTimer) - scheduleTimer(&dismissTimer, delay: QuitOverlay.overlayDuration) { [weak self] in - guard let self, self.state == .awaiting else { return } - self.state = .idle - self.hidePanel() - } - - case .holding: - state = .awaiting - cancelTimer(&quitTimer) - resetProgress() - setDefaultAppearance() scheduleTimer(&dismissTimer, delay: QuitOverlay.overlayDuration) { [weak self] in guard let self, self.state == .awaiting else { return } self.state = .idle @@ -113,42 +80,6 @@ final class QuitInterceptor { } } - // MARK: - Label Emphasis - - func setHoldingAppearance() { - pressLabel?.textColor = QuitOverlay.secondaryTextColor - holdLabel?.textColor = QuitOverlay.primaryTextColor - } - - func setDefaultAppearance() { - pressLabel?.textColor = QuitOverlay.primaryTextColor - holdLabel?.textColor = QuitOverlay.secondaryTextColor - } - - // MARK: - Progress Bar - - func startProgressAnimation() { - guard let progressLayer else { return } - - progressLayer.removeAllAnimations() - - let animation = CABasicAnimation(keyPath: "bounds.size.width") - animation.fromValue = 0 - animation.toValue = QuitOverlay.size.width - animation.duration = QuitOverlay.holdDuration - animation.timingFunction = CAMediaTimingFunction(name: .linear) - animation.fillMode = .forwards - animation.isRemovedOnCompletion = false - - progressLayer.add(animation, forKey: "progress") - } - - func resetProgress() { - guard let progressLayer else { return } - progressLayer.removeAllAnimations() - progressLayer.frame.size.width = 0 - } - // MARK: - Timer Helpers func scheduleTimer( diff --git a/crates/intercept/swift-lib/src/QuitOverlayConfig.swift b/crates/intercept/swift-lib/src/QuitOverlayConfig.swift index 7cc05ddc7f..a7fab0ea5a 100644 --- a/crates/intercept/swift-lib/src/QuitOverlayConfig.swift +++ b/crates/intercept/swift-lib/src/QuitOverlayConfig.swift @@ -1,22 +1,15 @@ import Cocoa enum QuitOverlay { - static let size = NSSize(width: 340, height: 96) + static let size = NSSize(width: 360, height: 84) static let cornerRadius: CGFloat = 12 static let verticalOffsetRatio: CGFloat = 0.15 static let backgroundColor = NSColor(white: 0.12, alpha: 0.88) - static let pressText = "Press ⌘Q to Close" - static let holdText = "Hold ⌘Q to Quit" + static let messageText = "Press ⌘Q again to Quit" static let font = NSFont.systemFont(ofSize: 22, weight: .medium) static let primaryTextColor = NSColor.white - static let secondaryTextColor = NSColor(white: 1.0, alpha: 0.5) static let animationDuration: TimeInterval = 0.15 - static let holdThreshold: TimeInterval = 0.2 - static let holdDuration: TimeInterval = 1.0 static let overlayDuration: TimeInterval = 1.5 - - static let progressBarHeight: CGFloat = 4 - static let progressBarColor = NSColor(white: 1.0, alpha: 0.6) } diff --git a/crates/intercept/swift-lib/src/QuitOverlayPanel.swift b/crates/intercept/swift-lib/src/QuitOverlayPanel.swift index 6e59805d1c..8df59dfe72 100644 --- a/crates/intercept/swift-lib/src/QuitOverlayPanel.swift +++ b/crates/intercept/swift-lib/src/QuitOverlayPanel.swift @@ -43,51 +43,17 @@ extension QuitInterceptor { container.layer?.cornerRadius = QuitOverlay.cornerRadius container.layer?.masksToBounds = true - let pressLabel = makeLabel(QuitOverlay.pressText, color: QuitOverlay.primaryTextColor) - let holdLabel = makeLabel(QuitOverlay.holdText, color: QuitOverlay.secondaryTextColor) - self.pressLabel = pressLabel - self.holdLabel = holdLabel - - let prefixDelta = - NSAttributedString( - string: "Press ", attributes: [.font: QuitOverlay.font] - ).size().width - - NSAttributedString( - string: "Hold ", attributes: [.font: QuitOverlay.font] - ).size().width - - let spacing: CGFloat = 10 - let totalHeight = pressLabel.frame.height + spacing + holdLabel.frame.height - let topY = (size.height + totalHeight) / 2 - pressLabel.frame.height - let pressX = (size.width - pressLabel.frame.width) / 2 - - pressLabel.frame = NSRect( - x: pressX, - y: topY, - width: pressLabel.frame.width, - height: pressLabel.frame.height + let messageLabel = makeLabel(QuitOverlay.messageText, color: QuitOverlay.primaryTextColor) + self.messageLabel = messageLabel + + messageLabel.frame = NSRect( + x: (size.width - messageLabel.frame.width) / 2, + y: (size.height - messageLabel.frame.height) / 2, + width: messageLabel.frame.width, + height: messageLabel.frame.height ) - holdLabel.frame = NSRect( - x: pressX + prefixDelta, - y: topY - spacing - holdLabel.frame.height, - width: holdLabel.frame.width, - height: holdLabel.frame.height - ) - - container.addSubview(pressLabel) - container.addSubview(holdLabel) - let progress = CALayer() - progress.anchorPoint = CGPoint(x: 0, y: 0) - progress.frame = NSRect( - x: 0, - y: 0, - width: 0, - height: QuitOverlay.progressBarHeight - ) - progress.backgroundColor = QuitOverlay.progressBarColor.cgColor - container.layer?.addSublayer(progress) - progressLayer = progress + container.addSubview(messageLabel) return container } diff --git a/plugins/tray/src/menu_items/tray_quit.rs b/plugins/tray/src/menu_items/tray_quit.rs index ce69f383d4..0246ded18b 100644 --- a/plugins/tray/src/menu_items/tray_quit.rs +++ b/plugins/tray/src/menu_items/tray_quit.rs @@ -11,7 +11,7 @@ impl MenuItemHandler for TrayQuit { const ID: &'static str = "hypr_tray_quit"; fn build(app: &AppHandle) -> Result> { - let item = MenuItem::with_id(app, Self::ID, "Quit Completely", true, Some("cmd+shift+q"))?; + let item = MenuItem::with_id(app, Self::ID, "Quit", true, Some("cmd+q"))?; Ok(MenuItemKind::MenuItem(item)) }