use crate::prelude::*; use arboard::Clipboard; use std::sync::Arc; use std::time::Instant; use winit::event::{Ime, WindowEvent}; use winit::event_loop::{ActiveEventLoop, EventLoopProxy}; use winit::window::{Window, WindowAttributes}; mod app; mod attr; mod event; mod input; mod render; pub use app::*; pub use attr::*; pub use event::*; pub use input::*; pub use render::*; pub type Proxy = EventLoopProxy; pub type DefaultApp = App>; pub struct DefaultState { ui: Ui, ui_state: UiState, app_state: AppState, } pub struct UiState { pub renderer: UiRenderer, pub input: Input, pub focus: Option>, pub clipboard: Clipboard, pub window: Arc, pub ime: usize, pub last_click: Instant, } pub trait DefaultAppState: 'static { type Event: 'static = (); fn new(ui: &mut Ui, state: &UiState, proxy: Proxy) -> Self; #[allow(unused_variables)] fn event(&mut self, event: Self::Event, ui: &mut Ui, state: &UiState) {} #[allow(unused_variables)] fn exit(&mut self, ui: &mut Ui, state: &UiState) {} #[allow(unused_variables)] fn window_event(&mut self, event: WindowEvent, ui: &mut Ui, state: &UiState) {} fn window_attrs() -> WindowAttributes { WindowAttributes::default() } } impl AppState for DefaultState { type Event = State::Event; fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy) -> Self { let window = Arc::new( event_loop .create_window(State::window_attrs()) .expect("failed to create window "), ); let mut ui = Ui::new(); let ui_state = UiState { renderer: UiRenderer::new(window.clone()), window, input: Input::default(), clipboard: Clipboard::new().unwrap(), ime: 0, last_click: Instant::now(), focus: None, }; let app_state = State::new(&mut ui, &ui_state, proxy); Self { ui, ui_state, app_state, } } fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) { self.app_state.event(event, &mut self.ui, &self.ui_state); } fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) { let Self { ui, ui_state, app_state, } = self; let input_changed = ui_state.input.event(&event); let cursor_state = ui_state.cursor_state().clone(); let old = ui_state.focus; if cursor_state.buttons.left.is_start() { ui_state.focus = None; } if input_changed { let window_size = ui_state.window_size(); // call sensors with all 3 important contexts // TODO: allow user to specify custom contexts? // and give them both states in case they need both ui.run_sensors(&mut (), &cursor_state, window_size); ui.run_sensors(ui_state, &cursor_state, window_size); ui.run_sensors(app_state, &cursor_state, window_size); } if old != ui_state.focus && let Some(old) = old { old.edit(ui).deselect(); } match &event { WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::RedrawRequested => { ui.update(); ui_state.renderer.update(ui); ui_state.renderer.draw(); } WindowEvent::Resized(size) => { ui.resize((size.width, size.height)); ui_state.renderer.resize(size) } WindowEvent::KeyboardInput { event, .. } => { if let Some(sel) = ui_state.focus && event.state.is_pressed() { let mut text = sel.edit(ui); match text.apply_event(event, &ui_state.input.modifiers) { TextInputResult::Unfocus => { ui_state.focus = None; ui_state.window.set_ime_allowed(false); } TextInputResult::Submit => { ui.run_event(app_state, sel, Submit, ()); } TextInputResult::Paste => { if let Ok(t) = ui_state.clipboard.get_text() { text.insert(&t); } ui.run_event(app_state, sel, Edited, ()); } TextInputResult::Copy(text) => { if let Err(err) = ui_state.clipboard.set_text(text) { eprintln!("failed to copy text to clipboard: {err}") } } TextInputResult::Used => { ui.run_event(app_state, sel, Edited, ()); } TextInputResult::Unused => {} } } } WindowEvent::Ime(ime) => { if let Some(sel) = &ui_state.focus { let mut text = sel.edit(ui); match ime { Ime::Enabled | Ime::Disabled => (), Ime::Preedit(content, _pos) => { // TODO: highlight once that's real text.replace(ui_state.ime, content); ui_state.ime = content.chars().count(); } Ime::Commit(content) => { text.insert(content); } } } } _ => (), } app_state.window_event(event, ui, ui_state); if ui.needs_redraw() { ui_state.renderer.window().request_redraw(); } ui_state.input.end_frame(); } fn exit(&mut self) { self.app_state.exit(&mut self.ui, &self.ui_state); } }