diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0cc851f..45044a3 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -9,3 +9,4 @@ _Replace this with a reference to an existing issue, or N/A if there is none_ ## Pre-requisites - [ ] I have tested & confirmed the changes work locally +- [ ] I have run `zig fmt` throughout my changes diff --git a/ly-ui/src/TerminalBuffer.zig b/ly-ui/src/TerminalBuffer.zig index b2383af..034916c 100644 --- a/ly-ui/src/TerminalBuffer.zig +++ b/ly-ui/src/TerminalBuffer.zig @@ -169,8 +169,8 @@ pub fn runEventLoop( self: *TerminalBuffer, allocator: Allocator, shared_error: SharedError, - layers: [][]Widget, - active_widget: Widget, + layers: [][]*Widget, + active_widget: *Widget, inactivity_delay: u16, position_widgets_fn: *const fn (*anyopaque) anyerror!void, inactivity_event_fn: ?*const fn (*anyopaque) anyerror!void, @@ -189,7 +189,7 @@ pub fn runEventLoop( var i: usize = 0; for (layers) |layer| { - for (layer) |*widget| { + for (layer) |widget| { try widget.update(context); if (widget.vtable.handle_fn != null) { @@ -210,7 +210,7 @@ pub fn runEventLoop( while (self.run) { if (self.update) { for (layers) |layer| { - for (layer) |*widget| { + for (layer) |widget| { try widget.update(context); } } @@ -229,7 +229,7 @@ pub fn runEventLoop( try TerminalBuffer.clearScreen(false); for (layers) |layer| { - for (layer) |*widget| { + for (layer) |widget| { widget.draw(); } } @@ -239,7 +239,7 @@ pub fn runEventLoop( var maybe_timeout: ?usize = null; for (layers) |layer| { - for (layer) |*widget| { + for (layer) |widget| { if (try widget.calculateTimeout(context)) |widget_timeout| { if (maybe_timeout == null or widget_timeout < maybe_timeout.?) maybe_timeout = widget_timeout; } @@ -275,7 +275,7 @@ pub fn runEventLoop( ); for (layers) |layer| { - for (layer) |*widget| { + for (layer) |widget| { widget.realloc() catch |err| { shared_error.writeError(error.WidgetReallocationFailed); try self.log_file.err( @@ -326,7 +326,7 @@ pub fn getActiveWidget(self: *TerminalBuffer) *Widget { return self.handlable_widgets.items[self.active_widget_index]; } -pub fn setActiveWidget(self: *TerminalBuffer, widget: Widget) void { +pub fn setActiveWidget(self: *TerminalBuffer, widget: *Widget) void { for (self.handlable_widgets.items, 0..) |widg, i| { if (widg.id == widget.id) self.active_widget_index = i; } diff --git a/ly-ui/src/components/BigLabel.zig b/ly-ui/src/components/BigLabel.zig index 156a498..bd9af6e 100644 --- a/ly-ui/src/components/BigLabel.zig +++ b/ly-ui/src/components/BigLabel.zig @@ -44,6 +44,7 @@ pub const BigLabelLocale = enum { fa, }; +instance: ?Widget = null, allocator: ?Allocator = null, buffer: *TerminalBuffer, text: []const u8, @@ -67,6 +68,7 @@ pub fn init( calculate_timeout_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!?usize, ) BigLabel { return .{ + .instance = null, .allocator = null, .buffer = buffer, .text = text, @@ -85,8 +87,9 @@ pub fn deinit(self: *BigLabel) void { if (self.allocator) |allocator| allocator.free(self.text); } -pub fn widget(self: *BigLabel) Widget { - return Widget.init( +pub fn widget(self: *BigLabel) *Widget { + if (self.instance) |*instance| return instance; + self.instance = Widget.init( "BigLabel", null, self, @@ -97,6 +100,7 @@ pub fn widget(self: *BigLabel) Widget { null, calculateTimeout, ); + return &self.instance.?; } pub fn setTextAlloc( diff --git a/ly-ui/src/components/CenteredBox.zig b/ly-ui/src/components/CenteredBox.zig index 934a83c..f68709e 100644 --- a/ly-ui/src/components/CenteredBox.zig +++ b/ly-ui/src/components/CenteredBox.zig @@ -7,6 +7,7 @@ const Widget = @import("../Widget.zig"); const CenteredBox = @This(); +instance: ?Widget = null, buffer: *TerminalBuffer, horizontal_margin: usize, vertical_margin: usize, @@ -40,6 +41,7 @@ pub fn init( update_fn: ?*const fn (*CenteredBox, *anyopaque) anyerror!void, ) CenteredBox { return .{ + .instance = null, .buffer = buffer, .horizontal_margin = horizontal_margin, .vertical_margin = vertical_margin, @@ -59,8 +61,9 @@ pub fn init( }; } -pub fn widget(self: *CenteredBox) Widget { - return Widget.init( +pub fn widget(self: *CenteredBox) *Widget { + if (self.instance) |*instance| return instance; + self.instance = Widget.init( "CenteredBox", null, self, @@ -71,6 +74,7 @@ pub fn widget(self: *CenteredBox) Widget { null, null, ); + return &self.instance.?; } pub fn positionXY(self: *CenteredBox, original_pos: Position) void { diff --git a/ly-ui/src/components/Label.zig b/ly-ui/src/components/Label.zig index 86603bb..c071058 100644 --- a/ly-ui/src/components/Label.zig +++ b/ly-ui/src/components/Label.zig @@ -8,6 +8,7 @@ const Position = @import("../Position.zig"); const TerminalBuffer = @import("../TerminalBuffer.zig"); const Widget = @import("../Widget.zig"); +instance: ?Widget, allocator: ?Allocator, instance: ?Widget, text: []const u8, @@ -31,6 +32,7 @@ pub fn init( ) Label { lidCounter += 1; return .{ + .instance = null, .allocator = null, .instance = null, .text = text, @@ -48,8 +50,8 @@ pub fn deinit(self: *Label) void { if (self.allocator) |allocator| allocator.free(self.text); } -pub fn widget(self: *Label) Widget { - if (self.instance) |inst| return inst; +pub fn widget(self: *Label) *Widget { + if (self.instance) |*instance| return instance; self.instance = Widget.init( "Label", null, @@ -61,7 +63,7 @@ pub fn widget(self: *Label) Widget { null, calculateTimeout, ); - return self.instance.?; // We already created the Widget. + return &self.instance.?; } pub fn setTextAlloc( diff --git a/ly-ui/src/components/Text.zig b/ly-ui/src/components/Text.zig index 0ad2128..fc04fe7 100644 --- a/ly-ui/src/components/Text.zig +++ b/ly-ui/src/components/Text.zig @@ -10,6 +10,7 @@ const DynamicString = std.ArrayListUnmanaged(u8); const Text = @This(); +instance: ?Widget, allocator: Allocator, buffer: *TerminalBuffer, text: DynamicString, @@ -38,6 +39,7 @@ pub fn init( ) !*Text { var self = try allocator.create(Text); self.* = Text{ + .instance = null, .allocator = allocator, .buffer = buffer, .text = .empty, @@ -70,8 +72,9 @@ pub fn deinit(self: *Text) void { self.allocator.destroy(self); } -pub fn widget(self: *Text) Widget { - return Widget.init( +pub fn widget(self: *Text) *Widget { + if (self.instance) |*instance| return instance; + self.instance = Widget.init( "Text", self.keybinds, self, @@ -82,6 +85,7 @@ pub fn widget(self: *Text) Widget { handle, null, ); + return &self.instance.?; } pub fn positionX(self: *Text, original_pos: Position) void { diff --git a/out.diff b/out.diff new file mode 100644 index 0000000..6c4a6ee --- /dev/null +++ b/out.diff @@ -0,0 +1,119 @@ +diff --git a/build.zig b/build.zig +index c53b905..960e649 100644 +--- a/build.zig ++++ b/build.zig +@@ -95,6 +95,9 @@ pub fn build(b: *std.Build) !void { + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + ++ const check_step = b.step("check", "Check for any errors"); ++ check_step.dependOn(&exe.step); ++ + const installexe_step = b.step("installexe", "Install Ly and the selected init system service"); + installexe_step.makeFn = Installer(true).make; + installexe_step.dependOn(b.getInstallStep()); +diff --git a/ly-ui/src/Widget.zig b/ly-ui/src/Widget.zig +index d66fce8..07f76c3 100644 +--- a/ly-ui/src/Widget.zig ++++ b/ly-ui/src/Widget.zig +@@ -12,6 +12,8 @@ const VTable = struct { + calculate_timeout_fn: ?*const fn (ptr: *anyopaque, ctx: *anyopaque) anyerror!?usize, + }; + ++pub var idCounter: u64 = 0; ++ + id: u64, + display_name: []const u8, + keybinds: ?TerminalBuffer.KeybindMap, +@@ -101,8 +103,9 @@ pub fn init( + }; + }; + ++ idCounter += 1; + return .{ +- .id = @intFromPtr(Impl.vtable.draw_fn), ++ .id = idCounter, + .display_name = display_name, + .keybinds = keybinds, + .pointer = pointer, +diff --git a/ly-ui/src/components/Label.zig b/ly-ui/src/components/Label.zig +index 83c651b..86603bb 100644 +--- a/ly-ui/src/components/Label.zig ++++ b/ly-ui/src/components/Label.zig +@@ -8,9 +8,8 @@ const Position = @import("../Position.zig"); + const TerminalBuffer = @import("../TerminalBuffer.zig"); + const Widget = @import("../Widget.zig"); + +-lid: u64, // HACK: we need to differenciate between labels in `updateCustomInfo`. +- // The underlying widget ID doesn't have the properties needed. + allocator: ?Allocator, ++instance: ?Widget, + text: []const u8, + max_width: ?usize, + fg: u32, +@@ -32,8 +31,8 @@ pub fn init( + ) Label { + lidCounter += 1; + return .{ +- .lid = lidCounter, + .allocator = null, ++ .instance = null, + .text = text, + .max_width = max_width, + .fg = fg, +@@ -50,7 +49,8 @@ pub fn deinit(self: *Label) void { + } + + pub fn widget(self: *Label) Widget { +- return Widget.init( ++ if (self.instance) |inst| return inst; ++ self.instance = Widget.init( + "Label", + null, + self, +@@ -61,6 +61,7 @@ pub fn widget(self: *Label) Widget { + null, + calculateTimeout, + ); ++ return self.instance.?; // We already created the Widget. + } + + pub fn setTextAlloc( +diff --git a/src/main.zig b/src/main.zig +index c049be2..2428f84 100644 +--- a/src/main.zig ++++ b/src/main.zig +@@ -1084,14 +1084,14 @@ pub fn main() !void { + + var lblIter = custom.labels.iterator(); + while (lblIter.next()) |i| { +- const w = Label.init("", ++ var w = Label.init("", + null, + state.buffer.fg, + state.buffer.bg, + &updateCustomInfo, + null, + ); +- i.value_ptr.id = w.lid; ++ i.value_ptr.id = w.widget().id; + i.value_ptr.counter = 2; + try state.custom_info.append(state.allocator, .{ + .info = i.value_ptr.*, +@@ -1763,7 +1763,7 @@ fn updateClock(self: *Label, ptr: *anyopaque) !void { + + fn updateCustomInfo(lbl: *Label, ptr: *anyopaque) !void { + const state: *UiState = @ptrCast(@alignCast(ptr)); +- const wid = lbl.lid; ++ const wid = lbl.widget().id; + var stdout = std.ArrayList(u8).empty; + defer stdout.deinit(state.allocator); + var stderr = std.ArrayList(u8).empty; +@@ -1925,6 +1925,7 @@ fn positionWidgets(ptr: *anyopaque) !void { + .addY(@intCast(i))); + } + for (state.custom_info.items, 0..) |*item, i| { ++ std.log.info("{s}: {}", .{item.info.name, item.lbl.widget().id}); + item.lbl.positionXY(state.edge_margin + .addY(@intCast(i)) + .invertX(state.buffer.width) diff --git a/src/animations/Cascade.zig b/src/animations/Cascade.zig index 970d99a..b11c728 100644 --- a/src/animations/Cascade.zig +++ b/src/animations/Cascade.zig @@ -8,6 +8,7 @@ const Widget = ly_ui.Widget; const Cascade = @This(); +instance: ?Widget = null, buffer: *TerminalBuffer, current_auth_fails: *usize, max_auth_fails: usize, @@ -18,14 +19,16 @@ pub fn init( max_auth_fails: usize, ) Cascade { return .{ + .instance = null, .buffer = buffer, .current_auth_fails = current_auth_fails, .max_auth_fails = max_auth_fails, }; } -pub fn widget(self: *Cascade) Widget { - return Widget.init( +pub fn widget(self: *Cascade) *Widget { + if (self.instance) |*instance| return instance; + self.instance = Widget.init( "Cascade", null, self, @@ -36,6 +39,7 @@ pub fn widget(self: *Cascade) Widget { null, null, ); + return &self.instance.?; } fn draw(self: *Cascade) void { diff --git a/src/animations/ColorMix.zig b/src/animations/ColorMix.zig index 227205f..0715825 100644 --- a/src/animations/ColorMix.zig +++ b/src/animations/ColorMix.zig @@ -21,6 +21,7 @@ fn length(vec: Vec2) f32 { return math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]); } +instance: ?Widget = null, start_time: TimeOfDay, terminal_buffer: *TerminalBuffer, animate: *bool, @@ -41,6 +42,7 @@ pub fn init( frame_delay: u16, ) !ColorMix { return .{ + .instance = null, .start_time = try interop.getTimeOfDay(), .terminal_buffer = terminal_buffer, .animate = animate, @@ -66,8 +68,9 @@ pub fn init( }; } -pub fn widget(self: *ColorMix) Widget { - return Widget.init( +pub fn widget(self: *ColorMix) *Widget { + if (self.instance) |*instance| return instance; + self.instance = Widget.init( "ColorMix", null, self, @@ -78,6 +81,7 @@ pub fn widget(self: *ColorMix) Widget { null, calculateTimeout, ); + return &self.instance.?; } fn draw(self: *ColorMix) void { diff --git a/src/animations/Doom.zig b/src/animations/Doom.zig index 8fa842b..9b0abae 100644 --- a/src/animations/Doom.zig +++ b/src/animations/Doom.zig @@ -16,6 +16,7 @@ pub const STEPS = 12; pub const HEIGHT_MAX = 9; pub const SPREAD_MAX = 4; +instance: ?Widget = null, start_time: TimeOfDay, allocator: Allocator, terminal_buffer: *TerminalBuffer, @@ -60,6 +61,7 @@ pub fn init( }; return .{ + .instance = null, .start_time = try interop.getTimeOfDay(), .allocator = allocator, .terminal_buffer = terminal_buffer, @@ -73,8 +75,9 @@ pub fn init( }; } -pub fn widget(self: *Doom) Widget { - return Widget.init( +pub fn widget(self: *Doom) *Widget { + if (self.instance) |*instance| return instance; + self.instance = Widget.init( "Doom", null, self, @@ -85,6 +88,7 @@ pub fn widget(self: *Doom) Widget { null, calculateTimeout, ); + return &self.instance.?; } fn deinit(self: *Doom) void { diff --git a/src/animations/DurFile.zig b/src/animations/DurFile.zig index 04fc8ff..b6aceae 100644 --- a/src/animations/DurFile.zig +++ b/src/animations/DurFile.zig @@ -304,6 +304,7 @@ const VEC_Y = 1; const DurFile = @This(); +instance: ?Widget = null, start_time: TimeOfDay, allocator: Allocator, terminal_buffer: *TerminalBuffer, @@ -408,6 +409,7 @@ pub fn init( const frame_time: u32 = @intFromFloat(1000 / dur_movie.framerate.?); return .{ + .instance = null, .start_time = try interop.getTimeOfDay(), .allocator = allocator, .terminal_buffer = terminal_buffer, @@ -427,8 +429,9 @@ pub fn init( }; } -pub fn widget(self: *DurFile) Widget { - return Widget.init( +pub fn widget(self: *DurFile) *Widget { + if (self.instance) |*instance| return instance; + self.instance = Widget.init( "DurFile", null, self, @@ -439,6 +442,7 @@ pub fn widget(self: *DurFile) Widget { null, calculateTimeout, ); + return &self.instance.?; } fn deinit(self: *DurFile) void { diff --git a/src/animations/GameOfLife.zig b/src/animations/GameOfLife.zig index 3f6e5d5..c100c52 100644 --- a/src/animations/GameOfLife.zig +++ b/src/animations/GameOfLife.zig @@ -21,6 +21,7 @@ const NEIGHBOR_DIRS = [_][2]i8{ .{ 1, 0 }, .{ 1, 1 }, }; +instance: ?Widget = null, start_time: TimeOfDay, allocator: Allocator, terminal_buffer: *TerminalBuffer, @@ -58,6 +59,7 @@ pub fn init( const next_grid = try allocator.alloc(bool, grid_size); var game = GameOfLife{ + .instance = null, .start_time = try interop.getTimeOfDay(), .allocator = allocator, .terminal_buffer = terminal_buffer, @@ -83,8 +85,9 @@ pub fn init( return game; } -pub fn widget(self: *GameOfLife) Widget { - return Widget.init( +pub fn widget(self: *GameOfLife) *Widget { + if (self.instance) |*instance| return instance; + self.instance = Widget.init( "GameOfLife", null, self, @@ -95,6 +98,7 @@ pub fn widget(self: *GameOfLife) Widget { null, calculateTimeout, ); + return &self.instance.?; } fn deinit(self: *GameOfLife) void { diff --git a/src/animations/Matrix.zig b/src/animations/Matrix.zig index 9becbd6..e7af489 100644 --- a/src/animations/Matrix.zig +++ b/src/animations/Matrix.zig @@ -29,6 +29,7 @@ pub const Line = struct { update: usize, }; +instance: ?Widget = null, start_time: TimeOfDay, allocator: Allocator, terminal_buffer: *TerminalBuffer, @@ -62,6 +63,7 @@ pub fn init( initBuffers(dots, lines, terminal_buffer.width, terminal_buffer.height, terminal_buffer.random); return .{ + .instance = null, .start_time = try interop.getTimeOfDay(), .allocator = allocator, .terminal_buffer = terminal_buffer, @@ -80,8 +82,9 @@ pub fn init( }; } -pub fn widget(self: *Matrix) Widget { - return Widget.init( +pub fn widget(self: *Matrix) *Widget { + if (self.instance) |*instance| return instance; + self.instance = Widget.init( "Matrix", null, self, @@ -92,6 +95,7 @@ pub fn widget(self: *Matrix) Widget { null, calculateTimeout, ); + return &self.instance.?; } fn deinit(self: *Matrix) void { diff --git a/src/components/InfoLine.zig b/src/components/InfoLine.zig index 7e81cf3..7a1a06b 100644 --- a/src/components/InfoLine.zig +++ b/src/components/InfoLine.zig @@ -18,6 +18,7 @@ const Message = struct { fg: u32, }; +instance: ?Widget = null, label: *MessageLabel, pub fn init( @@ -28,6 +29,7 @@ pub fn init( arrow_bg: u32, ) !InfoLine { return .{ + .instance = null, .label = try MessageLabel.init( allocator, buffer, @@ -46,8 +48,9 @@ pub fn deinit(self: *InfoLine) void { self.label.deinit(); } -pub fn widget(self: *InfoLine) Widget { - return Widget.init( +pub fn widget(self: *InfoLine) *Widget { + if (self.instance) |*instance| return instance; + self.instance = Widget.init( "InfoLine", self.label.keybinds, self, @@ -58,6 +61,7 @@ pub fn widget(self: *InfoLine) Widget { handle, null, ); + return &self.instance.?; } pub fn addMessage(self: *InfoLine, text: []const u8, bg: u32, fg: u32) !void { diff --git a/src/components/Session.zig b/src/components/Session.zig index 373145e..1abf9b9 100644 --- a/src/components/Session.zig +++ b/src/components/Session.zig @@ -18,6 +18,7 @@ const EnvironmentLabel = CyclableLabel(Env, *UserList); const Session = @This(); +instance: ?Widget = null, label: *EnvironmentLabel, user_list: *UserList, @@ -31,6 +32,7 @@ pub fn init( bg: u32, ) !Session { return .{ + .instance = null, .label = try EnvironmentLabel.init( allocator, buffer, @@ -55,8 +57,9 @@ pub fn deinit(self: *Session) void { self.label.deinit(); } -pub fn widget(self: *Session) Widget { - return Widget.init( +pub fn widget(self: *Session) *Widget { + if (self.instance) |*instance| return instance; + self.instance = Widget.init( "Session", self.label.keybinds, self, @@ -67,6 +70,7 @@ pub fn widget(self: *Session) Widget { handle, null, ); + return &self.instance.?; } pub fn addEnvironment(self: *Session, environment: Environment) !void { diff --git a/src/components/UserList.zig b/src/components/UserList.zig index 5747ed7..2131cae 100644 --- a/src/components/UserList.zig +++ b/src/components/UserList.zig @@ -21,6 +21,7 @@ const UserLabel = CyclableLabel(User, *Session); const UserList = @This(); +instance: ?Widget = null, label: *UserLabel, pub fn init( @@ -35,6 +36,7 @@ pub fn init( bg: u32, ) !UserList { var user_list = UserList{ + .instance = null, .label = try UserLabel.init( allocator, buffer, @@ -89,8 +91,9 @@ pub fn deinit(self: *UserList) void { self.label.deinit(); } -pub fn widget(self: *UserList) Widget { - return Widget.init( +pub fn widget(self: *UserList) *Widget { + if (self.instance) |*instance| return instance; + self.instance = Widget.init( "UserList", self.label.keybinds, self, @@ -101,6 +104,7 @@ pub fn widget(self: *UserList) Widget { handle, null, ); + return &self.instance.?; } pub fn getCurrentUsername(self: UserList) []const u8 { diff --git a/src/main.zig b/src/main.zig index cd7bb01..98a7fa1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -107,7 +107,7 @@ const UiState = struct { saved_users: SavedUsers, login: UserList, password: *Text, - password_widget: Widget, + password_widget: *Widget, insert_mode: bool, edge_margin: Position, config: Config, @@ -946,7 +946,7 @@ pub fn main() !void { } // Initialize the animation, if any - var animation: ?Widget = null; + var animation: ?*Widget = null; switch (state.config.animation) { .none => {}, .doom => { @@ -1021,7 +1021,7 @@ pub fn main() !void { animation = dur.widget(); }, } - defer if (animation) |*a| a.deinit(); + defer if (animation) |a| a.deinit(); var cascade = Cascade.init( &state.buffer, @@ -1066,17 +1066,17 @@ pub fn main() !void { const session_widget = state.session.widget(); const login_widget = state.login.widget(); - var widgets: std.ArrayList([]Widget) = .empty; + var widgets: std.ArrayList([]*Widget) = .empty; defer widgets.deinit(state.allocator); // Layer 1 if (animation) |a| { - var layer1 = [_]Widget{a}; + var layer1 = [_]*Widget{a}; try widgets.append(state.allocator, &layer1); } // Layer 2 - var layer2: std.ArrayList(Widget) = .empty; + var layer2: std.ArrayList(*Widget) = .empty; state.custom_binds = .empty; state.custom_info = .empty; defer state.custom_binds.deinit(state.allocator); @@ -1195,7 +1195,7 @@ pub fn main() !void { // Layer 3 if (state.config.auth_fails > 0) { - var layer3 = [_]Widget{cascade.widget()}; + var layer3 = [_]*Widget{cascade.widget()}; try widgets.append(state.allocator, &layer3); }