Now make the browse shortcut controls appear.

It works, mvp. Lots of code cleanup / performance / edge cases needing
cleanup though.
This commit is contained in:
Dustin Carlino 2022-07-17 18:11:04 +02:00
parent dd99afddc6
commit 8e94eeeb17
7 changed files with 121 additions and 41 deletions

View File

@ -1,6 +1,6 @@
use std::collections::HashSet;
use abstutil::{Counter, Timer};
use abstutil::Counter;
use geom::Distance;
use map_gui::tools::{ColorNetwork, DrawRoadLabels};
use synthpop::Scenario;
@ -123,7 +123,7 @@ impl State<App> for BrowseNeighbourhoods {
} else if x == "style" {
app.session.draw_neighbourhood_style = self.left_panel.dropdown_value("style");
ctx.loading_screen("change style", |ctx, timer| {
ctx.loading_screen("change style", |ctx, _| {
self.world = make_world(ctx, app);
self.draw_over_roads = draw_over_roads(ctx, app);
});

View File

@ -87,7 +87,6 @@ impl Viewer {
app,
Tab::Connectivity,
&self.top_panel,
&self.neighbourhood,
Widget::col(vec![
format!(
"Neighbourhood area: {}",
@ -189,7 +188,7 @@ impl State<App> for Viewer {
_ => {}
}
match self.edit.event(ctx, app) {
match self.edit.event(ctx, app, &self.neighbourhood) {
EditOutcome::Nothing => {}
EditOutcome::Recalculate => {
self.neighbourhood = Neighbourhood::new(ctx, app, self.neighbourhood.id);

View File

@ -62,12 +62,11 @@ impl Tab {
}
// TODO This will replace Tab soon
#[derive(Clone, Copy, PartialEq)]
pub enum EditMode {
Filters,
Oneways,
// Is a road clicked on right now?
Shortcuts(Option<RoadID>),
Shortcuts(Option<shortcuts::FocusedRoad>),
}
pub struct EditNeighbourhood {
@ -108,7 +107,7 @@ impl EditNeighbourhood {
pub fn new(ctx: &mut EventCtx, app: &App, neighbourhood: &Neighbourhood) -> Self {
Self {
world: match app.session.edit_mode {
world: match &app.session.edit_mode {
EditMode::Filters => filters::make_world(ctx, app, neighbourhood),
EditMode::Oneways => one_ways::make_world(ctx, app, neighbourhood),
EditMode::Shortcuts(focus) => shortcuts::make_world(ctx, app, neighbourhood, focus),
@ -122,7 +121,6 @@ impl EditNeighbourhood {
app: &App,
tab: Tab,
top_panel: &Panel,
neighbourhood: &Neighbourhood,
per_tab_contents: Widget,
) -> PanelBuilder {
let contents = Widget::col(vec![
@ -131,11 +129,11 @@ impl EditNeighbourhood {
Line("Editing neighbourhood")
.small_heading()
.into_widget(ctx),
edit_mode(ctx, app.session.edit_mode),
edit_mode(ctx, &app.session.edit_mode),
match app.session.edit_mode {
EditMode::Filters => filters::widget(ctx, app),
EditMode::Oneways => one_ways::widget(ctx),
EditMode::Shortcuts(focus) => shortcuts::widget(ctx, app, neighbourhood, focus),
EditMode::Shortcuts(ref focus) => shortcuts::widget(ctx, app, focus.as_ref()),
}
.section(ctx),
tab.make_buttons(ctx, app),
@ -145,12 +143,17 @@ impl EditNeighbourhood {
crate::components::LeftPanel::builder(ctx, top_panel, contents)
}
pub fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> EditOutcome {
pub fn event(
&mut self,
ctx: &mut EventCtx,
app: &mut App,
neighbourhood: &Neighbourhood,
) -> EditOutcome {
let outcome = self.world.event(ctx);
let outcome = match app.session.edit_mode {
EditMode::Filters => filters::handle_world_outcome(ctx, app, outcome),
EditMode::Oneways => one_ways::handle_world_outcome(ctx, app, outcome),
EditMode::Shortcuts(_) => shortcuts::handle_world_outcome(ctx, app, outcome),
EditMode::Shortcuts(_) => shortcuts::handle_world_outcome(app, outcome, neighbourhood),
};
if matches!(outcome, EditOutcome::Transition(_)) {
self.world.hack_unset_hovering();
@ -213,16 +216,28 @@ impl EditNeighbourhood {
app.session.edit_mode = EditMode::Shortcuts(None);
Some(Transition::Recreate)
}
"previous shortcut" => {
if let EditMode::Shortcuts(Some(ref mut focus)) = app.session.edit_mode {
focus.current_idx -= 1;
}
Some(Transition::Recreate)
}
"next shortcut" => {
if let EditMode::Shortcuts(Some(ref mut focus)) = app.session.edit_mode {
focus.current_idx += 1;
}
Some(Transition::Recreate)
}
_ => None,
}
}
}
fn edit_mode(ctx: &mut EventCtx, edit_mode: EditMode) -> Widget {
fn edit_mode(ctx: &mut EventCtx, edit_mode: &EditMode) -> Widget {
let mut row = Vec::new();
for (label, is_current) in [
("Filters", edit_mode == EditMode::Filters),
("One-ways", edit_mode == EditMode::Oneways),
("Filters", matches!(edit_mode, EditMode::Filters)),
("One-ways", matches!(edit_mode, EditMode::Oneways)),
("Shortcuts", matches!(edit_mode, EditMode::Shortcuts(_))),
] {
row.push(

View File

@ -1,24 +1,44 @@
use geom::Distance;
use map_model::RoadID;
use widgetry::mapspace::{World, WorldOutcome};
use widgetry::{Color, EventCtx, Text, TextExt, Widget};
use map_model::{Path, RoadID, NORMAL_LANE_THICKNESS};
use widgetry::mapspace::{ToggleZoomed, World, WorldOutcome};
use widgetry::{Color, EventCtx, Key, Line, Text, TextExt, Widget};
use super::{EditMode, EditOutcome, Obj};
use crate::{colors, App, Neighbourhood};
pub fn widget(
ctx: &mut EventCtx,
app: &App,
neighbourhood: &Neighbourhood,
focus: Option<RoadID>,
) -> Widget {
pub struct FocusedRoad {
pub r: RoadID,
pub paths: Vec<Path>,
pub current_idx: usize,
}
pub fn widget(ctx: &mut EventCtx, app: &App, focus: Option<&FocusedRoad>) -> Widget {
match focus {
Some(r) => Widget::col(vec![format!(
"{} possible shortcuts cross {}",
neighbourhood.shortcuts.count_per_road.get(r),
app.map.get_r(r).get_name(app.opts.language.as_ref()),
)
.text_widget(ctx)]),
Some(focus) => Widget::col(vec![
format!(
"{} possible shortcuts cross {}",
focus.paths.len(),
app.map.get_r(focus.r).get_name(app.opts.language.as_ref()),
)
.text_widget(ctx),
Widget::row(vec![
ctx.style()
.btn_prev()
.disabled(focus.current_idx == 0)
.hotkey(Key::LeftArrow)
.build_widget(ctx, "previous shortcut"),
Text::from(
Line(format!("{}/{}", focus.current_idx + 1, focus.paths.len())).secondary(),
)
.into_widget(ctx)
.centered_vert(),
ctx.style()
.btn_next()
.disabled(focus.current_idx == focus.paths.len() - 1)
.hotkey(Key::RightArrow)
.build_widget(ctx, "next shortcut"),
]),
]),
None => Widget::col(vec![
"Click a road to view shortcuts through it".text_widget(ctx)
]),
@ -29,14 +49,15 @@ pub fn make_world(
ctx: &mut EventCtx,
app: &App,
neighbourhood: &Neighbourhood,
focus: Option<RoadID>,
focus: &Option<FocusedRoad>,
) -> World<Obj> {
let map = &app.map;
let mut world = World::bounded(map.get_bounds());
let focused_road = focus.as_ref().map(|f| f.r);
for r in &neighbourhood.orig_perimeter.interior {
let road = map.get_r(*r);
if focus == Some(*r) {
if focused_road == Some(*r) {
world
.add(Obj::InteriorRoad(*r))
.hitbox(road.get_thick_polygon())
@ -58,20 +79,48 @@ pub fn make_world(
}
}
if let Some(ref focus) = focus {
let mut draw_path = ToggleZoomed::builder();
if let Some(pl) = focus.paths[focus.current_idx].trace(&app.map) {
let color = colors::SHORTCUT_PATH;
let shape = pl.make_polygons(3.0 * NORMAL_LANE_THICKNESS);
draw_path.unzoomed.push(color.alpha(0.8), shape.clone());
draw_path.zoomed.push(color.alpha(0.5), shape);
draw_path
.unzoomed
.append(map_gui::tools::start_marker(ctx, pl.first_pt(), 2.0));
draw_path
.zoomed
.append(map_gui::tools::start_marker(ctx, pl.first_pt(), 0.5));
draw_path
.unzoomed
.append(map_gui::tools::goal_marker(ctx, pl.last_pt(), 2.0));
draw_path
.zoomed
.append(map_gui::tools::goal_marker(ctx, pl.last_pt(), 0.5));
}
world.draw_master_batch(ctx, draw_path);
}
world.initialize_hover(ctx);
world
}
pub fn handle_world_outcome(
ctx: &mut EventCtx,
app: &mut App,
outcome: WorldOutcome<Obj>,
neighbourhood: &Neighbourhood,
) -> EditOutcome {
match outcome {
WorldOutcome::ClickedObject(Obj::InteriorRoad(r)) => {
app.session.edit_mode = EditMode::Shortcuts(Some(r));
// TODO make the scroller thing
// TODO handle 0 shortcut case
app.session.edit_mode = EditMode::Shortcuts(Some(FocusedRoad {
r,
paths: neighbourhood.shortcuts.subset(r),
current_idx: 0,
}));
EditOutcome::Recalculate
}
WorldOutcome::ClickedFreeSpace(_) => {

View File

@ -66,7 +66,6 @@ impl BrowseShortcuts {
app,
Tab::Shortcuts,
&self.top_panel,
&self.neighbourhood,
percentage_bar(
ctx,
Text::from(Line(format!(
@ -92,7 +91,6 @@ impl BrowseShortcuts {
app,
Tab::Shortcuts,
&self.top_panel,
&self.neighbourhood,
Widget::col(vec![
percentage_bar(
ctx,
@ -211,7 +209,7 @@ impl State<App> for BrowseShortcuts {
_ => {}
}
match self.edit.event(ctx, app) {
match self.edit.event(ctx, app, &self.neighbourhood) {
EditOutcome::Nothing => {}
EditOutcome::Recalculate => {
// Reset state, but if possible, preserve the current individual shortcut.

View File

@ -36,6 +36,14 @@ impl Shortcuts {
let total_streets = neighbourhood.orig_perimeter.interior.len();
(quiet_streets, total_streets)
}
pub fn subset(&self, r: RoadID) -> Vec<Path> {
self.paths
.iter()
.filter(|path| path.crosses_road(r))
.cloned()
.collect()
}
}
pub fn find_shortcuts(app: &App, neighbourhood: &Neighbourhood, timer: &mut Timer) -> Shortcuts {

View File

@ -8,8 +8,8 @@ use abstutil::prettyprint_usize;
use geom::{Distance, Duration, PolyLine, Polygon, Ring, Speed, EPSILON_DIST};
use crate::{
BuildingID, DirectedRoadID, LaneID, Map, PathConstraints, Position, Traversable, TurnID,
UberTurn,
BuildingID, DirectedRoadID, LaneID, Map, PathConstraints, Position, RoadID, Traversable,
TurnID, UberTurn,
};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
@ -558,6 +558,17 @@ impl Path {
dist_along
);
}
pub fn crosses_road(&self, r: RoadID) -> bool {
for step in &self.steps {
if let PathStep::Lane(l) | PathStep::ContraflowLane(l) = step {
if l.road == r {
return true;
}
}
}
false
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]