gtk: Scrollbars
parent
58699c7992
commit
ed443bc6ed
|
|
@ -46,6 +46,7 @@ pub const blueprints: []const Blueprint = &.{
|
|||
.{ .major = 1, .minor = 5, .name = "split-tree" },
|
||||
.{ .major = 1, .minor = 5, .name = "split-tree-split" },
|
||||
.{ .major = 1, .minor = 2, .name = "surface" },
|
||||
.{ .major = 1, .minor = 5, .name = "surface-scrolled-window" },
|
||||
.{ .major = 1, .minor = 5, .name = "surface-title-dialog" },
|
||||
.{ .major = 1, .minor = 3, .name = "surface-child-exited" },
|
||||
.{ .major = 1, .minor = 5, .name = "tab" },
|
||||
|
|
|
|||
|
|
@ -709,6 +709,8 @@ pub const Application = extern struct {
|
|||
|
||||
.ring_bell => Action.ringBell(target),
|
||||
|
||||
.scrollbar => Action.scrollbar(target, value),
|
||||
|
||||
.set_title => Action.setTitle(target, value),
|
||||
|
||||
.show_child_exited => return Action.showChildExited(target, value),
|
||||
|
|
@ -728,7 +730,6 @@ pub const Application = extern struct {
|
|||
.command_finished => return Action.commandFinished(target, value),
|
||||
|
||||
// Unimplemented
|
||||
.scrollbar,
|
||||
.secure_input,
|
||||
.close_all_windows,
|
||||
.float_window,
|
||||
|
|
@ -2328,6 +2329,16 @@ const Action = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn scrollbar(
|
||||
target: apprt.Target,
|
||||
value: apprt.Action.Value(.scrollbar),
|
||||
) void {
|
||||
switch (target) {
|
||||
.app => {},
|
||||
.surface => |v| v.rt_surface.surface.setScrollbar(value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setTitle(
|
||||
target: apprt.Target,
|
||||
value: apprt.action.SetTitle,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ const Config = @import("config.zig").Config;
|
|||
const Application = @import("application.zig").Application;
|
||||
const CloseConfirmationDialog = @import("close_confirmation_dialog.zig").CloseConfirmationDialog;
|
||||
const Surface = @import("surface.zig").Surface;
|
||||
const SurfaceScrolledWindow = @import("surface_scrolled_window.zig").SurfaceScrolledWindow;
|
||||
|
||||
const log = std.log.scoped(.gtk_ghostty_split_tree);
|
||||
|
||||
|
|
@ -874,7 +875,9 @@ pub const SplitTree = extern struct {
|
|||
current: Surface.Tree.Node.Handle,
|
||||
) *gtk.Widget {
|
||||
return switch (tree.nodes[current.idx()]) {
|
||||
.leaf => |v| v.as(gtk.Widget),
|
||||
.leaf => |v| gobject.ext.newInstance(SurfaceScrolledWindow, .{
|
||||
.surface = v,
|
||||
}).as(gtk.Widget),
|
||||
.split => |s| SplitTreeSplit.new(
|
||||
current,
|
||||
&s,
|
||||
|
|
|
|||
|
|
@ -40,12 +40,16 @@ pub const Surface = extern struct {
|
|||
const Self = @This();
|
||||
parent_instance: Parent,
|
||||
pub const Parent = adw.Bin;
|
||||
pub const Implements = [_]type{gtk.Scrollable};
|
||||
pub const getGObjectType = gobject.ext.defineClass(Self, .{
|
||||
.name = "GhosttySurface",
|
||||
.instanceInit = &init,
|
||||
.classInit = &Class.init,
|
||||
.parent_class = &Class.parent,
|
||||
.private = .{ .Type = Private, .offset = &Private.offset },
|
||||
.implements = &.{
|
||||
gobject.ext.implement(gtk.Scrollable, .{}),
|
||||
},
|
||||
});
|
||||
|
||||
/// A SplitTree implementation that stores surfaces.
|
||||
|
|
@ -301,6 +305,62 @@ pub const Surface = extern struct {
|
|||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const hadjustment = struct {
|
||||
pub const name = "hadjustment";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
?*gtk.Adjustment,
|
||||
.{
|
||||
.accessor = .{
|
||||
.getter = getHAdjustmentValue,
|
||||
.setter = setHAdjustmentValue,
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const vadjustment = struct {
|
||||
pub const name = "vadjustment";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
?*gtk.Adjustment,
|
||||
.{
|
||||
.accessor = .{
|
||||
.getter = getVAdjustmentValue,
|
||||
.setter = setVAdjustmentValue,
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const @"hscroll-policy" = struct {
|
||||
pub const name = "hscroll-policy";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
gtk.ScrollablePolicy,
|
||||
.{
|
||||
.default = .natural,
|
||||
.accessor = C.privateShallowFieldAccessor("hscroll_policy"),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const @"vscroll-policy" = struct {
|
||||
pub const name = "vscroll-policy";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
gtk.ScrollablePolicy,
|
||||
.{
|
||||
.default = .natural,
|
||||
.accessor = C.privateShallowFieldAccessor("vscroll_policy"),
|
||||
},
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
pub const signals = struct {
|
||||
|
|
@ -548,6 +608,13 @@ pub const Surface = extern struct {
|
|||
|
||||
action_group: ?*gio.SimpleActionGroup = null,
|
||||
|
||||
// Gtk.Scrollable interface adjustments
|
||||
hadj: ?*gtk.Adjustment = null,
|
||||
vadj: ?*gtk.Adjustment = null,
|
||||
hscroll_policy: gtk.ScrollablePolicy = .natural,
|
||||
vscroll_policy: gtk.ScrollablePolicy = .natural,
|
||||
vadj_signal_group: ?*gobject.SignalGroup = null,
|
||||
|
||||
// Template binds
|
||||
child_exited_overlay: *ChildExited,
|
||||
context_menu: *gtk.PopoverMenu,
|
||||
|
|
@ -714,6 +781,47 @@ pub const Surface = extern struct {
|
|||
return priv.im_context.as(gtk.IMContext).activateOsk(event) != 0;
|
||||
}
|
||||
|
||||
/// Set the scrollbar state for this surface. This will setup the
|
||||
/// properties for our Gtk.Scrollable interface properly.
|
||||
pub fn setScrollbar(self: *Self, scrollbar: terminal.Scrollbar) void {
|
||||
// Update existing adjustment in-place. If we don't have an
|
||||
// adjustment then we do nothing because we're not part of a
|
||||
// scrolled window.
|
||||
const vadj = self.getVAdjustment() orelse return;
|
||||
|
||||
// Check if values match existing adjustment and skip update if so
|
||||
const value: f64 = @floatFromInt(scrollbar.offset);
|
||||
const upper: f64 = @floatFromInt(scrollbar.total);
|
||||
const page_size: f64 = @floatFromInt(scrollbar.len);
|
||||
|
||||
if (std.math.approxEqAbs(f64, vadj.getValue(), value, 0.001) and
|
||||
std.math.approxEqAbs(f64, vadj.getUpper(), upper, 0.001) and
|
||||
std.math.approxEqAbs(f64, vadj.getPageSize(), page_size, 0.001))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a vadjustment we MUST have the signal group since
|
||||
// it is setup in the prop handler.
|
||||
const priv = self.private();
|
||||
const group = priv.vadj_signal_group.?;
|
||||
|
||||
// During manual scrollbar changes from Ghostty core we don't
|
||||
// want to emit value-changed signals so we block them. This would
|
||||
// cause a waste of resources at best and infinite loops at worst.
|
||||
group.block();
|
||||
defer group.unblock();
|
||||
|
||||
vadj.configure(
|
||||
value, // value: current scroll position
|
||||
0, // lower: minimum value
|
||||
upper, // upper: maximum value (total scrollable area)
|
||||
1, // step_increment: amount to scroll on arrow click
|
||||
page_size, // page_increment: amount to scroll on page up/down
|
||||
page_size, // page_size: size of visible area
|
||||
);
|
||||
}
|
||||
|
||||
/// Set the current progress report state.
|
||||
pub fn setProgressReport(
|
||||
self: *Self,
|
||||
|
|
@ -1519,6 +1627,7 @@ pub const Surface = extern struct {
|
|||
priv.mouse_hidden = false;
|
||||
priv.focused = true;
|
||||
priv.size = .{ .width = 0, .height = 0 };
|
||||
priv.vadj_signal_group = null;
|
||||
|
||||
// If our configuration is null then we get the configuration
|
||||
// from the application.
|
||||
|
|
@ -1583,6 +1692,22 @@ pub const Surface = extern struct {
|
|||
priv.config = null;
|
||||
}
|
||||
|
||||
if (priv.vadj_signal_group) |group| {
|
||||
group.setTarget(null);
|
||||
group.as(gobject.Object).unref();
|
||||
priv.vadj_signal_group = null;
|
||||
}
|
||||
|
||||
if (priv.hadj) |v| {
|
||||
v.as(gobject.Object).unref();
|
||||
priv.hadj = null;
|
||||
}
|
||||
|
||||
if (priv.vadj) |v| {
|
||||
v.as(gobject.Object).unref();
|
||||
priv.vadj = null;
|
||||
}
|
||||
|
||||
if (priv.progress_bar_timer) |timer| {
|
||||
if (glib.Source.remove(timer) == 0) {
|
||||
log.warn("unable to remove progress bar timer", .{});
|
||||
|
|
@ -1996,6 +2121,43 @@ pub const Surface = extern struct {
|
|||
self.as(gtk.Widget).setCursorFromName(name.ptr);
|
||||
}
|
||||
|
||||
fn vadjValueChanged(adj: *gtk.Adjustment, self: *Self) callconv(.c) void {
|
||||
// This will trigger for every single pixel change in the adjustment,
|
||||
// but our core surface handles the noise from this so that identical
|
||||
// rows are cheap.
|
||||
const core_surface = self.core() orelse return;
|
||||
const row: usize = @intFromFloat(@round(adj.getValue()));
|
||||
_ = core_surface.performBindingAction(.{ .scroll_to_row = row }) catch |err| {
|
||||
log.err("error performing scroll_to_row action err={}", .{err});
|
||||
};
|
||||
}
|
||||
|
||||
fn propVAdjustment(
|
||||
self: *Self,
|
||||
_: *gobject.ParamSpec,
|
||||
_: ?*anyopaque,
|
||||
) callconv(.c) void {
|
||||
const priv = self.private();
|
||||
|
||||
// When vadjustment is first set, we setup the signal group lazily.
|
||||
// This makes it so that if we don't use scrollbars, we never
|
||||
// pay the memory cost of this.
|
||||
const group: *gobject.SignalGroup = priv.vadj_signal_group orelse group: {
|
||||
const group = gobject.SignalGroup.new(gtk.Adjustment.getGObjectType());
|
||||
group.connect(
|
||||
"value-changed",
|
||||
@ptrCast(&vadjValueChanged),
|
||||
self,
|
||||
);
|
||||
|
||||
priv.vadj_signal_group = group;
|
||||
break :group group;
|
||||
};
|
||||
|
||||
// Setup our signal group target
|
||||
group.setTarget(if (priv.vadj) |v| v.as(gobject.Object) else null);
|
||||
}
|
||||
|
||||
/// Handle bell features that need to happen every time a BEL is received
|
||||
/// Currently this is audio and system but this could change in the future.
|
||||
fn ringBell(self: *Self) void {
|
||||
|
|
@ -2060,6 +2222,66 @@ pub const Surface = extern struct {
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Gtk.Scrollable interface implementation
|
||||
|
||||
pub fn getHAdjustment(self: *Self) ?*gtk.Adjustment {
|
||||
return self.private().hadj;
|
||||
}
|
||||
|
||||
pub fn setHAdjustment(self: *Self, adj_: ?*gtk.Adjustment) void {
|
||||
self.as(gobject.Object).freezeNotify();
|
||||
defer self.as(gobject.Object).thawNotify();
|
||||
self.as(gobject.Object).notifyByPspec(properties.hadjustment.impl.param_spec);
|
||||
|
||||
const priv = self.private();
|
||||
if (priv.hadj) |old| {
|
||||
old.as(gobject.Object).unref();
|
||||
priv.hadj = null;
|
||||
}
|
||||
|
||||
const adj = adj_ orelse return;
|
||||
_ = adj.as(gobject.Object).ref();
|
||||
priv.hadj = adj;
|
||||
}
|
||||
|
||||
fn getHAdjustmentValue(self: *Self, value: *gobject.Value) void {
|
||||
gobject.ext.Value.set(value, self.getHAdjustment());
|
||||
}
|
||||
|
||||
fn setHAdjustmentValue(self: *Self, value: *const gobject.Value) void {
|
||||
self.setHAdjustment(gobject.ext.Value.get(value, ?*gtk.Adjustment));
|
||||
}
|
||||
|
||||
pub fn getVAdjustment(self: *Self) ?*gtk.Adjustment {
|
||||
return self.private().vadj;
|
||||
}
|
||||
|
||||
pub fn setVAdjustment(self: *Self, adj_: ?*gtk.Adjustment) void {
|
||||
self.as(gobject.Object).freezeNotify();
|
||||
defer self.as(gobject.Object).thawNotify();
|
||||
self.as(gobject.Object).notifyByPspec(properties.vadjustment.impl.param_spec);
|
||||
|
||||
const priv = self.private();
|
||||
|
||||
if (priv.vadj) |old| {
|
||||
old.as(gobject.Object).unref();
|
||||
priv.vadj = null;
|
||||
}
|
||||
|
||||
const adj = adj_ orelse return;
|
||||
_ = adj.as(gobject.Object).ref();
|
||||
priv.vadj = adj;
|
||||
}
|
||||
|
||||
fn getVAdjustmentValue(self: *Self, value: *gobject.Value) void {
|
||||
gobject.ext.Value.set(value, self.getVAdjustment());
|
||||
}
|
||||
|
||||
fn setVAdjustmentValue(self: *Self, value: *const gobject.Value) void {
|
||||
self.setVAdjustment(gobject.ext.Value.get(value, ?*gtk.Adjustment));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Signal Handlers
|
||||
|
||||
|
|
@ -3013,6 +3235,7 @@ pub const Surface = extern struct {
|
|||
class.bindTemplateCallback("notify_mouse_hover_url", &propMouseHoverUrl);
|
||||
class.bindTemplateCallback("notify_mouse_hidden", &propMouseHidden);
|
||||
class.bindTemplateCallback("notify_mouse_shape", &propMouseShape);
|
||||
class.bindTemplateCallback("notify_vadjustment", &propVAdjustment);
|
||||
class.bindTemplateCallback("should_border_be_shown", &closureShouldBorderBeShown);
|
||||
class.bindTemplateCallback("should_unfocused_split_be_shown", &closureShouldUnfocusedSplitBeShown);
|
||||
|
||||
|
|
@ -3034,6 +3257,12 @@ pub const Surface = extern struct {
|
|||
properties.@"title-override".impl,
|
||||
properties.zoom.impl,
|
||||
properties.@"is-split".impl,
|
||||
|
||||
// For Gtk.Scrollable
|
||||
properties.hadjustment.impl,
|
||||
properties.vadjustment.impl,
|
||||
properties.@"hscroll-policy".impl,
|
||||
properties.@"vscroll-policy".impl,
|
||||
});
|
||||
|
||||
// Signals
|
||||
|
|
|
|||
|
|
@ -0,0 +1,209 @@
|
|||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const adw = @import("adw");
|
||||
const gobject = @import("gobject");
|
||||
const gtk = @import("gtk");
|
||||
|
||||
const gresource = @import("../build/gresource.zig");
|
||||
const Common = @import("../class.zig").Common;
|
||||
const Surface = @import("surface.zig").Surface;
|
||||
const Config = @import("config.zig").Config;
|
||||
|
||||
const log = std.log.scoped(.gtk_ghostty_surface_scrolled_window);
|
||||
|
||||
/// A wrapper widget that embeds a Surface inside a GtkScrolledWindow.
|
||||
/// This provides scrollbar functionality for the terminal surface.
|
||||
/// The surface property can be set during initialization or changed
|
||||
/// dynamically via the surface property.
|
||||
pub const SurfaceScrolledWindow = extern struct {
|
||||
const Self = @This();
|
||||
parent_instance: Parent,
|
||||
pub const Parent = adw.Bin;
|
||||
pub const getGObjectType = gobject.ext.defineClass(Self, .{
|
||||
.name = "GhostttySurfaceScrolledWindow",
|
||||
.instanceInit = &init,
|
||||
.classInit = &Class.init,
|
||||
.parent_class = &Class.parent,
|
||||
.private = .{ .Type = Private, .offset = &Private.offset },
|
||||
});
|
||||
|
||||
pub const properties = struct {
|
||||
pub const config = struct {
|
||||
pub const name = "config";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
?*Config,
|
||||
.{
|
||||
.accessor = C.privateObjFieldAccessor("config"),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const surface = struct {
|
||||
pub const name = "surface";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
?*Surface,
|
||||
.{
|
||||
.accessor = .{
|
||||
.getter = getSurfaceValue,
|
||||
.setter = setSurfaceValue,
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const Private = struct {
|
||||
config: ?*Config = null,
|
||||
config_binding: ?*gobject.Binding = null,
|
||||
surface: ?*Surface = null,
|
||||
pub var offset: c_int = 0;
|
||||
};
|
||||
|
||||
fn init(self: *Self, _: *Class) callconv(.c) void {
|
||||
gtk.Widget.initTemplate(self.as(gtk.Widget));
|
||||
}
|
||||
|
||||
fn dispose(self: *Self) callconv(.c) void {
|
||||
const priv = self.private();
|
||||
|
||||
if (priv.config_binding) |binding| {
|
||||
binding.as(gobject.Object).unref();
|
||||
priv.config_binding = null;
|
||||
}
|
||||
|
||||
if (priv.config) |v| {
|
||||
v.unref();
|
||||
priv.config = null;
|
||||
}
|
||||
|
||||
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 {
|
||||
gobject.Object.virtual_methods.finalize.call(
|
||||
Class.parent,
|
||||
self.as(Parent),
|
||||
);
|
||||
}
|
||||
|
||||
fn getSurfaceValue(self: *Self, value: *gobject.Value) void {
|
||||
gobject.ext.Value.set(
|
||||
value,
|
||||
self.private().surface,
|
||||
);
|
||||
}
|
||||
|
||||
fn setSurfaceValue(self: *Self, value: *const gobject.Value) void {
|
||||
self.setSurface(gobject.ext.Value.get(
|
||||
value,
|
||||
?*Surface,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn getSurface(self: *Self) ?*Surface {
|
||||
return self.private().surface;
|
||||
}
|
||||
|
||||
pub fn setSurface(self: *Self, surface_: ?*Surface) void {
|
||||
const priv = self.private();
|
||||
|
||||
if (surface_ == priv.surface) return;
|
||||
|
||||
self.as(gobject.Object).freezeNotify();
|
||||
defer self.as(gobject.Object).thawNotify();
|
||||
self.as(gobject.Object).notifyByPspec(properties.surface.impl.param_spec);
|
||||
|
||||
priv.surface = surface_;
|
||||
}
|
||||
|
||||
fn closureScrollbarPolicy(
|
||||
_: *Self,
|
||||
config_: ?*Config,
|
||||
) callconv(.c) gtk.PolicyType {
|
||||
const config = if (config_) |c| c.get() else return .automatic;
|
||||
return switch (config.scrollbar) {
|
||||
.never => .never,
|
||||
.system => .automatic,
|
||||
};
|
||||
}
|
||||
|
||||
fn propSurface(
|
||||
self: *Self,
|
||||
_: *gobject.ParamSpec,
|
||||
_: ?*anyopaque,
|
||||
) callconv(.c) void {
|
||||
const priv = self.private();
|
||||
const child: *gtk.Widget = self.as(Parent).getChild().?;
|
||||
const scrolled_window = gobject.ext.cast(gtk.ScrolledWindow, child).?;
|
||||
scrolled_window.setChild(if (priv.surface) |s| s.as(gtk.Widget) else null);
|
||||
|
||||
// Unbind old config binding if it exists
|
||||
if (priv.config_binding) |binding| {
|
||||
binding.as(gobject.Object).unref();
|
||||
priv.config_binding = null;
|
||||
}
|
||||
|
||||
// Bind config from surface to our config property
|
||||
if (priv.surface) |surface| {
|
||||
priv.config_binding = surface.as(gobject.Object).bindProperty(
|
||||
properties.config.name,
|
||||
self.as(gobject.Object),
|
||||
properties.config.name,
|
||||
.{ .sync_create = true },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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 = 5,
|
||||
.name = "surface-scrolled-window",
|
||||
}),
|
||||
);
|
||||
|
||||
// Bindings
|
||||
class.bindTemplateCallback("scrollbar_policy", &closureScrollbarPolicy);
|
||||
class.bindTemplateCallback("notify_surface", &propSurface);
|
||||
|
||||
// Properties
|
||||
gobject.ext.registerProperties(class, &.{
|
||||
properties.config.impl,
|
||||
properties.surface.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;
|
||||
pub const bindTemplateCallback = C.Class.bindTemplateCallback;
|
||||
};
|
||||
};
|
||||
|
|
@ -174,6 +174,7 @@ template $GhosttySurface: Adw.Bin {
|
|||
notify::mouse-hover-url => $notify_mouse_hover_url();
|
||||
notify::mouse-hidden => $notify_mouse_hidden();
|
||||
notify::mouse-shape => $notify_mouse_shape();
|
||||
notify::vadjustment => $notify_vadjustment();
|
||||
// Some history: we used to use a Stack here and swap between the
|
||||
// terminal and error pages as needed. But a Stack doesn't play nice
|
||||
// with our SplitTree and Gtk.Paned usage[^1]. Replacing this with
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
template $GhostttySurfaceScrolledWindow: Adw.Bin {
|
||||
notify::surface => $notify_surface();
|
||||
|
||||
Gtk.ScrolledWindow {
|
||||
hscrollbar-policy: never;
|
||||
vscrollbar-policy: bind $scrollbar_policy(template.config) as <Gtk.PolicyType>;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue