improve layout and scaling

This commit is contained in:
Jeeves 2025-05-31 21:30:26 -06:00
parent 52a4b4ee59
commit 984b70a39d

View file

@ -102,11 +102,15 @@ pub const Scales = struct {
item_title_font_size: f32,
item_subtitle_font_size: f32,
column_icon_padding_top_curve: Curve,
column_item_spacing_curve: Curve,
column_selected_item_icon_scale_curve: Curve,
column_title_font_size: f32,
column_position_center: raylib.Vector2,
column_position_spacing: f32,
column_item_spacing: f32,
column_selected_item_icon_scale: f32,
column_icon_padding_top: f32,
column_item_after_start: f32,
column_item_before_start: f32,
@ -115,6 +119,7 @@ pub const Scales = struct {
self.font_size_curve = Curve{ .points = &[_]Curve.Point{
.{ .x = 0, .y = 18 },
.{ .x = 0.25, .y = 18 },
.{ .x = 1, .y = 26 },
} };
@ -123,12 +128,26 @@ pub const Scales = struct {
.{ .x = 1, .y = 80 },
} };
self.column_icon_padding_top_curve = Curve{ .points = &[_]Curve.Point{
.{ .x = 0, .y = 0 },
.{ .x = 0.25, .y = 0 },
.{ .x = 1, .y = 16 },
} };
self.column_item_spacing_curve = Curve{ .points = &[_]Curve.Point{
.{ .x = 0, .y = 16, .right_tangent = -90 },
.{ .x = 0, .y = 10, .right_tangent = -90 },
.{ .x = 0.25, .y = 0 },
.{ .x = 1, .y = 0 },
} };
self.column_selected_item_icon_scale_curve = Curve{
.points = &[_]Curve.Point{
.{ .x = 0, .y = 1 },
.{ .x = 0.25, .y = 1.5 },
.{ .x = 1, .y = 2 },
},
};
self.recalculate();
return self;
}
@ -152,12 +171,13 @@ pub const Scales = struct {
};
self.column_position_spacing = 64;
self.column_item_spacing = self.column_item_spacing_curve.sample(height);
self.column_selected_item_icon_scale = self.column_selected_item_icon_scale_curve.sample(height);
self.column_icon_padding_top = self.column_icon_padding_top_curve.sample(height);
self.column_item_after_start = self.column_position_center.y + self.icon_height + self.column_title_font_size + 12;
self.column_item_before_start = self.column_position_center.y;
self.column_item_before_start = self.column_position_center.y - self.column_icon_padding_top;
}
};
// TODO selected item scale up
// TODO item actions
// TODO item groups
// TODO item group sort
@ -205,7 +225,6 @@ pub const Column = struct {
.align_h = .center,
};
for (self.items.items) |item| item.update();
for (self.items.items) |item| item.draw();
icon.draw();
@ -239,22 +258,24 @@ pub const Column = struct {
fn refresh(self: *Column, comptime animate: bool) void {
var y = scales.column_item_before_start - (scales.icon_height + scales.column_item_spacing) * @as(f32, @floatFromInt(self.selected));
for (self.items.items[0..self.selected]) |item| {
item.setPosition(animate, .{ .x = scales.column_position_center.x, .y = y });
item.position.set(animate, .{ .x = scales.column_position_center.x, .y = y });
item.icon_scale.set(animate, 1.0);
y += scales.icon_height + scales.column_item_spacing;
}
y = scales.column_item_after_start;
for (self.items.items[self.selected..]) |item| {
item.setPosition(animate, .{ .x = scales.column_position_center.x, .y = y });
y += scales.icon_height + scales.column_item_spacing;
for (self.items.items[self.selected..], self.selected..) |item, i| {
const icon_scale = if (i == self.selected) scales.column_selected_item_icon_scale else 1.0;
item.position.set(animate, .{ .x = scales.column_position_center.x, .y = y });
item.icon_scale.set(animate, icon_scale);
y += scales.icon_height * icon_scale + scales.column_item_spacing + (std.math.pow(f32, 4.0, icon_scale) - 4.0);
}
}
};
// TODO animated item icon scale
// TODO animated text fade in/out
pub const Item = struct {
position: raylib.Vector2 = .{ .x = 0, .y = 0 },
animator: Animator,
position: Tweener(raylib.Vector2, .{}) = .{},
icon_scale: Tweener(f32, 1.0) = .{},
icon: raylib.Texture2D,
title: []const u8,
@ -266,21 +287,20 @@ pub const Item = struct {
.icon = texture,
.title = title,
.subtitle = subtitle,
.animator = .{
.start_position = .{},
.target_position = .{},
},
};
}
pub fn draw(self: *Item) void {
const position = self.position.update();
const icon_scale = self.icon_scale.update();
var icon = Image{
.texture = self.icon,
.box = .{
.x = self.position.x - scales.icon_width / 2.0 + 67.0 / 2.0,
.y = self.position.y,
.w = scales.icon_width,
.h = scales.icon_height,
.x = position.x - scales.icon_width / 2.0 * (icon_scale - 1),
.y = position.y,
.w = scales.icon_width * icon_scale,
.h = scales.icon_height * icon_scale,
},
};
@ -314,38 +334,47 @@ pub const Item = struct {
title.draw();
subtitle.draw();
}
pub const Animator = struct {
time: f32 = 0,
start_position: raylib.Vector2,
target_position: raylib.Vector2,
fn easeOutExpo(self: Animator, length: f32) f32 {
return 1.0 - std.math.pow(f32, 2, -10 * std.math.clamp(self.time / length, 0.0, 1.0));
}
};
fn update(self: *Item) void {
self.animator.time += raylib.GetFrameTime();
self.position = raylib.Vector2Lerp(
self.animator.start_position,
self.animator.target_position,
self.animator.easeOutExpo(0.333),
);
}
pub fn setPosition(self: *Item, comptime animate: bool, position: raylib.Vector2) void {
if (animate) {
self.animator.time = 0;
self.animator.start_position = self.position;
self.animator.target_position = position;
} else {
self.animator.start_position = position;
self.animator.target_position = position;
}
}
};
pub fn Tweener(comptime T: type, comptime default: T) type {
return struct {
time: f32 = 0.0,
length: f32 = 0.333,
start: T = default,
target: T = default,
current: T = default,
fn set(self: *Self, comptime animate: bool, value: T) void {
if (animate) {
self.time = 0;
self.start = self.current;
self.target = value;
} else {
self.start = value;
self.target = value;
self.current = value;
}
}
fn update(self: *Self) T {
self.time += raylib.GetFrameTime();
self.current = switch (T) {
f32 => std.math.lerp(self.start, self.target, self.easeOutExpo()),
raylib.Vector2 => raylib.Vector2Lerp(self.start, self.target, self.easeOutExpo()),
else => @compileError("no lerp function for type " ++ @typeName(T)),
};
return self.current;
}
fn easeOutExpo(self: Self) f32 {
return 1.0 - std.math.pow(f32, 2, -10 * std.math.clamp(self.time / self.length, 0.0, 1.0));
}
const Self = @This();
};
}
/// Draws the dynamic gradient background.
// TODO shift based on time of day
// TODO image wallpaper