mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 23:35:10 +00:00
Character panel: the highlighted font height icon keeps its contrast (#6304)
On the open list's row of the current reference, drawn on the accent-tinted selection fill, the icon draws its measure in the name's colour instead of the accent, which lost its contrast there. The other rows and the closed field keep the accent.
This commit is contained in:
@@ -951,8 +951,11 @@ fn font_height_reference(app: &mut VectorcraftApp, ui: &mut Ui, width: f32) {
|
||||
let labels = [tl!("Em Box"), tl!("Cap Height"), tl!("x-Height"), tl!("ICF Box")];
|
||||
let current = shared(app, |s| s.font_height_reference);
|
||||
let shown = FONT_HEIGHT_REFERENCES.iter().position(|(r, _)| Some(*r) == current);
|
||||
let mark = Tokens::get(ui.ctx()).accent;
|
||||
let icon = |ui: &Ui, rect: egui::Rect, i: usize, ink: egui::Color32| {
|
||||
let accent = Tokens::get(ui.ctx()).accent;
|
||||
let icon = |ui: &Ui, rect: egui::Rect, i: usize, ink: egui::Color32, highlighted: bool| {
|
||||
// On the highlighted row the selection fill is accent-tinted, so the measure takes the
|
||||
// name's colour there to keep its contrast.
|
||||
let mark = if highlighted { ink } else { accent };
|
||||
if let Some((r, _)) = FONT_HEIGHT_REFERENCES.get(i) {
|
||||
paint_font_height_icon(ui.painter(), rect, *r, ink, mark);
|
||||
}
|
||||
|
||||
@@ -886,6 +886,71 @@ fn font_height_references_show_their_icons() {
|
||||
}
|
||||
}
|
||||
|
||||
/// In the open Set Font Height Reference list, the current reference's row is drawn on the
|
||||
/// accent-tinted selection fill, so its icon draws the measure in the name's colour rather than
|
||||
/// the accent, which would lose its contrast there; the other rows keep the accent (#6304).
|
||||
#[test]
|
||||
fn the_highlighted_font_height_icon_keeps_its_contrast() {
|
||||
use super::super::tests_appearance::click;
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 300, "height": 200})).unwrap();
|
||||
let id = app.run("text.create", json!({"x": 20, "y": 50, "text": "Hx", "size": 12})).unwrap()["id"].clone();
|
||||
app.run("select.set", json!({"ids": [id]})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
set_pstate(&ctx, FONT_HEIGHT_OPTIONS, true);
|
||||
let accent = Tokens::get(&ctx).accent;
|
||||
// Every text drawn with its rect, and every stroke drawn with its rect and colour.
|
||||
let frame = |ctx: &egui::Context, app: &mut VectorcraftApp| {
|
||||
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| show(app, ui));
|
||||
out.textures_delta.clear();
|
||||
fn collect(s: &egui::Shape, texts: &mut Vec<(String, egui::Rect)>, strokes: &mut Vec<(egui::Rect, egui::Color32)>) {
|
||||
match s {
|
||||
egui::Shape::Text(t) => texts.push((t.galley.text().to_string(), t.visual_bounding_rect())),
|
||||
egui::Shape::Vec(v) => v.iter().for_each(|s| collect(s, texts, strokes)),
|
||||
egui::Shape::LineSegment { stroke, .. } => strokes.push((s.visual_bounding_rect(), stroke.color)),
|
||||
egui::Shape::Path(p) => {
|
||||
if let egui::epaint::ColorMode::Solid(c) = p.stroke.color {
|
||||
strokes.push((s.visual_bounding_rect(), c));
|
||||
}
|
||||
}
|
||||
egui::Shape::Rect(r) => strokes.push((s.visual_bounding_rect(), r.stroke.color)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let (mut texts, mut strokes) = (vec![], vec![]);
|
||||
for c in &out.shapes {
|
||||
collect(&c.shape, &mut texts, &mut strokes);
|
||||
}
|
||||
(texts, strokes)
|
||||
};
|
||||
// The stroke colours in the icon's place before the text `rect`.
|
||||
let colours = |strokes: &[(egui::Rect, egui::Color32)], rect: egui::Rect| -> Vec<egui::Color32> {
|
||||
let place = egui::Rect::from_min_max(
|
||||
egui::pos2(rect.min.x - FONT_HEIGHT_ICON - 8.0, rect.center().y - 10.0),
|
||||
egui::pos2(rect.min.x, rect.center().y + 10.0),
|
||||
);
|
||||
strokes.iter().filter(|(r, _)| r.is_finite() && place.contains_rect(*r)).map(|(_, c)| *c).collect()
|
||||
};
|
||||
frame(&ctx, &mut app);
|
||||
let (texts, _) = frame(&ctx, &mut app);
|
||||
let field = texts.iter().find(|(t, _)| t == "Em Box").map(|(_, r)| *r).expect("the closed field shows Em Box");
|
||||
click(&ctx, &mut app, field.center(), show);
|
||||
frame(&ctx, &mut app);
|
||||
let (texts, strokes) = frame(&ctx, &mut app);
|
||||
let row = |name: &str| texts.iter().rev().find(|(t, _)| t == name).map(|(_, r)| *r).unwrap_or_else(|| panic!("the open list shows {name}"));
|
||||
// The list fades in, so colours are compared without their alpha.
|
||||
let rgb = |c: egui::Color32| {
|
||||
let [r, g, b, _] = c.to_srgba_unmultiplied();
|
||||
[r, g, b]
|
||||
};
|
||||
let is_accent = |c: &egui::Color32| rgb(*c).iter().zip(rgb(accent)).all(|(a, b)| a.abs_diff(b) <= 4);
|
||||
let current = colours(&strokes, row("Em Box"));
|
||||
assert!(!current.is_empty() && !current.iter().any(is_accent), "the highlighted Em Box icon draws in the accent: {current:?}");
|
||||
let other = colours(&strokes, row("Cap Height"));
|
||||
assert!(other.iter().any(is_accent), "the other rows keep the accent measure: {other:?}");
|
||||
}
|
||||
|
||||
/// Tate-chu-yoko is in the panel menu with the East Asian options, and choosing it marks the
|
||||
/// selected characters.
|
||||
#[test]
|
||||
|
||||
@@ -37,9 +37,11 @@ pub fn dropdown_names_named(ui: &mut Ui, id: impl std::hash::Hash, name: Option<
|
||||
|
||||
/// [`dropdown_names`] whose options each show an icon before their name, in the open list and in
|
||||
/// the closed field, so that a name that says little on its own is shown with a picture of what it
|
||||
/// means (#6304). `icon(ui, rect, index, ink)` paints option `index`'s icon into `rect`, an
|
||||
/// `icon_size` square, with `ink`, the colour the row's name is drawn in. `current` is the option
|
||||
/// shown; `None` shows a blank field (characters that differ).
|
||||
/// means (#6304). `icon(ui, rect, index, ink, highlighted)` paints option `index`'s icon into
|
||||
/// `rect`, an `icon_size` square, with `ink`, the colour the row's name is drawn in;
|
||||
/// `highlighted` is true on the open list's row of the current option, drawn on the selection
|
||||
/// fill, where an accent-coloured mark would lose its contrast. `current` is the option shown;
|
||||
/// `None` shows a blank field (characters that differ).
|
||||
pub fn dropdown_icon_names(
|
||||
ui: &mut Ui,
|
||||
id: impl std::hash::Hash,
|
||||
@@ -47,18 +49,21 @@ pub fn dropdown_icon_names(
|
||||
options: &[&str],
|
||||
width: f32,
|
||||
icon_size: f32,
|
||||
icon: impl Fn(&Ui, Rect, usize, egui::Color32),
|
||||
icon: impl Fn(&Ui, Rect, usize, egui::Color32, bool),
|
||||
) -> Option<usize> {
|
||||
let icon = &icon;
|
||||
let atom = move |i: usize| egui::Atom::paint(Vec2::splat(icon_size), move |ui, a| icon(ui, a.rect, i, a.fallback_text_color));
|
||||
let atom = move |i: usize, highlighted: bool| {
|
||||
egui::Atom::paint(Vec2::splat(icon_size), move |ui, a| icon(ui, a.rect, i, a.fallback_text_color, highlighted))
|
||||
};
|
||||
let shown = match current.and_then(|i| options.get(i).map(|name| (i, *name))) {
|
||||
Some((i, name)) => (atom(i), egui::RichText::new(name).size(12.0)).into_atoms(),
|
||||
Some((i, name)) => (atom(i, false), egui::RichText::new(name).size(12.0)).into_atoms(),
|
||||
None => egui::RichText::new("").size(12.0).into_atoms(),
|
||||
};
|
||||
combo_atoms_named(ui, id, None, shown, width, |ui| {
|
||||
let mut chosen = None;
|
||||
for (i, o) in options.iter().enumerate() {
|
||||
if ui.add(egui::Button::selectable(current == Some(i), (atom(i), *o))).clicked() {
|
||||
let on = current == Some(i);
|
||||
if ui.add(egui::Button::selectable(on, (atom(i, on), *o))).clicked() {
|
||||
chosen = Some(i);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user