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