From 8858c2ba4e59b7226ca3b9cfa6e58d4823796307 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 6 Jul 2024 15:00:52 -0700 Subject: [PATCH] apprt/gtk: convert surface to overlay so we can support the url overlay --- src/apprt/gtk/Surface.zig | 59 ++++++++++++++++++++++++++++++++------- src/apprt/gtk/Tab.zig | 3 +- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 096441229..f6ecce8ec 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -72,7 +72,7 @@ pub const Container = union(enum) { /// element pub fn widget(self: Elem) *c.GtkWidget { return switch (self) { - .surface => |s| @ptrCast(s.gl_area), + .surface => |s| s.primaryWidget(), .split => |s| @ptrCast(@alignCast(s.paned)), }; } @@ -223,9 +223,15 @@ container: Container = .{ .none = {} }, /// The app we're part of app: *App, +/// The overlay, this is the primary widget +overlay: *c.GtkOverlay, + /// Our GTK area gl_area: *c.GtkGLArea, +/// If non-null this is the widget on the overlay that shows the URL. +url_widget: ?*c.GtkWidget = null, + /// Any active cursor we may have cursor: ?*c.GdkCursor = null, @@ -271,15 +277,19 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void { const widget: *c.GtkWidget = c.gtk_gl_area_new(); const gl_area: *c.GtkGLArea = @ptrCast(widget); - // We grab the floating reference to GL area. This lets the - // GL area be moved around i.e. between a split, a tab, etc. + // Create an overlay so we can layer the GL area with other widgets. + const overlay: *c.GtkOverlay = @ptrCast(c.gtk_overlay_new()); + c.gtk_overlay_set_child(@ptrCast(overlay), widget); + + // We grab the floating reference to the primary widget. This allows the + // widget tree to be moved around i.e. between a split, a tab, etc. // without having to be really careful about ordering to // prevent a destroy. // // This is unref'd in the unref() method that's called by the // self.container through Elem.deinit. - _ = c.g_object_ref_sink(@ptrCast(gl_area)); - errdefer c.g_object_unref(@ptrCast(gl_area)); + _ = c.g_object_ref_sink(@ptrCast(overlay)); + errdefer c.g_object_unref(@ptrCast(overlay)); // We want the gl area to expand to fill the parent container. c.gtk_widget_set_hexpand(widget, 1); @@ -381,6 +391,7 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void { self.* = .{ .app = app, .container = .{ .none = {} }, + .overlay = overlay, .gl_area = gl_area, .title_text = null, .core_surface = undefined, @@ -488,7 +499,7 @@ pub fn deinit(self: *Surface) void { // unref removes the long-held reference to the gl_area and kicks off the // deinit/destroy process for this surface. pub fn unref(self: *Surface) void { - c.g_object_unref(self.gl_area); + c.g_object_unref(self.overlay); } pub fn destroy(self: *Surface, alloc: Allocator) void { @@ -496,6 +507,10 @@ pub fn destroy(self: *Surface, alloc: Allocator) void { alloc.destroy(self); } +pub fn primaryWidget(self: *Surface) *c.GtkWidget { + return @ptrCast(@alignCast(self.overlay)); +} + fn render(self: *Surface) !void { try self.core_surface.renderer.drawFrame(self); } @@ -879,10 +894,34 @@ pub fn setMouseVisibility(self: *Surface, visible: bool) void { c.gtk_widget_set_cursor(@ptrCast(self.gl_area), self.app.cursor_none); } -pub fn mouseOverLink(self: *Surface, uri: ?[]const u8) void { - // TODO: GTK - _ = self; - _ = uri; +pub fn mouseOverLink(self: *Surface, uri_: ?[]const u8) void { + const uri = uri_ orelse { + if (self.url_widget) |widget| { + c.gtk_overlay_remove_overlay(@ptrCast(self.overlay), widget); + self.url_widget = null; + } + + return; + }; + + // We need a null-terminated string + const alloc = self.app.core_app.alloc; + const uriZ = alloc.dupeZ(u8, uri) catch return; + defer alloc.free(uriZ); + + // If we have a URL widget already just change the text. + if (self.url_widget) |widget| { + c.gtk_label_set_text(@ptrCast(widget), uriZ.ptr); + return; + } + + // Create the widget + const label = c.gtk_label_new(uriZ.ptr); + c.gtk_widget_set_halign(label, c.GTK_ALIGN_START); + c.gtk_widget_set_valign(label, c.GTK_ALIGN_END); + c.gtk_widget_set_margin_bottom(label, 2); + c.gtk_overlay_add_overlay(@ptrCast(self.overlay), label); + self.url_widget = label; } pub fn clipboardRequest( diff --git a/src/apprt/gtk/Tab.zig b/src/apprt/gtk/Tab.zig index a41c72d93..32b0c7888 100644 --- a/src/apprt/gtk/Tab.zig +++ b/src/apprt/gtk/Tab.zig @@ -104,8 +104,7 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void { self.elem = .{ .surface = surface }; // Add Surface to the Tab - const gl_area_widget = @as(*c.GtkWidget, @ptrCast(surface.gl_area)); - c.gtk_box_append(self.box, gl_area_widget); + c.gtk_box_append(self.box, surface.primaryWidget()); // Add the notebook page (create tab). const parent_page_idx = switch (window.app.config.@"window-new-tab-position") {