From 6da89b6ea8e58be2da7f84d97b39f36f91c2a045 Mon Sep 17 00:00:00 2001 From: Kristian Myram Date: Fri, 30 Oct 2026 02:17:34 +0100 Subject: [PATCH] #712: share eyedropper previews and translate the sampling hint --- crates/ui-egui/src/i18n/ui.rs | 8 ++++ .../src/panels/effect_controls/viewer.rs | 40 ++++++++----------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/crates/ui-egui/src/i18n/ui.rs b/crates/ui-egui/src/i18n/ui.rs index 46493296b..a7a9be62b 100644 --- a/crates/ui-egui/src/i18n/ui.rs +++ b/crates/ui-egui/src/i18n/ui.rs @@ -163,6 +163,7 @@ pub(crate) fn tr_args(text: &str, args: &[&dyn Display]) -> String { /// Japanese: the same rows, in the same order, as the Chinese catalogs below. const JAPANESE: &[(&str, &str)] = &[ + ("Click to sample color", "クリックして色をサンプル"), ("Appearance Settings…", "外観設定…"), ("Add to favorites", "お気に入りに追加"), ("Remove from favorites", "お気に入りから削除"), @@ -904,6 +905,7 @@ const JAPANESE: &[(&str, &str)] = &[ ]; const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[ + ("Click to sample color", "单击以取样颜色"), ("Appearance Settings…", "外观设置…"), ("Add to favorites", "添加到收藏"), ("Remove from favorites", "从收藏中移除"), @@ -1644,6 +1646,7 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[ ]; const TRADITIONAL_CHINESE: &[(&str, &str)] = &[ + ("Click to sample color", "按一下以取樣顏色"), ("Appearance Settings…", "外觀設定…"), ("Add to favorites", "加入最愛"), ("Remove from favorites", "從最愛移除"), @@ -2332,6 +2335,7 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[ /// Ukrainian (uk): original translations of the same interface rows as the Chinese catalogs. /// Technical names, key names and substituted project content keep their original spelling. const UKRAINIAN: &[(&str, &str)] = &[ + ("Click to sample color", "Клацніть, щоб узяти зразок кольору"), ("Appearance Settings…", "Налаштування вигляду…"), ("Add to favorites", "Додати до вибраного"), ("Remove from favorites", "Видалити з вибраного"), @@ -2906,6 +2910,7 @@ const UKRAINIAN: &[(&str, &str)] = &[ // Brazilian Portuguese. const PORTUGUESE: &[(&str, &str)] = &[ + ("Click to sample color", "Clique para obter uma amostra de cor"), (" OK ", " OK "), (" Delete ", " Excluir "), (" Open Auto-Save ", " Abrir salvamento automático "), @@ -3357,6 +3362,7 @@ const PORTUGUESE: &[(&str, &str)] = &[ /// Czech (cs): original translations covering every row in the Chinese panel catalogs. const CZECH: &[(&str, &str)] = &[ + ("Click to sample color", "Kliknutím naberete barvu"), (" OK ", " OK "), (" Delete ", " Odstranit "), (" Open Auto-Save ", " Otevřít automatickou zálohu "), @@ -3911,6 +3917,7 @@ const CZECH: &[(&str, &str)] = &[ /// order. Terms follow the Spanish menu catalog (composición, material de archivo, cola de procesamiento, /// ajuste preestablecido, método abreviado). Written from the English source. const SPANISH: &[(&str, &str)] = &[ + ("Click to sample color", "Haz clic para tomar una muestra de color"), ("Copy Source Path", "Copiar ruta de origen"), ("Appearance Settings…", "Ajustes de apariencia…"), ("Add to favorites", "Añadir a favoritos"), @@ -4467,6 +4474,7 @@ const SPANISH: &[(&str, &str)] = &[ /// Georgian (ka): every row of the Chinese catalogs, in the same order (the tests keep them in step). const GEORGIAN: &[(&str, &str)] = &[ + ("Click to sample color", "დააწკაპუნეთ ფერის ნიმუშის ასაღებად"), ("Appearance Settings…", "იერსახის პარამეტრები…"), ("Add to favorites", "რჩეულებში დამატება"), ("Remove from favorites", "რჩეულებიდან ამოშლა"), diff --git a/crates/ui-egui/src/panels/effect_controls/viewer.rs b/crates/ui-egui/src/panels/effect_controls/viewer.rs index c7f8981d1..7bf65d40c 100644 --- a/crates/ui-egui/src/panels/effect_controls/viewer.rs +++ b/crates/ui-egui/src/panels/effect_controls/viewer.rs @@ -119,6 +119,21 @@ fn fx_pick_session( ControlFlow::Continue(point_preview) } +/// Shared hint chip and sampled-color swatch for text and popup eyedroppers. +fn color_pick_preview(app: &EffectcraftApp, painter: &egui::Painter, map: &crate::panels::viewer::ViewerMap, hint: &str, pointer: Option) { + let chip = Rect::from_min_size(map.area.min + vec2(10.0, 10.0), vec2(220.0, 22.0)); + painter.rect_filled(chip, 11.0, Color32::from_black_alpha(180)); + painter.text(chip.center(), Align2::CENTER_CENTER, hint, Tokens::ui(11.5), app.tokens.text); + if let Some(pos) = pointer + && let Some(img) = &app.viewer_image + && let Some(sample) = fw::sample_frame(img, crate::panels::viewer_tools::shown_region(painter.ctx(), map.comp), map.to_comp(pos)) + { + let swatch = Rect::from_min_size(pos + vec2(14.0, 14.0), vec2(26.0, 18.0)); + painter.rect_filled(swatch, 2.0, Color32::from_rgb((sample[0] * 255.0) as u8, (sample[1] * 255.0) as u8, (sample[2] * 255.0) as u8)); + painter.rect_stroke(swatch, 2.0, Stroke::new(1.0, Color32::WHITE), egui::StrokeKind::Outside); + } +} + /// An armed text-colour pick: sample the shown frame on click, Esc cancels. fn text_color_pick( app: &mut EffectcraftApp, @@ -128,7 +143,6 @@ fn text_color_pick( ectx: &EvalCtx, actions: &mut Actions, ) { - let t = app.tokens; let ctx = ui.ctx().clone(); let Some(pick) = app.ui.text_color_pick.clone() else { return; @@ -140,20 +154,11 @@ fn text_color_pick( app.viewer_pixels(); let resp = ui.interact(map.area, egui::Id::unique("viewer-text-color-pick"), Sense::click()); app.auto.add("viewer.textColorPick", map.area, &pick.field); - let chip = Rect::from_min_size(map.area.min + vec2(10.0, 10.0), vec2(220.0, 22.0)); - painter.rect_filled(chip, 11.0, Color32::from_black_alpha(180)); - painter.text(chip.center(), Align2::CENTER_CENTER, crate::i18n::tr_args("Click to sample text {}", &[&pick.field]), Tokens::ui(11.5), t.text); + color_pick_preview(app, painter, map, &crate::i18n::tr_args("Click to sample text {}", &[&pick.field]), resp.hover_pos()); if let Some(pos) = resp.hover_pos() { ctx.set_cursor_icon(egui::CursorIcon::Crosshair); let c = map.to_comp(pos); app.pointer_comp = Some([c[0] as f32, c[1] as f32]); - if let Some(img) = &app.viewer_image - && let Some(s) = fw::sample_frame(img, crate::panels::viewer_tools::shown_region(&ctx, map.comp), c) - { - let sw = Rect::from_min_size(pos + vec2(14.0, 14.0), vec2(26.0, 18.0)); - painter.rect_filled(sw, 2.0, Color32::from_rgb((s[0] * 255.0) as u8, (s[1] * 255.0) as u8, (s[2] * 255.0) as u8)); - painter.rect_stroke(sw, 2.0, Stroke::new(1.0, Color32::WHITE), egui::StrokeKind::Outside); - } } if resp.clicked() && let Some(pos) = resp.interact_pointer_pos() @@ -180,7 +185,6 @@ fn text_color_pick( /// the pointer, and the next press on it samples the frame into the picker. The press is read from /// the pointer, since a modal dialog over the viewer takes its clicks. fn popup_eyedropper(app: &mut EffectcraftApp, ui: &mut egui::Ui, painter: &egui::Painter, map: &crate::panels::viewer::ViewerMap) { - let t = app.tokens; let ctx = ui.ctx().clone(); if !crate::color_picker::eyedropper_armed(&ctx) { return; @@ -190,17 +194,7 @@ fn popup_eyedropper(app: &mut EffectcraftApp, ui: &mut egui::Ui, painter: &egui: app.auto.add("viewer.colorPickerEyedropper", map.area, "Sample color"); ctx.set_cursor_icon(egui::CursorIcon::Crosshair); app.viewer_pixels(); - let chip = Rect::from_min_size(map.area.min + vec2(10.0, 10.0), vec2(220.0, 22.0)); - painter.rect_filled(chip, 11.0, Color32::from_black_alpha(180)); - painter.text(chip.center(), Align2::CENTER_CENTER, "Click to sample color", Tokens::ui(11.5), t.text); - if let Some(hp) = crate::color_picker::eyedropper_point(&ctx) - && let Some(img) = &app.viewer_image - && let Some(s) = fw::sample_frame(img, crate::panels::viewer_tools::shown_region(&ctx, map.comp), map.to_comp(hp)) - { - let sw = Rect::from_min_size(hp + vec2(14.0, 14.0), vec2(26.0, 18.0)); - painter.rect_filled(sw, 2.0, Color32::from_rgb((s[0] * 255.0) as u8, (s[1] * 255.0) as u8, (s[2] * 255.0) as u8)); - painter.rect_stroke(sw, 2.0, Stroke::new(1.0, Color32::WHITE), egui::StrokeKind::Outside); - } + color_pick_preview(app, painter, map, crate::i18n::tr("Click to sample color"), crate::color_picker::eyedropper_point(&ctx)); if let Some(pos) = crate::color_picker::sample_press(&ctx) && let Some(img) = &app.viewer_image && let Some(s) = fw::sample_frame(img, crate::panels::viewer_tools::shown_region(&ctx, map.comp), map.to_comp(pos))