apprt/gtk-ng: setup split tree property
parent
fa08434b28
commit
70b050ebb4
|
|
@ -5,6 +5,7 @@ const glib = @import("glib");
|
|||
const gobject = @import("gobject");
|
||||
const gtk = @import("gtk");
|
||||
|
||||
const ext = @import("ext.zig");
|
||||
pub const Application = @import("class/application.zig").Application;
|
||||
pub const Window = @import("class/window.zig").Window;
|
||||
pub const Config = @import("class/config.zig").Config;
|
||||
|
|
@ -79,7 +80,10 @@ pub fn Common(
|
|||
fn set(self: *Self, value: *const gobject.Value) void {
|
||||
const priv = private(self);
|
||||
if (@field(priv, name)) |v| {
|
||||
glib.ext.destroy(v);
|
||||
ext.boxedFree(
|
||||
@typeInfo(@TypeOf(v)).pointer.child,
|
||||
v,
|
||||
);
|
||||
}
|
||||
|
||||
const T = @TypeOf(@field(priv, name));
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ const input = @import("../../../input.zig");
|
|||
const CoreSurface = @import("../../../Surface.zig");
|
||||
const gtk_version = @import("../gtk_version.zig");
|
||||
const adw_version = @import("../adw_version.zig");
|
||||
const ext = @import("../ext.zig");
|
||||
const gresource = @import("../build/gresource.zig");
|
||||
const Common = @import("../class.zig").Common;
|
||||
const Config = @import("config.zig").Config;
|
||||
|
|
@ -55,29 +56,39 @@ pub const SplitTree = extern struct {
|
|||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const tree = struct {
|
||||
pub const name = "tree";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
?*Surface.Tree,
|
||||
.{
|
||||
.nick = "Tree Model",
|
||||
.blurb = "Underlying data model for the tree.",
|
||||
.accessor = C.privateBoxedFieldAccessor("tree"),
|
||||
},
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const Private = struct {
|
||||
/// The tree datastructure containing all of our surface views.
|
||||
tree: Surface.Tree,
|
||||
tree: ?*Surface.Tree,
|
||||
|
||||
pub var offset: c_int = 0;
|
||||
};
|
||||
|
||||
fn init(self: *Self, _: *Class) callconv(.c) void {
|
||||
gtk.Widget.initTemplate(self.as(gtk.Widget));
|
||||
|
||||
// Start with an empty split tree.
|
||||
const priv = self.private();
|
||||
priv.tree = .empty;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Properties
|
||||
|
||||
pub fn getIsEmpty(self: *Self) bool {
|
||||
const priv = self.private();
|
||||
return priv.tree.isEmpty();
|
||||
const tree: *const Surface.Tree = self.private().tree orelse &.empty;
|
||||
return tree.isEmpty();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
|
|
@ -97,8 +108,10 @@ pub const SplitTree = extern struct {
|
|||
|
||||
fn finalize(self: *Self) callconv(.c) void {
|
||||
const priv = self.private();
|
||||
priv.tree.deinit();
|
||||
priv.tree = .empty;
|
||||
if (priv.tree) |tree| {
|
||||
ext.boxedFree(Surface.Tree, tree);
|
||||
priv.tree = null;
|
||||
}
|
||||
|
||||
gobject.Object.virtual_methods.finalize.call(
|
||||
Class.parent,
|
||||
|
|
@ -109,6 +122,14 @@ pub const SplitTree = extern struct {
|
|||
//---------------------------------------------------------------
|
||||
// Signal handlers
|
||||
|
||||
fn propTree(
|
||||
self: *Self,
|
||||
_: *gobject.ParamSpec,
|
||||
_: ?*anyopaque,
|
||||
) callconv(.c) void {
|
||||
self.as(gobject.Object).notifyByPspec(properties.@"is-empty".impl.param_spec);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Class
|
||||
|
||||
|
|
@ -137,11 +158,13 @@ pub const SplitTree = extern struct {
|
|||
// Properties
|
||||
gobject.ext.registerProperties(class, &.{
|
||||
properties.@"is-empty".impl,
|
||||
properties.tree.impl,
|
||||
});
|
||||
|
||||
// Bindings
|
||||
|
||||
// Template Callbacks
|
||||
class.bindTemplateCallback("notify_tree", &propTree);
|
||||
|
||||
// Signals
|
||||
|
||||
|
|
|
|||
|
|
@ -2,15 +2,28 @@ using Gtk 4.0;
|
|||
using Adw 1;
|
||||
|
||||
template $GhosttySplitTree: Adw.Bin {
|
||||
// This could be a lot more visually pleasing but in practice this doesn't
|
||||
// ever happen at the time of writing this comment. A surface-less split
|
||||
// tree always closes its parent.
|
||||
Label {
|
||||
visible: bind template.is-empty;
|
||||
// Purposely not localized currently because this shouldn't really
|
||||
// ever appear. When we have a situation it does appear, we may want
|
||||
// to change the styling and text so I don't want to burden localizers
|
||||
// to handle this yet.
|
||||
label: "No surfaces.";
|
||||
notify::tree => $notify_tree();
|
||||
|
||||
Box {
|
||||
orientation: vertical;
|
||||
|
||||
Box surface_box {
|
||||
visible: bind template.is-empty inverted;
|
||||
orientation: vertical;
|
||||
hexpand: true;
|
||||
vexpand: true;
|
||||
}
|
||||
|
||||
// This could be a lot more visually pleasing but in practice this doesn't
|
||||
// ever happen at the time of writing this comment. A surface-less split
|
||||
// tree always closes its parent.
|
||||
Label {
|
||||
visible: bind template.is-empty;
|
||||
// Purposely not localized currently because this shouldn't really
|
||||
// ever appear. When we have a situation it does appear, we may want
|
||||
// to change the styling and text so I don't want to burden localizers
|
||||
// to handle this yet.
|
||||
label: "No surfaces.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const build_config = @import("../build_config.zig");
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
|
|
@ -116,6 +117,25 @@ pub fn SplitTree(comptime V: type) type {
|
|||
self.* = undefined;
|
||||
}
|
||||
|
||||
/// Clone this tree, returning a new tree with the same nodes.
|
||||
pub fn clone(self: *const Self, gpa: Allocator) Allocator.Error!Self {
|
||||
// Create a new arena allocator for the clone.
|
||||
var arena = ArenaAllocator.init(gpa);
|
||||
errdefer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
// Allocate a new nodes array and copy the existing nodes into it.
|
||||
const nodes = try alloc.dupe(Node, self.nodes);
|
||||
|
||||
// Increase the reference count of all the views in the nodes.
|
||||
try refNodes(gpa, nodes);
|
||||
|
||||
return .{
|
||||
.arena = arena,
|
||||
.nodes = nodes,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns true if this is an empty tree.
|
||||
pub fn isEmpty(self: *const Self) bool {
|
||||
// An empty tree has no nodes.
|
||||
|
|
@ -685,6 +705,51 @@ pub fn SplitTree(comptime V: type) type {
|
|||
else => @compileError("invalid view unref function"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Make this a valid gobject if we're in a GTK environment.
|
||||
pub const getGObjectType = switch (build_config.app_runtime) {
|
||||
.gtk, .@"gtk-ng" => @import("gobject").ext.defineBoxed(
|
||||
Self,
|
||||
.{
|
||||
// To get the type name we get the non-qualified type name
|
||||
// of the view and append that to `GhosttySplitTree`.
|
||||
.name = name: {
|
||||
const type_name = @typeName(View);
|
||||
const last = if (std.mem.lastIndexOfScalar(
|
||||
u8,
|
||||
type_name,
|
||||
'.',
|
||||
)) |idx|
|
||||
type_name[idx + 1 ..]
|
||||
else
|
||||
type_name;
|
||||
assert(last.len > 0);
|
||||
break :name "GhosttySplitTree" ++ last;
|
||||
},
|
||||
|
||||
.funcs = .{
|
||||
// The @ptrCast below is to workaround this bug:
|
||||
// https://github.com/ianprime0509/zig-gobject/issues/115
|
||||
.copy = @ptrCast(&struct {
|
||||
fn copy(self: *Self) callconv(.c) *Self {
|
||||
const ptr = @import("glib").ext.create(Self);
|
||||
const alloc = self.arena.child_allocator;
|
||||
ptr.* = self.clone(alloc) catch @panic("oom");
|
||||
return ptr;
|
||||
}
|
||||
}.copy),
|
||||
.free = @ptrCast(&struct {
|
||||
fn free(self: *Self) callconv(.c) void {
|
||||
self.deinit();
|
||||
@import("glib").ext.destroy(self);
|
||||
}
|
||||
}.free),
|
||||
},
|
||||
},
|
||||
),
|
||||
|
||||
.none => void,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue