Implement single_window_ratio
This is a new config option that allows the user to set the width ratio when only a single window is tiled and visible. The main idea is that, on ultrawides, a single window taking the full width could be ugly. With this new config, you can make the window take a smaller width. I also renamed consts to snake_case instead of SCREAMING_CASE and fixed a bug where the default primary_count and primary_ratio weren't updated on config reload.
This commit is contained in:
parent
07794019dc
commit
b921751100
6 changed files with 74 additions and 10 deletions
|
|
@ -45,6 +45,7 @@ wallpaper_image_path "~/Pictures/wallpaper.png"
|
|||
| `attach_mode` | enum | `top` | Where new windows go in the stack (`top` or `bottom`) |
|
||||
| `primary_count` | u8 | `1` | Number of windows in the primary stack (0+) |
|
||||
| `primary_ratio` | float | `0.55` | Proportion of output width for the primary stack (0.10-0.90) |
|
||||
| `single_window_ratio` | float | `1.00` | Proportion of output width taken when a single tiled window is visible (0.10-1.00) |
|
||||
| `focus_follows_pointer` | bool | `#true` | Focus follows the pointer between windows |
|
||||
| `pointer_warp_on_focus_change` | bool | `#true` | Warp pointer to center of newly-focused windows |
|
||||
| `wallpaper_image_path` | string | none | Path to wallpaper image |
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ attach_mode top
|
|||
primary_count 1
|
||||
// Proportion of output width taken by the primary stack
|
||||
primary_ratio 0.55
|
||||
// Proportion of output width taken by a window if it's the only visible tiled window
|
||||
// This is intended to be useful for ultrawides where a very wide window might not look very nice
|
||||
// When this is < 1.0 and only one window is being tiled, the window will have width
|
||||
// output_width * single_window_ratio and be centered on the output
|
||||
single_window_ratio 0.70
|
||||
// Whether mousing over a new window should move focus
|
||||
focus_follows_pointer #true
|
||||
// Whether the focus should warp to the center of newly-focused windows
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@
|
|||
|
||||
const Config = @This();
|
||||
|
||||
const CONFIG_FILE = "beansprout/config.kdl";
|
||||
const config_file = "beansprout/config.kdl";
|
||||
|
||||
pub const min_primary_ratio = 0.10;
|
||||
pub const max_primary_ratio = 0.90;
|
||||
|
||||
/// Width of window borders in pixels
|
||||
border_width: u8 = 2,
|
||||
|
|
@ -19,6 +22,11 @@ primary_count: u8 = 1,
|
|||
/// Proportion of output width taken by the primary stack
|
||||
/// This is a global default, but each tagmask can have its own value
|
||||
primary_ratio: f32 = 0.55,
|
||||
/// Proportion of output width taken by a window if it's the only visible tiled window
|
||||
/// This is intended to be useful for ultrawides where a very wide window might not look very nice
|
||||
/// When this is < 1.0 and only one window is being tiled, the window will have width
|
||||
/// output_width * single_window_ratio and be centered on the output
|
||||
single_window_ratio: f32 = 1.0,
|
||||
|
||||
/// Where a new window should attach, top or bottom of the stack
|
||||
attach_mode: AttachMode = .top,
|
||||
|
|
@ -58,6 +66,7 @@ const NodeName = enum {
|
|||
attach_mode,
|
||||
primary_count,
|
||||
primary_ratio,
|
||||
single_window_ratio,
|
||||
focus_follows_pointer,
|
||||
pointer_warp_on_focus_change,
|
||||
wallpaper_image_path,
|
||||
|
|
@ -80,7 +89,7 @@ pub fn create() !*Config {
|
|||
defer utils.gpa.free(config_dir);
|
||||
|
||||
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const config_path = std.fmt.bufPrint(&path_buf, "{s}/{s}", .{ config_dir, CONFIG_FILE }) catch return config;
|
||||
const config_path = std.fmt.bufPrint(&path_buf, "{s}/{s}", .{ config_dir, config_file }) catch return config;
|
||||
|
||||
const file = fs.openFileAbsolute(config_path, .{}) catch break :blk;
|
||||
|
||||
|
|
@ -193,7 +202,24 @@ fn load(config: *Config, reader: *Io.Reader) !void {
|
|||
logWarnInvalidNodeArg(name, ratio_str);
|
||||
continue;
|
||||
};
|
||||
config.primary_ratio = std.math.clamp(ratio, 0.10, 0.90);
|
||||
config.primary_ratio = std.math.clamp(ratio, min_primary_ratio, max_primary_ratio);
|
||||
if (ratio != config.primary_ratio) {
|
||||
log.warn("primary_ratio outside of valid range ({d} to {d}); clamping it", .{ min_primary_ratio, max_primary_ratio });
|
||||
}
|
||||
logDebugSettingNode(name, ratio_str);
|
||||
},
|
||||
.single_window_ratio => {
|
||||
const ratio_str = utils.stripQuotes(node.arg(&parser, 0) orelse "");
|
||||
const ratio = fmt.parseFloat(f32, ratio_str) catch {
|
||||
logWarnInvalidNodeArg(name, ratio_str);
|
||||
continue;
|
||||
};
|
||||
// We use 1.00 here because it doesn't make sense for a window to have a ratio > 1,
|
||||
// i.e. for it to be wider than the output
|
||||
config.single_window_ratio = std.math.clamp(ratio, min_primary_ratio, 1.00);
|
||||
if (ratio != config.single_window_ratio) {
|
||||
log.warn("single_window_ratio outside of valid range ({d} to {d}); clamping it", .{ min_primary_ratio, 1.00 });
|
||||
}
|
||||
logDebugSettingNode(name, ratio_str);
|
||||
},
|
||||
.attach_mode => {
|
||||
|
|
|
|||
|
|
@ -151,6 +151,14 @@ pub fn manage(context: *Context) void {
|
|||
context.config = new_config;
|
||||
context.initialized = false;
|
||||
|
||||
// Update output defaults from new config
|
||||
var out_it_cfg = context.wm.outputs.iterator(.forward);
|
||||
while (out_it_cfg.next()) |output| {
|
||||
output.primary_ratio = new_config.primary_ratio;
|
||||
output.primary_count = new_config.primary_count;
|
||||
output.single_window_ratio = new_config.single_window_ratio;
|
||||
}
|
||||
|
||||
// Mark all libinput devices as needing config re-application
|
||||
var dev_it = context.im.libinput_devices.iterator(.forward);
|
||||
while (dev_it.next()) |libinput_device| {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ primary_ratio: f32,
|
|||
/// Number of windows in the primary stack
|
||||
primary_count: u8,
|
||||
|
||||
/// Proportion of output width taken by a window when it is the only visible tiled window
|
||||
single_window_ratio: f32,
|
||||
|
||||
/// Per-tagmask layout overrides
|
||||
/// These only get added when the user modifies primary count or ratio
|
||||
/// Any tagmask NOT in this map keeps using the defaults from Config
|
||||
|
|
@ -74,6 +77,7 @@ pub const PendingManage = struct {
|
|||
tags: ?u32 = null,
|
||||
primary_ratio: ?f32 = null,
|
||||
primary_count: ?u8 = null,
|
||||
single_window_ratio: ?f32 = null,
|
||||
};
|
||||
|
||||
pub fn create(context: *Context, river_output_v1: *river.OutputV1) !*Output {
|
||||
|
|
@ -88,6 +92,7 @@ pub fn create(context: *Context, river_output_v1: *river.OutputV1) !*Output {
|
|||
.tag_overlay = null,
|
||||
.primary_count = context.config.primary_count,
|
||||
.primary_ratio = context.config.primary_ratio,
|
||||
.single_window_ratio = context.config.single_window_ratio,
|
||||
.windows = undefined, // we will initialize this shortly
|
||||
.link = undefined, // Handled by the wl.list
|
||||
};
|
||||
|
|
@ -536,12 +541,24 @@ pub fn manage(output: *Output) void {
|
|||
|
||||
if (output.pending_manage.primary_ratio) |primary_ratio| {
|
||||
// Ratios outside of this range could cause crashes (when doing the layout calculation)
|
||||
output.primary_ratio = std.math.clamp(primary_ratio, 0.10, 0.90);
|
||||
output.primary_ratio = std.math.clamp(
|
||||
primary_ratio,
|
||||
Config.min_primary_ratio,
|
||||
Config.max_primary_ratio,
|
||||
);
|
||||
}
|
||||
if (output.pending_manage.primary_count) |primary_count| {
|
||||
// Don't allow less than 1 primary
|
||||
output.primary_count = @max(1, primary_count);
|
||||
}
|
||||
if (output.pending_manage.single_window_ratio) |single_window_ratio| {
|
||||
output.single_window_ratio = std.math.clamp(
|
||||
single_window_ratio,
|
||||
Config.min_primary_ratio,
|
||||
1.00,
|
||||
);
|
||||
}
|
||||
|
||||
if (output.pending_manage.tags) |new_tags| {
|
||||
// Save current layout for the old tagmask
|
||||
output.tag_layout_overrides.put(utils.gpa, output.tags, .{
|
||||
|
|
@ -627,7 +644,8 @@ pub fn render(output: *Output) void {
|
|||
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);
|
||||
// Get a list of active windows
|
||||
// Get a list of active tiled windows
|
||||
// i.e. any windows that are on this output with at least one active tag and aren't fullscreen or floating
|
||||
var active_list: DoublyLinkedList = .{};
|
||||
var active_count: u31 = 0;
|
||||
var it = output.windows.iterator(.forward);
|
||||
|
|
@ -661,9 +679,14 @@ fn calculateLayout(output: *Output) void {
|
|||
// Single window: maximize and return early
|
||||
if (active_count == 1) {
|
||||
const window: *Window = @fieldParentPtr("active_list_node", active_list.popFirst().?);
|
||||
window.pending_render.position = .{ .x = output_x + border_width, .y = output_y + border_width };
|
||||
|
||||
const width = @as(u31, @intFromFloat(@as(f32, @floatFromInt(output_width)) * output.single_window_ratio)) -
|
||||
2 * border_width;
|
||||
const x = output_x + @divFloor(output_width - width, 2);
|
||||
|
||||
window.pending_render.position = .{ .x = x, .y = output_y + border_width };
|
||||
window.pending_manage.dimensions = .{
|
||||
.width = output_width - 2 * border_width,
|
||||
.width = width,
|
||||
.height = output_height - 2 * border_width,
|
||||
};
|
||||
window.pending_manage.maximized = true;
|
||||
|
|
@ -771,6 +794,7 @@ const utils = @import("utils.zig");
|
|||
const Rect = utils.Rect;
|
||||
const Bar = @import("Bar.zig");
|
||||
const Buffer = @import("Buffer.zig");
|
||||
const Config = @import("Config.zig");
|
||||
const Context = @import("Context.zig");
|
||||
const TagOverlay = @import("TagOverlay.zig");
|
||||
const Window = @import("Window.zig");
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
const WindowManager = @This();
|
||||
|
||||
const MIN_RIVER_SEAT_V1_VERSION: u2 = 3;
|
||||
const min_river_seat_v1_version: u2 = 3;
|
||||
|
||||
context: *Context,
|
||||
|
||||
|
|
@ -239,9 +239,9 @@ fn windowManagerV1Listener(window_manager_v1: *river.WindowManagerV1, event: riv
|
|||
.seat => |ev| {
|
||||
const river_seat_v1 = ev.id;
|
||||
const river_seat_v1_version = river_seat_v1.getVersion();
|
||||
if (river_seat_v1_version < MIN_RIVER_SEAT_V1_VERSION) {
|
||||
if (river_seat_v1_version < min_river_seat_v1_version) {
|
||||
@branchHint(.cold); // If we're in here, the program is exiting anyways
|
||||
utils.versionNotSupported(river.SeatV1, river_seat_v1_version, MIN_RIVER_SEAT_V1_VERSION);
|
||||
utils.versionNotSupported(river.SeatV1, river_seat_v1_version, min_river_seat_v1_version);
|
||||
}
|
||||
|
||||
const seat = Seat.create(context, river_seat_v1) catch @panic("Out of memory");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue