Popup error messages when the user tries to do something invalid (like filtering a one-way street)

This commit is contained in:
Dustin Carlino 2022-06-23 10:16:06 -05:00
parent f0b09633c2
commit 86f908946f
6 changed files with 78 additions and 34 deletions

View File

@ -51,6 +51,7 @@ impl FreehandFilters {
continue;
}
let road = app.map.get_r(*r);
// Don't show error messages for these
if !PathConstraints::Car.can_use_road(road, &app.map)
|| road.oneway_for_driving().is_some()
{

View File

@ -9,7 +9,7 @@ use widgetry::{
};
use crate::draw_cells::RenderCells;
use crate::edit::{EditNeighbourhood, Tab};
use crate::edit::{EditNeighbourhood, EditOutcome, Tab};
use crate::filters::auto::Heuristic;
use crate::shortcuts::find_shortcuts;
use crate::{colors, App, Neighbourhood, NeighbourhoodID, Transition};
@ -179,9 +179,15 @@ impl State<App> for Viewer {
_ => {}
}
if self.edit.event(ctx, app) {
self.neighbourhood = Neighbourhood::new(ctx, app, self.neighbourhood.id);
self.update(ctx, app);
match self.edit.event(ctx, app) {
EditOutcome::Nothing => {}
EditOutcome::Recalculate => {
self.neighbourhood = Neighbourhood::new(ctx, app, self.neighbourhood.id);
self.update(ctx, app);
}
EditOutcome::Transition(t) => {
return t;
}
}
Transition::Keep

View File

@ -4,7 +4,7 @@ use widgetry::mapspace::{World, WorldOutcome};
use widgetry::tools::open_browser;
use widgetry::{lctrl, EventCtx, Image, Key, Line, Text, TextExt, Widget};
use super::Obj;
use super::{EditOutcome, Obj};
use crate::shortcuts::Shortcuts;
use crate::{after_edit, colors, App, DiagonalFilter, Neighbourhood};
@ -86,16 +86,23 @@ pub fn make_world(
world
}
pub fn handle_world_outcome(ctx: &mut EventCtx, app: &mut App, outcome: WorldOutcome<Obj>) -> bool {
pub fn handle_world_outcome(
ctx: &mut EventCtx,
app: &mut App,
outcome: WorldOutcome<Obj>,
) -> EditOutcome {
let map = &app.map;
match outcome {
WorldOutcome::ClickedObject(Obj::InteriorRoad(r)) => {
let road = map.get_r(r);
// Filtering a road that's already marked bike-only doesn't make sense. Likewise for
// one-ways.
if !PathConstraints::Car.can_use_road(road, map) || road.oneway_for_driving().is_some()
{
return true;
if !PathConstraints::Car.can_use_road(road, map) {
return EditOutcome::error(
ctx,
"This street isn't accessible by car; no need for a filter",
);
}
if road.oneway_for_driving().is_some() {
return EditOutcome::error(ctx, "You can't filter a one-way street");
}
app.session.modal_filters.before_edit();
@ -111,22 +118,22 @@ pub fn handle_world_outcome(ctx: &mut EventCtx, app: &mut App, outcome: WorldOut
app.session.modal_filters.roads.insert(r, distance);
}
after_edit(ctx, app);
true
EditOutcome::Recalculate
}
WorldOutcome::ClickedObject(Obj::InteriorIntersection(i)) => {
app.session.modal_filters.before_edit();
DiagonalFilter::cycle_through_alternatives(app, i);
after_edit(ctx, app);
true
EditOutcome::Recalculate
}
WorldOutcome::Keypress("debug", Obj::InteriorIntersection(i)) => {
open_browser(app.map.get_i(i).orig_id.to_string());
false
EditOutcome::Nothing
}
WorldOutcome::Keypress("debug", Obj::InteriorRoad(r)) => {
open_browser(app.map.get_r(r).orig_id.osm_way_id.to_string());
false
EditOutcome::Nothing
}
_ => false,
_ => EditOutcome::Nothing,
}
}

View File

@ -3,6 +3,7 @@ mod one_ways;
use map_model::{IntersectionID, RoadID};
use widgetry::mapspace::{ObjectID, World};
use widgetry::tools::PopupMsg;
use widgetry::{EventCtx, Key, Line, Panel, PanelBuilder, Widget, DEFAULT_CORNER_RADIUS};
use crate::shortcuts::Shortcuts;
@ -72,6 +73,23 @@ pub enum Obj {
}
impl ObjectID for Obj {}
pub enum EditOutcome {
Nothing,
/// The neighbourhood has changed and the caller should recalculate stuff, including the panel
Recalculate,
Transition(Transition),
}
impl EditOutcome {
fn error(ctx: &mut EventCtx, msg: &str) -> Self {
Self::Transition(Transition::Push(PopupMsg::new_state(
ctx,
"Error",
vec![msg],
)))
}
}
impl EditNeighbourhood {
pub fn temporary() -> Self {
Self {
@ -122,15 +140,17 @@ impl EditNeighbourhood {
crate::components::LeftPanel::builder(ctx, top_panel, contents)
}
/// If true, the neighbourhood has changed and the caller should recalculate stuff, including
/// the panel
pub fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> bool {
pub fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> EditOutcome {
let outcome = self.world.event(ctx);
if app.session.edit_filters {
let outcome = if app.session.edit_filters {
filters::handle_world_outcome(ctx, app, outcome)
} else {
one_ways::handle_world_outcome(ctx, app, outcome)
};
if matches!(outcome, EditOutcome::Transition(_)) {
self.world.hack_unset_hovering();
}
outcome
}
pub fn handle_panel_action(

View File

@ -4,7 +4,7 @@ use raw_map::{Direction, DrivingSide, LaneSpec, LaneType};
use widgetry::mapspace::{World, WorldOutcome};
use widgetry::{EventCtx, Image, Text, TextExt, Widget};
use super::Obj;
use super::{EditOutcome, Obj};
use crate::{colors, App, Neighbourhood};
pub fn widget(ctx: &mut EventCtx) -> Widget {
@ -45,7 +45,11 @@ pub fn make_world(ctx: &mut EventCtx, app: &App, neighbourhood: &Neighbourhood)
world
}
pub fn handle_world_outcome(ctx: &mut EventCtx, app: &mut App, outcome: WorldOutcome<Obj>) -> bool {
pub fn handle_world_outcome(
ctx: &mut EventCtx,
app: &mut App,
outcome: WorldOutcome<Obj>,
) -> EditOutcome {
match outcome {
WorldOutcome::ClickedObject(Obj::InteriorRoad(r)) => {
let leftmost_dir = if app.map.get_config().driving_side == DrivingSide::Right {
@ -113,8 +117,8 @@ pub fn handle_world_outcome(ctx: &mut EventCtx, app: &mut App, outcome: WorldOut
app.map.keep_pathfinder_despite_edits();
});
true
EditOutcome::Recalculate
}
_ => false,
_ => EditOutcome::Nothing,
}
}

View File

@ -3,7 +3,7 @@ use map_model::{PathRequest, NORMAL_LANE_THICKNESS};
use widgetry::mapspace::ToggleZoomed;
use widgetry::{EventCtx, GfxCtx, Key, Line, Outcome, Panel, State, Text, TextExt, Widget};
use crate::edit::{EditNeighbourhood, Tab};
use crate::edit::{EditNeighbourhood, EditOutcome, Tab};
use crate::shortcuts::{find_shortcuts, Shortcuts};
use crate::{colors, App, Neighbourhood, NeighbourhoodID, Transition};
@ -209,15 +209,21 @@ impl State<App> for BrowseShortcuts {
_ => {}
}
if self.edit.event(ctx, app) {
// Reset state, but if possible, preserve the current individual shortcut.
let current_request = self.shortcuts.paths[self.current_idx].get_req().clone();
return Transition::Replace(BrowseShortcuts::new_state(
ctx,
app,
self.neighbourhood.id,
Some(current_request),
));
match self.edit.event(ctx, app) {
EditOutcome::Nothing => {}
EditOutcome::Recalculate => {
// Reset state, but if possible, preserve the current individual shortcut.
let current_request = self.shortcuts.paths[self.current_idx].get_req().clone();
return Transition::Replace(BrowseShortcuts::new_state(
ctx,
app,
self.neighbourhood.id,
Some(current_request),
));
}
EditOutcome::Transition(t) => {
return t;
}
}
Transition::Keep