Skip to content
This repository was archived by the owner on Apr 29, 2021. It is now read-only.

Commit 2cfaa8c

Browse files
authored
Merge pull request #253 from IIzzaya/drag_and_drop
[Feature] Add Drag and Drop feature between UnityObject and UIWidgets
2 parents 3c6e377 + d00bd53 commit 2cfaa8c

22 files changed

+1341
-46
lines changed

README-ZH.md

+10
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,16 @@ UIWidgets支持渲染文本中包含的表情。表情的图片来自[https://ww
226226
如果您希望使用自己的表情图片,请更新纹理图`Tests/Resources/Emoji.png`,以及`Runtime/ui/txt/emoji.cs`中将Unicode映射到纹理图中具体位置的映射表。
227227
特别地,请记得更新Dictionary变量`emojiLookupTable`,纹理图的行数`rowCount`以及纹理图的列数`colCount`
228228

229+
#### 十一、与GameObject进行拖拽交互
230+
231+
<div style="text-align: center">
232+
<img src="https://connect-prd-cdn.unity.com/20190718/p/images/e3c9cf9b-c732-4eb2-9afd-fe7de894f342_Custom_Inspector_Showcase_320px.gif" width="300"/>
233+
</div>
234+
235+
我们提供了一个包装好的`UnityObjectDetector`组件以及`onRelease`回调函数,借此您可以实现简单地将物体(例如Hierarchy内的场景物体、Project窗口下的文件等)拖拽至区域内,来获得`UnityEngine.Object[] `类型的引用并进行操作。
236+
237+
你可以在“UIWidgetsTests -> Drag&Drop”下找到简单的实例样例。
238+
229239

230240
## 调试UIWidgets应用程序
231241

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,16 @@ and the unicode-index table in `Runtime/ui/txt/emoji.cs` which maps unicodes to
265265
in the texture. Specifically, remember to update the Dictionary `emojiLookupTable`, number of rows
266266
in the texture `rowCount`, and number of columns `colCount`.
267267

268+
#### Interact with GameObject Drag&Drops
269+
270+
<div style="text-align: center">
271+
<img src="https://connect-prd-cdn.unity.com/20190718/p/images/e3c9cf9b-c732-4eb2-9afd-fe7de894f342_Custom_Inspector_Showcase_320px.gif" width="300"/>
272+
</div>
273+
274+
With the provided packaged stateful widget `UnityObjectDetector` and its `onRelease` callback function, you can easily drag some objects (for example GameObject from Hierarchy, files from Project Window, etc) into the area, get the UnityEngine.Object[] references and make further modification.
275+
276+
Please refer to "UIWidgetsTests -> Drag&Drop" for simple examples.
277+
268278

269279
## Debug UIWidgets Application
270280

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
using System.Collections.Generic;
2+
using Unity.UIWidgets.foundation;
3+
using Unity.UIWidgets.scheduler;
4+
using UnityEditor;
5+
6+
namespace Unity.UIWidgets.gestures {
7+
#if UNITY_EDITOR
8+
public partial class MouseTracker {
9+
bool _enableDragFromEditorRelease = false;
10+
11+
void _handleDragFromEditorEvent(PointerEvent evt, int deviceId) {
12+
if (!this.inEditorWindow) {
13+
return;
14+
}
15+
16+
if (evt is PointerDragFromEditorReleaseEvent) {
17+
this._enableDragFromEditorRelease = false;
18+
this._scheduleDragFromEditorReleaseCheck();
19+
this._lastMouseEvent.Remove(deviceId);
20+
}
21+
else if (evt is PointerDragFromEditorEnterEvent ||
22+
evt is PointerDragFromEditorHoverEvent ||
23+
evt is PointerDragFromEditorExitEvent) {
24+
if (!this._lastMouseEvent.ContainsKey(deviceId) ||
25+
this._lastMouseEvent[deviceId].position != evt.position) {
26+
this._scheduleDragFromEditorMousePositionCheck();
27+
}
28+
29+
this._lastMouseEvent[deviceId] = evt;
30+
}
31+
}
32+
33+
void detachDragFromEditorAnnotation(MouseTrackerAnnotation annotation, int deviceId) {
34+
if (!this.inEditorWindow) {
35+
return;
36+
}
37+
38+
if (annotation.onDragFromEditorExit != null) {
39+
annotation.onDragFromEditorExit(
40+
PointerDragFromEditorExitEvent.fromDragFromEditorEvent(this._lastMouseEvent[deviceId]));
41+
}
42+
}
43+
44+
void _scheduleDragFromEditorReleaseCheck() {
45+
DragAndDrop.AcceptDrag();
46+
47+
var lastMouseEvent = new List<PointerEvent>();
48+
foreach (int deviceId in this._lastMouseEvent.Keys) {
49+
var _deviceId = deviceId;
50+
lastMouseEvent.Add(this._lastMouseEvent[_deviceId]);
51+
SchedulerBinding.instance.addPostFrameCallback(_ => {
52+
foreach (var lastEvent in lastMouseEvent) {
53+
MouseTrackerAnnotation hit = this.annotationFinder(lastEvent.position);
54+
55+
if (hit == null) {
56+
foreach (_TrackedAnnotation trackedAnnotation in this._trackedAnnotations.Values) {
57+
if (trackedAnnotation.activeDevices.Contains(_deviceId)) {
58+
trackedAnnotation.activeDevices.Remove(_deviceId);
59+
}
60+
}
61+
62+
return;
63+
}
64+
65+
_TrackedAnnotation hitAnnotation = this._findAnnotation(hit);
66+
67+
// release
68+
if (hitAnnotation.activeDevices.Contains(_deviceId)) {
69+
if (hitAnnotation.annotation?.onDragFromEditorRelease != null) {
70+
hitAnnotation.annotation.onDragFromEditorRelease(
71+
PointerDragFromEditorReleaseEvent
72+
.fromDragFromEditorEvent(
73+
lastEvent, DragAndDrop.objectReferences));
74+
}
75+
76+
hitAnnotation.activeDevices.Remove(_deviceId);
77+
}
78+
}
79+
});
80+
}
81+
82+
SchedulerBinding.instance.scheduleFrame();
83+
}
84+
85+
/// <summary>
86+
/// Due to the [DragAndDrop] property, DragAndDrop.visualMode must be set to Copy
87+
/// after which editor window can trigger DragPerform event.
88+
/// And because visualMode will be set to None when every frame finished in IMGUI,
89+
/// here we start a scheduler to update VisualMode in every post frame.
90+
/// When [_enableDragFromEditorRelease] set to false, it will stop, vice versa.
91+
/// </summary>
92+
void _enableDragFromEditorReleaseVisualModeLoop() {
93+
if (this._enableDragFromEditorRelease) {
94+
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
95+
SchedulerBinding.instance.addPostFrameCallback(_ => {
96+
this._enableDragFromEditorReleaseVisualModeLoop();
97+
});
98+
SchedulerBinding.instance.scheduleFrame();
99+
}
100+
}
101+
102+
void _scheduleDragFromEditorMousePositionCheck() {
103+
if (!this.inEditorWindow) {
104+
return;
105+
}
106+
107+
SchedulerBinding.instance.addPostFrameCallback(_ => { this.collectDragFromEditorMousePositions(); });
108+
SchedulerBinding.instance.scheduleFrame();
109+
}
110+
111+
public void collectDragFromEditorMousePositions() {
112+
void exitAnnotation(_TrackedAnnotation trackedAnnotation, int deviceId) {
113+
if (trackedAnnotation.activeDevices.Contains(deviceId)) {
114+
this._enableDragFromEditorRelease = false;
115+
if (trackedAnnotation.annotation?.onDragFromEditorExit != null) {
116+
trackedAnnotation.annotation.onDragFromEditorExit(
117+
PointerDragFromEditorExitEvent.fromDragFromEditorEvent(
118+
this._lastMouseEvent[deviceId]));
119+
}
120+
121+
trackedAnnotation.activeDevices.Remove(deviceId);
122+
}
123+
}
124+
125+
void exitAllDevices(_TrackedAnnotation trackedAnnotation) {
126+
if (trackedAnnotation.activeDevices.isNotEmpty()) {
127+
HashSet<int> deviceIds = new HashSet<int>(trackedAnnotation.activeDevices);
128+
foreach (int deviceId in deviceIds) {
129+
exitAnnotation(trackedAnnotation, deviceId);
130+
}
131+
}
132+
}
133+
134+
if (!this.mouseIsConnected) {
135+
foreach (var annotation in this._trackedAnnotations.Values) {
136+
exitAllDevices(annotation);
137+
}
138+
139+
return;
140+
}
141+
142+
foreach (int deviceId in this._lastMouseEvent.Keys) {
143+
PointerEvent lastEvent = this._lastMouseEvent[deviceId];
144+
MouseTrackerAnnotation hit = this.annotationFinder(lastEvent.position);
145+
146+
if (hit == null) {
147+
foreach (_TrackedAnnotation trackedAnnotation in this._trackedAnnotations.Values) {
148+
exitAnnotation(trackedAnnotation, deviceId);
149+
}
150+
151+
return;
152+
}
153+
154+
_TrackedAnnotation hitAnnotation = this._findAnnotation(hit);
155+
156+
// While acrossing two areas, set the flag to true to prevent setting the Pointer Copy VisualMode to None
157+
bool enterFlag = false;
158+
159+
// enter
160+
if (!hitAnnotation.activeDevices.Contains(deviceId)) {
161+
hitAnnotation.activeDevices.Add(deviceId);
162+
enterFlag = true;
163+
// Both onRelease or onEnter event will enable Copy VisualMode
164+
if (hitAnnotation.annotation?.onDragFromEditorRelease != null ||
165+
hitAnnotation.annotation?.onDragFromEditorEnter != null) {
166+
if (!this._enableDragFromEditorRelease) {
167+
this._enableDragFromEditorRelease = true;
168+
this._enableDragFromEditorReleaseVisualModeLoop();
169+
}
170+
171+
if (hitAnnotation.annotation?.onDragFromEditorEnter != null) {
172+
hitAnnotation.annotation.onDragFromEditorEnter(
173+
PointerDragFromEditorEnterEvent
174+
.fromDragFromEditorEvent(lastEvent));
175+
}
176+
}
177+
}
178+
179+
// hover
180+
if (hitAnnotation.annotation?.onDragFromEditorHover != null) {
181+
hitAnnotation.annotation.onDragFromEditorHover(
182+
PointerDragFromEditorHoverEvent.fromDragFromEditorEvent(lastEvent));
183+
}
184+
185+
// leave
186+
foreach (_TrackedAnnotation trackedAnnotation in this._trackedAnnotations.Values) {
187+
if (hitAnnotation == trackedAnnotation) {
188+
continue;
189+
}
190+
191+
if (trackedAnnotation.activeDevices.Contains(deviceId)) {
192+
if (!enterFlag) {
193+
this._enableDragFromEditorRelease = false;
194+
}
195+
196+
if (trackedAnnotation.annotation?.onDragFromEditorExit != null) {
197+
trackedAnnotation.annotation.onDragFromEditorExit(
198+
PointerDragFromEditorExitEvent
199+
.fromDragFromEditorEvent(lastEvent));
200+
}
201+
202+
trackedAnnotation.activeDevices.Remove(deviceId);
203+
}
204+
}
205+
}
206+
}
207+
}
208+
209+
#endif
210+
}

Runtime/editor/editor_mouse_tracking.cs.meta

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/editor/editor_window.cs

+30-4
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public Window window {
7171
public class EditorWindowAdapter : WindowAdapter {
7272
public readonly EditorWindow editorWindow;
7373

74-
public EditorWindowAdapter(EditorWindow editorWindow) {
74+
public EditorWindowAdapter(EditorWindow editorWindow) : base(true) {
7575
this.editorWindow = editorWindow;
7676
}
7777

@@ -126,6 +126,10 @@ public interface WindowHost {
126126
public abstract class WindowAdapter : Window {
127127
static readonly List<WindowAdapter> _windowAdapters = new List<WindowAdapter>();
128128

129+
public WindowAdapter(bool inEditorWindow = false) {
130+
this.inEditorWindow = inEditorWindow;
131+
}
132+
129133
public static List<WindowAdapter> windowAdapters {
130134
get { return _windowAdapters; }
131135
}
@@ -163,7 +167,7 @@ protected virtual TimeSpan getTime() {
163167

164168
protected float deltaTime;
165169
protected float unscaledDeltaTime;
166-
170+
167171
void updatePhysicalSize() {
168172
var size = this.queryWindowSize();
169173
this._physicalSize = new Size(
@@ -183,7 +187,7 @@ protected virtual void updateSafeArea() {
183187
public void onViewMetricsChanged() {
184188
this._viewMetricsChanged = true;
185189
}
186-
190+
187191
protected abstract bool hasFocus();
188192

189193
public void OnEnable() {
@@ -214,12 +218,14 @@ public void OnDisable() {
214218
this._surface = null;
215219
}
216220

221+
readonly protected bool inEditorWindow;
222+
217223
public override IDisposable getScope() {
218224
WindowAdapter oldInstance = (WindowAdapter) _instance;
219225
_instance = this;
220226

221227
if (this._binding == null) {
222-
this._binding = new WidgetsBinding();
228+
this._binding = new WidgetsBinding(this.inEditorWindow);
223229
}
224230

225231
SchedulerBinding._instance = this._binding;
@@ -400,6 +406,26 @@ void _doOnGUI(Event evt) {
400406
evt.button
401407
);
402408
}
409+
else if (evt.type == EventType.DragUpdated) {
410+
pointerData = new PointerData(
411+
timeStamp: Timer.timespanSinceStartup,
412+
change: PointerChange.dragFromEditorMove,
413+
kind: PointerDeviceKind.mouse,
414+
device: evt.button,
415+
physicalX: evt.mousePosition.x * this._devicePixelRatio,
416+
physicalY: evt.mousePosition.y * this._devicePixelRatio
417+
);
418+
}
419+
else if (evt.type == EventType.DragPerform) {
420+
pointerData = new PointerData(
421+
timeStamp: Timer.timespanSinceStartup,
422+
change: PointerChange.dragFromEditorRelease,
423+
kind: PointerDeviceKind.mouse,
424+
device: evt.button,
425+
physicalX: evt.mousePosition.x * this._devicePixelRatio,
426+
physicalY: evt.mousePosition.y * this._devicePixelRatio
427+
);
428+
}
403429

404430
if (pointerData != null) {
405431
this.onPointerEvent(new PointerDataPacket(new List<PointerData> {

Runtime/editor/widgets.meta

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)