Yuwen's new changelist UI (except for describing some of the changes in detail)

This commit is contained in:
Dustin Carlino 2020-09-14 17:40:04 -07:00
parent 45876ed66b
commit fff75fa5ba
3 changed files with 101 additions and 48 deletions

View File

@ -15,7 +15,7 @@ pub use self::stop_signs::StopSignEditor;
pub use self::traffic_signals::TrafficSignalEditor;
pub use self::validate::{check_blackholes, check_sidewalk_connectivity, try_change_lt};
use crate::app::{App, ShowEverything};
use crate::common::{tool_panel, CommonState, Warping};
use crate::common::{tool_panel, ColorLegend, CommonState, Warping};
use crate::debug::DebugMode;
use crate::game::{ChooseSomething, PopupMsg, State, Transition};
use crate::helpers::ID;
@ -30,7 +30,7 @@ use sim::DontDrawAgents;
use std::collections::BTreeSet;
use widgetry::{
hotkey, lctrl, Btn, Choice, Color, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line,
Menu, Outcome, Panel, RewriteColor, Text, TextExt, VerticalAlignment, Widget,
Menu, Outcome, Panel, Text, TextExt, VerticalAlignment, Widget,
};
pub struct EditMode {
@ -219,16 +219,23 @@ impl State for EditMode {
Transition::Replace(LoadEdits::new(ctx, app, mode.clone()))
}
}
"create a blank proposal" => Transition::Replace(SaveEdits::new(
ctx,
app,
"Do you want to save your proposal first?",
true,
Some(Transition::Pop),
Box::new(|ctx, app| {
"create a blank proposal" => {
if app.primary.map.unsaved_edits() {
Transition::Replace(SaveEdits::new(
ctx,
app,
"Do you want to save your proposal first?",
true,
Some(Transition::Pop),
Box::new(|ctx, app| {
apply_map_edits(ctx, app, app.primary.map.new_edits());
}),
))
} else {
apply_map_edits(ctx, app, app.primary.map.new_edits());
}),
)),
Transition::Pop
}
}
"save this proposal as..." => Transition::Replace(SaveEdits::new(
ctx,
app,
@ -268,11 +275,8 @@ impl State for EditMode {
}
}
x => {
let idx = x["most recent change #".len()..].parse::<usize>().unwrap();
if let Some(id) = cmd_to_id(
&app.primary.map.get_edits().commands
[app.primary.map.get_edits().commands.len() - idx],
) {
let idx = x["change #".len()..].parse::<usize>().unwrap();
if let Some(id) = cmd_to_id(&app.primary.map.get_edits().commands[idx - 1]) {
return Transition::Push(Warping::new(
ctx,
id.canonical_point(&app.primary).unwrap(),
@ -742,38 +746,42 @@ fn make_changelist(ctx: &mut EventCtx, app: &App) -> Panel {
.container()
.padding(10)
.bg(Color::hex("#5D9630")),
(if !edits.commands.is_empty() {
Btn::svg_def("system/assets/tools/undo.svg").build(ctx, "undo", lctrl(Key::Z))
} else {
Widget::draw_svg_transform(
ctx,
"system/assets/tools/undo.svg",
RewriteColor::ChangeAll(Color::WHITE.alpha(0.5)),
)
})
.centered_vert(),
]),
Text::from_multiline(vec![
Line(format!("{} roads changed", edits.changed_roads.len())),
Line(format!(
"{} intersections changed",
ColorLegend::row(
ctx,
app.cs.edits_layer,
format!(
"{} roads, {} intersections changed",
edits.changed_roads.len(),
edits.original_intersections.len()
)),
])
.draw(ctx),
),
),
];
for (idx, cmd) in edits.commands.iter().rev().take(5).enumerate() {
col.push(
Btn::plaintext(format!("{}) {}", idx + 1, cmd.short_name(&app.primary.map))).build(
ctx,
format!("most recent change #{}", idx + 1),
None,
),
);
}
if edits.commands.len() > 5 {
col.push(format!("{} more...", edits.commands.len()).draw_text(ctx));
col.push(format!("{} more...", edits.commands.len() - 5).draw_text(ctx));
}
for idx in edits.commands.len().max(5) - 5..edits.commands.len() {
let (summary, details) = edits.commands[idx].describe(&app.primary.map);
let mut txt = Text::from(Line(format!("{}) {}", idx + 1, summary)));
for line in details {
txt.add(Line(line).secondary());
}
let btn = Btn::plaintext_custom(format!("change #{}", idx + 1), txt).build_def(ctx, None);
if idx == edits.commands.len() - 1 {
col.push(
Widget::row(vec![
btn,
Btn::plaintext("X")
.build(ctx, "undo", lctrl(Key::Z))
.align_right(),
])
.padding(16)
.outline(2.0, Color::WHITE),
);
} else {
col.push(btn);
}
}
Panel::new(Widget::col(col))

View File

@ -56,6 +56,38 @@ impl EditRoad {
access_restrictions: r.access_restrictions_from_osm(),
}
}
fn diff(&self, other: &EditRoad) -> Vec<String> {
let mut lt = 0;
let mut dir = 0;
for ((lt1, dir1), (lt2, dir2)) in self.lanes_ltr.iter().zip(other.lanes_ltr.iter()) {
if lt1 != lt2 {
lt += 1;
}
if dir1 != dir2 {
dir += 1;
}
}
let mut changes = Vec::new();
if lt == 1 {
changes.push(format!("1 lane type"));
} else if lt > 1 {
changes.push(format!("{} lane types", lt));
}
if dir == 1 {
changes.push(format!("1 lane reversal"));
} else if dir > 1 {
changes.push(format!("{} lane reversal", dir));
}
if self.speed_limit != other.speed_limit {
changes.push(format!("speed limit"));
}
if self.access_restrictions != other.access_restrictions {
changes.push(format!("access restrictions"));
}
changes
}
}
#[derive(Debug, Clone, PartialEq)]
@ -227,10 +259,15 @@ impl EditEffects {
}
impl EditCmd {
pub fn short_name(&self, map: &Map) -> String {
match self {
// TODO Way more details
EditCmd::ChangeRoad { r, .. } => format!("road #{}", r.0),
// (summary, details)
pub fn describe(&self, map: &Map) -> (String, Vec<String>) {
let mut details = Vec::new();
let summary = match self {
EditCmd::ChangeRoad { r, old, new } => {
details = new.diff(old);
format!("road #{}", r.0)
}
// TODO Describe changes
EditCmd::ChangeIntersection { i, new, .. } => match new {
EditIntersection::StopSign(_) => format!("stop sign #{}", i.0),
EditIntersection::TrafficSignal(_) => format!("traffic signal #{}", i.0),
@ -239,7 +276,8 @@ impl EditCmd {
EditCmd::ChangeRouteSchedule { id, .. } => {
format!("reschedule route {}", map.get_br(*id).short_name)
}
}
};
(summary, details)
}
// Must be idempotent

View File

@ -134,6 +134,13 @@ impl Btn {
maybe_tooltip: None,
}
}
pub fn plaintext_custom<I: Into<String>>(label: I, txt: Text) -> BtnBuilder {
BtnBuilder::PlainText {
label: label.into(),
txt,
maybe_tooltip: None,
}
}
pub fn text_fg<I: Into<String>>(label: I) -> BtnBuilder {
let label = label.into();