mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 20:29:04 +03:00
enter signal metadata via a form with multiple textboxes at once. needs
work.
This commit is contained in:
parent
1ad434f3d6
commit
0fd4de749d
@ -6,8 +6,7 @@ use geom::{Angle, Duration, Polygon, Pt2D};
|
||||
|
||||
// TODO Add text to the logo (showing zoom)
|
||||
// TODO Some kind of plot?!
|
||||
// TODO Some popup dialogs?
|
||||
// TODO Something scrolling
|
||||
// TODO Some popup dialogs with form entry, even some scrolling
|
||||
// TODO Loading screen with timer?
|
||||
|
||||
struct App {
|
||||
|
@ -41,6 +41,18 @@ impl UserInput {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn any_key_pressed(&mut self) -> Option<Key> {
|
||||
if self.event_consumed {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Event::KeyPress(key) = self.event {
|
||||
self.consume_event();
|
||||
return Some(key);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn unimportant_key_pressed(&mut self, key: Key, action: &str) -> bool {
|
||||
self.reserve_key(key, action);
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::layout::Widget;
|
||||
use crate::widgets::{Checkbox, PopupMenu};
|
||||
use crate::text;
|
||||
use crate::widgets::{Checkbox, PopupMenu, TextBox};
|
||||
use crate::{
|
||||
Button, Color, Drawable, EventCtx, Filler, GeomBatch, GfxCtx, Histogram, HorizontalAlignment,
|
||||
JustDraw, MultiKey, Plot, RewriteColor, ScreenDims, ScreenPt, ScreenRectangle, Slider, Text,
|
||||
@ -30,6 +31,7 @@ enum WidgetType {
|
||||
Draw(JustDraw),
|
||||
Btn(Button),
|
||||
Checkbox(Checkbox),
|
||||
TextBox(TextBox),
|
||||
Slider(String),
|
||||
Menu(String),
|
||||
Filler(String),
|
||||
@ -289,6 +291,11 @@ impl ManagedWidget {
|
||||
.named(label)
|
||||
}
|
||||
|
||||
pub fn text_entry(ctx: &EventCtx, prefilled: String) -> ManagedWidget {
|
||||
// TODO Hardcoded style, max chars
|
||||
ManagedWidget::new(WidgetType::TextBox(TextBox::new(ctx, 50, prefilled))).bg(text::BG_COLOR)
|
||||
}
|
||||
|
||||
pub(crate) fn duration_plot(plot: Plot<Duration>) -> ManagedWidget {
|
||||
ManagedWidget::new(WidgetType::DurationPlot(plot))
|
||||
}
|
||||
@ -329,6 +336,9 @@ impl ManagedWidget {
|
||||
WidgetType::Checkbox(ref mut checkbox) => {
|
||||
checkbox.event(ctx);
|
||||
}
|
||||
WidgetType::TextBox(ref mut textbox) => {
|
||||
textbox.event(ctx);
|
||||
}
|
||||
WidgetType::Slider(ref name) => {
|
||||
sliders.get_mut(name).unwrap().event(ctx);
|
||||
}
|
||||
@ -364,6 +374,7 @@ impl ManagedWidget {
|
||||
WidgetType::Draw(ref j) => j.draw(g),
|
||||
WidgetType::Btn(ref btn) => btn.draw(g),
|
||||
WidgetType::Checkbox(ref checkbox) => checkbox.draw(g),
|
||||
WidgetType::TextBox(ref textbox) => textbox.draw(g),
|
||||
WidgetType::Slider(ref name) => {
|
||||
if name != "horiz scrollbar" && name != "vert scrollbar" {
|
||||
sliders[name].draw(g);
|
||||
@ -397,6 +408,7 @@ impl ManagedWidget {
|
||||
WidgetType::Draw(ref widget) => widget,
|
||||
WidgetType::Btn(ref widget) => widget,
|
||||
WidgetType::Checkbox(ref widget) => widget,
|
||||
WidgetType::TextBox(ref widget) => widget,
|
||||
WidgetType::Slider(ref name) => &sliders[name],
|
||||
WidgetType::Menu(ref name) => &menus[name],
|
||||
WidgetType::Filler(ref name) => &fillers[name],
|
||||
@ -505,6 +517,9 @@ impl ManagedWidget {
|
||||
WidgetType::Checkbox(ref mut widget) => {
|
||||
widget.set_pos(top_left);
|
||||
}
|
||||
WidgetType::TextBox(ref mut widget) => {
|
||||
widget.set_pos(top_left);
|
||||
}
|
||||
WidgetType::Slider(ref name) => {
|
||||
sliders.get_mut(name).unwrap().set_pos(top_left);
|
||||
}
|
||||
@ -566,6 +581,7 @@ impl ManagedWidget {
|
||||
| WidgetType::Menu(_)
|
||||
| WidgetType::Filler(_)
|
||||
| WidgetType::Checkbox(_)
|
||||
| WidgetType::TextBox(_)
|
||||
| WidgetType::DurationPlot(_)
|
||||
| WidgetType::UsizePlot(_) => {}
|
||||
WidgetType::Histogram(_) => {}
|
||||
@ -597,7 +613,9 @@ impl ManagedWidget {
|
||||
fn find(&self, name: &str) -> Option<&ManagedWidget> {
|
||||
let found = match self.widget {
|
||||
// TODO Consolidate and just do this
|
||||
WidgetType::Draw(_) | WidgetType::Checkbox(_) => self.id == Some(name.to_string()),
|
||||
WidgetType::Draw(_) | WidgetType::Checkbox(_) | WidgetType::TextBox(_) => {
|
||||
self.id == Some(name.to_string())
|
||||
}
|
||||
WidgetType::Btn(ref btn) => btn.action == name,
|
||||
WidgetType::Slider(ref n) => n == name,
|
||||
WidgetType::Menu(ref n) => n == name,
|
||||
@ -623,7 +641,9 @@ impl ManagedWidget {
|
||||
fn find_mut(&mut self, name: &str) -> Option<&mut ManagedWidget> {
|
||||
let found = match self.widget {
|
||||
// TODO Consolidate and just do this
|
||||
WidgetType::Draw(_) | WidgetType::Checkbox(_) => self.id == Some(name.to_string()),
|
||||
WidgetType::Draw(_) | WidgetType::Checkbox(_) | WidgetType::TextBox(_) => {
|
||||
self.id == Some(name.to_string())
|
||||
}
|
||||
WidgetType::Btn(ref btn) => btn.action == name,
|
||||
WidgetType::Slider(ref n) => n == name,
|
||||
WidgetType::Menu(ref n) => n == name,
|
||||
@ -925,6 +945,13 @@ impl Composite {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text_box(&self, name: &str) -> String {
|
||||
match self.find(name).widget {
|
||||
WidgetType::TextBox(ref textbox) => textbox.get_entry(),
|
||||
_ => panic!("{} isn't a textbox", name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filler_rect(&self, name: &str) -> ScreenRectangle {
|
||||
let f = &self.fillers[name];
|
||||
ScreenRectangle::top_left(f.top_left, f.dims)
|
||||
|
@ -14,7 +14,7 @@ pub const INACTIVE_CHOICE_COLOR: Color = Color::grey(0.4);
|
||||
pub const SCALE_LINE_HEIGHT: f64 = 1.2;
|
||||
|
||||
// TODO Don't do this!
|
||||
const MAX_CHAR_WIDTH: f64 = 25.0;
|
||||
pub const MAX_CHAR_WIDTH: f64 = 25.0;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Font {
|
||||
|
@ -24,5 +24,6 @@ pub use self::plot::{Plot, PlotOptions, Series};
|
||||
pub(crate) use self::popup_menu::PopupMenu;
|
||||
pub(crate) use self::screenshot::{screenshot_current, screenshot_everything};
|
||||
pub use self::slider::{ItemSlider, Slider, WarpingItemSlider};
|
||||
pub(crate) use self::text_box::TextBox;
|
||||
pub use self::warper::Warper;
|
||||
pub use self::wizard::{Choice, Wizard, WrappedWizard};
|
||||
|
@ -1,12 +1,9 @@
|
||||
use crate::layout::Widget;
|
||||
use crate::{
|
||||
text, Event, EventCtx, GfxCtx, InputResult, Key, Line, ScreenDims, ScreenPt, Text, UserInput,
|
||||
};
|
||||
use crate::{text, EventCtx, GfxCtx, Key, Line, ScreenDims, ScreenPt, Text};
|
||||
|
||||
// TODO right now, only a single line
|
||||
|
||||
pub struct TextBox {
|
||||
prompt: String,
|
||||
// TODO A rope would be cool.
|
||||
line: String,
|
||||
cursor_x: usize,
|
||||
@ -17,25 +14,65 @@ pub struct TextBox {
|
||||
}
|
||||
|
||||
impl TextBox {
|
||||
pub fn new(ctx: &EventCtx, prompt: &str, prefilled: Option<String>) -> TextBox {
|
||||
let line = prefilled.unwrap_or_else(String::new);
|
||||
let mut tb = TextBox {
|
||||
prompt: prompt.to_string(),
|
||||
cursor_x: line.len(),
|
||||
line,
|
||||
pub fn new(ctx: &EventCtx, max_chars: usize, prefilled: String) -> TextBox {
|
||||
TextBox {
|
||||
cursor_x: prefilled.len(),
|
||||
line: prefilled,
|
||||
shift_pressed: false,
|
||||
|
||||
top_left: ScreenPt::new(0.0, 0.0),
|
||||
dims: ScreenDims::new(0.0, 0.0),
|
||||
};
|
||||
// TODO Assume the dims never exceed the prompt width?
|
||||
tb.dims = tb.get_text().dims(&ctx.prerender.assets);
|
||||
tb
|
||||
dims: ScreenDims::new(
|
||||
(max_chars as f64) * text::MAX_CHAR_WIDTH,
|
||||
ctx.default_line_height(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_text(&self) -> Text {
|
||||
let mut txt = Text::from(Line(&self.prompt).roboto_bold()).with_bg();
|
||||
txt.add(Line(&self.line[0..self.cursor_x]));
|
||||
pub fn event(&mut self, ctx: &mut EventCtx) {
|
||||
if let Some(key) = ctx.input.any_key_pressed() {
|
||||
match key {
|
||||
Key::LeftShift => {
|
||||
self.shift_pressed = true;
|
||||
}
|
||||
Key::LeftArrow => {
|
||||
if self.cursor_x > 0 {
|
||||
self.cursor_x -= 1;
|
||||
}
|
||||
}
|
||||
Key::RightArrow => {
|
||||
self.cursor_x = (self.cursor_x + 1).min(self.line.len());
|
||||
}
|
||||
Key::Backspace => {
|
||||
if self.cursor_x > 0 {
|
||||
self.line.remove(self.cursor_x - 1);
|
||||
self.cursor_x -= 1;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if let Some(c) = key.to_char(self.shift_pressed) {
|
||||
self.line.insert(self.cursor_x, c);
|
||||
self.cursor_x += 1;
|
||||
} else {
|
||||
ctx.input.unconsume_event();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if ctx.input.key_released(Key::LeftShift) {
|
||||
self.shift_pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
g.draw_blocking_text_at_screenspace_topleft(self.calculate_text(), self.top_left);
|
||||
}
|
||||
|
||||
pub fn get_entry(&self) -> String {
|
||||
self.line.clone()
|
||||
}
|
||||
|
||||
fn calculate_text(&self) -> Text {
|
||||
let mut txt = Text::from(Line(&self.line[0..self.cursor_x]));
|
||||
if self.cursor_x < self.line.len() {
|
||||
// TODO This "cursor" looks awful!
|
||||
txt.append_all(vec![
|
||||
@ -48,45 +85,6 @@ impl TextBox {
|
||||
}
|
||||
txt
|
||||
}
|
||||
|
||||
pub fn event(&mut self, input: &mut UserInput) -> InputResult<()> {
|
||||
let maybe_ev = input.use_event_directly();
|
||||
if maybe_ev.is_none() {
|
||||
return InputResult::StillActive;
|
||||
}
|
||||
let ev = maybe_ev.unwrap();
|
||||
|
||||
if ev == Event::KeyPress(Key::Escape) {
|
||||
return InputResult::Canceled;
|
||||
} else if ev == Event::KeyPress(Key::Enter) {
|
||||
return InputResult::Done(self.line.clone(), ());
|
||||
} else if ev == Event::KeyPress(Key::LeftShift) {
|
||||
self.shift_pressed = true;
|
||||
} else if ev == Event::KeyRelease(Key::LeftShift) {
|
||||
self.shift_pressed = false;
|
||||
} else if ev == Event::KeyPress(Key::LeftArrow) {
|
||||
if self.cursor_x > 0 {
|
||||
self.cursor_x -= 1;
|
||||
}
|
||||
} else if ev == Event::KeyPress(Key::RightArrow) {
|
||||
self.cursor_x = (self.cursor_x + 1).min(self.line.len());
|
||||
} else if ev == Event::KeyPress(Key::Backspace) {
|
||||
if self.cursor_x > 0 {
|
||||
self.line.remove(self.cursor_x - 1);
|
||||
self.cursor_x -= 1;
|
||||
}
|
||||
} else if let Event::KeyPress(key) = ev {
|
||||
if let Some(c) = key.to_char(self.shift_pressed) {
|
||||
self.line.insert(self.cursor_x, c);
|
||||
self.cursor_x += 1;
|
||||
}
|
||||
};
|
||||
InputResult::StillActive
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
g.draw_blocking_text_at_screenspace_topleft(self.get_text(), self.top_left);
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for TextBox {
|
||||
|
@ -1,15 +1,14 @@
|
||||
use crate::widgets::text_box::TextBox;
|
||||
use crate::widgets::PopupMenu;
|
||||
use crate::{
|
||||
hotkey, layout, Button, Color, Composite, EventCtx, GfxCtx, HorizontalAlignment, InputResult,
|
||||
Key, Line, ManagedWidget, MultiKey, Outcome, Text, VerticalAlignment,
|
||||
hotkey, Button, Color, Composite, EventCtx, GfxCtx, HorizontalAlignment, InputResult, Key,
|
||||
Line, ManagedWidget, MultiKey, Outcome, Text, VerticalAlignment,
|
||||
};
|
||||
use abstutil::Cloneable;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub struct Wizard {
|
||||
alive: bool,
|
||||
tb: Option<TextBox>,
|
||||
tb_comp: Option<Composite>,
|
||||
menu_comp: Option<Composite>,
|
||||
ack: Option<Composite>,
|
||||
|
||||
@ -21,7 +20,7 @@ impl Wizard {
|
||||
pub fn new() -> Wizard {
|
||||
Wizard {
|
||||
alive: true,
|
||||
tb: None,
|
||||
tb_comp: None,
|
||||
menu_comp: None,
|
||||
ack: None,
|
||||
confirmed_state: Vec::new(),
|
||||
@ -32,8 +31,8 @@ impl Wizard {
|
||||
if let Some(ref comp) = self.menu_comp {
|
||||
comp.draw(g);
|
||||
}
|
||||
if let Some(ref tb) = self.tb {
|
||||
tb.draw(g);
|
||||
if let Some(ref comp) = self.tb_comp {
|
||||
comp.draw(g);
|
||||
}
|
||||
if let Some(ref s) = self.ack {
|
||||
s.draw(g);
|
||||
@ -82,30 +81,69 @@ impl Wizard {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.tb.is_none() {
|
||||
self.tb = Some(TextBox::new(ctx, query, prefilled));
|
||||
if self.tb_comp.is_none() {
|
||||
self.tb_comp = Some(
|
||||
Composite::new(
|
||||
ManagedWidget::col(vec![
|
||||
ManagedWidget::row(vec![
|
||||
ManagedWidget::draw_text(ctx, Text::from(Line(query).roboto_bold())),
|
||||
// TODO nice text button
|
||||
ManagedWidget::btn(Button::text_bg(
|
||||
Text::from(Line("X").fg(Color::BLACK)),
|
||||
Color::WHITE,
|
||||
Color::ORANGE,
|
||||
hotkey(Key::Escape),
|
||||
"quit",
|
||||
ctx,
|
||||
))
|
||||
.margin(5)
|
||||
.align_right(),
|
||||
]),
|
||||
ManagedWidget::text_entry(ctx, prefilled.unwrap_or_else(String::new))
|
||||
.named("input"),
|
||||
ManagedWidget::btn(Button::text_bg(
|
||||
Text::from(Line("Done").fg(Color::BLACK)),
|
||||
Color::WHITE,
|
||||
Color::ORANGE,
|
||||
hotkey(Key::Enter),
|
||||
"done",
|
||||
ctx,
|
||||
)),
|
||||
])
|
||||
.bg(Color::grey(0.4))
|
||||
.outline(5.0, Color::WHITE)
|
||||
.padding(5),
|
||||
)
|
||||
.build(ctx),
|
||||
);
|
||||
}
|
||||
layout::stack_vertically(
|
||||
layout::ContainerOrientation::Centered,
|
||||
ctx,
|
||||
vec![self.tb.as_mut().unwrap()],
|
||||
);
|
||||
|
||||
match self.tb.as_mut().unwrap().event(&mut ctx.input) {
|
||||
InputResult::StillActive => None,
|
||||
InputResult::Canceled => {
|
||||
self.alive = false;
|
||||
None
|
||||
}
|
||||
InputResult::Done(line, _) => {
|
||||
self.tb = None;
|
||||
if let Some(result) = parser(line.clone()) {
|
||||
Some(result)
|
||||
} else {
|
||||
println!("Invalid input {}", line);
|
||||
None
|
||||
assert!(self.alive);
|
||||
|
||||
// Otherwise, we try to use one event for two inputs potentially
|
||||
if ctx.input.has_been_consumed() {
|
||||
return None;
|
||||
}
|
||||
|
||||
match self.tb_comp.as_mut().unwrap().event(ctx) {
|
||||
Some(Outcome::Clicked(x)) => match x.as_ref() {
|
||||
"quit" => {
|
||||
self.alive = false;
|
||||
self.tb_comp = None;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
"done" => {
|
||||
let line = self.tb_comp.take().unwrap().text_box("input");
|
||||
if let Some(result) = parser(line.clone()) {
|
||||
Some(result)
|
||||
} else {
|
||||
println!("Invalid input {}", line);
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -399,7 +437,7 @@ impl<'a, 'b> WrappedWizard<'a, 'b> {
|
||||
|
||||
// If the control flow through a wizard block needs to change, might need to call this.
|
||||
pub fn reset(&mut self) {
|
||||
assert!(self.wizard.tb.is_none());
|
||||
assert!(self.wizard.tb_comp.is_none());
|
||||
assert!(self.wizard.menu_comp.is_none());
|
||||
assert!(self.wizard.ack.is_none());
|
||||
self.wizard.confirmed_state.clear();
|
||||
|
@ -177,7 +177,12 @@ impl State for TrafficSignalEditor {
|
||||
);
|
||||
}
|
||||
"Edit metadata" => {
|
||||
return Transition::Push(edit_md(self.i));
|
||||
// TODO Not sure which one I prefer usability or code wise...
|
||||
if true {
|
||||
return Transition::Push(Box::new(EditMetadata::new(ctx, app, self.i)));
|
||||
} else {
|
||||
return Transition::Push(edit_md(self.i));
|
||||
}
|
||||
}
|
||||
"Export" => {
|
||||
if orig_signal.observation_md.is_none() {
|
||||
@ -879,6 +884,140 @@ impl State for PreviewTrafficSignal {
|
||||
}
|
||||
}
|
||||
|
||||
struct EditMetadata {
|
||||
composite: Composite,
|
||||
}
|
||||
|
||||
impl EditMetadata {
|
||||
fn new(ctx: &mut EventCtx, app: &App, i: IntersectionID) -> EditMetadata {
|
||||
let default = traffic_signals::Metadata {
|
||||
author: "Anonymous".to_string(),
|
||||
datetime: "MM/DD/YYYY HH:MM:SS".to_string(),
|
||||
notes: "no notes".to_string(),
|
||||
};
|
||||
let prev_observed = app
|
||||
.primary
|
||||
.map
|
||||
.get_traffic_signal(i)
|
||||
.observation_md
|
||||
.clone()
|
||||
.unwrap_or_else(|| default.clone());
|
||||
// TODO Always ask this?
|
||||
let prev_audited = app
|
||||
.primary
|
||||
.map
|
||||
.get_traffic_signal(i)
|
||||
.audit_md
|
||||
.clone()
|
||||
.unwrap_or_else(|| default.clone());
|
||||
|
||||
EditMetadata {
|
||||
composite: Composite::new(
|
||||
ManagedWidget::col(vec![
|
||||
ManagedWidget::row(vec![
|
||||
ManagedWidget::draw_text(
|
||||
ctx,
|
||||
Text::from(Line("Metadata about the traffic signal").roboto_bold()),
|
||||
),
|
||||
WrappedComposite::text_button(ctx, "X", hotkey(Key::Escape)).align_right(),
|
||||
]),
|
||||
ManagedWidget::draw_text(ctx, Text::from(Line("The mapper").roboto_bold())),
|
||||
ManagedWidget::draw_text(
|
||||
ctx,
|
||||
Text::from(Line(
|
||||
"Who mapped this signal? (Feel free to remain anonymous.)",
|
||||
)),
|
||||
),
|
||||
ManagedWidget::text_entry(ctx, prev_observed.author).named("observed author"),
|
||||
ManagedWidget::draw_text(
|
||||
ctx,
|
||||
Text::from(Line("When was this signal mapped? TODO format")),
|
||||
),
|
||||
ManagedWidget::text_entry(ctx, prev_observed.datetime)
|
||||
.named("observed datetime"),
|
||||
ManagedWidget::draw_text(
|
||||
ctx,
|
||||
Text::from(Line("Any other observations about the signal?")),
|
||||
),
|
||||
ManagedWidget::text_entry(ctx, prev_observed.notes).named("observed notes"),
|
||||
ManagedWidget::draw_text(
|
||||
ctx,
|
||||
Text::from(
|
||||
Line("The last person to audit the mapped signal").roboto_bold(),
|
||||
),
|
||||
),
|
||||
ManagedWidget::draw_text(
|
||||
ctx,
|
||||
Text::from(Line(
|
||||
"Who audited this signal? (Feel free to remain anonymous.)",
|
||||
)),
|
||||
),
|
||||
ManagedWidget::text_entry(ctx, prev_audited.author).named("audited author"),
|
||||
ManagedWidget::draw_text(
|
||||
ctx,
|
||||
Text::from(Line("When was this signal audited? TODO format")),
|
||||
),
|
||||
ManagedWidget::text_entry(ctx, prev_audited.datetime).named("audited datetime"),
|
||||
ManagedWidget::draw_text(
|
||||
ctx,
|
||||
Text::from(Line("Any other notes about auditing the signal?")),
|
||||
),
|
||||
ManagedWidget::text_entry(ctx, prev_audited.notes).named("audited notes"),
|
||||
WrappedComposite::text_bg_button(ctx, "Done", hotkey(Key::Enter))
|
||||
.centered_horiz(),
|
||||
])
|
||||
.bg(colors::PANEL_BG),
|
||||
)
|
||||
.build(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State for EditMetadata {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||
match self.composite.event(ctx) {
|
||||
Some(Outcome::Clicked(x)) => match x.as_ref() {
|
||||
"X" => {
|
||||
return Transition::Pop;
|
||||
}
|
||||
"Done" => {
|
||||
// This feels like overkill...
|
||||
let observed = traffic_signals::Metadata {
|
||||
author: self.composite.text_box("observed author"),
|
||||
datetime: self.composite.text_box("observed datetime"),
|
||||
notes: self.composite.text_box("observed notes"),
|
||||
};
|
||||
let audited = traffic_signals::Metadata {
|
||||
author: self.composite.text_box("audited author"),
|
||||
datetime: self.composite.text_box("audited datetime"),
|
||||
notes: self.composite.text_box("audited notes"),
|
||||
};
|
||||
return Transition::PopWithData(Box::new(move |state, app, ctx| {
|
||||
let editor = state.downcast_mut::<TrafficSignalEditor>().unwrap();
|
||||
let orig_signal = app.primary.map.get_traffic_signal(editor.i);
|
||||
let mut new_signal = orig_signal.clone();
|
||||
new_signal.observation_md = Some(observed);
|
||||
new_signal.audit_md = Some(audited);
|
||||
|
||||
editor.command_stack.push(orig_signal.clone());
|
||||
editor.redo_stack.clear();
|
||||
editor.top_panel = make_top_panel(ctx, app, true, false);
|
||||
change_traffic_signal(new_signal, app, ctx);
|
||||
}));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _: &App) {
|
||||
self.composite.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
fn edit_md(i: IntersectionID) -> Box<dyn State> {
|
||||
WizardState::new(Box::new(move |wiz, ctx, app| {
|
||||
let mut wizard = wiz.wrap(ctx);
|
||||
|
Loading…
Reference in New Issue
Block a user