mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 03:35:51 +03:00
Generalize the State/Transition GUI structure, moving it into widgetry.
Nothing about it is specific to A/B Street, and other apps built with widgetry could organize themselves as a stack of states. This is also a first step towards sharing more common code between A/B Street and a future OSM viewer. Mostly mechanical change. Some more cleanup / documentation coming up next.
This commit is contained in:
parent
89977b3b31
commit
c2b6c917ae
@ -6,9 +6,9 @@ use rand::seq::SliceRandom;
|
||||
|
||||
use abstutil::Timer;
|
||||
use geom::{Bounds, Circle, Distance, Duration, Pt2D, Time};
|
||||
use map_model::{IntersectionID, Map, Traversable};
|
||||
use map_model::{IntersectionID, Map, PermanentMapEdits, Traversable};
|
||||
use sim::{Analytics, Sim, SimCallback, SimFlags};
|
||||
use widgetry::{EventCtx, GfxCtx, Prerender};
|
||||
use widgetry::{Canvas, EventCtx, GfxCtx, Prerender, SharedAppState};
|
||||
|
||||
use crate::challenges::HighScore;
|
||||
use crate::colors::ColorScheme;
|
||||
@ -671,3 +671,60 @@ impl SimCallback for FindDelayedIntersections {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SharedAppState for App {
|
||||
fn before_event(&mut self) {
|
||||
self.per_obj.reset();
|
||||
}
|
||||
|
||||
fn draw_default(&self, g: &mut GfxCtx) {
|
||||
self.draw(g, DrawOptions::new(), &ShowEverything::new());
|
||||
}
|
||||
|
||||
fn dump_before_abort(&self, canvas: &Canvas) {
|
||||
println!();
|
||||
println!(
|
||||
"********************************************************************************"
|
||||
);
|
||||
canvas.save_camera_state(self.primary.map.get_name());
|
||||
println!(
|
||||
"Crash! Please report to https://github.com/dabreegster/abstreet/issues/ and include \
|
||||
all output.txt; at least everything starting from the stack trace above!"
|
||||
);
|
||||
|
||||
println!();
|
||||
self.primary.sim.dump_before_abort();
|
||||
|
||||
println!();
|
||||
println!("Camera:");
|
||||
println!(
|
||||
r#"{{ "cam_x": {}, "cam_y": {}, "cam_zoom": {} }}"#,
|
||||
canvas.cam_x, canvas.cam_y, canvas.cam_zoom
|
||||
);
|
||||
|
||||
println!();
|
||||
if self.primary.map.get_edits().commands.is_empty() {
|
||||
println!("No edits");
|
||||
} else {
|
||||
abstutil::write_json(
|
||||
"edits_during_crash.json".to_string(),
|
||||
&PermanentMapEdits::to_permanent(self.primary.map.get_edits(), &self.primary.map),
|
||||
);
|
||||
println!("Please include edits_during_crash.json in your bug report.");
|
||||
}
|
||||
|
||||
// Repeat, because it can be hard to see the top of the report if it's long
|
||||
println!();
|
||||
println!(
|
||||
"Crash! Please report to https://github.com/dabreegster/abstreet/issues/ and include \
|
||||
all output.txt; at least everything above here until the start of the report!"
|
||||
);
|
||||
println!(
|
||||
"********************************************************************************"
|
||||
);
|
||||
}
|
||||
|
||||
fn before_quit(&self, canvas: &Canvas) {
|
||||
canvas.save_camera_state(self.primary.map.get_name());
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,13 @@ use abstutil::{prettyprint_usize, Timer};
|
||||
use geom::{Duration, Percent, Time};
|
||||
use map_model::Map;
|
||||
use sim::{AlertHandler, OrigPersonID, Scenario, Sim, SimFlags, SimOptions};
|
||||
use widgetry::{Btn, Color, EventCtx, GfxCtx, Key, Line, Outcome, Panel, Text, TextExt, Widget};
|
||||
use widgetry::{
|
||||
Btn, Color, DrawBaselayer, EventCtx, GfxCtx, Key, Line, Outcome, Panel, State, Text, TextExt,
|
||||
Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::game::{DrawBaselayer, State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::sandbox::gameplay::Tutorial;
|
||||
use crate::sandbox::{GameplayMode, SandboxMode, TutorialState};
|
||||
|
||||
@ -17,7 +20,7 @@ pub struct Challenge {
|
||||
pub description: Vec<String>,
|
||||
pub alias: String,
|
||||
pub gameplay: GameplayMode,
|
||||
pub cutscene: Option<fn(&mut EventCtx, &App, &GameplayMode) -> Box<dyn State>>,
|
||||
pub cutscene: Option<fn(&mut EventCtx, &App, &GameplayMode) -> Box<dyn State<App>>>,
|
||||
}
|
||||
|
||||
pub struct HighScore {
|
||||
@ -117,7 +120,7 @@ pub struct ChallengesPicker {
|
||||
}
|
||||
|
||||
impl ChallengesPicker {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
ChallengesPicker::make(ctx, app, None)
|
||||
}
|
||||
|
||||
@ -125,7 +128,7 @@ impl ChallengesPicker {
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
challenge_and_stage: Option<(String, usize)>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let mut links = BTreeMap::new();
|
||||
let mut master_col = vec![
|
||||
Btn::svg_def("system/assets/pregame/back.svg")
|
||||
@ -244,7 +247,7 @@ impl ChallengesPicker {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ChallengesPicker {
|
||||
impl State<App> for ChallengesPicker {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -1,11 +1,12 @@
|
||||
use geom::{Distance, Polygon, Pt2D};
|
||||
use map_model::City;
|
||||
use widgetry::{
|
||||
Btn, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, Panel, ScreenPt, Text, Widget,
|
||||
Btn, Color, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, Panel, ScreenPt,
|
||||
State, Text, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::game::{DrawBaselayer, State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::{grey_out_map, nice_map_name};
|
||||
use crate::load::MapLoader;
|
||||
use crate::render::DrawArea;
|
||||
@ -24,7 +25,7 @@ impl CityPicker {
|
||||
ctx: &mut EventCtx,
|
||||
app: &mut App,
|
||||
on_load: Box<dyn FnOnce(&mut EventCtx, &mut App) -> Transition>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
app.primary.current_selection = None;
|
||||
|
||||
let mut batch = GeomBatch::new();
|
||||
@ -104,7 +105,7 @@ impl CityPicker {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for CityPicker {
|
||||
impl State<App> for CityPicker {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -2,12 +2,12 @@ use geom::{Distance, Polygon};
|
||||
use map_model::{connectivity, BuildingID};
|
||||
use widgetry::{
|
||||
Btn, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome,
|
||||
Panel, VerticalAlignment, Widget,
|
||||
Panel, State, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::heatmap::Grid;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
|
||||
// TODO Move cursor live
|
||||
pub struct IsochroneViewer {
|
||||
@ -16,7 +16,7 @@ pub struct IsochroneViewer {
|
||||
}
|
||||
|
||||
impl IsochroneViewer {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, start: BuildingID) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, start: BuildingID) -> Box<dyn State<App>> {
|
||||
let draw = make_isochrone(ctx, app, start);
|
||||
Box::new(IsochroneViewer {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
@ -35,7 +35,7 @@ impl IsochroneViewer {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for IsochroneViewer {
|
||||
impl State<App> for IsochroneViewer {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
|
||||
|
@ -3,12 +3,12 @@ use std::collections::HashSet;
|
||||
use map_model::RoadID;
|
||||
use widgetry::{
|
||||
Autocomplete, Btn, Color, Drawable, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, Panel,
|
||||
Text, Widget,
|
||||
State, Text, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::Warping;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::{grey_out_map, ID};
|
||||
|
||||
// TODO Canonicalize names, handling abbreviations like east/e and street/st
|
||||
@ -17,7 +17,7 @@ pub struct Navigator {
|
||||
}
|
||||
|
||||
impl Navigator {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
Box::new(Navigator {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
@ -43,7 +43,7 @@ impl Navigator {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for Navigator {
|
||||
impl State<App> for Navigator {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -84,7 +84,7 @@ struct CrossStreet {
|
||||
}
|
||||
|
||||
impl CrossStreet {
|
||||
fn new(ctx: &mut EventCtx, app: &App, first: Vec<RoadID>) -> Box<dyn State> {
|
||||
fn new(ctx: &mut EventCtx, app: &App, first: Vec<RoadID>) -> Box<dyn State<App>> {
|
||||
let map = &app.primary.map;
|
||||
let mut cross_streets = HashSet::new();
|
||||
let mut batch = GeomBatch::new();
|
||||
@ -134,7 +134,7 @@ impl CrossStreet {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for CrossStreet {
|
||||
impl State<App> for CrossStreet {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
let map = &app.primary.map;
|
||||
|
||||
@ -199,7 +199,7 @@ struct SearchBuildings {
|
||||
}
|
||||
|
||||
impl SearchBuildings {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
Box::new(SearchBuildings {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
@ -249,7 +249,7 @@ impl SearchBuildings {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for SearchBuildings {
|
||||
impl State<App> for SearchBuildings {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -3,11 +3,13 @@ use std::collections::BTreeMap;
|
||||
use geom::Pt2D;
|
||||
use map_model::{AreaID, BuildingID, BusRouteID, IntersectionID, LaneID, RoadID};
|
||||
use sim::{PedestrianID, PersonID, TripID};
|
||||
use widgetry::{Btn, EventCtx, GfxCtx, Key, Line, Outcome, Panel, Text, TextExt, Warper, Widget};
|
||||
use widgetry::{
|
||||
Btn, EventCtx, GfxCtx, Key, Line, Outcome, Panel, State, Text, TextExt, Warper, Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, PerMap};
|
||||
use crate::common::Tab;
|
||||
use crate::game::{PopupMsg, State, Transition};
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
use crate::helpers::{grey_out_map, ID};
|
||||
use crate::info::OpenTrip;
|
||||
use crate::sandbox::SandboxMode;
|
||||
@ -26,7 +28,7 @@ impl Warping {
|
||||
target_cam_zoom: Option<f64>,
|
||||
id: Option<ID>,
|
||||
primary: &mut PerMap,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
primary.last_warped_from = Some((ctx.canvas.center_to_map_pt(), ctx.canvas.cam_zoom));
|
||||
Box::new(Warping {
|
||||
warper: Warper::new(ctx, pt, target_cam_zoom),
|
||||
@ -35,7 +37,7 @@ impl Warping {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for Warping {
|
||||
impl State<App> for Warping {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||
if self.warper.event(ctx) {
|
||||
Transition::Keep
|
||||
@ -70,7 +72,7 @@ pub struct DebugWarp {
|
||||
}
|
||||
|
||||
impl DebugWarp {
|
||||
pub fn new(ctx: &mut EventCtx) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx) -> Box<dyn State<App>> {
|
||||
let c = ctx.style().hotkey_color;
|
||||
Box::new(DebugWarp {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
@ -124,7 +126,7 @@ impl DebugWarp {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for DebugWarp {
|
||||
impl State<App> for DebugWarp {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -1,10 +1,10 @@
|
||||
use widgetry::{
|
||||
hotkeys, Btn, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, Panel, RewriteColor,
|
||||
Text, Widget,
|
||||
hotkeys, Btn, Color, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, Panel,
|
||||
RewriteColor, State, Text, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::game::{DrawBaselayer, State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::grey_out_map;
|
||||
|
||||
pub struct CutsceneBuilder {
|
||||
@ -65,7 +65,7 @@ impl CutsceneBuilder {
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
make_task: Box<dyn Fn(&mut EventCtx) -> Widget>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
Box::new(CutscenePlayer {
|
||||
panel: make_panel(ctx, app, &self.name, &self.scenes, &make_task, 0),
|
||||
name: self.name,
|
||||
@ -84,7 +84,7 @@ struct CutscenePlayer {
|
||||
make_task: Box<dyn Fn(&mut EventCtx) -> Widget>,
|
||||
}
|
||||
|
||||
impl State for CutscenePlayer {
|
||||
impl State<App> for CutscenePlayer {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -288,7 +288,7 @@ pub struct FYI {
|
||||
}
|
||||
|
||||
impl FYI {
|
||||
pub fn new(ctx: &mut EventCtx, contents: Widget, bg: Color) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, contents: Widget, bg: Color) -> Box<dyn State<App>> {
|
||||
Box::new(FYI {
|
||||
panel: Panel::new(
|
||||
Widget::custom_col(vec![
|
||||
@ -307,7 +307,7 @@ impl FYI {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for FYI {
|
||||
impl State<App> for FYI {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -3,12 +3,12 @@ use std::collections::HashSet;
|
||||
use map_model::{connectivity, LaneID, Map, PathConstraints};
|
||||
use widgetry::{
|
||||
Btn, Choice, Color, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel,
|
||||
TextExt, VerticalAlignment, Widget,
|
||||
State, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::ColorDiscrete;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
|
||||
pub struct Floodfiller {
|
||||
panel: Panel,
|
||||
@ -18,11 +18,11 @@ pub struct Floodfiller {
|
||||
}
|
||||
|
||||
impl Floodfiller {
|
||||
pub fn floodfill(ctx: &mut EventCtx, app: &App, l: LaneID) -> Box<dyn State> {
|
||||
pub fn floodfill(ctx: &mut EventCtx, app: &App, l: LaneID) -> Box<dyn State<App>> {
|
||||
let constraints = PathConstraints::from_lt(app.primary.map.get_l(l).lane_type);
|
||||
Floodfiller::new(ctx, app, Source::Floodfill(l), constraints)
|
||||
}
|
||||
pub fn scc(ctx: &mut EventCtx, app: &App, l: LaneID) -> Box<dyn State> {
|
||||
pub fn scc(ctx: &mut EventCtx, app: &App, l: LaneID) -> Box<dyn State<App>> {
|
||||
let constraints = PathConstraints::from_lt(app.primary.map.get_l(l).lane_type);
|
||||
Floodfiller::new(ctx, app, Source::SCC, constraints)
|
||||
}
|
||||
@ -32,7 +32,7 @@ impl Floodfiller {
|
||||
app: &App,
|
||||
source: Source,
|
||||
constraints: PathConstraints,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let (reachable_lanes, unreachable_lanes, title) =
|
||||
source.calculate(&app.primary.map, constraints);
|
||||
let mut colorer = ColorDiscrete::new(
|
||||
@ -80,7 +80,7 @@ impl Floodfiller {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for Floodfiller {
|
||||
impl State<App> for Floodfiller {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
if ctx.redo_mouseover() {
|
||||
app.recalculate_current_selection(ctx);
|
||||
|
@ -5,13 +5,14 @@ use geom::{Distance, Pt2D};
|
||||
use map_model::{osm, ControlTrafficSignal, NORMAL_LANE_THICKNESS};
|
||||
use sim::{AgentID, Sim};
|
||||
use widgetry::{
|
||||
lctrl, Btn, Checkbox, Choice, Color, Drawable, EventCtx, GeomBatch, GfxCtx,
|
||||
HorizontalAlignment, Key, Line, Outcome, Panel, Text, UpdateType, VerticalAlignment, Widget,
|
||||
lctrl, Btn, Checkbox, Choice, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx,
|
||||
HorizontalAlignment, Key, Line, Outcome, Panel, State, Text, UpdateType, VerticalAlignment,
|
||||
Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, ShowLayers, ShowObject};
|
||||
use crate::common::{tool_panel, CommonState, ContextualActions};
|
||||
use crate::game::{ChooseSomething, DrawBaselayer, PopupMsg, PromptInput, State, Transition};
|
||||
use crate::game::{ChooseSomething, PopupMsg, PromptInput, Transition};
|
||||
use crate::helpers::ID;
|
||||
use crate::load::MapLoader;
|
||||
use crate::options::OptionsPanel;
|
||||
@ -39,7 +40,7 @@ pub struct DebugMode {
|
||||
}
|
||||
|
||||
impl DebugMode {
|
||||
pub fn new(ctx: &mut EventCtx) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx) -> Box<dyn State<App>> {
|
||||
Box::new(DebugMode {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
@ -111,7 +112,7 @@ impl DebugMode {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for DebugMode {
|
||||
impl State<App> for DebugMode {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
|
||||
@ -737,7 +738,7 @@ struct ScreenshotTest {
|
||||
}
|
||||
|
||||
impl ScreenshotTest {
|
||||
fn new(ctx: &mut EventCtx, app: &App, mut todo_maps: Vec<&'static str>) -> Box<dyn State> {
|
||||
fn new(ctx: &mut EventCtx, app: &App, mut todo_maps: Vec<&'static str>) -> Box<dyn State<App>> {
|
||||
MapLoader::new(
|
||||
ctx,
|
||||
app,
|
||||
@ -752,7 +753,7 @@ impl ScreenshotTest {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ScreenshotTest {
|
||||
impl State<App> for ScreenshotTest {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
if self.screenshot_done {
|
||||
if self.todo_maps.is_empty() {
|
||||
|
@ -3,13 +3,13 @@
|
||||
use abstutil::Counter;
|
||||
use map_model::{IntersectionID, PathStep, RoadID, Traversable};
|
||||
use widgetry::{
|
||||
Btn, Color, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, Text,
|
||||
VerticalAlignment, Widget,
|
||||
Btn, Color, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State,
|
||||
Text, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::{ColorLegend, ColorNetwork, CommonState};
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::ID;
|
||||
|
||||
pub struct PathCounter {
|
||||
@ -25,7 +25,7 @@ impl PathCounter {
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
i: IntersectionID,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let map = &app.primary.map;
|
||||
let sim = &app.primary.sim;
|
||||
let mut cnt = Counter::new();
|
||||
@ -84,7 +84,7 @@ impl PathCounter {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for PathCounter {
|
||||
impl State<App> for PathCounter {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
if ctx.redo_mouseover() {
|
||||
|
@ -1,11 +1,11 @@
|
||||
use geom::{Polygon, Pt2D, Triangle};
|
||||
use widgetry::{
|
||||
Btn, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, Slider, Text,
|
||||
TextExt, VerticalAlignment, Widget,
|
||||
Btn, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, Slider,
|
||||
State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
|
||||
pub struct PolygonDebugger {
|
||||
panel: Panel,
|
||||
@ -26,7 +26,7 @@ impl PolygonDebugger {
|
||||
noun: &str,
|
||||
items: Vec<Item>,
|
||||
center: Option<Pt2D>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
Box::new(PolygonDebugger {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
@ -55,7 +55,7 @@ impl PolygonDebugger {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for PolygonDebugger {
|
||||
impl State<App> for PolygonDebugger {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
|
||||
|
@ -3,12 +3,12 @@ use map_model::BuildingID;
|
||||
use sim::{Scenario, TripEndpoint};
|
||||
use widgetry::{
|
||||
Btn, Checkbox, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line,
|
||||
Outcome, Panel, Text, VerticalAlignment, Widget,
|
||||
Outcome, Panel, State, Text, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::{make_heatmap, HeatmapOptions};
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::{amenity_type, ID};
|
||||
|
||||
pub struct PopularDestinations {
|
||||
@ -18,7 +18,7 @@ pub struct PopularDestinations {
|
||||
}
|
||||
|
||||
impl PopularDestinations {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, scenario: &Scenario) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, scenario: &Scenario) -> Box<dyn State<App>> {
|
||||
let mut per_bldg = Counter::new();
|
||||
for p in &scenario.people {
|
||||
for trip in &p.trips {
|
||||
@ -35,7 +35,7 @@ impl PopularDestinations {
|
||||
app: &App,
|
||||
per_bldg: Counter<BuildingID>,
|
||||
opts: Option<HeatmapOptions>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let map = &app.primary.map;
|
||||
let mut batch = GeomBatch::new();
|
||||
let controls = if let Some(ref o) = opts {
|
||||
@ -108,7 +108,7 @@ impl PopularDestinations {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for PopularDestinations {
|
||||
impl State<App> for PopularDestinations {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
if ctx.redo_mouseover() {
|
||||
|
@ -8,12 +8,12 @@ use kml::ExtraShapes;
|
||||
use map_model::BuildingID;
|
||||
use widgetry::{
|
||||
lctrl, Btn, Choice, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
||||
Line, Outcome, Panel, Text, TextExt, VerticalAlignment, Widget,
|
||||
Line, Outcome, Panel, State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::colors::ColorScheme;
|
||||
use crate::game::{ChooseSomething, State, Transition};
|
||||
use crate::game::{ChooseSomething, Transition};
|
||||
|
||||
pub struct ViewKML {
|
||||
panel: Panel,
|
||||
@ -37,7 +37,7 @@ const RADIUS: Distance = Distance::const_meters(5.0);
|
||||
const THICKNESS: Distance = Distance::const_meters(2.0);
|
||||
|
||||
impl ViewKML {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, path: Option<String>) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, path: Option<String>) -> Box<dyn State<App>> {
|
||||
ctx.loading_screen("load kml", |ctx, mut timer| {
|
||||
let raw_shapes = if let Some(ref path) = path {
|
||||
if path.ends_with(".kml") {
|
||||
@ -151,7 +151,7 @@ impl ViewKML {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ViewKML {
|
||||
impl State<App> for ViewKML {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
if ctx.redo_mouseover() {
|
||||
|
@ -8,12 +8,12 @@ use geom::{Distance, FindClosest, PolyLine, Polygon};
|
||||
use map_model::{osm, RoadID};
|
||||
use widgetry::{
|
||||
Btn, Checkbox, Choice, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
||||
Line, Menu, Outcome, Panel, Text, TextExt, VerticalAlignment, Widget,
|
||||
Line, Menu, Outcome, Panel, State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::{CityPicker, ColorLegend};
|
||||
use crate::game::{PopupMsg, State, Transition};
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
use crate::helpers::{nice_map_name, open_browser, ID};
|
||||
|
||||
pub struct ParkingMapper {
|
||||
@ -43,7 +43,7 @@ pub enum Value {
|
||||
}
|
||||
|
||||
impl ParkingMapper {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App) -> Box<dyn State<App>> {
|
||||
app.primary.current_selection = None;
|
||||
ParkingMapper::make(ctx, app, Show::TODO, BTreeMap::new())
|
||||
}
|
||||
@ -53,7 +53,7 @@ impl ParkingMapper {
|
||||
app: &mut App,
|
||||
show: Show,
|
||||
data: BTreeMap<WayID, Value>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
app.opts.min_zoom_for_detail = 2.0;
|
||||
|
||||
let map = &app.primary.map;
|
||||
@ -186,7 +186,7 @@ impl ParkingMapper {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ParkingMapper {
|
||||
impl State<App> for ParkingMapper {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
let map = &app.primary.map;
|
||||
|
||||
@ -397,7 +397,7 @@ impl ChangeWay {
|
||||
selected: &HashSet<RoadID>,
|
||||
show: Show,
|
||||
data: BTreeMap<WayID, Value>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let map = &app.primary.map;
|
||||
let osm_way_id = map
|
||||
.get_r(*selected.iter().next().unwrap())
|
||||
@ -457,7 +457,7 @@ impl ChangeWay {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ChangeWay {
|
||||
impl State<App> for ChangeWay {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
match self.panel.event(ctx) {
|
||||
|
@ -1,13 +1,13 @@
|
||||
use abstutil::Timer;
|
||||
use geom::{LonLat, Percent};
|
||||
use widgetry::{
|
||||
lctrl, Btn, Choice, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, TextExt,
|
||||
VerticalAlignment, Widget,
|
||||
lctrl, Btn, Choice, DrawBaselayer, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome,
|
||||
Panel, State, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::CityPicker;
|
||||
use crate::game::{ChooseSomething, DrawBaselayer, State, Transition};
|
||||
use crate::game::{ChooseSomething, Transition};
|
||||
use crate::helpers::nice_map_name;
|
||||
|
||||
mod destinations;
|
||||
@ -22,7 +22,7 @@ pub struct DevToolsMode {
|
||||
}
|
||||
|
||||
impl DevToolsMode {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
Box::new(DevToolsMode {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
@ -54,7 +54,7 @@ impl DevToolsMode {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for DevToolsMode {
|
||||
impl State<App> for DevToolsMode {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -3,13 +3,13 @@ use std::io::{Error, Write};
|
||||
|
||||
use geom::{Circle, Distance, LonLat, Pt2D, Ring};
|
||||
use widgetry::{
|
||||
Btn, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, Text,
|
||||
Btn, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, Text,
|
||||
VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::CommonState;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
|
||||
const POINT_RADIUS: Distance = Distance::const_meters(10.0);
|
||||
// Localized and internal, so don't put in ColorScheme.
|
||||
@ -27,7 +27,7 @@ pub struct PolygonEditor {
|
||||
}
|
||||
|
||||
impl PolygonEditor {
|
||||
pub fn new(ctx: &mut EventCtx, name: String, mut points: Vec<LonLat>) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, name: String, mut points: Vec<LonLat>) -> Box<dyn State<App>> {
|
||||
points.pop();
|
||||
Box::new(PolygonEditor {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
@ -49,7 +49,7 @@ impl PolygonEditor {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for PolygonEditor {
|
||||
impl State<App> for PolygonEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
let gps_bounds = app.primary.map.get_gps_bounds();
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
use abstutil::prettyprint_usize;
|
||||
use sim::Scenario;
|
||||
use widgetry::{
|
||||
Btn, Color, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, Text,
|
||||
VerticalAlignment, Widget,
|
||||
Btn, Color, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State,
|
||||
Text, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::{ColorDiscrete, CommonState};
|
||||
use crate::devtools::destinations::PopularDestinations;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
|
||||
pub struct ScenarioManager {
|
||||
panel: Panel,
|
||||
@ -18,7 +18,7 @@ pub struct ScenarioManager {
|
||||
}
|
||||
|
||||
impl ScenarioManager {
|
||||
pub fn new(scenario: Scenario, ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(scenario: Scenario, ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
let mut colorer = ColorDiscrete::new(
|
||||
app,
|
||||
vec![
|
||||
@ -85,7 +85,7 @@ impl ScenarioManager {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ScenarioManager {
|
||||
impl State<App> for ScenarioManager {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -2,13 +2,14 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use geom::{Distance, LonLat, PolyLine, Polygon, Pt2D, Ring};
|
||||
use widgetry::{
|
||||
lctrl, Btn, Choice, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
||||
Line, Outcome, Panel, RewriteColor, Text, VerticalAlignment, Widget,
|
||||
lctrl, Btn, Choice, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx,
|
||||
HorizontalAlignment, Key, Line, Outcome, Panel, RewriteColor, State, Text, VerticalAlignment,
|
||||
Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, ShowEverything};
|
||||
use crate::common::CommonState;
|
||||
use crate::game::{ChooseSomething, DrawBaselayer, PromptInput, State, Transition};
|
||||
use crate::game::{ChooseSomething, PromptInput, Transition};
|
||||
use crate::render::DrawOptions;
|
||||
|
||||
// TODO This is a really great example of things that widgetry ought to make easier. Maybe a radio
|
||||
@ -37,7 +38,7 @@ enum Mode {
|
||||
}
|
||||
|
||||
impl StoryMapEditor {
|
||||
pub fn new(ctx: &mut EventCtx) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx) -> Box<dyn State<App>> {
|
||||
let story = StoryMap::new();
|
||||
let mode = Mode::View;
|
||||
let dirty = false;
|
||||
@ -55,7 +56,7 @@ impl StoryMapEditor {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for StoryMapEditor {
|
||||
impl State<App> for StoryMapEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.mode {
|
||||
Mode::View => {
|
||||
|
@ -4,13 +4,13 @@ use geom::Speed;
|
||||
use map_model::{LaneType, RoadID};
|
||||
use widgetry::{
|
||||
hotkeys, Btn, Choice, Color, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line,
|
||||
Outcome, Panel, Text, TextExt, VerticalAlignment, Widget,
|
||||
Outcome, Panel, State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::edit::select::RoadSelector;
|
||||
use crate::edit::{apply_map_edits, speed_limit_choices, try_change_lt, ConfirmDiscard};
|
||||
use crate::game::{PopupMsg, State, Transition};
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
|
||||
pub struct BulkSelect {
|
||||
panel: Panel,
|
||||
@ -18,7 +18,7 @@ pub struct BulkSelect {
|
||||
}
|
||||
|
||||
impl BulkSelect {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App, start: RoadID) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App, start: RoadID) -> Box<dyn State<App>> {
|
||||
let selector = RoadSelector::new(ctx, app, btreeset! {start});
|
||||
let panel = make_select_panel(ctx, &selector);
|
||||
Box::new(BulkSelect { panel, selector })
|
||||
@ -53,7 +53,7 @@ fn make_select_panel(ctx: &mut EventCtx, selector: &RoadSelector) -> Panel {
|
||||
.build(ctx)
|
||||
}
|
||||
|
||||
impl State for BulkSelect {
|
||||
impl State<App> for BulkSelect {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -122,7 +122,7 @@ struct BulkEdit {
|
||||
}
|
||||
|
||||
impl BulkEdit {
|
||||
fn new(ctx: &mut EventCtx, roads: Vec<RoadID>, preview: Drawable) -> Box<dyn State> {
|
||||
fn new(ctx: &mut EventCtx, roads: Vec<RoadID>, preview: Drawable) -> Box<dyn State<App>> {
|
||||
Box::new(BulkEdit {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Line(format!("Editing {} roads", roads.len()))
|
||||
@ -158,7 +158,7 @@ impl BulkEdit {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for BulkEdit {
|
||||
impl State<App> for BulkEdit {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
|
||||
@ -272,7 +272,7 @@ fn make_bulk_edits(
|
||||
roads: &Vec<RoadID>,
|
||||
speed_limit: Option<Speed>,
|
||||
lt_transformations: Vec<(Option<LaneType>, Option<LaneType>)>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let mut speed_changes = 0;
|
||||
let mut lt_changes = 0;
|
||||
let mut errors = Vec::new();
|
||||
|
@ -3,12 +3,12 @@ use std::collections::BTreeSet;
|
||||
use geom::ArrowCap;
|
||||
use map_model::{IntersectionCluster, IntersectionID};
|
||||
use widgetry::{
|
||||
Btn, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Outcome, Panel, VerticalAlignment,
|
||||
Widget,
|
||||
Btn, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Outcome, Panel,
|
||||
State, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, ShowEverything};
|
||||
use crate::game::{DrawBaselayer, State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::render::{DrawOptions, DrawUberTurnGroup, BIG_ARROW_THICKNESS};
|
||||
|
||||
pub struct ClusterTrafficSignalEditor {
|
||||
@ -20,7 +20,7 @@ pub struct ClusterTrafficSignalEditor {
|
||||
}
|
||||
|
||||
impl ClusterTrafficSignalEditor {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App, ic: &IntersectionCluster) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App, ic: &IntersectionCluster) -> Box<dyn State<App>> {
|
||||
app.primary.current_selection = None;
|
||||
Box::new(ClusterTrafficSignalEditor {
|
||||
panel: Panel::new(Widget::row(vec![
|
||||
@ -35,7 +35,7 @@ impl ClusterTrafficSignalEditor {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ClusterTrafficSignalEditor {
|
||||
impl State<App> for ClusterTrafficSignalEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use map_model::{EditCmd, LaneID, LaneType, Map};
|
||||
use widgetry::{
|
||||
Btn, Choice, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, Text,
|
||||
TextExt, VerticalAlignment, Widget,
|
||||
Btn, Choice, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State,
|
||||
Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
@ -10,7 +10,7 @@ use crate::edit::zones::ZoneEditor;
|
||||
use crate::edit::{
|
||||
apply_map_edits, can_edit_lane, maybe_edit_intersection, speed_limit_choices, try_change_lt,
|
||||
};
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::ID;
|
||||
use crate::render::Renderable;
|
||||
use crate::sandbox::GameplayMode;
|
||||
@ -22,7 +22,12 @@ pub struct LaneEditor {
|
||||
}
|
||||
|
||||
impl LaneEditor {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, l: LaneID, mode: GameplayMode) -> Box<dyn State> {
|
||||
pub fn new(
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
l: LaneID,
|
||||
mode: GameplayMode,
|
||||
) -> Box<dyn State<App>> {
|
||||
let mut row = Vec::new();
|
||||
let lt = app.primary.map.get_l(l).lane_type;
|
||||
for (icon, label, key, active) in vec![
|
||||
@ -105,7 +110,7 @@ impl LaneEditor {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for LaneEditor {
|
||||
impl State<App> for LaneEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
// Restrict what can be selected.
|
||||
|
@ -7,7 +7,7 @@ use geom::Speed;
|
||||
use map_model::{EditCmd, IntersectionID, LaneID, LaneType, MapEdits};
|
||||
use widgetry::{
|
||||
lctrl, Btn, Choice, Color, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Menu,
|
||||
Outcome, Panel, Text, TextExt, VerticalAlignment, Widget,
|
||||
Outcome, Panel, State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
pub use self::cluster_traffic_signals::ClusterTrafficSignalEditor;
|
||||
@ -19,7 +19,7 @@ pub use self::validate::{check_blackholes, check_sidewalk_connectivity, try_chan
|
||||
use crate::app::App;
|
||||
use crate::common::{tool_panel, ColorLegend, CommonState, Warping};
|
||||
use crate::debug::DebugMode;
|
||||
use crate::game::{ChooseSomething, PopupMsg, State, Transition};
|
||||
use crate::game::{ChooseSomething, PopupMsg, Transition};
|
||||
use crate::helpers::{grey_out_map, ID};
|
||||
use crate::options::OptionsPanel;
|
||||
use crate::render::DrawMap;
|
||||
@ -53,7 +53,7 @@ pub struct EditMode {
|
||||
}
|
||||
|
||||
impl EditMode {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App, mode: GameplayMode) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App, mode: GameplayMode) -> Box<dyn State<App>> {
|
||||
let orig_dirty = app.primary.dirty_from_edits;
|
||||
assert!(app.primary.suspended_sim.is_none());
|
||||
app.primary.suspended_sim = Some(app.primary.clear_sim());
|
||||
@ -139,7 +139,7 @@ impl EditMode {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for EditMode {
|
||||
impl State<App> for EditMode {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
{
|
||||
let edits = app.primary.map.get_edits();
|
||||
@ -389,7 +389,7 @@ impl SaveEdits {
|
||||
discard: bool,
|
||||
cancel: Option<Transition>,
|
||||
on_success: Box<dyn Fn(&mut EventCtx, &mut App)>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let initial_name = if app.primary.map.unsaved_edits() {
|
||||
String::new()
|
||||
} else {
|
||||
@ -470,7 +470,7 @@ impl SaveEdits {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for SaveEdits {
|
||||
impl State<App> for SaveEdits {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -519,7 +519,7 @@ struct LoadEdits {
|
||||
}
|
||||
|
||||
impl LoadEdits {
|
||||
fn new(ctx: &mut EventCtx, app: &App, mode: GameplayMode) -> Box<dyn State> {
|
||||
fn new(ctx: &mut EventCtx, app: &App, mode: GameplayMode) -> Box<dyn State<App>> {
|
||||
let current_edits_name = &app.primary.map.get_edits().edits_name;
|
||||
let your_edits = vec![
|
||||
Line("Your proposals").small_heading().draw(ctx),
|
||||
@ -560,7 +560,7 @@ impl LoadEdits {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for LoadEdits {
|
||||
impl State<App> for LoadEdits {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => {
|
||||
@ -733,7 +733,7 @@ pub fn maybe_edit_intersection(
|
||||
app: &mut App,
|
||||
id: IntersectionID,
|
||||
mode: &GameplayMode,
|
||||
) -> Option<Box<dyn State>> {
|
||||
) -> Option<Box<dyn State<App>>> {
|
||||
if app.primary.map.maybe_get_stop_sign(id).is_some()
|
||||
&& mode.can_edit_stop_signs()
|
||||
&& app.per_obj.left_click(ctx, "edit stop signs")
|
||||
@ -839,7 +839,7 @@ pub struct ConfirmDiscard {
|
||||
}
|
||||
|
||||
impl ConfirmDiscard {
|
||||
pub fn new(ctx: &mut EventCtx, discard: Box<dyn Fn(&mut App)>) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, discard: Box<dyn Fn(&mut App)>) -> Box<dyn State<App>> {
|
||||
Box::new(ConfirmDiscard {
|
||||
discard,
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
@ -862,7 +862,7 @@ impl ConfirmDiscard {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ConfirmDiscard {
|
||||
impl State<App> for ConfirmDiscard {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -1,13 +1,13 @@
|
||||
use geom::{Duration, Time};
|
||||
use map_model::{BusRouteID, EditCmd};
|
||||
use widgetry::{
|
||||
Btn, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, Spinner, TextExt,
|
||||
Btn, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, Spinner, State, TextExt,
|
||||
VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::edit::apply_map_edits;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
|
||||
pub struct RouteEditor {
|
||||
panel: Panel,
|
||||
@ -15,7 +15,7 @@ pub struct RouteEditor {
|
||||
}
|
||||
|
||||
impl RouteEditor {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App, id: BusRouteID) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App, id: BusRouteID) -> Box<dyn State<App>> {
|
||||
app.primary.current_selection = None;
|
||||
|
||||
let route = app.primary.map.get_br(id);
|
||||
@ -42,7 +42,7 @@ impl RouteEditor {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for RouteEditor {
|
||||
impl State<App> for RouteEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
|
||||
|
@ -8,14 +8,14 @@ use map_model::{
|
||||
ControlStopSign, ControlTrafficSignal, EditCmd, EditIntersection, IntersectionID, RoadID,
|
||||
};
|
||||
use widgetry::{
|
||||
Btn, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, Text,
|
||||
Btn, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, Text,
|
||||
VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::CommonState;
|
||||
use crate::edit::{apply_map_edits, check_sidewalk_connectivity, TrafficSignalEditor};
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::render::DrawIntersection;
|
||||
use crate::sandbox::GameplayMode;
|
||||
|
||||
@ -36,7 +36,7 @@ impl StopSignEditor {
|
||||
app: &mut App,
|
||||
id: IntersectionID,
|
||||
mode: GameplayMode,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
app.primary.current_selection = None;
|
||||
let geom = app
|
||||
.primary
|
||||
@ -77,7 +77,7 @@ impl StopSignEditor {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for StopSignEditor {
|
||||
impl State<App> for StopSignEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
|
||||
|
@ -4,13 +4,14 @@ use map_model::{
|
||||
ControlStopSign, ControlTrafficSignal, EditCmd, EditIntersection, IntersectionID, PhaseType,
|
||||
};
|
||||
use widgetry::{
|
||||
Btn, Checkbox, Choice, EventCtx, GfxCtx, Key, Line, Outcome, Panel, Spinner, TextExt, Widget,
|
||||
Btn, Checkbox, Choice, DrawBaselayer, EventCtx, GfxCtx, Key, Line, Outcome, Panel, Spinner,
|
||||
State, TextExt, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::edit::traffic_signals::{BundleEdits, TrafficSignalEditor};
|
||||
use crate::edit::{apply_map_edits, check_sidewalk_connectivity, StopSignEditor};
|
||||
use crate::game::{ChooseSomething, DrawBaselayer, State, Transition};
|
||||
use crate::game::{ChooseSomething, Transition};
|
||||
use crate::sandbox::GameplayMode;
|
||||
|
||||
pub struct ChangeDuration {
|
||||
@ -19,7 +20,11 @@ pub struct ChangeDuration {
|
||||
}
|
||||
|
||||
impl ChangeDuration {
|
||||
pub fn new(ctx: &mut EventCtx, signal: &ControlTrafficSignal, idx: usize) -> Box<dyn State> {
|
||||
pub fn new(
|
||||
ctx: &mut EventCtx,
|
||||
signal: &ControlTrafficSignal,
|
||||
idx: usize,
|
||||
) -> Box<dyn State<App>> {
|
||||
Box::new(ChangeDuration {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
@ -70,7 +75,7 @@ impl ChangeDuration {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ChangeDuration {
|
||||
impl State<App> for ChangeDuration {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -119,7 +124,7 @@ pub fn edit_entire_signal(
|
||||
i: IntersectionID,
|
||||
mode: GameplayMode,
|
||||
original: BundleEdits,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let has_sidewalks = app
|
||||
.primary
|
||||
.map
|
||||
|
@ -7,14 +7,15 @@ use map_model::{
|
||||
TurnPriority,
|
||||
};
|
||||
use widgetry::{
|
||||
lctrl, Btn, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line,
|
||||
MultiButton, Outcome, Panel, RewriteColor, Text, TextExt, VerticalAlignment, Widget,
|
||||
lctrl, Btn, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment,
|
||||
Key, Line, MultiButton, Outcome, Panel, RewriteColor, State, Text, TextExt, VerticalAlignment,
|
||||
Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, ShowEverything};
|
||||
use crate::common::{CommonState, Warping};
|
||||
use crate::edit::{apply_map_edits, ConfirmDiscard};
|
||||
use crate::game::{DrawBaselayer, PopupMsg, State, Transition};
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
use crate::options::TrafficSignalStyle;
|
||||
use crate::render::{traffic_signal, DrawMovement, DrawOptions};
|
||||
use crate::sandbox::GameplayMode;
|
||||
@ -60,7 +61,7 @@ impl TrafficSignalEditor {
|
||||
app: &mut App,
|
||||
members: BTreeSet<IntersectionID>,
|
||||
mode: GameplayMode,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
app.primary.current_selection = None;
|
||||
|
||||
let original = BundleEdits::get_current(app, &members);
|
||||
@ -176,7 +177,7 @@ impl TrafficSignalEditor {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for TrafficSignalEditor {
|
||||
impl State<App> for TrafficSignalEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
if self.warn_changed {
|
||||
self.warn_changed = false;
|
||||
|
@ -7,13 +7,13 @@ use map_model::IntersectionID;
|
||||
use sim::Scenario;
|
||||
use widgetry::{
|
||||
Btn, Color, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel,
|
||||
RewriteColor, Spinner, Text, TextExt, VerticalAlignment, Widget,
|
||||
RewriteColor, Spinner, State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::CommonState;
|
||||
use crate::edit::traffic_signals::fade_irrelevant;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::ID;
|
||||
|
||||
pub struct ShowAbsolute {
|
||||
@ -23,7 +23,11 @@ pub struct ShowAbsolute {
|
||||
}
|
||||
|
||||
impl ShowAbsolute {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, members: BTreeSet<IntersectionID>) -> Box<dyn State> {
|
||||
pub fn new(
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
members: BTreeSet<IntersectionID>,
|
||||
) -> Box<dyn State<App>> {
|
||||
let mut batch = fade_irrelevant(app, &members);
|
||||
for i in &members {
|
||||
batch.append(
|
||||
@ -58,7 +62,7 @@ impl ShowAbsolute {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ShowAbsolute {
|
||||
impl State<App> for ShowAbsolute {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
if ctx.redo_mouseover() {
|
||||
@ -117,7 +121,7 @@ impl ShowRelative {
|
||||
app: &App,
|
||||
base: IntersectionID,
|
||||
members: BTreeSet<IntersectionID>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let base_offset = app.primary.map.get_traffic_signal(base).offset;
|
||||
let mut batch = fade_irrelevant(app, &members);
|
||||
for i in &members {
|
||||
@ -160,7 +164,7 @@ impl ShowRelative {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ShowRelative {
|
||||
impl State<App> for ShowRelative {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
if ctx.redo_mouseover() {
|
||||
@ -220,7 +224,7 @@ impl TuneRelative {
|
||||
i1: IntersectionID,
|
||||
i2: IntersectionID,
|
||||
members: BTreeSet<IntersectionID>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let mut batch = fade_irrelevant(app, &btreeset! {i1, i2});
|
||||
let map = &app.primary.map;
|
||||
// TODO Colors aren't clear. Show directionality.
|
||||
@ -281,7 +285,7 @@ impl TuneRelative {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for TuneRelative {
|
||||
impl State<App> for TuneRelative {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
match self.panel.event(ctx) {
|
||||
|
@ -3,13 +3,13 @@ use std::collections::BTreeSet;
|
||||
use map_model::IntersectionID;
|
||||
use widgetry::{
|
||||
hotkeys, Btn, Color, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome,
|
||||
Panel, VerticalAlignment, Widget,
|
||||
Panel, State, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::CommonState;
|
||||
use crate::edit::TrafficSignalEditor;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::ID;
|
||||
use crate::sandbox::gameplay::GameplayMode;
|
||||
|
||||
@ -24,7 +24,7 @@ impl SignalPicker {
|
||||
ctx: &mut EventCtx,
|
||||
members: BTreeSet<IntersectionID>,
|
||||
mode: GameplayMode,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
Box::new(SignalPicker {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
@ -45,7 +45,7 @@ impl SignalPicker {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for SignalPicker {
|
||||
impl State<App> for SignalPicker {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
if ctx.redo_mouseover() {
|
||||
|
@ -4,12 +4,12 @@ use abstutil::Timer;
|
||||
use geom::Duration;
|
||||
use map_model::IntersectionID;
|
||||
use widgetry::{
|
||||
Btn, Choice, EventCtx, GfxCtx, HorizontalAlignment, Key, Outcome, Panel, TextExt, UpdateType,
|
||||
VerticalAlignment, Widget,
|
||||
Btn, Choice, EventCtx, GfxCtx, HorizontalAlignment, Key, Outcome, Panel, State, TextExt,
|
||||
UpdateType, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::game::{ChooseSomething, State, Transition};
|
||||
use crate::game::{ChooseSomething, Transition};
|
||||
use crate::sandbox::{spawn_agents_around, SpeedControls, TimePanel};
|
||||
|
||||
// TODO Show diagram, auto-sync the stage.
|
||||
@ -21,7 +21,7 @@ struct PreviewTrafficSignal {
|
||||
}
|
||||
|
||||
impl PreviewTrafficSignal {
|
||||
fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
Box::new(PreviewTrafficSignal {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
"Previewing traffic signal".draw_text(ctx),
|
||||
@ -35,7 +35,7 @@ impl PreviewTrafficSignal {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for PreviewTrafficSignal {
|
||||
impl State<App> for PreviewTrafficSignal {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
|
||||
@ -76,7 +76,7 @@ pub fn make_previewer(
|
||||
app: &App,
|
||||
members: BTreeSet<IntersectionID>,
|
||||
stage: usize,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let random = "random agents around these intersections".to_string();
|
||||
let right_now = format!(
|
||||
"change the traffic signal live at {}",
|
||||
|
@ -2,11 +2,11 @@ use std::collections::BTreeSet;
|
||||
|
||||
use abstutil::Timer;
|
||||
use map_model::{connectivity, EditCmd, LaneID, LaneType, Map, PathConstraints};
|
||||
use widgetry::{Color, EventCtx};
|
||||
use widgetry::{Color, EventCtx, State};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::ColorDiscrete;
|
||||
use crate::game::{PopupMsg, State};
|
||||
use crate::game::PopupMsg;
|
||||
|
||||
// All of these take a candidate EditCmd to do, then see if it's valid. If they return None, it's
|
||||
// fine. They always leave the map in the original state without the new EditCmd.
|
||||
@ -16,7 +16,7 @@ pub fn check_sidewalk_connectivity(
|
||||
ctx: &mut EventCtx,
|
||||
app: &mut App,
|
||||
cmd: EditCmd,
|
||||
) -> Option<Box<dyn State>> {
|
||||
) -> Option<Box<dyn State<App>>> {
|
||||
let orig_edits = app.primary.map.get_edits().clone();
|
||||
let (_, disconnected_before) =
|
||||
connectivity::find_scc(&app.primary.map, PathConstraints::Pedestrian);
|
||||
@ -61,7 +61,11 @@ pub fn check_sidewalk_connectivity(
|
||||
|
||||
#[allow(unused)]
|
||||
// Could be caused by closing intersections, changing lane types, or reversing lanes
|
||||
pub fn check_blackholes(ctx: &mut EventCtx, app: &mut App, cmd: EditCmd) -> Option<Box<dyn State>> {
|
||||
pub fn check_blackholes(
|
||||
ctx: &mut EventCtx,
|
||||
app: &mut App,
|
||||
cmd: EditCmd,
|
||||
) -> Option<Box<dyn State<App>>> {
|
||||
let orig_edits = app.primary.map.get_edits().clone();
|
||||
let mut driving_ok_originally = BTreeSet::new();
|
||||
let mut biking_ok_originally = BTreeSet::new();
|
||||
@ -120,7 +124,7 @@ pub fn try_change_lt(
|
||||
map: &mut Map,
|
||||
l: LaneID,
|
||||
new_lt: LaneType,
|
||||
) -> Result<EditCmd, Box<dyn State>> {
|
||||
) -> Result<EditCmd, Box<dyn State<App>>> {
|
||||
let orig_edits = map.get_edits().clone();
|
||||
|
||||
let mut edits = orig_edits.clone();
|
||||
|
@ -7,7 +7,7 @@ use map_model::{AccessRestrictions, PathConstraints, RoadID};
|
||||
use sim::TripMode;
|
||||
use widgetry::{
|
||||
Btn, Color, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel,
|
||||
Spinner, Text, TextExt, VerticalAlignment, Widget,
|
||||
Spinner, State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
@ -15,7 +15,7 @@ use crate::common::ColorDiscrete;
|
||||
use crate::common::CommonState;
|
||||
use crate::edit::apply_map_edits;
|
||||
use crate::edit::select::RoadSelector;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::{checkbox_per_mode, intersections_from_roads};
|
||||
|
||||
pub struct ZoneEditor {
|
||||
@ -29,7 +29,7 @@ pub struct ZoneEditor {
|
||||
}
|
||||
|
||||
impl ZoneEditor {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App, start: RoadID) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App, start: RoadID) -> Box<dyn State<App>> {
|
||||
let start = app.primary.map.get_r(start);
|
||||
let members = if let Some(z) = start.get_zone(&app.primary.map) {
|
||||
z.members.clone()
|
||||
@ -82,7 +82,7 @@ impl ZoneEditor {
|
||||
}
|
||||
|
||||
// TODO Handle splitting/merging zones.
|
||||
impl State for ZoneEditor {
|
||||
impl State<App> for ZoneEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
223
game/src/game.rs
223
game/src/game.rs
@ -1,23 +1,18 @@
|
||||
use map_model::PermanentMapEdits;
|
||||
use widgetry::{
|
||||
hotkeys, Btn, Canvas, Choice, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
||||
Line, Menu, Outcome, Panel, ScreenRectangle, Text, VerticalAlignment, Widget, GUI,
|
||||
hotkeys, Btn, Choice, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx,
|
||||
HorizontalAlignment, Key, Line, Menu, Outcome, Panel, ScreenRectangle, State, Text,
|
||||
VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, Flags, ShowEverything};
|
||||
use crate::app::{App, Flags};
|
||||
use crate::helpers::grey_out_map;
|
||||
use crate::options::Options;
|
||||
use crate::pregame::TitleScreen;
|
||||
use crate::render::DrawOptions;
|
||||
use crate::sandbox::{GameplayMode, SandboxMode};
|
||||
|
||||
// This is the top-level of the GUI logic. This module should just manage interactions between the
|
||||
// top-level game states.
|
||||
pub struct Game {
|
||||
// A stack of states
|
||||
states: Vec<Box<dyn State>>,
|
||||
app: App,
|
||||
}
|
||||
pub struct Game;
|
||||
|
||||
pub type Transition = widgetry::Transition<App>;
|
||||
|
||||
impl Game {
|
||||
pub fn new(
|
||||
@ -26,7 +21,7 @@ impl Game {
|
||||
start_with_edits: Option<String>,
|
||||
maybe_mode: Option<GameplayMode>,
|
||||
ctx: &mut EventCtx,
|
||||
) -> Game {
|
||||
) -> (App, Vec<Box<dyn State<App>>>) {
|
||||
let title = !opts.dev
|
||||
&& !flags.sim_flags.load.contains("player/save")
|
||||
&& !flags.sim_flags.load.contains("system/scenarios")
|
||||
@ -65,7 +60,7 @@ impl Game {
|
||||
app.primary.clear_sim();
|
||||
}
|
||||
|
||||
let states: Vec<Box<dyn State>> = if title {
|
||||
let states: Vec<Box<dyn State<App>>> = if title {
|
||||
vec![Box::new(TitleScreen::new(ctx, &mut app))]
|
||||
} else {
|
||||
let mode = maybe_mode
|
||||
@ -77,185 +72,9 @@ impl Game {
|
||||
// PlayScenario without clobbering.
|
||||
app.primary.sim = ss;
|
||||
}
|
||||
Game { states, app }
|
||||
|
||||
(app, states)
|
||||
}
|
||||
|
||||
// If true, then the top-most state on the stack needs to be "woken up" with a fake mouseover
|
||||
// event.
|
||||
fn execute_transition(&mut self, ctx: &mut EventCtx, transition: Transition) -> bool {
|
||||
match transition {
|
||||
Transition::Keep => false,
|
||||
Transition::KeepWithMouseover => true,
|
||||
Transition::Pop => {
|
||||
self.states.pop().unwrap().on_destroy(ctx, &mut self.app);
|
||||
if self.states.is_empty() {
|
||||
self.before_quit(ctx.canvas);
|
||||
std::process::exit(0);
|
||||
}
|
||||
true
|
||||
}
|
||||
Transition::ModifyState(cb) => {
|
||||
cb(self.states.last_mut().unwrap(), ctx, &mut self.app);
|
||||
true
|
||||
}
|
||||
Transition::ReplaceWithData(cb) => {
|
||||
let mut last = self.states.pop().unwrap();
|
||||
last.on_destroy(ctx, &mut self.app);
|
||||
let new_states = cb(last, ctx, &mut self.app);
|
||||
self.states.extend(new_states);
|
||||
true
|
||||
}
|
||||
Transition::Push(state) => {
|
||||
self.states.push(state);
|
||||
true
|
||||
}
|
||||
Transition::Replace(state) => {
|
||||
self.states.pop().unwrap().on_destroy(ctx, &mut self.app);
|
||||
self.states.push(state);
|
||||
true
|
||||
}
|
||||
Transition::Clear(states) => {
|
||||
while !self.states.is_empty() {
|
||||
self.states.pop().unwrap().on_destroy(ctx, &mut self.app);
|
||||
}
|
||||
self.states.extend(states);
|
||||
true
|
||||
}
|
||||
Transition::Multi(list) => {
|
||||
// Always wake-up just the last state remaining after the sequence
|
||||
for t in list {
|
||||
self.execute_transition(ctx, t);
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GUI for Game {
|
||||
fn event(&mut self, ctx: &mut EventCtx) {
|
||||
self.app.per_obj.reset();
|
||||
|
||||
let transition = self.states.last_mut().unwrap().event(ctx, &mut self.app);
|
||||
if self.execute_transition(ctx, transition) {
|
||||
// Let the new state initialize with a fake event. Usually these just return
|
||||
// Transition::Keep, but nothing stops them from doing whatever. (For example, entering
|
||||
// tutorial mode immediately pushes on a Warper.) So just recurse.
|
||||
ctx.no_op_event(true, |ctx| self.event(ctx));
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
let state = self.states.last().unwrap();
|
||||
|
||||
match state.draw_baselayer() {
|
||||
DrawBaselayer::DefaultMap => {
|
||||
self.app.draw(g, DrawOptions::new(), &ShowEverything::new());
|
||||
}
|
||||
DrawBaselayer::Custom => {}
|
||||
DrawBaselayer::PreviousState => {
|
||||
match self.states[self.states.len() - 2].draw_baselayer() {
|
||||
DrawBaselayer::DefaultMap => {
|
||||
self.app.draw(g, DrawOptions::new(), &ShowEverything::new());
|
||||
}
|
||||
DrawBaselayer::Custom => {}
|
||||
// Nope, don't recurse
|
||||
DrawBaselayer::PreviousState => {}
|
||||
}
|
||||
|
||||
self.states[self.states.len() - 2].draw(g, &self.app);
|
||||
}
|
||||
}
|
||||
state.draw(g, &self.app);
|
||||
}
|
||||
|
||||
fn dump_before_abort(&self, canvas: &Canvas) {
|
||||
println!();
|
||||
println!(
|
||||
"********************************************************************************"
|
||||
);
|
||||
canvas.save_camera_state(self.app.primary.map.get_name());
|
||||
println!(
|
||||
"Crash! Please report to https://github.com/dabreegster/abstreet/issues/ and include \
|
||||
all output.txt; at least everything starting from the stack trace above!"
|
||||
);
|
||||
|
||||
println!();
|
||||
self.app.primary.sim.dump_before_abort();
|
||||
|
||||
println!();
|
||||
println!("Camera:");
|
||||
println!(
|
||||
r#"{{ "cam_x": {}, "cam_y": {}, "cam_zoom": {} }}"#,
|
||||
canvas.cam_x, canvas.cam_y, canvas.cam_zoom
|
||||
);
|
||||
|
||||
println!();
|
||||
if self.app.primary.map.get_edits().commands.is_empty() {
|
||||
println!("No edits");
|
||||
} else {
|
||||
abstutil::write_json(
|
||||
"edits_during_crash.json".to_string(),
|
||||
&PermanentMapEdits::to_permanent(
|
||||
self.app.primary.map.get_edits(),
|
||||
&self.app.primary.map,
|
||||
),
|
||||
);
|
||||
println!("Please include edits_during_crash.json in your bug report.");
|
||||
}
|
||||
|
||||
// Repeat, because it can be hard to see the top of the report if it's long
|
||||
println!();
|
||||
println!(
|
||||
"Crash! Please report to https://github.com/dabreegster/abstreet/issues/ and include \
|
||||
all output.txt; at least everything above here until the start of the report!"
|
||||
);
|
||||
println!(
|
||||
"********************************************************************************"
|
||||
);
|
||||
}
|
||||
|
||||
fn before_quit(&self, canvas: &Canvas) {
|
||||
canvas.save_camera_state(self.app.primary.map.get_name());
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DrawBaselayer {
|
||||
DefaultMap,
|
||||
Custom,
|
||||
PreviousState,
|
||||
}
|
||||
|
||||
pub trait State: downcast_rs::Downcast {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition;
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App);
|
||||
|
||||
fn draw_baselayer(&self) -> DrawBaselayer {
|
||||
DrawBaselayer::DefaultMap
|
||||
}
|
||||
|
||||
// Before this state is popped or replaced, call this.
|
||||
fn on_destroy(&mut self, _: &mut EventCtx, _: &mut App) {}
|
||||
// We don't need an on_enter -- the constructor for the state can just do it.
|
||||
}
|
||||
|
||||
downcast_rs::impl_downcast!(State);
|
||||
|
||||
pub enum Transition {
|
||||
Keep,
|
||||
KeepWithMouseover,
|
||||
Pop,
|
||||
// If a state needs to pass data back to the parent, use this. Sadly, runtime type casting.
|
||||
ModifyState(Box<dyn FnOnce(&mut Box<dyn State>, &mut EventCtx, &mut App)>),
|
||||
// TODO This is like Replace + ModifyState, then returning a few Push's from the callback. Not
|
||||
// sure how to express it in terms of the others without complicating ModifyState everywhere.
|
||||
ReplaceWithData(
|
||||
Box<dyn FnOnce(Box<dyn State>, &mut EventCtx, &mut App) -> Vec<Box<dyn State>>>,
|
||||
),
|
||||
Push(Box<dyn State>),
|
||||
Replace(Box<dyn State>),
|
||||
Clear(Vec<Box<dyn State>>),
|
||||
Multi(Vec<Transition>),
|
||||
}
|
||||
|
||||
pub struct ChooseSomething<T> {
|
||||
@ -269,7 +88,7 @@ impl<T: 'static> ChooseSomething<T> {
|
||||
query: &str,
|
||||
choices: Vec<Choice<T>>,
|
||||
cb: Box<dyn Fn(T, &mut EventCtx, &mut App) -> Transition>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
Box::new(ChooseSomething {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
@ -290,7 +109,7 @@ impl<T: 'static> ChooseSomething<T> {
|
||||
rect: &ScreenRectangle,
|
||||
choices: Vec<Choice<T>>,
|
||||
cb: Box<dyn Fn(T, &mut EventCtx, &mut App) -> Transition>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
Box::new(ChooseSomething {
|
||||
panel: Panel::new(Menu::new(ctx, choices).named("menu").container())
|
||||
.aligned(
|
||||
@ -303,7 +122,7 @@ impl<T: 'static> ChooseSomething<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> State for ChooseSomething<T> {
|
||||
impl<T: 'static> State<App> for ChooseSomething<T> {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -346,7 +165,7 @@ impl PromptInput {
|
||||
ctx: &mut EventCtx,
|
||||
query: &str,
|
||||
cb: Box<dyn Fn(String, &mut EventCtx, &mut App) -> Transition>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
Box::new(PromptInput {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
@ -364,7 +183,7 @@ impl PromptInput {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for PromptInput {
|
||||
impl State<App> for PromptInput {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -401,7 +220,11 @@ pub struct PopupMsg {
|
||||
}
|
||||
|
||||
impl PopupMsg {
|
||||
pub fn new<I: Into<String>>(ctx: &mut EventCtx, title: &str, lines: Vec<I>) -> Box<dyn State> {
|
||||
pub fn new<I: Into<String>>(
|
||||
ctx: &mut EventCtx,
|
||||
title: &str,
|
||||
lines: Vec<I>,
|
||||
) -> Box<dyn State<App>> {
|
||||
PopupMsg::also_draw(
|
||||
ctx,
|
||||
title,
|
||||
@ -417,7 +240,7 @@ impl PopupMsg {
|
||||
lines: Vec<I>,
|
||||
unzoomed: Drawable,
|
||||
zoomed: Drawable,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let mut txt = Text::new();
|
||||
txt.add(Line(title).small_heading());
|
||||
for l in lines {
|
||||
@ -435,7 +258,7 @@ impl PopupMsg {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for PopupMsg {
|
||||
impl State<App> for PopupMsg {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -1,8 +1,10 @@
|
||||
use widgetry::{Btn, EventCtx, GfxCtx, Key, Line, Outcome, Panel, TextExt, Widget};
|
||||
use widgetry::{
|
||||
Btn, DrawBaselayer, EventCtx, GfxCtx, Key, Line, Outcome, Panel, State, TextExt, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::HeatmapOptions;
|
||||
use crate::game::{DrawBaselayer, State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::{grey_out_map, hotkey_btn};
|
||||
use crate::sandbox::dashboards;
|
||||
|
||||
@ -79,7 +81,7 @@ impl PickLayer {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn pick(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn pick(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
let mut col = vec![Widget::custom_row(vec![
|
||||
Line("Layers").small_heading().draw(ctx),
|
||||
Btn::plaintext("X")
|
||||
@ -155,7 +157,7 @@ impl PickLayer {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for PickLayer {
|
||||
impl State<App> for PickLayer {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -5,10 +5,10 @@ use serde::de::DeserializeOwned;
|
||||
|
||||
use abstutil::Timer;
|
||||
use sim::Sim;
|
||||
use widgetry::{Color, EventCtx, GfxCtx};
|
||||
use widgetry::{Color, EventCtx, GfxCtx, State};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use native_loader::FileLoader;
|
||||
@ -24,7 +24,7 @@ impl MapLoader {
|
||||
app: &App,
|
||||
name: String,
|
||||
on_load: Box<dyn FnOnce(&mut EventCtx, &mut App) -> Transition>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
if app.primary.map.get_name() == &name {
|
||||
return Box::new(MapAlreadyLoaded {
|
||||
on_load: Some(on_load),
|
||||
@ -58,7 +58,7 @@ impl MapLoader {
|
||||
struct MapAlreadyLoaded {
|
||||
on_load: Option<Box<dyn FnOnce(&mut EventCtx, &mut App) -> Transition>>,
|
||||
}
|
||||
impl State for MapAlreadyLoaded {
|
||||
impl State<App> for MapAlreadyLoaded {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
(self.on_load.take().unwrap())(ctx, app)
|
||||
}
|
||||
@ -82,7 +82,7 @@ mod native_loader {
|
||||
_: &mut EventCtx,
|
||||
path: String,
|
||||
on_load: Box<dyn FnOnce(&mut EventCtx, &mut App, &mut Timer, Option<T>) -> Transition>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
Box::new(FileLoader {
|
||||
path,
|
||||
on_load: Some(on_load),
|
||||
@ -90,7 +90,7 @@ mod native_loader {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + DeserializeOwned> State for FileLoader<T> {
|
||||
impl<T: 'static + DeserializeOwned> State<App> for FileLoader<T> {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.loading_screen(format!("load {}", self.path), |ctx, timer| {
|
||||
// Assumes a binary file
|
||||
@ -114,7 +114,7 @@ mod wasm_loader {
|
||||
use web_sys::{Request, RequestInit, RequestMode, Response};
|
||||
|
||||
use geom::Duration;
|
||||
use widgetry::{Line, Panel, Text, UpdateType};
|
||||
use widgetry::{DrawBaselayer, Line, Panel, State, Text, UpdateType};
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -135,7 +135,7 @@ mod wasm_loader {
|
||||
ctx: &mut EventCtx,
|
||||
path: String,
|
||||
on_load: Box<dyn FnOnce(&mut EventCtx, &mut App, &mut Timer, Option<T>) -> Transition>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let url = if cfg!(feature = "wasm_s3") {
|
||||
format!(
|
||||
"http://abstreet.s3-website.us-east-2.amazonaws.com/{}",
|
||||
|
@ -1,12 +1,12 @@
|
||||
use geom::{Duration, UnitFmt};
|
||||
use widgetry::{
|
||||
Btn, Checkbox, Choice, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, Panel, Spinner,
|
||||
Btn, Checkbox, Choice, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, Panel, Spinner, State,
|
||||
TextExt, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::colors::{ColorScheme, ColorSchemeChoice};
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::grey_out_map;
|
||||
use crate::render::{DrawBuilding, DrawMap};
|
||||
|
||||
@ -93,7 +93,7 @@ pub struct OptionsPanel {
|
||||
}
|
||||
|
||||
impl OptionsPanel {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
Box::new(OptionsPanel {
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Widget::custom_row(vec![
|
||||
@ -239,7 +239,7 @@ impl OptionsPanel {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for OptionsPanel {
|
||||
impl State<App> for OptionsPanel {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -9,15 +9,15 @@ use geom::{Duration, Line, Percent, Pt2D, Speed};
|
||||
use map_model::PermanentMapEdits;
|
||||
use sim::ScenarioGenerator;
|
||||
use widgetry::{
|
||||
hotkeys, Btn, Color, EventCtx, GfxCtx, Key, Line, Outcome, Panel, RewriteColor, Text,
|
||||
UpdateType, Widget,
|
||||
hotkeys, Btn, Color, DrawBaselayer, EventCtx, GfxCtx, Key, Line, Outcome, Panel, RewriteColor,
|
||||
State, Text, UpdateType, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::challenges::ChallengesPicker;
|
||||
use crate::devtools::DevToolsMode;
|
||||
use crate::edit::apply_map_edits;
|
||||
use crate::game::{DrawBaselayer, PopupMsg, State, Transition};
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
use crate::helpers::open_browser;
|
||||
use crate::load::MapLoader;
|
||||
use crate::sandbox::gameplay::Tutorial;
|
||||
@ -61,7 +61,7 @@ impl TitleScreen {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for TitleScreen {
|
||||
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() {
|
||||
@ -89,7 +89,7 @@ pub struct MainMenu {
|
||||
}
|
||||
|
||||
impl MainMenu {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
let col = vec![
|
||||
Btn::svg_def("system/assets/pregame/quit.svg")
|
||||
.build(ctx, "quit", Key::Escape)
|
||||
@ -169,7 +169,7 @@ impl MainMenu {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for MainMenu {
|
||||
impl State<App> for MainMenu {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -242,7 +242,7 @@ struct About {
|
||||
}
|
||||
|
||||
impl About {
|
||||
fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
let col = vec![
|
||||
Btn::svg_def("system/assets/pregame/back.svg")
|
||||
.build(ctx, "back", Key::Escape)
|
||||
@ -289,7 +289,7 @@ impl About {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for About {
|
||||
impl State<App> for About {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -324,7 +324,7 @@ struct Proposals {
|
||||
}
|
||||
|
||||
impl Proposals {
|
||||
fn new(ctx: &mut EventCtx, app: &App, current: Option<String>) -> Box<dyn State> {
|
||||
fn new(ctx: &mut EventCtx, app: &App, current: Option<String>) -> Box<dyn State<App>> {
|
||||
let mut proposals = HashMap::new();
|
||||
let mut buttons = Vec::new();
|
||||
let mut current_tab = Vec::new();
|
||||
@ -400,7 +400,7 @@ impl Proposals {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for Proposals {
|
||||
impl State<App> for Proposals {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -531,7 +531,7 @@ impl Screensaver {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[allow(unused)]
|
||||
mod built_info {
|
||||
use widgetry::{Color, Line, Text};
|
||||
use widgetry::{Color, DrawBaselayer, Line, State, Text};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/built.rs"));
|
||||
|
||||
|
@ -7,13 +7,14 @@ use geom::{Distance, PolyLine, Polygon, Time};
|
||||
use map_model::{osm, BuildingID, BuildingType, IntersectionID, LaneID, Map, RoadID, TurnType};
|
||||
use sim::{TripEndpoint, TripInfo, TripMode};
|
||||
use widgetry::{
|
||||
AreaSlider, Btn, Checkbox, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment,
|
||||
Key, Line, Outcome, Panel, RewriteColor, Text, TextExt, VerticalAlignment, Widget,
|
||||
AreaSlider, Btn, Checkbox, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx,
|
||||
HorizontalAlignment, Key, Line, Outcome, Panel, RewriteColor, State, Text, TextExt,
|
||||
VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, ShowEverything};
|
||||
use crate::common::{ColorLegend, CommonState};
|
||||
use crate::game::{DrawBaselayer, State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::checkbox_per_mode;
|
||||
use crate::render::DrawOptions;
|
||||
|
||||
@ -71,7 +72,7 @@ struct Filter {
|
||||
type BlockID = usize;
|
||||
|
||||
impl CommuterPatterns {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App) -> Box<dyn State<App>> {
|
||||
assert!(app.primary.suspended_sim.is_none());
|
||||
app.primary.suspended_sim = Some(app.primary.clear_sim());
|
||||
|
||||
@ -341,7 +342,7 @@ impl CommuterPatterns {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for CommuterPatterns {
|
||||
impl State<App> for CommuterPatterns {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
use geom::{Distance, Pt2D};
|
||||
use sim::{TripEndpoint, TripID};
|
||||
use widgetry::{Color, EventCtx, GeomBatch, GfxCtx, Outcome, Panel, RewriteColor, ScreenPt};
|
||||
use widgetry::{
|
||||
Color, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, Outcome, Panel, RewriteColor, ScreenPt,
|
||||
State,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::game::{DrawBaselayer, State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::color_for_trip_phase;
|
||||
use crate::info::{OpenTrip, Tab};
|
||||
use crate::sandbox::dashboards::table::Table;
|
||||
@ -27,7 +30,7 @@ impl<T: 'static, F: 'static, P: 'static + Fn(&mut EventCtx, &App, &Table<T, F>)
|
||||
tab: DashTab,
|
||||
table: Table<T, F>,
|
||||
make_panel: P,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let panel = (make_panel)(ctx, app, &table);
|
||||
Box::new(GenericTripTable {
|
||||
table,
|
||||
@ -44,7 +47,7 @@ impl<T: 'static, F: 'static, P: 'static + Fn(&mut EventCtx, &App, &Table<T, F>)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, F: 'static, P: 'static + Fn(&mut EventCtx, &App, &Table<T, F>) -> Panel> State
|
||||
impl<T: 'static, F: 'static, P: 'static + Fn(&mut EventCtx, &App, &Table<T, F>) -> Panel> State<App>
|
||||
for GenericTripTable<T, F, P>
|
||||
{
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
|
@ -2,13 +2,13 @@ use abstutil::{prettyprint_usize, Counter};
|
||||
use geom::Time;
|
||||
use map_model::BusRouteID;
|
||||
use widgetry::{
|
||||
Autocomplete, Btn, EventCtx, GfxCtx, Line, LinePlot, Outcome, Panel, PlotOptions, Series,
|
||||
TextExt, Widget,
|
||||
Autocomplete, Btn, DrawBaselayer, EventCtx, GfxCtx, Line, LinePlot, Outcome, Panel,
|
||||
PlotOptions, Series, State, TextExt, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::Tab;
|
||||
use crate::game::{DrawBaselayer, State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::sandbox::dashboards::DashTab;
|
||||
use crate::sandbox::SandboxMode;
|
||||
|
||||
@ -17,7 +17,7 @@ pub struct ActiveTraffic {
|
||||
}
|
||||
|
||||
impl ActiveTraffic {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
// TODO Downsampling in the middle of the day and comparing to the downsampled entire day
|
||||
// doesn't work. For the same simulation, by end of day, the plots will be identical, but
|
||||
// until then, they'll differ. See https://github.com/dabreegster/abstreet/issues/85 for
|
||||
@ -54,7 +54,7 @@ impl ActiveTraffic {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ActiveTraffic {
|
||||
impl State<App> for ActiveTraffic {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -110,7 +110,7 @@ pub struct TransitRoutes {
|
||||
}
|
||||
|
||||
impl TransitRoutes {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
// Count totals per route
|
||||
let mut boardings = Counter::new();
|
||||
for list in app.primary.sim.get_analytics().passengers_boarding.values() {
|
||||
@ -195,7 +195,7 @@ impl TransitRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for TransitRoutes {
|
||||
impl State<App> for TransitRoutes {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
let route = match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => {
|
||||
|
@ -1,9 +1,8 @@
|
||||
use geom::Duration;
|
||||
use sim::{TripEndpoint, TripID, TripPhaseType};
|
||||
use widgetry::{Checkbox, EventCtx, Filler, Line, Panel, Text, Widget};
|
||||
use widgetry::{Checkbox, EventCtx, Filler, Line, Panel, State, Text, Widget};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::game::State;
|
||||
use crate::sandbox::dashboards::generic_trip_table::GenericTripTable;
|
||||
use crate::sandbox::dashboards::table::{Col, Filter, Table};
|
||||
use crate::sandbox::dashboards::DashTab;
|
||||
@ -13,7 +12,7 @@ use crate::sandbox::dashboards::DashTab;
|
||||
pub struct ParkingOverhead;
|
||||
|
||||
impl ParkingOverhead {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
let table = make_table(app);
|
||||
GenericTripTable::new(ctx, app, DashTab::ParkingOverhead, table, make_panel)
|
||||
}
|
||||
|
@ -6,12 +6,12 @@ use abstutil::prettyprint_usize;
|
||||
use geom::{Distance, Duration, Polygon, Pt2D};
|
||||
use sim::TripMode;
|
||||
use widgetry::{
|
||||
Btn, Checkbox, Choice, Color, CompareTimes, DrawWithTooltips, EventCtx, GeomBatch, GfxCtx,
|
||||
Line, Outcome, Panel, Text, TextExt, Widget,
|
||||
Btn, Checkbox, Choice, Color, CompareTimes, DrawBaselayer, DrawWithTooltips, EventCtx,
|
||||
GeomBatch, GfxCtx, Line, Outcome, Panel, State, Text, TextExt, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::game::{DrawBaselayer, PopupMsg, State, Transition};
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
use crate::helpers::color_for_mode;
|
||||
use crate::sandbox::dashboards::DashTab;
|
||||
|
||||
@ -20,7 +20,7 @@ pub struct TripSummaries {
|
||||
}
|
||||
|
||||
impl TripSummaries {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, filter: Filter) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, filter: Filter) -> Box<dyn State<App>> {
|
||||
let mut filters = vec!["Filters".draw_text(ctx)];
|
||||
for mode in TripMode::all() {
|
||||
filters.push(Checkbox::colored(
|
||||
@ -68,7 +68,7 @@ impl TripSummaries {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for TripSummaries {
|
||||
impl State<App> for TripSummaries {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -5,13 +5,13 @@ use geom::{ArrowCap, Distance, Duration, Polygon, Time};
|
||||
use map_model::{ControlTrafficSignal, IntersectionID, MovementID, PathStep, TurnType};
|
||||
use sim::TripEndpoint;
|
||||
use widgetry::{
|
||||
Btn, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome,
|
||||
Panel, Spinner, Text, TextExt, VerticalAlignment, Widget,
|
||||
Btn, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
||||
Line, Outcome, Panel, Spinner, State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, ShowEverything};
|
||||
use crate::common::CommonState;
|
||||
use crate::game::{DrawBaselayer, State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::ID;
|
||||
use crate::render::DrawOptions;
|
||||
|
||||
@ -24,7 +24,7 @@ pub struct TrafficSignalDemand {
|
||||
}
|
||||
|
||||
impl TrafficSignalDemand {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App) -> Box<dyn State<App>> {
|
||||
let all_demand = ctx.loading_screen("predict all demand", |_, timer| {
|
||||
Demand::all_demand(app, timer)
|
||||
});
|
||||
@ -68,7 +68,7 @@ impl TrafficSignalDemand {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for TrafficSignalDemand {
|
||||
impl State<App> for TrafficSignalDemand {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
// TODO DrawWithTooltips works great in screenspace; make a similar tool for mapspace?
|
||||
|
@ -3,10 +3,9 @@ use std::collections::{BTreeSet, HashMap};
|
||||
use abstutil::prettyprint_usize;
|
||||
use geom::{Duration, Time};
|
||||
use sim::{TripEndpoint, TripID, TripMode};
|
||||
use widgetry::{Btn, Checkbox, EventCtx, Filler, Line, Panel, Text, Widget};
|
||||
use widgetry::{Btn, Checkbox, EventCtx, Filler, Line, Panel, State, Text, Widget};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::game::State;
|
||||
use crate::helpers::{checkbox_per_mode, cmp_duration_shorter, color_for_mode};
|
||||
use crate::sandbox::dashboards::generic_trip_table::GenericTripTable;
|
||||
use crate::sandbox::dashboards::table::{Col, Filter, Table};
|
||||
@ -15,7 +14,7 @@ use crate::sandbox::dashboards::DashTab;
|
||||
pub struct FinishedTripTable;
|
||||
|
||||
impl FinishedTripTable {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
GenericTripTable::new(
|
||||
ctx,
|
||||
app,
|
||||
@ -29,7 +28,7 @@ impl FinishedTripTable {
|
||||
pub struct CancelledTripTable;
|
||||
|
||||
impl CancelledTripTable {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
GenericTripTable::new(
|
||||
ctx,
|
||||
app,
|
||||
@ -43,7 +42,7 @@ impl CancelledTripTable {
|
||||
pub struct UnfinishedTripTable;
|
||||
|
||||
impl UnfinishedTripTable {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
GenericTripTable::new(
|
||||
ctx,
|
||||
app,
|
||||
|
@ -3,8 +3,8 @@ use std::collections::BTreeMap;
|
||||
use geom::{Duration, Time};
|
||||
use sim::{OrigPersonID, PersonID, TripID};
|
||||
use widgetry::{
|
||||
Btn, Color, EventCtx, GfxCtx, HorizontalAlignment, Line, Outcome, Panel, RewriteColor, Text,
|
||||
TextExt, VerticalAlignment, Widget,
|
||||
Btn, Color, EventCtx, GfxCtx, HorizontalAlignment, Line, Outcome, Panel, RewriteColor, State,
|
||||
Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
@ -12,7 +12,7 @@ use crate::challenges::{Challenge, HighScore};
|
||||
use crate::common::Tab;
|
||||
use crate::cutscene::{CutsceneBuilder, FYI};
|
||||
use crate::edit::EditMode;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::cmp_duration_shorter;
|
||||
use crate::sandbox::gameplay::{challenge_header, FinalScore, GameplayMode, GameplayState};
|
||||
use crate::sandbox::{Actions, SandboxControls};
|
||||
@ -74,7 +74,7 @@ impl OptimizeCommute {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cutscene_pt1(ctx: &mut EventCtx, app: &App, mode: &GameplayMode) -> Box<dyn State> {
|
||||
pub fn cutscene_pt1(ctx: &mut EventCtx, app: &App, mode: &GameplayMode) -> Box<dyn State<App>> {
|
||||
CutsceneBuilder::new("Optimize one commute: part 1")
|
||||
.boss("Listen up, I've got a special job for you today.")
|
||||
.player("What is it? The scooter coalition back with demands for more valet parking?")
|
||||
@ -96,7 +96,7 @@ impl OptimizeCommute {
|
||||
.build(ctx, app, cutscene_task(mode))
|
||||
}
|
||||
|
||||
pub fn cutscene_pt2(ctx: &mut EventCtx, app: &App, mode: &GameplayMode) -> Box<dyn State> {
|
||||
pub fn cutscene_pt2(ctx: &mut EventCtx, app: &App, mode: &GameplayMode) -> Box<dyn State<App>> {
|
||||
// TODO The person chosen for this currently has more of an issue needing PBLs, actually.
|
||||
CutsceneBuilder::new("Optimize one commute: part 2")
|
||||
.boss("I've got another, er, friend who's sick of this parking situation.")
|
||||
@ -259,7 +259,7 @@ fn final_score(
|
||||
before: Duration,
|
||||
after: Duration,
|
||||
goal: Duration,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let mut next_mode: Option<GameplayMode> = None;
|
||||
|
||||
let msg = if before == after {
|
||||
|
@ -2,7 +2,7 @@ use geom::{Duration, Time};
|
||||
use map_model::IntersectionID;
|
||||
use widgetry::{
|
||||
Btn, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, RewriteColor,
|
||||
Text, VerticalAlignment, Widget,
|
||||
State, Text, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, FindDelayedIntersections};
|
||||
@ -10,7 +10,7 @@ use crate::challenges::HighScore;
|
||||
use crate::common::Warping;
|
||||
use crate::cutscene::{CutsceneBuilder, FYI};
|
||||
use crate::edit::EditMode;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::ID;
|
||||
use crate::sandbox::gameplay::{challenge_header, FinalScore, GameplayMode, GameplayState};
|
||||
use crate::sandbox::{Actions, SandboxControls, SandboxMode};
|
||||
@ -55,7 +55,7 @@ impl FixTrafficSignals {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cutscene_pt1(ctx: &mut EventCtx, app: &App, _: &GameplayMode) -> Box<dyn State> {
|
||||
pub fn cutscene_pt1(ctx: &mut EventCtx, app: &App, _: &GameplayMode) -> Box<dyn State<App>> {
|
||||
CutsceneBuilder::new("Traffic signal survivor")
|
||||
.boss("I hope you've had your coffee. There's a huge mess downtown.")
|
||||
.player("Did two buses get tangled together again?")
|
||||
@ -330,7 +330,7 @@ fn final_score(
|
||||
app: &mut App,
|
||||
mode: GameplayMode,
|
||||
failed: bool,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let score = app.primary.sim.time() - Time::START_OF_DAY;
|
||||
HighScore {
|
||||
goal: format!(
|
||||
|
@ -10,13 +10,13 @@ use sim::{
|
||||
};
|
||||
use widgetry::{
|
||||
lctrl, Btn, Choice, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel,
|
||||
ScreenRectangle, Spinner, Text, TextExt, VerticalAlignment, Widget,
|
||||
ScreenRectangle, Spinner, State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::{CityPicker, CommonState};
|
||||
use crate::edit::EditMode;
|
||||
use crate::game::{ChooseSomething, PopupMsg, PromptInput, State, Transition};
|
||||
use crate::game::{ChooseSomething, PopupMsg, PromptInput, Transition};
|
||||
use crate::helpers::{nice_map_name, ID};
|
||||
use crate::sandbox::gameplay::{GameplayMode, GameplayState};
|
||||
use crate::sandbox::{Actions, SandboxControls, SandboxMode};
|
||||
@ -146,7 +146,7 @@ pub fn make_change_traffic(
|
||||
app: &App,
|
||||
btn: ScreenRectangle,
|
||||
current: String,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let mut choices = Vec::new();
|
||||
for name in abstutil::list_all_objects(abstutil::path_all_scenarios(app.primary.map.get_name()))
|
||||
{
|
||||
@ -219,7 +219,7 @@ struct AgentSpawner {
|
||||
}
|
||||
|
||||
impl AgentSpawner {
|
||||
fn new(ctx: &mut EventCtx, start: Option<BuildingID>) -> Box<dyn State> {
|
||||
fn new(ctx: &mut EventCtx, start: Option<BuildingID>) -> Box<dyn State<App>> {
|
||||
let mut spawner = AgentSpawner {
|
||||
source: None,
|
||||
goal: None,
|
||||
@ -269,7 +269,7 @@ impl AgentSpawner {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for AgentSpawner {
|
||||
impl State<App> for AgentSpawner {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -5,7 +5,8 @@ use geom::Duration;
|
||||
use map_model::{EditCmd, EditIntersection, Map, MapEdits};
|
||||
use sim::{OrigPersonID, Scenario, ScenarioGenerator, ScenarioModifier};
|
||||
use widgetry::{
|
||||
lctrl, Btn, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, Panel, TextExt, Widget,
|
||||
lctrl, Btn, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, Panel, State, TextExt,
|
||||
Widget,
|
||||
};
|
||||
|
||||
pub use self::freeform::spawn_agents_around;
|
||||
@ -13,7 +14,7 @@ pub use self::tutorial::{Tutorial, TutorialPointer, TutorialState};
|
||||
use crate::app::App;
|
||||
use crate::challenges::{Challenge, ChallengesPicker};
|
||||
use crate::edit::{apply_map_edits, SaveEdits};
|
||||
use crate::game::{State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::pregame::MainMenu;
|
||||
use crate::sandbox::{Actions, SandboxControls, SandboxMode};
|
||||
|
||||
@ -221,7 +222,7 @@ impl FinalScore {
|
||||
msg: String,
|
||||
mode: GameplayMode,
|
||||
next_mode: Option<GameplayMode>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
Box::new(FinalScore {
|
||||
panel: Panel::new(
|
||||
Widget::custom_row(vec![
|
||||
@ -260,7 +261,7 @@ impl FinalScore {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for FinalScore {
|
||||
impl State<App> for FinalScore {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -5,13 +5,13 @@ use maplit::btreeset;
|
||||
use sim::{ScenarioModifier, TripMode};
|
||||
use widgetry::{
|
||||
lctrl, AreaSlider, Btn, Choice, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line,
|
||||
Outcome, Panel, Spinner, Text, TextExt, VerticalAlignment, Widget,
|
||||
Outcome, Panel, Spinner, State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::CityPicker;
|
||||
use crate::edit::EditMode;
|
||||
use crate::game::{ChooseSomething, PopupMsg, State, Transition};
|
||||
use crate::game::{ChooseSomething, PopupMsg, Transition};
|
||||
use crate::helpers::{checkbox_per_mode, grey_out_map, nice_map_name};
|
||||
use crate::sandbox::gameplay::freeform::make_change_traffic;
|
||||
use crate::sandbox::gameplay::{GameplayMode, GameplayState};
|
||||
@ -165,7 +165,7 @@ impl EditScenarioModifiers {
|
||||
ctx: &mut EventCtx,
|
||||
scenario_name: String,
|
||||
modifiers: Vec<ScenarioModifier>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
let mut rows = vec![
|
||||
Line("Modify traffic patterns").small_heading().draw(ctx),
|
||||
Text::from_multiline(vec![
|
||||
@ -217,7 +217,7 @@ impl EditScenarioModifiers {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for EditScenarioModifiers {
|
||||
impl State<App> for EditScenarioModifiers {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -324,7 +324,7 @@ impl ChangeMode {
|
||||
app: &App,
|
||||
scenario_name: String,
|
||||
modifiers: Vec<ScenarioModifier>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
Box::new(ChangeMode {
|
||||
scenario_name,
|
||||
modifiers,
|
||||
@ -369,7 +369,7 @@ impl ChangeMode {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for ChangeMode {
|
||||
impl State<App> for ChangeMode {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
|
@ -11,14 +11,14 @@ use sim::{
|
||||
};
|
||||
use widgetry::{
|
||||
hotkeys, lctrl, Btn, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel,
|
||||
RewriteColor, ScreenPt, Text, TextExt, VerticalAlignment, Widget,
|
||||
RewriteColor, ScreenPt, State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::common::{tool_panel, Minimap, Warping};
|
||||
use crate::cutscene::CutsceneBuilder;
|
||||
use crate::edit::EditMode;
|
||||
use crate::game::{PopupMsg, State, Transition};
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
use crate::helpers::{grey_out_map, ID};
|
||||
use crate::sandbox::gameplay::{GameplayMode, GameplayState};
|
||||
use crate::sandbox::{
|
||||
@ -1408,7 +1408,7 @@ pub fn execute(ctx: &mut EventCtx, app: &mut App, id: ID, action: &str) -> Trans
|
||||
Transition::Push(response)
|
||||
}
|
||||
|
||||
fn intro_story(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
fn intro_story(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||
CutsceneBuilder::new("Introduction")
|
||||
.boss(
|
||||
"Argh, the mayor's on my case again about the West Seattle bridge. This day couldn't \
|
||||
|
@ -2,13 +2,13 @@ use geom::{ArrowCap, Distance, Time};
|
||||
use map_model::{LaneID, TurnType};
|
||||
use sim::AgentID;
|
||||
use widgetry::{
|
||||
Btn, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome,
|
||||
Panel, Text, TextExt, VerticalAlignment, Widget,
|
||||
Btn, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
||||
Line, Outcome, Panel, State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, ShowEverything};
|
||||
use crate::common::ColorLegend;
|
||||
use crate::game::{DrawBaselayer, State, Transition};
|
||||
use crate::game::Transition;
|
||||
use crate::render::{DrawOptions, BIG_ARROW_THICKNESS};
|
||||
|
||||
/// Draws a preview of the path for the agent under the mouse cursor.
|
||||
@ -79,7 +79,7 @@ pub struct TurnExplorer {
|
||||
}
|
||||
|
||||
impl TurnExplorer {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, l: LaneID) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, l: LaneID) -> Box<dyn State<App>> {
|
||||
Box::new(TurnExplorer {
|
||||
l,
|
||||
idx: 0,
|
||||
@ -88,7 +88,7 @@ impl TurnExplorer {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for TurnExplorer {
|
||||
impl State<App> for TurnExplorer {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
|
||||
|
@ -7,8 +7,8 @@ use abstutil::prettyprint_usize;
|
||||
use geom::{Circle, Distance, Time};
|
||||
use sim::{Analytics, Scenario};
|
||||
use widgetry::{
|
||||
lctrl, Btn, Choice, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, Text,
|
||||
TextExt, UpdateType, VerticalAlignment, Widget,
|
||||
lctrl, Btn, Choice, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State,
|
||||
Text, TextExt, UpdateType, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use self::misc_tools::{RoutePreview, TurnExplorer};
|
||||
@ -19,7 +19,7 @@ use crate::edit::{
|
||||
apply_map_edits, can_edit_lane, EditMode, LaneEditor, SaveEdits, StopSignEditor,
|
||||
TrafficSignalEditor,
|
||||
};
|
||||
use crate::game::{ChooseSomething, PopupMsg, State, Transition};
|
||||
use crate::game::{ChooseSomething, PopupMsg, Transition};
|
||||
use crate::helpers::ID;
|
||||
use crate::layer::PickLayer;
|
||||
use crate::load::{FileLoader, MapLoader};
|
||||
@ -56,7 +56,11 @@ pub struct SandboxControls {
|
||||
impl SandboxMode {
|
||||
/// If you don't need to chain any transitions after the SandboxMode that rely on its resources
|
||||
/// being loaded, use this. Otherwise, see `async_new`.
|
||||
pub fn simple_new(ctx: &mut EventCtx, app: &mut App, mode: GameplayMode) -> Box<dyn State> {
|
||||
pub fn simple_new(
|
||||
ctx: &mut EventCtx,
|
||||
app: &mut App,
|
||||
mode: GameplayMode,
|
||||
) -> Box<dyn State<App>> {
|
||||
SandboxMode::async_new(ctx, app, mode, Box::new(|_, _| Vec::new()))
|
||||
}
|
||||
|
||||
@ -70,7 +74,7 @@ impl SandboxMode {
|
||||
app: &mut App,
|
||||
mode: GameplayMode,
|
||||
finalize: Box<dyn FnOnce(&mut EventCtx, &mut App) -> Vec<Transition>>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
app.primary.clear_sim();
|
||||
Box::new(SandboxLoader {
|
||||
stage: Some(LoadStage::LoadingMap),
|
||||
@ -94,7 +98,7 @@ impl SandboxMode {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for SandboxMode {
|
||||
impl State<App> for SandboxMode {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
// Do this before gameplay
|
||||
if self.gameplay.can_move_canvas() {
|
||||
@ -273,7 +277,7 @@ pub fn maybe_exit_sandbox(ctx: &mut EventCtx) -> Transition {
|
||||
|
||||
struct BackToMainMenu;
|
||||
|
||||
impl State for BackToMainMenu {
|
||||
impl State<App> for BackToMainMenu {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.loading_screen("reset map and sim", |ctx, mut timer| {
|
||||
// Always safe to do this
|
||||
@ -614,7 +618,7 @@ struct SandboxLoader {
|
||||
finalize: Option<Box<dyn FnOnce(&mut EventCtx, &mut App) -> Vec<Transition>>>,
|
||||
}
|
||||
|
||||
impl State for SandboxLoader {
|
||||
impl State<App> for SandboxLoader {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
loop {
|
||||
match self.stage.take().unwrap() {
|
||||
|
@ -3,13 +3,13 @@ use instant::Instant;
|
||||
use abstutil::prettyprint_usize;
|
||||
use geom::{Duration, Polygon, Pt2D, Ring, Time};
|
||||
use widgetry::{
|
||||
AreaSlider, Btn, Checkbox, Choice, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome,
|
||||
Panel, Text, UpdateType, Widget,
|
||||
AreaSlider, Btn, Checkbox, Choice, Color, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, Key,
|
||||
Line, Outcome, Panel, State, Text, UpdateType, Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, FindDelayedIntersections, ShowEverything};
|
||||
use crate::common::Warping;
|
||||
use crate::game::{DrawBaselayer, PopupMsg, State, Transition};
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
use crate::helpers::{grey_out_map, ID};
|
||||
use crate::render::DrawOptions;
|
||||
use crate::sandbox::{GameplayMode, SandboxMode};
|
||||
@ -22,7 +22,11 @@ pub struct JumpToTime {
|
||||
}
|
||||
|
||||
impl JumpToTime {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, maybe_mode: Option<GameplayMode>) -> Box<dyn State> {
|
||||
pub fn new(
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
maybe_mode: Option<GameplayMode>,
|
||||
) -> Box<dyn State<App>> {
|
||||
let target = app.primary.sim.time();
|
||||
let end_of_day = app.primary.sim.get_end_of_day();
|
||||
Box::new(JumpToTime {
|
||||
@ -77,7 +81,7 @@ impl JumpToTime {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for JumpToTime {
|
||||
impl State<App> for JumpToTime {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -152,7 +156,11 @@ struct JumpToDelay {
|
||||
}
|
||||
|
||||
impl JumpToDelay {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, maybe_mode: Option<GameplayMode>) -> Box<dyn State> {
|
||||
pub fn new(
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
maybe_mode: Option<GameplayMode>,
|
||||
) -> Box<dyn State<App>> {
|
||||
Box::new(JumpToDelay {
|
||||
maybe_mode,
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
@ -195,7 +203,7 @@ impl JumpToDelay {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for JumpToDelay {
|
||||
impl State<App> for JumpToDelay {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
@ -256,7 +264,7 @@ impl TimeWarpScreen {
|
||||
app: &mut App,
|
||||
target: Time,
|
||||
mut halt_upon_delay: Option<Duration>,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
if let Some(halt_limit) = halt_upon_delay {
|
||||
if app.primary.sim_cb.is_none() {
|
||||
app.primary.sim_cb = Some(Box::new(FindDelayedIntersections {
|
||||
@ -291,7 +299,7 @@ impl TimeWarpScreen {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for TimeWarpScreen {
|
||||
impl State<App> for TimeWarpScreen {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
if ctx.input.nonblocking_is_update_event().is_some() {
|
||||
ctx.input.use_update_event();
|
||||
|
@ -3,14 +3,15 @@ use std::collections::BTreeSet;
|
||||
use geom::ArrowCap;
|
||||
use map_model::{IntersectionCluster, IntersectionID, PathConstraints};
|
||||
use widgetry::{
|
||||
Btn, Checkbox, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line,
|
||||
Outcome, Panel, Text, TextExt, VerticalAlignment, Widget,
|
||||
Btn, Checkbox, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx,
|
||||
HorizontalAlignment, Key, Line, Outcome, Panel, State, Text, TextExt, VerticalAlignment,
|
||||
Widget,
|
||||
};
|
||||
|
||||
use crate::app::{App, ShowEverything};
|
||||
use crate::common::CommonState;
|
||||
use crate::edit::ClusterTrafficSignalEditor;
|
||||
use crate::game::{DrawBaselayer, PopupMsg, State, Transition};
|
||||
use crate::game::{PopupMsg, Transition};
|
||||
use crate::helpers::ID;
|
||||
use crate::render::{DrawOptions, BIG_ARROW_THICKNESS};
|
||||
|
||||
@ -20,7 +21,7 @@ pub struct UberTurnPicker {
|
||||
}
|
||||
|
||||
impl UberTurnPicker {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, i: IntersectionID) -> Box<dyn State> {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, i: IntersectionID) -> Box<dyn State<App>> {
|
||||
let mut members = BTreeSet::new();
|
||||
if let Some(list) = IntersectionCluster::autodetect(i, &app.primary.map) {
|
||||
members.extend(list);
|
||||
@ -49,7 +50,7 @@ impl UberTurnPicker {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for UberTurnPicker {
|
||||
impl State<App> for UberTurnPicker {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
if ctx.redo_mouseover() {
|
||||
@ -145,7 +146,7 @@ impl UberTurnViewer {
|
||||
members: BTreeSet<IntersectionID>,
|
||||
idx: usize,
|
||||
legal_turns: bool,
|
||||
) -> Box<dyn State> {
|
||||
) -> Box<dyn State<App>> {
|
||||
app.primary.current_selection = None;
|
||||
let map = &app.primary.map;
|
||||
|
||||
@ -218,7 +219,7 @@ impl UberTurnViewer {
|
||||
}
|
||||
}
|
||||
|
||||
impl State for UberTurnViewer {
|
||||
impl State<App> for UberTurnViewer {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
|
||||
|
@ -6,14 +6,31 @@ use map_model::osm;
|
||||
use map_model::raw::OriginalRoad;
|
||||
use widgetry::{
|
||||
Btn, Canvas, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line,
|
||||
Outcome, Panel, ScreenPt, Text, VerticalAlignment, Widget, GUI,
|
||||
Outcome, Panel, ScreenPt, SharedAppState, Text, Transition, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
mod model;
|
||||
mod world;
|
||||
|
||||
struct UI {
|
||||
struct App {
|
||||
model: Model,
|
||||
}
|
||||
|
||||
impl SharedAppState for App {
|
||||
fn dump_before_abort(&self, canvas: &Canvas) {
|
||||
if !self.model.map.name.is_empty() {
|
||||
canvas.save_camera_state(&self.model.map.name);
|
||||
}
|
||||
}
|
||||
|
||||
fn before_quit(&self, canvas: &Canvas) {
|
||||
if !self.model.map.name.is_empty() {
|
||||
canvas.save_camera_state(&self.model.map.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MainState {
|
||||
state: State,
|
||||
panel: Panel,
|
||||
popup: Option<Drawable>,
|
||||
@ -32,8 +49,8 @@ enum State {
|
||||
PreviewIntersection(Drawable, bool),
|
||||
}
|
||||
|
||||
impl UI {
|
||||
fn new(ctx: &mut EventCtx) -> UI {
|
||||
impl MainState {
|
||||
fn new(ctx: &mut EventCtx) -> (App, MainState) {
|
||||
let mut args = CmdArgs::new();
|
||||
let load = args.optional_free();
|
||||
let include_bldgs = args.enabled("--bldgs");
|
||||
@ -50,30 +67,32 @@ impl UI {
|
||||
}
|
||||
let bounds = model.map.gps_bounds.to_bounds();
|
||||
ctx.canvas.map_dims = (bounds.width(), bounds.height());
|
||||
UI {
|
||||
model,
|
||||
state: State::Viewing,
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Line("Map Editor").small_heading().draw(ctx),
|
||||
Text::new().draw(ctx).named("current info"),
|
||||
Widget::col(vec![
|
||||
Btn::text_fg("quit").build_def(ctx, Key::Escape),
|
||||
Btn::text_fg("save raw map").build_def(ctx, None),
|
||||
Btn::text_fg("preview all intersections").build_def(ctx, Key::G),
|
||||
]),
|
||||
]))
|
||||
.aligned(HorizontalAlignment::Right, VerticalAlignment::Top)
|
||||
.build(ctx),
|
||||
popup: None,
|
||||
info_key_held: false,
|
||||
(
|
||||
App { model },
|
||||
MainState {
|
||||
state: State::Viewing,
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Line("Map Editor").small_heading().draw(ctx),
|
||||
Text::new().draw(ctx).named("current info"),
|
||||
Widget::col(vec![
|
||||
Btn::text_fg("quit").build_def(ctx, Key::Escape),
|
||||
Btn::text_fg("save raw map").build_def(ctx, None),
|
||||
Btn::text_fg("preview all intersections").build_def(ctx, Key::G),
|
||||
]),
|
||||
]))
|
||||
.aligned(HorizontalAlignment::Right, VerticalAlignment::Top)
|
||||
.build(ctx),
|
||||
popup: None,
|
||||
info_key_held: false,
|
||||
|
||||
last_id: None,
|
||||
}
|
||||
last_id: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl GUI for UI {
|
||||
fn event(&mut self, ctx: &mut EventCtx) {
|
||||
impl widgetry::State<App> for MainState {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition<App> {
|
||||
if self.info_key_held {
|
||||
self.info_key_held = !ctx.input.key_released(Key::LeftAlt);
|
||||
} else {
|
||||
@ -82,7 +101,7 @@ impl GUI for UI {
|
||||
|
||||
ctx.canvas_movement();
|
||||
if ctx.redo_mouseover() {
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
app.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
|
||||
let mut cursor = ctx.canvas.get_cursor_in_map_space();
|
||||
@ -102,32 +121,32 @@ impl GUI for UI {
|
||||
Some(ID::Road(r)) | Some(ID::RoadPoint(r, _)) => Some(r),
|
||||
_ => None,
|
||||
};
|
||||
let after = match self.model.world.get_selection() {
|
||||
let after = match app.model.world.get_selection() {
|
||||
Some(ID::Road(r)) | Some(ID::RoadPoint(r, _)) => Some(r),
|
||||
_ => None,
|
||||
};
|
||||
if before != after {
|
||||
if let Some(id) = before {
|
||||
self.model.stop_showing_pts(id);
|
||||
app.model.stop_showing_pts(id);
|
||||
}
|
||||
if let Some(r) = after {
|
||||
self.model.show_r_points(r, ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
app.model.show_r_points(r, ctx);
|
||||
app.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match self.model.world.get_selection() {
|
||||
match app.model.world.get_selection() {
|
||||
Some(ID::Intersection(i)) => {
|
||||
if ctx.input.pressed(Key::LeftControl) {
|
||||
self.state = State::MovingIntersection(i);
|
||||
} else if ctx.input.pressed(Key::R) {
|
||||
self.state = State::CreatingRoad(i);
|
||||
} else if ctx.input.pressed(Key::Backspace) {
|
||||
self.model.delete_i(i);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
} else if !self.model.intersection_geom && ctx.input.pressed(Key::P) {
|
||||
let draw = preview_intersection(i, &self.model, ctx);
|
||||
app.model.delete_i(i);
|
||||
app.model.world.handle_mouseover(ctx);
|
||||
} else if !app.model.intersection_geom && ctx.input.pressed(Key::P) {
|
||||
let draw = preview_intersection(i, &app.model, ctx);
|
||||
self.state = State::PreviewIntersection(draw, false);
|
||||
}
|
||||
}
|
||||
@ -135,44 +154,44 @@ impl GUI for UI {
|
||||
if ctx.input.pressed(Key::LeftControl) {
|
||||
self.state = State::MovingBuilding(b);
|
||||
} else if ctx.input.pressed(Key::Backspace) {
|
||||
self.model.delete_b(b);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
app.model.delete_b(b);
|
||||
app.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
Some(ID::Road(r)) => {
|
||||
if ctx.input.pressed(Key::Backspace) {
|
||||
self.model.delete_r(r);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
app.model.delete_r(r);
|
||||
app.model.world.handle_mouseover(ctx);
|
||||
} else if cursor.is_some() && ctx.input.pressed(Key::P) {
|
||||
if let Some(id) = self.model.insert_r_pt(r, cursor.unwrap(), ctx) {
|
||||
self.model.world.force_set_selection(id);
|
||||
if let Some(id) = app.model.insert_r_pt(r, cursor.unwrap(), ctx) {
|
||||
app.model.world.force_set_selection(id);
|
||||
}
|
||||
} else if ctx.input.pressed(Key::X) {
|
||||
self.model.clear_r_pts(r, ctx);
|
||||
app.model.clear_r_pts(r, ctx);
|
||||
}
|
||||
}
|
||||
Some(ID::RoadPoint(r, idx)) => {
|
||||
if ctx.input.pressed(Key::LeftControl) {
|
||||
self.state = State::MovingRoadPoint(r, idx);
|
||||
} else if ctx.input.pressed(Key::Backspace) {
|
||||
self.model.delete_r_pt(r, idx, ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
app.model.delete_r_pt(r, idx, ctx);
|
||||
app.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
"quit" => {
|
||||
self.before_quit(ctx.canvas);
|
||||
app.before_quit(ctx.canvas);
|
||||
std::process::exit(0);
|
||||
}
|
||||
"save raw map" => {
|
||||
// TODO Only do this for synthetic maps
|
||||
self.model.export();
|
||||
app.model.export();
|
||||
}
|
||||
"preview all intersections" => {
|
||||
if !self.model.intersection_geom {
|
||||
let draw = preview_all_intersections(&self.model, ctx);
|
||||
if !app.model.intersection_geom {
|
||||
let draw = preview_all_intersections(&app.model, ctx);
|
||||
self.state = State::PreviewIntersection(draw, false);
|
||||
}
|
||||
}
|
||||
@ -181,16 +200,16 @@ impl GUI for UI {
|
||||
_ => {
|
||||
if ctx.input.pressed(Key::I) {
|
||||
if let Some(pt) = cursor {
|
||||
self.model.create_i(pt, ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
app.model.create_i(pt, ctx);
|
||||
app.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
// TODO Silly bug: Mouseover doesn't actually work! I think the
|
||||
// cursor being dead-center messes
|
||||
// up the precomputed triangles.
|
||||
} else if ctx.input.pressed(Key::B) {
|
||||
if let Some(pt) = cursor {
|
||||
let id = self.model.create_b(pt, ctx);
|
||||
self.model.world.force_set_selection(id);
|
||||
let id = app.model.create_b(pt, ctx);
|
||||
app.model.world.force_set_selection(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,7 +219,7 @@ impl GUI for UI {
|
||||
}
|
||||
State::MovingIntersection(id) => {
|
||||
if let Some(pt) = cursor {
|
||||
self.model.move_i(id, pt, ctx);
|
||||
app.model.move_i(id, pt, ctx);
|
||||
if ctx.input.key_released(Key::LeftControl) {
|
||||
self.state = State::Viewing;
|
||||
}
|
||||
@ -208,7 +227,7 @@ impl GUI for UI {
|
||||
}
|
||||
State::MovingBuilding(id) => {
|
||||
if let Some(pt) = cursor {
|
||||
self.model.move_b(id, pt, ctx);
|
||||
app.model.move_b(id, pt, ctx);
|
||||
if ctx.input.key_released(Key::LeftControl) {
|
||||
self.state = State::Viewing;
|
||||
}
|
||||
@ -216,7 +235,7 @@ impl GUI for UI {
|
||||
}
|
||||
State::MovingRoadPoint(r, idx) => {
|
||||
if let Some(pt) = cursor {
|
||||
self.model.move_r_pt(r, idx, pt, ctx);
|
||||
app.model.move_r_pt(r, idx, pt, ctx);
|
||||
if ctx.input.key_released(Key::LeftControl) {
|
||||
self.state = State::Viewing;
|
||||
}
|
||||
@ -225,12 +244,12 @@ impl GUI for UI {
|
||||
State::CreatingRoad(i1) => {
|
||||
if ctx.input.pressed(Key::Escape) {
|
||||
self.state = State::Viewing;
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
} else if let Some(ID::Intersection(i2)) = self.model.world.get_selection() {
|
||||
app.model.world.handle_mouseover(ctx);
|
||||
} else if let Some(ID::Intersection(i2)) = app.model.world.get_selection() {
|
||||
if i1 != i2 && ctx.input.pressed(Key::R) {
|
||||
self.model.create_r(i1, i2, ctx);
|
||||
app.model.create_r(i1, i2, ctx);
|
||||
self.state = State::Viewing;
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
app.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -244,24 +263,26 @@ impl GUI for UI {
|
||||
// TODO Woops, not communicating this kind of thing anymore
|
||||
if ctx.input.pressed(Key::P) {
|
||||
self.state = State::Viewing;
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
app.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.popup = None;
|
||||
if self.info_key_held {
|
||||
if let Some(id) = self.model.world.get_selection() {
|
||||
let txt = self.model.describe_obj(id);
|
||||
if let Some(id) = app.model.world.get_selection() {
|
||||
let txt = app.model.describe_obj(id);
|
||||
// TODO We used to display actions and hotkeys here
|
||||
self.popup = Some(ctx.upload(txt.render_to_batch(ctx.prerender)));
|
||||
}
|
||||
}
|
||||
|
||||
self.last_id = self.model.world.get_selection();
|
||||
self.last_id = app.model.world.get_selection();
|
||||
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
g.clear(Color::BLACK);
|
||||
|
||||
// It's useful to see the origin.
|
||||
@ -270,20 +291,20 @@ impl GUI for UI {
|
||||
|
||||
g.draw_polygon(
|
||||
Color::rgb(242, 239, 233),
|
||||
self.model.map.boundary_polygon.clone(),
|
||||
app.model.map.boundary_polygon.clone(),
|
||||
);
|
||||
match self.state {
|
||||
State::PreviewIntersection(_, _) => self.model.world.draw(g, |id| match id {
|
||||
State::PreviewIntersection(_, _) => app.model.world.draw(g, |id| match id {
|
||||
ID::Intersection(_) => false,
|
||||
_ => true,
|
||||
}),
|
||||
_ => self.model.world.draw(g, |_| true),
|
||||
_ => app.model.world.draw(g, |_| true),
|
||||
}
|
||||
|
||||
match self.state {
|
||||
State::CreatingRoad(i1) => {
|
||||
if let Some(cursor) = g.get_cursor_in_map_space() {
|
||||
if let Some(l) = Line::new(self.model.map.intersections[&i1].point, cursor) {
|
||||
if let Some(l) = Line::new(app.model.map.intersections[&i1].point, cursor) {
|
||||
g.draw_polygon(Color::GREEN, l.make_polygons(Distance::meters(5.0)));
|
||||
}
|
||||
}
|
||||
@ -309,18 +330,6 @@ impl GUI for UI {
|
||||
g.redraw_at(ScreenPt::new(0.0, 0.0), popup);
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_before_abort(&self, canvas: &Canvas) {
|
||||
if !self.model.map.name.is_empty() {
|
||||
canvas.save_camera_state(&self.model.map.name);
|
||||
}
|
||||
}
|
||||
|
||||
fn before_quit(&self, canvas: &Canvas) {
|
||||
if !self.model.map.name.is_empty() {
|
||||
canvas.save_camera_state(&self.model.map.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn preview_intersection(i: osm::NodeID, model: &Model, ctx: &EventCtx) -> Drawable {
|
||||
@ -362,5 +371,8 @@ fn preview_all_intersections(model: &Model, ctx: &EventCtx) -> Drawable {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
widgetry::run(widgetry::Settings::new("RawMap editor"), |ctx| UI::new(ctx));
|
||||
widgetry::run(widgetry::Settings::new("RawMap editor"), |ctx| {
|
||||
let (app, state) = MainState::new(ctx);
|
||||
(app, vec![Box::new(state)])
|
||||
});
|
||||
}
|
||||
|
163
widgetry/src/app_state.rs
Normal file
163
widgetry/src/app_state.rs
Normal file
@ -0,0 +1,163 @@
|
||||
// TODO Lotsa docs needed
|
||||
|
||||
use crate::{Canvas, EventCtx, GfxCtx};
|
||||
|
||||
pub trait SharedAppState {
|
||||
fn before_event(&mut self) {}
|
||||
fn draw_default(&self, _: &mut GfxCtx) {}
|
||||
|
||||
// Will be called if event or draw panics.
|
||||
fn dump_before_abort(&self, _: &Canvas) {}
|
||||
// Only before a normal exit, like window close
|
||||
fn before_quit(&self, _: &Canvas) {}
|
||||
}
|
||||
|
||||
pub(crate) struct App<A: SharedAppState> {
|
||||
/// A stack of states
|
||||
pub(crate) states: Vec<Box<dyn State<A>>>,
|
||||
pub(crate) shared_app_state: A,
|
||||
}
|
||||
|
||||
impl<A: SharedAppState> App<A> {
|
||||
pub(crate) fn event(&mut self, ctx: &mut EventCtx) {
|
||||
self.shared_app_state.before_event();
|
||||
|
||||
let transition = self
|
||||
.states
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.event(ctx, &mut self.shared_app_state);
|
||||
if self.execute_transition(ctx, transition) {
|
||||
// Let the new state initialize with a fake event. Usually these just return
|
||||
// Transition::Keep, but nothing stops them from doing whatever. (For example, entering
|
||||
// tutorial mode immediately pushes on a Warper.) So just recurse.
|
||||
ctx.no_op_event(true, |ctx| self.event(ctx));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn draw(&self, g: &mut GfxCtx) {
|
||||
let state = self.states.last().unwrap();
|
||||
|
||||
match state.draw_baselayer() {
|
||||
DrawBaselayer::DefaultDraw => {
|
||||
self.shared_app_state.draw_default(g);
|
||||
}
|
||||
DrawBaselayer::Custom => {}
|
||||
DrawBaselayer::PreviousState => {
|
||||
match self.states[self.states.len() - 2].draw_baselayer() {
|
||||
DrawBaselayer::DefaultDraw => {
|
||||
self.shared_app_state.draw_default(g);
|
||||
}
|
||||
DrawBaselayer::Custom => {}
|
||||
// Nope, don't recurse
|
||||
DrawBaselayer::PreviousState => {}
|
||||
}
|
||||
|
||||
self.states[self.states.len() - 2].draw(g, &self.shared_app_state);
|
||||
}
|
||||
}
|
||||
state.draw(g, &self.shared_app_state);
|
||||
}
|
||||
|
||||
/// If true, then the top-most state on the stack needs to be "woken up" with a fake mouseover
|
||||
/// event.
|
||||
fn execute_transition(&mut self, ctx: &mut EventCtx, transition: Transition<A>) -> bool {
|
||||
match transition {
|
||||
Transition::Keep => false,
|
||||
Transition::KeepWithMouseover => true,
|
||||
Transition::Pop => {
|
||||
self.states
|
||||
.pop()
|
||||
.unwrap()
|
||||
.on_destroy(ctx, &mut self.shared_app_state);
|
||||
if self.states.is_empty() {
|
||||
self.shared_app_state.before_quit(ctx.canvas);
|
||||
std::process::exit(0);
|
||||
}
|
||||
true
|
||||
}
|
||||
Transition::ModifyState(cb) => {
|
||||
cb(
|
||||
self.states.last_mut().unwrap(),
|
||||
ctx,
|
||||
&mut self.shared_app_state,
|
||||
);
|
||||
true
|
||||
}
|
||||
Transition::ReplaceWithData(cb) => {
|
||||
let mut last = self.states.pop().unwrap();
|
||||
last.on_destroy(ctx, &mut self.shared_app_state);
|
||||
let new_states = cb(last, ctx, &mut self.shared_app_state);
|
||||
self.states.extend(new_states);
|
||||
true
|
||||
}
|
||||
Transition::Push(state) => {
|
||||
self.states.push(state);
|
||||
true
|
||||
}
|
||||
Transition::Replace(state) => {
|
||||
self.states
|
||||
.pop()
|
||||
.unwrap()
|
||||
.on_destroy(ctx, &mut self.shared_app_state);
|
||||
self.states.push(state);
|
||||
true
|
||||
}
|
||||
Transition::Clear(states) => {
|
||||
while !self.states.is_empty() {
|
||||
self.states
|
||||
.pop()
|
||||
.unwrap()
|
||||
.on_destroy(ctx, &mut self.shared_app_state);
|
||||
}
|
||||
self.states.extend(states);
|
||||
true
|
||||
}
|
||||
Transition::Multi(list) => {
|
||||
// Always wake-up just the last state remaining after the sequence
|
||||
for t in list {
|
||||
self.execute_transition(ctx, t);
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DrawBaselayer {
|
||||
DefaultDraw,
|
||||
Custom,
|
||||
PreviousState,
|
||||
}
|
||||
|
||||
pub trait State<A>: downcast_rs::Downcast {
|
||||
fn event(&mut self, ctx: &mut EventCtx, shared_app_state: &mut A) -> Transition<A>;
|
||||
fn draw(&self, g: &mut GfxCtx, shared_app_state: &A);
|
||||
|
||||
fn draw_baselayer(&self) -> DrawBaselayer {
|
||||
DrawBaselayer::DefaultDraw
|
||||
}
|
||||
|
||||
/// Before this state is popped or replaced, call this.
|
||||
fn on_destroy(&mut self, _: &mut EventCtx, _: &mut A) {}
|
||||
// We don't need an on_enter -- the constructor for the state can just do it.
|
||||
}
|
||||
|
||||
downcast_rs::impl_downcast!(State<A>);
|
||||
|
||||
pub enum Transition<A> {
|
||||
Keep,
|
||||
KeepWithMouseover,
|
||||
Pop,
|
||||
/// If a state needs to pass data back to the parent, use this. Sadly, runtime type casting.
|
||||
ModifyState(Box<dyn FnOnce(&mut Box<dyn State<A>>, &mut EventCtx, &mut A)>),
|
||||
// TODO This is like Replace + ModifyState, then returning a few Push's from the callback. Not
|
||||
// sure how to express it in terms of the others without complicating ModifyState everywhere.
|
||||
ReplaceWithData(
|
||||
Box<dyn FnOnce(Box<dyn State<A>>, &mut EventCtx, &mut A) -> Vec<Box<dyn State<A>>>>,
|
||||
),
|
||||
Push(Box<dyn State<A>>),
|
||||
Replace(Box<dyn State<A>>),
|
||||
Clear(Vec<Box<dyn State<A>>>),
|
||||
Multi(Vec<Transition<A>>),
|
||||
}
|
@ -28,6 +28,7 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub use crate::app_state::{DrawBaselayer, SharedAppState, State, Transition};
|
||||
pub use crate::backend::Drawable;
|
||||
pub use crate::canvas::{Canvas, HorizontalAlignment, VerticalAlignment};
|
||||
pub use crate::color::{Color, Fill, LinearGradient, Texture};
|
||||
@ -36,7 +37,7 @@ pub use crate::event::{hotkeys, lctrl, Event, Key, MultiKey};
|
||||
pub use crate::event_ctx::{EventCtx, UpdateType};
|
||||
pub use crate::geom::{GeomBatch, RewriteColor};
|
||||
pub use crate::input::UserInput;
|
||||
pub use crate::runner::{run, Settings, GUI};
|
||||
pub use crate::runner::{run, Settings};
|
||||
pub use crate::screen_geom::{ScreenDims, ScreenPt, ScreenRectangle};
|
||||
pub use crate::style::Style;
|
||||
pub use crate::text::{Line, Text, TextExt, TextSpan};
|
||||
@ -60,6 +61,7 @@ pub use crate::widgets::spinner::Spinner;
|
||||
pub(crate) use crate::widgets::text_box::TextBox;
|
||||
pub use crate::widgets::{EdgeInsets, Outcome, Panel, Widget, WidgetImpl, WidgetOutput};
|
||||
|
||||
mod app_state;
|
||||
mod assets;
|
||||
#[cfg(any(feature = "glow-backend", feature = "wasm-backend"))]
|
||||
mod backend_glow;
|
||||
|
@ -7,28 +7,24 @@ use winit::window::Icon;
|
||||
|
||||
use geom::Duration;
|
||||
|
||||
use crate::app_state::App;
|
||||
use crate::assets::Assets;
|
||||
use crate::tools::screenshot::screenshot_everything;
|
||||
use crate::{Canvas, Event, EventCtx, GfxCtx, Key, Prerender, Style, Text, UpdateType, UserInput};
|
||||
use crate::{
|
||||
Canvas, Event, EventCtx, GfxCtx, Key, Prerender, SharedAppState, Style, Text, UpdateType,
|
||||
UserInput,
|
||||
};
|
||||
|
||||
const UPDATE_FREQUENCY: std::time::Duration = std::time::Duration::from_millis(1000 / 30);
|
||||
|
||||
pub trait GUI {
|
||||
fn event(&mut self, ctx: &mut EventCtx);
|
||||
fn draw(&self, g: &mut GfxCtx);
|
||||
// Will be called if event or draw panics.
|
||||
fn dump_before_abort(&self, _canvas: &Canvas) {}
|
||||
// Only before a normal exit, like window close
|
||||
fn before_quit(&self, _canvas: &Canvas) {}
|
||||
}
|
||||
|
||||
pub(crate) struct State<G: GUI> {
|
||||
pub(crate) gui: G,
|
||||
// TODO Rename this GUI or something
|
||||
pub(crate) struct State<A: SharedAppState> {
|
||||
pub(crate) app: App<A>,
|
||||
pub(crate) canvas: Canvas,
|
||||
style: Style,
|
||||
}
|
||||
|
||||
impl<G: GUI> State<G> {
|
||||
impl<A: SharedAppState> State<A> {
|
||||
// The bool indicates if the input was actually used.
|
||||
fn event(&mut self, mut ev: Event, prerender: &Prerender) -> (Vec<UpdateType>, bool) {
|
||||
if let Event::MouseWheelScroll(dx, dy) = ev {
|
||||
@ -111,7 +107,7 @@ impl<G: GUI> State<G> {
|
||||
style: &mut self.style,
|
||||
updates_requested: vec![],
|
||||
};
|
||||
self.gui.event(&mut ctx);
|
||||
self.app.event(&mut ctx);
|
||||
// TODO We should always do has_been_consumed, but various hacks prevent this from being
|
||||
// true. For now, just avoid the specific annoying redraw case when a KeyRelease event
|
||||
// is unused.
|
||||
@ -123,7 +119,7 @@ impl<G: GUI> State<G> {
|
||||
})) {
|
||||
Ok(pair) => pair,
|
||||
Err(err) => {
|
||||
self.gui.dump_before_abort(&self.canvas);
|
||||
self.app.shared_app_state.dump_before_abort(&self.canvas);
|
||||
panic::resume_unwind(err);
|
||||
}
|
||||
}
|
||||
@ -136,9 +132,9 @@ impl<G: GUI> State<G> {
|
||||
self.canvas.start_drawing();
|
||||
|
||||
if let Err(err) = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
self.gui.draw(&mut g);
|
||||
self.app.draw(&mut g);
|
||||
})) {
|
||||
self.gui.dump_before_abort(&self.canvas);
|
||||
self.app.shared_app_state.dump_before_abort(&self.canvas);
|
||||
panic::resume_unwind(err);
|
||||
}
|
||||
let naming_hint = g.naming_hint.take();
|
||||
@ -194,7 +190,13 @@ impl Settings {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings, make_gui: F) -> ! {
|
||||
pub fn run<
|
||||
A: 'static + SharedAppState,
|
||||
F: FnOnce(&mut EventCtx) -> (A, Vec<Box<dyn crate::app_state::State<A>>>),
|
||||
>(
|
||||
settings: Settings,
|
||||
make_app: F,
|
||||
) -> ! {
|
||||
let (prerender_innards, event_loop) = crate::backend::setup(&settings.window_title);
|
||||
|
||||
if let Some(ref path) = settings.window_icon {
|
||||
@ -224,7 +226,7 @@ pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings,
|
||||
let mut canvas = Canvas::new(initial_size);
|
||||
prerender.window_resized(initial_size);
|
||||
|
||||
let gui = make_gui(&mut EventCtx {
|
||||
let (shared_app_state, states) = make_app(&mut EventCtx {
|
||||
fake_mouseover: true,
|
||||
input: UserInput::new(Event::NoOp, &canvas),
|
||||
canvas: &mut canvas,
|
||||
@ -232,8 +234,12 @@ pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings,
|
||||
style: &mut style,
|
||||
updates_requested: vec![],
|
||||
});
|
||||
let app = App {
|
||||
shared_app_state,
|
||||
states,
|
||||
};
|
||||
|
||||
let mut state = State { canvas, gui, style };
|
||||
let mut state = State { canvas, app, style };
|
||||
|
||||
let dump_raw_events = settings.dump_raw_events;
|
||||
|
||||
@ -251,7 +257,7 @@ pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings,
|
||||
// ControlFlow::Exit cleanly shuts things down, meaning on larger maps, lots of
|
||||
// GPU stuff is dropped. Better to just abort violently and let the OS clean
|
||||
// up.
|
||||
state.gui.before_quit(&state.canvas);
|
||||
state.app.shared_app_state.before_quit(&state.canvas);
|
||||
std::process::exit(0);
|
||||
}
|
||||
winit::event::Event::WindowEvent { event, .. } => {
|
||||
|
@ -3,11 +3,11 @@ use std::{fs, process, thread, time};
|
||||
|
||||
use abstutil::Timer;
|
||||
|
||||
use crate::runner::{State, GUI};
|
||||
use crate::Prerender;
|
||||
use crate::runner::State;
|
||||
use crate::{Prerender, SharedAppState};
|
||||
|
||||
pub(crate) fn screenshot_everything<G: GUI>(
|
||||
state: &mut State<G>,
|
||||
pub(crate) fn screenshot_everything<A: SharedAppState>(
|
||||
state: &mut State<A>,
|
||||
dir_path: &str,
|
||||
prerender: &Prerender,
|
||||
zoom: f64,
|
||||
|
@ -7,18 +7,23 @@ use geom::{Angle, Duration, Percent, Polygon, Pt2D, Time};
|
||||
use widgetry::{
|
||||
lctrl, Btn, Checkbox, Choice, Color, Drawable, EventCtx, Fill, GeomBatch, GfxCtx,
|
||||
HorizontalAlignment, Key, Line, LinePlot, Outcome, Panel, PersistentSplit, PlotOptions, Series,
|
||||
Text, TextExt, Texture, UpdateType, VerticalAlignment, Widget, GUI,
|
||||
SharedAppState, State, Text, TextExt, Texture, Transition, UpdateType, VerticalAlignment,
|
||||
Widget,
|
||||
};
|
||||
|
||||
pub fn main() {
|
||||
// Control flow surrendered here. App implements State, which has an event handler and a draw
|
||||
// callback.
|
||||
widgetry::run(widgetry::Settings::new("widgetry demo"), |ctx| {
|
||||
App::new(ctx)
|
||||
(App {}, vec![Box::new(Demo::new(ctx))])
|
||||
});
|
||||
}
|
||||
|
||||
struct App {
|
||||
struct App {}
|
||||
|
||||
impl SharedAppState for App {}
|
||||
|
||||
struct Demo {
|
||||
controls: Panel,
|
||||
timeseries_panel: Option<(Duration, Panel)>,
|
||||
scrollable_canvas: Drawable,
|
||||
@ -26,9 +31,9 @@ struct App {
|
||||
elapsed: Duration,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn new(ctx: &mut EventCtx) -> App {
|
||||
App {
|
||||
impl Demo {
|
||||
fn new(ctx: &mut EventCtx) -> Demo {
|
||||
Demo {
|
||||
controls: make_controls(ctx),
|
||||
timeseries_panel: None,
|
||||
scrollable_canvas: setup_scrollable_canvas(ctx),
|
||||
@ -124,8 +129,8 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
impl GUI for App {
|
||||
fn event(&mut self, ctx: &mut EventCtx) {
|
||||
impl State<App> for Demo {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition<App> {
|
||||
// Allow panning and zooming to work.
|
||||
ctx.canvas_movement();
|
||||
|
||||
@ -194,9 +199,11 @@ impl GUI for App {
|
||||
if !self.controls.is_checked("paused") {
|
||||
ctx.request_update(UpdateType::Game);
|
||||
}
|
||||
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
fn draw(&self, g: &mut GfxCtx, _: &App) {
|
||||
g.clear(Color::BLACK);
|
||||
|
||||
if self.controls.is_checked("Draw scrollable canvas") {
|
||||
|
Loading…
Reference in New Issue
Block a user