#![windows_subsystem = "windows"] use crate::{ app::App, net::NetSender, rsc::{CLIENT_DATA, ClientData}, ui::*, }; pub use app::AppHandle; use arboard::Clipboard; use input::Input; use iris::prelude::*; use openworm::{ net::{ClientMsg, ServerMsg, install_crypto_provider}, rsc::DataDir, }; use render::Renderer; use std::{sync::Arc, time::Instant}; use winit::{ event::{ElementState, Ime, MouseButton, WindowEvent}, event_loop::{ActiveEventLoop, EventLoopProxy}, window::Window, }; mod app; mod input; mod net; mod render; mod rsc; mod ui; fn main() { install_crypto_provider(); App::run(); } pub enum ClientEvent { Connect { send: NetSender, username: String }, ServerMsg(ServerMsg), Err(String), } pub struct Client { renderer: Renderer, input: Input, ui: Ui, focus: Option>, channel: Option>, username: String, clipboard: Clipboard, dir: DataDir, data: ClientData, handle: AppHandle, error: Option>, ime: usize, last_click: Instant, } impl Client { pub fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy) -> Self { let window = Arc::new( event_loop .create_window(Window::default_attributes().with_title("OPENWORM")) .unwrap(), ); let renderer = Renderer::new(window.clone()); let dir = DataDir::default(); let handle = AppHandle { proxy, window }; let mut s = Self { handle, renderer, input: Input::default(), ui: Ui::new(), data: dir.load(CLIENT_DATA), dir, channel: None, focus: None, username: "".to_string(), clipboard: Clipboard::new().unwrap(), error: None, ime: 0, last_click: Instant::now(), }; ui::init(&mut s); s } pub fn event(&mut self, event: ClientEvent, _: &ActiveEventLoop) { match event { ClientEvent::Connect { send, username } => { self.username = username; send.send(ClientMsg::RequestMsgs); main_view(self, send).set_root(&mut self.ui); } ClientEvent::ServerMsg(msg) => match msg { ServerMsg::SendMsg(msg) => { if let Some(msg_area) = &self.channel { let msg = msg_widget(msg).add(&mut self.ui); self.ui[msg_area].children.push(msg.any()); } } ServerMsg::LoadMsgs(msgs) => { if let Some(msg_area) = &self.channel { for msg in msgs { let msg = msg_widget(msg).add(&mut self.ui); self.ui[msg_area].children.push(msg.any()); } } } }, ClientEvent::Err(msg) => { if let Some(err) = &self.error { self.ui[err].inner = Some(ui::error(&mut self.ui, &msg)); } } } } pub fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) { let input_changed = self.input.event(&event); let cursor_state = self.cursor_state().clone(); let old = self.focus.clone(); if cursor_state.buttons.left.is_start() { self.focus = None; } if input_changed { let window_size = self.window_size(); self.run_sensors(&cursor_state, window_size); self.ui.run_sensors(&cursor_state, window_size); } if old != self.focus && let Some(old) = old { self.ui.text(&old).deselect(); } match event { WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::RedrawRequested => { self.ui.update(); self.renderer.update(&mut self.ui); self.renderer.draw(); } WindowEvent::Resized(size) => { self.ui.resize((size.width, size.height)); self.renderer.resize(&size) } WindowEvent::KeyboardInput { event, .. } => { if let Some(sel) = &self.focus && event.state.is_pressed() { let sel = &sel.clone(); let mut text = self.ui.text(sel); match text.apply_event(&event, &self.input.modifiers) { TextInputResult::Unfocus => { self.focus = None; self.handle.window.set_ime_allowed(false); } TextInputResult::Submit => { self.run_event(sel, Submit, ()); } TextInputResult::Paste => { if let Ok(t) = self.clipboard.get_text() { text.insert(&t); } self.run_event(sel, Edited, ()); } TextInputResult::Copy(text) => { if let Err(err) = self.clipboard.set_text(text) { eprintln!("failed to copy text to clipboard: {err}") } } TextInputResult::Used => { self.run_event(sel, Edited, ()); } TextInputResult::Unused => {} } } } WindowEvent::MouseInput { state: ElementState::Pressed, button: MouseButton::Middle, .. } => { self.ui.debug_layers(); } WindowEvent::Ime(ime) => { if let Some(sel) = &self.focus { let mut text = self.ui.text(sel); match ime { Ime::Enabled | Ime::Disabled => (), Ime::Preedit(content, _pos) => { // TODO: highlight once that's real text.replace(self.ime, &content); self.ime = content.chars().count(); } Ime::Commit(content) => { text.insert(&content); } } } } _ => (), } if self.ui.needs_redraw() { self.renderer.window().request_redraw(); } self.input.end_frame(); } pub fn exit(&mut self) { self.dir.save(CLIENT_DATA, &self.data); } } impl UiCtx for Client { fn ui(&mut self) -> &mut Ui { &mut self.ui } }