forked from 1b-agent/opencadstudio-upstream
feat(web): copy/paste in text fields via the browser clipboard (#346)
iced's clipboard rides window_clipboard, which has no wasm backend — a focused text_input captured Ctrl+C/V on the web and did nothing. Ctrl+V (captured, wasm only): read the async browser clipboard (the keypress is a user gesture, so it's permitted) and replay the text as synthetic KeyboardEvents on the winit canvas — the only route into a focused text_input, since iced exposes no insert-text operation. Control characters are dropped and the length is capped. The MText and inline-TEXT editors keep their dedicated paste paths (no double feed), and an unfocused Ctrl+V still runs PASTECLIP. Ctrl+C (captured, wasm only): a widget operation pairs each text_input with its focus state (iced fires both callbacks back-to-back on the same widget) to collect the focused field's visible text, then writes it with navigator.clipboard.writeText. Whole-field text, not the selection — iced doesn't expose the selected range. Native behaviour is unchanged. Closes #346 Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
1 parent
dd40e1f632
commit
5f08b77ebc
5 files changed
+147
No files matched your search
+38
@@ -31,6 +31,44 @@ pub async fn read_clipboard_text() -> Option<String> {
|
||||
value.as_string()
|
||||
}
|
||||
|
||||
/// Web: write text to the system clipboard (fire-and-forget). Backs Ctrl+C in
|
||||
/// plain text_input fields, whose iced-internal copy is a no-op on the web
|
||||
/// (#346). The triggering keypress is a user gesture, so the write is allowed.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn write_clipboard_text(text: &str) {
|
||||
if let Some(window) = web_sys::window() {
|
||||
let _ = window.navigator().clipboard().write_text(text);
|
||||
}
|
||||
}
|
||||
|
||||
/// Web: replay `text` into the focused iced widget as synthetic KeyboardEvents
|
||||
/// dispatched on the winit canvas. This is the only route INTO a focused
|
||||
/// text_input on the web — iced's clipboard read is a no-op there and it
|
||||
/// exposes no insert-text operation (#346). Control characters are dropped
|
||||
/// and the length capped so a runaway clipboard can't wedge the event loop.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn synthesize_typing(text: &str) {
|
||||
let Some(canvas) = web_sys::window()
|
||||
.and_then(|w| w.document())
|
||||
.and_then(|d| d.query_selector("canvas").ok().flatten())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
for ch in text.chars().filter(|c| !c.is_control()).take(1024) {
|
||||
let init = web_sys::KeyboardEventInit::new();
|
||||
init.set_key(&ch.to_string());
|
||||
init.set_bubbles(true);
|
||||
init.set_cancelable(true);
|
||||
for kind in ["keydown", "keyup"] {
|
||||
if let Ok(ev) =
|
||||
web_sys::KeyboardEvent::new_with_keyboard_event_init_dict(kind, &init)
|
||||
{
|
||||
let _ = canvas.dispatch_event(&ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn an `rfd` file handle into a `PathBuf` the rest of the app keys on.
|
||||
///
|
||||
/// Desktop returns the real filesystem path. The browser has no path, so we
|
||||
|
||||
Reference in new issue
Block a user