Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions app/automation/overlay.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const std = @import("std");
const dvui = @import("dvui");
const core = @import("core");
const Player = @import("replay").Player;
/// The pointer, and the layer every part of this overlay is drawn in.
/// The pointer, and the layer every part of this overlay is drawn in (`layer`).
const plain = @import("replay").overlay;
const LiveDriver = @import("replay").LiveDriver;
const Input = @import("replay").Input;
Expand Down Expand Up @@ -66,7 +66,16 @@ pub fn drawLive(driver: *LiveDriver) void {

/// How long the tape's pointer takes to step aside when it types, and back when it moves.
const plain_fade_ms = (plain.Options{}).fade_ms;
const layer = plain.layer;

/// A part of the overlay in a layer of its own (`replay.overlay.layer`), kept to the main window
/// (`core.screens.markMain`): the captions, keys and bar are the stage's, and stay on it while a
/// dialog or a menu is open in a window of its own over it. Only the pointer, and a callout beside
/// what it acts on (`drawCallout`), go into those windows. Every part goes through this.
fn layer(src: std.builtin.SourceLocation, opts: dvui.Options, init: dvui.FloatingWidget.InitOptions) *dvui.FloatingWidget {
const fw = plain.layer(src, opts, init);
core.screens.markMain(fw.data().id);
return fw;
}

fn theme() dvui.Theme {
return dvui.themeGet();
Expand Down Expand Up @@ -337,7 +346,8 @@ fn drawCallout(player: *Player, tape: *const Tape, c: Tape.Caption, index: usize
// Its size as last drawn, to keep the whole of it clear.
const src = @src();
const size: dvui.Size = dvui.minSizeGet(dvui.parentGet().extendId(src, index + 1)) orelse .{};
const spot = callout(player, tape, c, win, size, floor) orelse blk: {
const beside = callout(player, tape, c, win, size, floor);
const spot = beside orelse blk: {
const home = (if (tape.home.len > 0) tagRect(tape.home) else null) orelse win;
break :blk Spot{ .at = .{
.x = home.x + home.w / 2 - size.w / 2,
Expand All @@ -348,12 +358,15 @@ fn drawCallout(player: *Player, tape: *const Tape, c: Tape.Caption, index: usize
const w = @min(440, win.w - 32);
// Where it belongs while it opens — it is placed by its size, which it only has once drawn —
// and gliding after.
const fw = layer(src, .{ .id_extra = index + 1, .max_size_content = .{ .w = w, .h = win.h } }, .{
const fw = plain.layer(src, .{ .id_extra = index + 1, .max_size_content = .{ .w = w, .h = win.h } }, .{
.from = dvui.windowRectScale().pointToPhysical(glide(c, spot.at, size.w == 0 or shown.alpha < 1)),
.from_gravity_x = 1,
.from_gravity_y = 1,
});
defer fw.deinit();
// Beside what the pointer acts on, it goes where that is, as the pointer does: over a dialog's
// button, into the dialog's window. At the foot of the home view it is the stage's (`layer`).
if (beside == null) core.screens.markMain(fw.data().id);
// It opens out of the side nearest what it is beside, as a popover does from its anchor.
captionCard(c, shown, spot.origin, w);
}
Expand Down
29 changes: 29 additions & 0 deletions core/screens.zig
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,35 @@ const Everywhere = struct {
ids: [8]dvui.Id = undefined,
};

/// A floating thing that belongs to the main window this frame — a demo's captions, its keys and
/// its bar, which are the stage's: drawn in the main window and nowhere else. Without it, a layer
/// that takes no input goes into whichever of the app's OS windows (a menu's, a dialog's, a
/// float's) its middle is over, as the demo's pointer does, and a caption at the foot of the main
/// window was drawn inside a dialog over it, cut at that window's edge (`isMain`).
pub fn markMain(id: dvui.Id) void {
const now = dvui.currentWindow().frame_time_ns;
const m = dvui.dataGetPtrDefault(null, key_id, "_main", Main, .{});
if (m.frame != now) m.* = .{ .frame = now };
if (m.n < m.ids.len) {
m.ids[m.n] = id;
m.n += 1;
}
}

/// Whether `id` was marked as the main window's this frame (`markMain`).
pub fn isMain(id: dvui.Id) bool {
const m = dvui.dataGetPtr(null, key_id, "_main", Main) orelse return false;
if (m.frame != dvui.currentWindow().frame_time_ns) return false;
for (m.ids[0..m.n]) |i| if (i == id) return true;
return false;
}

const Main = struct {
frame: i128 = 0,
n: u8 = 0,
ids: [64]dvui.Id = undefined,
};

/// What is carried, in a layer of its own this frame, where the app shows it in a window of its
/// own over every other (fizzy's carry window): that window takes the layer's drawing whole, and
/// the app's own windows draw none of it. A copy left in them, under the carry window, was drawn
Expand Down
9 changes: 9 additions & 0 deletions sdk/replay/Player.zig
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ pub const Transport = struct {
scrub: ?f64 = null,
/// The real pointer, physical, as last seen.
pointer: ?dvui.Point.Physical = null,
/// Where an app shows parts of its frame in OS windows of their own (a dialog's, a menu's) over
/// the window the bar is in, those windows' rects last frame, physical: the app's to set. The
/// bar is drawn under them, so a press there is theirs, not the bar's.
covered: [8]dvui.Rect.Physical = undefined,
covered_n: u8 = 0,
/// When the real pointer was last on the bar, or the dome brought it (ns, `wallNs`): while
/// playing, the bar shows for a while after, then goes back into its dome.
stirred_ns: ?i128 = null,
Expand Down Expand Up @@ -868,6 +873,10 @@ fn transportTakes(self: *Player, e: *dvui.Event, me: dvui.Event.Mouse) bool {
e.handle(@src(), wd);
return true;
}
for (tr.covered[0..tr.covered_n]) |r| if (r.contains(me.p)) {
tr.pointer = null;
return false;
};
// A press on the dome brings the bar out of it at once, and is not the app's.
if (tr.dome) |d| if (d.contains(me.p)) {
if (me.action == .press and me.button.pointer() and !tr.closing) {
Expand Down
11 changes: 11 additions & 0 deletions src/editor/Editor.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4797,6 +4797,17 @@ pub fn tick(editor: *Editor) !dvui.App.Result {
// out and removes itself when the timer expires.
editor.drawSaveToasts();

// The app's OS windows over the main one: a demo's bar stays in the main window, under them,
// so a press on one is that window's, not the bar's (`replay.Player.Transport.covered`).
if (editor.demo.player.tape() != null) {
var buf: [8]Popout.Shown = undefined;
const tr = &editor.demo.player.transport;
tr.covered_n = 0;
for (Popout.windowsShown(&buf)) |w| {
tr.covered[tr.covered_n] = w.part;
tr.covered_n += 1;
}
}
// Over everything the frame drew: a demo's pointer, keystrokes, captions and transport bar,
// or a live tape's pointer.
@import("app").automation.overlay.draw(&editor.demo.player);
Expand Down
8 changes: 5 additions & 3 deletions src/editor/Popout.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1358,14 +1358,16 @@ fn dialogsRide() void {
/// What takes no input, drawn last: a demo's pointer and the ripple of its click
/// (`replay.overlay`, its layer round the pointer's tip), a tooltip. Each goes into the topmost of
/// the app's windows over its middle — a menu's or a dialog's, else a float's — over all that
/// window shows, so the pointer is seen on whatever it is over; over none of them it is left to
/// the main window. Those windows' pictures are handed over already (`windowFrame`, `menuFrame`):
/// a window copies its picture only when the frame ends, so this still goes in.
/// window shows, so the pointer is seen on whatever it is over; over none of them, or marked as the
/// main window's (`core.screens.markMain`), it is left to the main window. Those windows' pictures
/// are handed over already (`windowFrame`, `menuFrame`): a window copies its picture only when the
/// frame ends, so this still goes in.
fn overlaysFrame() void {
const cw = dvui.currentWindow();
for (cw.subwindows.stack.items) |*sw| {
if (sw.mouse_events) continue;
if (fizzy.core.screens.isCarried(sw.id) or fizzy.core.screens.isEverywhere(sw.id)) continue;
if (fizzy.core.screens.isMain(sw.id)) continue;
const mid = sw.rect_pixels.center();
const Into = struct { target: dvui.Texture.Target, part: viewports.Rect };
const into: ?Into = blk: {
Expand Down
Loading