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 {};
|
}).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 {
|
fn init(self: *Self, _: *Class) callconv(.C) void {
|
||||||
gtk.Widget.initTemplate(self.as(gtk.Widget));
|
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
|
// Trigger initial values
|
||||||
self.propBlur(undefined, null);
|
self.propBlur(undefined, null);
|
||||||
self.propRequest(undefined, null);
|
self.propRequest(undefined, null);
|
||||||
@ -374,6 +340,12 @@ pub const ClipboardConfirmationDialog = extern struct {
|
|||||||
class.bindTemplateChildPrivate("remember_choice", .{});
|
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
|
// Properties
|
||||||
gobject.ext.registerProperties(class, &.{
|
gobject.ext.registerProperties(class, &.{
|
||||||
properties.blur.impl,
|
properties.blur.impl,
|
||||||
@ -394,5 +366,6 @@ pub const ClipboardConfirmationDialog = extern struct {
|
|||||||
|
|
||||||
pub const as = C.Class.as;
|
pub const as = C.Class.as;
|
||||||
pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate;
|
pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate;
|
||||||
|
pub const bindTemplateCallback = C.Class.bindTemplateCallback;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -245,10 +245,6 @@ pub const Surface = extern struct {
|
|||||||
/// focus events.
|
/// focus events.
|
||||||
focused: bool = true,
|
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
|
/// The GLAarea that renders the actual surface. This is a binding
|
||||||
/// to the template so it doesn't have to be unrefed manually.
|
/// to the template so it doesn't have to be unrefed manually.
|
||||||
gl_area: *gtk.GLArea,
|
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.
|
/// The labels for the left/right sides of the URL hover tooltip.
|
||||||
url_left: *gtk.Label,
|
url_left: *gtk.Label,
|
||||||
url_right: *gtk.Label,
|
url_right: *gtk.Label,
|
||||||
url_ec_motion: *gtk.EventControllerMotion,
|
|
||||||
|
|
||||||
/// The resize overlay
|
/// The resize overlay
|
||||||
resize_overlay: *ResizeOverlay,
|
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.
|
/// The apprt Surface.
|
||||||
rt_surface: ApprtSurface = undefined,
|
rt_surface: ApprtSurface = undefined,
|
||||||
|
|
||||||
@ -288,7 +276,7 @@ pub const Surface = extern struct {
|
|||||||
|
|
||||||
/// Various input method state. All related to key input.
|
/// Various input method state. All related to key input.
|
||||||
in_keyevent: IMKeyEvent = .false,
|
in_keyevent: IMKeyEvent = .false,
|
||||||
im_context: ?*gtk.IMMulticontext = null,
|
im_context: *gtk.IMMulticontext,
|
||||||
im_composing: bool = false,
|
im_composing: bool = false,
|
||||||
im_buf: [128]u8 = undefined,
|
im_buf: [128]u8 = undefined,
|
||||||
im_len: u7 = 0,
|
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
|
// The block below is all related to input method handling. See the function
|
||||||
// comment for some high level details and then the comments within
|
// comment for some high level details and then the comments within
|
||||||
// the block for more specifics.
|
// 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
|
// 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
|
// where the cursor is so it can render the dropdowns in the correct
|
||||||
// place.
|
// place.
|
||||||
if (priv.core_surface) |surface| {
|
if (priv.core_surface) |surface| {
|
||||||
const ime_point = surface.imePoint();
|
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_x = @intFromFloat(ime_point.x),
|
||||||
.f_y = @intFromFloat(ime_point.y),
|
.f_y = @intFromFloat(ime_point.y),
|
||||||
.f_width = 1,
|
.f_width = 1,
|
||||||
@ -415,7 +403,7 @@ pub const Surface = extern struct {
|
|||||||
// triggered despite being technically consumed. At the time of
|
// triggered despite being technically consumed. At the time of
|
||||||
// writing, both Kitty and Alacritty have the same behavior. I
|
// writing, both Kitty and Alacritty have the same behavior. I
|
||||||
// know of no way to fix this.
|
// 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={}", .{
|
// log.warn("GTKIM: im_handled={} im_len={} im_composing={}", .{
|
||||||
// im_handled,
|
// im_handled,
|
||||||
// self.im_len,
|
// self.im_len,
|
||||||
@ -546,10 +534,7 @@ pub const Surface = extern struct {
|
|||||||
// because there is other IME state that we want to preserve,
|
// because there is other IME state that we want to preserve,
|
||||||
// such as quotation mark ordering for Chinese input.
|
// such as quotation mark ordering for Chinese input.
|
||||||
if (priv.im_composing) {
|
if (priv.im_composing) {
|
||||||
if (priv.im_context) |im_context| {
|
priv.im_context.as(gtk.IMContext).reset();
|
||||||
im_context.as(gtk.IMContext).reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
surface.preeditCallback(null) catch {};
|
surface.preeditCallback(null) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -803,238 +788,30 @@ pub const Surface = extern struct {
|
|||||||
priv.config = app.getConfig();
|
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
|
// Setup our input method state
|
||||||
const im_context = gtk.IMMulticontext.new();
|
|
||||||
priv.im_context = im_context;
|
|
||||||
priv.in_keyevent = .false;
|
priv.in_keyevent = .false;
|
||||||
priv.im_composing = false;
|
priv.im_composing = false;
|
||||||
priv.im_len = 0;
|
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
|
// Initialize our GLArea. We only set the values we can't set
|
||||||
// the Blueprint file but I think its cleaner to separate
|
// in our blueprint file.
|
||||||
// the "UI" part of the blueprint file from the internal logic/config
|
|
||||||
// part.
|
|
||||||
const gl_area = priv.gl_area;
|
const gl_area = priv.gl_area;
|
||||||
gl_area.setRequiredVersion(
|
gl_area.setRequiredVersion(
|
||||||
renderer.OpenGL.MIN_VERSION_MAJOR,
|
renderer.OpenGL.MIN_VERSION_MAJOR,
|
||||||
renderer.OpenGL.MIN_VERSION_MINOR,
|
renderer.OpenGL.MIN_VERSION_MINOR,
|
||||||
);
|
);
|
||||||
gl_area.setHasStencilBuffer(0);
|
|
||||||
gl_area.setHasDepthBuffer(0);
|
|
||||||
gl_area.setUseEs(0);
|
|
||||||
gl_area.as(gtk.Widget).setCursorFromName("text");
|
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
|
// Initialize our config
|
||||||
self.propConfig(undefined, null);
|
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 {
|
fn dispose(self: *Self) callconv(.C) void {
|
||||||
const priv = self.private();
|
const priv = self.private();
|
||||||
if (priv.config) |v| {
|
if (priv.config) |v| {
|
||||||
v.unref();
|
v.unref();
|
||||||
priv.config = null;
|
priv.config = null;
|
||||||
}
|
}
|
||||||
if (priv.im_context) |v| {
|
|
||||||
v.unref();
|
|
||||||
priv.im_context = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
gtk.Widget.disposeTemplate(
|
gtk.Widget.disposeTemplate(
|
||||||
self.as(gtk.Widget),
|
self.as(gtk.Widget),
|
||||||
@ -1260,22 +1037,14 @@ pub const Surface = extern struct {
|
|||||||
fn ecFocusEnter(_: *gtk.EventControllerFocus, self: *Self) callconv(.c) void {
|
fn ecFocusEnter(_: *gtk.EventControllerFocus, self: *Self) callconv(.c) void {
|
||||||
const priv = self.private();
|
const priv = self.private();
|
||||||
priv.focused = true;
|
priv.focused = true;
|
||||||
|
priv.im_context.as(gtk.IMContext).focusIn();
|
||||||
if (priv.im_context) |im_context| {
|
|
||||||
im_context.as(gtk.IMContext).focusIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = glib.idleAddOnce(idleFocus, self.ref());
|
_ = glib.idleAddOnce(idleFocus, self.ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ecFocusLeave(_: *gtk.EventControllerFocus, self: *Self) callconv(.c) void {
|
fn ecFocusLeave(_: *gtk.EventControllerFocus, self: *Self) callconv(.c) void {
|
||||||
const priv = self.private();
|
const priv = self.private();
|
||||||
priv.focused = false;
|
priv.focused = false;
|
||||||
|
priv.im_context.as(gtk.IMContext).focusOut();
|
||||||
if (priv.im_context) |im_context| {
|
|
||||||
im_context.as(gtk.IMContext).focusOut();
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = glib.idleAddOnce(idleFocus, self.ref());
|
_ = 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
|
// Setup our input method. We do this here because this will
|
||||||
// create a strong reference back to ourself and we want to be
|
// create a strong reference back to ourself and we want to be
|
||||||
// able to release that in unrealize.
|
// able to release that in unrealize.
|
||||||
if (self.private().im_context) |im_context| {
|
const priv = self.private();
|
||||||
im_context.as(gtk.IMContext).setClientWidget(self.as(gtk.Widget));
|
priv.im_context.as(gtk.IMContext).setClientWidget(self.as(gtk.Widget));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glareaUnrealize(
|
fn glareaUnrealize(
|
||||||
@ -1683,9 +1451,7 @@ pub const Surface = extern struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unset our input method
|
// Unset our input method
|
||||||
if (priv.im_context) |im_context| {
|
priv.im_context.as(gtk.IMContext).setClientWidget(null);
|
||||||
im_context.as(gtk.IMContext).setClientWidget(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glareaRender(
|
fn glareaRender(
|
||||||
@ -1899,29 +1665,38 @@ pub const Surface = extern struct {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Bindings
|
// Bindings
|
||||||
class.bindTemplateChildPrivate("overlay", .{});
|
|
||||||
class.bindTemplateChildPrivate("gl_area", .{});
|
class.bindTemplateChildPrivate("gl_area", .{});
|
||||||
class.bindTemplateChildPrivate("url_left", .{});
|
class.bindTemplateChildPrivate("url_left", .{});
|
||||||
class.bindTemplateChildPrivate("url_right", .{});
|
class.bindTemplateChildPrivate("url_right", .{});
|
||||||
class.bindTemplateChildPrivate("resize_overlay", .{});
|
class.bindTemplateChildPrivate("resize_overlay", .{});
|
||||||
|
class.bindTemplateChildPrivate("im_context", .{});
|
||||||
|
|
||||||
// EventControllers don't work with our helper.
|
// Template Callbacks
|
||||||
// https://github.com/ianprime0509/zig-gobject/issues/111
|
class.bindTemplateCallback("focus_enter", &ecFocusEnter);
|
||||||
inline for (&.{
|
class.bindTemplateCallback("focus_leave", &ecFocusLeave);
|
||||||
"ec_focus",
|
class.bindTemplateCallback("key_pressed", &ecKeyPressed);
|
||||||
"ec_key",
|
class.bindTemplateCallback("key_released", &ecKeyReleased);
|
||||||
"ec_motion",
|
class.bindTemplateCallback("mouse_down", &gcMouseDown);
|
||||||
"ec_scroll",
|
class.bindTemplateCallback("mouse_up", &gcMouseUp);
|
||||||
"gesture_click",
|
class.bindTemplateCallback("mouse_motion", &ecMouseMotion);
|
||||||
"url_ec_motion",
|
class.bindTemplateCallback("mouse_leave", &ecMouseLeave);
|
||||||
}) |name| {
|
class.bindTemplateCallback("scroll", &ecMouseScroll);
|
||||||
gtk.Widget.Class.bindTemplateChildFull(
|
class.bindTemplateCallback("scroll_begin", &ecMouseScrollPrecisionBegin);
|
||||||
gobject.ext.as(gtk.Widget.Class, class),
|
class.bindTemplateCallback("scroll_end", &ecMouseScrollPrecisionEnd);
|
||||||
name,
|
class.bindTemplateCallback("gl_realize", &glareaRealize);
|
||||||
@intFromBool(false),
|
class.bindTemplateCallback("gl_unrealize", &glareaUnrealize);
|
||||||
Private.offset + @offsetOf(Private, name),
|
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
|
// Properties
|
||||||
gobject.ext.registerProperties(class, &.{
|
gobject.ext.registerProperties(class, &.{
|
||||||
@ -1946,6 +1721,7 @@ pub const Surface = extern struct {
|
|||||||
|
|
||||||
pub const as = C.Class.as;
|
pub const as = C.Class.as;
|
||||||
pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate;
|
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 {
|
fn init(self: *Self, _: *Class) callconv(.C) void {
|
||||||
gtk.Widget.initTemplate(self.as(gtk.Widget));
|
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
|
// Bindings
|
||||||
class.bindTemplateChildPrivate("surface", .{});
|
class.bindTemplateChildPrivate("surface", .{});
|
||||||
|
|
||||||
|
// Template Callbacks
|
||||||
|
class.bindTemplateCallback("surface_close_request", &surfaceCloseRequest);
|
||||||
|
|
||||||
// Virtual methods
|
// Virtual methods
|
||||||
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const as = C.Class.as;
|
pub const as = C.Class.as;
|
||||||
pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate;
|
pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate;
|
||||||
|
pub const bindTemplateCallback = C.Class.bindTemplateCallback;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,8 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
|||||||
"clipboard-confirmation-dialog",
|
"clipboard-confirmation-dialog",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
notify::blur => $notify_blur();
|
||||||
|
notify::request => $notify_request();
|
||||||
heading: _("Authorize Clipboard Access");
|
heading: _("Authorize Clipboard Access");
|
||||||
// Not localized because this is a placeholder users never see.
|
// 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.";
|
body: "If you see this text, there is a bug in Ghostty. Please report it.";
|
||||||
@ -50,6 +52,7 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
|||||||
|
|
||||||
[overlay]
|
[overlay]
|
||||||
Button reveal_button {
|
Button reveal_button {
|
||||||
|
clicked => $reveal_clicked();
|
||||||
visible: false;
|
visible: false;
|
||||||
halign: end;
|
halign: end;
|
||||||
valign: start;
|
valign: start;
|
||||||
@ -63,6 +66,7 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
|||||||
|
|
||||||
[overlay]
|
[overlay]
|
||||||
Button hide_button {
|
Button hide_button {
|
||||||
|
clicked => $hide_clicked();
|
||||||
visible: false;
|
visible: false;
|
||||||
halign: end;
|
halign: end;
|
||||||
valign: start;
|
valign: start;
|
||||||
|
@ -2,19 +2,27 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $GhosttySurface: Adw.Bin {
|
template $GhosttySurface: Adw.Bin {
|
||||||
// We need to wrap our Overlay one more time because if you bind a
|
notify::config => $notify_config();
|
||||||
// direct child of your widget to a property, it will double free:
|
notify::mouse-hover-url => $notify_mouse_hover_url();
|
||||||
// https://gitlab.gnome.org/GNOME/gtk/-/blob/847571a1e314aba79260e4ef282e2ed9ba91a0d9/gtk/gtkwidget.c#L11423-11425
|
notify::mouse-hidden => $notify_mouse_hidden();
|
||||||
Adw.Bin {
|
notify::mouse-shape => $notify_mouse_shape();
|
||||||
Overlay overlay {
|
|
||||||
|
Overlay {
|
||||||
focusable: false;
|
focusable: false;
|
||||||
focus-on-click: false;
|
focus-on-click: false;
|
||||||
|
|
||||||
GLArea gl_area {
|
GLArea gl_area {
|
||||||
|
realize => $gl_realize();
|
||||||
|
unrealize => $gl_unrealize();
|
||||||
|
render => $gl_render();
|
||||||
|
resize => $gl_resize();
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
vexpand: true;
|
vexpand: true;
|
||||||
focusable: true;
|
focusable: true;
|
||||||
focus-on-click: true;
|
focus-on-click: true;
|
||||||
|
has-stencil-buffer: false;
|
||||||
|
has-depth-buffer: false;
|
||||||
|
use-es: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[overlay]
|
[overlay]
|
||||||
@ -35,7 +43,10 @@ template $GhosttySurface: Adw.Bin {
|
|||||||
valign: end;
|
valign: end;
|
||||||
label: bind template.mouse-hover-url;
|
label: bind template.mouse-hover-url;
|
||||||
|
|
||||||
EventControllerMotion url_ec_motion {}
|
EventControllerMotion url_ec_motion {
|
||||||
|
enter => $url_mouse_enter();
|
||||||
|
leave => $url_mouse_leave();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[overlay]
|
[overlay]
|
||||||
@ -50,18 +61,39 @@ template $GhosttySurface: Adw.Bin {
|
|||||||
label: bind template.mouse-hover-url;
|
label: bind template.mouse-hover-url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Event controllers for interactivity
|
// 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;
|
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",
|
"clipboard-confirmation-dialog",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
notify::blur => $notify_blur();
|
||||||
|
notify::request => $notify_request();
|
||||||
heading: _("Authorize Clipboard Access");
|
heading: _("Authorize Clipboard Access");
|
||||||
// Not localized because this is a placeholder users never see.
|
// 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.";
|
body: "If you see this text, there is a bug in Ghostty. Please report it.";
|
||||||
@ -50,6 +52,7 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
|||||||
|
|
||||||
[overlay]
|
[overlay]
|
||||||
Button reveal_button {
|
Button reveal_button {
|
||||||
|
clicked => $reveal_clicked();
|
||||||
visible: false;
|
visible: false;
|
||||||
halign: end;
|
halign: end;
|
||||||
valign: start;
|
valign: start;
|
||||||
@ -63,6 +66,7 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
|||||||
|
|
||||||
[overlay]
|
[overlay]
|
||||||
Button hide_button {
|
Button hide_button {
|
||||||
|
clicked => $hide_clicked();
|
||||||
visible: false;
|
visible: false;
|
||||||
halign: end;
|
halign: end;
|
||||||
valign: start;
|
valign: start;
|
||||||
|
@ -5,5 +5,7 @@ template $GhosttyWindow: Adw.ApplicationWindow {
|
|||||||
default-width: 800;
|
default-width: 800;
|
||||||
default-height: 600;
|
default-height: 600;
|
||||||
|
|
||||||
content: $GhosttySurface surface {};
|
content: $GhosttySurface surface {
|
||||||
|
close-request => $surface_close_request();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user