mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 01:15:12 +03:00
beef up person schedule page. plumb markers like hyperlinks...
This commit is contained in:
parent
ce180f7228
commit
02a9a97bbc
@ -34,6 +34,7 @@ pub fn car_info(
|
||||
action_btns: Vec<Widget>,
|
||||
batch: &mut GeomBatch,
|
||||
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
|
||||
warpers: &mut HashMap<String, ID>,
|
||||
) -> (Vec<Widget>, Option<TripDetails>) {
|
||||
let mut rows = vec![];
|
||||
|
||||
@ -87,13 +88,22 @@ pub fn car_info(
|
||||
app,
|
||||
t,
|
||||
app.primary.sim.progress_along_path(AgentID::Car(id)),
|
||||
warpers,
|
||||
);
|
||||
rows.push(more);
|
||||
details
|
||||
});
|
||||
}
|
||||
InfoTab::Agent(Tab::Person(p)) => {
|
||||
rows.extend(person::info(ctx, app, p, None, Vec::new(), hyperlinks));
|
||||
rows.extend(person::info(
|
||||
ctx,
|
||||
app,
|
||||
p,
|
||||
None,
|
||||
Vec::new(),
|
||||
hyperlinks,
|
||||
warpers,
|
||||
));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@ -118,6 +128,7 @@ pub fn ped_info(
|
||||
header_btns: Widget,
|
||||
action_btns: Vec<Widget>,
|
||||
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
|
||||
warpers: &mut HashMap<String, ID>,
|
||||
) -> (Vec<Widget>, Option<TripDetails>) {
|
||||
let mut rows = vec![];
|
||||
|
||||
@ -172,12 +183,21 @@ pub fn ped_info(
|
||||
.agent_to_trip(AgentID::Pedestrian(id))
|
||||
.unwrap(),
|
||||
app.primary.sim.progress_along_path(AgentID::Pedestrian(id)),
|
||||
warpers,
|
||||
);
|
||||
rows.push(more);
|
||||
details = Some(trip_details);
|
||||
}
|
||||
InfoTab::Agent(Tab::Person(p)) => {
|
||||
rows.extend(person::info(ctx, app, p, None, Vec::new(), hyperlinks));
|
||||
rows.extend(person::info(
|
||||
ctx,
|
||||
app,
|
||||
p,
|
||||
None,
|
||||
Vec::new(),
|
||||
hyperlinks,
|
||||
warpers,
|
||||
));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@ -192,6 +212,7 @@ pub fn crowd_info(
|
||||
mut tab: InfoTab,
|
||||
header_btns: Widget,
|
||||
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
|
||||
warpers: &mut HashMap<String, ID>,
|
||||
) -> Vec<Widget> {
|
||||
let mut rows = vec![];
|
||||
|
||||
@ -230,6 +251,7 @@ pub fn crowd_info(
|
||||
Widget::nothing(),
|
||||
Vec::new(),
|
||||
hyperlinks,
|
||||
warpers,
|
||||
)
|
||||
.0,
|
||||
);
|
||||
|
@ -25,7 +25,7 @@ impl std::cmp::PartialEq for Tab {
|
||||
}
|
||||
|
||||
pub fn info(
|
||||
ctx: &EventCtx,
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
id: BuildingID,
|
||||
tab: InfoTab,
|
||||
@ -33,6 +33,7 @@ pub fn info(
|
||||
action_btns: Vec<Widget>,
|
||||
batch: &mut GeomBatch,
|
||||
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
|
||||
warpers: &mut HashMap<String, ID>,
|
||||
) -> Vec<Widget> {
|
||||
let mut rows = vec![];
|
||||
|
||||
@ -142,6 +143,7 @@ pub fn info(
|
||||
None,
|
||||
Vec::new(),
|
||||
hyperlinks,
|
||||
warpers,
|
||||
));
|
||||
rows.push(Widget::col(inner).bg(colors::INNER_PANEL_BG));
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ pub struct InfoPanel {
|
||||
|
||||
actions: Vec<(Key, String)>,
|
||||
hyperlinks: HashMap<String, (ID, InfoTab)>,
|
||||
warpers: HashMap<String, ID>,
|
||||
}
|
||||
|
||||
// TODO Safer to expand out ID cases here
|
||||
@ -51,7 +52,6 @@ pub struct TripDetails {
|
||||
id: TripID,
|
||||
unzoomed: Drawable,
|
||||
zoomed: Drawable,
|
||||
markers: HashMap<String, ID>,
|
||||
}
|
||||
|
||||
impl InfoPanel {
|
||||
@ -141,6 +141,7 @@ impl InfoPanel {
|
||||
])
|
||||
.align_right();
|
||||
let mut hyperlinks = HashMap::new();
|
||||
let mut warpers = HashMap::new();
|
||||
let (col, trip_details) = match id.clone() {
|
||||
ID::Road(_) => unreachable!(),
|
||||
ID::Lane(id) => (
|
||||
@ -178,6 +179,7 @@ impl InfoPanel {
|
||||
action_btns,
|
||||
&mut batch,
|
||||
&mut hyperlinks,
|
||||
&mut warpers,
|
||||
),
|
||||
None,
|
||||
),
|
||||
@ -190,6 +192,7 @@ impl InfoPanel {
|
||||
action_btns,
|
||||
&mut batch,
|
||||
&mut hyperlinks,
|
||||
&mut warpers,
|
||||
),
|
||||
ID::Pedestrian(id) => agents::ped_info(
|
||||
ctx,
|
||||
@ -199,6 +202,7 @@ impl InfoPanel {
|
||||
header_btns,
|
||||
action_btns,
|
||||
&mut hyperlinks,
|
||||
&mut warpers,
|
||||
),
|
||||
ID::PedCrowd(members) => {
|
||||
assert!(action_btns.is_empty());
|
||||
@ -210,6 +214,7 @@ impl InfoPanel {
|
||||
tab.clone(),
|
||||
header_btns,
|
||||
&mut hyperlinks,
|
||||
&mut warpers,
|
||||
),
|
||||
None,
|
||||
)
|
||||
@ -230,6 +235,7 @@ impl InfoPanel {
|
||||
Vec::new(),
|
||||
&mut batch,
|
||||
&mut hyperlinks,
|
||||
&mut warpers,
|
||||
),
|
||||
Some(AgentID::Pedestrian(p)) => agents::ped_info(
|
||||
ctx,
|
||||
@ -239,10 +245,17 @@ impl InfoPanel {
|
||||
header_btns,
|
||||
Vec::new(),
|
||||
&mut hyperlinks,
|
||||
&mut warpers,
|
||||
),
|
||||
None => trip::inactive_info(
|
||||
ctx,
|
||||
app,
|
||||
id,
|
||||
tab.clone(),
|
||||
action_btns,
|
||||
&mut hyperlinks,
|
||||
&mut warpers,
|
||||
),
|
||||
None => {
|
||||
trip::inactive_info(ctx, app, id, tab.clone(), action_btns, &mut hyperlinks)
|
||||
}
|
||||
},
|
||||
ID::Person(id) => (
|
||||
person::info(
|
||||
@ -252,6 +265,7 @@ impl InfoPanel {
|
||||
Some(header_btns),
|
||||
action_btns,
|
||||
&mut hyperlinks,
|
||||
&mut warpers,
|
||||
),
|
||||
None,
|
||||
),
|
||||
@ -281,6 +295,7 @@ impl InfoPanel {
|
||||
.build(ctx),
|
||||
also_draw: batch.upload(ctx),
|
||||
hyperlinks,
|
||||
warpers,
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,14 +419,10 @@ impl InfoPanel {
|
||||
} else if action == "follow agent" {
|
||||
maybe_speed.unwrap().resume_realtime(ctx);
|
||||
(false, None)
|
||||
} else if let Some(_) = strip_prefix_usize(&action, "examine trip phase ") {
|
||||
} else if action.starts_with("examine trip phase") {
|
||||
// Don't do anything! Just using buttons for convenient tooltips.
|
||||
(false, None)
|
||||
} else if let Some(id) = self
|
||||
.trip_details
|
||||
.as_ref()
|
||||
.and_then(|d| d.markers.get(&action))
|
||||
{
|
||||
} else if let Some(id) = self.warpers.get(&action) {
|
||||
(
|
||||
false,
|
||||
Some(Transition::Push(Warping::new(
|
||||
@ -555,16 +566,6 @@ fn agent_name(a: AgentID) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Can't easily use this in the other few cases, which use a match...
|
||||
fn strip_prefix_usize(x: &String, prefix: &str) -> Option<usize> {
|
||||
if x.starts_with(prefix) {
|
||||
// If it starts with the prefix, insist on there being a valid number there
|
||||
Some(x[prefix.len()..].parse::<usize>().unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn make_tabs(
|
||||
ctx: &EventCtx,
|
||||
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
|
||||
|
@ -1,23 +1,26 @@
|
||||
use crate::app::App;
|
||||
use crate::colors;
|
||||
use crate::helpers::ID;
|
||||
use crate::info::trip::trip_details;
|
||||
use crate::info::InfoTab;
|
||||
use ezgui::{Btn, EventCtx, Line, TextExt, Widget};
|
||||
use sim::{PersonID, PersonState};
|
||||
use ezgui::{EventCtx, Line, TextExt, Widget};
|
||||
use map_model::Map;
|
||||
use sim::{Person, PersonID, PersonState, TripResult};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn info(
|
||||
ctx: &EventCtx,
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
id: PersonID,
|
||||
// If None, then the panel is embedded
|
||||
header_btns: Option<Widget>,
|
||||
action_btns: Vec<Widget>,
|
||||
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
|
||||
warpers: &mut HashMap<String, ID>,
|
||||
) -> Vec<Widget> {
|
||||
let mut rows = vec![];
|
||||
|
||||
// Header
|
||||
let standalone = header_btns.is_some();
|
||||
if let Some(btns) = header_btns {
|
||||
rows.push(Widget::row(vec![
|
||||
Line(format!("Person #{}", id.0)).roboto_bold().draw(ctx),
|
||||
@ -26,51 +29,61 @@ pub fn info(
|
||||
} else {
|
||||
rows.push(Line(format!("Person #{}", id.0)).roboto_bold().draw(ctx));
|
||||
}
|
||||
// TODO None of these right now
|
||||
rows.extend(action_btns);
|
||||
|
||||
let person = app.primary.sim.get_person(id);
|
||||
let map = &app.primary.map;
|
||||
let sim = &app.primary.sim;
|
||||
let person = sim.get_person(id);
|
||||
|
||||
// TODO Redundant to say they're inside when the panel is embedded. But... if the person leaves
|
||||
// while we have the panel open, then it IS relevant.
|
||||
if standalone {
|
||||
// TODO Point out where the person is now, relative to schedule...
|
||||
rows.push(match person.state {
|
||||
PersonState::Inside(b) => {
|
||||
// TODO not the best tooltip, but disambiguous:(
|
||||
hyperlinks.insert(
|
||||
format!("examine Building #{}", b.0),
|
||||
(ID::Building(b), InfoTab::Nil),
|
||||
);
|
||||
Btn::text_bg1(format!(
|
||||
"Currently inside {}",
|
||||
app.primary.map.get_b(b).just_address(&app.primary.map)
|
||||
))
|
||||
.build(ctx, format!("examine Building #{}", b.0), None)
|
||||
}
|
||||
PersonState::Trip(t) => format!("Currently doing Trip #{}", t.0).draw_text(ctx),
|
||||
PersonState::OffMap => "Currently outside the map boundaries".draw_text(ctx),
|
||||
PersonState::Limbo => "Currently in limbo -- they broke out of the Matrix! Woops. (A \
|
||||
bug occurred)"
|
||||
.draw_text(ctx),
|
||||
});
|
||||
}
|
||||
|
||||
rows.push(Line("Schedule").roboto_bold().draw(ctx));
|
||||
// I'm sorry for bad variable names
|
||||
let mut wheres_waldo = true;
|
||||
for t in &person.trips {
|
||||
let (start_time, _, _, _) = app.primary.sim.trip_info(*t);
|
||||
hyperlinks.insert(
|
||||
format!("examine Trip #{}", t.0),
|
||||
(ID::Trip(*t), InfoTab::Nil),
|
||||
match sim.trip_to_agent(*t) {
|
||||
TripResult::TripNotStarted => {
|
||||
if wheres_waldo {
|
||||
wheres_waldo = false;
|
||||
rows.push(current_status(ctx, person, map));
|
||||
}
|
||||
}
|
||||
TripResult::Ok(_) | TripResult::ModeChange => {
|
||||
// ongoing
|
||||
assert!(wheres_waldo);
|
||||
wheres_waldo = false;
|
||||
}
|
||||
TripResult::TripDone => {
|
||||
assert!(wheres_waldo);
|
||||
}
|
||||
TripResult::TripDoesntExist => unreachable!(),
|
||||
}
|
||||
rows.push(
|
||||
Widget::col(vec![
|
||||
Line(format!("Trip #{}", t.0)).roboto_bold().draw(ctx),
|
||||
trip_details(ctx, app, *t, None, warpers).0,
|
||||
])
|
||||
.bg(colors::SECTION_BG)
|
||||
.margin(10),
|
||||
);
|
||||
rows.push(Widget::row(vec![
|
||||
format!("{}: ", start_time.ampm_tostring()).draw_text(ctx),
|
||||
Btn::text_bg1(format!("Trip #{}", t.0))
|
||||
.build(ctx, format!("examine Trip #{}", t.0), None)
|
||||
.margin(5),
|
||||
]));
|
||||
}
|
||||
if wheres_waldo {
|
||||
rows.push(current_status(ctx, person, map));
|
||||
}
|
||||
|
||||
// TODO All the colorful side info
|
||||
|
||||
rows
|
||||
}
|
||||
|
||||
fn current_status(ctx: &EventCtx, person: &Person, map: &Map) -> Widget {
|
||||
match person.state {
|
||||
PersonState::Inside(b) => {
|
||||
// TODO hyperlink
|
||||
format!("Currently inside {}", map.get_b(b).just_address(map)).draw_text(ctx)
|
||||
}
|
||||
PersonState::Trip(_) => unreachable!(),
|
||||
PersonState::OffMap => "Currently outside the map boundaries".draw_text(ctx),
|
||||
PersonState::Limbo => "Currently in limbo -- they broke out of the Matrix! Woops. (A bug \
|
||||
occurred)"
|
||||
.draw_text(ctx),
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ pub fn inactive_info(
|
||||
tab: InfoTab,
|
||||
action_btns: Vec<Widget>,
|
||||
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
|
||||
warpers: &mut HashMap<String, ID>,
|
||||
) -> (Vec<Widget>, Option<TripDetails>) {
|
||||
let mut rows = vec![];
|
||||
|
||||
@ -54,12 +55,20 @@ pub fn inactive_info(
|
||||
InfoTab::Nil => {
|
||||
rows.extend(action_btns);
|
||||
|
||||
let (more, trip_details) = trip_details(ctx, app, id, None);
|
||||
let (more, trip_details) = trip_details(ctx, app, id, None, warpers);
|
||||
rows.push(more);
|
||||
details = Some(trip_details);
|
||||
}
|
||||
InfoTab::Trip(Tab::Person(p)) => {
|
||||
rows.extend(person::info(ctx, app, p, None, Vec::new(), hyperlinks));
|
||||
rows.extend(person::info(
|
||||
ctx,
|
||||
app,
|
||||
p,
|
||||
None,
|
||||
Vec::new(),
|
||||
hyperlinks,
|
||||
warpers,
|
||||
));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@ -72,6 +81,7 @@ pub fn trip_details(
|
||||
app: &App,
|
||||
trip: TripID,
|
||||
progress_along_path: Option<f64>,
|
||||
warpers: &mut HashMap<String, ID>,
|
||||
) -> (Widget, TripDetails) {
|
||||
let map = &app.primary.map;
|
||||
let phases = app.primary.sim.get_analytics().get_trip_phases(trip, map);
|
||||
@ -79,15 +89,15 @@ pub fn trip_details(
|
||||
|
||||
let mut unzoomed = GeomBatch::new();
|
||||
let mut zoomed = GeomBatch::new();
|
||||
let mut markers = HashMap::new();
|
||||
|
||||
if phases.is_empty() {
|
||||
// The trip hasn't started
|
||||
let kv = vec![
|
||||
("Trip start", start_time.ampm_tostring()),
|
||||
("Departure", start_time.ampm_tostring()),
|
||||
("Type", trip_mode.to_string()),
|
||||
// TODO If we're looking at a building, then "here"...
|
||||
// TODO Buttons
|
||||
// TODO Should we explicitly print this for the other case, instead of the icons?
|
||||
("From", endpoint(&trip_start, map).2),
|
||||
("To", endpoint(&trip_end, map).2),
|
||||
];
|
||||
@ -97,14 +107,13 @@ pub fn trip_details(
|
||||
id: trip,
|
||||
unzoomed: unzoomed.upload(ctx),
|
||||
zoomed: zoomed.upload(ctx),
|
||||
markers,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let start_btn = {
|
||||
let (id, center, name) = endpoint(&trip_start, map);
|
||||
markers.insert("jump to start".to_string(), id);
|
||||
warpers.insert(format!("jump to start of Trip #{}", trip.0), id);
|
||||
unzoomed.add_svg(
|
||||
ctx.prerender,
|
||||
"../data/system/assets/timeline/start_pos.svg",
|
||||
@ -127,14 +136,14 @@ pub fn trip_details(
|
||||
RewriteColor::Change(Color::WHITE, colors::HOVERING),
|
||||
)
|
||||
.tooltip(txt)
|
||||
.build(ctx, "jump to start", None)
|
||||
.build(ctx, format!("jump to start of Trip #{}", trip.0), None)
|
||||
};
|
||||
|
||||
let end_time = phases.last().as_ref().and_then(|p| p.end_time);
|
||||
|
||||
let goal_btn = {
|
||||
let (id, center, name) = endpoint(&trip_end, map);
|
||||
markers.insert("jump to goal".to_string(), id);
|
||||
warpers.insert(format!("jump to goal of Trip #{}", trip.0), id);
|
||||
unzoomed.add_svg(
|
||||
ctx.prerender,
|
||||
"../data/system/assets/timeline/goal_pos.svg",
|
||||
@ -159,7 +168,7 @@ pub fn trip_details(
|
||||
RewriteColor::Change(Color::WHITE, colors::HOVERING),
|
||||
)
|
||||
.tooltip(txt)
|
||||
.build(ctx, "jump to goal", None)
|
||||
.build(ctx, format!("jump to goal of Trip #{}", trip.0), None)
|
||||
};
|
||||
|
||||
let total_duration_so_far =
|
||||
@ -247,7 +256,11 @@ pub fn trip_details(
|
||||
timeline.push(
|
||||
Btn::custom(normal, hovered, rect)
|
||||
.tooltip(txt)
|
||||
.build(ctx, format!("examine trip phase {}", idx + 1), None)
|
||||
.build(
|
||||
ctx,
|
||||
format!("examine trip phase {} of Trip #{}", idx + 1, trip.0),
|
||||
None,
|
||||
)
|
||||
.centered_vert(),
|
||||
);
|
||||
|
||||
@ -300,7 +313,6 @@ pub fn trip_details(
|
||||
id: trip,
|
||||
unzoomed: unzoomed.upload(ctx),
|
||||
zoomed: zoomed.upload(ctx),
|
||||
markers,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user