Merge remote-tracking branch 'origin/master' into 1.11
# Conflicts: # Content/Editor/DebugMaterials/DDGIDebugProbes.flax # Source/Engine/Scripting/Scripting.cpp
This commit is contained in:
@@ -215,8 +215,8 @@ namespace FlaxEditor.GUI.Docking
|
||||
switch (state)
|
||||
{
|
||||
case DockState.DockFill:
|
||||
result.Location.Y += DockPanel.DefaultHeaderHeight;
|
||||
result.Size.Y -= DockPanel.DefaultHeaderHeight;
|
||||
result.Location.Y += Editor.Instance.Options.Options.Interface.TabHeight;
|
||||
result.Size.Y -= Editor.Instance.Options.Options.Interface.TabHeight;
|
||||
break;
|
||||
case DockState.DockTop:
|
||||
result.Size.Y *= DockPanel.DefaultSplitterValue;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
@@ -13,12 +14,16 @@ namespace FlaxEditor.GUI.Docking
|
||||
public class DockPanelProxy : ContainerControl
|
||||
{
|
||||
private DockPanel _panel;
|
||||
private InterfaceOptions.TabCloseButtonVisibility closeButtonVisibility;
|
||||
private double _dragEnterTime = -1;
|
||||
#if PLATFORM_WINDOWS
|
||||
private const bool HideTabForSingleTab = true;
|
||||
#else
|
||||
private const bool HideTabForSingleTab = false;
|
||||
#endif
|
||||
private float _tabHeight = Editor.Instance.Options.Options.Interface.TabHeight;
|
||||
private bool _useMinimumTabWidth = Editor.Instance.Options.Options.Interface.UseMinimumTabWidth;
|
||||
private float _minimumTabWidth = Editor.Instance.Options.Options.Interface.MinimumTabWidth;
|
||||
#if PLATFORM_WINDOWS
|
||||
private readonly bool _hideTabForSingleTab = Editor.Instance.Options.Options.Interface.HideSingleTabWindowTabBars;
|
||||
#else
|
||||
private readonly bool _hideTabForSingleTab = false;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The is mouse down flag (left button).
|
||||
@@ -55,8 +60,8 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// </summary>
|
||||
public DockWindow StartDragAsyncWindow;
|
||||
|
||||
private Rectangle HeaderRectangle => new Rectangle(0, 0, Width, DockPanel.DefaultHeaderHeight);
|
||||
private bool IsSingleFloatingWindow => HideTabForSingleTab && _panel.TabsCount == 1 && _panel.IsFloating && _panel.ChildPanelsCount == 0;
|
||||
private Rectangle HeaderRectangle => new Rectangle(0, 0, Width, _tabHeight);
|
||||
private bool IsSingleFloatingWindow => _hideTabForSingleTab && _panel.TabsCount == 1 && _panel.IsFloating && _panel.ChildPanelsCount == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DockPanelProxy"/> class.
|
||||
@@ -68,6 +73,14 @@ namespace FlaxEditor.GUI.Docking
|
||||
_panel = panel;
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
|
||||
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||
OnEditorOptionsChanged(Editor.Instance.Options.Options);
|
||||
}
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
closeButtonVisibility = options.Interface.ShowTabCloseButton;
|
||||
}
|
||||
|
||||
private DockWindow GetTabAtPos(Float2 position, out bool closeButton)
|
||||
@@ -78,11 +91,11 @@ namespace FlaxEditor.GUI.Docking
|
||||
var tabsCount = _panel.TabsCount;
|
||||
if (tabsCount == 1)
|
||||
{
|
||||
var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
if (HeaderRectangle.Contains(position))
|
||||
{
|
||||
closeButton = crossRect.Contains(position);
|
||||
result = _panel.GetTab(0);
|
||||
closeButton = crossRect.Contains(position) && IsCloseButtonVisible(result, closeButtonVisibility);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -91,15 +104,17 @@ namespace FlaxEditor.GUI.Docking
|
||||
for (int i = 0; i < tabsCount; i++)
|
||||
{
|
||||
var tab = _panel.GetTab(i);
|
||||
var titleSize = tab.TitleSize;
|
||||
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 tabRect = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight);
|
||||
float width = CalculateTabWidth(tab, closeButtonVisibility);
|
||||
|
||||
if (_useMinimumTabWidth && width < _minimumTabWidth)
|
||||
width = _minimumTabWidth;
|
||||
|
||||
var tabRect = new Rectangle(x, 0, width, HeaderRectangle.Height);
|
||||
var isMouseOver = tabRect.Contains(position);
|
||||
if (isMouseOver)
|
||||
{
|
||||
var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
closeButton = crossRect.Contains(position);
|
||||
var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
closeButton = crossRect.Contains(position) && IsCloseButtonVisible(tab, closeButtonVisibility);
|
||||
result = tab;
|
||||
break;
|
||||
}
|
||||
@@ -110,6 +125,24 @@ namespace FlaxEditor.GUI.Docking
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool IsCloseButtonVisible(DockWindow win, InterfaceOptions.TabCloseButtonVisibility visibilityMode)
|
||||
{
|
||||
return visibilityMode != InterfaceOptions.TabCloseButtonVisibility.Never &&
|
||||
(visibilityMode == InterfaceOptions.TabCloseButtonVisibility.Always ||
|
||||
(visibilityMode == InterfaceOptions.TabCloseButtonVisibility.SelectedTab && _panel.SelectedTab == win));
|
||||
}
|
||||
|
||||
private float CalculateTabWidth(DockWindow win, InterfaceOptions.TabCloseButtonVisibility visibilityMode)
|
||||
{
|
||||
var iconWidth = win.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0;
|
||||
var width = win.TitleSize.X + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth;
|
||||
|
||||
if (IsCloseButtonVisible(win, visibilityMode))
|
||||
width += 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultButtonsSize;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
private void GetTabRect(DockWindow win, out Rectangle bounds)
|
||||
{
|
||||
FlaxEngine.Assertions.Assert.IsTrue(_panel.ContainsTab(win));
|
||||
@@ -127,10 +160,10 @@ namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
var tab = _panel.GetTab(i);
|
||||
var titleSize = tab.TitleSize;
|
||||
float width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin;
|
||||
float width = CalculateTabWidth(tab, closeButtonVisibility);
|
||||
if (tab == win)
|
||||
{
|
||||
bounds = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight);
|
||||
bounds = new Rectangle(x, 0, width, HeaderRectangle.Height);
|
||||
return;
|
||||
}
|
||||
x += width;
|
||||
@@ -210,7 +243,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
Render2D.DrawSprite(
|
||||
tab.Icon,
|
||||
new Rectangle(DockPanel.DefaultLeftTextMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize),
|
||||
new Rectangle(DockPanel.DefaultLeftTextMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize),
|
||||
style.Foreground);
|
||||
|
||||
}
|
||||
@@ -219,17 +252,20 @@ namespace FlaxEditor.GUI.Docking
|
||||
Render2D.DrawText(
|
||||
style.FontMedium,
|
||||
tab.Title,
|
||||
new Rectangle(DockPanel.DefaultLeftTextMargin + iconWidth, 0, Width - DockPanel.DefaultLeftTextMargin - DockPanel.DefaultButtonsSize - 2 * DockPanel.DefaultButtonsMargin, DockPanel.DefaultHeaderHeight),
|
||||
new Rectangle(DockPanel.DefaultLeftTextMargin + iconWidth, 0, Width - DockPanel.DefaultLeftTextMargin - DockPanel.DefaultButtonsSize - 2 * DockPanel.DefaultButtonsMargin, HeaderRectangle.Height),
|
||||
style.Foreground,
|
||||
TextAlignment.Near,
|
||||
TextAlignment.Center);
|
||||
|
||||
// Draw cross
|
||||
var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
bool isMouseOverCross = isMouseOver && crossRect.Contains(MousePosition);
|
||||
if (isMouseOverCross)
|
||||
Render2D.FillRectangle(crossRect, (containsFocus ? style.BackgroundSelected : style.LightBackground) * 1.3f);
|
||||
Render2D.DrawSprite(style.Cross, crossRect, isMouseOverCross ? style.Foreground : style.ForegroundGrey);
|
||||
if (IsCloseButtonVisible(tab, closeButtonVisibility))
|
||||
{
|
||||
// Draw cross
|
||||
var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
bool isMouseOverCross = isMouseOver && crossRect.Contains(MousePosition);
|
||||
if (isMouseOverCross)
|
||||
Render2D.FillRectangle(crossRect, (containsFocus ? style.BackgroundSelected : style.LightBackground) * 1.3f);
|
||||
Render2D.DrawSprite(style.Cross, crossRect, isMouseOverCross ? style.Foreground : style.ForegroundGrey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -243,10 +279,14 @@ namespace FlaxEditor.GUI.Docking
|
||||
// Cache data
|
||||
var tab = _panel.GetTab(i);
|
||||
var tabColor = Color.Black;
|
||||
var titleSize = tab.TitleSize;
|
||||
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 tabRect = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight);
|
||||
|
||||
float width = CalculateTabWidth(tab, closeButtonVisibility);
|
||||
|
||||
if (_useMinimumTabWidth && width < _minimumTabWidth)
|
||||
width = _minimumTabWidth;
|
||||
|
||||
var tabRect = new Rectangle(x, 0, width, headerRect.Height);
|
||||
var isMouseOver = tabRect.Contains(MousePosition);
|
||||
var isSelected = _panel.SelectedTab == tab;
|
||||
|
||||
@@ -273,7 +313,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
Render2D.DrawSprite(
|
||||
tab.Icon,
|
||||
new Rectangle(x + DockPanel.DefaultLeftTextMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize),
|
||||
new Rectangle(x + DockPanel.DefaultLeftTextMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize),
|
||||
style.Foreground);
|
||||
|
||||
}
|
||||
@@ -282,27 +322,27 @@ namespace FlaxEditor.GUI.Docking
|
||||
Render2D.DrawText(
|
||||
style.FontMedium,
|
||||
tab.Title,
|
||||
new Rectangle(x + DockPanel.DefaultLeftTextMargin + iconWidth, 0, 10000, DockPanel.DefaultHeaderHeight),
|
||||
new Rectangle(x + DockPanel.DefaultLeftTextMargin + iconWidth, 0, 10000, HeaderRectangle.Height),
|
||||
style.Foreground,
|
||||
TextAlignment.Near,
|
||||
TextAlignment.Center);
|
||||
|
||||
// Draw cross
|
||||
if (isSelected || isMouseOver)
|
||||
if (IsCloseButtonVisible(tab, closeButtonVisibility))
|
||||
{
|
||||
var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
bool isMouseOverCross = isMouseOver && crossRect.Contains(MousePosition);
|
||||
if (isMouseOverCross)
|
||||
Render2D.FillRectangle(crossRect, tabColor * 1.3f);
|
||||
Render2D.DrawSprite(style.Cross, crossRect, isMouseOverCross ? style.Foreground : style.ForegroundGrey);
|
||||
}
|
||||
|
||||
// Move
|
||||
// Set the start position for the next tab
|
||||
x += width;
|
||||
}
|
||||
|
||||
// Draw selected tab strip
|
||||
Render2D.FillRectangle(new Rectangle(0, DockPanel.DefaultHeaderHeight - 2, Width, 2), containsFocus ? style.BackgroundSelected : style.BackgroundNormal);
|
||||
Render2D.FillRectangle(new Rectangle(0, HeaderRectangle.Height - 2, Width, 2), containsFocus ? style.BackgroundSelected : style.BackgroundNormal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,7 +560,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (IsSingleFloatingWindow)
|
||||
rect = new Rectangle(0, 0, Width, Height);
|
||||
else
|
||||
rect = new Rectangle(0, DockPanel.DefaultHeaderHeight, Width, Height - DockPanel.DefaultHeaderHeight);
|
||||
rect = new Rectangle(0, HeaderRectangle.Height, Width, Height - HeaderRectangle.Height);
|
||||
}
|
||||
|
||||
private DragDropEffect TrySelectTabUnderLocation(ref Float2 location)
|
||||
|
||||
@@ -514,20 +514,15 @@ namespace FlaxEditor.GUI
|
||||
var items = ItemsPanel.Children;
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (items[i] is Item item && item.Visible)
|
||||
var currentItem = items[i];
|
||||
if (currentItem is Item item && item.Visible)
|
||||
result.Add(item);
|
||||
}
|
||||
if (_categoryPanels != null)
|
||||
{
|
||||
for (int i = 0; i < _categoryPanels.Count; i++)
|
||||
else if (currentItem is DropPanel category && (!ignoreFoldedCategories || !category.IsClosed) && currentItem.Visible)
|
||||
{
|
||||
var category = _categoryPanels[i];
|
||||
if (!category.Visible || (ignoreFoldedCategories && category is DropPanel panel && panel.IsClosed))
|
||||
continue;
|
||||
for (int j = 0; j < category.Children.Count; j++)
|
||||
{
|
||||
if (category.Children[j] is Item item2 && item2.Visible)
|
||||
result.Add(item2);
|
||||
if (category.Children[j] is Item categoryItem && categoryItem.Visible)
|
||||
result.Add(categoryItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -591,10 +586,6 @@ namespace FlaxEditor.GUI
|
||||
var items = GetVisibleItems(!controlDown);
|
||||
var focusedIndex = items.IndexOf(focusedItem);
|
||||
|
||||
// If the user hasn't selected anything yet and is holding control, focus first folded item
|
||||
if (focusedIndex == -1 && controlDown)
|
||||
focusedIndex = GetVisibleItems(true).Count - 1;
|
||||
|
||||
int delta = key == KeyboardKeys.ArrowDown ? -1 : 1;
|
||||
int nextIndex = Mathf.Wrap(focusedIndex - delta, 0, items.Count - 1);
|
||||
var nextItem = items[nextIndex];
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="FlaxEngine.GUI.Panel" />
|
||||
public class NavigationBar : Panel
|
||||
{
|
||||
private float _toolstripHeight = 0;
|
||||
private Margin _toolstripMargin;
|
||||
|
||||
/// <summary>
|
||||
/// The default buttons margin.
|
||||
/// </summary>
|
||||
@@ -50,9 +53,42 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
if (toolstrip == null)
|
||||
return;
|
||||
|
||||
if (_toolstripHeight <= 0.0f)
|
||||
{
|
||||
// Cache initial toolstrip state
|
||||
_toolstripHeight = toolstrip.Height;
|
||||
_toolstripMargin = toolstrip.ItemsMargin;
|
||||
}
|
||||
|
||||
// Control toolstrip bottom margin to prevent navigation bar scroll going over the buttons
|
||||
var toolstripLocked = toolstrip.IsLayoutLocked;
|
||||
toolstrip.IsLayoutLocked = true;
|
||||
var toolstripHeight = _toolstripHeight;
|
||||
var toolstripMargin = _toolstripMargin;
|
||||
if (HScrollBar.Visible)
|
||||
{
|
||||
float scrollMargin = 8;
|
||||
toolstripHeight += scrollMargin;
|
||||
toolstripMargin.Bottom += scrollMargin;
|
||||
}
|
||||
toolstrip.Height = toolstripHeight;
|
||||
toolstrip.IsLayoutLocked = toolstripLocked;
|
||||
toolstrip.ItemsMargin = toolstripMargin;
|
||||
|
||||
var lastToolstripButton = toolstrip.LastButton;
|
||||
var parentSize = Parent.Size;
|
||||
Bounds = new Rectangle(lastToolstripButton.Right + 8.0f, 0, parentSize.X - X - 8.0f, toolstrip.Height);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PerformLayout(bool force = false)
|
||||
{
|
||||
base.PerformLayout(force);
|
||||
|
||||
// Stretch excluding toolstrip margin to fill the space
|
||||
if (Parent is ToolStrip toolStrip)
|
||||
Height = toolStrip.Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,10 @@ namespace FlaxEditor.GUI
|
||||
/// <returns>Created popup.</returns>
|
||||
public static RenamePopup Show(Control control, Rectangle area, string value, bool isMultiline)
|
||||
{
|
||||
// hardcoded flushing layout for tree controls
|
||||
if (control is Tree.TreeNode treeNode && treeNode.ParentTree != null)
|
||||
treeNode.ParentTree.FlushPendingPerformLayout();
|
||||
|
||||
// Calculate the control size in the window space to handle scaled controls
|
||||
var upperLeft = control.PointToWindow(area.UpperLeft);
|
||||
var bottomRight = control.PointToWindow(area.BottomRight);
|
||||
|
||||
@@ -13,15 +13,7 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public class ToolStrip : ContainerControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The default margin vertically.
|
||||
/// </summary>
|
||||
public const int DefaultMarginV = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The default margin horizontally.
|
||||
/// </summary>
|
||||
public const int DefaultMarginH = 2;
|
||||
private Margin _itemsMargin;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when button gets clicked with the primary mouse button.
|
||||
@@ -66,10 +58,26 @@ namespace FlaxEditor.GUI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the space around items.
|
||||
/// </summary>
|
||||
public Margin ItemsMargin
|
||||
{
|
||||
get => _itemsMargin;
|
||||
set
|
||||
{
|
||||
if (_itemsMargin != value)
|
||||
{
|
||||
_itemsMargin = value;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height for the items.
|
||||
/// </summary>
|
||||
public float ItemsHeight => Height - 2 * DefaultMarginV;
|
||||
public float ItemsHeight => Height - _itemsMargin.Height;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ToolStrip"/> class.
|
||||
@@ -82,6 +90,7 @@ namespace FlaxEditor.GUI
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
Offsets = new Margin(0, 0, y, height * Editor.Instance.Options.Options.Interface.IconsScale);
|
||||
_itemsMargin = new Margin(2, 2, 1, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -161,7 +170,7 @@ namespace FlaxEditor.GUI
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
// Arrange controls
|
||||
float x = DefaultMarginH;
|
||||
float x = _itemsMargin.Left;
|
||||
float h = ItemsHeight;
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
@@ -169,8 +178,8 @@ namespace FlaxEditor.GUI
|
||||
if (c.Visible)
|
||||
{
|
||||
var w = c.Width;
|
||||
c.Bounds = new Rectangle(x, DefaultMarginV, w, h);
|
||||
x += w + DefaultMarginH;
|
||||
c.Bounds = new Rectangle(x, _itemsMargin.Top, w, h);
|
||||
x += w + _itemsMargin.Width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace FlaxEditor.GUI.Tree
|
||||
private Margin _margin;
|
||||
private bool _autoSize = true;
|
||||
private bool _deferLayoutUpdate = false;
|
||||
private TreeNode _lastSelectedNode;
|
||||
|
||||
/// <summary>
|
||||
/// The TreeNode that is being dragged over. This could have a value when not dragging.
|
||||
@@ -67,7 +68,7 @@ namespace FlaxEditor.GUI.Tree
|
||||
/// Gets the first selected node or null.
|
||||
/// </summary>
|
||||
public TreeNode SelectedNode => Selection.Count > 0 ? Selection[0] : null;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Allow nodes to Draw the root tree line.
|
||||
/// </summary>
|
||||
@@ -364,6 +365,19 @@ namespace FlaxEditor.GUI.Tree
|
||||
BulkSelectUpdateExpanded(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes any pending layout perming action that has been delayed until next update to optimize performance of the complex tree hierarchy.
|
||||
/// </summary>
|
||||
public void FlushPendingPerformLayout()
|
||||
{
|
||||
if (_deferLayoutUpdate)
|
||||
{
|
||||
base.PerformLayout();
|
||||
AfterDeferredLayout?.Invoke();
|
||||
_deferLayoutUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PerformLayout(bool force = false)
|
||||
{
|
||||
@@ -378,25 +392,31 @@ namespace FlaxEditor.GUI.Tree
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (_deferLayoutUpdate)
|
||||
{
|
||||
base.PerformLayout();
|
||||
AfterDeferredLayout?.Invoke();
|
||||
_deferLayoutUpdate = false;
|
||||
}
|
||||
FlushPendingPerformLayout();
|
||||
var window = Root;
|
||||
bool shiftDown = window.GetKey(KeyboardKeys.Shift);
|
||||
bool keyUpArrow = window.GetKey(KeyboardKeys.ArrowUp);
|
||||
bool keyDownArrow = window.GetKey(KeyboardKeys.ArrowDown);
|
||||
|
||||
var node = SelectedNode;
|
||||
// Use last selection for last selected node if sift is down
|
||||
if (Selection.Count < 2)
|
||||
_lastSelectedNode = null;
|
||||
else if (shiftDown)
|
||||
_lastSelectedNode ??= Selection[^1];
|
||||
|
||||
// Skip root to prevent blocking input
|
||||
if (_lastSelectedNode != null && _lastSelectedNode.IsRoot)
|
||||
_lastSelectedNode = null;
|
||||
|
||||
var node = _lastSelectedNode ?? SelectedNode;
|
||||
|
||||
// Check if has focus and if any node is focused and it isn't a root
|
||||
if (ContainsFocus && node != null && node.AutoFocus)
|
||||
{
|
||||
var window = Root;
|
||||
if (window.GetKeyDown(KeyboardKeys.ArrowUp) || window.GetKeyDown(KeyboardKeys.ArrowDown))
|
||||
_keyUpdateTime = KeyUpdateTimeout;
|
||||
if (_keyUpdateTime >= KeyUpdateTimeout && window is WindowRootControl windowRoot && windowRoot.Window.IsFocused)
|
||||
{
|
||||
bool keyUpArrow = window.GetKey(KeyboardKeys.ArrowUp);
|
||||
bool keyDownArrow = window.GetKey(KeyboardKeys.ArrowDown);
|
||||
|
||||
// Check if arrow flags are different
|
||||
if (keyDownArrow != keyUpArrow)
|
||||
{
|
||||
@@ -406,24 +426,38 @@ namespace FlaxEditor.GUI.Tree
|
||||
Assert.AreNotEqual(-1, myIndex);
|
||||
|
||||
// Up
|
||||
TreeNode toSelect = null;
|
||||
List<TreeNode> toSelect = new List<TreeNode>();
|
||||
if (shiftDown && _supportMultiSelect)
|
||||
{
|
||||
toSelect.AddRange(Selection);
|
||||
}
|
||||
if (keyUpArrow)
|
||||
{
|
||||
if (myIndex == 0)
|
||||
{
|
||||
// Select parent
|
||||
toSelect = parentNode;
|
||||
if (toSelect.Contains(parentNode))
|
||||
toSelect.Remove(node);
|
||||
else if (parentNode != null)
|
||||
toSelect.Add(parentNode);
|
||||
_lastSelectedNode = parentNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select previous parent child
|
||||
toSelect = nodeParent.GetChild(myIndex - 1) as TreeNode;
|
||||
var select = nodeParent.GetChild(myIndex - 1) as TreeNode;
|
||||
|
||||
// Select last child if is valid and expanded and has any children
|
||||
if (toSelect != null && toSelect.IsExpanded && toSelect.HasAnyVisibleChild)
|
||||
if (select != null && select.IsExpanded && select.HasAnyVisibleChild)
|
||||
{
|
||||
toSelect = toSelect.GetChild(toSelect.ChildrenCount - 1) as TreeNode;
|
||||
select = select.GetChild(select.ChildrenCount - 1) as TreeNode;
|
||||
}
|
||||
|
||||
if (select == null || toSelect.Contains(select))
|
||||
toSelect.Remove(node);
|
||||
else
|
||||
toSelect.Add(select);
|
||||
_lastSelectedNode = select;
|
||||
}
|
||||
}
|
||||
// Down
|
||||
@@ -432,32 +466,48 @@ namespace FlaxEditor.GUI.Tree
|
||||
if (node.IsExpanded && node.HasAnyVisibleChild)
|
||||
{
|
||||
// Select the first child
|
||||
toSelect = node.GetChild(0) as TreeNode;
|
||||
var select = node.GetChild(0) as TreeNode;
|
||||
if (select == null || toSelect.Contains(select))
|
||||
toSelect.Remove(node);
|
||||
else
|
||||
toSelect.Add(select);
|
||||
_lastSelectedNode = select;
|
||||
}
|
||||
else if (myIndex == nodeParent.ChildrenCount - 1)
|
||||
{
|
||||
// Select next node after parent
|
||||
while (parentNode != null && toSelect == null)
|
||||
TreeNode select = null;
|
||||
while (parentNode != null && select == null)
|
||||
{
|
||||
int parentIndex = parentNode.IndexInParent;
|
||||
if (parentIndex != -1 && parentIndex < parentNode.Parent.ChildrenCount - 1)
|
||||
{
|
||||
toSelect = parentNode.Parent.GetChild(parentIndex + 1) as TreeNode;
|
||||
select = parentNode.Parent.GetChild(parentIndex + 1) as TreeNode;
|
||||
}
|
||||
parentNode = parentNode.Parent as TreeNode;
|
||||
}
|
||||
if (select == null || toSelect.Contains(select))
|
||||
toSelect.Remove(node);
|
||||
else
|
||||
toSelect.Add(select);
|
||||
_lastSelectedNode = select;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select next parent child
|
||||
toSelect = nodeParent.GetChild(myIndex + 1) as TreeNode;
|
||||
var select = nodeParent.GetChild(myIndex + 1) as TreeNode;
|
||||
if (select == null || toSelect.Contains(select))
|
||||
toSelect.Remove(node);
|
||||
else
|
||||
toSelect.Add(select);
|
||||
_lastSelectedNode = select;
|
||||
}
|
||||
}
|
||||
if (toSelect != null && toSelect.AutoFocus)
|
||||
if (toSelect.Count > 0)
|
||||
{
|
||||
// Select
|
||||
Select(toSelect);
|
||||
toSelect.Focus();
|
||||
_lastSelectedNode?.Focus();
|
||||
}
|
||||
|
||||
// Reset time
|
||||
|
||||
Reference in New Issue
Block a user