mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: hook up all the bindings so we're ready to handle focus event
This commit is contained in:
@ -32,6 +32,8 @@ typedef const char* (*ghostty_runtime_read_clipboard_cb)(void *);
|
|||||||
typedef void (*ghostty_runtime_write_clipboard_cb)(void *, const char *);
|
typedef void (*ghostty_runtime_write_clipboard_cb)(void *, const char *);
|
||||||
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_close_surface_cb)(void *);
|
typedef void (*ghostty_runtime_close_surface_cb)(void *);
|
||||||
|
typedef void (*ghostty_runtime_focus_next_split_cb)(void *);
|
||||||
|
typedef void (*ghostty_runtime_focus_previous_split_cb)(void *);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void *userdata;
|
void *userdata;
|
||||||
@ -41,6 +43,8 @@ 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_close_surface_cb close_surface_cb;
|
ghostty_runtime_close_surface_cb close_surface_cb;
|
||||||
|
ghostty_runtime_focus_next_split_cb focus_next_split_cb;
|
||||||
|
ghostty_runtime_focus_previous_split_cb focus_previous_split_cb;
|
||||||
} ghostty_runtime_config_s;
|
} ghostty_runtime_config_s;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -58,7 +58,9 @@ extension Ghostty {
|
|||||||
read_clipboard_cb: { userdata in AppState.readClipboard(userdata) },
|
read_clipboard_cb: { userdata in AppState.readClipboard(userdata) },
|
||||||
write_clipboard_cb: { userdata, str in AppState.writeClipboard(userdata, string: str) },
|
write_clipboard_cb: { userdata, str in AppState.writeClipboard(userdata, string: str) },
|
||||||
new_split_cb: { userdata, direction in AppState.newSplit(userdata, direction: ghostty_split_direction_e(UInt32(direction))) },
|
new_split_cb: { userdata, direction in AppState.newSplit(userdata, direction: ghostty_split_direction_e(UInt32(direction))) },
|
||||||
close_surface_cb: { userdata in AppState.closeSurface(userdata) }
|
close_surface_cb: { userdata in AppState.closeSurface(userdata) },
|
||||||
|
focus_next_split_cb: { userdata in AppState.focusSplit(userdata, direction: .next) },
|
||||||
|
focus_previous_split_cb: { userdata in AppState.focusSplit(userdata, direction: .previous) }
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create the ghostty app.
|
// Create the ghostty app.
|
||||||
@ -106,6 +108,17 @@ extension Ghostty {
|
|||||||
NotificationCenter.default.post(name: Notification.ghosttyCloseSurface, object: surface)
|
NotificationCenter.default.post(name: Notification.ghosttyCloseSurface, object: surface)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func focusSplit(_ userdata: UnsafeMutableRawPointer?, direction: SplitFocusDirection) {
|
||||||
|
guard let surface = self.surfaceUserdata(from: userdata) else { return }
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: Notification.ghosttyFocusSplit,
|
||||||
|
object: surface,
|
||||||
|
userInfo: [
|
||||||
|
Notification.SplitDirectionKey: direction,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
static func readClipboard(_ userdata: UnsafeMutableRawPointer?) -> UnsafePointer<CChar>? {
|
static func readClipboard(_ userdata: UnsafeMutableRawPointer?) -> UnsafePointer<CChar>? {
|
||||||
guard let appState = self.appState(fromSurface: userdata) else { return nil }
|
guard let appState = self.appState(fromSurface: userdata) else { return nil }
|
||||||
guard let str = NSPasteboard.general.string(forType: .string) else { return nil }
|
guard let str = NSPasteboard.general.string(forType: .string) else { return nil }
|
||||||
|
@ -123,11 +123,14 @@ extension Ghostty {
|
|||||||
@Binding var requestClose: Bool
|
@Binding var requestClose: Bool
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let pub = NotificationCenter.default.publisher(for: Notification.ghosttyNewSplit, object: leaf.surface)
|
let center = NotificationCenter.default
|
||||||
let pubClose = NotificationCenter.default.publisher(for: Notification.ghosttyCloseSurface, object: leaf.surface)
|
let pub = center.publisher(for: Notification.ghosttyNewSplit, object: leaf.surface)
|
||||||
|
let pubClose = center.publisher(for: Notification.ghosttyCloseSurface, object: leaf.surface)
|
||||||
|
let pubFocus = center.publisher(for: Notification.ghosttyFocusSplit, object: leaf.surface)
|
||||||
SurfaceWrapper(surfaceView: leaf.surface)
|
SurfaceWrapper(surfaceView: leaf.surface)
|
||||||
.onReceive(pub) { onNewSplit(notification: $0) }
|
.onReceive(pub) { onNewSplit(notification: $0) }
|
||||||
.onReceive(pubClose) { _ in requestClose = true }
|
.onReceive(pubClose) { _ in requestClose = true }
|
||||||
|
.onReceive(pubFocus) { onMoveFocus(notification: $0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private func onNewSplit(notification: SwiftUI.Notification) {
|
private func onNewSplit(notification: SwiftUI.Notification) {
|
||||||
@ -162,6 +165,13 @@ extension Ghostty {
|
|||||||
Self.fixFocus(container.bottomRight, previous: node)
|
Self.fixFocus(container.bottomRight, previous: node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func onMoveFocus(notification: SwiftUI.Notification) {
|
||||||
|
// Determine our desired direction
|
||||||
|
guard let directionAny = notification.userInfo?[Notification.SplitDirectionKey] else { return }
|
||||||
|
guard let direction = directionAny as? SplitFocusDirection else { return }
|
||||||
|
print("MOVE FOCUS: \(direction)")
|
||||||
|
}
|
||||||
|
|
||||||
/// There is a bug I can't figure out where when changing the split state, the terminal view
|
/// There is a bug I can't figure out where when changing the split state, the terminal view
|
||||||
/// will lose focus. There has to be some nice SwiftUI-native way to fix this but I can't
|
/// will lose focus. There has to be some nice SwiftUI-native way to fix this but I can't
|
||||||
/// figure it out so we're going to do this hacky thing to bring focus back to the terminal
|
/// figure it out so we're going to do this hacky thing to bring focus back to the terminal
|
||||||
|
@ -1,4 +1,28 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
struct Ghostty {
|
struct Ghostty {
|
||||||
// All the notifications that will be emitted will be put here.
|
// All the notifications that will be emitted will be put here.
|
||||||
struct Notification {}
|
struct Notification {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Surface Notifications
|
||||||
|
|
||||||
|
extension Ghostty {
|
||||||
|
/// An enum that is used for the directions that a split focus event can change.
|
||||||
|
enum SplitFocusDirection {
|
||||||
|
case previous, next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Ghostty.Notification {
|
||||||
|
/// Posted when a new split is requested. The sending object will be the surface that had focus. The
|
||||||
|
/// userdata has one key "direction" with the direction to split to.
|
||||||
|
static let ghosttyNewSplit = Notification.Name("com.mitchellh.ghostty.newSplit")
|
||||||
|
|
||||||
|
/// Close the calling surface.
|
||||||
|
static let ghosttyCloseSurface = Notification.Name("com.mitchellh.ghostty.closeSurface")
|
||||||
|
|
||||||
|
/// Focus previous/next split. Has a SplitFocusDirection in the userinfo.
|
||||||
|
static let ghosttyFocusSplit = Notification.Name("com.mitchellh.ghostty.focusSplit")
|
||||||
|
static let SplitDirectionKey = ghosttyFocusSplit.rawValue
|
||||||
|
}
|
||||||
|
@ -509,18 +509,6 @@ extension Ghostty {
|
|||||||
0x4E: GHOSTTY_KEY_KP_SUBTRACT,
|
0x4E: GHOSTTY_KEY_KP_SUBTRACT,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Surface Notifications
|
|
||||||
|
|
||||||
extension Ghostty.Notification {
|
|
||||||
/// Posted when a new split is requested. The sending object will be the surface that had focus. The
|
|
||||||
/// userdata has one key "direction" with the direction to split to.
|
|
||||||
static let ghosttyNewSplit = Notification.Name("com.mitchellh.ghostty.newSplit")
|
|
||||||
|
|
||||||
/// Close the calling surface.
|
|
||||||
static let ghosttyCloseSurface = Notification.Name("com.mitchellh.ghostty.closeSurface")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Surface Environment Keys
|
// MARK: Surface Environment Keys
|
||||||
|
@ -947,6 +947,18 @@ pub fn keyCallback(
|
|||||||
} else log.warn("runtime doesn't implement newSplit", .{});
|
} else log.warn("runtime doesn't implement newSplit", .{});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.next_split => {
|
||||||
|
if (@hasDecl(apprt.Surface, "gotoNextSplit")) {
|
||||||
|
self.rt_surface.gotoNextSplit();
|
||||||
|
} else log.warn("runtime doesn't implement gotoNextSplit", .{});
|
||||||
|
},
|
||||||
|
|
||||||
|
.previous_split => {
|
||||||
|
if (@hasDecl(apprt.Surface, "gotoPreviousSplit")) {
|
||||||
|
self.rt_surface.gotoPreviousSplit();
|
||||||
|
} else log.warn("runtime doesn't implement gotoPreviousSplit", .{});
|
||||||
|
},
|
||||||
|
|
||||||
.close_surface => {
|
.close_surface => {
|
||||||
if (@hasDecl(apprt.Surface, "closeSurface")) {
|
if (@hasDecl(apprt.Surface, "closeSurface")) {
|
||||||
try self.rt_surface.closeSurface();
|
try self.rt_surface.closeSurface();
|
||||||
|
@ -51,6 +51,10 @@ pub const App = struct {
|
|||||||
|
|
||||||
/// Close the current surface given by this function.
|
/// Close the current surface given by this function.
|
||||||
close_surface: ?*const fn (SurfaceUD) callconv(.C) void = null,
|
close_surface: ?*const fn (SurfaceUD) callconv(.C) void = null,
|
||||||
|
|
||||||
|
/// Focus the previous/next split (if any).
|
||||||
|
focus_next_split: ?*const fn (SurfaceUD) callconv(.C) void = null,
|
||||||
|
focus_previous_split: ?*const fn (SurfaceUD) callconv(.C) void = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
core_app: *CoreApp,
|
core_app: *CoreApp,
|
||||||
@ -166,7 +170,25 @@ pub const Surface = struct {
|
|||||||
|
|
||||||
pub fn closeSurface(self: *const Surface) !void {
|
pub fn closeSurface(self: *const Surface) !void {
|
||||||
const func = self.app.opts.close_surface orelse {
|
const func = self.app.opts.close_surface orelse {
|
||||||
log.info("runtime embedder does not closing a surface", .{});
|
log.info("runtime embedder does not support closing a surface", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
func(self.opts.userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gotoNextSplit(self: *const Surface) void {
|
||||||
|
const func = self.app.opts.focus_next_split orelse {
|
||||||
|
log.info("runtime embedder does not support focus next split", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
func(self.opts.userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gotoPreviousSplit(self: *const Surface) void {
|
||||||
|
const func = self.app.opts.focus_previous_split orelse {
|
||||||
|
log.info("runtime embedder does not support focus previous split", .{});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -311,6 +311,16 @@ pub const Config = struct {
|
|||||||
.{ .key = .d, .mods = .{ .super = true, .shift = true } },
|
.{ .key = .d, .mods = .{ .super = true, .shift = true } },
|
||||||
.{ .new_split = .down },
|
.{ .new_split = .down },
|
||||||
);
|
);
|
||||||
|
try result.keybind.set.put(
|
||||||
|
alloc,
|
||||||
|
.{ .key = .left_bracket, .mods = .{ .super = true } },
|
||||||
|
.{ .previous_split = {} },
|
||||||
|
);
|
||||||
|
try result.keybind.set.put(
|
||||||
|
alloc,
|
||||||
|
.{ .key = .right_bracket, .mods = .{ .super = true } },
|
||||||
|
.{ .next_split = {} },
|
||||||
|
);
|
||||||
{
|
{
|
||||||
// Cmd+N for goto tab N
|
// Cmd+N for goto tab N
|
||||||
const start = @enumToInt(inputpkg.Key.one);
|
const start = @enumToInt(inputpkg.Key.one);
|
||||||
|
@ -184,6 +184,12 @@ pub const Action = union(enum) {
|
|||||||
/// in the direction given.
|
/// in the direction given.
|
||||||
new_split: SplitDirection,
|
new_split: SplitDirection,
|
||||||
|
|
||||||
|
/// Go to the previous split.
|
||||||
|
previous_split: void,
|
||||||
|
|
||||||
|
/// Go to the next split.
|
||||||
|
next_split: void,
|
||||||
|
|
||||||
/// Close the current "surface", whether that is a window, tab, split,
|
/// Close the current "surface", whether that is a window, tab, split,
|
||||||
/// etc. This only closes ONE surface.
|
/// etc. This only closes ONE surface.
|
||||||
close_surface: void,
|
close_surface: void,
|
||||||
|
Reference in New Issue
Block a user