diff --git a/crates/ui-egui/locales/pt-BR.toml b/crates/ui-egui/locales/pt-BR.toml index aabc8806..d970db14 100644 --- a/crates/ui-egui/locales/pt-BR.toml +++ b/crates/ui-egui/locales/pt-BR.toml @@ -614,7 +614,7 @@ status = "draft — needs native review" "Instance swap (no other component)" = "Troca de instância (nenhum outro componente)" "Instances" = "Instâncias" "Instant" = "Instantâneo" -"Intensity" = "Intensidade" +"Intensity" = "Brilho" "Interaction" = "Interação" "Interactions" = "Interações" "Interface design and prototyping. A free, open-source, clean-room design tool written from scratch in Rust, part of the ArtCraft Crafting Apps." = "Design de interfaces e prototipagem. Uma ferramenta de design gratuita e de código aberto, com implementação independente (clean-room), escrita do zero em Rust, parte dos Crafting Apps da ArtCraft." diff --git a/crates/ui-egui/locales/ru.toml b/crates/ui-egui/locales/ru.toml index 7ec18507..dc850023 100644 --- a/crates/ui-egui/locales/ru.toml +++ b/crates/ui-egui/locales/ru.toml @@ -635,7 +635,7 @@ name = "Русский" "Instance swap (no other component)" = "Замена экземпляра (других компонентов нет)" "Instances" = "Экземпляры" "Instant" = "Мгновенно" -"Intensity" = "Интенсивность" +"Intensity" = "Яркость" "Interaction" = "Взаимодействие" "Interactions" = "Взаимодействия" "Interface design and prototyping. A free, open-source, clean-room design tool written from scratch in Rust, part of the ArtCraft Crafting Apps." = "Дизайн интерфейсов и прототипирование. Бесплатный инструмент с открытым исходным кодом, независимо созданный с нуля на Rust, из семейства ArtCraft Crafting Apps." diff --git a/crates/ui-egui/src/field_tests.rs b/crates/ui-egui/src/field_tests.rs index 14158972..9ddc48e5 100644 --- a/crates/ui-egui/src/field_tests.rs +++ b/crates/ui-egui/src/field_tests.rs @@ -502,34 +502,31 @@ fn the_glass_light_intensity_field_has_a_label() { u.frame(vec![]); let shapes = u.frame_output(vec![]).shapes; let lead = u.ctx.read_response(Id::new(("fx_gi", 0usize)).with("lead")).expect("the intensity field is shown").rect; - let labels: Vec = painted_texts(&shapes).into_iter().filter(|(_, _, at)| lead.contains(at.center())).map(|(text, ..)| text).collect(); - assert_eq!(labels, ["Intensity"]); + let labels: Vec<(String, bool)> = + painted_texts(&shapes).into_iter().filter(|(_, _, at)| lead.contains(at.center())).map(|(text, cut, _)| (text, cut)).collect(); + assert_eq!(labels, [("Intensity".to_string(), false)], "labelled, and not cut short"); + let field = u.ctx.read_response(Id::new(("fx_gi", 0usize)).with("field")).expect("its value").rect; + assert_eq!(field.max.x - lead.min.x, GLASS_FIELD_W, "the width the next test measures against"); } /// A glass settings field's width in the Design panel at its default width. -const GLASS_FIELD_W: f32 = 98.0; +const GLASS_FIELD_W: f32 = 98.5; -/// The glass settings' labels leave their values room in every language, and are cut only when -/// they would crowd them (#256). +/// The light intensity label this fix adds fits whole beside its value in every language (#256). +/// Other glass labels may end in "…" in some languages (es "Profundidad"); a longer +/// "Intensity" translation fails here. #[test] -fn glass_labels_fit_beside_their_values_in_every_language() { +fn the_intensity_label_fits_whole_in_every_language() { use crate::i18n::{self, Text}; - let keys = ["Light|glass", "Intensity", "Refraction", "Depth", "Dispersion", "Frost", "Splay"]; let ctx = egui::Context::default(); let mut out = ctx.run_ui(Default::default(), |ui| { let width = |s: &str| ui.painter().layout_no_wrap(s.to_string(), crate::theme::regular(11.0), egui::Color32::WHITE).size().x; for lang in i18n::languages() { - for key in keys { - let label = lang.ui.iter().find(|(k, _)| *k == key).map_or(key, |(_, t)| match t { - Text::Plain(s) | Text::Plural { other: s, .. } => s, - }); - let (lead, value) = (crate::widgets::text_lead_width(ui, label, GLASS_FIELD_W), width("100%")); - assert!(lead + value <= GLASS_FIELD_W, "{}: no room for the value beside {label:?}", lang.code); - // Cut off only when it would crowd the value's 32 points (es "Profundidad"). - if 12.0 + width(label) <= GLASS_FIELD_W - 32.0 { - assert!(lead >= 12.0 + width(label), "{}: {label:?} is cut off", lang.code); - } - } + let label = lang.ui.iter().find(|(k, _)| *k == "Intensity").map_or("Intensity", |(_, t)| match t { + Text::Plain(s) | Text::Plural { other: s, .. } => s, + }); + let lead = crate::widgets::text_lead_width(ui, label, GLASS_FIELD_W); + assert!(lead >= 12.0 + width(label), "{}: {label:?} would end in \"…\"", lang.code); } }); out.textures_delta.clear(); diff --git a/crates/ui-egui/src/widgets.rs b/crates/ui-egui/src/widgets.rs index 4763b47c..c415842d 100644 --- a/crates/ui-egui/src/widgets.rs +++ b/crates/ui-egui/src/widgets.rs @@ -223,7 +223,12 @@ fn number_field_impl( } match lead { Lead::Text(s) => { - crate::bidi::paint_name(ui.painter(), pos2(lead_rect.min.x + 8.0, rect.center().y), s, regular(11.0), t.text_secondary, lead_w - 12.0); + let galley = crate::bidi::name_galley(ui.painter(), s, regular(11.0), t.text_secondary, lead_w - 12.0); + // A label cut short shows whole on hover. + if galley.elided { + lead_resp.clone().on_hover_text(s); + } + ui.painter().galley(pos2(lead_rect.min.x + 8.0, rect.center().y - galley.size().y / 2.0), galley, t.text_secondary); } Lead::Icon(i, _) => { icons::paint(ui.painter(), Rect::from_center_size(lead_rect.center() + vec2(1.0, 0.0), vec2(14.0, 14.0)), i, t.icon_secondary) @@ -347,7 +352,8 @@ fn number_field_impl( None => empty.to_string(), }; let col = if value.is_some() { t.text } else { t.text_secondary }; - ui.painter().with_clip_rect(edit_rect).text(pos2(edit_rect.min.x, rect.center().y), Align2::LEFT_CENTER, s, regular(11.0), col); + // A value too long for its room ends in "…", so a cut number never reads as another. + crate::bidi::paint_name(ui.painter(), pos2(edit_rect.min.x, rect.center().y), &s, regular(11.0), col, edit_rect.width()); } } // The variable button shows while the pointer is over a bindable field.