diff --git a/README.md b/README.md index ca66b7b..611a19b 100644 --- a/README.md +++ b/README.md @@ -73,16 +73,12 @@ the primary use-case for myself to create this library in the first place. ## Roadmap - [ ] Container rendering - - [ ] Layout + - [x] Layout - [x] direction - [x] vertical - [x] horizontal - [x] padding - [x] gap - - [x] alignment (removed - for now at least) - - center - - left - - right - [x] sizing (removed - for now at least) - width - height @@ -96,10 +92,23 @@ the primary use-case for myself to create this library in the first place. - [x] corners - [x] separators - [x] Rectangle - - [ ] Scroll - - [ ] vertical - - [ ] horizontal - - [ ] scroll bar(s) + - [ ] User control + - [x] event handling + - [x] user content + - [ ] Default `Element` implementations + - [ ] Scrollable + - [ ] user input handling + - [ ] vertical + - [ ] horizontal + - [ ] scroll bar(s) rendering + - [ ] vertical + - [ ] horizontal + - [ ] Content alignment (i.e. standard calculations done with the provided `Size`) + - [ ] Text display + - [ ] User input + - [ ] single line + - [ ] multi line + - [ ] min size? (I don't have access to the `.resize` `Event`..) Decorations should respect the layout and the viewport accordingly. This means that scrollbars are always visible (except there is no need to have a scrollbar) @@ -119,6 +128,30 @@ This still has one issue: Layout of child elements that are already too large scrollbar given the right parameters however. The scrolling input action would then also be implemented by the user. +Open questions are regarding the sizing options (i.e. how is the size of a +`Container` actually controlled?, how should it be controlled?, etc.). There +should be support for the child elements to provide some kind of 'list' +functionality built-in. + +**REMINDER**: (mostly for myself) The library should be and remain simple. This +means that some code for using the library may be duplicated, but this is not +the main goal. Others may provide more re-usable code snippets that build on top +of this library instead. + +### User specific event handling and content rendering + +For interactions controlled by the user each container can use an `Element` +interface which contains functions which are called by the `Container` +during event handling (i.e. `fn handle(..)`) and during rendering (i.e. `fn +content(..)`) to provide user specific content and user interaction. The +`Element` may be stateful, but may also be stateless and then be re-used in +multiple different `Container`s. + +Composing multiple `Element`s currently requires the implementation of a wrapper +which contains the `Element`s that need to be handled (should work pretty well +for stateless `Element`s). Such *stateless* `Element`s may be provided by this +library. + ### Input How is the user input handled in the containers? Should there be active diff --git a/examples/container.zig b/examples/container.zig index 6bb5012..09d3474 100644 --- a/examples/container.zig +++ b/examples/container.zig @@ -6,7 +6,7 @@ const Key = zterm.Key; const log = std.log.scoped(.example); -pub const ExampleElement = struct { +pub const ExampleElement = packed struct { pub fn element(this: *@This()) App.Element { return .{ .ptr = this, diff --git a/src/container.zig b/src/container.zig index d9af312..eca763c 100644 --- a/src/container.zig +++ b/src/container.zig @@ -185,17 +185,6 @@ pub const Rectangle = packed struct { } }; -/// Scroll configuration struct -pub const Scroll = packed struct { - /// Enable horizontal scrolling for this element - horizontal: bool = false, - /// Enable vertical scrolling for this element - vertical: bool = false, - - // TODO: rendering enhancements: - // - render corresponding scroll-bars? -}; - /// Layout configuration struct pub const Layout = packed struct { /// control the direction in which child elements are laid out @@ -244,7 +233,6 @@ pub fn Container(comptime Event: type) type { pub const Properties = packed struct { border: Border = .{}, rectangle: Rectangle = .{}, - scroll: Scroll = .{}, layout: Layout = .{}, }; @@ -328,9 +316,7 @@ pub fn Container(comptime Event: type) type { break :blk rows - element_rows * len; }, }; - // TODO: make sure that items cannot underflow in size! - // - make their size and position still according (even if outside of the visible space!) - // - don't render them then accordingly -> avoid index out of bounce accesses! + for (this.elements.items) |*element| { var element_size: Size = undefined; switch (layout.direction) { diff --git a/src/element.zig b/src/element.zig index 0b59394..9a2ce93 100644 --- a/src/element.zig +++ b/src/element.zig @@ -1,4 +1,6 @@ //! Interface for Element's which describe the contents of a `Container`. +const std = @import("std"); + const Cell = @import("cell.zig"); const Size = @import("size.zig").Size; @@ -12,12 +14,57 @@ pub fn Element(Event: type) type { content: ?*const fn (ctx: *anyopaque, cells: []Cell, size: Size) anyerror!void = null, }; + /// Handle the received event. The event is one of the user provided + /// events or a system event, with the exception of the `.resize` + /// `Event` as every `Container` already handles that event. + /// + /// In case of user errors this function should return an error. This + /// error may then be used by the application to display information + /// about the user error. pub inline fn handle(this: @This(), event: Event) !void { - if (this.vtable.handle) |handle_fn| try handle_fn(this.ptr, event); + if (this.vtable.handle) |handle_fn| + try handle_fn(this.ptr, event); } + /// Write content into the `cells` of the `Container`. The associated + /// `cells` slice has the size of (`size.cols * size.rows`). The + /// renderer will know where to place the contents on the screen. + /// + /// This function should only fail with an error if the error is + /// non-recoverable (i.e. an allocation error, system error, etc.). + /// Otherwise user specific errors should be caught using the `handle` + /// function before the rendering of the `Container` happens. pub inline fn content(this: @This(), cells: []Cell, size: Size) !void { - if (this.vtable.content) |content_fn| try content_fn(this.ptr, cells, size); + if (this.vtable.content) |content_fn| + try content_fn(this.ptr, cells, size); + } + }; +} + +/// This is an empty template implementation for an Element type which `zterm` may provide. +/// +/// TODO: Should elements need to be composible with each other, such that they may build complexer outputs? +/// - the goal would rather be to have re-usable parts of handlers and/or content functions which serve similar functionalities. +pub fn Template(Event: type) type { + return packed struct { + pub fn element(this: *@This()) Element(Event) { + return .{ + .ptr = this, + .vtable = &.{ + .handle = handle, + .content = content, + }, + }; + } + + fn handle(ctx: *anyopaque, event: Event) !void { + _ = ctx; + _ = event; + } + + fn content(ctx: *anyopaque, cells: []Cell, size: Size) !void { + _ = ctx; + std.debug.assert(cells.len == @as(usize, size.cols) * @as(usize, size.rows)); } }; }