mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 09:16:11 +03:00
gtk: update URLWidget to use zig-gobject
Also move URLWidget to a separate file to cut down on the size of Surface.zig.
This commit is contained in:
@ -4,6 +4,7 @@
|
|||||||
const Surface = @This();
|
const Surface = @This();
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const adw = @import("adw");
|
const adw = @import("adw");
|
||||||
const gtk = @import("gtk");
|
const gtk = @import("gtk");
|
||||||
const gio = @import("gio");
|
const gio = @import("gio");
|
||||||
@ -28,6 +29,7 @@ const Window = @import("Window.zig");
|
|||||||
const Menu = @import("menu.zig").Menu;
|
const Menu = @import("menu.zig").Menu;
|
||||||
const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig");
|
const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig");
|
||||||
const ResizeOverlay = @import("ResizeOverlay.zig");
|
const ResizeOverlay = @import("ResizeOverlay.zig");
|
||||||
|
const URLWidget = @import("URLWidget.zig");
|
||||||
const inspector = @import("inspector.zig");
|
const inspector = @import("inspector.zig");
|
||||||
const gtk_key = @import("key.zig");
|
const gtk_key = @import("key.zig");
|
||||||
const c = @import("c.zig").c;
|
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
|
/// 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
|
/// "realized" it means that the OpenGL context is ready and the core
|
||||||
/// surface has been initialized.
|
/// 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 {
|
pub fn mouseOverLink(self: *Surface, uri_: ?[]const u8) void {
|
||||||
const uri = uri_ orelse {
|
const uri = uri_ orelse {
|
||||||
if (self.url_widget) |*widget| {
|
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;
|
self.url_widget = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1187,7 +1097,8 @@ pub fn mouseOverLink(self: *Surface, uri_: ?[]const u8) void {
|
|||||||
return;
|
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(
|
pub fn supportsClipboard(
|
||||||
|
115
src/apprt/gtk/URLWidget.zig
Normal file
115
src/apprt/gtk/URLWidget.zig
Normal file
@ -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");
|
||||||
|
}
|
Reference in New Issue
Block a user