From a4293ff2433c3ae84f077aa8e7e28601baf6f033 Mon Sep 17 00:00:00 2001 From: Yves Biener Date: Sun, 20 Apr 2025 20:53:46 +0200 Subject: [PATCH] add(event): mouse event has relative position for receiving elements --- examples/demo.zig | 43 ++++++++++++++++++++- examples/elements/input.zig | 74 +++++++++++++++++++++++++++++++++++-- src/container.zig | 8 +++- 3 files changed, 118 insertions(+), 7 deletions(-) diff --git a/examples/demo.zig b/examples/demo.zig index 995a5f4..a4dbc8b 100644 --- a/examples/demo.zig +++ b/examples/demo.zig @@ -87,15 +87,54 @@ pub fn main() !void { }, quit_text.element()); try container.append(try App.Container.init(allocator, .{}, scrollable.element())); - try container.append(try App.Container.init(allocator, .{ + var nested_container: App.Container = try .init(allocator, .{ + .layout = .{ + .direction = .vertical, + .separator = .{ + .enabled = true, + }, + }, + }, .{}); + var inner_container: App.Container = try .init(allocator, .{ + .layout = .{ + .direction = .vertical, + }, .border = .{ .color = .light_blue, .sides = .all, }, + }, .{}); + try inner_container.append(try .init(allocator, .{ + .rectangle = .{ + .fill = .blue, + }, .size = .{ - .dim = .{ .x = 100 }, + .grow = .horizontal, + .dim = .{ .y = 5 }, }, }, .{})); + try inner_container.append(try .init(allocator, .{ + .rectangle = .{ + .fill = .red, + }, + .size = .{ + .grow = .horizontal, + .dim = .{ .y = 5 }, + }, + }, .{})); + try inner_container.append(try .init(allocator, .{ + .rectangle = .{ + .fill = .green, + }, + }, .{})); + try nested_container.append(inner_container); + try nested_container.append(try .init(allocator, .{ + .size = .{ + .grow = .horizontal, + .dim = .{ .y = 1 }, + }, + }, .{})); + try container.append(nested_container); try container.append(try App.Container.init(allocator, .{ .rectangle = .{ .fill = .blue }, .size = .{ diff --git a/examples/elements/input.zig b/examples/elements/input.zig index c7f545e..9cdb0fa 100644 --- a/examples/elements/input.zig +++ b/examples/elements/input.zig @@ -86,10 +86,46 @@ const InputField = struct { for (this.input.items, 0..) |cp, idx| { cells[anchor + idx].style.fg = .black; + cells[anchor + idx].style.cursor = false; cells[anchor + idx].cp = cp; // NOTE do not write over the contents of this `Container`'s `Size` if (anchor + idx == cells.len - 1) break; + + if (idx == this.input.items.len - 1) cells[anchor + idx + 1].style.cursor = true; + } + } +}; + +const MouseDraw = struct { + position: ?zterm.Point = null, + + pub fn element(this: *@This()) App.Element { + return .{ + .ptr = this, + .vtable = &.{ + .handle = handle, + .content = content, + }, + }; + } + + fn handle(ctx: *anyopaque, event: App.Event) !void { + const this: *@This() = @ptrCast(@alignCast(ctx)); + switch (event) { + .mouse => |mouse| this.position = .{ .x = mouse.x, .y = mouse.y }, + else => this.position = null, + } + } + + fn content(ctx: *anyopaque, cells: []zterm.Cell, size: zterm.Point) !void { + std.debug.assert(cells.len == @as(usize, size.x) * @as(usize, size.y)); + const this: *@This() = @ptrCast(@alignCast(ctx)); + + if (this.position) |pos| { + const idx = @as(usize, size.x) * @as(usize, pos.y) + @as(usize, pos.x); + cells[idx].cp = 'x'; + cells[idx].style.fg = .red; } } }; @@ -109,17 +145,47 @@ pub fn main() !void { var input_field: InputField = .init(allocator, &app.queue); defer input_field.deinit(); + var mouse_draw: MouseDraw = .{}; + var second_mouse_draw: MouseDraw = .{}; var quit_text: QuitText = .{}; - const element = input_field.element(); - var container = try App.Container.init(allocator, .{ .rectangle = .{ .fill = .grey }, - .layout = .{ .padding = .all(5) }, + .layout = .{ + .direction = .vertical, + .padding = .all(5), + }, }, quit_text.element()); defer container.deinit(); - try container.append(try App.Container.init(allocator, .{ .rectangle = .{ .fill = .light_grey } }, element)); + try container.append(try App.Container.init(allocator, .{ + .rectangle = .{ .fill = .light_grey }, + .size = .{ + .grow = .horizontal, + .dim = .{ .y = 10 }, + }, + }, input_field.element())); + + var nested_container: App.Container = try .init(allocator, .{ + .border = .{ + .sides = .all, + .color = .black, + }, + .rectangle = .{ .fill = .light_grey }, + .layout = .{ + .separator = .{ + .enabled = true, + .color = .black, + }, + }, + }, .{}); + try nested_container.append(try .init(allocator, .{ + .rectangle = .{ .fill = .light_grey }, + }, mouse_draw.element())); + try nested_container.append(try .init(allocator, .{ + .rectangle = .{ .fill = .light_grey }, + }, second_mouse_draw.element())); + try container.append(nested_container); try app.start(); defer app.stop() catch |err| log.err("Failed to stop application: {any}", .{err}); diff --git a/src/container.zig b/src/container.zig index 9c07b4c..4878170 100644 --- a/src/container.zig +++ b/src/container.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const input = @import("input.zig"); const isTaggedUnion = @import("event.zig").isTaggedUnion; @@ -870,7 +871,12 @@ pub fn Container(comptime Event: type) type { pub fn handle(this: *@This(), event: Event) !void { switch (event) { .mouse => |mouse| if (mouse.in(this.origin, this.size)) { - try this.element.handle(event); + // the element receives the mouse event with relative position + std.debug.assert(mouse.x >= this.origin.x and mouse.y >= this.origin.y); + var relative_mouse: input.Mouse = mouse; + relative_mouse.x -= this.origin.x; + relative_mouse.y -= this.origin.y; + try this.element.handle(.{ .mouse = relative_mouse }); for (this.elements.items) |*element| try element.handle(event); }, else => {