init
This commit is contained in:
commit
a5f7d377fd
5 changed files with 416 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/zig-*
|
31
build.zig
Normal file
31
build.zig
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const zenolith_dep = b.dependency("zenolith", .{ .target = target, .optimize = optimize });
|
||||||
|
const zenolith_sdl2_dep = b.dependency("zenolith_sdl2", .{ .target = target, .optimize = optimize });
|
||||||
|
|
||||||
|
inline for (.{ "themeswitcher", "counter" }) |name| {
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = name,
|
||||||
|
.root_source_file = .{ .path = "src/" ++ name ++ ".zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
exe.root_module.addImport("zenolith", zenolith_dep.module("zenolith"));
|
||||||
|
exe.root_module.addImport("zenolith-sdl2", zenolith_sdl2_dep.module("zenolith-sdl2"));
|
||||||
|
|
||||||
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
if (b.args) |args| run_cmd.addArgs(args);
|
||||||
|
|
||||||
|
const run_step = b.step("run-" ++ name, "Run the " ++ name ++ " example");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
}
|
||||||
|
}
|
19
build.zig.zon
Normal file
19
build.zig.zon
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
.{
|
||||||
|
.name = "zenolith-exmaples",
|
||||||
|
.version = "0.0.0",
|
||||||
|
|
||||||
|
.dependencies = .{
|
||||||
|
// Make sure this is the same version as the one used by zenolith-sdl2.
|
||||||
|
// See: https://git.mzte.de/zenolith/zenolith-sdl2
|
||||||
|
.zenolith = .{
|
||||||
|
.url = "git+https://git.mzte.de/zenolith/zenolith.git#6292688915f91da3ace4f38caa1dc33c718c8fc0",
|
||||||
|
.hash = "1220b709a856fe648fd5be6351b319d3626b3e368062d301e666a969032121b5ecde",
|
||||||
|
},
|
||||||
|
.zenolith_sdl2 = .{
|
||||||
|
.url = "git+https://git.mzte.de/zenolith/zenolith-sdl2.git#46431dbe65d50c899c3b701e2a3218dbaf1b4c8d",
|
||||||
|
.hash = "1220e3161064d9eae555ae33e74e0d0bcd07daa9a70b9052dd4094451f4b27ac2e2a",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
.paths = .{""},
|
||||||
|
}
|
187
src/counter.zig
Normal file
187
src/counter.zig
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const zl = @import("zenolith");
|
||||||
|
const zsdl2 = @import("zenolith-sdl2");
|
||||||
|
|
||||||
|
pub const zenolith_options = struct {
|
||||||
|
// Register zenolith-sdl2 platform and painter implementations.
|
||||||
|
// This is required as Zenolith builds a statspatch type from these.
|
||||||
|
// See: https://git.mzte.de/lordmzte/statspatch
|
||||||
|
pub const platform_impls = [_]type{zsdl2.Sdl2Platform};
|
||||||
|
pub const painter_impls = [_]type{zsdl2.Sdl2Painter};
|
||||||
|
|
||||||
|
// Register widget implementations: builtin implementations and our Root widget.
|
||||||
|
pub const widget_impls = zl.default_widget_impls ++ [_]type{Root};
|
||||||
|
|
||||||
|
// Enable to draw debug information.
|
||||||
|
//pub const debug_render = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Our root widget type. Most functions in here will be invoked by Zenolith.
|
||||||
|
const Root = struct {
|
||||||
|
// We use this box as our only child widget. It will contain our buttons and a label.
|
||||||
|
box: *zl.widget.Widget,
|
||||||
|
|
||||||
|
// These aren't immediate children, we just save them have to be able to easily reference them.
|
||||||
|
// Zenolith will take care of freeing these.
|
||||||
|
sub_btn: *zl.widget.Widget,
|
||||||
|
label: *zl.widget.Widget,
|
||||||
|
add_btn: *zl.widget.Widget,
|
||||||
|
|
||||||
|
counter: i32,
|
||||||
|
|
||||||
|
pub fn init(alloc: std.mem.Allocator) !*zl.widget.Widget {
|
||||||
|
const vbox = try zl.widget.Box.init(alloc, .vertical);
|
||||||
|
errdefer vbox.deinit();
|
||||||
|
|
||||||
|
try vbox.addChild(null, try zl.widget.Label.init(alloc, "A simple counter example."));
|
||||||
|
try vbox.addChild(null, try zl.widget.Label.init(alloc, "Use the '+' and '-' buttons to change the counter!"));
|
||||||
|
try vbox.addChild(null, try zl.widget.Spacer.init(alloc, .{ .fixed = .{ .width = 0, .height = 20 } }));
|
||||||
|
|
||||||
|
const hbox = try zl.widget.Box.init(alloc, .horizontal);
|
||||||
|
|
||||||
|
const sub_btn = try zl.widget.Button.init(alloc, "-");
|
||||||
|
try hbox.addChild(null, sub_btn); // null => append to end
|
||||||
|
|
||||||
|
// Add a spacer with a flex-expand of 1. This works somewhat like
|
||||||
|
// (but not completely identical to) CSS.
|
||||||
|
try hbox.addChild(null, try zl.widget.Spacer.init(alloc, .{ .flex = 1 }));
|
||||||
|
|
||||||
|
const label = try zl.widget.Label.init(alloc, "0");
|
||||||
|
try hbox.addChild(null, label);
|
||||||
|
|
||||||
|
try hbox.addChild(null, try zl.widget.Spacer.init(alloc, .{ .flex = 1 }));
|
||||||
|
|
||||||
|
const add_btn = try zl.widget.Button.init(alloc, "+");
|
||||||
|
try hbox.addChild(null, add_btn);
|
||||||
|
|
||||||
|
try vbox.addChild(null, hbox);
|
||||||
|
try vbox.addChild(null, try zl.widget.Spacer.init(alloc, .{ .flex = 1 }));
|
||||||
|
try vbox.addChild(null, try zl.widget.Label.init(alloc, "Hint: also try the plus and minus keys or tab + space!"));
|
||||||
|
|
||||||
|
const self = Root{
|
||||||
|
.box = vbox,
|
||||||
|
|
||||||
|
.sub_btn = sub_btn,
|
||||||
|
.label = label,
|
||||||
|
.add_btn = add_btn,
|
||||||
|
|
||||||
|
.counter = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
return try zl.widget.Widget.init(alloc, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is called by Zenolith whenever a treevent is fired on our widget tree.
|
||||||
|
/// We'll handle a few ones, and simply call the default dispatcher on others.
|
||||||
|
pub fn treevent(self: *Root, selfw: *zl.widget.Widget, tv: anytype) !void {
|
||||||
|
switch (@TypeOf(tv)) {
|
||||||
|
// The LayoutSize treevent is fired when a layout path is being done on the Widget tree.
|
||||||
|
// We'll simply let the Box do it's layout and use the exact same size.
|
||||||
|
// We force box to use the maximum available size (= the window's size),
|
||||||
|
// so it takes up all space.
|
||||||
|
zl.treevent.LayoutSize => {
|
||||||
|
var tvv = tv;
|
||||||
|
tvv.constraints = zl.layout.Constraints.tight(tv.constraints.max);
|
||||||
|
try self.box.treevent(tvv);
|
||||||
|
selfw.data.size = self.box.data.size;
|
||||||
|
},
|
||||||
|
|
||||||
|
// This event is fired when a letter is typed.
|
||||||
|
*zl.treevent.CharType => {
|
||||||
|
// Dispatch the key event to children first...
|
||||||
|
try tv.dispatch(selfw);
|
||||||
|
|
||||||
|
// If no child handled it, and it's a press event..
|
||||||
|
if (!tv.handled) {
|
||||||
|
// ... update the counter accordingly
|
||||||
|
switch (tv.codepoint) {
|
||||||
|
'+' => {
|
||||||
|
self.counter +%= 1;
|
||||||
|
try self.updateLabel();
|
||||||
|
},
|
||||||
|
'-' => {
|
||||||
|
self.counter -%= 1;
|
||||||
|
try self.updateLabel();
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
else => try tv.dispatch(selfw),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called upon this widget receiving a backevent. Backevents are events which travel the
|
||||||
|
/// widget tree upwards. Buttons emit the ButtonActivated backevent upon being clicked.
|
||||||
|
pub fn backevent(self: *Root, selfw: *zl.widget.Widget, be: zl.backevent.Backevent) !void {
|
||||||
|
if (be.downcast(zl.backevent.ButtonActivated)) |ba| {
|
||||||
|
if (ba.btn_widget == self.add_btn) {
|
||||||
|
self.counter +%= 1;
|
||||||
|
} else if (ba.btn_widget == self.sub_btn) {
|
||||||
|
self.counter -%= 1;
|
||||||
|
}
|
||||||
|
try self.updateLabel();
|
||||||
|
} else {
|
||||||
|
// Dispatch other backevents.
|
||||||
|
try be.dispatch(selfw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function is required so Zenolith can operate on our child widgets.
|
||||||
|
pub fn children(self: *Root, selfw: *zl.widget.Widget) []const *zl.widget.Widget {
|
||||||
|
_ = selfw;
|
||||||
|
return @as([*]const *zl.widget.Widget, @ptrCast(&self.box))[0..1];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn updateLabel(self: *Root) !void {
|
||||||
|
// update label text
|
||||||
|
var buf: [64]u8 = undefined;
|
||||||
|
try self.label.downcast(zl.widget.Label).?.span.?.updateGlyphs(.{
|
||||||
|
.text = try std.fmt.bufPrint(&buf, "{}", .{self.counter}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const alloc = std.heap.c_allocator;
|
||||||
|
|
||||||
|
// Initialize the SDL2 platform.
|
||||||
|
var platform = try zsdl2.Sdl2Platform.init(.{ .alloc = alloc });
|
||||||
|
defer platform.deinit();
|
||||||
|
|
||||||
|
// Use the platform to create a font. Font discovery will be supported in a future version.
|
||||||
|
var font = zl.text.Font.create(try platform.createFont(.{
|
||||||
|
.source = .{ .path = "/usr/share/fonts/liberation/LiberationSans-Regular.ttf" },
|
||||||
|
}), {});
|
||||||
|
defer font.deinit();
|
||||||
|
|
||||||
|
// Create statspatch-based platform type for use by platform-agnostic code.
|
||||||
|
var zplatf = zl.platform.Platform.create(platform, .{});
|
||||||
|
|
||||||
|
const root = try Root.init(alloc);
|
||||||
|
defer root.deinit();
|
||||||
|
|
||||||
|
// Create an AttreebuteMap for the root widget.
|
||||||
|
// Attreebutes are tree-bound attributes, which are inherited to child widgets.
|
||||||
|
// They're backed by a type-indexed map (think ECS).
|
||||||
|
{
|
||||||
|
var attrs = zl.attreebute.AttreebuteMap.init();
|
||||||
|
errdefer attrs.deinit(alloc);
|
||||||
|
|
||||||
|
// This is required by various widgets in order to render text.
|
||||||
|
(try attrs.mod(alloc, zl.attreebute.CurrentFont)).* = .{ .font = &font };
|
||||||
|
|
||||||
|
// This sets a variety of theming-related attributes to the builtin catppuccin mocha theme.
|
||||||
|
try zl.Theme.catppuccin_mocha.apply(alloc, &attrs);
|
||||||
|
|
||||||
|
root.data.attreebutes = attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This links the widget tree (ie. correctly sets parent pointers and updates the platform).
|
||||||
|
// Some widgets also do initialization here.
|
||||||
|
try root.link(null, &zplatf);
|
||||||
|
|
||||||
|
// Run the main event loop until the application is closed;
|
||||||
|
try platform.run(root);
|
||||||
|
}
|
178
src/themeswitcher.zig
Normal file
178
src/themeswitcher.zig
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const zl = @import("zenolith");
|
||||||
|
const zsdl2 = @import("zenolith-sdl2");
|
||||||
|
|
||||||
|
pub const zenolith_options = struct {
|
||||||
|
// Register zenolith-sdl2 platform and painter implementations.
|
||||||
|
// This is required as Zenolith builds a statspatch type from these.
|
||||||
|
// See: https://git.mzte.de/lordmzte/statspatch
|
||||||
|
pub const platform_impls = [_]type{zsdl2.Sdl2Platform};
|
||||||
|
pub const painter_impls = [_]type{zsdl2.Sdl2Painter};
|
||||||
|
|
||||||
|
// Register widget implementations: builtin implementations and our Root widget.
|
||||||
|
pub const widget_impls = zl.default_widget_impls ++ [_]type{Root};
|
||||||
|
|
||||||
|
// Enable to draw debug information.
|
||||||
|
//pub const debug_render = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Our root widget type. Most functions in here will be invoked by Zenolith.
|
||||||
|
const Root = struct {
|
||||||
|
// We use this box as our only child widget. It will contain our buttons and a label.
|
||||||
|
box: *zl.widget.Widget,
|
||||||
|
|
||||||
|
// These aren't immediate children, we just save them have to be able to easily reference them.
|
||||||
|
// Zenolith will take care of freeing these.
|
||||||
|
label: *zl.widget.Widget,
|
||||||
|
latte_btn: *zl.widget.Widget,
|
||||||
|
frappe_btn: *zl.widget.Widget,
|
||||||
|
macchiato_btn: *zl.widget.Widget,
|
||||||
|
mocha_btn: *zl.widget.Widget,
|
||||||
|
|
||||||
|
pub fn init(alloc: std.mem.Allocator) !*zl.widget.Widget {
|
||||||
|
const vbox = try zl.widget.Box.init(alloc, .vertical);
|
||||||
|
errdefer vbox.deinit();
|
||||||
|
|
||||||
|
try vbox.addChild(null, try zl.widget.Label.init(alloc, "An example demonstrating theming in Zenolith."));
|
||||||
|
try vbox.addChild(null, try zl.widget.Label.init(alloc, "Use the buttons below to change the theme!"));
|
||||||
|
try vbox.addChild(null, try zl.widget.Spacer.init(alloc, .{ .fixed = .{ .width = 0, .height = 20 } }));
|
||||||
|
const label = try zl.widget.Label.init(alloc, "Current Theme: Mocha");
|
||||||
|
try vbox.addChild(null, label);
|
||||||
|
try vbox.addChild(null, try zl.widget.Spacer.init(alloc, .{ .fixed = .{ .width = 0, .height = 20 } }));
|
||||||
|
|
||||||
|
const hbox = try zl.widget.Box.init(alloc, .horizontal);
|
||||||
|
|
||||||
|
const latte_btn = try zl.widget.Button.init(alloc, "Latte");
|
||||||
|
const frappe_btn = try zl.widget.Button.init(alloc, "Frappe");
|
||||||
|
const macchiato_btn = try zl.widget.Button.init(alloc, "Macchiato");
|
||||||
|
const mocha_btn = try zl.widget.Button.init(alloc, "Mocha");
|
||||||
|
|
||||||
|
try hbox.addChild(null, latte_btn);
|
||||||
|
try hbox.addChild(null, try zl.widget.Spacer.init(alloc, .{ .flex = 1 }));
|
||||||
|
try hbox.addChild(null, frappe_btn);
|
||||||
|
try hbox.addChild(null, try zl.widget.Spacer.init(alloc, .{ .flex = 1 }));
|
||||||
|
try hbox.addChild(null, macchiato_btn);
|
||||||
|
try hbox.addChild(null, try zl.widget.Spacer.init(alloc, .{ .flex = 1 }));
|
||||||
|
try hbox.addChild(null, mocha_btn);
|
||||||
|
|
||||||
|
try vbox.addChild(null, hbox);
|
||||||
|
|
||||||
|
const self = Root{
|
||||||
|
.box = vbox,
|
||||||
|
|
||||||
|
.label = label,
|
||||||
|
.latte_btn = latte_btn,
|
||||||
|
.frappe_btn = frappe_btn,
|
||||||
|
.macchiato_btn = macchiato_btn,
|
||||||
|
.mocha_btn = mocha_btn,
|
||||||
|
};
|
||||||
|
|
||||||
|
return try zl.widget.Widget.init(alloc, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is called by Zenolith whenever a treevent is fired on our widget tree.
|
||||||
|
/// We'll handle a few ones, and simply call the default dispatcher on others.
|
||||||
|
pub fn treevent(self: *Root, selfw: *zl.widget.Widget, tv: anytype) !void {
|
||||||
|
switch (@TypeOf(tv)) {
|
||||||
|
// The LayoutSize treevent is fired when a layout path is being done on the Widget tree.
|
||||||
|
// We'll simply let the Box do it's layout and use the exact same size.
|
||||||
|
// We force box to use the maximum available size (= the window's size),
|
||||||
|
// so it takes up all space.
|
||||||
|
zl.treevent.LayoutSize => {
|
||||||
|
var tvv = tv;
|
||||||
|
tvv.constraints = zl.layout.Constraints.tight(tv.constraints.max);
|
||||||
|
try self.box.treevent(tvv);
|
||||||
|
selfw.data.size = self.box.data.size;
|
||||||
|
},
|
||||||
|
|
||||||
|
else => try tv.dispatch(selfw),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called upon this widget receiving a backevent. Backevents are events which travel the
|
||||||
|
/// widget tree upwards. Buttons emit the ButtonActivated backevent upon being clicked.
|
||||||
|
pub fn backevent(self: *Root, selfw: *zl.widget.Widget, be: zl.backevent.Backevent) !void {
|
||||||
|
if (be.downcast(zl.backevent.ButtonActivated)) |ba| {
|
||||||
|
const theme = if (ba.btn_widget == self.latte_btn)
|
||||||
|
zl.Theme.catppuccin_latte
|
||||||
|
else if (ba.btn_widget == self.frappe_btn)
|
||||||
|
zl.Theme.catppuccin_frappe
|
||||||
|
else if (ba.btn_widget == self.macchiato_btn)
|
||||||
|
zl.Theme.catppuccin_macchiato
|
||||||
|
else if (ba.btn_widget == self.mocha_btn)
|
||||||
|
zl.Theme.catppuccin_mocha
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
try theme.apply(selfw.data.allocator, &selfw.data.attreebutes.?);
|
||||||
|
|
||||||
|
const text = if (ba.btn_widget == self.latte_btn)
|
||||||
|
"Latte"
|
||||||
|
else if (ba.btn_widget == self.frappe_btn)
|
||||||
|
"Frappe"
|
||||||
|
else if (ba.btn_widget == self.macchiato_btn)
|
||||||
|
"Macchiato"
|
||||||
|
else if (ba.btn_widget == self.mocha_btn)
|
||||||
|
"Mocha"
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
// update label text
|
||||||
|
var buf: [64]u8 = undefined;
|
||||||
|
try self.label.downcast(zl.widget.Label).?.span.?.updateGlyphs(.{
|
||||||
|
.text = try std.fmt.bufPrint(&buf, "Active Theme: {s}", .{text}),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Dispatch other backevents.
|
||||||
|
try be.dispatch(selfw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function is required so Zenolith can operate on our child widgets.
|
||||||
|
pub fn children(self: *Root, selfw: *zl.widget.Widget) []const *zl.widget.Widget {
|
||||||
|
_ = selfw;
|
||||||
|
return @as([*]const *zl.widget.Widget, @ptrCast(&self.box))[0..1];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const alloc = std.heap.c_allocator;
|
||||||
|
|
||||||
|
// Initialize the SDL2 platform.
|
||||||
|
var platform = try zsdl2.Sdl2Platform.init(.{ .alloc = alloc });
|
||||||
|
defer platform.deinit();
|
||||||
|
|
||||||
|
// Use the platform to create a font. Font discovery will be supported in a future version.
|
||||||
|
var font = zl.text.Font.create(try platform.createFont(.{
|
||||||
|
.source = .{ .path = "/usr/share/fonts/liberation/LiberationSans-Regular.ttf" },
|
||||||
|
}), {});
|
||||||
|
defer font.deinit();
|
||||||
|
|
||||||
|
// Create statspatch-based platform type for use by platform-agnostic code.
|
||||||
|
var zplatf = zl.platform.Platform.create(platform, .{});
|
||||||
|
|
||||||
|
const root = try Root.init(alloc);
|
||||||
|
defer root.deinit();
|
||||||
|
|
||||||
|
// Create an AttreebuteMap for the root widget.
|
||||||
|
// Attreebutes are tree-bound attributes, which are inherited to child widgets.
|
||||||
|
// They're backed by a type-indexed map (think ECS).
|
||||||
|
{
|
||||||
|
var attrs = zl.attreebute.AttreebuteMap.init();
|
||||||
|
errdefer attrs.deinit(alloc);
|
||||||
|
|
||||||
|
// This is required by various widgets in order to render text.
|
||||||
|
(try attrs.mod(alloc, zl.attreebute.CurrentFont)).* = .{ .font = &font };
|
||||||
|
|
||||||
|
// This sets a variety of theming-related attributes to the builtin catppuccin mocha theme.
|
||||||
|
try zl.Theme.catppuccin_mocha.apply(alloc, &attrs);
|
||||||
|
|
||||||
|
root.data.attreebutes = attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This links the widget tree (ie. correctly sets parent pointers and updates the platform).
|
||||||
|
// Some widgets also do initialization here.
|
||||||
|
try root.link(null, &zplatf);
|
||||||
|
|
||||||
|
// Run the main event loop until the application is closed;
|
||||||
|
try platform.run(root);
|
||||||
|
}
|
Loading…
Reference in a new issue