Implement left-side primary

This adds a new config `primary_side` that can be either `left` or `right`
and determines whether the primary stack is on the left or the right
side of the screen.
This commit is contained in:
Ben Buhse 2026-02-25 15:16:48 -06:00
commit 164ae9a7ab
No known key found for this signature in database
GPG key ID: 7916ACFCD38FD0B4
5 changed files with 42 additions and 18 deletions

View file

@ -28,6 +28,9 @@ primary_ratio: f32 = 0.55,
/// output_width * single_window_ratio and be centered on the output
single_window_ratio: f32 = 1.0,
/// Side the primary should be on
primary_side: PrimarySide = .left,
/// Where a new window should attach, top or bottom of the stack
attach_mode: AttachMode = .top,
/// Should focus change when the cursor moves onto a new window
@ -57,16 +60,16 @@ pub const PointerBind = pointer_bind.PointerBind;
pub const WindowRule = window_rule.Rule;
pub const WindowRuleAction = window_rule.Action;
pub const AttachMode = enum {
top,
bottom,
};
pub const PrimarySide = enum { left, right };
pub const AttachMode = enum { top, bottom };
const NodeName = enum {
attach_mode,
primary_count,
primary_ratio,
single_window_ratio,
primary_side,
focus_follows_pointer,
pointer_warp_on_focus_change,
wallpaper_image_path,
@ -208,6 +211,16 @@ fn load(config: *Config, reader: *Io.Reader) !void {
}
logDebugSettingNode(name, ratio_str);
},
.primary_side => {
const primary_side_str = utils.stripQuotes(node.arg(&parser, 0) orelse "");
if (std.meta.stringToEnum(PrimarySide, primary_side_str)) |mode| {
config.primary_side = mode;
logDebugSettingNode(name, primary_side_str);
} else {
logWarnInvalidNodeArg(name, primary_side_str);
continue;
}
},
.single_window_ratio => {
const ratio_str = utils.stripQuotes(node.arg(&parser, 0) orelse "");
const ratio = fmt.parseFloat(f32, ratio_str) catch {

View file

@ -639,8 +639,9 @@ pub fn render(output: *Output) void {
}
/// Calculate primary/stack layout positions for all windows.
/// - Single window: maximized
/// - Multiple windows: stack (45% left, vertically tiled), primary (55% right)
/// - Single window: window is told it's maximized and takes up usable_width * single_window_ratio width
/// - Multiple windows: two stacks, primary and secondary. By default, the stack is on the right and takes
/// up 55% of the output width, but this can be configured. Each tagmask has its own primary ratio and count.
fn calculateLayout(output: *Output) void {
// Shouldn't be called if height/width are not positive
assert(output.geometry.width > 0 and output.geometry.height > 0);
@ -711,6 +712,12 @@ fn calculateLayout(output: *Output) void {
else
0;
// Determine the stack x coordinates based on whether primary is set to the left or right
const primary_x, const stack_x = switch (output.context.config.primary_side) {
.right => .{ output_x + @as(i32, stack_width), output_x },
.left => .{ output_x, output_x + @as(i32, primary_width) },
};
// Iterate through the active windows and apply positions
var i: u31 = 0;
while (active_list.popFirst()) |node| : (i += 1) {
@ -718,9 +725,9 @@ fn calculateLayout(output: *Output) void {
window.pending_manage.maximized = false;
if (i < primary_count) {
// Primary window(s) - right side
// Primary window(s)
window.pending_render.position = .{
.x = output_x + @as(i32, stack_width),
.x = primary_x,
.y = output_y + @as(i32, i) * @as(i32, primary_height),
};
const pending_width = primary_width;
@ -734,10 +741,10 @@ fn calculateLayout(output: *Output) void {
.height = pending_height,
};
} else {
// Stack window(s) - left side
// Stack window(s)
const stack_index = i - primary_count;
window.pending_render.position = .{
.x = output_x,
.x = stack_x,
.y = output_y + @as(i32, stack_index) * @as(i32, stack_height),
};
const pending_width = stack_width;