mirror of
https://github.com/a-b-street/abstreet.git
synced 2025-01-06 22:47:48 +03:00
Undo/redo in the new road editor
This commit is contained in:
parent
8dbb1a47e5
commit
e0871d9db2
@ -1,11 +1,11 @@
|
|||||||
use geom::Distance;
|
use geom::Distance;
|
||||||
use map_gui::render::{Renderable, OUTLINE_THICKNESS};
|
use map_gui::render::{Renderable, OUTLINE_THICKNESS};
|
||||||
use map_model::{
|
use map_model::{
|
||||||
Direction, EditRoad, LaneID, LaneSpec, LaneType, Road, RoadID, NORMAL_LANE_THICKNESS,
|
Direction, EditCmd, EditRoad, LaneID, LaneSpec, LaneType, Road, RoadID, NORMAL_LANE_THICKNESS,
|
||||||
};
|
};
|
||||||
use widgetry::{
|
use widgetry::{
|
||||||
Choice, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome,
|
lctrl, Choice, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line,
|
||||||
Panel, State, Text, TextExt, VerticalAlignment, Widget,
|
Outcome, Panel, State, Text, TextExt, VerticalAlignment, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::app::{App, Transition};
|
use crate::app::{App, Transition};
|
||||||
@ -17,20 +17,18 @@ pub struct RoadEditor {
|
|||||||
top_panel: Panel,
|
top_panel: Panel,
|
||||||
main_panel: Panel,
|
main_panel: Panel,
|
||||||
highlight_selection: (Option<LaneID>, Drawable),
|
highlight_selection: (Option<LaneID>, Drawable),
|
||||||
|
|
||||||
|
// Undo/redo management
|
||||||
|
num_edit_cmds_originally: usize,
|
||||||
|
redo_stack: Vec<EditCmd>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RoadEditor {
|
impl RoadEditor {
|
||||||
pub fn new(ctx: &mut EventCtx, app: &mut App, r: RoadID) -> Box<dyn State<App>> {
|
pub fn new(ctx: &mut EventCtx, app: &mut App, r: RoadID) -> Box<dyn State<App>> {
|
||||||
app.primary.current_selection = None;
|
app.primary.current_selection = None;
|
||||||
|
|
||||||
let top_panel = Panel::new(Widget::row(vec![ctx
|
let num_edit_cmds_originally = app.primary.map.get_edits().commands.len();
|
||||||
.style()
|
let top_panel = make_top_panel(ctx, app, num_edit_cmds_originally, true);
|
||||||
.btn_solid_primary
|
|
||||||
.text("Finish")
|
|
||||||
.hotkey(Key::Escape)
|
|
||||||
.build_def(ctx)]))
|
|
||||||
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
|
|
||||||
.build(ctx);
|
|
||||||
let current_lane = None;
|
let current_lane = None;
|
||||||
let main_panel = make_main_panel(ctx, app, app.primary.map.get_r(r), current_lane);
|
let main_panel = make_main_panel(ctx, app, app.primary.map.get_r(r), current_lane);
|
||||||
let highlight_selection = highlight_current_selection(ctx, app, r, current_lane);
|
let highlight_selection = highlight_current_selection(ctx, app, r, current_lane);
|
||||||
@ -40,6 +38,9 @@ impl RoadEditor {
|
|||||||
top_panel,
|
top_panel,
|
||||||
main_panel,
|
main_panel,
|
||||||
highlight_selection,
|
highlight_selection,
|
||||||
|
|
||||||
|
num_edit_cmds_originally,
|
||||||
|
redo_stack: Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +62,7 @@ impl RoadEditor {
|
|||||||
.commands
|
.commands
|
||||||
.push(app.primary.map.edit_road_cmd(self.r, |new| (f)(new, idx)));
|
.push(app.primary.map.edit_road_cmd(self.r, |new| (f)(new, idx)));
|
||||||
apply_map_edits(ctx, app, edits);
|
apply_map_edits(ctx, app, edits);
|
||||||
|
self.redo_stack.clear();
|
||||||
|
|
||||||
self.current_lane = if let Some(offset) = select_new_lane_offset {
|
self.current_lane = if let Some(offset) = select_new_lane_offset {
|
||||||
Some(app.primary.map.get_r(self.r).lanes_ltr()[((idx as isize) + offset) as usize].0)
|
Some(app.primary.map.get_r(self.r).lanes_ltr()[((idx as isize) + offset) as usize].0)
|
||||||
@ -71,6 +73,12 @@ impl RoadEditor {
|
|||||||
self.main_panel =
|
self.main_panel =
|
||||||
make_main_panel(ctx, app, app.primary.map.get_r(self.r), self.current_lane);
|
make_main_panel(ctx, app, app.primary.map.get_r(self.r), self.current_lane);
|
||||||
self.highlight_selection = highlight_current_selection(ctx, app, self.r, self.current_lane);
|
self.highlight_selection = highlight_current_selection(ctx, app, self.r, self.current_lane);
|
||||||
|
self.top_panel = make_top_panel(
|
||||||
|
ctx,
|
||||||
|
app,
|
||||||
|
self.num_edit_cmds_originally,
|
||||||
|
self.redo_stack.is_empty(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +91,40 @@ impl State<App> for RoadEditor {
|
|||||||
"Finish" => {
|
"Finish" => {
|
||||||
return Transition::Pop;
|
return Transition::Pop;
|
||||||
}
|
}
|
||||||
|
"undo" => {
|
||||||
|
let mut edits = app.primary.map.get_edits().clone();
|
||||||
|
self.redo_stack.push(edits.commands.pop().unwrap());
|
||||||
|
apply_map_edits(ctx, app, edits);
|
||||||
|
|
||||||
|
self.current_lane = None;
|
||||||
|
self.main_panel =
|
||||||
|
make_main_panel(ctx, app, app.primary.map.get_r(self.r), self.current_lane);
|
||||||
|
self.highlight_selection =
|
||||||
|
highlight_current_selection(ctx, app, self.r, self.current_lane);
|
||||||
|
self.top_panel = make_top_panel(
|
||||||
|
ctx,
|
||||||
|
app,
|
||||||
|
self.num_edit_cmds_originally,
|
||||||
|
self.redo_stack.is_empty(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"redo" => {
|
||||||
|
let mut edits = app.primary.map.get_edits().clone();
|
||||||
|
edits.commands.push(self.redo_stack.pop().unwrap());
|
||||||
|
apply_map_edits(ctx, app, edits);
|
||||||
|
|
||||||
|
self.current_lane = None;
|
||||||
|
self.main_panel =
|
||||||
|
make_main_panel(ctx, app, app.primary.map.get_r(self.r), self.current_lane);
|
||||||
|
self.highlight_selection =
|
||||||
|
highlight_current_selection(ctx, app, self.r, self.current_lane);
|
||||||
|
self.top_panel = make_top_panel(
|
||||||
|
ctx,
|
||||||
|
app,
|
||||||
|
self.num_edit_cmds_originally,
|
||||||
|
self.redo_stack.is_empty(),
|
||||||
|
);
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -131,6 +173,7 @@ impl State<App> for RoadEditor {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
apply_map_edits(ctx, app, edits);
|
apply_map_edits(ctx, app, edits);
|
||||||
|
self.redo_stack.clear();
|
||||||
|
|
||||||
assert!(self.current_lane.is_none());
|
assert!(self.current_lane.is_none());
|
||||||
self.current_lane =
|
self.current_lane =
|
||||||
@ -139,6 +182,12 @@ impl State<App> for RoadEditor {
|
|||||||
make_main_panel(ctx, app, app.primary.map.get_r(self.r), self.current_lane);
|
make_main_panel(ctx, app, app.primary.map.get_r(self.r), self.current_lane);
|
||||||
self.highlight_selection =
|
self.highlight_selection =
|
||||||
highlight_current_selection(ctx, app, self.r, self.current_lane);
|
highlight_current_selection(ctx, app, self.r, self.current_lane);
|
||||||
|
self.top_panel = make_top_panel(
|
||||||
|
ctx,
|
||||||
|
app,
|
||||||
|
self.num_edit_cmds_originally,
|
||||||
|
self.redo_stack.is_empty(),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
@ -153,10 +202,17 @@ impl State<App> for RoadEditor {
|
|||||||
new.speed_limit = speed_limit;
|
new.speed_limit = speed_limit;
|
||||||
}));
|
}));
|
||||||
apply_map_edits(ctx, app, edits);
|
apply_map_edits(ctx, app, edits);
|
||||||
|
self.redo_stack.clear();
|
||||||
|
|
||||||
// Lane IDs don't change
|
// Lane IDs don't change
|
||||||
self.main_panel =
|
self.main_panel =
|
||||||
make_main_panel(ctx, app, app.primary.map.get_r(self.r), self.current_lane);
|
make_main_panel(ctx, app, app.primary.map.get_r(self.r), self.current_lane);
|
||||||
|
self.top_panel = make_top_panel(
|
||||||
|
ctx,
|
||||||
|
app,
|
||||||
|
self.num_edit_cmds_originally,
|
||||||
|
self.redo_stack.is_empty(),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
let width = self.main_panel.dropdown_value("width");
|
let width = self.main_panel.dropdown_value("width");
|
||||||
self.modify_current_lane(ctx, app, Some(0), |new, idx| {
|
self.modify_current_lane(ctx, app, Some(0), |new, idx| {
|
||||||
@ -198,6 +254,36 @@ impl State<App> for RoadEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_top_panel(
|
||||||
|
ctx: &mut EventCtx,
|
||||||
|
app: &App,
|
||||||
|
num_edit_cmds_originally: usize,
|
||||||
|
no_redo_cmds: bool,
|
||||||
|
) -> Panel {
|
||||||
|
Panel::new(Widget::row(vec![
|
||||||
|
ctx.style()
|
||||||
|
.btn_solid_primary
|
||||||
|
.text("Finish")
|
||||||
|
.hotkey(Key::Escape)
|
||||||
|
.build_def(ctx),
|
||||||
|
ctx.style()
|
||||||
|
.btn_plain
|
||||||
|
.icon("system/assets/tools/undo.svg")
|
||||||
|
.disabled(app.primary.map.get_edits().commands.len() == num_edit_cmds_originally)
|
||||||
|
.hotkey(lctrl(Key::Z))
|
||||||
|
.build_widget(ctx, "undo"),
|
||||||
|
ctx.style()
|
||||||
|
.btn_plain
|
||||||
|
.icon("system/assets/tools/redo.svg")
|
||||||
|
.disabled(no_redo_cmds)
|
||||||
|
// TODO ctrl+shift+Z!
|
||||||
|
.hotkey(lctrl(Key::Y))
|
||||||
|
.build_widget(ctx, "redo"),
|
||||||
|
]))
|
||||||
|
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
|
||||||
|
.build(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
fn make_main_panel(
|
fn make_main_panel(
|
||||||
ctx: &mut EventCtx,
|
ctx: &mut EventCtx,
|
||||||
app: &App,
|
app: &App,
|
||||||
|
Loading…
Reference in New Issue
Block a user