apprt/gtk-ng: setup split tree property
parent
fa08434b28
commit
70b050ebb4
|
|
@ -5,6 +5,7 @@ const glib = @import("glib");
|
||||||
const gobject = @import("gobject");
|
const gobject = @import("gobject");
|
||||||
const gtk = @import("gtk");
|
const gtk = @import("gtk");
|
||||||
|
|
||||||
|
const ext = @import("ext.zig");
|
||||||
pub const Application = @import("class/application.zig").Application;
|
pub const Application = @import("class/application.zig").Application;
|
||||||
pub const Window = @import("class/window.zig").Window;
|
pub const Window = @import("class/window.zig").Window;
|
||||||
pub const Config = @import("class/config.zig").Config;
|
pub const Config = @import("class/config.zig").Config;
|
||||||
|
|
@ -79,7 +80,10 @@ pub fn Common(
|
||||||
fn set(self: *Self, value: *const gobject.Value) void {
|
fn set(self: *Self, value: *const gobject.Value) void {
|
||||||
const priv = private(self);
|
const priv = private(self);
|
||||||
if (@field(priv, name)) |v| {
|
if (@field(priv, name)) |v| {
|
||||||
glib.ext.destroy(v);
|
ext.boxedFree(
|
||||||
|
@typeInfo(@TypeOf(v)).pointer.child,
|
||||||
|
v,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const T = @TypeOf(@field(priv, name));
|
const T = @TypeOf(@field(priv, name));
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ const input = @import("../../../input.zig");
|
||||||
const CoreSurface = @import("../../../Surface.zig");
|
const CoreSurface = @import("../../../Surface.zig");
|
||||||
const gtk_version = @import("../gtk_version.zig");
|
const gtk_version = @import("../gtk_version.zig");
|
||||||
const adw_version = @import("../adw_version.zig");
|
const adw_version = @import("../adw_version.zig");
|
||||||
|
const ext = @import("../ext.zig");
|
||||||
const gresource = @import("../build/gresource.zig");
|
const gresource = @import("../build/gresource.zig");
|
||||||
const Common = @import("../class.zig").Common;
|
const Common = @import("../class.zig").Common;
|
||||||
const Config = @import("config.zig").Config;
|
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 {
|
const Private = struct {
|
||||||
/// The tree datastructure containing all of our surface views.
|
/// The tree datastructure containing all of our surface views.
|
||||||
tree: Surface.Tree,
|
tree: ?*Surface.Tree,
|
||||||
|
|
||||||
pub var offset: c_int = 0;
|
pub var offset: c_int = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
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));
|
||||||
|
|
||||||
// Start with an empty split tree.
|
|
||||||
const priv = self.private();
|
|
||||||
priv.tree = .empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
pub fn getIsEmpty(self: *Self) bool {
|
pub fn getIsEmpty(self: *Self) bool {
|
||||||
const priv = self.private();
|
const tree: *const Surface.Tree = self.private().tree orelse &.empty;
|
||||||
return priv.tree.isEmpty();
|
return tree.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
|
|
@ -97,8 +108,10 @@ pub const SplitTree = extern struct {
|
||||||
|
|
||||||
fn finalize(self: *Self) callconv(.c) void {
|
fn finalize(self: *Self) callconv(.c) void {
|
||||||
const priv = self.private();
|
const priv = self.private();
|
||||||
priv.tree.deinit();
|
if (priv.tree) |tree| {
|
||||||
priv.tree = .empty;
|
ext.boxedFree(Surface.Tree, tree);
|
||||||
|
priv.tree = null;
|
||||||
|
}
|
||||||
|
|
||||||
gobject.Object.virtual_methods.finalize.call(
|
gobject.Object.virtual_methods.finalize.call(
|
||||||
Class.parent,
|
Class.parent,
|
||||||
|
|
@ -109,6 +122,14 @@ pub const SplitTree = extern struct {
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
// Signal handlers
|
// Signal handlers
|
||||||
|
|
||||||
|
fn propTree(
|
||||||
|
self: *Self,
|
||||||
|
_: *gobject.ParamSpec,
|
||||||
|
_: ?*anyopaque,
|
||||||
|
) callconv(.c) void {
|
||||||
|
self.as(gobject.Object).notifyByPspec(properties.@"is-empty".impl.param_spec);
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
// Class
|
// Class
|
||||||
|
|
||||||
|
|
@ -137,11 +158,13 @@ pub const SplitTree = extern struct {
|
||||||
// Properties
|
// Properties
|
||||||
gobject.ext.registerProperties(class, &.{
|
gobject.ext.registerProperties(class, &.{
|
||||||
properties.@"is-empty".impl,
|
properties.@"is-empty".impl,
|
||||||
|
properties.tree.impl,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bindings
|
// Bindings
|
||||||
|
|
||||||
// Template Callbacks
|
// Template Callbacks
|
||||||
|
class.bindTemplateCallback("notify_tree", &propTree);
|
||||||
|
|
||||||
// Signals
|
// Signals
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,28 @@ using Gtk 4.0;
|
||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $GhosttySplitTree: Adw.Bin {
|
template $GhosttySplitTree: Adw.Bin {
|
||||||
// This could be a lot more visually pleasing but in practice this doesn't
|
notify::tree => $notify_tree();
|
||||||
// ever happen at the time of writing this comment. A surface-less split
|
|
||||||
// tree always closes its parent.
|
Box {
|
||||||
Label {
|
orientation: vertical;
|
||||||
visible: bind template.is-empty;
|
|
||||||
// Purposely not localized currently because this shouldn't really
|
Box surface_box {
|
||||||
// ever appear. When we have a situation it does appear, we may want
|
visible: bind template.is-empty inverted;
|
||||||
// to change the styling and text so I don't want to burden localizers
|
orientation: vertical;
|
||||||
// to handle this yet.
|
hexpand: true;
|
||||||
label: "No surfaces.";
|
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 std = @import("std");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
const build_config = @import("../build_config.zig");
|
||||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
|
@ -116,6 +117,25 @@ pub fn SplitTree(comptime V: type) type {
|
||||||
self.* = undefined;
|
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.
|
/// Returns true if this is an empty tree.
|
||||||
pub fn isEmpty(self: *const Self) bool {
|
pub fn isEmpty(self: *const Self) bool {
|
||||||
// An empty tree has no nodes.
|
// An empty tree has no nodes.
|
||||||
|
|
@ -685,6 +705,51 @@ pub fn SplitTree(comptime V: type) type {
|
||||||
else => @compileError("invalid view unref function"),
|
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