Skip to main content

lumen_script/
dnd.rs

1//! Script-facing drag-and-drop events.
2//!
3//! Surfaces the in-app DnD pipeline (`lumen-os-dnd`) to app scripts the
4//! same way pointer events reach them - one uniform host seam, not a
5//! Rhai-only builtin, so every binding / SDK gets it (like `page()` /
6//! `http()`):
7//!
8//! * [`DropAccepted`] -> `on_drop(target_id, payload)` with per-id routing
9//!   via `on("drop", "<target_id>", "<fn>")`.
10//! * [`DragStarted`] -> `on_drag_start(source_id, payload)` with per-id
11//!   routing via `on("drag_start", "<source_id>", "<fn>")`.
12//!
13//! `payload` is the source's text payload (its `drag-payload` attr, or
14//! its `id` when the payload was derived from the element id).
15//!
16//! Mirrors HTML5 `ondrop(event)` - `event.target` is the drop zone,
17//! `event.dataTransfer.getData()` is the payload - and Qt's `dropEvent`
18//! carrying the source + `QMimeData`. Kept in its own submodule so the
19//! shared `lib.rs` touch stays to two `add_systems` lines.
20
21use crate::ScriptHost;
22use bevy_ecs::component::Mutable;
23use bevy_ecs::message::{MessageReader, MessageWriter};
24use bevy_ecs::prelude::*;
25use lumen_core::components::LumenId;
26use lumen_core::warn_line;
27use lumen_os_dnd::{DragStarted, DropAccepted};
28
29use crate::runtime::{ScriptCommandEvent, prefix, route_event_two_args};
30
31/// Forward each [`DropAccepted`] to the script as
32/// `on_drop(target_id, payload)`. A reactive `on_drop` that reassigns a
33/// signal drives the next reconcile - no per-frame polling.
34pub fn dispatch_drops_to_script<H: ScriptHost + Resource<Mutability = Mutable>>(
35    mut host: ResMut<H>,
36    mut events: MessageReader<DropAccepted>,
37    ids: Query<&LumenId>,
38    mut out: MessageWriter<ScriptCommandEvent>,
39) {
40    for ev in events.read() {
41        let target_id = ids.get(ev.target).map(|i| i.0.as_str()).unwrap_or("");
42        let payload = ev.payload.text().unwrap_or_default();
43        if let Err(e) =
44            route_event_two_args(&mut *host, "drop", "on_drop", target_id, &payload, &mut out)
45        {
46            warn_line!("{}: on_drop failed: {e}", prefix(host.lang()));
47        }
48    }
49}
50
51/// Forward each [`DragStarted`] to the script as
52/// `on_drag_start(source_id, payload)`.
53pub fn dispatch_drag_start_to_script<H: ScriptHost + Resource<Mutability = Mutable>>(
54    mut host: ResMut<H>,
55    mut events: MessageReader<DragStarted>,
56    ids: Query<&LumenId>,
57    mut out: MessageWriter<ScriptCommandEvent>,
58) {
59    for ev in events.read() {
60        let source_id = ids.get(ev.source).map(|i| i.0.as_str()).unwrap_or("");
61        let payload = ev.payload.text().unwrap_or_default();
62        if let Err(e) = route_event_two_args(
63            &mut *host,
64            "drag_start",
65            "on_drag_start",
66            source_id,
67            &payload,
68            &mut out,
69        ) {
70            warn_line!("{}: on_drag_start failed: {e}", prefix(host.lang()));
71        }
72    }
73}