mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-25 10:46:50 -06:00
#201: route macOS text clipboard commands through egui
Forward native Copy/Cut/Paste for active canvas text through egui so the desktop integration can use the system clipboard. Add regression coverage for the native clipboard event path. Co-authored-by: GitHub Copilot <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
GitHub Copilot
parent
92649ffdb0
commit
f05e8f5d98
@@ -1928,9 +1928,15 @@ fn clipped_submenu<'a, R>(
|
||||
/// ⌘A, ⌘C, ⌘X, ⌘V, ⌘Z and ⇧⌘Z before the window sees them, so while a text field has keyboard
|
||||
/// focus, Select All, Copy, Cut, Paste, Undo and Redo act on that field, as in any Mac app.
|
||||
pub fn activate_native(app: &mut DesignApp, ctx: &egui::Context, id: &str, params: &Value) {
|
||||
if !(ctx.text_edit_focused() && params.is_null() && forward_to_text_field(app, ctx, id)) {
|
||||
activate(app, id, params);
|
||||
if params.is_null() && ctx.text_edit_focused() && forward_to_text_edit(app, ctx, id) {
|
||||
return;
|
||||
}
|
||||
// The canvas text editor is not an egui TextEdit, so its clipboard events must pass through
|
||||
// egui for the desktop integration to read/write the macOS clipboard.
|
||||
if params.is_null() && app.session.wants_text() && matches!(id, "edit.copy" | "edit.cut" | "edit.paste") && forward_to_text_edit(app, ctx, id) {
|
||||
return;
|
||||
}
|
||||
activate(app, id, params);
|
||||
}
|
||||
|
||||
/// Whether a native menu bar item can be chosen: as in the in-window menus, but the editing
|
||||
@@ -1944,7 +1950,7 @@ const TEXT_FIELD_COMMANDS: [&str; 6] = ["edit.selectAll", "edit.copy", "edit.cut
|
||||
|
||||
/// Hand a standard editing command to the focused text field as next frame's input; false when
|
||||
/// `id` isn't one of them.
|
||||
fn forward_to_text_field(app: &mut DesignApp, ctx: &egui::Context, id: &str) -> bool {
|
||||
fn forward_to_text_edit(app: &mut DesignApp, ctx: &egui::Context, id: &str) -> bool {
|
||||
let key = |key: egui::Key, modifiers: egui::Modifiers| {
|
||||
[true, false].map(|pressed| egui::Event::Key { key, physical_key: None, pressed, repeat: false, modifiers })
|
||||
};
|
||||
@@ -2957,6 +2963,31 @@ mod tests {
|
||||
assert_eq!(document_items(&app), (2, 0), "nothing was pasted into the document");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_copy_and_paste_use_the_system_clipboard_for_canvas_text() {
|
||||
let ctx = egui::Context::default();
|
||||
let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
app.session.execute("file.new", &json!({})).unwrap();
|
||||
app.session.execute("frame.create", &json!({"rect": [36, 36, 200, 200], "content": "text", "text": "hello"})).unwrap();
|
||||
let story = *app.session.active().unwrap().doc.stories.keys().next().unwrap();
|
||||
app.select_tool("type");
|
||||
app.run("text.select", json!({"story": story.0, "anchor": 0, "focus": 5})).unwrap();
|
||||
for _ in 0..3 {
|
||||
native_frame(&mut app, &ctx, vec![], None);
|
||||
}
|
||||
assert!(app.session.wants_text());
|
||||
|
||||
native_frame(&mut app, &ctx, vec![], Some("edit.copy"));
|
||||
let copied = native_frame(&mut app, &ctx, vec![], None);
|
||||
assert!(copied.platform_output.commands.contains(&egui::OutputCommand::CopyText("hello".into())));
|
||||
|
||||
let paste = native_frame(&mut app, &ctx, vec![], Some("edit.paste"));
|
||||
let commands = &paste.viewport_output[&egui::ViewportId::ROOT].commands;
|
||||
assert!(commands.contains(&egui::ViewportCommand::RequestPaste), "{commands:?}");
|
||||
native_frame(&mut app, &ctx, vec![egui::Event::Paste("world".into())], None);
|
||||
assert_eq!(app.session.active().unwrap().doc.story(story).unwrap().text, "world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_undo_and_redo_act_on_the_focused_field() {
|
||||
let ctx = egui::Context::default();
|
||||
|
||||
Reference in New Issue
Block a user