mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-24 10:46:50 -06:00
Expose basic file operations in the shared ribbon (#93)
This commit is contained in:
@@ -132,6 +132,7 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) {
|
|||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.spacing_mut().item_spacing.x = 2.0;
|
ui.spacing_mut().item_spacing.x = 2.0;
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
|
file_menu(app, ui);
|
||||||
let mut tabs: Vec<&str> = TABS.to_vec();
|
let mut tabs: Vec<&str> = TABS.to_vec();
|
||||||
let ctx_tabs = contextual_tabs(app);
|
let ctx_tabs = contextual_tabs(app);
|
||||||
tabs.extend(ctx_tabs.iter());
|
tabs.extend(ctx_tabs.iter());
|
||||||
@@ -202,6 +203,32 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn file_menu(app: &mut SheetApp, ui: &mut Ui) {
|
||||||
|
let t = Tokens::get(ui.ctx());
|
||||||
|
let label = egui::RichText::new("File").font(theme::ui_font(13.5)).color(t.accent);
|
||||||
|
let file = ui.add_sized(vec2(46.0, 30.0), egui::Button::new(label).frame(false));
|
||||||
|
egui::Popup::menu(&file).show(|ui| {
|
||||||
|
for (label, id, needs_document) in [
|
||||||
|
("New Workbook", "file.new", false),
|
||||||
|
("Open…", "file.open", false),
|
||||||
|
("Save", "file.save", true),
|
||||||
|
("Save As…", "file.saveAs", true),
|
||||||
|
("Close", "file.close", true),
|
||||||
|
] {
|
||||||
|
if id == "file.save" || id == "file.close" {
|
||||||
|
ui.separator();
|
||||||
|
}
|
||||||
|
if ui.add_enabled(!needs_document || app.session.active().is_some(), egui::Button::new(label)).clicked() {
|
||||||
|
// File operations must include the cell the user is still editing.
|
||||||
|
if app.commit_edit(0, 0, false, false) {
|
||||||
|
app.run_or_alert(id, json!({}));
|
||||||
|
}
|
||||||
|
ui.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn contextual_tabs(app: &SheetApp) -> Vec<&'static str> {
|
fn contextual_tabs(app: &SheetApp) -> Vec<&'static str> {
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
if let Some(d) = app.session.active()
|
if let Some(d) = app.session.active()
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
//! The shared ribbon exposes basic file operations without requiring a native menu.
|
||||||
|
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use egui_kittest::kittest::Queryable as _;
|
||||||
|
use gridcraft_engine::{Session, core::CellRef, core::Value};
|
||||||
|
use gridcraft_ui_egui::{Services, SheetApp};
|
||||||
|
|
||||||
|
fn harness(services: Services) -> egui_kittest::Harness<'static, SheetApp> {
|
||||||
|
let mut session = Session::new();
|
||||||
|
session.new_workbook();
|
||||||
|
let mut h = egui_kittest::Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_ui_state(
|
||||||
|
|ui, app: &mut SheetApp| {
|
||||||
|
let ctx = ui.ctx().clone();
|
||||||
|
app.logic(&ctx);
|
||||||
|
app.ui(ui);
|
||||||
|
},
|
||||||
|
SheetApp::new(session, services),
|
||||||
|
);
|
||||||
|
h.run_steps(4);
|
||||||
|
h
|
||||||
|
}
|
||||||
|
|
||||||
|
fn choose(h: &mut egui_kittest::Harness<'static, SheetApp>, label: &str) {
|
||||||
|
h.get_by_label("File").click();
|
||||||
|
h.run_steps(3);
|
||||||
|
h.get_by_label(label).click();
|
||||||
|
h.run_steps(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_and_open_are_visible_even_when_the_ribbon_is_collapsed() {
|
||||||
|
let opened = Arc::new(Mutex::new(0));
|
||||||
|
let capture = opened.clone();
|
||||||
|
let mut h = harness(Services {
|
||||||
|
pick_open: Some(Box::new(move || {
|
||||||
|
*capture.lock().unwrap() += 1;
|
||||||
|
None
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
h.state_mut().ui.ribbon_tab = "Data".into();
|
||||||
|
h.state_mut().ui.ribbon_collapsed = true;
|
||||||
|
h.run_steps(2);
|
||||||
|
choose(&mut h, "New Workbook");
|
||||||
|
assert_eq!(h.state().session.documents().len(), 2);
|
||||||
|
choose(&mut h, "Open…");
|
||||||
|
assert_eq!(*opened.lock().unwrap(), 1, "Open invokes the platform file picker");
|
||||||
|
assert_eq!(h.state().ui.ribbon_tab, "Data");
|
||||||
|
assert!(h.state().ui.ribbon_collapsed);
|
||||||
|
assert!(h.query_by_label("Open…").is_none(), "choosing an action dismisses File");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn save_actions_include_the_cell_draft_and_close_keeps_the_unsaved_prompt() {
|
||||||
|
let downloads = Arc::new(Mutex::new(Vec::<Vec<u8>>::new()));
|
||||||
|
let capture = downloads.clone();
|
||||||
|
let mut h = harness(Services { download: Some(Box::new(move |_, bytes| capture.lock().unwrap().push(bytes.to_vec()))), ..Default::default() });
|
||||||
|
h.input_mut().events.push(egui::Event::Text("123".into()));
|
||||||
|
h.run_steps(2);
|
||||||
|
assert!(h.state().editor.is_some());
|
||||||
|
choose(&mut h, "Save");
|
||||||
|
choose(&mut h, "Save As…");
|
||||||
|
let saved = downloads.lock().unwrap();
|
||||||
|
assert_eq!(saved.len(), 2);
|
||||||
|
for bytes in saved.iter() {
|
||||||
|
let (wb, _) = gridcraft_engine::io::open_bytes("saved.xlsx", bytes).unwrap();
|
||||||
|
assert_eq!(wb.active().unwrap().value(CellRef::new(0, 0)), Value::Number(123.0));
|
||||||
|
}
|
||||||
|
drop(saved);
|
||||||
|
assert!(h.state().editor.is_none());
|
||||||
|
choose(&mut h, "Close");
|
||||||
|
assert_eq!(h.state().session.documents().len(), 1);
|
||||||
|
assert_eq!(h.state().dialog.as_ref().map(|d| d.name()), Some("saveChanges"));
|
||||||
|
assert_eq!(h.state().session.active().unwrap().wb.active().unwrap().value(CellRef::new(0, 0)), Value::Number(123.0));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user