A game about forced loneliness, made by TACStudios
1using System;
2using UnityEngine;
3using UnityEditor.Experimental.GraphView;
4using UnityEngine.UIElements;
5using UnityEngine.UIElements.StyleSheets;
6
7namespace UnityEditor.ShaderGraph.Drawing
8{
9 class WindowDraggable : MouseManipulator
10 {
11 bool m_Active;
12
13 WindowDockingLayout m_WindowDockingLayout;
14
15 Vector2 m_LocalMosueOffset;
16
17 VisualElement m_Handle;
18 GraphView m_GraphView;
19
20 public Action OnDragFinished;
21
22 public WindowDraggable(VisualElement handle = null, VisualElement container = null)
23 {
24 m_Handle = handle;
25 m_Active = false;
26 m_WindowDockingLayout = new WindowDockingLayout();
27
28 if (container != null)
29 container.RegisterCallback<GeometryChangedEvent>(OnParentGeometryChanged);
30 }
31
32 protected override void RegisterCallbacksOnTarget()
33 {
34 if (m_Handle == null)
35 m_Handle = target;
36 m_Handle.RegisterCallback(new EventCallback<MouseDownEvent>(OnMouseDown), TrickleDownEnum.NoTrickleDown);
37 m_Handle.RegisterCallback(new EventCallback<MouseMoveEvent>(OnMouseMove), TrickleDownEnum.NoTrickleDown);
38 m_Handle.RegisterCallback(new EventCallback<MouseUpEvent>(OnMouseUp), TrickleDownEnum.NoTrickleDown);
39 target.RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
40 }
41
42 protected override void UnregisterCallbacksFromTarget()
43 {
44 m_Handle.UnregisterCallback(new EventCallback<MouseDownEvent>(OnMouseDown), TrickleDownEnum.NoTrickleDown);
45 m_Handle.UnregisterCallback(new EventCallback<MouseMoveEvent>(OnMouseMove), TrickleDownEnum.NoTrickleDown);
46 m_Handle.UnregisterCallback(new EventCallback<MouseUpEvent>(OnMouseUp), TrickleDownEnum.NoTrickleDown);
47 target.UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged);
48 if (m_GraphView != null)
49 m_GraphView.UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged);
50 }
51
52 void OnMouseDown(MouseDownEvent evt)
53 {
54 m_Active = true;
55
56 VisualElement parent = target.parent;
57 while (parent != null && !(parent is GraphView))
58 parent = parent.parent;
59 m_GraphView = parent as GraphView;
60
61 if (m_GraphView != null)
62 m_GraphView.RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
63
64 // m_LocalMouseOffset is offset from the target element's (0, 0) to the
65 // to the mouse position.
66 m_LocalMosueOffset = m_Handle.WorldToLocal(evt.mousePosition);
67
68 m_Handle.CaptureMouse();
69 evt.StopImmediatePropagation();
70 }
71
72 void OnMouseMove(MouseMoveEvent evt)
73 {
74 if (m_Active)
75 {
76 // The mouse position of is corrected according to the offset within the target
77 // element (m_LocalWorldOffset) to set the position relative to the mouse position
78 // when the dragging started.
79 Vector2 position = target.parent.WorldToLocal(evt.mousePosition) - m_LocalMosueOffset;
80
81 // Make sure that the object remains in the parent window
82 position.x = Mathf.Clamp(position.x, 0f, target.parent.layout.width - target.layout.width);
83 position.y = Mathf.Clamp(position.y, 0f, target.parent.layout.height - target.layout.height);
84
85 // While moving, use only the left and top position properties,
86 // while keeping the others NaN to not affect layout.
87 target.style.left = position.x;
88 target.style.top = position.y;
89 target.style.right = float.NaN;
90 target.style.bottom = float.NaN;
91 }
92 }
93
94 void OnMouseUp(MouseUpEvent evt)
95 {
96 bool emitDragFinishedEvent = m_Active;
97
98 m_Active = false;
99
100 if (m_Handle.HasMouseCapture())
101 {
102 m_Handle.ReleaseMouse();
103 }
104
105 evt.StopImmediatePropagation();
106
107 // Recalculate which corner to dock to
108 m_WindowDockingLayout.CalculateDockingCornerAndOffset(target.layout, target.parent.layout);
109 m_WindowDockingLayout.ClampToParentWindow();
110
111 // Use the docking results to figure which of left/right and top/bottom needs to be set.
112 m_WindowDockingLayout.ApplyPosition(target);
113
114 // Signal that the dragging has finished.
115 if (emitDragFinishedEvent && OnDragFinished != null)
116 OnDragFinished();
117 }
118
119 void OnGeometryChanged(GeometryChangedEvent geometryChangedEvent)
120 {
121 // Make the target clamp to the border of the window if the
122 // parent window becomes too small to contain it.
123 if (target.parent.layout.width < target.layout.width)
124 {
125 if (m_WindowDockingLayout.dockingLeft)
126 {
127 target.style.left = 0f;
128 target.style.right = float.NaN;
129 }
130 else
131 {
132 target.style.left = float.NaN;
133 target.style.right = 0f;
134 }
135 }
136
137 if (target.parent.layout.height < target.layout.height)
138 {
139 if (m_WindowDockingLayout.dockingTop)
140 {
141 target.style.top = 0f;
142 target.style.bottom = float.NaN;
143 }
144 else
145 {
146 target.style.top = float.NaN;
147 target.style.bottom = 0f;
148 }
149 }
150 }
151
152 void OnParentGeometryChanged(GeometryChangedEvent geometryChangedEvent)
153 {
154 // Check if the parent window can no longer contain the target window.
155 // If the window is out of bounds, make one edge clamp to the border of the
156 // parent window.
157 if (target.layout.xMin < 0f)
158 {
159 target.style.left = 0f;
160 target.style.right = float.NaN;
161 }
162
163 if (target.layout.xMax > geometryChangedEvent.newRect.width)
164 {
165 target.style.left = float.NaN;
166 target.style.right = 0f;
167 }
168
169 if (target.layout.yMax > geometryChangedEvent.newRect.height)
170 {
171 target.style.top = float.NaN;
172 target.style.bottom = 0f;
173 }
174
175 if (target.layout.yMin < 0f)
176 {
177 target.style.top = 0f;
178 target.style.bottom = float.NaN;
179 }
180 }
181 }
182}