Ely Office: six offline-first desktop apps in Rust and GPUI
ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git), ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
This commit is contained in:
817 files changed
+143101
No files matched your search
@@ -0,0 +1,142 @@
|
||||
//! The sheet dialogs: rename, show, delete (TABLE-01).
|
||||
|
||||
use ely_gpui_component::{
|
||||
buttons::{Button, ButtonVariant},
|
||||
forms::{Choice, Select, TextInput},
|
||||
overlays::{Dialog as Card, PromptDialog},
|
||||
};
|
||||
use ely_lok::sheet::References;
|
||||
use ely_office::prelude::*;
|
||||
use gpui::{App, Entity, ParentElement as _, SharedString, Window};
|
||||
|
||||
use super::Dialog;
|
||||
use crate::book::Book;
|
||||
|
||||
/// Why `name` cannot name a sheet, as a message key.
|
||||
fn refused(name: &str, names: &[String]) -> Option<&'static str> {
|
||||
if name.is_empty() {
|
||||
return Some("table.rename.empty");
|
||||
}
|
||||
let bad = name.contains([':', '\\', '/', '?', '*', '[', ']'])
|
||||
|| name.starts_with('\'')
|
||||
|| name.ends_with('\'');
|
||||
if bad {
|
||||
return Some("table.rename.invalid");
|
||||
}
|
||||
names
|
||||
.iter()
|
||||
.any(|taken| taken.to_lowercase() == name.to_lowercase())
|
||||
.then_some("table.rename.taken")
|
||||
}
|
||||
|
||||
pub(crate) fn rename(
|
||||
this: Entity<Book>,
|
||||
(index, field, names): (usize, &Entity<TextInput>, Vec<String>),
|
||||
dismiss: impl Fn(&mut Window, &mut App) + 'static,
|
||||
cx: &App,
|
||||
) -> PromptDialog {
|
||||
let current = names.get(index).cloned().unwrap_or_default();
|
||||
let others: Vec<String> = names.into_iter().filter(|name| *name != current).collect();
|
||||
let title = Book::text(cx, "table.rename.title", &[("name", ¤t)]);
|
||||
let messages: Vec<(&str, SharedString)> = [
|
||||
"table.rename.empty",
|
||||
"table.rename.invalid",
|
||||
"table.rename.taken",
|
||||
]
|
||||
.into_iter()
|
||||
.map(|key| (key, Book::text(cx, key, &[])))
|
||||
.collect();
|
||||
PromptDialog::new("table-rename", title, field, dismiss)
|
||||
.label(Book::text(cx, "table.rename.label", &[]))
|
||||
.submit(Book::text(cx, "table.size.apply", &[]))
|
||||
.check(move |text| match refused(text, &others) {
|
||||
Some(key) => Err(messages
|
||||
.iter()
|
||||
.find(|(known, _)| *known == key)
|
||||
.map(|(_, message)| message.clone())
|
||||
.expect("every refusal has a message")),
|
||||
None => Ok(()),
|
||||
})
|
||||
.on_submit(move |text, _, cx| {
|
||||
let name = text.to_string();
|
||||
this.update(cx, |book, cx| book.rename_asked(&name, cx))
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn show(
|
||||
this: Entity<Book>,
|
||||
(tab, hidden, chosen): (u64, &[String], &str),
|
||||
dismiss: impl Fn(&mut Window, &mut App) + 'static,
|
||||
cx: &App,
|
||||
) -> Card {
|
||||
let choices = hidden
|
||||
.iter()
|
||||
.map(|name| Choice::new(name.clone(), name.clone()));
|
||||
let picked = this.clone();
|
||||
let select = Select::new("table-show-pick", choices)
|
||||
.selected(chosen.to_string())
|
||||
.on_change(move |value, _, cx| {
|
||||
picked.update(cx, |book, cx| {
|
||||
if let Some(Dialog::Show { chosen, .. }) = &mut book.dialog {
|
||||
*chosen = value.to_string();
|
||||
}
|
||||
cx.notify();
|
||||
})
|
||||
});
|
||||
let label = Book::text(cx, "table.show.go", &[]);
|
||||
Card::new(
|
||||
"table-show",
|
||||
Book::text(cx, "table.show.title", &[]),
|
||||
dismiss,
|
||||
)
|
||||
.child(select)
|
||||
.action(ely_office::cancel(cx))
|
||||
.action(move |_| {
|
||||
Button::new("table-show-go", label)
|
||||
.variant(ButtonVariant::Primary)
|
||||
.on_click(move |_, _, cx| {
|
||||
this.update(cx, |book, cx| {
|
||||
let Some(Dialog::Show { chosen, .. }) = book.dialog.take() else {
|
||||
return;
|
||||
};
|
||||
book.run_in(Some(tab), "sheet.show", &[&chosen], cx);
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn delete(
|
||||
this: Entity<Book>,
|
||||
(name, references): (&str, &References),
|
||||
dismiss: impl Fn(&mut Window, &mut App) + 'static,
|
||||
cx: &App,
|
||||
) -> Card {
|
||||
let mut detail = if references.formulas + references.names == 0 {
|
||||
Book::text(cx, "table.delete.alone", &[])
|
||||
} else {
|
||||
Book::text(
|
||||
cx,
|
||||
"table.delete.refs",
|
||||
&[
|
||||
("formulas", &references.formulas.to_string()),
|
||||
("first", &references.first),
|
||||
("names", &references.names.to_string()),
|
||||
],
|
||||
)
|
||||
};
|
||||
if references.dynamic > 0 {
|
||||
let count = references.dynamic.to_string();
|
||||
let dynamic = Book::text(cx, "table.delete.dynamic", &[("count", &count)]);
|
||||
detail = format!("{detail} {dynamic}").into();
|
||||
}
|
||||
let label = Book::text(cx, "table.delete.go", &[]);
|
||||
let title = Book::text(cx, "table.delete.title", &[("name", name)]);
|
||||
Card::new("table-delete", title, dismiss)
|
||||
.detail(detail)
|
||||
.action(ely_office::cancel(cx))
|
||||
.action(move |_| {
|
||||
Button::new("table-delete-go", label)
|
||||
.variant(ButtonVariant::Danger)
|
||||
.on_click(move |_, _, cx| this.update(cx, |book, cx| book.delete_asked(cx)))
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user