diff --git a/examples/demo.zig b/examples/demo.zig index 47ffa040..d288fdba 100644 --- a/examples/demo.zig +++ b/examples/demo.zig @@ -74,7 +74,7 @@ fn drawRounded(cnv: *anyopaque, ctx: *capy.DrawContext) !void { ctx.setColor(0.7, 0.9, 0.3); ctx.setLinearGradient(.{ .x0 = 80, .y0 = 0, .x1 = 100, .y1 = 100, .stops = &.{ .{ .offset = 0.1, .color = capy.Color.yellow }, - .{ .offset = 0.8, .color = capy.Color.white }, + .{ .offset = 0.8, .color = capy.Color.red }, } }); ctx.roundedRectangleEx( 0, diff --git a/examples/fade.zig b/examples/fade.zig index cc7295f0..44a7a94d 100644 --- a/examples/fade.zig +++ b/examples/fade.zig @@ -33,10 +33,10 @@ pub fn main() !void { capy.expanded((try capy.row(.{}, .{ capy.label(.{ .text = "Hello Zig" }), capy.expanded( - capy.image(.{ .url = "asset:///ziglogo.png", .scaling = .Fit }), + capy.image(.{ .url = "asset:///ziglogo.png", .scaling = .Fit, .opacity = 0 }) + .bind("opacity", &opacity), ), - })) - .bind("opacity", &opacity)), + }))), capy.button(.{ .label = "Hide", .onclick = startAnimation }), }), ); diff --git a/examples/time-feed.zig b/examples/time-feed.zig index 14d3bde7..d31f195f 100644 --- a/examples/time-feed.zig +++ b/examples/time-feed.zig @@ -56,7 +56,7 @@ fn onSubmit(_: *anyopaque) !void { try list_model.add(.{ .start = @as(u64, @intCast(std.time.timestamp() - 1000)), .end = @as(u64, @intCast(std.time.timestamp())), - .description = submitDesc.get(), + .description = try capy.internal.lasting_allocator.dupe(u8, submitDesc.get()), }); // clear description @@ -72,7 +72,7 @@ pub fn InsertCard() anyerror!capy.Container { return try capy.column(.{}, .{ // TODO: TextArea when it supports data wrappers - capy.textField(.{ .name = "description" }) + capy.textArea(.{ .name = "description" }) .bind("text", &submitDesc), // placeholder = "Task description..." capy.label(.{ .text = "Going on since.. 00:00:20" }), capy.alignment(.{ .x = 1 }, capy.row(.{}, .{ diff --git a/src/components/Button.zig b/src/components/Button.zig index bdce4a75..ec1c97cc 100644 --- a/src/components/Button.zig +++ b/src/components/Button.zig @@ -69,12 +69,7 @@ pub const Button = struct { pub fn button(config: Button.Config) Button { var btn = Button.init(); - btn.label.set(config.label); - btn.enabled.set(config.enabled); - btn.widget_data.atoms.name.set(config.name); - if (config.onclick) |onclick| { - btn.addClickHandler(onclick) catch unreachable; // TODO: improve - } + @import("../internal.zig").applyConfigStruct(&btn, config); return btn; } diff --git a/src/components/Canvas.zig b/src/components/Canvas.zig index 9ffc30be..a058ac5f 100644 --- a/src/components/Canvas.zig +++ b/src/components/Canvas.zig @@ -39,14 +39,7 @@ pub const Canvas = struct { pub fn canvas(config: Canvas.Config) Canvas { var cnv = Canvas.init(); - cnv.preferredSize = Atom(?Size).of(config.preferredSize); - cnv.widget_data.atoms.name.set(config.name); - if (config.onclick) |onclick| { - cnv.addClickHandler(onclick) catch unreachable; // TODO: improve - } - if (config.ondraw) |ondraw| { - cnv.addDrawHandler(ondraw) catch unreachable; // TODO: improve - } + @import("../internal.zig").applyConfigStruct(&cnv, config); return cnv; } @@ -108,10 +101,7 @@ pub const Rect = struct { pub fn rect(config: Rect.Config) Rect { var r = Rect.init(); r.addDrawHandler(&Rect.draw) catch unreachable; - r.preferredSize = Atom(?Size).of(config.preferredSize); - r.color = Atom(Color).of(config.color); - r.cornerRadius = Atom([4]f32).of(config.cornerRadius); - r.widget_data.atoms.name.set(config.name); + @import("../internal.zig").applyConfigStruct(&r, config); return r; } diff --git a/src/components/CheckBox.zig b/src/components/CheckBox.zig index a72914a3..dbd7ba0f 100644 --- a/src/components/CheckBox.zig +++ b/src/components/CheckBox.zig @@ -81,12 +81,6 @@ pub const CheckBox = struct { pub fn checkBox(config: CheckBox.Config) CheckBox { var btn = CheckBox.init(); - btn.checked.set(config.checked); - btn.label.set(config.label); - btn.enabled.set(config.enabled); - btn.widget_data.atoms.name.set(config.name); - if (config.onclick) |onclick| { - btn.addClickHandler(onclick) catch unreachable; // TODO: improve - } + @import("../internal.zig").applyConfigStruct(&btn, config); return btn; } diff --git a/src/components/Image.zig b/src/components/Image.zig index ec7dfceb..d9964baa 100644 --- a/src/components/Image.zig +++ b/src/components/Image.zig @@ -41,12 +41,9 @@ pub const Image = struct { // TODO: just directly accept an URL or file path if there's no data pub fn init(config: Image.Config) Image { - var self = Image.init_events(Image{ - .url = Atom([]const u8).of(config.url), - .data = Atom(?ImageData).of(config.data), - .scaling = Atom(Scaling).of(config.scaling), - }); + var self = Image.init_events(Image{ .url = Atom([]const u8).of(config.url) }); self.addDrawHandler(&Image.draw) catch unreachable; + @import("../internal.zig").applyConfigStruct(&self, config); return self; } @@ -147,6 +144,5 @@ pub const Image = struct { }; pub fn image(config: Image.Config) Image { - var img = Image.init(config); - return img; + return Image.init(config); } diff --git a/src/components/Label.zig b/src/components/Label.zig index 91542e12..d6ca2fb7 100644 --- a/src/components/Label.zig +++ b/src/components/Label.zig @@ -12,10 +12,9 @@ pub const Label = struct { alignment: Atom(TextAlignment) = Atom(TextAlignment).of(.Left), pub fn init(config: Label.Config) Label { - return Label.init_events(Label{ - .text = Atom([]const u8).of(config.text), - .alignment = Atom(TextAlignment).of(config.alignment), - }); + var lbl = Label.init_events(Label{}); + @import("../internal.zig").applyConfigStruct(&lbl, config); + return lbl; } pub fn _pointerMoved(self: *Label) void { diff --git a/src/image.zig b/src/image.zig index 8611ff69..c7bfba18 100644 --- a/src/image.zig +++ b/src/image.zig @@ -46,7 +46,7 @@ pub const ImageData = struct { /// Load from a png file using a buffer (which can be provided by @embedFile) pub fn fromBuffer(allocator: std.mem.Allocator, buf: []const u8) !ImageData { // stage1 crashes with LLVM ERROR: Unable to expand fixed point multiplication. - //const img = try zigimg.Image.fromMemory(allocator, buf); + // const img = try zigimg.Image.fromMemory(allocator, buf); var stream = std.io.StreamSource{ .const_buffer = std.io.fixedBufferStream(buf) }; return readFromStream(allocator, &stream); diff --git a/src/internal.zig b/src/internal.zig index 1c11924a..240bcaea 100644 --- a/src/internal.zig +++ b/src/internal.zig @@ -8,7 +8,7 @@ const Widget = @import("widget.zig").Widget; const Class = @import("widget.zig").Class; const Size = dataStructures.Size; const Atom = dataStructures.Atom; -const Container_Impl = @import("containers.zig").Container_Impl; +const Container = @import("containers.zig").Container; const Layout = @import("containers.zig").Layout; const MouseButton = @import("backends/shared.zig").MouseButton; @@ -175,7 +175,7 @@ pub fn Widgeting(comptime T: type) type { } else { comptime { var compileError: []const u8 = "No such property: " ++ name; - if (T == Container_Impl) { + if (T == Container) { compileError = compileError ++ ", did you mean to use getChild() ?"; } @compileError(compileError); @@ -317,9 +317,32 @@ fn iterateFields(comptime config_fields: *[]const std.builtin.Type.StructField, } } -pub fn applyConfigStruct(target: anytype, config: anytype) void { - _ = target; - _ = config; +/// target is a pointer to a component +/// config is a config struct generated by GenerateConfigStruct(T) +pub fn applyConfigStruct(target: anytype, config: GenerateConfigStruct(std.meta.Child(@TypeOf(target)))) void { + std.debug.assert(std.meta.trait.isPtrTo(.Struct)(@TypeOf(target))); + iterateApplyFields(std.meta.Child(@TypeOf(target)), target, config); + + if (config.onclick) |onclick| { + target.addClickHandler(onclick) catch unreachable; // TODO: improve + } + if (config.ondraw) |ondraw| { + target.addDrawHandler(ondraw) catch unreachable; // TODO: improve + } +} + +fn iterateApplyFields(comptime T: type, target: anytype, config: GenerateConfigStruct(T)) void { + inline for (std.meta.fields(std.meta.Child(@TypeOf(target)))) |field| { + const FieldType = field.type; + if (comptime dataStructures.isAtom(FieldType)) { + const name = field.name; + @field(target, field.name).set( + @field(config, name), + ); + } else if (comptime std.meta.trait.is(.Struct)(FieldType)) { + iterateApplyFields(T, &@field(target, field.name), config); + } + } } /// If T is a pointer, return the type it points to, otherwise return T. @@ -349,7 +372,7 @@ pub fn genericWidgetFrom(component: anytype) anyerror!Widget { break :blk copy; }; - // Udate things like data wrappers, this happens once, at initialization, + // Update things like data wrappers, this happens once, at initialization, // after that the component isn't moved in memory anymore cp.pointerMoved(); @@ -555,6 +578,7 @@ pub fn Events(comptime T: type) type { /// When the value is changed in the opacity data wrapper fn opacityChanged(newValue: f32, userdata: usize) void { const widget = @as(*T, @ptrFromInt(userdata)); + std.log.info("opaccity changed to {d}", .{newValue}); if (widget.peer) |*peer| { peer.setOpacity(newValue); }