Slightly simplify some plumbing of variables through nested closures, by

remembering that FnOnce exists
This commit is contained in:
Dustin Carlino 2020-10-14 15:36:08 -05:00
parent 5ef48a5d1c
commit 81dae1e39c
7 changed files with 36 additions and 34 deletions

View File

@ -15,14 +15,15 @@ pub struct CityPicker {
// In untranslated screen-space // In untranslated screen-space
regions: Vec<(String, Color, Polygon)>, regions: Vec<(String, Color, Polygon)>,
selected: Option<usize>, selected: Option<usize>,
on_load: Box<dyn Fn(&mut EventCtx, &mut App) -> Transition>, // Wrapped in an Option just to make calling from event() work.
on_load: Option<Box<dyn FnOnce(&mut EventCtx, &mut App) -> Transition>>,
} }
impl CityPicker { impl CityPicker {
pub fn new( pub fn new(
ctx: &mut EventCtx, ctx: &mut EventCtx,
app: &mut App, app: &mut App,
on_load: Box<dyn Fn(&mut EventCtx, &mut App) -> Transition>, on_load: Box<dyn FnOnce(&mut EventCtx, &mut App) -> Transition>,
) -> Box<dyn State> { ) -> Box<dyn State> {
app.primary.current_selection = None; app.primary.current_selection = None;
@ -81,7 +82,7 @@ impl CityPicker {
Box::new(CityPicker { Box::new(CityPicker {
regions, regions,
selected: None, selected: None,
on_load, on_load: Some(on_load),
panel: Panel::new( panel: Panel::new(
Widget::col(vec![ Widget::col(vec![
Widget::row(vec![ Widget::row(vec![
@ -111,13 +112,11 @@ impl State for CityPicker {
return Transition::Pop; return Transition::Pop;
} }
name => { name => {
let on_load =
std::mem::replace(&mut self.on_load, Box::new(|_, _| Transition::Keep));
return Transition::Replace(MapLoader::new( return Transition::Replace(MapLoader::new(
ctx, ctx,
app, app,
name.to_string(), name.to_string(),
on_load, self.on_load.take().unwrap(),
)); ));
} }
}, },
@ -152,9 +151,12 @@ impl State for CityPicker {
.per_obj .per_obj
.left_click(ctx, format!("switch to {}", nice_map_name(name))) .left_click(ctx, format!("switch to {}", nice_map_name(name)))
{ {
let on_load = return Transition::Replace(MapLoader::new(
std::mem::replace(&mut self.on_load, Box::new(|_, _| Transition::Keep)); ctx,
return Transition::Replace(MapLoader::new(ctx, app, name.to_string(), on_load)); app,
name.to_string(),
self.on_load.take().unwrap(),
));
} }
} }

View File

@ -743,7 +743,7 @@ impl ScreenshotTest {
todo_maps.pop().unwrap().to_string(), todo_maps.pop().unwrap().to_string(),
Box::new(move |_, _| { Box::new(move |_, _| {
Transition::Replace(Box::new(ScreenshotTest { Transition::Replace(Box::new(ScreenshotTest {
todo_maps: todo_maps.clone(), todo_maps,
screenshot_done: false, screenshot_done: false,
})) }))
}), }),

View File

@ -101,7 +101,6 @@ impl EditMode {
app.primary.sim.handle_live_edits(&app.primary.map); app.primary.sim.handle_live_edits(&app.primary.map);
Transition::Pop Transition::Pop
} else { } else {
let resume_time = old_sim.time();
Transition::Multi(vec![ Transition::Multi(vec![
Transition::Pop, Transition::Pop,
Transition::Replace(SandboxMode::async_new( Transition::Replace(SandboxMode::async_new(
@ -112,7 +111,7 @@ impl EditMode {
vec![Transition::Push(TimeWarpScreen::new( vec![Transition::Push(TimeWarpScreen::new(
ctx, ctx,
app, app,
resume_time, old_sim.time(),
None, None,
))] ))]
}), }),

View File

@ -22,10 +22,12 @@ impl MapLoader {
ctx: &mut EventCtx, ctx: &mut EventCtx,
app: &App, app: &App,
name: String, name: String,
on_load: Box<dyn Fn(&mut EventCtx, &mut App) -> Transition>, on_load: Box<dyn FnOnce(&mut EventCtx, &mut App) -> Transition>,
) -> Box<dyn State> { ) -> Box<dyn State> {
if app.primary.map.get_name() == &name { if app.primary.map.get_name() == &name {
return Box::new(MapAlreadyLoaded { on_load }); return Box::new(MapAlreadyLoaded {
on_load: Some(on_load),
});
} }
// TODO If we want to load montlake on the web, just pull from bundled data. // TODO If we want to load montlake on the web, just pull from bundled data.
@ -66,11 +68,11 @@ impl MapLoader {
} }
struct MapAlreadyLoaded { struct MapAlreadyLoaded {
on_load: Box<dyn Fn(&mut EventCtx, &mut App) -> Transition>, on_load: Option<Box<dyn FnOnce(&mut EventCtx, &mut App) -> Transition>>,
} }
impl State for MapAlreadyLoaded { impl State for MapAlreadyLoaded {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition { fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
(self.on_load)(ctx, app) (self.on_load.take().unwrap())(ctx, app)
} }
fn draw(&self, _: &mut GfxCtx, _: &App) {} fn draw(&self, _: &mut GfxCtx, _: &App) {}
} }
@ -81,16 +83,21 @@ mod native_loader {
pub struct FileLoader<T> { pub struct FileLoader<T> {
path: String, path: String,
on_load: Box<dyn Fn(&mut EventCtx, &mut App, Option<T>) -> Transition>, // Wrapped in an Option just to make calling from event() work. Technically this is unsafe
// if a caller fails to pop the FileLoader state in their transitions!
on_load: Option<Box<dyn FnOnce(&mut EventCtx, &mut App, Option<T>) -> Transition>>,
} }
impl<T: 'static + DeserializeOwned> FileLoader<T> { impl<T: 'static + DeserializeOwned> FileLoader<T> {
pub fn new( pub fn new(
_: &mut EventCtx, _: &mut EventCtx,
path: String, path: String,
on_load: Box<dyn Fn(&mut EventCtx, &mut App, Option<T>) -> Transition>, on_load: Box<dyn FnOnce(&mut EventCtx, &mut App, Option<T>) -> Transition>,
) -> Box<dyn State> { ) -> Box<dyn State> {
Box::new(FileLoader { path, on_load }) Box::new(FileLoader {
path,
on_load: Some(on_load),
})
} }
} }
@ -98,7 +105,7 @@ mod native_loader {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition { fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
ctx.loading_screen(format!("load {}", self.path), |ctx, timer| { ctx.loading_screen(format!("load {}", self.path), |ctx, timer| {
// Assumes a binary file // Assumes a binary file
(self.on_load)( (self.on_load.take().unwrap())(
ctx, ctx,
app, app,
abstutil::maybe_read_binary(self.path.clone(), timer).ok(), abstutil::maybe_read_binary(self.path.clone(), timer).ok(),
@ -130,7 +137,7 @@ mod wasm_loader {
// compatible with winit's event loop. // compatible with winit's event loop.
pub struct FileLoader<T> { pub struct FileLoader<T> {
response: oneshot::Receiver<Vec<u8>>, response: oneshot::Receiver<Vec<u8>>,
on_load: Box<dyn Fn(&mut EventCtx, &mut App, Option<T>) -> Transition>, on_load: Option<Box<dyn FnOnce(&mut EventCtx, &mut App, Option<T>) -> Transition>>,
panel: Panel, panel: Panel,
started: Instant, started: Instant,
url: String, url: String,
@ -140,7 +147,7 @@ mod wasm_loader {
pub fn new( pub fn new(
ctx: &mut EventCtx, ctx: &mut EventCtx,
path: String, path: String,
on_load: Box<dyn Fn(&mut EventCtx, &mut App, Option<T>) -> Transition>, on_load: Box<dyn FnOnce(&mut EventCtx, &mut App, Option<T>) -> Transition>,
) -> Box<dyn State> { ) -> Box<dyn State> {
let url = if cfg!(feature = "wasm_s3") { let url = if cfg!(feature = "wasm_s3") {
format!( format!(
@ -177,7 +184,7 @@ mod wasm_loader {
Box::new(FileLoader { Box::new(FileLoader {
response: rx, response: rx,
on_load, on_load: Some(on_load),
panel: ctx.make_loading_screen(Text::from(Line(format!("Loading {}...", url)))), panel: ctx.make_loading_screen(Text::from(Line(format!("Loading {}...", url)))),
started: Instant::now(), started: Instant::now(),
url, url,
@ -194,7 +201,7 @@ mod wasm_loader {
// TODO Plumb failures // TODO Plumb failures
let obj: T = abstutil::from_binary(&resp).unwrap(); let obj: T = abstutil::from_binary(&resp).unwrap();
return (self.on_load)(ctx, app, Some(obj)); return (self.on_load.take().unwrap())(ctx, app, Some(obj));
} }
self.panel = ctx.make_loading_screen(Text::from_multiline(vec![ self.panel = ctx.make_loading_screen(Text::from_multiline(vec![

View File

@ -417,10 +417,7 @@ impl State for Proposals {
Box::new(move |ctx, app| { Box::new(move |ctx, app| {
// Apply edits before setting up the sandbox, for simplicity // Apply edits before setting up the sandbox, for simplicity
let maybe_err = ctx.loading_screen("apply edits", |ctx, mut timer| { let maybe_err = ctx.loading_screen("apply edits", |ctx, mut timer| {
match PermanentMapEdits::from_permanent( match PermanentMapEdits::from_permanent(edits, &app.primary.map) {
edits.clone(),
&app.primary.map,
) {
Ok(edits) => { Ok(edits) => {
apply_map_edits(ctx, app, edits); apply_map_edits(ctx, app, edits);
app.primary app.primary

View File

@ -65,7 +65,7 @@ impl GameplayState for PlayScenario {
)) { )) {
GameplayMode::PlayScenario( GameplayMode::PlayScenario(
app.primary.map.get_name().clone(), app.primary.map.get_name().clone(),
scenario.clone(), scenario,
Vec::new(), Vec::new(),
) )
} else { } else {

View File

@ -618,15 +618,12 @@ impl State for SandboxLoader {
ctx, ctx,
abstutil::path_prebaked_results(app.primary.map.get_name(), &scenario_name), abstutil::path_prebaked_results(app.primary.map.get_name(), &scenario_name),
Box::new(move |_, _, prebaked| { Box::new(move |_, _, prebaked| {
let scenario_name = scenario_name.clone();
Transition::Multi(vec![ Transition::Multi(vec![
Transition::Pop, Transition::Pop,
Transition::ModifyState(Box::new(move |state, _, _| { Transition::ModifyState(Box::new(move |state, _, _| {
let loader = state.downcast_mut::<SandboxLoader>().unwrap(); let loader = state.downcast_mut::<SandboxLoader>().unwrap();
loader.stage = Some(LoadStage::GotPrebaked( loader.stage =
scenario_name.clone(), Some(LoadStage::GotPrebaked(scenario_name, prebaked));
prebaked,
));
})), })),
]) ])
}), }),