apprt/gtk: search overlay UI
parent
832883b600
commit
548d1f0300
|
|
@ -43,6 +43,7 @@ pub const blueprints: []const Blueprint = &.{
|
||||||
.{ .major = 1, .minor = 5, .name = "inspector-widget" },
|
.{ .major = 1, .minor = 5, .name = "inspector-widget" },
|
||||||
.{ .major = 1, .minor = 5, .name = "inspector-window" },
|
.{ .major = 1, .minor = 5, .name = "inspector-window" },
|
||||||
.{ .major = 1, .minor = 2, .name = "resize-overlay" },
|
.{ .major = 1, .minor = 2, .name = "resize-overlay" },
|
||||||
|
.{ .major = 1, .minor = 2, .name = "search-overlay" },
|
||||||
.{ .major = 1, .minor = 5, .name = "split-tree" },
|
.{ .major = 1, .minor = 5, .name = "split-tree" },
|
||||||
.{ .major = 1, .minor = 5, .name = "split-tree-split" },
|
.{ .major = 1, .minor = 5, .name = "split-tree-split" },
|
||||||
.{ .major = 1, .minor = 2, .name = "surface" },
|
.{ .major = 1, .minor = 2, .name = "surface" },
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const adw = @import("adw");
|
||||||
|
const glib = @import("glib");
|
||||||
|
const gobject = @import("gobject");
|
||||||
|
const gtk = @import("gtk");
|
||||||
|
|
||||||
|
const gresource = @import("../build/gresource.zig");
|
||||||
|
const Common = @import("../class.zig").Common;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.gtk_ghostty_search_overlay);
|
||||||
|
|
||||||
|
/// The overlay that shows the current size while a surface is resizing.
|
||||||
|
/// This can be used generically to show pretty much anything with a
|
||||||
|
/// disappearing overlay, but we have no other use at this point so it
|
||||||
|
/// is named specifically for what it does.
|
||||||
|
///
|
||||||
|
/// General usage:
|
||||||
|
///
|
||||||
|
/// 1. Add it to an overlay
|
||||||
|
/// 2. Set the label with `setLabel`
|
||||||
|
/// 3. Schedule to show it with `schedule`
|
||||||
|
///
|
||||||
|
/// Set any properties to change the behavior.
|
||||||
|
pub const SearchOverlay = extern struct {
|
||||||
|
const Self = @This();
|
||||||
|
parent_instance: Parent,
|
||||||
|
pub const Parent = adw.Bin;
|
||||||
|
pub const getGObjectType = gobject.ext.defineClass(Self, .{
|
||||||
|
.name = "GhosttySearchOverlay",
|
||||||
|
.instanceInit = &init,
|
||||||
|
.classInit = &Class.init,
|
||||||
|
.parent_class = &Class.parent,
|
||||||
|
.private = .{ .Type = Private, .offset = &Private.offset },
|
||||||
|
});
|
||||||
|
|
||||||
|
pub const properties = struct {
|
||||||
|
pub const duration = struct {
|
||||||
|
pub const name = "duration";
|
||||||
|
const impl = gobject.ext.defineProperty(
|
||||||
|
name,
|
||||||
|
Self,
|
||||||
|
c_uint,
|
||||||
|
.{
|
||||||
|
.default = 750,
|
||||||
|
.minimum = 250,
|
||||||
|
.maximum = std.math.maxInt(c_uint),
|
||||||
|
.accessor = gobject.ext.privateFieldAccessor(
|
||||||
|
Self,
|
||||||
|
Private,
|
||||||
|
&Private.offset,
|
||||||
|
"duration",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const Private = struct {
|
||||||
|
/// The time that the overlay appears.
|
||||||
|
duration: c_uint,
|
||||||
|
|
||||||
|
pub var offset: c_int = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn init(self: *Self, _: *Class) callconv(.c) void {
|
||||||
|
gtk.Widget.initTemplate(self.as(gtk.Widget));
|
||||||
|
|
||||||
|
const priv = self.private();
|
||||||
|
_ = priv;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// Virtual methods
|
||||||
|
|
||||||
|
fn dispose(self: *Self) callconv(.c) void {
|
||||||
|
const priv = self.private();
|
||||||
|
_ = priv;
|
||||||
|
|
||||||
|
gtk.Widget.disposeTemplate(
|
||||||
|
self.as(gtk.Widget),
|
||||||
|
getGObjectType(),
|
||||||
|
);
|
||||||
|
|
||||||
|
gobject.Object.virtual_methods.dispose.call(
|
||||||
|
Class.parent,
|
||||||
|
self.as(Parent),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalize(self: *Self) callconv(.c) void {
|
||||||
|
const priv = self.private();
|
||||||
|
_ = priv;
|
||||||
|
|
||||||
|
gobject.Object.virtual_methods.finalize.call(
|
||||||
|
Class.parent,
|
||||||
|
self.as(Parent),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const C = Common(Self, Private);
|
||||||
|
pub const as = C.as;
|
||||||
|
pub const ref = C.ref;
|
||||||
|
pub const unref = C.unref;
|
||||||
|
const private = C.private;
|
||||||
|
|
||||||
|
pub const Class = extern struct {
|
||||||
|
parent_class: Parent.Class,
|
||||||
|
var parent: *Parent.Class = undefined;
|
||||||
|
pub const Instance = Self;
|
||||||
|
|
||||||
|
fn init(class: *Class) callconv(.c) void {
|
||||||
|
gtk.Widget.Class.setTemplateFromResource(
|
||||||
|
class.as(gtk.Widget.Class),
|
||||||
|
comptime gresource.blueprint(.{
|
||||||
|
.major = 1,
|
||||||
|
.minor = 2,
|
||||||
|
.name = "search-overlay",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Bindings
|
||||||
|
// class.bindTemplateChildPrivate("label", .{});
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
// gobject.ext.registerProperties(class, &.{
|
||||||
|
// properties.duration.impl,
|
||||||
|
// properties.label.impl,
|
||||||
|
// properties.@"first-delay".impl,
|
||||||
|
// properties.@"overlay-halign".impl,
|
||||||
|
// properties.@"overlay-valign".impl,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// Virtual methods
|
||||||
|
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||||
|
gobject.Object.virtual_methods.finalize.implement(class, &finalize);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const as = C.Class.as;
|
||||||
|
pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -25,6 +25,7 @@ const Common = @import("../class.zig").Common;
|
||||||
const Application = @import("application.zig").Application;
|
const Application = @import("application.zig").Application;
|
||||||
const Config = @import("config.zig").Config;
|
const Config = @import("config.zig").Config;
|
||||||
const ResizeOverlay = @import("resize_overlay.zig").ResizeOverlay;
|
const ResizeOverlay = @import("resize_overlay.zig").ResizeOverlay;
|
||||||
|
const SearchOverlay = @import("search_overlay.zig").SearchOverlay;
|
||||||
const ChildExited = @import("surface_child_exited.zig").SurfaceChildExited;
|
const ChildExited = @import("surface_child_exited.zig").SurfaceChildExited;
|
||||||
const ClipboardConfirmationDialog = @import("clipboard_confirmation_dialog.zig").ClipboardConfirmationDialog;
|
const ClipboardConfirmationDialog = @import("clipboard_confirmation_dialog.zig").ClipboardConfirmationDialog;
|
||||||
const TitleDialog = @import("surface_title_dialog.zig").SurfaceTitleDialog;
|
const TitleDialog = @import("surface_title_dialog.zig").SurfaceTitleDialog;
|
||||||
|
|
@ -3184,6 +3185,7 @@ pub const Surface = extern struct {
|
||||||
|
|
||||||
fn init(class: *Class) callconv(.c) void {
|
fn init(class: *Class) callconv(.c) void {
|
||||||
gobject.ext.ensureType(ResizeOverlay);
|
gobject.ext.ensureType(ResizeOverlay);
|
||||||
|
gobject.ext.ensureType(SearchOverlay);
|
||||||
gobject.ext.ensureType(ChildExited);
|
gobject.ext.ensureType(ChildExited);
|
||||||
gtk.Widget.Class.setTemplateFromResource(
|
gtk.Widget.Class.setTemplateFromResource(
|
||||||
class.as(gtk.Widget.Class),
|
class.as(gtk.Widget.Class),
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,18 @@ label.url-overlay.right {
|
||||||
border-radius: 6px 0px 0px 0px;
|
border-radius: 6px 0px 0px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GhosttySurface search overlay
|
||||||
|
*/
|
||||||
|
.search-overlay {
|
||||||
|
padding: 6px 8px;
|
||||||
|
margin: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
outline-style: solid;
|
||||||
|
outline-color: #555555;
|
||||||
|
outline-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GhosttySurface resize overlay
|
* GhosttySurface resize overlay
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
using Gtk 4.0;
|
||||||
|
using Gdk 4.0;
|
||||||
|
using Adw 1;
|
||||||
|
|
||||||
|
template $GhosttySearchOverlay: Adw.Bin {
|
||||||
|
halign: end;
|
||||||
|
valign: start;
|
||||||
|
|
||||||
|
Adw.Bin {
|
||||||
|
Box container {
|
||||||
|
styles [
|
||||||
|
"background",
|
||||||
|
"search-overlay",
|
||||||
|
]
|
||||||
|
|
||||||
|
orientation: horizontal;
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
SearchEntry search_entry {
|
||||||
|
placeholder-text: _("Find…");
|
||||||
|
width-chars: 20;
|
||||||
|
hexpand: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Label match_label {
|
||||||
|
styles [
|
||||||
|
"dim-label",
|
||||||
|
]
|
||||||
|
|
||||||
|
label: "0/0";
|
||||||
|
width-chars: 6;
|
||||||
|
xalign: 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Box button_box {
|
||||||
|
orientation: horizontal;
|
||||||
|
spacing: 1;
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"linked",
|
||||||
|
]
|
||||||
|
|
||||||
|
Button prev_button {
|
||||||
|
icon-name: "go-up-symbolic";
|
||||||
|
tooltip-text: _("Previous Match");
|
||||||
|
|
||||||
|
cursor: Gdk.Cursor {
|
||||||
|
name: "pointer";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Button next_button {
|
||||||
|
icon-name: "go-down-symbolic";
|
||||||
|
tooltip-text: _("Next Match");
|
||||||
|
|
||||||
|
cursor: Gdk.Cursor {
|
||||||
|
name: "pointer";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button close_button {
|
||||||
|
icon-name: "window-close-symbolic";
|
||||||
|
tooltip-text: _("Close");
|
||||||
|
|
||||||
|
cursor: Gdk.Cursor {
|
||||||
|
name: "pointer";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -147,12 +147,24 @@ Overlay terminal_page {
|
||||||
label: bind template.mouse-hover-url;
|
label: bind template.mouse-hover-url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[overlay]
|
||||||
|
$GhosttySearchOverlay search_overlay {
|
||||||
|
halign: end;
|
||||||
|
valign: start;
|
||||||
|
}
|
||||||
|
|
||||||
[overlay]
|
[overlay]
|
||||||
// Apply unfocused-split-fill and unfocused-split-opacity to current surface
|
// Apply unfocused-split-fill and unfocused-split-opacity to current surface
|
||||||
// this is only applied when a tab has more than one surface
|
// this is only applied when a tab has more than one surface
|
||||||
Revealer {
|
Revealer {
|
||||||
reveal-child: bind $should_unfocused_split_be_shown(template.focused, template.is-split) as <bool>;
|
reveal-child: bind $should_unfocused_split_be_shown(template.focused, template.is-split) as <bool>;
|
||||||
transition-duration: 0;
|
transition-duration: 0;
|
||||||
|
// This is all necessary so that the Revealer itself doesn't override
|
||||||
|
// any input events from the other overlays. Namely, if you don't have
|
||||||
|
// these then the search overlay won't get mouse events.
|
||||||
|
can-focus: false;
|
||||||
|
can-target: false;
|
||||||
|
focusable: false;
|
||||||
|
|
||||||
DrawingArea {
|
DrawingArea {
|
||||||
styles [
|
styles [
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue