Skip to content

Commit

Permalink
Wait for modifier keys to release before moving items
Browse files Browse the repository at this point in the history
As a result of this change, we can remove the command key check in the layout bar.

This commit also simplifies other waiters belonging to the menu bar item manager.
  • Loading branch information
jordanbaird committed Jan 16, 2025
1 parent 14d8a60 commit 56ce5e8
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 39 deletions.
73 changes: 52 additions & 21 deletions Ice/MenuBar/MenuBarItems/MenuBarItemManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -479,26 +479,33 @@ extension MenuBarItemManager {
// MARK: - Async Waiters

extension MenuBarItemManager {
/// Waits asynchronously for the given operation to complete.
///
/// - Parameters:
/// - timeout: Amount of time to wait before throwing an error.
/// - operation: The operation to perform.
private func waitWithTask(timeout: Duration?, operation: @escaping @Sendable () async throws -> Void) async throws {
let task = if let timeout {
Task(timeout: timeout, operation: operation)
} else {
Task(operation: operation)
}
try await task.value
}

/// Waits asynchronously for all menu bar items to stop moving.
///
/// - Parameter timeout: Amount of time to wait before throwing an error.
func waitForItemsToStopMoving(timeout: Duration? = nil) async throws {
let taskBody: @Sendable () async throws -> Void = {
while await self.isMovingItem {
try await waitWithTask(timeout: timeout) { [weak self] in
guard let self else {
return
}
while await isMovingItem {
try Task.checkCancellation()
try await Task.sleep(for: .milliseconds(10))
}
}
let checkTask = if let timeout {
Task(timeout: timeout) {
try await taskBody()
}
} else {
Task {
try await taskBody()
}
}
try await checkTask.value
}

/// Waits asynchronously for the mouse to stop moving.
Expand All @@ -507,10 +514,13 @@ extension MenuBarItemManager {
/// - threshold: A threshold to use to determine whether the mouse has stopped moving.
/// - timeout: Amount of time to wait before throwing an error.
func waitForMouseToStopMoving(threshold: TimeInterval = 0.1, timeout: Duration? = nil) async throws {
let taskBody: @Sendable () async throws -> Void = {
try await waitWithTask(timeout: timeout) { [weak self] in
guard let self else {
return
}
while true {
try Task.checkCancellation()
guard let date = await self.lastMouseMoveStartDate else {
guard let date = await lastMouseMoveStartDate else {
break
}
if Date.now.timeIntervalSince(date) > threshold {
Expand All @@ -519,16 +529,34 @@ extension MenuBarItemManager {
try await Task.sleep(for: .milliseconds(10))
}
}
let checkTask = if let timeout {
Task(timeout: timeout) {
try await taskBody()
}

/// Waits asynchronously until no modifier keys are pressed.
///
/// - Parameter timeout: Amount of time to wait before throwing an error.
func waitForNoModifiersPressed(timeout: Duration? = nil) async throws {
try await waitWithTask(timeout: timeout) {
// Return early if no flags are pressed.
if NSEvent.modifierFlags.isEmpty {
return
}
} else {
Task {
try await taskBody()

var cancellable: AnyCancellable?

await withCheckedContinuation { continuation in
cancellable = Publishers.Merge(
UniversalEventMonitor.publisher(for: .flagsChanged),
RunLoopLocalEventMonitor.publisher(for: .flagsChanged, mode: .eventTracking)
)
.removeDuplicates()
.sink { _ in
if NSEvent.modifierFlags.isEmpty {
cancellable?.cancel()
continuation.resume()
}
}
}
}
try await checkTask.value
}
}

Expand Down Expand Up @@ -1051,6 +1079,9 @@ extension MenuBarItemManager {
}

do {
// Order of these waiters matters, as the modifiers could be released
// while the mouse is still moving.
try await waitForNoModifiersPressed()
try await waitForMouseToStopMoving()
} catch {
throw EventError(code: .couldNotComplete, item: item)
Expand Down
14 changes: 0 additions & 14 deletions Ice/UI/LayoutBar/LayoutBarItemView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,6 @@ final class LayoutBarItemView: NSView {
return alert
}

/// Provides an alert to display when the user is pressing the Command key
/// while moving a menu bar item.
func provideAlertForCommandKeyDown() -> NSAlert {
let alert = NSAlert()
alert.messageText = "Do not hold the Command key while moving a menu bar item."
return alert
}

override func draw(_ dirtyRect: NSRect) {
if !isDraggingPlaceholder {
image?.draw(
Expand Down Expand Up @@ -160,12 +152,6 @@ final class LayoutBarItemView: NSView {
override func mouseDragged(with event: NSEvent) {
super.mouseDragged(with: event)

guard !event.modifierFlags.contains(.command) else {
let alert = provideAlertForCommandKeyDown()
alert.runModal()
return
}

guard isEnabled else {
let alert = provideAlertForDisabledItem()
alert.runModal()
Expand Down
5 changes: 1 addition & 4 deletions Ice/UI/LayoutBar/LayoutBarPaddingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,7 @@ final class LayoutBarPaddingView: NSView {
}
}

guard
sender.draggingSourceOperationMask == .move,
let draggingSource = sender.draggingSource as? LayoutBarItemView
else {
guard let draggingSource = sender.draggingSource as? LayoutBarItemView else {
return false
}

Expand Down

0 comments on commit 56ce5e8

Please sign in to comment.