Start a new control for viewing shortcuts related to a road.

Incomplete! Turns out this requires rearranging how layers of stuff are
drawn first.
This commit is contained in:
Dustin Carlino 2022-07-17 17:02:19 +02:00
parent 4f368ffe01
commit b4ba9f5394
5 changed files with 172 additions and 61 deletions

View File

@ -9,9 +9,9 @@ use widgetry::{
};
use crate::draw_cells::RenderCells;
use crate::edit::{EditNeighbourhood, EditOutcome, Tab};
use crate::edit::{EditMode, EditNeighbourhood, EditOutcome, Tab};
use crate::filters::auto::Heuristic;
use crate::shortcuts::find_shortcuts;
use crate::shortcuts::{find_shortcuts, Shortcuts};
use crate::{colors, App, Neighbourhood, NeighbourhoodID, Transition};
pub struct Viewer {
@ -19,6 +19,7 @@ pub struct Viewer {
left_panel: Panel,
neighbourhood: Neighbourhood,
draw_top_layer: ToggleZoomed,
draw_under_roads_layer: Drawable,
highlight_cell: World<DummyID>,
edit: EditNeighbourhood,
@ -34,6 +35,7 @@ impl Viewer {
left_panel: Panel::empty(ctx),
neighbourhood,
draw_top_layer: ToggleZoomed::empty(ctx),
draw_under_roads_layer: Drawable::empty(ctx),
highlight_cell: World::unbounded(),
edit: EditNeighbourhood::temporary(),
show_error: Drawable::empty(ctx),
@ -43,10 +45,11 @@ impl Viewer {
}
fn update(&mut self, ctx: &mut EventCtx, app: &App) {
let (edit, draw_top_layer, render_cells, highlight_cell) =
let (edit, draw_top_layer, draw_under_roads_layer, render_cells, highlight_cell, shortcuts) =
setup_editing(ctx, app, &self.neighbourhood);
self.edit = edit;
self.draw_top_layer = draw_top_layer;
self.draw_under_roads_layer = draw_under_roads_layer;
self.highlight_cell = highlight_cell;
let mut show_error = GeomBatch::new();
@ -85,6 +88,7 @@ impl Viewer {
app,
Tab::Connectivity,
&self.top_panel,
&shortcuts,
Widget::col(vec![
format!(
"Neighbourhood area: {}",
@ -175,10 +179,11 @@ impl State<App> for Viewer {
app.session.heuristic = self.left_panel.dropdown_value("heuristic");
if x != "heuristic" {
let (edit, draw_top_layer, _, highlight_cell) =
let (edit, draw_top_layer, draw_under_roads_layer, _, highlight_cell, _) =
setup_editing(ctx, app, &self.neighbourhood);
self.edit = edit;
self.draw_top_layer = draw_top_layer;
self.draw_under_roads_layer = draw_under_roads_layer;
self.highlight_cell = highlight_cell;
}
}
@ -206,10 +211,11 @@ impl State<App> for Viewer {
}
fn draw(&self, g: &mut GfxCtx, app: &App) {
crate::draw_with_layering(g, app, |g| self.edit.world.draw(g));
crate::draw_with_layering(g, app, |g| g.redraw(&self.draw_under_roads_layer));
g.redraw(&self.neighbourhood.fade_irrelevant);
self.draw_top_layer.draw(g);
self.highlight_cell.draw(g);
self.edit.world.draw(g);
self.top_panel.draw(g);
self.left_panel.draw(g);
@ -235,26 +241,31 @@ fn setup_editing(
ctx: &mut EventCtx,
app: &App,
neighbourhood: &Neighbourhood,
) -> (EditNeighbourhood, ToggleZoomed, RenderCells, World<DummyID>) {
) -> (
EditNeighbourhood,
ToggleZoomed,
Drawable,
RenderCells,
World<DummyID>,
Shortcuts,
) {
let shortcuts = ctx.loading_screen("find shortcuts", |_, timer| {
find_shortcuts(app, neighbourhood, timer)
});
let mut edit = EditNeighbourhood::new(ctx, app, neighbourhood, &shortcuts);
let edit = EditNeighbourhood::new(ctx, app, neighbourhood, &shortcuts);
let map = &app.map;
// The world is drawn in between areas and roads, but some things need to be drawn on top of
// roads
// Draw some stuff under roads and other stuff on top
let mut draw_top_layer = ToggleZoomed::builder();
let mut draw_under_roads_layer = GeomBatch::new();
// Use a separate world to highlight cells when hovering on them. This is separate from
// edit.world so it can be drawn at the right layer and also so that we draw it even while
// hovering on roads/intersections in a cell
// edit.world so that we draw it even while hovering on roads/intersections in a cell
let mut highlight_cell = World::bounded(app.map.get_bounds());
let render_cells = RenderCells::new(map, neighbourhood);
if app.session.draw_cells_as_areas {
edit.world
.draw_master_batch(ctx, render_cells.draw_colored_areas());
draw_under_roads_layer = render_cells.draw_colored_areas();
} else {
draw_top_layer
.unzoomed
@ -291,7 +302,9 @@ fn setup_editing(
&app.cs.good_to_bad_red,
);
draw_top_layer.append(colorer.draw);
if !matches!(app.session.edit_mode, EditMode::Shortcuts(_)) {
draw_top_layer.append(colorer.draw);
}
// Draw the borders of each cell
for (idx, cell) in neighbourhood.cells.iter().enumerate() {
@ -341,8 +354,10 @@ fn setup_editing(
(
edit,
draw_top_layer.build(ctx),
ctx.upload(draw_under_roads_layer),
render_cells,
highlight_cell,
shortcuts,
)
}

View File

@ -1,5 +1,6 @@
mod filters;
mod one_ways;
mod shortcuts;
use map_model::{IntersectionID, RoadID};
use widgetry::mapspace::{ObjectID, World};
@ -21,7 +22,7 @@ impl Tab {
let mut row = Vec::new();
for (tab, label, key) in [
(Tab::Connectivity, "Connectivity", Key::F1),
(Tab::Shortcuts, "Shortcuts", Key::F2),
(Tab::Shortcuts, "Shortcuts (old)", Key::F2),
] {
// TODO Match the TabController styling
row.push(
@ -61,6 +62,15 @@ 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>),
}
pub struct EditNeighbourhood {
// Only pub for drawing
pub world: World<Obj>,
@ -104,10 +114,12 @@ impl EditNeighbourhood {
shortcuts: &Shortcuts,
) -> Self {
Self {
world: if app.session.edit_filters {
filters::make_world(ctx, app, neighbourhood, shortcuts)
} else {
one_ways::make_world(ctx, app, neighbourhood)
world: match app.session.edit_mode {
EditMode::Filters => filters::make_world(ctx, app, neighbourhood, shortcuts),
EditMode::Oneways => one_ways::make_world(ctx, app, neighbourhood),
EditMode::Shortcuts(focus) => {
shortcuts::make_world(ctx, app, neighbourhood, shortcuts, focus)
}
},
}
}
@ -118,6 +130,7 @@ impl EditNeighbourhood {
app: &App,
tab: Tab,
top_panel: &Panel,
shortcuts: &Shortcuts,
per_tab_contents: Widget,
) -> PanelBuilder {
let contents = Widget::col(vec![
@ -126,11 +139,11 @@ impl EditNeighbourhood {
Line("Editing neighbourhood")
.small_heading()
.into_widget(ctx),
edit_mode(ctx, app.session.edit_filters),
if app.session.edit_filters {
filters::widget(ctx, app)
} else {
one_ways::widget(ctx)
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, shortcuts, focus),
}
.section(ctx),
tab.make_buttons(ctx, app),
@ -142,10 +155,10 @@ impl EditNeighbourhood {
pub fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> EditOutcome {
let outcome = self.world.event(ctx);
let outcome = if app.session.edit_filters {
filters::handle_world_outcome(ctx, app, outcome)
} else {
one_ways::handle_world_outcome(ctx, app, outcome)
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),
};
if matches!(outcome, EditOutcome::Transition(_)) {
self.world.hack_unset_hovering();
@ -175,7 +188,7 @@ impl EditNeighbourhood {
"Connectivity" => Some(Transition::Replace(crate::connectivity::Viewer::new_state(
ctx, app, id,
))),
"Shortcuts" => Some(Transition::Replace(
"Shortcuts (old)" => Some(Transition::Replace(
crate::shortcut_viewer::BrowseShortcuts::new_state(ctx, app, id, None),
)),
// Overkill to force all mode-specific code into the module
@ -197,11 +210,15 @@ impl EditNeighbourhood {
crate::route_planner::RoutePlanner::new_state(ctx, app),
)),
"Filters" => {
app.session.edit_filters = true;
app.session.edit_mode = EditMode::Filters;
Some(Transition::Recreate)
}
"One-ways" => {
app.session.edit_filters = false;
app.session.edit_mode = EditMode::Oneways;
Some(Transition::Recreate)
}
"Shortcuts" => {
app.session.edit_mode = EditMode::Shortcuts(None);
Some(Transition::Recreate)
}
_ => None,
@ -209,33 +226,26 @@ impl EditNeighbourhood {
}
}
fn edit_mode(ctx: &mut EventCtx, filters: bool) -> Widget {
fn edit_mode(ctx: &mut EventCtx, edit_mode: EditMode) -> Widget {
let mut row = Vec::new();
row.push(
ctx.style()
.btn_tab
.text("Filters")
.corner_rounding(geom::CornerRadii {
top_left: DEFAULT_CORNER_RADIUS,
top_right: DEFAULT_CORNER_RADIUS,
bottom_left: 0.0,
bottom_right: 0.0,
})
.disabled(filters)
.build_def(ctx),
);
row.push(
ctx.style()
.btn_tab
.text("One-ways")
.corner_rounding(geom::CornerRadii {
top_left: DEFAULT_CORNER_RADIUS,
top_right: DEFAULT_CORNER_RADIUS,
bottom_left: 0.0,
bottom_right: 0.0,
})
.disabled(!filters)
.build_def(ctx),
);
for (label, is_current) in [
("Filters", edit_mode == EditMode::Filters),
("One-ways", edit_mode == EditMode::Oneways),
("Shortcuts", matches!(edit_mode, EditMode::Shortcuts(_))),
] {
row.push(
ctx.style()
.btn_tab
.text(label)
.corner_rounding(geom::CornerRadii {
top_left: DEFAULT_CORNER_RADIUS,
top_right: DEFAULT_CORNER_RADIUS,
bottom_left: 0.0,
bottom_right: 0.0,
})
.disabled(is_current)
.build_def(ctx),
);
}
Widget::row(row)
}

View File

@ -0,0 +1,85 @@
use geom::Distance;
use map_model::RoadID;
use widgetry::mapspace::{World, WorldOutcome};
use widgetry::{Color, EventCtx, Text, TextExt, Widget};
use super::{EditMode, EditOutcome, Obj};
use crate::shortcuts::Shortcuts;
use crate::{colors, App, Neighbourhood};
pub fn widget(
ctx: &mut EventCtx,
app: &App,
shortcuts: &Shortcuts,
focus: Option<RoadID>,
) -> Widget {
match focus {
Some(r) => Widget::col(vec![format!(
"{} possible shortcuts cross {}",
shortcuts.count_per_road.get(r),
app.map.get_r(r).get_name(app.opts.language.as_ref()),
)
.text_widget(ctx)]),
None => Widget::col(vec![
"Click a road to view shortcuts through it".text_widget(ctx)
]),
}
}
pub fn make_world(
ctx: &mut EventCtx,
app: &App,
neighbourhood: &Neighbourhood,
shortcuts: &Shortcuts,
focus: Option<RoadID>,
) -> World<Obj> {
let map = &app.map;
let mut world = World::bounded(map.get_bounds());
for r in &neighbourhood.orig_perimeter.interior {
let road = map.get_r(*r);
if focus == Some(*r) {
world
.add(Obj::InteriorRoad(*r))
.hitbox(road.get_thick_polygon())
.draw_color(Color::BLUE)
.build(ctx);
} else {
world
.add(Obj::InteriorRoad(*r))
.hitbox(road.get_thick_polygon())
.drawn_in_master_batch()
.hover_outline(colors::OUTLINE, Distance::meters(5.0))
.tooltip(Text::from(format!(
"{} possible shortcuts cross {}",
shortcuts.count_per_road.get(*r),
road.get_name(app.opts.language.as_ref()),
)))
.clickable()
.build(ctx);
}
}
world.initialize_hover(ctx);
world
}
pub fn handle_world_outcome(
ctx: &mut EventCtx,
app: &mut App,
outcome: WorldOutcome<Obj>,
) -> EditOutcome {
match outcome {
WorldOutcome::ClickedObject(Obj::InteriorRoad(r)) => {
app.session.edit_mode = EditMode::Shortcuts(Some(r));
// TODO make the scroller thing
EditOutcome::Recalculate
}
WorldOutcome::ClickedFreeSpace(_) => {
app.session.edit_mode = EditMode::Shortcuts(None);
EditOutcome::Recalculate
}
_ => EditOutcome::Nothing,
}
}

View File

@ -85,7 +85,7 @@ fn run(mut settings: Settings) {
draw_all_filters: Toggle3Zoomed::empty(ctx),
impact: impact::Impact::empty(ctx),
edit_filters: true,
edit_mode: edit::EditMode::Filters,
draw_neighbourhood_style: browse::Style::Simple,
draw_cells_as_areas: false,
@ -206,8 +206,7 @@ pub struct Session {
pub draw_all_filters: Toggle3Zoomed,
pub impact: impact::Impact,
// True if we're editing filters, false if we're editing one-ways. (An enum is overkill)
pub edit_filters: bool,
pub edit_mode: edit::EditMode,
// Remember form settings in different tabs.
// Browse neighbourhoods:

View File

@ -70,6 +70,7 @@ impl BrowseShortcuts {
app,
Tab::Shortcuts,
&self.top_panel,
&self.shortcuts,
percentage_bar(
ctx,
Text::from(Line(format!(
@ -95,6 +96,7 @@ impl BrowseShortcuts {
app,
Tab::Shortcuts,
&self.top_panel,
&self.shortcuts,
Widget::col(vec![
percentage_bar(
ctx,