1mod access;
39mod gpu;
40mod translate;
41
42use std::error::Error;
43use std::fmt;
44use std::sync::Arc;
45use std::time::Instant;
46
47use densa_core::{Point, Size};
48use densa_draw::{Canvas, InstanceBuffer};
49use densa_paragraph::TextSystem;
50use densa_text::{AtlasConfig, GlyphCache};
51use densa_ui::{Action, Cursor, KeyEvent, PointerEvent, PointerKind, Theme, Ui};
52use winit::application::ApplicationHandler;
53use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
54use winit::event::{ElementState, WindowEvent};
55use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy};
56use winit::keyboard::ModifiersState;
57use winit::window::{Window, WindowId};
58
59use crate::gpu::Gpu;
60
61pub trait App: 'static {
65 fn build(&mut self, ui: &mut Ui, host: &mut Host<'_>) -> Result<(), Box<dyn Error>>;
70
71 fn action(&mut self, ui: &mut Ui, host: &mut Host<'_>, action: Action) {
74 let _ = (ui, host, action);
75 }
76
77 fn frame(&mut self, ui: &mut Ui, host: &mut Host<'_>) -> bool {
81 let _ = (ui, host);
82 false
83 }
84}
85
86#[derive(Debug)]
88pub struct Host<'a> {
89 window: &'a Window,
90 proxy: &'a EventLoopProxy<UserEvent>,
91 close: &'a mut bool,
92}
93
94impl Host<'_> {
95 pub fn waker(&self) -> Waker {
98 Waker(self.proxy.clone())
99 }
100
101 pub fn set_title(&self, title: &str) {
103 self.window.set_title(title);
104 }
105
106 pub fn close(&mut self) {
108 *self.close = true;
109 }
110}
111
112#[derive(Debug, Clone)]
114pub struct Waker(EventLoopProxy<UserEvent>);
115
116impl Waker {
117 pub fn wake(&self) -> bool {
120 self.0.send_event(UserEvent::Wake).is_ok()
121 }
122}
123
124#[derive(Debug)]
127enum UserEvent {
128 Wake,
129 Access(accesskit_winit::Event),
130}
131
132impl From<accesskit_winit::Event> for UserEvent {
133 fn from(event: accesskit_winit::Event) -> Self {
134 Self::Access(event)
135 }
136}
137
138#[derive(Debug, Clone)]
140#[non_exhaustive]
141pub struct WindowOptions {
142 pub title: String,
144 pub size: Size,
146 pub theme: Theme,
148 pub instances: usize,
150}
151
152impl WindowOptions {
153 pub fn new(title: impl Into<String>) -> Self {
155 Self {
156 title: title.into(),
157 size: Size::new(1280.0, 800.0),
158 theme: Theme::default(),
159 instances: 65_536,
160 }
161 }
162
163 #[must_use]
165 pub fn with_size(mut self, width: f32, height: f32) -> Self {
166 self.size = Size::new(width, height);
167 self
168 }
169
170 #[must_use]
172 pub fn with_theme(mut self, theme: Theme) -> Self {
173 self.theme = theme;
174 self
175 }
176
177 #[must_use]
179 pub fn with_instances(mut self, instances: usize) -> Self {
180 self.instances = instances;
181 self
182 }
183}
184
185#[derive(Debug)]
187#[non_exhaustive]
188pub enum WindowError {
189 Window(String),
191 Gpu(String),
193 Text(String),
195 App(Box<dyn Error>),
197}
198
199impl fmt::Display for WindowError {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 match self {
202 Self::Window(message) => write!(f, "window system: {message}"),
203 Self::Gpu(message) => write!(f, "GPU: {message}"),
204 Self::Text(message) => write!(f, "fonts: {message}"),
205 Self::App(error) => write!(f, "application: {error}"),
206 }
207 }
208}
209
210impl Error for WindowError {
211 fn source(&self) -> Option<&(dyn Error + 'static)> {
212 match self {
213 Self::App(error) => Some(error.as_ref()),
214 _ => None,
215 }
216 }
217}
218
219pub fn run(options: WindowOptions, app: impl App) -> Result<(), WindowError> {
225 let event_loop = EventLoop::<UserEvent>::with_user_event()
226 .build()
227 .map_err(|e| WindowError::Window(e.to_string()))?;
228 let mut shell = Shell {
229 app,
230 options,
231 proxy: event_loop.create_proxy(),
232 session: None,
233 error: None,
234 close: false,
235 };
236 event_loop
237 .run_app(&mut shell)
238 .map_err(|e| WindowError::Window(e.to_string()))?;
239 shell.error.map_or(Ok(()), Err)
240}
241
242struct Shell<A: App> {
243 app: A,
244 options: WindowOptions,
245 proxy: EventLoopProxy<UserEvent>,
246 session: Option<Session>,
247 error: Option<WindowError>,
248 close: bool,
250}
251
252struct Session {
254 window: Arc<Window>,
255 access: accesskit_winit::Adapter,
257 gpu: Gpu,
258 ui: Ui,
259 cache: GlyphCache,
260 instances: InstanceBuffer,
261 start: Instant,
262 pointer: Point,
263 modifiers: ModifiersState,
264 cursor: Cursor,
265 continuous: bool,
266}
267
268impl<A: App> Shell<A> {
269 fn fail(&mut self, event_loop: &ActiveEventLoop, error: WindowError) {
270 self.error = Some(error);
271 event_loop.exit();
272 }
273
274 fn open(&mut self, event_loop: &ActiveEventLoop) -> Result<Session, WindowError> {
275 let size = self.options.size;
276 let attributes = Window::default_attributes()
278 .with_title(self.options.title.clone())
279 .with_inner_size(LogicalSize::new(size.width, size.height))
280 .with_visible(false);
281 let window = event_loop
282 .create_window(attributes)
283 .map_err(|e| WindowError::Window(e.to_string()))?;
284 let access = accesskit_winit::Adapter::with_event_loop_proxy(
285 event_loop,
286 &window,
287 self.proxy.clone(),
288 );
289 let window = Arc::new(window);
290 window.set_ime_allowed(true);
291 let mut cache = GlyphCache::new(AtlasConfig::DEFAULT);
292 let gpu = Gpu::new(event_loop, &window, self.options.instances, &mut cache)?;
293 let text = TextSystem::new().map_err(|e| WindowError::Text(e.to_string()))?;
294 let mut ui = Ui::new(text);
295 ui.set_theme(self.options.theme.clone());
296 ui.set_scale(window.scale_factor() as f32);
297 let inner = window.inner_size();
298 ui.resize(Size::new(inner.width as f32, inner.height as f32));
299 let mut host = Host {
300 window: &window,
301 proxy: &self.proxy,
302 close: &mut self.close,
303 };
304 self.app
305 .build(&mut ui, &mut host)
306 .map_err(WindowError::App)?;
307 window.set_visible(true);
308 Ok(Session {
309 window,
310 access,
311 gpu,
312 ui,
313 cache,
314 instances: InstanceBuffer::with_capacity(self.options.instances),
315 start: Instant::now(),
316 pointer: Point::ORIGIN,
317 modifiers: ModifiersState::empty(),
318 cursor: Cursor::Default,
319 continuous: false,
320 })
321 }
322}
323
324impl<A: App> ApplicationHandler<UserEvent> for Shell<A> {
325 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
326 if self.session.is_some() {
327 return;
328 }
329 match self.open(event_loop) {
330 Ok(session) => {
331 session.window.request_redraw();
332 self.session = Some(session);
333 if self.close {
334 event_loop.exit();
335 }
336 }
337 Err(error) => self.fail(event_loop, error),
338 }
339 }
340
341 fn window_event(&mut self, event_loop: &ActiveEventLoop, _: WindowId, event: WindowEvent) {
342 let Some(session) = self.session.as_mut() else {
343 return;
344 };
345 let window = Arc::clone(&session.window);
346 session.access.process_event(&window, &event);
347 let redraw = matches!(event, WindowEvent::RedrawRequested);
348 let mut close = false;
349 let mut failed = None;
350 {
351 let mut host = Host {
352 window: &window,
353 proxy: &self.proxy,
354 close: &mut close,
355 };
356 match event {
357 WindowEvent::CloseRequested => host.close(),
358 WindowEvent::RedrawRequested => {
359 session.continuous = self.app.frame(&mut session.ui, &mut host);
360 failed = session.paint().err();
361 }
362 other => session.input(&other),
363 }
364 if failed.is_none() {
365 while let Some(action) = session.ui.next_action() {
366 self.app.action(&mut session.ui, &mut host, action);
367 }
368 session.reflect();
369 if redraw {
372 session.publish_access();
373 }
374 }
375 }
376 if let Some(error) = failed {
377 self.fail(event_loop, error);
378 } else if close {
379 event_loop.exit();
380 }
381 }
382
383 fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {
384 let Some(session) = self.session.as_mut() else {
385 return;
386 };
387 let access = match event {
388 UserEvent::Wake => {
389 session.window.request_redraw();
390 return;
391 }
392 UserEvent::Access(access) => access.window_event,
393 };
394 match access {
395 accesskit_winit::WindowEvent::InitialTreeRequested => session.publish_access(),
396 accesskit_winit::WindowEvent::ActionRequested(request) => {
397 access::perform(&mut session.ui, &request);
398 let window = Arc::clone(&session.window);
399 let mut close = false;
400 let mut host = Host {
401 window: &window,
402 proxy: &self.proxy,
403 close: &mut close,
404 };
405 while let Some(action) = session.ui.next_action() {
406 self.app.action(&mut session.ui, &mut host, action);
407 }
408 session.reflect();
409 session.publish_access();
410 if close {
411 event_loop.exit();
412 }
413 }
414 accesskit_winit::WindowEvent::AccessibilityDeactivated => {}
415 }
416 }
417
418 fn about_to_wait(&mut self, _: &ActiveEventLoop) {
419 if let Some(session) = &self.session {
420 if session.continuous {
421 session.window.request_redraw();
422 }
423 }
424 }
425}
426
427impl Session {
428 fn input(&mut self, event: &WindowEvent) {
430 let modifiers = translate::modifiers(self.modifiers);
431 let at = |kind: PointerKind, position: Point, start: Instant| {
432 PointerEvent::new(kind, position)
433 .with_modifiers(modifiers)
434 .at(start.elapsed())
435 };
436 match event {
437 WindowEvent::Resized(size) => {
438 self.gpu.resize(size.width, size.height);
439 self.ui
440 .resize(Size::new(size.width as f32, size.height as f32));
441 }
442 WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
443 self.ui.set_scale(*scale_factor as f32);
444 }
445 WindowEvent::ModifiersChanged(state) => self.modifiers = state.state(),
446 WindowEvent::CursorMoved { position, .. } => {
447 self.pointer = Point::new(position.x as f32, position.y as f32);
448 self.ui
449 .pointer(at(PointerKind::Move, self.pointer, self.start));
450 }
451 WindowEvent::CursorLeft { .. } => self.ui.pointer_left(self.pointer),
452 WindowEvent::Focused(false) => {
454 self.ui.cancel_pointer();
455 self.modifiers = ModifiersState::empty();
456 }
457 WindowEvent::MouseInput { state, button, .. } => {
458 let button = translate::button(*button);
459 let kind = match state {
460 ElementState::Pressed => PointerKind::Down(button),
461 ElementState::Released => PointerKind::Up(button),
462 };
463 self.ui.pointer(at(kind, self.pointer, self.start));
464 }
465 WindowEvent::MouseWheel { delta, .. } => {
466 let kind = PointerKind::Scroll(translate::scroll(*delta));
467 self.ui.pointer(at(kind, self.pointer, self.start));
468 }
469 WindowEvent::KeyboardInput {
470 event,
471 is_synthetic: false,
472 ..
473 } => {
474 let Some(key) = translate::key(&event.logical_key) else {
475 return;
476 };
477 let mut key = if event.state == ElementState::Pressed {
478 KeyEvent::down(key)
479 } else {
480 KeyEvent::up(key)
481 }
482 .with_modifiers(modifiers);
483 if let Some(text) = translate::text(event.text.as_deref()) {
484 key = key.with_text(text);
485 }
486 if event.repeat {
487 key = key.repeated();
488 }
489 self.ui.key(&key);
490 }
491 WindowEvent::Ime(ime) => {
492 self.ui.ime(&translate::ime(ime));
493 }
494 _ => {}
495 }
496 }
497
498 fn paint(&mut self) -> Result<(), WindowError> {
500 {
501 let mut canvas = Canvas::begin(&mut self.cache, &mut self.instances);
502 self.ui.paint(&mut canvas);
503 }
504 let background = self.ui.theme().background;
505 self.gpu.draw(
506 &self.window,
507 &mut self.cache,
508 self.instances.as_slice(),
509 background,
510 )?;
511 Ok(())
512 }
513
514 fn publish_access(&mut self) {
517 let Self {
518 access, ui, window, ..
519 } = self;
520 access.update_if_active(|| access::tree_update(&ui.access_tree(), &window.title()));
521 }
522
523 fn reflect(&mut self) {
525 let cursor = self.ui.cursor();
526 if cursor != self.cursor {
527 self.cursor = cursor;
528 self.window.set_cursor(translate::cursor(cursor));
529 }
530 if let Some(area) = self.ui.ime_area() {
531 self.window.set_ime_cursor_area(
532 PhysicalPosition::new(area.left(), area.top()),
533 PhysicalSize::new(area.width().max(1.0), area.height().max(1.0)),
534 );
535 }
536 if self.ui.needs_paint() {
537 self.window.request_redraw();
538 }
539 }
540}