mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
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:
parent
4f368ffe01
commit
b4ba9f5394
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
85
apps/ltn/src/edit/shortcuts.rs
Normal file
85
apps/ltn/src/edit/shortcuts.rs
Normal 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,
|
||||
}
|
||||
}
|
@ -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:
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user