ref(event): remove .resize and replace with recursize method calls
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 40s
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 40s
This also means that currently the dynamic resizing through the app's detached thread is not working, as it cannot send size updates. The examples have been overhauled to still implement intermediate mode applications accordingly.
This commit is contained in:
@@ -108,10 +108,8 @@ pub fn main() !void {
|
||||
const event = app.nextEvent();
|
||||
log.debug("received event: {s}", .{@tagName(event)});
|
||||
|
||||
// pre event handling
|
||||
switch (event) {
|
||||
.init => continue,
|
||||
.quit => break,
|
||||
.size => |size| try renderer.resize(size),
|
||||
.key => |key| {
|
||||
if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit();
|
||||
|
||||
@@ -140,6 +138,16 @@ pub fn main() !void {
|
||||
.msg = "Container Event handling failed",
|
||||
},
|
||||
});
|
||||
|
||||
// post event handling
|
||||
switch (event) {
|
||||
.quit => break,
|
||||
else => {},
|
||||
}
|
||||
|
||||
try renderer.resize();
|
||||
container.reposition(.{});
|
||||
container.resize(renderer.size);
|
||||
try renderer.render(@TypeOf(container), &container);
|
||||
try renderer.flush();
|
||||
}
|
||||
|
||||
@@ -118,10 +118,8 @@ pub fn main() !void {
|
||||
const event = app.nextEvent();
|
||||
log.debug("received event: {s}", .{@tagName(event)});
|
||||
|
||||
// pre event handling
|
||||
switch (event) {
|
||||
.init => continue,
|
||||
.quit => break,
|
||||
.size => |size| try renderer.resize(size),
|
||||
.key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(),
|
||||
.click => |button| {
|
||||
log.info("Clicked with mouse using Button: {s}", .{button});
|
||||
@@ -137,6 +135,15 @@ pub fn main() !void {
|
||||
},
|
||||
});
|
||||
|
||||
// post event handling
|
||||
switch (event) {
|
||||
.quit => break,
|
||||
else => {},
|
||||
}
|
||||
|
||||
try renderer.resize();
|
||||
container.reposition(.{});
|
||||
container.resize(renderer.size);
|
||||
try renderer.render(@TypeOf(container), &container);
|
||||
try renderer.flush();
|
||||
}
|
||||
|
||||
@@ -131,10 +131,8 @@ pub fn main() !void {
|
||||
const event = app.nextEvent();
|
||||
log.debug("received event: {s}", .{@tagName(event)});
|
||||
|
||||
// pre event handling
|
||||
switch (event) {
|
||||
.init => continue,
|
||||
.quit => break,
|
||||
.size => |size| try renderer.resize(size),
|
||||
.key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(),
|
||||
.accept => |input| {
|
||||
defer allocator.free(input);
|
||||
@@ -151,6 +149,15 @@ pub fn main() !void {
|
||||
},
|
||||
});
|
||||
|
||||
// post event handling
|
||||
switch (event) {
|
||||
.quit => break,
|
||||
else => {},
|
||||
}
|
||||
|
||||
try renderer.resize();
|
||||
container.reposition(.{});
|
||||
container.resize(renderer.size);
|
||||
try renderer.render(@TypeOf(container), &container);
|
||||
try renderer.flush();
|
||||
}
|
||||
|
||||
@@ -157,10 +157,8 @@ pub fn main() !void {
|
||||
const event = app.nextEvent();
|
||||
log.debug("received event: {s}", .{@tagName(event)});
|
||||
|
||||
// pre event handling
|
||||
switch (event) {
|
||||
.init => continue,
|
||||
.quit => break,
|
||||
.size => |size| try renderer.resize(size),
|
||||
.key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(),
|
||||
.err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }),
|
||||
else => {},
|
||||
@@ -172,6 +170,16 @@ pub fn main() !void {
|
||||
.msg = "Container Event handling failed",
|
||||
},
|
||||
});
|
||||
|
||||
// post event handling
|
||||
switch (event) {
|
||||
.quit => break,
|
||||
else => {},
|
||||
}
|
||||
|
||||
try renderer.resize();
|
||||
container.reposition(.{});
|
||||
container.resize(renderer.size);
|
||||
try renderer.render(@TypeOf(container), &container);
|
||||
try renderer.flush();
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@ const ErrorNotification = struct {
|
||||
const this: *@This() = @ptrCast(@alignCast(ctx));
|
||||
switch (event) {
|
||||
.key => |key| if (!key.isAscii()) return error.UnsupportedKey,
|
||||
.size => |_| {},
|
||||
.err => |err| this.msg = err.msg,
|
||||
else => {},
|
||||
}
|
||||
@@ -134,10 +133,8 @@ pub fn main() !void {
|
||||
const event = app.nextEvent();
|
||||
log.debug("received event: {s}", .{@tagName(event)});
|
||||
|
||||
// pre event handling
|
||||
switch (event) {
|
||||
.init => continue,
|
||||
.quit => break,
|
||||
.size => |size| try renderer.resize(size),
|
||||
.key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(),
|
||||
.err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }),
|
||||
else => {},
|
||||
@@ -149,6 +146,16 @@ pub fn main() !void {
|
||||
.msg = "Container Event handling failed",
|
||||
},
|
||||
});
|
||||
|
||||
// post event handling
|
||||
switch (event) {
|
||||
.quit => break,
|
||||
else => {},
|
||||
}
|
||||
|
||||
try renderer.resize();
|
||||
container.reposition(.{});
|
||||
container.resize(renderer.size);
|
||||
try renderer.render(@TypeOf(container), &container);
|
||||
try renderer.flush();
|
||||
}
|
||||
|
||||
@@ -85,10 +85,8 @@ pub fn main() !void {
|
||||
const event = app.nextEvent();
|
||||
log.debug("received event: {s}", .{@tagName(event)});
|
||||
|
||||
// pre event handling
|
||||
switch (event) {
|
||||
.init => continue,
|
||||
.quit => break,
|
||||
.size => |size| try renderer.resize(size),
|
||||
.key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(),
|
||||
// NOTE errors could be displayed in another container in case one was received, etc. to provide the user with feedback
|
||||
.err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }),
|
||||
@@ -102,6 +100,16 @@ pub fn main() !void {
|
||||
.msg = "Container Event handling failed",
|
||||
},
|
||||
});
|
||||
|
||||
// post event handling
|
||||
switch (event) {
|
||||
.quit => break,
|
||||
else => {},
|
||||
}
|
||||
|
||||
try renderer.resize();
|
||||
container.reposition(.{});
|
||||
container.resize(renderer.size);
|
||||
try renderer.render(@TypeOf(container), &container);
|
||||
try renderer.flush();
|
||||
}
|
||||
|
||||
@@ -77,10 +77,8 @@ pub fn main() !void {
|
||||
const event = app.nextEvent();
|
||||
log.debug("received event: {s}", .{@tagName(event)});
|
||||
|
||||
// pre event handling
|
||||
switch (event) {
|
||||
.init => continue,
|
||||
.quit => break,
|
||||
.size => |size| try renderer.resize(size),
|
||||
.key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(),
|
||||
// NOTE errors could be displayed in another container in case one was received, etc. to provide the user with feedback
|
||||
.err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }),
|
||||
@@ -94,6 +92,16 @@ pub fn main() !void {
|
||||
.msg = "Container Event handling failed",
|
||||
},
|
||||
});
|
||||
|
||||
// post event handling
|
||||
switch (event) {
|
||||
.quit => break,
|
||||
else => {},
|
||||
}
|
||||
|
||||
try renderer.resize();
|
||||
container.reposition(.{});
|
||||
container.resize(renderer.size);
|
||||
try renderer.render(@TypeOf(container), &container);
|
||||
try renderer.flush();
|
||||
}
|
||||
|
||||
@@ -89,10 +89,8 @@ pub fn main() !void {
|
||||
const event = app.nextEvent();
|
||||
log.debug("received event: {s}", .{@tagName(event)});
|
||||
|
||||
// pre event handling
|
||||
switch (event) {
|
||||
.init => continue,
|
||||
.quit => break,
|
||||
.size => |size| try renderer.resize(size),
|
||||
.key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(),
|
||||
// NOTE errors could be displayed in another container in case one was received, etc. to provide the user with feedback
|
||||
.err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }),
|
||||
@@ -106,6 +104,16 @@ pub fn main() !void {
|
||||
.msg = "Container Event handling failed",
|
||||
},
|
||||
});
|
||||
|
||||
// post event handling
|
||||
switch (event) {
|
||||
.quit => break,
|
||||
else => {},
|
||||
}
|
||||
|
||||
try renderer.resize();
|
||||
container.reposition(.{});
|
||||
container.resize(renderer.size);
|
||||
try renderer.render(@TypeOf(container), &container);
|
||||
try renderer.flush();
|
||||
}
|
||||
|
||||
@@ -76,10 +76,8 @@ pub fn main() !void {
|
||||
const event = app.nextEvent();
|
||||
log.debug("received event: {s}", .{@tagName(event)});
|
||||
|
||||
// pre event handling
|
||||
switch (event) {
|
||||
.init => continue,
|
||||
.quit => break,
|
||||
.size => |size| try renderer.resize(size),
|
||||
.key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(),
|
||||
// NOTE errors could be displayed in another container in case one was received, etc. to provide the user with feedback
|
||||
.err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }),
|
||||
@@ -93,6 +91,16 @@ pub fn main() !void {
|
||||
.msg = "Container Event handling failed",
|
||||
},
|
||||
});
|
||||
|
||||
// post event handling
|
||||
switch (event) {
|
||||
.quit => break,
|
||||
else => {},
|
||||
}
|
||||
|
||||
try renderer.resize();
|
||||
container.reposition(.{});
|
||||
container.resize(renderer.size);
|
||||
try renderer.render(@TypeOf(container), &container);
|
||||
try renderer.flush();
|
||||
}
|
||||
|
||||
@@ -74,10 +74,8 @@ pub fn main() !void {
|
||||
const event = app.nextEvent();
|
||||
log.debug("received event: {s}", .{@tagName(event)});
|
||||
|
||||
// pre event handling
|
||||
switch (event) {
|
||||
.init => continue,
|
||||
.quit => break,
|
||||
.size => |size| try renderer.resize(size),
|
||||
.key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(),
|
||||
.err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }),
|
||||
else => {},
|
||||
@@ -89,6 +87,16 @@ pub fn main() !void {
|
||||
.msg = "Container Event handling failed",
|
||||
},
|
||||
});
|
||||
|
||||
// post event handling
|
||||
switch (event) {
|
||||
.quit => break,
|
||||
else => {},
|
||||
}
|
||||
|
||||
try renderer.resize();
|
||||
container.reposition(.{});
|
||||
container.resize(renderer.size);
|
||||
try renderer.render(@TypeOf(container), &container);
|
||||
try renderer.flush();
|
||||
}
|
||||
|
||||
@@ -127,10 +127,8 @@ pub fn main() !void {
|
||||
const event = app.nextEvent();
|
||||
log.debug("received event: {s}", .{@tagName(event)});
|
||||
|
||||
// pre event handling
|
||||
switch (event) {
|
||||
.init => continue,
|
||||
.quit => break,
|
||||
.size => |size| try renderer.resize(size),
|
||||
.key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(),
|
||||
.err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }),
|
||||
else => {},
|
||||
@@ -142,6 +140,16 @@ pub fn main() !void {
|
||||
.msg = "Container Event handling failed",
|
||||
},
|
||||
});
|
||||
|
||||
// post event handling
|
||||
switch (event) {
|
||||
.quit => break,
|
||||
else => {},
|
||||
}
|
||||
|
||||
try renderer.resize();
|
||||
container.reposition(.{});
|
||||
container.resize(renderer.size);
|
||||
try renderer.render(@TypeOf(container), &container);
|
||||
try renderer.flush();
|
||||
}
|
||||
|
||||
16
src/app.zig
16
src/app.zig
@@ -97,9 +97,6 @@ pub fn App(comptime E: type) type {
|
||||
try terminal.enterAltScreen();
|
||||
try terminal.hideCursor();
|
||||
try terminal.enableMouseSupport();
|
||||
|
||||
// send initial size afterwards
|
||||
this.postEvent(.{ .size = terminal.getTerminalSize() });
|
||||
}
|
||||
|
||||
pub fn interrupt(this: *@This()) !void {
|
||||
@@ -144,7 +141,8 @@ pub fn App(comptime E: type) type {
|
||||
|
||||
fn winsizeCallback(ptr: *anyopaque) void {
|
||||
const this: *@This() = @ptrCast(@alignCast(ptr));
|
||||
this.postEvent(.{ .size = terminal.getTerminalSize() });
|
||||
_ = this;
|
||||
// this.postEvent(.{ .size = terminal.getTerminalSize() });
|
||||
}
|
||||
|
||||
var winch_handler: ?SignalHandler = null;
|
||||
@@ -329,10 +327,12 @@ pub fn App(comptime E: type) type {
|
||||
const width_char = iter.next() orelse break;
|
||||
const height_char = iter.next() orelse break;
|
||||
|
||||
this.postEvent(.{ .size = .{
|
||||
.x = std.fmt.parseUnsigned(u16, width_char, 10) catch break,
|
||||
.y = std.fmt.parseUnsigned(u16, height_char, 10) catch break,
|
||||
} });
|
||||
_ = width_char;
|
||||
_ = height_char;
|
||||
// this.postEvent(.{ .size = .{
|
||||
// .x = std.fmt.parseUnsigned(u16, width_char, 10) catch break,
|
||||
// .y = std.fmt.parseUnsigned(u16, height_char, 10) catch break,
|
||||
// } });
|
||||
}
|
||||
},
|
||||
'u' => {
|
||||
|
||||
@@ -601,173 +601,174 @@ pub fn Container(comptime Event: type) type {
|
||||
try this.elements.append(element);
|
||||
}
|
||||
|
||||
pub fn position(this: *@This(), pos: Point) !void {
|
||||
log.debug("pos: .{{ .x = {d}, .y = {d} }}", .{ pos.x, pos.y });
|
||||
this.origin = pos;
|
||||
pub fn reposition(this: *@This(), origin: Point) void {
|
||||
log.debug("origin: .{{ .x = {d}, .y = {d} }}", .{ origin.x, origin.y });
|
||||
this.origin = origin;
|
||||
this.element.reposition(origin);
|
||||
}
|
||||
|
||||
pub fn resize(this: *@This(), size: Point) void {
|
||||
log.debug("Event .size: {{ .x = {d}, .y = {d} }}", .{ size.x, size.y });
|
||||
this.size = size;
|
||||
if (this.properties.fixed_size.x > 0 and size.x < this.properties.fixed_size.x) return;
|
||||
if (this.properties.fixed_size.y > 0 and size.y < this.properties.fixed_size.y) return;
|
||||
|
||||
this.element.resize(size);
|
||||
|
||||
if (this.elements.items.len == 0) return;
|
||||
|
||||
const layout = this.properties.layout;
|
||||
var fixed_size_elements: u16 = 0;
|
||||
var fixed_size: Point = .{};
|
||||
for (this.elements.items) |element| {
|
||||
switch (layout.direction) {
|
||||
.horizontal => if (element.properties.fixed_size.x > 0) {
|
||||
fixed_size_elements += 1;
|
||||
},
|
||||
.vertical => if (element.properties.fixed_size.y > 0) {
|
||||
fixed_size_elements += 1;
|
||||
},
|
||||
}
|
||||
fixed_size = fixed_size.add(element.properties.fixed_size);
|
||||
}
|
||||
// check if the available screen is large enough
|
||||
switch (layout.direction) {
|
||||
.horizontal => if (fixed_size.x > size.x) return,
|
||||
.vertical => if (fixed_size.y > size.y) return,
|
||||
}
|
||||
const sides = this.properties.border.sides;
|
||||
const padding = layout.padding;
|
||||
var gap = layout.gap;
|
||||
if (layout.separator.enabled) gap += 1;
|
||||
|
||||
const len: u16 = @truncate(this.elements.items.len);
|
||||
const element_x = blk: {
|
||||
var x = size.x - fixed_size.x - gap * (len - 1);
|
||||
if (sides.left) x -= 1;
|
||||
if (sides.right) x -= 1;
|
||||
x -= padding.left + padding.right;
|
||||
if (fixed_size_elements == len) break :blk 0;
|
||||
if (fixed_size_elements == 0) {
|
||||
break :blk @divTrunc(x, len);
|
||||
} else {
|
||||
break :blk @divTrunc(x, len - fixed_size_elements);
|
||||
}
|
||||
};
|
||||
const element_y = blk: {
|
||||
var y = size.y - fixed_size.y - gap * (len - 1);
|
||||
if (sides.top) y -= 1;
|
||||
if (sides.bottom) y -= 1;
|
||||
y -= padding.top + padding.bottom;
|
||||
if (fixed_size_elements == len) break :blk 0;
|
||||
if (fixed_size_elements == 0) {
|
||||
break :blk @divTrunc(y, len);
|
||||
} else {
|
||||
break :blk @divTrunc(y, len - fixed_size_elements);
|
||||
}
|
||||
};
|
||||
var offset: u16 = switch (layout.direction) {
|
||||
.horizontal => padding.left,
|
||||
.vertical => padding.top,
|
||||
};
|
||||
var overflow = switch (layout.direction) {
|
||||
.horizontal => blk: {
|
||||
var x = size.x - fixed_size.x - gap * (len - 1);
|
||||
if (sides.left) x -= 1;
|
||||
if (sides.right) x -= 1;
|
||||
x -= padding.left + padding.right;
|
||||
if (fixed_size_elements == len) break :blk 0;
|
||||
if (fixed_size_elements == 0) {
|
||||
break :blk x - element_x * len;
|
||||
} else {
|
||||
break :blk x - element_x * (len - fixed_size_elements);
|
||||
}
|
||||
},
|
||||
.vertical => blk: {
|
||||
var y = size.y - fixed_size.y - gap * (len - 1);
|
||||
if (sides.top) y -= 1;
|
||||
if (sides.bottom) y -= 1;
|
||||
y -= padding.top + padding.bottom;
|
||||
if (fixed_size_elements == len) break :blk 0;
|
||||
if (fixed_size_elements == 0) {
|
||||
break :blk y - element_y * len;
|
||||
} else {
|
||||
break :blk y - element_y * (len - fixed_size_elements);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
for (this.elements.items) |*element| {
|
||||
var element_size: Point = undefined;
|
||||
var element_origin: Point = undefined;
|
||||
switch (layout.direction) {
|
||||
.horizontal => {
|
||||
// TODO this should not always be the max size property!
|
||||
var x = blk: {
|
||||
if (element.properties.fixed_size.x > 0) break :blk element.properties.fixed_size.x;
|
||||
break :blk element_x;
|
||||
};
|
||||
if (overflow > 0) {
|
||||
overflow -|= 1;
|
||||
x += 1;
|
||||
}
|
||||
element_origin = .{
|
||||
.x = this.origin.x + offset,
|
||||
.y = this.origin.y,
|
||||
};
|
||||
element_size = .{
|
||||
.x = x,
|
||||
.y = size.y,
|
||||
};
|
||||
// border
|
||||
if (sides.top) element_size.y -= 1;
|
||||
if (sides.bottom) element_size.y -= 1;
|
||||
// padding
|
||||
element_origin.y += padding.top;
|
||||
element_size.y -= padding.top + padding.bottom;
|
||||
// gap
|
||||
offset += gap;
|
||||
offset += x;
|
||||
},
|
||||
.vertical => {
|
||||
var y = blk: {
|
||||
if (element.properties.fixed_size.y > 0) break :blk element.properties.fixed_size.y;
|
||||
break :blk element_y;
|
||||
};
|
||||
if (overflow > 0) {
|
||||
overflow -|= 1;
|
||||
y += 1;
|
||||
}
|
||||
element_origin = .{
|
||||
.x = this.origin.x,
|
||||
.y = this.origin.y + offset,
|
||||
};
|
||||
element_size = .{
|
||||
.x = size.x,
|
||||
.y = y,
|
||||
};
|
||||
// border
|
||||
if (sides.left) element_size.x -= 1;
|
||||
if (sides.right) element_size.x -= 1;
|
||||
// padding
|
||||
element_origin.x += padding.left;
|
||||
element_size.x -= padding.left + padding.right;
|
||||
// gap
|
||||
offset += gap;
|
||||
offset += y;
|
||||
},
|
||||
}
|
||||
|
||||
// border resizing
|
||||
if (sides.top) element_origin.y += 1;
|
||||
if (sides.left) element_origin.x += 1;
|
||||
|
||||
element.reposition(element_origin);
|
||||
element.resize(element_size);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(this: *@This(), event: Event) !void {
|
||||
switch (event) {
|
||||
.size => |size| resize: {
|
||||
log.debug("Event .size: {{ .x = {d}, .y = {d} }}", .{ size.x, size.y });
|
||||
this.size = size;
|
||||
if (this.properties.fixed_size.x > 0 and size.x < this.properties.fixed_size.x) return Error.TooSmall;
|
||||
if (this.properties.fixed_size.y > 0 and size.y < this.properties.fixed_size.y) return Error.TooSmall;
|
||||
|
||||
try this.element.handle(event);
|
||||
|
||||
if (this.elements.items.len == 0) break :resize;
|
||||
|
||||
const layout = this.properties.layout;
|
||||
var fixed_size_elements: u16 = 0;
|
||||
var fixed_size: Point = .{};
|
||||
for (this.elements.items) |element| {
|
||||
switch (layout.direction) {
|
||||
.horizontal => if (element.properties.fixed_size.x > 0) {
|
||||
fixed_size_elements += 1;
|
||||
},
|
||||
.vertical => if (element.properties.fixed_size.y > 0) {
|
||||
fixed_size_elements += 1;
|
||||
},
|
||||
}
|
||||
fixed_size = fixed_size.add(element.properties.fixed_size);
|
||||
}
|
||||
// check if the available screen is large enough
|
||||
switch (layout.direction) {
|
||||
.horizontal => if (fixed_size.x > size.x) return Error.TooSmall,
|
||||
.vertical => if (fixed_size.y > size.y) return Error.TooSmall,
|
||||
}
|
||||
const sides = this.properties.border.sides;
|
||||
const padding = layout.padding;
|
||||
var gap = layout.gap;
|
||||
if (layout.separator.enabled) gap += 1;
|
||||
|
||||
const len: u16 = @truncate(this.elements.items.len);
|
||||
const element_x = blk: {
|
||||
var x = size.x - fixed_size.x - gap * (len - 1);
|
||||
if (sides.left) x -= 1;
|
||||
if (sides.right) x -= 1;
|
||||
x -= padding.left + padding.right;
|
||||
if (fixed_size_elements == len) break :blk 0;
|
||||
if (fixed_size_elements == 0) {
|
||||
break :blk @divTrunc(x, len);
|
||||
} else {
|
||||
break :blk @divTrunc(x, len - fixed_size_elements);
|
||||
}
|
||||
};
|
||||
const element_y = blk: {
|
||||
var y = size.y - fixed_size.y - gap * (len - 1);
|
||||
if (sides.top) y -= 1;
|
||||
if (sides.bottom) y -= 1;
|
||||
y -= padding.top + padding.bottom;
|
||||
if (fixed_size_elements == len) break :blk 0;
|
||||
if (fixed_size_elements == 0) {
|
||||
break :blk @divTrunc(y, len);
|
||||
} else {
|
||||
break :blk @divTrunc(y, len - fixed_size_elements);
|
||||
}
|
||||
};
|
||||
var offset: u16 = switch (layout.direction) {
|
||||
.horizontal => padding.left,
|
||||
.vertical => padding.top,
|
||||
};
|
||||
var overflow = switch (layout.direction) {
|
||||
.horizontal => blk: {
|
||||
var x = size.x - fixed_size.x - gap * (len - 1);
|
||||
if (sides.left) x -= 1;
|
||||
if (sides.right) x -= 1;
|
||||
x -= padding.left + padding.right;
|
||||
if (fixed_size_elements == len) break :blk 0;
|
||||
if (fixed_size_elements == 0) {
|
||||
break :blk x - element_x * len;
|
||||
} else {
|
||||
break :blk x - element_x * (len - fixed_size_elements);
|
||||
}
|
||||
},
|
||||
.vertical => blk: {
|
||||
var y = size.y - fixed_size.y - gap * (len - 1);
|
||||
if (sides.top) y -= 1;
|
||||
if (sides.bottom) y -= 1;
|
||||
y -= padding.top + padding.bottom;
|
||||
if (fixed_size_elements == len) break :blk 0;
|
||||
if (fixed_size_elements == 0) {
|
||||
break :blk y - element_y * len;
|
||||
} else {
|
||||
break :blk y - element_y * (len - fixed_size_elements);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
for (this.elements.items) |*element| {
|
||||
var element_size: Point = undefined;
|
||||
var element_origin: Point = undefined;
|
||||
switch (layout.direction) {
|
||||
.horizontal => {
|
||||
// TODO this should not always be the max size property!
|
||||
var x = blk: {
|
||||
if (element.properties.fixed_size.x > 0) break :blk element.properties.fixed_size.x;
|
||||
break :blk element_x;
|
||||
};
|
||||
if (overflow > 0) {
|
||||
overflow -|= 1;
|
||||
x += 1;
|
||||
}
|
||||
element_origin = .{
|
||||
.x = this.origin.x + offset,
|
||||
.y = this.origin.y,
|
||||
};
|
||||
element_size = .{
|
||||
.x = x,
|
||||
.y = size.y,
|
||||
};
|
||||
// border
|
||||
if (sides.top) element_size.y -= 1;
|
||||
if (sides.bottom) element_size.y -= 1;
|
||||
// padding
|
||||
element_origin.y += padding.top;
|
||||
element_size.y -= padding.top + padding.bottom;
|
||||
// gap
|
||||
offset += gap;
|
||||
offset += x;
|
||||
},
|
||||
.vertical => {
|
||||
var y = blk: {
|
||||
if (element.properties.fixed_size.y > 0) break :blk element.properties.fixed_size.y;
|
||||
break :blk element_y;
|
||||
};
|
||||
if (overflow > 0) {
|
||||
overflow -|= 1;
|
||||
y += 1;
|
||||
}
|
||||
element_origin = .{
|
||||
.x = this.origin.x,
|
||||
.y = this.origin.y + offset,
|
||||
};
|
||||
element_size = .{
|
||||
.x = size.x,
|
||||
.y = y,
|
||||
};
|
||||
// border
|
||||
if (sides.left) element_size.x -= 1;
|
||||
if (sides.right) element_size.x -= 1;
|
||||
// padding
|
||||
element_origin.x += padding.left;
|
||||
element_size.x -= padding.left + padding.right;
|
||||
// gap
|
||||
offset += gap;
|
||||
offset += y;
|
||||
},
|
||||
}
|
||||
|
||||
// border resizing
|
||||
if (sides.top) element_origin.y += 1;
|
||||
if (sides.left) element_origin.x += 1;
|
||||
|
||||
// TODO tell the element its origin
|
||||
try element.position(element_origin);
|
||||
try element.handle(.{ .size = element_size });
|
||||
}
|
||||
},
|
||||
.mouse => |mouse| if (mouse.in(this.origin, this.size)) {
|
||||
try this.element.handle(event);
|
||||
for (this.elements.items) |*element| try element.handle(event);
|
||||
|
||||
@@ -13,10 +13,24 @@ pub fn Element(Event: type) type {
|
||||
vtable: *const VTable = &.{},
|
||||
|
||||
pub const VTable = struct {
|
||||
resize: ?*const fn (ctx: *anyopaque, size: Point) void = null,
|
||||
reposition: ?*const fn (ctx: *anyopaque, origin: Point) void = null,
|
||||
handle: ?*const fn (ctx: *anyopaque, event: Event) anyerror!void = null,
|
||||
content: ?*const fn (ctx: *anyopaque, cells: []Cell, origin: Point, size: Point) anyerror!void = null,
|
||||
};
|
||||
|
||||
/// Resize the corresponding `Element` with the given *size*.
|
||||
pub fn resize(this: @This(), size: Point) void {
|
||||
if (this.vtable.resize) |resize_fn|
|
||||
resize_fn(this.ptr, size);
|
||||
}
|
||||
|
||||
/// Reposition the corresponding `Element` with the given *origin*.
|
||||
pub fn reposition(this: @This(), origin: Point) void {
|
||||
if (this.vtable.reposition) |reposition_fn|
|
||||
reposition_fn(this.ptr, origin);
|
||||
}
|
||||
|
||||
/// Handle the received event. The event is one of the user provided
|
||||
/// events or a system event, with the exception of the `.size`
|
||||
/// `Event` as every `Container` already handles that event.
|
||||
@@ -73,6 +87,8 @@ pub fn Scrollable(Event: type) type {
|
||||
return .{
|
||||
.ptr = this,
|
||||
.vtable = &.{
|
||||
.resize = resize,
|
||||
.reposition = reposition,
|
||||
.handle = handle,
|
||||
.content = content,
|
||||
},
|
||||
@@ -86,16 +102,23 @@ pub fn Scrollable(Event: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
fn resize(ctx: *anyopaque, size: Point) void {
|
||||
const this: *@This() = @ptrCast(@alignCast(ctx));
|
||||
this.size = size;
|
||||
// TODO scrollbar space - depending on configuration and only if necessary?
|
||||
this.container_size = Point.max(size, this.min_size);
|
||||
this.container_origin = size; // TODO the size should be a provided origin
|
||||
this.container.resize(this.container_size);
|
||||
}
|
||||
|
||||
fn reposition(ctx: *anyopaque, origin: Point) void {
|
||||
const this: *@This() = @ptrCast(@alignCast(ctx));
|
||||
this.container_origin = origin; // TODO the size should be a provided origin
|
||||
}
|
||||
|
||||
fn handle(ctx: *anyopaque, event: Event) !void {
|
||||
const this: *@This() = @ptrCast(@alignCast(ctx));
|
||||
switch (event) {
|
||||
.size => |size| {
|
||||
this.size = size;
|
||||
// TODO scrollbar space - depending on configuration and only if necessary?
|
||||
this.container_size = size.max(this.min_size);
|
||||
this.container_origin = size; // TODO the size should be a provided origin
|
||||
try this.container.handle(.{ .size = this.container_size });
|
||||
},
|
||||
// TODO other means to scroll except with the mouse? (i.e. Ctrl-u/d, k/j, etc.?)
|
||||
.mouse => |mouse| switch (mouse.button) {
|
||||
Mouse.Button.wheel_up => if (this.container_size.y > this.size.y) {
|
||||
@@ -224,7 +247,7 @@ test "scrollable vertical" {
|
||||
var renderer: testing.Renderer = .init(allocator, size);
|
||||
defer renderer.deinit();
|
||||
|
||||
try container.handle(.{ .size = size });
|
||||
container.resize(size);
|
||||
try renderer.render(Container(event.SystemEvent), &container);
|
||||
try testing.expectEqualCells(.{}, renderer.size, @import("test/element/scrollable.vertical.top.zon"), renderer.screen);
|
||||
|
||||
@@ -298,7 +321,7 @@ test "scrollable horizontal" {
|
||||
var renderer: testing.Renderer = .init(allocator, size);
|
||||
defer renderer.deinit();
|
||||
|
||||
try container.handle(.{ .size = size });
|
||||
container.resize(size);
|
||||
try renderer.render(Container(event.SystemEvent), &container);
|
||||
try testing.expectEqualCells(.{}, renderer.size, @import("test/element/scrollable.horizontal.left.zon"), renderer.screen);
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@ pub const SystemEvent = union(enum) {
|
||||
/// associated error message
|
||||
msg: []const u8,
|
||||
},
|
||||
/// Size event emitted by the terminal to derive the `Size` of the current terminal the application is rendered in
|
||||
size: Point,
|
||||
/// Input key event received from the user
|
||||
key: Key,
|
||||
/// Mouse input event
|
||||
|
||||
@@ -2,17 +2,17 @@ pub const Point = packed struct {
|
||||
x: u16 = 0,
|
||||
y: u16 = 0,
|
||||
|
||||
pub fn add(this: @This(), other: @This()) @This() {
|
||||
pub fn add(a: @This(), b: @This()) @This() {
|
||||
return .{
|
||||
.x = this.x + other.x,
|
||||
.y = this.y + other.y,
|
||||
.x = a.x + b.x,
|
||||
.y = a.y + b.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn max(this: @This(), other: @This()) @This() {
|
||||
pub fn max(a: @This(), b: @This()) @This() {
|
||||
return .{
|
||||
.x = @max(this.x, other.x),
|
||||
.y = @max(this.y, other.y),
|
||||
.x = @max(a.x, b.x),
|
||||
.y = @max(a.y, b.y),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,12 @@ pub const Buffered = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(this: *@This(), size: Point) !void {
|
||||
pub fn resize(this: *@This()) !void {
|
||||
const size = terminal.getTerminalSize();
|
||||
if (std.meta.eql(this.size, size)) return;
|
||||
|
||||
this.size = size;
|
||||
const n = @as(usize, size.x) * @as(usize, size.y);
|
||||
const n = @as(usize, this.size.x) * @as(usize, this.size.y);
|
||||
|
||||
if (!this.created) {
|
||||
this.screen = this.allocator.alloc(Cell, n) catch @panic("render.zig: Out of memory.");
|
||||
|
||||
Reference in New Issue
Block a user