Skip to content

Commit 2368dc8

Browse files
committed
feat(examples): add drag and drop picking
1 parent ffc66f1 commit 2368dc8

File tree

3 files changed

+171
-0
lines changed

3 files changed

+171
-0
lines changed

Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4591,6 +4591,18 @@ description = "Demonstrates picking debug overlay"
45914591
category = "Picking"
45924592
wasm = true
45934593

4594+
[[example]]
4595+
name = "dragdrop_picking"
4596+
path = "examples/picking/dragdrop_picking.rs"
4597+
doc-scrape-examples = true
4598+
required-features = ["mesh_picking"]
4599+
4600+
[package.metadata.example.dragdrop_picking]
4601+
name = "Drag and Drop"
4602+
description = "Demonstrates drag and drop using picking events"
4603+
category = "Picking"
4604+
wasm = true
4605+
45944606
[[example]]
45954607
name = "animation_masks"
45964608
path = "examples/animation/animation_masks.rs"

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ Example | Description
416416

417417
Example | Description
418418
--- | ---
419+
[Drag and Drop](../examples/picking/dragdrop_picking.rs) | Demonstrates drag and drop using picking events
419420
[Mesh Picking](../examples/picking/mesh_picking.rs) | Demonstrates picking meshes
420421
[Picking Debug Tools](../examples/picking/debug_picking.rs) | Demonstrates picking debug overlay
421422
[Showcases simple picking events and usage](../examples/picking/simple_picking.rs) | Demonstrates how to use picking events to spawn simple objects
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
//! Demonstrates drag and drop functionality using picking events.
2+
3+
use bevy::prelude::*;
4+
5+
#[derive(Component)]
6+
struct DropArea;
7+
8+
#[derive(Component)]
9+
struct DraggableButton;
10+
11+
#[derive(Component)]
12+
struct GhostPreview;
13+
14+
#[derive(Component)]
15+
struct DroppedElement;
16+
17+
const AREA_SIZE: f32 = 500.0;
18+
const BUTTON_SIZE: f32 = 50.0;
19+
const ELEMENT_SIZE: f32 = 25.0;
20+
21+
fn main() {
22+
App::new()
23+
.add_plugins((DefaultPlugins, MeshPickingPlugin))
24+
.add_systems(Startup, setup)
25+
.run();
26+
}
27+
28+
fn setup(
29+
mut commands: Commands,
30+
mut meshes: ResMut<Assets<Mesh>>,
31+
mut materials: ResMut<Assets<ColorMaterial>>,
32+
) {
33+
commands.spawn(Camera2d);
34+
35+
commands
36+
.spawn((
37+
Node {
38+
width: Val::Percent(100.0),
39+
height: Val::Percent(100.0),
40+
align_items: AlignItems::Center,
41+
justify_content: JustifyContent::Start,
42+
..default()
43+
},
44+
Pickable::IGNORE,
45+
))
46+
.with_children(|parent| {
47+
parent
48+
.spawn((
49+
DraggableButton,
50+
Node {
51+
width: Val::Px(BUTTON_SIZE),
52+
height: Val::Px(BUTTON_SIZE),
53+
margin: UiRect::all(Val::Px(10.0)),
54+
border_radius: BorderRadius::MAX,
55+
..default()
56+
},
57+
BackgroundColor(Color::srgb(1.0, 0.0, 0.0)),
58+
))
59+
.observe(
60+
|mut event: On<Pointer<DragStart>>,
61+
mut button_color: Single<&mut BackgroundColor, With<DraggableButton>>| {
62+
button_color.0 = Color::srgb(1.0, 0.5, 0.0);
63+
event.propagate(false);
64+
},
65+
)
66+
.observe(
67+
|mut event: On<Pointer<DragEnd>>,
68+
mut button_color: Single<&mut BackgroundColor, With<DraggableButton>>| {
69+
button_color.0 = Color::srgb(1.0, 0.0, 0.0);
70+
event.propagate(false);
71+
},
72+
);
73+
});
74+
75+
commands
76+
.spawn((
77+
DropArea,
78+
Mesh2d(meshes.add(Rectangle::new(AREA_SIZE, AREA_SIZE))),
79+
MeshMaterial2d(materials.add(Color::srgb(0.1, 0.4, 0.1))),
80+
Transform::IDENTITY,
81+
))
82+
.observe(on_drag_enter)
83+
.observe(on_drag_over)
84+
.observe(on_drag_drop)
85+
.observe(on_drag_leave);
86+
}
87+
88+
fn on_drag_enter(
89+
mut event: On<Pointer<DragEnter>>,
90+
button: Single<Entity, With<DraggableButton>>,
91+
mut commands: Commands,
92+
mut meshes: ResMut<Assets<Mesh>>,
93+
mut materials: ResMut<Assets<ColorMaterial>>,
94+
) {
95+
if event.dragged == *button {
96+
let Some(position) = event.hit.position else {
97+
return;
98+
};
99+
commands.spawn((
100+
GhostPreview,
101+
Mesh2d(meshes.add(Circle::new(ELEMENT_SIZE))),
102+
MeshMaterial2d(materials.add(Color::srgba(1.0, 1.0, 0.6, 0.5))),
103+
Transform::from_translation(position),
104+
Pickable::IGNORE,
105+
));
106+
event.propagate(false);
107+
}
108+
}
109+
110+
fn on_drag_over(
111+
mut event: On<Pointer<DragOver>>,
112+
button: Single<Entity, With<DraggableButton>>,
113+
mut ghost_transform: Single<&mut Transform, With<GhostPreview>>,
114+
) {
115+
if event.dragged == *button {
116+
let Some(position) = event.hit.position else {
117+
return;
118+
};
119+
ghost_transform.translation = position;
120+
event.propagate(false);
121+
}
122+
}
123+
124+
fn on_drag_drop(
125+
mut event: On<Pointer<DragDrop>>,
126+
button: Single<Entity, With<DraggableButton>>,
127+
mut commands: Commands,
128+
ghost: Single<Entity, With<GhostPreview>>,
129+
mut meshes: ResMut<Assets<Mesh>>,
130+
mut materials: ResMut<Assets<ColorMaterial>>,
131+
) {
132+
if event.dropped == *button {
133+
commands.entity(*ghost).despawn();
134+
let Some(position) = event.hit.position else {
135+
return;
136+
};
137+
commands.spawn((
138+
DroppedElement,
139+
Mesh2d(meshes.add(Circle::new(ELEMENT_SIZE))),
140+
MeshMaterial2d(materials.add(Color::srgb(1.0, 1.0, 0.6))),
141+
Transform::from_translation(position),
142+
Pickable::IGNORE,
143+
));
144+
event.propagate(false);
145+
}
146+
}
147+
148+
fn on_drag_leave(
149+
mut event: On<Pointer<DragLeave>>,
150+
button: Single<Entity, With<DraggableButton>>,
151+
mut commands: Commands,
152+
ghost: Single<Entity, With<GhostPreview>>,
153+
) {
154+
if event.dragged == *button {
155+
commands.entity(*ghost).despawn();
156+
event.propagate(false);
157+
}
158+
}

0 commit comments

Comments
 (0)