Make SimpleApps all manage their own CLI parsing, so they can add their own overrides.

This commit is contained in:
Dustin Carlino 2022-01-31 11:20:50 +00:00
parent eda9ff20bd
commit 942c3ecdaf
12 changed files with 101 additions and 55 deletions

4
Cargo.lock generated
View File

@ -1024,6 +1024,7 @@ dependencies = [
"log",
"map_gui",
"map_model",
"structopt",
"wasm-bindgen",
"widgetry",
]
@ -2137,6 +2138,7 @@ dependencies = [
"maplit",
"serde",
"serde_json",
"structopt",
"synthpop",
"wasm-bindgen",
"web-sys",
@ -2777,6 +2779,7 @@ dependencies = [
"getrandom",
"map_gui",
"map_model",
"structopt",
"wasm-bindgen",
"widgetry",
]
@ -2865,6 +2868,7 @@ dependencies = [
"map_gui",
"map_model",
"reqwest",
"structopt",
"widgetry",
"xmltree",
]

View File

@ -21,5 +21,6 @@ getrandom = { version = "0.2.3", optional = true }
log = "0.4"
map_gui = { path = "../map_gui" }
map_model = { path = "../map_model" }
structopt = "0.3.23"
wasm-bindgen = { version = "0.2.70", optional = true }
widgetry = { path = "../widgetry" }

View File

@ -1,5 +1,7 @@
#![allow(clippy::type_complexity)]
use structopt::StructOpt;
use widgetry::Settings;
#[macro_use]
@ -18,12 +20,15 @@ pub fn main() {
}
fn run(mut settings: Settings) {
let options = map_gui::options::Options::load_or_default();
let mut options = map_gui::options::Options::load_or_default();
let args = map_gui::SimpleAppArgs::from_iter(abstutil::cli_args());
args.override_options(&mut options);
settings = settings
.read_svg(Box::new(abstio::slurp_bytes))
.canvas_settings(options.canvas_settings.clone());
widgetry::run(settings, |ctx| {
map_gui::SimpleApp::new(ctx, options, (), |ctx, app| {
map_gui::SimpleApp::new(ctx, options, args.map_name(), args.cam, (), |ctx, app| {
vec![
map_gui::tools::TitleScreen::new_state(
ctx,

View File

@ -32,6 +32,7 @@ serde_json = "1.0.61"
synthpop = { path = "../synthpop" }
wasm-bindgen = { version = "0.2.70", optional = true }
widgetry = { path = "../widgetry" }
structopt = "0.3.23"
[dependencies.web-sys]
version = "0.3.47"

View File

@ -1,5 +1,7 @@
#![allow(clippy::type_complexity)]
use structopt::StructOpt;
use widgetry::Settings;
pub use browse::BrowseNeighborhoods;
@ -38,6 +40,9 @@ pub fn main() {
fn run(mut settings: Settings) {
let mut opts = map_gui::options::Options::load_or_default();
opts.color_scheme = map_gui::colors::ColorSchemeChoice::DayMode;
let args = map_gui::SimpleAppArgs::from_iter(abstutil::cli_args());
args.override_options(&mut opts);
settings = settings
.read_svg(Box::new(abstio::slurp_bytes))
.canvas_settings(opts.canvas_settings.clone());
@ -57,7 +62,7 @@ fn run(mut settings: Settings) {
current_trip_name: None,
};
map_gui::SimpleApp::new(ctx, opts, session, |ctx, app| {
map_gui::SimpleApp::new(ctx, opts, args.map_name(), args.cam, session, |ctx, app| {
vec![
map_gui::tools::TitleScreen::new_state(
ctx,

View File

@ -17,7 +17,7 @@ use map_model::{
use sim::{AgentID, CarID, PedestrianID, Sim};
use widgetry::{EventCtx, GfxCtx, State};
pub use self::simple_app::SimpleApp;
pub use self::simple_app::{SimpleApp, SimpleAppArgs};
use crate::render::DrawOptions;
use colors::{ColorScheme, ColorSchemeChoice};
use options::Options;

View File

@ -28,55 +28,47 @@ pub struct SimpleApp<T> {
pub time: Time,
}
// Don't give a name and description. If an app cares enough to override, they can't use SimpleApp
// (or refactor to plumb through options themselves)
// A SimpleApp can directly use this (`let args = SimpleAppArgs::from_iter(abstutil::cli_args())`)
// or embed in their own struct and define other flags.
#[derive(StructOpt)]
struct Args {
pub struct SimpleAppArgs {
/// Path to a map to initially load. If not provided, load the last map used or a fixed
/// default.
#[structopt()]
map_path: Option<String>,
pub map_path: Option<String>,
/// Initially position the camera here. The format is an OSM-style `zoom/lat/lon` string
/// (https://wiki.openstreetmap.org/wiki/Browsing#Other_URL_tricks).
#[structopt(long)]
cam: Option<String>,
pub cam: Option<String>,
/// Dev mode exposes experimental tools useful for debugging, but that'd likely confuse most
/// players.
#[structopt(long)]
dev: bool,
pub dev: bool,
/// The color scheme for map elements, agents, and the UI.
#[structopt(long, parse(try_from_str = ColorSchemeChoice::parse))]
color_scheme: Option<ColorSchemeChoice>,
pub color_scheme: Option<ColorSchemeChoice>,
/// When making a screen recording, enable this option to hide some UI elements
#[structopt(long)]
minimal_controls: bool,
pub minimal_controls: bool,
}
impl<T: 'static> SimpleApp<T> {
pub fn new<
F: 'static + Fn(&mut EventCtx, &mut SimpleApp<T>) -> Vec<Box<dyn State<SimpleApp<T>>>>,
>(
ctx: &mut EventCtx,
mut opts: Options,
session: T,
init_states: F,
) -> (SimpleApp<T>, Vec<Box<dyn State<SimpleApp<T>>>>) {
abstutil::logger::setup();
let args = Args::from_iter(abstutil::cli_args());
// Options are passed in by each app, usually seeded with defaults or from a config file.
// For the few options that we allow to be specified by command-line, overwrite the values.
opts.dev = args.dev;
opts.minimal_controls = args.minimal_controls;
if let Some(cs) = args.color_scheme {
impl SimpleAppArgs {
/// Options are passed in by each app, usually seeded with defaults or from a config file. For
/// the few options that we allow to be specified by command-line, overwrite the values.
pub fn override_options(&self, opts: &mut Options) {
opts.dev = self.dev;
opts.minimal_controls = self.minimal_controls;
if let Some(cs) = self.color_scheme {
opts.color_scheme = cs;
opts.toggle_day_night_colors = false;
}
}
ctx.canvas.settings = opts.canvas_settings.clone();
let map_name = args
.map_path
pub fn map_name(&self) -> MapName {
self.map_path
.as_ref()
.map(|path| {
MapName::from_path(&path).unwrap_or_else(|| panic!("bad map path: {}", path))
MapName::from_path(path).unwrap_or_else(|| panic!("bad map path: {}", path))
})
.or_else(|| {
abstio::maybe_read_json::<crate::tools::DefaultMap>(
@ -86,7 +78,23 @@ impl<T: 'static> SimpleApp<T> {
.ok()
.map(|x| x.last_map)
})
.unwrap_or_else(|| MapName::seattle("montlake"));
.unwrap_or_else(|| MapName::seattle("montlake"))
}
}
impl<T: 'static> SimpleApp<T> {
pub fn new<
F: 'static + Fn(&mut EventCtx, &mut SimpleApp<T>) -> Vec<Box<dyn State<SimpleApp<T>>>>,
>(
ctx: &mut EventCtx,
opts: Options,
map_name: MapName,
cam: Option<String>,
session: T,
init_states: F,
) -> (SimpleApp<T>, Vec<Box<dyn State<SimpleApp<T>>>>) {
abstutil::logger::setup();
ctx.canvas.settings = opts.canvas_settings.clone();
let cs = ColorScheme::new(ctx, opts.color_scheme);
// Start with a blank map
@ -107,7 +115,7 @@ impl<T: 'static> SimpleApp<T> {
&app,
map_name,
Box::new(move |ctx, app| {
URLManager::change_camera(ctx, args.cam.as_ref(), app.map().get_gps_bounds());
URLManager::change_camera(ctx, cam.as_ref(), app.map().get_gps_bounds());
Transition::Clear(init_states(ctx, app))
}),
)];

View File

@ -18,5 +18,6 @@ geom = { path = "../geom" }
getrandom = { version = "0.2.3", optional = true }
map_gui = { path = "../map_gui" }
map_model = { path = "../map_model" }
structopt = "0.3.23"
wasm-bindgen = { version = "0.2.70", optional = true }
widgetry = { path = "../widgetry" }

View File

@ -1,5 +1,7 @@
mod viewer;
use structopt::StructOpt;
use widgetry::Settings;
pub fn main() {
@ -8,13 +10,16 @@ pub fn main() {
}
pub fn run(mut settings: Settings) {
let mut options = map_gui::options::Options::load_or_default();
options.show_building_driveways = false;
let mut opts = map_gui::options::Options::load_or_default();
opts.show_building_driveways = false;
let args = map_gui::SimpleAppArgs::from_iter(abstutil::cli_args());
args.override_options(&mut opts);
settings = settings
.read_svg(Box::new(abstio::slurp_bytes))
.canvas_settings(options.canvas_settings.clone());
.canvas_settings(opts.canvas_settings.clone());
widgetry::run(settings, |ctx| {
map_gui::SimpleApp::new(ctx, options, (), |ctx, app| {
map_gui::SimpleApp::new(ctx, opts, args.map_name(), args.cam, (), |ctx, app| {
vec![
map_gui::tools::TitleScreen::new_state(
ctx,

View File

@ -17,5 +17,6 @@ log = "0.4.14"
map_gui = { path = "../map_gui" }
map_model = { path = "../map_model" }
reqwest = { version = "0.11.0", optional = true, default-features=false, features=["blocking", "rustls-tls"] }
structopt = "0.3.23"
widgetry = { path = "../widgetry" }
xmltree = "0.10.1"

View File

@ -1,16 +1,21 @@
#[macro_use]
extern crate log;
use structopt::StructOpt;
mod mapper;
fn main() {
let mut options = map_gui::options::Options::load_or_default();
options.canvas_settings.min_zoom_for_detail = 2.0;
let args = map_gui::SimpleAppArgs::from_iter(abstutil::cli_args());
args.override_options(&mut options);
let settings = widgetry::Settings::new("OSM parking mapper")
.read_svg(Box::new(abstio::slurp_bytes))
.canvas_settings(options.canvas_settings.clone());
widgetry::run(settings, |ctx| {
map_gui::SimpleApp::new(ctx, options, (), |ctx, app| {
map_gui::SimpleApp::new(ctx, options, args.map_name(), args.cam, (), |ctx, app| {
vec![
map_gui::tools::TitleScreen::new_state(
ctx,

View File

@ -30,6 +30,8 @@ pub fn main() {
fn run(mut settings: Settings) {
let mut opts = map_gui::options::Options::load_or_default();
opts.color_scheme = map_gui::colors::ColorSchemeChoice::NightMode;
// Note we don't take any CLI arguments at all. Always start on the first level's map
settings = settings
.read_svg(Box::new(abstio::slurp_bytes))
.canvas_settings(opts.canvas_settings.clone());
@ -37,23 +39,31 @@ fn run(mut settings: Settings) {
let session = session::Session::load();
session.save();
map_gui::SimpleApp::new(ctx, opts, session, |ctx, app| {
if app.opts.dev {
app.session.unlock_all();
}
app.session.music = music::Music::start(ctx, app.session.play_music, "jingle_bells");
app.session.music.specify_volume(music::OUT_OF_GAME);
map_gui::SimpleApp::new(
ctx,
opts,
abstio::MapName::seattle("qa"),
None,
session,
|ctx, app| {
if app.opts.dev {
app.session.unlock_all();
}
app.session.music =
music::Music::start(ctx, app.session.play_music, "jingle_bells");
app.session.music.specify_volume(music::OUT_OF_GAME);
vec![
map_gui::tools::TitleScreen::new_state(
ctx,
app,
map_gui::tools::Executable::Santa,
Box::new(|ctx, app, _| title::TitleScreen::new_state(ctx, app)),
),
title::TitleScreen::new_state(ctx, app),
]
})
vec![
map_gui::tools::TitleScreen::new_state(
ctx,
app,
map_gui::tools::Executable::Santa,
Box::new(|ctx, app, _| title::TitleScreen::new_state(ctx, app)),
),
title::TitleScreen::new_state(ctx, app),
]
},
)
});
}