mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macOS: new window can inherit font size
This is the second part of #281 and adds the same mechanism that was added for tabs in #296 for windows. It works exactly the same way.
This commit is contained in:
@ -234,6 +234,7 @@ typedef enum {
|
|||||||
GHOSTTY_BINDING_COPY_TO_CLIPBOARD,
|
GHOSTTY_BINDING_COPY_TO_CLIPBOARD,
|
||||||
GHOSTTY_BINDING_PASTE_FROM_CLIPBOARD,
|
GHOSTTY_BINDING_PASTE_FROM_CLIPBOARD,
|
||||||
GHOSTTY_BINDING_NEW_TAB,
|
GHOSTTY_BINDING_NEW_TAB,
|
||||||
|
GHOSTTY_BINDING_NEW_WINDOW,
|
||||||
} ghostty_binding_action_e;
|
} ghostty_binding_action_e;
|
||||||
|
|
||||||
// Fully defined types. This MUST be kept in sync with equivalent Zig
|
// Fully defined types. This MUST be kept in sync with equivalent Zig
|
||||||
@ -253,6 +254,7 @@ typedef const char* (*ghostty_runtime_read_clipboard_cb)(void *, ghostty_clipboa
|
|||||||
typedef void (*ghostty_runtime_write_clipboard_cb)(void *, const char *, ghostty_clipboard_e);
|
typedef void (*ghostty_runtime_write_clipboard_cb)(void *, const char *, ghostty_clipboard_e);
|
||||||
typedef void (*ghostty_runtime_new_split_cb)(void *, ghostty_split_direction_e);
|
typedef void (*ghostty_runtime_new_split_cb)(void *, ghostty_split_direction_e);
|
||||||
typedef void (*ghostty_runtime_new_tab_cb)(void *, ghostty_surface_config_s);
|
typedef void (*ghostty_runtime_new_tab_cb)(void *, ghostty_surface_config_s);
|
||||||
|
typedef void (*ghostty_runtime_new_window_cb)(void *, ghostty_surface_config_s);
|
||||||
typedef void (*ghostty_runtime_close_surface_cb)(void *, bool);
|
typedef void (*ghostty_runtime_close_surface_cb)(void *, bool);
|
||||||
typedef void (*ghostty_runtime_focus_split_cb)(void *, ghostty_split_focus_direction_e);
|
typedef void (*ghostty_runtime_focus_split_cb)(void *, ghostty_split_focus_direction_e);
|
||||||
typedef void (*ghostty_runtime_goto_tab_cb)(void *, int32_t);
|
typedef void (*ghostty_runtime_goto_tab_cb)(void *, int32_t);
|
||||||
@ -268,6 +270,7 @@ typedef struct {
|
|||||||
ghostty_runtime_write_clipboard_cb write_clipboard_cb;
|
ghostty_runtime_write_clipboard_cb write_clipboard_cb;
|
||||||
ghostty_runtime_new_split_cb new_split_cb;
|
ghostty_runtime_new_split_cb new_split_cb;
|
||||||
ghostty_runtime_new_tab_cb new_tab_cb;
|
ghostty_runtime_new_tab_cb new_tab_cb;
|
||||||
|
ghostty_runtime_new_window_cb new_window_cb;
|
||||||
ghostty_runtime_close_surface_cb close_surface_cb;
|
ghostty_runtime_close_surface_cb close_surface_cb;
|
||||||
ghostty_runtime_focus_split_cb focus_split_cb;
|
ghostty_runtime_focus_split_cb focus_split_cb;
|
||||||
ghostty_runtime_goto_tab_cb goto_tab_cb;
|
ghostty_runtime_goto_tab_cb goto_tab_cb;
|
||||||
|
@ -77,7 +77,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func newWindow(_ sender: Any?) {
|
@IBAction func newWindow(_ sender: Any?) {
|
||||||
windowManager.addNewWindow()
|
windowManager.newWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func newTab(_ sender: Any?) {
|
@IBAction func newTab(_ sender: Any?) {
|
||||||
|
@ -46,21 +46,32 @@ class PrimaryWindowManager {
|
|||||||
init(ghostty: Ghostty.AppState) {
|
init(ghostty: Ghostty.AppState) {
|
||||||
self.ghostty = ghostty
|
self.ghostty = ghostty
|
||||||
|
|
||||||
// Register self as observer for the NewTab notification that
|
// Register self as observer for the NewTab/NewWindow notifications that
|
||||||
// is triggered via callback from Zig code.
|
// are triggered via callback from Zig code.
|
||||||
NotificationCenter.default.addObserver(
|
let center = NotificationCenter.default;
|
||||||
|
center.addObserver(
|
||||||
self,
|
self,
|
||||||
selector: #selector(onNewTab),
|
selector: #selector(onNewTab),
|
||||||
name: Ghostty.Notification.ghosttyNewTab,
|
name: Ghostty.Notification.ghosttyNewTab,
|
||||||
object: nil)
|
object: nil)
|
||||||
|
center.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(onNewWindow),
|
||||||
|
name: Ghostty.Notification.ghosttyNewWindow,
|
||||||
|
object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
// Clean up the observer.
|
// Clean up the observers.
|
||||||
NotificationCenter.default.removeObserver(
|
let center = NotificationCenter.default;
|
||||||
|
center.removeObserver(
|
||||||
self,
|
self,
|
||||||
name: Ghostty.Notification.ghosttyNewTab,
|
name: Ghostty.Notification.ghosttyNewTab,
|
||||||
object: nil)
|
object: nil)
|
||||||
|
center.removeObserver(
|
||||||
|
self,
|
||||||
|
name: Ghostty.Notification.ghosttyNewWindow,
|
||||||
|
object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add the initial window for the application. This should only be called once from the AppDelegate.
|
/// Add the initial window for the application. This should only be called once from the AppDelegate.
|
||||||
@ -73,12 +84,33 @@ class PrimaryWindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addNewWindow() {
|
func newWindow() {
|
||||||
guard let controller = createWindowController() else { return }
|
if let window = mainWindow as? PrimaryWindow {
|
||||||
|
// If we already have a window, we go through Zig core code, which calls back into Swift.
|
||||||
|
self.triggerNewWindow(withParent: window)
|
||||||
|
} else {
|
||||||
|
self.addNewWindow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func triggerNewWindow(withParent window: PrimaryWindow) {
|
||||||
|
guard let surface = window.focusedSurfaceWrapper.surface else { return }
|
||||||
|
ghostty.newWindow(surface: surface)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addNewWindow(withBaseConfig config: ghostty_surface_config_s? = nil) {
|
||||||
|
guard let controller = createWindowController(withBaseConfig: config) else { return }
|
||||||
guard let newWindow = addManagedWindow(windowController: controller)?.window else { return }
|
guard let newWindow = addManagedWindow(windowController: controller)?.window else { return }
|
||||||
newWindow.makeKeyAndOrderFront(nil)
|
newWindow.makeKeyAndOrderFront(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func onNewWindow(notification: SwiftUI.Notification) {
|
||||||
|
let configAny = notification.userInfo?[Ghostty.Notification.NewWindowKey]
|
||||||
|
let config = configAny as? ghostty_surface_config_s
|
||||||
|
|
||||||
|
self.addNewWindow(withBaseConfig: config)
|
||||||
|
}
|
||||||
|
|
||||||
// triggerNewTab tells the Zig core code to create a new tab, which then calls
|
// triggerNewTab tells the Zig core code to create a new tab, which then calls
|
||||||
// back into Swift code.
|
// back into Swift code.
|
||||||
func triggerNewTab(for window: PrimaryWindow) {
|
func triggerNewTab(for window: PrimaryWindow) {
|
||||||
|
@ -62,6 +62,7 @@ extension Ghostty {
|
|||||||
write_clipboard_cb: { userdata, str, loc in AppState.writeClipboard(userdata, string: str, location: loc) },
|
write_clipboard_cb: { userdata, str, loc in AppState.writeClipboard(userdata, string: str, location: loc) },
|
||||||
new_split_cb: { userdata, direction in AppState.newSplit(userdata, direction: direction) },
|
new_split_cb: { userdata, direction in AppState.newSplit(userdata, direction: direction) },
|
||||||
new_tab_cb: { userdata, surfaceConfig in AppState.newTab(userdata, config: surfaceConfig) },
|
new_tab_cb: { userdata, surfaceConfig in AppState.newTab(userdata, config: surfaceConfig) },
|
||||||
|
new_window_cb: { userdata, surfaceConfig in AppState.newWindow(userdata, config: surfaceConfig) },
|
||||||
close_surface_cb: { userdata, processAlive in AppState.closeSurface(userdata, processAlive: processAlive) },
|
close_surface_cb: { userdata, processAlive in AppState.closeSurface(userdata, processAlive: processAlive) },
|
||||||
focus_split_cb: { userdata, direction in AppState.focusSplit(userdata, direction: direction) },
|
focus_split_cb: { userdata, direction in AppState.focusSplit(userdata, direction: direction) },
|
||||||
goto_tab_cb: { userdata, n in AppState.gotoTab(userdata, n: n) },
|
goto_tab_cb: { userdata, n in AppState.gotoTab(userdata, n: n) },
|
||||||
@ -142,6 +143,10 @@ extension Ghostty {
|
|||||||
ghostty_surface_binding_action(surface, GHOSTTY_BINDING_NEW_TAB, nil)
|
ghostty_surface_binding_action(surface, GHOSTTY_BINDING_NEW_TAB, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newWindow(surface: ghostty_surface_t) {
|
||||||
|
ghostty_surface_binding_action(surface, GHOSTTY_BINDING_NEW_WINDOW, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func split(surface: ghostty_surface_t, direction: ghostty_split_direction_e) {
|
func split(surface: ghostty_surface_t, direction: ghostty_split_direction_e) {
|
||||||
ghostty_surface_split(surface, direction)
|
ghostty_surface_split(surface, direction)
|
||||||
}
|
}
|
||||||
@ -266,13 +271,24 @@ extension Ghostty {
|
|||||||
static func newTab(_ userdata: UnsafeMutableRawPointer?, config: ghostty_surface_config_s) {
|
static func newTab(_ userdata: UnsafeMutableRawPointer?, config: ghostty_surface_config_s) {
|
||||||
guard let surface = self.surfaceUserdata(from: userdata) else { return }
|
guard let surface = self.surfaceUserdata(from: userdata) else { return }
|
||||||
|
|
||||||
var userInfo: [AnyHashable : Any] = [:];
|
|
||||||
userInfo[Notification.NewTabKey] = config;
|
|
||||||
|
|
||||||
NotificationCenter.default.post(
|
NotificationCenter.default.post(
|
||||||
name: Notification.ghosttyNewTab,
|
name: Notification.ghosttyNewTab,
|
||||||
object: surface,
|
object: surface,
|
||||||
userInfo: userInfo
|
userInfo: [
|
||||||
|
Notification.NewTabKey: config
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func newWindow(_ userdata: UnsafeMutableRawPointer?, config: ghostty_surface_config_s) {
|
||||||
|
guard let surface = self.surfaceUserdata(from: userdata) else { return }
|
||||||
|
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: Notification.ghosttyNewWindow,
|
||||||
|
object: surface,
|
||||||
|
userInfo: [
|
||||||
|
Notification.NewWindowKey: config
|
||||||
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,9 +79,13 @@ extension Ghostty.Notification {
|
|||||||
static let ghosttyGotoTab = Notification.Name("com.mitchellh.ghostty.gotoTab")
|
static let ghosttyGotoTab = Notification.Name("com.mitchellh.ghostty.gotoTab")
|
||||||
static let GotoTabKey = ghosttyGotoTab.rawValue
|
static let GotoTabKey = ghosttyGotoTab.rawValue
|
||||||
|
|
||||||
/// New tab. Has base surface config requestesd in userinfo.
|
/// New tab. Has base surface config requested in userinfo.
|
||||||
static let ghosttyNewTab = Notification.Name("com.mitchellh.ghostty.newTab")
|
static let ghosttyNewTab = Notification.Name("com.mitchellh.ghostty.newTab")
|
||||||
static let NewTabKey = ghosttyNewTab.rawValue
|
static let NewTabKey = ghosttyNewTab.rawValue
|
||||||
|
|
||||||
|
/// New window. Has base surface config requested in userinfo.
|
||||||
|
static let ghosttyNewWindow = Notification.Name("com.mitchellh.ghostty.newWindow")
|
||||||
|
static let NewWindowKey = ghosttyNewWindow.rawValue
|
||||||
|
|
||||||
/// Toggle fullscreen of current window
|
/// Toggle fullscreen of current window
|
||||||
static let ghosttyToggleFullscreen = Notification.Name("com.mitchellh.ghostty.toggleFullscreen")
|
static let ghosttyToggleFullscreen = Notification.Name("com.mitchellh.ghostty.toggleFullscreen")
|
||||||
|
@ -2119,11 +2119,15 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !void
|
|||||||
} else log.warn("dev mode was not compiled into this binary", .{}),
|
} else log.warn("dev mode was not compiled into this binary", .{}),
|
||||||
|
|
||||||
.new_window => {
|
.new_window => {
|
||||||
_ = self.app_mailbox.push(.{
|
if (@hasDecl(apprt.Surface, "newWindow")) {
|
||||||
.new_window = .{
|
try self.rt_surface.newWindow();
|
||||||
.parent = self,
|
} else {
|
||||||
},
|
_ = self.app_mailbox.push(.{
|
||||||
}, .{ .instant = {} });
|
.new_window = .{
|
||||||
|
.parent = self,
|
||||||
|
},
|
||||||
|
}, .{ .instant = {} });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.new_tab => {
|
.new_tab => {
|
||||||
|
@ -62,6 +62,9 @@ pub const App = struct {
|
|||||||
/// New tab with options.
|
/// New tab with options.
|
||||||
new_tab: ?*const fn (SurfaceUD, apprt.Surface.Options) callconv(.C) void = null,
|
new_tab: ?*const fn (SurfaceUD, apprt.Surface.Options) callconv(.C) void = null,
|
||||||
|
|
||||||
|
/// New window with options.
|
||||||
|
new_window: ?*const fn (SurfaceUD, apprt.Surface.Options) callconv(.C) void = null,
|
||||||
|
|
||||||
/// Close the current surface given by this function.
|
/// Close the current surface given by this function.
|
||||||
close_surface: ?*const fn (SurfaceUD, bool) callconv(.C) void = null,
|
close_surface: ?*const fn (SurfaceUD, bool) callconv(.C) void = null,
|
||||||
|
|
||||||
@ -615,14 +618,29 @@ pub const Surface = struct {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const options = self.newSurfaceOptions();
|
||||||
|
func(self.opts.userdata, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn newWindow(self: *const Surface) !void {
|
||||||
|
const func = self.app.opts.new_window orelse {
|
||||||
|
log.info("runtime embedder does not support new_window", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = self.newSurfaceOptions();
|
||||||
|
func(self.opts.userdata, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn newSurfaceOptions(self: *const Surface) apprt.Surface.Options {
|
||||||
const font_size: u16 = font_size: {
|
const font_size: u16 = font_size: {
|
||||||
if (!self.app.config.@"window-inherit-font-size") break :font_size 0;
|
if (!self.app.config.@"window-inherit-font-size") break :font_size 0;
|
||||||
break :font_size self.core_surface.font_size.points;
|
break :font_size self.core_surface.font_size.points;
|
||||||
};
|
};
|
||||||
|
|
||||||
func(self.opts.userdata, .{
|
return .{
|
||||||
.font_size = font_size,
|
.font_size = font_size,
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The cursor position from the host directly is in screen coordinates but
|
/// The cursor position from the host directly is in screen coordinates but
|
||||||
@ -855,6 +873,7 @@ pub const CAPI = struct {
|
|||||||
.copy_to_clipboard => .{ .copy_to_clipboard = {} },
|
.copy_to_clipboard => .{ .copy_to_clipboard = {} },
|
||||||
.paste_from_clipboard => .{ .paste_from_clipboard = {} },
|
.paste_from_clipboard => .{ .paste_from_clipboard = {} },
|
||||||
.new_tab => .{ .new_tab = {} },
|
.new_tab => .{ .new_tab = {} },
|
||||||
|
.new_window => .{ .new_window = {} },
|
||||||
};
|
};
|
||||||
|
|
||||||
ptr.core_surface.performBindingAction(action) catch |err| {
|
ptr.core_surface.performBindingAction(action) catch |err| {
|
||||||
|
@ -277,6 +277,7 @@ pub const Key = enum(c_int) {
|
|||||||
copy_to_clipboard,
|
copy_to_clipboard,
|
||||||
paste_from_clipboard,
|
paste_from_clipboard,
|
||||||
new_tab,
|
new_tab,
|
||||||
|
new_window,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Trigger is the associated key state that can trigger an action.
|
/// Trigger is the associated key state that can trigger an action.
|
||||||
|
Reference in New Issue
Block a user