diff --git a/nannou/Cargo.toml b/nannou/Cargo.toml index 3d50152bf..adc1c109e 100644 --- a/nannou/Cargo.toml +++ b/nannou/Cargo.toml @@ -29,6 +29,7 @@ serde = "1" serde_derive = "1" serde_json = "1" toml = "0.5" +trait-set = "0.3.0" walkdir = "2" web-sys = { version = "0.3.55", optional = true } wgpu_upstream = { version = "0.11.1", package = "wgpu" } diff --git a/nannou/src/app.rs b/nannou/src/app.rs index 6fd899182..9a165e891 100644 --- a/nannou/src/app.rs +++ b/nannou/src/app.rs @@ -27,43 +27,46 @@ use std::sync::atomic::{self, AtomicBool}; use std::sync::Arc; use std::time::Duration; use std::{self, future}; +use trait_set::trait_set; use winit; use winit::event_loop::ControlFlow; -/// The user function type for initialising their model. -pub type ModelFn = fn(&App) -> Model; +trait_set! { + /// The user function type for initialising their model. + pub trait ModelFn = 'static + Fn(&App) -> Model; -/// The user function type for updating their model in accordance with some event. -pub type EventFn = fn(&App, &mut Model, Event); + /// The user function type for updating their model in accordance with some event. + pub trait EventFn = 'static + Fn(&App, &mut Model, Event); -/// The user function type for updating the user model within the application loop. -pub type UpdateFn = fn(&App, &mut Model, Update); + /// The user function type for updating the user model within the application loop. + pub trait UpdateFn = 'static + Fn(&App, &mut Model, Update); -/// The user function type for drawing their model to the surface of a single window. -pub type ViewFn = fn(&App, &Model, Frame); + /// The user function type for drawing their model to the surface of a single window. + pub trait ViewFn = 'static + Fn(&App, &Model, Frame); -/// A shorthand version of `ViewFn` for sketches where the user does not need a model. -pub type SketchViewFn = fn(&App, Frame); + /// A shorthand version of `ViewFn` for sketches where the user does not need a model. + pub trait SketchViewFn = 'static + Fn(&App, Frame); -/// The user function type allowing them to consume the `model` when the application exits. -pub type ExitFn = fn(&App, Model); + /// The user function type allowing them to consume the `model` when the application exits. + pub trait ExitFn = 'static + Fn(&App, Model); +} /// The **App**'s view function. enum View { /// A view function allows for viewing the user's model. - WithModel(ViewFn), + WithModel(Box>), /// A **Simple** view function does not require a user **Model**. Simpler to get started. - Sketch(SketchViewFn), + Sketch(Box), } /// A nannou `App` builder. pub struct Builder { model: Box Box + '_>>, config: Config, - event: Option>, - update: Option>, + event: Option>>, + update: Option>>, default_view: Option>, - exit: Option>, + exit: Option>>, create_default_window: bool, default_window_size: Option, capture_frame_timeout: Option>, @@ -251,7 +254,7 @@ where /// /// The Model that is returned by the function is the same model that will be passed to the /// given event and view functions. - pub fn new(model: ModelFn) -> Self { + pub fn new(model: impl ModelFn) -> Self { Self::new_async(move |app| Box::new(future::ready(model(app)))) } @@ -281,7 +284,7 @@ where /// occur during the life of the program. These include things like `Update`s and /// `WindowEvent`s such as `KeyPressed`, `MouseMoved`, and so on. #[cfg_attr(rustfmt, rustfmt_skip)] - pub fn event(self, event: EventFn) -> Builder + pub fn event(self, event: impl EventFn) -> Builder where E: LoopEvent, { @@ -301,7 +304,7 @@ where Builder { model, config, - event: Some(event), + event: Some(Box::new(event)), update, default_view, exit, @@ -332,8 +335,8 @@ where /// /// Note that when working with more than one window, you can use `frame.window_id()` to /// determine which window the current call is associated with. - pub fn view(mut self, view: ViewFn) -> Self { - self.default_view = Some(View::WithModel(view)); + pub fn view(mut self, view: impl ViewFn) -> Self { + self.default_view = Some(View::WithModel(Box::new(view))); self } @@ -345,8 +348,8 @@ where /// Update events are also emitted as a variant of the `event` function. Note that if you /// specify both an `event` function and an `update` function, the `event` function will always /// be called with an update event prior to this `update` function. - pub fn update(mut self, update: UpdateFn) -> Self { - self.update = Some(update); + pub fn update(mut self, update: impl UpdateFn) -> Self { + self.update = Some(Box::new(update)); self } @@ -363,8 +366,8 @@ where /// `App::new_window` method. The role of this `simple_window` method is to provide a /// quick-and-easy way to start with a simple window. This can be very useful for quick ideas, /// small single-window applications and examples. - pub fn simple_window(mut self, view: ViewFn) -> Self { - self.default_view = Some(View::WithModel(view)); + pub fn simple_window(mut self, view: impl ViewFn) -> Self { + self.default_view = Some(View::WithModel(Box::new(view))); self.create_default_window = true; self } @@ -373,8 +376,8 @@ where /// /// The exit function gives ownership of the model back to you for any cleanup that might be /// necessary. - pub fn exit(mut self, exit: ExitFn) -> Self { - self.exit = Some(exit); + pub fn exit(mut self, exit: impl ExitFn) -> Self { + self.exit = Some(Box::new(exit)); self } @@ -547,9 +550,9 @@ impl Builder<(), Event> { /// /// This is useful for late night hack sessions where you just don't care about all that other /// stuff, you just want to play around with some ideas or make something pretty. - pub fn sketch(view: SketchViewFn) -> SketchBuilder { + pub fn sketch(view: impl SketchViewFn) -> SketchBuilder { let mut builder = Builder::new(default_model); - builder.default_view = Some(View::Sketch(view)); + builder.default_view = Some(View::Sketch(Box::new(view))); builder.create_default_window = true; SketchBuilder { builder } } @@ -1028,7 +1031,7 @@ impl EventLoopWindowTarget { // This method is solely used during `window::Builder::build` to allow for pub(crate) fn as_ref(&self) -> &winit::event_loop::EventLoopWindowTarget<()> { match *self { - EventLoopWindowTarget::Owned(ref event_loop) => (&**event_loop), + EventLoopWindowTarget::Owned(ref event_loop) => &**event_loop, EventLoopWindowTarget::Pointer(ptr) => { // This cast is safe, assuming that the `App`'s `EventLoopWindowTarget` will only // ever be in the `Pointer` state while the pointer is valid - that is, during the @@ -1056,10 +1059,10 @@ impl EventLoopWindowTarget { fn run_loop( mut app: App, model: M, - event_fn: Option>, - update_fn: Option>, + event_fn: Option>, + update_fn: Option>, default_view: Option>, - exit_fn: Option>, + exit_fn: Option>, ) where M: 'static, E: LoopEvent, @@ -1100,9 +1103,6 @@ fn run_loop( if let Some(model) = model.as_mut() { let loop_mode = app.loop_mode(); let now = Instant::now(); - let mut do_update = |loop_state: &mut LoopState| { - apply_update(&mut app, model, event_fn, update_fn, loop_state, now); - }; match loop_mode { LoopMode::NTimes { number_of_updates } if loop_state.total_updates >= number_of_updates as u64 => {} @@ -1114,7 +1114,7 @@ fn run_loop( // LoopMode::Wait { updates_before_waiting } => // if loop_state.updates_since_event > updates_before_waiting as u64 => {} _ => { - do_update(&mut loop_state); + apply_update(&mut app, model, &event_fn, &update_fn, &mut loop_state, now); }, } } @@ -1231,13 +1231,13 @@ fn run_loop( (*raw_view)(&app, &model, raw_frame); } None => match default_view { - Some(View::Sketch(view)) => { + Some(View::Sketch(ref view)) => { let data = frame_data.as_ref().expect("missing `frame_data`"); let frame = Frame::new_empty(raw_frame, &data.render, &data.capture); view(&app, frame); } - Some(View::WithModel(view)) => { + Some(View::WithModel(ref view)) => { let data = frame_data.as_ref().expect("missing `frame_data`"); let frame = Frame::new_empty(raw_frame, &data.render, &data.capture); @@ -1330,7 +1330,7 @@ fn run_loop( // Process the event with the user's functions and see if we need to exit. if let Some(model) = model.as_mut() { - exit |= process_and_emit_winit_event::(&mut app, model, event_fn, &event); + exit |= process_and_emit_winit_event::(&mut app, model, &event_fn, &event); } // Set the control flow based on the loop mode. @@ -1348,7 +1348,7 @@ fn run_loop( // If we need to exit, call the user's function and update control flow. if exit { if let Some(model) = model.take() { - if let Some(exit_fn) = exit_fn { + if let Some(exit_fn) = exit_fn.as_ref() { exit_fn(&app, model); } } @@ -1372,8 +1372,8 @@ fn run_loop( fn apply_update( app: &mut App, model: &mut M, - event_fn: Option>, - update_fn: Option>, + event_fn: &Option>, + update_fn: &Option>, loop_state: &mut LoopState, now: Instant, ) where @@ -1458,7 +1458,7 @@ fn should_toggle_fullscreen( fn process_and_emit_winit_event<'a, M, E>( app: &mut App, model: &mut M, - event_fn: Option>, + event_fn: &Option>, winit_event: &winit::event::Event<'a, ()>, ) -> bool where diff --git a/nannou/src/lib.rs b/nannou/src/lib.rs index f63e0f60e..78afb113d 100644 --- a/nannou/src/lib.rs +++ b/nannou/src/lib.rs @@ -54,7 +54,7 @@ pub mod window; /// /// The Model that is returned by the function is the same model that will be passed to the /// given event and view functions. -pub fn app(model: app::ModelFn) -> app::Builder { +pub fn app(model: impl app::ModelFn) -> app::Builder { app::Builder::new(model) } @@ -63,6 +63,6 @@ pub fn app(model: app::ModelFn) -> app::Builder { /// /// This is useful for late night hack sessions where you just don't care about all that other /// stuff, you just want to play around with some ideas or make something pretty. -pub fn sketch(view: app::SketchViewFn) -> app::SketchBuilder { +pub fn sketch(view: impl app::SketchViewFn) -> app::SketchBuilder { app::Builder::sketch(view) }