mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -04:00
Add Atom prefix/suffix support to DragValue (#7949)
This commit is contained in:
@@ -26,6 +26,18 @@ impl<'a> Atoms<'a> {
|
|||||||
self.0.insert(0, atom.into());
|
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.
|
/// Concatenate and return the text contents.
|
||||||
// TODO(lucasmerlin): It might not always make sense to return the concatenated text, e.g.
|
// 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.
|
// in a submenu button there is a right text '⏵' which is now passed to the screen reader.
|
||||||
|
|||||||
@@ -259,6 +259,13 @@ impl<'a> Button<'a> {
|
|||||||
self
|
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.
|
/// Show the button and return a [`AtomLayoutResponse`] for painting custom contents.
|
||||||
pub fn atom_ui(self, ui: &mut Ui) -> AtomLayoutResponse {
|
pub fn atom_ui(self, ui: &mut Ui) -> AtomLayoutResponse {
|
||||||
let Button {
|
let Button {
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
#![expect(clippy::needless_pass_by_value)] // False positives with `impl ToString`
|
|
||||||
|
|
||||||
use std::{cmp::Ordering, ops::RangeInclusive};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Button, CursorIcon, Id, Key, MINUS_CHAR_STR, Modifiers, NumExt as _, Response, RichText, Sense,
|
Atom, AtomExt as _, AtomKind, Atoms, Button, CursorIcon, Id, IntoAtoms, Key, MINUS_CHAR_STR,
|
||||||
TextEdit, TextWrapMode, Ui, Widget, WidgetInfo, emath, text,
|
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> {
|
pub struct DragValue<'a> {
|
||||||
get_set_value: GetSetValue<'a>,
|
get_set_value: GetSetValue<'a>,
|
||||||
speed: f64,
|
speed: f64,
|
||||||
prefix: String,
|
atoms: Atoms<'a>,
|
||||||
suffix: String,
|
|
||||||
range: RangeInclusive<f64>,
|
range: RangeInclusive<f64>,
|
||||||
clamp_existing_to_range: bool,
|
clamp_existing_to_range: bool,
|
||||||
min_decimals: usize,
|
min_decimals: usize,
|
||||||
@@ -50,6 +48,8 @@ pub struct DragValue<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DragValue<'a> {
|
impl<'a> DragValue<'a> {
|
||||||
|
const ATOM_ID: &'static str = "drag_item";
|
||||||
|
|
||||||
pub fn new<Num: emath::Numeric>(value: &'a mut Num) -> Self {
|
pub fn new<Num: emath::Numeric>(value: &'a mut Num) -> Self {
|
||||||
let slf = Self::from_get_set(move |v: Option<f64>| {
|
let slf = Self::from_get_set(move |v: Option<f64>| {
|
||||||
if let Some(v) = v {
|
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>) -> f64) -> Self {
|
pub fn from_get_set(get_set_value: impl 'a + FnMut(Option<f64>) -> f64) -> Self {
|
||||||
|
let atoms = Atoms::new(Atom::custom(Id::new(Self::ATOM_ID), Vec2::ZERO).atom_grow(true));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
get_set_value: Box::new(get_set_value),
|
get_set_value: Box::new(get_set_value),
|
||||||
speed: 1.0,
|
speed: 1.0,
|
||||||
prefix: Default::default(),
|
atoms,
|
||||||
suffix: Default::default(),
|
|
||||||
range: f64::NEG_INFINITY..=f64::INFINITY,
|
range: f64::NEG_INFINITY..=f64::INFINITY,
|
||||||
clamp_existing_to_range: true,
|
clamp_existing_to_range: true,
|
||||||
min_decimals: 0,
|
min_decimals: 0,
|
||||||
@@ -164,15 +165,15 @@ impl<'a> DragValue<'a> {
|
|||||||
|
|
||||||
/// Show a prefix before the number, e.g. "x: "
|
/// Show a prefix before the number, e.g. "x: "
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prefix(mut self, prefix: impl ToString) -> Self {
|
pub fn prefix(mut self, prefix: impl IntoAtoms<'a>) -> Self {
|
||||||
self.prefix = prefix.to_string();
|
self.atoms.extend_left(prefix);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a suffix to the number, this can be e.g. a unit ("°" or " m")
|
/// Add a suffix to the number, this can be e.g. a unit ("°" or " m")
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn suffix(mut self, suffix: impl ToString) -> Self {
|
pub fn suffix(mut self, suffix: impl IntoAtoms<'a>) -> Self {
|
||||||
self.suffix = suffix.to_string();
|
self.atoms.extend_right(suffix);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,8 +434,7 @@ impl Widget for DragValue<'_> {
|
|||||||
speed,
|
speed,
|
||||||
range,
|
range,
|
||||||
clamp_existing_to_range,
|
clamp_existing_to_range,
|
||||||
prefix,
|
mut atoms,
|
||||||
suffix,
|
|
||||||
min_decimals,
|
min_decimals,
|
||||||
max_decimals,
|
max_decimals,
|
||||||
custom_formatter,
|
custom_formatter,
|
||||||
@@ -442,6 +442,26 @@ impl Widget for DragValue<'_> {
|
|||||||
update_while_editing,
|
update_while_editing,
|
||||||
} = self;
|
} = 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());
|
let shift = ui.input(|i| i.modifiers.shift_only());
|
||||||
// The widget has the same ID whether it's in edit or button mode.
|
// The widget has the same ID whether it's in edit or button mode.
|
||||||
let id = ui.next_auto_id();
|
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 response = if is_kb_editing {
|
||||||
let mut value_text = ui
|
let mut value_text = ui
|
||||||
.data_mut(|data| data.remove_temp::<String>(id))
|
.data_mut(|data| data.remove_temp::<String>(id))
|
||||||
@@ -586,13 +604,22 @@ impl Widget for DragValue<'_> {
|
|||||||
ui.data_mut(|data| data.insert_temp(id, value_text));
|
ui.data_mut(|data| data.insert_temp(id, value_text));
|
||||||
response
|
response
|
||||||
} else {
|
} else {
|
||||||
let button = Button::new(
|
atoms.map_atoms(|atom| {
|
||||||
RichText::new(format!("{}{}{}", prefix, value_text.clone(), suffix))
|
if let AtomKind::Custom(id) = atom.kind
|
||||||
.text_style(text_style),
|
&& id == atom_id
|
||||||
)
|
{
|
||||||
.wrap_mode(TextWrapMode::Extend)
|
RichText::new(value_text.clone())
|
||||||
.sense(Sense::click_and_drag())
|
.text_style(text_style.clone())
|
||||||
.min_size(ui.spacing().interact_size); // TODO(emilk): find some more generic solution to `min_size`
|
.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() {
|
let cursor_icon = if value <= *range.start() {
|
||||||
CursorIcon::ResizeEast
|
CursorIcon::ResizeEast
|
||||||
@@ -607,10 +634,8 @@ impl Widget for DragValue<'_> {
|
|||||||
|
|
||||||
if ui.style().explanation_tooltips {
|
if ui.style().explanation_tooltips {
|
||||||
response = response.on_hover_text(format!(
|
response = response.on_hover_text(format!(
|
||||||
"{}{}{}\nDrag to edit or click to enter a value.\nPress 'Shift' while dragging for better control.",
|
"{}\nDrag to edit or click to enter a value.\nPress 'Shift' while dragging for better control.",
|
||||||
prefix,
|
|
||||||
value as f32, // Show full precision value on-hover. TODO(emilk): figure out f64 vs f32
|
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
|
// The value is exposed as a string by the text edit widget
|
||||||
// when in edit mode.
|
// when in edit mode.
|
||||||
if !is_kb_editing {
|
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);
|
builder.set_value(value_text);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9302478abb0b86fae1af3af45d91f032272a56a2098405525d08aba4f9534644
|
oid sha256:9d6ba2c4825517b4cc030b7639771d06913da86c2d52fd40e6263692335afa04
|
||||||
size 76103
|
size 76079
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f5e67baf0696792e50f7ab3121874d055ddee2de0514712aacbf8e135ec4743d
|
oid sha256:a169cda21797152fb8aa69928ad3f4cef1b45cc5f213e5bfa01b8fe7723a4852
|
||||||
size 25425
|
size 25391
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:7336c53885add09360df098b6b131323e8ad3ef0ec2b85bf022e78bc4269276a
|
oid sha256:01b34a7371dd8b3539ed20594a42f3cd9792d391d2cb44740aa5ef301c9652f3
|
||||||
size 70255
|
size 70244
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a2b7b54a1af0f5cd31bd64f0506e3035dd423314ce3389e61730fa160434fbf3
|
oid sha256:0574527ce659559f5b0709d84903afcec60b4ffa6b7979e8985027d326fd782a
|
||||||
size 45074
|
size 45066
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:7b66a0be67ff2d684a54c2321123521b3ad06dfe5ebffd50e89260d77efcfcc4
|
oid sha256:50eca0feefe5d43db74f5e3bc08abda13c5986710cc4aaa03e9382af56264fc2
|
||||||
size 86833
|
size 86826
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:19320291c99a23429b114a59de4636689e281e1e68766abe2aa1e56562128e50
|
oid sha256:91110d6a1da995e3e215cc92fdcceac84335e60b5b2fdbb2f16d5ecc6065fe55
|
||||||
size 118919
|
size 118912
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:5edf089c00715f1456fe7838e85aadcfc42b6216a3fd95b48d9c21fc8d700cba
|
oid sha256:31464d796f1660bdd5c98cf73186d7b68918fd42f292bc03e274e43d995edc16
|
||||||
size 51371
|
size 51363
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:6cd1a10639dcb323bdc3b2c43e0c35665184fc809731ced90088ee9edb9de845
|
oid sha256:9dca12eb3b99976db20c77a6c540cda450e53f6ded89708d2e2320194723c0e2
|
||||||
size 54577
|
size 54569
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:87e34024f701dc93f4026213ac7eb468a2cd6d3393eb0dbec382bf58007f8e61
|
oid sha256:05a2778e7da867ab46a6760ea3925a2398c6b9a21d2767aef11cf98ef5292c82
|
||||||
size 55042
|
size 55034
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d7940ff56796efb27bec66b632ff33aa2ad390c4962a711bf520aee341f035a4
|
oid sha256:686e37635da6ba218c9539f8b145239bbed2bab6696384ed1cb725db657ec642
|
||||||
size 35968
|
size 35961
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b7bbd16c8aad444f0d11aacf87cf2292d494cc80a1ca46e7e8db86ca3041d35a
|
oid sha256:151171625b9cb8eaac3fb83260c6cc76cbf66003d9a940be1d5021a3303956c9
|
||||||
size 35931
|
size 35923
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:bbdc4199dee2ae853b8a240cd84528482dc6762233bd0d1249f2daa296b49487
|
oid sha256:79d2935aa8f0f6941167b69840142599a2994a5eaa239757d91847d4d6533174
|
||||||
size 64172
|
size 64165
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f6d38b6b47839d0e4eae530d203c83971fba8a41c9caa3d5b5d89ee7ed582613
|
oid sha256:a5665e3a715ca7576df5b63af14b871f355d3e1db801c20089a60640373388ff
|
||||||
size 150090
|
size 150095
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c0635f1564d6c9707efa68003fb8c9b6eb00408aa8f24c972e33c6c79fed5bdf
|
oid sha256:bd31bddf25cf93d3ae79ce9b314cf3a3ebbf8c3b6cae2027f3f3b1593ac293e5
|
||||||
size 59354
|
size 59346
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4288ee4a0d2229d59c31538179cdda50035a3849f69b400127e1618efe30cdc1
|
oid sha256:04dd62767ae9c18b5e89cea5bdd243b66c5986bfdb71fb9b01772ab9d150ab7e
|
||||||
size 145224
|
size 145223
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b2cd4d27748e193d4f46ad7a5be6ff411ad3152b4fd546c0dc98dd3bb5333d93
|
oid sha256:96c78de8d82a5cb4e91912823b88bc0465bf67f09b500e5bde8f43b001f35a66
|
||||||
size 236090
|
size 264421
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:3d42e002c3fd34f96d58ddfd4d2f91cf1ac7755ff71b5da315be4bee6bf00e03
|
oid sha256:0f6babaa4f9359517f58b1160a915069c56c338b7c0d8d4306cde67628442397
|
||||||
size 8411
|
size 8995
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ use egui::accesskit::Role;
|
|||||||
use egui::load::SizedTexture;
|
use egui::load::SizedTexture;
|
||||||
use egui::{
|
use egui::{
|
||||||
Align, AtomExt as _, AtomLayout, Button, Color32, ColorImage, Direction, DragValue, Event,
|
Align, AtomExt as _, AtomLayout, Button, Color32, ColorImage, Direction, DragValue, Event,
|
||||||
Grid, IntoAtoms as _, Layout, PointerButton, Response, Slider, Stroke, StrokeKind, TextEdit,
|
Grid, IntoAtoms as _, Layout, PointerButton, Response, RichText, Slider, Stroke, StrokeKind,
|
||||||
TextWrapMode, TextureHandle, TextureOptions, Ui, UiBuilder, Vec2, Widget as _, include_image,
|
TextEdit, TextWrapMode, TextureHandle, TextureOptions, Ui, UiBuilder, Vec2, Widget as _,
|
||||||
|
include_image,
|
||||||
};
|
};
|
||||||
use egui_kittest::kittest::{Queryable as _, by};
|
use egui_kittest::kittest::{Queryable as _, by};
|
||||||
use egui_kittest::{Harness, Node, SnapshotResult, SnapshotResults};
|
use egui_kittest::{Harness, Node, SnapshotResult, SnapshotResults};
|
||||||
@@ -74,7 +75,11 @@ fn widget_tests() {
|
|||||||
|
|
||||||
test_widget(
|
test_widget(
|
||||||
"drag_value",
|
"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,
|
&mut results,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user