mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-24 23:15:24 +03:00
Give SimpleApp a way to stash session-wide state. First use case is high scores for the experiment.
This commit is contained in:
parent
c9809c805b
commit
df04fd7e18
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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>,
|
||||
|
@ -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)
|
||||
});
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
});
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user