First draft of feature implementation

This commit is contained in:
Ivan Duran
2024-11-01 15:13:37 +03:00
parent c413f2445d
commit fe82c7d367
8 changed files with 103 additions and 44 deletions

View File

@ -312,8 +312,8 @@ class AppDelegate: NSObject,
syncMenuShortcut(action: "close_surface", menuItem: self.menuClose)
syncMenuShortcut(action: "close_window", menuItem: self.menuCloseWindow)
syncMenuShortcut(action: "close_all_windows", menuItem: self.menuCloseAllWindows)
syncMenuShortcut(action: "new_split:right", menuItem: self.menuSplitRight)
syncMenuShortcut(action: "new_split:down", menuItem: self.menuSplitDown)
syncMenuShortcut(action: "new_split:right,50%", menuItem: self.menuSplitRight)
syncMenuShortcut(action: "new_split:down,50%", menuItem: self.menuSplitDown)
syncMenuShortcut(action: "copy_to_clipboard", menuItem: self.menuCopy)
syncMenuShortcut(action: "paste_from_clipboard", menuItem: self.menuPaste)

View File

@ -3919,18 +3919,25 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
.{ .amount = position },
),
.new_split => |direction| try self.rt_app.performAction(
.new_split => |value| try self.rt_app.performAction(
.{ .surface = self },
.new_split,
switch (direction) {
.right => .right,
.left => .left,
.down => .down,
.up => .up,
.auto => if (self.screen_size.width > self.screen_size.height)
.right
else
.down,
.{
.percent = std.fmt.parseInt(
u16,
value[1],
10,
) catch return error.InvalidType,
.direction = switch (value[0]) {
.right => .right,
.left => .left,
.down => .down,
.up => .up,
.auto => if (self.screen_size.width > self.screen_size.height)
.right
else
.down,
},
},
),

View File

@ -282,13 +282,19 @@ pub const Action = union(Key) {
}
};
// This is made extern (c_int) to make interop easier with our embedded
// runtime. The small size cost doesn't make a difference in our union.
pub const SplitDirection = enum(c_int) {
right,
down,
left,
up,
/// The percentage and direction to create the split
pub const SplitDirection = extern struct {
percent: u16,
direction: Direction,
// This is made extern (c_int) to make interop easier with our embedded
// runtime. The small size cost doesn't make a difference in our union.
pub const Direction = enum(c_int) {
right,
down,
left,
up,
};
};
// This is made extern (c_int) to make interop easier with our embedded

View File

@ -548,13 +548,13 @@ fn moveTab(_: *App, target: apprt.Target, move_tab: apprt.action.MoveTab) void {
fn newSplit(
self: *App,
target: apprt.Target,
direction: apprt.action.SplitDirection,
new_split: apprt.action.SplitDirection,
) !void {
switch (target) {
.app => {},
.surface => |v| {
const alloc = self.core_app.alloc;
_ = try Split.create(alloc, v.rt_surface, direction);
_ = try Split.create(alloc, v.rt_surface, new_split);
},
}
}
@ -863,10 +863,10 @@ fn syncActionAccelerators(self: *App) !void {
try self.syncActionAccelerator("win.close", .{ .close_surface = {} });
try self.syncActionAccelerator("win.new_window", .{ .new_window = {} });
try self.syncActionAccelerator("win.new_tab", .{ .new_tab = {} });
try self.syncActionAccelerator("win.split_right", .{ .new_split = .right });
try self.syncActionAccelerator("win.split_down", .{ .new_split = .down });
try self.syncActionAccelerator("win.split_left", .{ .new_split = .left });
try self.syncActionAccelerator("win.split_up", .{ .new_split = .up });
try self.syncActionAccelerator("win.split_right", .{ .new_split = .{ .right, "50%" } });
try self.syncActionAccelerator("win.split_down", .{ .new_split = .{ .down, "50%" } });
try self.syncActionAccelerator("win.split_left", .{ .new_split = .{ .left, "50%" } });
try self.syncActionAccelerator("win.split_up", .{ .new_split = .{ .up, "50%" } });
try self.syncActionAccelerator("win.copy", .{ .copy_to_clipboard = {} });
try self.syncActionAccelerator("win.paste", .{ .paste_from_clipboard = {} });
try self.syncActionAccelerator("win.reset", .{ .reset = {} });

View File

@ -20,7 +20,7 @@ pub const Orientation = enum {
horizontal,
vertical,
pub fn fromDirection(direction: apprt.action.SplitDirection) Orientation {
pub fn fromDirection(direction: apprt.action.SplitDirection.Direction) Orientation {
return switch (direction) {
.right, .left => .horizontal,
.down, .up => .vertical,
@ -57,18 +57,18 @@ bottom_right: Surface.Container.Elem,
pub fn create(
alloc: Allocator,
sibling: *Surface,
direction: apprt.action.SplitDirection,
new_split: apprt.action.SplitDirection,
) !*Split {
var split = try alloc.create(Split);
errdefer alloc.destroy(split);
try split.init(sibling, direction);
try split.init(sibling, new_split);
return split;
}
pub fn init(
self: *Split,
sibling: *Surface,
direction: apprt.action.SplitDirection,
new_split: apprt.action.SplitDirection,
) !void {
// Create the new child surface for the other direction.
const alloc = sibling.app.core_app.alloc;
@ -79,7 +79,7 @@ pub fn init(
sibling.dimSurface();
// Create the actual GTKPaned, attach the proper children.
const orientation: c_uint = switch (direction) {
const orientation: c_uint = switch (new_split.direction) {
.right, .left => c.GTK_ORIENTATION_HORIZONTAL,
.down, .up => c.GTK_ORIENTATION_VERTICAL,
};
@ -94,7 +94,7 @@ pub fn init(
// we're inheriting its parent. The sibling points to its location
// in the split, and the surface points to the other location.
const container = sibling.container;
const tl: *Surface, const br: *Surface = switch (direction) {
const tl: *Surface, const br: *Surface = switch (new_split.direction) {
.right, .down => right_down: {
sibling.container = .{ .split_tl = &self.top_left };
surface.container = .{ .split_br = &self.bottom_right };
@ -113,7 +113,7 @@ pub fn init(
.container = container,
.top_left = .{ .surface = tl },
.bottom_right = .{ .surface = br },
.orientation = Orientation.fromDirection(direction),
.orientation = Orientation.fromDirection(new_split.direction),
};
// Replace the previous containers element with our split.
@ -125,6 +125,25 @@ pub fn init(
// added to the paned.
self.updateChildren();
// Skip resize logic if percentage is 50 (this is the default behavior)
if (new_split.percent != 50) {
const allocation = sibling.size;
const split_percentage: f32 = @as(f32, @floatFromInt(new_split.percent)) / 100;
const total_surface_size: f32 = switch (self.orientation) {
.horizontal => @floatFromInt(allocation.width),
.vertical => @floatFromInt(allocation.height),
};
// percentage to apply based on direction
const pct = switch (new_split.direction) {
.right, .down => 1 - split_percentage,
.left, .up => split_percentage,
};
const divider_position = @as(c_int, @intFromFloat(total_surface_size * pct));
c.gtk_paned_set_position(self.paned, divider_position);
}
// The new surface should always grab focus
surface.grabFocus();
}

View File

@ -811,7 +811,7 @@ fn gtkActionSplitRight(
) callconv(.C) void {
const self: *Window = @ptrCast(@alignCast(ud orelse return));
const surface = self.actionSurface() orelse return;
_ = surface.performBindingAction(.{ .new_split = .right }) catch |err| {
_ = surface.performBindingAction(.{ .new_split = .{ .right, "50%" } }) catch |err| {
log.warn("error performing binding action error={}", .{err});
return;
};
@ -824,7 +824,7 @@ fn gtkActionSplitDown(
) callconv(.C) void {
const self: *Window = @ptrCast(@alignCast(ud orelse return));
const surface = self.actionSurface() orelse return;
_ = surface.performBindingAction(.{ .new_split = .down }) catch |err| {
_ = surface.performBindingAction(.{ .new_split = .{ .down, "50%" } }) catch |err| {
log.warn("error performing binding action error={}", .{err});
return;
};
@ -837,7 +837,7 @@ fn gtkActionSplitLeft(
) callconv(.C) void {
const self: *Window = @ptrCast(@alignCast(ud orelse return));
const surface = self.actionSurface() orelse return;
_ = surface.performBindingAction(.{ .new_split = .left }) catch |err| {
_ = surface.performBindingAction(.{ .new_split = .{ .left, "50%" } }) catch |err| {
log.warn("error performing binding action error={}", .{err});
return;
};
@ -850,7 +850,7 @@ fn gtkActionSplitUp(
) callconv(.C) void {
const self: *Window = @ptrCast(@alignCast(ud orelse return));
const surface = self.actionSurface() orelse return;
_ = surface.performBindingAction(.{ .new_split = .up }) catch |err| {
_ = surface.performBindingAction(.{ .new_split = .{ .up, "50%" } }) catch |err| {
log.warn("error performing binding action error={}", .{err});
return;
};

View File

@ -1908,12 +1908,12 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config {
try result.keybind.set.put(
alloc,
.{ .key = .{ .translated = .o }, .mods = .{ .ctrl = true, .shift = true } },
.{ .new_split = .right },
.{ .new_split = .{ .right, "50%" } },
);
try result.keybind.set.put(
alloc,
.{ .key = .{ .translated = .e }, .mods = .{ .ctrl = true, .shift = true } },
.{ .new_split = .down },
.{ .new_split = .{ .down, "50%" } },
);
try result.keybind.set.put(
alloc,
@ -2171,12 +2171,12 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config {
try result.keybind.set.put(
alloc,
.{ .key = .{ .translated = .d }, .mods = .{ .super = true } },
.{ .new_split = .right },
.{ .new_split = .{ .right, "50%" } },
);
try result.keybind.set.put(
alloc,
.{ .key = .{ .translated = .d }, .mods = .{ .super = true, .shift = true } },
.{ .new_split = .down },
.{ .new_split = .{ .down, "50%" } },
);
try result.keybind.set.put(
alloc,

View File

@ -310,9 +310,9 @@ pub const Action = union(enum) {
/// This only works with libadwaita enabled currently.
toggle_tab_overview: void,
/// Create a new split in the given direction. The new split will appear in
/// Create a new split in the given direction and percentage. The new split will appear in
/// the direction given.
new_split: SplitDirection,
new_split: SplitParameter,
/// Focus on a split in a given direction.
goto_split: SplitFocusDirection,
@ -454,6 +454,13 @@ pub const Action = union(enum) {
auto, // splits along the larger direction
};
pub const Percentage = []const u8;
pub const SplitParameter = struct {
SplitDirection,
Percentage,
};
pub const SplitFocusDirection = enum {
previous,
next,
@ -500,6 +507,14 @@ pub const Action = union(enum) {
return std.fmt.parseFloat(T, value) catch return Error.InvalidFormat;
}
fn parsePercentage(value: []const u8) !Percentage {
if (value.len < 2) return Error.InvalidFormat;
if (value[value.len - 1] != '%') return Error.InvalidFormat;
const percent = value[0 .. value.len - 1];
_ = std.fmt.parseInt(u16, percent, 10) catch return Error.InvalidFormat;
return percent;
}
fn parseParameter(
comptime field: std.builtin.Type.UnionField,
param: []const u8,
@ -521,6 +536,7 @@ pub const Action = union(enum) {
.Enum => try parseEnum(field_.type, next),
.Int => try parseInt(field_.type, next),
.Float => try parseFloat(field_.type, next),
.Pointer => if (field_.type == Percentage) try parsePercentage(next),
else => unreachable,
};
}
@ -1688,10 +1704,21 @@ test "parse: action with enum" {
// parameter
{
const binding = try parseSingle("a=new_split:right");
// Note: The "50%" in the binding string gets parsed to just "50"
const binding = try parseSingle("a=new_split:right,50%");
try testing.expect(binding.action == .new_split);
try testing.expectEqual(Action.SplitDirection.right, binding.action.new_split);
try testing.expectEqual(Action.SplitDirection.right, binding.action.new_split[0]);
try testing.expectEqualSlices(u8, "50", binding.action.new_split[1]);
}
// missing unit %
try testing.expectError(Error.InvalidFormat, parseSingle("a=new_split:right,50"));
// too many
try testing.expectError(Error.InvalidFormat, parseSingle("a=new_split:right,30%,50%"));
// invalid type
try testing.expectError(Error.InvalidFormat, parseSingle("a=new_split:right,0.5%"));
}
test "parse: action with int" {