From aaee72422abdab80b016d1c9331b1c3bc5ee6420 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Mon, 11 Mar 2019 22:18:58 -0400 Subject: [PATCH] Rearchitect to allow API compliance --- src/event_loop.rs | 2 +- src/platform_impl/stdweb/events.rs | 271 ++++++++++++++---------- src/platform_impl/stdweb/input_binds.rs | 2 +- src/platform_impl/stdweb/window.rs | 2 +- 4 files changed, 163 insertions(+), 114 deletions(-) diff --git a/src/event_loop.rs b/src/event_loop.rs index c1a85a54..ab6839cd 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -139,7 +139,7 @@ impl EventLoop { /// /// [`ControlFlow`]: ./enum.ControlFlow.html #[inline] - pub fn run(self, event_handler: F) // TODO: this needs to be ! + pub fn run(self, event_handler: F) -> ! where F: 'static + FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow) { self.event_loop.run(event_handler) diff --git a/src/platform_impl/stdweb/events.rs b/src/platform_impl/stdweb/events.rs index d3b39d9e..06b8c32b 100644 --- a/src/platform_impl/stdweb/events.rs +++ b/src/platform_impl/stdweb/events.rs @@ -32,30 +32,55 @@ impl DeviceId { pub struct EventLoop { elw: RootELW, -} - -#[derive(Clone)] -struct EventLoopData { - events: VecDeque>, - control: ControlFlow, + runner: EventLoopRunnerShared } pub struct EventLoopWindowTarget { - data: Rc>>, + pub(crate) canvases: RefCell>, + _marker: PhantomData +} + +impl EventLoopWindowTarget { + fn new() -> Self { + EventLoopWindowTarget { + canvases: RefCell::new(Vec::new()), + _marker: PhantomData + } + } +} + +#[derive(Clone)] +pub struct EventLoopProxy { + runner: EventLoopRunnerShared +} + +impl EventLoopProxy { + pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { + self.runner.send_event(Event::UserEvent(event)); + Ok(()) + } +} + +type EventLoopRunnerShared = Rc>; + +struct ELRShared { + runner: RefCell>>, + events: RefCell>>, // TODO: this may not be necessary? +} + +struct EventLoopRunner { + control: ControlFlow, + event_handler: Box, &mut ControlFlow)>, } impl EventLoop { pub fn new() -> Self { EventLoop { elw: RootELW { - p: EventLoopWindowTarget { - data: Rc::new(RefCell::new(EventLoopData { - events: VecDeque::new(), - control: ControlFlow::Poll - })) - }, + p: EventLoopWindowTarget::new(), _marker: PhantomData - } + }, + runner: Rc::new(ELRShared::blank()), } } @@ -67,50 +92,41 @@ impl EventLoop { MonitorHandle } - pub fn run(mut self, event_handler: F) + pub fn run(mut self, mut event_handler: F) -> ! where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) { // TODO: Create event handlers for the JS events // TODO: how to handle request redraw? // TODO: onclose (stdweb PR) // TODO: file dropping, PathBuf isn't useful for web + let EventLoop { elw, runner } = self; + for canvas in elw.p.canvases.borrow().iter() { + register(&runner, canvas); + } + let relw = RootELW { + p: EventLoopWindowTarget::new(), + _marker: PhantomData + }; + runner.set_listener(Box::new(move |evt, ctrl| event_handler(evt, &relw, ctrl))); let document = &document(); - self.elw.p.add_event(document, |mut data, event: BlurEvent| { + add_event(&runner, document, |_, _: BlurEvent| { }); - self.elw.p.add_event(document, |mut data, event: FocusEvent| { + add_event(&runner, document, |_, _: FocusEvent| { + }); - - stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? - } - - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy { - data: self.elw.p.data.clone() - } - } - - pub fn window_target(&self) -> &RootELW { - &self.elw - } -} - -impl EventLoopWindowTarget { - pub fn register_window(&self, other: &Window) { - let canvas = &other.canvas; - - self.add_event(canvas, |mut data, event: KeyDownEvent| { + add_event(&runner, document, |elrs, event: KeyDownEvent| { let key = event.key(); let mut characters = key.chars(); let first = characters.next(); let second = characters.next(); if let (Some(key), None) = (first, second) { - data.events.push_back(Event::WindowEvent { + elrs.send_event(Event::WindowEvent { window_id: RootWI(WindowId), event: WindowEvent::ReceivedCharacter(key) }); } - data.events.push_back(Event::WindowEvent { + elrs.send_event(Event::WindowEvent { window_id: RootWI(WindowId), event: WindowEvent::KeyboardInput { // TODO: is there a way to get keyboard device? @@ -124,8 +140,8 @@ impl EventLoopWindowTarget { } }); }); - self.add_event(canvas, |mut data, event: KeyUpEvent| { - data.events.push_back(Event::WindowEvent { + add_event(&runner, document, |elrs, event: KeyUpEvent| { + elrs.send_event(Event::WindowEvent { window_id: RootWI(WindowId), event: WindowEvent::KeyboardInput { // TODO: is there a way to get keyboard device? @@ -139,84 +155,117 @@ impl EventLoopWindowTarget { } }); }); - self.add_event(canvas, |mut data, event: PointerOutEvent| { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorLeft { - device_id: RootDI(DeviceId(event.pointer_id())) - } - }); - }); - self.add_event(canvas, |mut data, event: PointerOverEvent| { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorEntered { - device_id: RootDI(DeviceId(event.pointer_id())) - } - }); - }); - self.add_event(canvas, |mut data, event: PointerMoveEvent| { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorMoved { - device_id: RootDI(DeviceId(event.pointer_id())), - position: LogicalPosition { - x: event.offset_x(), - y: event.offset_y() - }, - modifiers: mouse_modifiers_state(&event) - } - }); - }); - self.add_event(canvas, |mut data, event: PointerUpEvent| { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::MouseInput { - device_id: RootDI(DeviceId(event.pointer_id())), - state: ElementState::Pressed, - button: mouse_button(&event), - modifiers: mouse_modifiers_state(&event) - } - }); - }); - self.add_event(canvas, |mut data, event: PointerDownEvent| { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::MouseInput { - device_id: RootDI(DeviceId(event.pointer_id())), - state: ElementState::Released, - button: mouse_button(&event), - modifiers: mouse_modifiers_state(&event) - } - }); - }); + stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? + js! { + throw "Using exceptions for control flow, don't mind me"; + } + unreachable!(); } + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy { + runner: self.runner.clone() + } + } - fn add_event(&self, target: &impl IEventTarget, mut handler: F) - where E: ConcreteEvent, F: FnMut(RefMut>, E) + 'static { - let data = self.data.clone(); - - target.add_event_listener(move |event: E| { - event.prevent_default(); - event.stop_propagation(); - event.cancel_bubble(); - - handler(data.borrow_mut(), event); - }); + pub fn window_target(&self) -> &RootELW { + &self.elw } } -#[derive(Clone)] -pub struct EventLoopProxy { - data: Rc>> +fn register(elrs: &EventLoopRunnerShared, canvas: &CanvasElement) { + add_event(elrs, canvas, |elrs, event: PointerOutEvent| { + elrs.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorLeft { + device_id: RootDI(DeviceId(event.pointer_id())) + } + }); + }); + add_event(elrs, canvas, |elrs, event: PointerOverEvent| { + elrs.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorEntered { + device_id: RootDI(DeviceId(event.pointer_id())) + } + }); + }); + add_event(elrs, canvas, |elrs, event: PointerMoveEvent| { + elrs.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorMoved { + device_id: RootDI(DeviceId(event.pointer_id())), + position: LogicalPosition { + x: event.offset_x(), + y: event.offset_y() + }, + modifiers: mouse_modifiers_state(&event) + } + }); + }); + add_event(elrs, canvas, |elrs, event: PointerUpEvent| { + elrs.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::MouseInput { + device_id: RootDI(DeviceId(event.pointer_id())), + state: ElementState::Pressed, + button: mouse_button(&event), + modifiers: mouse_modifiers_state(&event) + } + }); + }); + add_event(elrs, canvas, |elrs, event: PointerDownEvent| { + elrs.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::MouseInput { + device_id: RootDI(DeviceId(event.pointer_id())), + state: ElementState::Released, + button: mouse_button(&event), + modifiers: mouse_modifiers_state(&event) + } + }); + }); } -impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.data.borrow_mut().events.push_back(Event::UserEvent(event)); - Ok(()) +fn add_event(elrs: &EventLoopRunnerShared, target: &impl IEventTarget, mut handler: F) + where E: ConcreteEvent, F: FnMut(&EventLoopRunnerShared, E) + 'static { + let elrs = elrs.clone(); // TODO: necessary? + + target.add_event_listener(move |event: E| { + event.prevent_default(); + event.stop_propagation(); + event.cancel_bubble(); + + handler(&elrs, event); + }); +} + +impl ELRShared { + fn blank() -> ELRShared { + ELRShared { + runner: RefCell::new(None), + events: RefCell::new(VecDeque::new()) + } } + + fn set_listener(&self, event_handler: Box, &mut ControlFlow)>) { + *self.runner.borrow_mut() = Some(EventLoopRunner { + control: ControlFlow::Poll, + event_handler + }); + } + + // TODO: handle event loop closures + // TODO: handle event buffer + fn send_event(&self, event: Event) { + match *self.runner.borrow_mut() { + Some(ref mut runner) => { + // TODO: bracket this in control flow events? + (runner.event_handler)(event, &mut runner.control); + } + None => () + } + } + } - diff --git a/src/platform_impl/stdweb/input_binds.rs b/src/platform_impl/stdweb/input_binds.rs index 282c6664..7068fba0 100644 --- a/src/platform_impl/stdweb/input_binds.rs +++ b/src/platform_impl/stdweb/input_binds.rs @@ -197,6 +197,6 @@ pub fn keyboard_modifiers_state(event: &impl IKeyboardEvent) -> ModifiersState { } pub fn scancode(event: &T) -> ScanCode { - let which = js! ( return @{event}.which(); ); + let which = js! ( return @{event}.which; ); which.try_into().expect("The which value should be a number") } diff --git a/src/platform_impl/stdweb/window.rs b/src/platform_impl/stdweb/window.rs index 11461041..fd6d6f57 100644 --- a/src/platform_impl/stdweb/window.rs +++ b/src/platform_impl/stdweb/window.rs @@ -63,6 +63,7 @@ impl Window { document().body() .ok_or_else(|| CreationError::OsError("Failed to find body node".to_owned()))? .append_child(&canvas); + target.canvases.borrow_mut().push(canvas.clone()); let window = Window { canvas }; if let Some(dimensions) = attr.dimensions { window.set_inner_size(dimensions); @@ -87,7 +88,6 @@ impl Window { window.set_decorations(attr.decorations); window.set_always_on_top(attr.always_on_top); window.set_window_icon(attr.window_icon); - target.register_window(&window); Ok(window) }