From 02696b1f75960c1684eab676425b1c12256ca4a3 Mon Sep 17 00:00:00 2001 From: shadow cat Date: Thu, 4 Dec 2025 14:31:34 -0500 Subject: [PATCH] switch to default iris app --- iris | 2 +- src/bin/client/app.rs | 67 -------------- src/bin/client/input.rs | 83 ----------------- src/bin/client/main.rs | 172 ++++++----------------------------- src/bin/client/net.rs | 16 +++- src/bin/client/render/mod.rs | 153 ------------------------------- src/bin/client/ui/login.rs | 10 +- src/bin/client/ui/main.rs | 12 ++- src/bin/client/ui/misc.rs | 59 ------------ src/bin/client/ui/mod.rs | 15 --- 10 files changed, 61 insertions(+), 528 deletions(-) delete mode 100644 src/bin/client/app.rs delete mode 100644 src/bin/client/input.rs delete mode 100644 src/bin/client/render/mod.rs diff --git a/iris b/iris index 84c460a..f7b100e 160000 --- a/iris +++ b/iris @@ -1 +1 @@ -Subproject commit 84c460a91f7d98d565f4b71a41420a3e4a8c8c31 +Subproject commit f7b100e00c9e0fca1a39f9d8922f41b6f27a9b6a diff --git a/src/bin/client/app.rs b/src/bin/client/app.rs deleted file mode 100644 index 1ea16b7..0000000 --- a/src/bin/client/app.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::sync::Arc; - -use winit::{ - application::ApplicationHandler, - event::WindowEvent, - event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy}, - window::{Window, WindowId}, -}; - -use crate::ClientEvent; - -use super::Client; - -#[derive(Clone)] -pub struct AppHandle { - pub proxy: EventLoopProxy, - pub window: Arc, -} - -pub struct App { - client: Option, - proxy: EventLoopProxy, -} - -impl App { - pub fn run() { - let event_loop = EventLoop::with_user_event().build().unwrap(); - let proxy = event_loop.create_proxy(); - event_loop - .run_app(&mut App { - client: Default::default(), - proxy, - }) - .unwrap(); - } -} - -impl ApplicationHandler for App { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - if self.client.is_none() { - let client = Client::new(event_loop, self.proxy.clone()); - self.client = Some(client); - } - } - - fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { - let client = self.client.as_mut().unwrap(); - client.window_event(event, event_loop); - } - - fn user_event(&mut self, event_loop: &ActiveEventLoop, event: ClientEvent) { - let client = self.client.as_mut().unwrap(); - client.event(event, event_loop); - } - - fn exiting(&mut self, _: &ActiveEventLoop) { - let client = self.client.as_mut().unwrap(); - client.exit(); - } -} - -impl AppHandle { - pub fn send(&self, event: ClientEvent) { - self.proxy.send_event(event).unwrap_or_else(|_| panic!()); - self.window.request_redraw(); - } -} diff --git a/src/bin/client/input.rs b/src/bin/client/input.rs deleted file mode 100644 index 7512677..0000000 --- a/src/bin/client/input.rs +++ /dev/null @@ -1,83 +0,0 @@ -use iris::{ - core::{CursorState, Modifiers}, - layout::Vec2, -}; -use winit::{ - event::{MouseButton, MouseScrollDelta, WindowEvent}, - keyboard::{Key, NamedKey}, -}; - -use super::Client; - -#[derive(Default)] -pub struct Input { - cursor: CursorState, - pub modifiers: Modifiers, -} - -impl Input { - pub fn event(&mut self, event: &WindowEvent) -> bool { - match event { - WindowEvent::CursorMoved { position, .. } => { - self.cursor.pos = Vec2::new(position.x as f32, position.y as f32); - self.cursor.exists = true; - } - WindowEvent::MouseInput { state, button, .. } => { - let buttons = &mut self.cursor.buttons; - let pressed = state.is_pressed(); - match button { - MouseButton::Left => buttons.left.update(pressed), - MouseButton::Right => buttons.right.update(pressed), - MouseButton::Middle => buttons.middle.update(pressed), - _ => (), - } - } - WindowEvent::MouseWheel { delta, .. } => { - let mut delta = match *delta { - MouseScrollDelta::LineDelta(x, y) => Vec2::new(x, y), - MouseScrollDelta::PixelDelta(pos) => Vec2::new(pos.x as f32, pos.y as f32), - }; - if delta.x == 0.0 && self.modifiers.shift { - delta.x = delta.y; - delta.y = 0.0; - } - self.cursor.scroll_delta = delta; - } - WindowEvent::CursorLeft { .. } => { - self.cursor.exists = false; - self.modifiers.clear(); - } - WindowEvent::KeyboardInput { event, .. } => { - if let Key::Named(named) = event.logical_key { - let pressed = event.state.is_pressed(); - match named { - NamedKey::Control => { - self.modifiers.control = pressed; - } - NamedKey::Shift => { - self.modifiers.shift = pressed; - } - _ => (), - } - } - } - _ => return false, - } - true - } - - pub fn end_frame(&mut self) { - self.cursor.end_frame(); - } -} - -impl Client { - pub fn window_size(&self) -> Vec2 { - let size = self.renderer.window().inner_size(); - (size.width, size.height).into() - } - - pub fn cursor_state(&self) -> &CursorState { - &self.input.cursor - } -} diff --git a/src/bin/client/main.rs b/src/bin/client/main.rs index ebe55ad..8dc6331 100644 --- a/src/bin/client/main.rs +++ b/src/bin/client/main.rs @@ -1,56 +1,37 @@ #![windows_subsystem = "windows"] use crate::{ - app::App, net::{NetHandle, NetSender}, rsc::{CLIENT_DATA, ClientData}, state::{ClientState, LoggedIn, Login}, ui::*, }; -pub use app::AppHandle; -use arboard::Clipboard; -use input::Input; -use iris::prelude::*; +use iris::{prelude::*, winit::*}; 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, -}; +use winit::event::{ElementState, MouseButton, WindowEvent}; -mod app; mod debug; -mod input; mod net; -mod render; mod rsc; mod state; mod ui; fn main() { install_crypto_provider(); - App::run(); + UiApp::::run(); } pub struct Client { - renderer: Renderer, - input: Input, - ui: Ui, - focus: Option>, - clipboard: Clipboard, dir: DataDir, data: ClientData, - handle: AppHandle, state: ClientState, - ime: usize, - last_click: Instant, main_ui: WidgetId, notif: WidgetId, + ui: DefaultUi, + proxy: Proxy, } pub enum ClientEvent { @@ -59,18 +40,15 @@ pub enum ClientEvent { Err(String), } -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 }; +impl DefaultUiState for Client { + type Event = ClientEvent; - let mut ui = Ui::new(); + fn ui(&mut self) -> &mut DefaultUi { + &mut self.ui + } + + fn new(mut ui: DefaultUi, proxy: Proxy) -> Self { + let dir = DataDir::default(); let notif = WidgetPtr::default().add(&mut ui); let main_ui = WidgetPtr::default().add(&mut ui); ( @@ -81,31 +59,25 @@ impl Client { .set_root(&mut ui); let mut s = Self { - handle, - renderer, - input: Input::default(), - ui, data: dir.load(CLIENT_DATA), state: Default::default(), dir, - focus: None, - clipboard: Clipboard::new().unwrap(), - ime: 0, - last_click: Instant::now(), main_ui: main_ui.clone(), notif, + ui, + proxy, }; - connect_screen(&mut s).set_ptr(&main_ui, &mut s.ui); + connect_screen(&mut s).set_ptr(&s.main_ui, &mut s.ui); s } - pub fn event(&mut self, event: ClientEvent, _: &ActiveEventLoop) { + fn event(&mut self, event: ClientEvent) { match event { ClientEvent::Connect { send } => { - let ClientState::Connect(state) = self.state.take() else { + let ClientState::Connect(connect) = self.state.take() else { panic!("invalid state"); }; - let th = state.handle.unwrap(); + let th = connect.handle.unwrap(); self.state = ClientState::Login(Login { handle: NetHandle { send: send.clone(), @@ -158,105 +130,19 @@ impl Client { } } - 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, - .. - } => { - debug::debug(self); - } - 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) { + fn exit(&mut self) { self.state.exit(); self.dir.save(CLIENT_DATA, &self.data); } -} -impl UiCtx for Client { - fn ui(&mut self) -> &mut Ui { - &mut self.ui + fn window_event(&mut self, event: WindowEvent) { + if let WindowEvent::MouseInput { + state: ElementState::Pressed, + button: MouseButton::Middle, + .. + } = event + { + debug::debug(self); + } } } diff --git a/src/bin/client/net.rs b/src/bin/client/net.rs index c4f835c..117b461 100644 --- a/src/bin/client/net.rs +++ b/src/bin/client/net.rs @@ -1,4 +1,4 @@ -use crate::{AppHandle, ClientEvent}; +use crate::ClientEvent; use openworm::net::{ ClientMsg, RecvHandler, SERVER_NAME, ServerMsg, SkipServerVerification, recv_uni, send_uni, }; @@ -13,6 +13,7 @@ use std::{ time::Duration, }; use tokio::sync::mpsc::UnboundedSender; +use winit::{event_loop::EventLoopProxy, window::Window}; pub const CLIENT_SOCKET: SocketAddr = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)); @@ -26,6 +27,19 @@ pub struct NetHandle { pub thread: JoinHandle<()>, } +#[derive(Clone)] +pub struct AppHandle { + pub proxy: EventLoopProxy, + pub window: Arc, +} + +impl AppHandle { + pub fn send(&self, event: ClientEvent) { + self.proxy.send_event(event).unwrap_or_else(|_| panic!()); + self.window.request_redraw(); + } +} + pub fn connect(handle: AppHandle, info: ConnectInfo) -> JoinHandle<()> { std::thread::spawn(move || { if let Err(msg) = connect_the(handle.clone(), info) { diff --git a/src/bin/client/render/mod.rs b/src/bin/client/render/mod.rs deleted file mode 100644 index e433ddf..0000000 --- a/src/bin/client/render/mod.rs +++ /dev/null @@ -1,153 +0,0 @@ -use iris::{ - layout::Ui, - render::{UiLimits, UiRenderer}, -}; -use pollster::FutureExt; -use std::sync::Arc; -use wgpu::{util::StagingBelt, *}; -use winit::{dpi::PhysicalSize, window::Window}; - -pub const CLEAR_COLOR: Color = Color::BLACK; - -pub struct Renderer { - window: Arc, - surface: Surface<'static>, - device: Device, - queue: Queue, - config: SurfaceConfiguration, - encoder: CommandEncoder, - staging_belt: StagingBelt, - pub ui: UiRenderer, -} - -impl Renderer { - pub fn update(&mut self, updates: &mut Ui) { - self.ui.update(&self.device, &self.queue, updates); - } - - pub fn draw(&mut self) { - let output = self.surface.get_current_texture().unwrap(); - let view = output - .texture - .create_view(&TextureViewDescriptor::default()); - - let mut encoder = std::mem::replace(&mut self.encoder, Self::create_encoder(&self.device)); - { - let render_pass = &mut encoder.begin_render_pass(&RenderPassDescriptor { - color_attachments: &[Some(RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: Operations { - load: LoadOp::Clear(CLEAR_COLOR), - store: StoreOp::Store, - }, - depth_slice: None, - })], - ..Default::default() - }); - self.ui.draw(render_pass); - } - - self.queue.submit(std::iter::once(encoder.finish())); - self.staging_belt.finish(); - output.present(); - self.staging_belt.recall(); - } - - pub fn resize(&mut self, size: &PhysicalSize) { - self.config.width = size.width; - self.config.height = size.height; - self.surface.configure(&self.device, &self.config); - self.ui.resize(size, &self.queue); - } - - fn create_encoder(device: &Device) -> CommandEncoder { - device.create_command_encoder(&CommandEncoderDescriptor { - label: Some("Render Encoder"), - }) - } - - pub fn new(window: Arc) -> Self { - let size = window.inner_size(); - - let instance = Instance::new(&InstanceDescriptor { - backends: Backends::PRIMARY, - flags: InstanceFlags::empty(), - ..Default::default() - }); - - let surface = instance - .create_surface(window.clone()) - .expect("Could not create window surface!"); - - let adapter = instance - .request_adapter(&RequestAdapterOptions { - power_preference: PowerPreference::default(), - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }) - .block_on() - .expect("Could not get adapter!"); - - let ui_limits = UiLimits::default(); - - let (device, queue) = adapter - .request_device(&DeviceDescriptor { - required_features: Features::TEXTURE_BINDING_ARRAY - | Features::PARTIALLY_BOUND_BINDING_ARRAY - | Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, - required_limits: Limits { - max_binding_array_elements_per_shader_stage: ui_limits - .max_binding_array_elements_per_shader_stage(), - max_binding_array_sampler_elements_per_shader_stage: ui_limits - .max_binding_array_sampler_elements_per_shader_stage(), - max_buffer_size: 1 << 30, - ..Default::default() - }, - ..Default::default() - }) - .block_on() - .expect("Could not get device!"); - - let surface_caps = surface.get_capabilities(&adapter); - let surface_format = surface_caps - .formats - .iter() - .copied() - .find(|f| f.is_srgb()) - .unwrap_or(surface_caps.formats[0]); - - let config = SurfaceConfiguration { - usage: TextureUsages::RENDER_ATTACHMENT, - format: surface_format, - width: size.width, - height: size.height, - present_mode: PresentMode::AutoVsync, - alpha_mode: surface_caps.alpha_modes[0], - desired_maximum_frame_latency: 2, - view_formats: vec![], - }; - - surface.configure(&device, &config); - - let staging_belt = StagingBelt::new(4096 * 4); - let encoder = Self::create_encoder(&device); - - let shape_pipeline = UiRenderer::new(&device, &queue, &config, ui_limits); - - Self { - surface, - device, - queue, - config, - encoder, - staging_belt, - ui: shape_pipeline, - window, - } - } - - pub fn window(&self) -> &Window { - self.window.as_ref() - } -} diff --git a/src/bin/client/ui/login.rs b/src/bin/client/ui/login.rs index 26de33e..e335f24 100644 --- a/src/bin/client/ui/login.rs +++ b/src/bin/client/ui/login.rs @@ -1,6 +1,7 @@ +use iris::winit::{attr::Selector, event::Edited}; use openworm::net::ClientMsg; -use crate::state::ClientState; +use crate::{net::AppHandle, state::ClientState}; use super::*; @@ -37,11 +38,14 @@ pub fn submit_button(text: &str, on_submit: impl Fn(&mut Client) + 'static) -> i pub fn connect_screen(client: &mut Client) -> WidgetId { let Client { - data, ui, handle, .. + data, ui, proxy, .. } = client; let ip = field_widget(&data.ip, "ip", ui); let ip_ = ip.clone(); - let handle = handle.clone(); + let handle = AppHandle { + proxy: proxy.clone(), + window: ui.window.clone(), + }; let submit = submit_button("connect", move |client| { let ClientState::Connect(state) = &mut client.state else { return; diff --git a/src/bin/client/ui/main.rs b/src/bin/client/ui/main.rs index eee182a..292e76d 100644 --- a/src/bin/client/ui/main.rs +++ b/src/bin/client/ui/main.rs @@ -1,7 +1,13 @@ use super::*; use crate::state::{ClientState, LoggedIn}; -use iris::layout::len_fns::*; -use openworm::net::{ClientMsg, NetClientMsg, NetServerMsg}; +use iris::{ + layout::len_fns::*, + winit::{ + attr::{Selectable, Selector}, + event::Submit, + }, +}; +use openworm::net::{ClientMsg, NetClientMsg}; pub const SIZE: u32 = 20; @@ -74,7 +80,7 @@ pub fn msg_panel(ui: &mut Ui, state: &mut LoggedIn) -> impl WidgetRet + use<> { content: content.clone(), }; state.network.send(ClientMsg::SendMsg(msg.clone())); - let msg = msg_widget(&client.data.username, &content).add(&mut client.ui); + let msg = msg_widget(&state.username, &content).add(&mut client.ui); client.ui[&msg_area].children.push(msg.any()); }) .pad(15) diff --git a/src/bin/client/ui/misc.rs b/src/bin/client/ui/misc.rs index 6839f34..bfbc8d5 100644 --- a/src/bin/client/ui/misc.rs +++ b/src/bin/client/ui/misc.rs @@ -1,5 +1,3 @@ -use std::time::{Duration, Instant}; - use super::*; pub fn werror(ui: &mut Ui, msg: &str) -> WidgetId { @@ -11,63 +9,6 @@ pub fn werror(ui: &mut Ui, msg: &str) -> WidgetId { .any() } -pub struct Selector; - -impl WidgetAttr for Selector { - type Input = WidgetId; - - fn run(ui: &mut Ui, container: &WidgetId, id: Self::Input) { - let container = container.clone(); - ui.register_event( - &container.clone(), - CursorSense::click_or_drag(), - move |client: &mut Client, mut data| { - let region = client.ui.window_region(&id).unwrap(); - let id_pos = region.top_left; - let container_pos = client.ui.window_region(&container).unwrap().top_left; - data.cursor += container_pos - id_pos; - data.size = region.size(); - select(id.clone(), client, data); - }, - ); - } -} - -pub struct Selectable; - -impl WidgetAttr for Selectable { - type Input = (); - - fn run(ui: &mut Ui, id: &WidgetId, _: Self::Input) { - let id = id.clone(); - ui.register_event( - &id.clone(), - CursorSense::click_or_drag(), - move |client: &mut Client, data| { - select(id.clone(), client, data); - }, - ); - } -} - -fn select(id: WidgetId, client: &mut Client, data: CursorData) { - let now = Instant::now(); - let recent = (now - client.last_click) < Duration::from_millis(300); - client.last_click = now; - client - .ui - .text(&id) - .select(data.cursor, data.size, data.sense.is_dragging(), recent); - if let Some(region) = client.ui.window_region(&id) { - client.handle.window.set_ime_allowed(true); - client.handle.window.set_ime_cursor_area( - LogicalPosition::::from(region.top_left.tuple()), - LogicalSize::::from(region.size().tuple()), - ); - } - client.focus = Some(id); -} - pub fn hint(msg: impl Into) -> TextBuilder { wtext(msg).size(20).color(Color::GRAY) } diff --git a/src/bin/client/ui/mod.rs b/src/bin/client/ui/mod.rs index 15fdb22..7d6e13f 100644 --- a/src/bin/client/ui/mod.rs +++ b/src/bin/client/ui/mod.rs @@ -3,7 +3,6 @@ use crate::{ net::{ConnectInfo, connect}, }; use iris::prelude::*; -use winit::dpi::{LogicalPosition, LogicalSize}; mod login; mod main; @@ -11,17 +10,3 @@ mod misc; pub use login::*; pub use main::*; pub use misc::*; - -#[derive(Eq, PartialEq, Hash, Clone)] -pub struct Submit; - -#[derive(Eq, PartialEq, Hash, Clone)] -pub struct Edited; - -impl DefaultEvent for Submit { - type Data = (); -} - -impl DefaultEvent for Edited { - type Data = (); -}