Implement initial tiling layout.

For now, it's hardcoded to be a right-primary/stack layout (similar to
dwm but with the primary on the right) with the primary window getting
55% of the width of the screen.
This commit is contained in:
Ben Buhse 2026-01-19 14:29:08 -06:00
commit 61fd784246
No known key found for this signature in database
GPG key ID: 7916ACFCD38FD0B4
2 changed files with 80 additions and 24 deletions

View file

@ -4,6 +4,8 @@
const WindowManager = @This();
const PRIMARY_RATIO: f32 = 0.55;
context: *Context,
window_manager_v1: *river.WindowManagerV1,
@ -31,6 +33,60 @@ pub fn init(wm: *WindowManager, context: *Context, window_manager_v1: *river.Win
wm.window_manager_v1.setListener(*WindowManager, windowManagerV1Listener, wm);
}
/// Calculate primary/stack layout positions for all windows.
/// - Single window: fullscreen
/// - Multiple windows: stack (45% left, vertically tiled), primary (55% right)
pub fn calculatePrimaryStackLayout(wm: *WindowManager) void {
const count = wm.window_count;
if (count == 0) return;
const output = wm.outputs.first() orelse return;
// Output dimensions come as i32 from the protocol, convert to u31 for window dimensions
const output_width: u31 = @intCast(output.width);
const output_height: u31 = @intCast(output.height);
const output_x = output.x;
const output_y = output.y;
var index: u8 = 0;
var it = wm.windows.iterator(.forward);
while (it.next()) |window| {
if (count == 1) {
// Single window: fullscreen
window.new_x = output_x;
window.new_y = output_y;
window.new_width = output_width;
window.new_height = output_height;
} else {
// Multiple windows: primary/stack layout
const primary_width: u31 = @intFromFloat(@as(f32, @floatFromInt(output_width)) * PRIMARY_RATIO);
const stack_width: u31 = output_width - primary_width;
const stack_count = count - 1;
const stack_height: u31 = @divFloor(output_height, stack_count);
if (index == 0) {
// Primary window (first window) - right side
window.new_x = output_x + @as(i32, stack_width);
window.new_y = output_y;
window.new_width = primary_width;
window.new_height = output_height;
} else {
// Stack window - left side
const stack_index = index - 1;
window.new_x = output_x;
window.new_y = output_y + @as(i32, stack_index) * @as(i32, stack_height);
window.new_width = stack_width;
// Last stack window gets remaining height to avoid gaps from integer division
if (index == count - 1) {
window.new_height = output_height - stack_index * stack_height;
} else {
window.new_height = stack_height;
}
}
}
index += 1;
}
}
fn windowManagerV1Listener(window_manager_v1: *river.WindowManagerV1, event: river.WindowManagerV1.Event, wm: *WindowManager) void {
assert(wm.window_manager_v1 == window_manager_v1);
const context = wm.context;
@ -56,6 +112,8 @@ fn windowManagerV1Listener(window_manager_v1: *river.WindowManagerV1, event: riv
output.manage();
}
}
// Calculate layout before managing windows
wm.calculatePrimaryStackLayout();
{
var it = wm.windows.iterator(.forward);
while (it.next()) |window| {
@ -103,9 +161,6 @@ fn windowManagerV1Listener(window_manager_v1: *river.WindowManagerV1, event: riv
window.init(context, ev.id);
wm.windows.append(window);
wm.window_count += 1;
if (wm.window_count == 1) {
window.is_head = true;
}
log.debug("window_count = {d}", .{wm.window_count});
},
else => |ev| {