diff --git a/src/root.zig b/src/root.zig index d3c3717..8d57c45 100644 --- a/src/root.zig +++ b/src/root.zig @@ -176,14 +176,15 @@ pub const Colour = struct { a: u8 = 255, }; -const Dir = enum { - x, - y, -}; - pub const Scrollable = struct { - offset: *Real, - dir: Dir, + offset: *Pos, + child_size: Rect = .{}, + scroll_amount: f32 = 10.0, + dir: enum { + x, + y, + // both, + }, }; pub const MouseState = struct { @@ -191,6 +192,7 @@ pub const MouseState = struct { left: bool, right: bool, middle: bool, + scroll_delta: Pos, }; pub fn pointinRect(point: Pos, rect_pos: Pos, rect: Rect) bool { @@ -238,12 +240,12 @@ pub fn Shoots( try printWithLevel(writer, level + 1, "pos : {any},\n", .{node.element.pos}); try printWithLevel(writer, level + 1, "on_click : {?},\n", .{node.element.on_click}); try printWithLevel(writer, level + 1, "rect : {any},\n", .{node.element.rect}); - try printWithLevel(writer, level + 1, "style : .{{\n", .{}); try printWithLevel(writer, level + 1, "on_click : {any},\n", .{node.element.on_click}); try printWithLevel(writer, level + 1, "allow_on_click_when_occluded : {any},\n", .{node.element.allow_on_click_when_occluded}); try printWithLevel(writer, level + 1, "on_hover : {any},\n", .{node.element.on_hover}); try printWithLevel(writer, level + 1, "allow_on_hover_when_occluded : {any},\n", .{node.element.allow_on_hover_when_occluded}); try printWithLevel(writer, level + 1, "scrollable : {?},\n", .{node.element.scrollable}); + try printWithLevel(writer, level + 1, "style : .{{\n", .{}); try node.element.style.printStyle(writer, level + 1); try printWithLevel(writer, level + 1, "children : {{\n", .{}); for (node.element.children, 0..) |_, i| { @@ -301,8 +303,8 @@ pub fn Shoots( rect, text, texture, - // clip_start, - // clip_end, + clip_start, + clip_end, }; pub const RenderCommand = union(RenderCommandType) { @@ -323,13 +325,16 @@ pub fn Shoots( z_index: usize = 0, pos: Pos, }, - // clip_start: struct { - // pos: Pos, - // z_index: usize = 0, - // rect: Rect, - // rounding: ?Real, - // }, - // clip_end: struct {}, + clip_start: struct { + pos: Pos, + z_index: usize = 0, + rect: Rect, + // might allow rounded rectangles for clipping, + // hover this would require the user to implement a + // stencil buffer and that would be annoying + // rounding: ?Real, + }, + clip_end: struct {}, }; pub inline fn ElementWborder( @@ -420,14 +425,49 @@ pub fn Shoots( // std.debug.print("parent.element.rect.w : {}\n", .{parent.element.rect.w}); }, .texture => { - std.debug.print("before {any}\n", .{parent.element.rect}); parent.element.rect.h += node.texture.rect.h; parent.element.rect.w += node.texture.rect.w; - std.debug.print("after {any}\n", .{parent.element.rect}); }, } } + fn computeSizes(node: *Node, parent: *Node) void { + switch (node.*) { + .element => { + // std.debug.print("before {s} {}\n", .{ node.element.name, node.element.rect }); + // std.debug.print("parent name : {s}\n", .{parent.element.name}); + + const size = node.element.rect; + + for (node.element.children, 0..) |_, i| { + computeSizes(@constCast(&node.element.children[i]), node); + } + + // cheaters way to beat the fence post problem + // TODO fix this + switch (node.element.style.layout) { + .right_to_left => node.element.rect.w -= node.element.style.child_gap, + .top_to_bottom => node.element.rect.h -= node.element.style.child_gap, + } + if (node.element.scrollable) |scroll| { + // have to remove the parents size + node.element.scrollable.?.child_size = node.element.rect; + node.element.scrollable.?.child_size.w -= size.w; + node.element.scrollable.?.child_size.h -= size.h; + + switch (scroll.dir) { + // .both => node.element.rect = size, // retain size + .x => node.element.rect.w = size.w, // scroll in the x then the hieght stays the same + .y => node.element.rect.h = size.h, // scroll in the y then the width stays the same + } + } + // std.debug.print("after {s} {}\n", .{ node.element.name, node.element.rect }); + }, + else => {}, + } + closeElement(node, parent); + } + fn deepCloneHelper(node: Node, node_location: *Node, alloc: std.mem.Allocator) !void { // std.debug.print("nodeloc : {}\n", .{node_location}); node_location.* = node; @@ -473,15 +513,26 @@ pub fn Shoots( switch (node) { .element => { // std.debug.print("parent name : {s}\n", .{parent.element.name}); - try command_list.append(alloc, .{ - .rect = .{ - .colour = node.element.style.background_colour, - .pos = node.element.pos, - .rect = node.element.rect, - .rounding = node.element.style.rounded, - .z_index = node.element.z_index, - }, - }); + + if (node.element.scrollable) |_| { + try command_list.append(alloc, .{ + .clip_start = .{ + .rect = node.element.rect, + .z_index = node.element.z_index, + .pos = node.element.pos, + }, + }); + } else { + try command_list.append(alloc, .{ + .rect = .{ + .colour = node.element.style.background_colour, + .pos = node.element.pos, + .rect = node.element.rect, + .rounding = node.element.style.rounded, + .z_index = node.element.z_index, + }, + }); + } for (node.element.children, 0..) |_, i| { switch (node.element.children[i]) { @@ -500,6 +551,12 @@ pub fn Shoots( }), } } + + if (node.element.scrollable) |_| { + try command_list.append(alloc, .{ + .clip_end = .{}, + }); + } }, else => return, // the commands get created above, since the text and texture need a pos // but they dont have the pos one the parent has it @@ -512,27 +569,6 @@ pub fn Shoots( return render_commands; } - fn computeSizes(node: *Node, parent: *Node) void { - switch (node.*) { - .element => { - // std.debug.print("parent name : {s}\n", .{parent.element.name}); - - for (node.element.children, 0..) |_, i| { - computeSizes(@constCast(&node.element.children[i]), node); - } - - // cheaters way to beat the fence post problem - // TODO fix this - switch (node.element.style.layout) { - .right_to_left => node.element.rect.w -= node.element.style.child_gap, - .top_to_bottom => node.element.rect.h -= node.element.style.child_gap, - } - }, - else => {}, - } - closeElement(node, parent); - } - fn filterGrowableChildren(index: *usize, node: Node) ?*Ele { switch (node) { .element => { @@ -758,6 +794,14 @@ pub fn Shoots( fn computeChildernsPostions(ele: *Ele) void { const layout_style = ele.style.layout; var children = @constCast(ele.children); + const scroll_offset: Pos = blk: { + if (ele.scrollable) |scroll| { + const scroll_offset = scroll.offset.*; + break :blk scroll_offset; + } + + break :blk .{}; + }; switch (layout_style) { .right_to_left => { @@ -767,8 +811,8 @@ pub fn Shoots( switch (ele.children[i]) { .element => { // off set it by the parent - children[i].element.pos.x += ele.pos.x + ele.style.padding.left + off_set_left; - children[i].element.pos.y += ele.pos.y + ele.style.padding.top; + children[i].element.pos.x += ele.pos.x + ele.style.padding.left + off_set_left - scroll_offset.x; + children[i].element.pos.y += ele.pos.y + ele.style.padding.top - scroll_offset.y; off_set_left += children[i].element.rect.w + child_gap; // add }, @@ -783,8 +827,8 @@ pub fn Shoots( switch (ele.children[i]) { .element => { // off set it by the parent - children[i].element.pos.x += ele.pos.x + ele.style.padding.left; - children[i].element.pos.y += ele.pos.y + ele.style.padding.top + offset_top; + children[i].element.pos.x += ele.pos.x + ele.style.padding.left - scroll_offset.x; + children[i].element.pos.y += ele.pos.y + ele.style.padding.top + offset_top - scroll_offset.y; offset_top += children[i].element.rect.h + child_gap; // add }, @@ -843,6 +887,22 @@ pub fn Shoots( .element => |e| { // std.debug.print("clicked : {} hovered node name : {s}\n", .{ clicked, e.name }); + // TODO fix when another scrollable contaier occludes the other one + if (current_node.element.scrollable) |scroll| { + current_node.element.scrollable.?.offset.x += mouse_state.scroll_delta.x * scroll.child_size.w / current_node.element.rect.w; + current_node.element.scrollable.?.offset.y += mouse_state.scroll_delta.y * scroll.child_size.h / current_node.element.rect.h; + + // fixes being able to scroll past the min value + if (scroll.offset.x < 0) current_node.element.scrollable.?.offset.x = 0; + if (scroll.offset.y < 0) current_node.element.scrollable.?.offset.y = 0; + + // fixes being able to scroll past the max value + if (scroll.offset.x > scroll.child_size.w - current_node.element.rect.w) + current_node.element.scrollable.?.offset.x = scroll.child_size.w - current_node.element.rect.w; + if (scroll.offset.y > scroll.child_size.h - current_node.element.rect.h) + current_node.element.scrollable.?.offset.y = scroll.child_size.h - current_node.element.rect.h; + } + if (e.on_hover != null and e.allow_on_hover_when_occluded) { needs_redraw |= node.element.on_hover.?.func(&node.element, mouse_state, node.element.on_click.?.data); }