diff --git a/crates/ui-egui/src/panels/character.rs b/crates/ui-egui/src/panels/character.rs index 7156d0003..9a452a2ae 100644 --- a/crates/ui-egui/src/panels/character.rs +++ b/crates/ui-egui/src/panels/character.rs @@ -775,16 +775,18 @@ fn style_toggle(ui: &mut Ui, g: Glyph, tip: &str, on: bool) -> bool { /// The Character Direction submenu: Default, Left to Right and Right to Left set the characters the /// panel acts on ([`char_cmd`]) as the bidi algorithm sets them, or left to right or right to left /// inside their paragraph. The direction those characters share is checked, and none is checked -/// when they differ. +/// when they differ. At a caret in text the Type tool edits, with no characters selected, the items +/// are disabled. fn char_direction_items(app: &mut VectorcraftApp, ui: &mut Ui) { use vectorcraft_doc::ParaDirection; let direction = shared(app, |s| s.char_direction); + let enabled = text_editing(app).is_none_or(|(_, a, b)| b > a); for (label, d, key) in [ (tl!("Default"), None, "default"), (tl!("Left to Right"), Some(ParaDirection::LeftToRight), "leftToRight"), (tl!("Right to Left"), Some(ParaDirection::RightToLeft), "rightToLeft"), ] { - if menu_item(ui, label, true, direction == Some(d)) { + if menu_item(ui, label, enabled, direction == Some(d)) { format(app, json!({"charDirection": key})); } } @@ -1437,6 +1439,36 @@ mod tests { assert_eq!(checked(&mut app), ["Default"]); } + /// At a caret in text the Type tool edits, with no characters selected, the Character + /// Direction items are disabled: a click on one leaves the direction of every character as it was. + #[test] + fn character_direction_does_nothing_at_an_empty_caret() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 300, "height": 200})).unwrap(); + let id = app.run("text.create", json!({"x": 20, "y": 50, "text": "שלום 21 10 2026"})).unwrap()["id"].as_u64().unwrap(); + app.run("select.set", json!({"ids": [id]})).unwrap(); + app.select_tool("type"); + app.session.apply_actions(vec![vectorcraft_tools::Action::Notify("text.editNew".into())]).unwrap(); + app.session.set_tool_option("select", &json!({"start": 3, "end": 3})); + let directions = |app: &VectorcraftApp| match &app.session.active().unwrap().doc.node(NodeId(id)).unwrap().kind { + NodeKind::Text(t) => t.runs.iter().map(|r| (r.text.clone(), r.style.char_direction)).collect::>(), + _ => panic!("text"), + }; + let before = directions(&app); + let ctx = egui::Context::default(); + let click_rtl = |app: &mut VectorcraftApp| { + let texts = super::super::tests_appearance::frame_events(&ctx, app, vec![], char_direction_items); + let at = texts.iter().find(|(t, _)| t.trim_start_matches(['✓', ' ']) == "Right to Left").unwrap_or_else(|| panic!("{texts:?}")).1; + super::super::tests_appearance::click(&ctx, app, at.center(), char_direction_items); + }; + click_rtl(&mut app); + assert_eq!(directions(&app), before); + // With characters selected, the same click sets their direction. + app.session.set_tool_option("select", &json!({"start": 0, "end": 4})); + click_rtl(&mut app); + assert_ne!(directions(&app), before); + } + /// No Break is in the panel menu, and choosing it marks the selected characters. #[test] fn no_break_menu_item_toggles_the_selected_characters() {