From dc1e38e737d824465cde7c8b5a60e7ebeaf6c6d7 Mon Sep 17 00:00:00 2001 From: Ben Buhse Date: Wed, 18 Feb 2026 15:39:58 -0600 Subject: [PATCH] Support river_window_v1.dimensions_hint This is currently only used when floating a window for the first time. If the window has preferred dimensions, we will use those isntead of the 75% of the screen size rule we were using before. --- src/Window.zig | 79 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 13 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index 5c38bfb..d1b5474 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -11,6 +11,7 @@ river_node_v1: *river.NodeV1, app_id: ?[]const u8 = null, title: ?[]const u8 = null, +parent: ?*river.WindowV1 = null, rect: utils.Rect = .{}, @@ -22,6 +23,7 @@ output: ?*Output, floating: bool = false, floating_rect: utils.Rect = .{}, +dimensions_hint: DimensionsHint = .{}, initialized: bool = false, @@ -64,6 +66,37 @@ pub const PendingRender = struct { show: ?bool = null, }; +pub const DimensionsHint = struct { + min_width: u31 = 0, + min_height: u31 = 0, + max_width: u31 = 0, + max_height: u31 = 0, + + fn preferredWidth(hint: DimensionsHint) ?u31 { + if (hint.min_width != 0 and hint.max_width != 0) + // Two separate divisions so we don't overflow the u31 + return hint.min_width / 2 + hint.max_width / 2 + else if (hint.min_width != 0) + return hint.min_width + else if (hint.max_width != 0) + return hint.max_width + else + return null; + } + + fn preferredHeight(hint: DimensionsHint) ?u31 { + if (hint.min_height != 0 and hint.max_height != 0) + // Two separate divisions so we don't overflow the u31 + return hint.min_height / 2 + hint.max_height / 2 + else if (hint.min_height != 0) + return hint.min_height + else if (hint.max_height != 0) + return hint.max_height + else + return null; + } +}; + pub fn create(context: *Context, river_window_v1: *river.WindowV1, output: ?*Output) !*Window { var window = try utils.gpa.create(Window); errdefer utils.gpa.destroy(window); @@ -144,19 +177,39 @@ fn windowListener(river_window_v1: *river.WindowV1, event: river.WindowV1.Event, assert(ev.width > 0 and ev.height > 0); window.pending_manage.dimensions = .{ .width = @intCast(ev.width), .height = @intCast(ev.height) }; }, - .dimensions_hint => { - // TODO: Use this for clamping windows on resize + .dimensions_hint => |ev| { + window.dimensions_hint = .{ + .min_width = @intCast(ev.min_width), + .min_height = @intCast(ev.min_height), + .max_width = @intCast(ev.max_width), + .max_height = @intCast(ev.max_height), + }; }, .app_id => |ev| { if (window.app_id) |app_id| utils.gpa.free(app_id); - window.app_id = utils.gpa.dupe(u8, std.mem.span(ev.app_id.?)) catch @panic("Out of memory"); + window.app_id = if (ev.app_id) |aid| + utils.gpa.dupe(u8, std.mem.span(aid)) catch @panic("Out of memory") + else + null; }, .title => |ev| { if (window.title) |title| utils.gpa.free(title); - window.title = utils.gpa.dupe(u8, std.mem.span(ev.title.?)) catch @panic("Out of memory"); + window.title = if (ev.title) |t| + utils.gpa.dupe(u8, std.mem.span(t)) catch @panic("Out of memory") + else + null; }, - .parent => { - // TODO: float this window directly over its parent + .parent => |ev| { + const parent = ev.parent orelse return; + window.parent = parent; + + // Make window float on top of its parent + window.pending_manage.floating = true; + const parent_window: *Window = @ptrCast(@alignCast(parent.getUserData() orelse return)); + window.pending_render.position = .{ + .x = parent_window.rect.x + @divTrunc(parent_window.rect.width, 2), + .y = parent_window.rect.y + @divTrunc(parent_window.rect.height, 2), + }; }, else => |ev| { log.debug("unhandled event: {s}", .{@tagName(ev)}); @@ -186,9 +239,7 @@ pub fn initialize(window: *Window) void { const res = window.applyRules(); if (res.tags) |tags| window.tags = tags; if (res.float) |should_float| - window.pending_manage.floating = should_float - else - window.pending_manage.floating = false; + window.pending_manage.floating = should_float; } pub fn manage(window: *Window) void { @@ -208,10 +259,12 @@ pub fn manage(window: *Window) void { river_window_v1.setTiled(.{}); if (window.floating_rect.width == 0) { - // Never floated before; use 75% of usable area, centered on output + // This window has never floated before, let's give it floating dimensions + // Go with the mid-point of the preferred width/height if the window has one + // If not, go with 75% of the output's usable size in the same dimension if (window.output) |output| { - window.floating_rect.width = @divFloor(output.usable_geometry.width * 3, 4); - window.floating_rect.height = @divFloor(output.usable_geometry.height * 3, 4); + window.floating_rect.width = if (window.dimensions_hint.preferredWidth()) |w| w else @divFloor(output.usable_geometry.width * 3, 4); + window.floating_rect.height = if (window.dimensions_hint.preferredHeight()) |h| h else @divFloor(output.usable_geometry.height * 3, 4); window.floating_rect.x = output.usable_geometry.x + @divFloor(output.usable_geometry.width, 2) - @divFloor(window.floating_rect.width, 2); window.floating_rect.y = output.usable_geometry.y + @divFloor(output.usable_geometry.height, 2) - @divFloor(window.floating_rect.height, 2); } else { @@ -233,7 +286,7 @@ pub fn manage(window: *Window) void { window.floating_rect.y = window.rect.y; } } - // Layout (pre-computed by WindowManager.calculatePrimaryStackLayout()) + // Layout (pre-computed by WindowManager.caluclateLayout()) if (pending_manage.dimensions) |dimensions| { window.rect.width = dimensions.width; window.rect.height = dimensions.height;