Skip to content

Commit 911fed3

Browse files
committed
merge: frome office github update,auto merge well
2 parents 2ce068e + 3ffe1ed commit 911fed3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1020
-360
lines changed

.github/workflows/rust.yml

+2-4
Original file line numberDiff line numberDiff line change
@@ -238,12 +238,10 @@ jobs:
238238
uses: Swatinem/rust-cache@v2
239239

240240
- name: Run tests
241-
# TODO(lucasmerlin): Enable --all-features (currently this breaks the rendering in the tests because of the `unity` feature)
242-
run: cargo test
241+
run: cargo test --all-features
243242

244243
- name: Run doc-tests
245-
# TODO(lucasmerlin): Enable --all-features (currently this breaks the rendering in the tests because of the `unity` feature)
246-
run: cargo test --doc
244+
run: cargo test --all-features --doc
247245

248246
- name: Upload artifacts
249247
uses: actions/upload-artifact@v4

.vscode/settings.json

+4
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,8 @@
3333
"--all-features",
3434
],
3535
"rust-analyzer.showUnlinkedFileNotification": false,
36+
37+
// Uncomment the following options and restart rust-analyzer to get it to check code behind `cfg(target_arch=wasm32)`.
38+
// Don't forget to put it in a comment again before committing.
39+
// "rust-analyzer.cargo.target": "wasm32-unknown-unknown",
3640
}

CONTRIBUTING.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ For small things, just go ahead an open a PR. For bigger things, please file an
3232
Browse through [`ARCHITECTURE.md`](ARCHITECTURE.md) to get a sense of how all pieces connects.
3333

3434
You can test your code locally by running `./scripts/check.sh`.
35-
There are snapshots test that might need to be updated. Run the tests with `UPDATE_SNAPSHOTS=true` to update them.
35+
There are snapshots test that might need to be updated.
36+
Run the tests with `UPDATE_SNAPSHOTS=true cargo test --workspace --all-features` to update all of them.
3637
For more info about the tests see [egui_kittest](./crates/egui_kittest/README.md).
3738

3839
We use [git-lfs](https://git-lfs.com/) to store big files in the repository.

Cargo.lock

+39
Original file line numberDiff line numberDiff line change
@@ -240,11 +240,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
240240
checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4"
241241
dependencies = [
242242
"clipboard-win",
243+
"core-graphics",
244+
"image",
243245
"log",
244246
"objc2",
245247
"objc2-app-kit",
246248
"objc2-foundation",
247249
"parking_lot",
250+
"windows-sys 0.48.0",
248251
"x11rb",
249252
]
250253

@@ -1292,6 +1295,7 @@ dependencies = [
12921295
"accesskit_winit",
12931296
"ahash",
12941297
"arboard",
1298+
"bytemuck",
12951299
"document-features",
12961300
"egui",
12971301
"log",
@@ -2218,12 +2222,24 @@ dependencies = [
22182222
"byteorder-lite",
22192223
"color_quant",
22202224
"gif",
2225+
"image-webp",
22212226
"num-traits",
22222227
"png",
2228+
"tiff",
22232229
"zune-core",
22242230
"zune-jpeg",
22252231
]
22262232

2233+
[[package]]
2234+
name = "image-webp"
2235+
version = "0.2.0"
2236+
source = "registry+https://github.com/rust-lang/crates.io-index"
2237+
checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f"
2238+
dependencies = [
2239+
"byteorder-lite",
2240+
"quick-error",
2241+
]
2242+
22272243
[[package]]
22282244
name = "images"
22292245
version = "0.1.0"
@@ -2316,6 +2332,12 @@ dependencies = [
23162332
"libc",
23172333
]
23182334

2335+
[[package]]
2336+
name = "jpeg-decoder"
2337+
version = "0.3.1"
2338+
source = "registry+https://github.com/rust-lang/crates.io-index"
2339+
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
2340+
23192341
[[package]]
23202342
name = "js-sys"
23212343
version = "0.3.72"
@@ -3181,6 +3203,12 @@ dependencies = [
31813203
"puffin_http",
31823204
]
31833205

3206+
[[package]]
3207+
name = "quick-error"
3208+
version = "2.0.1"
3209+
source = "registry+https://github.com/rust-lang/crates.io-index"
3210+
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
3211+
31843212
[[package]]
31853213
name = "quick-xml"
31863214
version = "0.30.0"
@@ -3881,6 +3909,17 @@ dependencies = [
38813909
"syn",
38823910
]
38833911

3912+
[[package]]
3913+
name = "tiff"
3914+
version = "0.9.1"
3915+
source = "registry+https://github.com/rust-lang/crates.io-index"
3916+
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
3917+
dependencies = [
3918+
"flate2",
3919+
"jpeg-decoder",
3920+
"weezl",
3921+
]
3922+
38843923
[[package]]
38853924
name = "time"
38863925
version = "0.3.36"

crates/eframe/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -203,15 +203,18 @@ windows-sys = { workspace = true, features = [
203203
# web:
204204
[target.'cfg(target_arch = "wasm32")'.dependencies]
205205
bytemuck.workspace = true
206+
image = { workspace = true, features = ["png"] } # For copying images
206207
js-sys = "0.3"
207208
percent-encoding = "2.1"
208209
wasm-bindgen.workspace = true
209210
wasm-bindgen-futures.workspace = true
210211
web-sys = { workspace = true, features = [
211212
"BinaryType",
212213
"Blob",
214+
"BlobPropertyBag",
213215
"Clipboard",
214216
"ClipboardEvent",
217+
"ClipboardItem",
215218
"CompositionEvent",
216219
"console",
217220
"CssStyleDeclaration",

crates/eframe/src/web/app_runner.rs

+18
Original file line numberDiff line numberDiff line change
@@ -292,12 +292,15 @@ impl AppRunner {
292292
}
293293

294294
fn handle_platform_output(&self, platform_output: egui::PlatformOutput) {
295+
#![allow(deprecated)]
296+
295297
#[cfg(feature = "web_screen_reader")]
296298
if self.egui_ctx.options(|o| o.screen_reader) {
297299
super::screen_reader::speak(&platform_output.events_description());
298300
}
299301

300302
let egui::PlatformOutput {
303+
commands,
301304
cursor_icon,
302305
open_url,
303306
copied_text,
@@ -310,7 +313,22 @@ impl AppRunner {
310313
request_discard_reasons: _, // handled by `Context::run`
311314
} = platform_output;
312315

316+
for command in commands {
317+
match command {
318+
egui::OutputCommand::CopyText(text) => {
319+
super::set_clipboard_text(&text);
320+
}
321+
egui::OutputCommand::CopyImage(image) => {
322+
super::set_clipboard_image(&image);
323+
}
324+
egui::OutputCommand::OpenUrl(open_url) => {
325+
super::open_url(&open_url.url, open_url.new_tab);
326+
}
327+
}
328+
}
329+
313330
super::set_cursor_icon(cursor_icon);
331+
314332
if let Some(open) = open_url {
315333
super::open_url(&open.url, open.new_tab);
316334
}

crates/eframe/src/web/mod.rs

+89
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,95 @@ fn set_clipboard_text(s: &str) {
192192
}
193193
}
194194

195+
/// Set the clipboard image.
196+
fn set_clipboard_image(image: &egui::ColorImage) {
197+
if let Some(window) = web_sys::window() {
198+
if !window.is_secure_context() {
199+
log::error!(
200+
"Clipboard is not available because we are not in a secure context. \
201+
See https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts"
202+
);
203+
return;
204+
}
205+
206+
let png_bytes = to_image(image).and_then(|image| to_png_bytes(&image));
207+
let png_bytes = match png_bytes {
208+
Ok(png_bytes) => png_bytes,
209+
Err(err) => {
210+
log::error!("Failed to encode image to png: {err}");
211+
return;
212+
}
213+
};
214+
215+
let mime = "image/png";
216+
217+
let item = match create_clipboard_item(mime, &png_bytes) {
218+
Ok(item) => item,
219+
Err(err) => {
220+
log::error!("Failed to copy image: {}", string_from_js_value(&err));
221+
return;
222+
}
223+
};
224+
let items = js_sys::Array::of1(&item);
225+
let promise = window.navigator().clipboard().write(&items);
226+
let future = wasm_bindgen_futures::JsFuture::from(promise);
227+
let future = async move {
228+
if let Err(err) = future.await {
229+
log::error!(
230+
"Copy/cut image action failed: {}",
231+
string_from_js_value(&err)
232+
);
233+
}
234+
};
235+
wasm_bindgen_futures::spawn_local(future);
236+
}
237+
}
238+
239+
fn to_image(image: &egui::ColorImage) -> Result<image::RgbaImage, String> {
240+
profiling::function_scope!();
241+
image::RgbaImage::from_raw(
242+
image.width() as _,
243+
image.height() as _,
244+
bytemuck::cast_slice(&image.pixels).to_vec(),
245+
)
246+
.ok_or_else(|| "Invalid IconData".to_owned())
247+
}
248+
249+
fn to_png_bytes(image: &image::RgbaImage) -> Result<Vec<u8>, String> {
250+
profiling::function_scope!();
251+
let mut png_bytes: Vec<u8> = Vec::new();
252+
image
253+
.write_to(
254+
&mut std::io::Cursor::new(&mut png_bytes),
255+
image::ImageFormat::Png,
256+
)
257+
.map_err(|err| err.to_string())?;
258+
Ok(png_bytes)
259+
}
260+
261+
fn create_clipboard_item(mime: &str, bytes: &[u8]) -> Result<web_sys::ClipboardItem, JsValue> {
262+
let array = js_sys::Uint8Array::from(bytes);
263+
let blob_parts = js_sys::Array::new();
264+
blob_parts.push(&array);
265+
266+
let options = web_sys::BlobPropertyBag::new();
267+
options.set_type(mime);
268+
269+
let blob = web_sys::Blob::new_with_u8_array_sequence_and_options(&blob_parts, &options)?;
270+
271+
let items = js_sys::Object::new();
272+
273+
// SAFETY: I hope so
274+
#[allow(unsafe_code, unused_unsafe)] // Weird false positive
275+
unsafe {
276+
js_sys::Reflect::set(&items, &JsValue::from_str(mime), &blob)?
277+
};
278+
279+
let clipboard_item = web_sys::ClipboardItem::new_with_record_from_str_to_blob_promise(&items)?;
280+
281+
Ok(clipboard_item)
282+
}
283+
195284
fn cursor_web_name(cursor: egui::CursorIcon) -> &'static str {
196285
match cursor {
197286
egui::CursorIcon::Alias => "alias",

crates/egui-winit/Cargo.toml

+7-3
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ android-game-activity = ["winit/android-game-activity"]
3636
android-native-activity = ["winit/android-native-activity"]
3737

3838
## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`egui::epaint::Vertex`], [`egui::Vec2`] etc to `&[u8]`.
39-
bytemuck = ["egui/bytemuck"]
39+
bytemuck = ["egui/bytemuck", "dep:bytemuck"]
4040

4141
## Enable cut/copy/paste to OS clipboard.
4242
## If disabled a clipboard will be simulated so you can still copy/paste within the egui app.
43-
clipboard = ["arboard", "smithay-clipboard"]
43+
clipboard = ["arboard", "bytemuck", "smithay-clipboard"]
4444

4545
## Enable opening links in a browser when an egui hyperlink is clicked.
4646
links = ["webbrowser"]
@@ -69,6 +69,8 @@ winit = { workspace = true, default-features = false }
6969
# feature accesskit
7070
accesskit_winit = { version = "0.23", optional = true }
7171

72+
bytemuck = { workspace = true, optional = true }
73+
7274
## Enable this when generating docs.
7375
document-features = { workspace = true, optional = true }
7476

@@ -84,4 +86,6 @@ smithay-clipboard = { version = "0.7.2", optional = true }
8486
wayland-cursor = { version = "0.31.1", default-features = false, optional = true }
8587

8688
[target.'cfg(not(target_os = "android"))'.dependencies]
87-
arboard = { version = "3.3", optional = true, default-features = false }
89+
arboard = { version = "3.3", optional = true, default-features = false, features = [
90+
"image-data",
91+
] }

crates/egui-winit/src/clipboard.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ impl Clipboard {
8282
Some(self.clipboard.clone())
8383
}
8484

85-
pub fn set(&mut self, text: String) {
85+
pub fn set_text(&mut self, text: String) {
8686
#[cfg(all(
8787
any(
8888
target_os = "linux",
@@ -108,6 +108,24 @@ impl Clipboard {
108108

109109
self.clipboard = text;
110110
}
111+
112+
pub fn set_image(&mut self, image: &egui::ColorImage) {
113+
#[cfg(all(feature = "arboard", not(target_os = "android")))]
114+
if let Some(clipboard) = &mut self.arboard {
115+
if let Err(err) = clipboard.set_image(arboard::ImageData {
116+
width: image.width(),
117+
height: image.height(),
118+
bytes: std::borrow::Cow::Borrowed(bytemuck::cast_slice(&image.pixels)),
119+
}) {
120+
log::error!("arboard copy/cut error: {err}");
121+
}
122+
log::debug!("Copied image to clipboard");
123+
return;
124+
}
125+
126+
log::error!("Copying images is not supported. Enable the 'clipboard' feature of `egui-winit` to enable it.");
127+
_ = image;
128+
}
111129
}
112130

113131
#[cfg(all(feature = "arboard", not(target_os = "android")))]

0 commit comments

Comments
 (0)