mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-10 20:40:05 -06:00
152 lines
6.7 KiB
Rust
152 lines
6.7 KiB
Rust
use super::show::ok_cancel;
|
|
use super::{Dialog, Json, Key, SheetApp, json};
|
|
|
|
/// Draws the Find/Replace form with case, whole-cell and workbook options, requests field focus once, and runs find/replace with the current parameters.
|
|
pub(super) fn find(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dialog) {
|
|
ui.horizontal(|ui| {
|
|
for tab in ["Find", "Replace"] {
|
|
if ui.selectable_label(d.tab == tab, tab).clicked() {
|
|
d.tab = tab.into();
|
|
}
|
|
}
|
|
});
|
|
let mut what = d.values.get("what").and_then(Json::as_str).unwrap_or("").to_string();
|
|
let mut with = d.values.get("with").and_then(Json::as_str).unwrap_or("").to_string();
|
|
ui.horizontal(|ui| {
|
|
let label = ui.label("Find what:");
|
|
let r = ui.add(egui::TextEdit::singleline(&mut what).desired_width(260.0)).labelled_by(label.id);
|
|
// Like Excel, the cursor starts in "Find what" - asked for once, so clicking another field still works.
|
|
if !d.values.contains_key("focused") {
|
|
r.request_focus();
|
|
d.values.insert("focused".into(), json!(true));
|
|
}
|
|
});
|
|
if d.tab == "Replace" {
|
|
ui.horizontal(|ui| {
|
|
let label = ui.label("Replace with:");
|
|
ui.add(egui::TextEdit::singleline(&mut with).desired_width(244.0)).labelled_by(label.id);
|
|
});
|
|
}
|
|
let mut case = d.values.get("matchCase").and_then(Json::as_bool).unwrap_or(false);
|
|
let mut whole = d.values.get("wholeCell").and_then(Json::as_bool).unwrap_or(false);
|
|
let mut wb = d.values.get("within").and_then(Json::as_str) == Some("workbook");
|
|
ui.horizontal(|ui| {
|
|
ui.checkbox(&mut case, "Match case");
|
|
ui.checkbox(&mut whole, "Match entire cell contents");
|
|
ui.checkbox(&mut wb, "Within workbook");
|
|
});
|
|
d.values.insert("what".into(), json!(what));
|
|
d.values.insert("with".into(), json!(with));
|
|
d.values.insert("matchCase".into(), json!(case));
|
|
d.values.insert("wholeCell".into(), json!(whole));
|
|
d.values.insert("within".into(), json!(if wb { "workbook" } else { "sheet" }));
|
|
let base = json!({"what": what, "matchCase": case, "wholeCell": whole, "within": if wb { "workbook" } else { "sheet" }});
|
|
ui.horizontal(|ui| {
|
|
if d.tab == "Replace" {
|
|
if ui.button("Replace All").clicked() {
|
|
let mut p = base.clone();
|
|
p["with"] = json!(with);
|
|
p["all"] = json!(true);
|
|
match app.run("edit.replace", p) {
|
|
Ok(r) => app.toast = Some((format!("All done. We made {} replacements.", r["replaced"]), crate::now_ms())),
|
|
Err(e) => app.message = Some(("Find and Replace".into(), crate::clean_error(&e))),
|
|
}
|
|
}
|
|
if ui.button("Replace").clicked() {
|
|
let mut p = base.clone();
|
|
p["with"] = json!(with);
|
|
p["all"] = json!(false);
|
|
let _ = app.run("edit.replace", p);
|
|
let _ = app.run("edit.find", base.clone());
|
|
}
|
|
}
|
|
if ui.button("Find All").clicked() {
|
|
let mut p = base.clone();
|
|
p["all"] = json!(true);
|
|
d.result = app.run("edit.find", p).ok();
|
|
}
|
|
if ui.button("Find Next").clicked() || ui.input(|i| i.key_pressed(Key::Enter)) {
|
|
if let Err(e) = app.run("edit.find", base.clone()) {
|
|
app.message = Some(("Find and Replace".into(), crate::clean_error(&e)));
|
|
}
|
|
app.grid.ensure_visible = true;
|
|
}
|
|
});
|
|
if let Some(r) = &d.result
|
|
&& let Some(list) = r["results"].as_array()
|
|
{
|
|
ui.separator();
|
|
ui.label(format!("{} cell(s) found", list.len()));
|
|
egui::ScrollArea::vertical().max_height(180.0).show(ui, |ui| {
|
|
for item in list {
|
|
let label = format!(
|
|
"{} {} {}",
|
|
item["sheet"].as_str().unwrap_or(""),
|
|
item["cell"].as_str().unwrap_or(""),
|
|
item["value"].as_str().unwrap_or("")
|
|
);
|
|
if ui.selectable_label(false, label).clicked() {
|
|
if let Some(s) = item["sheet"].as_str() {
|
|
let _ = app.run("sheet.activate", json!({"sheet": s}));
|
|
}
|
|
let _ = app.run("selection.set", json!({"cell": item["cell"]}));
|
|
app.grid.ensure_visible = true;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/// What the Go To dialog lists: the named ranges of every sheet, then the references gone to
|
|
/// before, newest first.
|
|
pub(super) fn go_to_entries(app: &SheetApp) -> Vec<String> {
|
|
app.session
|
|
.active()
|
|
.map(|doc| {
|
|
let mut v = gridcraft_engine::cmd::edit::go_to_names(&doc.wb, doc.wb.active_sheet());
|
|
v.extend(doc.go_to_recent.iter().cloned());
|
|
v
|
|
})
|
|
.unwrap_or_default()
|
|
}
|
|
|
|
/// Draws a virtualized name/reference list and editable reference field, links to Go To Special, and confirms with `edit.goTo`.
|
|
pub(super) fn go_to(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dialog, confirm: &mut bool) {
|
|
let lang = app.ui.language;
|
|
let entries: Vec<String> =
|
|
d.result.as_ref().and_then(Json::as_array).map(|a| a.iter().filter_map(Json::as_str).map(str::to_owned).collect()).unwrap_or_default();
|
|
let current = d.values.get("reference").and_then(Json::as_str).unwrap_or("").to_string();
|
|
// A workbook can hold thousands of names: only the rows in view are laid out.
|
|
let row_height = ui.text_style_height(&egui::TextStyle::Button) + 2.0 * ui.spacing().button_padding.y;
|
|
egui::ScrollArea::vertical().max_height(160.0).auto_shrink([false, true]).show_rows(ui, row_height, entries.len(), |ui, rows| {
|
|
for n in entries.get(rows).unwrap_or_default() {
|
|
let resp = ui.selectable_label(current == *n, n);
|
|
if resp.clicked() || resp.double_clicked() {
|
|
d.values.insert("reference".into(), json!(n));
|
|
}
|
|
if resp.double_clicked() {
|
|
*confirm = true;
|
|
}
|
|
}
|
|
});
|
|
let mut r = d.values.get("reference").and_then(Json::as_str).unwrap_or("").to_string();
|
|
ui.horizontal(|ui| {
|
|
let label = ui.label(lang.tr("Reference:"));
|
|
ui.text_edit_singleline(&mut r).labelled_by(label.id).request_focus();
|
|
});
|
|
d.values.insert("reference".into(), json!(r.clone()));
|
|
if ui.button("Special…").clicked() {
|
|
app.open_dialog("goToSpecial", json!({}));
|
|
return;
|
|
}
|
|
let mut open = true;
|
|
ok_cancel(ui, confirm, &mut open);
|
|
if *confirm {
|
|
app.run_or_alert("edit.goTo", json!({"reference": r, "record": true}));
|
|
app.grid.ensure_visible = true;
|
|
}
|
|
if !open {
|
|
*confirm = true;
|
|
}
|
|
}
|