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
regions: Vec<(String, Color, Polygon)>,
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 {
pub fn new(
ctx: &mut EventCtx,
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> {
app.primary.current_selection = None;
@ -81,7 +82,7 @@ impl CityPicker {
Box::new(CityPicker {
regions,
selected: None,
on_load,
on_load: Some(on_load),
panel: Panel::new(
Widget::col(vec![
Widget::row(vec![
@ -111,13 +112,11 @@ impl State for CityPicker {
return Transition::Pop;
}
name => {
let on_load =
std::mem::replace(&mut self.on_load, Box::new(|_, _| Transition::Keep));
return Transition::Replace(MapLoader::new(
ctx,
app,
name.to_string(),
on_load,
self.on_load.take().unwrap(),
));
}
},
@ -152,9 +151,12 @@ impl State for CityPicker {
.per_obj
.left_click(ctx, format!("switch to {}", nice_map_name(name)))
{
let on_load =
std::mem::replace(&mut self.on_load, Box::new(|_, _| Transition::Keep));
return Transition::Replace(MapLoader::new(ctx, app, name.to_string(), on_load));
return Transition::Replace(MapLoader::new(
ctx,
app,
name.to_string(),
self.on_load.take().unwrap(),
));
}
}

View File

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

View File

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

View File

@ -22,10 +22,12 @@ impl MapLoader {
ctx: &mut EventCtx,
app: &App,
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> {
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.
@ -66,11 +68,11 @@ impl MapLoader {
}
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 {
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) {}
}
@ -81,16 +83,21 @@ mod native_loader {
pub struct FileLoader<T> {
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> {
pub fn new(
_: &mut EventCtx,
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::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 {
ctx.loading_screen(format!("load {}", self.path), |ctx, timer| {
// Assumes a binary file
(self.on_load)(
(self.on_load.take().unwrap())(
ctx,
app,
abstutil::maybe_read_binary(self.path.clone(), timer).ok(),
@ -130,7 +137,7 @@ mod wasm_loader {
// compatible with winit's event loop.
pub struct FileLoader<T> {
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,
started: Instant,
url: String,
@ -140,7 +147,7 @@ mod wasm_loader {
pub fn new(
ctx: &mut EventCtx,
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> {
let url = if cfg!(feature = "wasm_s3") {
format!(
@ -177,7 +184,7 @@ mod wasm_loader {
Box::new(FileLoader {
response: rx,
on_load,
on_load: Some(on_load),
panel: ctx.make_loading_screen(Text::from(Line(format!("Loading {}...", url)))),
started: Instant::now(),
url,
@ -194,7 +201,7 @@ mod wasm_loader {
// TODO Plumb failures
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![

View File

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

View File

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

View File

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