control: implement swap

This commit is contained in:
Marten Ringwelski 2020-10-25 11:41:19 +00:00 committed by GitHub
parent 16c8752de2
commit 3f1b0dfaa9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 134 additions and 0 deletions

View file

@ -72,6 +72,12 @@ used to control and configure river.
_shell_command_ if you do not want special characters to get _shell_command_ if you do not want special characters to get
interpreted by your shell before the command gets passed to _/bin/sh_. interpreted by your shell before the command gets passed to _/bin/sh_.
*swap* *next*|*previous*
Swap the focused window with the next/previous visible non-floating window.
When the focused view is the first view there is no previous view.
In this case *previous* swaps with the last view.
*next* behaves analogous.
*toggle-float* *toggle-float*
If the focused view is floating, make it tiled. If it is tiled, make If the focused view is floating, make it tiled. If it is tiled, make
it floating. it floating.

View file

@ -70,6 +70,7 @@ const str_to_impl_fn = [_]struct {
.{ .name = "set-view-tags", .impl = @import("command/tags.zig").setViewTags }, .{ .name = "set-view-tags", .impl = @import("command/tags.zig").setViewTags },
.{ .name = "snap", .impl = @import("command/move.zig").snap }, .{ .name = "snap", .impl = @import("command/move.zig").snap },
.{ .name = "spawn", .impl = @import("command/spawn.zig").spawn }, .{ .name = "spawn", .impl = @import("command/spawn.zig").spawn },
.{ .name = "swap", .impl = @import("command/swap.zig").swap},
.{ .name = "toggle-float", .impl = @import("command/toggle_float.zig").toggleFloat }, .{ .name = "toggle-float", .impl = @import("command/toggle_float.zig").toggleFloat },
.{ .name = "toggle-focused-tags", .impl = @import("command/tags.zig").toggleFocusedTags }, .{ .name = "toggle-focused-tags", .impl = @import("command/tags.zig").toggleFocusedTags },
.{ .name = "toggle-fullscreen", .impl = @import("command/toggle_fullscreen.zig").toggleFullscreen }, .{ .name = "toggle-fullscreen", .impl = @import("command/toggle_fullscreen.zig").toggleFullscreen },

83
river/command/swap.zig Normal file
View file

@ -0,0 +1,83 @@
// This file is part of river, a dynamic tiling wayland compositor.
//
// Copyright 2020 Marten Ringwelski
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const Error = @import("../command.zig").Error;
const Direction = @import("../command.zig").Direction;
const Seat = @import("../Seat.zig");
const View = @import("../View.zig");
const ViewStack = @import("../view_stack.zig").ViewStack;
/// Swap the currently focused view with either the view higher or lower in the visible stack
pub fn swap(
allocator: *std.mem.Allocator,
seat: *Seat,
args: []const []const u8,
out: *?[]const u8,
) Error!void {
if (args.len < 2) return Error.NotEnoughArguments;
if (args.len > 2) return Error.TooManyArguments;
if (seat.focused != .view)
return;
// Filter out everything that is not part of the current layout
if (seat.focused.view.pending.float or seat.focused.view.pending.fullscreen) return;
const direction = std.meta.stringToEnum(Direction, args[1]) orelse return Error.InvalidDirection;
const focused_node = @fieldParentPtr(ViewStack(View).Node, "view", seat.focused.view);
const output = seat.focused_output;
var it = ViewStack(View).iter(
focused_node,
if (direction == .next) .forward else .reverse,
output.pending.tags,
filter,
);
var it_wrap = ViewStack(View).iter(
if (direction == .next) output.views.first else output.views.last,
.forward,
output.pending.tags,
filter,
);
// skip the first node which is focused_node
_ = it.next().?;
const to_swap = @fieldParentPtr(
ViewStack(View).Node,
"view",
// Wrap around if needed
if (it.next()) |next| next else it_wrap.next().?,
);
// Dont swap when only the focused view is part of the layout
if (focused_node == to_swap) {
return;
}
output.views.swap(focused_node, to_swap);
output.arrangeViews();
output.root.startTransaction();
}
fn filter(view: *View, filter_tags: u32) bool {
return !view.destroying and !view.pending.float and
!view.pending.fullscreen and view.pending.tags & filter_tags != 0;
}

View file

@ -108,6 +108,50 @@ pub fn ViewStack(comptime T: type) type {
} }
} }
/// Swap the nodes a and b.
/// pointers to Node.T will point to the same data as before
pub fn swap(self: *Self, a: *Node, b: *Node) void {
// Set self.first and self.last
const first = self.first;
const last = self.last;
if (a == first) {
self.first = b;
} else if (a == last) {
self.last = b;
}
if (b == first) {
self.first = a;
} else if (b == last) {
self.last = a;
}
// This is so complicated to make sure everything works when a and b are neighbors
const a_next = if (b.next == a) b else b.next;
const a_prev = if (b.prev == a) b else b.prev;
const b_next = if (a.next == b) a else a.next;
const b_prev = if (a.prev == b) a else a.prev;
a.next = a_next;
a.prev = a_prev;
b.next = b_next;
b.prev = b_prev;
// Update all neighbors
if (a.next) |next| {
next.prev = a;
}
if (a.prev) |prev| {
prev.next = a;
}
if (b.next) |next| {
next.prev = b;
}
if (b.prev) |prev| {
prev.next = b;
}
}
const Direction = enum { const Direction = enum {
forward, forward,
reverse, reverse,