Give SimpleApp a way to stash session-wide state. First use case is high scores for the experiment.

This commit is contained in:
Dustin Carlino 2020-12-04 15:08:15 -08:00
parent c9809c805b
commit df04fd7e18
16 changed files with 131 additions and 125 deletions

View File

@ -1,12 +1,11 @@
use abstutil::prettyprint_usize;
use map_gui::SimpleApp;
use widgetry::{
Btn, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, Text, Transition,
Btn, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, Text,
VerticalAlignment, Widget,
};
use crate::levels::Level;
use crate::session::Session;
use crate::{App, Transition};
const ZOOM: f64 = 2.0;
@ -20,16 +19,14 @@ pub struct Results {
impl Results {
pub fn new(
ctx: &mut EventCtx,
app: &SimpleApp,
app: &mut App,
score: usize,
level: &Level,
) -> Box<dyn State<SimpleApp>> {
) -> Box<dyn State<App>> {
ctx.canvas.cam_zoom = ZOOM;
ctx.canvas.center_on_map_pt(app.map.get_bounds().center());
// TODO Store in app
let mut session = Session::new();
session.record_score(level.title, score);
app.session.record_score(level.title, score);
let mut txt = Text::new();
txt.add(Line(format!("Results for {}", level.title)).small_heading());
@ -41,7 +38,7 @@ impl Results {
)));
txt.add(Line(""));
txt.add(Line("High scores:"));
for (idx, score) in session.high_scores[level.title].iter().enumerate() {
for (idx, score) in app.session.high_scores[level.title].iter().enumerate() {
txt.add(Line(format!("{}) {}", idx + 1, prettyprint_usize(*score))));
}
@ -55,8 +52,8 @@ impl Results {
}
}
impl State<SimpleApp> for Results {
fn event(&mut self, ctx: &mut EventCtx, _: &mut SimpleApp) -> Transition<SimpleApp> {
impl State<App> for Results {
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
ctx.canvas_movement();
match self.panel.event(ctx) {
@ -72,7 +69,7 @@ impl State<SimpleApp> for Results {
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, _: &SimpleApp) {
fn draw(&self, g: &mut GfxCtx, _: &App) {
self.panel.draw(g);
}
}

View File

@ -2,17 +2,18 @@ use std::collections::HashSet;
use abstutil::prettyprint_usize;
use map_gui::load::MapLoader;
use map_gui::{SimpleApp, ID};
use map_gui::ID;
use map_model::BuildingID;
use widgetry::{
Btn, Choice, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State,
Text, TextExt, Transition, VerticalAlignment, Widget,
Text, TextExt, VerticalAlignment, Widget,
};
use crate::buildings::{BldgState, Buildings};
use crate::game::Game;
use crate::levels::Level;
use crate::vehicles::Vehicle;
use crate::{App, Transition};
const ZOOM: f64 = 2.0;
@ -24,7 +25,7 @@ pub struct Picker {
}
impl Picker {
pub fn new(ctx: &mut EventCtx, app: &SimpleApp, level: Level) -> Box<dyn State<SimpleApp>> {
pub fn new(ctx: &mut EventCtx, app: &App, level: Level) -> Box<dyn State<App>> {
MapLoader::new(
ctx,
app,
@ -89,8 +90,8 @@ impl Picker {
}
}
impl State<SimpleApp> for Picker {
fn event(&mut self, ctx: &mut EventCtx, app: &mut SimpleApp) -> Transition<SimpleApp> {
impl State<App> for Picker {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
ctx.canvas_movement();
if ctx.redo_mouseover() {
@ -132,7 +133,7 @@ impl State<SimpleApp> for Picker {
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, app: &SimpleApp) {
fn draw(&self, g: &mut GfxCtx, app: &App) {
self.panel.draw(g);
g.redraw(&self.bldgs.draw_all);
for b in &self.current_picks {

View File

@ -1,9 +1,10 @@
use std::collections::{HashMap, HashSet};
use map_gui::SimpleApp;
use map_model::{BuildingID, BuildingType};
use widgetry::{Color, Drawable, EventCtx, GeomBatch, Line, Text};
use crate::App;
pub struct Buildings {
// Every building in the map is here, to simplify lookup logic.
pub buildings: HashMap<BuildingID, BldgState>,
@ -23,7 +24,7 @@ pub enum BldgState {
}
impl Buildings {
pub fn new(ctx: &mut EventCtx, app: &SimpleApp, upzones: HashSet<BuildingID>) -> Buildings {
pub fn new(ctx: &mut EventCtx, app: &App, upzones: HashSet<BuildingID>) -> Buildings {
let house_color = app.cs.residential_building;
let apartment_color = Color::CYAN;
let store_color = Color::YELLOW;

View File

@ -3,11 +3,10 @@ use std::collections::HashSet;
use abstutil::prettyprint_usize;
use geom::{ArrowCap, Circle, Distance, Duration, PolyLine, Pt2D, Time};
use map_gui::tools::{ChooseSomething, ColorLegend, SimpleMinimap};
use map_gui::SimpleApp;
use map_model::BuildingID;
use widgetry::{
Btn, Choice, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line,
Outcome, Panel, State, Text, TextExt, Transition, UpdateType, VerticalAlignment, Widget,
Outcome, Panel, State, Text, TextExt, UpdateType, VerticalAlignment, Widget,
};
use crate::after_level::Results;
@ -17,6 +16,7 @@ use crate::levels::Level;
use crate::meters::{custom_bar, make_bar};
use crate::movement::Player;
use crate::vehicles::Vehicle;
use crate::{App, Transition};
const ACQUIRE_BOOST_RATE: f64 = 0.5;
const BOOST_SPEED_MULTIPLIER: f64 = 2.0;
@ -39,11 +39,11 @@ pub struct Game {
impl Game {
pub fn new(
ctx: &mut EventCtx,
app: &SimpleApp,
app: &App,
level: Level,
vehicle: Vehicle,
upzones: HashSet<BuildingID>,
) -> Box<dyn State<SimpleApp>> {
) -> Box<dyn State<App>> {
let title_panel = Panel::new(Widget::row(vec![
Btn::svg_def("system/assets/tools/home.svg").build(ctx, "back", Key::Escape),
"15 min Santa".draw_text(ctx),
@ -153,8 +153,8 @@ impl Game {
}
}
impl State<SimpleApp> for Game {
fn event(&mut self, ctx: &mut EventCtx, app: &mut SimpleApp) -> Transition<SimpleApp> {
impl State<App> for Game {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
if let Some(dt) = ctx.input.nonblocking_is_update_event() {
self.time += dt;
@ -301,7 +301,7 @@ impl State<SimpleApp> for Game {
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, app: &SimpleApp) {
fn draw(&self, g: &mut GfxCtx, app: &App) {
self.title_panel.draw(g);
self.status_panel.draw(g);
self.time_panel.draw(g);
@ -364,7 +364,7 @@ struct GameState {
impl GameState {
fn new(
ctx: &mut EventCtx,
app: &SimpleApp,
app: &App,
level: Level,
vehicle: Vehicle,
bldgs: Buildings,
@ -386,7 +386,7 @@ impl GameState {
s
}
fn recalc_deliveries(&mut self, ctx: &mut EventCtx, app: &SimpleApp) {
fn recalc_deliveries(&mut self, ctx: &mut EventCtx, app: &App) {
let mut batch = GeomBatch::new();
for (b, state) in &self.bldgs.buildings {
if let BldgState::Done = state {
@ -398,12 +398,7 @@ impl GameState {
}
// If something changed, return the update to the score
fn present_dropped(
&mut self,
ctx: &mut EventCtx,
app: &SimpleApp,
id: BuildingID,
) -> Option<usize> {
fn present_dropped(&mut self, ctx: &mut EventCtx, app: &App, id: BuildingID) -> Option<usize> {
if !self.has_energy() {
return None;
}
@ -442,7 +437,7 @@ impl EnergylessArrow {
fn update(
&mut self,
ctx: &mut EventCtx,
app: &SimpleApp,
app: &App,
time: Time,
sleigh: Pt2D,
all_stores: Vec<BuildingID>,

View File

@ -14,11 +14,19 @@ mod session;
mod title;
mod vehicles;
type App = map_gui::SimpleApp<session::Session>;
type Transition = widgetry::Transition<App>;
pub fn main() {
widgetry::run(widgetry::Settings::new("experiment"), |ctx| {
let mut opts = map_gui::options::Options::default();
opts.color_scheme = map_gui::colors::ColorSchemeChoice::NightMode;
let app = map_gui::SimpleApp::new_with_opts(ctx, abstutil::CmdArgs::new(), opts);
let app = map_gui::SimpleApp::new_with_opts(
ctx,
abstutil::CmdArgs::new(),
opts,
session::Session::new(),
);
let states = vec![title::TitleScreen::new(ctx)];
(app, states)
});

View File

@ -2,11 +2,12 @@ use std::collections::{HashMap, HashSet};
use abstutil::MultiMap;
use geom::{Angle, Circle, Distance, Pt2D, Speed};
use map_gui::{SimpleApp, ID};
use map_gui::ID;
use map_model::{BuildingID, Direction, IntersectionID, LaneType, RoadID};
use widgetry::EventCtx;
use crate::controls::InstantController;
use crate::App;
pub const ZOOM: f64 = 10.0;
@ -19,7 +20,7 @@ pub struct Player {
}
impl Player {
pub fn new(ctx: &mut EventCtx, app: &SimpleApp, start: IntersectionID) -> Player {
pub fn new(ctx: &mut EventCtx, app: &App, start: IntersectionID) -> Player {
ctx.canvas.cam_zoom = ZOOM;
let pos = app.map.get_i(start).polygon.center();
ctx.canvas.center_on_map_pt(pos);
@ -37,7 +38,7 @@ impl Player {
pub fn update_with_speed(
&mut self,
ctx: &mut EventCtx,
app: &SimpleApp,
app: &App,
speed: Speed,
) -> Vec<BuildingID> {
let (dx, dy) = self.controls.displacement(ctx, speed);
@ -52,7 +53,7 @@ impl Player {
fn apply_displacement(
&mut self,
ctx: &mut EventCtx,
app: &SimpleApp,
app: &App,
dx: f64,
dy: f64,
recurse: bool,
@ -144,7 +145,7 @@ impl Player {
}
/// Is the player currently on a road with a bus or bike lane?
pub fn on_good_road(&self, app: &SimpleApp) -> bool {
pub fn on_good_road(&self, app: &App) -> bool {
if let On::Road(r, _) = self.on {
for (_, _, lt) in app.map.get_r(r).lanes_ltr() {
if lt == LaneType::Biking || lt == LaneType::Bus {
@ -164,7 +165,7 @@ enum On {
}
impl On {
fn get_connections(&self, app: &SimpleApp) -> (HashSet<RoadID>, HashSet<IntersectionID>) {
fn get_connections(&self, app: &App) -> (HashSet<RoadID>, HashSet<IntersectionID>) {
let mut valid_roads = HashSet::new();
let mut valid_intersections = HashSet::new();
match self {
@ -198,7 +199,7 @@ struct BuildingsAlongRoad {
}
impl BuildingsAlongRoad {
fn new(app: &SimpleApp) -> BuildingsAlongRoad {
fn new(app: &App) -> BuildingsAlongRoad {
let mut raw: MultiMap<RoadID, (Distance, BuildingID)> = MultiMap::new();
for b in app.map.all_buildings() {
// TODO Happily assuming road and lane length is roughly the same

View File

@ -1,18 +1,17 @@
use map_gui::tools::{open_browser, PopupMsg};
use map_gui::SimpleApp;
use widgetry::{
Btn, DrawBaselayer, EventCtx, GfxCtx, Key, Line, Outcome, Panel, State, Text, Transition,
Widget,
Btn, DrawBaselayer, EventCtx, GfxCtx, Key, Line, Outcome, Panel, State, Text, Widget,
};
use crate::levels::Level;
use crate::{App, Transition};
pub struct TitleScreen {
panel: Panel,
}
impl TitleScreen {
pub fn new(ctx: &mut EventCtx) -> Box<dyn State<SimpleApp>> {
pub fn new(ctx: &mut EventCtx) -> Box<dyn State<App>> {
let levels = Level::all();
Box::new(TitleScreen {
@ -47,8 +46,8 @@ impl TitleScreen {
}
}
impl State<SimpleApp> for TitleScreen {
fn event(&mut self, ctx: &mut EventCtx, app: &mut SimpleApp) -> Transition<SimpleApp> {
impl State<App> for TitleScreen {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
match self.panel.event(ctx) {
Outcome::Clicked(x) => match x.as_ref() {
"quit" => {
@ -102,7 +101,7 @@ impl State<SimpleApp> for TitleScreen {
DrawBaselayer::Custom
}
fn draw(&self, g: &mut GfxCtx, app: &SimpleApp) {
fn draw(&self, g: &mut GfxCtx, app: &App) {
g.clear(app.cs.dialog_bg);
self.panel.draw(g);
}

View File

@ -3,10 +3,11 @@ use std::collections::HashMap;
use abstutil::MultiMap;
use geom::{Duration, Polygon};
use map_gui::tools::{amenity_type, Grid};
use map_gui::SimpleApp;
use map_model::{connectivity, BuildingID, Map, Path, PathConstraints, PathRequest};
use widgetry::{Color, Drawable, EventCtx, GeomBatch};
use crate::App;
/// Represents the area reachable from a single building.
pub struct Isochrone {
/// The center of the isochrone
@ -24,7 +25,7 @@ pub struct Isochrone {
impl Isochrone {
pub fn new(
ctx: &mut EventCtx,
app: &SimpleApp,
app: &App,
start: BuildingID,
constraints: PathConstraints,
) -> Isochrone {
@ -57,10 +58,7 @@ impl Isochrone {
}
}
fn draw_isochrone(
app: &SimpleApp,
time_to_reach_building: &HashMap<BuildingID, Duration>,
) -> GeomBatch {
fn draw_isochrone(app: &App, time_to_reach_building: &HashMap<BuildingID, Duration>) -> GeomBatch {
// To generate the polygons covering areas between 0-5 mins, 5-10 mins, etc, we have to feed
// in a 2D grid of costs. Use a 100x100 meter resolution.
let bounds = app.map.get_bounds();

View File

@ -4,9 +4,11 @@ mod viewer;
#[macro_use]
extern crate log;
type App = map_gui::SimpleApp<()>;
fn main() {
widgetry::run(widgetry::Settings::new("15-minute neighborhoods"), |ctx| {
let app = map_gui::SimpleApp::new(ctx, abstutil::CmdArgs::new());
let app = map_gui::SimpleApp::new(ctx, abstutil::CmdArgs::new(), ());
let states = vec![viewer::Viewer::random_start(ctx, &app)];
(app, states)
});

View File

@ -6,7 +6,7 @@
use geom::{Distance, Pt2D};
use map_gui::tools::{amenity_type, nice_map_name, CityPicker, PopupMsg};
use map_gui::{Cached, SimpleApp, ID};
use map_gui::{Cached, ID};
use map_model::{Building, BuildingID, PathConstraints};
use widgetry::{
lctrl, Btn, Checkbox, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
@ -14,6 +14,7 @@ use widgetry::{
};
use crate::isochrone::Isochrone;
use crate::App;
/// This is the UI state for exploring the isochrone/walkshed from a single building.
pub struct Viewer {
@ -26,17 +27,13 @@ pub struct Viewer {
impl Viewer {
/// Start with a random building
pub fn random_start(ctx: &mut EventCtx, app: &SimpleApp) -> Box<dyn State<SimpleApp>> {
pub fn random_start(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
let bldgs = app.map.all_buildings();
let start = bldgs[bldgs.len() / 2].id;
Viewer::new(ctx, app, start)
}
pub fn new(
ctx: &mut EventCtx,
app: &SimpleApp,
start: BuildingID,
) -> Box<dyn State<SimpleApp>> {
pub fn new(ctx: &mut EventCtx, app: &App, start: BuildingID) -> Box<dyn State<App>> {
let constraints = PathConstraints::Pedestrian;
let start = app.map.get_b(start);
let isochrone = Isochrone::new(ctx, app, start.id, constraints);
@ -52,8 +49,8 @@ impl Viewer {
}
}
impl State<SimpleApp> for Viewer {
fn event(&mut self, ctx: &mut EventCtx, app: &mut SimpleApp) -> Transition<SimpleApp> {
impl State<App> for Viewer {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition<App> {
// Allow panning and zooming
ctx.canvas_movement();
@ -155,7 +152,7 @@ impl State<SimpleApp> for Viewer {
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, _: &SimpleApp) {
fn draw(&self, g: &mut GfxCtx, _: &App) {
g.redraw(&self.isochrone.draw);
g.redraw(&self.highlight_start);
self.panel.draw(g);
@ -175,12 +172,7 @@ fn draw_star(ctx: &mut EventCtx, center: Pt2D) -> Drawable {
)
}
fn build_panel(
ctx: &mut EventCtx,
app: &SimpleApp,
start: &Building,
isochrone: &Isochrone,
) -> Panel {
fn build_panel(ctx: &mut EventCtx, app: &App, start: &Building, isochrone: &Isochrone) -> Panel {
let mut rows = Vec::new();
rows.push(Widget::row(vec![
@ -236,7 +228,7 @@ struct HoverOnBuilding {
type HoverKey = (BuildingID, f64);
impl HoverOnBuilding {
fn key(ctx: &EventCtx, app: &SimpleApp) -> Option<HoverKey> {
fn key(ctx: &EventCtx, app: &App) -> Option<HoverKey> {
match app.mouseover_unzoomed_buildings(ctx) {
Some(ID::Building(b)) => {
let scale_factor = if ctx.canvas.cam_zoom >= app.opts.min_zoom_for_detail {
@ -252,7 +244,7 @@ impl HoverOnBuilding {
fn value(
ctx: &mut EventCtx,
app: &SimpleApp,
app: &App,
key: HoverKey,
isochrone: &Isochrone,
) -> HoverOnBuilding {

View File

@ -11,20 +11,27 @@ use crate::render::{DrawOptions, Renderable};
use crate::{AppLike, ID};
/// Simple app state that just renders a static map, without any dynamic agents on the map.
pub struct SimpleApp {
pub struct SimpleApp<T> {
pub map: Map,
pub draw_map: DrawMap,
pub cs: ColorScheme,
pub opts: Options,
pub current_selection: Option<ID>,
/// Custom per-app state can be stored here
pub session: T,
}
impl SimpleApp {
pub fn new(ctx: &mut EventCtx, args: CmdArgs) -> SimpleApp {
SimpleApp::new_with_opts(ctx, args, Options::default())
impl<T> SimpleApp<T> {
pub fn new(ctx: &mut EventCtx, args: CmdArgs, session: T) -> SimpleApp<T> {
SimpleApp::new_with_opts(ctx, args, Options::default(), session)
}
pub fn new_with_opts(ctx: &mut EventCtx, mut args: CmdArgs, mut opts: Options) -> SimpleApp {
pub fn new_with_opts(
ctx: &mut EventCtx,
mut args: CmdArgs,
mut opts: Options,
session: T,
) -> SimpleApp<T> {
ctx.loading_screen("load map", |ctx, mut timer| {
opts.update_from_args(&mut args);
let map_path = args
@ -42,6 +49,7 @@ impl SimpleApp {
cs,
opts,
current_selection: None,
session,
}
})
}
@ -189,7 +197,7 @@ impl SimpleApp {
}
}
impl AppLike for SimpleApp {
impl<T> AppLike for SimpleApp<T> {
#[inline]
fn map(&self) -> &Map {
&self.map
@ -244,7 +252,7 @@ impl AppLike for SimpleApp {
pt: Pt2D,
target_cam_zoom: Option<f64>,
_: Option<ID>,
) -> Box<dyn State<SimpleApp>> {
) -> Box<dyn State<SimpleApp<T>>> {
Box::new(SimpleWarper {
warper: Warper::new(ctx, pt, target_cam_zoom),
})
@ -264,7 +272,7 @@ impl AppLike for SimpleApp {
}
}
impl SharedAppState for SimpleApp {
impl<T> SharedAppState for SimpleApp<T> {
fn draw_default(&self, g: &mut GfxCtx) {
self.draw_with_opts(g, DrawOptions::new());
}
@ -282,8 +290,8 @@ struct SimpleWarper {
warper: Warper,
}
impl State<SimpleApp> for SimpleWarper {
fn event(&mut self, ctx: &mut EventCtx, _: &mut SimpleApp) -> Transition<SimpleApp> {
impl<T> State<SimpleApp<T>> for SimpleWarper {
fn event(&mut self, ctx: &mut EventCtx, _: &mut SimpleApp<T>) -> Transition<SimpleApp<T>> {
if self.warper.event(ctx) {
Transition::Keep
} else {
@ -291,7 +299,7 @@ impl State<SimpleApp> for SimpleWarper {
}
}
fn draw(&self, _: &mut GfxCtx, _: &SimpleApp) {}
fn draw(&self, _: &mut GfxCtx, _: &SimpleApp<T>) {}
}
/// Store a cached key/value pair, only recalculating when the key changes.

View File

@ -25,7 +25,7 @@ pub struct SimpleMinimap {
}
impl SimpleMinimap {
pub fn new(ctx: &mut EventCtx, app: &SimpleApp, with_zorder: bool) -> SimpleMinimap {
pub fn new<T>(ctx: &mut EventCtx, app: &SimpleApp<T>, with_zorder: bool) -> SimpleMinimap {
// Initially pick a zoom to fit the smaller of the entire map's width or height in the
// minimap. Arbitrary and probably pretty weird.
let bounds = app.map.get_bounds();
@ -49,7 +49,7 @@ impl SimpleMinimap {
m
}
pub fn recreate_panel(&mut self, ctx: &mut EventCtx, app: &SimpleApp) {
pub fn recreate_panel<T>(&mut self, ctx: &mut EventCtx, app: &SimpleApp<T>) {
if ctx.canvas.cam_zoom < app.opts.min_zoom_for_detail {
self.panel = Panel::empty(ctx);
return;
@ -138,7 +138,7 @@ impl SimpleMinimap {
(pct_x, pct_y)
}
pub fn set_zoom(&mut self, ctx: &mut EventCtx, app: &SimpleApp, zoom_lvl: usize) {
pub fn set_zoom<T>(&mut self, ctx: &mut EventCtx, app: &SimpleApp<T>, zoom_lvl: usize) {
// Make the frame wind up in the same relative position on the minimap
let (pct_x, pct_y) = self.map_to_minimap_pct(ctx.canvas.center_to_map_pt());
@ -154,7 +154,7 @@ impl SimpleMinimap {
self.offset_y = map_center.y() * self.zoom - pct_y * inner_rect.height();
}
fn recenter(&mut self, ctx: &EventCtx, app: &SimpleApp) {
fn recenter<T>(&mut self, ctx: &EventCtx, app: &SimpleApp<T>) {
// Recenter the minimap on the screen bounds
let map_center = ctx.canvas.center_to_map_pt();
let rect = self.panel.rect_of("minimap");
@ -169,11 +169,11 @@ impl SimpleMinimap {
self.offset_y = off_y.max(0.0).min(bounds.max_y * self.zoom - rect.height());
}
pub fn event(
pub fn event<T: 'static>(
&mut self,
ctx: &mut EventCtx,
app: &mut SimpleApp,
) -> Option<Transition<SimpleApp>> {
app: &mut SimpleApp<T>,
) -> Option<Transition<SimpleApp<T>>> {
let zoomed = ctx.canvas.cam_zoom >= app.opts.min_zoom_for_detail;
if zoomed != self.zoomed {
let just_zoomed_in = zoomed && !self.zoomed;
@ -303,11 +303,16 @@ impl SimpleMinimap {
None
}
pub fn draw(&self, g: &mut GfxCtx, app: &SimpleApp) {
pub fn draw<T>(&self, g: &mut GfxCtx, app: &SimpleApp<T>) {
self.draw_with_extra_layers(g, app, Vec::new());
}
pub fn draw_with_extra_layers(&self, g: &mut GfxCtx, app: &SimpleApp, extra: Vec<&Drawable>) {
pub fn draw_with_extra_layers<T>(
&self,
g: &mut GfxCtx,
app: &SimpleApp<T>,
extra: Vec<&Drawable>,
) {
self.panel.draw(g);
if !self.zoomed {
return;

View File

@ -2,7 +2,7 @@ mod viewer;
fn main() {
widgetry::run(widgetry::Settings::new("OpenStreetMap viewer"), |ctx| {
let app = map_gui::SimpleApp::new(ctx, abstutil::CmdArgs::new());
let app = map_gui::SimpleApp::new(ctx, abstutil::CmdArgs::new(), ());
let states = vec![viewer::Viewer::new(ctx, &app)];
(app, states)
});

View File

@ -15,6 +15,8 @@ use widgetry::{
VerticalAlignment, Widget,
};
type App = SimpleApp<()>;
pub struct Viewer {
top_panel: Panel,
fixed_object_outline: Option<Drawable>,
@ -23,7 +25,7 @@ pub struct Viewer {
}
impl Viewer {
pub fn new(ctx: &mut EventCtx, app: &SimpleApp) -> Box<dyn State<SimpleApp>> {
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
let with_zorder = true;
let mut viewer = Viewer {
fixed_object_outline: None,
@ -40,7 +42,7 @@ impl Viewer {
fn recalculate_top_panel(
&mut self,
ctx: &mut EventCtx,
app: &SimpleApp,
app: &App,
biz_search_panel: Option<Widget>,
) {
let top_panel = Panel::new(Widget::col(vec![
@ -75,7 +77,7 @@ impl Viewer {
self.top_panel = top_panel;
}
fn calculate_tags(&self, ctx: &EventCtx, app: &SimpleApp) -> Widget {
fn calculate_tags(&self, ctx: &EventCtx, app: &App) -> Widget {
let mut col = Vec::new();
if self.fixed_object_outline.is_some() {
col.push("Click something else to examine it".draw_text(ctx));
@ -217,8 +219,8 @@ impl Viewer {
}
}
impl State<SimpleApp> for Viewer {
fn event(&mut self, ctx: &mut EventCtx, app: &mut SimpleApp) -> Transition<SimpleApp> {
impl State<App> for Viewer {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition<App> {
ctx.canvas_movement();
if ctx.redo_mouseover() {
let old_id = app.current_selection.clone();
@ -347,7 +349,7 @@ impl State<SimpleApp> for Viewer {
DrawBaselayer::Custom
}
fn draw(&self, g: &mut GfxCtx, app: &SimpleApp) {
fn draw(&self, g: &mut GfxCtx, app: &App) {
if g.canvas.cam_zoom < app.opts.min_zoom_for_detail {
app.draw_unzoomed(g);
} else {
@ -378,7 +380,7 @@ struct BusinessSearch {
}
impl BusinessSearch {
fn new(ctx: &mut EventCtx, app: &SimpleApp) -> BusinessSearch {
fn new(ctx: &mut EventCtx, app: &App) -> BusinessSearch {
let mut counts = Counter::new();
for b in app.map.all_buildings() {
for a in &b.amenities {
@ -400,7 +402,7 @@ impl BusinessSearch {
}
// Updates the highlighted buildings
fn update(&mut self, ctx: &mut EventCtx, app: &SimpleApp) {
fn update(&mut self, ctx: &mut EventCtx, app: &App) {
let mut batch = GeomBatch::new();
for b in app.map.all_buildings() {
if b.amenities
@ -413,12 +415,7 @@ impl BusinessSearch {
self.highlight = ctx.upload(batch);
}
fn hovering_on_amenity(
&mut self,
ctx: &mut EventCtx,
app: &SimpleApp,
amenity: Option<String>,
) {
fn hovering_on_amenity(&mut self, ctx: &mut EventCtx, app: &App, amenity: Option<String>) {
if amenity.is_none() {
self.hovering_on_amenity = None;
return;

View File

@ -2,7 +2,7 @@ mod mapper;
fn main() {
widgetry::run(widgetry::Settings::new("OSM parking mapper"), |ctx| {
let mut app = map_gui::SimpleApp::new(ctx, abstutil::CmdArgs::new());
let mut app = map_gui::SimpleApp::new(ctx, abstutil::CmdArgs::new(), ());
app.opts.min_zoom_for_detail = 2.0;
let states = vec![mapper::ParkingMapper::new(ctx, &app)];
(app, states)

View File

@ -13,6 +13,8 @@ use widgetry::{
Line, Menu, Outcome, Panel, State, Text, TextExt, Transition, VerticalAlignment, Widget,
};
type App = SimpleApp<()>;
pub struct ParkingMapper {
panel: Panel,
draw_layer: Drawable,
@ -41,16 +43,16 @@ pub enum Value {
}
impl ParkingMapper {
pub fn new(ctx: &mut EventCtx, app: &SimpleApp) -> Box<dyn State<SimpleApp>> {
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
ParkingMapper::make(ctx, app, Show::TODO, BTreeMap::new())
}
fn make(
ctx: &mut EventCtx,
app: &SimpleApp,
app: &App,
show: Show,
data: BTreeMap<WayID, Value>,
) -> Box<dyn State<SimpleApp>> {
) -> Box<dyn State<App>> {
let map = &app.map;
let color = match show {
@ -189,8 +191,8 @@ impl ParkingMapper {
}
}
impl State<SimpleApp> for ParkingMapper {
fn event(&mut self, ctx: &mut EventCtx, app: &mut SimpleApp) -> Transition<SimpleApp> {
impl State<App> for ParkingMapper {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition<App> {
let map = &app.map;
ctx.canvas_movement();
@ -371,7 +373,7 @@ impl State<SimpleApp> for ParkingMapper {
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, _: &SimpleApp) {
fn draw(&self, g: &mut GfxCtx, _: &App) {
g.redraw(&self.draw_layer);
if let Some((_, ref roads)) = self.selected {
g.redraw(roads);
@ -391,11 +393,11 @@ struct ChangeWay {
impl ChangeWay {
fn new(
ctx: &mut EventCtx,
app: &SimpleApp,
app: &App,
selected: &HashSet<RoadID>,
show: Show,
data: BTreeMap<WayID, Value>,
) -> Box<dyn State<SimpleApp>> {
) -> Box<dyn State<App>> {
let map = &app.map;
let osm_way_id = map
.get_r(*selected.iter().next().unwrap())
@ -453,8 +455,8 @@ impl ChangeWay {
}
}
impl State<SimpleApp> for ChangeWay {
fn event(&mut self, ctx: &mut EventCtx, app: &mut SimpleApp) -> Transition<SimpleApp> {
impl State<App> for ChangeWay {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition<App> {
ctx.canvas_movement();
match self.panel.event(ctx) {
Outcome::Clicked(x) => match x.as_ref() {
@ -493,7 +495,7 @@ impl State<SimpleApp> for ChangeWay {
}
}
fn draw(&self, g: &mut GfxCtx, _: &SimpleApp) {
fn draw(&self, g: &mut GfxCtx, _: &App) {
g.redraw(&self.draw);
self.panel.draw(g);
}
@ -597,7 +599,7 @@ fn generate_osmc(
Ok(())
}
fn find_divided_highways(app: &SimpleApp) -> HashSet<RoadID> {
fn find_divided_highways(app: &App) -> HashSet<RoadID> {
let map = &app.map;
let mut closest: FindClosest<RoadID> = FindClosest::new(map.get_bounds());
// TODO Consider not even filtering by oneway. I keep finding mistakes where people split a
@ -640,7 +642,7 @@ fn find_divided_highways(app: &SimpleApp) -> HashSet<RoadID> {
}
// TODO Lots of false positives here... why?
fn find_overlapping_stuff(app: &SimpleApp, timer: &mut Timer) -> Vec<Polygon> {
fn find_overlapping_stuff(app: &App, timer: &mut Timer) -> Vec<Polygon> {
let map = &app.map;
let mut closest: FindClosest<RoadID> = FindClosest::new(map.get_bounds());
for r in map.all_roads() {