refactor(editor): unify toolbar icon buttons into one paint_icon_button helper

Adds button::paint_icon_button (full icon button = hover/pressed/active feedback on jian Button + centred icon) and paint_icon_button_feedback (the bg+active funnel). toolbar's paint_button now delegates to it instead of hand-rolling active-fill + ghost-feedback + draw_icon separately. All toolbar icon buttons (tool buttons + shape slot) now funnel hover/active rendering through one place. Active highlight now jian Button Primary (r6). Removed dead BUTTON_RADIUS.
This commit is contained in:
Kayshen-X 2026-06-20 10:38:42 +08:00
parent f763e2f339
commit 8eef1188ce
2 changed files with 61 additions and 22 deletions

View file

@ -1,4 +1,5 @@
use crate::{Color, Rect, RenderBackend, Theme};
use crate::widgets::icons::{draw_icon, Icon};
use crate::{Color, Point2D, Rect, RenderBackend, Theme};
use jian_widgets::components::button::{Button, ButtonVariant};
use jian_widgets::{Density, Tokens};
@ -77,6 +78,59 @@ pub(crate) fn paint_button_feedback_wash(
}
}
/// Icon-button background + feedback, centralised on jian Button. `active` paints
/// the solid primary highlight (e.g. the selected toolbar tool); inactive uses the
/// ghost hover/press wash. Returns the icon color to stroke on top — every icon
/// button funnels its hover/active rendering through here instead of hand-rolling it.
pub(crate) fn paint_icon_button_feedback(
backend: &mut dyn RenderBackend,
theme: &Theme,
rect: Rect,
hovered: bool,
pressed: bool,
active: bool,
) -> Color {
if active {
Button {
label: "",
icon_d: None,
variant: ButtonVariant::Primary,
enabled: true,
hovered,
pressed,
font_size: 0.0,
}
.paint(backend, rect, &tokens_from_theme(theme));
theme.primary_foreground
} else {
paint_ghost_button_feedback(backend, theme, rect, hovered, pressed)
}
}
/// A complete icon button: hover / pressed / active feedback (centralised on
/// jian Button) PLUS the centred icon glyph — so every toolbar and chrome icon
/// button is a single call instead of hand-rolling `feedback + draw_icon`
/// separately in each widget. `active` paints the solid primary highlight.
#[allow(clippy::too_many_arguments)]
pub(crate) fn paint_icon_button(
backend: &mut dyn RenderBackend,
theme: &Theme,
rect: Rect,
icon: Icon,
hovered: bool,
pressed: bool,
active: bool,
icon_size: f32,
stroke_w: f32,
) {
let color = paint_icon_button_feedback(backend, theme, rect, hovered, pressed, active);
let origin = Point2D::new(
rect.origin.x + (rect.size.x - icon_size) / 2.0,
rect.origin.y + (rect.size.y - icon_size) / 2.0,
);
draw_icon(backend, icon, origin, icon_size, color, stroke_w);
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -25,7 +25,6 @@ use op_editor_core::Tool;
/// Outer column width (matches the TS app's `w-12` toolbar).
pub const TOOLBAR_WIDTH: f32 = 44.0;
const BUTTON_SIZE: f32 = 32.0;
const BUTTON_RADIUS: f32 = 8.0;
const ICON_SIZE: f32 = 18.0;
const STROKE_W: f32 = 1.6;
const BUTTON_GAP: f32 = 4.0;
@ -454,29 +453,15 @@ fn paint_button(
origin: Point2D::new(x, y),
size: Point2D::new(BUTTON_SIZE, BUTTON_SIZE),
};
let icon_color = if active {
cx.backend
.fill_round_rect(button_rect, BUTTON_RADIUS, theme.primary);
theme.primary_foreground
} else {
crate::widgets::button::paint_ghost_button_feedback(
cx.backend,
theme,
button_rect,
hovered,
pressed,
)
};
let icon_origin = Point2D::new(
x + (BUTTON_SIZE - ICON_SIZE) / 2.0,
y + (BUTTON_SIZE - ICON_SIZE) / 2.0,
);
draw_icon(
crate::widgets::button::paint_icon_button(
cx.backend,
theme,
button_rect,
icon,
icon_origin,
hovered,
pressed,
active,
ICON_SIZE,
icon_color,
STROKE_W,
);
}