DVUI

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;
}