From 63456d28a53bc28cd6e883e4f3695389042a3586 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Sep 2024 21:28:57 -0700 Subject: [PATCH] macos: make sliding logic a bit more extensible --- .../SlideTerminalController.swift | 35 +++++++++++++++++-- .../SlideTerminal/SlideTerminalPosition.swift | 24 +++++++++---- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/macos/Sources/Features/SlideTerminal/SlideTerminalController.swift b/macos/Sources/Features/SlideTerminal/SlideTerminalController.swift index 3db695eab..c117ae0f1 100644 --- a/macos/Sources/Features/SlideTerminal/SlideTerminalController.swift +++ b/macos/Sources/Features/SlideTerminal/SlideTerminalController.swift @@ -4,7 +4,7 @@ import SwiftUI import GhosttyKit /// Controller for the slide-style terminal. -class SlideTerminalController: NSWindowController, TerminalViewDelegate, TerminalViewModel { +class SlideTerminalController: NSWindowController, NSWindowDelegate, TerminalViewDelegate, TerminalViewModel { override var windowNibName: NSNib.Name? { "SlideTerminal" } /// The app instance that this terminal view will represent. @@ -40,6 +40,10 @@ class SlideTerminalController: NSWindowController, TerminalViewDelegate, Termina override func windowDidLoad() { guard let window = self.window else { return } + // The controller is the window delegate so we can detect events such as + // window close so we can animate out. + window.delegate = self + // The slide window is not restorable (yet!). "Yet" because in theory we can // make this restorable, but it isn't currently implemented. window.isRestorable = false @@ -52,7 +56,13 @@ class SlideTerminalController: NSWindowController, TerminalViewDelegate, Termina )) // Animate the window in - slideWindowIn(window: window, from: position) + slideIn() + } + + // MARK: NSWindowDelegate + + func windowDidResignKey(_ notification: Notification) { + slideOut() } //MARK: TerminalViewDelegate @@ -68,7 +78,17 @@ class SlideTerminalController: NSWindowController, TerminalViewDelegate, Termina } } - // MARK: Slide Logic + // MARK: Slide Methods + + func slideIn() { + guard let window = self.window else { return } + slideWindowIn(window: window, from: position) + } + + func slideOut() { + guard let window = self.window else { return } + slideWindowOut(window: window, to: position) + } private func slideWindowIn(window: NSWindow, from position: SlideTerminalPosition) { guard let screen = NSScreen.main else { return } @@ -87,4 +107,13 @@ class SlideTerminalController: NSWindowController, TerminalViewDelegate, Termina position.setFinal(in: window.animator(), on: screen) } } + + private func slideWindowOut(window: NSWindow, to position: SlideTerminalPosition) { + guard let screen = NSScreen.main else { return } + NSAnimationContext.runAnimationGroup { context in + context.duration = 0.3 + context.timingFunction = .init(name: .easeIn) + position.setInitial(in: window.animator(), on: screen) + } + } } diff --git a/macos/Sources/Features/SlideTerminal/SlideTerminalPosition.swift b/macos/Sources/Features/SlideTerminal/SlideTerminalPosition.swift index 89c521a47..3ef7d7dcc 100644 --- a/macos/Sources/Features/SlideTerminal/SlideTerminalPosition.swift +++ b/macos/Sources/Features/SlideTerminal/SlideTerminalPosition.swift @@ -12,9 +12,7 @@ enum SlideTerminalPosition { switch (self) { case .top: window.setFrame(.init( - origin: .init( - x: 0, - y: screen.frame.maxY), + origin: initialOrigin(for: window, on: screen), size: .init( width: screen.frame.width, height: window.frame.height) @@ -31,11 +29,25 @@ enum SlideTerminalPosition { switch (self) { case .top: window.setFrame(.init( - origin: .init( - x: window.frame.origin.x, - y: screen.visibleFrame.maxY - window.frame.height), + origin: finalOrigin(for: window, on: screen), size: window.frame.size ), display: true) } } + + /// The initial point origin for this position. + func initialOrigin(for window: NSWindow, on screen: NSScreen) -> CGPoint { + switch (self) { + case .top: + return .init(x: 0, y: screen.frame.maxY) + } + } + + /// The final point origin for this position. + func finalOrigin(for window: NSWindow, on screen: NSScreen) -> CGPoint { + switch (self) { + case .top: + return .init(x: window.frame.origin.x, y: screen.visibleFrame.maxY - window.frame.height) + } + } }