diff --git a/src/core/context.zig b/src/core/context.zig index c463f91..ed439a6 100644 --- a/src/core/context.zig +++ b/src/core/context.zig @@ -495,17 +495,104 @@ pub const Context = struct { } // ========================================================================= - // High-level Drawing Helpers + // High-level Drawing Helpers - Bevel System // ========================================================================= - /// Draw a rectangle with 3D bevel effect - /// Creates illusion of depth with light from top-left - /// - Top/Left edges: lighter (raised) - /// - Bottom/Right edges: darker (shadow) - /// Note: Bevel is drawn INSIDE the rect (inset by 1px) to not overlap border - pub fn drawBeveledRect(self: *Self, x: i32, y: i32, w: u32, h: u32, base_color: Style.Color) void { - const light = base_color.lightenHsl(10); - const dark = base_color.darkenHsl(15); + /// Configuración de estilo de bisel 3D + /// Permite control fino sobre el efecto visual + pub const BevelStyle = struct { + /// Tipo de efecto 3D + effect: Effect = .raised, + + /// Offset del bisel en pixels (0 = borde exterior, 1 = interior) + inset: u8 = 1, + + /// Intensidad de la luz (0-100, típico 10-20) + light_top: u8 = 10, + light_left: u8 = 10, + + /// Intensidad de la sombra (0-100, típico 10-20) + dark_bottom: u8 = 15, + dark_right: u8 = 15, + + /// Usar HSL (true) o RGB (false) para calcular colores + use_hsl: bool = true, + + pub const Effect = enum { + raised, // Elevado: luz arriba/izq, sombra abajo/der + sunken, // Hundido: sombra arriba/izq, luz abajo/der + }; + + // === PRESETS COMUNES === + + /// Bisel elevado interior (default, para botones) + pub const raised_inset = BevelStyle{}; + + /// Bisel hundido interior (para botones presionados) + pub const sunken_inset = BevelStyle{ .effect = .sunken }; + + /// Bisel elevado exterior (estilo Windows 95/StatusLine) + pub const raised_outer = BevelStyle{ + .inset = 0, + .use_hsl = false, + .light_top = 15, + .light_left = 12, + .dark_bottom = 15, + .dark_right = 12, + }; + + /// Bisel hundido exterior (estilo Windows 95/StatusLine) + pub const sunken_outer = BevelStyle{ + .effect = .sunken, + .inset = 0, + .use_hsl = false, + .light_top = 10, + .light_left = 8, + .dark_bottom = 20, + .dark_right = 15, + }; + }; + + /// Dibuja un rectángulo con efecto bisel 3D configurable + /// + /// Ejemplo de uso: + /// ```zig + /// // Bisel elevado (default) + /// ctx.drawBevel(x, y, w, h, color, .{}); + /// + /// // Bisel hundido exterior (estilo StatusLine) + /// ctx.drawBevel(x, y, w, h, color, BevelStyle.sunken_outer); + /// + /// // Personalizado + /// ctx.drawBevel(x, y, w, h, color, .{ .effect = .raised, .inset = 0, .light_top = 20 }); + /// ``` + pub fn drawBevel(self: *Self, x: i32, y: i32, w: u32, h: u32, base_color: Style.Color, style: BevelStyle) void { + // Calcular colores de luz y sombra + const light_top = if (style.use_hsl) + base_color.lightenHsl(@floatFromInt(style.light_top)) + else + base_color.lighten(style.light_top); + + const light_left = if (style.use_hsl) + base_color.lightenHsl(@floatFromInt(style.light_left)) + else + base_color.lighten(style.light_left); + + const dark_bottom = if (style.use_hsl) + base_color.darkenHsl(@floatFromInt(style.dark_bottom)) + else + base_color.darken(style.dark_bottom); + + const dark_right = if (style.use_hsl) + base_color.darkenHsl(@floatFromInt(style.dark_right)) + else + base_color.darken(style.dark_right); + + // Determinar colores según efecto (raised vs sunken) + const top_color = if (style.effect == .raised) light_top else dark_bottom; + const left_color = if (style.effect == .raised) light_left else dark_right; + const bottom_color = if (style.effect == .raised) dark_bottom else light_top; + const right_color = if (style.effect == .raised) dark_right else light_left; // Main fill self.pushCommand(.{ .rect = .{ @@ -516,102 +603,57 @@ pub const Context = struct { .color = base_color, } }); - // Bevel inset by 1px to stay inside border - const inner_w = if (w > 2) w - 2 else 1; - const inner_h = if (h > 2) h - 2 else 1; + // Calcular dimensiones según inset + const offset: i32 = @intCast(style.inset); + const size_reduction: u32 = @as(u32, style.inset) * 2; + const inner_w = if (w > size_reduction) w - size_reduction else 1; + const inner_h = if (h > size_reduction) h - size_reduction else 1; - // Top edge (light) - inset + // Top edge self.pushCommand(.{ .rect = .{ - .x = x + 1, - .y = y + 1, + .x = x + offset, + .y = y + offset, .w = inner_w, .h = 1, - .color = light, + .color = top_color, } }); - // Left edge (light) - inset + // Left edge self.pushCommand(.{ .rect = .{ - .x = x + 1, - .y = y + 1, + .x = x + offset, + .y = y + offset + 1, .w = 1, - .h = inner_h, - .color = light, + .h = if (inner_h > 2) inner_h - 2 else 1, + .color = left_color, } }); - // Bottom edge (dark) - inset + // Bottom edge self.pushCommand(.{ .rect = .{ - .x = x + 1, - .y = y + @as(i32, @intCast(h)) - 2, + .x = x + offset, + .y = y + @as(i32, @intCast(h)) - 1 - offset, .w = inner_w, .h = 1, - .color = dark, + .color = bottom_color, } }); - // Right edge (dark) - inset + // Right edge self.pushCommand(.{ .rect = .{ - .x = x + @as(i32, @intCast(w)) - 2, - .y = y + 1, + .x = x + @as(i32, @intCast(w)) - 1 - offset, + .y = y + offset + 1, .w = 1, - .h = inner_h, - .color = dark, + .h = if (inner_h > 2) inner_h - 2 else 1, + .color = right_color, } }); } - /// Draw a rectangle with inverted 3D bevel effect (pressed state) - /// Dark edges on top/left, light on bottom/right - /// Note: Bevel is drawn INSIDE the rect (inset by 1px) to not overlap border + /// Wrapper simple: bisel elevado interior (compatibilidad) + pub fn drawBeveledRect(self: *Self, x: i32, y: i32, w: u32, h: u32, base_color: Style.Color) void { + self.drawBevel(x, y, w, h, base_color, BevelStyle.raised_inset); + } + + /// Wrapper simple: bisel hundido interior (compatibilidad) pub fn drawBeveledRectPressed(self: *Self, x: i32, y: i32, w: u32, h: u32, base_color: Style.Color) void { - const light = base_color.lightenHsl(10); - const dark = base_color.darkenHsl(15); - - // Main fill - self.pushCommand(.{ .rect = .{ - .x = x, - .y = y, - .w = w, - .h = h, - .color = base_color, - } }); - - // Bevel inset by 1px to stay inside border - const inner_w = if (w > 2) w - 2 else 1; - const inner_h = if (h > 2) h - 2 else 1; - - // Top edge (dark - inverted) - inset - self.pushCommand(.{ .rect = .{ - .x = x + 1, - .y = y + 1, - .w = inner_w, - .h = 1, - .color = dark, - } }); - - // Left edge (dark - inverted) - inset - self.pushCommand(.{ .rect = .{ - .x = x + 1, - .y = y + 1, - .w = 1, - .h = inner_h, - .color = dark, - } }); - - // Bottom edge (light - inverted) - inset - self.pushCommand(.{ .rect = .{ - .x = x + 1, - .y = y + @as(i32, @intCast(h)) - 2, - .w = inner_w, - .h = 1, - .color = light, - } }); - - // Right edge (light - inverted) - inset - self.pushCommand(.{ .rect = .{ - .x = x + @as(i32, @intCast(w)) - 2, - .y = y + 1, - .w = 1, - .h = inner_h, - .color = light, - } }); + self.drawBevel(x, y, w, h, base_color, BevelStyle.sunken_inset); } /// Draw a complete panel frame with focus-dependent styling.