2021-01-02 10:59:20 +00:00
|
|
|
//! [`egui`] bindings for web apps (compiling to WASM).
|
|
|
|
//!
|
|
|
|
//! This library is an [`epi`] backend.
|
|
|
|
//!
|
|
|
|
//! If you are writing an app, you may want to look at [`eframe`](https://docs.rs/eframe) instead.
|
2021-06-03 16:56:26 +00:00
|
|
|
//!
|
|
|
|
//! ## Specifying the size of the egui canvas
|
|
|
|
//! For performance reasons (on some browsers) the egui canvas does not, by default,
|
|
|
|
//! fill the whole width of the browser.
|
|
|
|
//! This can be changed by overriding [`epi::App::max_size_points`].
|
2021-01-02 10:59:20 +00:00
|
|
|
|
2021-06-23 07:16:39 +00:00
|
|
|
// Forbid warnings in release builds:
|
|
|
|
#![cfg_attr(not(debug_assertions), deny(warnings))]
|
2021-04-15 08:35:15 +00:00
|
|
|
#![forbid(unsafe_code)]
|
2021-12-26 08:55:13 +00:00
|
|
|
#![warn(clippy::all, rustdoc::missing_crate_level_docs, rust_2018_idioms)]
|
2019-01-12 22:07:30 +00:00
|
|
|
|
2020-07-23 16:54:16 +00:00
|
|
|
pub mod backend;
|
2021-11-03 18:17:07 +00:00
|
|
|
#[cfg(feature = "glow")]
|
|
|
|
mod glow_wrapping;
|
2022-02-21 08:23:02 +00:00
|
|
|
mod input;
|
2021-01-16 00:30:00 +00:00
|
|
|
mod painter;
|
2021-03-08 19:58:01 +00:00
|
|
|
pub mod screen_reader;
|
2022-02-21 08:23:02 +00:00
|
|
|
mod text_agent;
|
2021-12-31 14:17:55 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "webgl")]
|
2021-01-16 00:30:00 +00:00
|
|
|
pub mod webgl1;
|
2021-12-31 14:17:55 +00:00
|
|
|
#[cfg(feature = "webgl")]
|
2021-01-16 00:30:00 +00:00
|
|
|
pub mod webgl2;
|
2019-02-11 19:27:32 +00:00
|
|
|
|
2020-07-23 16:54:16 +00:00
|
|
|
pub use backend::*;
|
|
|
|
|
2021-01-16 00:30:00 +00:00
|
|
|
use egui::mutex::Mutex;
|
2020-12-29 14:57:13 +00:00
|
|
|
pub use wasm_bindgen;
|
|
|
|
pub use web_sys;
|
|
|
|
|
2022-02-21 08:23:02 +00:00
|
|
|
use input::*;
|
2021-01-16 00:30:00 +00:00
|
|
|
pub use painter::Painter;
|
2022-02-21 08:23:02 +00:00
|
|
|
|
2022-02-17 15:46:43 +00:00
|
|
|
use std::collections::BTreeMap;
|
2020-07-18 16:35:17 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
use wasm_bindgen::prelude::*;
|
|
|
|
|
2019-02-11 19:27:32 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
2020-11-17 23:43:58 +00:00
|
|
|
|
2020-11-20 19:35:16 +00:00
|
|
|
/// Current time in seconds (since undefined point in time)
|
2019-02-11 19:27:32 +00:00
|
|
|
pub fn now_sec() -> f64 {
|
|
|
|
web_sys::window()
|
|
|
|
.expect("should have a Window")
|
|
|
|
.performance()
|
|
|
|
.expect("should have a Performance")
|
|
|
|
.now()
|
|
|
|
/ 1000.0
|
|
|
|
}
|
|
|
|
|
2020-10-17 21:54:46 +00:00
|
|
|
pub fn screen_size_in_native_points() -> Option<egui::Vec2> {
|
|
|
|
let window = web_sys::window()?;
|
2022-01-21 09:48:44 +00:00
|
|
|
Some(egui::vec2(
|
2020-10-17 21:54:46 +00:00
|
|
|
window.inner_width().ok()?.as_f64()? as f32,
|
|
|
|
window.inner_height().ok()?.as_f64()? as f32,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn native_pixels_per_point() -> f32 {
|
2020-07-18 08:54:31 +00:00
|
|
|
let pixels_per_point = web_sys::window().unwrap().device_pixel_ratio() as f32;
|
|
|
|
if pixels_per_point > 0.0 && pixels_per_point.is_finite() {
|
|
|
|
pixels_per_point
|
|
|
|
} else {
|
|
|
|
1.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 18:56:18 +00:00
|
|
|
pub fn prefer_dark_mode() -> Option<bool> {
|
|
|
|
Some(
|
|
|
|
web_sys::window()?
|
|
|
|
.match_media("(prefers-color-scheme: dark)")
|
|
|
|
.ok()??
|
|
|
|
.matches(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-07-18 16:00:05 +00:00
|
|
|
pub fn canvas_element(canvas_id: &str) -> Option<web_sys::HtmlCanvasElement> {
|
2020-07-18 08:54:31 +00:00
|
|
|
use wasm_bindgen::JsCast;
|
|
|
|
let document = web_sys::window()?.document()?;
|
|
|
|
let canvas = document.get_element_by_id(canvas_id)?;
|
2020-07-18 16:00:05 +00:00
|
|
|
canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn canvas_element_or_die(canvas_id: &str) -> web_sys::HtmlCanvasElement {
|
|
|
|
crate::canvas_element(canvas_id)
|
|
|
|
.unwrap_or_else(|| panic!("Failed to find canvas with id '{}'", canvas_id))
|
|
|
|
}
|
|
|
|
|
Basic multi touch support (issue #279) (#306)
* translate touch events from glium to egui
Unfortunately, winit does not seem to create _Touch_ events for the touch pad
on my mac. Only _TouchpadPressure_ events are sent.
Found some issues (like
[this](https://github.com/rust-windowing/winit/issues/54)), but I am not sure
what they exactly mean: Sometimes, touch events are mixed with
touch-to-pointer translation in the discussions.
* translate touch events from web_sys to egui
The are a few open topics:
- egui_web currently translates touch events into pointer events.
I guess this should change, such that egui itself performs this kind of
conversion.
- `pub fn egui_web::pos_from_touch_event` is a public function, but I
would like to change the return type to an `Option`. Shouldn't this
function be private, anyway?
* introduce `TouchState` and `Gesture`
InputState.touch was introduced with type `TouchState`, just as
InputState.pointer is of type `Pointer`.
The TouchState internally relies on a collection of `Gesture`s. This commit
provides the first rudimentary implementation of a Gesture, but has no
functionality, yet.
* add method InputState::zoom()
So far, the method always returns `None`, but it should work as soon as the
`Zoom` gesture is implemented.
* manage one `TouchState` per individual device
Although quite unlikely, it is still possible to connect more than one touch
device. (I have three touch pads connected to my MacBook in total, but
unfortunately `winit` sends touch events for none of them.)
We do not want to mix-up the touches from different devices.
* implement control loop for gesture detection
The basic idea is that each gesture can focus on detection logic and does not
have to care (too much) about managing touch state in general.
* streamline `Gesture` trait, simplifying impl's
* implement first version of Zoom gesture
* fix failing doctest
a simple `TODO` should be enough
* get rid of `Gesture`s
* Provide a Zoom/Rotate window in the demo app
For now, it works for two fingers only. The third finger interrupts the
gesture.
Bugs:
- Pinching in the demo window also moves the window -> Pointer events must be
ignored when touch is active
- Pinching also works when doing it outside the demo window -> it would be nice
to return the touch info in the `Response` of the painter allocation
* fix comments and non-idiomatic code
* update touch state *each frame*
* change egui_demo to use *relative* touch data
* support more than two fingers
This commit includes an improved Demo Window for egui_demo, and a complete
re-write of the gesture detection. The PR should be ready for review, soon.
* cleanup code and comments for review
* minor code simplifications
* oops – forgot the changelog
* resolve comment https://github.com/emilk/egui/pull/306/files/fee8ed83dbe715b5b70433faacfe74b59c99e4a4#r623226656
* accept suggestion https://github.com/emilk/egui/pull/306#discussion_r623229228
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
* fix syntax error (dough!)
* remove `dbg!` (why didnt clippy see this?)
* apply suggested diffs from review
* fix conversion of physical location to Pos2
* remove redundanct type `TouchAverages`
* remove trailing space
* avoid initial translation jump in plot demo
* extend the demo so it shows off translation
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2021-05-06 19:01:10 +00:00
|
|
|
fn canvas_origin(canvas_id: &str) -> egui::Pos2 {
|
|
|
|
let rect = canvas_element(canvas_id)
|
|
|
|
.unwrap()
|
|
|
|
.get_bounding_client_rect();
|
|
|
|
egui::Pos2::new(rect.left() as f32, rect.top() as f32)
|
|
|
|
}
|
|
|
|
|
2020-12-18 21:51:23 +00:00
|
|
|
pub fn canvas_size_in_points(canvas_id: &str) -> egui::Vec2 {
|
|
|
|
let canvas = canvas_element(canvas_id).unwrap();
|
|
|
|
let pixels_per_point = native_pixels_per_point();
|
|
|
|
egui::vec2(
|
|
|
|
canvas.width() as f32 / pixels_per_point,
|
|
|
|
canvas.height() as f32 / pixels_per_point,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-02-12 16:57:53 +00:00
|
|
|
pub fn resize_canvas_to_screen_size(canvas_id: &str, max_size_points: egui::Vec2) -> Option<()> {
|
2020-07-18 16:00:05 +00:00
|
|
|
let canvas = canvas_element(canvas_id)?;
|
2020-07-18 08:54:31 +00:00
|
|
|
|
2020-12-18 21:51:23 +00:00
|
|
|
let screen_size_points = screen_size_in_native_points()?;
|
2020-10-17 21:54:46 +00:00
|
|
|
let pixels_per_point = native_pixels_per_point();
|
2020-12-18 21:51:23 +00:00
|
|
|
|
2021-02-12 16:57:53 +00:00
|
|
|
let max_size_pixels = pixels_per_point * max_size_points;
|
|
|
|
|
2020-12-18 21:51:23 +00:00
|
|
|
let canvas_size_pixels = pixels_per_point * screen_size_points;
|
|
|
|
let canvas_size_pixels = canvas_size_pixels.min(max_size_pixels);
|
|
|
|
let canvas_size_points = canvas_size_pixels / pixels_per_point;
|
|
|
|
|
2021-02-01 19:44:39 +00:00
|
|
|
// Make sure that the height and width are always even numbers.
|
|
|
|
// otherwise, the page renders blurry on some platforms.
|
|
|
|
// See https://github.com/emilk/egui/issues/103
|
|
|
|
fn round_to_even(v: f32) -> f32 {
|
|
|
|
(v / 2.0).round() * 2.0
|
|
|
|
}
|
|
|
|
|
2020-07-18 08:54:31 +00:00
|
|
|
canvas
|
|
|
|
.style()
|
2021-02-01 19:44:39 +00:00
|
|
|
.set_property(
|
|
|
|
"width",
|
|
|
|
&format!("{}px", round_to_even(canvas_size_points.x)),
|
|
|
|
)
|
2020-07-18 08:54:31 +00:00
|
|
|
.ok()?;
|
|
|
|
canvas
|
|
|
|
.style()
|
2021-02-01 19:44:39 +00:00
|
|
|
.set_property(
|
|
|
|
"height",
|
|
|
|
&format!("{}px", round_to_even(canvas_size_points.y)),
|
|
|
|
)
|
2020-07-18 08:54:31 +00:00
|
|
|
.ok()?;
|
2021-02-01 19:44:39 +00:00
|
|
|
canvas.set_width(round_to_even(canvas_size_pixels.x) as u32);
|
|
|
|
canvas.set_height(round_to_even(canvas_size_pixels.y) as u32);
|
2020-07-18 08:54:31 +00:00
|
|
|
|
|
|
|
Some(())
|
|
|
|
}
|
|
|
|
|
2020-12-18 21:51:23 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2019-02-11 19:27:32 +00:00
|
|
|
pub fn local_storage() -> Option<web_sys::Storage> {
|
|
|
|
web_sys::window()?.local_storage().ok()?
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn local_storage_get(key: &str) -> Option<String> {
|
|
|
|
local_storage().map(|storage| storage.get_item(key).ok())??
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn local_storage_set(key: &str, value: &str) {
|
|
|
|
local_storage().map(|storage| storage.set_item(key, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn local_storage_remove(key: &str) {
|
|
|
|
local_storage().map(|storage| storage.remove_item(key));
|
|
|
|
}
|
2020-05-02 09:37:12 +00:00
|
|
|
|
2021-01-04 00:44:02 +00:00
|
|
|
#[cfg(feature = "persistence")]
|
2020-05-30 08:22:35 +00:00
|
|
|
pub fn load_memory(ctx: &egui::Context) {
|
2021-04-05 10:09:01 +00:00
|
|
|
if let Some(memory_string) = local_storage_get("egui_memory_ron") {
|
|
|
|
match ron::from_str(&memory_string) {
|
2020-05-02 09:37:12 +00:00
|
|
|
Ok(memory) => {
|
|
|
|
*ctx.memory() = memory;
|
|
|
|
}
|
|
|
|
Err(err) => {
|
2022-02-01 11:27:39 +00:00
|
|
|
tracing::error!("Failed to parse memory RON: {}", err);
|
2020-05-02 09:37:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-04 00:44:02 +00:00
|
|
|
#[cfg(not(feature = "persistence"))]
|
|
|
|
pub fn load_memory(_: &egui::Context) {}
|
|
|
|
|
|
|
|
#[cfg(feature = "persistence")]
|
2020-05-30 08:22:35 +00:00
|
|
|
pub fn save_memory(ctx: &egui::Context) {
|
2021-04-05 10:09:01 +00:00
|
|
|
match ron::to_string(&*ctx.memory()) {
|
|
|
|
Ok(ron) => {
|
|
|
|
local_storage_set("egui_memory_ron", &ron);
|
2020-05-02 09:37:12 +00:00
|
|
|
}
|
|
|
|
Err(err) => {
|
2022-02-01 11:27:39 +00:00
|
|
|
tracing::error!("Failed to serialize memory as RON: {}", err);
|
2020-05-02 09:37:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-18 08:54:31 +00:00
|
|
|
|
2021-01-04 00:44:02 +00:00
|
|
|
#[cfg(not(feature = "persistence"))]
|
|
|
|
pub fn save_memory(_: &egui::Context) {}
|
|
|
|
|
2020-12-19 19:50:00 +00:00
|
|
|
#[derive(Default)]
|
2020-12-19 13:58:00 +00:00
|
|
|
pub struct LocalStorage {}
|
|
|
|
|
2020-12-29 13:15:46 +00:00
|
|
|
impl epi::Storage for LocalStorage {
|
2020-12-19 13:58:00 +00:00
|
|
|
fn get_string(&self, key: &str) -> Option<String> {
|
|
|
|
local_storage_get(key)
|
|
|
|
}
|
|
|
|
fn set_string(&mut self, key: &str, value: String) {
|
|
|
|
local_storage_set(key, &value);
|
|
|
|
}
|
|
|
|
fn flush(&mut self) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2020-07-18 08:54:31 +00:00
|
|
|
pub fn set_cursor_icon(cursor: egui::CursorIcon) -> Option<()> {
|
|
|
|
let document = web_sys::window()?.document()?;
|
|
|
|
document
|
|
|
|
.body()?
|
|
|
|
.style()
|
|
|
|
.set_property("cursor", cursor_web_name(cursor))
|
|
|
|
.ok()
|
|
|
|
}
|
|
|
|
|
2020-12-29 11:42:15 +00:00
|
|
|
#[cfg(web_sys_unstable_apis)]
|
2020-11-15 19:55:41 +00:00
|
|
|
pub fn set_clipboard_text(s: &str) {
|
|
|
|
if let Some(window) = web_sys::window() {
|
2021-08-15 14:55:33 +00:00
|
|
|
if let Some(clipboard) = window.navigator().clipboard() {
|
|
|
|
let promise = clipboard.write_text(s);
|
|
|
|
let future = wasm_bindgen_futures::JsFuture::from(promise);
|
|
|
|
let future = async move {
|
|
|
|
if let Err(err) = future.await {
|
2022-02-01 11:27:39 +00:00
|
|
|
tracing::error!("Copy/cut action denied: {:?}", err);
|
2021-08-15 14:55:33 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
wasm_bindgen_futures::spawn_local(future);
|
|
|
|
}
|
2020-11-15 19:55:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-17 23:43:58 +00:00
|
|
|
pub fn spawn_future<F>(future: F)
|
|
|
|
where
|
|
|
|
F: std::future::Future<Output = ()> + 'static,
|
|
|
|
{
|
|
|
|
wasm_bindgen_futures::spawn_local(future);
|
|
|
|
}
|
|
|
|
|
2020-07-18 08:54:31 +00:00
|
|
|
fn cursor_web_name(cursor: egui::CursorIcon) -> &'static str {
|
|
|
|
match cursor {
|
2021-03-13 11:38:03 +00:00
|
|
|
egui::CursorIcon::Alias => "alias",
|
|
|
|
egui::CursorIcon::AllScroll => "all-scroll",
|
|
|
|
egui::CursorIcon::Cell => "cell",
|
|
|
|
egui::CursorIcon::ContextMenu => "context-menu",
|
|
|
|
egui::CursorIcon::Copy => "copy",
|
|
|
|
egui::CursorIcon::Crosshair => "crosshair",
|
|
|
|
egui::CursorIcon::Default => "default",
|
|
|
|
egui::CursorIcon::Grab => "grab",
|
|
|
|
egui::CursorIcon::Grabbing => "grabbing",
|
|
|
|
egui::CursorIcon::Help => "help",
|
|
|
|
egui::CursorIcon::Move => "move",
|
|
|
|
egui::CursorIcon::NoDrop => "no-drop",
|
|
|
|
egui::CursorIcon::None => "none",
|
|
|
|
egui::CursorIcon::NotAllowed => "not-allowed",
|
|
|
|
egui::CursorIcon::PointingHand => "pointer",
|
|
|
|
egui::CursorIcon::Progress => "progress",
|
|
|
|
egui::CursorIcon::ResizeHorizontal => "ew-resize",
|
|
|
|
egui::CursorIcon::ResizeNeSw => "nesw-resize",
|
|
|
|
egui::CursorIcon::ResizeNwSe => "nwse-resize",
|
|
|
|
egui::CursorIcon::ResizeVertical => "ns-resize",
|
|
|
|
egui::CursorIcon::Text => "text",
|
|
|
|
egui::CursorIcon::VerticalText => "vertical-text",
|
|
|
|
egui::CursorIcon::Wait => "wait",
|
|
|
|
egui::CursorIcon::ZoomIn => "zoom-in",
|
|
|
|
egui::CursorIcon::ZoomOut => "zoom-out",
|
2020-07-18 08:54:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-08 16:48:23 +00:00
|
|
|
pub fn open_url(url: &str, new_tab: bool) -> Option<()> {
|
|
|
|
let name = if new_tab { "_blank" } else { "_self" };
|
|
|
|
|
2020-07-18 08:54:31 +00:00
|
|
|
web_sys::window()?
|
2021-03-08 16:48:23 +00:00
|
|
|
.open_with_url_and_target(url, name)
|
2020-07-18 08:54:31 +00:00
|
|
|
.ok()?;
|
|
|
|
Some(())
|
|
|
|
}
|
|
|
|
|
2022-02-17 15:46:43 +00:00
|
|
|
/// e.g. "#fragment" part of "www.example.com/index.html#fragment",
|
|
|
|
///
|
|
|
|
/// Percent decoded
|
|
|
|
pub fn location_hash() -> String {
|
|
|
|
percent_decode(
|
|
|
|
&web_sys::window()
|
|
|
|
.unwrap()
|
|
|
|
.location()
|
|
|
|
.hash()
|
|
|
|
.unwrap_or_default(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn percent_decode(s: &str) -> String {
|
|
|
|
percent_encoding::percent_decode_str(s)
|
|
|
|
.decode_utf8_lossy()
|
|
|
|
.to_string()
|
2020-07-18 08:54:31 +00:00
|
|
|
}
|
2020-07-18 16:00:05 +00:00
|
|
|
|
2020-07-18 16:35:17 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct AppRunnerRef(Arc<Mutex<AppRunner>>);
|
|
|
|
|
2020-07-18 17:40:24 +00:00
|
|
|
fn paint_and_schedule(runner_ref: AppRunnerRef) -> Result<(), JsValue> {
|
2020-07-18 22:44:06 +00:00
|
|
|
fn paint_if_needed(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
2020-07-18 17:40:24 +00:00
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
2020-11-20 19:35:16 +00:00
|
|
|
if runner_lock.needs_repaint.fetch_and_clear() {
|
2022-01-15 12:59:52 +00:00
|
|
|
let (needs_repaint, clipped_meshes) = runner_lock.logic()?;
|
2021-01-25 20:43:17 +00:00
|
|
|
runner_lock.paint(clipped_meshes)?;
|
2022-01-15 12:59:52 +00:00
|
|
|
if needs_repaint {
|
2020-11-20 19:35:16 +00:00
|
|
|
runner_lock.needs_repaint.set_true();
|
|
|
|
}
|
2020-12-19 19:50:00 +00:00
|
|
|
runner_lock.auto_save();
|
2020-07-18 21:56:37 +00:00
|
|
|
}
|
2020-12-19 19:50:00 +00:00
|
|
|
|
2020-07-18 21:56:37 +00:00
|
|
|
Ok(())
|
2020-07-18 16:35:17 +00:00
|
|
|
}
|
2020-07-18 17:40:24 +00:00
|
|
|
|
2020-07-18 21:56:37 +00:00
|
|
|
fn request_animation_frame(runner_ref: AppRunnerRef) -> Result<(), JsValue> {
|
2020-07-18 17:40:24 +00:00
|
|
|
use wasm_bindgen::JsCast;
|
|
|
|
let window = web_sys::window().unwrap();
|
|
|
|
let closure = Closure::once(move || paint_and_schedule(runner_ref));
|
|
|
|
window.request_animation_frame(closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget(); // We must forget it, or else the callback is canceled on drop
|
2020-07-18 21:56:37 +00:00
|
|
|
Ok(())
|
2020-07-18 17:40:24 +00:00
|
|
|
}
|
2020-07-18 16:35:17 +00:00
|
|
|
|
2020-07-18 22:44:06 +00:00
|
|
|
paint_if_needed(&runner_ref)?;
|
2020-07-18 17:40:24 +00:00
|
|
|
request_animation_frame(runner_ref)
|
2020-07-18 16:35:17 +00:00
|
|
|
}
|
|
|
|
|
2020-07-18 17:40:24 +00:00
|
|
|
fn install_document_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
2020-07-18 16:35:17 +00:00
|
|
|
use wasm_bindgen::JsCast;
|
2020-07-20 13:08:27 +00:00
|
|
|
let window = web_sys::window().unwrap();
|
|
|
|
let document = window.document().unwrap();
|
2020-07-18 16:35:17 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
// keydown
|
2020-07-18 17:40:24 +00:00
|
|
|
let runner_ref = runner_ref.clone();
|
2020-07-18 16:35:17 +00:00
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::KeyboardEvent| {
|
2020-07-30 09:54:42 +00:00
|
|
|
if event.is_composing() || event.key_code() == 229 {
|
|
|
|
// https://www.fxsitecompat.dev/en-CA/docs/2018/keydown-and-keyup-events-are-now-fired-during-ime-composition/
|
|
|
|
return;
|
|
|
|
}
|
2020-11-15 13:21:21 +00:00
|
|
|
|
2020-07-18 17:40:24 +00:00
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
2020-11-15 13:21:21 +00:00
|
|
|
let modifiers = modifiers_from_event(&event);
|
|
|
|
runner_lock.input.raw.modifiers = modifiers;
|
|
|
|
|
2020-07-18 16:35:17 +00:00
|
|
|
let key = event.key();
|
2020-11-14 20:01:21 +00:00
|
|
|
|
|
|
|
if let Some(key) = translate_key(&key) {
|
2020-11-15 13:21:21 +00:00
|
|
|
runner_lock.input.raw.events.push(egui::Event::Key {
|
2020-11-14 20:01:21 +00:00
|
|
|
key,
|
|
|
|
pressed: true,
|
2020-11-15 13:21:21 +00:00
|
|
|
modifiers,
|
2020-11-14 20:01:21 +00:00
|
|
|
});
|
2020-07-18 16:35:17 +00:00
|
|
|
}
|
2021-03-26 12:56:26 +00:00
|
|
|
if !modifiers.ctrl
|
|
|
|
&& !modifiers.command
|
|
|
|
&& !should_ignore_key(&key)
|
|
|
|
// When text agent is shown, it sends text event instead.
|
2022-02-21 08:23:02 +00:00
|
|
|
&& text_agent::text_agent().hidden()
|
2021-03-26 12:56:26 +00:00
|
|
|
{
|
2020-11-15 13:21:21 +00:00
|
|
|
runner_lock.input.raw.events.push(egui::Event::Text(key));
|
|
|
|
}
|
2020-11-20 19:35:16 +00:00
|
|
|
runner_lock.needs_repaint.set_true();
|
2020-11-18 18:16:13 +00:00
|
|
|
|
2021-01-17 11:22:19 +00:00
|
|
|
let egui_wants_keyboard = runner_lock.egui_ctx().wants_keyboard_input();
|
|
|
|
|
|
|
|
let prevent_default = if matches!(event.key().as_str(), "Tab") {
|
|
|
|
// Always prevent moving cursor to url bar.
|
2021-01-17 13:48:59 +00:00
|
|
|
// egui wants to use tab to move to the next text field.
|
2021-01-17 11:22:19 +00:00
|
|
|
true
|
|
|
|
} else if egui_wants_keyboard {
|
|
|
|
matches!(
|
|
|
|
event.key().as_str(),
|
|
|
|
"Backspace" // so we don't go back to previous page when deleting text
|
|
|
|
| "ArrowDown" | "ArrowLeft" | "ArrowRight" | "ArrowUp" // cmd-left is "back" on Mac (https://github.com/emilk/egui/issues/58)
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
// We never want to prevent:
|
|
|
|
// * F5 / cmd-R (refresh)
|
|
|
|
// * cmd-shift-C (debug tools)
|
|
|
|
// * cmd/ctrl-c/v/x (or we stop copy/past/cut events)
|
|
|
|
false
|
|
|
|
};
|
|
|
|
|
2022-02-01 11:27:39 +00:00
|
|
|
// tracing::debug!(
|
2021-01-17 11:22:19 +00:00
|
|
|
// "On key-down {:?}, egui_wants_keyboard: {}, prevent_default: {}",
|
|
|
|
// event.key().as_str(),
|
|
|
|
// egui_wants_keyboard,
|
|
|
|
// prevent_default
|
2022-02-01 11:27:39 +00:00
|
|
|
// );
|
2021-01-17 11:22:19 +00:00
|
|
|
|
|
|
|
if prevent_default {
|
2020-11-18 18:16:13 +00:00
|
|
|
event.prevent_default();
|
|
|
|
}
|
2020-07-18 16:35:17 +00:00
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
document.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// keyup
|
2020-07-18 17:40:24 +00:00
|
|
|
let runner_ref = runner_ref.clone();
|
2020-07-18 16:35:17 +00:00
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::KeyboardEvent| {
|
2020-07-18 17:40:24 +00:00
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
2020-11-15 13:21:21 +00:00
|
|
|
let modifiers = modifiers_from_event(&event);
|
|
|
|
runner_lock.input.raw.modifiers = modifiers;
|
|
|
|
if let Some(key) = translate_key(&event.key()) {
|
|
|
|
runner_lock.input.raw.events.push(egui::Event::Key {
|
2020-07-18 16:35:17 +00:00
|
|
|
key,
|
|
|
|
pressed: false,
|
2020-11-15 13:21:21 +00:00
|
|
|
modifiers,
|
2020-07-18 16:35:17 +00:00
|
|
|
});
|
|
|
|
}
|
2020-11-20 19:35:16 +00:00
|
|
|
runner_lock.needs_repaint.set_true();
|
2020-07-18 16:35:17 +00:00
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
document.add_event_listener_with_callback("keyup", closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
2020-12-29 11:42:15 +00:00
|
|
|
#[cfg(web_sys_unstable_apis)]
|
2021-09-28 18:47:02 +00:00
|
|
|
{
|
2020-11-15 19:55:41 +00:00
|
|
|
// paste
|
|
|
|
let runner_ref = runner_ref.clone();
|
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::ClipboardEvent| {
|
|
|
|
if let Some(data) = event.clipboard_data() {
|
|
|
|
if let Ok(text) = data.get_data("text") {
|
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
2021-10-20 20:26:26 +00:00
|
|
|
runner_lock
|
|
|
|
.input
|
|
|
|
.raw
|
|
|
|
.events
|
2022-01-10 22:12:30 +00:00
|
|
|
.push(egui::Event::Paste(text.replace("\r\n", "\n")));
|
2020-11-20 19:35:16 +00:00
|
|
|
runner_lock.needs_repaint.set_true();
|
2021-09-28 18:47:02 +00:00
|
|
|
event.stop_propagation();
|
|
|
|
event.prevent_default();
|
2020-11-15 19:55:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
document.add_event_listener_with_callback("paste", closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
2020-12-29 11:42:15 +00:00
|
|
|
#[cfg(web_sys_unstable_apis)]
|
2020-11-15 19:55:41 +00:00
|
|
|
{
|
|
|
|
// cut
|
|
|
|
let runner_ref = runner_ref.clone();
|
|
|
|
let closure = Closure::wrap(Box::new(move |_: web_sys::ClipboardEvent| {
|
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
|
|
|
runner_lock.input.raw.events.push(egui::Event::Cut);
|
2020-11-20 19:35:16 +00:00
|
|
|
runner_lock.needs_repaint.set_true();
|
2020-11-15 19:55:41 +00:00
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
document.add_event_listener_with_callback("cut", closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
2020-12-29 11:42:15 +00:00
|
|
|
#[cfg(web_sys_unstable_apis)]
|
2020-11-15 19:55:41 +00:00
|
|
|
{
|
|
|
|
// copy
|
|
|
|
let runner_ref = runner_ref.clone();
|
|
|
|
let closure = Closure::wrap(Box::new(move |_: web_sys::ClipboardEvent| {
|
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
|
|
|
runner_lock.input.raw.events.push(egui::Event::Copy);
|
2020-11-20 19:35:16 +00:00
|
|
|
runner_lock.needs_repaint.set_true();
|
2020-11-15 19:55:41 +00:00
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
document.add_event_listener_with_callback("copy", closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
2020-07-18 16:35:17 +00:00
|
|
|
for event_name in &["load", "pagehide", "pageshow", "resize"] {
|
2020-07-18 17:40:24 +00:00
|
|
|
let runner_ref = runner_ref.clone();
|
2020-07-18 16:35:17 +00:00
|
|
|
let closure = Closure::wrap(Box::new(move || {
|
2020-11-20 19:35:16 +00:00
|
|
|
runner_ref.0.lock().needs_repaint.set_true();
|
2020-07-18 16:35:17 +00:00
|
|
|
}) as Box<dyn FnMut()>);
|
2020-07-20 13:08:27 +00:00
|
|
|
window.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
2020-07-18 16:35:17 +00:00
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
2022-01-21 18:41:18 +00:00
|
|
|
{
|
|
|
|
// hashchange
|
|
|
|
let runner_ref = runner_ref.clone();
|
|
|
|
let closure = Closure::wrap(Box::new(move || {
|
|
|
|
let runner_lock = runner_ref.0.lock();
|
|
|
|
let mut frame_lock = runner_lock.frame.lock();
|
|
|
|
|
|
|
|
// `epi::Frame::info(&self)` clones `epi::IntegrationInfo`, but we need to modify the original here
|
|
|
|
if let Some(web_info) = &mut frame_lock.info.web_info {
|
2022-02-17 15:46:43 +00:00
|
|
|
web_info.location.hash = location_hash();
|
2022-01-21 18:41:18 +00:00
|
|
|
}
|
|
|
|
}) as Box<dyn FnMut()>);
|
|
|
|
window.add_event_listener_with_callback("hashchange", closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
2020-07-18 16:35:17 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-11-18 00:07:32 +00:00
|
|
|
/// Repaint at least every `ms` milliseconds.
|
|
|
|
fn repaint_every_ms(runner_ref: &AppRunnerRef, milliseconds: i32) -> Result<(), JsValue> {
|
|
|
|
assert!(milliseconds >= 0);
|
|
|
|
use wasm_bindgen::JsCast;
|
|
|
|
let window = web_sys::window().unwrap();
|
|
|
|
let runner_ref = runner_ref.clone();
|
|
|
|
let closure = Closure::wrap(Box::new(move || {
|
2020-11-20 19:35:16 +00:00
|
|
|
runner_ref.0.lock().needs_repaint.set_true();
|
2020-11-18 00:07:32 +00:00
|
|
|
}) as Box<dyn FnMut()>);
|
|
|
|
window.set_interval_with_callback_and_timeout_and_arguments_0(
|
|
|
|
closure.as_ref().unchecked_ref(),
|
|
|
|
milliseconds,
|
|
|
|
)?;
|
|
|
|
closure.forget();
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-07-18 17:40:24 +00:00
|
|
|
fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
2020-07-18 16:35:17 +00:00
|
|
|
use wasm_bindgen::JsCast;
|
2020-07-18 17:40:24 +00:00
|
|
|
let canvas = canvas_element(runner_ref.0.lock().canvas_id()).unwrap();
|
2020-07-18 16:35:17 +00:00
|
|
|
|
2021-01-25 17:50:19 +00:00
|
|
|
{
|
|
|
|
// By default, right-clicks open a context menu.
|
|
|
|
// We don't want to do that (right clicks is handled by egui):
|
|
|
|
let event_name = "contextmenu";
|
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
|
|
|
|
event.prevent_default();
|
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
2020-07-18 16:35:17 +00:00
|
|
|
{
|
|
|
|
let event_name = "mousedown";
|
2020-07-18 17:40:24 +00:00
|
|
|
let runner_ref = runner_ref.clone();
|
2020-07-18 16:35:17 +00:00
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
|
2021-08-28 06:37:07 +00:00
|
|
|
if let Some(button) = button_from_mouse_event(&event) {
|
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
|
|
|
let pos = pos_from_mouse_event(runner_lock.canvas_id(), &event);
|
|
|
|
let modifiers = runner_lock.input.raw.modifiers;
|
|
|
|
runner_lock
|
|
|
|
.input
|
|
|
|
.raw
|
|
|
|
.events
|
|
|
|
.push(egui::Event::PointerButton {
|
|
|
|
pos,
|
|
|
|
button,
|
|
|
|
pressed: true,
|
|
|
|
modifiers,
|
|
|
|
});
|
|
|
|
runner_lock.needs_repaint.set_true();
|
2020-07-18 16:35:17 +00:00
|
|
|
}
|
2021-08-28 06:37:07 +00:00
|
|
|
event.stop_propagation();
|
2022-02-05 07:56:53 +00:00
|
|
|
// Note: prevent_default breaks VSCode tab focusing, hence why we don't call it here.
|
2020-07-18 16:35:17 +00:00
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let event_name = "mousemove";
|
2020-07-18 17:40:24 +00:00
|
|
|
let runner_ref = runner_ref.clone();
|
2020-07-18 16:35:17 +00:00
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
|
2020-07-18 17:40:24 +00:00
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
2021-08-28 06:37:07 +00:00
|
|
|
let pos = pos_from_mouse_event(runner_lock.canvas_id(), &event);
|
|
|
|
runner_lock
|
|
|
|
.input
|
|
|
|
.raw
|
|
|
|
.events
|
|
|
|
.push(egui::Event::PointerMoved(pos));
|
|
|
|
runner_lock.needs_repaint.set_true();
|
|
|
|
event.stop_propagation();
|
|
|
|
event.prevent_default();
|
2020-07-18 16:35:17 +00:00
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let event_name = "mouseup";
|
2020-07-18 17:40:24 +00:00
|
|
|
let runner_ref = runner_ref.clone();
|
2020-07-18 16:35:17 +00:00
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
|
2021-08-28 06:37:07 +00:00
|
|
|
if let Some(button) = button_from_mouse_event(&event) {
|
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
|
|
|
let pos = pos_from_mouse_event(runner_lock.canvas_id(), &event);
|
|
|
|
let modifiers = runner_lock.input.raw.modifiers;
|
|
|
|
runner_lock
|
|
|
|
.input
|
|
|
|
.raw
|
|
|
|
.events
|
|
|
|
.push(egui::Event::PointerButton {
|
|
|
|
pos,
|
|
|
|
button,
|
|
|
|
pressed: false,
|
|
|
|
modifiers,
|
|
|
|
});
|
|
|
|
runner_lock.needs_repaint.set_true();
|
2021-10-23 14:23:29 +00:00
|
|
|
|
2022-02-21 08:23:02 +00:00
|
|
|
text_agent::update_text_agent(&runner_lock);
|
2020-07-18 16:35:17 +00:00
|
|
|
}
|
2021-08-28 06:37:07 +00:00
|
|
|
event.stop_propagation();
|
|
|
|
event.prevent_default();
|
2020-07-18 16:35:17 +00:00
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let event_name = "mouseleave";
|
2020-07-18 17:40:24 +00:00
|
|
|
let runner_ref = runner_ref.clone();
|
2020-07-18 16:35:17 +00:00
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
|
2020-07-18 17:40:24 +00:00
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
2021-08-28 06:37:07 +00:00
|
|
|
runner_lock.input.raw.events.push(egui::Event::PointerGone);
|
|
|
|
runner_lock.needs_repaint.set_true();
|
|
|
|
event.stop_propagation();
|
|
|
|
event.prevent_default();
|
2020-07-18 16:35:17 +00:00
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let event_name = "touchstart";
|
2020-07-18 17:40:24 +00:00
|
|
|
let runner_ref = runner_ref.clone();
|
2020-07-18 16:35:17 +00:00
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::TouchEvent| {
|
2020-07-18 17:40:24 +00:00
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
Basic multi touch support (issue #279) (#306)
* translate touch events from glium to egui
Unfortunately, winit does not seem to create _Touch_ events for the touch pad
on my mac. Only _TouchpadPressure_ events are sent.
Found some issues (like
[this](https://github.com/rust-windowing/winit/issues/54)), but I am not sure
what they exactly mean: Sometimes, touch events are mixed with
touch-to-pointer translation in the discussions.
* translate touch events from web_sys to egui
The are a few open topics:
- egui_web currently translates touch events into pointer events.
I guess this should change, such that egui itself performs this kind of
conversion.
- `pub fn egui_web::pos_from_touch_event` is a public function, but I
would like to change the return type to an `Option`. Shouldn't this
function be private, anyway?
* introduce `TouchState` and `Gesture`
InputState.touch was introduced with type `TouchState`, just as
InputState.pointer is of type `Pointer`.
The TouchState internally relies on a collection of `Gesture`s. This commit
provides the first rudimentary implementation of a Gesture, but has no
functionality, yet.
* add method InputState::zoom()
So far, the method always returns `None`, but it should work as soon as the
`Zoom` gesture is implemented.
* manage one `TouchState` per individual device
Although quite unlikely, it is still possible to connect more than one touch
device. (I have three touch pads connected to my MacBook in total, but
unfortunately `winit` sends touch events for none of them.)
We do not want to mix-up the touches from different devices.
* implement control loop for gesture detection
The basic idea is that each gesture can focus on detection logic and does not
have to care (too much) about managing touch state in general.
* streamline `Gesture` trait, simplifying impl's
* implement first version of Zoom gesture
* fix failing doctest
a simple `TODO` should be enough
* get rid of `Gesture`s
* Provide a Zoom/Rotate window in the demo app
For now, it works for two fingers only. The third finger interrupts the
gesture.
Bugs:
- Pinching in the demo window also moves the window -> Pointer events must be
ignored when touch is active
- Pinching also works when doing it outside the demo window -> it would be nice
to return the touch info in the `Response` of the painter allocation
* fix comments and non-idiomatic code
* update touch state *each frame*
* change egui_demo to use *relative* touch data
* support more than two fingers
This commit includes an improved Demo Window for egui_demo, and a complete
re-write of the gesture detection. The PR should be ready for review, soon.
* cleanup code and comments for review
* minor code simplifications
* oops – forgot the changelog
* resolve comment https://github.com/emilk/egui/pull/306/files/fee8ed83dbe715b5b70433faacfe74b59c99e4a4#r623226656
* accept suggestion https://github.com/emilk/egui/pull/306#discussion_r623229228
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
* fix syntax error (dough!)
* remove `dbg!` (why didnt clippy see this?)
* apply suggested diffs from review
* fix conversion of physical location to Pos2
* remove redundanct type `TouchAverages`
* remove trailing space
* avoid initial translation jump in plot demo
* extend the demo so it shows off translation
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2021-05-06 19:01:10 +00:00
|
|
|
let mut latest_touch_pos_id = runner_lock.input.latest_touch_pos_id;
|
|
|
|
let pos =
|
|
|
|
pos_from_touch_event(runner_lock.canvas_id(), &event, &mut latest_touch_pos_id);
|
|
|
|
runner_lock.input.latest_touch_pos_id = latest_touch_pos_id;
|
2021-01-25 17:50:19 +00:00
|
|
|
runner_lock.input.latest_touch_pos = Some(pos);
|
|
|
|
let modifiers = runner_lock.input.raw.modifiers;
|
|
|
|
runner_lock
|
|
|
|
.input
|
|
|
|
.raw
|
|
|
|
.events
|
|
|
|
.push(egui::Event::PointerButton {
|
|
|
|
pos,
|
|
|
|
button: egui::PointerButton::Primary,
|
|
|
|
pressed: true,
|
|
|
|
modifiers,
|
|
|
|
});
|
Basic multi touch support (issue #279) (#306)
* translate touch events from glium to egui
Unfortunately, winit does not seem to create _Touch_ events for the touch pad
on my mac. Only _TouchpadPressure_ events are sent.
Found some issues (like
[this](https://github.com/rust-windowing/winit/issues/54)), but I am not sure
what they exactly mean: Sometimes, touch events are mixed with
touch-to-pointer translation in the discussions.
* translate touch events from web_sys to egui
The are a few open topics:
- egui_web currently translates touch events into pointer events.
I guess this should change, such that egui itself performs this kind of
conversion.
- `pub fn egui_web::pos_from_touch_event` is a public function, but I
would like to change the return type to an `Option`. Shouldn't this
function be private, anyway?
* introduce `TouchState` and `Gesture`
InputState.touch was introduced with type `TouchState`, just as
InputState.pointer is of type `Pointer`.
The TouchState internally relies on a collection of `Gesture`s. This commit
provides the first rudimentary implementation of a Gesture, but has no
functionality, yet.
* add method InputState::zoom()
So far, the method always returns `None`, but it should work as soon as the
`Zoom` gesture is implemented.
* manage one `TouchState` per individual device
Although quite unlikely, it is still possible to connect more than one touch
device. (I have three touch pads connected to my MacBook in total, but
unfortunately `winit` sends touch events for none of them.)
We do not want to mix-up the touches from different devices.
* implement control loop for gesture detection
The basic idea is that each gesture can focus on detection logic and does not
have to care (too much) about managing touch state in general.
* streamline `Gesture` trait, simplifying impl's
* implement first version of Zoom gesture
* fix failing doctest
a simple `TODO` should be enough
* get rid of `Gesture`s
* Provide a Zoom/Rotate window in the demo app
For now, it works for two fingers only. The third finger interrupts the
gesture.
Bugs:
- Pinching in the demo window also moves the window -> Pointer events must be
ignored when touch is active
- Pinching also works when doing it outside the demo window -> it would be nice
to return the touch info in the `Response` of the painter allocation
* fix comments and non-idiomatic code
* update touch state *each frame*
* change egui_demo to use *relative* touch data
* support more than two fingers
This commit includes an improved Demo Window for egui_demo, and a complete
re-write of the gesture detection. The PR should be ready for review, soon.
* cleanup code and comments for review
* minor code simplifications
* oops – forgot the changelog
* resolve comment https://github.com/emilk/egui/pull/306/files/fee8ed83dbe715b5b70433faacfe74b59c99e4a4#r623226656
* accept suggestion https://github.com/emilk/egui/pull/306#discussion_r623229228
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
* fix syntax error (dough!)
* remove `dbg!` (why didnt clippy see this?)
* apply suggested diffs from review
* fix conversion of physical location to Pos2
* remove redundanct type `TouchAverages`
* remove trailing space
* avoid initial translation jump in plot demo
* extend the demo so it shows off translation
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2021-05-06 19:01:10 +00:00
|
|
|
|
|
|
|
push_touches(&mut *runner_lock, egui::TouchPhase::Start, &event);
|
2020-11-20 19:35:16 +00:00
|
|
|
runner_lock.needs_repaint.set_true();
|
2020-07-18 16:35:17 +00:00
|
|
|
event.stop_propagation();
|
|
|
|
event.prevent_default();
|
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let event_name = "touchmove";
|
2020-07-18 17:40:24 +00:00
|
|
|
let runner_ref = runner_ref.clone();
|
2020-07-18 16:35:17 +00:00
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::TouchEvent| {
|
2020-07-18 17:40:24 +00:00
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
Basic multi touch support (issue #279) (#306)
* translate touch events from glium to egui
Unfortunately, winit does not seem to create _Touch_ events for the touch pad
on my mac. Only _TouchpadPressure_ events are sent.
Found some issues (like
[this](https://github.com/rust-windowing/winit/issues/54)), but I am not sure
what they exactly mean: Sometimes, touch events are mixed with
touch-to-pointer translation in the discussions.
* translate touch events from web_sys to egui
The are a few open topics:
- egui_web currently translates touch events into pointer events.
I guess this should change, such that egui itself performs this kind of
conversion.
- `pub fn egui_web::pos_from_touch_event` is a public function, but I
would like to change the return type to an `Option`. Shouldn't this
function be private, anyway?
* introduce `TouchState` and `Gesture`
InputState.touch was introduced with type `TouchState`, just as
InputState.pointer is of type `Pointer`.
The TouchState internally relies on a collection of `Gesture`s. This commit
provides the first rudimentary implementation of a Gesture, but has no
functionality, yet.
* add method InputState::zoom()
So far, the method always returns `None`, but it should work as soon as the
`Zoom` gesture is implemented.
* manage one `TouchState` per individual device
Although quite unlikely, it is still possible to connect more than one touch
device. (I have three touch pads connected to my MacBook in total, but
unfortunately `winit` sends touch events for none of them.)
We do not want to mix-up the touches from different devices.
* implement control loop for gesture detection
The basic idea is that each gesture can focus on detection logic and does not
have to care (too much) about managing touch state in general.
* streamline `Gesture` trait, simplifying impl's
* implement first version of Zoom gesture
* fix failing doctest
a simple `TODO` should be enough
* get rid of `Gesture`s
* Provide a Zoom/Rotate window in the demo app
For now, it works for two fingers only. The third finger interrupts the
gesture.
Bugs:
- Pinching in the demo window also moves the window -> Pointer events must be
ignored when touch is active
- Pinching also works when doing it outside the demo window -> it would be nice
to return the touch info in the `Response` of the painter allocation
* fix comments and non-idiomatic code
* update touch state *each frame*
* change egui_demo to use *relative* touch data
* support more than two fingers
This commit includes an improved Demo Window for egui_demo, and a complete
re-write of the gesture detection. The PR should be ready for review, soon.
* cleanup code and comments for review
* minor code simplifications
* oops – forgot the changelog
* resolve comment https://github.com/emilk/egui/pull/306/files/fee8ed83dbe715b5b70433faacfe74b59c99e4a4#r623226656
* accept suggestion https://github.com/emilk/egui/pull/306#discussion_r623229228
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
* fix syntax error (dough!)
* remove `dbg!` (why didnt clippy see this?)
* apply suggested diffs from review
* fix conversion of physical location to Pos2
* remove redundanct type `TouchAverages`
* remove trailing space
* avoid initial translation jump in plot demo
* extend the demo so it shows off translation
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2021-05-06 19:01:10 +00:00
|
|
|
let mut latest_touch_pos_id = runner_lock.input.latest_touch_pos_id;
|
|
|
|
let pos =
|
|
|
|
pos_from_touch_event(runner_lock.canvas_id(), &event, &mut latest_touch_pos_id);
|
|
|
|
runner_lock.input.latest_touch_pos_id = latest_touch_pos_id;
|
2021-01-25 17:50:19 +00:00
|
|
|
runner_lock.input.latest_touch_pos = Some(pos);
|
|
|
|
runner_lock
|
|
|
|
.input
|
|
|
|
.raw
|
|
|
|
.events
|
|
|
|
.push(egui::Event::PointerMoved(pos));
|
Basic multi touch support (issue #279) (#306)
* translate touch events from glium to egui
Unfortunately, winit does not seem to create _Touch_ events for the touch pad
on my mac. Only _TouchpadPressure_ events are sent.
Found some issues (like
[this](https://github.com/rust-windowing/winit/issues/54)), but I am not sure
what they exactly mean: Sometimes, touch events are mixed with
touch-to-pointer translation in the discussions.
* translate touch events from web_sys to egui
The are a few open topics:
- egui_web currently translates touch events into pointer events.
I guess this should change, such that egui itself performs this kind of
conversion.
- `pub fn egui_web::pos_from_touch_event` is a public function, but I
would like to change the return type to an `Option`. Shouldn't this
function be private, anyway?
* introduce `TouchState` and `Gesture`
InputState.touch was introduced with type `TouchState`, just as
InputState.pointer is of type `Pointer`.
The TouchState internally relies on a collection of `Gesture`s. This commit
provides the first rudimentary implementation of a Gesture, but has no
functionality, yet.
* add method InputState::zoom()
So far, the method always returns `None`, but it should work as soon as the
`Zoom` gesture is implemented.
* manage one `TouchState` per individual device
Although quite unlikely, it is still possible to connect more than one touch
device. (I have three touch pads connected to my MacBook in total, but
unfortunately `winit` sends touch events for none of them.)
We do not want to mix-up the touches from different devices.
* implement control loop for gesture detection
The basic idea is that each gesture can focus on detection logic and does not
have to care (too much) about managing touch state in general.
* streamline `Gesture` trait, simplifying impl's
* implement first version of Zoom gesture
* fix failing doctest
a simple `TODO` should be enough
* get rid of `Gesture`s
* Provide a Zoom/Rotate window in the demo app
For now, it works for two fingers only. The third finger interrupts the
gesture.
Bugs:
- Pinching in the demo window also moves the window -> Pointer events must be
ignored when touch is active
- Pinching also works when doing it outside the demo window -> it would be nice
to return the touch info in the `Response` of the painter allocation
* fix comments and non-idiomatic code
* update touch state *each frame*
* change egui_demo to use *relative* touch data
* support more than two fingers
This commit includes an improved Demo Window for egui_demo, and a complete
re-write of the gesture detection. The PR should be ready for review, soon.
* cleanup code and comments for review
* minor code simplifications
* oops – forgot the changelog
* resolve comment https://github.com/emilk/egui/pull/306/files/fee8ed83dbe715b5b70433faacfe74b59c99e4a4#r623226656
* accept suggestion https://github.com/emilk/egui/pull/306#discussion_r623229228
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
* fix syntax error (dough!)
* remove `dbg!` (why didnt clippy see this?)
* apply suggested diffs from review
* fix conversion of physical location to Pos2
* remove redundanct type `TouchAverages`
* remove trailing space
* avoid initial translation jump in plot demo
* extend the demo so it shows off translation
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2021-05-06 19:01:10 +00:00
|
|
|
|
|
|
|
push_touches(&mut *runner_lock, egui::TouchPhase::Move, &event);
|
2020-11-20 19:35:16 +00:00
|
|
|
runner_lock.needs_repaint.set_true();
|
2020-07-18 16:35:17 +00:00
|
|
|
event.stop_propagation();
|
|
|
|
event.prevent_default();
|
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let event_name = "touchend";
|
2020-07-18 17:40:24 +00:00
|
|
|
let runner_ref = runner_ref.clone();
|
2020-07-18 16:35:17 +00:00
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::TouchEvent| {
|
2020-07-18 17:40:24 +00:00
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
2021-10-23 14:23:29 +00:00
|
|
|
|
2021-01-25 17:50:19 +00:00
|
|
|
if let Some(pos) = runner_lock.input.latest_touch_pos {
|
|
|
|
let modifiers = runner_lock.input.raw.modifiers;
|
|
|
|
// First release mouse to click:
|
|
|
|
runner_lock
|
|
|
|
.input
|
|
|
|
.raw
|
|
|
|
.events
|
|
|
|
.push(egui::Event::PointerButton {
|
|
|
|
pos,
|
|
|
|
button: egui::PointerButton::Primary,
|
|
|
|
pressed: false,
|
|
|
|
modifiers,
|
|
|
|
});
|
|
|
|
// Then remove hover effect:
|
|
|
|
runner_lock.input.raw.events.push(egui::Event::PointerGone);
|
Basic multi touch support (issue #279) (#306)
* translate touch events from glium to egui
Unfortunately, winit does not seem to create _Touch_ events for the touch pad
on my mac. Only _TouchpadPressure_ events are sent.
Found some issues (like
[this](https://github.com/rust-windowing/winit/issues/54)), but I am not sure
what they exactly mean: Sometimes, touch events are mixed with
touch-to-pointer translation in the discussions.
* translate touch events from web_sys to egui
The are a few open topics:
- egui_web currently translates touch events into pointer events.
I guess this should change, such that egui itself performs this kind of
conversion.
- `pub fn egui_web::pos_from_touch_event` is a public function, but I
would like to change the return type to an `Option`. Shouldn't this
function be private, anyway?
* introduce `TouchState` and `Gesture`
InputState.touch was introduced with type `TouchState`, just as
InputState.pointer is of type `Pointer`.
The TouchState internally relies on a collection of `Gesture`s. This commit
provides the first rudimentary implementation of a Gesture, but has no
functionality, yet.
* add method InputState::zoom()
So far, the method always returns `None`, but it should work as soon as the
`Zoom` gesture is implemented.
* manage one `TouchState` per individual device
Although quite unlikely, it is still possible to connect more than one touch
device. (I have three touch pads connected to my MacBook in total, but
unfortunately `winit` sends touch events for none of them.)
We do not want to mix-up the touches from different devices.
* implement control loop for gesture detection
The basic idea is that each gesture can focus on detection logic and does not
have to care (too much) about managing touch state in general.
* streamline `Gesture` trait, simplifying impl's
* implement first version of Zoom gesture
* fix failing doctest
a simple `TODO` should be enough
* get rid of `Gesture`s
* Provide a Zoom/Rotate window in the demo app
For now, it works for two fingers only. The third finger interrupts the
gesture.
Bugs:
- Pinching in the demo window also moves the window -> Pointer events must be
ignored when touch is active
- Pinching also works when doing it outside the demo window -> it would be nice
to return the touch info in the `Response` of the painter allocation
* fix comments and non-idiomatic code
* update touch state *each frame*
* change egui_demo to use *relative* touch data
* support more than two fingers
This commit includes an improved Demo Window for egui_demo, and a complete
re-write of the gesture detection. The PR should be ready for review, soon.
* cleanup code and comments for review
* minor code simplifications
* oops – forgot the changelog
* resolve comment https://github.com/emilk/egui/pull/306/files/fee8ed83dbe715b5b70433faacfe74b59c99e4a4#r623226656
* accept suggestion https://github.com/emilk/egui/pull/306#discussion_r623229228
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
* fix syntax error (dough!)
* remove `dbg!` (why didnt clippy see this?)
* apply suggested diffs from review
* fix conversion of physical location to Pos2
* remove redundanct type `TouchAverages`
* remove trailing space
* avoid initial translation jump in plot demo
* extend the demo so it shows off translation
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2021-05-06 19:01:10 +00:00
|
|
|
|
|
|
|
push_touches(&mut *runner_lock, egui::TouchPhase::End, &event);
|
2021-01-25 17:50:19 +00:00
|
|
|
runner_lock.needs_repaint.set_true();
|
|
|
|
event.stop_propagation();
|
|
|
|
event.prevent_default();
|
|
|
|
}
|
2021-10-23 14:23:29 +00:00
|
|
|
|
|
|
|
// Finally, focus or blur text agent to toggle mobile keyboard:
|
2022-02-21 08:23:02 +00:00
|
|
|
text_agent::update_text_agent(&runner_lock);
|
2020-07-18 16:35:17 +00:00
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
Basic multi touch support (issue #279) (#306)
* translate touch events from glium to egui
Unfortunately, winit does not seem to create _Touch_ events for the touch pad
on my mac. Only _TouchpadPressure_ events are sent.
Found some issues (like
[this](https://github.com/rust-windowing/winit/issues/54)), but I am not sure
what they exactly mean: Sometimes, touch events are mixed with
touch-to-pointer translation in the discussions.
* translate touch events from web_sys to egui
The are a few open topics:
- egui_web currently translates touch events into pointer events.
I guess this should change, such that egui itself performs this kind of
conversion.
- `pub fn egui_web::pos_from_touch_event` is a public function, but I
would like to change the return type to an `Option`. Shouldn't this
function be private, anyway?
* introduce `TouchState` and `Gesture`
InputState.touch was introduced with type `TouchState`, just as
InputState.pointer is of type `Pointer`.
The TouchState internally relies on a collection of `Gesture`s. This commit
provides the first rudimentary implementation of a Gesture, but has no
functionality, yet.
* add method InputState::zoom()
So far, the method always returns `None`, but it should work as soon as the
`Zoom` gesture is implemented.
* manage one `TouchState` per individual device
Although quite unlikely, it is still possible to connect more than one touch
device. (I have three touch pads connected to my MacBook in total, but
unfortunately `winit` sends touch events for none of them.)
We do not want to mix-up the touches from different devices.
* implement control loop for gesture detection
The basic idea is that each gesture can focus on detection logic and does not
have to care (too much) about managing touch state in general.
* streamline `Gesture` trait, simplifying impl's
* implement first version of Zoom gesture
* fix failing doctest
a simple `TODO` should be enough
* get rid of `Gesture`s
* Provide a Zoom/Rotate window in the demo app
For now, it works for two fingers only. The third finger interrupts the
gesture.
Bugs:
- Pinching in the demo window also moves the window -> Pointer events must be
ignored when touch is active
- Pinching also works when doing it outside the demo window -> it would be nice
to return the touch info in the `Response` of the painter allocation
* fix comments and non-idiomatic code
* update touch state *each frame*
* change egui_demo to use *relative* touch data
* support more than two fingers
This commit includes an improved Demo Window for egui_demo, and a complete
re-write of the gesture detection. The PR should be ready for review, soon.
* cleanup code and comments for review
* minor code simplifications
* oops – forgot the changelog
* resolve comment https://github.com/emilk/egui/pull/306/files/fee8ed83dbe715b5b70433faacfe74b59c99e4a4#r623226656
* accept suggestion https://github.com/emilk/egui/pull/306#discussion_r623229228
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
* fix syntax error (dough!)
* remove `dbg!` (why didnt clippy see this?)
* apply suggested diffs from review
* fix conversion of physical location to Pos2
* remove redundanct type `TouchAverages`
* remove trailing space
* avoid initial translation jump in plot demo
* extend the demo so it shows off translation
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2021-05-06 19:01:10 +00:00
|
|
|
{
|
|
|
|
let event_name = "touchcancel";
|
|
|
|
let runner_ref = runner_ref.clone();
|
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::TouchEvent| {
|
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
|
|
|
push_touches(&mut *runner_lock, egui::TouchPhase::Cancel, &event);
|
|
|
|
event.stop_propagation();
|
|
|
|
event.prevent_default();
|
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
2020-07-18 16:35:17 +00:00
|
|
|
{
|
|
|
|
let event_name = "wheel";
|
2020-07-18 17:40:24 +00:00
|
|
|
let runner_ref = runner_ref.clone();
|
2020-07-18 16:35:17 +00:00
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::WheelEvent| {
|
2020-07-18 17:40:24 +00:00
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
2021-04-24 09:01:44 +00:00
|
|
|
|
|
|
|
let scroll_multiplier = match event.delta_mode() {
|
|
|
|
web_sys::WheelEvent::DOM_DELTA_PAGE => {
|
|
|
|
canvas_size_in_points(runner_ref.0.lock().canvas_id()).y
|
|
|
|
}
|
|
|
|
web_sys::WheelEvent::DOM_DELTA_LINE => {
|
2021-09-07 17:59:14 +00:00
|
|
|
#[allow(clippy::let_and_return)]
|
2021-09-07 18:47:53 +00:00
|
|
|
let points_per_scroll_line = 8.0; // Note that this is intentionally different from what we use in egui_glium / winit.
|
2021-09-07 17:45:13 +00:00
|
|
|
points_per_scroll_line
|
2021-04-24 09:01:44 +00:00
|
|
|
}
|
2022-01-21 09:48:44 +00:00
|
|
|
_ => 1.0, // DOM_DELTA_PIXEL
|
2021-04-24 09:01:44 +00:00
|
|
|
};
|
|
|
|
|
2022-01-21 09:48:44 +00:00
|
|
|
let mut delta =
|
|
|
|
-scroll_multiplier * egui::vec2(event.delta_x() as f32, event.delta_y() as f32);
|
2021-04-25 15:04:34 +00:00
|
|
|
|
2021-05-20 17:56:33 +00:00
|
|
|
// Report a zoom event in case CTRL (on Windows or Linux) or CMD (on Mac) is pressed.
|
|
|
|
// This if-statement is equivalent to how `Modifiers.command` is determined in
|
|
|
|
// `modifiers_from_event()`, but we cannot directly use that fn for a `WheelEvent`.
|
|
|
|
if event.ctrl_key() || event.meta_key() {
|
2021-11-03 21:57:13 +00:00
|
|
|
let factor = (delta.y / 200.0).exp();
|
|
|
|
runner_lock.input.raw.events.push(egui::Event::Zoom(factor));
|
2021-04-25 15:04:34 +00:00
|
|
|
} else {
|
2022-01-21 09:48:44 +00:00
|
|
|
if event.shift_key() {
|
|
|
|
// Treat as horizontal scrolling.
|
|
|
|
// Note: one Mac we already get horizontal scroll events when shift is down.
|
|
|
|
delta = egui::vec2(delta.x + delta.y, 0.0);
|
|
|
|
}
|
|
|
|
|
2021-11-03 21:57:13 +00:00
|
|
|
runner_lock
|
|
|
|
.input
|
|
|
|
.raw
|
|
|
|
.events
|
|
|
|
.push(egui::Event::Scroll(delta));
|
2021-04-25 15:04:34 +00:00
|
|
|
}
|
|
|
|
|
2020-11-20 19:35:16 +00:00
|
|
|
runner_lock.needs_repaint.set_true();
|
2020-07-18 16:35:17 +00:00
|
|
|
event.stop_propagation();
|
|
|
|
event.prevent_default();
|
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
2021-08-20 20:20:45 +00:00
|
|
|
{
|
|
|
|
let event_name = "dragover";
|
|
|
|
let runner_ref = runner_ref.clone();
|
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::DragEvent| {
|
|
|
|
if let Some(data_transfer) = event.data_transfer() {
|
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
|
|
|
runner_lock.input.raw.hovered_files.clear();
|
|
|
|
for i in 0..data_transfer.items().length() {
|
|
|
|
if let Some(item) = data_transfer.items().get(i) {
|
|
|
|
runner_lock.input.raw.hovered_files.push(egui::HoveredFile {
|
|
|
|
mime: item.type_(),
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
runner_lock.needs_repaint.set_true();
|
|
|
|
event.stop_propagation();
|
|
|
|
event.prevent_default();
|
|
|
|
}
|
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let event_name = "dragleave";
|
|
|
|
let runner_ref = runner_ref.clone();
|
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::DragEvent| {
|
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
|
|
|
runner_lock.input.raw.hovered_files.clear();
|
|
|
|
runner_lock.needs_repaint.set_true();
|
|
|
|
event.stop_propagation();
|
|
|
|
event.prevent_default();
|
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let event_name = "drop";
|
|
|
|
let runner_ref = runner_ref.clone();
|
|
|
|
let closure = Closure::wrap(Box::new(move |event: web_sys::DragEvent| {
|
|
|
|
if let Some(data_transfer) = event.data_transfer() {
|
|
|
|
{
|
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
|
|
|
runner_lock.input.raw.hovered_files.clear();
|
|
|
|
runner_lock.needs_repaint.set_true();
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(files) = data_transfer.files() {
|
|
|
|
for i in 0..files.length() {
|
|
|
|
if let Some(file) = files.get(i) {
|
|
|
|
let name = file.name();
|
|
|
|
let last_modified = std::time::UNIX_EPOCH
|
|
|
|
+ std::time::Duration::from_millis(file.last_modified() as u64);
|
|
|
|
|
2022-02-01 11:27:39 +00:00
|
|
|
tracing::debug!("Loading {:?} ({} bytes)…", name, file.size());
|
2021-08-20 20:20:45 +00:00
|
|
|
|
|
|
|
let future = wasm_bindgen_futures::JsFuture::from(file.array_buffer());
|
|
|
|
|
|
|
|
let runner_ref = runner_ref.clone();
|
|
|
|
let future = async move {
|
|
|
|
match future.await {
|
|
|
|
Ok(array_buffer) => {
|
|
|
|
let bytes = js_sys::Uint8Array::new(&array_buffer).to_vec();
|
2022-02-01 11:27:39 +00:00
|
|
|
tracing::debug!(
|
2021-08-20 20:20:45 +00:00
|
|
|
"Loaded {:?} ({} bytes).",
|
|
|
|
name,
|
|
|
|
bytes.len()
|
2022-02-01 11:27:39 +00:00
|
|
|
);
|
2021-08-20 20:20:45 +00:00
|
|
|
|
|
|
|
let mut runner_lock = runner_ref.0.lock();
|
|
|
|
runner_lock.input.raw.dropped_files.push(
|
|
|
|
egui::DroppedFile {
|
|
|
|
name,
|
|
|
|
last_modified: Some(last_modified),
|
|
|
|
bytes: Some(bytes.into()),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
);
|
|
|
|
runner_lock.needs_repaint.set_true();
|
|
|
|
}
|
|
|
|
Err(err) => {
|
2022-02-01 11:27:39 +00:00
|
|
|
tracing::error!("Failed to read file: {:?}", err);
|
2021-08-20 20:20:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
wasm_bindgen_futures::spawn_local(future);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
event.stop_propagation();
|
|
|
|
event.prevent_default();
|
|
|
|
}
|
|
|
|
}) as Box<dyn FnMut(_)>);
|
|
|
|
canvas.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())?;
|
|
|
|
closure.forget();
|
|
|
|
}
|
|
|
|
|
2020-07-18 16:35:17 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-03-26 12:56:26 +00:00
|
|
|
|
2022-01-10 22:19:30 +00:00
|
|
|
pub(crate) fn webgl1_requires_brightening(gl: &web_sys::WebGlRenderingContext) -> bool {
|
|
|
|
// See https://github.com/emilk/egui/issues/794
|
|
|
|
|
|
|
|
// detect WebKitGTK
|
|
|
|
|
|
|
|
// WebKitGTK use WebKit default unmasked vendor and renderer
|
|
|
|
// but safari use same vendor and renderer
|
|
|
|
// so exclude "Mac OS X" user-agent.
|
|
|
|
let user_agent = web_sys::window().unwrap().navigator().user_agent().unwrap();
|
|
|
|
!user_agent.contains("Mac OS X") && crate::is_safari_and_webkit_gtk(gl)
|
|
|
|
}
|
|
|
|
|
2021-12-31 14:17:55 +00:00
|
|
|
/// detecting Safari and webkitGTK.
|
|
|
|
///
|
|
|
|
/// Safari and webkitGTK use unmasked renderer :Apple GPU
|
|
|
|
///
|
|
|
|
/// If we detect safari or webkitGTK returns true.
|
|
|
|
///
|
|
|
|
/// This function used to avoid displaying linear color with `sRGB` supported systems.
|
2022-01-10 22:19:30 +00:00
|
|
|
fn is_safari_and_webkit_gtk(gl: &web_sys::WebGlRenderingContext) -> bool {
|
|
|
|
// This call produces a warning in Firefox ("WEBGL_debug_renderer_info is deprecated in Firefox and will be removed.")
|
|
|
|
// but unless we call it we get errors in Chrome when we call `get_parameter` below.
|
|
|
|
// TODO: do something smart based on user agent?
|
|
|
|
if gl
|
|
|
|
.get_extension("WEBGL_debug_renderer_info")
|
|
|
|
.unwrap()
|
|
|
|
.is_some()
|
2021-12-31 14:17:55 +00:00
|
|
|
{
|
2022-01-10 22:19:30 +00:00
|
|
|
if let Ok(renderer) =
|
|
|
|
gl.get_parameter(web_sys::WebglDebugRendererInfo::UNMASKED_RENDERER_WEBGL)
|
|
|
|
{
|
|
|
|
if let Some(renderer) = renderer.as_string() {
|
|
|
|
if renderer.contains("Apple") {
|
|
|
|
return true;
|
|
|
|
}
|
2021-12-31 14:17:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
}
|