diff --git a/src/apprt/gtk/Paned.zig b/src/apprt/gtk/Paned.zig index 241902be3..a77e599c8 100644 --- a/src/apprt/gtk/Paned.zig +++ b/src/apprt/gtk/Paned.zig @@ -87,11 +87,7 @@ pub fn setParent(self: *Paned, parent: Parent) void { pub fn focusFirstSurfaceInPosition(self: *Paned, position: Position) void { const child = self.childInPosition(position); switch (child) { - .surface => |s| { - const widget = @as(*c.GtkWidget, @ptrCast(s.gl_area)); - s.tab.focus_child = s; - _ = c.gtk_widget_grab_focus(widget); - }, + .surface => |s| s.grabFocus(), .paned => |p| p.focusFirstSurfaceInPosition(position), .none => { log.warn("attempted to focus on first surface, found none", .{}); diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 3969200f1..ef212b115 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -90,6 +90,11 @@ cursor: ?*c.GdkCursor = null, /// Our title label (if there is one). title: Title, +/// Our title. The raw value of the title. This will be kept up to date and +/// .title will be updated if we have focus. +/// TODO: what's a big enough value? +titleText: [4096]u8, + /// The core surface backing this surface core_surface: CoreSurface, @@ -175,6 +180,7 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void { .title = if (opts.title_label) |label| .{ .label = label, } else .{ .none = {} }, + .titleText = undefined, .core_surface = undefined, .font_size = opts.font_size, .parentSurface = opts.parentSurface, @@ -445,24 +451,38 @@ pub fn setSizeLimits(self: *Surface, min: apprt.SurfaceSize, max_: ?apprt.Surfac _ = max_; } -pub fn setTitle(self: *Surface, slice: [:0]const u8) !void { +pub fn grabFocus(self: *Surface) void { + self.updateTitleLabels(); + self.tab.focus_child = self; + const widget = @as(*c.GtkWidget, @ptrCast(self.gl_area)); + _ = c.gtk_widget_grab_focus(widget); +} + +fn updateTitleLabels(self: *Surface) void { switch (self.title) { .none => {}, - .label => |label| { - c.gtk_label_set_text(label, slice.ptr); - - const widget = @as(*c.GtkWidget, @ptrCast(self.gl_area)); - if (c.gtk_widget_is_focus(widget) == 1) { + const slice: []u8 = std.mem.sliceTo(&self.titleText, 0); + // Check whether there's even something in the buffer yet. + // TODO: This check is really bad. + if (slice.len != self.titleText.len) { + c.gtk_label_set_text(label, @as([*c]const u8, @ptrCast(slice))); c.gtk_window_set_title(self.window.window, c.gtk_label_get_text(label)); } }, } +} - // const root = c.gtk_widget_get_root(@ptrCast( - // *c.GtkWidget, - // self.gl_area, - // )); +pub fn setTitle(self: *Surface, slice: [:0]const u8) !void { + // TODO: I'm sure there has to be a better way than this in Zig. + const len = @min(self.titleText.len - 1, slice.len); + @memcpy(self.titleText[0..len], slice[0..]); + self.titleText[len] = 0; + + const widget = @as(*c.GtkWidget, @ptrCast(self.gl_area)); + if (c.gtk_widget_is_focus(widget) == 1) { + self.updateTitleLabels(); + } } pub fn setParent(self: *Surface, parent: Parent) void { @@ -838,6 +858,10 @@ fn gtkMouseDown( if (c.gtk_widget_has_focus(gl_widget) == 0) { self.tab.focus_child = self; _ = c.gtk_widget_grab_focus(gl_widget); + + // If we have siblings, we also update the title, since it means + // another sibling might have updated the title. + if (self.parent != Parent.tab) self.updateTitleLabels(); } self.core_surface.mouseButtonCallback(.press, button, mods) catch |err| { @@ -1267,6 +1291,7 @@ fn gtkInputCommit( fn gtkFocusEnter(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) void { const self = userdataSelf(ud.?); + // Notify our IM context c.gtk_im_context_focus_in(self.im_context); diff --git a/src/apprt/gtk/Tab.zig b/src/apprt/gtk/Tab.zig index bdeda014d..6c38cc6bd 100644 --- a/src/apprt/gtk/Tab.zig +++ b/src/apprt/gtk/Tab.zig @@ -120,9 +120,6 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void { c.gtk_notebook_set_show_tabs(window.notebook, 1); } - // TODO: This needs to happen before we show the page - self.focus_child = surface; - // Set the userdata of the box to point to this tab. c.g_object_set_data(@ptrCast(box_widget), GHOSTTY_TAB, self); @@ -131,7 +128,7 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void { // We need to grab focus after Surface and Tab is added to the window. When // creating a Tab we want to always focus on the widget. - _ = c.gtk_widget_grab_focus(gl_area_widget); + surface.grabFocus(); } /// Allocates and initializes a new Surface, but doesn't add it to the Tab yet. diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index cb51a8696..58355bf5e 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -314,11 +314,7 @@ fn closeSurfaceInPaned(self: *Window, surface: *Surface, paned: *Paned, position } switch (sibling_child) { - .surface => |s| { - s.tab.focus_child = s; - const widget = @as(*c.GtkWidget, @ptrCast(s.gl_area)); - _ = c.gtk_widget_grab_focus(widget); - }, + .surface => |s| s.grabFocus(), .paned => |p| { // Focus on first surface in sibling Paned p.focusFirstSurfaceInPosition(position);