diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/gui/gui.zig | 84 |
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 { |
