M10: Design panel: section header icons draw at the panel's icon size (#2227)

Section header buttons painted their glyph in a 12 pt box inside the 24 pt button while icon_button_at used 14 pt. Both now use icon_glyph_rect (ICON_BUTTON_INSET 5 pt), as do the 24 pt Close and Swap buttons in the assets and prototype panels. Test compares the painted Eye bounds of a header and an icon button.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
This commit is contained in:
Trevor Brown
2026-11-10 15:55:41 -05:00
co-authored by Claude Sonnet 5.5
parent be991533b0
commit ebcaa58c4f
6 changed files with 63 additions and 6 deletions
@@ -0,0 +1,45 @@
//! A section header's icon buttons (Appearance, Fill, Stroke, Effects...) draw their glyphs at the
//! same size as the panel's other 24 pt icon buttons (#2227).
use egui::{Context, Rect, pos2, vec2};
use crate::icons::Icon;
use crate::widgets::{icon_button_at, icon_glyph_rect, section_header};
/// Bounding box of everything painted in one frame of `add`.
fn painted_box(add: impl FnOnce(&mut egui::Ui)) -> Rect {
let ctx = Context::default();
let mut add = Some(add);
let raw = egui::RawInput { screen_rect: Some(Rect::from_min_size(pos2(0.0, 0.0), vec2(400.0, 200.0))), ..Default::default() };
let mut out = ctx.run_ui(raw, |ui| {
if let Some(f) = add.take() {
f(ui);
}
});
out.textures_delta.clear();
out.shapes
.iter()
.filter(|s| matches!(s.shape, egui::Shape::Path(_) | egui::Shape::Mesh(_) | egui::Shape::LineSegment { .. } | egui::Shape::Circle(_)))
.map(|s| s.shape.visual_bounding_rect())
.fold(Rect::NOTHING, |a, b| a.union(b))
}
#[test]
fn header_eye_draws_as_large_as_an_icon_buttons_eye() {
let header = painted_box(|ui| {
section_header(ui, "Appearance", &[(Icon::Eye, "Hide")], false);
});
let button = painted_box(|ui| {
let r = Rect::from_min_size(pos2(20.0, 20.0), vec2(24.0, 24.0));
icon_button_at(ui, ui.scope_id().with("b"), r, Icon::Eye, "Hide");
});
assert!(header.is_finite() && button.is_finite(), "{header:?} {button:?}");
assert!((header.width() - button.width()).abs() < 0.5, "widths {} vs {}", header.width(), button.width());
assert!((header.height() - button.height()).abs() < 0.5, "heights {} vs {}", header.height(), button.height());
}
#[test]
fn glyph_rect_is_the_shared_inset_of_the_button() {
let b = Rect::from_min_size(pos2(0.0, 0.0), vec2(24.0, 24.0));
assert_eq!(icon_glyph_rect(b).size(), vec2(14.0, 14.0));
}
+2
View File
@@ -122,6 +122,8 @@ mod guide_release_tests;
#[cfg(test)]
mod guide_snap_tests;
#[cfg(test)]
mod header_icon_size_tests;
#[cfg(test)]
mod home_recents_tests;
#[cfg(test)]
mod home_tests;
+2 -2
View File
@@ -1157,7 +1157,7 @@ fn enabled_libraries(app: &mut FigApp, ui: &mut Ui, w: f32, doc: &Document) {
if xr.hovered() {
ui.painter().rect_filled(x, CornerRadius::same(5), t.hover);
}
icons::paint(ui.painter(), x.shrink(6.0), Icon::Close, t.icon);
icons::paint(ui.painter(), crate::widgets::icon_glyph_rect(x), Icon::Close, t.icon);
if xr.clicked() {
remove = Some(l.id.clone());
}
@@ -1169,7 +1169,7 @@ fn enabled_libraries(app: &mut FigApp, ui: &mut Ui, w: f32, doc: &Document) {
if sr.hovered() {
ui.painter().rect_filled(sw, CornerRadius::same(5), t.hover);
}
icons::paint(ui.painter(), sw.shrink(6.0), Icon::Swap, t.icon);
icons::paint(ui.painter(), crate::widgets::icon_glyph_rect(sw), Icon::Swap, t.icon);
crate::right::menu_popup(&sr).show(|ui| {
for other in doc.libraries.enabled.iter().filter(|o| o.id != l.id) {
if crate::right::menu_item(ui, &trf("Swap to {name}", &[("name", &other.name)]), "") {
+1 -1
View File
@@ -684,7 +684,7 @@ fn editor_popover(app: &mut FigApp, ctx: &egui::Context, popover: InteractionPop
if xr.hovered() {
ui.painter().rect_filled(x, CornerRadius::same(5), t.hover);
}
icons::paint(ui.painter(), x.shrink(6.0), Icon::Close, t.icon);
icons::paint(ui.painter(), crate::widgets::icon_glyph_rect(x), Icon::Close, t.icon);
if xr.clicked() {
close = true;
}
+11 -2
View File
@@ -1013,7 +1013,7 @@ pub fn section_header_resp(ui: &mut Ui, title: &str, buttons: &[(Icon, &str)], d
if resp.hovered() {
ui.painter().rect_filled(r, CornerRadius::same(5), t.hover);
}
icons::paint(ui.painter(), r.shrink(6.0), *icon, t.icon);
icons::paint(ui.painter(), icon_glyph_rect(r), *icon, t.icon);
if resp.clicked() {
out = Some(i);
}
@@ -1045,6 +1045,15 @@ pub fn tight_end_n<R>(ui: &mut Ui, count: usize, add: impl FnOnce(&mut Ui) -> R)
add(&mut child)
}
/// Inset of the glyph inside a 24 pt icon button: a 14 pt glyph, the size every icon button in the
/// panels draws at (section header buttons included).
pub const ICON_BUTTON_INSET: f32 = 5.0;
/// The rect a 24 pt icon button paints its glyph in.
pub fn icon_glyph_rect(button: Rect) -> Rect {
button.shrink(ICON_BUTTON_INSET)
}
/// An icon button at `rect` (hover highlight, tooltip); true when clicked.
pub fn icon_button_at(ui: &mut Ui, id: Id, rect: Rect, icon: Icon, tip: &str) -> bool {
let t = Tokens::get(ui.ctx());
@@ -1052,7 +1061,7 @@ pub fn icon_button_at(ui: &mut Ui, id: Id, rect: Rect, icon: Icon, tip: &str) ->
if resp.hovered() {
ui.painter().rect_filled(rect, CornerRadius::same(5), t.hover);
}
icons::paint(ui.painter(), rect.shrink(5.0), icon, t.icon);
icons::paint(ui.painter(), icon_glyph_rect(rect), icon, t.icon);
resp.clicked()
}
+2 -1
View File
File diff suppressed because one or more lines are too long