DVUI

begin

Make this window the current window.

All widgets for this window should be declared between this call and Window.end.

Parameters

#
self:*Self
*Self
time_ns:i128
i128

Source

Implementation

#
pub fn begin(
    self: *Self,
    time_ns: i128,
) dvui.Backend.GenericError!void {
    self.ft_total_start = self.backend.nanoTime();

    try self.backend.accessKitInitInBegin(&self.accesskit);

    // If time_ns jumps backward, then we will stay on the current
    // frame_time_ns.  In that case we use a dummy value (10ms) for updating
    // animations and `secondsSinceLastFrame`.
    var micros_since_last: u32 = 10_000;
    if (time_ns > self.frame_time_ns) {
        // enforce monotinicity
        var nanos_since_last = time_ns - self.frame_time_ns;

        // make sure the @intCast below doesn't panic
        const max_nanos_since_last: i128 = std.math.maxInt(u32) * std.time.ns_per_us;
        nanos_since_last = @min(nanos_since_last, max_nanos_since_last);

        micros_since_last = @as(u32, @intCast(@divFloor(nanos_since_last, std.time.ns_per_us)));
        micros_since_last = @max(1, micros_since_last);
        self.frame_time_ns = time_ns;
    }

    //std.debug.print(" frame_time_ns {d}\n", .{self.frame_time_ns});

    self.previous_window = dvui.current_window;
    dvui.current_window = self;

    if (self.previous_window) |pw| {
        if (pw == self) {
            log.err("Window.begin() window is already the current_window - ensure Window.end() is called for each Window.begin()\n", .{});
        }
    }

    self.end_rendering_done = false;
    self.render_stats = .{};
    self.cursor_requested = null;
    self.text_input_rect = null;
    self.last_focused_id_this_frame = .zero;
    self.last_focused_id_in_subwindow = .zero;

    // just in case something went wrong, start at zero
    dvui.TabIndexGroup.current = .zero;

    if (self.is_primary)
        dvui.debug.reset(self.gpa);

    self.data_store.reset(self.gpa);
    self.texture_cache.reset(self.backend);
    self.subwindows.reset();
    self.child_os_wins.reset();
    self.fonts.reset(self.gpa, self.backend);

    for (self.frame_times, 0..) |_, i| {
        if (i == (self.frame_times.len - 1)) {
            self.frame_times[i] = 0;
        } else {
            self.frame_times[i] = self.frame_times[i + 1] +| micros_since_last;
        }
    }

    self.min_sizes.reset();
    //std.debug.print("min_sizes {d}\n", .{self.min_sizes.count()});

    {
        var it = self.tags.iterator();
        while (it.next_resetting()) |kv| {
            //std.debug.print("tag dead free {s}\n", .{kv.key});
            self.gpa.free(kv.key);
        }
        //std.debug.print("tags {d}\n", .{self.tags.count()});
    }

    // Swap current and previous tab index lists
    std.mem.swap(@TypeOf(self.tab_index), &self.tab_index, &self.tab_index_prev);
    // Retain capacity because it's likely to be small and that the same capacity will be needed again
    self.tab_index.clearRetainingCapacity();

    // call this before we call any backend functions like pixelSize or windowSize
    try self.backend.begin(self.arena());

    self.rect_pixels = .fromSize(self.backend.pixelSize());
    dvui.clipSet(self.rect_pixels);

    const sysContentScale = self.backend.contentScale();
    self.data().rect = Rect.Natural.fromSize(self.backend.windowSize()).scale(1.0 / sysContentScale / self.content_scale, Rect);
    self.natural_scale = if (self.data().rect.w == 0) 1.0 else self.rect_pixels.w / self.data().rect.w;

    // deal with floating point weirdness when content_scale is like 1.25
    // otherwise we could end up with rect.w == 753.60004 or natural_scale 1.2499999
    self.data().rect.w = @round(self.data().rect.w * 100.0) / 100.0;
    self.data().rect.h = @round(self.data().rect.h * 100.0) / 100.0;
    self.natural_scale = @round(self.natural_scale * 100.0) / 100.0;

    //dvui.log.debug("window size {d} x {d} renderer size {d} x {d} scale {d} system content scale {d} dvui content_scale {d}", .{ self.data().rect.w, self.data().rect.h, self.rect_pixels.w, self.rect_pixels.h, self.natural_scale, sysContentScale, self.content_scale });

    try self.subwindows.add(self.gpa, self.data().id, self.data().rect, self.rect_pixels, false, null, true);
    _ = self.subwindows.setCurrent(self.data().id, .cast(self.data().rect));

    self.extra_frames_needed -|= 1;
    self.secs_since_last_frame = @as(f32, @floatFromInt(micros_since_last)) / 1_000_000;

    {
        // update animations and remove dead ones
        // animations are checked in `end()` to affect waiting time
        const micros: i32 = if (micros_since_last > math.maxInt(i32)) math.maxInt(i32) else @as(i32, @intCast(micros_since_last));
        var it = self.animations.iterator();
        while (it.next_used()) |kv| {
            if (kv.value_ptr.end_time <= 0) {
                // was in the past before subtracting micros, so was "done" last frame, remove it
                @TypeOf(self.animations).setUsed(kv.value_ptr, false);
            } else {
                kv.value_ptr.start_time -|= micros;
                kv.value_ptr.end_time -|= micros;
                // was in the future last frame, so even if end_time is now
                // negative, we keep it so it's "done" this frame
            }
        }
        self.animations.reset();
    }

    if (!self.captured_last_frame) {
        // widget that had capture went away
        self.capture = null;
    }
    self.captured_last_frame = false;

    self.data().parent = self.widget();
    self.current_parent = self.widget();
    self.data().register();

    self.layout = .{};

    // Frame phase timing: begin() is done, the app will now pump OS events and
    // then run widget code. The first widget to register ends the event phase
    // and starts the build phase (see `WidgetData.register`). Default
    // ft_build_start to here so an empty frame (no widgets) stays sane.
    self.ft_events_start = self.backend.nanoTime();
    self.ft_build_start = self.ft_events_start;
    self.ft_awaiting_build = true;
}