From 7ba4787128c104748b228d6aebf7480edbd68d1b Mon Sep 17 00:00:00 2001 From: Pascal Date: Thu, 11 Jun 2026 01:03:09 -0400 Subject: [PATCH 1/2] Fix mouse tick resume after ruler drag --- Free Ruler/RulerController.swift | 6 ++++++ Free Ruler/RulerWindow.swift | 8 ++++++++ FreeRulerTests/RulerCoreTests.swift | 31 +++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/Free Ruler/RulerController.swift b/Free Ruler/RulerController.swift index fc198ca..22f949a 100644 --- a/Free Ruler/RulerController.swift +++ b/Free Ruler/RulerController.swift @@ -118,6 +118,12 @@ class RulerController: NSWindowController, NSWindowDelegate, NotificationObserve } override func mouseUp(with event: NSEvent) { + finishMouseDrag(with: event) + } + + func finishMouseDrag(with event: NSEvent) { + guard mouseIsDraggingRuler else { return } + mouseIsDraggingRuler = false scheduleMouseTickResume() rulerCursorController?.mouseUpInRuler(mouseIsInsideRuler: isMouseInsideRuler(with: event)) diff --git a/Free Ruler/RulerWindow.swift b/Free Ruler/RulerWindow.swift index 9a30405..3cd534c 100644 --- a/Free Ruler/RulerWindow.swift +++ b/Free Ruler/RulerWindow.swift @@ -60,6 +60,10 @@ class RulerWindow: NSPanel { override func mouseDown(with event: NSEvent) { nextResponder?.mouseDown(with: event) super.mouseDown(with: event) + + if !leftMouseButtonIsPressed { + (nextResponder as? RulerController)?.finishMouseDrag(with: event) + } } override func mouseUp(with event: NSEvent) { @@ -67,6 +71,10 @@ class RulerWindow: NSPanel { super.mouseUp(with: event) } + private var leftMouseButtonIsPressed: Bool { + return NSEvent.pressedMouseButtons & 1 == 1 + } + } private func getTitle(for orientation: Orientation) -> String { diff --git a/FreeRulerTests/RulerCoreTests.swift b/FreeRulerTests/RulerCoreTests.swift index 3a64c58..025e5c4 100644 --- a/FreeRulerTests/RulerCoreTests.swift +++ b/FreeRulerTests/RulerCoreTests.swift @@ -289,4 +289,35 @@ final class RulerCoreTests: XCTestCase { XCTAssertTrue(controller.rulerWindow.rule.showMouseTick) } + + func testRulerControllerResumesMouseTicksWhenWindowDragLoopEnds() { + let ruler = Ruler(.horizontal, frame: NSRect(x: 0, y: 0, width: 300, height: Ruler.thickness)) + let controller = RulerController(ruler: ruler) + let otherWindow = RulerWindow(Ruler(.vertical, frame: NSRect(x: 0, y: 0, width: Ruler.thickness, height: 300))) + controller.otherWindow = otherWindow + let mouseDownEvent = NSEvent.mouseEvent( + with: .leftMouseDown, + location: NSPoint(x: 10, y: 10), + modifierFlags: [], + timestamp: 0, + windowNumber: controller.rulerWindow.windowNumber, + context: nil, + eventNumber: 0, + clickCount: 1, + pressure: 1 + )! + + controller.mouseDown(with: mouseDownEvent) + controller.windowDidMove(Notification(name: NSWindow.didMoveNotification, object: controller.rulerWindow)) + RunLoop.current.run(until: Date().addingTimeInterval(0.2)) + + XCTAssertFalse(controller.rulerWindow.rule.showMouseTick) + XCTAssertFalse(otherWindow.rule.showMouseTick) + + controller.finishMouseDrag(with: mouseDownEvent) + RunLoop.current.run(until: Date().addingTimeInterval(0.2)) + + XCTAssertTrue(controller.rulerWindow.rule.showMouseTick) + XCTAssertTrue(otherWindow.rule.showMouseTick) + } } From 325dbda2a3e6388e3bdd2ec674128539bbc1195b Mon Sep 17 00:00:00 2001 From: Pascal Date: Thu, 11 Jun 2026 01:10:19 -0400 Subject: [PATCH 2/2] Fix grouped ruler mouse tick resume --- Free Ruler/RulerController.swift | 7 ++++--- FreeRulerTests/RulerCoreTests.swift | 32 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Free Ruler/RulerController.swift b/Free Ruler/RulerController.swift index 22f949a..fd3a46d 100644 --- a/Free Ruler/RulerController.swift +++ b/Free Ruler/RulerController.swift @@ -16,6 +16,9 @@ class RulerController: NSWindowController, NSWindowDelegate, NotificationObserve private var mouseTickResumeTimer: Timer? private let mouseTickResumeDelay: TimeInterval = 0.15 private var mouseIsDraggingRuler = false + var isLeftMouseButtonPressed = { + return NSEvent.pressedMouseButtons & 1 == 1 + } var preferencesWindowOpen = false { didSet { @@ -89,7 +92,7 @@ class RulerController: NSWindowController, NSWindowDelegate, NotificationObserve func windowDidMove(_ notification: Notification) { rulerWindow.invalidateShadow() - guard !mouseIsDraggingRuler else { return } + guard !mouseIsDraggingRuler && !isLeftMouseButtonPressed() else { return } scheduleMouseTickResume() } @@ -143,8 +146,6 @@ class RulerController: NSWindowController, NSWindowDelegate, NotificationObserve } func enableMouseTicks() { - guard !rulerWindow.rule.showMouseTick || otherWindow?.rule.showMouseTick == false else { return } - rulerWindow.rule.showMouseTick = true otherWindow?.rule.showMouseTick = true appDelegate?.resumeMouseTickUpdates(owner: self) diff --git a/FreeRulerTests/RulerCoreTests.swift b/FreeRulerTests/RulerCoreTests.swift index 025e5c4..f02aa9c 100644 --- a/FreeRulerTests/RulerCoreTests.swift +++ b/FreeRulerTests/RulerCoreTests.swift @@ -320,4 +320,36 @@ final class RulerCoreTests: XCTestCase { XCTAssertTrue(controller.rulerWindow.rule.showMouseTick) XCTAssertTrue(otherWindow.rule.showMouseTick) } + + func testGroupedChildMoveDoesNotResumeMouseTicksDuringDrag() { + let draggedController = RulerController( + ruler: Ruler(.horizontal, frame: NSRect(x: 0, y: 0, width: 300, height: Ruler.thickness)) + ) + let groupedChildController = RulerController( + ruler: Ruler(.vertical, frame: NSRect(x: 0, y: 0, width: Ruler.thickness, height: 300)) + ) + draggedController.otherWindow = groupedChildController.rulerWindow + groupedChildController.otherWindow = draggedController.rulerWindow + groupedChildController.isLeftMouseButtonPressed = { true } + let mouseDownEvent = NSEvent.mouseEvent( + with: .leftMouseDown, + location: NSPoint(x: 10, y: 10), + modifierFlags: [], + timestamp: 0, + windowNumber: draggedController.rulerWindow.windowNumber, + context: nil, + eventNumber: 0, + clickCount: 1, + pressure: 1 + )! + + draggedController.mouseDown(with: mouseDownEvent) + groupedChildController.windowDidMove( + Notification(name: NSWindow.didMoveNotification, object: groupedChildController.rulerWindow) + ) + RunLoop.current.run(until: Date().addingTimeInterval(0.2)) + + XCTAssertFalse(draggedController.rulerWindow.rule.showMouseTick) + XCTAssertFalse(groupedChildController.rulerWindow.rule.showMouseTick) + } }