diff --git a/crates/egui/src/atomics/atoms.rs b/crates/egui/src/atomics/atoms.rs index 1db7c63c6..2cb668cff 100644 --- a/crates/egui/src/atomics/atoms.rs +++ b/crates/egui/src/atomics/atoms.rs @@ -26,6 +26,18 @@ impl<'a> Atoms<'a> { self.0.insert(0, atom.into()); } + /// Insert atoms at the beginning of the list (left side). + pub fn extend_left(&mut self, atoms: impl IntoAtoms<'a>) { + let mut left = atoms.into_atoms(); + left.0.append(&mut self.0); + *self = left; + } + + /// Insert atoms at the end of the list (right side). + pub fn extend_right(&mut self, atoms: impl IntoAtoms<'a>) { + self.0.append(&mut atoms.into_atoms().0); + } + /// Concatenate and return the text contents. // TODO(lucasmerlin): It might not always make sense to return the concatenated text, e.g. // in a submenu button there is a right text '⏵' which is now passed to the screen reader. diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index 4b4c2fe32..7ad155f16 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -259,6 +259,13 @@ impl<'a> Button<'a> { self } + /// Set the gap between atoms. + #[inline] + pub fn gap(mut self, gap: f32) -> Self { + self.layout = self.layout.gap(gap); + self + } + /// Show the button and return a [`AtomLayoutResponse`] for painting custom contents. pub fn atom_ui(self, ui: &mut Ui) -> AtomLayoutResponse { let Button { diff --git a/crates/egui/src/widgets/drag_value.rs b/crates/egui/src/widgets/drag_value.rs index 7841fee61..d12020097 100644 --- a/crates/egui/src/widgets/drag_value.rs +++ b/crates/egui/src/widgets/drag_value.rs @@ -1,11 +1,10 @@ -#![expect(clippy::needless_pass_by_value)] // False positives with `impl ToString` - -use std::{cmp::Ordering, ops::RangeInclusive}; - use crate::{ - Button, CursorIcon, Id, Key, MINUS_CHAR_STR, Modifiers, NumExt as _, Response, RichText, Sense, - TextEdit, TextWrapMode, Ui, Widget, WidgetInfo, emath, text, + Atom, AtomExt as _, AtomKind, Atoms, Button, CursorIcon, Id, IntoAtoms, Key, MINUS_CHAR_STR, + Modifiers, NumExt as _, Response, RichText, Sense, TextEdit, TextWrapMode, Ui, Widget, + WidgetInfo, emath, text, }; +use emath::Vec2; +use std::{cmp::Ordering, ops::RangeInclusive}; // ---------------------------------------------------------------------------- @@ -38,8 +37,7 @@ fn set(get_set_value: &mut GetSetValue<'_>, value: f64) { pub struct DragValue<'a> { get_set_value: GetSetValue<'a>, speed: f64, - prefix: String, - suffix: String, + atoms: Atoms<'a>, range: RangeInclusive, clamp_existing_to_range: bool, min_decimals: usize, @@ -50,6 +48,8 @@ pub struct DragValue<'a> { } impl<'a> DragValue<'a> { + const ATOM_ID: &'static str = "drag_item"; + pub fn new(value: &'a mut Num) -> Self { let slf = Self::from_get_set(move |v: Option| { if let Some(v) = v { @@ -66,11 +66,12 @@ impl<'a> DragValue<'a> { } pub fn from_get_set(get_set_value: impl 'a + FnMut(Option) -> f64) -> Self { + let atoms = Atoms::new(Atom::custom(Id::new(Self::ATOM_ID), Vec2::ZERO).atom_grow(true)); + Self { get_set_value: Box::new(get_set_value), speed: 1.0, - prefix: Default::default(), - suffix: Default::default(), + atoms, range: f64::NEG_INFINITY..=f64::INFINITY, clamp_existing_to_range: true, min_decimals: 0, @@ -164,15 +165,15 @@ impl<'a> DragValue<'a> { /// Show a prefix before the number, e.g. "x: " #[inline] - pub fn prefix(mut self, prefix: impl ToString) -> Self { - self.prefix = prefix.to_string(); + pub fn prefix(mut self, prefix: impl IntoAtoms<'a>) -> Self { + self.atoms.extend_left(prefix); self } /// Add a suffix to the number, this can be e.g. a unit ("°" or " m") #[inline] - pub fn suffix(mut self, suffix: impl ToString) -> Self { - self.suffix = suffix.to_string(); + pub fn suffix(mut self, suffix: impl IntoAtoms<'a>) -> Self { + self.atoms.extend_right(suffix); self } @@ -433,8 +434,7 @@ impl Widget for DragValue<'_> { speed, range, clamp_existing_to_range, - prefix, - suffix, + mut atoms, min_decimals, max_decimals, custom_formatter, @@ -442,6 +442,26 @@ impl Widget for DragValue<'_> { update_while_editing, } = self; + let mut prefix_text = String::new(); + let mut suffix_text = String::new(); + let mut past_value = false; + let atom_id = Id::new(Self::ATOM_ID); + for atom in atoms.iter() { + match &atom.kind { + AtomKind::Custom(id) if *id == atom_id => { + past_value = true; + } + AtomKind::Text(text) => { + if past_value { + suffix_text.push_str(text.text()); + } else { + prefix_text.push_str(text.text()); + } + } + _ => {} + } + } + let shift = ui.input(|i| i.modifiers.shift_only()); // The widget has the same ID whether it's in edit or button mode. let id = ui.next_auto_id(); @@ -543,8 +563,6 @@ impl Widget for DragValue<'_> { } } - // some clones below are redundant if AccessKit is disabled - #[expect(clippy::redundant_clone)] let mut response = if is_kb_editing { let mut value_text = ui .data_mut(|data| data.remove_temp::(id)) @@ -586,13 +604,22 @@ impl Widget for DragValue<'_> { ui.data_mut(|data| data.insert_temp(id, value_text)); response } else { - let button = Button::new( - RichText::new(format!("{}{}{}", prefix, value_text.clone(), suffix)) - .text_style(text_style), - ) - .wrap_mode(TextWrapMode::Extend) - .sense(Sense::click_and_drag()) - .min_size(ui.spacing().interact_size); // TODO(emilk): find some more generic solution to `min_size` + atoms.map_atoms(|atom| { + if let AtomKind::Custom(id) = atom.kind + && id == atom_id + { + RichText::new(value_text.clone()) + .text_style(text_style.clone()) + .into() + } else { + atom + } + }); + let button = Button::new(atoms) + .wrap_mode(TextWrapMode::Extend) + .sense(Sense::click_and_drag()) + .gap(0.0) + .min_size(ui.spacing().interact_size); // TODO(emilk): find some more generic solution to `min_size` let cursor_icon = if value <= *range.start() { CursorIcon::ResizeEast @@ -607,10 +634,8 @@ impl Widget for DragValue<'_> { if ui.style().explanation_tooltips { response = response.on_hover_text(format!( - "{}{}{}\nDrag to edit or click to enter a value.\nPress 'Shift' while dragging for better control.", - prefix, + "{}\nDrag to edit or click to enter a value.\nPress 'Shift' while dragging for better control.", value as f32, // Show full precision value on-hover. TODO(emilk): figure out f64 vs f32 - suffix )); } @@ -704,7 +729,7 @@ impl Widget for DragValue<'_> { // The value is exposed as a string by the text edit widget // when in edit mode. if !is_kb_editing { - let value_text = format!("{prefix}{value_text}{suffix}"); + let value_text = format!("{prefix_text}{value_text}{suffix_text}"); builder.set_value(value_text); } }); diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png index 5d4cc4d49..742c62272 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9302478abb0b86fae1af3af45d91f032272a56a2098405525d08aba4f9534644 -size 76103 +oid sha256:9d6ba2c4825517b4cc030b7639771d06913da86c2d52fd40e6263692335afa04 +size 76079 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png index 6efc3f4a9..9ce775f7e 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5e67baf0696792e50f7ab3121874d055ddee2de0514712aacbf8e135ec4743d -size 25425 +oid sha256:a169cda21797152fb8aa69928ad3f4cef1b45cc5f213e5bfa01b8fe7723a4852 +size 25391 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Tessellation Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Tessellation Test.png index 99ab541d4..9d9d26c7c 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Tessellation Test.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Tessellation Test.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7336c53885add09360df098b6b131323e8ad3ef0ec2b85bf022e78bc4269276a -size 70255 +oid sha256:01b34a7371dd8b3539ed20594a42f3cd9792d391d2cb44740aa5ef301c9652f3 +size 70244 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png index 0eb5ebd6a..5c6b867d0 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2b7b54a1af0f5cd31bd64f0506e3035dd423314ce3389e61730fa160434fbf3 -size 45074 +oid sha256:0574527ce659559f5b0709d84903afcec60b4ffa6b7979e8985027d326fd782a +size 45066 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png index bd9942eb7..64c5f12e1 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b66a0be67ff2d684a54c2321123521b3ad06dfe5ebffd50e89260d77efcfcc4 -size 86833 +oid sha256:50eca0feefe5d43db74f5e3bc08abda13c5986710cc4aaa03e9382af56264fc2 +size 86826 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png index 64ddf5c49..e0dcebac3 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19320291c99a23429b114a59de4636689e281e1e68766abe2aa1e56562128e50 -size 118919 +oid sha256:91110d6a1da995e3e215cc92fdcceac84335e60b5b2fdbb2f16d5ecc6065fe55 +size 118912 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png index 105bbf285..1a69351b8 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5edf089c00715f1456fe7838e85aadcfc42b6216a3fd95b48d9c21fc8d700cba -size 51371 +oid sha256:31464d796f1660bdd5c98cf73186d7b68918fd42f292bc03e274e43d995edc16 +size 51363 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png index 035eb931d..5c3621856 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6cd1a10639dcb323bdc3b2c43e0c35665184fc809731ced90088ee9edb9de845 -size 54577 +oid sha256:9dca12eb3b99976db20c77a6c540cda450e53f6ded89708d2e2320194723c0e2 +size 54569 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png index 26014a12c..c8c5c6899 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87e34024f701dc93f4026213ac7eb468a2cd6d3393eb0dbec382bf58007f8e61 -size 55042 +oid sha256:05a2778e7da867ab46a6760ea3925a2398c6b9a21d2767aef11cf98ef5292c82 +size 55034 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png index dcbbba2b6..8bfc2f47a 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7940ff56796efb27bec66b632ff33aa2ad390c4962a711bf520aee341f035a4 -size 35968 +oid sha256:686e37635da6ba218c9539f8b145239bbed2bab6696384ed1cb725db657ec642 +size 35961 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png index 0a3d062af..274e577d5 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7bbd16c8aad444f0d11aacf87cf2292d494cc80a1ca46e7e8db86ca3041d35a -size 35931 +oid sha256:151171625b9cb8eaac3fb83260c6cc76cbf66003d9a940be1d5021a3303956c9 +size 35923 diff --git a/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x1.png b/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x1.png index 112605454..408cba0b8 100644 --- a/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x1.png +++ b/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bbdc4199dee2ae853b8a240cd84528482dc6762233bd0d1249f2daa296b49487 -size 64172 +oid sha256:79d2935aa8f0f6941167b69840142599a2994a5eaa239757d91847d4d6533174 +size 64165 diff --git a/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x2.png b/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x2.png index 1b5b60c8a..9aa9c130a 100644 --- a/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x2.png +++ b/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6d38b6b47839d0e4eae530d203c83971fba8a41c9caa3d5b5d89ee7ed582613 -size 150090 +oid sha256:a5665e3a715ca7576df5b63af14b871f355d3e1db801c20089a60640373388ff +size 150095 diff --git a/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x1.png b/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x1.png index 5a2b44feb..ef68941bc 100644 --- a/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x1.png +++ b/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0635f1564d6c9707efa68003fb8c9b6eb00408aa8f24c972e33c6c79fed5bdf -size 59354 +oid sha256:bd31bddf25cf93d3ae79ce9b314cf3a3ebbf8c3b6cae2027f3f3b1593ac293e5 +size 59346 diff --git a/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x2.png b/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x2.png index 81c7452e6..67d8979ff 100644 --- a/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x2.png +++ b/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4288ee4a0d2229d59c31538179cdda50035a3849f69b400127e1618efe30cdc1 -size 145224 +oid sha256:04dd62767ae9c18b5e89cea5bdd243b66c5986bfdb71fb9b01772ab9d150ab7e +size 145223 diff --git a/tests/egui_tests/tests/snapshots/layout/drag_value.png b/tests/egui_tests/tests/snapshots/layout/drag_value.png index 44bf0bfcb..86dfef652 100644 --- a/tests/egui_tests/tests/snapshots/layout/drag_value.png +++ b/tests/egui_tests/tests/snapshots/layout/drag_value.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2cd4d27748e193d4f46ad7a5be6ff411ad3152b4fd546c0dc98dd3bb5333d93 -size 236090 +oid sha256:96c78de8d82a5cb4e91912823b88bc0465bf67f09b500e5bde8f43b001f35a66 +size 264421 diff --git a/tests/egui_tests/tests/snapshots/visuals/drag_value.png b/tests/egui_tests/tests/snapshots/visuals/drag_value.png index 2b04df5a5..463c4cbef 100644 --- a/tests/egui_tests/tests/snapshots/visuals/drag_value.png +++ b/tests/egui_tests/tests/snapshots/visuals/drag_value.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d42e002c3fd34f96d58ddfd4d2f91cf1ac7755ff71b5da315be4bee6bf00e03 -size 8411 +oid sha256:0f6babaa4f9359517f58b1160a915069c56c338b7c0d8d4306cde67628442397 +size 8995 diff --git a/tests/egui_tests/tests/test_widgets.rs b/tests/egui_tests/tests/test_widgets.rs index a53ad4a0c..5ef98c8a8 100644 --- a/tests/egui_tests/tests/test_widgets.rs +++ b/tests/egui_tests/tests/test_widgets.rs @@ -4,8 +4,9 @@ use egui::accesskit::Role; use egui::load::SizedTexture; use egui::{ Align, AtomExt as _, AtomLayout, Button, Color32, ColorImage, Direction, DragValue, Event, - Grid, IntoAtoms as _, Layout, PointerButton, Response, Slider, Stroke, StrokeKind, TextEdit, - TextWrapMode, TextureHandle, TextureOptions, Ui, UiBuilder, Vec2, Widget as _, include_image, + Grid, IntoAtoms as _, Layout, PointerButton, Response, RichText, Slider, Stroke, StrokeKind, + TextEdit, TextWrapMode, TextureHandle, TextureOptions, Ui, UiBuilder, Vec2, Widget as _, + include_image, }; use egui_kittest::kittest::{Queryable as _, by}; use egui_kittest::{Harness, Node, SnapshotResult, SnapshotResults}; @@ -74,7 +75,11 @@ fn widget_tests() { test_widget( "drag_value", - |ui| DragValue::new(&mut 12.0).ui(ui), + |ui| { + DragValue::new(&mut 12.0) + .suffix(RichText::new(" px").weak().small()) + .ui(ui) + }, &mut results, );