From f84d6be6be10724f8baba1cf51da8fd03701d84c Mon Sep 17 00:00:00 2001 From: LordMZTE Date: Sat, 13 Jan 2024 15:04:08 +0100 Subject: [PATCH] feat: implement builtin themes and make Labels use attreebutes --- README.md | 2 +- src/Theme.zig | 97 ++++++++++++++++++++++++++++++++++ src/attreebute.zig | 2 + src/attreebutes/LabelStyle.zig | 4 ++ src/main.zig | 2 + src/widgets/Label.zig | 48 ++++++++++++----- 6 files changed, 141 insertions(+), 14 deletions(-) create mode 100644 src/Theme.zig create mode 100644 src/attreebutes/LabelStyle.zig diff --git a/README.md b/README.md index 6de8d4e..cf51e88 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ If you'd like to suggest a new feature or design change, please open an issue fi - [ ] Scrolled Pane - [x] Focus System - [x] Theming - - [ ] Built-in themes + - [x] Built-in themes - [ ] Layout overflow handling - [x] Logo - [x] Treevents (Tree-Bound, downwards events) diff --git a/src/Theme.zig b/src/Theme.zig new file mode 100644 index 0000000..4e50824 --- /dev/null +++ b/src/Theme.zig @@ -0,0 +1,97 @@ +//! A structure containing all theme-related values of zenolith's builtin widgets. +//! This allows for convenient handling of themes for default widgets. +const std = @import("std"); + +const AttreebuteMap = @import("attreebute.zig").AttreebuteMap; +const BoxStyle = @import("attreebutes/BoxStyle.zig"); +const ButtonStyle = @import("attreebutes/ButtonStyle.zig"); +const Color = @import("Color.zig"); +const LabelStyle = @import("attreebutes/LabelStyle.zig"); + +pub const catppuccin_latte = Theme{ + .box = .{ .background = .{ .fill = Color.fromInt(0xeff1f5ff) } }, + .button = .{ + .background = .{ .stroked = .{ + .stroke = Color.fromInt(0xe64553ff), + .fill = Color.fromInt(0xccd0daff), + .width = 4, + } }, + .background_hovered = .{ .stroked = .{ + .stroke = Color.fromInt(0xe64553ff), + .fill = Color.fromInt(0xbcc0ccff), + .width = 2, + } }, + .padding = 4, + .font_style = .{ .color = Color.fromInt(0x4c4f69ff) }, + }, + .label = .{ .font_style = .{ .color = Color.fromInt(0x4c4f69ff) } }, +}; + +pub const catppuccin_frappe = Theme{ + .box = .{ .background = .{ .fill = Color.fromInt(0x303446ff) } }, + .button = .{ + .background = .{ .stroked = .{ + .stroke = Color.fromInt(0xea999cff), + .fill = Color.fromInt(0x414559ff), + .width = 4, + } }, + .background_hovered = .{ .stroked = .{ + .stroke = Color.fromInt(0xea999cff), + .fill = Color.fromInt(0x51576dff), + .width = 2, + } }, + .padding = 4, + .font_style = .{ .color = Color.fromInt(0xc6d0f5ff) }, + }, + .label = .{ .font_style = .{ .color = Color.fromInt(0xc6d0f5ff) } }, +}; + +pub const catppuccin_macchiato = Theme{ + .box = .{ .background = .{ .fill = Color.fromInt(0x24273aff) } }, + .button = .{ + .background = .{ .stroked = .{ + .stroke = Color.fromInt(0xee99a0ff), + .fill = Color.fromInt(0x363a4fff), + .width = 4, + } }, + .background_hovered = .{ .stroked = .{ + .stroke = Color.fromInt(0xee99a0ff), + .fill = Color.fromInt(0x09494d64ff), + .width = 2, + } }, + .padding = 4, + .font_style = .{ .color = Color.fromInt(0xcad3f5ff) }, + }, + .label = .{ .font_style = .{ .color = Color.fromInt(0xcad3f5ff) } }, +}; + +pub const catppuccin_mocha = Theme{ + .box = .{ .background = .{ .fill = Color.fromInt(0x1e1e2eff) } }, + .button = .{ + .background = .{ .stroked = .{ + .stroke = Color.fromInt(0xeba0acff), + .fill = Color.fromInt(0x313244ff), + .width = 4, + } }, + .background_hovered = .{ .stroked = .{ + .stroke = Color.fromInt(0xeba0acff), + .fill = Color.fromInt(0x45475aff), + .width = 2, + } }, + .padding = 4, + .font_style = .{ .color = Color.fromInt(0xcdd6f4ff) }, + }, + .label = .{ .font_style = .{ .color = Color.fromInt(0xcdd6f4ff) } }, +}; + +box: BoxStyle, +button: ButtonStyle, +label: LabelStyle, + +const Theme = @This(); + +pub fn apply(self: Theme, alloc: std.mem.Allocator, map: *AttreebuteMap) !void { + (try map.mod(alloc, BoxStyle)).* = self.box; + (try map.mod(alloc, ButtonStyle)).* = self.button; + (try map.mod(alloc, LabelStyle)).* = self.label; +} diff --git a/src/attreebute.zig b/src/attreebute.zig index a79e397..6bd4de8 100644 --- a/src/attreebute.zig +++ b/src/attreebute.zig @@ -18,11 +18,13 @@ test { _ = BoxStyle; _ = ButtonStyle; _ = CurrentFont; + _ = LabelStyle; } pub const BoxStyle = @import("attreebutes/BoxStyle.zig"); pub const ButtonStyle = @import("attreebutes/ButtonStyle.zig"); pub const CurrentFont = @import("attreebutes/CurrentFont.zig"); +pub const LabelStyle = @import("attreebutes/LabelStyle.zig"); pub const AttreebuteMap = struct { const Context = struct { diff --git a/src/attreebutes/LabelStyle.zig b/src/attreebutes/LabelStyle.zig new file mode 100644 index 0000000..8cdeea0 --- /dev/null +++ b/src/attreebutes/LabelStyle.zig @@ -0,0 +1,4 @@ +//! Style options for Labels. +const Style = @import("../text/Style.zig"); + +font_style: Style, diff --git a/src/main.zig b/src/main.zig index cb21ed9..3b19ce2 100644 --- a/src/main.zig +++ b/src/main.zig @@ -19,6 +19,7 @@ test { _ = widget; _ = Color; + _ = Theme; _ = WidgetIter; } @@ -35,6 +36,7 @@ pub const util = @import("util.zig"); pub const widget = @import("widget.zig"); pub const Color = @import("Color.zig"); +pub const Theme = @import("Theme.zig"); pub const WidgetIter = @import("WidgetIter.zig"); /// List of the default widget implementations included with Zenolith. diff --git a/src/widgets/Label.zig b/src/widgets/Label.zig index 925f4d7..20e94cb 100644 --- a/src/widgets/Label.zig +++ b/src/widgets/Label.zig @@ -1,5 +1,4 @@ -//! A simple text label with a given color and size. -// TODO: use CurrentFont +//! A simple text label displaying given text. Font style is controlled via attreebutes. const std = @import("std"); const font = @import("../text/font.zig"); @@ -7,42 +6,65 @@ const treev = @import("../treevent.zig"); const layout = @import("../layout.zig"); const Color = @import("../Color.zig"); +const CurrentFont = @import("../attreebutes/CurrentFont.zig"); +const LabelStyle = @import("../attreebutes/LabelStyle.zig"); const Span = @import("../text/Span.zig"); const Widget = @import("../widget.zig").Widget; -font: *font.Font, -span: Span, +/// The text the Label is to be initialized with. +/// This will not change if the text is updated afterwards. +/// The reason this exists is that the widget is not linked into a tree and +/// thus has no font yet when being constructed. +initial_text: []const u8, + +/// This is the inner span. Initialized upon the widget being linked. +span: ?Span, const Label = @This(); -pub fn init(alloc: std.mem.Allocator, opts: Span.InitOptions) !*Widget { +pub fn init(alloc: std.mem.Allocator, text: []const u8) !*Widget { const self = Label{ - .font = opts.font, - .span = try Span.init(alloc, opts), + .initial_text = text, + .span = null, }; - errdefer self.span.deinit(); return try Widget.init(alloc, self); } pub fn deinit(self: *Label, selfw: *Widget) void { _ = selfw; - self.span.deinit(); + if (self.span) |s| s.deinit(); } pub fn treevent(self: *Label, selfw: *Widget, tv: anytype) !void { switch (@TypeOf(tv)) { + treev.Link => { + try tv.dispatch(selfw); + + const curfont = selfw.getAttreebute(CurrentFont) orelse + @panic("Labels require the CurrentFont attreebute!"); + self.span = try Span.init(selfw.data.allocator, .{ + .font = curfont.font, + .text = self.initial_text, + }); + }, treev.LayoutSize => { selfw.data.size = tv.constraints.clamp(.{ - .width = self.span.baseline_width, - .height = self.span.font.yOffset(self.span.style.size), + .width = self.span.?.baseline_width, + .height = self.span.?.font.yOffset(self.span.?.style.size), }); }, treev.Draw => { + const style = selfw.getAttreebute(LabelStyle) orelse + @panic("The Button widget must have the ButtonStyle attreebute set!"); + + self.span.?.style = style.font_style; + self.span.?.layout(); + try tv.painter.span(selfw.data.position.add(.{ .x = 0, - .y = self.span.font.yOffset(self.span.style.size) - self.span.baseline_y, - }), self.span); + .y = self.span.?.font.yOffset(self.span.?.style.size) - self.span.?.baseline_y, + }), self.span.?); }, else => try tv.dispatch(selfw), }