feat: Focus ring con anti-aliasing
Command helpers: - focusRing(x, y, w, h, radius) - focus indicator con AA - focusRingColor() - versión con color custom Características: - Dibuja 2px fuera del widget (offset) - Color semi-transparente (primary alpha 180) - Radio +2 para mantener proporciones - Thickness 2px para visibilidad - Anti-aliasing habilitado Widgets actualizados: - TextInput: focus ring cuando has_focus - Select: focus ring cuando has_focus (no cuando dropdown abierto) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
6e1f8b79d7
commit
ed0e3e8e5b
3 changed files with 45 additions and 0 deletions
|
|
@ -208,6 +208,43 @@ pub fn roundedRectOutline(x: i32, y: i32, w: u32, h: u32, color: Style.Color, ra
|
|||
} };
|
||||
}
|
||||
|
||||
/// Create a focus ring command (visual focus indicator with AA)
|
||||
/// Draws a rounded outline outside the widget bounds with semi-transparent primary color.
|
||||
/// Use when a widget has keyboard focus to provide clear visual feedback.
|
||||
pub fn focusRing(x: i32, y: i32, w: u32, h: u32, radius: u8) DrawCommand {
|
||||
const offset: i32 = 2; // Draw outside widget bounds
|
||||
const focus_color = Style.Color.primary.withAlpha(180); // Semi-transparent
|
||||
const focus_radius = if (radius > 0) radius + 2 else 0;
|
||||
|
||||
return .{ .rounded_rect_outline = .{
|
||||
.x = x - offset,
|
||||
.y = y - offset,
|
||||
.w = w + @as(u32, @intCast(offset * 2)),
|
||||
.h = h + @as(u32, @intCast(offset * 2)),
|
||||
.color = focus_color,
|
||||
.radius = focus_radius,
|
||||
.thickness = 2,
|
||||
.aa = true,
|
||||
} };
|
||||
}
|
||||
|
||||
/// Create a focus ring with custom color
|
||||
pub fn focusRingColor(x: i32, y: i32, w: u32, h: u32, radius: u8, color: Style.Color) DrawCommand {
|
||||
const offset: i32 = 2;
|
||||
const focus_radius = if (radius > 0) radius + 2 else 0;
|
||||
|
||||
return .{ .rounded_rect_outline = .{
|
||||
.x = x - offset,
|
||||
.y = y - offset,
|
||||
.w = w + @as(u32, @intCast(offset * 2)),
|
||||
.h = h + @as(u32, @intCast(offset * 2)),
|
||||
.color = color,
|
||||
.radius = focus_radius,
|
||||
.thickness = 2,
|
||||
.aa = true,
|
||||
} };
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Tests
|
||||
// =============================================================================
|
||||
|
|
|
|||
|
|
@ -139,6 +139,10 @@ pub fn selectRect(
|
|||
if (Style.isFancy() and config.corner_radius > 0) {
|
||||
ctx.pushCommand(Command.roundedRect(bounds.x, bounds.y, bounds.w, bounds.h, bg_color, config.corner_radius));
|
||||
ctx.pushCommand(Command.roundedRectOutline(bounds.x, bounds.y, bounds.w, bounds.h, border_color, config.corner_radius));
|
||||
// Draw focus ring when focused (not when dropdown is open)
|
||||
if (has_focus and !state.open) {
|
||||
ctx.pushCommand(Command.focusRing(bounds.x, bounds.y, bounds.w, bounds.h, config.corner_radius));
|
||||
}
|
||||
} else {
|
||||
ctx.pushCommand(Command.rect(bounds.x, bounds.y, bounds.w, bounds.h, bg_color));
|
||||
ctx.pushCommand(Command.rectOutline(bounds.x, bounds.y, bounds.w, bounds.h, border_color));
|
||||
|
|
|
|||
|
|
@ -291,6 +291,10 @@ pub fn textInputRect(
|
|||
// Fancy mode: rounded corners
|
||||
ctx.pushCommand(Command.roundedRect(bounds.x, bounds.y, bounds.w, bounds.h, bg_color, config.corner_radius));
|
||||
ctx.pushCommand(Command.roundedRectOutline(bounds.x, bounds.y, bounds.w, bounds.h, border_color, config.corner_radius));
|
||||
// Draw focus ring when focused
|
||||
if (has_focus) {
|
||||
ctx.pushCommand(Command.focusRing(bounds.x, bounds.y, bounds.w, bounds.h, config.corner_radius));
|
||||
}
|
||||
} else {
|
||||
// Simple mode: square corners
|
||||
ctx.pushCommand(Command.rect(bounds.x, bounds.y, bounds.w, bounds.h, bg_color));
|
||||
|
|
|
|||
Loading…
Reference in a new issue