1use egui::scroll_area::{ScrollBarVisibility, ScrollSource};
39use egui::Ui;
40
41use crate::ViewCtx;
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
45pub enum ScrollDirection {
46 #[default]
47 Vertical,
48 Horizontal,
49 Both,
50}
51
52#[derive(Clone)]
54pub struct ScrollArea {
55 direction: ScrollDirection,
56 id_salt: Option<egui::Id>,
57 max_height: Option<f32>,
58 max_width: Option<f32>,
59 min_scrolled_height: Option<f32>,
60 min_scrolled_width: Option<f32>,
61 auto_shrink: [bool; 2],
62 scroll_bar_visibility: ScrollBarVisibility,
63 animated: bool,
64 enable_scrolling: bool,
65 scroll_offset: Option<egui::Vec2>,
66}
67
68impl Default for ScrollArea {
69 fn default() -> Self {
70 Self {
71 direction: ScrollDirection::Vertical,
72 id_salt: None,
73 max_height: None,
74 max_width: None,
75 min_scrolled_height: None,
76 min_scrolled_width: None,
77 auto_shrink: [true; 2],
78 scroll_bar_visibility: ScrollBarVisibility::VisibleWhenNeeded,
79 animated: true,
80 enable_scrolling: true,
81 scroll_offset: None,
82 }
83 }
84}
85
86impl ScrollArea {
87 pub fn new() -> Self {
89 Self::default()
90 }
91
92 pub fn vertical() -> Self {
94 Self {
95 direction: ScrollDirection::Vertical,
96 ..Default::default()
97 }
98 }
99
100 pub fn horizontal() -> Self {
102 Self {
103 direction: ScrollDirection::Horizontal,
104 ..Default::default()
105 }
106 }
107
108 pub fn both() -> Self {
110 Self {
111 direction: ScrollDirection::Both,
112 ..Default::default()
113 }
114 }
115
116 pub fn id_salt(mut self, id: impl std::hash::Hash) -> Self {
118 self.id_salt = Some(egui::Id::new(id));
119 self
120 }
121
122 pub fn max_height(mut self, max_height: f32) -> Self {
124 self.max_height = Some(max_height);
125 self
126 }
127
128 pub fn max_width(mut self, max_width: f32) -> Self {
130 self.max_width = Some(max_width);
131 self
132 }
133
134 pub fn min_scrolled_height(mut self, height: f32) -> Self {
136 self.min_scrolled_height = Some(height);
137 self
138 }
139
140 pub fn min_scrolled_width(mut self, width: f32) -> Self {
142 self.min_scrolled_width = Some(width);
143 self
144 }
145
146 pub fn auto_shrink(mut self, auto_shrink: [bool; 2]) -> Self {
151 self.auto_shrink = auto_shrink;
152 self
153 }
154
155 pub fn no_shrink(mut self) -> Self {
157 self.auto_shrink = [false, false];
158 self
159 }
160
161 pub fn scroll_bar_visibility(mut self, visibility: ScrollBarVisibility) -> Self {
163 self.scroll_bar_visibility = visibility;
164 self
165 }
166
167 pub fn always_show_scroll(mut self) -> Self {
169 self.scroll_bar_visibility = ScrollBarVisibility::AlwaysVisible;
170 self
171 }
172
173 pub fn hide_scroll(mut self) -> Self {
175 self.scroll_bar_visibility = ScrollBarVisibility::AlwaysHidden;
176 self
177 }
178
179 pub fn animated(mut self, animated: bool) -> Self {
181 self.animated = animated;
182 self
183 }
184
185 pub fn enable_scrolling(mut self, enable: bool) -> Self {
187 self.enable_scrolling = enable;
188 self
189 }
190
191 pub fn scroll_offset(mut self, offset: egui::Vec2) -> Self {
193 self.scroll_offset = Some(offset);
194 self
195 }
196
197 pub fn show_ctx<Msg, R>(
199 self,
200 ctx: &mut ViewCtx<'_, Msg>,
201 f: impl FnOnce(&mut ViewCtx<'_, Msg>) -> R,
202 ) -> R {
203 let area = self.build();
204 ctx.scroll_area_with(|_| area, f)
205 }
206
207 pub fn show<R>(
209 self,
210 ui: &mut Ui,
211 f: impl FnOnce(&mut Ui) -> R,
212 ) -> egui::scroll_area::ScrollAreaOutput<R> {
213 self.build().show(ui, f)
214 }
215
216 fn build(self) -> egui::ScrollArea {
218 let mut area = match self.direction {
219 ScrollDirection::Vertical => egui::ScrollArea::vertical(),
220 ScrollDirection::Horizontal => egui::ScrollArea::horizontal(),
221 ScrollDirection::Both => egui::ScrollArea::both(),
222 };
223
224 if let Some(id) = self.id_salt {
225 area = area.id_salt(id);
226 }
227
228 if let Some(h) = self.max_height {
229 area = area.max_height(h);
230 }
231
232 if let Some(w) = self.max_width {
233 area = area.max_width(w);
234 }
235
236 if let Some(h) = self.min_scrolled_height {
237 area = area.min_scrolled_height(h);
238 }
239
240 if let Some(w) = self.min_scrolled_width {
241 area = area.min_scrolled_width(w);
242 }
243
244 area = area.auto_shrink(self.auto_shrink);
245 area = area.scroll_bar_visibility(self.scroll_bar_visibility);
246 area = area.animated(self.animated);
247 area = area.scroll_source(if self.enable_scrolling {
248 ScrollSource::ALL
249 } else {
250 ScrollSource::NONE
251 });
252
253 if let Some(offset) = self.scroll_offset {
254 area = area.vertical_scroll_offset(offset.y);
255 area = area.horizontal_scroll_offset(offset.x);
256 }
257
258 area
259 }
260}