DVUI

init

It's expected to call this when self is undefined

Parameters

#
self:*CacheWidget
*CacheWidget
src:std.builtin.SourceLocation
std.builtin.SourceLocation
init_opts:InitOptions
InitOptions
opts:Options
Options

Source

Implementation

#
pub fn init(self: *CacheWidget, src: std.builtin.SourceLocation, init_opts: InitOptions, opts: Options) void {
    const defaults = Options{ .name = "Cache" };
    self.* = .{
        .wd = .init(src, .{}, defaults.override(opts)),
        .init_opts = init_opts,
        // SAFETY: Set bellow
        .hash = undefined,
        // SAFETY: Set bellow
        .tex_uv = undefined,
        .refresh_prev_value = dvui.currentWindow().extra_frames_needed,
    };

    self.hash = self.data().id.update("_tex").asU64();
    self.tex_uv = dvui.dataGet(null, self.data().id, "_tex_uv", Size) orelse .{};

    dvui.currentWindow().extra_frames_needed = 0;
    if (dvui.dataGet(null, self.data().id, "_unsupported", bool) orelse false) self.state = .unsupported;

    dvui.parentSet(self.widget());
    self.data().register();
    self.data().borderAndBackground(.{});

    if (self.init_opts.invalidate) {
        if (dvui.textureGetCached(self.hash)) |t| {
            // if we had a texture, show it this frame because our contents needs a frame to get sizing
            self.drawCachedTexture(t);
            dvui.textureInvalidateCache(self.hash);

            // now we've shown the texture, so prevent any widgets from drawing on top of it this frame
            // - can happen if some widgets precalculate their size (like label)
            self.old_clip = dvui.clip(.{});
        }
    }

    if (self.state != .ok) return;

    if (dvui.textureGetCached(self.hash)) |t| {
        // successful cache, draw texture and enforce min size
        self.drawCachedTexture(t);
        const tex_size: dvui.Size = .{ .w = @floatFromInt(t.width), .h = @floatFromInt(t.height) };
        const rs = self.data().rectScale();
        self.data().minSizeMax(self.data().options.padSize(tex_size.scale(1.0 / rs.s, dvui.Size)));
    } else {

        // we need to cache, but only do it if we didn't have any refreshes from last frame
        if (dvui.dataGet(null, self.data().id, "_cache_now", bool) orelse false) {
            const rs = self.data().contentRectScale();
            const w: u32 = @ceil(rs.r.w);
            const h: u32 = @ceil(rs.r.h);
            self.tex_uv = .{ .w = rs.r.w / @ceil(rs.r.w), .h = rs.r.h / @ceil(rs.r.h) };

            self.caching_tex = dvui.textureCreateTarget(.{ .width = w, .height = h }) catch |err| blk: switch (err) {
                error.TextureCreate, error.NotImplemented => {
                    self.state = .texture_create_error;
                    if (dvui.dataGet(null, self.data().id, "_texture_create_error", bool) orelse false) {
                        // indicate that texture failed last frame to prevent backends that always return errors from forever refreshing
                        dvui.dataSet(null, self.data().id, "_texture_create_error", true);
                    }
                    break :blk null;
                },
                else => {
                    dvui.logError(@src(), err, "Could not create cache texture", .{});
                    break :blk null;
                },
            };

            if (self.caching_tex) |tex| {
                var offset = rs.r.topLeft();
                if (dvui.snapToPixels()) {
                    offset.x = @round(offset.x);
                    offset.y = @round(offset.y);
                }
                self.old_target = dvui.renderTarget(.{ .texture = tex, .offset = offset });

                // clip to just us, even if we are off screen
                self.old_clip = dvui.clipGet();
                dvui.clipSet(rs.r);
            } else if (self.state != .texture_create_error) {
                // `textureCreateTarget` returned null, indicating render target are unsupported
                dvui.dataSet(null, self.data().id, "_unsupported", true);
            }
        }
    }
}