use crate::prelude::*; use arboard::Clipboard; use iris_core::util::forget_mut; use std::{marker::Sized, sync::Arc, time::Instant}; use winit::{ event::{Ime, WindowEvent}, event_loop::{ActiveEventLoop, EventLoopProxy}, window::Window, }; mod app; mod attr; mod event; mod input; mod render; mod sense; pub use app::*; pub use attr::*; pub use event::*; pub use input::*; pub use render::*; pub use sense::*; pub type Proxy = EventLoopProxy; 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, } impl UiState { pub fn new(window: impl Into>) -> Self { let window = window.into(); Self { renderer: UiRenderer::new(window.clone()), window, input: Input::default(), clipboard: Clipboard::new().unwrap(), ime: 0, last_click: Instant::now(), focus: None, } } } pub trait HasUiState: Sized + 'static + HasUi { fn ui_state(&mut self) -> &mut UiState; fn ui_with_ui_state(&mut self) -> (&mut Ui, &mut UiState) { // as long as you're not doing anything actually unhinged this should always work safely (unsafe { forget_mut(self.ui()) }, self.ui_state()) } } pub trait DefaultAppState: Sized + 'static + HasUi + HasUiState { type Event: 'static = (); fn new(event_loop: &ActiveEventLoop, proxy: Proxy) -> Self; #[allow(unused_variables)] fn event(&mut self, event: Self::Event) {} #[allow(unused_variables)] fn exit(&mut self) {} #[allow(unused_variables)] fn window_event(&mut self, event: WindowEvent) {} } impl AppState for State { type Event = State::Event; fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy) -> Self { Self::new(event_loop, proxy) } fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) { self.event(event); } fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) { let ui_state = self.ui_state(); 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(); self.run_sensors(&cursor_state, window_size); } let (mut ui, mut ui_state) = self.ui_with_ui_state(); 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 => { self.run_event::(sel, &mut ()); } TextInputResult::Paste => { if let Ok(t) = ui_state.clipboard.get_text() { text.insert(&t); } self.run_event::(sel, &mut ()); } TextInputResult::Copy(text) => { if let Err(err) = ui_state.clipboard.set_text(text) { eprintln!("failed to copy text to clipboard: {err}") } } TextInputResult::Used => { self.run_event::(sel, &mut ()); } 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); } } } } _ => (), } self.window_event(event); (ui, ui_state) = self.ui_with_ui_state(); if ui.needs_redraw() { ui_state.renderer.window().request_redraw(); } ui_state.input.end_frame(); } fn exit(&mut self) { self.exit(); } }