diff options
Diffstat (limited to 'src/gui/gui.zig')
| -rw-r--r-- | src/gui/gui.zig | 103 |
1 files changed, 90 insertions, 13 deletions
diff --git a/src/gui/gui.zig b/src/gui/gui.zig index 449cae15..b5da9ad6 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -5963,7 +5963,9 @@ fn renderFrame( const track = groups.items[group_index].track; const data_effect = if (track) |active| active.effect == .dissolve else false; const data_diff = data_effect and gripCellChanged(surface, covered, previous_covered, col, row); - if (data_diff) emitSurfaceCell( + // The new cell, then (a dissolve) the old one fading out over + // it, as the old layers come after the new ones. + emitSurfaceCell( g, chrome, instances, @@ -5973,16 +5975,16 @@ fn renderFrame( layout, win_w, win_h, - track, - surface.previous_cells[@as(usize, row) * surface.cols ..][0..surface.cols], - previous_covered[@as(usize, row) * surface.cols ..][0..surface.cols], + if (data_effect and !data_diff) null else track, + line, + line_cover, col, - null, - true, + line_cursor, false, + cursors_shown and at == cursor_idx and !cursor_bar and bodyLayerAt(surface, col, row) == null, page, ); - emitSurfaceCell( + if (data_diff) emitSurfaceCell( g, chrome, instances, @@ -5992,13 +5994,13 @@ fn renderFrame( layout, win_w, win_h, - if (data_effect and !data_diff) null else track, - line, - line_cover, + track, + surface.previous_cells[@as(usize, row) * surface.cols ..][0..surface.cols], + previous_covered[@as(usize, row) * surface.cols ..][0..surface.cols], col, - line_cursor, + null, + true, false, - cursors_shown and at == cursor_idx and !cursor_bar and bodyLayerAt(surface, col, row) == null, page, ); } @@ -6104,7 +6106,10 @@ fn renderFrame( } const has_shifted = index == 0 and shifted != 0; if ((group.cell_count != 0 or has_shifted) and g.vbuf != null) { - c.SDL_BindGPUGraphicsPipeline(rp, if (cell_decor.nplaques != 0) g.pipeline_over else g.pipeline); + // Blended over what is under: a theme's plaques show through + // blank tag cells; a dissolve's old cells fade over its new. + const dissolving = if (group.track) |active| active.effect == .dissolve else false; + c.SDL_BindGPUGraphicsPipeline(rp, if (cell_decor.nplaques != 0 or dissolving) g.pipeline_over else g.pipeline); const samp_binding = c.SDL_GPUTextureSamplerBinding{ .texture = g.atlas_tex, .sampler = g.atlas_sampler }; c.SDL_BindGPUFragmentSamplers(rp, 0, &samp_binding, 1); if (group.cell_count != 0) { @@ -8715,6 +8720,34 @@ fn buildDecor(g: *Gui, gpa: std.mem.Allocator, surface: *const pardes.Surface, g } else null; try addLift(g, gpa, lift_style, ceilings, box, rim_edge, bodies, grip.lift, track, win_w, win_h); } + // G7: a pane on the move stands off the page, rising as it sets + // out and settling as it lands, its shadow on the other panes' + // bodies only (never a tag, a grip or its own text), moving + // with it. + if (track) |active| if (active.phase != .closing and (active.effect == .slide or active.effect == .zoom or active.effect == .vertical)) { + const steps: f32 = @floatFromInt(@max(1, active.frames() -| 1)); + const u = std.math.clamp(@as(f32, @floatFromInt(active.frame)) / steps, 0, 1); + const lift = @sin(std.math.pi * u); + var box: [4]f32 = .{ std.math.inf(f32), std.math.inf(f32), -std.math.inf(f32), -std.math.inf(f32) }; + var others: [pardes.MAX_PANES][4]f32 = undefined; + var nothers: usize = 0; + for (placed) |part| { + if (part.kind != .tag and part.kind != .rail and part.kind != .body and part.kind != .grip) continue; + const r = bodyPixels(part, layout); + if (part.owner == active.pane) { + box = .{ @min(box[0], r[0]), @min(box[1], r[1]), @max(box[2], r[2]), @max(box[3], r[3]) }; + } else if ((part.kind == .body or part.kind == .rail) and nothers < others.len) { + others[nothers] = r; + nothers += 1; + } + } + if (box[2] > box[0] and lift > 0.01) { + const h: f32 = @floatFromInt(g.cell_h); + const first = g.decor.items.len; + try addSoft(g, gpa, box, Shadow.sigma * h * lift, Shadow.side * h * lift, Shadow.drop * h * lift, @min(Shadow.strength * lift, ceilings.shadow), .{ 0, 0, 0 }, track, win_w, win_h); + try clipToBodies(g, gpa, first, others[0..nothers]); + } + }; } // A notice chip is tagline hung over the body's top rows, so it is // ruled off from the text beneath it the way a tag is: the same @@ -9287,6 +9320,50 @@ test "decor is read from the regions, each pane's in its own group" { try std.testing.expectEqual(@as(u32, 0), groups.items[groups.ink].decor_count); } +test "a pane on the move casts a shadow on the other panes' bodies alone, most at mid-way" { + const gpa = std.testing.allocator; + const core = try pardes.Pardes.init(gpa, .{ .cols = 80, .rows = 24 }); + defer core.deinit(); + _ = try core.newShell(1, ""); + try std.testing.expect(pardes.layout.splitColumn(core, 0, 1, false)); + try std.testing.expect(core.executeBuiltinLine(0, "Theme acme")); + for (0..40) |_| core.update(.tick); + pardes.test_api.sync(core); + const s = try core.render(core.scratch.allocator()); + var g: Gui = undefined; + g.decor = .empty; + defer g.decor.deinit(gpa); + g.cell_w = 10; + g.cell_h = 20; + g.tagline_width = 6; + g.tagline_height = 12; + const layout = fixedCellLayout(&g); + const serial = core.panes[1].?.serial; + var strengths: [3]f32 = @splat(0); + for ([_]u16{ 0, 6, 12 }, 0..) |frame, i| { + g.decor.clearRetainingCapacity(); + const moving: pardes.animation.Track = .{ .pane = 1, .serial = serial, .phase = .moving, .effect = .slide, .frame = frame, .frame_count = 13, .from = .{ .x = 0, .y = 1, .w = 40, .h = 23 }, .to = .{ .x = 40, .y = 1, .w = 40, .h = 23 } }; + var groups = makeGroups(&.{moving}, true); + if (!moving.active()) groups = makeGroups(&.{}, true); + try buildDecor(&g, gpa, s, &groups, layout, 800, 480, false); + for (g.decor.items) |item| { + if (item.effect & soft_shadow_bit == 0) continue; + strengths[i] = @max(strengths[i], item.fr); + // Clipped to pane 0's body or rail, never pane 1's, never a tag. + try std.testing.expect(item.clip_x1 >= item.clip_x0); + for (s.regionList()) |region| { + if (region.kind != .tag and region.kind != .grip and !(region.owner == 1 and (region.kind == .body or region.kind == .rail))) continue; + const r = bodyPixels(region, layout); + try std.testing.expect(item.clip_x1 <= r[0] or item.clip_x0 >= r[2] or item.clip_y1 <= r[1] or item.clip_y0 >= r[3]); + } + } + } + // Rising as it sets out, gone as it lands. + try std.testing.expectEqual(@as(f32, 0), strengths[0]); + try std.testing.expect(strengths[1] > 0.05); + try std.testing.expectEqual(@as(f32, 0), strengths[2]); +} + /// What a bundled post pass leaves alone (Post.Frame.spare), in window /// pixels: every tag with its grip, the column and workspace tags, and the /// notices, the chrome the focus indicators live in. A grip, the gap after |
