mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-25 13:16:11 +03:00
apprt/gtk-ng: bind template callbacks so we can connect signals in blp (#8035)
This creates a helper so that we can call [`gtk_widget_class_bind_template_callback_full`](https://docs.gtk.org/gtk4/class_method.Widget.bind_template_callback_full.html) and register signal handlers directly in our Blueprint file. This gets rid of a LOT of boilerplate! A draft, since there are TODOs: - [x] Add comptime verification of the `func` param - [x] Convert more blueprint files
This commit is contained in:
@ -114,6 +114,34 @@ pub fn Common(
|
||||
);
|
||||
}
|
||||
}).bindTemplateChildPrivate else {};
|
||||
|
||||
/// Bind a function pointer to a template callback symbol.
|
||||
pub fn bindTemplateCallback(
|
||||
class: *Self.Class,
|
||||
comptime name: [:0]const u8,
|
||||
comptime func: anytype,
|
||||
) void {
|
||||
{
|
||||
const ptr_ti = @typeInfo(@TypeOf(func));
|
||||
if (ptr_ti != .pointer) {
|
||||
@compileError("bound function must be a pointer type");
|
||||
}
|
||||
if (ptr_ti.pointer.size != .one) {
|
||||
@compileError("bound function must be a pointer to a function");
|
||||
}
|
||||
|
||||
const func_ti = @typeInfo(ptr_ti.pointer.child);
|
||||
if (func_ti != .@"fn") {
|
||||
@compileError("bound function must be a function pointer");
|
||||
}
|
||||
}
|
||||
|
||||
gtk.Widget.Class.bindTemplateCallbackFull(
|
||||
class.as(gtk.Widget.Class),
|
||||
name,
|
||||
@ptrCast(func),
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -163,40 +163,6 @@ pub const ClipboardConfirmationDialog = extern struct {
|
||||
fn init(self: *Self, _: *Class) callconv(.C) void {
|
||||
gtk.Widget.initTemplate(self.as(gtk.Widget));
|
||||
|
||||
const priv = self.private();
|
||||
|
||||
// Signals
|
||||
_ = gtk.Button.signals.clicked.connect(
|
||||
priv.reveal_button,
|
||||
*Self,
|
||||
revealButtonClicked,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.Button.signals.clicked.connect(
|
||||
priv.hide_button,
|
||||
*Self,
|
||||
hideButtonClicked,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
// Some property signals
|
||||
_ = gobject.Object.signals.notify.connect(
|
||||
self,
|
||||
?*anyopaque,
|
||||
&propBlur,
|
||||
null,
|
||||
.{ .detail = "blur" },
|
||||
);
|
||||
_ = gobject.Object.signals.notify.connect(
|
||||
self,
|
||||
?*anyopaque,
|
||||
&propRequest,
|
||||
null,
|
||||
.{ .detail = "request" },
|
||||
);
|
||||
|
||||
// Trigger initial values
|
||||
self.propBlur(undefined, null);
|
||||
self.propRequest(undefined, null);
|
||||
@ -374,6 +340,12 @@ pub const ClipboardConfirmationDialog = extern struct {
|
||||
class.bindTemplateChildPrivate("remember_choice", .{});
|
||||
}
|
||||
|
||||
// Template Callbacks
|
||||
class.bindTemplateCallback("reveal_clicked", &revealButtonClicked);
|
||||
class.bindTemplateCallback("hide_clicked", &hideButtonClicked);
|
||||
class.bindTemplateCallback("notify_blur", &propBlur);
|
||||
class.bindTemplateCallback("notify_request", &propRequest);
|
||||
|
||||
// Properties
|
||||
gobject.ext.registerProperties(class, &.{
|
||||
properties.blur.impl,
|
||||
@ -394,5 +366,6 @@ pub const ClipboardConfirmationDialog = extern struct {
|
||||
|
||||
pub const as = C.Class.as;
|
||||
pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate;
|
||||
pub const bindTemplateCallback = C.Class.bindTemplateCallback;
|
||||
};
|
||||
};
|
||||
|
@ -245,10 +245,6 @@ pub const Surface = extern struct {
|
||||
/// focus events.
|
||||
focused: bool = true,
|
||||
|
||||
/// The overlay we use for things such as the URL hover label
|
||||
/// or resize box. Bound from the template.
|
||||
overlay: *gtk.Overlay,
|
||||
|
||||
/// The GLAarea that renders the actual surface. This is a binding
|
||||
/// to the template so it doesn't have to be unrefed manually.
|
||||
gl_area: *gtk.GLArea,
|
||||
@ -256,18 +252,10 @@ pub const Surface = extern struct {
|
||||
/// The labels for the left/right sides of the URL hover tooltip.
|
||||
url_left: *gtk.Label,
|
||||
url_right: *gtk.Label,
|
||||
url_ec_motion: *gtk.EventControllerMotion,
|
||||
|
||||
/// The resize overlay
|
||||
resize_overlay: *ResizeOverlay,
|
||||
|
||||
// Event controllers
|
||||
ec_focus: *gtk.EventControllerFocus,
|
||||
ec_key: *gtk.EventControllerKey,
|
||||
ec_motion: *gtk.EventControllerMotion,
|
||||
ec_scroll: *gtk.EventControllerScroll,
|
||||
gesture_click: *gtk.GestureClick,
|
||||
|
||||
/// The apprt Surface.
|
||||
rt_surface: ApprtSurface = undefined,
|
||||
|
||||
@ -288,7 +276,7 @@ pub const Surface = extern struct {
|
||||
|
||||
/// Various input method state. All related to key input.
|
||||
in_keyevent: IMKeyEvent = .false,
|
||||
im_context: ?*gtk.IMMulticontext = null,
|
||||
im_context: *gtk.IMMulticontext,
|
||||
im_composing: bool = false,
|
||||
im_buf: [128]u8 = undefined,
|
||||
im_len: u7 = 0,
|
||||
@ -368,13 +356,13 @@ pub const Surface = extern struct {
|
||||
// The block below is all related to input method handling. See the function
|
||||
// comment for some high level details and then the comments within
|
||||
// the block for more specifics.
|
||||
if (priv.im_context) |im_context| {
|
||||
{
|
||||
// This can trigger an input method so we need to notify the im context
|
||||
// where the cursor is so it can render the dropdowns in the correct
|
||||
// place.
|
||||
if (priv.core_surface) |surface| {
|
||||
const ime_point = surface.imePoint();
|
||||
im_context.as(gtk.IMContext).setCursorLocation(&.{
|
||||
priv.im_context.as(gtk.IMContext).setCursorLocation(&.{
|
||||
.f_x = @intFromFloat(ime_point.x),
|
||||
.f_y = @intFromFloat(ime_point.y),
|
||||
.f_width = 1,
|
||||
@ -415,7 +403,7 @@ pub const Surface = extern struct {
|
||||
// triggered despite being technically consumed. At the time of
|
||||
// writing, both Kitty and Alacritty have the same behavior. I
|
||||
// know of no way to fix this.
|
||||
const im_handled = im_context.as(gtk.IMContext).filterKeypress(event) != 0;
|
||||
const im_handled = priv.im_context.as(gtk.IMContext).filterKeypress(event) != 0;
|
||||
// log.warn("GTKIM: im_handled={} im_len={} im_composing={}", .{
|
||||
// im_handled,
|
||||
// self.im_len,
|
||||
@ -546,10 +534,7 @@ pub const Surface = extern struct {
|
||||
// because there is other IME state that we want to preserve,
|
||||
// such as quotation mark ordering for Chinese input.
|
||||
if (priv.im_composing) {
|
||||
if (priv.im_context) |im_context| {
|
||||
im_context.as(gtk.IMContext).reset();
|
||||
}
|
||||
|
||||
priv.im_context.as(gtk.IMContext).reset();
|
||||
surface.preeditCallback(null) catch {};
|
||||
}
|
||||
|
||||
@ -803,238 +788,30 @@ pub const Surface = extern struct {
|
||||
priv.config = app.getConfig();
|
||||
}
|
||||
|
||||
// Setup our event controllers to get input events
|
||||
_ = gtk.EventControllerKey.signals.key_pressed.connect(
|
||||
priv.ec_key,
|
||||
*Self,
|
||||
ecKeyPressed,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.EventControllerKey.signals.key_released.connect(
|
||||
priv.ec_key,
|
||||
*Self,
|
||||
ecKeyReleased,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
// Focus controller will tell us about focus enter/exit events
|
||||
_ = gtk.EventControllerFocus.signals.enter.connect(
|
||||
priv.ec_focus,
|
||||
*Self,
|
||||
ecFocusEnter,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.EventControllerFocus.signals.leave.connect(
|
||||
priv.ec_focus,
|
||||
*Self,
|
||||
ecFocusLeave,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
// Clicks
|
||||
_ = gtk.GestureClick.signals.pressed.connect(
|
||||
priv.gesture_click,
|
||||
*Self,
|
||||
gcMouseDown,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.GestureClick.signals.released.connect(
|
||||
priv.gesture_click,
|
||||
*Self,
|
||||
gcMouseUp,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
// Mouse movement
|
||||
_ = gtk.EventControllerMotion.signals.motion.connect(
|
||||
priv.ec_motion,
|
||||
*Self,
|
||||
ecMouseMotion,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.EventControllerMotion.signals.leave.connect(
|
||||
priv.ec_motion,
|
||||
*Self,
|
||||
ecMouseLeave,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
// Scroll
|
||||
_ = gtk.EventControllerScroll.signals.scroll.connect(
|
||||
priv.ec_scroll,
|
||||
*Self,
|
||||
ecMouseScroll,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.EventControllerScroll.signals.scroll_begin.connect(
|
||||
priv.ec_scroll,
|
||||
*Self,
|
||||
ecMouseScrollPrecisionBegin,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.EventControllerScroll.signals.scroll_end.connect(
|
||||
priv.ec_scroll,
|
||||
*Self,
|
||||
ecMouseScrollPrecisionEnd,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
// Setup our input method state
|
||||
const im_context = gtk.IMMulticontext.new();
|
||||
priv.im_context = im_context;
|
||||
priv.in_keyevent = .false;
|
||||
priv.im_composing = false;
|
||||
priv.im_len = 0;
|
||||
_ = gtk.IMContext.signals.preedit_start.connect(
|
||||
im_context,
|
||||
*Self,
|
||||
imPreeditStart,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.IMContext.signals.preedit_changed.connect(
|
||||
im_context,
|
||||
*Self,
|
||||
imPreeditChanged,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.IMContext.signals.preedit_end.connect(
|
||||
im_context,
|
||||
*Self,
|
||||
imPreeditEnd,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.IMContext.signals.commit.connect(
|
||||
im_context,
|
||||
*Self,
|
||||
imCommit,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
// Initialize our GLArea. We could do a lot of this in
|
||||
// the Blueprint file but I think its cleaner to separate
|
||||
// the "UI" part of the blueprint file from the internal logic/config
|
||||
// part.
|
||||
// Initialize our GLArea. We only set the values we can't set
|
||||
// in our blueprint file.
|
||||
const gl_area = priv.gl_area;
|
||||
gl_area.setRequiredVersion(
|
||||
renderer.OpenGL.MIN_VERSION_MAJOR,
|
||||
renderer.OpenGL.MIN_VERSION_MINOR,
|
||||
);
|
||||
gl_area.setHasStencilBuffer(0);
|
||||
gl_area.setHasDepthBuffer(0);
|
||||
gl_area.setUseEs(0);
|
||||
gl_area.as(gtk.Widget).setCursorFromName("text");
|
||||
_ = gtk.Widget.signals.realize.connect(
|
||||
gl_area,
|
||||
*Self,
|
||||
glareaRealize,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.Widget.signals.unrealize.connect(
|
||||
gl_area,
|
||||
*Self,
|
||||
glareaUnrealize,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.GLArea.signals.render.connect(
|
||||
gl_area,
|
||||
*Self,
|
||||
glareaRender,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.GLArea.signals.resize.connect(
|
||||
gl_area,
|
||||
*Self,
|
||||
glareaResize,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
// Some property signals
|
||||
_ = gobject.Object.signals.notify.connect(
|
||||
self,
|
||||
?*anyopaque,
|
||||
&propConfig,
|
||||
null,
|
||||
.{ .detail = "config" },
|
||||
);
|
||||
_ = gobject.Object.signals.notify.connect(
|
||||
self,
|
||||
?*anyopaque,
|
||||
&propMouseHoverUrl,
|
||||
null,
|
||||
.{ .detail = "mouse-hover-url" },
|
||||
);
|
||||
_ = gobject.Object.signals.notify.connect(
|
||||
self,
|
||||
?*anyopaque,
|
||||
&propMouseHidden,
|
||||
null,
|
||||
.{ .detail = "mouse-hidden" },
|
||||
);
|
||||
_ = gobject.Object.signals.notify.connect(
|
||||
self,
|
||||
?*anyopaque,
|
||||
&propMouseShape,
|
||||
null,
|
||||
.{ .detail = "mouse-shape" },
|
||||
);
|
||||
|
||||
// Some other initialization steps
|
||||
self.initUrlOverlay();
|
||||
|
||||
// Initialize our config
|
||||
self.propConfig(undefined, null);
|
||||
}
|
||||
|
||||
fn initUrlOverlay(self: *Self) void {
|
||||
const priv = self.private();
|
||||
|
||||
// Setup a motion controller to handle moving the label
|
||||
// to avoid the mouse.
|
||||
_ = gtk.EventControllerMotion.signals.enter.connect(
|
||||
priv.url_ec_motion,
|
||||
*Self,
|
||||
ecUrlMouseEnter,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = gtk.EventControllerMotion.signals.leave.connect(
|
||||
priv.url_ec_motion,
|
||||
*Self,
|
||||
ecUrlMouseLeave,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
}
|
||||
|
||||
fn dispose(self: *Self) callconv(.C) void {
|
||||
const priv = self.private();
|
||||
if (priv.config) |v| {
|
||||
v.unref();
|
||||
priv.config = null;
|
||||
}
|
||||
if (priv.im_context) |v| {
|
||||
v.unref();
|
||||
priv.im_context = null;
|
||||
}
|
||||
|
||||
gtk.Widget.disposeTemplate(
|
||||
self.as(gtk.Widget),
|
||||
@ -1260,22 +1037,14 @@ pub const Surface = extern struct {
|
||||
fn ecFocusEnter(_: *gtk.EventControllerFocus, self: *Self) callconv(.c) void {
|
||||
const priv = self.private();
|
||||
priv.focused = true;
|
||||
|
||||
if (priv.im_context) |im_context| {
|
||||
im_context.as(gtk.IMContext).focusIn();
|
||||
}
|
||||
|
||||
priv.im_context.as(gtk.IMContext).focusIn();
|
||||
_ = glib.idleAddOnce(idleFocus, self.ref());
|
||||
}
|
||||
|
||||
fn ecFocusLeave(_: *gtk.EventControllerFocus, self: *Self) callconv(.c) void {
|
||||
const priv = self.private();
|
||||
priv.focused = false;
|
||||
|
||||
if (priv.im_context) |im_context| {
|
||||
im_context.as(gtk.IMContext).focusOut();
|
||||
}
|
||||
|
||||
priv.im_context.as(gtk.IMContext).focusOut();
|
||||
_ = glib.idleAddOnce(idleFocus, self.ref());
|
||||
}
|
||||
|
||||
@ -1647,9 +1416,8 @@ pub const Surface = extern struct {
|
||||
// Setup our input method. We do this here because this will
|
||||
// create a strong reference back to ourself and we want to be
|
||||
// able to release that in unrealize.
|
||||
if (self.private().im_context) |im_context| {
|
||||
im_context.as(gtk.IMContext).setClientWidget(self.as(gtk.Widget));
|
||||
}
|
||||
const priv = self.private();
|
||||
priv.im_context.as(gtk.IMContext).setClientWidget(self.as(gtk.Widget));
|
||||
}
|
||||
|
||||
fn glareaUnrealize(
|
||||
@ -1683,9 +1451,7 @@ pub const Surface = extern struct {
|
||||
}
|
||||
|
||||
// Unset our input method
|
||||
if (priv.im_context) |im_context| {
|
||||
im_context.as(gtk.IMContext).setClientWidget(null);
|
||||
}
|
||||
priv.im_context.as(gtk.IMContext).setClientWidget(null);
|
||||
}
|
||||
|
||||
fn glareaRender(
|
||||
@ -1899,29 +1665,38 @@ pub const Surface = extern struct {
|
||||
);
|
||||
|
||||
// Bindings
|
||||
class.bindTemplateChildPrivate("overlay", .{});
|
||||
class.bindTemplateChildPrivate("gl_area", .{});
|
||||
class.bindTemplateChildPrivate("url_left", .{});
|
||||
class.bindTemplateChildPrivate("url_right", .{});
|
||||
class.bindTemplateChildPrivate("resize_overlay", .{});
|
||||
class.bindTemplateChildPrivate("im_context", .{});
|
||||
|
||||
// EventControllers don't work with our helper.
|
||||
// https://github.com/ianprime0509/zig-gobject/issues/111
|
||||
inline for (&.{
|
||||
"ec_focus",
|
||||
"ec_key",
|
||||
"ec_motion",
|
||||
"ec_scroll",
|
||||
"gesture_click",
|
||||
"url_ec_motion",
|
||||
}) |name| {
|
||||
gtk.Widget.Class.bindTemplateChildFull(
|
||||
gobject.ext.as(gtk.Widget.Class, class),
|
||||
name,
|
||||
@intFromBool(false),
|
||||
Private.offset + @offsetOf(Private, name),
|
||||
);
|
||||
}
|
||||
// Template Callbacks
|
||||
class.bindTemplateCallback("focus_enter", &ecFocusEnter);
|
||||
class.bindTemplateCallback("focus_leave", &ecFocusLeave);
|
||||
class.bindTemplateCallback("key_pressed", &ecKeyPressed);
|
||||
class.bindTemplateCallback("key_released", &ecKeyReleased);
|
||||
class.bindTemplateCallback("mouse_down", &gcMouseDown);
|
||||
class.bindTemplateCallback("mouse_up", &gcMouseUp);
|
||||
class.bindTemplateCallback("mouse_motion", &ecMouseMotion);
|
||||
class.bindTemplateCallback("mouse_leave", &ecMouseLeave);
|
||||
class.bindTemplateCallback("scroll", &ecMouseScroll);
|
||||
class.bindTemplateCallback("scroll_begin", &ecMouseScrollPrecisionBegin);
|
||||
class.bindTemplateCallback("scroll_end", &ecMouseScrollPrecisionEnd);
|
||||
class.bindTemplateCallback("gl_realize", &glareaRealize);
|
||||
class.bindTemplateCallback("gl_unrealize", &glareaUnrealize);
|
||||
class.bindTemplateCallback("gl_render", &glareaRender);
|
||||
class.bindTemplateCallback("gl_resize", &glareaResize);
|
||||
class.bindTemplateCallback("im_preedit_start", &imPreeditStart);
|
||||
class.bindTemplateCallback("im_preedit_changed", &imPreeditChanged);
|
||||
class.bindTemplateCallback("im_preedit_end", &imPreeditEnd);
|
||||
class.bindTemplateCallback("im_commit", &imCommit);
|
||||
class.bindTemplateCallback("url_mouse_enter", &ecUrlMouseEnter);
|
||||
class.bindTemplateCallback("url_mouse_leave", &ecUrlMouseLeave);
|
||||
class.bindTemplateCallback("notify_config", &propConfig);
|
||||
class.bindTemplateCallback("notify_mouse_hover_url", &propMouseHoverUrl);
|
||||
class.bindTemplateCallback("notify_mouse_hidden", &propMouseHidden);
|
||||
class.bindTemplateCallback("notify_mouse_shape", &propMouseShape);
|
||||
|
||||
// Properties
|
||||
gobject.ext.registerProperties(class, &.{
|
||||
@ -1946,6 +1721,7 @@ pub const Surface = extern struct {
|
||||
|
||||
pub const as = C.Class.as;
|
||||
pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate;
|
||||
pub const bindTemplateCallback = C.Class.bindTemplateCallback;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -36,15 +36,6 @@ pub const Window = extern struct {
|
||||
|
||||
fn init(self: *Self, _: *Class) callconv(.C) void {
|
||||
gtk.Widget.initTemplate(self.as(gtk.Widget));
|
||||
|
||||
const surface = self.private().surface;
|
||||
_ = Surface.signals.@"close-request".connect(
|
||||
surface,
|
||||
*Self,
|
||||
surfaceCloseRequest,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
@ -102,11 +93,15 @@ pub const Window = extern struct {
|
||||
// Bindings
|
||||
class.bindTemplateChildPrivate("surface", .{});
|
||||
|
||||
// Template Callbacks
|
||||
class.bindTemplateCallback("surface_close_request", &surfaceCloseRequest);
|
||||
|
||||
// Virtual methods
|
||||
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||
}
|
||||
|
||||
pub const as = C.Class.as;
|
||||
pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate;
|
||||
pub const bindTemplateCallback = C.Class.bindTemplateCallback;
|
||||
};
|
||||
};
|
||||
|
@ -7,6 +7,8 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
||||
"clipboard-confirmation-dialog",
|
||||
]
|
||||
|
||||
notify::blur => $notify_blur();
|
||||
notify::request => $notify_request();
|
||||
heading: _("Authorize Clipboard Access");
|
||||
// Not localized because this is a placeholder users never see.
|
||||
body: "If you see this text, there is a bug in Ghostty. Please report it.";
|
||||
@ -50,6 +52,7 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
||||
|
||||
[overlay]
|
||||
Button reveal_button {
|
||||
clicked => $reveal_clicked();
|
||||
visible: false;
|
||||
halign: end;
|
||||
valign: start;
|
||||
@ -63,6 +66,7 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
||||
|
||||
[overlay]
|
||||
Button hide_button {
|
||||
clicked => $hide_clicked();
|
||||
visible: false;
|
||||
halign: end;
|
||||
valign: start;
|
||||
|
@ -2,66 +2,98 @@ using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
template $GhosttySurface: Adw.Bin {
|
||||
// We need to wrap our Overlay one more time because if you bind a
|
||||
// direct child of your widget to a property, it will double free:
|
||||
// https://gitlab.gnome.org/GNOME/gtk/-/blob/847571a1e314aba79260e4ef282e2ed9ba91a0d9/gtk/gtkwidget.c#L11423-11425
|
||||
Adw.Bin {
|
||||
Overlay overlay {
|
||||
focusable: false;
|
||||
focus-on-click: false;
|
||||
notify::config => $notify_config();
|
||||
notify::mouse-hover-url => $notify_mouse_hover_url();
|
||||
notify::mouse-hidden => $notify_mouse_hidden();
|
||||
notify::mouse-shape => $notify_mouse_shape();
|
||||
|
||||
GLArea gl_area {
|
||||
hexpand: true;
|
||||
vexpand: true;
|
||||
focusable: true;
|
||||
focus-on-click: true;
|
||||
Overlay {
|
||||
focusable: false;
|
||||
focus-on-click: false;
|
||||
|
||||
GLArea gl_area {
|
||||
realize => $gl_realize();
|
||||
unrealize => $gl_unrealize();
|
||||
render => $gl_render();
|
||||
resize => $gl_resize();
|
||||
hexpand: true;
|
||||
vexpand: true;
|
||||
focusable: true;
|
||||
focus-on-click: true;
|
||||
has-stencil-buffer: false;
|
||||
has-depth-buffer: false;
|
||||
use-es: false;
|
||||
}
|
||||
|
||||
[overlay]
|
||||
$GhosttyResizeOverlay resize_overlay {
|
||||
styles [
|
||||
"size-overlay",
|
||||
]
|
||||
}
|
||||
|
||||
[overlay]
|
||||
Label url_left {
|
||||
styles [
|
||||
"url-overlay",
|
||||
]
|
||||
|
||||
visible: false;
|
||||
halign: start;
|
||||
valign: end;
|
||||
label: bind template.mouse-hover-url;
|
||||
|
||||
EventControllerMotion url_ec_motion {
|
||||
enter => $url_mouse_enter();
|
||||
leave => $url_mouse_leave();
|
||||
}
|
||||
}
|
||||
|
||||
[overlay]
|
||||
$GhosttyResizeOverlay resize_overlay {
|
||||
styles [
|
||||
"size-overlay",
|
||||
]
|
||||
}
|
||||
[overlay]
|
||||
Label url_right {
|
||||
styles [
|
||||
"url-overlay",
|
||||
]
|
||||
|
||||
[overlay]
|
||||
Label url_left {
|
||||
styles [
|
||||
"url-overlay",
|
||||
]
|
||||
|
||||
visible: false;
|
||||
halign: start;
|
||||
valign: end;
|
||||
label: bind template.mouse-hover-url;
|
||||
|
||||
EventControllerMotion url_ec_motion {}
|
||||
}
|
||||
|
||||
[overlay]
|
||||
Label url_right {
|
||||
styles [
|
||||
"url-overlay",
|
||||
]
|
||||
|
||||
visible: false;
|
||||
halign: end;
|
||||
valign: end;
|
||||
label: bind template.mouse-hover-url;
|
||||
}
|
||||
visible: false;
|
||||
halign: end;
|
||||
valign: end;
|
||||
label: bind template.mouse-hover-url;
|
||||
}
|
||||
}
|
||||
|
||||
// Event controllers for interactivity
|
||||
EventControllerFocus ec_focus {}
|
||||
EventControllerFocus {
|
||||
enter => $focus_enter();
|
||||
leave => $focus_leave();
|
||||
}
|
||||
|
||||
EventControllerKey ec_key {}
|
||||
EventControllerKey {
|
||||
key-pressed => $key_pressed();
|
||||
key-released => $key_released();
|
||||
}
|
||||
|
||||
EventControllerMotion ec_motion {}
|
||||
EventControllerMotion {
|
||||
motion => $mouse_motion();
|
||||
leave => $mouse_leave();
|
||||
}
|
||||
|
||||
EventControllerScroll ec_scroll {}
|
||||
EventControllerScroll {
|
||||
scroll => $scroll();
|
||||
scroll-begin => $scroll_begin();
|
||||
scroll-end => $scroll_end();
|
||||
}
|
||||
|
||||
GestureClick gesture_click {
|
||||
GestureClick {
|
||||
pressed => $mouse_down();
|
||||
released => $mouse_up();
|
||||
button: 0;
|
||||
}
|
||||
}
|
||||
|
||||
IMMulticontext im_context {
|
||||
preedit-start => $im_preedit_start();
|
||||
preedit-changed => $im_preedit_changed();
|
||||
preedit-end => $im_preedit_end();
|
||||
commit => $im_commit();
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
||||
"clipboard-confirmation-dialog",
|
||||
]
|
||||
|
||||
notify::blur => $notify_blur();
|
||||
notify::request => $notify_request();
|
||||
heading: _("Authorize Clipboard Access");
|
||||
// Not localized because this is a placeholder users never see.
|
||||
body: "If you see this text, there is a bug in Ghostty. Please report it.";
|
||||
@ -50,6 +52,7 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
||||
|
||||
[overlay]
|
||||
Button reveal_button {
|
||||
clicked => $reveal_clicked();
|
||||
visible: false;
|
||||
halign: end;
|
||||
valign: start;
|
||||
@ -63,6 +66,7 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
||||
|
||||
[overlay]
|
||||
Button hide_button {
|
||||
clicked => $hide_clicked();
|
||||
visible: false;
|
||||
halign: end;
|
||||
valign: start;
|
||||
|
@ -5,5 +5,7 @@ template $GhosttyWindow: Adw.ApplicationWindow {
|
||||
default-width: 800;
|
||||
default-height: 600;
|
||||
|
||||
content: $GhosttySurface surface {};
|
||||
content: $GhosttySurface surface {
|
||||
close-request => $surface_close_request();
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user