Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Free Ruler/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@ import Sparkle

let env = ProcessInfo.processInfo.environment
let UI_TESTS = env["FREE_RULER_UI_TESTS"] != nil
let UI_TEST_CURSOR_STATE_NAME: String? = {
guard UI_TESTS,
let name = env["FREE_RULER_UI_TEST_CURSOR_STATE_NAME"] else { return nil }

let lastPathComponent = URL(fileURLWithPath: name).lastPathComponent
guard !lastPathComponent.isEmpty, lastPathComponent == name else { return nil }

return name
}()

func writeUITestCursorState(_ value: String) {
guard let name = UI_TEST_CURSOR_STATE_NAME else { return }

let url = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
.appendingPathComponent(name)
try? value.write(to: url, atomically: true, encoding: .utf8)
}
Comment thread
pascalpp marked this conversation as resolved.

private enum HotkeyBezelLocalizationKey: String {
case rulersFloated = "HotkeyBezel.RulersFloated"
Expand Down Expand Up @@ -99,6 +116,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {

if UI_TESTS {
resetStateForUITests()
writeUITestCursorState("none")
}

subscribeToPrefs()
Expand Down
24 changes: 24 additions & 0 deletions Free Ruler/RuleView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,30 @@ class RuleView: NSView {
addTrackingArea(trackingArea!)
}

override func mouseEntered(with event: NSEvent) {
nextResponder?.mouseEntered(with: event)
}

override func mouseExited(with event: NSEvent) {
nextResponder?.mouseExited(with: event)
}

override func mouseDown(with event: NSEvent) {
nextResponder?.mouseDown(with: event)
}

override func mouseUp(with event: NSEvent) {
nextResponder?.mouseUp(with: event)
}

override func mouseMoved(with event: NSEvent) {
nextResponder?.mouseMoved(with: event)
}

override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
return true
}

func drawMouseTick(at mouseLoc: NSPoint) {
// required override
// TODO: is there a better way to do this, maybe via a protocol?
Expand Down
20 changes: 20 additions & 0 deletions Free Ruler/RulerCursorController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ final class RulerCursorController {
case openHand
case closedHand

var uiTestStateValue: String {
switch self {
case .arrow:
return "arrow"
case .crosshair:
return "crosshair"
case .openHand:
return "open-hand"
case .closedHand:
return "closed-hand"
}
}

var nsCursor: NSCursor {
switch self {
case .arrow:
Expand Down Expand Up @@ -83,5 +96,12 @@ final class RulerCursorController {

currentCursor = cursor
applyCursor(cursor)
cursor.writeUITestStateIfNeeded()
}
}

private extension RulerCursorController.CursorStyle {
func writeUITestStateIfNeeded() {
writeUITestCursorState(uiTestStateValue)
}
}
10 changes: 10 additions & 0 deletions Free Ruler/RulerWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ class RulerWindow: NSPanel {
set {}
}

override func mouseDown(with event: NSEvent) {
nextResponder?.mouseDown(with: event)
super.mouseDown(with: event)
}

override func mouseUp(with event: NSEvent) {
nextResponder?.mouseUp(with: event)
super.mouseUp(with: event)
}

}

private func getTitle(for orientation: Orientation) -> String {
Expand Down
53 changes: 53 additions & 0 deletions FreeRulerTests/RulerCoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,59 @@ final class RulerCoreTests: XCTestCase {
])
}

func testRulerCursorControllerTracksMouseOverMouseDownAndMouseOutActions() {
var appliedCursors: [RulerCursorController.CursorStyle] = []
let controller = RulerCursorController { appliedCursors.append($0) }

func assertCursor(
after actionName: String,
_ action: () -> Void,
is expectedCursor: RulerCursorController.CursorStyle,
file: StaticString = #filePath,
line: UInt = #line
) {
action()
XCTAssertEqual(controller.currentCursor, expectedCursor, actionName, file: file, line: line)
XCTAssertEqual(appliedCursors.last, expectedCursor, actionName, file: file, line: line)
}

controller.applicationDidBecomeActive()
XCTAssertEqual(controller.currentCursor, .crosshair)

assertCursor(after: "mouse over ruler", {
controller.mouseEnteredRuler()
}, is: .openHand)

assertCursor(after: "mouse down inside ruler", {
controller.mouseDownInRuler()
}, is: .closedHand)

let appliedCursorCountBeforeMouseOut = appliedCursors.count
controller.mouseExitedRuler()
XCTAssertEqual(controller.currentCursor, .closedHand, "mouse out while dragging")
XCTAssertEqual(appliedCursors.count, appliedCursorCountBeforeMouseOut, "mouse out while dragging")

assertCursor(after: "mouse up outside ruler", {
controller.mouseUpInRuler(mouseIsInsideRuler: false)
}, is: .crosshair)

assertCursor(after: "mouse over ruler again", {
controller.mouseEnteredRuler()
}, is: .openHand)

assertCursor(after: "mouse down inside ruler again", {
controller.mouseDownInRuler()
}, is: .closedHand)

assertCursor(after: "mouse up inside ruler", {
controller.mouseUpInRuler(mouseIsInsideRuler: true)
}, is: .openHand)

assertCursor(after: "mouse out after release", {
controller.mouseExitedRuler()
}, is: .crosshair)
}

func testMouseTickTimerPolicyRunsOnlyWhenRulersAreVisible() {
let policy = MouseTickTimerPolicy(foregroundInterval: 1 / 60, backgroundInterval: 1 / 30)

Expand Down
Loading