doc(Elements): update and stateful Element implementations
55
Elements.md
55
Elements.md
@@ -8,7 +8,7 @@ Currently the only library provided `Element` implementation is the `Scrollable`
|
|||||||
|
|
||||||
Contents that is scrollable should be done *virtually* through the contents of the `Container`. This means each container contents implements scrolling for itself if required. Provided with a minimal size option the corresponding container becomes scrollable if the actual containing `Container` is too small to hold the minimal size. Otherwise the `Container` is not scrollable (as it is not necessary). With this the `Container` is dynamically scrollable if necessary (i.e. if the screen size changes, etc.).
|
Contents that is scrollable should be done *virtually* through the contents of the `Container`. This means each container contents implements scrolling for itself if required. Provided with a minimal size option the corresponding container becomes scrollable if the actual containing `Container` is too small to hold the minimal size. Otherwise the `Container` is not scrollable (as it is not necessary). With this the `Container` is dynamically scrollable if necessary (i.e. if the screen size changes, etc.).
|
||||||
|
|
||||||
Known issues:
|
**Known issues:**
|
||||||
|
|
||||||
- The scrolling input action would then also be implemented by the user, but how given that some if the user input is already handled (i.e. scrolling with the mouse).
|
- The scrolling input action would then also be implemented by the user, but how given that some if the user input is already handled (i.e. scrolling with the mouse).
|
||||||
- *To be implemented:* Automatic rendering of a scrollbar (vertical and/or horizontal - according to the container size and the virtual size).
|
- *To be implemented:* Automatic rendering of a scrollbar (vertical and/or horizontal - according to the container size and the virtual size).
|
||||||
@@ -20,39 +20,62 @@ The following snippet shows a template for an `Element` implementation:
|
|||||||
|
|
||||||
```zig
|
```zig
|
||||||
/// This is an empty template implementation for an `Element` type.
|
/// This is an empty template implementation for an `Element` type.
|
||||||
pub fn Template(Event: type) type {
|
pub fn Template(Model: type, Event: type) type {
|
||||||
return packed struct {
|
// *Model* should be `App.Model`
|
||||||
pub fn element(this: *@This()) Element(Event) {
|
// *Event* should be `App.Event`
|
||||||
|
return struct {
|
||||||
|
pub fn element(this: *@This()) App.Element {
|
||||||
return .{
|
return .{
|
||||||
.ptr = this,
|
.ptr = this,
|
||||||
.vtable = &.{
|
.vtable = &.{
|
||||||
|
.resize = resize,
|
||||||
|
.reposition = reposition,
|
||||||
.handle = handle,
|
.handle = handle,
|
||||||
.content = content,
|
.content = content,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(ctx: *anyopaque, event: Event) !void {
|
fn resize(ctx: *anyopaque, size: Point) void {
|
||||||
const this: *@This() = @ptrCast(@alignCast(ctx));
|
const this: *@This() = @ptrCast(@alignCast(ctx));
|
||||||
_ = this;
|
_ = this;
|
||||||
|
_ = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reposition(ctx: *anyopaque, origin: Point) void {
|
||||||
|
const this: *@This() = @ptrCast(@alignCast(ctx));
|
||||||
|
_ = this;
|
||||||
|
_ = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(ctx: *anyopaque, model: *Model, event: Event) !void {
|
||||||
|
const this: *@This() = @ptrCast(@alignCast(ctx));
|
||||||
|
_ = this;
|
||||||
|
_ = model;
|
||||||
switch (event) {
|
switch (event) {
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content(ctx: *anyopaque, cells: []Cell, size: Size) !void {
|
fn content(ctx: *anyopaque, model: *const Model, cells: []Cell, size: Size) !void {
|
||||||
const this: *@This() = @ptrCast(@alignCast(ctx));
|
const this: *@This() = @ptrCast(@alignCast(ctx));
|
||||||
std.debug.assert(cells.len == @as(usize, size.cols) * @as(usize, size.rows));
|
std.debug.assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
||||||
_ = this;
|
_ = this;
|
||||||
|
_ = model;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
For an example implementations of `Element` types, please refer to [examples](Examples.md).
|
> [!TIP]
|
||||||
|
> For example implementations of `Element` types, please refer to [examples](Examples.md).
|
||||||
|
|
||||||
### Tips
|
### Tips
|
||||||
|
|
||||||
|
Only provide function callbacks for the `Element` implementation if the callback is necessary for the functionality, otherwise do not provide a callback function in the *vtable* when creating an `Element` instance of the implementation.
|
||||||
|
|
||||||
|
The `element` initializer function should be the only public function of the `Element` implementation, as the cast of the `*anyopaque` pointer will provide you will an instance of the specific implementation, giving you access to the private functions, members, etc. anyway.
|
||||||
|
|
||||||
The documentation contains tips about how to implement corresponding event loops or how to design own `Element`s. And also on how to test accordingly and use the library itself for examples on how to design the test cases.
|
The documentation contains tips about how to implement corresponding event loops or how to design own `Element`s. And also on how to test accordingly and use the library itself for examples on how to design the test cases.
|
||||||
|
|
||||||
### User specific event handling and content rendering
|
### User specific event handling and content rendering
|
||||||
@@ -61,3 +84,19 @@ For interactions controlled by the user each container can use an `Element` inte
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
### Stateful `Element`s
|
||||||
|
|
||||||
|
You can capture state in two different places:
|
||||||
|
|
||||||
|
1. On a per `Element` instance basis through *member* variables of a given instance.
|
||||||
|
2. In the `Model` that is shared accross the entire application.
|
||||||
|
|
||||||
|
State using the first idea should be keept at a minimum and used if they are specific to only that `Element` implementation and no others in your application.
|
||||||
|
|
||||||
|
State that shall be shared between other `Element`s in your application should propagate their state to the `Model`.
|
||||||
|
|
||||||
|
> [!WARN]
|
||||||
|
> However keep in might that this might cause side-effects (i.e. having the same `Element` implementation instanciated multiple times in the same `Container` tree, might cause the `handle` callback to be called multiple times in a single event loop iteration).
|
||||||
|
|
||||||
|
> [!INFO]
|
||||||
|
> This exact seperatation is also used in android app development using [Jetpack compose](https://developer.android.com/compose). Common patterns, best practices, etc. may also apply for *zterm*.
|
||||||
|
|||||||
Reference in New Issue
Block a user