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

[Feature] Add Drag and Drop feature between UnityObject and UIWidgets #253

Merged
merged 12 commits into from
Jul 18, 2019
10 changes: 10 additions & 0 deletions README-ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,16 @@ UIWidgets支持渲染文本中包含的表情。表情的图片来自[https://ww
如果您希望使用自己的表情图片,请更新纹理图`Tests/Resources/Emoji.png`,以及`Runtime/ui/txt/emoji.cs`中将Unicode映射到纹理图中具体位置的映射表。
特别地,请记得更新Dictionary变量`emojiLookupTable`,纹理图的行数`rowCount`以及纹理图的列数`colCount`。

#### 十一、与GameObject进行拖拽交互

<div style="text-align: center">
<img src="https://connect-prd-cdn.unity.com/20190718/p/images/e3c9cf9b-c732-4eb2-9afd-fe7de894f342_Custom_Inspector_Showcase_320px.gif" width="300"/>
</div>

我们提供了一个包装好的`UnityObjectDetector`组件以及`onRelease`回调函数,借此您可以实现简单地将物体(例如Hierarchy内的场景物体、Project窗口下的文件等)拖拽至区域内,来获得`UnityEngine.Object[] `类型的引用并进行操作。

你可以在“UIWidgetsTests -> Drag&Drop”下找到简单的实例样例。


## 调试UIWidgets应用程序

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,16 @@ and the unicode-index table in `Runtime/ui/txt/emoji.cs` which maps unicodes to
in the texture. Specifically, remember to update the Dictionary `emojiLookupTable`, number of rows
in the texture `rowCount`, and number of columns `colCount`.

#### Interact with GameObject Drag&Drops

<div style="text-align: center">
<img src="https://connect-prd-cdn.unity.com/20190718/p/images/e3c9cf9b-c732-4eb2-9afd-fe7de894f342_Custom_Inspector_Showcase_320px.gif" width="300"/>
</div>

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.

Please refer to "UIWidgetsTests -> Drag&Drop" for simple examples.


## Debug UIWidgets Application

Expand Down
210 changes: 210 additions & 0 deletions Runtime/editor/editor_mouse_tracking.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.scheduler;
using UnityEditor;

namespace Unity.UIWidgets.gestures {
#if UNITY_EDITOR
public partial class MouseTracker {
bool _enableDragFromEditorRelease = false;

void _handleDragFromEditorEvent(PointerEvent evt, int deviceId) {
if (!this.inEditorWindow) {
return;
}

if (evt is PointerDragFromEditorReleaseEvent) {
this._enableDragFromEditorRelease = false;
this._scheduleDragFromEditorReleaseCheck();
this._lastMouseEvent.Remove(deviceId);
}
else if (evt is PointerDragFromEditorEnterEvent ||
evt is PointerDragFromEditorHoverEvent ||
evt is PointerDragFromEditorExitEvent) {
if (!this._lastMouseEvent.ContainsKey(deviceId) ||
this._lastMouseEvent[deviceId].position != evt.position) {
this._scheduleDragFromEditorMousePositionCheck();
}

this._lastMouseEvent[deviceId] = evt;
}
}

void detachDragFromEditorAnnotation(MouseTrackerAnnotation annotation, int deviceId) {
if (!this.inEditorWindow) {
return;
}

if (annotation.onDragFromEditorExit != null) {
annotation.onDragFromEditorExit(
PointerDragFromEditorExitEvent.fromDragFromEditorEvent(this._lastMouseEvent[deviceId]));
}
}

void _scheduleDragFromEditorReleaseCheck() {
DragAndDrop.AcceptDrag();

var lastMouseEvent = new List<PointerEvent>();
foreach (int deviceId in this._lastMouseEvent.Keys) {
var _deviceId = deviceId;
lastMouseEvent.Add(this._lastMouseEvent[_deviceId]);
SchedulerBinding.instance.addPostFrameCallback(_ => {
foreach (var lastEvent in lastMouseEvent) {
MouseTrackerAnnotation hit = this.annotationFinder(lastEvent.position);

if (hit == null) {
foreach (_TrackedAnnotation trackedAnnotation in this._trackedAnnotations.Values) {
if (trackedAnnotation.activeDevices.Contains(_deviceId)) {
trackedAnnotation.activeDevices.Remove(_deviceId);
}
}

return;
}

_TrackedAnnotation hitAnnotation = this._findAnnotation(hit);

// release
if (hitAnnotation.activeDevices.Contains(_deviceId)) {
if (hitAnnotation.annotation?.onDragFromEditorRelease != null) {
hitAnnotation.annotation.onDragFromEditorRelease(
PointerDragFromEditorReleaseEvent
.fromDragFromEditorEvent(
lastEvent, DragAndDrop.objectReferences));
}

hitAnnotation.activeDevices.Remove(_deviceId);
}
}
});
}

SchedulerBinding.instance.scheduleFrame();
}

/// <summary>
/// Due to the [DragAndDrop] property, DragAndDrop.visualMode must be set to Copy
/// after which editor window can trigger DragPerform event.
/// And because visualMode will be set to None when every frame finished in IMGUI,
/// here we start a scheduler to update VisualMode in every post frame.
/// When [_enableDragFromEditorRelease] set to false, it will stop, vice versa.
/// </summary>
void _enableDragFromEditorReleaseVisualModeLoop() {
if (this._enableDragFromEditorRelease) {
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
SchedulerBinding.instance.addPostFrameCallback(_ => {
this._enableDragFromEditorReleaseVisualModeLoop();
});
SchedulerBinding.instance.scheduleFrame();
}
}

void _scheduleDragFromEditorMousePositionCheck() {
if (!this.inEditorWindow) {
return;
}

SchedulerBinding.instance.addPostFrameCallback(_ => { this.collectDragFromEditorMousePositions(); });
SchedulerBinding.instance.scheduleFrame();
}

public void collectDragFromEditorMousePositions() {
void exitAnnotation(_TrackedAnnotation trackedAnnotation, int deviceId) {
if (trackedAnnotation.activeDevices.Contains(deviceId)) {
this._enableDragFromEditorRelease = false;
if (trackedAnnotation.annotation?.onDragFromEditorExit != null) {
trackedAnnotation.annotation.onDragFromEditorExit(
PointerDragFromEditorExitEvent.fromDragFromEditorEvent(
this._lastMouseEvent[deviceId]));
}

trackedAnnotation.activeDevices.Remove(deviceId);
}
}

void exitAllDevices(_TrackedAnnotation trackedAnnotation) {
if (trackedAnnotation.activeDevices.isNotEmpty()) {
HashSet<int> deviceIds = new HashSet<int>(trackedAnnotation.activeDevices);
foreach (int deviceId in deviceIds) {
exitAnnotation(trackedAnnotation, deviceId);
}
}
}

if (!this.mouseIsConnected) {
foreach (var annotation in this._trackedAnnotations.Values) {
exitAllDevices(annotation);
}

return;
}

foreach (int deviceId in this._lastMouseEvent.Keys) {
PointerEvent lastEvent = this._lastMouseEvent[deviceId];
MouseTrackerAnnotation hit = this.annotationFinder(lastEvent.position);

if (hit == null) {
foreach (_TrackedAnnotation trackedAnnotation in this._trackedAnnotations.Values) {
exitAnnotation(trackedAnnotation, deviceId);
}

return;
}

_TrackedAnnotation hitAnnotation = this._findAnnotation(hit);

// While acrossing two areas, set the flag to true to prevent setting the Pointer Copy VisualMode to None
bool enterFlag = false;

// enter
if (!hitAnnotation.activeDevices.Contains(deviceId)) {
hitAnnotation.activeDevices.Add(deviceId);
enterFlag = true;
// Both onRelease or onEnter event will enable Copy VisualMode
if (hitAnnotation.annotation?.onDragFromEditorRelease != null ||
hitAnnotation.annotation?.onDragFromEditorEnter != null) {
if (!this._enableDragFromEditorRelease) {
this._enableDragFromEditorRelease = true;
this._enableDragFromEditorReleaseVisualModeLoop();
}

if (hitAnnotation.annotation?.onDragFromEditorEnter != null) {
hitAnnotation.annotation.onDragFromEditorEnter(
PointerDragFromEditorEnterEvent
.fromDragFromEditorEvent(lastEvent));
}
}
}

// hover
if (hitAnnotation.annotation?.onDragFromEditorHover != null) {
hitAnnotation.annotation.onDragFromEditorHover(
PointerDragFromEditorHoverEvent.fromDragFromEditorEvent(lastEvent));
}

// leave
foreach (_TrackedAnnotation trackedAnnotation in this._trackedAnnotations.Values) {
if (hitAnnotation == trackedAnnotation) {
continue;
}

if (trackedAnnotation.activeDevices.Contains(deviceId)) {
if (!enterFlag) {
this._enableDragFromEditorRelease = false;
}

if (trackedAnnotation.annotation?.onDragFromEditorExit != null) {
trackedAnnotation.annotation.onDragFromEditorExit(
PointerDragFromEditorExitEvent
.fromDragFromEditorEvent(lastEvent));
}

trackedAnnotation.activeDevices.Remove(deviceId);
}
}
}
}
}

#endif
}
11 changes: 11 additions & 0 deletions Runtime/editor/editor_mouse_tracking.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 30 additions & 4 deletions Runtime/editor/editor_window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public Window window {
public class EditorWindowAdapter : WindowAdapter {
public readonly EditorWindow editorWindow;

public EditorWindowAdapter(EditorWindow editorWindow) {
public EditorWindowAdapter(EditorWindow editorWindow) : base(true) {
this.editorWindow = editorWindow;
}

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

public WindowAdapter(bool inEditorWindow = false) {
this.inEditorWindow = inEditorWindow;
}

public static List<WindowAdapter> windowAdapters {
get { return _windowAdapters; }
}
Expand Down Expand Up @@ -163,7 +167,7 @@ protected virtual TimeSpan getTime() {

protected float deltaTime;
protected float unscaledDeltaTime;

void updatePhysicalSize() {
var size = this.queryWindowSize();
this._physicalSize = new Size(
Expand All @@ -183,7 +187,7 @@ protected virtual void updateSafeArea() {
public void onViewMetricsChanged() {
this._viewMetricsChanged = true;
}

protected abstract bool hasFocus();

public void OnEnable() {
Expand Down Expand Up @@ -214,12 +218,14 @@ public void OnDisable() {
this._surface = null;
}

readonly protected bool inEditorWindow;

public override IDisposable getScope() {
WindowAdapter oldInstance = (WindowAdapter) _instance;
_instance = this;

if (this._binding == null) {
this._binding = new WidgetsBinding();
this._binding = new WidgetsBinding(this.inEditorWindow);
}

SchedulerBinding._instance = this._binding;
Expand Down Expand Up @@ -400,6 +406,26 @@ void _doOnGUI(Event evt) {
evt.button
);
}
else if (evt.type == EventType.DragUpdated) {
pointerData = new PointerData(
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.dragFromEditorMove,
kind: PointerDeviceKind.mouse,
device: evt.button,
physicalX: evt.mousePosition.x * this._devicePixelRatio,
physicalY: evt.mousePosition.y * this._devicePixelRatio
);
}
else if (evt.type == EventType.DragPerform) {
pointerData = new PointerData(
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.dragFromEditorRelease,
kind: PointerDeviceKind.mouse,
device: evt.button,
physicalX: evt.mousePosition.x * this._devicePixelRatio,
physicalY: evt.mousePosition.y * this._devicePixelRatio
);
}

if (pointerData != null) {
this.onPointerEvent(new PointerDataPacket(new List<PointerData> {
Expand Down
8 changes: 8 additions & 0 deletions Runtime/editor/widgets.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading