summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 00:41:23 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commit0fe78621cc0db8b835ebcb7c51bbd2d20d6dbcc2 (patch)
tree9e263810f35b82f4d1b667fa601e6b55cdcb0ebc
parent6732ce6edcf1562d3e126ed02f96466ab07c498e (diff)
downloadpardes-0fe78621cc0db8b835ebcb7c51bbd2d20d6dbcc2.tar.gz
pardes-0fe78621cc0db8b835ebcb7c51bbd2d20d6dbcc2.zip
A focus write makes its pane's column the active one, and layout's active is it
Writing a serial to /focus gave that pane the keyboard but left the active column where it was, so pane/new under Placement acme went elsewhere, and /layout's active named the keyboard's column rather than where a new pane would go. A focus write now sets the active column as the user's click there would, under either Placement, and /layout's active names the active column, which pane/new and a look place into. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--docs/fs.md6
-rw-r--r--src/ninep/cols.zig25
-rw-r--r--src/ninep/ctl.zig5
3 files changed, 31 insertions, 5 deletions
diff --git a/docs/fs.md b/docs/fs.md
index 55660bc0..a5d3c4ca 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -102,11 +102,13 @@ Existing Plan9port/v9fs clients need a userspace bridge for QUIC.
write follow to that open to wait for more
/screen rendered screen JSON; frozen per open handle
/listeners the session's dial addresses
-/focus the serial of the pane with the keyboard; write a serial to give it the keyboard
+/focus the serial of the pane with the keyboard; write a serial to give it the keyboard,
+ which makes its column the active one as a click there would
/ctl the settings, one a line as a write takes them; write a setting or a session builtin
/commands every builtin: word, `arg` if it takes one, `root` or `pane` (the ctl that takes it), a setting's values
/layout one line per column, left to right: serial index x width current|notcurrent
- empty|full pane-serials...; then active <serial> (- when no column has the keyboard)
+ empty|full pane-serials...; then active <serial>: the active column, where
+ pane/new and a look place a pane next (- when there is none)
/tag the workspace tag; > replaces it, >> appends, one line
/col/<n>/tag the tag of the column with serial n, the same way
/pane/new open it to make a pane; the read answers that pane's serial. A session holds
diff --git a/src/ninep/cols.zig b/src/ninep/cols.zig
index 682c2a02..79943b1b 100644
--- a/src/ninep/cols.zig
+++ b/src/ninep/cols.zig
@@ -27,7 +27,8 @@ pub fn activeColumn(p: *const Pardes) ?usize {
/// it, which stays while it lives), its index left to right, x and width in
/// cells, `current` or `notcurrent` (rio's words, for the column with the
/// keyboard), `empty` or `full`, and its panes' serials, top to bottom;
-/// then `active <serial>`.
+/// then `active <serial>`: the active column, where pane/new and a look
+/// place a pane next (acme's activecol), not always the keyboard's.
pub fn writeLayout(p: *Pardes, w: *std.Io.Writer) !void {
const active = activeColumn(p);
for (0..p.ncol) |c| {
@@ -38,7 +39,7 @@ pub fn writeLayout(p: *Pardes, w: *std.Io.Writer) !void {
for (p.col_panes[c][0..p.col_n[c]]) |id| if (p.panes[id]) |pane| try w.print(" {d}", .{pane.serial});
try w.writeByte('\n');
}
- if (active) |c| try w.print("active {d}\n", .{layout.columnSerial(p, c)}) else try w.writeAll("active -\n");
+ if (p.activeColumn() orelse active) |c| try w.print("active {d}\n", .{layout.columnSerial(p, c)}) else try w.writeAll("active -\n");
}
pub fn layoutLen(p: *Pardes) u64 {
@@ -179,3 +180,23 @@ test "a column's tag and the workspace's read, replace and append as a pane tag
_ = th.wr(p, top, "Newcol Exit\n");
try testing.expectEqualStrings("Newcol Exit", tagline.topbar(p));
}
+
+test "a focus write makes its pane's column the active one: layout says so and pane/new goes there" {
+ for ([_][]const u8{ "Placement acme", "Placement pardes" }) |placement| {
+ const p = try th.withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ try testing.expect(p.executeBuiltinLine(p.active, placement));
+ const left = p.panes[0].?.serial;
+ p.newColumn(p.active);
+ p.newScratchFor(p.active, .fs); // fills the new, right column
+ p.sync();
+ try testing.expectEqual(@as(usize, 1), layout.findPane(p, p.active).?.col);
+ var digits: [16]u8 = undefined;
+ _ = th.wr(p, @intFromEnum(tree.TopFile.focus), try std.fmt.bufPrint(&digits, "{d}\n", .{left}));
+ var want: [32]u8 = undefined;
+ const text = th.rd(p, @intFromEnum(tree.TopFile.layout), 0, 4096).bytes;
+ try testing.expect(std.mem.endsWith(u8, text, try std.fmt.bufPrint(&want, "active {d}\n", .{layout.columnSerial(p, 0)})));
+ const made = th.call(p, .{ .tag = 1, .op = .open, .node = @intFromEnum(tree.TopFile.new) }).reply.handle;
+ try testing.expectEqual(@as(usize, 0), layout.findPane(p, p.paneBySerial(made).?).?.col);
+ }
+}
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 325fc8c8..98120555 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -486,9 +486,12 @@ pub fn writeFocus(p: *Pardes, req: Req) Reply {
const text = std.mem.trim(u8, req.data, " \t\r\n");
const serial = std.fmt.parseInt(u32, text, 10) catch return tree.failText(req.tag, E.INVAL, tree.e_bad_ctl);
const id = p.paneBySerial(serial) orelse return tree.failText(req.tag, E.NOENT, e_no_pane);
- // The keyboard goes to the pane itself, off any header that had it.
+ // The keyboard goes to the pane itself, off any header that had it, and
+ // its column becomes the active one, as the user's click there would
+ // make it: pane/new and a look place there next, whatever Placement.
tagline.exitHeader(p);
p.active = id;
+ if (pardes.layout.findPane(p, id)) |at| p.setActiveColumn(at.col);
return .{ .tag = req.tag, .written = @intCast(req.data.len) };
}