mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
make Warp and Navigate use nested states inside CommonState
This commit is contained in:
parent
8250bd5a1e
commit
551866169a
@ -88,8 +88,8 @@ impl State for ABTestMode {
|
||||
false,
|
||||
);
|
||||
}
|
||||
if let Some(evmode) = self.common.event(ctx, ui, &mut self.menu) {
|
||||
return (Transition::Keep, evmode);
|
||||
if let Some(pair) = self.common.event(ctx, ui, &mut self.menu) {
|
||||
return pair;
|
||||
}
|
||||
|
||||
if self.menu.action("quit") {
|
||||
|
@ -3,6 +3,7 @@ mod navigate;
|
||||
mod turn_cycler;
|
||||
mod warp;
|
||||
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::ID;
|
||||
use crate::render::DrawOptions;
|
||||
use crate::ui::UI;
|
||||
@ -18,8 +19,6 @@ use std::time::Instant;
|
||||
pub struct CommonState {
|
||||
associated: associated::ShowAssociatedState,
|
||||
turn_cycler: turn_cycler::TurnCyclerState,
|
||||
warp: Option<warp::WarpState>,
|
||||
navigate: Option<navigate::Navigator>,
|
||||
}
|
||||
|
||||
impl CommonState {
|
||||
@ -27,8 +26,6 @@ impl CommonState {
|
||||
CommonState {
|
||||
associated: associated::ShowAssociatedState::Inactive,
|
||||
turn_cycler: turn_cycler::TurnCyclerState::new(),
|
||||
warp: None,
|
||||
navigate: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,48 +38,35 @@ impl CommonState {
|
||||
]
|
||||
}
|
||||
|
||||
// If this returns something, then this common state should prevent other things from
|
||||
// happening.
|
||||
pub fn event(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
ui: &mut UI,
|
||||
menu: &mut ModalMenu,
|
||||
) -> Option<EventLoopMode> {
|
||||
if let Some(ref mut warp) = self.warp {
|
||||
if let Some(evmode) = warp.event(ctx, ui) {
|
||||
return Some(evmode);
|
||||
}
|
||||
self.warp = None;
|
||||
}
|
||||
) -> Option<(Transition, EventLoopMode)> {
|
||||
if menu.action("warp") {
|
||||
self.warp = Some(warp::WarpState::new());
|
||||
}
|
||||
if let Some(ref mut navigate) = self.navigate {
|
||||
if let Some(evmode) = navigate.event(ctx, ui) {
|
||||
return Some(evmode);
|
||||
}
|
||||
self.navigate = None;
|
||||
return Some((
|
||||
Transition::Push(Box::new(warp::EnteringWarp::new())),
|
||||
EventLoopMode::InputOnly,
|
||||
));
|
||||
}
|
||||
if menu.action("navigate") {
|
||||
self.navigate = Some(navigate::Navigator::new(ui));
|
||||
return Some((
|
||||
Transition::Push(Box::new(navigate::Navigator::new(ui))),
|
||||
EventLoopMode::InputOnly,
|
||||
));
|
||||
}
|
||||
|
||||
self.associated.event(ui);
|
||||
self.turn_cycler.event(ctx, ui);
|
||||
if menu.action("take a screenshot") {
|
||||
return Some(EventLoopMode::ScreenCaptureCurrentShot);
|
||||
return Some((Transition::Keep, EventLoopMode::ScreenCaptureCurrentShot));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
if let Some(ref warp) = self.warp {
|
||||
warp.draw(g);
|
||||
}
|
||||
if let Some(ref navigate) = self.navigate {
|
||||
navigate.draw(g);
|
||||
}
|
||||
self.turn_cycler.draw(g, ui);
|
||||
|
||||
CommonState::draw_osd(g, ui, ui.primary.current_selection);
|
||||
|
@ -1,111 +1,123 @@
|
||||
use crate::common::warp::Warping;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::helpers::ID;
|
||||
use crate::ui::UI;
|
||||
use ezgui::{Autocomplete, EventCtx, EventLoopMode, GfxCtx, InputResult, Warper};
|
||||
use map_model::RoadID;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub enum Navigator {
|
||||
FirstStreet(Autocomplete<RoadID>),
|
||||
CrossStreet(RoadID, Autocomplete<RoadID>),
|
||||
Warping(Warper, ID),
|
||||
pub struct Navigator {
|
||||
autocomplete: Autocomplete<RoadID>,
|
||||
}
|
||||
|
||||
impl Navigator {
|
||||
pub fn new(ui: &UI) -> Navigator {
|
||||
// TODO Canonicalize names, handling abbreviations like east/e and street/st
|
||||
Navigator::FirstStreet(Autocomplete::new(
|
||||
"Warp where?",
|
||||
ui.primary
|
||||
.map
|
||||
.all_roads()
|
||||
.iter()
|
||||
.map(|r| (r.get_name(), r.id))
|
||||
.collect(),
|
||||
))
|
||||
Navigator {
|
||||
autocomplete: Autocomplete::new(
|
||||
"Warp where?",
|
||||
ui.primary
|
||||
.map
|
||||
.all_roads()
|
||||
.iter()
|
||||
.map(|r| (r.get_name(), r.id))
|
||||
.collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When None, this is done.
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Option<EventLoopMode> {
|
||||
impl State for Navigator {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
let map = &ui.primary.map;
|
||||
|
||||
match self {
|
||||
Navigator::FirstStreet(autocomplete) => match autocomplete.event(ctx.input) {
|
||||
InputResult::Canceled => None,
|
||||
InputResult::Done(name, ids) => {
|
||||
// Roads share intersections, so of course there'll be overlap here.
|
||||
let mut cross_streets = HashSet::new();
|
||||
for r in &ids {
|
||||
let road = map.get_r(*r);
|
||||
for i in &[road.src_i, road.dst_i] {
|
||||
for cross in &map.get_i(*i).roads {
|
||||
if !ids.contains(cross) {
|
||||
cross_streets.insert(*cross);
|
||||
}
|
||||
match self.autocomplete.event(ctx.input) {
|
||||
InputResult::Canceled => (Transition::Pop, EventLoopMode::InputOnly),
|
||||
InputResult::Done(name, ids) => {
|
||||
// Roads share intersections, so of course there'll be overlap here.
|
||||
let mut cross_streets = HashSet::new();
|
||||
for r in &ids {
|
||||
let road = map.get_r(*r);
|
||||
for i in &[road.src_i, road.dst_i] {
|
||||
for cross in &map.get_i(*i).roads {
|
||||
if !ids.contains(cross) {
|
||||
cross_streets.insert(*cross);
|
||||
}
|
||||
}
|
||||
}
|
||||
*self = Navigator::CrossStreet(
|
||||
*ids.iter().next().unwrap(),
|
||||
Autocomplete::new(
|
||||
}
|
||||
(
|
||||
Transition::Replace(Box::new(CrossStreet {
|
||||
first: *ids.iter().next().unwrap(),
|
||||
autocomplete: Autocomplete::new(
|
||||
&format!("{} and what?", name),
|
||||
cross_streets
|
||||
.into_iter()
|
||||
.map(|r| (map.get_r(r).get_name(), r))
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
Some(EventLoopMode::InputOnly)
|
||||
}
|
||||
InputResult::StillActive => Some(EventLoopMode::InputOnly),
|
||||
},
|
||||
Navigator::CrossStreet(first_id, autocomplete) => match autocomplete.event(ctx.input) {
|
||||
InputResult::Canceled => {
|
||||
// Just warp to somewhere on the first road
|
||||
let road = map.get_r(*first_id);
|
||||
println!("Warping to {}", road.get_name());
|
||||
*self = Navigator::Warping(
|
||||
Warper::new(
|
||||
ctx,
|
||||
road.center_pts.dist_along(road.center_pts.length() / 2.0).0,
|
||||
),
|
||||
ID::Lane(road.all_lanes()[0]),
|
||||
);
|
||||
Some(EventLoopMode::Animation)
|
||||
}
|
||||
InputResult::Done(name, ids) => {
|
||||
println!(
|
||||
"Warping to {} and {}",
|
||||
map.get_r(*first_id).get_name(),
|
||||
name
|
||||
);
|
||||
let road = map.get_r(*ids.iter().next().unwrap());
|
||||
let pt = if map.get_i(road.src_i).roads.contains(first_id) {
|
||||
map.get_i(road.src_i).polygon.center()
|
||||
} else {
|
||||
map.get_i(road.dst_i).polygon.center()
|
||||
};
|
||||
*self = Navigator::Warping(Warper::new(ctx, pt), ID::Lane(road.all_lanes()[0]));
|
||||
Some(EventLoopMode::Animation)
|
||||
}
|
||||
InputResult::StillActive => Some(EventLoopMode::InputOnly),
|
||||
},
|
||||
Navigator::Warping(ref warper, id) => {
|
||||
let result = warper.event(ctx);
|
||||
if result.is_none() {
|
||||
ui.primary.current_selection = Some(*id);
|
||||
}
|
||||
result
|
||||
})),
|
||||
EventLoopMode::InputOnly,
|
||||
)
|
||||
}
|
||||
InputResult::StillActive => (Transition::Keep, EventLoopMode::InputOnly),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
match self {
|
||||
Navigator::FirstStreet(ref autocomplete)
|
||||
| Navigator::CrossStreet(_, ref autocomplete) => {
|
||||
autocomplete.draw(g);
|
||||
}
|
||||
Navigator::Warping(_, _) => {}
|
||||
}
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
self.autocomplete.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
struct CrossStreet {
|
||||
first: RoadID,
|
||||
autocomplete: Autocomplete<RoadID>,
|
||||
}
|
||||
|
||||
impl State for CrossStreet {
|
||||
// When None, this is done.
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
let map = &ui.primary.map;
|
||||
match self.autocomplete.event(ctx.input) {
|
||||
InputResult::Canceled => {
|
||||
// Just warp to somewhere on the first road
|
||||
let road = map.get_r(self.first);
|
||||
println!("Warping to {}", road.get_name());
|
||||
(
|
||||
Transition::Replace(Box::new(Warping {
|
||||
warper: Warper::new(
|
||||
ctx,
|
||||
road.center_pts.dist_along(road.center_pts.length() / 2.0).0,
|
||||
),
|
||||
id: ID::Lane(road.all_lanes()[0]),
|
||||
})),
|
||||
EventLoopMode::Animation,
|
||||
)
|
||||
}
|
||||
InputResult::Done(name, ids) => {
|
||||
println!(
|
||||
"Warping to {} and {}",
|
||||
map.get_r(self.first).get_name(),
|
||||
name
|
||||
);
|
||||
let road = map.get_r(*ids.iter().next().unwrap());
|
||||
let pt = if map.get_i(road.src_i).roads.contains(&self.first) {
|
||||
map.get_i(road.src_i).polygon.center()
|
||||
} else {
|
||||
map.get_i(road.dst_i).polygon.center()
|
||||
};
|
||||
(
|
||||
Transition::Replace(Box::new(Warping {
|
||||
warper: Warper::new(ctx, pt),
|
||||
id: ID::Lane(road.all_lanes()[0]),
|
||||
})),
|
||||
EventLoopMode::Animation,
|
||||
)
|
||||
}
|
||||
InputResult::StillActive => (Transition::Keep, EventLoopMode::InputOnly),
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
self.autocomplete.draw(g);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::game::{State, Transition};
|
||||
use crate::helpers::ID;
|
||||
use crate::ui::{PerMapUI, UI};
|
||||
use ezgui::{EventCtx, EventLoopMode, GfxCtx, InputResult, TextBox, Warper};
|
||||
@ -6,46 +7,60 @@ use map_model::{raw_data, AreaID, BuildingID, IntersectionID, LaneID, RoadID};
|
||||
use sim::{PedestrianID, TripID};
|
||||
use std::usize;
|
||||
|
||||
pub enum WarpState {
|
||||
EnteringSearch(TextBox),
|
||||
Warping(Warper, ID),
|
||||
pub struct EnteringWarp {
|
||||
tb: TextBox,
|
||||
}
|
||||
|
||||
impl WarpState {
|
||||
pub fn new() -> WarpState {
|
||||
WarpState::EnteringSearch(TextBox::new("Warp to what?", None))
|
||||
impl EnteringWarp {
|
||||
pub fn new() -> EnteringWarp {
|
||||
EnteringWarp {
|
||||
tb: TextBox::new("Warp to what?", None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When None, this is done.
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Option<EventLoopMode> {
|
||||
match self {
|
||||
WarpState::EnteringSearch(tb) => match tb.event(ctx.input) {
|
||||
InputResult::Canceled => None,
|
||||
InputResult::Done(to, _) => {
|
||||
if let Some((id, pt)) = warp_point(to, &ui.primary) {
|
||||
*self = WarpState::Warping(Warper::new(ctx, pt), id);
|
||||
Some(EventLoopMode::Animation)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
impl State for EnteringWarp {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
match self.tb.event(ctx.input) {
|
||||
InputResult::Canceled => (Transition::Pop, EventLoopMode::InputOnly),
|
||||
InputResult::Done(to, _) => {
|
||||
if let Some((id, pt)) = warp_point(to, &ui.primary) {
|
||||
(
|
||||
Transition::Replace(Box::new(Warping {
|
||||
warper: Warper::new(ctx, pt),
|
||||
id,
|
||||
})),
|
||||
EventLoopMode::Animation,
|
||||
)
|
||||
} else {
|
||||
(Transition::Pop, EventLoopMode::InputOnly)
|
||||
}
|
||||
InputResult::StillActive => Some(EventLoopMode::InputOnly),
|
||||
},
|
||||
WarpState::Warping(ref warper, id) => {
|
||||
let result = warper.event(ctx);
|
||||
if result.is_none() {
|
||||
ui.primary.current_selection = Some(*id);
|
||||
}
|
||||
result
|
||||
}
|
||||
InputResult::StillActive => (Transition::Keep, EventLoopMode::InputOnly),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
if let WarpState::EnteringSearch(tb) = self {
|
||||
tb.draw(g);
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
self.tb.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Warping {
|
||||
pub warper: Warper,
|
||||
pub id: ID,
|
||||
}
|
||||
|
||||
impl State for Warping {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
if let Some(evmode) = self.warper.event(ctx) {
|
||||
(Transition::Keep, evmode)
|
||||
} else {
|
||||
ui.primary.current_selection = Some(self.id);
|
||||
(Transition::Pop, EventLoopMode::InputOnly)
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&self, _: &mut GfxCtx, _: &UI) {}
|
||||
}
|
||||
|
||||
fn warp_point(line: String, primary: &PerMapUI) -> Option<(ID, Pt2D)> {
|
||||
|
@ -127,8 +127,8 @@ impl State for DebugMode {
|
||||
self.menu.handle_event(ctx, Some(txt));
|
||||
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
if let Some(evmode) = self.common.event(ctx, ui, &mut self.menu) {
|
||||
return (Transition::Keep, evmode);
|
||||
if let Some(pair) = self.common.event(ctx, ui, &mut self.menu) {
|
||||
return pair;
|
||||
}
|
||||
|
||||
if self.menu.action("quit") {
|
||||
|
@ -87,8 +87,8 @@ impl State for EditMode {
|
||||
false,
|
||||
);
|
||||
}
|
||||
if let Some(evmode) = self.common.event(ctx, ui, &mut self.menu) {
|
||||
return (Transition::Keep, evmode);
|
||||
if let Some(pair) = self.common.event(ctx, ui, &mut self.menu) {
|
||||
return pair;
|
||||
}
|
||||
|
||||
if self.menu.action("quit") {
|
||||
|
@ -106,8 +106,8 @@ impl State for SandboxMode {
|
||||
false,
|
||||
);
|
||||
}
|
||||
if let Some(evmode) = self.common.event(ctx, ui, &mut self.menu) {
|
||||
return (Transition::Keep, evmode);
|
||||
if let Some(pair) = self.common.event(ctx, ui, &mut self.menu) {
|
||||
return pair;
|
||||
}
|
||||
|
||||
if let Some(spawner) = spawner::AgentSpawner::new(ctx, ui, &mut self.menu) {
|
||||
|
Loading…
Reference in New Issue
Block a user