mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-03 00:41:56 -06:00
UI: Python mode starts on no draft typed inside a spill
Python mode refuses a cell inside another cell's spill, but a draft typed on a spill child could still switch to it with Ctrl+Alt+Shift+P, the PY badge, ui.pythonMode or =PY then Tab, and Ctrl+Enter then wrote code into the spill. toggle_python_mode and the formula bar's switch now check the draft's own cell (draft_in_spill) and keep such a draft an ordinary entry. Tests: python_mode_does_not_start_on_a_draft_typed_inside_a_spill (control) and a_draft_typed_inside_a_spill_does_not_enter_python_mode (keys); both fail without the fix.
This commit is contained in:
@@ -321,6 +321,9 @@ impl SheetApp {
|
||||
/// Ctrl+Alt+Shift+P (and the PY badge): Python mode on or off. An edit in progress keeps its
|
||||
/// text; with no edit, Python mode starts on the active cell.
|
||||
pub fn toggle_python_mode(&mut self) {
|
||||
if self.editor.as_ref().is_some_and(|ed| !ed.python && self.draft_in_spill(ed)) {
|
||||
return;
|
||||
}
|
||||
match self.editor.as_mut() {
|
||||
Some(ed) if ed.python => ed.leave_python(),
|
||||
Some(ed) => ed.enter_python(),
|
||||
@@ -329,6 +332,14 @@ impl SheetApp {
|
||||
self.session.mode = gridcraft_engine::Mode::Edit;
|
||||
}
|
||||
|
||||
/// Whether a draft's cell lies inside another cell's spill. A draft typed there stays an
|
||||
/// ordinary entry: Python mode starts on no cell inside a spill, as the formula bar's entry
|
||||
/// paths don't.
|
||||
pub(crate) fn draft_in_spill(&self, ed: &editor::EditState) -> bool {
|
||||
let doc = self.session.documents().iter().find(|d| d.uid == ed.doc);
|
||||
doc.and_then(|d| d.wb.sheet(ed.sheet)).is_some_and(|sh| sh.spill_anchor(ed.cell).is_some())
|
||||
}
|
||||
|
||||
/// Commits a Python mode edit through `python.set`, on the sheet the edit started on; with no
|
||||
/// code left, a Python cell is cleared through `cell.set`. When the command refuses (code
|
||||
/// longer than a cell holds), its message shows and the edit stays open with the code as
|
||||
|
||||
@@ -1233,3 +1233,21 @@ fn a_draft_typed_over_a_hidden_python_cell_stays_open_through_commands() {
|
||||
assert_eq!(b2.map(|c| (c.input_text(), c.py.is_some())), Some(("7".to_string(), false)), "the typed entry replaces the Python cell");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn python_mode_does_not_start_on_a_draft_typed_inside_a_spill() {
|
||||
// Typing on a spill child is an ordinary entry; switching that draft to Python mode would let
|
||||
// Ctrl+Enter write code into a cell another cell's result spills over, which every other way
|
||||
// into Python mode refuses.
|
||||
let mut session = gridcraft_engine::Session::new();
|
||||
session.new_workbook();
|
||||
session.execute("cell.set", json!({"cell": "A1", "input": "=SEQUENCE(3)"})).unwrap();
|
||||
session.execute("selection.set", json!({"cell": "A2"})).unwrap();
|
||||
let mut app = SheetApp::new(session, crate::Services::default());
|
||||
let ctx = egui::Context::default();
|
||||
call(&mut app, &ctx, "ui.edit.begin", json!({"text": "5"}));
|
||||
execute(&mut app, &ctx, "ui.pythonMode", json!({"on": true}));
|
||||
assert!(app.editor.as_ref().is_some_and(|e| !e.python && e.text == "5"), "the draft stays an ordinary entry");
|
||||
call(&mut app, &ctx, "ui.edit.commit", json!({"move": "none"}));
|
||||
let a2 = app.session.active().and_then(|d| d.wb.sheet(0)).and_then(|sh| sh.cell(gridcraft_engine::core::CellRef::new(1, 0)).cloned());
|
||||
assert_eq!(a2.map(|c| (c.input_text(), c.py.is_some())), Some(("5".to_string(), false)));
|
||||
}
|
||||
|
||||
@@ -518,7 +518,7 @@ pub fn editor_widget(app: &mut SheetApp, ui: &mut egui::Ui, id: egui::Id, font:
|
||||
show_editor_suggestions(ui, EditorSuggestion { app, id, resp: &resp, ed: &mut ed, galley: &output.galley, tokens: &t, mine });
|
||||
let lines = output.galley.rows.len().max(1);
|
||||
let caret = Some(output.galley.pos_from_cursor(egui::text::CCursor::new(ed.caret)).translate(output.galley_pos.to_vec2()));
|
||||
if to_python {
|
||||
if to_python && !app.draft_in_spill(&ed) {
|
||||
ed.enter_python();
|
||||
app.editor = Some(ed);
|
||||
app.session.mode = gridcraft_engine::Mode::Edit;
|
||||
|
||||
@@ -126,6 +126,25 @@ fn typing_py_then_tab_enters_python_mode_as_in_excel() {
|
||||
assert_eq!(mode(&h), None);
|
||||
}
|
||||
|
||||
/// A cell inside a spill: typing there is an ordinary entry, as on any spill child, and neither
|
||||
/// `=PY` then Tab nor the shortcut turns that draft into code for the cell, as Python mode starts
|
||||
/// on no cell inside a spill.
|
||||
#[test]
|
||||
fn a_draft_typed_inside_a_spill_does_not_enter_python_mode() {
|
||||
let mut s = blank();
|
||||
s.execute("cell.set", json!({"cell": "A1", "input": "=SEQUENCE(3)"})).unwrap();
|
||||
s.execute("selection.set", json!({"cell": "A2"})).unwrap();
|
||||
let mut h = harness(s);
|
||||
type_text(&mut h, "=PY");
|
||||
assert_eq!(mode(&h), Some(false), "typing starts an ordinary entry");
|
||||
send(&mut h, press(Key::Tab, Modifiers::NONE));
|
||||
assert_eq!(mode(&h), Some(false), "=PY then Tab");
|
||||
shortcut(&mut h);
|
||||
assert_eq!(mode(&h), Some(false), "the shortcut");
|
||||
ctrl_enter(&mut h);
|
||||
assert_eq!(code(&h, "A2"), None, "no code is written into the spill");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enter_and_tab_edit_the_code_and_ctrl_enter_commits_through_python_set() {
|
||||
let mut h = harness(blank());
|
||||
|
||||
Reference in New Issue
Block a user