fix(ui): fitting Intensity translations; cut values end in "…", cut labels show on hover (#256)

From review:
- ru and pt-BR "Intensity" were too long for the glass field and ended
  in "…"; they are now "Яркость" and "Brilho", which fit.
- A value too long for its field ends in "…" instead of being clipped,
  so "100000" never shows as "10".
- A label cut short shows whole on hover.
- The every-language test now checks the Intensity label fits whole in
  each catalog (it fails with the old ru translation); the panel test
  checks the label isn't cut and that the field is the 98.5 points the
  language test measures against.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
RealtaNua
2026-10-17 23:50:52 +07:00
co-authored by Claude Opus 5.5
parent a672a6650f
commit 7b9ebaacbb
4 changed files with 25 additions and 22 deletions
+1 -1
View File
@@ -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."
+1 -1
View File
@@ -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."
+15 -18
View File
@@ -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<String> = 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();
+8 -2
View File
@@ -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.