summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gui/gui.zig84
1 files changed, 73 insertions, 11 deletions
diff --git a/src/gui/gui.zig b/src/gui/gui.zig
index b631a1cb..127f9b5d 100644
--- a/src/gui/gui.zig
+++ b/src/gui/gui.zig
@@ -7114,11 +7114,12 @@ test "lapis plaques stay in their tag's band, clear of every grip; only the focu
for (under) |item| {
const r: [4]f32 = .{ (item.x0 + 1) / 2 * 1280, (1 - item.y0) / 2 * 768, (item.x1 + 1) / 2 * 1280, (1 - item.y1) / 2 * 768 };
// Inside one tag's band, whole: never on a body, a grip or the
- // next pane.
+ // next pane. A box stacked under another tag's lays its top frame
+ // over their shared edge, its own width up into the band above.
const home = for (s.regionList()) |region| {
if (region.kind != .tag and region.kind != .column_tag and region.kind != .workspace_tag) continue;
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] - 0.01 and r[2] <= band.band[2] + 0.01 and r[3] <= band.band[3] + 0.01) break region;
+ 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;
for (s.regionList()) |grip| {
if (grip.kind != .grip and grip.kind != .column_grip) continue;
@@ -7137,6 +7138,26 @@ test "lapis plaques stay in their tag's band, clear of every grip; only the focu
}
try std.testing.expectEqual(@as(usize, 1), stripes);
try std.testing.expect(shadows >= 2);
+ // Stacked boxes share an edge and line up: a column's box spans its
+ // first pane's across, runs down to the edge that pane's frame lies
+ // over, and no shadow reaches the next column.
+ var stacks: usize = 0;
+ for (s.regionList()) |column| {
+ if (column.kind != .column_tag) continue;
+ const upper = plaqueBox(&g, &s.chrome, s.regionList(), column, layout, 1280, 768).?;
+ for (s.regionList()) |tag| {
+ 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]);
+ 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]);
+ try std.testing.expectEqual(@as(f32, 0), upper.shadow[1]);
+ if (lower.band[2] < 1280) try std.testing.expect(lower.box[2] + lower.shadow[0] < lower.band[2]);
+ stacks += 1;
+ }
+ }
+ try std.testing.expectEqual(@as(usize, 2), stacks);
// The scroll track is chequered.
var checkers: usize = 0;
for (g.decor.items[groups.items[0].decor_start + groups.items[0].under_count ..]) |item| checkers += @intFromBool(item.effect & checker_bit != 0);
@@ -9296,8 +9317,11 @@ fn tagPlaques(g: *Gui, gpa: std.mem.Allocator, regions: []const pardes.Region, g
const border_rgb = chrome.decor_box_border orelse return;
if (cell_decor.plaques_off) return;
const page = chrome.page orelse bg_default;
- for (regions) |tag| {
+ // The workspace and column plaques first: a pane's plaque right under
+ // them shares their bottom edge (plaqueBox) and draws its frame last.
+ for ([_]bool{ false, true }) |panes| for (regions) |tag| {
const pane = tag.kind == .tag;
+ if (pane != panes) continue;
if (!pane and tag.kind != .column_tag and tag.kind != .workspace_tag) continue;
if (tag.rect.w == 0 or tag.rect.h == 0) continue;
if (pane and groupOf(groups, tag.serial) != index) continue;
@@ -9316,7 +9340,7 @@ fn tagPlaques(g: *Gui, gpa: std.mem.Allocator, regions: []const pardes.Region, g
try addPattern(g, gpa, inner[0], inner[1], inner[2], inner[3], chrome.decor_box_stripe.?, fill, dp(@floatFromInt(@max(1, chrome.decor_box_stripe_px))), true, track, win_w, win_h)
else
try addDecor(g, gpa, inner[0], inner[1], inner[2], inner[3], fill, 1, track, win_w, win_h);
- }
+ };
}
const Plaque = struct { band: [4]f32, box: [4]f32, border: f32, shadow: [2]f32 };
@@ -9325,16 +9349,28 @@ const Plaque = struct { band: [4]f32, box: [4]f32, border: f32, shadow: [2]f32 }
/// grip to the end), the framed box round the tagline's text, and the
/// frame's and the shadow's widths, the shadow cut to what the band's slack
/// below the text and its right end leave. Null where there is no room.
+/// Tags stacked on each other (the workspace's over the columns', a
+/// column's over its first pane's) share one edge, the ruled one between
+/// their bands: the upper box runs down to it and casts no shadow down, the
+/// lower one's frame lies over it. Every box in a column spans the same
+/// pixels across, right of the grips, its shadow short of the next column.
fn plaqueBox(g: *const Gui, chrome: *const pardes.Chrome, regions: []const pardes.Region, tag: pardes.Region, layout: CellLayout, win_w: f32, win_h: f32) ?Plaque {
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.
- _ = regions;
- const x0 = @as(f32, @floatFromInt(tag.rect.x)) * layout.w;
+ // 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;
const x1_cells = @as(f32, @floatFromInt(tag.rect.x + tag.rect.w)) * layout.w;
- const x1 = if (win_w - x1_cells < layout.w) @max(x1_cells, win_w) else x1_cells;
+ const window_edge = win_w - x1_cells < layout.w;
+ const x1 = if (window_edge) @max(x1_cells, win_w) else x1_cells;
+ const stacked_on = tagBandAt(regions, tag, .above);
+ const stacked_under = tagBandAt(regions, tag, .below);
const row_top = @as(f32, @floatFromInt(first)) * layout.h;
const row_bottom = @as(f32, @floatFromInt(last + 1)) * layout.h;
const text_top = row_top + @as(f32, @floatFromInt(taglineBandOffset(first, win_h, g.cell_h, g.tagline_height)));
@@ -9344,16 +9380,42 @@ fn plaqueBox(g: *const Gui, chrome: *const pardes.Chrome, regions: []const parde
// The shadow's full offset to the right; down, what the slack under the
// text leaves (a pixel or three at a tagline under the body's size).
const want: f32 = if (pane) dp(@floatFromInt(chrome.decor_box_shadow_px)) else dp(2);
- const drop = std.math.clamp(@floor(row_bottom - text_bottom - border), 0, want);
- if (x1 - x0 < 4 * border + want) return null;
+ const drop = if (stacked_under) 0 else std.math.clamp(@floor(row_bottom - text_bottom - border), 0, want);
+ // Every box ends where a pane's does, short of the next column's rule
+ // by a pane frame's width past its shadow, so no shadow meets the rule
+ // and a column's box lines up with its panes'; the window's edge needs
+ // no gap.
+ const pane_shadow = dp(@floatFromInt(chrome.decor_box_shadow_px));
+ const gap = @max(0, pane_shadow - want) + if (window_edge) 0 else dp(@floatFromInt(@max(1, chrome.decor_box_border_px)));
+ if (x1 - x0 < 4 * border + want + gap) return null;
return .{
.band = .{ x0, row_top, x1, row_bottom },
- .box = .{ x0, text_top - border, x1 - want, text_bottom + border },
+ .box = .{
+ x0,
+ if (stacked_on) row_top - border else text_top - border,
+ x1 - want - gap,
+ if (stacked_under) row_bottom else text_bottom + border,
+ },
.border = border,
.shadow = .{ want, drop },
};
}
+/// Whether another tag's band (the workspace's, a column's, a pane's) meets
+/// `tag`'s right above or below it, across some of its width.
+fn tagBandAt(regions: []const pardes.Region, tag: pardes.Region, side: enum { above, below }) bool {
+ for (regions) |other| {
+ if (other.kind != .tag and other.kind != .column_tag and other.kind != .workspace_tag) continue;
+ if (other.rect.h == 0 or std.meta.eql(other.rect, tag.rect)) continue;
+ const meets = switch (side) {
+ .above => other.rect.y + other.rect.h == tag.rect.y,
+ .below => tag.rect.y + tag.rect.h == other.rect.y,
+ };
+ if (meets and other.rect.x < tag.rect.x + tag.rect.w and tag.rect.x < other.rect.x + other.rect.w) return true;
+ }
+ return false;
+}
+
/// The two short bars on each row of a grip with no mode glyph there,
/// centered in its anchor. Only the panes of group `index`.
fn gripDecor(g: *Gui, gpa: std.mem.Allocator, regions: []const pardes.Region, cells: []const pardes.Cell, groups: *const Groups, index: usize, chrome: *const pardes.Chrome, layout: CellLayout, cols: u16, rows: u16, win_w: f32, win_h: f32, track: ?pardes.animation.Track) !void {