Compare commits
No commits in common. "9c29faaa810c6b25134fe76b54c2f740649175bd" and "b67de1ce01b03e9d121ab6093f0a46b44cf0b129" have entirely different histories.
9c29faaa81
...
b67de1ce01
12 changed files with 38 additions and 77 deletions
|
|
@ -285,14 +285,6 @@ zcatgui/
|
|||
|
||||
## HITOS RECIENTES
|
||||
|
||||
### Liquid UI V2 ✅ (2025-12-30)
|
||||
Sistema de transiciones de color suaves para paneles:
|
||||
- **ColorTransition**: 500ms (medio segundo) para transiciones perceptibles
|
||||
- **Mayor contraste**: Dark mode 4%/20% base, Light mode 1%/6% base
|
||||
- **requestAnimationFrame()**: Paneles solicitan redraw durante animación
|
||||
- Fondo de paneles "fluye" al cambiar focus
|
||||
→ Archivos: `render/animation.zig`, `core/style.zig`, `core/context.zig`
|
||||
|
||||
### IdleCompanion Widget ✅ (2025-12-30)
|
||||
Mascota animada que aparece tras inactividad del usuario:
|
||||
- Se asoma por bordes de paneles aleatorios
|
||||
|
|
|
|||
|
|
@ -102,10 +102,6 @@ pub const Context = struct {
|
|||
/// Used for idle detection (e.g., cursor stops blinking after inactivity)
|
||||
last_input_time_ms: u64 = 0,
|
||||
|
||||
/// Flag set by widgets that have ongoing animations (e.g., color transitions).
|
||||
/// Main loop should check this and request another frame if true.
|
||||
needs_animation_frame: bool = false,
|
||||
|
||||
/// Optional text measurement function (set by application with TTF font)
|
||||
/// Returns pixel width of text. If null, falls back to char_width * len.
|
||||
text_measure_fn: ?*const fn ([]const u8) u32 = null,
|
||||
|
|
@ -208,9 +204,6 @@ pub const Context = struct {
|
|||
// Focus system frame start
|
||||
self.focus.beginFrame();
|
||||
|
||||
// Reset animation request (set by widgets during draw)
|
||||
self.needs_animation_frame = false;
|
||||
|
||||
self.frame += 1;
|
||||
}
|
||||
|
||||
|
|
@ -370,19 +363,6 @@ pub const Context = struct {
|
|||
return @intCast(@max(time_until_toggle, 16)); // Minimum 16ms to avoid busy loop
|
||||
}
|
||||
|
||||
/// Request another animation frame (for color transitions, etc.).
|
||||
/// Widgets call this during draw when they have ongoing animations.
|
||||
/// Main loop should check needsAnimationFrame() after draw and schedule redraw.
|
||||
pub fn requestAnimationFrame(self: *Self) void {
|
||||
self.needs_animation_frame = true;
|
||||
}
|
||||
|
||||
/// Check if any widget requested an animation frame.
|
||||
/// Call after draw to determine if immediate redraw is needed.
|
||||
pub fn needsAnimationFrame(self: Self) bool {
|
||||
return self.needs_animation_frame;
|
||||
}
|
||||
|
||||
/// Determina si el cursor debe ser visible basado en tiempo de actividad.
|
||||
/// Usa parpadeo mientras hay actividad reciente, sólido cuando está idle.
|
||||
/// @param last_activity_time_ms: Última vez que hubo actividad (edición, input)
|
||||
|
|
|
|||
|
|
@ -1031,9 +1031,9 @@ pub fn derivePanelPalette(base: Color, mode: ThemeMode) PanelColorScheme {
|
|||
}
|
||||
|
||||
/// Derive palette for dark mode (dark backgrounds, light text)
|
||||
/// Z-Design V2 + Liquid UI: Mayor contraste para transiciones perceptibles
|
||||
/// - fondo_sin_focus: 4% base color (más oscuro, punto de partida bajo)
|
||||
/// - fondo_con_focus: 20% base color (brilla al ganar foco, destino alto)
|
||||
/// Z-Design V2: "Atmósfera, no fogonazo"
|
||||
/// - fondo_sin_focus: 7% base color (sutil identidad del panel)
|
||||
/// - fondo_con_focus: 15% base color (brilla al ganar foco)
|
||||
fn deriveDarkPalette(base: Color) PanelColorScheme {
|
||||
// Reference colors for dark mode
|
||||
const black = Color.soft_black; // RGB(17, 17, 20) - not pure black
|
||||
|
|
@ -1042,11 +1042,11 @@ fn deriveDarkPalette(base: Color) PanelColorScheme {
|
|||
const dark_border = Color.rgb(60, 60, 65);
|
||||
|
||||
return .{
|
||||
// Backgrounds: Liquid UI V2 - mayor recorrido para transición perceptible
|
||||
// Focus: 20% base, 80% black (destino luminoso)
|
||||
.fondo_con_focus = base.blendTowards(black, 80),
|
||||
// Sin focus: 4% base, 96% black (punto de partida oscuro)
|
||||
.fondo_sin_focus = base.blendTowards(black, 96),
|
||||
// Backgrounds: Z-Design V2 - panel siempre tiene su "identidad"
|
||||
// Focus: 15% base, 85% black (brilla más)
|
||||
.fondo_con_focus = base.blendTowards(black, 85),
|
||||
// Sin focus: 7% base, 93% black (sutil pero presente)
|
||||
.fondo_sin_focus = base.blendTowards(black, 93),
|
||||
|
||||
// Text: high contrast
|
||||
.datos = white,
|
||||
|
|
@ -1068,9 +1068,9 @@ fn deriveDarkPalette(base: Color) PanelColorScheme {
|
|||
}
|
||||
|
||||
/// Derive palette for light mode (light backgrounds, dark text)
|
||||
/// Z-Design V2 + Liquid UI: Mayor contraste para transiciones perceptibles
|
||||
/// - fondo_sin_focus: 1% base (casi blanco, punto de partida)
|
||||
/// - fondo_con_focus: 6% base (brilla al ganar foco, destino)
|
||||
/// Z-Design V2: Sincronizado con dark mode
|
||||
/// - fondo_sin_focus: 1% base (casi blanco, sutil identidad)
|
||||
/// - fondo_con_focus: 3% base (brilla al ganar foco)
|
||||
fn deriveLightPalette(base: Color) PanelColorScheme {
|
||||
// Reference colors for light mode
|
||||
const white = Color.soft_white; // RGB(250, 250, 252) - slight cool tint
|
||||
|
|
@ -1079,10 +1079,10 @@ fn deriveLightPalette(base: Color) PanelColorScheme {
|
|||
const light_border = Color.rgb(220, 220, 225);
|
||||
|
||||
return .{
|
||||
// Backgrounds: Liquid UI V2 - mayor recorrido para transición perceptible
|
||||
// Focus: 6% base, 94% white (destino más tintado)
|
||||
.fondo_con_focus = base.blendTowards(white, 94),
|
||||
// Sin focus: 1% base, 99% white (punto de partida neutro)
|
||||
// Backgrounds: Z-Design V2 - panel siempre tiene su "identidad"
|
||||
// Focus: 3% base, 97% white
|
||||
.fondo_con_focus = base.blendTowards(white, 97),
|
||||
// Sin focus: 1% base, 99% white (sutil pero presente)
|
||||
.fondo_sin_focus = base.blendTowards(white, 99),
|
||||
|
||||
// Text: high contrast
|
||||
|
|
@ -1151,11 +1151,11 @@ test "derivePanelPalette dark mode" {
|
|||
try std.testing.expectEqual(Color.laravel_red.g, palette.seleccion_fondo_con_focus.g);
|
||||
try std.testing.expectEqual(Color.laravel_red.b, palette.seleccion_fondo_con_focus.b);
|
||||
|
||||
// Background should be dark with visible tint (Liquid UI V2: 20% base color)
|
||||
// 80% blend towards black = dark with noticeable tint for transitions
|
||||
try std.testing.expect(palette.fondo_con_focus.r < 65); // Red tint visible
|
||||
try std.testing.expect(palette.fondo_con_focus.g < 35);
|
||||
try std.testing.expect(palette.fondo_con_focus.b < 35);
|
||||
// Background should be very dark (close to black, slight red tint)
|
||||
// 95% blend towards black = mostly black with 5% of base color
|
||||
try std.testing.expect(palette.fondo_con_focus.r < 35); // Red tint visible
|
||||
try std.testing.expect(palette.fondo_con_focus.g < 25);
|
||||
try std.testing.expect(palette.fondo_con_focus.b < 25);
|
||||
|
||||
// The red component should be slightly higher than G/B (tint visible)
|
||||
try std.testing.expect(palette.fondo_con_focus.r >= palette.fondo_con_focus.g);
|
||||
|
|
@ -1167,11 +1167,10 @@ test "derivePanelPalette light mode" {
|
|||
// Selection should be the full base color
|
||||
try std.testing.expectEqual(Color.laravel_blue.r, palette.seleccion_fondo_con_focus.r);
|
||||
|
||||
// Background should be light with visible tint (Liquid UI V2: 6% base color)
|
||||
// 94% blend towards white = light with noticeable tint for transitions
|
||||
try std.testing.expect(palette.fondo_con_focus.r > 230);
|
||||
try std.testing.expect(palette.fondo_con_focus.g > 235);
|
||||
try std.testing.expect(palette.fondo_con_focus.b > 240);
|
||||
// Background should be very light (close to white, slight blue tint)
|
||||
try std.testing.expect(palette.fondo_con_focus.r > 240);
|
||||
try std.testing.expect(palette.fondo_con_focus.g > 245);
|
||||
try std.testing.expect(palette.fondo_con_focus.b > 250);
|
||||
}
|
||||
|
||||
test "contrastTextColor" {
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ pub const ActionButtonsOpts = struct {
|
|||
can_delete: bool = false,
|
||||
/// Ancho de cada boton
|
||||
button_width: u32 = 80,
|
||||
/// Alto de cada boton (Z-Design V2: reducido de 28 a 22px)
|
||||
button_height: u32 = 22,
|
||||
/// Alto de cada boton
|
||||
button_height: u32 = 28,
|
||||
/// Espacio entre botones
|
||||
spacing: i32 = 8,
|
||||
/// Offset X desde el borde izquierdo del panel
|
||||
|
|
@ -96,8 +96,8 @@ pub const NavButtonsOpts = struct {
|
|||
can_last: bool = false,
|
||||
/// Ancho de cada boton
|
||||
button_width: u32 = 40,
|
||||
/// Alto de cada boton (Z-Design V2: reducido de 28 a 22px)
|
||||
button_height: u32 = 22,
|
||||
/// Alto de cada boton
|
||||
button_height: u32 = 28,
|
||||
/// Offset X desde el borde izquierdo del panel
|
||||
x_offset: i32 = 8,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -704,8 +704,8 @@ pub const ColorTransition = struct {
|
|||
current: Style.Color = Style.Color.rgb(0, 0, 0),
|
||||
/// Is initialized with a color?
|
||||
initialized: bool = false,
|
||||
/// Transition duration in milliseconds (500ms = Liquid UI feel)
|
||||
duration_ms: f32 = 500.0,
|
||||
/// Transition duration in milliseconds
|
||||
duration_ms: f32 = 200.0,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
|
|
|
|||
|
|
@ -170,16 +170,6 @@ pub fn advancedTableRect(
|
|||
var memory_ds = MemoryDataSource.init(table_state, table_schema.columns);
|
||||
const data_src = memory_ds.toDataSource();
|
||||
|
||||
// Z-Design: Pintar fondo del área de contenido ANTES de las filas
|
||||
// Esto asegura que tablas vacías o con pocas filas no muestren negro
|
||||
ctx.pushCommand(Command.rect(
|
||||
bounds.x,
|
||||
bounds.y + @as(i32, @intCast(header_h)),
|
||||
bounds.w,
|
||||
content_h,
|
||||
colors.row_normal,
|
||||
));
|
||||
|
||||
// Construir RowRenderColors manualmente (los dos TableColors son tipos diferentes)
|
||||
const render_colors = table_core.RowRenderColors{
|
||||
.row_normal = colors.row_normal,
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ pub fn drawScrollbar(
|
|||
const total_rows = table_state.getRowCount();
|
||||
if (total_rows == 0) return;
|
||||
|
||||
const scrollbar_w: u32 = 14; // Z-Design V2: más ancho para mejor visibilidad
|
||||
const scrollbar_w: u32 = 12;
|
||||
const header_h: u32 = if (config.show_headers) config.header_height else 0;
|
||||
const scrollbar_h = bounds.h -| header_h;
|
||||
|
||||
|
|
|
|||
|
|
@ -346,7 +346,7 @@ pub fn gridRect(
|
|||
}
|
||||
|
||||
fn drawScrollbar(ctx: *Context, bounds: Layout.Rect, scroll_y: i32, total_height: u32, visible_height: u32, colors: Colors) void {
|
||||
const scrollbar_width: u32 = 14; // Z-Design V2: más ancho
|
||||
const scrollbar_width: u32 = 8;
|
||||
const scrollbar_x = bounds.x + @as(i32, @intCast(bounds.w)) - @as(i32, @intCast(scrollbar_width)) - 2;
|
||||
const scrollbar_y = bounds.y + 2;
|
||||
const scrollbar_h = bounds.h -| 4;
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ pub fn listRect(
|
|||
|
||||
// Draw scrollbar if needed
|
||||
if (items.len > visible_count) {
|
||||
const scrollbar_w: u32 = 14; // Z-Design V2: más ancho para mejor visibilidad
|
||||
const scrollbar_w: u32 = 8;
|
||||
const scrollbar_x = bounds.x + @as(i32, @intCast(bounds.w)) - @as(i32, @intCast(scrollbar_w + 1));
|
||||
|
||||
// Scrollbar track
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ pub fn drawScrollbar(
|
|||
) void {
|
||||
_ = config;
|
||||
|
||||
const scrollbar_w: u32 = 14; // Z-Design V2: más ancho
|
||||
const scrollbar_w: u32 = 12;
|
||||
const header_h: u32 = 28; // Assume header
|
||||
|
||||
const track_x = bounds.right() - @as(i32, @intCast(scrollbar_w));
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ pub fn drawFilterBar(
|
|||
const chip_h: u32 = 22;
|
||||
const chip_padding: i32 = 10;
|
||||
const chip_spacing: i32 = 6;
|
||||
const chip_radius: u8 = 4; // Z-Design V2: consistente con botones
|
||||
const chip_radius: u8 = 11;
|
||||
const clear_btn_w: u32 = 22;
|
||||
|
||||
// Background
|
||||
|
|
@ -446,7 +446,7 @@ pub fn drawScrollbar(
|
|||
total_rows: usize,
|
||||
colors: *const VirtualAdvancedTableConfig.Colors,
|
||||
) void {
|
||||
const scrollbar_w: u32 = 14; // Z-Design V2: más ancho para mejor visibilidad
|
||||
const scrollbar_w: u32 = 12;
|
||||
const content_h = bounds.h -| header_h -| footer_h;
|
||||
|
||||
table_core.drawVerticalScrollbar(ctx, .{
|
||||
|
|
|
|||
|
|
@ -129,8 +129,8 @@ pub const VirtualScrollState = struct {
|
|||
pub const VirtualScrollConfig = struct {
|
||||
/// Show scrollbar
|
||||
show_scrollbar: bool = true,
|
||||
/// Scrollbar width (Z-Design V2: 14px para mejor visibilidad)
|
||||
scrollbar_width: u16 = 14,
|
||||
/// Scrollbar width
|
||||
scrollbar_width: u16 = 12,
|
||||
/// Overscan (render extra items above/below viewport)
|
||||
overscan: u16 = 2,
|
||||
/// Enable smooth scrolling
|
||||
|
|
|
|||
Loading…
Reference in a new issue