mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-23 17:07:12 +03:00
Refactor common event handlers for the 3 modes
This commit is contained in:
parent
57d1cba40c
commit
0a03887eab
@ -109,44 +109,6 @@ impl State<App> for ExploreMap {
|
||||
|
||||
if let Outcome::Clicked(x) = self.top_panel.event(ctx) {
|
||||
match x.as_ref() {
|
||||
"about A/B Street" => {
|
||||
return Transition::Push(About::new_state(ctx));
|
||||
}
|
||||
"change map" => {
|
||||
return Transition::Push(CityPicker::new_state(
|
||||
ctx,
|
||||
app,
|
||||
Box::new(|ctx, app| {
|
||||
Transition::Multi(vec![
|
||||
Transition::Pop,
|
||||
// Since we're totally changing maps, don't reuse the Layers
|
||||
Transition::Replace(ExploreMap::launch(ctx, app)),
|
||||
])
|
||||
}),
|
||||
));
|
||||
}
|
||||
"Create new bike lanes" => {
|
||||
app.primary.current_selection = None;
|
||||
return Transition::ConsumeState(Box::new(|state, ctx, app| {
|
||||
let state = state.downcast::<ExploreMap>().ok().unwrap();
|
||||
vec![crate::ungap::quick_sketch::QuickSketch::new_state(
|
||||
ctx,
|
||||
app,
|
||||
state.layers,
|
||||
)]
|
||||
}));
|
||||
}
|
||||
"Plan a route" => {
|
||||
app.primary.current_selection = None;
|
||||
return Transition::ConsumeState(Box::new(|state, ctx, app| {
|
||||
let state = state.downcast::<ExploreMap>().ok().unwrap();
|
||||
vec![crate::ungap::route::RoutePlanner::new_state(
|
||||
ctx,
|
||||
app,
|
||||
state.layers,
|
||||
)]
|
||||
}));
|
||||
}
|
||||
"Open a proposal" => {
|
||||
// Dummy mode, just to allow all edits
|
||||
// TODO Actually, should we make one to express that only road edits are
|
||||
@ -171,7 +133,9 @@ impl State<App> for ExploreMap {
|
||||
"Share proposal" => {
|
||||
return Transition::Push(share::upload_proposal(ctx, app));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
x => {
|
||||
return Tab::Explore.handle_action::<ExploreMap>(ctx, app, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,7 +221,7 @@ fn make_top_panel(ctx: &mut EventCtx, app: &App) -> Panel {
|
||||
// TODO Should undo/redo, save, share functionality also live here?
|
||||
|
||||
Panel::new_builder(Widget::col(vec![
|
||||
make_header(ctx, app, Tab::Explore),
|
||||
Tab::Explore.make_header(ctx, app),
|
||||
Widget::col(file_management).section(ctx),
|
||||
]))
|
||||
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
|
||||
@ -267,7 +231,7 @@ fn make_top_panel(ctx: &mut EventCtx, app: &App) -> Panel {
|
||||
struct About;
|
||||
|
||||
impl About {
|
||||
pub fn new_state(ctx: &mut EventCtx) -> Box<dyn State<App>> {
|
||||
fn new_state(ctx: &mut EventCtx) -> Box<dyn State<App>> {
|
||||
let panel = Panel::new_builder(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
Line("About A/B Street").small_heading().into_widget(ctx),
|
||||
@ -309,44 +273,102 @@ pub enum Tab {
|
||||
Route,
|
||||
}
|
||||
|
||||
pub fn make_header(ctx: &mut EventCtx, app: &App, current_tab: Tab) -> Widget {
|
||||
Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
ctx.style()
|
||||
.btn_plain
|
||||
.btn()
|
||||
.image_path("system/assets/pregame/logo.svg")
|
||||
.image_dims(50.0)
|
||||
.build_widget(ctx, "about A/B Street"),
|
||||
ctx.style()
|
||||
.btn_popup_icon_text(
|
||||
"system/assets/tools/map.svg",
|
||||
nice_map_name(app.primary.map.get_name()),
|
||||
)
|
||||
.hotkey(lctrl(Key::L))
|
||||
.build_widget(ctx, "change map")
|
||||
.centered_vert()
|
||||
.align_right(),
|
||||
]),
|
||||
Widget::row(vec![
|
||||
ctx.style()
|
||||
.btn_tab
|
||||
.icon_text("system/assets/tools/pan.svg", "Explore")
|
||||
.hotkey(Key::E)
|
||||
.disabled(current_tab == Tab::Explore)
|
||||
.build_def(ctx),
|
||||
ctx.style()
|
||||
.btn_tab
|
||||
.icon_text("system/assets/tools/pencil.svg", "Create new bike lanes")
|
||||
.hotkey(Key::C)
|
||||
.disabled(current_tab == Tab::Create)
|
||||
.build_def(ctx),
|
||||
ctx.style()
|
||||
.btn_tab
|
||||
.icon_text("system/assets/tools/pin.svg", "Plan a route")
|
||||
.hotkey(Key::R)
|
||||
.disabled(current_tab == Tab::Route)
|
||||
.build_def(ctx),
|
||||
]),
|
||||
])
|
||||
pub trait TakeLayers {
|
||||
fn take_layers(self) -> Layers;
|
||||
}
|
||||
|
||||
impl TakeLayers for ExploreMap {
|
||||
fn take_layers(self) -> Layers {
|
||||
self.layers
|
||||
}
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn make_header(self, ctx: &mut EventCtx, app: &App) -> Widget {
|
||||
Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
ctx.style()
|
||||
.btn_plain
|
||||
.btn()
|
||||
.image_path("system/assets/pregame/logo.svg")
|
||||
.image_dims(50.0)
|
||||
.build_widget(ctx, "about A/B Street"),
|
||||
ctx.style()
|
||||
.btn_popup_icon_text(
|
||||
"system/assets/tools/map.svg",
|
||||
nice_map_name(app.primary.map.get_name()),
|
||||
)
|
||||
.hotkey(lctrl(Key::L))
|
||||
.build_widget(ctx, "change map")
|
||||
.centered_vert()
|
||||
.align_right(),
|
||||
]),
|
||||
Widget::row(vec![
|
||||
ctx.style()
|
||||
.btn_tab
|
||||
.icon_text("system/assets/tools/pan.svg", "Explore")
|
||||
.hotkey(Key::E)
|
||||
.disabled(self == Tab::Explore)
|
||||
.build_def(ctx),
|
||||
ctx.style()
|
||||
.btn_tab
|
||||
.icon_text("system/assets/tools/pencil.svg", "Create new bike lanes")
|
||||
.hotkey(Key::C)
|
||||
.disabled(self == Tab::Create)
|
||||
.build_def(ctx),
|
||||
ctx.style()
|
||||
.btn_tab
|
||||
.icon_text("system/assets/tools/pin.svg", "Plan a route")
|
||||
.hotkey(Key::R)
|
||||
.disabled(self == Tab::Route)
|
||||
.build_def(ctx),
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn handle_action<T: TakeLayers + State<App>>(
|
||||
self,
|
||||
ctx: &mut EventCtx,
|
||||
app: &mut App,
|
||||
action: &str,
|
||||
) -> Transition {
|
||||
match action {
|
||||
"about A/B Street" => Transition::Push(About::new_state(ctx)),
|
||||
"change map" => {
|
||||
Transition::Push(CityPicker::new_state(
|
||||
ctx,
|
||||
app,
|
||||
Box::new(|ctx, app| {
|
||||
Transition::Multi(vec![
|
||||
Transition::Pop,
|
||||
// Since we're totally changing maps, don't reuse the Layers
|
||||
// TODO Keep current tab...
|
||||
Transition::Replace(ExploreMap::launch(ctx, app)),
|
||||
])
|
||||
}),
|
||||
))
|
||||
}
|
||||
"Create new bike lanes" => {
|
||||
// This is only necessary to do coming from ExploreMap, but eh
|
||||
app.primary.current_selection = None;
|
||||
Transition::ConsumeState(Box::new(|state, ctx, app| {
|
||||
let state = state.downcast::<T>().ok().unwrap();
|
||||
vec![quick_sketch::QuickSketch::new_state(
|
||||
ctx,
|
||||
app,
|
||||
state.take_layers(),
|
||||
)]
|
||||
}))
|
||||
}
|
||||
"Plan a route" => Transition::ConsumeState(Box::new(|state, ctx, app| {
|
||||
let state = state.downcast::<T>().ok().unwrap();
|
||||
vec![route::RoutePlanner::new_state(
|
||||
ctx,
|
||||
app,
|
||||
state.take_layers(),
|
||||
)]
|
||||
})),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use abstutil::Tags;
|
||||
use map_gui::tools::{CityPicker, PopupMsg};
|
||||
use map_gui::tools::PopupMsg;
|
||||
use map_model::{BufferType, Direction, EditCmd, EditRoad, LaneSpec, LaneType, RoadID};
|
||||
use widgetry::{
|
||||
Choice, EventCtx, GfxCtx, HorizontalAlignment, Key, Outcome, Panel, State, TextExt,
|
||||
@ -9,7 +9,7 @@ use widgetry::{
|
||||
use crate::app::{App, Transition};
|
||||
use crate::common::RouteSketcher;
|
||||
use crate::edit::apply_map_edits;
|
||||
use crate::ungap::{make_header, Layers, Tab};
|
||||
use crate::ungap::{Layers, Tab, TakeLayers};
|
||||
|
||||
pub struct QuickSketch {
|
||||
top_panel: Panel,
|
||||
@ -17,6 +17,12 @@ pub struct QuickSketch {
|
||||
route_sketcher: RouteSketcher,
|
||||
}
|
||||
|
||||
impl TakeLayers for QuickSketch {
|
||||
fn take_layers(self) -> Layers {
|
||||
self.layers
|
||||
}
|
||||
}
|
||||
|
||||
impl QuickSketch {
|
||||
pub fn new_state(ctx: &mut EventCtx, app: &mut App, layers: Layers) -> Box<dyn State<App>> {
|
||||
let mut qs = QuickSketch {
|
||||
@ -30,7 +36,7 @@ impl QuickSketch {
|
||||
|
||||
fn update_top_panel(&mut self, ctx: &mut EventCtx, app: &App) {
|
||||
let mut col = vec![
|
||||
make_header(ctx, app, Tab::Create),
|
||||
Tab::Create.make_header(ctx, app),
|
||||
self.route_sketcher.get_widget_to_describe(ctx),
|
||||
];
|
||||
|
||||
@ -80,39 +86,6 @@ impl State<App> for QuickSketch {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
if let Outcome::Clicked(x) = self.top_panel.event(ctx) {
|
||||
match x.as_ref() {
|
||||
"about A/B Street" => {
|
||||
return Transition::Push(crate::ungap::About::new_state(ctx));
|
||||
}
|
||||
"change map" => {
|
||||
return Transition::Push(CityPicker::new_state(
|
||||
ctx,
|
||||
app,
|
||||
Box::new(|ctx, app| {
|
||||
Transition::Multi(vec![
|
||||
Transition::Pop,
|
||||
// Since we're totally changing maps, don't reuse the Layers
|
||||
// TODO Should we keep the current tab or always reset here?
|
||||
Transition::Replace(crate::ungap::ExploreMap::launch(ctx, app)),
|
||||
])
|
||||
}),
|
||||
));
|
||||
}
|
||||
"Explore" => {
|
||||
return Transition::ConsumeState(Box::new(|state, ctx, app| {
|
||||
let state = state.downcast::<QuickSketch>().ok().unwrap();
|
||||
vec![crate::ungap::ExploreMap::new_state(ctx, app, state.layers)]
|
||||
}));
|
||||
}
|
||||
"Plan a route" => {
|
||||
return Transition::ConsumeState(Box::new(|state, ctx, app| {
|
||||
let state = state.downcast::<QuickSketch>().ok().unwrap();
|
||||
vec![crate::ungap::route::RoutePlanner::new_state(
|
||||
ctx,
|
||||
app,
|
||||
state.layers,
|
||||
)]
|
||||
}));
|
||||
}
|
||||
"Add bike lanes" => {
|
||||
let messages = make_quick_changes(
|
||||
ctx,
|
||||
@ -128,7 +101,9 @@ impl State<App> for QuickSketch {
|
||||
]
|
||||
}));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
x => {
|
||||
return Tab::Create.handle_action::<QuickSketch>(ctx, app, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ use rand::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use geom::{Circle, Distance, Duration, FindClosest, Polygon};
|
||||
use map_gui::tools::CityPicker;
|
||||
use map_model::{PathStep, NORMAL_LANE_THICKNESS};
|
||||
use sim::{TripEndpoint, TripMode};
|
||||
use widgetry::{
|
||||
@ -14,7 +13,7 @@ use widgetry::{
|
||||
};
|
||||
|
||||
use crate::app::{App, Transition};
|
||||
use crate::ungap::{make_header, Layers, Tab};
|
||||
use crate::ungap::{Layers, Tab, TakeLayers};
|
||||
|
||||
pub struct RoutePlanner {
|
||||
layers: Layers,
|
||||
@ -36,6 +35,12 @@ pub struct RoutePlanner {
|
||||
results_panel: Panel,
|
||||
}
|
||||
|
||||
impl TakeLayers for RoutePlanner {
|
||||
fn take_layers(self) -> Layers {
|
||||
self.layers
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Maybe it's been a while and I've forgotten some UI patterns, but this is painfully manual.
|
||||
// I think we need a draggable map-space thing.
|
||||
struct Waypoint {
|
||||
@ -82,7 +87,7 @@ impl RoutePlanner {
|
||||
}
|
||||
|
||||
fn update_input_panel(&mut self, ctx: &mut EventCtx, app: &App) {
|
||||
let mut col = vec![make_header(ctx, app, Tab::Route)];
|
||||
let mut col = vec![Tab::Route.make_header(ctx, app)];
|
||||
|
||||
for (idx, waypt) in self.waypoints.iter().enumerate() {
|
||||
col.push(Widget::row(vec![
|
||||
@ -346,39 +351,6 @@ impl State<App> for RoutePlanner {
|
||||
|
||||
if let Outcome::Clicked(x) = self.input_panel.event(ctx) {
|
||||
match x.as_ref() {
|
||||
"about A/B Street" => {
|
||||
return Transition::Push(crate::ungap::About::new_state(ctx));
|
||||
}
|
||||
"change map" => {
|
||||
return Transition::Push(CityPicker::new_state(
|
||||
ctx,
|
||||
app,
|
||||
Box::new(|ctx, app| {
|
||||
Transition::Multi(vec![
|
||||
Transition::Pop,
|
||||
// Since we're totally changing maps, don't reuse the Layers
|
||||
// TODO Should we keep the current tab or always reset here?
|
||||
Transition::Replace(crate::ungap::ExploreMap::launch(ctx, app)),
|
||||
])
|
||||
}),
|
||||
));
|
||||
}
|
||||
"Explore" => {
|
||||
return Transition::ConsumeState(Box::new(|state, ctx, app| {
|
||||
let state = state.downcast::<RoutePlanner>().ok().unwrap();
|
||||
vec![crate::ungap::ExploreMap::new_state(ctx, app, state.layers)]
|
||||
}));
|
||||
}
|
||||
"Create new bike lanes" => {
|
||||
return Transition::ConsumeState(Box::new(|state, ctx, app| {
|
||||
let state = state.downcast::<RoutePlanner>().ok().unwrap();
|
||||
vec![crate::ungap::quick_sketch::QuickSketch::new_state(
|
||||
ctx,
|
||||
app,
|
||||
state.layers,
|
||||
)]
|
||||
}));
|
||||
}
|
||||
"Add waypoint" => {
|
||||
self.waypoints.push(self.make_new_waypt(ctx, app));
|
||||
self.update_input_panel(ctx, app);
|
||||
@ -399,7 +371,7 @@ impl State<App> for RoutePlanner {
|
||||
self.update_waypoints_drawable(ctx);
|
||||
self.update_route(ctx, app);
|
||||
} else {
|
||||
unreachable!()
|
||||
return Tab::Route.handle_action::<RoutePlanner>(ctx, app, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ pub struct CityPicker<A: AppLike> {
|
||||
impl<A: AppLike + 'static> CityPicker<A> {
|
||||
pub fn new_state(
|
||||
ctx: &mut EventCtx,
|
||||
app: &mut A,
|
||||
app: &A,
|
||||
on_load: Box<dyn FnOnce(&mut EventCtx, &mut A) -> Transition<A>>,
|
||||
) -> Box<dyn State<A>> {
|
||||
let city = app.map().get_city_name().clone();
|
||||
|
Loading…
Reference in New Issue
Block a user