egui_extras/
strip.rs
1use crate::{
2 layout::{CellDirection, CellSize, StripLayout, StripLayoutFlags},
3 sizing::Sizing,
4 Size,
5};
6use egui::{Response, Ui};
7
8pub struct StripBuilder<'a> {
45 ui: &'a mut Ui,
46 sizing: Sizing,
47 clip: bool,
48 cell_layout: egui::Layout,
49 sense: egui::Sense,
50}
51
52impl<'a> StripBuilder<'a> {
53 pub fn new(ui: &'a mut Ui) -> Self {
55 let cell_layout = *ui.layout();
56 Self {
57 ui,
58 sizing: Default::default(),
59 clip: false,
60 cell_layout,
61 sense: egui::Sense::hover(),
62 }
63 }
64
65 #[inline]
67 pub fn clip(mut self, clip: bool) -> Self {
68 self.clip = clip;
69 self
70 }
71
72 #[inline]
74 pub fn cell_layout(mut self, cell_layout: egui::Layout) -> Self {
75 self.cell_layout = cell_layout;
76 self
77 }
78
79 #[inline]
81 pub fn sense(mut self, sense: egui::Sense) -> Self {
82 self.sense = sense;
83 self
84 }
85
86 #[inline]
88 pub fn size(mut self, size: Size) -> Self {
89 self.sizing.add(size);
90 self
91 }
92
93 #[inline]
95 pub fn sizes(mut self, size: Size, count: usize) -> Self {
96 for _ in 0..count {
97 self.sizing.add(size);
98 }
99 self
100 }
101
102 pub fn horizontal<F>(self, strip: F) -> Response
107 where
108 F: for<'b> FnOnce(Strip<'a, 'b>),
109 {
110 let widths = self.sizing.to_lengths(
111 self.ui.available_rect_before_wrap().width(),
112 self.ui.spacing().item_spacing.x,
113 );
114 let mut layout = StripLayout::new(
115 self.ui,
116 CellDirection::Horizontal,
117 self.cell_layout,
118 self.sense,
119 );
120 strip(Strip {
121 layout: &mut layout,
122 direction: CellDirection::Horizontal,
123 clip: self.clip,
124 sizes: widths,
125 size_index: 0,
126 });
127 layout.allocate_rect()
128 }
129
130 pub fn vertical<F>(self, strip: F) -> Response
135 where
136 F: for<'b> FnOnce(Strip<'a, 'b>),
137 {
138 let heights = self.sizing.to_lengths(
139 self.ui.available_rect_before_wrap().height(),
140 self.ui.spacing().item_spacing.y,
141 );
142 let mut layout = StripLayout::new(
143 self.ui,
144 CellDirection::Vertical,
145 self.cell_layout,
146 self.sense,
147 );
148 strip(Strip {
149 layout: &mut layout,
150 direction: CellDirection::Vertical,
151 clip: self.clip,
152 sizes: heights,
153 size_index: 0,
154 });
155 layout.allocate_rect()
156 }
157}
158
159pub struct Strip<'a, 'b> {
162 layout: &'b mut StripLayout<'a>,
163 direction: CellDirection,
164 clip: bool,
165 sizes: Vec<f32>,
166 size_index: usize,
167}
168
169impl<'a, 'b> Strip<'a, 'b> {
170 #[cfg_attr(debug_assertions, track_caller)]
171 fn next_cell_size(&mut self) -> (CellSize, CellSize) {
172 let size = if let Some(size) = self.sizes.get(self.size_index) {
173 self.size_index += 1;
174 *size
175 } else {
176 crate::log_or_panic!(
177 "Added more `Strip` cells than were pre-allocated ({} pre-allocated)",
178 self.sizes.len()
179 );
180 8.0 };
182
183 match self.direction {
184 CellDirection::Horizontal => (CellSize::Absolute(size), CellSize::Remainder),
185 CellDirection::Vertical => (CellSize::Remainder, CellSize::Absolute(size)),
186 }
187 }
188
189 #[cfg_attr(debug_assertions, track_caller)]
191 pub fn cell(&mut self, add_contents: impl FnOnce(&mut Ui)) {
192 let (width, height) = self.next_cell_size();
193 let flags = StripLayoutFlags {
194 clip: self.clip,
195 ..Default::default()
196 };
197 self.layout.add(
198 flags,
199 width,
200 height,
201 egui::Id::new(self.size_index),
202 add_contents,
203 );
204 }
205
206 #[cfg_attr(debug_assertions, track_caller)]
208 pub fn empty(&mut self) {
209 let (width, height) = self.next_cell_size();
210 self.layout.empty(width, height);
211 }
212
213 pub fn strip(&mut self, strip_builder: impl FnOnce(StripBuilder<'_>)) {
215 let clip = self.clip;
216 self.cell(|ui| {
217 strip_builder(StripBuilder::new(ui).clip(clip));
218 });
219 }
220}
221
222impl<'a, 'b> Drop for Strip<'a, 'b> {
223 fn drop(&mut self) {
224 while self.size_index < self.sizes.len() {
225 self.empty();
226 }
227 }
228}