diff --git a/src/core/context.zig b/src/core/context.zig index 4e806b6..ea9362a 100644 --- a/src/core/context.zig +++ b/src/core/context.zig @@ -665,14 +665,18 @@ pub const Context = struct { break :blk config.border_color; }; - // Título adaptativo: siempre alta legibilidad (blanco teñido sobre fondo oscuro) + // Título adaptativo: siempre alta legibilidad + // - Si hay title_color explícito: usarlo + // - Si hay base_color: derivar con tinte sutil + // - Si no hay ninguno: contrastTextColor sobre focus_bg (blanco/negro según fondo) const title_color: ?Style.Color = blk: { if (config.title_color) |tc| break :blk tc; if (config.base_color) |base| { const derived = Style.derivePanelFrameColors(base); break :blk derived.title_color; // Siempre legible, focus o no } - break :blk border_color; + // FIX: usar contraste sobre fondo, NO border_color (que es oscuro) + break :blk Style.contrastTextColor(focus_bg); }; // 1. Calculate target color and update transition @@ -707,11 +711,12 @@ pub const Context = struct { self.pushCommand(Command.rectOutline(rect.x, rect.y, rect.w, rect.h, border)); } - // 5. Draw title if specified (margen 10,5 para aire visual) + // 5. Draw title if specified (margen 28,5 para dejar espacio al semáforo de estado) + // El semáforo de DetailPanelBase se dibuja en (x+8, y+4) con 12x12px if (config.title) |title| { if (title_color) |tc| { self.pushCommand(.{ .text = .{ - .x = rect.x + 10, + .x = rect.x + 28, .y = rect.y + 5, .text = title, .color = tc, diff --git a/src/panels/detail/base.zig b/src/panels/detail/base.zig index 9606fe3..91bb6cc 100644 --- a/src/panels/detail/base.zig +++ b/src/panels/detail/base.zig @@ -225,32 +225,37 @@ pub const DetailPanelBase = struct { // Dibujo: Semaforo // ========================================================================= - /// Dibuja el semaforo de estado en la esquina superior izquierda del rect. + /// Dibuja el semaforo de estado en la esquina superior derecha del rect. /// - /// Muestra: - /// - Cuadrado de color (12x12) indicando el estado - /// - Texto del estado en la esquina superior derecha + /// Layout: "[N] Título Viendo ■" + /// - Texto del estado primero (ej: "Viendo", "Editando", "Guardando...") + /// - Cuadrado de color (12x12) como remate visual al final pub fn drawSemaphore(self: *Self, ctx: *Context, rect: Rect) void { const state_color = self.state.getColor(); const state_text = self.state.getText(); - // Cuadrado de color (indicador visual) - ctx.pushCommand(.{ .rect = .{ - .x = rect.x + 8, - .y = rect.y + 4, - .w = 12, - .h = 12, - .color = state_color, - } }); + // Posiciones: texto + cuadrado alineados a la derecha + // Cuadrado: 12x12 con 8px de margen del borde derecho + const square_x = rect.x + @as(i32, @intCast(rect.w)) - 20; + // Texto: antes del cuadrado, con 8px de separación + const text_x = rect.x + @as(i32, @intCast(rect.w)) - 100; - // Texto del estado (esquina derecha) - const text_x = rect.x + @as(i32, @intCast(rect.w)) - 80; + // 1. Texto del estado ctx.pushCommand(.{ .text = .{ .x = text_x, .y = rect.y + 4, .text = state_text, .color = state_color, } }); + + // 2. Cuadrado de color (indicador visual al final) + ctx.pushCommand(.{ .rect = .{ + .x = square_x, + .y = rect.y + 4, + .w = 12, + .h = 12, + .color = state_color, + } }); } // ========================================================================= diff --git a/src/widgets/button.zig b/src/widgets/button.zig index 3043346..da57a68 100644 --- a/src/widgets/button.zig +++ b/src/widgets/button.zig @@ -132,11 +132,11 @@ 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 V5: usar char_height real + offset -1 para compensar efecto 3D + // Z-Design V5: usar char_height real + offset +2 para compensar baseline TTF const text_width = ctx.measureText(text); 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 visual_adjust: i32 = 2; // Compensa baseline de fuente TTF (DroidSans 14pt) 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 + visual_adjust; @@ -248,11 +248,11 @@ pub fn buttonStatefulRect( } // Draw text centered (con offset +1px cuando está pulsado = "se hunde") - // Z-Design V5: usar char_height real + offset -1 para compensar efecto 3D + // Z-Design V5: usar char_height real + offset +2 para compensar baseline TTF const text_width = ctx.measureText(text); 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 visual_adjust: i32 = 2; // Compensa baseline de fuente TTF (DroidSans 14pt) 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 + visual_adjust;