mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-29 04:35:51 +03:00
refactor menu text for all ItemSliders
This commit is contained in:
parent
22db36c0f4
commit
cdacaef1a6
@ -7,7 +7,6 @@ use map_model::BusStopID;
|
||||
|
||||
pub struct BusRouteExplorer {
|
||||
slider: WarpingItemSlider<BusStopID>,
|
||||
route_name: String,
|
||||
}
|
||||
|
||||
impl BusRouteExplorer {
|
||||
@ -24,18 +23,22 @@ impl BusRouteExplorer {
|
||||
return None;
|
||||
}
|
||||
|
||||
let stops: Vec<(Pt2D, BusStopID)> = route
|
||||
let stops: Vec<(Pt2D, BusStopID, Text)> = route
|
||||
.stops
|
||||
.iter()
|
||||
.map(|bs| {
|
||||
let stop = map.get_bs(*bs);
|
||||
(stop.sidewalk_pos.pt(map), stop.id)
|
||||
(stop.sidewalk_pos.pt(map), stop.id, Text::new())
|
||||
})
|
||||
.collect();
|
||||
|
||||
Some(BusRouteExplorer {
|
||||
route_name: route.name.clone(),
|
||||
slider: WarpingItemSlider::new(stops, "Bus Route Explorer", "stop", ctx),
|
||||
slider: WarpingItemSlider::new(
|
||||
stops,
|
||||
&format!("Bus Route Explorer for {}", route.name),
|
||||
"stop",
|
||||
ctx,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
@ -52,14 +55,9 @@ impl BusRouteExplorer {
|
||||
}
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
let (idx, stop_id) = self.slider.get();
|
||||
let stop_id = *stop_id;
|
||||
let mut txt = Text::prompt(&format!("Bus Route Explorer for {:?}", self.route_name));
|
||||
txt.add_line(format!("Step {}/{}", idx + 1, self.slider.len()));
|
||||
|
||||
let (evmode, done_warping) = self.slider.event(ctx, Some(txt))?;
|
||||
let (evmode, done_warping) = self.slider.event(ctx)?;
|
||||
if done_warping {
|
||||
ui.primary.current_selection = Some(ID::BusStop(stop_id));
|
||||
ui.primary.current_selection = Some(ID::BusStop(*self.slider.get().1));
|
||||
}
|
||||
Some(evmode)
|
||||
}
|
||||
|
@ -30,7 +30,9 @@ impl PolygonDebugger {
|
||||
pts_without_last.pop();
|
||||
return Some(PolygonDebugger {
|
||||
slider: ItemSlider::new(
|
||||
pts.iter().map(|pt| Item::Point(*pt)).collect(),
|
||||
pts.iter()
|
||||
.map(|pt| (Item::Point(*pt), Text::new()))
|
||||
.collect(),
|
||||
"Polygon Debugger",
|
||||
"point",
|
||||
vec![(hotkey(Key::Escape), "quit")],
|
||||
@ -50,7 +52,7 @@ impl PolygonDebugger {
|
||||
&mut Timer::new("calculate corners"),
|
||||
)
|
||||
.into_iter()
|
||||
.map(Item::Polygon)
|
||||
.map(|poly| (Item::Polygon(poly), Text::new()))
|
||||
.collect(),
|
||||
"Polygon Debugger",
|
||||
"corner",
|
||||
@ -71,7 +73,7 @@ impl PolygonDebugger {
|
||||
.lane_center_pts
|
||||
.points()
|
||||
.iter()
|
||||
.map(|pt| Item::Point(*pt))
|
||||
.map(|pt| (Item::Point(*pt), Text::new()))
|
||||
.collect(),
|
||||
"Polygon Debugger",
|
||||
"point",
|
||||
@ -89,7 +91,7 @@ impl PolygonDebugger {
|
||||
.polygon
|
||||
.triangles()
|
||||
.into_iter()
|
||||
.map(Item::Triangle)
|
||||
.map(|tri| (Item::Triangle(tri), Text::new()))
|
||||
.collect(),
|
||||
"Polygon Debugger",
|
||||
"triangle",
|
||||
@ -112,7 +114,9 @@ impl PolygonDebugger {
|
||||
};
|
||||
return Some(PolygonDebugger {
|
||||
slider: ItemSlider::new(
|
||||
pts.iter().map(|pt| Item::Point(*pt)).collect(),
|
||||
pts.iter()
|
||||
.map(|pt| (Item::Point(*pt), Text::new()))
|
||||
.collect(),
|
||||
"Polygon Debugger",
|
||||
"point",
|
||||
vec![(hotkey(Key::Escape), "quit")],
|
||||
@ -129,7 +133,7 @@ impl PolygonDebugger {
|
||||
.polygon
|
||||
.triangles()
|
||||
.into_iter()
|
||||
.map(Item::Triangle)
|
||||
.map(|tri| (Item::Triangle(tri), Text::new()))
|
||||
.collect(),
|
||||
"Polygon Debugger",
|
||||
"triangle",
|
||||
@ -147,10 +151,7 @@ impl PolygonDebugger {
|
||||
|
||||
// True when done
|
||||
pub fn event(&mut self, ctx: &mut EventCtx) -> bool {
|
||||
let (idx, _) = self.slider.get();
|
||||
let mut txt = Text::prompt("Polygon Debugger");
|
||||
txt.add_line(format!("Item {}/{}", idx + 1, self.slider.len()));
|
||||
self.slider.event(ctx, Some(txt));
|
||||
self.slider.event(ctx);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
if self.slider.action("quit") {
|
||||
|
@ -2,7 +2,6 @@ use crate::common::CommonState;
|
||||
use crate::helpers::ID;
|
||||
use crate::mission::trips::{clip_trips, Trip, TripEndpt};
|
||||
use crate::ui::{ShowEverything, UI};
|
||||
use abstutil::prettyprint_usize;
|
||||
use ezgui::{hotkey, Color, EventCtx, GfxCtx, ItemSlider, Key, Text};
|
||||
use geom::{Circle, Distance, Line, Speed};
|
||||
use map_model::BuildingID;
|
||||
@ -18,7 +17,29 @@ impl TripsVisualizer {
|
||||
pub fn new(ctx: &mut EventCtx, ui: &UI) -> TripsVisualizer {
|
||||
let (trips, bldgs) = ctx.loading_screen("load trip data", |_, mut timer| {
|
||||
// TODO We'll break if there are no matching trips
|
||||
clip_trips(ui, &mut timer)
|
||||
let (trips, bldgs) = clip_trips(ui, &mut timer);
|
||||
(
|
||||
trips
|
||||
.into_iter()
|
||||
.map(|trip| {
|
||||
let mut txt = Text::new();
|
||||
txt.add_line(format!("Leave at {}", trip.depart_at));
|
||||
txt.add_line(format!(
|
||||
"Purpose: {:?} -> {:?}",
|
||||
trip.purpose.0, trip.purpose.1
|
||||
));
|
||||
txt.add_line(format!("Mode: {:?}", trip.mode));
|
||||
txt.add_line(format!("Trip time: {}", trip.trip_time));
|
||||
txt.add_line(format!("Trip distance: {}", trip.trip_dist));
|
||||
txt.add_line(format!(
|
||||
"Average speed {}",
|
||||
Speed::from_dist_time(trip.trip_dist, trip.trip_time)
|
||||
));
|
||||
(trip, txt)
|
||||
})
|
||||
.collect(),
|
||||
bldgs,
|
||||
)
|
||||
});
|
||||
TripsVisualizer {
|
||||
slider: ItemSlider::new(
|
||||
@ -34,27 +55,7 @@ impl TripsVisualizer {
|
||||
|
||||
// Returns true if the we're done
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> bool {
|
||||
let (idx, trip) = self.slider.get();
|
||||
let mut txt = Text::prompt("Trips Visualizer");
|
||||
txt.add_line(format!(
|
||||
"Trip {}/{}",
|
||||
prettyprint_usize(idx + 1),
|
||||
prettyprint_usize(self.slider.len())
|
||||
));
|
||||
txt.add_line(format!("Leave at {}", trip.depart_at));
|
||||
txt.add_line(format!(
|
||||
"Purpose: {:?} -> {:?}",
|
||||
trip.purpose.0, trip.purpose.1
|
||||
));
|
||||
txt.add_line(format!("Mode: {:?}", trip.mode));
|
||||
txt.add_line(format!("Trip time: {}", trip.trip_time));
|
||||
txt.add_line(format!("Trip distance: {}", trip.trip_dist));
|
||||
txt.add_line(format!(
|
||||
"Average speed {}",
|
||||
Speed::from_dist_time(trip.trip_dist, trip.trip_time)
|
||||
));
|
||||
|
||||
self.slider.event(ctx, Some(txt));
|
||||
self.slider.event(ctx);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
if ctx.redo_mouseover() {
|
||||
|
@ -8,7 +8,6 @@ use sim::AgentID;
|
||||
|
||||
pub struct RouteExplorer {
|
||||
slider: WarpingItemSlider<Traversable>,
|
||||
agent: AgentID,
|
||||
entire_trace: Option<Polygon>,
|
||||
}
|
||||
|
||||
@ -40,7 +39,7 @@ impl RouteExplorer {
|
||||
.trace(&ui.primary.map, Distance::ZERO, None)
|
||||
.map(|pl| pl.make_polygons(LANE_THICKNESS));
|
||||
|
||||
let steps: Vec<(Pt2D, Traversable)> = path
|
||||
let steps: Vec<(Pt2D, Traversable, Text)> = path
|
||||
.get_steps()
|
||||
.iter()
|
||||
.map(|step| {
|
||||
@ -49,12 +48,17 @@ impl RouteExplorer {
|
||||
t.dist_along(t.length(&ui.primary.map) / 2.0, &ui.primary.map)
|
||||
.0,
|
||||
t,
|
||||
Text::from_line(format!("{:?}", t)),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
Some(RouteExplorer {
|
||||
agent,
|
||||
slider: WarpingItemSlider::new(steps, "Route Explorer", "step", ctx),
|
||||
slider: WarpingItemSlider::new(
|
||||
steps,
|
||||
&format!("Route Explorer for {}", agent),
|
||||
"step",
|
||||
ctx,
|
||||
),
|
||||
entire_trace,
|
||||
})
|
||||
}
|
||||
@ -72,15 +76,9 @@ impl RouteExplorer {
|
||||
}
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
let (idx, step) = self.slider.get();
|
||||
let step = *step;
|
||||
let mut txt = Text::prompt(&format!("Route Explorer for {:?}", self.agent));
|
||||
txt.add_line(format!("Step {}/{}", idx + 1, self.slider.len()));
|
||||
txt.add_line(format!("{:?}", step));
|
||||
|
||||
// We don't really care about setting current_selection to the current step; drawing covers
|
||||
// it up anyway.
|
||||
self.slider.event(ctx, Some(txt)).map(|(evmode, _)| evmode)
|
||||
self.slider.event(ctx).map(|(evmode, _)| evmode)
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
|
@ -58,7 +58,7 @@ impl Scoreboard {
|
||||
}
|
||||
}
|
||||
Scoreboard::BrowseTrips(ref trips, ref mut wizard) => {
|
||||
if let Some(_) = pick_trip(trips, &mut wizard.wrap(ctx)) {
|
||||
if pick_trip(trips, &mut wizard.wrap(ctx)).is_some() {
|
||||
// TODO show trip departure, where it started and ended
|
||||
*self = Scoreboard::new(ctx, ui);
|
||||
} else if wizard.aborted() {
|
||||
|
@ -10,7 +10,7 @@ pub enum TimeTravel {
|
||||
Active(ItemSlider<StateAtTime>),
|
||||
Inactive {
|
||||
should_record: bool,
|
||||
moments: Vec<StateAtTime>,
|
||||
moments: Vec<(StateAtTime, Text)>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ impl TimeTravel {
|
||||
let sim = &ui.primary.sim;
|
||||
let now = sim.time();
|
||||
|
||||
if let Some(ref state) = moments.last() {
|
||||
if let Some((ref state, _)) = moments.last() {
|
||||
// Already have this
|
||||
if now == state.time {
|
||||
return;
|
||||
@ -101,7 +101,8 @@ impl TimeTravel {
|
||||
state.peds_per_traversable.insert(draw.on, draw.id);
|
||||
state.peds.insert(draw.id, draw);
|
||||
}
|
||||
moments.push(state);
|
||||
let label = Text::from_line(state.time.to_string());
|
||||
moments.push((state, label));
|
||||
}
|
||||
TimeTravel::Active(_) => unreachable!(),
|
||||
}
|
||||
@ -112,11 +113,7 @@ impl TimeTravel {
|
||||
match self {
|
||||
TimeTravel::Inactive { .. } => unreachable!(),
|
||||
TimeTravel::Active(ref mut slider) => {
|
||||
let (idx, state) = slider.get();
|
||||
let mut txt = Text::prompt("Time Traveler");
|
||||
txt.add_line(format!("{}", state.time));
|
||||
txt.add_line(format!("{}/{}", idx + 1, slider.len()));
|
||||
slider.event(ctx, Some(txt));
|
||||
slider.event(ctx);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
if slider.action("quit") {
|
||||
|
@ -140,6 +140,10 @@ impl Text {
|
||||
self.lines.len()
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, other: &Text) {
|
||||
self.lines.extend(other.lines.clone())
|
||||
}
|
||||
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.lines.is_empty()
|
||||
}
|
||||
|
@ -171,9 +171,10 @@ impl Slider {
|
||||
}
|
||||
|
||||
pub struct ItemSlider<T> {
|
||||
items: Vec<T>,
|
||||
items: Vec<(T, Text)>,
|
||||
slider: Slider,
|
||||
menu: ModalMenu,
|
||||
menu_title: String,
|
||||
|
||||
prev: String,
|
||||
next: String,
|
||||
@ -183,7 +184,7 @@ pub struct ItemSlider<T> {
|
||||
|
||||
impl<T> ItemSlider<T> {
|
||||
pub fn new(
|
||||
items: Vec<T>,
|
||||
items: Vec<(T, Text)>,
|
||||
menu_title: &str,
|
||||
noun: &str,
|
||||
other_choices: Vec<(Option<MultiKey>, &str)>,
|
||||
@ -207,6 +208,7 @@ impl<T> ItemSlider<T> {
|
||||
items,
|
||||
slider: Slider::new(None),
|
||||
menu: ModalMenu::new(menu_title, choices, ctx),
|
||||
menu_title: menu_title.to_string(),
|
||||
|
||||
prev,
|
||||
next,
|
||||
@ -216,10 +218,19 @@ impl<T> ItemSlider<T> {
|
||||
}
|
||||
|
||||
// Returns true if the value changed.
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, menu_prompt: Option<Text>) -> bool {
|
||||
let current = self.slider.get_value(self.items.len());
|
||||
|
||||
self.menu.handle_event(ctx, menu_prompt);
|
||||
pub fn event(&mut self, ctx: &mut EventCtx) -> bool {
|
||||
let current = {
|
||||
let idx = self.slider.get_value(self.items.len());
|
||||
let mut txt = Text::prompt(&self.menu_title);
|
||||
txt.add_line(format!(
|
||||
"{}/{}",
|
||||
abstutil::prettyprint_usize(idx + 1),
|
||||
abstutil::prettyprint_usize(self.items.len())
|
||||
));
|
||||
txt.extend(&self.items[idx].1);
|
||||
self.menu.handle_event(ctx, Some(txt));
|
||||
idx
|
||||
};
|
||||
|
||||
if current != self.items.len() - 1 && self.menu.action(&self.next) {
|
||||
self.slider.set_value(ctx, current + 1, self.items.len());
|
||||
@ -243,11 +254,7 @@ impl<T> ItemSlider<T> {
|
||||
|
||||
pub fn get(&self) -> (usize, &T) {
|
||||
let idx = self.slider.get_value(self.items.len());
|
||||
(idx, &self.items[idx])
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.len()
|
||||
(idx, &self.items[idx].0)
|
||||
}
|
||||
|
||||
pub fn action(&mut self, name: &str) -> bool {
|
||||
@ -255,7 +262,7 @@ impl<T> ItemSlider<T> {
|
||||
}
|
||||
|
||||
// TODO Consume self
|
||||
pub fn consume_all_items(&mut self) -> Vec<T> {
|
||||
pub fn consume_all_items(&mut self) -> Vec<(T, Text)> {
|
||||
std::mem::replace(&mut self.items, Vec::new())
|
||||
}
|
||||
}
|
||||
@ -268,7 +275,7 @@ pub struct WarpingItemSlider<T> {
|
||||
impl<T> WarpingItemSlider<T> {
|
||||
// Note other_choices is hardcoded to quitting.
|
||||
pub fn new(
|
||||
items: Vec<(Pt2D, T)>,
|
||||
items: Vec<(Pt2D, T, Text)>,
|
||||
menu_title: &str,
|
||||
noun: &str,
|
||||
ctx: &mut EventCtx,
|
||||
@ -276,7 +283,10 @@ impl<T> WarpingItemSlider<T> {
|
||||
WarpingItemSlider {
|
||||
warper: Some(Warper::new(ctx, items[0].0)),
|
||||
slider: ItemSlider::new(
|
||||
items,
|
||||
items
|
||||
.into_iter()
|
||||
.map(|(pt, obj, label)| ((pt, obj), label))
|
||||
.collect(),
|
||||
menu_title,
|
||||
noun,
|
||||
vec![(hotkey(Key::Escape), "quit")],
|
||||
@ -286,11 +296,7 @@ impl<T> WarpingItemSlider<T> {
|
||||
}
|
||||
|
||||
// Done when None. If the bool is true, done warping.
|
||||
pub fn event(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
menu_prompt: Option<Text>,
|
||||
) -> Option<(EventLoopMode, bool)> {
|
||||
pub fn event(&mut self, ctx: &mut EventCtx) -> Option<(EventLoopMode, bool)> {
|
||||
// Don't block while we're warping
|
||||
let (ev_mode, done_warping) = if let Some(ref warper) = self.warper {
|
||||
if let Some(mode) = warper.event(ctx) {
|
||||
@ -303,7 +309,7 @@ impl<T> WarpingItemSlider<T> {
|
||||
(EventLoopMode::InputOnly, false)
|
||||
};
|
||||
|
||||
let changed = self.slider.event(ctx, menu_prompt);
|
||||
let changed = self.slider.event(ctx);
|
||||
|
||||
if self.slider.action("quit") {
|
||||
return None;
|
||||
@ -325,8 +331,4 @@ impl<T> WarpingItemSlider<T> {
|
||||
let (idx, (_, data)) = self.slider.get();
|
||||
(idx, data)
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.slider.len()
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ impl GUI for UI {
|
||||
}
|
||||
};
|
||||
let pt = Pt2D::from_gps(gps_pt, &self.raw.gps_bounds)?;
|
||||
Some((pt, h.clone()))
|
||||
Some((pt, h.clone(), Text::from_line(describe(h))))
|
||||
})
|
||||
.collect(),
|
||||
"Hints Browser",
|
||||
@ -244,13 +244,7 @@ impl GUI for UI {
|
||||
}
|
||||
State::BrowsingHints(ref mut slider) => {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
let mut txt = Text::prompt("Hints Browser");
|
||||
{
|
||||
let (idx, hint) = slider.get();
|
||||
txt.add_line(format!("Hint {}/{}", idx + 1, slider.len()));
|
||||
txt.add_line(describe(hint));
|
||||
}
|
||||
if let Some((evmode, _)) = slider.event(ctx, Some(txt)) {
|
||||
if let Some((evmode, _)) = slider.event(ctx) {
|
||||
evmode
|
||||
} else {
|
||||
self.state = State::main(ctx);
|
||||
|
@ -60,11 +60,15 @@ impl SidewalkPathfinder {
|
||||
|
||||
// Connect each adjacent stop along a route, again with a "free" cost.
|
||||
for route in map.get_all_bus_routes() {
|
||||
for (stop1, stop2) in route
|
||||
.stops
|
||||
.iter()
|
||||
.zip(route.stops.iter().skip(1))
|
||||
.chain(std::iter::once((route.stops.last().unwrap(), &route.stops[0])))
|
||||
for (stop1, stop2) in
|
||||
route
|
||||
.stops
|
||||
.iter()
|
||||
.zip(route.stops.iter().skip(1))
|
||||
.chain(std::iter::once((
|
||||
route.stops.last().unwrap(),
|
||||
&route.stops[0],
|
||||
)))
|
||||
{
|
||||
input_graph.add_edge(
|
||||
nodes.get(Node::RideBus(*stop1)),
|
||||
@ -76,14 +80,13 @@ impl SidewalkPathfinder {
|
||||
}
|
||||
}
|
||||
input_graph.freeze();
|
||||
println!(
|
||||
"{} nodes, {} edges",
|
||||
abstutil::prettyprint_usize(input_graph.get_num_nodes()),
|
||||
abstutil::prettyprint_usize(input_graph.get_num_edges())
|
||||
);
|
||||
let graph = fast_paths::prepare(&input_graph);
|
||||
|
||||
SidewalkPathfinder { graph, nodes, connections }
|
||||
SidewalkPathfinder {
|
||||
graph,
|
||||
nodes,
|
||||
connections,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pathfind(&self, req: &PathRequest, map: &Map) -> Option<Path> {
|
||||
@ -176,11 +179,8 @@ impl SidewalkPathfinder {
|
||||
)?;
|
||||
|
||||
for pair in self.nodes.translate(&raw_path).windows(2) {
|
||||
match (pair[0], pair[1]) {
|
||||
(Node::RideBus(stop1), Node::RideBus(stop2)) => {
|
||||
return Some((stop1, stop2, self.connections[&(stop1, stop2)]));
|
||||
}
|
||||
_ => {}
|
||||
if let (Node::RideBus(stop1), Node::RideBus(stop2)) = (pair[0], pair[1]) {
|
||||
return Some((stop1, stop2, self.connections[&(stop1, stop2)]));
|
||||
}
|
||||
}
|
||||
None
|
||||
|
Loading…
Reference in New Issue
Block a user