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.
|
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
|
**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
|
one is rendered.
|
||||||
maps, so it comes up in front instead of hidden behind the current one.
|
|
||||||
|
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
|
**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
|
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
|
/// Put a freshly mapped window at the front of its output's arrangement order
|
||||||
/// arrives into a stacking layout.
|
/// and focus it.
|
||||||
///
|
///
|
||||||
/// Those layouts render only the most recently focused window, so a new window
|
/// This is dwm's attach() plus focus(): a new client lands in master rather
|
||||||
/// that keeps its initial serial of zero is hidden the instant it appears and
|
/// than at the bottom of the stack, and the keyboard follows it there. In a
|
||||||
/// cannot even be reached with the pointer. Making it the top of the stack
|
/// stacking layout the move is not optional — those layouts render only the
|
||||||
/// without also moving focus would be worse still — the keyboard would go on
|
/// most recently focused window, so a window that kept its initial serial of
|
||||||
/// talking to a window that is no longer on screen — so the two move together,
|
/// zero would be hidden the instant it appeared and unreachable even with the
|
||||||
/// which is what dwm does for every new client anyway.
|
/// pointer, and promoting it without moving focus would leave the keyboard
|
||||||
///
|
/// talking to a window that is no longer on screen.
|
||||||
/// In master a new window is visible wherever it lands in the order, so focus
|
|
||||||
/// is left where the user put it.
|
|
||||||
///
|
///
|
||||||
/// Runs before `arrangeAll` so the serial set here is the one the layout picks
|
/// 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 {
|
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.wants_initial_focus) continue;
|
||||||
if (win.closed or !win.mapped) continue;
|
if (win.closed or !win.mapped) continue;
|
||||||
win.wants_initial_focus = false;
|
win.wants_initial_focus = false;
|
||||||
@@ -627,11 +631,12 @@ fn focusNewWindows(self: *Wm) void {
|
|||||||
if (win.floating or win.fullscreen) continue;
|
if (win.floating or win.fullscreen) continue;
|
||||||
|
|
||||||
const out = win.output orelse 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
|
// 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.
|
// front of, and stealing focus would yank the user off their tag.
|
||||||
if ((win.tags & out.tags) == 0) continue;
|
if ((win.tags & out.tags) == 0) continue;
|
||||||
|
|
||||||
|
self.attachToMaster(win, out);
|
||||||
|
|
||||||
self.focus_serial += 1;
|
self.focus_serial += 1;
|
||||||
win.focus_serial = self.focus_serial;
|
win.focus_serial = self.focus_serial;
|
||||||
for (self.seats.items) |seat| {
|
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
|
/// 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.
|
/// does: to the most recently focused visible window on the same output.
|
||||||
fn validateFocus(self: *Wm, seat: *Seat) void {
|
fn validateFocus(self: *Wm, seat: *Seat) void {
|
||||||
|
|||||||
@@ -56,16 +56,6 @@ pub const Result = struct {
|
|||||||
stacked: bool = false,
|
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.
|
/// Fill `cells` with one rectangle per window, in arrangement order.
|
||||||
///
|
///
|
||||||
/// Each cell is the *outer* rectangle including space for the border; the
|
/// 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);
|
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 cells: [2]Box = undefined;
|
||||||
var p = params(1, 0.55);
|
var p = params(1, 0.55);
|
||||||
p.tabbar_height = 22;
|
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" {
|
test "tabbed reserves the bar strip above the windows" {
|
||||||
|
|||||||
Reference in New Issue
Block a user