feat: animate the start-screen URL field

Use a scoped 250 ms fade and top-anchored scale transition with animated recent-row reflow. Respect Reduce Motion, preserve the fixed window size, and exercise native focus, draft retention and rapid reversals.
This commit is contained in:
2026-10-19 18:46:00 +02:00
parent 6d653e8875
commit f6485bf221
4 changed files with 54 additions and 10 deletions
+45 -8
View File
@@ -37,6 +37,8 @@ private enum WindowDragCheck {
static var implementations: [(Method, IMP)] = []
static var target: NSWindow?
static var dragDepth = 0
static var urlWindowFrame = NSRect.zero
static let urlDraft = "https://www.youtube.com/watch?v=aqz-KE-bpKQ"
static let names = ["video", "title", "playback padding", "play button", "volume slider",
"start whitespace", "start button", "start artwork", "URL text field",
"queue whitespace", "queue footer", "queue row", "queue button",
@@ -76,6 +78,17 @@ private enum WindowDragCheck {
return nil
}
static func clickStartURL(in window: NSWindow) {
for type in [NSEvent.EventType.leftMouseDown, .leftMouseUp] {
let event = NSEvent.mouseEvent(with: type, location: NSPoint(x: 400, y: 272),
modifierFlags: [], timestamp: ProcessInfo.processInfo.systemUptime,
windowNumber: window.windowNumber, context: nil,
eventNumber: probe * 100 + phase * 2 + (type == .leftMouseDown ? 0 : 1),
clickCount: 1, pressure: type == .leftMouseDown ? 1 : 0)!
NSApp.postEvent(NSEvent(cgEvent: event.cgEvent!)!, atStart: false)
}
}
static func point(in window: NSWindow) -> NSPoint? {
let content = window.contentView!
let w = content.bounds.width
@@ -150,11 +163,13 @@ private enum WindowDragCheck {
model.setVolume(0)
model.setPlayback(playing: false)
window.makeKeyAndOrderFront(nil)
window.makeMain()
NSApp.activate(ignoringOtherApps: true)
}
if phase == 0 {
guard NSApp.isActive, window.isKeyWindow else {
guard NSApp.isActive, window.isKeyWindow, window.isMainWindow else {
window.makeKeyAndOrderFront(nil)
window.makeMain()
NSApp.activate(ignoringOtherApps: true)
return
}
@@ -162,13 +177,8 @@ private enum WindowDragCheck {
switch probe {
case 5: model.closeFile()
case 8:
for type in [NSEvent.EventType.leftMouseDown, .leftMouseUp] {
let event = NSEvent.mouseEvent(with: type, location: NSPoint(x: 400, y: 272),
modifierFlags: [], timestamp: ProcessInfo.processInfo.systemUptime,
windowNumber: window.windowNumber, context: nil, eventNumber: type == .leftMouseDown ? 1 : 2,
clickCount: 1, pressure: type == .leftMouseDown ? 1 : 0)!
NSApp.postEvent(NSEvent(cgEvent: event.cgEvent!)!, atStart: false)
}
urlWindowFrame = window.frame
clickStartURL(in: window)
case 9:
model.open(fixtures.appendingPathComponent("cache.mp4"))
model.showsQueue = true
@@ -205,10 +215,37 @@ private enum WindowDragCheck {
guard drags - previousDrags == expected else {
finish("\(names[probe]): expected \(expected) native drag requests, got \(drags - previousDrags)"); return
}
if probe == 8 {
guard let editor = window.firstResponder as? NSTextView, editor.isFieldEditor else {
finish("Revealing the URL field did not focus its editor"); return
}
editor.insertText(urlDraft, replacementRange: NSRange(location: NSNotFound, length: 0))
clickStartURL(in: window)
phase = 7
return
}
probe += 1
if probe == names.count { finish(); return }
phase = 0
return
} else if probe == 8 && phase == 7 {
guard (window.firstResponder as? NSTextView)?.isFieldEditor != true else {
finish("Collapsing the URL field retained keyboard focus"); return
}
clickStartURL(in: window) // Reverse the collapse before its animation completes.
} else if probe == 8 && phase == 8 {
guard let editor = window.firstResponder as? NSTextView, editor.isFieldEditor else { return }
guard editor.string == urlDraft else { finish("Reopening the URL field lost its draft"); return }
clickStartURL(in: window)
} else if probe == 8 && phase == 9 {
guard nativeView(in: window.contentView!, matching: { ($0 as? NSTextField)?.isEditable == true }) == nil else { return }
guard (window.firstResponder as? NSTextView)?.isFieldEditor != true,
window.frame == urlWindowFrame, drags == previousDrags else {
finish("URL-field transitions retained focus, resized or dragged the start window"); return
}
probe += 1
phase = 0
return
}
phase += 1
}