macos: start work on SlideTerminal, slides in window from top

This commit is contained in:
Mitchell Hashimoto
2024-09-22 14:44:57 -07:00
parent 576453cfde
commit 93b2fe60f8
5 changed files with 158 additions and 1 deletions

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23094" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23094"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="SlideTerminalController" customModule="Ghostty" customModuleProvider="target">
<connections>
<outlet property="window" destination="QvC-M9-y7g" id="JMU-zX-9Ie"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="👻 Ghostty" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="SlideTerminalWindow" customModule="Ghostty" customModuleProvider="target">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
<rect key="screenRect" x="0.0" y="0.0" width="3008" height="1667"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="-2" id="u5f-FR-jJw"/>
</connections>
<point key="canvasLocation" x="132" y="-82"/>
</window>
</objects>
</document>

View File

@@ -0,0 +1,62 @@
import Foundation
import Cocoa
import SwiftUI
import GhosttyKit
/// Controller for the slide-style terminal.
class SlideTerminalController: NSWindowController {
override var windowNibName: NSNib.Name? { "SlideTerminal" }
override func windowDidLoad() {
guard let window = self.window else { return }
// Make the window full width
let screenFrame = NSScreen.main?.frame ?? .zero
window.setFrame(NSRect(
x: 0,
y: 0,
width: screenFrame.size.width,
height: window.frame.size.height
), display: false)
slideWindowIn(window: window)
}
private func slideWindowIn(window: NSWindow) {
guard let screen = NSScreen.main else { return }
// Determine our final position. Our final position is exactly
// pinned against the top menu bar.
let windowFrame = window.frame
let finalY = screen.visibleFrame.maxY - windowFrame.height
// Move our window off screen to the top
window.setFrameOrigin(.init(
x: windowFrame.origin.x,
y: screen.frame.maxY))
// Set the window invisible
window.alphaValue = 0
// Move it to the visible position since animation requires this
window.makeKeyAndOrderFront(nil)
// Run the animation that moves our window into the proper place and makes
// it visible.
NSAnimationContext.runAnimationGroup { context in
context.duration = 0.3
context.timingFunction = .init(name: .easeIn)
let animator = window.animator()
animator.setFrame(.init(
origin: .init(x: windowFrame.origin.x, y: finalY),
size: windowFrame.size
), display: true)
animator.alphaValue = 1
}
}
}
enum SlideTerminalLocation {
case top
}

View File

@@ -0,0 +1,39 @@
import Cocoa
class SlideTerminalWindow: NSWindow {
// Both of these must be true for windows without decorations to be able to
// still become key/main and receive events.
override var canBecomeKey: Bool { return true }
override var canBecomeMain: Bool { return true }
override func awakeFromNib() {
super.awakeFromNib()
// Note: almost all of this stuff can be done in the nib/xib directly
// but I prefer to do it programmatically because the properties we
// care about are less hidden.
// Remove the title completely. This will make the window square. One
// downside is it also hides the cursor indications of resize but the
// window remains resizable.
self.styleMask.remove(.titled)
// We need to set our window level to a high value. In testing, only
// popUpMenu and above do what we want. This gets it above the menu bar
// and lets us render off screen.
self.level = .popUpMenu
self.isMovableByWindowBackground = true
self.isMovable = true
self.collectionBehavior = [
// We want this to be part of every space because it is a singleton.
.canJoinAllSpaces,
// We don't want to be part of command-tilde
.ignoresCycle,
// We never support fullscreen
.fullScreenNone]
}
}