diff --git a/CHANGELOG.md b/CHANGELOG.md index 01bc61a..4da3b15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ | 2025-12-30 | v0.24.0 | ⭐ FilledCircle primitive: Midpoint Circle Algorithm (Bresenham) | | 2025-12-30 | v0.25.0 | ⭐ IdleCompanion widget: mascota animada que aparece tras inactividad | | 2025-12-31 | v0.26.0 | ⭐ Z-Design V5 Pixel Perfect: títulos legibles (contrastTextColor), botones centrados (+2px TTF), semáforo reubicado (texto + cuadrado derecha) | +| 2025-12-31 | v0.26.1 | Fix: drawBeveledRect bisel ahora +1px inset (no solapa borde exterior) | --- diff --git a/src/core/context.zig b/src/core/context.zig index ea9362a..b2e61b7 100644 --- a/src/core/context.zig +++ b/src/core/context.zig @@ -466,6 +466,7 @@ pub const Context = struct { /// 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); @@ -479,45 +480,50 @@ pub const Context = struct { .color = base_color, } }); - // Top edge (light) + // 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 (light) - inset self.pushCommand(.{ .rect = .{ - .x = x, - .y = y, - .w = w, + .x = x + 1, + .y = y + 1, + .w = inner_w, .h = 1, .color = light, } }); - // Left edge (light) + // Left edge (light) - inset self.pushCommand(.{ .rect = .{ - .x = x, - .y = y, + .x = x + 1, + .y = y + 1, .w = 1, - .h = h, + .h = inner_h, .color = light, } }); - // Bottom edge (dark) + // Bottom edge (dark) - inset self.pushCommand(.{ .rect = .{ - .x = x, - .y = y + @as(i32, @intCast(h)) - 1, - .w = w, + .x = x + 1, + .y = y + @as(i32, @intCast(h)) - 2, + .w = inner_w, .h = 1, .color = dark, } }); - // Right edge (dark) + // Right edge (dark) - inset self.pushCommand(.{ .rect = .{ - .x = x + @as(i32, @intCast(w)) - 1, - .y = y, + .x = x + @as(i32, @intCast(w)) - 2, + .y = y + 1, .w = 1, - .h = h, + .h = inner_h, .color = dark, } }); } /// 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 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); @@ -531,39 +537,43 @@ pub const Context = struct { .color = base_color, } }); - // Top edge (dark - inverted) + // 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, - .y = y, - .w = w, + .x = x + 1, + .y = y + 1, + .w = inner_w, .h = 1, .color = dark, } }); - // Left edge (dark - inverted) + // Left edge (dark - inverted) - inset self.pushCommand(.{ .rect = .{ - .x = x, - .y = y, + .x = x + 1, + .y = y + 1, .w = 1, - .h = h, + .h = inner_h, .color = dark, } }); - // Bottom edge (light - inverted) + // Bottom edge (light - inverted) - inset self.pushCommand(.{ .rect = .{ - .x = x, - .y = y + @as(i32, @intCast(h)) - 1, - .w = w, + .x = x + 1, + .y = y + @as(i32, @intCast(h)) - 2, + .w = inner_w, .h = 1, .color = light, } }); - // Right edge (light - inverted) + // Right edge (light - inverted) - inset self.pushCommand(.{ .rect = .{ - .x = x + @as(i32, @intCast(w)) - 1, - .y = y, + .x = x + @as(i32, @intCast(w)) - 2, + .y = y + 1, .w = 1, - .h = h, + .h = inner_h, .color = light, } }); }