Skip to content

Commit

Permalink
Merge pull request #741 from Sharktheone/event-loop/web
Browse files Browse the repository at this point in the history
  • Loading branch information
Sharktheone authored Jan 14, 2025
2 parents d00cea7 + e3a6b70 commit 0c5d668
Show file tree
Hide file tree
Showing 11 changed files with 595 additions and 90 deletions.
25 changes: 25 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions crates/gosub_instance/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ version = "0.1.0"
edition = "2021"

[dependencies]
gosub_shared = { path = "../gosub_shared" }
gosub_interface = { path = "../gosub_interface" }
gosub_shared = { path = "../gosub_shared", registry = "gosub" }
gosub_interface = { path = "../gosub_interface", registry = "gosub" }
gosub_web_platform = { path = "../gosub_web_platform", registry = "gosub" }
gosub_net = { path = "../gosub_net" }
tokio = { version = "1.43.0", features = ["sync", "rt"] }
url = "2.5.4"
Expand Down
27 changes: 18 additions & 9 deletions crates/gosub_instance/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use gosub_interface::render_backend::{ImageBuffer, NodeDesc};
use gosub_net::http::fetcher::Fetcher;
use gosub_shared::geo::SizeU32;
use gosub_shared::types::Result;
use gosub_web_platform::{WebEventLoop, WebEventLoopHandle, WebEventLoopMessage};
use log::warn;
use std::sync::mpsc::Sender as SyncSender;
use std::sync::Arc;
Expand All @@ -23,6 +24,7 @@ pub struct EngineInstance<C: ModuleConfiguration> {
pub title: String,
pub url: Url,
pub data: C::TreeDrawer,
web: WebEventLoopHandle,
rx: Receiver<InstanceMessage>,
irx: Receiver<InternalInstanceMessage<C>>,
el: El<C>,
Expand Down Expand Up @@ -61,8 +63,11 @@ impl<C: ModuleConfiguration> EngineInstance<C> {

let (itx, irx) = tokio::sync::mpsc::channel(128);

let web = WebEventLoop::new_on_thread(handles.clone());

Ok(EngineInstance {
title: "Gosub".to_string(),
web,
url,
data,
rx,
Expand Down Expand Up @@ -184,18 +189,22 @@ impl<C: ModuleConfiguration> EngineInstance<C> {
}
}
}
InstanceMessage::Input(event) => match event {
InputEvent::MouseScroll(delta) => {
self.data.scroll(delta);
self.redraw();
}
InputEvent::MouseMove(point) => {
if self.data.mouse_move(point.x, point.y) {
InstanceMessage::Input(event) => {
match event {
InputEvent::MouseScroll(delta) => {
self.data.scroll(delta);
self.redraw();
}
InputEvent::MouseMove(point) => {
if self.data.mouse_move(point.x, point.y) {
self.redraw();
}
}
_ => {}
}
_ => {} //TODO: send all events to the WebEventLoop
},

self.web.tx.send(WebEventLoopMessage::InputEvent(event)).await?;
}
}

Ok(())
Expand Down
4 changes: 4 additions & 0 deletions crates/gosub_interface/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ pub trait ModuleConfiguration:
pub trait HasDrawComponents: HasRenderTree + HasRenderBackend {}

impl<C: HasRenderTree + HasRenderBackend> HasDrawComponents for C {}

pub trait HasWebComponents: HasChrome + 'static {}

impl<C: HasChrome + 'static> HasWebComponents for C {}
15 changes: 15 additions & 0 deletions crates/gosub_web_platform/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "gosub_web_platform"
version = "0.1.0"
edition = "2021"
authors = ["Gosub Community <[email protected]>"]
license = "MIT"
description = "Gosub Web Platform implementation"

[dependencies]
gosub_shared = { version = "0.1.1", registry = "gosub", path = "../gosub_shared" }
gosub_interface = { version = "0.1.2", registry = "gosub", path = "../gosub_interface", features = [] }
slotmap = "1.0.7"
tokio = { version = "1.42.0", features = ["sync", "rt", "macros"] }
pin-project = "1.1.7"
log = "0.4.22"
49 changes: 49 additions & 0 deletions crates/gosub_web_platform/src/callback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use crate::poll_guard::{PollCallback, PollGuard};
use std::future::Future;
use tokio::task;

struct TestPollGuard;

impl PollCallback for TestPollGuard {
fn poll(&mut self) {
//TODO: execute the js microtasks
}
}

pub trait FutureExecutor {
fn execute<T: Future<Output = ()> + 'static>(&mut self, future: T);
}

pub struct Callback<T: FutureExecutor, D = ()> {
#[allow(clippy::type_complexity)]
spawner: Box<dyn FnMut(&mut T, D)>,
}

impl<T: FutureExecutor, D> Callback<T, D> {
pub fn new(spawner: impl FnMut(&mut T, D) + 'static) -> Self {
Self {
spawner: Box::new(spawner),
}
}

pub fn execute(&mut self, executor: &mut T, data: D) {
(self.spawner)(executor, data);
}
}

impl<T: FutureExecutor> Callback<T> {
pub fn exec(&mut self, executor: &mut T) {
self.execute(executor, ());
}
}

#[derive(Debug, Default)]
pub struct TokioExecutor;

impl FutureExecutor for TokioExecutor {
fn execute<T: Future<Output = ()> + 'static>(&mut self, future: T) {
let guard = PollGuard::new(future, TestPollGuard);

task::spawn_local(guard);
}
}
132 changes: 132 additions & 0 deletions crates/gosub_web_platform/src/event_listeners.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use crate::callback::{Callback, FutureExecutor};
use gosub_interface::input::{InputEvent, MouseButton};
use gosub_shared::geo::Point;
use log::info;
use std::fmt::Debug;

pub enum Listeners<E: FutureExecutor> {
MouseDown(Callback<E, MouseButtonEvent>),
MouseUp(Callback<E, MouseButtonEvent>),
MouseMove(Callback<E, MouseMoveEvent>),
MouseScroll(Callback<E, MouseScrollEvent>),
KeyboardUp(Callback<E, KeyboardEvent>),
KeyboardDown(Callback<E, KeyboardEvent>),
}

#[derive(Debug, Clone, Copy)]
pub struct MouseButtonEvent {
pub button: MouseButton,
}

#[derive(Debug, Clone, Copy)]
pub struct KeyboardEvent {
pub key: char,
}

#[derive(Debug, Clone, Copy)]
pub struct MouseMoveEvent {
pub pos: Point,
}

#[derive(Debug, Clone, Copy)]
pub struct MouseScrollEvent {
pub delta: Point,
}

pub struct EventListener<D, E: FutureExecutor> {
listeners: Vec<Callback<E, D>>,
}

impl<D: Clone + Debug, E: FutureExecutor> EventListener<D, E> {
pub fn handle_event(&mut self, event: D, e: &mut E) {
info!("Handling event: {:?}", event);
for listener in self.listeners.iter_mut() {
listener.execute(e, event.clone());
}
}
}

impl<D, E: FutureExecutor> Debug for EventListener<D, E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EventListener")
.field("listeners", &self.listeners.len())
.finish()
}
}

impl<D, E: FutureExecutor> Default for EventListener<D, E> {
fn default() -> Self {
Self { listeners: Vec::new() }
}
}

pub struct EventListeners<E: FutureExecutor> {
mouse_up: EventListener<MouseButtonEvent, E>,
mouse_down: EventListener<MouseButtonEvent, E>,
mouse_move: EventListener<MouseMoveEvent, E>,
mouse_scroll: EventListener<MouseScrollEvent, E>,
keyboard_up: EventListener<KeyboardEvent, E>,
keyboard_down: EventListener<KeyboardEvent, E>,
}

impl<E: FutureExecutor> EventListeners<E> {
pub(crate) fn add_listener(&mut self, listener: Listeners<E>) {
match listener {
Listeners::MouseDown(callback) => self.mouse_down.listeners.push(callback),
Listeners::MouseUp(callback) => self.mouse_up.listeners.push(callback),
Listeners::MouseMove(callback) => self.mouse_move.listeners.push(callback),
Listeners::MouseScroll(callback) => self.mouse_scroll.listeners.push(callback),
Listeners::KeyboardUp(callback) => self.keyboard_up.listeners.push(callback),
Listeners::KeyboardDown(callback) => self.keyboard_down.listeners.push(callback),
}
}

pub(crate) fn handle_input_event(&mut self, event: InputEvent, e: &mut E) {
match event {
InputEvent::MouseDown(button) => {
self.mouse_down.handle_event(MouseButtonEvent { button }, e);
}
InputEvent::MouseUp(button) => {
self.mouse_up.handle_event(MouseButtonEvent { button }, e);
}
InputEvent::MouseMove(pos) => {
self.mouse_move.handle_event(MouseMoveEvent { pos }, e);
}
InputEvent::MouseScroll(delta) => {
self.mouse_scroll.handle_event(MouseScrollEvent { delta }, e);
}
InputEvent::KeyboardDown(key) => {
self.keyboard_down.handle_event(KeyboardEvent { key }, e);
}
InputEvent::KeyboardUp(key) => {
self.keyboard_up.handle_event(KeyboardEvent { key }, e);
}
}
}
}

impl<E: FutureExecutor> Default for EventListeners<E> {
fn default() -> Self {
Self {
mouse_up: EventListener::default(),
mouse_down: EventListener::default(),
mouse_move: EventListener::default(),
mouse_scroll: EventListener::default(),
keyboard_up: EventListener::default(),
keyboard_down: EventListener::default(),
}
}
}

impl<E: FutureExecutor> Debug for EventListeners<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EventListeners")
.field("mouse_down", &self.mouse_down)
.field("mouse_up", &self.mouse_up)
.field("mouse_move", &self.mouse_move)
.field("mouse_scroll", &self.mouse_scroll)
.field("keyboard_down", &self.keyboard_down)
.field("keyboard_up", &self.keyboard_up)
.finish()
}
}
Loading

0 comments on commit 0c5d668

Please sign in to comment.