feat(console): lazy load items
This commit is contained in:
@@ -5,15 +5,19 @@ const vxfw = vaxis.vxfw;
|
|||||||
const models = @import("../core/models.zig");
|
const models = @import("../core/models.zig");
|
||||||
const provider = @import("../core/provider/provider.zig");
|
const provider = @import("../core/provider/provider.zig");
|
||||||
const local_provider = @import("../core/provider/local.zig");
|
const local_provider = @import("../core/provider/local.zig");
|
||||||
const tab = @import("../core/tab/tab.zig");
|
const Tab = @import("../core/tab/tab.zig").Tab;
|
||||||
const Tab = tab.Tab;
|
const locked = @import("../core/sync.zig").locked;
|
||||||
|
|
||||||
/// Our main application state
|
/// Our main application state
|
||||||
const Model = struct {
|
const Model = struct {
|
||||||
current_items: vxfw.ListView,
|
usage_number: locked(u16) = .{ .data = 0 },
|
||||||
|
running: bool = true,
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
current_items: *vxfw.ListView,
|
||||||
|
current_items_allocator: ?std.heap.ArenaAllocator = null,
|
||||||
tab: *Tab,
|
tab: *Tab,
|
||||||
/// State of the counter
|
/// State of the counter
|
||||||
count: u32 = 0,
|
count: usize = 0,
|
||||||
/// The button. This widget is stateful and must live between frames
|
/// The button. This widget is stateful and must live between frames
|
||||||
button: vxfw.Button,
|
button: vxfw.Button,
|
||||||
|
|
||||||
@@ -28,12 +32,12 @@ const Model = struct {
|
|||||||
|
|
||||||
/// This function will be called from the vxfw runtime.
|
/// This function will be called from the vxfw runtime.
|
||||||
fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void {
|
fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void {
|
||||||
const self: *Model = @ptrCast(@alignCast(ptr));
|
const vm: *Model = @ptrCast(@alignCast(ptr));
|
||||||
switch (event) {
|
switch (event) {
|
||||||
// The root widget is always sent an init event as the first event. Users of the
|
// The root widget is always sent an init event as the first event. Users of the
|
||||||
// library can also send this event to other widgets they create if they need to do
|
// library can also send this event to other widgets they create if they need to do
|
||||||
// some initialization.
|
// some initialization.
|
||||||
.init => return ctx.requestFocus(self.current_items.widget()),
|
.init => return ctx.requestFocus(vm.current_items.widget()),
|
||||||
.key_press => |key| {
|
.key_press => |key| {
|
||||||
if (key.matches('c', .{ .ctrl = true })) {
|
if (key.matches('c', .{ .ctrl = true })) {
|
||||||
ctx.quit = true;
|
ctx.quit = true;
|
||||||
@@ -44,7 +48,7 @@ const Model = struct {
|
|||||||
// our button. Having focus means that key events will be sent up the widget tree to
|
// our button. Having focus means that key events will be sent up the widget tree to
|
||||||
// the focused widget, and then bubble back down the tree to the root. Users can tell
|
// the focused widget, and then bubble back down the tree to the root. Users can tell
|
||||||
// the runtime the event was handled and the capture or bubble phase will stop
|
// the runtime the event was handled and the capture or bubble phase will stop
|
||||||
.focus_in => return ctx.requestFocus(self.current_items.widget()),
|
.focus_in => return ctx.requestFocus(vm.current_items.widget()),
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,6 +59,7 @@ const Model = struct {
|
|||||||
/// which don't change state (ie mouse motion, unhandled key events, etc)
|
/// which don't change state (ie mouse motion, unhandled key events, etc)
|
||||||
fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) std.mem.Allocator.Error!vxfw.Surface {
|
fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) std.mem.Allocator.Error!vxfw.Surface {
|
||||||
const vm: *Model = @ptrCast(@alignCast(ptr));
|
const vm: *Model = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
// The DrawContext is inspired from Flutter. Each widget will receive a minimum and maximum
|
// The DrawContext is inspired from Flutter. Each widget will receive a minimum and maximum
|
||||||
// constraint. The minimum constraint will always be set, even if it is set to 0x0. The
|
// constraint. The minimum constraint will always be set, even if it is set to 0x0. The
|
||||||
// maximum constraint can have null width and/or height - meaning there is no constraint in
|
// maximum constraint can have null width and/or height - meaning there is no constraint in
|
||||||
@@ -129,6 +134,59 @@ const Model = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn updateChildren(vm: *Model) !void {
|
||||||
|
if (vm.current_items_allocator) |a| {
|
||||||
|
a.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.current_items_allocator = std.heap.ArenaAllocator.init(vm.allocator);
|
||||||
|
const arena_allocator = vm.current_items_allocator.?.allocator();
|
||||||
|
|
||||||
|
const tab = vm.tab;
|
||||||
|
var listView = vm.current_items;
|
||||||
|
|
||||||
|
tab.currentItems.mutex.lock();
|
||||||
|
defer tab.currentItems.mutex.unlock();
|
||||||
|
const children = if (tab.currentItems.data) |items| blk: {
|
||||||
|
const children = try arena_allocator.alloc(vxfw.Widget, items.items.len);
|
||||||
|
for (0.., items.items[0..children.len]) |i, child| {
|
||||||
|
const text1 = try arena_allocator.dupe(u8, child.fullName.path);
|
||||||
|
|
||||||
|
const text_c = try arena_allocator.create(vxfw.Text);
|
||||||
|
text_c.* = vxfw.Text{
|
||||||
|
.text = text1,
|
||||||
|
.overflow = .clip,
|
||||||
|
.softwrap = false,
|
||||||
|
.style = .{ .bold = true },
|
||||||
|
};
|
||||||
|
children[i] = text_c.widget();
|
||||||
|
}
|
||||||
|
break :blk children;
|
||||||
|
} else &.{};
|
||||||
|
|
||||||
|
listView.children.slice = children;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loop1(vm: *Model) void {
|
||||||
|
vm.usage_number.data += 1;
|
||||||
|
while (vm.running) {
|
||||||
|
inner_loop(vm) catch {};
|
||||||
|
std.Thread.sleep(2000 * std.time.ns_per_ms);
|
||||||
|
}
|
||||||
|
vm.usage_number.data -= 1;
|
||||||
|
}
|
||||||
|
fn inner_loop(vm: *Model) !void {
|
||||||
|
if (vm.tab.currentItemsChanged) {
|
||||||
|
std.Thread.sleep(2000 * std.time.ns_per_ms);
|
||||||
|
try updateChildren(vm);
|
||||||
|
vm.tab.currentItemsChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.tab.currentItems.mutex.lock();
|
||||||
|
defer vm.tab.currentItems.mutex.unlock();
|
||||||
|
vm.count = if (vm.tab.currentItems.data) |c| c.items.len else 999;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var gpa = std.heap.DebugAllocator(.{}){};
|
var gpa = std.heap.DebugAllocator(.{}){};
|
||||||
const gp_allocator = gpa.allocator();
|
const gp_allocator = gpa.allocator();
|
||||||
@@ -148,9 +206,6 @@ pub fn main() !void {
|
|||||||
|
|
||||||
var localContentProvider = local_provider.LocalContentProvider{ .threadPool = &pool };
|
var localContentProvider = local_provider.LocalContentProvider{ .threadPool = &pool };
|
||||||
|
|
||||||
var app = try vxfw.App.init(allocator);
|
|
||||||
defer app.deinit();
|
|
||||||
|
|
||||||
const homeFullName: models.FullName = .{ .path = "/home/adam" };
|
const homeFullName: models.FullName = .{ .path = "/home/adam" };
|
||||||
const homeItem = try localContentProvider.getItemByFullName(homeFullName, &.{}, allocator);
|
const homeItem = try localContentProvider.getItemByFullName(homeFullName, &.{}, allocator);
|
||||||
// defer homeItem.deinit();
|
// defer homeItem.deinit();
|
||||||
@@ -167,33 +222,17 @@ pub fn main() !void {
|
|||||||
|
|
||||||
tab1.setCurrentLocation(c);
|
tab1.setCurrentLocation(c);
|
||||||
|
|
||||||
// We heap allocate our model because we will require a stable pointer to it in our Button
|
|
||||||
// widget
|
|
||||||
const model = try allocator.create(Model);
|
const model = try allocator.create(Model);
|
||||||
defer allocator.destroy(model);
|
defer allocator.destroy(model);
|
||||||
|
|
||||||
var arena = std.heap.ArenaAllocator.init(allocator);
|
|
||||||
defer arena.deinit();
|
|
||||||
const arena_allocator = arena.allocator();
|
|
||||||
|
|
||||||
std.Thread.sleep(1 * std.time.ns_per_s);
|
std.Thread.sleep(1 * std.time.ns_per_s);
|
||||||
const children1 = if (tab1.currentItems) |items| blk: {
|
|
||||||
const children = try arena_allocator.alloc(vxfw.Widget, items.items.len);
|
|
||||||
for (0.., items.items[0..children.len]) |i, child| {
|
|
||||||
const text1 = try arena_allocator.dupe(u8, child.fullName.path);
|
|
||||||
|
|
||||||
const text_c = try arena_allocator.create(vxfw.Text);
|
const list = try allocator.create(vxfw.ListView);
|
||||||
text_c.* = vxfw.Text{ .text = text1, .overflow = .clip, .softwrap = false, .style = .{ .bold = true } };
|
list.* = .{
|
||||||
children[i] = text_c.widget();
|
.children = .{ .slice = &.{} },
|
||||||
}
|
|
||||||
break :blk children;
|
|
||||||
} else &.{};
|
|
||||||
|
|
||||||
const list: vxfw.ListView = .{
|
|
||||||
.children = .{ .slice = children1 },
|
|
||||||
};
|
};
|
||||||
// Set the initial state of our button
|
|
||||||
model.* = .{
|
model.* = .{
|
||||||
|
.allocator = allocator,
|
||||||
.current_items = list,
|
.current_items = list,
|
||||||
.tab = tab1,
|
.tab = tab1,
|
||||||
.count = 0,
|
.count = 0,
|
||||||
@@ -204,6 +243,24 @@ pub fn main() !void {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
model.usage_number.data += 1;
|
||||||
|
|
||||||
|
try pool.spawn(loop1, .{model});
|
||||||
|
|
||||||
|
var app = try vxfw.App.init(allocator);
|
||||||
|
defer app.deinit();
|
||||||
|
|
||||||
try app.run(model.widget(), .{});
|
try app.run(model.widget(), .{});
|
||||||
std.Thread.sleep(1 * std.time.ns_per_s);
|
// std.Thread.sleep(10 * std.time.ns_per_s);
|
||||||
|
|
||||||
|
model.usage_number.data -= 1;
|
||||||
|
model.running = false;
|
||||||
|
|
||||||
|
while (model.usage_number.data > 0) {
|
||||||
|
std.Thread.sleep(100 * std.time.ns_per_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model.current_items_allocator) |a| {
|
||||||
|
a.deinit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
src/core/sync.zig
Normal file
7
src/core/sync.zig
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
pub fn locked(comptime T: type) type {
|
||||||
|
return struct {
|
||||||
|
data: T,
|
||||||
|
mutex: std.Thread.Mutex = .{},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -6,10 +6,12 @@ const models = @import("../models.zig");
|
|||||||
const Container = models.Container;
|
const Container = models.Container;
|
||||||
const Item = models.Item;
|
const Item = models.Item;
|
||||||
|
|
||||||
|
const locked = @import("../sync.zig").locked;
|
||||||
|
|
||||||
pub const Tab = struct {
|
pub const Tab = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
currentLocation: ?*Container,
|
currentLocation: ?*Container,
|
||||||
currentItems: ?std.ArrayList(*Item),
|
currentItems: locked(?std.ArrayList(*Item)),
|
||||||
currentItemsChanged: bool = false,
|
currentItemsChanged: bool = false,
|
||||||
threadPool: *std.Thread.Pool,
|
threadPool: *std.Thread.Pool,
|
||||||
_private: Private,
|
_private: Private,
|
||||||
@@ -25,7 +27,7 @@ pub const Tab = struct {
|
|||||||
) void {
|
) void {
|
||||||
self.* = Tab{
|
self.* = Tab{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.currentItems = null,
|
.currentItems = .{ .data = null },
|
||||||
.currentLocation = null,
|
.currentLocation = null,
|
||||||
.threadPool = threadPool,
|
.threadPool = threadPool,
|
||||||
._private = .{
|
._private = .{
|
||||||
@@ -52,15 +54,20 @@ pub const Tab = struct {
|
|||||||
if (self._private.currentItemsAllocator) |arena| {
|
if (self._private.currentItemsAllocator) |arena| {
|
||||||
arena.deinit();
|
arena.deinit();
|
||||||
}
|
}
|
||||||
if (self.currentItems) |items| {
|
|
||||||
items.deinit();
|
{
|
||||||
|
self.currentItems.mutex.lock();
|
||||||
|
defer self.currentItems.mutex.unlock();
|
||||||
|
if (self.currentItems.data) |items| {
|
||||||
|
items.deinit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self._private.currentItemsAllocator = std.heap.ArenaAllocator.init(self.allocator);
|
self._private.currentItemsAllocator = std.heap.ArenaAllocator.init(self.allocator);
|
||||||
const arenaAllocator = &self._private.currentItemsAllocator.?;
|
const arenaAllocator = &self._private.currentItemsAllocator.?;
|
||||||
const arena = arenaAllocator.allocator();
|
const arena = arenaAllocator.allocator();
|
||||||
|
|
||||||
var threadSafeAllocator = std.heap.ThreadSafeAllocator{.child_allocator = arena};
|
var threadSafeAllocator = std.heap.ThreadSafeAllocator{ .child_allocator = arena };
|
||||||
const allocator = threadSafeAllocator.allocator();
|
const allocator = threadSafeAllocator.allocator();
|
||||||
|
|
||||||
errdefer {
|
errdefer {
|
||||||
@@ -68,10 +75,15 @@ pub const Tab = struct {
|
|||||||
self._private.currentItemsAllocator = null;
|
self._private.currentItemsAllocator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.currentItems = std.ArrayList(*Item).init(allocator);
|
{
|
||||||
errdefer {
|
self.currentItems.mutex.lock();
|
||||||
self.currentItems.?.deinit();
|
defer self.currentItems.mutex.unlock();
|
||||||
self.currentItems = null;
|
|
||||||
|
self.currentItems.data = std.ArrayList(*Item).init(allocator);
|
||||||
|
errdefer {
|
||||||
|
self.currentItems.data.?.deinit();
|
||||||
|
self.currentItems.data = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (location.childrenLoading) {
|
while (location.childrenLoading) {
|
||||||
@@ -80,7 +92,11 @@ pub const Tab = struct {
|
|||||||
|
|
||||||
for (location.children.items) |item| {
|
for (location.children.items) |item| {
|
||||||
const resolvedItem = try location.item.provider.getItemByFullName(item, &.{ .skipChildInit = false }, allocator);
|
const resolvedItem = try location.item.provider.getItemByFullName(item, &.{ .skipChildInit = false }, allocator);
|
||||||
try self.currentItems.?.append(resolvedItem);
|
|
||||||
|
self.currentItems.mutex.lock();
|
||||||
|
defer self.currentItems.mutex.unlock();
|
||||||
|
|
||||||
|
try self.currentItems.data.?.append(resolvedItem);
|
||||||
self.currentItemsChanged = true;
|
self.currentItemsChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user