continuing with some of the harder info tabs. got everything moved over!

This commit is contained in:
Dustin Carlino 2020-03-27 11:41:56 -07:00
parent fff8c8ae9d
commit 49870c0f9f
6 changed files with 411 additions and 664 deletions

View File

@ -1,270 +0,0 @@
use crate::app::App;
use crate::colors;
use crate::helpers::ID;
use crate::info::trip::trip_details;
use crate::info::{make_browser, make_table, make_tabs, person, InfoTab, TripDetails};
use crate::render::Renderable;
use ezgui::{Color, EventCtx, GeomBatch, Line, Text, Widget};
use sim::{AgentID, CarID, PedestrianID, PersonID, VehicleType};
use std::collections::HashMap;
#[derive(Clone)]
pub enum Tab {
Person(PersonID),
// The crowd could change as we go; just freeze the list.
Crowd(Vec<PedestrianID>, usize),
}
impl std::cmp::PartialEq for Tab {
fn eq(&self, other: &Tab) -> bool {
match (self, other) {
// Only one possibility per ID
(Tab::Person(_), Tab::Person(_)) => true,
(Tab::Crowd(_, _), Tab::Crowd(_, _)) => true,
_ => false,
}
}
}
pub fn car_info(
ctx: &mut EventCtx,
app: &App,
id: CarID,
tab: InfoTab,
header_btns: Widget,
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![];
let label = match id.1 {
VehicleType::Car => "Car",
VehicleType::Bike => "Bike",
VehicleType::Bus => "Bus",
};
rows.push(Widget::row(vec![
Line(format!("{} #{}", label, id.0))
.small_heading()
.draw(ctx),
header_btns,
]));
rows.push(make_tabs(ctx, hyperlinks, ID::Car(id), tab.clone(), {
let mut tabs = vec![("Info", InfoTab::Nil)];
if let Some(p) = app
.primary
.sim
.agent_to_trip(AgentID::Car(id))
.map(|t| app.primary.sim.trip_to_person(t))
{
tabs.push(("Trips", InfoTab::Agent(Tab::Person(p))));
}
tabs
}));
let mut details: Option<TripDetails> = None;
match tab {
InfoTab::Nil => {
rows.extend(action_btns);
let (kv, extra) = app.primary.sim.car_properties(id, &app.primary.map);
rows.extend(make_table(ctx, kv));
if !extra.is_empty() {
let mut txt = Text::from(Line(""));
for line in extra {
txt.add(Line(line));
}
rows.push(txt.draw(ctx));
}
let trip = if id.1 == VehicleType::Bus {
None
} else {
app.primary.sim.agent_to_trip(AgentID::Car(id))
};
details = trip.map(|t| {
let (more, details) = trip_details(
ctx,
app,
t,
app.primary.sim.progress_along_path(AgentID::Car(id)),
warpers,
);
rows.push(more);
details
});
}
InfoTab::Agent(Tab::Person(p)) => {
// TODO Reorganize this
rows.extend(person::info(
ctx,
app,
p,
InfoTab::Nil,
None,
Vec::new(),
hyperlinks,
warpers,
));
}
_ => unreachable!(),
}
if let Some(b) = app.primary.sim.get_owner_of_car(id) {
// TODO Mention this, with a warp tool
batch.push(
app.cs
.get_def("something associated with something else", Color::PURPLE),
app.primary.draw_map.get_b(b).get_outline(&app.primary.map),
);
}
(rows, details)
}
pub fn ped_info(
ctx: &mut EventCtx,
app: &App,
id: PedestrianID,
tab: InfoTab,
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![];
rows.push(Widget::row(vec![
Line(format!("Pedestrian #{}", id.0))
.small_heading()
.draw(ctx),
header_btns,
]));
let trip = app
.primary
.sim
.agent_to_trip(AgentID::Pedestrian(id))
.unwrap();
rows.push(make_tabs(
ctx,
hyperlinks,
ID::Pedestrian(id),
tab.clone(),
vec![
("Info", InfoTab::Nil),
(
"Trips",
InfoTab::Agent(Tab::Person(app.primary.sim.trip_to_person(trip))),
),
],
));
let mut details: Option<TripDetails> = None;
match tab {
InfoTab::Nil => {
rows.extend(action_btns);
let (kv, extra) = app.primary.sim.ped_properties(id, &app.primary.map);
rows.extend(make_table(ctx, kv));
if !extra.is_empty() {
let mut txt = Text::from(Line(""));
for line in extra {
txt.add(Line(line));
}
rows.push(txt.draw(ctx));
}
let (more, trip_details) = trip_details(
ctx,
app,
app.primary
.sim
.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)) => {
// TODO Reorganize this
rows.extend(person::info(
ctx,
app,
p,
InfoTab::Nil,
None,
Vec::new(),
hyperlinks,
warpers,
));
}
_ => unreachable!(),
}
(rows, details)
}
pub fn crowd_info(
ctx: &mut EventCtx,
app: &App,
members: Vec<PedestrianID>,
mut tab: InfoTab,
header_btns: Widget,
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
warpers: &mut HashMap<String, ID>,
) -> Vec<Widget> {
let mut rows = vec![];
rows.push(Widget::row(vec![
Line("Pedestrian crowd").small_heading().draw(ctx),
header_btns,
]));
if tab == InfoTab::Nil {
tab = InfoTab::Agent(Tab::Crowd(members, 0));
}
match tab {
InfoTab::Agent(Tab::Crowd(peds, idx)) => {
let mut inner = vec![make_browser(
ctx,
hyperlinks,
"Pedestrian",
peds.len(),
idx,
|n| {
(
ID::PedCrowd(peds.clone()),
InfoTab::Agent(Tab::Crowd(peds.clone(), n)),
)
},
)];
// If we click a tab for a pedestrian, we lose the crowd. Woops?
inner.extend(
ped_info(
ctx,
app,
peds[idx],
InfoTab::Nil,
// No header buttons
Widget::nothing(),
Vec::new(),
hyperlinks,
warpers,
)
.0,
);
rows.push(Widget::col(inner).bg(colors::INNER_PANEL_BG));
}
_ => unreachable!(),
}
rows
}

View File

@ -1,51 +1,14 @@
use crate::app::App; use crate::app::App;
use crate::helpers::ID; use crate::helpers::ID;
use crate::info::{make_table, make_tabs, person, InfoTab}; use crate::info::{header_btns, make_table, make_tabs, Details, Tab};
use ezgui::{EventCtx, GeomBatch, Line, Text, TextExt, Widget}; use ezgui::{Btn, EventCtx, Line, Text, TextExt, Widget};
use geom::Time;
use map_model::BuildingID; use map_model::BuildingID;
use sim::TripEndpoint; use sim::{TripEndpoint, TripMode, TripResult};
use std::collections::HashMap;
#[derive(Clone, PartialEq)]
pub enum Tab {
// If we're live updating, the people inside could change! Re-calculate constantly.
People,
Debug,
}
pub fn info(
ctx: &mut EventCtx,
app: &App,
id: BuildingID,
tab: InfoTab,
header_btns: Widget,
action_btns: Vec<Widget>,
batch: &mut GeomBatch,
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
) -> Vec<Widget> {
let mut rows = vec![];
pub fn info(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BuildingID) -> Vec<Widget> {
let mut rows = header(ctx, app, details, id, Tab::BldgInfo(id));
let b = app.primary.map.get_b(id); let b = app.primary.map.get_b(id);
let ppl = app.primary.sim.bldg_to_people(id);
rows.push(Widget::row(vec![
Line(format!("Building #{}", id.0))
.small_heading()
.draw(ctx),
header_btns,
]));
rows.push(make_tabs(ctx, hyperlinks, ID::Building(id), tab.clone(), {
let mut tabs = vec![("Info", InfoTab::Nil), ("Debug", InfoTab::Bldg(Tab::Debug))];
if !ppl.is_empty() {
tabs.push(("People", InfoTab::Bldg(Tab::People)));
}
tabs
}));
match tab {
InfoTab::Nil => {
rows.extend(action_btns);
let mut kv = Vec::new(); let mut kv = Vec::new();
@ -103,8 +66,14 @@ pub fn info(
if !txt.is_empty() { if !txt.is_empty() {
rows.push(txt.draw(ctx)) rows.push(txt.draw(ctx))
} }
}
InfoTab::Bldg(Tab::Debug) => { rows
}
pub fn debug(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BuildingID) -> Vec<Widget> {
let mut rows = header(ctx, app, details, id, Tab::BldgDebug(id));
let b = app.primary.map.get_b(id);
rows.extend(make_table( rows.extend(make_table(
ctx, ctx,
vec![( vec![(
@ -114,21 +83,84 @@ pub fn info(
)); ));
rows.push("Raw OpenStreetMap data".draw_text(ctx)); rows.push("Raw OpenStreetMap data".draw_text(ctx));
rows.extend(make_table(ctx, b.osm_tags.clone().into_iter().collect())); rows.extend(make_table(ctx, b.osm_tags.clone().into_iter().collect()));
}
InfoTab::Bldg(Tab::People) => { rows
}
pub fn people(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BuildingID) -> Vec<Widget> {
let mut rows = header(ctx, app, details, id, Tab::BldgPeople(id));
// TODO Sort/group better // TODO Sort/group better
// Show minimal info: ID, next departure time, type of that trip // Show minimal info: ID, next departure time, type of that trip
for person in ppl { for p in app.primary.sim.bldg_to_people(id) {
rows.push(person::summary(ctx, app, person, hyperlinks)); let person = app.primary.sim.get_person(p);
let mut next_trip: Option<(Time, TripMode)> = None;
for t in &person.trips {
match app.primary.sim.trip_to_agent(*t) {
TripResult::TripNotStarted => {
let (start_time, _, _, mode) = app.primary.sim.trip_info(*t);
next_trip = Some((start_time, mode));
break;
} }
TripResult::Ok(_) | TripResult::ModeChange => {
// TODO What to do here? This is meant for building callers right now
break;
}
TripResult::TripDone => {}
TripResult::TripDoesntExist => unreachable!(),
} }
_ => unreachable!(),
} }
let label = format!("Person #{}", p.0);
details
.hyperlinks
.insert(label.clone(), Tab::PersonStatus(p));
rows.push(Widget::col(vec![
Btn::text_bg1(label).build_def(ctx, None),
if let Some((t, mode)) = next_trip {
format!("Leaving in {} to {}", t - app.primary.sim.time(), mode).draw_text(ctx)
} else {
"Staying inside".draw_text(ctx)
},
]));
}
rows
}
fn header(
ctx: &EventCtx,
app: &App,
details: &mut Details,
id: BuildingID,
tab: Tab,
) -> Vec<Widget> {
let mut rows = vec![];
let b = app.primary.map.get_b(id);
rows.push(Widget::row(vec![
Line(format!("Building #{}", id.0))
.small_heading()
.draw(ctx),
header_btns(ctx),
]));
rows.push(make_tabs(
ctx,
&mut details.hyperlinks,
tab,
vec![
("Info", Tab::BldgInfo(id)),
("Debug", Tab::BldgDebug(id)),
("People", Tab::BldgPeople(id)),
],
));
// TODO On every tab?
for p in app.primary.sim.get_parked_cars_by_owner(id) { for p in app.primary.sim.get_parked_cars_by_owner(id) {
batch.push( let shape = app
app.cs.get("something associated with something else"), .primary
app.primary
.draw_map .draw_map
.get_obj( .get_obj(
ID::Car(p.vehicle.id), ID::Car(p.vehicle.id),
@ -137,7 +169,14 @@ pub fn info(
ctx.prerender, ctx.prerender,
) )
.unwrap() .unwrap()
.get_outline(&app.primary.map), .get_outline(&app.primary.map);
details.unzoomed.push(
app.cs.get("something associated with something else"),
shape.clone(),
);
details.zoomed.push(
app.cs.get("something associated with something else"),
shape,
); );
} }

View File

@ -1,25 +1,20 @@
use crate::app::App; use crate::app::App;
use crate::info::{header_btns, make_table, Details};
use ezgui::{EventCtx, Line, Text, Widget}; use ezgui::{EventCtx, Line, Text, Widget};
use geom::Time; use geom::Time;
use map_model::BusStopID; use map_model::BusStopID;
use sim::CarID; use sim::CarID;
pub fn info( // TODO Needs much more work
ctx: &EventCtx, pub fn stop(ctx: &EventCtx, app: &App, details: &mut Details, id: BusStopID) -> Vec<Widget> {
app: &App,
id: BusStopID,
header_btns: Widget,
action_btns: Vec<Widget>,
) -> Vec<Widget> {
let mut rows = vec![]; let mut rows = vec![];
let sim = &app.primary.sim; let sim = &app.primary.sim;
rows.push(Widget::row(vec![ rows.push(Widget::row(vec![
Line("Bus stop").small_heading().draw(ctx), Line("Bus stop").small_heading().draw(ctx),
header_btns, header_btns(ctx),
])); ]));
rows.extend(action_btns);
let mut txt = Text::new(); let mut txt = Text::new();
txt.add(Line(format!( txt.add(Line(format!(
@ -53,3 +48,25 @@ pub fn info(
rows rows
} }
// TODO Likewise
pub fn bus(ctx: &EventCtx, app: &App, details: &mut Details, id: CarID) -> Vec<Widget> {
let mut rows = vec![];
rows.push(Widget::row(vec![
Line(format!("Bus #{}", id.0)).small_heading().draw(ctx),
header_btns(ctx),
]));
let (kv, extra) = app.primary.sim.car_properties(id, &app.primary.map);
rows.extend(make_table(ctx, kv));
if !extra.is_empty() {
let mut txt = Text::from(Line(""));
for line in extra {
txt.add(Line(line));
}
rows.push(txt.draw(ctx));
}
rows
}

View File

@ -1,9 +1,10 @@
//mod building; mod building;
//mod bus; mod bus;
mod debug; mod debug;
mod intersection; mod intersection;
mod lane; mod lane;
//mod person; mod person;
mod trip;
use crate::app::App; use crate::app::App;
use crate::colors; use crate::colors;
@ -200,20 +201,16 @@ impl InfoPanel {
}*/ }*/
let col = match tab { let col = match tab {
/*Tab::PersonStatus(p) => person::status(ctx, app, &mut details, p), Tab::PersonStatus(p) => person::status(ctx, app, &mut details, p),
Tab::PersonTrips(p) => person::trips(ctx, app, &mut details, p), Tab::PersonTrips(p) => person::trips(ctx, app, &mut details, p),
Tab::PersonBio(p) => person::bio(ctx, app, &mut details, p), Tab::PersonBio(p) => person::bio(ctx, app, &mut details, p),
Tab::Bus(c) => bus::bus(ctx, app, &mut details, c), Tab::Bus(c) => bus::bus(ctx, app, &mut details, c),
Tab::BusStop(bs) => bus::stop(ctx, app, &mut details, bs), Tab::BusStop(bs) => bus::stop(ctx, app, &mut details, bs),
Tab::ParkedCar(c) => person::parked_car(ctx, app, &mut details, c), Tab::ParkedCar(c) => person::parked_car(ctx, app, &mut details, c),
Tab::BldgInfo(b) => building::info(ctx, app, &mut details, b), Tab::BldgInfo(b) => building::info(ctx, app, &mut details, b),
Tab::BldgDebug(b) => building::debug(ctx, app, &mut details, b), Tab::BldgDebug(b) => building::debug(ctx, app, &mut details, b),
Tab::BldgPeople(b) => building::people(ctx, app, &mut details, b), Tab::BldgPeople(b) => building::people(ctx, app, &mut details, b),
Tab::Crowd(ref members) => person::crowd(ctx, app, &mut details, members),
Tab::Crowd(members) => person::crowd(ctx, app, &mut details, members),*/
Tab::Area(a) => debug::area(ctx, app, &mut details, a), Tab::Area(a) => debug::area(ctx, app, &mut details, a),
Tab::ExtraShape(es) => debug::extra_shape(ctx, app, &mut details, es), Tab::ExtraShape(es) => debug::extra_shape(ctx, app, &mut details, es),
Tab::IntersectionInfo(i) => intersection::info(ctx, app, &mut details, i), Tab::IntersectionInfo(i) => intersection::info(ctx, app, &mut details, i),
@ -222,7 +219,6 @@ impl InfoPanel {
Tab::LaneInfo(l) => lane::info(ctx, app, &mut details, l), Tab::LaneInfo(l) => lane::info(ctx, app, &mut details, l),
Tab::LaneDebug(l) => lane::debug(ctx, app, &mut details, l), Tab::LaneDebug(l) => lane::debug(ctx, app, &mut details, l),
Tab::LaneTraffic(l) => lane::traffic(ctx, app, &mut details, l), Tab::LaneTraffic(l) => lane::traffic(ctx, app, &mut details, l),
_ => panic!("TODO"),
}; };
/* /*
@ -430,35 +426,6 @@ fn make_tabs(
Widget::row(row).bg(Color::WHITE) Widget::row(row).bg(Color::WHITE)
} }
fn make_browser<F: Fn(usize) -> Tab>(
ctx: &EventCtx,
hyperlinks: &mut HashMap<String, Tab>,
noun: &str,
total: usize,
idx: usize,
make_link: F,
) -> Widget {
// TODO Keys are weird! But left/right for speed
Widget::row(vec![
if idx != 0 {
hyperlinks.insert("previous".to_string(), make_link(idx - 1));
Btn::text_fg("<").build(ctx, "previous", hotkey(Key::UpArrow))
} else {
Btn::text_fg("<").inactive(ctx)
}
.margin(5),
format!("{} {}/{}", noun, idx + 1, total).draw_text(ctx),
if idx != total - 1 {
hyperlinks.insert("next".to_string(), make_link(idx + 1));
Btn::text_fg(">").build(ctx, "next", hotkey(Key::DownArrow))
} else {
Btn::text_fg(">").inactive(ctx)
}
.margin(5),
])
.centered()
}
fn header_btns(ctx: &EventCtx) -> Widget { fn header_btns(ctx: &EventCtx) -> Widget {
Widget::row(vec![ Widget::row(vec![
Btn::svg_def("../data/system/assets/tools/location.svg") Btn::svg_def("../data/system/assets/tools/location.svg")

View File

@ -1,54 +1,84 @@
use crate::app::App; use crate::app::App;
use crate::colors; use crate::colors;
use crate::helpers::ID;
use crate::info::trip::trip_details; use crate::info::trip::trip_details;
use crate::info::{make_table, make_tabs, InfoTab}; use crate::info::{header_btns, make_table, make_tabs, Details, Tab, Text};
use ezgui::{Btn, EventCtx, Line, TextExt, Widget}; use crate::render::Renderable;
use geom::Time; use ezgui::{Btn, Color, EventCtx, Line, TextExt, Widget};
use map_model::Map; use map_model::Map;
use sim::{Person, PersonID, PersonState, TripMode, TripResult}; use sim::{AgentID, CarID, PedestrianID, Person, PersonID, PersonState, TripResult};
use std::collections::HashMap;
#[derive(Clone, PartialEq)] pub fn status(ctx: &mut EventCtx, app: &App, details: &mut Details, id: PersonID) -> Vec<Widget> {
pub enum Tab { let mut rows = header(ctx, app, details, id, Tab::PersonStatus(id));
Bio,
}
pub fn info( let map = &app.primary.map;
ctx: &mut EventCtx, let sim = &app.primary.sim;
app: &App, let person = sim.get_person(id);
id: PersonID,
tab: InfoTab,
// 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 match sim.get_person(id).state {
if let Some(btns) = header_btns { PersonState::Inside(b) => {
rows.push(Widget::row(vec![ // TODO hyperlink
Line(format!("Person #{}", id.0)).small_heading().draw(ctx), rows.push(
btns, format!("Currently inside {}", map.get_b(b).just_address(map)).draw_text(ctx),
);
}
PersonState::OffMap => {
rows.push("Currently outside the map boundaries".draw_text(ctx));
}
PersonState::Limbo => {
rows.push(
"Currently in limbo -- they broke out of the Matrix! Woops. (A bug occurred)"
.draw_text(ctx),
);
}
PersonState::Trip(t) => {
if let Some(a) = sim.trip_to_agent(t).ok() {
rows.push(Widget::col(vec![
Line(format!("Trip #{}", t.0)).small_heading().draw(ctx),
trip_details(ctx, app, t, sim.progress_along_path(a), details),
])); ]));
} else {
rows.push(Line(format!("Person #{}", id.0)).small_heading().draw(ctx)); let (kv, extra) = match a {
AgentID::Car(c) => sim.car_properties(c, map),
AgentID::Pedestrian(p) => sim.ped_properties(p, map),
};
rows.extend(make_table(ctx, kv));
if !extra.is_empty() {
let mut txt = Text::from(Line(""));
for line in extra {
txt.add(Line(line));
}
rows.push(txt.draw(ctx));
} }
rows.push(make_tabs( if let AgentID::Car(c) = a {
ctx, if let Some(b) = app.primary.sim.get_owner_of_car(c) {
hyperlinks, // TODO Mention this, with a warp tool
ID::Person(id), details.unzoomed.push(
tab.clone(), app.cs
vec![("Trips", InfoTab::Nil), ("Bio", InfoTab::Person(Tab::Bio))], .get_def("something associated with something else", Color::PURPLE),
)); app.primary.draw_map.get_b(b).get_outline(&app.primary.map),
);
details.zoomed.push(
app.cs.get("something associated with something else"),
app.primary.draw_map.get_b(b).get_outline(&app.primary.map),
);
}
}
} else {
// TODO Temporary mode change, what's going on?
rows.push(Widget::col(vec![
Line(format!("Trip #{}", t.0)).small_heading().draw(ctx),
trip_details(ctx, app, t, None, details),
]));
}
}
}
match tab { rows
InfoTab::Nil => { }
// TODO None of these right now
rows.extend(action_btns); pub fn trips(ctx: &mut EventCtx, app: &App, details: &mut Details, id: PersonID) -> Vec<Widget> {
let mut rows = header(ctx, app, details, id, Tab::PersonTrips(id));
let map = &app.primary.map; let map = &app.primary.map;
let sim = &app.primary.sim; let sim = &app.primary.sim;
@ -56,6 +86,7 @@ pub fn info(
// I'm sorry for bad variable names // I'm sorry for bad variable names
let mut wheres_waldo = true; let mut wheres_waldo = true;
// TODO Classify trips as not started, ongoing, done. Don't mention current status as much?
for t in &person.trips { for t in &person.trips {
match sim.trip_to_agent(*t) { match sim.trip_to_agent(*t) {
TripResult::TripNotStarted => { TripResult::TripNotStarted => {
@ -77,7 +108,7 @@ pub fn info(
rows.push( rows.push(
Widget::col(vec![ Widget::col(vec![
Line(format!("Trip #{}", t.0)).small_heading().draw(ctx), Line(format!("Trip #{}", t.0)).small_heading().draw(ctx),
trip_details(ctx, app, *t, None, warpers).0, trip_details(ctx, app, *t, None, details),
]) ])
.bg(colors::SECTION_BG) .bg(colors::SECTION_BG)
.margin(10), .margin(10),
@ -86,8 +117,13 @@ pub fn info(
if wheres_waldo { if wheres_waldo {
rows.push(current_status(ctx, person, map)); rows.push(current_status(ctx, person, map));
} }
}
InfoTab::Person(Tab::Bio) => { rows
}
pub fn bio(ctx: &EventCtx, app: &App, details: &mut Details, id: PersonID) -> Vec<Widget> {
let mut rows = header(ctx, app, details, id, Tab::PersonBio(id));
// TODO A little picture // TODO A little picture
rows.extend(make_table( rows.extend(make_table(
ctx, ctx,
@ -104,9 +140,83 @@ pub fn info(
// - Rides a fixie // - Rides a fixie
// - Has 17 pinky toe piercings (surprising, considering they're the state champ at // - Has 17 pinky toe piercings (surprising, considering they're the state champ at
// barefoot marathons) // barefoot marathons)
rows
}
pub fn crowd(
ctx: &EventCtx,
app: &App,
details: &mut Details,
members: &Vec<PedestrianID>,
) -> Vec<Widget> {
let mut rows = vec![];
rows.push(Widget::row(vec![
Line("Pedestrian crowd").small_heading().draw(ctx),
header_btns(ctx),
]));
for (idx, id) in members.into_iter().enumerate() {
let person = app
.primary
.sim
.agent_to_person(AgentID::Pedestrian(*id))
.unwrap();
// TODO What other info is useful to summarize?
rows.push(Widget::row(vec![
format!("{})", idx + 1).draw_text(ctx),
Btn::text_fg(format!("Person #{}", person.0)).build_def(ctx, None),
]));
details
.hyperlinks
.insert(format!("Person #{}", person.0), Tab::PersonStatus(person));
} }
_ => unreachable!(),
rows
}
pub fn parked_car(ctx: &EventCtx, app: &App, details: &mut Details, id: CarID) -> Vec<Widget> {
let mut rows = vec![];
rows.push(Widget::row(vec![
Line(format!("Parked car #{}", id.0))
.small_heading()
.draw(ctx),
header_btns(ctx),
]));
let (kv, extra) = app.primary.sim.car_properties(id, &app.primary.map);
rows.extend(make_table(ctx, kv));
if !extra.is_empty() {
let mut txt = Text::from(Line(""));
for line in extra {
txt.add(Line(line));
} }
rows.push(txt.draw(ctx));
}
rows
}
fn header(ctx: &EventCtx, app: &App, details: &mut Details, id: PersonID, tab: Tab) -> Vec<Widget> {
let mut rows = vec![];
rows.push(Widget::row(vec![
Line(format!("Person #{}", id.0)).small_heading().draw(ctx),
header_btns(ctx),
]));
rows.push(make_tabs(
ctx,
&mut details.hyperlinks,
tab,
vec![
("Status", Tab::PersonStatus(id)),
("Trips", Tab::PersonTrips(id)),
("Bio", Tab::PersonBio(id)),
],
));
rows rows
} }
@ -124,40 +234,3 @@ fn current_status(ctx: &EventCtx, person: &Person, map: &Map) -> Widget {
.draw_text(ctx), .draw_text(ctx),
} }
} }
pub fn summary(
ctx: &EventCtx,
app: &App,
id: PersonID,
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
) -> Widget {
let person = app.primary.sim.get_person(id);
let mut next_trip: Option<(Time, TripMode)> = None;
for t in &person.trips {
match app.primary.sim.trip_to_agent(*t) {
TripResult::TripNotStarted => {
let (start_time, _, _, mode) = app.primary.sim.trip_info(*t);
next_trip = Some((start_time, mode));
break;
}
TripResult::Ok(_) | TripResult::ModeChange => {
// TODO What to do here? This is meant for building callers right now
break;
}
TripResult::TripDone => {}
TripResult::TripDoesntExist => unreachable!(),
}
}
let label = format!("Person #{}", id.0);
hyperlinks.insert(label.clone(), (ID::Person(id), InfoTab::Nil));
Widget::col(vec![
Btn::text_bg1(label).build_def(ctx, None),
if let Some((t, mode)) = next_trip {
format!("Leaving in {} to {}", t - app.primary.sim.time(), mode).draw_text(ctx)
} else {
"Staying inside".draw_text(ctx)
},
])
}

View File

@ -1,97 +1,26 @@
use crate::app::App; use crate::app::App;
use crate::colors; use crate::colors;
use crate::helpers::ID; use crate::helpers::ID;
use crate::info::{make_table, make_tabs, person, InfoTab, TripDetails}; use crate::info::{make_table, Details};
use crate::render::dashed_lines; use crate::render::dashed_lines;
use ezgui::{ use ezgui::{
hotkey, Btn, Color, EventCtx, GeomBatch, Key, Line, Plot, PlotOptions, RewriteColor, Series, Btn, Color, EventCtx, GeomBatch, Line, Plot, PlotOptions, RewriteColor, Series, Text, Widget,
Text, Widget,
}; };
use geom::{Angle, Distance, Duration, Polygon, Pt2D, Time}; use geom::{Angle, Distance, Duration, Polygon, Pt2D, Time};
use map_model::{Map, Path, PathStep}; use map_model::{Map, Path, PathStep};
use sim::{PersonID, TripEndpoint, TripID, TripPhaseType}; use sim::{TripEndpoint, TripID, TripPhaseType};
use std::collections::HashMap;
#[derive(Clone, PartialEq)]
pub enum Tab {
Person(PersonID),
}
pub fn inactive_info(
ctx: &mut EventCtx,
app: &App,
id: TripID,
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![];
rows.push(Widget::row(vec![
Line(format!("Trip #{}", id.0)).small_heading().draw(ctx),
Btn::text_fg("X")
.build(ctx, "close info", hotkey(Key::Escape))
.align_right(),
]));
rows.push(make_tabs(
ctx,
hyperlinks,
ID::Trip(id),
tab.clone(),
vec![
("Info", InfoTab::Nil),
(
"Trips",
InfoTab::Trip(Tab::Person(app.primary.sim.trip_to_person(id))),
),
],
));
let mut details: Option<TripDetails> = None;
match tab {
InfoTab::Nil => {
rows.extend(action_btns);
let (more, trip_details) = trip_details(ctx, app, id, None, warpers);
rows.push(more);
details = Some(trip_details);
}
InfoTab::Trip(Tab::Person(p)) => {
// TODO Hyperlink?
rows.extend(person::info(
ctx,
app,
p,
InfoTab::Nil,
None,
Vec::new(),
hyperlinks,
warpers,
));
}
_ => unreachable!(),
}
(rows, details)
}
pub fn trip_details( pub fn trip_details(
ctx: &mut EventCtx, ctx: &mut EventCtx,
app: &App, app: &App,
trip: TripID, trip: TripID,
progress_along_path: Option<f64>, progress_along_path: Option<f64>,
warpers: &mut HashMap<String, ID>, details: &mut Details,
) -> (Widget, TripDetails) { ) -> Widget {
let map = &app.primary.map; let map = &app.primary.map;
let phases = app.primary.sim.get_analytics().get_trip_phases(trip, map); let phases = app.primary.sim.get_analytics().get_trip_phases(trip, map);
let (start_time, trip_start, trip_end, trip_mode) = app.primary.sim.trip_info(trip); let (start_time, trip_start, trip_end, trip_mode) = app.primary.sim.trip_info(trip);
let mut unzoomed = GeomBatch::new();
let mut zoomed = GeomBatch::new();
if phases.is_empty() { if phases.is_empty() {
// The trip hasn't started // The trip hasn't started
let kv = vec![ let kv = vec![
@ -103,27 +32,22 @@ pub fn trip_details(
("From", endpoint(&trip_start, map).2), ("From", endpoint(&trip_start, map).2),
("To", endpoint(&trip_end, map).2), ("To", endpoint(&trip_end, map).2),
]; ];
return ( return Widget::col(make_table(ctx, kv));
Widget::col(make_table(ctx, kv)),
TripDetails {
id: trip,
unzoomed: unzoomed.upload(ctx),
zoomed: zoomed.upload(ctx),
},
);
} }
let start_btn = { let start_btn = {
let (id, center, name) = endpoint(&trip_start, map); let (id, center, name) = endpoint(&trip_start, map);
warpers.insert(format!("jump to start of Trip #{}", trip.0), id); details
unzoomed.add_svg( .warpers
.insert(format!("jump to start of Trip #{}", trip.0), id);
details.unzoomed.add_svg(
ctx.prerender, ctx.prerender,
"../data/system/assets/timeline/start_pos.svg", "../data/system/assets/timeline/start_pos.svg",
center, center,
1.0, 1.0,
Angle::ZERO, Angle::ZERO,
); );
zoomed.add_svg( details.zoomed.add_svg(
ctx.prerender, ctx.prerender,
"../data/system/assets/timeline/start_pos.svg", "../data/system/assets/timeline/start_pos.svg",
center, center,
@ -145,15 +69,17 @@ pub fn trip_details(
let goal_btn = { let goal_btn = {
let (id, center, name) = endpoint(&trip_end, map); let (id, center, name) = endpoint(&trip_end, map);
warpers.insert(format!("jump to goal of Trip #{}", trip.0), id); details
unzoomed.add_svg( .warpers
.insert(format!("jump to goal of Trip #{}", trip.0), id);
details.unzoomed.add_svg(
ctx.prerender, ctx.prerender,
"../data/system/assets/timeline/goal_pos.svg", "../data/system/assets/timeline/goal_pos.svg",
center, center,
1.0, 1.0,
Angle::ZERO, Angle::ZERO,
); );
zoomed.add_svg( details.zoomed.add_svg(
ctx.prerender, ctx.prerender,
"../data/system/assets/timeline/goal_pos.svg", "../data/system/assets/timeline/goal_pos.svg",
center, center,
@ -281,8 +207,10 @@ pub fn trip_details(
} }
if let Some(trace) = path.trace(map, dist, None) { if let Some(trace) = path.trace(map, dist, None) {
unzoomed.push(color, trace.make_polygons(Distance::meters(10.0))); details
zoomed.extend( .unzoomed
.push(color, trace.make_polygons(Distance::meters(10.0)));
details.zoomed.extend(
color, color,
dashed_lines( dashed_lines(
&trace, &trace,
@ -309,14 +237,7 @@ pub fn trip_details(
col.extend(make_table(ctx, kv)); col.extend(make_table(ctx, kv));
col.extend(elevation); col.extend(elevation);
( Widget::col(col)
Widget::col(col),
TripDetails {
id: trip,
unzoomed: unzoomed.upload(ctx),
zoomed: zoomed.upload(ctx),
},
)
} }
fn make_elevation(ctx: &EventCtx, color: Color, walking: bool, path: &Path, map: &Map) -> Widget { fn make_elevation(ctx: &EventCtx, color: Color, walking: bool, path: &Path, map: &Map) -> Widget {