Add **UI Control gizmo for editing UIs**
This commit is contained in:
@@ -117,5 +117,10 @@ namespace FlaxEditor.Gizmo
|
||||
/// </summary>
|
||||
/// <param name="actor">The new actor to spawn.</param>
|
||||
void Spawn(Actor actor);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the context menu at the current mouse location (using current selection).
|
||||
/// </summary>
|
||||
void OpenContextMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@ namespace FlaxEditor.Gizmo
|
||||
/// </summary>
|
||||
public Action Duplicate;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the array of selected objects.
|
||||
/// </summary>
|
||||
public List<SceneGraphNode> Selection => _selection;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the array of selected parent objects (as actors).
|
||||
/// </summary>
|
||||
|
||||
724
Source/Editor/Gizmo/UIEditorGizmo.cs
Normal file
724
Source/Editor/Gizmo/UIEditorGizmo.cs
Normal file
@@ -0,0 +1,724 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// UI editor camera.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
internal sealed class UIEditorCamera : ViewportCamera
|
||||
{
|
||||
public UIEditorRoot UIEditor;
|
||||
|
||||
public void ShowActors(IEnumerable<Actor> actors)
|
||||
{
|
||||
// Calculate bounds of all selected objects
|
||||
var areaRect = Rectangle.Empty;
|
||||
var root = UIEditor.UIRoot;
|
||||
foreach (var actor in actors)
|
||||
{
|
||||
Rectangle bounds;
|
||||
if (actor is UIControl uiControl && uiControl.HasControl && uiControl.IsActive)
|
||||
{
|
||||
var control = uiControl.Control;
|
||||
bounds = control.EditorBounds;
|
||||
|
||||
var ul = control.PointToParent(root, bounds.UpperLeft);
|
||||
var ur = control.PointToParent(root, bounds.UpperRight);
|
||||
var bl = control.PointToParent(root, bounds.BottomLeft);
|
||||
var br = control.PointToParent(root, bounds.BottomRight);
|
||||
|
||||
var min = Float2.Min(Float2.Min(ul, ur), Float2.Min(bl, br));
|
||||
var max = Float2.Max(Float2.Max(ul, ur), Float2.Max(bl, br));
|
||||
bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero));
|
||||
}
|
||||
else if (actor is UICanvas uiCanvas && uiCanvas.IsActive && uiCanvas.GUI.Parent == root)
|
||||
{
|
||||
bounds = uiCanvas.GUI.Bounds;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
if (areaRect == Rectangle.Empty)
|
||||
areaRect = bounds;
|
||||
else
|
||||
areaRect = Rectangle.Union(areaRect, bounds);
|
||||
}
|
||||
if (areaRect == Rectangle.Empty)
|
||||
return;
|
||||
|
||||
// Add margin
|
||||
areaRect = areaRect.MakeExpanded(100.0f);
|
||||
|
||||
// Show bounds
|
||||
UIEditor.ViewScale = (UIEditor.Size / areaRect.Size).MinValue * 0.95f;
|
||||
UIEditor.ViewCenterPosition = areaRect.Center;
|
||||
}
|
||||
|
||||
public override void FocusSelection(GizmosCollection gizmos, ref Quaternion orientation)
|
||||
{
|
||||
ShowActors(gizmos.Get<TransformGizmo>().Selection, ref orientation);
|
||||
}
|
||||
|
||||
public override void ShowActor(Actor actor)
|
||||
{
|
||||
ShowActors(new[] { actor });
|
||||
}
|
||||
|
||||
public override void ShowActors(List<SceneGraphNode> selection, ref Quaternion orientation)
|
||||
{
|
||||
ShowActors(selection.ConvertAll(x => (Actor)x.EditableObject));
|
||||
}
|
||||
|
||||
public override void UpdateView(float dt, ref Vector3 moveDelta, ref Float2 mouseDelta, out bool centerMouse)
|
||||
{
|
||||
centerMouse = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Root control for UI Controls presentation in the game/prefab viewport.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
internal class UIEditorRoot : InputsPassThrough
|
||||
{
|
||||
/// <summary>
|
||||
/// View for the UI structure to be linked in for camera zoom and panning operations.
|
||||
/// </summary>
|
||||
private sealed class View : ContainerControl
|
||||
{
|
||||
public View(UIEditorRoot parent)
|
||||
{
|
||||
AutoFocus = false;
|
||||
ClipChildren = false;
|
||||
CullChildren = false;
|
||||
Pivot = Float2.Zero;
|
||||
Size = new Float2(1920, 1080);
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
public override bool RayCast(ref Float2 location, out Control hit)
|
||||
{
|
||||
// Ignore self
|
||||
return RayCastChildren(ref location, out hit);
|
||||
}
|
||||
|
||||
public override bool IntersectsContent(ref Float2 locationParent, out Float2 location)
|
||||
{
|
||||
location = PointFromParent(ref locationParent);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void DrawSelf()
|
||||
{
|
||||
var uiRoot = (UIEditorRoot)Parent;
|
||||
if (!uiRoot.EnableBackground)
|
||||
return;
|
||||
|
||||
// Draw canvas area
|
||||
var bounds = new Rectangle(Float2.Zero, Size);
|
||||
Render2D.FillRectangle(bounds, new Color(0, 0, 0, 0.2f));
|
||||
}
|
||||
}
|
||||
|
||||
private bool _mouseMovesControl, _mouseMovesView;
|
||||
private Float2 _mouseMovesPos, _moveSnapDelta;
|
||||
private float _mouseMoveSum;
|
||||
private UndoMultiBlock _undoBlock;
|
||||
private View _view;
|
||||
private float[] _gridTickSteps = Utilities.Utils.CurveTickSteps, _gridTickStrengths;
|
||||
|
||||
/// <summary>
|
||||
/// True if enable displaying UI editing background and grid elements.
|
||||
/// </summary>
|
||||
public virtual bool EnableBackground => false;
|
||||
|
||||
/// <summary>
|
||||
/// True if enable selecting controls with mouse button.
|
||||
/// </summary>
|
||||
public virtual bool EnableSelecting => false;
|
||||
|
||||
/// <summary>
|
||||
/// True if enable panning and zooming the view.
|
||||
/// </summary>
|
||||
public bool EnableCamera => _view != null;
|
||||
|
||||
/// <summary>
|
||||
/// Transform gizmo to use sync with (selection, snapping, transformation settings).
|
||||
/// </summary>
|
||||
public virtual TransformGizmo TransformGizmo => null;
|
||||
|
||||
/// <summary>
|
||||
/// The root control for controls to be linked in.
|
||||
/// </summary>
|
||||
public readonly ContainerControl UIRoot;
|
||||
|
||||
internal Float2 ViewPosition
|
||||
{
|
||||
get => _view.Location / -ViewScale;
|
||||
set => _view.Location = value * -ViewScale;
|
||||
}
|
||||
|
||||
internal Float2 ViewCenterPosition
|
||||
{
|
||||
get => (_view.Location - Size * 0.5f) / -ViewScale;
|
||||
set => _view.Location = Size * 0.5f + value * -ViewScale;
|
||||
}
|
||||
|
||||
internal float ViewScale
|
||||
{
|
||||
get => _view.Scale.X;
|
||||
set
|
||||
{
|
||||
value = Mathf.Clamp(value, 0.1f, 4.0f);
|
||||
_view.Scale = new Float2(value);
|
||||
}
|
||||
}
|
||||
|
||||
public UIEditorRoot(bool enableCamera = false)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
AutoFocus = false;
|
||||
UIRoot = this;
|
||||
CullChildren = false;
|
||||
ClipChildren = true;
|
||||
if (enableCamera)
|
||||
{
|
||||
_view = new View(this);
|
||||
UIRoot = _view;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseDown(location, button))
|
||||
return true;
|
||||
|
||||
var transformGizmo = TransformGizmo;
|
||||
var owner = transformGizmo?.Owner;
|
||||
if (EnableSelecting && owner != null && !_mouseMovesControl && button == MouseButton.Left)
|
||||
{
|
||||
// Raycast the control under the mouse
|
||||
var mousePos = PointFromWindow(RootWindow.MousePosition);
|
||||
if (RayCastControl(ref mousePos, out var hitControl))
|
||||
{
|
||||
var uiControlNode = FindUIControlNode(hitControl);
|
||||
if (uiControlNode != null)
|
||||
{
|
||||
// Select node (with additive mode)
|
||||
var selection = new List<SceneGraphNode>();
|
||||
if (Root.GetKey(KeyboardKeys.Control))
|
||||
{
|
||||
// Add/remove from selection
|
||||
selection.AddRange(transformGizmo.Selection);
|
||||
if (transformGizmo.Selection.Contains(uiControlNode))
|
||||
selection.Remove(uiControlNode);
|
||||
else
|
||||
selection.Add(uiControlNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select
|
||||
selection.Add(uiControlNode);
|
||||
}
|
||||
owner.Select(selection);
|
||||
|
||||
// Initialize control movement
|
||||
_mouseMovesControl = true;
|
||||
_mouseMovesPos = location;
|
||||
_mouseMoveSum = 0.0f;
|
||||
_moveSnapDelta = Float2.Zero;
|
||||
Focus();
|
||||
StartMouseCapture();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (EnableCamera && (button == MouseButton.Right || button == MouseButton.Middle))
|
||||
{
|
||||
// Initialize surface movement
|
||||
_mouseMovesView = true;
|
||||
_mouseMovesPos = location;
|
||||
_mouseMoveSum = 0.0f;
|
||||
Focus();
|
||||
StartMouseCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
base.OnMouseMove(location);
|
||||
|
||||
var transformGizmo = TransformGizmo;
|
||||
if (_mouseMovesControl && transformGizmo != null)
|
||||
{
|
||||
// Calculate transform delta
|
||||
var delta = location - _mouseMovesPos;
|
||||
if (transformGizmo.TranslationSnapEnable || transformGizmo.Owner.UseSnapping)
|
||||
{
|
||||
_moveSnapDelta += delta;
|
||||
delta = Float2.SnapToGrid(_moveSnapDelta, new Float2(transformGizmo.TranslationSnapValue * ViewScale));
|
||||
_moveSnapDelta -= delta;
|
||||
}
|
||||
|
||||
// Move selected controls
|
||||
if (delta.LengthSquared > 0.0f)
|
||||
{
|
||||
StartUndo();
|
||||
var moved = false;
|
||||
var moveLocation = _mouseMovesPos + delta;
|
||||
var selection = transformGizmo.Selection;
|
||||
for (var i = 0; i < selection.Count; i++)
|
||||
{
|
||||
if (IsValidControl(selection[i], out var uiControl))
|
||||
{
|
||||
// Move control (handle any control transformations by moving in editor's local-space)
|
||||
var control = uiControl.Control;
|
||||
var localLocation = control.LocalLocation;
|
||||
var pointOrigin = control.Parent ?? control;
|
||||
var startPos = pointOrigin.PointFromParent(this, _mouseMovesPos);
|
||||
var endPos = pointOrigin.PointFromParent(this, moveLocation);
|
||||
var uiControlDelta = endPos - startPos;
|
||||
control.LocalLocation = localLocation + uiControlDelta;
|
||||
|
||||
// Don't move if layout doesn't allow it
|
||||
if (control.Parent != null)
|
||||
control.Parent.PerformLayout();
|
||||
else
|
||||
control.PerformLayout();
|
||||
|
||||
// Check if control was moved (parent container could block it)
|
||||
if (localLocation != control.LocalLocation)
|
||||
moved = true;
|
||||
}
|
||||
}
|
||||
_mouseMovesPos = location;
|
||||
_mouseMoveSum += delta.Length;
|
||||
if (moved)
|
||||
Cursor = CursorType.SizeAll;
|
||||
}
|
||||
}
|
||||
if (_mouseMovesView)
|
||||
{
|
||||
// Move view
|
||||
var delta = location - _mouseMovesPos;
|
||||
if (delta.LengthSquared > 4.0f)
|
||||
{
|
||||
_mouseMovesPos = location;
|
||||
_mouseMoveSum += delta.Length;
|
||||
_view.Location += delta;
|
||||
Cursor = CursorType.SizeAll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
EndMovingControls();
|
||||
if (_mouseMovesView)
|
||||
{
|
||||
EndMovingView();
|
||||
if (button == MouseButton.Right && _mouseMoveSum < 2.0f)
|
||||
TransformGizmo.Owner.OpenContextMenu();
|
||||
}
|
||||
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
EndMovingControls();
|
||||
EndMovingView();
|
||||
|
||||
base.OnMouseLeave();
|
||||
}
|
||||
|
||||
public override void OnLostFocus()
|
||||
{
|
||||
EndMovingControls();
|
||||
EndMovingView();
|
||||
|
||||
base.OnLostFocus();
|
||||
}
|
||||
|
||||
public override bool OnMouseWheel(Float2 location, float delta)
|
||||
{
|
||||
if (base.OnMouseWheel(location, delta))
|
||||
return true;
|
||||
|
||||
if (EnableCamera && !_mouseMovesControl)
|
||||
{
|
||||
// Zoom view
|
||||
var nextViewScale = ViewScale + delta * 0.1f;
|
||||
if (delta > 0 && !_mouseMovesControl)
|
||||
{
|
||||
// Scale towards mouse when zooming in
|
||||
var nextCenterPosition = ViewPosition + location / ViewScale;
|
||||
ViewScale = nextViewScale;
|
||||
ViewPosition = nextCenterPosition - (location / ViewScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scale while keeping center position when zooming out or when dragging view
|
||||
var viewCenter = ViewCenterPosition;
|
||||
ViewScale = nextViewScale;
|
||||
ViewCenterPosition = viewCenter;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
if (EnableBackground)
|
||||
{
|
||||
// Draw background
|
||||
Surface.VisjectSurface.DrawBackgroundDefault(Editor.Instance.UI.VisjectSurfaceBackground, Width, Height);
|
||||
|
||||
// Draw grid
|
||||
var viewRect = GetClientArea();
|
||||
var upperLeft = _view.PointFromParent(viewRect.Location);
|
||||
var bottomRight = _view.PointFromParent(viewRect.Size);
|
||||
var min = Float2.Min(upperLeft, bottomRight);
|
||||
var max = Float2.Max(upperLeft, bottomRight);
|
||||
var pixelRange = (max - min) * ViewScale;
|
||||
Render2D.PushClip(ref viewRect);
|
||||
DrawAxis(Float2.UnitX, viewRect, min.X, max.X, pixelRange.X);
|
||||
DrawAxis(Float2.UnitY, viewRect, min.Y, max.Y, pixelRange.Y);
|
||||
Render2D.PopClip();
|
||||
}
|
||||
|
||||
base.Draw();
|
||||
|
||||
bool drawAnySelectedControl = false;
|
||||
var transformGizmo = TransformGizmo;
|
||||
if (transformGizmo != null)
|
||||
{
|
||||
// Selected UI controls outline
|
||||
var selection = transformGizmo.Selection;
|
||||
for (var i = 0; i < selection.Count; i++)
|
||||
{
|
||||
if (IsValidControl(selection[i], out var controlActor))
|
||||
{
|
||||
DrawControlBounds(controlActor.Control, true, ref drawAnySelectedControl);
|
||||
// TODO: draw anchors
|
||||
}
|
||||
}
|
||||
}
|
||||
if (EnableSelecting && !_mouseMovesControl && IsMouseOver)
|
||||
{
|
||||
// Highlight control under mouse for easier selecting (except if already selected)
|
||||
var mousePos = PointFromWindow(RootWindow.MousePosition);
|
||||
if (RayCastControl(ref mousePos, out var hitControl) &&
|
||||
(transformGizmo == null || !transformGizmo.Selection.Any(x => x.EditableObject is UIControl controlActor && controlActor.Control == hitControl)))
|
||||
{
|
||||
DrawControlBounds(hitControl, false, ref drawAnySelectedControl);
|
||||
}
|
||||
}
|
||||
if (drawAnySelectedControl)
|
||||
Render2D.PopTransform();
|
||||
|
||||
if (EnableBackground)
|
||||
{
|
||||
// Draw border
|
||||
if (ContainsFocus)
|
||||
{
|
||||
Render2D.DrawRectangle(new Rectangle(1, 1, Width - 2, Height - 2), Editor.IsPlayMode ? Color.OrangeRed : Style.Current.BackgroundSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (IsDisposing)
|
||||
return;
|
||||
EndMovingControls();
|
||||
EndMovingView();
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange)
|
||||
{
|
||||
var style = Style.Current;
|
||||
var linesColor = style.ForegroundDisabled.RGBMultiplied(0.5f);
|
||||
var labelsColor = style.ForegroundDisabled;
|
||||
var labelsSize = 10.0f;
|
||||
Utilities.Utils.DrawCurveTicks((float tick, float strength) =>
|
||||
{
|
||||
var p = _view.PointToParent(axis * tick);
|
||||
|
||||
// Draw line
|
||||
var lineRect = new Rectangle
|
||||
(
|
||||
viewRect.Location + (p - 0.5f) * axis,
|
||||
Float2.Lerp(viewRect.Size, Float2.One, axis)
|
||||
);
|
||||
Render2D.FillRectangle(lineRect, linesColor.AlphaMultiplied(strength));
|
||||
|
||||
// Draw label
|
||||
string label = tick.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
var labelRect = new Rectangle
|
||||
(
|
||||
viewRect.X + 4.0f + (p.X * axis.X),
|
||||
viewRect.Y - labelsSize + (p.Y * axis.Y) + (viewRect.Size.Y * axis.X),
|
||||
50,
|
||||
labelsSize
|
||||
);
|
||||
Render2D.DrawText(style.FontSmall, label, labelRect, labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
||||
}, _gridTickSteps, ref _gridTickStrengths, min, max, pixelRange);
|
||||
}
|
||||
|
||||
private void DrawControlBounds(Control control, bool selection, ref bool drawAnySelectedControl)
|
||||
{
|
||||
if (!drawAnySelectedControl)
|
||||
{
|
||||
drawAnySelectedControl = true;
|
||||
Render2D.PushTransform(ref _cachedTransform);
|
||||
}
|
||||
var options = Editor.Instance.Options.Options.Visual;
|
||||
var bounds = control.EditorBounds;
|
||||
var ul = control.PointToParent(this, bounds.UpperLeft);
|
||||
var ur = control.PointToParent(this, bounds.UpperRight);
|
||||
var bl = control.PointToParent(this, bounds.BottomLeft);
|
||||
var br = control.PointToParent(this, bounds.BottomRight);
|
||||
var color = selection ? options.SelectionOutlineColor0 : Style.Current.SelectionBorder;
|
||||
#if false
|
||||
// AABB
|
||||
var min = Float2.Min(Float2.Min(ul, ur), Float2.Min(bl, br));
|
||||
var max = Float2.Max(Float2.Max(ul, ur), Float2.Max(bl, br));
|
||||
bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero));
|
||||
Render2D.DrawRectangle(bounds, color, options.UISelectionOutlineSize);
|
||||
#else
|
||||
// OBB
|
||||
Render2D.DrawLine(ul, ur, color, options.UISelectionOutlineSize);
|
||||
Render2D.DrawLine(ur, br, color, options.UISelectionOutlineSize);
|
||||
Render2D.DrawLine(br, bl, color, options.UISelectionOutlineSize);
|
||||
Render2D.DrawLine(bl, ul, color, options.UISelectionOutlineSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
private bool IsValidControl(SceneGraphNode node, out UIControl uiControl)
|
||||
{
|
||||
uiControl = null;
|
||||
if (node.EditableObject is UIControl controlActor)
|
||||
uiControl = controlActor;
|
||||
return uiControl != null &&
|
||||
uiControl.Control != null &&
|
||||
uiControl.Control.VisibleInHierarchy &&
|
||||
uiControl.Control.RootWindow != null;
|
||||
}
|
||||
|
||||
private bool RayCastControl(ref Float2 location, out Control hit)
|
||||
{
|
||||
#if false
|
||||
// Raycast only controls with content (eg. skips transparent panels)
|
||||
return RayCastChildren(ref location, out hit);
|
||||
#else
|
||||
// Find any control under mouse (hierarchical)
|
||||
hit = GetChildAtRecursive(location);
|
||||
if (hit is View)
|
||||
hit = null;
|
||||
return hit != null;
|
||||
#endif
|
||||
}
|
||||
|
||||
private UIControlNode FindUIControlNode(Control control)
|
||||
{
|
||||
return FindUIControlNode(TransformGizmo.Owner.SceneGraphRoot, control);
|
||||
}
|
||||
|
||||
private UIControlNode FindUIControlNode(SceneGraphNode node, Control control)
|
||||
{
|
||||
var result = node as UIControlNode;
|
||||
if (result != null && ((UIControl)result.Actor).Control == control)
|
||||
return result;
|
||||
foreach (var e in node.ChildNodes)
|
||||
{
|
||||
result = FindUIControlNode(e, control);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void StartUndo()
|
||||
{
|
||||
var undo = TransformGizmo?.Owner?.Undo;
|
||||
if (undo == null || _undoBlock != null)
|
||||
return;
|
||||
_undoBlock = new UndoMultiBlock(undo, TransformGizmo.Selection.ConvertAll(x => x.EditableObject), "Edit control");
|
||||
}
|
||||
|
||||
private void EndUndo()
|
||||
{
|
||||
if (_undoBlock == null)
|
||||
return;
|
||||
_undoBlock.Dispose();
|
||||
_undoBlock = null;
|
||||
}
|
||||
|
||||
private void EndMovingControls()
|
||||
{
|
||||
if (!_mouseMovesControl)
|
||||
return;
|
||||
_mouseMovesControl = false;
|
||||
EndMouseCapture();
|
||||
Cursor = CursorType.Default;
|
||||
EndUndo();
|
||||
}
|
||||
|
||||
private void EndMovingView()
|
||||
{
|
||||
if (!_mouseMovesView)
|
||||
return;
|
||||
_mouseMovesView = false;
|
||||
EndMouseCapture();
|
||||
Cursor = CursorType.Default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Control that can optionally disable inputs to the children.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
internal class InputsPassThrough : ContainerControl
|
||||
{
|
||||
private bool _isMouseOver;
|
||||
|
||||
/// <summary>
|
||||
/// True if enable input events passing to the UI.
|
||||
/// </summary>
|
||||
public virtual bool EnableInputs => true;
|
||||
|
||||
public override bool RayCast(ref Float2 location, out Control hit)
|
||||
{
|
||||
return RayCastChildren(ref location, out hit);
|
||||
}
|
||||
|
||||
public override bool ContainsPoint(ref Float2 location, bool precise = false)
|
||||
{
|
||||
if (precise)
|
||||
return false;
|
||||
return base.ContainsPoint(ref location, precise);
|
||||
}
|
||||
|
||||
public override bool OnCharInput(char c)
|
||||
{
|
||||
if (!EnableInputs)
|
||||
return false;
|
||||
return base.OnCharInput(c);
|
||||
}
|
||||
|
||||
public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
|
||||
{
|
||||
if (!EnableInputs)
|
||||
return DragDropEffect.None;
|
||||
return base.OnDragDrop(ref location, data);
|
||||
}
|
||||
|
||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||
{
|
||||
if (!EnableInputs)
|
||||
return DragDropEffect.None;
|
||||
return base.OnDragEnter(ref location, data);
|
||||
}
|
||||
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
if (!EnableInputs)
|
||||
return;
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||
{
|
||||
if (!EnableInputs)
|
||||
return DragDropEffect.None;
|
||||
return base.OnDragMove(ref location, data);
|
||||
}
|
||||
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (!EnableInputs)
|
||||
return false;
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
|
||||
public override void OnKeyUp(KeyboardKeys key)
|
||||
{
|
||||
if (!EnableInputs)
|
||||
return;
|
||||
base.OnKeyUp(key);
|
||||
}
|
||||
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (!EnableInputs)
|
||||
return false;
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (!EnableInputs)
|
||||
return false;
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
public override bool IsMouseOver => _isMouseOver;
|
||||
|
||||
public override void OnMouseEnter(Float2 location)
|
||||
{
|
||||
_isMouseOver = true;
|
||||
if (!EnableInputs)
|
||||
return;
|
||||
base.OnMouseEnter(location);
|
||||
}
|
||||
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
_isMouseOver = false;
|
||||
if (!EnableInputs)
|
||||
return;
|
||||
base.OnMouseLeave();
|
||||
}
|
||||
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
if (!EnableInputs)
|
||||
return;
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (!EnableInputs)
|
||||
return false;
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
|
||||
public override bool OnMouseWheel(Float2 location, float delta)
|
||||
{
|
||||
if (!EnableInputs)
|
||||
return false;
|
||||
return base.OnMouseWheel(location, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
protected virtual void DrawBackground()
|
||||
{
|
||||
var background = Style.Background;
|
||||
DrawBackgroundDefault(Style.Background, Width, Height);
|
||||
}
|
||||
|
||||
internal static void DrawBackgroundDefault(Texture background, float width, float height)
|
||||
{
|
||||
if (background && background.ResidentMipLevels > 0)
|
||||
{
|
||||
var bSize = background.Size;
|
||||
@@ -77,8 +81,8 @@ namespace FlaxEditor.Surface
|
||||
if (pos.Y > 0)
|
||||
pos.Y -= bh;
|
||||
|
||||
int maxI = Mathf.CeilToInt(Width / bw + 1.0f);
|
||||
int maxJ = Mathf.CeilToInt(Height / bh + 1.0f);
|
||||
int maxI = Mathf.CeilToInt(width / bw + 1.0f);
|
||||
int maxJ = Mathf.CeilToInt(height / bh + 1.0f);
|
||||
|
||||
for (int i = 0; i < maxI; i++)
|
||||
{
|
||||
|
||||
@@ -93,6 +93,9 @@ namespace FlaxEditor.Viewport
|
||||
/// <inheritdoc />
|
||||
public abstract void Spawn(Actor actor);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract void OpenContextMenu();
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ using FlaxEditor.Viewport.Cameras;
|
||||
using FlaxEditor.Viewport.Widgets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Newtonsoft.Json;
|
||||
using JsonSerializer = FlaxEngine.Json.JsonSerializer;
|
||||
|
||||
namespace FlaxEditor.Viewport
|
||||
@@ -154,6 +153,7 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Input
|
||||
|
||||
internal bool _disableInputUpdate;
|
||||
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
|
||||
private int _deltaFilteringStep;
|
||||
private Float2 _startPos;
|
||||
@@ -1496,6 +1496,9 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_disableInputUpdate)
|
||||
return;
|
||||
|
||||
// Update camera
|
||||
bool useMovementSpeed = false;
|
||||
if (_camera != null)
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
@@ -223,7 +222,7 @@ namespace FlaxEditor.Viewport
|
||||
TransformGizmo = new TransformGizmo(this);
|
||||
TransformGizmo.ApplyTransformation += ApplyTransform;
|
||||
TransformGizmo.ModeChanged += OnGizmoModeChanged;
|
||||
TransformGizmo.Duplicate += Editor.Instance.SceneEditing.Duplicate;
|
||||
TransformGizmo.Duplicate += _editor.SceneEditing.Duplicate;
|
||||
Gizmos.Active = TransformGizmo;
|
||||
|
||||
// Add grid
|
||||
@@ -479,7 +478,7 @@ namespace FlaxEditor.Viewport
|
||||
};
|
||||
|
||||
// Spawn
|
||||
Editor.Instance.SceneEditing.Spawn(actor, parent);
|
||||
_editor.SceneEditing.Spawn(actor, parent);
|
||||
}
|
||||
|
||||
private void OnBegin(RenderTask task, GPUContext context)
|
||||
@@ -712,7 +711,7 @@ namespace FlaxEditor.Viewport
|
||||
Vector3 gizmoPosition = TransformGizmo.Position;
|
||||
|
||||
// Rotate selected objects
|
||||
bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode;
|
||||
bool isPlayMode = _editor.StateMachine.IsPlayMode;
|
||||
TransformGizmo.StartTransforming();
|
||||
for (int i = 0; i < selection.Count; i++)
|
||||
{
|
||||
@@ -787,7 +786,7 @@ namespace FlaxEditor.Viewport
|
||||
Vector3 gizmoPosition = TransformGizmo.Position;
|
||||
|
||||
// Transform selected objects
|
||||
bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode;
|
||||
bool isPlayMode = _editor.StateMachine.IsPlayMode;
|
||||
for (int i = 0; i < selection.Count; i++)
|
||||
{
|
||||
var obj = selection[i];
|
||||
@@ -929,7 +928,14 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
var parent = actor.Parent ?? Level.GetScene(0);
|
||||
actor.Name = Utilities.Utils.IncrementNameNumber(actor.Name, x => parent.GetChild(x) == null);
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
_editor.SceneEditing.Spawn(actor);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OpenContextMenu()
|
||||
{
|
||||
var mouse = PointFromWindow(Root.MousePosition);
|
||||
_editor.Windows.SceneWin.ShowContextMenu(this, mouse);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
public PrefabWindowViewport Viewport;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanRender()
|
||||
{
|
||||
return (Task.View.Flags & ViewFlags.EditorSprites) == ViewFlags.EditorSprites && Enabled;
|
||||
@@ -41,6 +40,24 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
}
|
||||
|
||||
[HideInEditor]
|
||||
private sealed class PrefabUIEditorRoot : UIEditorRoot
|
||||
{
|
||||
private readonly PrefabWindowViewport _viewport;
|
||||
|
||||
public PrefabUIEditorRoot(PrefabWindowViewport viewport)
|
||||
: base(true)
|
||||
{
|
||||
_viewport = viewport;
|
||||
Parent = viewport;
|
||||
}
|
||||
|
||||
public override bool EnableInputs => false;
|
||||
public override bool EnableSelecting => true;
|
||||
public override bool EnableBackground => _viewport._hasUILinkedCached;
|
||||
public override TransformGizmo TransformGizmo => _viewport.TransformGizmo;
|
||||
}
|
||||
|
||||
private readonly PrefabWindow _window;
|
||||
private UpdateDelegate _update;
|
||||
|
||||
@@ -56,6 +73,9 @@ namespace FlaxEditor.Viewport
|
||||
private PrefabSpritesRenderer _spritesRenderer;
|
||||
private IntPtr _tempDebugDrawContext;
|
||||
|
||||
private bool _hasUILinkedCached;
|
||||
private PrefabUIEditorRoot _uiRoot;
|
||||
|
||||
/// <summary>
|
||||
/// Drag and drop handlers
|
||||
/// </summary>
|
||||
@@ -111,6 +131,11 @@ namespace FlaxEditor.Viewport
|
||||
TransformGizmo.Duplicate += _window.Duplicate;
|
||||
Gizmos.Active = TransformGizmo;
|
||||
|
||||
// Use custom root for UI controls
|
||||
_uiRoot = new PrefabUIEditorRoot(this);
|
||||
_uiRoot.IndexInParent = 0; // Move viewport down below other widgets in the viewport
|
||||
_uiParentLink = _uiRoot.UIRoot;
|
||||
|
||||
// Transform space widget
|
||||
var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||
var transformSpaceToggle = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Globe32, null, true)
|
||||
@@ -237,8 +262,54 @@ namespace FlaxEditor.Viewport
|
||||
SetUpdate(ref _update, OnUpdate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the viewport's gizmos, especially to toggle between 3D and UI editing modes.
|
||||
/// </summary>
|
||||
internal void UpdateGizmoMode()
|
||||
{
|
||||
// Skip if gizmo mode was unmodified
|
||||
if (_hasUILinked == _hasUILinkedCached)
|
||||
return;
|
||||
_hasUILinkedCached = _hasUILinked;
|
||||
|
||||
if (_hasUILinked)
|
||||
{
|
||||
// UI widget
|
||||
Gizmos.Active = null;
|
||||
ViewportCamera = new UIEditorCamera { UIEditor = _uiRoot };
|
||||
|
||||
// Hide 3D visuals
|
||||
ShowEditorPrimitives = false;
|
||||
ShowDefaultSceneActors = false;
|
||||
ShowDebugDraw = false;
|
||||
|
||||
// Show whole UI on startup
|
||||
ViewportCamera.ShowActor(Instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generic prefab
|
||||
Gizmos.Active = TransformGizmo;
|
||||
ViewportCamera = new FPSCamera();
|
||||
}
|
||||
|
||||
// Update default components usage
|
||||
bool defaultFeatures = !_hasUILinked;
|
||||
_disableInputUpdate = _hasUILinked;
|
||||
_spritesRenderer.Enabled = defaultFeatures;
|
||||
SelectionOutline.Enabled = defaultFeatures;
|
||||
_showDefaultSceneButton.Visible = defaultFeatures;
|
||||
_cameraWidget.Visible = defaultFeatures;
|
||||
_cameraButton.Visible = defaultFeatures;
|
||||
_orthographicModeButton.Visible = defaultFeatures;
|
||||
Task.Enabled = defaultFeatures;
|
||||
UseAutomaticTaskManagement = defaultFeatures;
|
||||
TintColor = defaultFeatures ? Color.White : Color.Transparent;
|
||||
}
|
||||
|
||||
private void OnUpdate(float deltaTime)
|
||||
{
|
||||
UpdateGizmoMode();
|
||||
for (int i = 0; i < Gizmos.Count; i++)
|
||||
{
|
||||
Gizmos[i].Update(deltaTime);
|
||||
@@ -369,6 +440,13 @@ namespace FlaxEditor.Viewport
|
||||
_window.Spawn(actor);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OpenContextMenu()
|
||||
{
|
||||
var mouse = PointFromWindow(Root.MousePosition);
|
||||
_window.ShowContextMenu(this, ref mouse);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false;
|
||||
|
||||
@@ -545,40 +623,6 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
// Selected UI controls outline
|
||||
bool drawAnySelectedControl = false;
|
||||
// TODO: optimize this (eg. cache list of selected UIControl's when selection gets changed)
|
||||
for (var i = 0; i < _window.Selection.Count; i++)
|
||||
{
|
||||
if (_window.Selection[i]?.EditableObject is UIControl controlActor && controlActor && controlActor.Control != null && controlActor.Control.VisibleInHierarchy && controlActor.Control.RootWindow != null)
|
||||
{
|
||||
if (!drawAnySelectedControl)
|
||||
{
|
||||
drawAnySelectedControl = true;
|
||||
Render2D.PushTransform(ref _cachedTransform);
|
||||
}
|
||||
var control = controlActor.Control;
|
||||
var bounds = control.EditorBounds;
|
||||
var p1 = control.PointToParent(this, bounds.UpperLeft);
|
||||
var p2 = control.PointToParent(this, bounds.UpperRight);
|
||||
var p3 = control.PointToParent(this, bounds.BottomLeft);
|
||||
var p4 = control.PointToParent(this, bounds.BottomRight);
|
||||
var min = Float2.Min(Float2.Min(p1, p2), Float2.Min(p3, p4));
|
||||
var max = Float2.Max(Float2.Max(p1, p2), Float2.Max(p3, p4));
|
||||
bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero));
|
||||
var options = Editor.Instance.Options.Options.Visual;
|
||||
Render2D.DrawRectangle(bounds, options.SelectionOutlineColor0, options.UISelectionOutlineSize);
|
||||
}
|
||||
}
|
||||
if (drawAnySelectedControl)
|
||||
Render2D.PopTransform();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnLeftMouseButtonUp()
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
/// <seealso cref="FlaxEditor.Viewport.EditorViewport" />
|
||||
public abstract class AssetPreview : EditorViewport, IEditorPrimitivesOwner
|
||||
{
|
||||
private ContextMenuButton _showDefaultSceneButton;
|
||||
internal ContextMenuButton _showDefaultSceneButton;
|
||||
private IntPtr _debugDrawContext;
|
||||
private bool _debugDrawEnable;
|
||||
private bool _editorPrimitivesEnable;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
@@ -14,7 +15,9 @@ namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
private Prefab _prefab;
|
||||
private Actor _instance;
|
||||
internal UIControl _uiControlLinked;
|
||||
private UIControl _uiControlLinked;
|
||||
internal bool _hasUILinked;
|
||||
internal ContainerControl _uiParentLink;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the prefab asset to preview.
|
||||
@@ -72,7 +75,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
// Unlink UI control
|
||||
if (_uiControlLinked)
|
||||
{
|
||||
if (_uiControlLinked.Control?.Parent == this)
|
||||
if (_uiControlLinked.Control?.Parent == _uiParentLink)
|
||||
_uiControlLinked.Control.Parent = null;
|
||||
_uiControlLinked = null;
|
||||
}
|
||||
@@ -82,6 +85,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
}
|
||||
|
||||
_instance = value;
|
||||
_hasUILinked = false;
|
||||
|
||||
if (_instance)
|
||||
{
|
||||
@@ -103,20 +107,24 @@ namespace FlaxEditor.Viewport.Previews
|
||||
uiControl.Control != null &&
|
||||
uiControl.Control.Parent == null)
|
||||
{
|
||||
uiControl.Control.Parent = this;
|
||||
uiControl.Control.Parent = _uiParentLink;
|
||||
_uiControlLinked = uiControl;
|
||||
_hasUILinked = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void LinkCanvas(Actor actor)
|
||||
{
|
||||
if (actor is UICanvas uiCanvas)
|
||||
uiCanvas.EditorOverride(Task, this);
|
||||
{
|
||||
uiCanvas.EditorOverride(Task, _uiParentLink);
|
||||
if (uiCanvas.GUI.Parent == _uiParentLink)
|
||||
_hasUILinked = true;
|
||||
}
|
||||
|
||||
var children = actor.ChildrenCount;
|
||||
for (int i = 0; i < children; i++)
|
||||
{
|
||||
LinkCanvas(actor.GetChild(i));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -126,6 +134,8 @@ namespace FlaxEditor.Viewport.Previews
|
||||
public PrefabPreview(bool useWidgets)
|
||||
: base(useWidgets)
|
||||
{
|
||||
// Link to itself by default
|
||||
_uiParentLink = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -142,8 +152,6 @@ namespace FlaxEditor.Viewport.Previews
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (IsDisposing)
|
||||
return;
|
||||
Prefab = null;
|
||||
|
||||
base.OnDestroy();
|
||||
|
||||
@@ -360,10 +360,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent control.</param>
|
||||
/// <param name="location">The location (within a given control).</param>
|
||||
private void ShowContextMenu(Control parent, ref Float2 location)
|
||||
internal void ShowContextMenu(Control parent, ref Float2 location)
|
||||
{
|
||||
var contextMenu = CreateContextMenu();
|
||||
|
||||
contextMenu.Show(parent, location);
|
||||
}
|
||||
|
||||
|
||||
@@ -344,6 +344,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
private void OnPrefabOpened()
|
||||
{
|
||||
_viewport.Prefab = _asset;
|
||||
_viewport.UpdateGizmoMode();
|
||||
Graph.MainActor = _viewport.Instance;
|
||||
Selection.Clear();
|
||||
Select(Graph.Main);
|
||||
@@ -359,7 +360,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
try
|
||||
{
|
||||
Editor.Scene.OnSaveStart(_viewport);
|
||||
Editor.Scene.OnSaveStart(_viewport._uiParentLink);
|
||||
|
||||
// Simply update changes
|
||||
Editor.Prefabs.ApplyAll(_viewport.Instance);
|
||||
@@ -379,7 +380,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
finally
|
||||
{
|
||||
Editor.Scene.OnSaveEnd(_viewport);
|
||||
Editor.Scene.OnSaveEnd(_viewport._uiParentLink);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Options;
|
||||
@@ -194,133 +195,14 @@ namespace FlaxEditor.Windows
|
||||
public bool Active;
|
||||
}
|
||||
|
||||
private class GameRoot : ContainerControl
|
||||
/// <summary>
|
||||
/// Root control for game UI preview in Editor. Supports basic UI editing via <see cref="UIEditorRoot"/>.
|
||||
/// </summary>
|
||||
private class GameRoot : UIEditorRoot
|
||||
{
|
||||
public bool EnableEvents => !Time.GamePaused;
|
||||
|
||||
public override bool RayCast(ref Float2 location, out Control hit)
|
||||
{
|
||||
return RayCastChildren(ref location, out hit);
|
||||
}
|
||||
|
||||
public override bool ContainsPoint(ref Float2 location, bool precise = false)
|
||||
{
|
||||
if (precise)
|
||||
return false;
|
||||
return base.ContainsPoint(ref location, precise);
|
||||
}
|
||||
|
||||
public override bool OnCharInput(char c)
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return false;
|
||||
|
||||
return base.OnCharInput(c);
|
||||
}
|
||||
|
||||
public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return DragDropEffect.None;
|
||||
|
||||
return base.OnDragDrop(ref location, data);
|
||||
}
|
||||
|
||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return DragDropEffect.None;
|
||||
|
||||
return base.OnDragEnter(ref location, data);
|
||||
}
|
||||
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return;
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return DragDropEffect.None;
|
||||
|
||||
return base.OnDragMove(ref location, data);
|
||||
}
|
||||
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return false;
|
||||
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
|
||||
public override void OnKeyUp(KeyboardKeys key)
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return;
|
||||
|
||||
base.OnKeyUp(key);
|
||||
}
|
||||
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return false;
|
||||
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return false;
|
||||
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
public override void OnMouseEnter(Float2 location)
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return;
|
||||
|
||||
base.OnMouseEnter(location);
|
||||
}
|
||||
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return;
|
||||
|
||||
base.OnMouseLeave();
|
||||
}
|
||||
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return;
|
||||
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return false;
|
||||
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
|
||||
public override bool OnMouseWheel(Float2 location, float delta)
|
||||
{
|
||||
if (!EnableEvents)
|
||||
return false;
|
||||
|
||||
return base.OnMouseWheel(location, delta);
|
||||
}
|
||||
public override bool EnableInputs => !Time.GamePaused;
|
||||
public override bool EnableSelecting => !Editor.IsPlayMode || Time.GamePaused;
|
||||
public override TransformGizmo TransformGizmo => Editor.Instance.MainTransformGizmo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -348,13 +230,9 @@ namespace FlaxEditor.Windows
|
||||
// Override the game GUI root
|
||||
_guiRoot = new GameRoot
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
//Visible = false,
|
||||
AutoFocus = false,
|
||||
Parent = _viewport
|
||||
};
|
||||
RootControl.GameRoot = _guiRoot;
|
||||
RootControl.GameRoot = _guiRoot.UIRoot;
|
||||
|
||||
SizeChanged += control => { ResizeViewport(); };
|
||||
|
||||
@@ -916,35 +794,6 @@ namespace FlaxEditor.Windows
|
||||
Render2D.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
|
||||
}
|
||||
|
||||
// Selected UI controls outline
|
||||
bool drawAnySelectedControl = false;
|
||||
// TODO: optimize this (eg. cache list of selected UIControl's when selection gets changed)
|
||||
var selection = Editor.SceneEditing.Selection;
|
||||
for (var i = 0; i < selection.Count; i++)
|
||||
{
|
||||
if (selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null && controlActor.Control.VisibleInHierarchy && controlActor.Control.RootWindow != null)
|
||||
{
|
||||
if (!drawAnySelectedControl)
|
||||
{
|
||||
drawAnySelectedControl = true;
|
||||
Render2D.PushTransform(ref _viewport._cachedTransform);
|
||||
}
|
||||
var options = Editor.Options.Options.Visual;
|
||||
var control = controlActor.Control;
|
||||
var bounds = control.EditorBounds;
|
||||
var p1 = control.PointToParent(_viewport, bounds.UpperLeft);
|
||||
var p2 = control.PointToParent(_viewport, bounds.UpperRight);
|
||||
var p3 = control.PointToParent(_viewport, bounds.BottomLeft);
|
||||
var p4 = control.PointToParent(_viewport, bounds.BottomRight);
|
||||
var min = Float2.Min(Float2.Min(p1, p2), Float2.Min(p3, p4));
|
||||
var max = Float2.Max(Float2.Max(p1, p2), Float2.Max(p3, p4));
|
||||
bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero));
|
||||
Render2D.DrawRectangle(bounds, options.SelectionOutlineColor0, options.UISelectionOutlineSize);
|
||||
}
|
||||
}
|
||||
if (drawAnySelectedControl)
|
||||
Render2D.PopTransform();
|
||||
|
||||
// Play mode hints and overlay
|
||||
if (Editor.StateMachine.IsPlayMode)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.SceneGraph;
|
||||
@@ -258,10 +257,9 @@ namespace FlaxEditor.Windows
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent control.</param>
|
||||
/// <param name="location">The location (within a given control).</param>
|
||||
private void ShowContextMenu(Control parent, Float2 location)
|
||||
internal void ShowContextMenu(Control parent, Float2 location)
|
||||
{
|
||||
var contextMenu = CreateContextMenu();
|
||||
|
||||
contextMenu.Show(parent, location);
|
||||
}
|
||||
|
||||
|
||||
@@ -383,7 +383,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets the shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point.
|
||||
/// </summary>
|
||||
[DefaultValue(0.0f)]
|
||||
[DefaultValue(typeof(Float2), "0,0")]
|
||||
[ExpandGroups, EditorDisplay("Transform"), EditorOrder(1040), Tooltip("The shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point.")]
|
||||
public Float2 Shear
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user