From c4ae23938d9f64ffd8dd0662314f7b478818547a Mon Sep 17 00:00:00 2001 From: LordMZTE Date: Tue, 30 Jan 2024 16:13:59 +0100 Subject: [PATCH] feat: implement relayouting closes #18 --- src/backevent.zig | 19 ++++++++++++++++++- src/backevents/Relayout.zig | 29 +++++++++++++++++++++++++++++ src/main.zig | 1 + src/platform.zig | 12 ++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/backevents/Relayout.zig diff --git a/src/backevent.zig b/src/backevent.zig index 164ad22..f78a3c0 100644 --- a/src/backevent.zig +++ b/src/backevent.zig @@ -6,9 +6,11 @@ const statspatch = @import("statspatch"); test { _ = ButtonActivated; + _ = Relayout; } pub const ButtonActivated = @import("backevents/ButtonActivated.zig"); +pub const Relayout = @import("backevents/Relayout.zig"); const zenolith = @import("main.zig"); @@ -29,13 +31,28 @@ fn Prototype(comptime Self: type) type { ) orelse {}); } + /// A callback that is automatically invoked before the backevent is propageted up the tree. + /// Here, the backevent may make changes to itself before being passed to the parent widget. + /// It is not invoked if there is no parent widget, that causes `unhandled` to be called immediately. + pub fn prePropagate(self: *Self, next_widget: *Widget) !void { + try (statspatch.implcallOptional( + self, + .ptr, + "prePropagate", + anyerror!void, + .{ self, next_widget }, + ) orelse {}); + } + /// Propagates this backevent up the widget tree. If the current (given) Widget has a parent, /// the event is propagated to it. Otherwise, the backevent's unhandled handler is called. /// Widgets should call this in their backevent handler if they do not wish to modify or /// intercept the backevent. pub fn dispatch(self: Self, widget: *Widget) anyerror!void { if (widget.data.parent) |p| { - try p.backevent(self); + var selfv = self; + try selfv.prePropagate(p); + try p.backevent(selfv); } else { try self.unhandled(widget); } diff --git a/src/backevents/Relayout.zig b/src/backevents/Relayout.zig new file mode 100644 index 0000000..80deeb3 --- /dev/null +++ b/src/backevents/Relayout.zig @@ -0,0 +1,29 @@ +//! This backevent is fired on a widget to signal that this widget needs another layout pass done on it. +//! A widget should handle this if it can do another layout pass on its child while guaranteeing +//! that its own size won't change in the process. +//! If no widget is able to handle this backevent, the platform will be asked to do another layout pass. +const std = @import("std"); + +const Backevent = @import("../backevent.zig").Backevent; +const Widget = @import("../widget.zig").Widget; + +/// Not necessarily the widget this backevent originated from, but an immediate child of the +/// widget it is currently being dispatched on. This is the child that should be laid out again. +child: *Widget, + +const Relayout = @This(); + +pub fn prePropagate(self: *Relayout, selfb: *Backevent, next_widget: *Widget) !void { + _ = selfb; + self.child = next_widget; +} + +pub fn unhandled(self: Relayout, selfb: Backevent, root: *Widget) !void { + _ = selfb; + + std.debug.assert(self.child == root); + + if (root.data.platform) |plat| { + try plat.relayoutRoot(root); + } +} diff --git a/src/main.zig b/src/main.zig index 3b19ce2..0062479 100644 --- a/src/main.zig +++ b/src/main.zig @@ -58,6 +58,7 @@ pub const default_platform_impls = [_]type{}; /// The default backevents in Zenolith. Remember that these may be required by widgets. pub const default_backevents = [_]type{ backevent.ButtonActivated, + backevent.Relayout, }; const root_options = if (@hasDecl(root, "zenolith_options")) root.zenolith_options else struct {}; diff --git a/src/platform.zig b/src/platform.zig index 50e8df9..1c10cd7 100644 --- a/src/platform.zig +++ b/src/platform.zig @@ -29,6 +29,18 @@ fn Prototype(comptime Self: type) type { } } } + + /// Do a layout pass on a given widget. The passed widget is asserted to be a root widget of + /// this platform. + pub fn relayoutRoot(self: *Self, root: *Widget) !void { + try statspatch.implcall( + self, + .ptr, + "relayoutRoot", + anyerror!void, + .{ root }, + ); + } }; }