layout/master: new windows goes into master and gets focus
This commit is contained in:
@@ -182,8 +182,11 @@ separate setting, `repeat`, under [Input](#input).
|
||||
column is divided, so the bottom edge always lands exactly on the output edge.
|
||||
|
||||
**monocle** — every window gets the full area; only the most recently focused
|
||||
one is rendered. A window spawned into monocle or tabbed takes focus as it
|
||||
maps, so it comes up in front instead of hidden behind the current one.
|
||||
one is rendered.
|
||||
|
||||
In every layout a newly mapped window goes to the front of the arrangement
|
||||
order — the first master slot — and takes focus, as in dwm. A window spawned
|
||||
onto a tag nobody is viewing leaves both the order and focus alone.
|
||||
|
||||
**tabbed** — same geometry as monocle, minus a strip at the top where att_wm
|
||||
draws one solid colour block per window, the focused one highlighted. The blocks
|
||||
|
||||
+33
-14
@@ -602,23 +602,27 @@ fn assignOutputs(self: *Wm) void {
|
||||
}
|
||||
}
|
||||
|
||||
/// Hand a freshly mapped window the newest focus serial, and focus it, when it
|
||||
/// arrives into a stacking layout.
|
||||
/// Put a freshly mapped window at the front of its output's arrangement order
|
||||
/// and focus it.
|
||||
///
|
||||
/// Those layouts render only the most recently focused window, so a new window
|
||||
/// that keeps its initial serial of zero is hidden the instant it appears and
|
||||
/// cannot even be reached with the pointer. Making it the top of the stack
|
||||
/// without also moving focus would be worse still — the keyboard would go on
|
||||
/// talking to a window that is no longer on screen — so the two move together,
|
||||
/// which is what dwm does for every new client anyway.
|
||||
///
|
||||
/// In master a new window is visible wherever it lands in the order, so focus
|
||||
/// is left where the user put it.
|
||||
/// This is dwm's attach() plus focus(): a new client lands in master rather
|
||||
/// than at the bottom of the stack, and the keyboard follows it there. In a
|
||||
/// stacking layout the move is not optional — those layouts render only the
|
||||
/// most recently focused window, so a window that kept its initial serial of
|
||||
/// zero would be hidden the instant it appeared and unreachable even with the
|
||||
/// pointer, and promoting it without moving focus would leave the keyboard
|
||||
/// talking to a window that is no longer on screen.
|
||||
///
|
||||
/// Runs before `arrangeAll` so the serial set here is the one the layout picks
|
||||
/// its top window with.
|
||||
/// its top window with, and the order set here is the one it arranges.
|
||||
fn focusNewWindows(self: *Wm) void {
|
||||
for (self.windows.items) |win| {
|
||||
// `attachToMaster` reorders `windows`, so re-read the list each time round
|
||||
// rather than holding a slice across the move. It only ever moves a window
|
||||
// to a lower index, so nothing after `i` shifts and no new window is
|
||||
// skipped.
|
||||
var i: usize = 0;
|
||||
while (i < self.windows.items.len) : (i += 1) {
|
||||
const win = self.windows.items[i];
|
||||
if (!win.wants_initial_focus) continue;
|
||||
if (win.closed or !win.mapped) continue;
|
||||
win.wants_initial_focus = false;
|
||||
@@ -627,11 +631,12 @@ fn focusNewWindows(self: *Wm) void {
|
||||
if (win.floating or win.fullscreen) continue;
|
||||
|
||||
const out = win.output orelse continue;
|
||||
if (!layout.stacks(out.state().layout)) continue;
|
||||
// Spawned onto a tag nobody is looking at: nothing to come to the
|
||||
// front of, and stealing focus would yank the user off their tag.
|
||||
if ((win.tags & out.tags) == 0) continue;
|
||||
|
||||
self.attachToMaster(win, out);
|
||||
|
||||
self.focus_serial += 1;
|
||||
win.focus_serial = self.focus_serial;
|
||||
for (self.seats.items) |seat| {
|
||||
@@ -640,6 +645,20 @@ fn focusNewWindows(self: *Wm) void {
|
||||
}
|
||||
}
|
||||
|
||||
/// Move `win` ahead of every other tiled window on `out`, so it takes the
|
||||
/// first master slot. Windows on other outputs and on other tags keep their
|
||||
/// relative order.
|
||||
fn attachToMaster(self: *Wm, win: *Window, out: *Output) void {
|
||||
for (self.windows.items) |other| {
|
||||
if (other == win) return; // already the first tiled window here
|
||||
if (other.output != out) continue;
|
||||
if ((other.tags & out.tags) == 0) continue;
|
||||
if (other.floating or other.fullscreen) continue;
|
||||
self.moveToFront(win, other);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// If a seat's focused window is no longer visible, move focus the way dwm
|
||||
/// does: to the most recently focused visible window on the same output.
|
||||
fn validateFocus(self: *Wm, seat: *Seat) void {
|
||||
|
||||
@@ -56,16 +56,6 @@ pub const Result = struct {
|
||||
stacked: bool = false,
|
||||
};
|
||||
|
||||
/// Whether a layout puts every window in the same place, so only the top one
|
||||
/// is rendered. `arrange` reports the same thing after the fact; this answers
|
||||
/// it for callers that need to know before the geometry is computed.
|
||||
pub fn stacks(layout: Layout) bool {
|
||||
return switch (layout) {
|
||||
.master => false,
|
||||
.monocle, .tabbed => true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Fill `cells` with one rectangle per window, in arrangement order.
|
||||
///
|
||||
/// Each cell is the *outer* rectangle including space for the border; the
|
||||
|
||||
+3
-4
@@ -102,13 +102,12 @@ test "monocle gives every window the full area and reports stacked" {
|
||||
for (cells) |cell| try testing.expectEqual(area, cell);
|
||||
}
|
||||
|
||||
test "stacks agrees with what arrange reports" {
|
||||
test "master does not report stacked" {
|
||||
var cells: [2]Box = undefined;
|
||||
var p = params(1, 0.55);
|
||||
p.tabbar_height = 22;
|
||||
for ([_]layout.Layout{ .master, .monocle, .tabbed }) |mode| {
|
||||
try testing.expectEqual(layout.arrange(mode, p, &cells).stacked, layout.stacks(mode));
|
||||
}
|
||||
|
||||
try testing.expect(!layout.arrange(.master, p, &cells).stacked);
|
||||
}
|
||||
|
||||
test "tabbed reserves the bar strip above the windows" {
|
||||
|
||||
Reference in New Issue
Block a user