浏览代码
Merge pull request #253 from IIzzaya/drag_and_drop
Merge pull request #253 from IIzzaya/drag_and_drop
[Feature] Add Drag and Drop feature between UnityObject and UIWidgets/main
GitHub
5 年前
当前提交
8664527c
共有 24 个文件被更改,包括 1341 次插入 和 46 次删除
-
10README-ZH.md
-
10README.md
-
34Runtime/editor/editor_window.cs
-
11Runtime/gestures/binding.cs
-
27Runtime/gestures/converter.cs
-
120Runtime/gestures/events.cs
-
103Runtime/gestures/mouse_tracking.cs
-
13Runtime/rendering/binding.cs
-
109Runtime/rendering/proxy_box.cs
-
4Runtime/ui/pointer.cs
-
46Runtime/widgets/basic.cs
-
2Runtime/widgets/binding.cs
-
210Runtime/editor/editor_mouse_tracking.cs
-
11Runtime/editor/editor_mouse_tracking.cs.meta
-
8Runtime/editor/widgets.meta
-
8Samples/UIWidgetSample/DragNDrop.meta
-
11Runtime/editor/widgets/unity_object_detector.cs.meta
-
83Runtime/editor/widgets/unity_object_detector.cs
-
171Samples/UIWidgetSample/DragNDrop/UnityObjectDetectorSample.cs
-
11Samples/UIWidgetSample/DragNDrop/UnityObjectDetectorSample.cs.meta
-
11Samples/UIWidgetSample/DragNDrop/CustomInspectorSample.cs.meta
-
374Samples/UIWidgetSample/DragNDrop/CustomInspectorSample.cs
|
|||
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
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 08df36a525b2c486aaf1c342786f08db |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: d5417058cad6148859c8ffde0f0f054b |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 2f04d8a9cd0bd4db7b66310e37409f4b |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 899c58f21c1724297b48da615ccb7088 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.editor { |
|||
public delegate void DragFromEditorEnterCallback(); |
|||
|
|||
public delegate void DragFromEditorHoverCallback(); |
|||
|
|||
public delegate void DragFromEditorExitCallback(); |
|||
|
|||
public delegate void DragFromEditorReleaseCallback(DragFromEditorDetails details); |
|||
|
|||
public class DragFromEditorDetails { |
|||
public DragFromEditorDetails(Object[] objectReferences) { |
|||
this.objectReferences = objectReferences; |
|||
} |
|||
|
|||
public readonly Object[] objectReferences; |
|||
} |
|||
|
|||
public class UnityObjectDetector : StatefulWidget { |
|||
public UnityObjectDetector( |
|||
Key key = null, |
|||
Widget child = null, |
|||
DragFromEditorEnterCallback onEnter = null, |
|||
DragFromEditorHoverCallback onHover = null, |
|||
DragFromEditorExitCallback onExit = null, |
|||
DragFromEditorReleaseCallback onRelease = null, |
|||
HitTestBehavior? behavior = null |
|||
) : base(key: key) { |
|||
this.child = child; |
|||
this.onDragFromEditorEnter = onEnter; |
|||
this.onDragFromEditorHover = onHover; |
|||
this.onDragFromEditorExit = onExit; |
|||
this.onDragFromEditorRelease = onRelease; |
|||
this.behavior = behavior; |
|||
} |
|||
|
|||
public readonly Widget child; |
|||
|
|||
public readonly DragFromEditorEnterCallback onDragFromEditorEnter; |
|||
public readonly DragFromEditorHoverCallback onDragFromEditorHover; |
|||
public readonly DragFromEditorExitCallback onDragFromEditorExit; |
|||
public readonly DragFromEditorReleaseCallback onDragFromEditorRelease; |
|||
|
|||
public readonly HitTestBehavior? behavior; |
|||
|
|||
public override State createState() { |
|||
return new UnityObjectDetectorState(); |
|||
} |
|||
} |
|||
|
|||
public class UnityObjectDetectorState : State<UnityObjectDetector> { |
|||
HitTestBehavior _defaultBehavior { |
|||
get { return this.widget.child == null ? HitTestBehavior.translucent : HitTestBehavior.deferToChild; } |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
Widget result = new Listener( |
|||
child: this.widget.child, |
|||
onPointerDragFromEditorEnter: this.widget.onDragFromEditorEnter == null |
|||
? ((PointerDragFromEditorEnterEventListener) null) |
|||
: (evt) => { this.widget.onDragFromEditorEnter.Invoke(); }, |
|||
onPointerDragFromEditorHover: this.widget.onDragFromEditorHover == null |
|||
? ((PointerDragFromEditorHoverEventListener) null) |
|||
: (evt) => { this.widget.onDragFromEditorHover.Invoke(); }, |
|||
onPointerDragFromEditorExit: this.widget.onDragFromEditorExit == null |
|||
? ((PointerDragFromEditorExitEventListener) null) |
|||
: (evt) => { this.widget.onDragFromEditorExit.Invoke(); }, |
|||
onPointerDragFromEditorRelease: this.widget.onDragFromEditorRelease == null |
|||
? ((PointerDragFromEditorReleaseEventListener) null) |
|||
: (evt) => { |
|||
this.widget.onDragFromEditorRelease.Invoke(new DragFromEditorDetails(evt.objectReferences)); |
|||
}, |
|||
behavior: this.widget.behavior ?? this._defaultBehavior |
|||
); |
|||
return result; |
|||
} |
|||
} |
|||
} |
|
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.editor; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.material; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
using Color = Unity.UIWidgets.ui.Color; |
|||
|
|||
namespace UIWidgetsSample.DragNDrop { |
|||
public class UnityObjectDetectorSample : UIWidgetsEditorWindow { |
|||
[MenuItem("UIWidgetsTests/Drag&Drop/UnityObject Detector")] |
|||
public static void ShowEditorWindow() { |
|||
var window = GetWindow<UnityObjectDetectorSample>(); |
|||
window.titleContent.text = "UnityObject Detector Sample"; |
|||
} |
|||
|
|||
protected override Widget createWidget() { |
|||
Debug.Log("[ WIDGET RECREATED ]"); |
|||
return new WidgetsApp( |
|||
home: new UnityObjectDetectorSampleWidget(), |
|||
pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) => |
|||
new PageRouteBuilder( |
|||
settings: settings, |
|||
pageBuilder: (BuildContext context, Animation<float> animation, |
|||
Animation<float> secondaryAnimation) => builder(context) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
public class UnityObjectDetectorSampleWidget : StatefulWidget { |
|||
public UnityObjectDetectorSampleWidget(Key key = null) : base(key) { |
|||
} |
|||
|
|||
public override State createState() { |
|||
return new UnityObjectDetectorSampleWidgetState(); |
|||
} |
|||
} |
|||
|
|||
public class UnityObjectDetectorSampleWidgetState : State<UnityObjectDetectorSampleWidget> { |
|||
readonly Color highlightColor = Color.fromARGB(255, 88, 127, 219); |
|||
readonly Color defaultColor = Color.fromARGB(255, 211, 211, 211); |
|||
readonly List<bool> isHighlighted = new List<bool> { }; |
|||
readonly List<Object[]> objects = new List<Object[]>(); |
|||
|
|||
List<Widget> getUnityObjectDetectorList(int count) { |
|||
if (this.isHighlighted.isEmpty()) { |
|||
for (int i = 0; i < count; i++) { |
|||
this.isHighlighted.Add(false); |
|||
} |
|||
} |
|||
|
|||
if (this.objects.isEmpty()) { |
|||
for (int i = 0; i < count; i++) { |
|||
this.objects.Add(null); |
|||
} |
|||
} |
|||
|
|||
List<Widget> widgetList = new List<Widget>(); |
|||
widgetList.Add(this.getGapBox("Generated List with UnityObjectDetector")); |
|||
|
|||
for (int i = 0; i < count; i++) { |
|||
var _i = i; |
|||
|
|||
Widget widget = new Container( |
|||
decoration: this.isHighlighted[_i] |
|||
? new BoxDecoration(color: this.highlightColor) |
|||
: new BoxDecoration(color: this.defaultColor), |
|||
height: 100f, |
|||
child: new UnityObjectDetector( |
|||
onEnter: () => { |
|||
Debug.Log("Widget " + _i + " onEnter"); |
|||
this.setState(() => { this.isHighlighted[_i] = true; }); |
|||
}, |
|||
onRelease: (details) => { |
|||
Debug.Log("Widget " + _i + " onRelease"); |
|||
this.setState(() => { |
|||
this.isHighlighted[_i] = false; |
|||
this.objects[_i] = details.objectReferences; |
|||
}); |
|||
}, |
|||
onExit: () => { |
|||
Debug.Log("Widget " + _i + " onExit"); |
|||
this.setState(() => { this.isHighlighted[_i] = false; }); |
|||
}, |
|||
child: new Center( |
|||
child: new Text(this.objects[_i] != null |
|||
? this.getNameString(this.objects[_i]) |
|||
: "[Drop/Multi-Drop Here]") |
|||
) |
|||
) |
|||
); |
|||
|
|||
widgetList.Add(widget); |
|||
if (_i != count - 1) { |
|||
widgetList.Add(this.getGapBox()); |
|||
} |
|||
} |
|||
|
|||
return widgetList; |
|||
} |
|||
|
|||
string getNameString(Object[] objs) { |
|||
var str = ""; |
|||
for (int i = 0; i < objs.Length; i++) { |
|||
str += "[" + objs[i].name + "]"; |
|||
if (i != objs.Length - 1) { |
|||
str += "\n"; |
|||
} |
|||
} |
|||
|
|||
return str; |
|||
} |
|||
|
|||
Widget getGapBox(string str = "") { |
|||
return new Container( |
|||
height: 25, |
|||
child: str == "" |
|||
? null |
|||
: new Center( |
|||
child: new Text(str) |
|||
) |
|||
); |
|||
} |
|||
|
|||
bool highlight; |
|||
Object[] objRef; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
var columnList = new List<Widget>(); |
|||
|
|||
columnList.Add(this.getGapBox()); |
|||
columnList.AddRange(this.getUnityObjectDetectorList(3)); |
|||
columnList.AddRange( |
|||
new List<Widget> { |
|||
this.getGapBox("With Listener"), |
|||
new Container( |
|||
decoration: this.highlight |
|||
? new BoxDecoration(color: this.highlightColor) |
|||
: new BoxDecoration(color: this.defaultColor), |
|||
height: 100f, |
|||
child: new Listener( |
|||
onPointerDragFromEditorEnter: (evt) => { this.setState(() => { this.highlight = true; }); }, |
|||
onPointerDragFromEditorExit: (evt) => { this.setState(() => { this.highlight = false; }); }, |
|||
// onPointerDragFromEditorHover: (evt) => { },
|
|||
onPointerDragFromEditorRelease: (evt) => { |
|||
this.objRef = evt.objectReferences; |
|||
this.setState(() => { this.highlight = false; }); |
|||
}, |
|||
child: new Center( |
|||
child: new Text(this.objRef != null |
|||
? this.getNameString(this.objRef) |
|||
: "[Drop/Multi-Drop Here]") |
|||
) |
|||
) |
|||
) |
|||
} |
|||
); |
|||
|
|||
return new Container( |
|||
padding: EdgeInsets.symmetric(horizontal: 25f), |
|||
color: Colors.grey, |
|||
child: new ListView( |
|||
children: columnList |
|||
)); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 2948179a390a944a9a44b7f86cfa3c57 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 5978da8ac1ccd4638bfe3d8425443807 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.editor; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.material; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.service; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
using Color = Unity.UIWidgets.ui.Color; |
|||
using Transform = UnityEngine.Transform; |
|||
|
|||
namespace UIWidgetsSample.DragNDrop { |
|||
public class CustomInspectorSample : UIWidgetsEditorWindow { |
|||
[MenuItem("UIWidgetsTests/Drag&Drop/Custom Inspector")] |
|||
public static void ShowEditorWindow() { |
|||
var window = GetWindow<CustomInspectorSample>(); |
|||
window.titleContent.text = "Custom Inspector Sample"; |
|||
} |
|||
|
|||
protected override void OnEnable() { |
|||
FontManager.instance.addFont(Resources.Load<Font>("MaterialIcons-Regular"), "Material Icons"); |
|||
FontManager.instance.addFont(Resources.Load<Font>("GalleryIcons"), "GalleryIcons"); |
|||
|
|||
base.OnEnable(); |
|||
} |
|||
|
|||
protected override Widget createWidget() { |
|||
Debug.Log("[ WIDGET RECREATED ]"); |
|||
return new MaterialApp( |
|||
home: new CustomInspectorSampleWidget(), |
|||
darkTheme: new ThemeData(primaryColor: Colors.black26) |
|||
); |
|||
} |
|||
} |
|||
|
|||
public class CustomInspectorSampleWidget : StatefulWidget { |
|||
public CustomInspectorSampleWidget(Key key = null) : base(key) { |
|||
} |
|||
|
|||
public override State createState() { |
|||
return new CustomInspectorSampleWidgetState(); |
|||
} |
|||
} |
|||
|
|||
public class CustomInspectorSampleWidgetState : State<CustomInspectorSampleWidget> { |
|||
GameObject objectRef; |
|||
Transform transformRef; |
|||
|
|||
TextEditingController textController = new TextEditingController(); |
|||
|
|||
public override void initState() { |
|||
this.textController.addListener(() => { |
|||
var text = this.textController.text.ToLower(); |
|||
this.textController.value = this.textController.value.copyWith( |
|||
text: text, |
|||
selection: new TextSelection(baseOffset: text.Length, extentOffset: text.Length), |
|||
composing: TextRange.empty |
|||
); |
|||
}); |
|||
base.initState(); |
|||
} |
|||
|
|||
enum ETransfrom { |
|||
Position, |
|||
Rotation, |
|||
Scale |
|||
} |
|||
|
|||
// make custom control of cursor position in TextField.
|
|||
int oldCursorPosition = 0; |
|||
|
|||
// The decimal point input-and-parse exists problem.
|
|||
Widget getCardRow(ETransfrom type, bool hasRef) { |
|||
var xValue = hasRef |
|||
? type == ETransfrom.Position |
|||
? this.transformRef.position.x.ToString() |
|||
: type == ETransfrom.Rotation |
|||
? this.transformRef.localEulerAngles.x.ToString() |
|||
: this.transformRef.localScale.x.ToString() |
|||
: ""; |
|||
// Using individual TextEditingController to control TextField cursor position.
|
|||
var xValueController = TextEditingController.fromValue( |
|||
new TextEditingValue(xValue, TextSelection.collapsed(this.oldCursorPosition)) |
|||
); |
|||
|
|||
var yValue = hasRef |
|||
? type == ETransfrom.Position |
|||
? this.transformRef.position.y.ToString() |
|||
: type == ETransfrom.Rotation |
|||
? this.transformRef.localEulerAngles.y.ToString() |
|||
: this.transformRef.localScale.y.ToString() |
|||
: ""; |
|||
|
|||
var yValueController = TextEditingController.fromValue( |
|||
new TextEditingValue(yValue, TextSelection.collapsed(this.oldCursorPosition)) |
|||
); |
|||
|
|||
var zValue = hasRef |
|||
? type == ETransfrom.Position |
|||
? this.transformRef.position.z.ToString() |
|||
: type == ETransfrom.Rotation |
|||
? this.transformRef.localEulerAngles.z.ToString() |
|||
: this.transformRef.localScale.z.ToString() |
|||
: ""; |
|||
|
|||
var zValueController = TextEditingController.fromValue( |
|||
new TextEditingValue(zValue, TextSelection.collapsed(this.oldCursorPosition)) |
|||
); |
|||
|
|||
return new Column( |
|||
children: new List<Widget> { |
|||
new Container( |
|||
padding: EdgeInsets.symmetric(vertical: 8f), |
|||
child: new Align( |
|||
alignment: Alignment.centerLeft, |
|||
child: new Text( |
|||
type == ETransfrom.Position ? "Position" : |
|||
type == ETransfrom.Rotation ? "Rotation" : "Scale", |
|||
style: new TextStyle(fontSize: 16.0f) |
|||
) |
|||
) |
|||
), |
|||
new Row( |
|||
children: new List<Widget> { |
|||
new Flexible( |
|||
flex: 8, |
|||
child: new Container( |
|||
decoration: new BoxDecoration( |
|||
color: new Color(0xfff5f5f5)), |
|||
child: new TextField( |
|||
decoration: new InputDecoration( |
|||
border: new UnderlineInputBorder(), |
|||
contentPadding: |
|||
EdgeInsets.symmetric( |
|||
horizontal: 10f, vertical: 5f), |
|||
labelText: "X" |
|||
), |
|||
controller: xValueController, |
|||
onChanged: hasRef |
|||
? (str) => { |
|||
// While the TextField value changed, try to parse and assign to transformRef.
|
|||
this.setState(() => { |
|||
float result = 0; |
|||
float.TryParse(str, out result); |
|||
if (str == "" || str[0] == '0') { |
|||
this.oldCursorPosition = 1; |
|||
} |
|||
else { |
|||
this.oldCursorPosition = |
|||
xValueController.selection.startPos.offset; |
|||
} |
|||
|
|||
switch (type) { |
|||
case ETransfrom.Position: |
|||
var newPos = this.transformRef.position; |
|||
newPos.x = result; |
|||
this.transformRef.position = newPos; |
|||
break; |
|||
case ETransfrom.Rotation: |
|||
var newRot = this.transformRef.localEulerAngles; |
|||
newRot.x = result; |
|||
this.transformRef.localEulerAngles = newRot; |
|||
break; |
|||
case ETransfrom.Scale: |
|||
var newScale = this.transformRef.localScale; |
|||
newScale.x = result; |
|||
this.transformRef.localScale = newScale; |
|||
break; |
|||
} |
|||
}); |
|||
} |
|||
: (ValueChanged<string>) null |
|||
) |
|||
)), |
|||
new Flexible( |
|||
child: new Container() |
|||
), |
|||
new Flexible( |
|||
flex: 8, |
|||
child: new Container( |
|||
decoration: new BoxDecoration( |
|||
color: new Color(0xfff5f5f5)), |
|||
child: new TextField( |
|||
decoration: new InputDecoration( |
|||
border: new UnderlineInputBorder(), |
|||
contentPadding: |
|||
EdgeInsets.symmetric( |
|||
horizontal: 10f, vertical: 5f), |
|||
labelText: "Y" |
|||
), |
|||
controller: yValueController, |
|||
onChanged: hasRef |
|||
? (str) => { |
|||
this.setState(() => { |
|||
float result = 0; |
|||
float.TryParse(str, out result); |
|||
if (str == "" || str[0] == '0') { |
|||
this.oldCursorPosition = 1; |
|||
} |
|||
else { |
|||
this.oldCursorPosition = |
|||
yValueController.selection.startPos.offset; |
|||
} |
|||
|
|||
switch (type) { |
|||
case ETransfrom.Position: |
|||
var newPos = this.transformRef.position; |
|||
newPos.y = result; |
|||
this.transformRef.position = newPos; |
|||
break; |
|||
case ETransfrom.Rotation: |
|||
var newRot = this.transformRef.localEulerAngles; |
|||
newRot.y = result; |
|||
this.transformRef.localEulerAngles = newRot; |
|||
break; |
|||
case ETransfrom.Scale: |
|||
var newScale = this.transformRef.localScale; |
|||
newScale.y = result; |
|||
this.transformRef.localScale = newScale; |
|||
break; |
|||
} |
|||
}); |
|||
} |
|||
: (ValueChanged<string>) null |
|||
) |
|||
)), |
|||
new Flexible( |
|||
child: new Container() |
|||
), |
|||
new Flexible( |
|||
flex: 8, |
|||
child: new Container( |
|||
decoration: new BoxDecoration( |
|||
color: new Color(0xfff5f5f5)), |
|||
child: new TextField( |
|||
decoration: new InputDecoration( |
|||
border: new UnderlineInputBorder(), |
|||
contentPadding: |
|||
EdgeInsets.symmetric( |
|||
horizontal: 10f, vertical: 5f), |
|||
labelText: "Z" |
|||
), |
|||
controller: zValueController, |
|||
onChanged: hasRef |
|||
? (str) => { |
|||
this.setState(() => { |
|||
float result = 0; |
|||
float.TryParse(str, out result); |
|||
if (str == "" || str[0] == '0') { |
|||
this.oldCursorPosition = 1; |
|||
} |
|||
else { |
|||
this.oldCursorPosition = |
|||
zValueController.selection.startPos.offset; |
|||
} |
|||
|
|||
switch (type) { |
|||
case ETransfrom.Position: |
|||
var newPos = this.transformRef.position; |
|||
newPos.z = result; |
|||
this.transformRef.position = newPos; |
|||
break; |
|||
case ETransfrom.Rotation: |
|||
var newRot = this.transformRef.localEulerAngles; |
|||
newRot.z = result; |
|||
this.transformRef.localEulerAngles = newRot; |
|||
break; |
|||
case ETransfrom.Scale: |
|||
var newScale = this.transformRef.localScale; |
|||
newScale.z = result; |
|||
this.transformRef.localScale = newScale; |
|||
break; |
|||
} |
|||
}); |
|||
} |
|||
: (ValueChanged<string>) null |
|||
) |
|||
)) |
|||
} |
|||
) |
|||
} |
|||
); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new Theme( |
|||
data: new ThemeData( |
|||
appBarTheme: new AppBarTheme( |
|||
color: Colors.purple |
|||
), |
|||
cardTheme: new CardTheme( |
|||
color: Colors.white, |
|||
elevation: 2.0f |
|||
) |
|||
), |
|||
child: new Scaffold( |
|||
appBar: new AppBar(title: new Text("Custom Inspector")), |
|||
body: new ListView( |
|||
children: new List<Widget> { |
|||
new Card( |
|||
clipBehavior: Clip.antiAlias, |
|||
margin: EdgeInsets.all(20.0f), |
|||
shape: new RoundedRectangleBorder( |
|||
borderRadius: BorderRadius.circular(20.0f) |
|||
), |
|||
child: new Container( |
|||
padding: EdgeInsets.symmetric(vertical: 20f, horizontal: 10f), |
|||
child: new Column( |
|||
mainAxisSize: MainAxisSize.min, |
|||
children: new List<Widget> { |
|||
new UnityObjectDetector( |
|||
// When receiving a GameObject, get its transfrom.
|
|||
onRelease: (details) => { |
|||
this.setState(() => { |
|||
var gameObj = details.objectReferences[0] as GameObject; |
|||
if (gameObj) { |
|||
this.objectRef = gameObj; |
|||
if (this.objectRef) { |
|||
this.transformRef = this.objectRef.transform; |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
child: new ListTile( |
|||
title: new Text( |
|||
this.objectRef == null ? "Object Name" : this.objectRef.name, |
|||
style: new TextStyle(fontSize: 28.0f)), |
|||
subtitle: new Text("Drag an object here", |
|||
style: new TextStyle(fontSize: 16.0f)), |
|||
contentPadding: EdgeInsets.symmetric(horizontal: 10f) |
|||
) |
|||
), |
|||
new Card( |
|||
clipBehavior: Clip.antiAlias, |
|||
shape: new RoundedRectangleBorder( |
|||
borderRadius: BorderRadius.circular(20.0f) |
|||
), |
|||
child: new Container( |
|||
padding: EdgeInsets.symmetric(horizontal: 10.0f), |
|||
child: new Column( |
|||
mainAxisSize: MainAxisSize.min, |
|||
children: new List<Widget> { |
|||
new Container( |
|||
padding: EdgeInsets.only(top: 20f), |
|||
child: new Align( |
|||
alignment: Alignment.centerLeft, |
|||
child: new Text("Transform", |
|||
style: new TextStyle(fontSize: 20.0f)) |
|||
) |
|||
), |
|||
this.getCardRow(ETransfrom.Position, |
|||
this.objectRef != null), |
|||
this.getCardRow(ETransfrom.Rotation, |
|||
this.objectRef != null), |
|||
this.getCardRow(ETransfrom.Scale, this.objectRef != null), |
|||
new Container(padding: EdgeInsets.only(bottom: 20f)) |
|||
} |
|||
) |
|||
) |
|||
), |
|||
} |
|||
) |
|||
) |
|||
) |
|||
} |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
撰写
预览
正在加载...
取消
保存
Reference in new issue