From 5d5a632a893124bfb55ecdfcaa0b376e1fc451e6 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Sat, 1 Mar 2025 10:02:16 -0600 Subject: [PATCH] gtk: update URLWidget to use zig-gobject Also move URLWidget to a separate file to cut down on the size of Surface.zig. --- src/apprt/gtk/Surface.zig | 101 ++----------------------------- src/apprt/gtk/URLWidget.zig | 115 ++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 95 deletions(-) create mode 100644 src/apprt/gtk/URLWidget.zig diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 34bf0f069..7d471ff75 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -4,6 +4,7 @@ const Surface = @This(); const std = @import("std"); + const adw = @import("adw"); const gtk = @import("gtk"); const gio = @import("gio"); @@ -28,6 +29,7 @@ const Window = @import("Window.zig"); const Menu = @import("menu.zig").Menu; const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig"); const ResizeOverlay = @import("ResizeOverlay.zig"); +const URLWidget = @import("URLWidget.zig"); const inspector = @import("inspector.zig"); const gtk_key = @import("key.zig"); const c = @import("c.zig").c; @@ -219,99 +221,6 @@ pub const Container = union(enum) { } }; -/// Represents the URL hover widgets that show the hovered URL. -/// To explain a bit how this all works since its split across a few places: -/// We create a left/right pair of labels. The left label is shown by default, -/// and the right label is hidden. When the mouse enters the left label, we -/// show the right label. When the mouse leaves the left label, we hide the -/// right label. -/// -/// The hover and styling is done with a combination of GTK event controllers -/// and CSS in style.css. -pub const URLWidget = struct { - left: *c.GtkWidget, - right: *c.GtkWidget, - - pub fn init(surface: *const Surface, str: [:0]const u8) URLWidget { - // Create the left - const left = c.gtk_label_new(str.ptr); - c.gtk_label_set_ellipsize(@ptrCast(left), c.PANGO_ELLIPSIZE_MIDDLE); - c.gtk_widget_add_css_class(@ptrCast(left), "view"); - c.gtk_widget_add_css_class(@ptrCast(left), "url-overlay"); - c.gtk_widget_add_css_class(@ptrCast(left), "left"); - c.gtk_widget_set_halign(left, c.GTK_ALIGN_START); - c.gtk_widget_set_valign(left, c.GTK_ALIGN_END); - - // Create the right - const right = c.gtk_label_new(str.ptr); - c.gtk_label_set_ellipsize(@ptrCast(right), c.PANGO_ELLIPSIZE_MIDDLE); - c.gtk_widget_add_css_class(@ptrCast(right), "hidden"); - c.gtk_widget_add_css_class(@ptrCast(right), "view"); - c.gtk_widget_add_css_class(@ptrCast(right), "url-overlay"); - c.gtk_widget_add_css_class(@ptrCast(right), "right"); - c.gtk_widget_set_halign(right, c.GTK_ALIGN_END); - c.gtk_widget_set_valign(right, c.GTK_ALIGN_END); - - // Setup our mouse hover event for the left - const ec_motion = c.gtk_event_controller_motion_new(); - errdefer c.g_object_unref(ec_motion); - c.gtk_widget_add_controller(@ptrCast(left), ec_motion); - _ = c.g_signal_connect_data( - ec_motion, - "enter", - c.G_CALLBACK(>kLeftEnter), - right, - null, - c.G_CONNECT_DEFAULT, - ); - _ = c.g_signal_connect_data( - ec_motion, - "leave", - c.G_CALLBACK(>kLeftLeave), - right, - null, - c.G_CONNECT_DEFAULT, - ); - - // Show it - c.gtk_overlay_add_overlay(surface.overlay, left); - c.gtk_overlay_add_overlay(surface.overlay, right); - - return .{ - .left = left, - .right = right, - }; - } - - pub fn deinit(self: *URLWidget, overlay: *c.GtkOverlay) void { - c.gtk_overlay_remove_overlay(overlay, @ptrCast(self.left)); - c.gtk_overlay_remove_overlay(overlay, @ptrCast(self.right)); - } - - pub fn setText(self: *const URLWidget, str: [:0]const u8) void { - c.gtk_label_set_text(@ptrCast(self.left), str.ptr); - c.gtk_label_set_text(@ptrCast(self.right), str.ptr); - } - - fn gtkLeftEnter( - _: *c.GtkEventControllerMotion, - _: c.gdouble, - _: c.gdouble, - ud: ?*anyopaque, - ) callconv(.C) void { - const right: *c.GtkWidget = @ptrCast(@alignCast(ud orelse return)); - c.gtk_widget_remove_css_class(@ptrCast(right), "hidden"); - } - - fn gtkLeftLeave( - _: *c.GtkEventControllerMotion, - ud: ?*anyopaque, - ) callconv(.C) void { - const right: *c.GtkWidget = @ptrCast(@alignCast(ud orelse return)); - c.gtk_widget_add_css_class(@ptrCast(right), "hidden"); - } -}; - /// Whether the surface has been realized or not yet. When a surface is /// "realized" it means that the OpenGL context is ready and the core /// surface has been initialized. @@ -1169,7 +1078,8 @@ pub fn setMouseVisibility(self: *Surface, visible: bool) void { pub fn mouseOverLink(self: *Surface, uri_: ?[]const u8) void { const uri = uri_ orelse { if (self.url_widget) |*widget| { - widget.deinit(self.overlay); + // FIXME: when Surface gets converted to zig-gobject + widget.deinit(@ptrCast(@alignCast(self.overlay))); self.url_widget = null; } @@ -1187,7 +1097,8 @@ pub fn mouseOverLink(self: *Surface, uri_: ?[]const u8) void { return; } - self.url_widget = URLWidget.init(self, uriZ); + // FIXME: when Surface gets converted to zig-gobject + self.url_widget = URLWidget.init(@ptrCast(@alignCast(self.overlay)), uriZ); } pub fn supportsClipboard( diff --git a/src/apprt/gtk/URLWidget.zig b/src/apprt/gtk/URLWidget.zig new file mode 100644 index 000000000..d1628aa6e --- /dev/null +++ b/src/apprt/gtk/URLWidget.zig @@ -0,0 +1,115 @@ +//! Represents the URL hover widgets that show the hovered URL. +//! +//! To explain a bit how this all works since its split across a few places: +//! We create a left/right pair of labels. The left label is shown by default, +//! and the right label is hidden. When the mouse enters the left label, we +//! show the right label. When the mouse leaves the left label, we hide the +//! right label. +//! +//! The hover and styling is done with a combination of GTK event controllers +//! and CSS in style.css. +const URLWidget = @This(); + +const gtk = @import("gtk"); + +/// The label that appears on the bottom left. +left: *gtk.Label, + +/// The label that appears on the bottom right. +right: *gtk.Label, + +pub fn init( + /// The overlay that we will attach our labels to. + overlay: *gtk.Overlay, + /// The URL to display. + str: [:0]const u8, +) URLWidget { + // Create the left + const left = left: { + const left = gtk.Label.new(str.ptr); + left.setEllipsize(.middle); + const widget = left.as(gtk.Widget); + widget.addCssClass("view"); + widget.addCssClass("url-overlay"); + widget.addCssClass("left"); + widget.setHalign(.start); + widget.setValign(.end); + break :left left; + }; + + // Create the right + const right = right: { + const right = gtk.Label.new(str.ptr); + right.setEllipsize(.middle); + const widget = right.as(gtk.Widget); + widget.addCssClass("hidden"); + widget.addCssClass("view"); + widget.addCssClass("url-overlay"); + widget.addCssClass("right"); + widget.setHalign(.end); + widget.setValign(.end); + break :right right; + }; + + // Setup our mouse hover event controller for the left label. + const ec_motion = gtk.EventControllerMotion.new(); + errdefer ec_motion.unref(); + + left.as(gtk.Widget).addController(ec_motion.as(gtk.EventController)); + + _ = gtk.EventControllerMotion.signals.enter.connect( + ec_motion, + *gtk.Label, + gtkLeftEnter, + right, + .{}, + ); + _ = gtk.EventControllerMotion.signals.leave.connect( + ec_motion, + *gtk.Label, + gtkLeftLeave, + right, + .{}, + ); + + // Show it + overlay.addOverlay(left.as(gtk.Widget)); + overlay.addOverlay(right.as(gtk.Widget)); + + return .{ + .left = left, + .right = right, + }; +} + +/// Remove our labels from the overlay. +pub fn deinit(self: *URLWidget, overlay: *gtk.Overlay) void { + overlay.removeOverlay(self.left.as(gtk.Widget)); + overlay.removeOverlay(self.right.as(gtk.Widget)); +} + +/// Change the URL that is displayed. +pub fn setText(self: *const URLWidget, str: [:0]const u8) void { + self.left.setText(str.ptr); + self.right.setText(str.ptr); +} + +/// Callback for when the mouse enters the left label. That means that we should +/// show the right label. CSS will handle hiding the left label. +fn gtkLeftEnter( + _: *gtk.EventControllerMotion, + _: f64, + _: f64, + right: *gtk.Label, +) callconv(.C) void { + right.as(gtk.Widget).removeCssClass("hidden"); +} + +/// Callback for when the mouse leaves the left label. That means that we should +/// hide the right label. CSS will handle showing the left label. +fn gtkLeftLeave( + _: *gtk.EventControllerMotion, + right: *gtk.Label, +) callconv(.C) void { + right.as(gtk.Widget).addCssClass("hidden"); +}