diff --git a/src/core/context.zig b/src/core/context.zig index 2307224..4e806b6 100644 --- a/src/core/context.zig +++ b/src/core/context.zig @@ -115,6 +115,9 @@ pub const Context = struct { /// Default character width for fallback measurement (bitmap fonts) char_width: u32 = 8, + /// Default character height for vertical centering (TTF fonts typically 1.2-1.5x width) + char_height: u32 = 14, + /// Idle timeout for cursor blinking (ms). After this time without input, /// cursor becomes solid and no animation frames are needed. pub const CURSOR_IDLE_TIMEOUT_MS: u64 = 20000; @@ -341,6 +344,11 @@ pub const Context = struct { self.char_width = width; } + /// Set character height for vertical centering (TTF fonts) + pub fn setCharHeight(self: *Self, height: u32) void { + self.char_height = height; + } + /// Get current time in milliseconds pub fn getTime(self: Self) u64 { return self.current_time_ms; diff --git a/src/core/style.zig b/src/core/style.zig index 02aebbc..7dd0937 100644 --- a/src/core/style.zig +++ b/src/core/style.zig @@ -1382,13 +1382,14 @@ pub fn derivePanelFrameColors(base: Color) DerivedPanelColors { // Blend fijo: 20% color con focus, 12% sin focus const focus_bg = base.blendTowards(black, 80); // 20% color - // Título adaptativo: contraste máximo según luminosidad del fondo - // Fondos oscuros (L < 0.5) → blanco teñido, claros → negro teñido + // Título: BLANCO con tinte sutil del color base para identidad + // El contraste viene del blanco, el tinte da coherencia visual + // Fondos oscuros → blanco teñido, claros → negro teñido const bg_luminance = focus_bg.perceptualLuminance(); const title_color = if (bg_luminance < 0.5) - base.lightenHsl(90) // Blanco teñido (mantiene tono del panel) + Color.soft_white.blendTowards(base, 15) // 85% blanco + 15% tinte del panel else - base.darkenHsl(90); // Negro teñido + Color.soft_black.blendTowards(base, 15); // 85% negro + 15% tinte return .{ .focus_bg = focus_bg, diff --git a/src/widgets/button.zig b/src/widgets/button.zig index ec2273a..3043346 100644 --- a/src/widgets/button.zig +++ b/src/widgets/button.zig @@ -132,12 +132,13 @@ pub fn buttonRect(ctx: *Context, bounds: Layout.Rect, text: []const u8, config: } // Draw text centered (con offset +1px cuando está pulsado = "se hunde") - // Z-Design V2: usar métricas del contexto para centrado correcto con TTF + // Z-Design V5: usar char_height real + offset -1 para compensar efecto 3D const text_width = ctx.measureText(text); - const char_height = ctx.char_width; // Para fuentes cuadradas, height ≈ width + const char_height = ctx.char_height; const press_offset: i32 = if (pressed) 1 else 0; + const visual_adjust: i32 = -1; // Compensa efecto 3D del bisel que "hunde" visualmente const text_x = bounds.x + @as(i32, @intCast((bounds.w -| text_width) / 2)) + press_offset; - const text_y = bounds.y + @as(i32, @intCast((bounds.h -| char_height) / 2)) + press_offset; + const text_y = bounds.y + @as(i32, @intCast((bounds.h -| char_height) / 2)) + press_offset + visual_adjust; ctx.pushCommand(Command.text(text_x, text_y, text, fg_color)); @@ -247,12 +248,13 @@ pub fn buttonStatefulRect( } // Draw text centered (con offset +1px cuando está pulsado = "se hunde") - // Z-Design V2: usar métricas del contexto para centrado correcto con TTF + // Z-Design V5: usar char_height real + offset -1 para compensar efecto 3D const text_width = ctx.measureText(text); - const char_height = ctx.char_width; // Para fuentes cuadradas, height ≈ width + const char_height = ctx.char_height; const press_offset: i32 = if (pressed) 1 else 0; + const visual_adjust: i32 = -1; // Compensa efecto 3D del bisel que "hunde" visualmente const text_x = bounds.x + @as(i32, @intCast((bounds.w -| text_width) / 2)) + press_offset; - const text_y = bounds.y + @as(i32, @intCast((bounds.h -| char_height) / 2)) + press_offset; + const text_y = bounds.y + @as(i32, @intCast((bounds.h -| char_height) / 2)) + press_offset + visual_adjust; ctx.pushCommand(Command.text(text_x, text_y, text, fg_color));