apprt/gtk: convert surface to overlay so we can support the url overlay

This commit is contained in:
Mitchell Hashimoto
2024-07-06 15:00:52 -07:00
parent 9344676960
commit 8858c2ba4e
2 changed files with 50 additions and 12 deletions

View File

@ -72,7 +72,7 @@ pub const Container = union(enum) {
/// element /// element
pub fn widget(self: Elem) *c.GtkWidget { pub fn widget(self: Elem) *c.GtkWidget {
return switch (self) { return switch (self) {
.surface => |s| @ptrCast(s.gl_area), .surface => |s| s.primaryWidget(),
.split => |s| @ptrCast(@alignCast(s.paned)), .split => |s| @ptrCast(@alignCast(s.paned)),
}; };
} }
@ -223,9 +223,15 @@ container: Container = .{ .none = {} },
/// The app we're part of /// The app we're part of
app: *App, app: *App,
/// The overlay, this is the primary widget
overlay: *c.GtkOverlay,
/// Our GTK area /// Our GTK area
gl_area: *c.GtkGLArea, 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 /// Any active cursor we may have
cursor: ?*c.GdkCursor = null, 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 widget: *c.GtkWidget = c.gtk_gl_area_new();
const gl_area: *c.GtkGLArea = @ptrCast(widget); const gl_area: *c.GtkGLArea = @ptrCast(widget);
// We grab the floating reference to GL area. This lets the // Create an overlay so we can layer the GL area with other widgets.
// GL area be moved around i.e. between a split, a tab, etc. 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 // without having to be really careful about ordering to
// prevent a destroy. // prevent a destroy.
// //
// This is unref'd in the unref() method that's called by the // This is unref'd in the unref() method that's called by the
// self.container through Elem.deinit. // self.container through Elem.deinit.
_ = c.g_object_ref_sink(@ptrCast(gl_area)); _ = c.g_object_ref_sink(@ptrCast(overlay));
errdefer c.g_object_unref(@ptrCast(gl_area)); errdefer c.g_object_unref(@ptrCast(overlay));
// We want the gl area to expand to fill the parent container. // We want the gl area to expand to fill the parent container.
c.gtk_widget_set_hexpand(widget, 1); c.gtk_widget_set_hexpand(widget, 1);
@ -381,6 +391,7 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void {
self.* = .{ self.* = .{
.app = app, .app = app,
.container = .{ .none = {} }, .container = .{ .none = {} },
.overlay = overlay,
.gl_area = gl_area, .gl_area = gl_area,
.title_text = null, .title_text = null,
.core_surface = undefined, .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 // unref removes the long-held reference to the gl_area and kicks off the
// deinit/destroy process for this surface. // deinit/destroy process for this surface.
pub fn unref(self: *Surface) void { 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 { pub fn destroy(self: *Surface, alloc: Allocator) void {
@ -496,6 +507,10 @@ pub fn destroy(self: *Surface, alloc: Allocator) void {
alloc.destroy(self); alloc.destroy(self);
} }
pub fn primaryWidget(self: *Surface) *c.GtkWidget {
return @ptrCast(@alignCast(self.overlay));
}
fn render(self: *Surface) !void { fn render(self: *Surface) !void {
try self.core_surface.renderer.drawFrame(self); 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); c.gtk_widget_set_cursor(@ptrCast(self.gl_area), self.app.cursor_none);
} }
pub fn mouseOverLink(self: *Surface, uri: ?[]const u8) void { pub fn mouseOverLink(self: *Surface, uri_: ?[]const u8) void {
// TODO: GTK const uri = uri_ orelse {
_ = self; if (self.url_widget) |widget| {
_ = uri; 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( pub fn clipboardRequest(

View File

@ -104,8 +104,7 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void {
self.elem = .{ .surface = surface }; self.elem = .{ .surface = surface };
// Add Surface to the Tab // Add Surface to the Tab
const gl_area_widget = @as(*c.GtkWidget, @ptrCast(surface.gl_area)); c.gtk_box_append(self.box, surface.primaryWidget());
c.gtk_box_append(self.box, gl_area_widget);
// Add the notebook page (create tab). // Add the notebook page (create tab).
const parent_page_idx = switch (window.app.config.@"window-new-tab-position") { const parent_page_idx = switch (window.app.config.@"window-new-tab-position") {