further refine element tree
parent
32f8b66761
commit
0ab23322ea
|
@ -19,26 +19,26 @@ pub const Interface = struct {
|
||||||
destroy: *const fn (?*anyopaque) void,
|
destroy: *const fn (?*anyopaque) void,
|
||||||
minimum_size: *const fn (?*anyopaque, RenderResources) [2]f32,
|
minimum_size: *const fn (?*anyopaque, RenderResources) [2]f32,
|
||||||
render: *const fn (?*anyopaque, *seizer.Canvas, RenderResources, [2]f32, [2]f32) void,
|
render: *const fn (?*anyopaque, *seizer.Canvas, RenderResources, [2]f32, [2]f32) void,
|
||||||
get_actions: *const fn (?*anyopaque, *std.ArrayList(Action), RenderResources, [2]f32, [2]f32) Error!void,
|
event: *const fn (?*anyopaque, Event) ?Command,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const RenderResources = struct {
|
pub const RenderResources = struct {
|
||||||
hovered: ?Element.Command,
|
hovered: bool,
|
||||||
deck: DeckSprites,
|
deck: DeckSprites,
|
||||||
font: seizer.Canvas.Font,
|
font: seizer.Canvas.Font,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Action = struct {
|
pub const Event = union(enum) {
|
||||||
center: [2]f32,
|
up,
|
||||||
command: Command,
|
right,
|
||||||
|
down,
|
||||||
|
left,
|
||||||
|
activate,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Command = union(enum) {
|
pub const Command = union(enum) {
|
||||||
|
none,
|
||||||
goto_reference: protocol.Reference,
|
goto_reference: protocol.Reference,
|
||||||
callback: struct {
|
|
||||||
pointer: ?*anyopaque,
|
|
||||||
function: *const fn (?*anyopaque) void,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeckSprites = assets.DeckSprites;
|
const DeckSprites = assets.DeckSprites;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
children: std.ArrayListUnmanaged(Element),
|
children: std.ArrayListUnmanaged(Element),
|
||||||
|
hovered: usize,
|
||||||
|
|
||||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||||
const this = try allocator.create(@This());
|
const this = try allocator.create(@This());
|
||||||
|
@ -7,6 +8,7 @@ pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||||
this.* = .{
|
this.* = .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.children = .{},
|
.children = .{},
|
||||||
|
.hovered = 0,
|
||||||
};
|
};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +24,7 @@ pub fn element(this: *@This()) Element {
|
||||||
.destroy = &element_destroy,
|
.destroy = &element_destroy,
|
||||||
.minimum_size = &element_minimum_size,
|
.minimum_size = &element_minimum_size,
|
||||||
.render = &element_render,
|
.render = &element_render,
|
||||||
.get_actions = &element_get_actions,
|
.event = &element_event,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -76,50 +78,45 @@ pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resou
|
||||||
const space_around = empty_space / @as(f32, @floatFromInt(num_spaces));
|
const space_around = empty_space / @as(f32, @floatFromInt(num_spaces));
|
||||||
|
|
||||||
var x: f32 = min[0] + @max(space_around, 0);
|
var x: f32 = min[0] + @max(space_around, 0);
|
||||||
for (this.children.items) |child| {
|
for (this.children.items, 0..) |child, child_index| {
|
||||||
const child_size = child.interface.minimum_size(child.pointer, render_resources);
|
const child_size = child.interface.minimum_size(child.pointer, render_resources);
|
||||||
|
|
||||||
const child_min = [2]f32{ x, min[1] };
|
const child_min = [2]f32{ x, min[1] };
|
||||||
const child_max = [2]f32{ x + child_size[0], max[1] };
|
const child_max = [2]f32{ x + child_size[0], max[1] };
|
||||||
|
|
||||||
child.interface.render(child.pointer, canvas, render_resources, child_min, child_max);
|
var child_render_resources = render_resources;
|
||||||
|
child_render_resources.hovered = render_resources.hovered and child_index == this.hovered;
|
||||||
|
child.interface.render(child.pointer, canvas, child_render_resources, child_min, child_max);
|
||||||
|
|
||||||
x += child_size[0] + space_around;
|
x += child_size[0] + space_around;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element_get_actions(pointer: ?*anyopaque, actions: *std.ArrayList(Element.Action), render_resources: Element.RenderResources, min: [2]f32, max: [2]f32) Element.Error!void {
|
pub fn element_activate(pointer: ?*anyopaque) ?Element.Command {
|
||||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
|
|
||||||
if (this.children.items.len == 0) return;
|
if (this.hovered >= this.children.items.len) return null;
|
||||||
|
const hovered = this.children.items[this.hovered];
|
||||||
|
return hovered.interface.activate(hovered.pointer);
|
||||||
|
}
|
||||||
|
|
||||||
const parent_size = [2]f32{
|
pub fn element_event(pointer: ?*anyopaque, event: Element.Event) ?Element.Command {
|
||||||
max[0] - min[0],
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
max[1] - min[1],
|
|
||||||
|
if (this.hovered < this.children.items.len) {
|
||||||
|
const hovered = this.children.items[this.hovered];
|
||||||
|
if (hovered.interface.event(hovered.pointer, event)) |cmd| {
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const new_hovered: usize = switch (event) {
|
||||||
|
.right => if (this.hovered < this.children.items.len - 1) this.hovered + 1 else return null,
|
||||||
|
.left => if (this.hovered > 0) this.hovered - 1 else return null,
|
||||||
|
else => return null,
|
||||||
};
|
};
|
||||||
|
this.hovered = new_hovered;
|
||||||
var filled_space: f32 = 0;
|
return Element.Command.none;
|
||||||
for (this.children.items) |child| {
|
|
||||||
const child_size = child.interface.minimum_size(child.pointer, render_resources);
|
|
||||||
|
|
||||||
filled_space += child_size[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const empty_space = parent_size[0] - filled_space;
|
|
||||||
|
|
||||||
const space_around = empty_space / @as(f32, @floatFromInt((this.children.items.len + 1)));
|
|
||||||
|
|
||||||
var x: f32 = min[0] + space_around;
|
|
||||||
for (this.children.items) |child| {
|
|
||||||
const child_size = child.interface.minimum_size(child.pointer, render_resources);
|
|
||||||
|
|
||||||
const child_min = [2]f32{ x, min[1] };
|
|
||||||
const child_max = [2]f32{ x + child_size[0], max[1] };
|
|
||||||
|
|
||||||
try child.interface.get_actions(child.pointer, actions, render_resources, child_min, child_max);
|
|
||||||
|
|
||||||
x += child_size[0] + space_around;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Element = @import("../Element.zig");
|
const Element = @import("../Element.zig");
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub fn element(this: *@This()) Element {
|
||||||
.destroy = &element_destroy,
|
.destroy = &element_destroy,
|
||||||
.minimum_size = &element_minimum_size,
|
.minimum_size = &element_minimum_size,
|
||||||
.render = &element_render,
|
.render = &element_render,
|
||||||
.get_actions = &element_get_actions,
|
.event = &element_event,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resou
|
||||||
this.child.interface.render(this.child.pointer, canvas, render_resources, min, max);
|
this.child.interface.render(this.child.pointer, canvas, render_resources, min, max);
|
||||||
|
|
||||||
// render selection indicator on top if this link is selected
|
// render selection indicator on top if this link is selected
|
||||||
if (render_resources.hovered != null and std.meta.eql(render_resources.hovered.?, Element.Command{ .goto_reference = this.reference })) {
|
if (render_resources.hovered) {
|
||||||
canvas.rect(
|
canvas.rect(
|
||||||
.{ min[0], min[1] },
|
.{ min[0], min[1] },
|
||||||
.{ max[0] - min[0], max[1] - min[1] },
|
.{ max[0] - min[0], max[1] - min[1] },
|
||||||
|
@ -53,18 +53,13 @@ pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resou
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element_get_actions(pointer: ?*anyopaque, actions: *std.ArrayList(Element.Action), render_resources: Element.RenderResources, min: [2]f32, max: [2]f32) Element.Error!void {
|
pub fn element_event(pointer: ?*anyopaque, event: Element.Event) ?Element.Command {
|
||||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
|
|
||||||
_ = render_resources;
|
switch (event) {
|
||||||
|
.activate => return Element.Command{ .goto_reference = this.reference },
|
||||||
try actions.append(.{
|
else => return null,
|
||||||
.center = [2]f32{
|
}
|
||||||
(min[0] + max[0]) / 2,
|
|
||||||
(min[1] + max[1]) / 2,
|
|
||||||
},
|
|
||||||
.command = .{ .goto_reference = this.reference },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Element = @import("../Element.zig");
|
const Element = @import("../Element.zig");
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
children: std.ArrayListUnmanaged(Child),
|
children: std.ArrayListUnmanaged(Child),
|
||||||
|
hovered: usize,
|
||||||
|
|
||||||
pub const Child = struct {
|
pub const Child = struct {
|
||||||
/// Where is the child attached on the parent? Imagine it as a pin going through
|
/// Where is the child attached on the parent? Imagine it as a pin going through
|
||||||
|
@ -25,6 +26,7 @@ pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||||
this.* = .{
|
this.* = .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.children = .{},
|
.children = .{},
|
||||||
|
.hovered = 0,
|
||||||
};
|
};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +46,7 @@ pub fn element(this: *@This()) Element {
|
||||||
.destroy = &element_destroy,
|
.destroy = &element_destroy,
|
||||||
.minimum_size = &element_minimum_size,
|
.minimum_size = &element_minimum_size,
|
||||||
.render = &element_render,
|
.render = &element_render,
|
||||||
.get_actions = &element_get_actions,
|
.event = &element_event,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -82,7 +84,7 @@ pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resou
|
||||||
max[1] - min[1],
|
max[1] - min[1],
|
||||||
};
|
};
|
||||||
|
|
||||||
for (this.children.items) |child| {
|
for (this.children.items, 0..) |child, child_index| {
|
||||||
const pos_in_parent = [2]f32{
|
const pos_in_parent = [2]f32{
|
||||||
child.anchor_in_parent[0] * parent_size[0],
|
child.anchor_in_parent[0] * parent_size[0],
|
||||||
child.anchor_in_parent[1] * parent_size[1],
|
child.anchor_in_parent[1] * parent_size[1],
|
||||||
|
@ -104,41 +106,69 @@ pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resou
|
||||||
@min(max[1], pos_in_parent[1] + (child_size[1] - pos_in_child[1])),
|
@min(max[1], pos_in_parent[1] + (child_size[1] - pos_in_child[1])),
|
||||||
};
|
};
|
||||||
|
|
||||||
child.element.interface.render(child.element.pointer, canvas, render_resources, child_min, child_max);
|
var child_render_resources = render_resources;
|
||||||
|
child_render_resources.hovered = render_resources.hovered and child_index == this.hovered;
|
||||||
|
|
||||||
|
child.element.interface.render(child.element.pointer, canvas, child_render_resources, child_min, child_max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element_get_actions(pointer: ?*anyopaque, actions: *std.ArrayList(Element.Action), render_resources: Element.RenderResources, min: [2]f32, max: [2]f32) Element.Error!void {
|
pub fn element_event(pointer: ?*anyopaque, event: Element.Event) ?Element.Command {
|
||||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
|
|
||||||
const parent_size = [2]f32{
|
if (this.hovered < this.children.items.len) {
|
||||||
max[0] - min[0],
|
const hovered = this.children.items[this.hovered].element;
|
||||||
max[1] - min[1],
|
if (hovered.interface.event(hovered.pointer, event)) |cmd| {
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const direction: [2]f32 = switch (event) {
|
||||||
|
.up => .{ 0, -1 },
|
||||||
|
.right => .{ 1, 0 },
|
||||||
|
.down => .{ 0, 1 },
|
||||||
|
.left => .{ -1, 0 },
|
||||||
|
else => return null,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (this.children.items) |child| {
|
const current_center: [2]f32 = if (this.hovered < this.children.items.len) this.children.items[this.hovered].anchor_in_parent else .{ 0.5, 0.5 };
|
||||||
const pos_in_parent = [2]f32{
|
|
||||||
child.anchor_in_parent[0] * parent_size[0],
|
var new_distance: ?f32 = null;
|
||||||
child.anchor_in_parent[1] * parent_size[1],
|
var new_hovered: ?usize = null;
|
||||||
|
for (this.children.items, 0..) |child, child_index| {
|
||||||
|
if (child_index == this.hovered) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (distanceToAction(current_center, direction, child.anchor_in_parent)) |distance| {
|
||||||
|
if (new_distance == null or (new_distance != null and distance < new_distance.?)) {
|
||||||
|
new_hovered = child_index;
|
||||||
|
new_distance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_hovered) |h| {
|
||||||
|
this.hovered = h;
|
||||||
|
return Element.Command.none;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn distanceToAction(origin: [2]f32, direction: [2]f32, other_pos: [2]f32) ?f32 {
|
||||||
|
const off_axis_dir = [2]f32{ direction[1], -direction[0] };
|
||||||
|
const offset = [2]f32{
|
||||||
|
other_pos[0] - origin[0],
|
||||||
|
other_pos[1] - origin[1],
|
||||||
};
|
};
|
||||||
|
|
||||||
const child_size = child.element.interface.minimum_size(child.element.pointer, render_resources);
|
// use dot product to check how in line with each direction the point of interest is
|
||||||
|
const in_axis_distance = offset[0] * direction[0] + offset[1] * direction[1];
|
||||||
|
const off_axis_distance = offset[0] * off_axis_dir[0] + offset[1] * off_axis_dir[1];
|
||||||
|
|
||||||
const pos_in_child = [2]f32{
|
if (in_axis_distance > 0) {
|
||||||
child.anchor_in_child[0] * child_size[0],
|
return in_axis_distance + off_axis_distance * off_axis_distance;
|
||||||
child.anchor_in_child[1] * child_size[1],
|
} else {
|
||||||
};
|
return null;
|
||||||
|
|
||||||
const child_min = [2]f32{
|
|
||||||
pos_in_parent[0] - pos_in_child[0],
|
|
||||||
pos_in_parent[1] - pos_in_child[1],
|
|
||||||
};
|
|
||||||
const child_max = [2]f32{
|
|
||||||
pos_in_parent[0] + (child_size[0] - pos_in_child[0]),
|
|
||||||
pos_in_parent[1] + (child_size[1] - pos_in_child[1]),
|
|
||||||
};
|
|
||||||
|
|
||||||
try child.element.interface.get_actions(child.element.pointer, actions, render_resources, child_min, child_max);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub fn element(this: *@This()) Element {
|
||||||
.destroy = &element_destroy,
|
.destroy = &element_destroy,
|
||||||
.minimum_size = &element_minimum_size,
|
.minimum_size = &element_minimum_size,
|
||||||
.render = &element_render,
|
.render = &element_render,
|
||||||
.get_actions = &element_get_actions,
|
.event = &element_event,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -46,12 +46,11 @@ pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resou
|
||||||
_ = canvas.writeText(min, this.text, .{});
|
_ = canvas.writeText(min, this.text, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element_get_actions(pointer: ?*anyopaque, actions: *std.ArrayList(Element.Action), render_resources: Element.RenderResources, min: [2]f32, max: [2]f32) Element.Error!void {
|
pub fn element_event(pointer: ?*anyopaque, event: Element.Event) ?Element.Command {
|
||||||
_ = pointer;
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
_ = actions;
|
_ = this;
|
||||||
_ = render_resources;
|
_ = event;
|
||||||
_ = min;
|
return null;
|
||||||
_ = max;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Element = @import("../Element.zig");
|
const Element = @import("../Element.zig");
|
||||||
|
|
|
@ -1,30 +1,27 @@
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
reference: protocol.Reference,
|
reference: protocol.Reference,
|
||||||
|
|
||||||
|
handler_pointer: ?*anyopaque,
|
||||||
response: ?protocol.Response,
|
response: ?protocol.Response,
|
||||||
|
|
||||||
link_actions_in_view: std.AutoHashMapUnmanaged(*LinkProxy, void) = .{},
|
|
||||||
|
|
||||||
const View = @This();
|
const View = @This();
|
||||||
|
|
||||||
const LinkProxy = struct {
|
|
||||||
view: *View,
|
|
||||||
reference: protocol.Reference,
|
|
||||||
|
|
||||||
fn callback(pointer: ?*anyopaque) void {
|
|
||||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
|
||||||
|
|
||||||
this.view.setReference(this.reference);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn create(allocator: std.mem.Allocator, reference: protocol.Reference) !*@This() {
|
pub fn create(allocator: std.mem.Allocator, reference: protocol.Reference) !*@This() {
|
||||||
const this = try allocator.create(@This());
|
const this = try allocator.create(@This());
|
||||||
errdefer allocator.destroy(this);
|
errdefer allocator.destroy(this);
|
||||||
|
|
||||||
|
var handler_pointer: ?*anyopaque = null;
|
||||||
|
switch (reference) {
|
||||||
|
.handler => |interface| if (interface.create) |handler_create| {
|
||||||
|
handler_pointer = try handler_create(allocator);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
this.* = .{
|
this.* = .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.reference = reference,
|
.reference = reference,
|
||||||
.response = null,
|
.response = null,
|
||||||
|
.handler_pointer = handler_pointer,
|
||||||
};
|
};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -35,13 +32,18 @@ pub fn setReference(this: *@This(), reference: protocol.Reference) void {
|
||||||
this.response = null;
|
this.response = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (this.reference) {
|
||||||
|
.handler => |interface| if (interface.deinit) |deinit| deinit(this.handler_pointer),
|
||||||
|
}
|
||||||
|
this.handler_pointer = null;
|
||||||
|
|
||||||
this.reference = reference;
|
this.reference = reference;
|
||||||
|
|
||||||
var action_proxies_iter = this.link_actions_in_view.keyIterator();
|
switch (this.reference) {
|
||||||
while (action_proxies_iter.next()) |link_proxy| {
|
.handler => |interface| if (interface.create) |handler_create| {
|
||||||
this.allocator.destroy(link_proxy.*);
|
this.handler_pointer = handler_create(this.allocator) catch unreachable;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
this.link_actions_in_view.clearRetainingCapacity();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element(this: *@This()) Element {
|
pub fn element(this: *@This()) Element {
|
||||||
|
@ -51,7 +53,7 @@ pub fn element(this: *@This()) Element {
|
||||||
.destroy = &element_destroy,
|
.destroy = &element_destroy,
|
||||||
.minimum_size = &element_minimum_size,
|
.minimum_size = &element_minimum_size,
|
||||||
.render = &element_render,
|
.render = &element_render,
|
||||||
.get_actions = &element_get_actions,
|
.event = &element_event,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -67,14 +69,8 @@ pub fn element_destroy(pointer: ?*anyopaque) void {
|
||||||
response.arena.deinit();
|
response.arena.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
var action_proxies_iter = this.link_actions_in_view.keyIterator();
|
|
||||||
while (action_proxies_iter.next()) |link_proxy| {
|
|
||||||
this.allocator.destroy(link_proxy.*);
|
|
||||||
}
|
|
||||||
this.link_actions_in_view.deinit(this.allocator);
|
|
||||||
|
|
||||||
switch (this.reference) {
|
switch (this.reference) {
|
||||||
.handler => |handler| if (handler.interface.deinit) |deinit| deinit(handler.pointer),
|
.handler => |interface| if (interface.deinit) |deinit| deinit(this.handler_pointer),
|
||||||
}
|
}
|
||||||
|
|
||||||
this.allocator.destroy(this);
|
this.allocator.destroy(this);
|
||||||
|
@ -95,7 +91,7 @@ pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resou
|
||||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
|
|
||||||
if (this.response == null) {
|
if (this.response == null) {
|
||||||
this.response = this.reference.handler.interface.handle(this.reference.handler.pointer, .{
|
this.response = this.reference.handler.handle(this.handler_pointer, .{
|
||||||
.allocator = this.allocator,
|
.allocator = this.allocator,
|
||||||
}) catch response_error: {
|
}) catch response_error: {
|
||||||
std.log.warn("Could not get response", .{});
|
std.log.warn("Could not get response", .{});
|
||||||
|
@ -104,70 +100,29 @@ pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resou
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.response) |response| {
|
if (this.response) |response| {
|
||||||
var child_render_resources = render_resources;
|
|
||||||
|
|
||||||
if (render_resources.hovered) |hovered| {
|
|
||||||
switch (hovered) {
|
|
||||||
.callback => |cb| {
|
|
||||||
if (this.link_actions_in_view.getKey(@ptrCast(@alignCast(cb.pointer)))) |link_proxy| {
|
|
||||||
child_render_resources.hovered = .{ .goto_reference = link_proxy.reference };
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (response.body) {
|
switch (response.body) {
|
||||||
.element => |root| root.interface.render(root.pointer, canvas, child_render_resources, min, max),
|
.element => |root| root.interface.render(root.pointer, canvas, render_resources, min, max),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_ = canvas.writeText(min, LOADING_TEXT, .{});
|
_ = canvas.writeText(min, LOADING_TEXT, .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element_get_actions(pointer: ?*anyopaque, actions: *std.ArrayList(Element.Action), render_resources: Element.RenderResources, min: [2]f32, max: [2]f32) Element.Error!void {
|
pub fn element_event(pointer: ?*anyopaque, event: Element.Event) ?Element.Command {
|
||||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
|
|
||||||
// clean up previous link actions
|
|
||||||
var action_proxies_iter = this.link_actions_in_view.keyIterator();
|
|
||||||
while (action_proxies_iter.next()) |link_proxy| {
|
|
||||||
this.allocator.destroy(link_proxy.*);
|
|
||||||
}
|
|
||||||
this.link_actions_in_view.clearRetainingCapacity();
|
|
||||||
|
|
||||||
var children_actions = std.ArrayList(Element.Action).init(this.allocator);
|
|
||||||
defer children_actions.deinit();
|
|
||||||
|
|
||||||
if (this.response) |response| {
|
if (this.response) |response| {
|
||||||
switch (response.body) {
|
switch (response.body) {
|
||||||
.element => |root| {
|
.element => |root| if (root.interface.event(root.pointer, event)) |cmd| switch (cmd) {
|
||||||
try root.interface.get_actions(root.pointer, &children_actions, render_resources, min, max);
|
.none => return Element.Command.none,
|
||||||
|
.goto_reference => |ref| {
|
||||||
|
this.setReference(ref);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (children_actions.items) |child_action| {
|
return null;
|
||||||
switch (child_action.command) {
|
|
||||||
.goto_reference => |ref| {
|
|
||||||
const link_proxy_action = try this.allocator.create(LinkProxy);
|
|
||||||
try this.link_actions_in_view.put(this.allocator, link_proxy_action, {});
|
|
||||||
link_proxy_action.* = .{
|
|
||||||
.view = this,
|
|
||||||
.reference = ref,
|
|
||||||
};
|
|
||||||
try actions.append(.{
|
|
||||||
.center = child_action.center,
|
|
||||||
.command = .{
|
|
||||||
.callback = .{
|
|
||||||
.pointer = link_proxy_action,
|
|
||||||
.function = &LinkProxy.callback,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
.callback => try actions.append(child_action),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Element = @import("../Element.zig");
|
const Element = @import("../Element.zig");
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
pub const main_menu = Handler{
|
pub const main_menu = &Handler.Interface{
|
||||||
.pointer = null,
|
.create = null,
|
||||||
.interface = &.{
|
|
||||||
.handle = &main_menu_handler,
|
.handle = &main_menu_handler,
|
||||||
.deinit = null,
|
.deinit = null,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main_menu_handler(_: ?*anyopaque, request: Request) Handler.Error!Response {
|
fn main_menu_handler(_: ?*anyopaque, request: Request) Handler.Error!Response {
|
||||||
|
@ -26,12 +24,10 @@ fn main_menu_handler(_: ?*anyopaque, request: Request) Handler.Error!Response {
|
||||||
return Response{ .arena = arena, .body = .{ .element = page.element() } };
|
return Response{ .arena = arena, .body = .{ .element = page.element() } };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const join_multiplayer_game = Handler{
|
pub const join_multiplayer_game = &Handler.Interface{
|
||||||
.pointer = null,
|
.create = null,
|
||||||
.interface = &.{
|
|
||||||
.handle = &join_multiplayer_game_handler,
|
.handle = &join_multiplayer_game_handler,
|
||||||
.deinit = null,
|
.deinit = null,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn join_multiplayer_game_handler(_: ?*anyopaque, request: Request) Handler.Error!Response {
|
fn join_multiplayer_game_handler(_: ?*anyopaque, request: Request) Handler.Error!Response {
|
||||||
|
@ -46,12 +42,10 @@ fn join_multiplayer_game_handler(_: ?*anyopaque, request: Request) Handler.Error
|
||||||
return Response{ .arena = arena, .body = .{ .element = page.element() } };
|
return Response{ .arena = arena, .body = .{ .element = page.element() } };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const host_multiplayer_game = Handler{
|
pub const host_multiplayer_game = &Handler.Interface{
|
||||||
.pointer = null,
|
.create = null,
|
||||||
.interface = &.{
|
|
||||||
.handle = &host_multiplayer_game_handler,
|
.handle = &host_multiplayer_game_handler,
|
||||||
.deinit = null,
|
.deinit = null,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn host_multiplayer_game_handler(_: ?*anyopaque, request: Request) Handler.Error!Response {
|
fn host_multiplayer_game_handler(_: ?*anyopaque, request: Request) Handler.Error!Response {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
pub usingnamespace @cImport({
|
||||||
|
@cInclude("enet/enet.h");
|
||||||
|
@cInclude("mdns.h");
|
||||||
|
});
|
115
src/main.zig
115
src/main.zig
|
@ -187,12 +187,6 @@ pub fn main() !void {
|
||||||
const main_view = try Element.View.create(gpa.allocator(), .{ .handler = LocalUI.main_menu });
|
const main_view = try Element.View.create(gpa.allocator(), .{ .handler = LocalUI.main_menu });
|
||||||
defer main_view.element().interface.destroy(main_view.element().pointer);
|
defer main_view.element().interface.destroy(main_view.element().pointer);
|
||||||
|
|
||||||
var actions = std.ArrayList(Element.Action).init(gpa.allocator());
|
|
||||||
defer actions.deinit();
|
|
||||||
|
|
||||||
// TODO: Restore hovered_action when undoing
|
|
||||||
var hovered_action: ?Element.Action = null;
|
|
||||||
|
|
||||||
if (seizer.backend.glfw.c.glfwJoystickIsGamepad(seizer.backend.glfw.c.GLFW_JOYSTICK_1) != 0) {
|
if (seizer.backend.glfw.c.glfwJoystickIsGamepad(seizer.backend.glfw.c.GLFW_JOYSTICK_1) != 0) {
|
||||||
std.log.info("detected gamepad = \"{?s}\" {?s}", .{ seizer.backend.glfw.c.glfwGetGamepadName(seizer.backend.glfw.c.GLFW_JOYSTICK_1), seizer.backend.glfw.c.glfwGetJoystickGUID(seizer.backend.glfw.c.GLFW_JOYSTICK_1) });
|
std.log.info("detected gamepad = \"{?s}\" {?s}", .{ seizer.backend.glfw.c.glfwGetGamepadName(seizer.backend.glfw.c.GLFW_JOYSTICK_1), seizer.backend.glfw.c.glfwGetJoystickGUID(seizer.backend.glfw.c.GLFW_JOYSTICK_1) });
|
||||||
} else if (seizer.backend.glfw.c.glfwJoystickPresent(seizer.backend.glfw.c.GLFW_JOYSTICK_1) != 0) {
|
} else if (seizer.backend.glfw.c.glfwJoystickPresent(seizer.backend.glfw.c.GLFW_JOYSTICK_1) != 0) {
|
||||||
|
@ -276,58 +270,49 @@ pub fn main() !void {
|
||||||
prev_controller_input_state = controller_input_state;
|
prev_controller_input_state = controller_input_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hovered_action) |hovered| {
|
if (input_state.up) _ = main_view.element().interface.event(main_view.element().pointer, .up);
|
||||||
if (input_state.action) {
|
if (input_state.right) _ = main_view.element().interface.event(main_view.element().pointer, .right);
|
||||||
switch (hovered.command) {
|
if (input_state.down) _ = main_view.element().interface.event(main_view.element().pointer, .down);
|
||||||
.goto_reference => {
|
if (input_state.left) _ = main_view.element().interface.event(main_view.element().pointer, .left);
|
||||||
std.debug.panic("Got `.goto_reference` action at top-level; expected `main_view` to proxy them all into LinkProxy callbacks.", .{});
|
if (input_state.action) _ = main_view.element().interface.event(main_view.element().pointer, .activate);
|
||||||
},
|
|
||||||
.callback => |cb| {
|
|
||||||
cb.function(cb.pointer);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (actions.items.len > 0) {
|
|
||||||
hovered_action = actions.items[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hovered_action) |hovered| {
|
// if (hovered_action) |hovered| {
|
||||||
var direction = [2]f32{ 0, 0 };
|
// var direction = [2]f32{ 0, 0 };
|
||||||
if (input_state.left) {
|
// if (input_state.left) {
|
||||||
direction[0] -= 1;
|
// direction[0] -= 1;
|
||||||
}
|
// }
|
||||||
if (input_state.right) {
|
// if (input_state.right) {
|
||||||
direction[0] += 1;
|
// direction[0] += 1;
|
||||||
}
|
// }
|
||||||
if (input_state.up) {
|
// if (input_state.up) {
|
||||||
direction[1] -= 1;
|
// direction[1] -= 1;
|
||||||
}
|
// }
|
||||||
if (input_state.down) {
|
// if (input_state.down) {
|
||||||
direction[1] += 1;
|
// direction[1] += 1;
|
||||||
}
|
// }
|
||||||
|
|
||||||
var new_distance: ?f32 = null;
|
// var new_distance: ?f32 = null;
|
||||||
var new_action = hovered;
|
// var new_action = hovered;
|
||||||
for (actions.items) |action| {
|
// for (actions.items) |action| {
|
||||||
if (std.meta.eql(action.command, hovered.command)) {
|
// if (std.meta.eql(action.command, hovered.command)) {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
if (distanceToAction(hovered.center, direction, action.center)) |distance| {
|
// if (distanceToAction(hovered.center, direction, action.center)) |distance| {
|
||||||
if (new_distance == null or (new_distance != null and distance < new_distance.?)) {
|
// if (new_distance == null or (new_distance != null and distance < new_distance.?)) {
|
||||||
new_action = Element.Action{
|
// new_action = Element.Action{
|
||||||
.center = .{
|
// .center = .{
|
||||||
if (input_state.left or input_state.right) action.center[0] else hovered.center[0],
|
// if (input_state.left or input_state.right) action.center[0] else hovered.center[0],
|
||||||
if (input_state.up or input_state.down) action.center[1] else hovered.center[1],
|
// if (input_state.up or input_state.down) action.center[1] else hovered.center[1],
|
||||||
},
|
// },
|
||||||
.command = action.command,
|
// .command = action.command,
|
||||||
};
|
// };
|
||||||
new_distance = distance;
|
// new_distance = distance;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
hovered_action = new_action;
|
// hovered_action = new_action;
|
||||||
}
|
// }
|
||||||
|
|
||||||
gl.clearColor(0.7, 0.5, 0.5, 1.0);
|
gl.clearColor(0.7, 0.5, 0.5, 1.0);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
@ -373,7 +358,7 @@ pub fn main() !void {
|
||||||
main_view.element().pointer,
|
main_view.element().pointer,
|
||||||
&canvas,
|
&canvas,
|
||||||
.{
|
.{
|
||||||
.hovered = if (hovered_action) |ha| ha.command else null,
|
.hovered = true,
|
||||||
.deck = deck_sprites,
|
.deck = deck_sprites,
|
||||||
.font = canvas.font,
|
.font = canvas.font,
|
||||||
},
|
},
|
||||||
|
@ -384,27 +369,9 @@ pub fn main() !void {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
_ = canvas.printText(.{ 0, 0 }, "#actions = {}", .{actions.items.len}, .{});
|
|
||||||
|
|
||||||
canvas.end();
|
canvas.end();
|
||||||
|
|
||||||
seizer.backend.glfw.c.glfwSwapBuffers(window);
|
seizer.backend.glfw.c.glfwSwapBuffers(window);
|
||||||
|
|
||||||
actions.clearRetainingCapacity();
|
|
||||||
try main_view.element().interface.get_actions(
|
|
||||||
main_view.element().pointer,
|
|
||||||
&actions,
|
|
||||||
.{
|
|
||||||
.hovered = if (hovered_action) |ha| ha.command else null,
|
|
||||||
.deck = deck_sprites,
|
|
||||||
.font = canvas.font,
|
|
||||||
},
|
|
||||||
.{ 0, 0 },
|
|
||||||
.{
|
|
||||||
@floatFromInt(window_size[0]),
|
|
||||||
@floatFromInt(window_size[1]),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,14 @@ pub const Handler = struct {
|
||||||
pub const Error = error{OutOfMemory};
|
pub const Error = error{OutOfMemory};
|
||||||
pub const Fn = *const fn (Request) Error!Response;
|
pub const Fn = *const fn (Request) Error!Response;
|
||||||
pub const Interface = struct {
|
pub const Interface = struct {
|
||||||
|
create: ?*const fn (std.mem.Allocator) Error!?*anyopaque,
|
||||||
handle: *const fn (?*anyopaque, Request) Error!Response,
|
handle: *const fn (?*anyopaque, Request) Error!Response,
|
||||||
deinit: ?*const fn (?*anyopaque) void,
|
deinit: ?*const fn (?*anyopaque) void,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Reference = union(enum) {
|
pub const Reference = union(enum) {
|
||||||
handler: Handler,
|
handler: *const Handler.Interface,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Request = struct {
|
pub const Request = struct {
|
||||||
|
|
Loading…
Reference in New Issue