gpui_ui_kit/src-app/
miniapp.rs1use gpui::*;
28
29#[derive(Clone)]
31pub struct MiniAppConfig {
32 pub title: SharedString,
34 pub width: f32,
36 pub height: f32,
38 pub app_name: SharedString,
40 pub scrollable: bool,
42}
43
44impl MiniAppConfig {
45 pub fn new(title: impl Into<SharedString>) -> Self {
49 let title = title.into();
50 Self {
51 title: title.clone(),
52 width: 900.0,
53 height: 700.0,
54 app_name: title,
55 scrollable: true,
56 }
57 }
58
59 pub fn size(mut self, width: f32, height: f32) -> Self {
61 self.width = width;
62 self.height = height;
63 self
64 }
65
66 pub fn app_name(mut self, name: impl Into<SharedString>) -> Self {
70 self.app_name = name.into();
71 self
72 }
73
74 pub fn scrollable(mut self, scrollable: bool) -> Self {
78 self.scrollable = scrollable;
79 self
80 }
81}
82
83impl Default for MiniAppConfig {
84 fn default() -> Self {
85 Self::new("MiniApp")
86 }
87}
88
89actions!(miniapp, [Quit]);
91
92struct ScrollableWrapper {
94 inner: AnyView,
95}
96
97impl Render for ScrollableWrapper {
98 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
99 div()
100 .id("miniapp-scroll-container")
101 .size_full()
102 .overflow_y_scroll()
103 .child(self.inner.clone())
104 }
105}
106
107pub struct MiniApp;
115
116impl MiniApp {
117 pub fn run<V, F>(config: MiniAppConfig, build_view: F)
138 where
139 V: Render + 'static,
140 F: FnOnce(&mut App) -> Entity<V> + 'static,
141 {
142 let config_clone = config.clone();
143
144 Application::new().run(move |cx: &mut App| {
145 cx.on_action::<Quit>(|_action, cx| {
147 cx.quit();
148 });
149
150 let quit_label: SharedString = format!("Quit {}", config_clone.app_name).into();
152 cx.set_menus(vec![Menu {
153 name: config_clone.app_name.clone(),
154 items: vec![MenuItem::action(quit_label, Quit)],
155 }]);
156
157 cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
159
160 let bounds = Bounds::centered(
162 None,
163 size(px(config_clone.width), px(config_clone.height)),
164 cx,
165 );
166
167 if config_clone.scrollable {
168 cx.open_window(
169 WindowOptions {
170 window_bounds: Some(WindowBounds::Windowed(bounds)),
171 titlebar: Some(TitlebarOptions {
172 title: Some(config_clone.title.clone()),
173 ..Default::default()
174 }),
175 ..Default::default()
176 },
177 move |_, cx| {
178 let inner_view = build_view(cx);
179 cx.new(|_| ScrollableWrapper {
180 inner: inner_view.into(),
181 })
182 },
183 )
184 .unwrap();
185 } else {
186 cx.open_window(
187 WindowOptions {
188 window_bounds: Some(WindowBounds::Windowed(bounds)),
189 titlebar: Some(TitlebarOptions {
190 title: Some(config_clone.title.clone()),
191 ..Default::default()
192 }),
193 ..Default::default()
194 },
195 |_, cx| build_view(cx),
196 )
197 .unwrap();
198 }
199
200 cx.activate(true);
201 });
202 }
203
204 pub fn run_default<V, F>(build_view: F)
208 where
209 V: Render + 'static,
210 F: FnOnce(&mut App) -> Entity<V> + 'static,
211 {
212 Self::run(MiniAppConfig::default(), build_view);
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::MiniAppConfig;
219
220 #[test]
221 fn test_config_new() {
222 let config = MiniAppConfig::new("Test App");
223 assert_eq!(config.title.as_ref(), "Test App");
224 assert_eq!(config.app_name.as_ref(), "Test App");
225 assert_eq!(config.width, 900.0);
226 assert_eq!(config.height, 700.0);
227 }
228
229 #[test]
230 fn test_config_size() {
231 let config = MiniAppConfig::new("Test").size(1200.0, 800.0);
232 assert_eq!(config.width, 1200.0);
233 assert_eq!(config.height, 800.0);
234 }
235
236 #[test]
237 fn test_config_app_name() {
238 let config = MiniAppConfig::new("Window Title").app_name("Menu Name");
239 assert_eq!(config.title.as_ref(), "Window Title");
240 assert_eq!(config.app_name.as_ref(), "Menu Name");
241 }
242
243 #[test]
244 fn test_config_default() {
245 let config = MiniAppConfig::default();
246 assert_eq!(config.title.as_ref(), "MiniApp");
247 }
248
249 #[test]
250 fn test_config_builder_chain() {
251 let config = MiniAppConfig::new("Demo")
252 .size(1000.0, 600.0)
253 .app_name("My Demo App");
254
255 assert_eq!(config.title.as_ref(), "Demo");
256 assert_eq!(config.width, 1000.0);
257 assert_eq!(config.height, 600.0);
258 assert_eq!(config.app_name.as_ref(), "My Demo App");
259 }
260}