UI: Route text clipboard writes through egui (#1239)

Co-Authored-By: GPT-6 Luna <noreply@openai.com>
This commit is contained in:
Susiee The Linux User
2026-10-24 04:28:02 +02:00
committed by SusieeTheFemboy
co-authored by GPT-6 Luna
parent 8167f13a6a
commit c6dd115e78
4 changed files with 38 additions and 8 deletions
+3 -2
View File
@@ -244,14 +244,15 @@ fn main() -> eframe::Result {
let result = eframe::run_native(
"FigCraft",
options,
Box::new(move |_cc| {
Box::new(move |cc| {
with_app(|fig| fig.services.set_egui_context(&cc.egui_ctx));
Ok(Box::new(App {
queue,
ctx_slot,
title: String::new(),
recovery,
#[cfg(target_os = "macos")]
apple_events: apple_events.connect(&_cc.egui_ctx),
apple_events: apple_events.connect(&cc.egui_ctx),
}))
}),
);
+7 -3
View File
@@ -11,6 +11,7 @@ use serde_json::{Value, json};
pub struct Desktop {
clipboard: Option<arboard::Clipboard>,
egui_ctx: Option<egui::Context>,
/// Where the preferences live ([`config_dir`]).
dir: Option<PathBuf>,
pub prefs: Option<Prefs>,
@@ -54,7 +55,7 @@ impl Desktop {
let reduce_motion = Arc::new(AtomicBool::new(false));
let flag = reduce_motion.clone();
std::thread::spawn(move || flag.store(system_reduce_motion(), Ordering::Relaxed));
Self { clipboard: arboard::Clipboard::new().ok(), dir, prefs, reduce_motion, language: system_language(), recovery }
Self { clipboard: arboard::Clipboard::new().ok(), egui_ctx: None, dir, prefs, reduce_motion, language: system_language(), recovery }
}
}
@@ -277,10 +278,13 @@ impl Services for Desktop {
picked(dialog(title).pick_folder())
}
fn clipboard_set_text(&mut self, s: &str) {
if let Some(c) = self.clipboard.as_mut() {
let _ = c.set_text(s.to_string());
if let Some(ctx) = &self.egui_ctx {
ctx.copy_text(s.to_string());
}
}
fn set_egui_context(&mut self, ctx: &egui::Context) {
self.egui_ctx = Some(ctx.clone());
}
fn clipboard_get_text(&mut self) -> Option<String> {
read_clipboard(self.clipboard.as_mut()?, |c| c.get_text().ok())
}
+4 -3
View File
@@ -272,6 +272,8 @@ pub trait Services {
Box::pin(std::future::ready(None))
}
fn clipboard_set_text(&mut self, _s: &str) {}
/// Give native services access to the window context for platform clipboard output.
fn set_egui_context(&mut self, _ctx: &egui::Context) {}
fn clipboard_get_text(&mut self) -> Option<String> {
None
}
@@ -387,9 +389,8 @@ impl Wake for RepaintWaker {
pub struct NoServices;
impl Services for NoServices {}
/// Put `text` on the system clipboard through egui's output, which the browser shell writes while
/// it handles the copy event (the only time a page may write it). For shells without a clipboard
/// of their own.
/// Put `text` on the system clipboard through egui's platform output. Native integrations handle
/// it directly; the browser shell writes it while handling the copy event.
pub fn egui_copy_text(ctx: &egui::Context, text: &str) {
ctx.copy_text(text.to_string());
}
+24
View File
@@ -59,6 +59,30 @@ fn enter_edits_a_selected_text_with_all_of_it_selected_and_escape_keeps_the_laye
assert!(u.app.session.selection().is_empty());
}
#[test]
fn copying_and_cutting_selected_text_use_clipboard_service() {
use std::{cell::RefCell, rc::Rc};
struct Clipboard(Rc<RefCell<Vec<String>>>);
impl crate::Services for Clipboard {
fn clipboard_set_text(&mut self, text: &str) {
self.0.borrow_mut().push(text.to_string());
}
}
let mut s = Session::new();
let t = NodeId(s.execute("text.create", &json!({"text": "Hello world", "x": 20, "y": 20})).unwrap()["id"].as_u64().unwrap());
s.execute("select.set", &json!({"ids": [t]})).unwrap();
let mut u = Ui::new(s);
let copied = Rc::new(RefCell::new(Vec::new()));
u.app.services = Box::new(Clipboard(copied.clone()));
press(&mut u, Key::Enter);
u.frame_output(vec![Event::Copy]);
u.frame_output(vec![Event::Cut]);
assert_eq!(&*copied.borrow(), &["Hello world", "Hello world"]);
assert_eq!(u.app.session.doc().unwrap().node(t).unwrap().kind.text().unwrap().characters, "");
}
#[test]
fn a_new_file_takes_the_page_colour_of_the_theme_and_keeps_it() {
let mut s = Session::new();