Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions crates/eframe/src/epi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,10 +499,16 @@ pub struct WebOptions {
/// If the web event corresponding to an egui event should be propagated
/// to the rest of the web page.
///
/// The default is `false`, meaning
/// The default is `true`, meaning
/// [`stopPropagation`](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation)
/// is called on every event.
pub should_propagate_event: Box<dyn Fn(&egui::Event) -> bool>,
/// is called on every event, and the event is not propagated to the rest of the web page.
pub should_stop_propagation: Box<dyn Fn(&egui::Event) -> bool>,

/// Whether the web event corresponding to an egui event should have `prevent_default` called
/// on it or not.
///
/// Defaults to true.
pub should_prevent_default: Box<dyn Fn(&egui::Event) -> bool>,
}

#[cfg(target_arch = "wasm32")]
Expand All @@ -519,7 +525,8 @@ impl Default for WebOptions {

dithering: true,

should_propagate_event: Box::new(|_| false),
should_stop_propagation: Box::new(|_| true),
should_prevent_default: Box::new(|_| true),
}
}
}
Expand Down
141 changes: 97 additions & 44 deletions crates/eframe/src/web/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,20 @@ fn install_keydown(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), J
{
if let Some(text) = text_from_keyboard_event(&event) {
let egui_event = egui::Event::Text(text);
let should_propagate = (runner.web_options.should_propagate_event)(&egui_event);
let should_stop_propagation =
(runner.web_options.should_stop_propagation)(&egui_event);
let should_prevent_default =
(runner.web_options.should_prevent_default)(&egui_event);
runner.input.raw.events.push(egui_event);
runner.needs_repaint.repaint_asap();

// If this is indeed text, then prevent any other action.
event.prevent_default();
if should_prevent_default {
event.prevent_default();
}

// Use web options to tell if the event should be propagated to parent elements.
if !should_propagate {
if should_stop_propagation {
event.stop_propagation();
}
}
Expand Down Expand Up @@ -184,7 +189,7 @@ pub(crate) fn on_keydown(event: web_sys::KeyboardEvent, runner: &mut AppRunner)
repeat: false, // egui will fill this in for us!
modifiers,
};
let should_propagate = (runner.web_options.should_propagate_event)(&egui_event);
let should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);
runner.input.raw.events.push(egui_event);
runner.needs_repaint.repaint_asap();

Expand All @@ -201,7 +206,7 @@ pub(crate) fn on_keydown(event: web_sys::KeyboardEvent, runner: &mut AppRunner)
}

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
if !should_propagate {
if should_stop_propagation {
event.stop_propagation();
}
}
Expand Down Expand Up @@ -261,7 +266,7 @@ pub(crate) fn on_keyup(event: web_sys::KeyboardEvent, runner: &mut AppRunner) {
let modifiers = modifiers_from_kb_event(&event);
runner.input.raw.modifiers = modifiers;

let mut propagate_event = false;
let mut should_stop_propagation = true;

if let Some(key) = translate_key(&event.key()) {
let egui_event = egui::Event::Key {
Expand All @@ -271,7 +276,7 @@ pub(crate) fn on_keyup(event: web_sys::KeyboardEvent, runner: &mut AppRunner) {
repeat: false,
modifiers,
};
propagate_event |= (runner.web_options.should_propagate_event)(&egui_event);
should_stop_propagation &= (runner.web_options.should_stop_propagation)(&egui_event);
runner.input.raw.events.push(egui_event);
}

Expand All @@ -290,7 +295,7 @@ pub(crate) fn on_keyup(event: web_sys::KeyboardEvent, runner: &mut AppRunner) {
repeat: false,
modifiers,
};
propagate_event |= (runner.web_options.should_propagate_event)(&egui_event);
should_stop_propagation &= (runner.web_options.should_stop_propagation)(&egui_event);
runner.input.raw.events.push(egui_event);
}
}
Expand All @@ -299,7 +304,7 @@ pub(crate) fn on_keyup(event: web_sys::KeyboardEvent, runner: &mut AppRunner) {

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
let has_focus = runner.input.raw.focused;
if has_focus && !propagate_event {
if has_focus && should_stop_propagation {
event.stop_propagation();
}
}
Expand All @@ -310,19 +315,26 @@ fn install_copy_cut_paste(runner_ref: &WebRunner, target: &EventTarget) -> Resul
if let Ok(text) = data.get_data("text") {
let text = text.replace("\r\n", "\n");

let mut should_propagate = false;
let mut should_stop_propagation = true;
let mut should_prevent_default = true;
if !text.is_empty() && runner.input.raw.focused {
let egui_event = egui::Event::Paste(text);
should_propagate = (runner.web_options.should_propagate_event)(&egui_event);
should_stop_propagation =
(runner.web_options.should_stop_propagation)(&egui_event);
should_prevent_default =
(runner.web_options.should_prevent_default)(&egui_event);
runner.input.raw.events.push(egui_event);
runner.needs_repaint.repaint_asap();
}

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
if !should_propagate {
if should_stop_propagation {
event.stop_propagation();
}
event.prevent_default();

if should_prevent_default {
event.prevent_default();
}
}
}
})?;
Expand All @@ -340,10 +352,13 @@ fn install_copy_cut_paste(runner_ref: &WebRunner, target: &EventTarget) -> Resul
}

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
if !(runner.web_options.should_propagate_event)(&egui::Event::Cut) {
if (runner.web_options.should_stop_propagation)(&egui::Event::Cut) {
event.stop_propagation();
}
event.prevent_default();

if (runner.web_options.should_prevent_default)(&egui::Event::Cut) {
event.prevent_default();
}
})?;

runner_ref.add_event_listener(target, "copy", |event: web_sys::ClipboardEvent, runner| {
Expand All @@ -359,10 +374,13 @@ fn install_copy_cut_paste(runner_ref: &WebRunner, target: &EventTarget) -> Resul
}

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
if !(runner.web_options.should_propagate_event)(&egui::Event::Copy) {
if (runner.web_options.should_stop_propagation)(&egui::Event::Copy) {
event.stop_propagation();
}
event.prevent_default();

if (runner.web_options.should_prevent_default)(&egui::Event::Copy) {
event.prevent_default();
}
})?;

Ok(())
Expand Down Expand Up @@ -484,7 +502,7 @@ fn install_pointerdown(runner_ref: &WebRunner, target: &EventTarget) -> Result<(
|event: web_sys::PointerEvent, runner: &mut AppRunner| {
let modifiers = modifiers_from_mouse_event(&event);
runner.input.raw.modifiers = modifiers;
let mut should_propagate = false;
let mut should_stop_propagation = true;
if let Some(button) = button_from_mouse_event(&event) {
let pos = pos_from_mouse_event(runner.canvas(), &event, runner.egui_ctx());
let modifiers = runner.input.raw.modifiers;
Expand All @@ -494,7 +512,7 @@ fn install_pointerdown(runner_ref: &WebRunner, target: &EventTarget) -> Result<(
pressed: true,
modifiers,
};
should_propagate = (runner.web_options.should_propagate_event)(&egui_event);
should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);
runner.input.raw.events.push(egui_event);

// In Safari we are only allowed to write to the clipboard during the
Expand All @@ -506,7 +524,7 @@ fn install_pointerdown(runner_ref: &WebRunner, target: &EventTarget) -> Result<(
}

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
if !should_propagate {
if should_stop_propagation {
event.stop_propagation();
}
// Note: prevent_default breaks VSCode tab focusing, hence why we don't call it here.
Expand Down Expand Up @@ -536,7 +554,10 @@ fn install_pointerup(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),
pressed: false,
modifiers,
};
let should_propagate = (runner.web_options.should_propagate_event)(&egui_event);
let should_stop_propagation =
(runner.web_options.should_stop_propagation)(&egui_event);
let should_prevent_default =
(runner.web_options.should_prevent_default)(&egui_event);
runner.input.raw.events.push(egui_event);

// Previously on iOS, the canvas would not receive focus on
Expand All @@ -555,10 +576,12 @@ fn install_pointerup(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),
// Make sure we paint the output of the above logic call asap:
runner.needs_repaint.repaint_asap();

event.prevent_default();
if should_prevent_default {
event.prevent_default();
}

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
if !should_propagate {
if should_stop_propagation {
event.stop_propagation();
}
}
Expand Down Expand Up @@ -600,15 +623,19 @@ fn install_mousemove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),
egui::pos2(event.client_x() as f32, event.client_y() as f32),
) {
let egui_event = egui::Event::PointerMoved(pos);
let should_propagate = (runner.web_options.should_propagate_event)(&egui_event);
let should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);
let should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event);
runner.input.raw.events.push(egui_event);
runner.needs_repaint.repaint_asap();

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
if !should_propagate {
if should_stop_propagation {
event.stop_propagation();
}
event.prevent_default();

if should_prevent_default {
event.prevent_default();
}
}
})
}
Expand All @@ -622,10 +649,13 @@ fn install_mouseleave(runner_ref: &WebRunner, target: &EventTarget) -> Result<()
runner.needs_repaint.repaint_asap();

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
if !(runner.web_options.should_propagate_event)(&egui::Event::PointerGone) {
if (runner.web_options.should_stop_propagation)(&egui::Event::PointerGone) {
event.stop_propagation();
}
event.prevent_default();

if (runner.web_options.should_prevent_default)(&egui::Event::PointerGone) {
event.prevent_default();
}
},
)
}
Expand All @@ -635,26 +665,31 @@ fn install_touchstart(runner_ref: &WebRunner, target: &EventTarget) -> Result<()
target,
"touchstart",
|event: web_sys::TouchEvent, runner| {
let mut should_propagate = false;
let mut should_stop_propagation = true;
let mut should_prevent_default = true;
if let Some((pos, _)) = primary_touch_pos(runner, &event) {
let egui_event = egui::Event::PointerButton {
pos,
button: egui::PointerButton::Primary,
pressed: true,
modifiers: runner.input.raw.modifiers,
};
should_propagate = (runner.web_options.should_propagate_event)(&egui_event);
should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);
should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event);
runner.input.raw.events.push(egui_event);
}

push_touches(runner, egui::TouchPhase::Start, &event);
runner.needs_repaint.repaint_asap();

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
if !should_propagate {
if should_stop_propagation {
event.stop_propagation();
}
event.prevent_default();

if should_prevent_default {
event.prevent_default();
}
},
)
}
Expand All @@ -667,17 +702,23 @@ fn install_touchmove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),
egui::pos2(touch.client_x() as f32, touch.client_y() as f32),
) {
let egui_event = egui::Event::PointerMoved(pos);
let should_propagate = (runner.web_options.should_propagate_event)(&egui_event);
let should_stop_propagation =
(runner.web_options.should_stop_propagation)(&egui_event);
let should_prevent_default =
(runner.web_options.should_prevent_default)(&egui_event);
runner.input.raw.events.push(egui_event);

push_touches(runner, egui::TouchPhase::Move, &event);
runner.needs_repaint.repaint_asap();

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
if !should_propagate {
if should_stop_propagation {
event.stop_propagation();
}
event.prevent_default();

if should_prevent_default {
event.prevent_default();
}
}
}
})
Expand All @@ -691,29 +732,37 @@ fn install_touchend(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),
egui::pos2(touch.client_x() as f32, touch.client_y() as f32),
) {
// First release mouse to click:
let mut should_propagate = false;
let mut should_stop_propagation = true;
let mut should_prevent_default = true;
let egui_event = egui::Event::PointerButton {
pos,
button: egui::PointerButton::Primary,
pressed: false,
modifiers: runner.input.raw.modifiers,
};
should_propagate |= (runner.web_options.should_propagate_event)(&egui_event);
should_stop_propagation &=
(runner.web_options.should_stop_propagation)(&egui_event);
should_prevent_default &= (runner.web_options.should_prevent_default)(&egui_event);
runner.input.raw.events.push(egui_event);
// Then remove hover effect:
should_propagate |=
(runner.web_options.should_propagate_event)(&egui::Event::PointerGone);
should_stop_propagation &=
(runner.web_options.should_stop_propagation)(&egui::Event::PointerGone);
should_prevent_default &=
(runner.web_options.should_prevent_default)(&egui::Event::PointerGone);
runner.input.raw.events.push(egui::Event::PointerGone);

push_touches(runner, egui::TouchPhase::End, &event);

runner.needs_repaint.repaint_asap();

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
if !should_propagate {
if should_stop_propagation {
event.stop_propagation();
}
event.prevent_default();

if should_prevent_default {
event.prevent_default();
}

// Fix virtual keyboard IOS
// Need call focus at the same time of event
Expand Down Expand Up @@ -769,16 +818,20 @@ fn install_wheel(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsV
modifiers,
}
};
let should_propagate = (runner.web_options.should_propagate_event)(&egui_event);
let should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);
let should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event);
runner.input.raw.events.push(egui_event);

runner.needs_repaint.repaint_asap();

// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
if !should_propagate {
if should_stop_propagation {
event.stop_propagation();
}
event.prevent_default();

if should_prevent_default {
event.prevent_default();
}
})
}

Expand Down
Loading