fix(element/scrollable): display and user interaction
Fix initial render to show scrollbar immediately if required. Show and hide scrollbar correctly when content size or terminal size changes. Add keybindings for scrolling similar to pager keybindings.
This commit is contained in:
@@ -33,8 +33,6 @@ const zterm: *Dependency = b.dependency("zterm", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
// ...
|
||||
exe.root_module.addImport("zterm", zterm.module("zterm"));
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
@@ -267,8 +267,8 @@ pub fn Scrollable(Model: type, Event: type) type {
|
||||
|
||||
this.size = size;
|
||||
if (this.configuration.scrollbar) {
|
||||
if (this.container.properties.size.dim.x > this.size.x or this.container.size.x > this.size.x) this.configuration.y_axis = true;
|
||||
if (this.container.properties.size.dim.y > this.size.y or this.container.size.y > this.size.y) this.configuration.x_axis = true;
|
||||
this.configuration.y_axis = this.container.properties.size.dim.x > this.size.x or this.container.size.x > this.size.x;
|
||||
this.configuration.x_axis = this.container.properties.size.dim.y > this.size.y or this.container.size.y > this.size.y;
|
||||
|
||||
const min_size = this.container.element.minSize(size);
|
||||
this.container.resize(.{
|
||||
@@ -295,7 +295,43 @@ pub fn Scrollable(Model: type, Event: type) type {
|
||||
fn handle(ctx: *anyopaque, model: *Model, event: Event) !void {
|
||||
const this: *@This() = @ptrCast(@alignCast(ctx));
|
||||
switch (event) {
|
||||
// TODO other means to scroll except with the mouse? (i.e. Ctrl-u/d, k/j, etc.?)
|
||||
.init => this.container.resize(this.container.element.minSize(this.size)),
|
||||
// TODO what about multiple scrollable `Element` usages? mouse
|
||||
// can differ between them (due to the event being only send to
|
||||
// the `Container` / `Element` one under the cursor!)
|
||||
.key => |key| {
|
||||
if (key.eql(.{ .cp = 'j' }) or key.eql(.{ .cp = input.Down })) {
|
||||
const max_anchor_y = this.container_size.y -| this.size.y;
|
||||
this.anchor.y = @min(this.anchor.y + 1, max_anchor_y);
|
||||
}
|
||||
if (key.eql(.{ .cp = 'k' }) or key.eql(.{ .cp = input.Up })) {
|
||||
this.anchor.y -|= 1;
|
||||
}
|
||||
if (key.eql(.{ .cp = 'd', .mod = .{ .ctrl = true } })) {
|
||||
// half page down
|
||||
const half_page = this.size.y / 2;
|
||||
const max_anchor_y = this.container_size.y -| this.size.y;
|
||||
this.anchor.y = @min(this.anchor.y + half_page, max_anchor_y);
|
||||
}
|
||||
if (key.eql(.{ .cp = 'u', .mod = .{ .ctrl = true } })) {
|
||||
// half page up
|
||||
const half_page = this.size.y / 2;
|
||||
this.anchor.y -|= half_page;
|
||||
}
|
||||
if (key.eql(.{ .cp = 'f', .mod = .{ .ctrl = true } }) or key.eql(.{ .cp = input.PageDown })) {
|
||||
const max_anchor_y = this.container_size.y -| this.size.y;
|
||||
this.anchor.y = @min(this.anchor.y + this.size.y, max_anchor_y);
|
||||
}
|
||||
if (key.eql(.{ .cp = 'b', .mod = .{ .ctrl = true } }) or key.eql(.{ .cp = input.PageDown })) {
|
||||
this.anchor.y -|= this.size.y;
|
||||
}
|
||||
if (key.eql(.{ .cp = 'g' }) or key.eql(.{ .cp = input.Home })) {
|
||||
this.anchor.y = 0;
|
||||
}
|
||||
if (key.eql(.{ .cp = 'G' }) or key.eql(.{ .cp = input.End })) {
|
||||
this.anchor.y = this.container_size.y -| this.size.y;
|
||||
}
|
||||
},
|
||||
.mouse => |mouse| switch (mouse.button) {
|
||||
Mouse.Button.wheel_up => if (this.container_size.y > this.size.y) {
|
||||
this.anchor.y -|= 1;
|
||||
@@ -1073,8 +1109,8 @@ test "scrollable vertical" {
|
||||
try renderer.render(@TypeOf(container), &container, Model, &.{});
|
||||
try testing.expectEqualCells(.{}, renderer.size, @import("test/element/scrollable.vertical.top.zon"), renderer.screen);
|
||||
|
||||
// scroll down 15 times (exactly to the end)
|
||||
for (0..15) |_| try container.handle(&model, .{
|
||||
// scroll down 15 times (exactly to the end) (with both mouse and key inputs)
|
||||
for (0..7) |_| try container.handle(&model, .{
|
||||
.mouse = .{
|
||||
.button = .wheel_down,
|
||||
.kind = .press,
|
||||
@@ -1082,6 +1118,9 @@ test "scrollable vertical" {
|
||||
.y = 5,
|
||||
},
|
||||
});
|
||||
for (7..15) |_| try container.handle(&model, .{
|
||||
.key = .{ .cp = 'j' },
|
||||
});
|
||||
try renderer.render(@TypeOf(container), &container, Model, &.{});
|
||||
try testing.expectEqualCells(.{}, renderer.size, @import("test/element/scrollable.vertical.bottom.zon"), renderer.screen);
|
||||
|
||||
@@ -1096,6 +1135,40 @@ test "scrollable vertical" {
|
||||
});
|
||||
try renderer.render(@TypeOf(container), &container, Model, &.{});
|
||||
try testing.expectEqualCells(.{}, renderer.size, @import("test/element/scrollable.vertical.bottom.zon"), renderer.screen);
|
||||
|
||||
// scrolling up and down again
|
||||
for (0..5) |_| try container.handle(&model, .{
|
||||
.key = .{ .cp = 'k' },
|
||||
});
|
||||
for (0..5) |_| try container.handle(&model, .{
|
||||
.key = .{ .cp = 'j' },
|
||||
});
|
||||
try renderer.render(@TypeOf(container), &container, Model, &.{});
|
||||
try testing.expectEqualCells(.{}, renderer.size, @import("test/element/scrollable.vertical.bottom.zon"), renderer.screen);
|
||||
|
||||
// scrolling half page up and down again
|
||||
try container.handle(&model, .{
|
||||
.key = .{ .cp = 'u', .mod = .{ .ctrl = true } },
|
||||
});
|
||||
try container.handle(&model, .{
|
||||
.key = .{ .cp = 'd', .mod = .{ .ctrl = true } },
|
||||
});
|
||||
try renderer.render(@TypeOf(container), &container, Model, &.{});
|
||||
try testing.expectEqualCells(.{}, renderer.size, @import("test/element/scrollable.vertical.bottom.zon"), renderer.screen);
|
||||
|
||||
// scrolling page up
|
||||
try container.handle(&model, .{
|
||||
.key = .{ .cp = 'b', .mod = .{ .ctrl = true } },
|
||||
});
|
||||
try renderer.render(@TypeOf(container), &container, Model, &.{});
|
||||
try testing.expectEqualCells(.{}, renderer.size, @import("test/element/scrollable.vertical.top.zon"), renderer.screen);
|
||||
|
||||
// and down again
|
||||
try container.handle(&model, .{
|
||||
.key = .{ .cp = 'f', .mod = .{ .ctrl = true } },
|
||||
});
|
||||
try renderer.render(@TypeOf(container), &container, Model, &.{});
|
||||
try testing.expectEqualCells(.{}, renderer.size, @import("test/element/scrollable.vertical.bottom.zon"), renderer.screen);
|
||||
}
|
||||
|
||||
test "scrollable vertical with scrollbar" {
|
||||
|
||||
Reference in New Issue
Block a user