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
5 changes: 5 additions & 0 deletions .changeset/6732f3c3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
bump: patch
---

Add reusable NSWindow fade-in/out animation helpers.
33 changes: 33 additions & 0 deletions Sources/cmdcmd/NSWindowAnimations.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import AppKit

extension NSWindow {
func fadeInAndUp(distance: CGFloat = 50, duration: TimeInterval = 0.125, callback: (() -> Void)? = nil) {
let toFrame = frame
let fromFrame = NSRect(x: toFrame.minX, y: toFrame.minY - distance, width: toFrame.width, height: toFrame.height)
setFrame(fromFrame, display: true)
alphaValue = 0
NSAnimationContext.runAnimationGroup { context in
context.duration = duration
context.timingFunction = CAMediaTimingFunction(controlPoints: 0.4, 0, 0.2, 1)
animator().alphaValue = 1
animator().setFrame(toFrame, display: true)
} completionHandler: {
callback?()
}
}

func fadeOutAndDown(distance: CGFloat = 50, duration: TimeInterval = 0.125, callback: (() -> Void)? = nil) {
let fromFrame = frame
let toFrame = NSRect(x: fromFrame.minX, y: fromFrame.minY - distance, width: fromFrame.width, height: fromFrame.height)
setFrame(fromFrame, display: true)
alphaValue = 1
NSAnimationContext.runAnimationGroup { context in
context.duration = duration
context.timingFunction = CAMediaTimingFunction(controlPoints: 0.4, 0, 0.2, 1)
animator().alphaValue = 0
animator().setFrame(toFrame, display: true)
} completionHandler: {
callback?()
}
}
}
30 changes: 26 additions & 4 deletions Sources/cmdcmd/Overlay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ final class Overlay {
let w = window ?? makeWindow(frame: visibleFrame)
window = w
w.setFrame(visibleFrame, display: false)
w.alphaValue = 1
if config.animations {
w.alphaValue = 0
} else {
w.alphaValue = 1
}
let tWindow = CFAbsoluteTimeGetCurrent()
CATransaction.begin()
CATransaction.setDisableActions(true)
Expand All @@ -162,6 +166,9 @@ final class Overlay {
NSApp.activate(ignoringOtherApps: true)
if let v = view { w.makeFirstResponder(v) }
let tFront = CFAbsoluteTimeGetCurrent()
if config.animations {
w.fadeInAndUp(distance: 0, duration: 0.10)
}
animateShowFromFocused(in: w)
let tEnd = CFAbsoluteTimeGetCurrent()
Log.debug(String(format: "render: filter=%.1f window=%.1f(new=%@) installTiles=%.1f orderFront+activate=%.1f animate=%.1f total=%.1f n=%d",
Expand Down Expand Up @@ -483,7 +490,8 @@ private static func windowMostlyOn(displayBounds: CGRect, window: SCWindow) -> B
let toStop = allTiles
for t in toStop { t.suppressFrames = true }
stopActivityTimer()
window?.orderOut(nil)
let w = window
let animate = config.animations && w != nil && w!.alphaValue > 0
visible = false
if activatePrevious, prevFrontPID != 0,
let app = NSRunningApplication(processIdentifier: prevFrontPID) {
Expand All @@ -505,8 +513,22 @@ private static func windowMostlyOn(displayBounds: CGRect, window: SCWindow) -> B
}
isZoomed = false
savedFrames = []
if let root = window?.contentView?.layer {
root.sublayers?.forEach { $0.removeFromSuperlayer() }
let clearLayers = { [weak self] in
if let root = self?.window?.contentView?.layer {
root.sublayers?.forEach { $0.removeFromSuperlayer() }
}
}
if animate, let w {
w.fadeOutAndDown(distance: 0, duration: 0.10) { [weak self] in
guard let self else { return }
if !self.visible {
w.orderOut(nil)
clearLayers()
}
}
} else {
w?.orderOut(nil)
clearLayers()
}
hint.reset()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
Expand Down
Loading