use abstutil::Timer;
use map_gui::tools::{FilePicker, FileSaver, FileSaverContents};
use widgetry::tools::{ChooseSomething, PopupMsg};
use widgetry::{lctrl, Choice, EventCtx, Key, MultiKey, State, Widget};
use super::save_dialog::SaveDialog;
use super::share::ShareProposal;
use super::{PreserveState, Proposal, Proposals};
use crate::{App, Transition};
impl Proposals {
pub fn to_widget_expanded(&self, ctx: &EventCtx) -> Widget {
let mut col = Vec::new();
for (action, icon, hotkey) in [
("New", "pencil", None),
("Load", "folder", None),
("Save", "save", Some(MultiKey::from(lctrl(Key::S)))),
("Share", "share", None),
("Export GeoJSON", "export", None),
] {
col.push(
ctx.style()
.btn_plain
.icon_text(&format!("system/assets/tools/{icon}.svg"), action)
.hotkey(hotkey)
.build_def(ctx),
);
}
for (idx, proposal) in self.list.iter().enumerate() {
let button = ctx
.style()
.btn_solid_primary
.text(if idx == 0 {
"1 - existing LTNs".to_string()
} else {
format!("{} - {}", idx + 1, proposal.edits.edits_name)
})
.hotkey(Key::NUM_KEYS[idx])
.disabled(idx == self.current)
.build_widget(ctx, &format!("switch to proposal {}", idx));
col.push(Widget::row(vec![
button,
if idx != 0 {
ctx.style()
.btn_close()
.disabled(self.list.len() == 1)
.build_widget(ctx, &format!("hide proposal {}", idx))
} else {
Widget::nothing()
},
]));
if idx == 9 {
break;
}
}
Widget::col(col)
}
pub fn to_widget_collapsed(&self, ctx: &EventCtx) -> Widget {
let mut col = Vec::new();
for (action, icon) in [
("New", "pencil"),
("Load", "folder"),
("Save", "save"),
("Share", "share"),
("Export GeoJSON", "export"),
] {
col.push(
ctx.style()
.btn_plain
.icon(&format!("system/assets/tools/{icon}.svg"))
.build_widget(ctx, action),
);
}
Widget::col(col)
}
pub fn handle_action(
ctx: &mut EventCtx,
app: &mut App,
preserve_state: &PreserveState,
action: &str,
) -> Option<Transition> {
match action {
"New" => {
if app.per_map.proposals.current != 0 {
switch_to_existing_proposal(ctx, app, 0);
}
}
"Load" => {
return Some(Transition::Push(load_picker_ui(
ctx,
app,
preserve_state.clone(),
)));
}
"Save" => {
return Some(Transition::Push(SaveDialog::new_state(
ctx,
app,
preserve_state.clone(),
)));
}
"Share" => {
return Some(Transition::Push(ShareProposal::new_state(ctx, app)));
}
"Export GeoJSON" => {
return Some(Transition::Push(match crate::export::geojson_string(app) {
Ok(contents) => FileSaver::with_default_messages(
ctx,
format!("ltn_{}.geojson", app.per_map.map.get_name().map),
super::start_dir(),
FileSaverContents::String(contents),
),
Err(err) => PopupMsg::new_state(ctx, "Export failed", vec![err.to_string()]),
}));
}
_ => {
if let Some(x) = action.strip_prefix("switch to proposal ") {
let idx = x.parse::<usize>().unwrap();
switch_to_existing_proposal(ctx, app, idx);
} else if let Some(x) = action.strip_prefix("hide proposal ") {
let idx = x.parse::<usize>().unwrap();
if idx == app.per_map.proposals.current {
switch_to_existing_proposal(ctx, app, if idx == 0 { 1 } else { idx - 1 });
}
app.per_map.proposals.list.remove(idx);
if idx < app.per_map.proposals.current {
app.per_map.proposals.current -= 1;
}
} else {
return None;
}
}
}
Some(preserve_state.clone().switch_to_state(ctx, app))
}
}
fn switch_to_existing_proposal(ctx: &mut EventCtx, app: &mut App, idx: usize) {
app.per_map.proposals.current = idx;
app.per_map.map.must_apply_edits(
app.per_map.proposals.get_current().edits.clone(),
&mut Timer::throwaway(),
);
crate::redraw_all_icons(ctx, app);
}
fn load_picker_ui(
ctx: &mut EventCtx,
app: &App,
preserve_state: PreserveState,
) -> Box<dyn State<App>> {
ChooseSomething::new_state(
ctx,
"Load which proposal?",
{
let mut choices = vec!["Load from file on your computer".to_string()];
choices.extend(
abstio::list_all_objects(abstio::path_all_ltn_proposals(
app.per_map.map.get_name(),
))
.into_iter()
.map(abstutil::basename),
);
Choice::strings(choices)
},
Box::new(move |name, ctx, app| {
if name == "Load from file on your computer" {
Transition::Replace(FilePicker::new_state(
ctx,
super::start_dir(),
Box::new(move |ctx, app, maybe_file| {
match maybe_file {
Ok(Some((path, bytes))) => {
match Proposal::load_from_bytes(ctx, app, &path, Ok(bytes)) {
Some(err_state) => Transition::Replace(err_state),
None => preserve_state.switch_to_state(ctx, app),
}
}
Ok(None) => Transition::Pop,
Err(err) => Transition::Replace(PopupMsg::new_state(
ctx,
"Error",
vec![err.to_string()],
)),
}
}),
))
} else {
match Proposal::load_from_path(
ctx,
app,
abstio::path_ltn_proposals(app.per_map.map.get_name(), &name),
) {
Some(err_state) => Transition::Replace(err_state),
None => preserve_state.switch_to_state(ctx, app),
}
}
}),
)
}