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
7 changes: 7 additions & 0 deletions backend/src/viewports.zig
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,13 @@ pub const Picture = struct {
/// `target` cleared, and drawn into from now as `part` of the frame (physical).
pub fn begin(target: dvui.Texture.Target, part: Rect) Picture {
target.clear();
return again(target, part);
}

/// `target` drawn into again as `part` of the frame, what is in it kept: more for a picture
/// begun and ended earlier this frame. A window handed its picture (`present`) copies it only
/// when the frame ends, so what goes over it last — a demo's pointer — can still go in.
pub fn again(target: dvui.Texture.Target, part: Rect) Picture {
var rt = dvui.currentWindow().render_target;
rt.texture = target;
rt.offset = .{ .x = part.x, .y = part.y };
Expand Down
3 changes: 3 additions & 0 deletions backend/src/viewports_none.zig
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ pub const Picture = struct {
pub fn begin(_: dvui.Texture.Target, _: Rect) Picture {
return .{};
}
pub fn again(_: dvui.Texture.Target, _: Rect) Picture {
return .{};
}
pub fn moveTo(_: *Picture, _: dvui.Point.Physical) void {}
pub fn subwindow(_: Picture, _: *dvui.Subwindows.Subwindow, _: bool) void {}
pub fn end(_: Picture) void {}
Expand Down
13 changes: 11 additions & 2 deletions sdk/replay/overlay.zig
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub const Options = struct {
/// (`LiveDriver.Hand`).
pub fn drawLive(driver: *const LiveDriver, opts: Options) void {
if (!driver.playing()) return;
const fw = layer(@src(), .{ .rect = .cast(dvui.windowRect()), .name = "LivePointer" }, .{});
const fw = layer(@src(), .{ .rect = pointerRect(driver.seq.pointer), .name = "LivePointer" }, .{});
defer fw.deinit();

const now = dvui.frameTimeNS();
Expand All @@ -38,14 +38,23 @@ pub fn drawLive(driver: *const LiveDriver, opts: Options) void {
/// A demo's pointer, while it drives, timed in demo time: a seek shows what playing showed.
pub fn drawPlayer(player: *Player, opts: Options) void {
if (!player.driving()) return;
const fw = layer(@src(), .{ .rect = .cast(dvui.windowRect()), .name = "DemoPointer" }, .{});
const fw = layer(@src(), .{ .rect = pointerRect(player.seq.pointer), .name = "DemoPointer" }, .{});
defer fw.deinit();
const ripple: ?Ripple = if (player.last_press) |press| .{ .pt = press.pt, .age_ms = player.seq.now - press.at } else null;
// Out of the way of the words while the tape types, as a desktop's pointer is.
const shown = player.pointerShown(opts.fade_ms);
paintPointer(player.seq.pointer, ripple, shown, player.input.held.count() > 0);
}

/// The pointer's layer: round its tip, natural, so its middle is where the pointer is. An app that
/// shows parts of its frame in OS windows of their own (fizzy's floats, menus and dialogs) gives
/// each window the layers whose middle is in it, and the pointer is then drawn on whatever window
/// it is over. What it draws is clipped to the screen it is on (`dvui.screenFor`), not to this.
fn pointerRect(p: Sequencer.Point) dvui.Rect {
const s = dvui.windowNaturalScale();
return .{ .x = p.x / s - 16, .y = p.y / s - 16, .w = 32, .h = 32 };
}

/// A floating widget that takes no input, raised to the top. A floating widget otherwise stays
/// just above the window it was made in, under anything opened after it; re-added as a subwindow
/// of its own it can be raised, and each call puts the new one above the last.
Expand Down
46 changes: 45 additions & 1 deletion src/editor/Popout.zig
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const MenuOut = struct {
id: dvui.Id,
viewport: *viewports.Viewport,
target: ?dvui.Texture.Target = null,
/// The part of the frame its picture shows this frame, once drawn (`overlaysFrame`).
part: ?viewports.Rect = null,
/// Drawn this frame: a menu not drawn has closed, and its window goes.
seen: bool = false,
/// On Liquid Glass (`viewports.windowGlass`), else vibrancy with the menu's colour drawn over it.
Expand Down Expand Up @@ -98,6 +100,8 @@ const Out = struct {
/// the texture is kept larger through a fullscreen transition (`keepTarget`).
target: ?dvui.Texture.Target = null,
drawn: [2]u32 = .{ 0, 0 },
/// The part of the frame its picture shows this frame, once drawn (`overlaysFrame`).
part: ?viewports.Rect = null,
/// What its window is called now: the float's title, as its header says (`Floats.Float.titleText`).
title_buf: [96]u8 = undefined,
title_len: u8 = 0,
Expand Down Expand Up @@ -327,6 +331,12 @@ fn release(o: *Out) void {
pub fn endFrame(state: *State) void {
if (!enabled()) return;
cover_count = 0;
for (&outs) |*slot| if (slot.*) |*o| {
o.part = null;
};
for (&menus) |*slot| if (slot.*) |*m| {
m.part = null;
};
for (&outs) |*slot| {
if (slot.*) |*o| windowFrame(state, o);
}
Expand All @@ -340,6 +350,7 @@ pub fn endFrame(state: *State) void {
}
carryFrame(state);
menuFrame();
overlaysFrame();
// A drag's carry window no float grew out of in the frame after the drag: gone.
if (spare) |*sp| {
if (spare_kept) {
Expand Down Expand Up @@ -1081,6 +1092,7 @@ fn windowFrame(state: *State, o: *Out) void {
// Transparent where the float is not: past its corners.
const picture: viewports.Picture = .begin(target, shown);
defer picture.end();
o.part = shown;
// Zoomed or full screen there is no desktop behind it: opaque, eased there and back with the
// OS's transitions as the main window's base is. Where it has no material it is opaque
// throughout.
Expand All @@ -1101,6 +1113,8 @@ fn windowFrame(state: *State, o: *Out) void {
// A carried view is its carry window's (`carryFrame`); a menu, its own (`menuFrame`).
if (fizzy.core.screens.isCarried(sw.id)) continue;
if (fizzy.core.screens.isMenu(sw.id) and nativeMenus()) continue;
// What takes no input goes over everything, last (`overlaysFrame`).
if (!sw.mouse_events and !fizzy.core.screens.isEverywhere(sw.id)) continue;
const mine = area.contains(sw.rect_pixels.center());
if (mine or fizzy.core.screens.isEverywhere(sw.id)) picture.subwindow(sw, mine);
}
Expand Down Expand Up @@ -1273,7 +1287,8 @@ fn menuFrame() void {
const w: u32 = @intFromFloat(@max(1, @round(shown.w)));
const h: u32 = @intFromFloat(@max(1, @round(shown.h)));
const target = viewports.sizedTarget(&m.target, w, h) orelse continue;
const picture: viewports.Picture = .begin(target, .{ .x = shown.x, .y = shown.y, .w = shown.w, .h = shown.h });
m.part = .{ .x = shown.x, .y = shown.y, .w = shown.w, .h = shown.h };
const picture: viewports.Picture = .begin(target, m.part.?);
if (!m.glass) {
const prev_clip = dvui.clipGet();
defer dvui.clipSet(prev_clip);
Expand Down Expand Up @@ -1321,6 +1336,35 @@ 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.
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;
const mid = sw.rect_pixels.center();
const Into = struct { target: dvui.Texture.Target, part: viewports.Rect };
const into: ?Into = blk: {
for (menus) |slot| if (slot) |m| if (m.part) |p| if (m.target) |t| {
if ((dvui.Rect.Physical{ .x = p.x, .y = p.y, .w = p.w, .h = p.h }).contains(mid)) break :blk .{ .target = t, .part = p };
};
for (outs) |slot| if (slot) |o| if (o.part) |p| if (o.target) |t| {
if ((dvui.Rect.Physical{ .x = p.x, .y = p.y, .w = p.w, .h = p.h }).contains(mid)) break :blk .{ .target = t, .part = p };
};
break :blk null;
};
const w = into orelse continue;
const picture: viewports.Picture = .again(w.target, w.part);
picture.subwindow(sw, true);
picture.end();
}
}

/// Menu `id`'s window (`menuFrame`), opened at `place` (physical, in the main window's frame) where
/// it has none yet — riding on `ride`; a dialog's (`dialog`) in that window's stacking. Null where
/// no more windows can be opened: the menu is drawn in its window then.
Expand Down
Loading