diff --git a/src/command.zig b/src/command.zig index 01e9c33..d14831a 100644 --- a/src/command.zig +++ b/src/command.zig @@ -29,3 +29,4 @@ pub usingnamespace @import("command/spawn.zig"); pub usingnamespace @import("command/toggle_tags.zig"); pub usingnamespace @import("command/toggle_view_tags.zig"); pub usingnamespace @import("command/zoom.zig"); +pub usingnamespace @import("command/toggle_float.zig"); diff --git a/src/command/toggle_float.zig b/src/command/toggle_float.zig new file mode 100644 index 0000000..38250b8 --- /dev/null +++ b/src/command/toggle_float.zig @@ -0,0 +1,13 @@ +const c = @import("../c.zig"); + +const Arg = @import("../command.zig").Arg; +const Seat = @import("../seat.zig").Seat; + +/// Make the focused view float or stop floating, depending on its current +/// state. +pub fn toggleFloat(seat: *Seat, arg: Arg) void { + if (seat.focused_view) |view| { + view.setFloating(!view.floating); + view.output.root.arrange(); + } +} diff --git a/src/config.zig b/src/config.zig index f09dc1a..caa6cd6 100644 --- a/src/config.zig +++ b/src/config.zig @@ -189,5 +189,13 @@ pub const Config = struct { .command = command.sendToOutput, .arg = .{ .direction = .Prev }, }); + + // Mod+Space to toggle float + try self.keybinds.append(Keybind{ + .keysym = c.XKB_KEY_space, + .modifiers = mod, + .command = command.toggleFloat, + .arg = .{ .none = {} }, + }); } }; diff --git a/src/output.zig b/src/output.zig index 9979870..a2b7de9 100644 --- a/src/output.zig +++ b/src/output.zig @@ -148,7 +148,12 @@ pub const Output = struct { const visible_count = blk: { var count: u32 = 0; var it = ViewStack(View).pendingIterator(self.views.first, output_tags); - while (it.next() != null) count += 1; + while (it.next()) |node| { + if (node.view.floating) { + continue; + } + count += 1; + } break :blk count; }; @@ -178,6 +183,9 @@ pub const Output = struct { var it = ViewStack(View).pendingIterator(self.views.first, output_tags); while (it.next()) |node| { const view = &node.view; + if (view.floating) { + continue; + } if (i < master_count) { // Add the remainder to the first master to ensure every pixel of height is used const master_height = @divTrunc(layout_height, master_count); diff --git a/src/render.zig b/src/render.zig index 99bddc1..d8b46ec 100644 --- a/src/render.zig +++ b/src/render.zig @@ -39,6 +39,26 @@ pub fn renderOutput(output: *Output) void { if (view.current_box.width == 0 or view.current_box.height == 0) { continue; } + // Floating views are rendered on top of normal views + if (view.floating) { + continue; + } + renderView(output.*, view, &now); + renderBorders(output.*, view, &now); + } + + // Render floating views + it = ViewStack(View).reverseIterator(output.views.last, output.current_focused_tags); + while (it.next()) |node| { + const view = &node.view; + // This check prevents a race condition when a frame is requested + // between mapping of a view and the first configure being handled. + if (view.current_box.width == 0 or view.current_box.height == 0) { + continue; + } + if (!view.floating) { + continue; + } renderView(output.*, view, &now); renderBorders(output.*, view, &now); } diff --git a/src/view.zig b/src/view.zig index 9bde87d..3890013 100644 --- a/src/view.zig +++ b/src/view.zig @@ -14,9 +14,16 @@ pub const View = struct { mapped: bool, + /// If the view is floating or not + floating: bool, + current_box: Box, pending_box: ?Box, + /// The dimensions the view would have taken if we didn't force it to tile + natural_width: u32, + natural_height: u32, + current_tags: u32, pending_tags: ?u32, @@ -122,6 +129,22 @@ pub const View = struct { } } + /// If true is passsed, make the view float. If false, return it to the tiled + /// layout. + pub fn setFloating(self: *Self, float: bool) void { + if (float and !self.floating) { + self.floating = true; + self.pending_box = Box{ + .x = @intCast(i32, (self.output.usable_box.width - self.natural_width) / 2), + .y = @intCast(i32, (self.output.usable_box.height - self.natural_height) / 2), + .width = self.natural_width, + .height = self.natural_height, + }; + } else if (!float and self.floating) { + self.floating = false; + } + } + /// Move a view from one output to another, sending the required enter/leave /// events. pub fn sendToOutput(self: *Self, destination_output: *Output) void { @@ -161,6 +184,15 @@ pub const View = struct { c.wl_signal_add(&self.wlr_xdg_surface.surface.*.events.commit, &self.listen_commit); self.mapped = true; + self.floating = false; + + self.natural_width = @intCast(u32, self.wlr_xdg_surface.geometry.width); + self.natural_height = @intCast(u32, self.wlr_xdg_surface.geometry.height); + + if (self.natural_width == 0 and self.natural_height == 0) { + self.natural_width = @intCast(u32, self.wlr_xdg_surface.surface.*.current.width); + self.natural_height = @intCast(u32, self.wlr_xdg_surface.surface.*.current.height); + } // Focus the newly mapped view. Note: if a seat is focusing a different output // it will continue to do so.