end
End of this window gui's rendering. Renders retained dialogs and all
deferred rendering (subwindows, focus highlights). Returns micros we
want between last call to begin and next call to begin (or null
meaning wait for event). If wanted, pass return value to waitTime to
get a useful time to wait between render loops.
Parameters
- self:*Self
- *Self
- opts:endOptions
- endOptions
Source
Implementation
pub fn end(self: *Self, opts: endOptions) !?u32 {
// make sure all widgets reset the parent
dvui.parentReset(self.data().id, self.widget());
if (!self.end_rendering_done) {
self.endRendering(opts);
}
// Call this before freeing data so backend can use data allocated during frame.
try self.backend.end();
// events may have been tagged with a focus widget that never showed up
const evts = dvui.events();
for (evts) |*e| {
// deal with unhandled mouse release to stop drag, this is done before
// checking eventMatch, because we want to do it for all subwindows
if (!e.handled and self.dragging.state == .dragging and e.evt == .mouse and e.evt.mouse.action == .release and (self.dragging.button == .none or self.dragging.button == e.evt.mouse.button)) {
if (dvui.debug.logEvents(null)) {
log.debug("Clearing drag ({?s}) for unhandled mouse release", .{self.dragging.name});
}
self.dragging.end();
self.refreshWindow(@src(), null);
}
if (!dvui.eventMatch(e, .{ .id = self.data().id, .r = self.rect_pixels, .cleanup = true }))
continue;
if (e.evt == .mouse) {
if (e.evt.mouse.action == .focus) {
// unhandled click, clear focus
self.focusWidget(null, null, null);
}
} else if (e.evt == .key) {
if ((e.evt.key.action == .down or e.evt.key.action == .repeat) and e.evt.key.matchBind("next_widget")) {
e.handle(@src(), self.data());
dvui.tabIndexNext(e.num);
}
if ((e.evt.key.action == .down or e.evt.key.action == .repeat) and e.evt.key.matchBind("prev_widget")) {
e.handle(@src(), self.data());
dvui.tabIndexPrev(e.num);
}
} else if (e.evt == .window) {
if (e.evt.window.action == .close) {
e.handle(@src(), self.data());
self.close();
self.refreshWindow(@src(), null);
} else if (e.evt.window.action == .leave) {
std.debug.assert(e.target_windowId == self.data().id);
e.handle(@src(), self.data());
// Put off-screen to avoid things like hover to appear stucked
self.mouse_pt = .{ .x = -1, .y = -1 };
self.refreshWindow(@src(), null);
}
} else if (e.evt == .app) {
if (e.evt.app.action == .quit) {
e.handle(@src(), self.data());
self.close();
self.refreshWindow(@src(), null);
}
}
}
if (dvui.debug.logEvents(null)) {
for (evts) |*e| {
if (e.handled) continue;
log.debug("Unhandled {f}", .{e});
}
log.debug("Event Handing Frame End", .{});
}
self.mouse_pt_prev = self.mouse_pt;
const focused_sw = self.subwindows.focused();
if (focused_sw != null and !focused_sw.?.used) {
// our focused subwindow didn't show this frame, focus the highest one that did
var i = self.subwindows.stack.items.len;
while (i > 0) : (i -= 1) {
const sw = self.subwindows.stack.items[i - 1];
if (sw.used and sw.stay_above_parent_window == null) {
//std.debug.print("focused subwindow lost, focusing {d}\n", .{i - 1});
self.focusSubwindow(sw.id, null);
break;
}
}
self.refreshWindow(@src(), null);
}
// Check that the final event was our synthetic mouse position event.
// If one of the addEvent* functions forgot to add the synthetic mouse
// event to the end this will print a debug message.
self.positionMouseEventRemove();
{
const cap = self._arena.queryCapacity();
//std.log.debug("_arena capacity {d}", .{cap});
_ = self._arena.reset(.{ .retain_with_limit = cap - @divTrunc(cap, 10) });
}
{
const cap = self._lifo_arena.queryCapacity();
//std.log.debug("_lifo_arena capacity {d}", .{cap});
_ = self._lifo_arena.reset(.{ .retain_with_limit = cap - @divTrunc(cap, 10) });
}
{
const cap = self._widget_stack.arena.queryCapacity();
//std.log.debug("_widget_stack capacity {d}", .{cap});
_ = self._widget_stack.reset(.{ .retain_with_limit = cap - @divTrunc(cap, 10) });
}
try self.initEvents();
if (self.inject_motion_event) {
self.inject_motion_event = false;
_ = try self.addEventMouseMotion(.{ .pt = self.mouse_pt });
}
if (dvui.accesskit_enabled) {
self.accesskit.pushUpdates();
}
defer dvui.current_window = self.previous_window;
// Frame phase timing: all CPU work is done (event/build/render were filled
// in by endRendering). Snapshot the total before the backend presents, so a
// blocking/vsync renderPresent doesn't pollute this CPU timing.
self.frame_timing_last.total_ns = nsSince(self.backend.nanoTime(), self.ft_total_start);
if (opts.manage_backend) {
self.backend.setCursor(self.cursorRequested());
self.backend.textInputRect(self.textInputRequested());
self.backend.renderPresent();
}
{
// Now close the child os windows that we didn't see during this frame.
var child_win_it = self.child_os_wins.iterator();
while (child_win_it.next_resetting()) |missing_win| {
missing_win.value.deinit(self.gpa);
}
// And reset the `has_begin` flag to spot duplicate
child_win_it = self.child_os_wins.iterator();
while (child_win_it.next()) |remaining_win| {
remaining_win.value_ptr.has_begin = false;
}
}
// This is what refresh affects
if (self.extra_frames_needed > 0) {
return 0;
}
// If there are current animations, return 0 so we go as fast as we can.
// If all animations are scheduled in the future, pick the soonest start.
var ret: ?u32 = null;
var it = self.animations.iterator();
while (it.next_used()) |kv| {
if (kv.value_ptr.start_time > 0) {
const st = @as(u32, @intCast(kv.value_ptr.start_time));
ret = @min(ret orelse st, st);
} else if (kv.value_ptr.end_time > 0) {
ret = 0;
break;
}
}
return ret;
}