macos: support initial window size

This commit is contained in:
Mitchell Hashimoto
2023-09-30 20:47:31 -07:00
parent 8cb96a28c1
commit cc8e1cd936
6 changed files with 88 additions and 3 deletions

View File

@ -291,6 +291,15 @@ typedef enum {
GHOSTTY_BUILD_MODE_RELEASE_SMALL, GHOSTTY_BUILD_MODE_RELEASE_SMALL,
} ghostty_build_mode_e; } ghostty_build_mode_e;
typedef struct {
bool origin;
bool size;
uint32_t x;
uint32_t y;
uint32_t w;
uint32_t h;
} ghostty_rect_s;
// Fully defined types. This MUST be kept in sync with equivalent Zig // Fully defined types. This MUST be kept in sync with equivalent Zig
// structs. To find the Zig struct, grep for this type name. The documentation // structs. To find the Zig struct, grep for this type name. The documentation
// for all of these types is available in the Zig source. // for all of these types is available in the Zig source.
@ -380,6 +389,7 @@ ghostty_surface_t ghostty_surface_new(ghostty_app_t, ghostty_surface_config_s*);
void ghostty_surface_free(ghostty_surface_t); void ghostty_surface_free(ghostty_surface_t);
ghostty_app_t ghostty_surface_app(ghostty_surface_t); ghostty_app_t ghostty_surface_app(ghostty_surface_t);
bool ghostty_surface_transparent(ghostty_surface_t); bool ghostty_surface_transparent(ghostty_surface_t);
ghostty_rect_s ghostty_surface_window_frame(ghostty_surface_t);
void ghostty_surface_refresh(ghostty_surface_t); void ghostty_surface_refresh(ghostty_surface_t);
void ghostty_surface_set_content_scale(ghostty_surface_t, double, double); void ghostty_surface_set_content_scale(ghostty_surface_t, double, double);
void ghostty_surface_set_focus(ghostty_surface_t, bool); void ghostty_surface_set_focus(ghostty_surface_t, bool);

View File

@ -4,6 +4,9 @@ class PrimaryWindowController: NSWindowController, NSWindowDelegate {
// This is used to programmatically control tabs. // This is used to programmatically control tabs.
weak var windowManager: PrimaryWindowManager? weak var windowManager: PrimaryWindowManager?
// This should be set to true once a surface has been initialized once.
var didInitializeFromSurface: Bool = false
// This is required for the "+" button to show up in the tab bar to add a // This is required for the "+" button to show up in the tab bar to add a
// new tab. // new tab.
override func newWindowForTab(_ sender: Any?) { override func newWindowForTab(_ sender: Any?) {

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22154" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21701"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22154"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>

View File

@ -98,6 +98,10 @@ extension Ghostty.Notification {
/// Notification sent to toggle split maximize/unmaximize. /// Notification sent to toggle split maximize/unmaximize.
static let didToggleSplitZoom = Notification.Name("com.mitchellh.ghostty.didToggleSplitZoom") static let didToggleSplitZoom = Notification.Name("com.mitchellh.ghostty.didToggleSplitZoom")
/// Notification
static let didReceiveInitialWindowFrame = Notification.Name("com.mitchellh.ghostty.didReceiveInitialWindowFrame")
static let FrameKey = "com.mitchellh.ghostty.frame"
} }
// Make the input enum hashable. // Make the input enum hashable.

View File

@ -71,6 +71,7 @@ extension Ghostty {
// We use these notifications to determine when the window our surface is // We use these notifications to determine when the window our surface is
// attached to is or is not focused. // attached to is or is not focused.
let pubBecomeFocused = NotificationCenter.default.publisher(for: Notification.didBecomeFocusedSurface, object: surfaceView) let pubBecomeFocused = NotificationCenter.default.publisher(for: Notification.didBecomeFocusedSurface, object: surfaceView)
let pubInitialFrame = NotificationCenter.default.publisher(for: Notification.didReceiveInitialWindowFrame, object: surfaceView)
let pubBecomeKey = NotificationCenter.default.publisher(for: NSWindow.didBecomeKeyNotification) let pubBecomeKey = NotificationCenter.default.publisher(for: NSWindow.didBecomeKeyNotification)
let pubResign = NotificationCenter.default.publisher(for: NSWindow.didResignKeyNotification) let pubResign = NotificationCenter.default.publisher(for: NSWindow.didResignKeyNotification)
@ -100,6 +101,26 @@ extension Ghostty {
surfaceFocus = true surfaceFocus = true
} }
} }
.onReceive(pubInitialFrame) { notification in
// We never set the initial frame if we're a split
guard !isSplit else { return }
// We need a window to set the frame
guard let surfaceWindow = surfaceView.window else { return }
guard let frameAny = notification.userInfo?[Ghostty.Notification.FrameKey] else { return }
guard let frame = frameAny as? NSRect else { return }
// If we have tabs, then do not change the window size
guard let windowControllerRaw = surfaceWindow.windowController else { return }
guard let windowController = windowControllerRaw as? PrimaryWindowController else { return }
guard !windowController.didInitializeFromSurface else { return }
// We have no tabs and we are not a split, so set the initial size of the window.
surfaceWindow.setFrame(frame, display: true)
// Note that we did initialize
windowController.didInitializeFromSurface = true
}
.onAppear() { .onAppear() {
// Welcome to the SwiftUI bug house of horrors. On macOS 12 (at least // Welcome to the SwiftUI bug house of horrors. On macOS 12 (at least
// 12.5.1, didn't test other versions), the order in which the view // 12.5.1, didn't test other versions), the order in which the view
@ -407,6 +428,28 @@ extension Ghostty {
// If we have a blur, set the blur // If we have a blur, set the blur
ghostty_set_window_background_blur(surface, Unmanaged.passUnretained(window).toOpaque()) ghostty_set_window_background_blur(surface, Unmanaged.passUnretained(window).toOpaque())
// Set the window size
let rect = ghostty_surface_window_frame(surface)
if (rect.size || rect.origin) {
var frame = window.frame
if (rect.origin) {
frame.origin.x = Double(rect.x)
frame.origin.y = Double(rect.y)
}
if (rect.size) {
frame.size.width = Double(rect.w)
frame.size.height = Double(rect.h)
}
NotificationCenter.default.post(
name: Notification.didReceiveInitialWindowFrame,
object: self,
userInfo: [
Notification.FrameKey: frame,
]
)
}
} }
override func becomeFirstResponder() -> Bool { override func becomeFirstResponder() -> Bool {

View File

@ -757,6 +757,16 @@ pub const Surface = struct {
pub const CAPI = struct { pub const CAPI = struct {
const global = &@import("../main.zig").state; const global = &@import("../main.zig").state;
/// ghostty_rect_s
const Rect = extern struct {
origin: bool = false,
size: bool = false,
x: u32 = 0,
y: u32 = 0,
w: u32 = 0,
h: u32 = 0,
};
/// Create a new app. /// Create a new app.
export fn ghostty_app_new( export fn ghostty_app_new(
opts: *const apprt.runtime.App.Options, opts: *const apprt.runtime.App.Options,
@ -864,6 +874,21 @@ pub const CAPI = struct {
return surface.app.config.@"background-opacity" < 1.0; return surface.app.config.@"background-opacity" < 1.0;
} }
/// The desired window frame for a new window.
export fn ghostty_surface_window_frame(surface: *Surface) Rect {
const config = surface.app.config;
// If the desired height/width isn't configured, return 0.
if (config.@"window-height" == 0 or config.@"window-width" == 0) return .{};
// Return the desired rect
return .{
.size = true,
.h = @max(config.@"window-height" * surface.core_surface.cell_size.height, 480),
.w = @max(config.@"window-width" * surface.core_surface.cell_size.width, 640),
};
}
/// Tell the surface that it needs to schedule a render /// Tell the surface that it needs to schedule a render
export fn ghostty_surface_refresh(surface: *Surface) void { export fn ghostty_surface_refresh(surface: *Surface) void {
surface.refresh(); surface.refresh();