summaryrefslogtreecommitdiff
path: root/src/gui/gui.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 10:03:10 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit3d262763b304021720f1141ab33265759869d2fe (patch)
tree5c2e67cd794a4bc651f4ae75037e7f46c74ca5f2 /src/gui/gui.zig
parent826ec1fac3c012e71ab34207328faaac5b5827b7 (diff)
downloadpardes-3d262763b304021720f1141ab33265759869d2fe.tar.gz
pardes-3d262763b304021720f1141ab33265759869d2fe.zip
The focused cursor rides with a moving pane, and a focus change glides to the new one
G3's springs now keep the cursor's place in its pane: a pane that moves in the layout carries them, and the quad is drawn through the pane's presented box (its transition track), so through a slide, zoom or drag the cursor rides with the text, and with no transition it lands with it. A focus change, to another pane or into a column's or the workspace's tag, glides from where the cursor was drawn to the new place instead of snapping, unclamped while it crosses. While panes move the cursor is drawn as the overlay quad instead of vanishing (golden 18-mid-transition re-recorded for that: the only change is the quad, gliding into the new column's tag). Lapis: a tag plaque starts flush against its grip's button, the scroll gap between them part of the plaque, so grip and plaque read as one piece, on pane and column rows alike; below the button's row the grip keeps its band. Themes without plaques are unchanged (their grip band and tag band are one colour, no sliver).
Diffstat (limited to 'src/gui/gui.zig')
-rw-r--r--src/gui/gui.zig54
1 files changed, 36 insertions, 18 deletions
diff --git a/src/gui/gui.zig b/src/gui/gui.zig
index 2d31416b..18a1c8cb 100644
--- a/src/gui/gui.zig
+++ b/src/gui/gui.zig
@@ -5882,9 +5882,10 @@ fn renderFrame(
}
}
var groups = makeGroups(surface.panelTracks(), surface.hasPanelDiff());
- // Cursors stand still while panes move, as they always have; while the
- // focused one glides or blinks, it is the overlay's quad instead. One
- // or the other every frame (none only at a blink's off half).
+ // While panes move the cells' cursors are not drawn; the focused one is
+ // the overlay's quad, riding with its pane (draw.glideCursor), as it is
+ // while it glides or blinks. One or the other every frame (none only at
+ // a blink's off half).
const blink_alpha: f32 = if (blinkNow(g, chrome)) |b| b.alpha else 1;
const quad_mode = chrome.cursor_glide & 2 != 0 and (chrome.cursor_glide & 1 != 0 or blink_alpha < 1);
g.cursor_quad_alpha = if (quad_mode) chrome.cursor_alpha * blink_alpha else 0;
@@ -6706,9 +6707,12 @@ fn emitSurfaceCell(
// this underlay at tagline height leaves page-colored gutters between
// adjacent workspace, column and pane tags at TaglineSize < 100.
emitInstance(g, instances, next.*, col, row, body_layout, win_w, win_h, track, .body, (&band_base)[0..1], 0, null, old_layer, false, page);
- // Over a theme's plaque (not a grip's button) the underlay lets it
- // show (tagPlaques).
- if (cell_decor.nplaques != 0 and !here.grip and cell_decor.inPlaque((@as(f32, @floatFromInt(col)) + 0.5) * body_layout.w + body_layout.x_off, @as(f32, @floatFromInt(row)) * body_layout.h + body_layout.y_off + 0.5))
+ // Over a theme's plaque the underlay lets it show (tagPlaques),
+ // in a grip's cell too where the plaque reaches into it past the
+ // button (which is drawn over the cells).
+ const cell_x = @as(f32, @floatFromInt(col)) * body_layout.w + body_layout.x_off;
+ const cell_y = @as(f32, @floatFromInt(row)) * body_layout.h + body_layout.y_off + 0.5;
+ if (cell_decor.nplaques != 0 and cell_decor.inPlaque(if (here.grip) cell_x + body_layout.w - 0.5 else cell_x + 0.5, cell_y))
instances[next.*].effect |= clear_bg_bit;
next.* += 1;
// Tag glyphs keep their font size inside a full physical-width grip.
@@ -7145,10 +7149,13 @@ test "lapis plaques stay in their tag's band, clear of every grip; only the focu
const band = plaqueBox(&g, &s.chrome, s.regionList(), region, layout, 1280, 768) orelse continue;
if (r[0] >= band.band[0] - 0.01 and r[1] >= band.band[1] - band.border - 0.01 and r[2] <= band.band[2] + 0.01 and r[3] <= band.band[3] + 0.01) break region;
} else return error.PlaqueOutsideItsBand;
+ // Clear of every grip's button: a plaque starts flush against it,
+ // in the gap between it and the text.
for (s.regionList()) |grip| {
if (grip.kind != .grip and grip.kind != .column_grip) continue;
- const gx0 = @as(f32, @floatFromInt(grip.rect.x)) * 16;
- const gx1 = @as(f32, @floatFromInt(grip.rect.x + grip.rect.w)) * 16;
+ const column = scrollColumn(&g, &s.chrome, grip.rect.x, layout);
+ const gx0 = column[0];
+ const gx1 = column[1];
const gy0 = @as(f32, @floatFromInt(grip.rect.y)) * 32;
const gy1 = @as(f32, @floatFromInt(grip.rect.y + grip.rect.h)) * 32;
try std.testing.expect(r[2] <= gx0 + 0.01 or r[0] >= gx1 - 0.01 or r[3] <= gy0 + 0.01 or r[1] >= gy1 - 0.01);
@@ -7173,6 +7180,11 @@ test "lapis plaques stay in their tag's band, clear of every grip; only the focu
if (tag.kind != .tag or tag.rect.y != column.rect.y + column.rect.h or tag.rect.x + tag.rect.w != column.rect.x + column.rect.w) continue;
const lower = plaqueBox(&g, &s.chrome, s.regionList(), tag, layout, 1280, 768).?;
try std.testing.expectEqual(upper.box[0], lower.box[0]);
+ // Flush against the grips' buttons, which line up too.
+ const grip = for (s.regionList()) |part| {
+ if (part.kind == .grip and part.owner == tag.owner) break part;
+ } else unreachable;
+ try std.testing.expectEqual(scrollColumn(&g, &s.chrome, grip.rect.x, layout)[1], lower.box[0]);
try std.testing.expectEqual(upper.box[2], lower.box[2]);
try std.testing.expectEqual(upper.band[3], upper.box[3]);
try std.testing.expectEqual(lower.band[1] - lower.border, lower.box[1]);
@@ -9382,14 +9394,15 @@ fn plaqueBox(g: *const Gui, chrome: *const pardes.Chrome, regions: []const parde
const pane = tag.kind == .tag;
const first = tag.rect.y;
const last = tag.rect.y + tag.rect.h - 1;
- // From its start, right of its grip: the gap cell before its text is
- // the plaque's margin, so its frame is never under the first letter. A
- // column's text sits a cell further in than a pane's; its plaque starts
- // where the pane's under it does, at its grip's end.
- const start = if (tag.kind == .column_tag) for (regions) |grip| {
- if (grip.kind == .column_grip and grip.owner == tag.owner) break grip.rect.x + grip.rect.w;
- } else tag.rect.x else tag.rect.x;
- const x0 = @as(f32, @floatFromInt(@min(start, tag.rect.x))) * layout.w;
+ // Flush against its grip's button, so the two read as one piece: the
+ // gap between button and text (the scroll gap) is the plaque's, and so
+ // is the cell before its text, so its frame is never under the first
+ // letter. A column's plaque starts where the pane's under it does.
+ const grip_kind: pardes.Region.Kind = if (tag.kind == .column_tag) .column_grip else .grip;
+ const x0 = for (regions) |grip| {
+ if (tag.kind != .workspace_tag and grip.kind == grip_kind and grip.owner == tag.owner)
+ break scrollColumn(g, chrome, grip.rect.x, layout)[1];
+ } else @as(f32, @floatFromInt(tag.rect.x)) * layout.w;
const x1_cells = @as(f32, @floatFromInt(tag.rect.x + tag.rect.w)) * layout.w;
const window_edge = win_w - x1_cells < layout.w;
const x1 = if (window_edge) @max(x1_cells, win_w) else x1_cells;
@@ -9454,7 +9467,13 @@ fn gripDecor(g: *Gui, gpa: std.mem.Allocator, regions: []const pardes.Region, ce
const left = @as(f32, @floatFromInt(grip.rect.x)) * layout.w;
const right = @as(f32, @floatFromInt(grip.rect.x + grip.rect.w)) * layout.w;
try addDecor(g, gpa, left, button[1], button[0], button[3], band, 1, track, win_w, win_h);
- try addDecor(g, gpa, button[2], button[1], right, button[3], band, 1, track, win_w, win_h);
+ // Right of it, a theme's plaque starts flush against it (plaqueBox).
+ const plaqued = chrome.decor_box_border != null and !cell_decor.plaques_off;
+ if (!plaqued) try addDecor(g, gpa, button[2], button[1], right, button[3], band, 1, track, win_w, win_h);
+ // Its cells over the plaque's reach are let through (emitInstance):
+ // under the button's row, the band goes on as far as the plaque.
+ if (plaqued and grip.rect.h > 1)
+ try addDecor(g, gpa, left, button[3], button[2], @as(f32, @floatFromInt(grip.rect.y + grip.rect.h)) * layout.h, band, 1, track, win_w, win_h);
if (grip.kind != .grip) continue;
// acme's button ring, two pixels (ButtonBorder) inside the button:
// the focused one's in its own fill. An unfocused dirty one keeps a
@@ -10160,7 +10179,6 @@ fn buildOverlay(
fn appendCursorGlide(g: *Gui, surface: *const pardes.Surface, builder: *OverlayBuilder) void {
const chrome = &surface.chrome;
if (g.cursor_quad_alpha <= 0.001) return;
- if (surface.panelTracks().len != 0) return;
const layout = fixedCellLayout(g);
var px: [4][2]f32 = undefined;
var centre: [2]f32 = .{ 0, 0 };