feat: implement builtin themes and make Labels use attreebutes

This commit is contained in:
LordMZTE 2024-01-13 15:04:08 +01:00
parent ab4a4395d8
commit f84d6be6be
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
6 changed files with 141 additions and 14 deletions

View file

@ -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)

97
src/Theme.zig Normal file
View file

@ -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;
}

View file

@ -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 {

View file

@ -0,0 +1,4 @@
//! Style options for Labels.
const Style = @import("../text/Style.zig");
font_style: Style,

View file

@ -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.

View file

@ -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),
}