apprt/gtk-ng: split tree new split actions
parent
4742177daa
commit
ae5dc3a4fb
|
|
@ -1,6 +1,7 @@
|
|||
const std = @import("std");
|
||||
const build_config = @import("../../../build_config.zig");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const adw = @import("adw");
|
||||
const gio = @import("gio");
|
||||
const glib = @import("glib");
|
||||
|
|
@ -36,6 +37,30 @@ pub const SplitTree = extern struct {
|
|||
});
|
||||
|
||||
pub const properties = struct {
|
||||
/// The active surface is the surface that should be receiving all
|
||||
/// surface-targeted actions. This is usually the focused surface,
|
||||
/// but may also not be focused if the user has selected a non-surface
|
||||
/// widget.
|
||||
pub const @"active-surface" = struct {
|
||||
pub const name = "active-surface";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
?*Surface,
|
||||
.{
|
||||
.nick = "Active Surface",
|
||||
.blurb = "The currently active surface.",
|
||||
.accessor = gobject.ext.typedAccessor(
|
||||
Self,
|
||||
?*Surface,
|
||||
.{
|
||||
.getter = getActiveSurface,
|
||||
},
|
||||
),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const @"has-surfaces" = struct {
|
||||
pub const name = "has-surfaces";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
|
|
@ -98,11 +123,132 @@ pub const SplitTree = extern struct {
|
|||
|
||||
fn init(self: *Self, _: *Class) callconv(.c) void {
|
||||
gtk.Widget.initTemplate(self.as(gtk.Widget));
|
||||
|
||||
// Initialize our actions
|
||||
self.initActions();
|
||||
}
|
||||
|
||||
fn initActions(self: *Self) void {
|
||||
// The set of actions. Each action has (in order):
|
||||
// [0] The action name
|
||||
// [1] The callback function
|
||||
// [2] The glib.VariantType of the parameter
|
||||
//
|
||||
// For action names:
|
||||
// https://docs.gtk.org/gio/type_func.Action.name_is_valid.html
|
||||
const actions = .{
|
||||
// All of these will eventually take a target surface parameter.
|
||||
// For now all our targets originate from the focused surface.
|
||||
.{ "new-left", actionNew, null },
|
||||
.{ "new-right", actionNew, null },
|
||||
.{ "new-up", actionNew, null },
|
||||
.{ "new-down", actionNew, null },
|
||||
};
|
||||
|
||||
// We need to collect our actions into a group since we're just
|
||||
// a plain widget that doesn't implement ActionGroup directly.
|
||||
const group = gio.SimpleActionGroup.new();
|
||||
errdefer group.unref();
|
||||
const map = group.as(gio.ActionMap);
|
||||
inline for (actions) |entry| {
|
||||
const action = gio.SimpleAction.new(
|
||||
entry[0],
|
||||
entry[2],
|
||||
);
|
||||
defer action.unref();
|
||||
_ = gio.SimpleAction.signals.activate.connect(
|
||||
action,
|
||||
*Self,
|
||||
entry[1],
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
map.addAction(action.as(gio.Action));
|
||||
}
|
||||
|
||||
self.as(gtk.Widget).insertActionGroup(
|
||||
"split-tree",
|
||||
group.as(gio.ActionGroup),
|
||||
);
|
||||
}
|
||||
|
||||
/// Create a new split in the given direction from the currently
|
||||
/// active surface.
|
||||
///
|
||||
/// If the tree is empty this will create a new tree with a new surface
|
||||
/// and ignore the direction.
|
||||
///
|
||||
/// The parent will be used as the parent of the surface regardless of
|
||||
/// if that parent is in this split tree or not. This allows inheriting
|
||||
/// surface properties from anywhere.
|
||||
pub fn newSplit(
|
||||
self: *Self,
|
||||
direction: Surface.Tree.Split.Direction,
|
||||
parent_: ?*Surface,
|
||||
) Allocator.Error!void {
|
||||
const alloc = Application.default().allocator();
|
||||
|
||||
// Create our new surface.
|
||||
const surface: *Surface = .new();
|
||||
defer surface.unref();
|
||||
_ = surface.refSink();
|
||||
|
||||
// Inherit properly if we were asked to.
|
||||
if (parent_) |p| {
|
||||
if (p.core()) |core| {
|
||||
surface.setParent(core);
|
||||
}
|
||||
}
|
||||
|
||||
// Create our tree
|
||||
var single_tree = try Surface.Tree.init(alloc, surface);
|
||||
defer single_tree.deinit();
|
||||
|
||||
// If we have no tree yet, then this becomes our tree and we're done.
|
||||
const old_tree = self.getTree() orelse {
|
||||
self.setTree(&single_tree);
|
||||
return;
|
||||
};
|
||||
|
||||
// The handle we create the split relative to. Today this is the active
|
||||
// surface but this might be the handle of the given parent if we want.
|
||||
const handle = self.getActiveSurfaceHandle() orelse 0;
|
||||
|
||||
// Create our split!
|
||||
var new_tree = try old_tree.split(
|
||||
alloc,
|
||||
handle,
|
||||
direction,
|
||||
&single_tree,
|
||||
);
|
||||
defer new_tree.deinit();
|
||||
self.setTree(&new_tree);
|
||||
|
||||
// Focus our new surface
|
||||
surface.grabFocus();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Properties
|
||||
|
||||
/// Get the currently active surface. See the "active-surface" property.
|
||||
/// This does not ref the value.
|
||||
pub fn getActiveSurface(self: *Self) ?*Surface {
|
||||
const tree = self.getTree() orelse return null;
|
||||
const handle = self.getActiveSurfaceHandle() orelse return null;
|
||||
return tree.nodes[handle].leaf;
|
||||
}
|
||||
|
||||
fn getActiveSurfaceHandle(self: *Self) ?Surface.Tree.Node.Handle {
|
||||
const tree = self.getTree() orelse return null;
|
||||
var it = tree.iterator();
|
||||
while (it.next()) |entry| {
|
||||
if (entry.view.getFocused()) return entry.handle;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getHasSurfaces(self: *Self) bool {
|
||||
const tree: *const Surface.Tree = self.private().tree orelse &.empty;
|
||||
return !tree.isEmpty();
|
||||
|
|
@ -185,6 +331,20 @@ pub const SplitTree = extern struct {
|
|||
//---------------------------------------------------------------
|
||||
// Signal handlers
|
||||
|
||||
pub fn actionNew(
|
||||
_: *gio.SimpleAction,
|
||||
parameter_: ?*glib.Variant,
|
||||
self: *Self,
|
||||
) callconv(.c) void {
|
||||
_ = parameter_;
|
||||
self.newSplit(
|
||||
.right,
|
||||
self.getActiveSurface(),
|
||||
) catch |err| {
|
||||
log.warn("new split failed error={}", .{err});
|
||||
};
|
||||
}
|
||||
|
||||
fn propTree(
|
||||
self: *Self,
|
||||
_: *gobject.ParamSpec,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ pub const Tab = extern struct {
|
|||
});
|
||||
|
||||
pub const properties = struct {
|
||||
/// The active surface is the focus that should be receiving all
|
||||
/// The active surface is the surface that should be receiving all
|
||||
/// surface-targeted actions. This is usually the focused surface,
|
||||
/// but may also not be focused if the user has selected a non-surface
|
||||
/// widget.
|
||||
|
|
@ -164,23 +164,15 @@ pub const Tab = extern struct {
|
|||
.{},
|
||||
);
|
||||
|
||||
// A tab always starts with a single surface.
|
||||
const surface: *Surface = .new();
|
||||
defer surface.unref();
|
||||
_ = surface.refSink();
|
||||
const alloc = Application.default().allocator();
|
||||
if (Surface.Tree.init(alloc, surface)) |tree| {
|
||||
priv.split_tree.setTree(&tree);
|
||||
|
||||
// Hacky because we need a non-const result.
|
||||
var mut = tree;
|
||||
mut.deinit();
|
||||
} else |_| {
|
||||
// TODO: We should make our "no surfaces" state more aesthetically
|
||||
// pleasing and show something like an "Oops, something went wrong"
|
||||
// message. For now, this is incredibly unlikely.
|
||||
@panic("oom");
|
||||
}
|
||||
// Create our initial surface in the split tree.
|
||||
priv.split_tree.newSplit(.right, null) catch |err| switch (err) {
|
||||
error.OutOfMemory => {
|
||||
// TODO: We should make our "no surfaces" state more aesthetically
|
||||
// pleasing and show something like an "Oops, something went wrong"
|
||||
// message. For now, this is incredibly unlikely.
|
||||
@panic("oom");
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn connectSurfaceHandlers(
|
||||
|
|
@ -232,13 +224,7 @@ pub const Tab = extern struct {
|
|||
/// Get the currently active surface. See the "active-surface" property.
|
||||
/// This does not ref the value.
|
||||
pub fn getActiveSurface(self: *Self) ?*Surface {
|
||||
const tree = self.getSurfaceTree() orelse return null;
|
||||
var it = tree.iterator();
|
||||
while (it.next()) |entry| {
|
||||
if (entry.view.getFocused()) return entry.view;
|
||||
}
|
||||
|
||||
return null;
|
||||
return self.getSplitTree().getActiveSurface();
|
||||
}
|
||||
|
||||
/// Get the surface tree of this tab.
|
||||
|
|
|
|||
|
|
@ -172,22 +172,22 @@ menu context_menu_model {
|
|||
|
||||
item {
|
||||
label: _("Split Up");
|
||||
action: "win.split-up";
|
||||
action: "split-tree.new-up";
|
||||
}
|
||||
|
||||
item {
|
||||
label: _("Split Down");
|
||||
action: "win.split-down";
|
||||
action: "split-tree.new-down";
|
||||
}
|
||||
|
||||
item {
|
||||
label: _("Split Left");
|
||||
action: "win.split-left";
|
||||
action: "split-tree.new-left";
|
||||
}
|
||||
|
||||
item {
|
||||
label: _("Split Right");
|
||||
action: "win.split-right";
|
||||
action: "split-tree.new-right";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue