mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-25 13:16:11 +03:00
apprt/gtk-ng: clipboard confirmation dialog
This commit is contained in:
@ -7,6 +7,7 @@ const gtk = @import("gtk");
|
|||||||
|
|
||||||
const apprt = @import("../../../apprt.zig");
|
const apprt = @import("../../../apprt.zig");
|
||||||
const gresource = @import("../build/gresource.zig");
|
const gresource = @import("../build/gresource.zig");
|
||||||
|
const i18n = @import("../../../os/main.zig").i18n;
|
||||||
const Common = @import("../class.zig").Common;
|
const Common = @import("../class.zig").Common;
|
||||||
const Dialog = @import("dialog.zig").Dialog;
|
const Dialog = @import("dialog.zig").Dialog;
|
||||||
|
|
||||||
@ -25,6 +26,26 @@ pub const ClipboardConfirmationDialog = extern struct {
|
|||||||
});
|
});
|
||||||
|
|
||||||
pub const properties = struct {
|
pub const properties = struct {
|
||||||
|
pub const @"can-remember" = struct {
|
||||||
|
pub const name = "can-remember";
|
||||||
|
const impl = gobject.ext.defineProperty(
|
||||||
|
name,
|
||||||
|
Self,
|
||||||
|
bool,
|
||||||
|
.{
|
||||||
|
.nick = "Can Remember",
|
||||||
|
.blurb = "Allow remembering the choice.",
|
||||||
|
.default = false,
|
||||||
|
.accessor = gobject.ext.privateFieldAccessor(
|
||||||
|
Self,
|
||||||
|
Private,
|
||||||
|
&Private.offset,
|
||||||
|
"can_remember",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
pub const request = struct {
|
pub const request = struct {
|
||||||
pub const name = "request";
|
pub const name = "request";
|
||||||
const impl = gobject.ext.defineProperty(
|
const impl = gobject.ext.defineProperty(
|
||||||
@ -43,12 +64,37 @@ pub const ClipboardConfirmationDialog = extern struct {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const @"clipboard-contents" = struct {
|
||||||
|
pub const name = "clipboard-contents";
|
||||||
|
const impl = gobject.ext.defineProperty(
|
||||||
|
name,
|
||||||
|
Self,
|
||||||
|
?*gtk.TextBuffer,
|
||||||
|
.{
|
||||||
|
.nick = "Clipboard Contents",
|
||||||
|
.blurb = "The clipboard contents being read/written.",
|
||||||
|
.accessor = gobject.ext.privateFieldAccessor(
|
||||||
|
Self,
|
||||||
|
Private,
|
||||||
|
&Private.offset,
|
||||||
|
"clipboard_contents",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const Private = struct {
|
const Private = struct {
|
||||||
/// The request that this dialog is for.
|
/// The request that this dialog is for.
|
||||||
request: ?*apprt.ClipboardRequest = null,
|
request: ?*apprt.ClipboardRequest = null,
|
||||||
|
|
||||||
|
/// The clipboard contents being read/written.
|
||||||
|
clipboard_contents: ?*gtk.TextBuffer = null,
|
||||||
|
|
||||||
|
/// Whether the user can remember the choice.
|
||||||
|
can_remember: bool = false,
|
||||||
|
|
||||||
pub var offset: c_int = 0;
|
pub var offset: c_int = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -58,16 +104,60 @@ 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));
|
||||||
|
|
||||||
|
// Some property signals
|
||||||
|
_ = gobject.Object.signals.notify.connect(
|
||||||
|
self,
|
||||||
|
?*anyopaque,
|
||||||
|
&propRequest,
|
||||||
|
null,
|
||||||
|
.{ .detail = "request" },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Trigger initial values
|
||||||
|
self.propRequest(undefined, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn present(self: *Self, parent: ?*gtk.Widget) void {
|
pub fn present(self: *Self, parent: ?*gtk.Widget) void {
|
||||||
self.as(Dialog).present(parent);
|
self.as(Dialog).present(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// Signal Handlers
|
||||||
|
|
||||||
|
fn propRequest(
|
||||||
|
self: *Self,
|
||||||
|
_: *gobject.ParamSpec,
|
||||||
|
_: ?*anyopaque,
|
||||||
|
) callconv(.c) void {
|
||||||
|
const priv = self.private();
|
||||||
|
const req = priv.request orelse return;
|
||||||
|
switch (req.*) {
|
||||||
|
.osc_52_write => {
|
||||||
|
self.as(Dialog.Parent).setHeading(i18n._("Authorize Clipboard Access"));
|
||||||
|
self.as(Dialog.Parent).setBody(i18n._("An application is attempting to write to the clipboard. The current clipboard contents are shown below."));
|
||||||
|
},
|
||||||
|
.osc_52_read => {
|
||||||
|
self.as(Dialog.Parent).setHeading(i18n._("Authorize Clipboard Access"));
|
||||||
|
self.as(Dialog.Parent).setBody(i18n._("An application is attempting to read from the clipboard. The current clipboard contents are shown below."));
|
||||||
|
},
|
||||||
|
.paste => {
|
||||||
|
self.as(Dialog.Parent).setHeading(i18n._("Warning: Potentially Unsafe Paste"));
|
||||||
|
self.as(Dialog.Parent).setBody(i18n._("Pasting this text into the terminal may be dangerous as it looks like some commands may be executed."));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
// Virtual methods
|
// Virtual methods
|
||||||
|
|
||||||
fn dispose(self: *Self) callconv(.C) void {
|
fn dispose(self: *Self) callconv(.C) void {
|
||||||
|
const priv = self.private();
|
||||||
|
if (priv.clipboard_contents) |v| {
|
||||||
|
v.unref();
|
||||||
|
priv.clipboard_contents = null;
|
||||||
|
}
|
||||||
|
|
||||||
gtk.Widget.disposeTemplate(
|
gtk.Widget.disposeTemplate(
|
||||||
self.as(gtk.Widget),
|
self.as(gtk.Widget),
|
||||||
getGObjectType(),
|
getGObjectType(),
|
||||||
@ -79,6 +169,19 @@ pub const ClipboardConfirmationDialog = extern struct {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn finalize(self: *Self) callconv(.C) void {
|
||||||
|
const priv = self.private();
|
||||||
|
if (priv.request) |v| {
|
||||||
|
glib.ext.destroy(v);
|
||||||
|
priv.request = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
gobject.Object.virtual_methods.finalize.call(
|
||||||
|
Class.parent,
|
||||||
|
self.as(Parent),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const C = Common(Self, Private);
|
const C = Common(Self, Private);
|
||||||
pub const as = C.as;
|
pub const as = C.as;
|
||||||
pub const ref = C.ref;
|
pub const ref = C.ref;
|
||||||
@ -101,15 +204,18 @@ pub const ClipboardConfirmationDialog = extern struct {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Bindings
|
// Bindings
|
||||||
//class.bindTemplateChildPrivate("label", .{});
|
//class.bindTemplateChildPrivate("remember_bin", .{});
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
gobject.ext.registerProperties(class, &.{
|
gobject.ext.registerProperties(class, &.{
|
||||||
|
properties.@"can-remember".impl,
|
||||||
|
properties.@"clipboard-contents".impl,
|
||||||
properties.request.impl,
|
properties.request.impl,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Virtual methods
|
// Virtual methods
|
||||||
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||||
|
gobject.Object.virtual_methods.finalize.implement(class, &finalize);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const as = C.Class.as;
|
pub const as = C.Class.as;
|
||||||
|
@ -2001,19 +2001,21 @@ const Clipboard = struct {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build a text buffer for our contents
|
||||||
|
const contents_buf: *gtk.TextBuffer = .new(null);
|
||||||
|
defer contents_buf.unref();
|
||||||
|
contents_buf.insertAtCursor(val, @intCast(val.len));
|
||||||
|
|
||||||
// Confirm
|
// Confirm
|
||||||
const diag = gobject.ext.newInstance(
|
const diag = gobject.ext.newInstance(
|
||||||
ClipboardConfirmationDialog,
|
ClipboardConfirmationDialog,
|
||||||
.{ .request = &apprt.ClipboardRequest{
|
.{
|
||||||
.osc_52_write = clipboard_type,
|
.request = &apprt.ClipboardRequest{ .osc_52_write = clipboard_type },
|
||||||
} },
|
.@"can-remember" = true,
|
||||||
|
.@"clipboard-contents" = contents_buf,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// We need to trigger the dialog
|
|
||||||
|
|
||||||
diag.present(self.as(gtk.Widget));
|
diag.present(self.as(gtk.Widget));
|
||||||
|
|
||||||
log.warn("TODO: confirmation window", .{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request data from the clipboard (read the clipboard). This
|
/// Request data from the clipboard (read the clipboard). This
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
* https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.3/styles-and-appearance.html#custom-styles
|
* https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.3/styles-and-appearance.html#custom-styles
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GhosttySurface URL overlay
|
||||||
|
*/
|
||||||
label.url-overlay {
|
label.url-overlay {
|
||||||
padding: 4px 8px 4px 8px;
|
padding: 4px 8px 4px 8px;
|
||||||
outline-style: solid;
|
outline-style: solid;
|
||||||
@ -23,6 +26,9 @@ label.url-overlay.right {
|
|||||||
border-radius: 6px 0px 0px 0px;
|
border-radius: 6px 0px 0px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GhosttySurface resize overlay
|
||||||
|
*/
|
||||||
.size-overlay label {
|
.size-overlay label {
|
||||||
padding: 4px 8px 4px 8px;
|
padding: 4px 8px 4px 8px;
|
||||||
border-radius: 6px 6px 6px 6px;
|
border-radius: 6px 6px 6px 6px;
|
||||||
@ -30,3 +36,22 @@ label.url-overlay.right {
|
|||||||
outline-width: 1px;
|
outline-width: 1px;
|
||||||
outline-color: #555555;
|
outline-color: #555555;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GhosttyClipboardConfirmationDialog
|
||||||
|
*
|
||||||
|
* Based on boxed-list-separate:
|
||||||
|
* https://gitlab.gnome.org/GNOME/libadwaita/-/blob/ad446167acf3e6d1ee693f98ca636268be8592a1/src/stylesheet/widgets/_lists.scss#L548
|
||||||
|
*/
|
||||||
|
.clipboard-confirmation-dialog list {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clipboard-confirmation-dialog list > row {
|
||||||
|
border: none;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clipboard-confirmation-dialog list > row:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
@ -3,8 +3,13 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
||||||
|
styles [
|
||||||
|
"clipboard-confirmation-dialog",
|
||||||
|
]
|
||||||
|
|
||||||
heading: _("Authorize Clipboard Access");
|
heading: _("Authorize Clipboard Access");
|
||||||
body: _("An application is attempting to write to the clipboard. The current clipboard contents are shown below.");
|
// 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.";
|
||||||
|
|
||||||
responses [
|
responses [
|
||||||
cancel: _("Deny") suggested,
|
cancel: _("Deny") suggested,
|
||||||
@ -17,10 +22,6 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
|||||||
extra-child: ListBox {
|
extra-child: ListBox {
|
||||||
selection-mode: none;
|
selection-mode: none;
|
||||||
|
|
||||||
styles [
|
|
||||||
"boxed-list-separate",
|
|
||||||
]
|
|
||||||
|
|
||||||
Overlay {
|
Overlay {
|
||||||
styles [
|
styles [
|
||||||
"osd",
|
"osd",
|
||||||
@ -32,6 +33,10 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
|||||||
height-request: 200;
|
height-request: 200;
|
||||||
|
|
||||||
TextView text_view {
|
TextView text_view {
|
||||||
|
styles [
|
||||||
|
"clipboard-content-view",
|
||||||
|
]
|
||||||
|
|
||||||
cursor-visible: false;
|
cursor-visible: false;
|
||||||
editable: false;
|
editable: false;
|
||||||
monospace: true;
|
monospace: true;
|
||||||
@ -39,10 +44,7 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
|||||||
left-margin: 8;
|
left-margin: 8;
|
||||||
bottom-margin: 8;
|
bottom-margin: 8;
|
||||||
right-margin: 8;
|
right-margin: 8;
|
||||||
|
buffer: bind template.clipboard-contents;
|
||||||
styles [
|
|
||||||
"clipboard-content-view",
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,6 +76,11 @@ template $GhosttyClipboardConfirmationDialog: $GhosttyDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Adw.SwitchRow remember_choice {
|
Adw.SwitchRow remember_choice {
|
||||||
|
styles [
|
||||||
|
"card",
|
||||||
|
]
|
||||||
|
|
||||||
|
visible: bind template.can-remember;
|
||||||
title: _("Remember choice for this split");
|
title: _("Remember choice for this split");
|
||||||
subtitle: _("Reload configuration to show this prompt again");
|
subtitle: _("Reload configuration to show this prompt again");
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user