Optimize UI in Editor

This commit is contained in:
Wojtek Figat
2021-11-27 13:07:09 +01:00
parent a9c56caf84
commit 74f813ed3e
11 changed files with 204 additions and 165 deletions

View File

@@ -42,7 +42,7 @@ namespace FlaxEditor.GUI.Docking
/// <summary> /// <summary>
/// The mouse position. /// The mouse position.
/// </summary> /// </summary>
public Vector2 MousePosition; public Vector2 MousePosition = Vector2.Minimum;
/// <summary> /// <summary>
/// The start drag asynchronous window. /// The start drag asynchronous window.
@@ -192,7 +192,7 @@ namespace FlaxEditor.GUI.Docking
var tab = _panel.GetTab(0); var tab = _panel.GetTab(0);
// Draw header // Draw header
bool isMouseOver = IsMouseOver && headerRect.Contains(MousePosition); bool isMouseOver = headerRect.Contains(MousePosition);
Render2D.FillRectangle(headerRect, containsFocus ? style.BackgroundSelected : isMouseOver ? style.BackgroundHighlighted : style.LightBackground); Render2D.FillRectangle(headerRect, containsFocus ? style.BackgroundSelected : isMouseOver ? style.BackgroundHighlighted : style.LightBackground);
float iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0; float iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0;
@@ -238,7 +238,7 @@ namespace FlaxEditor.GUI.Docking
var iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0; var iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0;
var width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth; var width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth;
var tabRect = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight); var tabRect = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight);
var isMouseOver = IsMouseOver && tabRect.Contains(MousePosition); var isMouseOver = tabRect.Contains(MousePosition);
var isSelected = _panel.SelectedTab == tab; var isSelected = _panel.SelectedTab == tab;
// Check if tab is selected // Check if tab is selected
@@ -294,11 +294,11 @@ namespace FlaxEditor.GUI.Docking
/// <inheritdoc /> /// <inheritdoc />
public override void OnLostFocus() public override void OnLostFocus()
{ {
// Clear
IsMouseLeftButtonDown = false; IsMouseLeftButtonDown = false;
IsMouseRightButtonDown = false; IsMouseRightButtonDown = false;
IsMouseMiddleButtonDown = false; IsMouseMiddleButtonDown = false;
MouseDownWindow = null; MouseDownWindow = null;
MousePosition = Vector2.Minimum;
base.OnLostFocus(); base.OnLostFocus();
} }
@@ -306,7 +306,6 @@ namespace FlaxEditor.GUI.Docking
/// <inheritdoc /> /// <inheritdoc />
public override void OnMouseEnter(Vector2 location) public override void OnMouseEnter(Vector2 location)
{ {
// Cache mouse
MousePosition = location; MousePosition = location;
base.OnMouseEnter(location); base.OnMouseEnter(location);
@@ -403,10 +402,7 @@ namespace FlaxEditor.GUI.Docking
/// <inheritdoc /> /// <inheritdoc />
public override void OnMouseMove(Vector2 location) public override void OnMouseMove(Vector2 location)
{ {
// Cache mouse
MousePosition = location; MousePosition = location;
// Check if mouse is down
if (IsMouseLeftButtonDown) if (IsMouseLeftButtonDown)
{ {
// Check if mouse is outside the header // Check if mouse is outside the header
@@ -453,10 +449,8 @@ namespace FlaxEditor.GUI.Docking
/// <inheritdoc /> /// <inheritdoc />
public override void OnMouseLeave() public override void OnMouseLeave()
{ {
// Check if mouse is down
if (IsMouseLeftButtonDown) if (IsMouseLeftButtonDown)
{ {
// Clear flag
IsMouseLeftButtonDown = false; IsMouseLeftButtonDown = false;
// Check tabs under mouse position // Check tabs under mouse position
@@ -466,6 +460,7 @@ namespace FlaxEditor.GUI.Docking
} }
IsMouseRightButtonDown = false; IsMouseRightButtonDown = false;
IsMouseMiddleButtonDown = false; IsMouseMiddleButtonDown = false;
MousePosition = Vector2.Minimum;
base.OnMouseLeave(); base.OnMouseLeave();
} }

View File

@@ -889,19 +889,16 @@ namespace FlaxEditor.GUI.Tree
/// <inheritdoc /> /// <inheritdoc />
public override void OnChildResized(Control control) public override void OnChildResized(Control control)
{ {
// Optimize if child is tree node that is not visible
if (!_opened && control is TreeNode)
return;
PerformLayout(); PerformLayout();
ParentTree.UpdateSize(); ParentTree.UpdateSize();
base.OnChildResized(control); base.OnChildResized(control);
} }
/// <inheritdoc />
public override void OnParentResized()
{
base.OnParentResized();
Width = Parent.Width;
}
/// <inheritdoc /> /// <inheritdoc />
public override DragDropEffect OnDragEnter(ref Vector2 location, DragData data) public override DragDropEffect OnDragEnter(ref Vector2 location, DragData data)
{ {
@@ -1018,56 +1015,82 @@ namespace FlaxEditor.GUI.Tree
/// <inheritdoc /> /// <inheritdoc />
public override void PerformLayout(bool force = false) public override void PerformLayout(bool force = false)
{ {
// Check if update is locked if (_isLayoutLocked && !force)
if (IsLayoutLocked && !force)
return; return;
// Update the nodes nesting level before the actual positioning bool wasLocked = _isLayoutLocked;
float xOffset = _xOffset + ChildrenIndent; if (!wasLocked)
for (int i = 0; i < _children.Count; i++) LockChildrenRecursive();
// Optimize layout logic if node is collapsed
if (_opened || _animationProgress < 1.0f)
{ {
if (_children[i] is TreeNode node && node.Visible) PerformLayoutBeforeChildren();
node._xOffset = xOffset; for (int i = 0; i < _children.Count; i++)
_children[i].PerformLayout(true);
PerformLayoutAfterChildren();
}
else
{
// TODO: perform layout for any non-TreeNode controls
_cachedHeight = _headerHeight;
_cachedTextColor = CacheTextColor();
Size = new Vector2(Parent?.Width ?? Width, _headerHeight);
} }
base.PerformLayout(force); if (!wasLocked)
UnlockChildrenRecursive();
}
/// <inheritdoc />
protected override void PerformLayoutBeforeChildren()
{
if (_opened)
{
// Update the nodes nesting level before the actual positioning
float xOffset = _xOffset + ChildrenIndent;
for (int i = 0; i < _children.Count; i++)
{
if (_children[i] is TreeNode node)
node._xOffset = xOffset;
}
}
base.PerformLayoutBeforeChildren();
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void PerformLayoutAfterChildren() protected override void PerformLayoutAfterChildren()
{ {
_cachedTextColor = CacheTextColor();
// Prepare
float y = _headerHeight; float y = _headerHeight;
float height = _headerHeight; float height = _headerHeight;
float xOffset = _xOffset + ChildrenIndent; float xOffset = _xOffset + ChildrenIndent;
y -= _cachedHeight * (_opened ? 1.0f - _animationProgress : _animationProgress);
// Arrange children // Skip full layout if it's fully collapsed
for (int i = 0; i < _children.Count; i++) if (_opened || _animationProgress < 1.0f)
{ {
if (_children[i] is TreeNode node && node.Visible) y -= _cachedHeight * (_opened ? 1.0f - _animationProgress : _animationProgress);
// Arrange children
for (int i = 0; i < _children.Count; i++)
{ {
node._xOffset = xOffset; if (_children[i] is TreeNode node && node.Visible)
node.Location = new Vector2(0, y); {
float nodeHeight = node.Height + DefaultNodeOffsetY; node._xOffset = xOffset;
y += nodeHeight; node.Location = new Vector2(0, y);
height += nodeHeight; float nodeHeight = node.Height + DefaultNodeOffsetY;
y += nodeHeight;
height += nodeHeight;
}
} }
} }
// Cache calculated height // Cache data
_cachedHeight = height; _cachedHeight = height;
_cachedTextColor = CacheTextColor();
// Force to be closed // Set bounds
if (_animationProgress >= 1.0f && !_opened) Size = new Vector2(Parent?.Width ?? Width, Mathf.Max(_headerHeight, y));
{
y = _headerHeight;
}
// Set height
Height = Mathf.Max(_headerHeight, y);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -486,7 +486,6 @@ namespace FlaxEditor.Modules
var node = SceneGraphFactory.BuildActorNode(actor); var node = SceneGraphFactory.BuildActorNode(actor);
if (node != null) if (node != null)
{ {
node.TreeNode.UnlockChildrenRecursive();
node.ParentNode = parentNode; node.ParentNode = parentNode;
} }
} }
@@ -544,13 +543,8 @@ namespace FlaxEditor.Modules
return; return;
// Get the new parent node (may be missing) // Get the new parent node (may be missing)
if (parentNode != null) node.ParentNode = parentNode;
{ if (parentNode == null)
// Change parent
node.TreeNode.UnlockChildrenRecursive();
node.ParentNode = parentNode;
}
else
{ {
// Check if actor is selected in editor // Check if actor is selected in editor
if (Editor.SceneEditing.Selection.Contains(node)) if (Editor.SceneEditing.Selection.Contains(node))

View File

@@ -14,7 +14,7 @@ namespace FlaxEditor.SceneGraph
/// It's part of the Scene Graph. /// It's part of the Scene Graph.
/// </summary> /// </summary>
/// <seealso cref="SceneGraphNode" /> /// <seealso cref="SceneGraphNode" />
/// <seealso cref="Actor" /> /// <seealso cref="FlaxEngine.Actor" />
[HideInEditor] [HideInEditor]
public class ActorNode : SceneGraphNode public class ActorNode : SceneGraphNode
{ {
@@ -228,6 +228,7 @@ namespace FlaxEditor.SceneGraph
set => _actor.Transform = value; set => _actor.Transform = value;
} }
#if false
/// <inheritdoc /> /// <inheritdoc />
public override SceneGraphNode ParentNode public override SceneGraphNode ParentNode
{ {
@@ -238,6 +239,7 @@ namespace FlaxEditor.SceneGraph
base.ParentNode = value; base.ParentNode = value;
} }
} }
#endif
/// <inheritdoc /> /// <inheritdoc />
public override object EditableObject => _actor; public override object EditableObject => _actor;
@@ -250,7 +252,7 @@ namespace FlaxEditor.SceneGraph
// Skip actors that should not be selected // Skip actors that should not be selected
if (hit != null && _actor != null && (_actor.HideFlags & HideFlags.DontSelect) == HideFlags.DontSelect) if (hit != null && _actor != null && (_actor.HideFlags & HideFlags.DontSelect) == HideFlags.DontSelect)
{ {
hit = ParentNode; hit = parentNode;
} }
return hit; return hit;
@@ -306,22 +308,43 @@ namespace FlaxEditor.SceneGraph
/// <inheritdoc /> /// <inheritdoc />
protected override void OnParentChanged() protected override void OnParentChanged()
{ {
// Update UI node connections
_treeNode.Parent = (ParentNode as ActorNode)?.TreeNode;
// Check if it's a new node and parent has been already ready
// (eg. we build new node for spawned actor and link it to the game)
if (_treeNode.Parent != null && !_treeNode.Parent.IsLayoutLocked)
{
_treeNode.IndexInParent = _actor.OrderInParent;
_treeNode.Parent.SortChildren();
// Update UI
_treeNode.IsLayoutLocked = false;
_treeNode.PerformLayout();
}
base.OnParentChanged(); base.OnParentChanged();
// Update UI (special case if actor is spawned and added to existing scene tree)
var parentTreeNode = (parentNode as ActorNode)?.TreeNode;
if (parentTreeNode != null && !parentTreeNode.IsLayoutLocked)
{
parentTreeNode.IsLayoutLocked = true;
_treeNode.Parent = parentTreeNode;
_treeNode.IndexInParent = _actor.OrderInParent;
parentTreeNode.IsLayoutLocked = false;
// Skip UI update if node won't be in a view
if (parentTreeNode.IsCollapsed)
{
TreeNode.UnlockChildrenRecursive();
}
else
{
// Try to perform layout at the level where it makes it the most performant (the least computations)
var tree = parentTreeNode.ParentTree;
if (tree != null)
{
if (tree.Parent is FlaxEngine.GUI.Panel treeParent)
treeParent.PerformLayout();
else
tree.PerformLayout();
}
else
{
parentTreeNode.PerformLayout();
}
}
}
else
{
_treeNode.Parent = parentTreeNode;
}
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -489,7 +489,7 @@ namespace Math
static bool NearEqual(float a, float b) static bool NearEqual(float a, float b)
{ {
// Check if the numbers are really close - needed when comparing numbers near zero // Check if the numbers are really close - needed when comparing numbers near zero
if (Abs(a) < ZeroTolerance) if (Abs(a - b) < ZeroTolerance)
return true; return true;
// Original from Bruce Dawson: http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ // Original from Bruce Dawson: http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

View File

@@ -1816,7 +1816,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Vector2 left, Vector2 right) public static bool operator ==(Vector2 left, Vector2 right)
{ {
return left.Equals(ref right); return Mathf.NearEqual(left.X, left.X) && Mathf.NearEqual(left.Y, right.Y);
} }
/// <summary> /// <summary>
@@ -1831,7 +1831,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Vector2 left, Vector2 right) public static bool operator !=(Vector2 left, Vector2 right)
{ {
return !left.Equals(ref right); return !Mathf.NearEqual(left.X, left.X) || !Mathf.NearEqual(left.Y, right.Y);
} }
/// <summary> /// <summary>
@@ -1953,7 +1953,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Vector2 other) public bool Equals(Vector2 other)
{ {
return Equals(ref other); return Mathf.NearEqual(X, X) && Mathf.NearEqual(other.Y, other.Y);
} }
/// <summary> /// <summary>
@@ -1965,11 +1965,9 @@ namespace FlaxEngine
/// </returns> /// </returns>
public override bool Equals(object value) public override bool Equals(object value)
{ {
if (!(value is Vector2)) if (!(value is Vector2 other))
return false; return false;
return Mathf.NearEqual(X, X) && Mathf.NearEqual(other.Y, other.Y);
var strongValue = (Vector2)value;
return Equals(ref strongValue);
} }
} }
} }

View File

@@ -2049,7 +2049,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Vector3 left, Vector3 right) public static bool operator ==(Vector3 left, Vector3 right)
{ {
return left.Equals(ref right); return Mathf.NearEqual(left.X, right.X) && Mathf.NearEqual(left.Y, right.Y) && Mathf.NearEqual(left.Z, right.Z);
} }
/// <summary> /// <summary>
@@ -2061,7 +2061,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Vector3 left, Vector3 right) public static bool operator !=(Vector3 left, Vector3 right)
{ {
return !left.Equals(ref right); return !Mathf.NearEqual(left.X, right.X) || !Mathf.NearEqual(left.Y, right.Y) || !Mathf.NearEqual(left.Z, right.Z);
} }
/// <summary> /// <summary>
@@ -2162,7 +2162,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Vector3 other) public bool Equals(Vector3 other)
{ {
return Equals(ref other); return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z);
} }
/// <summary> /// <summary>
@@ -2172,10 +2172,9 @@ namespace FlaxEngine
/// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.</returns>
public override bool Equals(object value) public override bool Equals(object value)
{ {
if (!(value is Vector3)) if (!(value is Vector3 other))
return false; return false;
var strongValue = (Vector3)value; return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z);
return Equals(ref strongValue);
} }
} }
} }

View File

@@ -1680,7 +1680,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Vector4 other) public bool Equals(Vector4 other)
{ {
return Equals(ref other); return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z) && Mathf.NearEqual(other.W, W);
} }
/// <summary> /// <summary>
@@ -1692,11 +1692,9 @@ namespace FlaxEngine
/// </returns> /// </returns>
public override bool Equals(object value) public override bool Equals(object value)
{ {
if (!(value is Vector4)) if (!(value is Vector4 other))
return false; return false;
return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z) && Mathf.NearEqual(other.W, W);
var strongValue = (Vector4)value;
return Equals(ref strongValue);
} }
} }
} }

View File

@@ -23,12 +23,21 @@ namespace FlaxEngine.GUI
[NoSerialize] [NoSerialize]
protected bool _containsFocus; protected bool _containsFocus;
/// <summary>
/// The layout locking flag.
/// </summary>
[NoSerialize]
protected bool _isLayoutLocked;
private bool _clipChildren = true;
private bool _cullChildren = true;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ContainerControl"/> class. /// Initializes a new instance of the <see cref="ContainerControl"/> class.
/// </summary> /// </summary>
public ContainerControl() public ContainerControl()
{ {
IsLayoutLocked = true; _isLayoutLocked = true;
} }
/// <summary> /// <summary>
@@ -37,7 +46,7 @@ namespace FlaxEngine.GUI
public ContainerControl(float x, float y, float width, float height) public ContainerControl(float x, float y, float width, float height)
: base(x, y, width, height) : base(x, y, width, height)
{ {
IsLayoutLocked = true; _isLayoutLocked = true;
} }
/// <summary> /// <summary>
@@ -46,14 +55,14 @@ namespace FlaxEngine.GUI
public ContainerControl(Vector2 location, Vector2 size) public ContainerControl(Vector2 location, Vector2 size)
: base(location, size) : base(location, size)
{ {
IsLayoutLocked = true; _isLayoutLocked = true;
} }
/// <inheritdoc /> /// <inheritdoc />
public ContainerControl(Rectangle bounds) public ContainerControl(Rectangle bounds)
: base(bounds) : base(bounds)
{ {
IsLayoutLocked = true; _isLayoutLocked = true;
} }
/// <summary> /// <summary>
@@ -80,30 +89,39 @@ namespace FlaxEngine.GUI
/// True if automatic updates for control layout are locked (useful when creating a lot of GUI control to prevent lags). /// True if automatic updates for control layout are locked (useful when creating a lot of GUI control to prevent lags).
/// </summary> /// </summary>
[HideInEditor, NoSerialize] [HideInEditor, NoSerialize]
public bool IsLayoutLocked { get; set; } public bool IsLayoutLocked
{
get => _isLayoutLocked;
set => _isLayoutLocked = value;
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether apply clipping mask on children during rendering. /// Gets or sets a value indicating whether apply clipping mask on children during rendering.
/// </summary> /// </summary>
[EditorOrder(530), Tooltip("If checked, control will apply clipping mask on children during rendering.")] [EditorOrder(530), Tooltip("If checked, control will apply clipping mask on children during rendering.")]
public bool ClipChildren { get; set; } = true; public bool ClipChildren
{
get => _clipChildren;
set => _clipChildren = value;
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether perform view culling on children during rendering. /// Gets or sets a value indicating whether perform view culling on children during rendering.
/// </summary> /// </summary>
[EditorOrder(540), Tooltip("If checked, control will perform view culling on children during rendering.")] [EditorOrder(540), Tooltip("If checked, control will perform view culling on children during rendering.")]
public bool CullChildren { get; set; } = true; public bool CullChildren
{
get => _cullChildren;
set => _cullChildren = value;
}
/// <summary> /// <summary>
/// Locks all child controls layout and itself. /// Locks all child controls layout and itself.
/// </summary> /// </summary>
[NoAnimate] [NoAnimate]
public virtual void LockChildrenRecursive() public void LockChildrenRecursive()
{ {
// Itself _isLayoutLocked = true;
IsLayoutLocked = true;
// Every child container control
for (int i = 0; i < _children.Count; i++) for (int i = 0; i < _children.Count; i++)
{ {
if (_children[i] is ContainerControl child) if (_children[i] is ContainerControl child)
@@ -115,12 +133,9 @@ namespace FlaxEngine.GUI
/// Unlocks all the child controls layout and itself. /// Unlocks all the child controls layout and itself.
/// </summary> /// </summary>
[NoAnimate] [NoAnimate]
public virtual void UnlockChildrenRecursive() public void UnlockChildrenRecursive()
{ {
// Itself _isLayoutLocked = false;
IsLayoutLocked = false;
// Every child container control
for (int i = 0; i < _children.Count; i++) for (int i = 0; i < _children.Count; i++)
{ {
if (_children[i] is ContainerControl child) if (_children[i] is ContainerControl child)
@@ -134,8 +149,8 @@ namespace FlaxEngine.GUI
[NoAnimate] [NoAnimate]
public virtual void RemoveChildren() public virtual void RemoveChildren()
{ {
bool wasLayoutLocked = IsLayoutLocked; bool wasLayoutLocked = _isLayoutLocked;
IsLayoutLocked = true; _isLayoutLocked = true;
// Delete children // Delete children
while (_children.Count > 0) while (_children.Count > 0)
@@ -143,7 +158,7 @@ namespace FlaxEngine.GUI
_children[0].Parent = null; _children[0].Parent = null;
} }
IsLayoutLocked = wasLayoutLocked; _isLayoutLocked = wasLayoutLocked;
PerformLayout(); PerformLayout();
} }
@@ -152,8 +167,8 @@ namespace FlaxEngine.GUI
/// </summary> /// </summary>
public virtual void DisposeChildren() public virtual void DisposeChildren()
{ {
bool wasLayoutLocked = IsLayoutLocked; bool wasLayoutLocked = _isLayoutLocked;
IsLayoutLocked = true; _isLayoutLocked = true;
// Delete children // Delete children
while (_children.Count > 0) while (_children.Count > 0)
@@ -161,7 +176,7 @@ namespace FlaxEngine.GUI
_children[0].Dispose(); _children[0].Dispose();
} }
IsLayoutLocked = wasLayoutLocked; _isLayoutLocked = wasLayoutLocked;
PerformLayout(); PerformLayout();
} }
@@ -253,7 +268,6 @@ namespace FlaxEngine.GUI
return; return;
_children.RemoveAt(oldIndex); _children.RemoveAt(oldIndex);
// Check if index is invalid
if (newIndex < 0 || newIndex >= _children.Count) if (newIndex < 0 || newIndex >= _children.Count)
{ {
// Append at the end // Append at the end
@@ -279,8 +293,6 @@ namespace FlaxEngine.GUI
for (int i = _children.Count - 1; i >= 0; i--) for (int i = _children.Count - 1; i >= 0; i--)
{ {
var child = _children[i]; var child = _children[i];
// Check collision
if (IntersectsChildContent(child, point, out var childLocation)) if (IntersectsChildContent(child, point, out var childLocation))
{ {
result = i; result = i;
@@ -499,10 +511,7 @@ namespace FlaxEngine.GUI
// Check if state has been changed // Check if state has been changed
if (result != _containsFocus) if (result != _containsFocus)
{ {
// Cache flag
_containsFocus = result; _containsFocus = result;
// Fire event
if (result) if (result)
{ {
OnStartContainsFocus(); OnStartContainsFocus();
@@ -563,24 +572,6 @@ namespace FlaxEngine.GUI
_children.Clear(); _children.Clear();
} }
/// <inheritdoc />
public override bool IsMouseOver
{
get
{
if (base.IsMouseOver)
return true;
for (int i = 0; i < _children.Count && _children.Count > 0; i++)
{
if (_children[i].IsMouseOver)
return true;
}
return false;
}
}
/// <inheritdoc /> /// <inheritdoc />
public override bool IsTouchOver public override bool IsTouchOver
{ {
@@ -619,7 +610,7 @@ namespace FlaxEngine.GUI
{ {
DrawSelf(); DrawSelf();
if (ClipChildren) if (_clipChildren)
{ {
GetDesireClientArea(out var clientArea); GetDesireClientArea(out var clientArea);
Render2D.PushClip(ref clientArea); Render2D.PushClip(ref clientArea);
@@ -646,7 +637,7 @@ namespace FlaxEngine.GUI
protected virtual void DrawChildren() protected virtual void DrawChildren()
{ {
// Draw all visible child controls // Draw all visible child controls
if (CullChildren) if (_cullChildren)
{ {
Render2D.PeekClip(out var globalClipping); Render2D.PeekClip(out var globalClipping);
Render2D.PeekTransform(out var globalTransform); Render2D.PeekTransform(out var globalTransform);
@@ -684,10 +675,10 @@ namespace FlaxEngine.GUI
/// <inheritdoc /> /// <inheritdoc />
public override void PerformLayout(bool force = false) public override void PerformLayout(bool force = false)
{ {
if (IsLayoutLocked && !force) if (_isLayoutLocked && !force)
return; return;
bool wasLocked = IsLayoutLocked; bool wasLocked = _isLayoutLocked;
if (!wasLocked) if (!wasLocked)
LockChildrenRecursive(); LockChildrenRecursive();
@@ -711,7 +702,6 @@ namespace FlaxEngine.GUI
var child = _children[i]; var child = _children[i];
if (child.Visible && child.Enabled) if (child.Visible && child.Enabled)
{ {
// Fire event
if (IntersectsChildContent(child, location, out var childLocation)) if (IntersectsChildContent(child, location, out var childLocation))
{ {
// Enter // Enter
@@ -732,7 +722,6 @@ namespace FlaxEngine.GUI
var child = _children[i]; var child = _children[i];
if (child.Visible && child.Enabled) if (child.Visible && child.Enabled)
{ {
// Fire events
if (IntersectsChildContent(child, location, out var childLocation)) if (IntersectsChildContent(child, location, out var childLocation))
{ {
if (child.IsMouseOver) if (child.IsMouseOver)
@@ -1035,7 +1024,6 @@ namespace FlaxEngine.GUI
var child = _children[i]; var child = _children[i];
if (child.Visible && child.Enabled) if (child.Visible && child.Enabled)
{ {
// Fire event
if (IntersectsChildContent(child, location, out var childLocation)) if (IntersectsChildContent(child, location, out var childLocation))
{ {
// Enter // Enter
@@ -1061,7 +1049,6 @@ namespace FlaxEngine.GUI
var child = _children[i]; var child = _children[i];
if (child.Visible && child.Enabled) if (child.Visible && child.Enabled)
{ {
// Fire events
if (IntersectsChildContent(child, location, out var childLocation)) if (IntersectsChildContent(child, location, out var childLocation))
{ {
if (child.IsDragOver) if (child.IsDragOver)
@@ -1120,7 +1107,6 @@ namespace FlaxEngine.GUI
var child = _children[i]; var child = _children[i];
if (child.Visible && child.Enabled) if (child.Visible && child.Enabled)
{ {
// Fire event
if (IntersectsChildContent(child, location, out var childLocation)) if (IntersectsChildContent(child, location, out var childLocation))
{ {
// Enter // Enter
@@ -1138,10 +1124,9 @@ namespace FlaxEngine.GUI
protected override void OnSizeChanged() protected override void OnSizeChanged()
{ {
// Lock updates to prevent additional layout calculations // Lock updates to prevent additional layout calculations
bool wasLayoutLocked = IsLayoutLocked; bool wasLayoutLocked = _isLayoutLocked;
IsLayoutLocked = true; _isLayoutLocked = true;
// Base
base.OnSizeChanged(); base.OnSizeChanged();
// Fire event // Fire event
@@ -1151,7 +1136,7 @@ namespace FlaxEngine.GUI
} }
// Restore state // Restore state
IsLayoutLocked = wasLayoutLocked; _isLayoutLocked = wasLayoutLocked;
// Arrange child controls // Arrange child controls
PerformLayout(); PerformLayout();

View File

@@ -150,7 +150,13 @@ namespace FlaxEngine.GUI
public Vector2 Location public Vector2 Location
{ {
get => _bounds.Location; get => _bounds.Location;
set => Bounds = new Rectangle(value, _bounds.Size); set
{
if (_bounds.Location.Equals(ref value))
return;
var bounds = new Rectangle(value, _bounds.Size);
SetBounds(ref bounds);
}
} }
/// <summary> /// <summary>
@@ -170,7 +176,13 @@ namespace FlaxEngine.GUI
public float Width public float Width
{ {
get => _bounds.Size.X; get => _bounds.Size.X;
set => Bounds = new Rectangle(_bounds.Location, value, _bounds.Size.Y); set
{
if (Mathf.NearEqual(_bounds.Size.X, value))
return;
var bounds = new Rectangle(_bounds.Location, value, _bounds.Size.Y);
SetBounds(ref bounds);
}
} }
/// <summary> /// <summary>
@@ -180,7 +192,13 @@ namespace FlaxEngine.GUI
public float Height public float Height
{ {
get => _bounds.Size.Y; get => _bounds.Size.Y;
set => Bounds = new Rectangle(_bounds.Location, _bounds.Size.X, value); set
{
if (Mathf.NearEqual(_bounds.Size.Y, value))
return;
var bounds = new Rectangle(_bounds.Location, _bounds.Size.X, value);
SetBounds(ref bounds);
}
} }
/// <summary> /// <summary>
@@ -190,7 +208,13 @@ namespace FlaxEngine.GUI
public Vector2 Size public Vector2 Size
{ {
get => _bounds.Size; get => _bounds.Size;
set => Bounds = new Rectangle(_bounds.Location, value); set
{
if (_bounds.Size.Equals(ref value))
return;
var bounds = new Rectangle(_bounds.Location, value);
SetBounds(ref bounds);
}
} }
/// <summary> /// <summary>
@@ -425,12 +449,12 @@ namespace FlaxEngine.GUI
UpdateTransform(); UpdateTransform();
// Handle location/size changes // Handle location/size changes
if (_bounds.Location != prevBounds.Location) if (!_bounds.Location.Equals(ref prevBounds.Location))
{ {
OnLocationChanged(); OnLocationChanged();
} }
if (_bounds.Size != prevBounds.Size) if (!_bounds.Size.Equals(ref prevBounds.Size))
{ {
OnSizeChanged(); OnSizeChanged();
} }

View File

@@ -177,15 +177,15 @@ namespace FlaxEngine.GUI
/// <inheritdoc /> /// <inheritdoc />
protected override void SetViewOffset(ref Vector2 value) protected override void SetViewOffset(ref Vector2 value)
{ {
bool wasLocked = IsLayoutLocked; bool wasLocked = _isLayoutLocked;
IsLayoutLocked = true; _isLayoutLocked = true;
if (HScrollBar != null) if (HScrollBar != null)
HScrollBar.Value = -value.X; HScrollBar.Value = -value.X;
if (VScrollBar != null) if (VScrollBar != null)
VScrollBar.Value = -value.Y; VScrollBar.Value = -value.Y;
IsLayoutLocked = wasLocked; _isLayoutLocked = wasLocked;
base.SetViewOffset(ref value); base.SetViewOffset(ref value);
} }
@@ -239,15 +239,15 @@ namespace FlaxEngine.GUI
/// <param name="fastScroll">True of scroll to the item quickly without smoothing.</param> /// <param name="fastScroll">True of scroll to the item quickly without smoothing.</param>
public void ScrollViewTo(Rectangle bounds, bool fastScroll = false) public void ScrollViewTo(Rectangle bounds, bool fastScroll = false)
{ {
bool wasLocked = IsLayoutLocked; bool wasLocked = _isLayoutLocked;
IsLayoutLocked = true; _isLayoutLocked = true;
if (HScrollBar != null && HScrollBar.Enabled) if (HScrollBar != null && HScrollBar.Enabled)
HScrollBar.ScrollViewTo(bounds.Left, bounds.Right, fastScroll); HScrollBar.ScrollViewTo(bounds.Left, bounds.Right, fastScroll);
if (VScrollBar != null && VScrollBar.Enabled) if (VScrollBar != null && VScrollBar.Enabled)
VScrollBar.ScrollViewTo(bounds.Top, bounds.Bottom, fastScroll); VScrollBar.ScrollViewTo(bounds.Top, bounds.Bottom, fastScroll);
IsLayoutLocked = wasLocked; _isLayoutLocked = wasLocked;
PerformLayout(); PerformLayout();
} }
@@ -406,14 +406,14 @@ namespace FlaxEngine.GUI
return; return;
_layoutUpdateLock++; _layoutUpdateLock++;
if (!IsLayoutLocked) if (!_isLayoutLocked)
{ {
_layoutChanged = false; _layoutChanged = false;
} }
base.PerformLayout(force); base.PerformLayout(force);
if (!IsLayoutLocked && _layoutChanged) if (!_isLayoutLocked && _layoutChanged)
{ {
_layoutChanged = false; _layoutChanged = false;
PerformLayout(true); PerformLayout(true);