this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

at cb00a91471f6b858a828c754211862acb8e3640a 139 lines 6.5 kB view raw
1const std = @import("std"); 2const vaxis = @import("vaxis"); 3const vxfw = vaxis.vxfw; 4 5/// Our main application state 6const Model = struct { 7 /// State of the counter 8 count: u32 = 0, 9 /// The button. This widget is stateful and must live between frames 10 button: vxfw.Button, 11 12 /// Helper function to return a vxfw.Widget struct 13 pub fn widget(self: *Model) vxfw.Widget { 14 return .{ 15 .userdata = self, 16 .eventHandler = Model.typeErasedEventHandler, 17 .drawFn = Model.typeErasedDrawFn, 18 }; 19 } 20 21 /// This function will be called from the vxfw runtime. 22 fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void { 23 const self: *Model = @ptrCast(@alignCast(ptr)); 24 switch (event) { 25 // The root widget is always sent an init event as the first event. Users of the 26 // library can also send this event to other widgets they create if they need to do 27 // some initialization. 28 .init => return ctx.requestFocus(self.button.widget()), 29 .key_press => |key| { 30 if (key.matches('c', .{ .ctrl = true })) { 31 ctx.quit = true; 32 return; 33 } 34 }, 35 // We can request a specific widget gets focus. In this case, we always want to focus 36 // our button. Having focus means that key events will be sent up the widget tree to 37 // the focused widget, and then bubble back down the tree to the root. Users can tell 38 // the runtime the event was handled and the capture or bubble phase will stop 39 .focus_in => return ctx.requestFocus(self.button.widget()), 40 else => {}, 41 } 42 } 43 44 /// This function is called from the vxfw runtime. It will be called on a regular interval, and 45 /// only when any event handler has marked the redraw flag in EventContext as true. By 46 /// explicitly requiring setting the redraw flag, vxfw can prevent excessive redraws for events 47 /// which don't change state (ie mouse motion, unhandled key events, etc) 48 fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) std.mem.Allocator.Error!vxfw.Surface { 49 const self: *Model = @ptrCast(@alignCast(ptr)); 50 // The DrawContext is inspired from Flutter. Each widget will receive a minimum and maximum 51 // constraint. The minimum constraint will always be set, even if it is set to 0x0. The 52 // maximum constraint can have null width and/or height - meaning there is no constraint in 53 // that direction and the widget should take up as much space as it needs. By calling size() 54 // on the max, we assert that it has some constrained size. This is *always* the case for 55 // the root widget - the maximum size will always be the size of the terminal screen. 56 const max_size = ctx.max.size(); 57 58 // The DrawContext also contains an arena allocator that can be used for each frame. The 59 // lifetime of this allocation is until the next time we draw a frame. This is useful for 60 // temporary allocations such as the one below: we have an integer we want to print as text. 61 // We can safely allocate this with the ctx arena since we only need it for this frame. 62 if (self.count > 0) { 63 self.button.label = try std.fmt.allocPrint(ctx.arena, "Clicks: {d}", .{self.count}); 64 } else { 65 self.button.label = "Click me!"; 66 } 67 68 // Each widget returns a Surface from it's draw function. A Surface contains the rectangular 69 // area of the widget, as well as some information about the surface or widget: can we focus 70 // it? does it handle the mouse? 71 // 72 // It DOES NOT contain the location it should be within it's parent. Only the parent can set 73 // this via a SubSurface. Here, we will return a Surface for the root widget (Model), which 74 // has two SubSurfaces: one for the text and one for the button. A SubSurface is a Surface 75 // with an offset and a z-index - the offset can be negative. This lets a parent draw a 76 // child and place it within itself 77 const button_child: vxfw.SubSurface = .{ 78 .origin = .{ .row = 0, .col = 0 }, 79 .surface = try self.button.draw(ctx.withConstraints( 80 ctx.min, 81 // Here we explicitly set a new maximum size constraint for the Button. A Button will 82 // expand to fill it's area and must have some hard limit in the maximum constraint 83 .{ .width = 16, .height = 3 }, 84 )), 85 }; 86 87 // We also can use our arena to allocate the slice for our SubSurfaces. This slice only 88 // needs to live until the next frame, making this safe. 89 const children = try ctx.arena.alloc(vxfw.SubSurface, 1); 90 children[0] = button_child; 91 92 return .{ 93 // A Surface must have a size. Our root widget is the size of the screen 94 .size = max_size, 95 .widget = self.widget(), 96 // We didn't actually need to draw anything for the root. In this case, we can set 97 // buffer to a zero length slice. If this slice is *not zero length*, the runtime will 98 // assert that it's length is equal to the size.width * size.height. 99 .buffer = &.{}, 100 .children = children, 101 }; 102 } 103 104 /// The onClick callback for our button. This is also called if we press enter while the button 105 /// has focus 106 fn onClick(maybe_ptr: ?*anyopaque, ctx: *vxfw.EventContext) anyerror!void { 107 const ptr = maybe_ptr orelse return; 108 const self: *Model = @ptrCast(@alignCast(ptr)); 109 self.count +|= 1; 110 return ctx.consumeAndRedraw(); 111 } 112}; 113 114pub fn main() !void { 115 var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 116 defer _ = gpa.deinit(); 117 118 const allocator = gpa.allocator(); 119 120 var app = try vxfw.App.init(allocator); 121 defer app.deinit(); 122 123 // We heap allocate our model because we will require a stable pointer to it in our Button 124 // widget 125 const model = try allocator.create(Model); 126 defer allocator.destroy(model); 127 128 // Set the initial state of our button 129 model.* = .{ 130 .count = 0, 131 .button = .{ 132 .label = "Click me!", 133 .onClick = Model.onClick, 134 .userdata = model, 135 }, 136 }; 137 138 try app.run(model.widget(), .{}); 139}