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_surface", menuItem: self.menuClose)
syncMenuShortcut(action: "close_window", menuItem: self.menuCloseWindow) syncMenuShortcut(action: "close_window", menuItem: self.menuCloseWindow)
syncMenuShortcut(action: "close_all_windows", menuItem: self.menuCloseAllWindows) syncMenuShortcut(action: "close_all_windows", menuItem: self.menuCloseAllWindows)
syncMenuShortcut(action: "new_split:right", menuItem: self.menuSplitRight) syncMenuShortcut(action: "new_split:right,50%", menuItem: self.menuSplitRight)
syncMenuShortcut(action: "new_split:down", menuItem: self.menuSplitDown) syncMenuShortcut(action: "new_split:down,50%", menuItem: self.menuSplitDown)
syncMenuShortcut(action: "copy_to_clipboard", menuItem: self.menuCopy) syncMenuShortcut(action: "copy_to_clipboard", menuItem: self.menuCopy)
syncMenuShortcut(action: "paste_from_clipboard", menuItem: self.menuPaste) 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 }, .{ .amount = position },
), ),
.new_split => |direction| try self.rt_app.performAction( .new_split => |value| try self.rt_app.performAction(
.{ .surface = self }, .{ .surface = self },
.new_split, .new_split,
switch (direction) { .{
.right => .right, .percent = std.fmt.parseInt(
.left => .left, u16,
.down => .down, value[1],
.up => .up, 10,
.auto => if (self.screen_size.width > self.screen_size.height) ) catch return error.InvalidType,
.right .direction = switch (value[0]) {
else .right => .right,
.down, .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 /// The percentage and direction to create the split
// runtime. The small size cost doesn't make a difference in our union. pub const SplitDirection = extern struct {
pub const SplitDirection = enum(c_int) { percent: u16,
right, direction: Direction,
down,
left, // This is made extern (c_int) to make interop easier with our embedded
up, // 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 // 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( fn newSplit(
self: *App, self: *App,
target: apprt.Target, target: apprt.Target,
direction: apprt.action.SplitDirection, new_split: apprt.action.SplitDirection,
) !void { ) !void {
switch (target) { switch (target) {
.app => {}, .app => {},
.surface => |v| { .surface => |v| {
const alloc = self.core_app.alloc; 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.close", .{ .close_surface = {} });
try self.syncActionAccelerator("win.new_window", .{ .new_window = {} }); try self.syncActionAccelerator("win.new_window", .{ .new_window = {} });
try self.syncActionAccelerator("win.new_tab", .{ .new_tab = {} }); try self.syncActionAccelerator("win.new_tab", .{ .new_tab = {} });
try self.syncActionAccelerator("win.split_right", .{ .new_split = .right }); try self.syncActionAccelerator("win.split_right", .{ .new_split = .{ .right, "50%" } });
try self.syncActionAccelerator("win.split_down", .{ .new_split = .down }); try self.syncActionAccelerator("win.split_down", .{ .new_split = .{ .down, "50%" } });
try self.syncActionAccelerator("win.split_left", .{ .new_split = .left }); try self.syncActionAccelerator("win.split_left", .{ .new_split = .{ .left, "50%" } });
try self.syncActionAccelerator("win.split_up", .{ .new_split = .up }); try self.syncActionAccelerator("win.split_up", .{ .new_split = .{ .up, "50%" } });
try self.syncActionAccelerator("win.copy", .{ .copy_to_clipboard = {} }); try self.syncActionAccelerator("win.copy", .{ .copy_to_clipboard = {} });
try self.syncActionAccelerator("win.paste", .{ .paste_from_clipboard = {} }); try self.syncActionAccelerator("win.paste", .{ .paste_from_clipboard = {} });
try self.syncActionAccelerator("win.reset", .{ .reset = {} }); try self.syncActionAccelerator("win.reset", .{ .reset = {} });

View File

@ -20,7 +20,7 @@ pub const Orientation = enum {
horizontal, horizontal,
vertical, vertical,
pub fn fromDirection(direction: apprt.action.SplitDirection) Orientation { pub fn fromDirection(direction: apprt.action.SplitDirection.Direction) Orientation {
return switch (direction) { return switch (direction) {
.right, .left => .horizontal, .right, .left => .horizontal,
.down, .up => .vertical, .down, .up => .vertical,
@ -57,18 +57,18 @@ bottom_right: Surface.Container.Elem,
pub fn create( pub fn create(
alloc: Allocator, alloc: Allocator,
sibling: *Surface, sibling: *Surface,
direction: apprt.action.SplitDirection, new_split: apprt.action.SplitDirection,
) !*Split { ) !*Split {
var split = try alloc.create(Split); var split = try alloc.create(Split);
errdefer alloc.destroy(split); errdefer alloc.destroy(split);
try split.init(sibling, direction); try split.init(sibling, new_split);
return split; return split;
} }
pub fn init( pub fn init(
self: *Split, self: *Split,
sibling: *Surface, sibling: *Surface,
direction: apprt.action.SplitDirection, new_split: apprt.action.SplitDirection,
) !void { ) !void {
// Create the new child surface for the other direction. // Create the new child surface for the other direction.
const alloc = sibling.app.core_app.alloc; const alloc = sibling.app.core_app.alloc;
@ -79,7 +79,7 @@ pub fn init(
sibling.dimSurface(); sibling.dimSurface();
// Create the actual GTKPaned, attach the proper children. // 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, .right, .left => c.GTK_ORIENTATION_HORIZONTAL,
.down, .up => c.GTK_ORIENTATION_VERTICAL, .down, .up => c.GTK_ORIENTATION_VERTICAL,
}; };
@ -94,7 +94,7 @@ pub fn init(
// we're inheriting its parent. The sibling points to its location // we're inheriting its parent. The sibling points to its location
// in the split, and the surface points to the other location. // in the split, and the surface points to the other location.
const container = sibling.container; 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: { .right, .down => right_down: {
sibling.container = .{ .split_tl = &self.top_left }; sibling.container = .{ .split_tl = &self.top_left };
surface.container = .{ .split_br = &self.bottom_right }; surface.container = .{ .split_br = &self.bottom_right };
@ -113,7 +113,7 @@ pub fn init(
.container = container, .container = container,
.top_left = .{ .surface = tl }, .top_left = .{ .surface = tl },
.bottom_right = .{ .surface = br }, .bottom_right = .{ .surface = br },
.orientation = Orientation.fromDirection(direction), .orientation = Orientation.fromDirection(new_split.direction),
}; };
// Replace the previous containers element with our split. // Replace the previous containers element with our split.
@ -125,6 +125,25 @@ pub fn init(
// added to the paned. // added to the paned.
self.updateChildren(); 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 // The new surface should always grab focus
surface.grabFocus(); surface.grabFocus();
} }

View File

@ -811,7 +811,7 @@ fn gtkActionSplitRight(
) callconv(.C) void { ) callconv(.C) void {
const self: *Window = @ptrCast(@alignCast(ud orelse return)); const self: *Window = @ptrCast(@alignCast(ud orelse return));
const surface = self.actionSurface() 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}); log.warn("error performing binding action error={}", .{err});
return; return;
}; };
@ -824,7 +824,7 @@ fn gtkActionSplitDown(
) callconv(.C) void { ) callconv(.C) void {
const self: *Window = @ptrCast(@alignCast(ud orelse return)); const self: *Window = @ptrCast(@alignCast(ud orelse return));
const surface = self.actionSurface() 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}); log.warn("error performing binding action error={}", .{err});
return; return;
}; };
@ -837,7 +837,7 @@ fn gtkActionSplitLeft(
) callconv(.C) void { ) callconv(.C) void {
const self: *Window = @ptrCast(@alignCast(ud orelse return)); const self: *Window = @ptrCast(@alignCast(ud orelse return));
const surface = self.actionSurface() 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}); log.warn("error performing binding action error={}", .{err});
return; return;
}; };
@ -850,7 +850,7 @@ fn gtkActionSplitUp(
) callconv(.C) void { ) callconv(.C) void {
const self: *Window = @ptrCast(@alignCast(ud orelse return)); const self: *Window = @ptrCast(@alignCast(ud orelse return));
const surface = self.actionSurface() 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}); log.warn("error performing binding action error={}", .{err});
return; return;
}; };

View File

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

View File

@ -310,9 +310,9 @@ pub const Action = union(enum) {
/// This only works with libadwaita enabled currently. /// This only works with libadwaita enabled currently.
toggle_tab_overview: void, 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. /// the direction given.
new_split: SplitDirection, new_split: SplitParameter,
/// Focus on a split in a given direction. /// Focus on a split in a given direction.
goto_split: SplitFocusDirection, goto_split: SplitFocusDirection,
@ -454,6 +454,13 @@ pub const Action = union(enum) {
auto, // splits along the larger direction auto, // splits along the larger direction
}; };
pub const Percentage = []const u8;
pub const SplitParameter = struct {
SplitDirection,
Percentage,
};
pub const SplitFocusDirection = enum { pub const SplitFocusDirection = enum {
previous, previous,
next, next,
@ -500,6 +507,14 @@ pub const Action = union(enum) {
return std.fmt.parseFloat(T, value) catch return Error.InvalidFormat; 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( fn parseParameter(
comptime field: std.builtin.Type.UnionField, comptime field: std.builtin.Type.UnionField,
param: []const u8, param: []const u8,
@ -521,6 +536,7 @@ pub const Action = union(enum) {
.Enum => try parseEnum(field_.type, next), .Enum => try parseEnum(field_.type, next),
.Int => try parseInt(field_.type, next), .Int => try parseInt(field_.type, next),
.Float => try parseFloat(field_.type, next), .Float => try parseFloat(field_.type, next),
.Pointer => if (field_.type == Percentage) try parsePercentage(next),
else => unreachable, else => unreachable,
}; };
} }
@ -1688,10 +1704,21 @@ test "parse: action with enum" {
// parameter // 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.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" { test "parse: action with int" {