egui/atomics/
atom.rs

1use crate::{AtomKind, Id, SizedAtom, Ui};
2use emath::{NumExt as _, Vec2};
3use epaint::text::TextWrapMode;
4
5/// A low-level ui building block.
6///
7/// Implements [`From`] for [`String`], [`str`], [`crate::Image`] and much more for convenience.
8/// You can directly call the `atom_*` methods on anything that implements `Into<Atom>`.
9/// ```
10/// # use egui::{Image, emath::Vec2};
11/// use egui::AtomExt as _;
12/// let string_atom = "Hello".atom_grow(true);
13/// let image_atom = Image::new("some_image_url").atom_size(Vec2::splat(20.0));
14/// ```
15#[derive(Clone, Debug)]
16pub struct Atom<'a> {
17    /// See [`crate::AtomExt::atom_size`]
18    pub size: Option<Vec2>,
19
20    /// See [`crate::AtomExt::atom_max_size`]
21    pub max_size: Vec2,
22
23    /// See [`crate::AtomExt::atom_grow`]
24    pub grow: bool,
25
26    /// See [`crate::AtomExt::atom_shrink`]
27    pub shrink: bool,
28
29    /// The atom type
30    pub kind: AtomKind<'a>,
31}
32
33impl Default for Atom<'_> {
34    fn default() -> Self {
35        Atom {
36            size: None,
37            max_size: Vec2::INFINITY,
38            grow: false,
39            shrink: false,
40            kind: AtomKind::Empty,
41        }
42    }
43}
44
45impl<'a> Atom<'a> {
46    /// Create an empty [`Atom`] marked as `grow`.
47    ///
48    /// This will expand in size, allowing all preceding atoms to be left-aligned,
49    /// and all following atoms to be right-aligned
50    pub fn grow() -> Self {
51        Atom {
52            grow: true,
53            ..Default::default()
54        }
55    }
56
57    /// Create a [`AtomKind::Custom`] with a specific size.
58    pub fn custom(id: Id, size: impl Into<Vec2>) -> Self {
59        Atom {
60            size: Some(size.into()),
61            kind: AtomKind::Custom(id),
62            ..Default::default()
63        }
64    }
65
66    /// Turn this into a [`SizedAtom`].
67    pub fn into_sized(
68        self,
69        ui: &Ui,
70        mut available_size: Vec2,
71        mut wrap_mode: Option<TextWrapMode>,
72    ) -> SizedAtom<'a> {
73        if !self.shrink && self.max_size.x.is_infinite() {
74            wrap_mode = Some(TextWrapMode::Extend);
75        }
76        available_size = available_size.at_most(self.max_size);
77        if let Some(size) = self.size {
78            available_size = available_size.at_most(size);
79        }
80        if self.max_size.x.is_finite() {
81            wrap_mode = Some(TextWrapMode::Truncate);
82        }
83
84        let (intrinsic, kind) = self.kind.into_sized(ui, available_size, wrap_mode);
85
86        let size = self
87            .size
88            .map_or_else(|| kind.size(), |s| s.at_most(self.max_size));
89
90        SizedAtom {
91            size,
92            intrinsic_size: intrinsic.at_least(self.size.unwrap_or_default()),
93            grow: self.grow,
94            kind,
95        }
96    }
97}
98
99impl<'a, T> From<T> for Atom<'a>
100where
101    T: Into<AtomKind<'a>>,
102{
103    fn from(value: T) -> Self {
104        Atom {
105            kind: value.into(),
106            ..Default::default()
107        }
108    }
109}