Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept closures in Builder methods (e.g. new, view, update, etc.) #937

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nannou/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
90 changes: 45 additions & 45 deletions nannou/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Model> = fn(&App) -> Model;
trait_set! {
/// The user function type for initialising their model.
pub trait ModelFn<Model> = 'static + Fn(&App) -> Model;

/// The user function type for updating their model in accordance with some event.
pub type EventFn<Model, Event> = fn(&App, &mut Model, Event);
/// The user function type for updating their model in accordance with some event.
pub trait EventFn<Model, Event> = 'static + Fn(&App, &mut Model, Event);

/// The user function type for updating the user model within the application loop.
pub type UpdateFn<Model> = fn(&App, &mut Model, Update);
/// The user function type for updating the user model within the application loop.
pub trait UpdateFn<Model> = 'static + Fn(&App, &mut Model, Update);

/// The user function type for drawing their model to the surface of a single window.
pub type ViewFn<Model> = fn(&App, &Model, Frame);
/// The user function type for drawing their model to the surface of a single window.
pub trait ViewFn<Model> = '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<Model> = fn(&App, Model);
/// The user function type allowing them to consume the `model` when the application exits.
pub trait ExitFn<Model> = 'static + Fn(&App, Model);
}

/// The **App**'s view function.
enum View<Model = ()> {
/// A view function allows for viewing the user's model.
WithModel(ViewFn<Model>),
WithModel(Box<dyn ViewFn<Model>>),
/// A **Simple** view function does not require a user **Model**. Simpler to get started.
Sketch(SketchViewFn),
Sketch(Box<dyn SketchViewFn>),
}

/// A nannou `App` builder.
pub struct Builder<M = (), E = Event> {
model: Box<dyn FnOnce(&App) -> Box<dyn Future<Output = M> + '_>>,
config: Config,
event: Option<EventFn<M, E>>,
update: Option<UpdateFn<M>>,
event: Option<Box<dyn EventFn<M, E>>>,
update: Option<Box<dyn UpdateFn<M>>>,
default_view: Option<View<M>>,
exit: Option<ExitFn<M>>,
exit: Option<Box<dyn ExitFn<M>>>,
create_default_window: bool,
default_window_size: Option<DefaultWindowSize>,
capture_frame_timeout: Option<Option<Duration>>,
Expand Down Expand Up @@ -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<M>) -> Self {
pub fn new(model: impl ModelFn<M>) -> Self {
Self::new_async(move |app| Box::new(future::ready(model(app))))
}

Expand Down Expand Up @@ -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<E>(self, event: EventFn<M, E>) -> Builder<M, E>
pub fn event<E>(self, event: impl EventFn<M, E>) -> Builder<M, E>
where
E: LoopEvent,
{
Expand All @@ -301,7 +304,7 @@ where
Builder {
model,
config,
event: Some(event),
event: Some(Box::new(event)),
update,
default_view,
exit,
Expand Down Expand Up @@ -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<M>) -> Self {
self.default_view = Some(View::WithModel(view));
pub fn view(mut self, view: impl ViewFn<M>) -> Self {
self.default_view = Some(View::WithModel(Box::new(view)));
self
}

Expand All @@ -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<M>) -> Self {
self.update = Some(update);
pub fn update(mut self, update: impl UpdateFn<M>) -> Self {
self.update = Some(Box::new(update));
self
}

Expand All @@ -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<M>) -> Self {
self.default_view = Some(View::WithModel(view));
pub fn simple_window(mut self, view: impl ViewFn<M>) -> Self {
self.default_view = Some(View::WithModel(Box::new(view)));
self.create_default_window = true;
self
}
Expand All @@ -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<M>) -> Self {
self.exit = Some(exit);
pub fn exit(mut self, exit: impl ExitFn<M>) -> Self {
self.exit = Some(Box::new(exit));
self
}

Expand Down Expand Up @@ -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<Event> {
pub fn sketch(view: impl SketchViewFn) -> SketchBuilder<Event> {
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 }
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1056,10 +1059,10 @@ impl EventLoopWindowTarget {
fn run_loop<M, E>(
mut app: App,
model: M,
event_fn: Option<EventFn<M, E>>,
update_fn: Option<UpdateFn<M>>,
event_fn: Option<impl EventFn<M, E>>,
update_fn: Option<impl UpdateFn<M>>,
default_view: Option<View<M>>,
exit_fn: Option<ExitFn<M>>,
exit_fn: Option<impl ExitFn<M>>,
) where
M: 'static,
E: LoopEvent,
Expand Down Expand Up @@ -1100,9 +1103,6 @@ fn run_loop<M, E>(
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 => {}
Expand All @@ -1114,7 +1114,7 @@ fn run_loop<M, E>(
// 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);
},
}
}
Expand Down Expand Up @@ -1231,13 +1231,13 @@ fn run_loop<M, E>(
(*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);
Expand Down Expand Up @@ -1330,7 +1330,7 @@ fn run_loop<M, E>(

// 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::<M, E>(&mut app, model, event_fn, &event);
exit |= process_and_emit_winit_event::<M, E>(&mut app, model, &event_fn, &event);
}

// Set the control flow based on the loop mode.
Expand All @@ -1348,7 +1348,7 @@ fn run_loop<M, E>(
// 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);
}
}
Expand All @@ -1372,8 +1372,8 @@ fn run_loop<M, E>(
fn apply_update<M, E>(
app: &mut App,
model: &mut M,
event_fn: Option<EventFn<M, E>>,
update_fn: Option<UpdateFn<M>>,
event_fn: &Option<impl EventFn<M, E>>,
update_fn: &Option<impl UpdateFn<M>>,
loop_state: &mut LoopState,
now: Instant,
) where
Expand Down Expand Up @@ -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<EventFn<M, E>>,
event_fn: &Option<impl EventFn<M, E>>,
winit_event: &winit::event::Event<'a, ()>,
) -> bool
where
Expand Down
4 changes: 2 additions & 2 deletions nannou/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<M: 'static>(model: app::ModelFn<M>) -> app::Builder<M, Event> {
pub fn app<M: 'static>(model: impl app::ModelFn<M>) -> app::Builder<M, Event> {
app::Builder::new(model)
}

Expand All @@ -63,6 +63,6 @@ pub fn app<M: 'static>(model: app::ModelFn<M>) -> app::Builder<M, 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: app::SketchViewFn) -> app::SketchBuilder<Event> {
pub fn sketch(view: impl app::SketchViewFn) -> app::SketchBuilder<Event> {
app::Builder::sketch(view)
}