use dioxus::prelude::*;
use dioxus_desktop::tao::event::{Event, WindowEvent};
use dioxus_desktop::tao::keyboard::ModifiersState as TaoModifiers;
use dioxus_desktop::use_wry_event_handler;
use crate::core::{use_joined_window, DndContext, DragSessionId, JoinedWindow, PointerKind};
use super::platform;
fn bridge_needed(dragging: bool, pointer_kind: PointerKind, bridging_enabled: bool) -> bool {
dragging && bridging_enabled && !pointer_kind.implicitly_captured()
}
pub(super) fn bridged<T: Clone + 'static>(joined: JoinedWindow<T>, ctx: &DndContext<T>) -> bool {
bridge_needed(
ctx.dragging(),
ctx.pointer_kind(),
joined.world.bridging_enabled(),
)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) struct BridgeGeneration {
pub(super) world: u64,
pub(super) session: Option<DragSessionId>,
}
pub(super) fn current_generation<T: Clone + 'static>(
joined: JoinedWindow<T>,
) -> Option<BridgeGeneration> {
let (world, session) = joined.world.drag_generation_peek()?;
joined
.world
.is_drag_generation(world, session)
.then_some(BridgeGeneration { world, session })
}
pub(super) fn subscribed_generation<T: Clone + 'static>(
joined: JoinedWindow<T>,
) -> Option<BridgeGeneration> {
let (world, session) = joined.world.drag_generation()?;
joined
.world
.is_drag_generation(world, session)
.then_some(BridgeGeneration { world, session })
}
pub(super) fn current_bridged_generation<T: Clone + 'static>(
joined: JoinedWindow<T>,
ctx: &DndContext<T>,
) -> Option<BridgeGeneration> {
if !bridged(joined, ctx) {
return None;
}
current_generation(joined)
}
fn map_modifiers(native: TaoModifiers) -> Modifiers {
let mut mapped = Modifiers::empty();
if native.shift_key() {
mapped.insert(Modifiers::SHIFT);
}
if native.control_key() {
mapped.insert(Modifiers::CONTROL);
}
if native.alt_key() {
mapped.insert(Modifiers::ALT);
}
if native.super_key() {
mapped.insert(Modifiers::META);
}
mapped
}
#[component]
pub fn DragBridge<T: Clone + PartialEq + 'static>(
#[props(default)]
phantom: std::marker::PhantomData<T>,
) -> Element {
let _ = phantom;
let Some(joined) = use_joined_window::<T>() else {
return rsx! {};
};
use_legs(joined);
rsx! {}
}
fn use_legs<T: Clone + PartialEq + 'static>(joined: JoinedWindow<T>) {
let ctx = joined.world.context();
let capability = platform::use_global_capability();
use_shared_window_events(joined, ctx);
platform::use_pointer_legs(joined, ctx, capability);
}
fn use_shared_window_events<T: Clone + PartialEq + 'static>(
joined: JoinedWindow<T>,
ctx: DndContext<T>,
) {
let tripped = use_hook(|| std::rc::Rc::new(std::cell::Cell::new(None::<BridgeGeneration>)));
use_wry_event_handler(move |event, _| {
let Event::WindowEvent { event, .. } = event else {
return;
};
match event {
WindowEvent::CursorMoved { .. } | WindowEvent::MouseInput { .. }
if cfg!(target_os = "windows") =>
{
let Some(generation) = current_bridged_generation(joined, &ctx) else {
return;
};
if tripped.get() != Some(generation) {
tripped.set(Some(generation));
tracing::warn!(
?generation,
"WebView2 event routing changed - tao window events arrived during a \
raw-input-bridged drag; ignoring them. Cross-window drags may \
double-drive on this platform; please report this warning upstream."
);
}
}
WindowEvent::ModifiersChanged(modifiers)
if current_bridged_generation(joined, &ctx).is_some()
&& joined.world.record(joined.key).is_some() =>
{
joined.world.update_modifiers(map_modifiers(*modifiers));
}
WindowEvent::Resized(_) | WindowEvent::ScaleFactorChanged { .. }
if current_generation(joined).is_some()
&& joined.world.record(joined.key).is_some() =>
{
joined.world.refresh_all_rects();
}
_ => {}
}
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn implicitly_captured_touch_never_bridges() {
assert!(bridge_needed(true, PointerKind::Mouse, true));
assert!(bridge_needed(true, PointerKind::Pen, true));
assert!(!bridge_needed(true, PointerKind::Touch, true));
assert!(!bridge_needed(false, PointerKind::Mouse, true));
}
#[test]
fn kill_switch_vetoes_every_bridgeable_pointer() {
assert!(!bridge_needed(true, PointerKind::Mouse, false));
assert!(!bridge_needed(true, PointerKind::Pen, false));
assert!(!bridge_needed(true, PointerKind::Touch, false));
assert!(!bridge_needed(false, PointerKind::Mouse, false));
}
#[test]
fn tao_modifiers_map_to_drop_effect_modifiers() {
let native =
TaoModifiers::SHIFT | TaoModifiers::CONTROL | TaoModifiers::ALT | TaoModifiers::SUPER;
let mapped = map_modifiers(native);
assert!(mapped.contains(Modifiers::SHIFT));
assert!(mapped.contains(Modifiers::CONTROL));
assert!(mapped.contains(Modifiers::ALT));
assert!(mapped.contains(Modifiers::META));
assert_eq!(map_modifiers(TaoModifiers::empty()), Modifiers::empty());
}
}