271 lines
7.7 KiB
C#
271 lines
7.7 KiB
C#
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
|
|
|
using FlaxEditor.CustomEditors.Elements;
|
|
using FlaxEngine;
|
|
using FlaxEngine.GUI;
|
|
|
|
namespace FlaxEditor.CustomEditors.GUI
|
|
{
|
|
/// <summary>
|
|
/// <see cref="CustomEditor"/> properties list control.
|
|
/// </summary>
|
|
/// <seealso cref="FlaxEngine.GUI.PanelWithMargins" />
|
|
[HideInEditor]
|
|
public class PropertiesList : PanelWithMargins
|
|
{
|
|
// TODO: sync splitter for whole presenter
|
|
|
|
/// <summary>
|
|
/// The splitter size (in pixels).
|
|
/// </summary>
|
|
public const int SplitterSize = 2;
|
|
|
|
/// <summary>
|
|
/// The splitter margin (in pixels).
|
|
/// </summary>
|
|
public const int SplitterMargin = 4;
|
|
|
|
private const int SplitterSizeHalf = SplitterSize / 2;
|
|
|
|
private PropertiesListElement _element;
|
|
private float _splitterValue;
|
|
private Rectangle _splitterRect;
|
|
private bool _splitterClicked, _mouseOverSplitter;
|
|
private bool _cursorChanged;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the splitter value (always in range [0; 1]).
|
|
/// </summary>
|
|
/// <value>
|
|
/// The splitter value (always in range [0; 1]).
|
|
/// </value>
|
|
public float SplitterValue
|
|
{
|
|
get => _splitterValue;
|
|
set
|
|
{
|
|
value = Mathf.Clamp(value, 0.05f, 0.95f);
|
|
if (!Mathf.NearEqual(_splitterValue, value))
|
|
{
|
|
_splitterValue = value;
|
|
UpdateSplitRect();
|
|
PerformLayout(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the properties list element. It's a parent object for this control.
|
|
/// </summary>
|
|
public PropertiesListElement Element => _element;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PropertiesList"/> class.
|
|
/// </summary>
|
|
/// <param name="element">The element.</param>
|
|
public PropertiesList(PropertiesListElement element)
|
|
{
|
|
_element = element;
|
|
_splitterValue = 0.4f;
|
|
BottomMargin = TopMargin = RightMargin = SplitterMargin;
|
|
UpdateSplitRect();
|
|
}
|
|
|
|
private void UpdateSplitRect()
|
|
{
|
|
_splitterRect = new Rectangle(Mathf.Clamp(_splitterValue * Width - SplitterSizeHalf, 0.0f, Width), 0, SplitterSize, Height);
|
|
LeftMargin = _splitterValue * Width + SplitterMargin;
|
|
}
|
|
|
|
private void StartTracking()
|
|
{
|
|
// Start move
|
|
_splitterClicked = true;
|
|
|
|
// Start capturing mouse
|
|
StartMouseCapture();
|
|
}
|
|
|
|
private void EndTracking()
|
|
{
|
|
if (_splitterClicked)
|
|
{
|
|
// Clear flag
|
|
_splitterClicked = false;
|
|
|
|
// End capturing mouse
|
|
EndMouseCapture();
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void Draw()
|
|
{
|
|
base.Draw();
|
|
|
|
var style = Style.Current;
|
|
|
|
// Draw splitter
|
|
Render2D.FillRectangle(_splitterRect, _splitterClicked ? style.BackgroundSelected : _mouseOverSplitter ? style.BackgroundHighlighted : style.Background * 0.8f);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnLostFocus()
|
|
{
|
|
EndTracking();
|
|
|
|
base.OnLostFocus();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnMouseMove(Float2 location)
|
|
{
|
|
_mouseOverSplitter = _splitterRect.Contains(location);
|
|
|
|
if (_splitterClicked)
|
|
{
|
|
SplitterValue = location.X / Width;
|
|
Cursor = CursorType.SizeWE;
|
|
_cursorChanged = true;
|
|
}
|
|
else if (_mouseOverSplitter)
|
|
{
|
|
Cursor = CursorType.SizeWE;
|
|
_cursorChanged = true;
|
|
}
|
|
else if (_cursorChanged)
|
|
{
|
|
Cursor = CursorType.Default;
|
|
_cursorChanged = false;
|
|
}
|
|
|
|
base.OnMouseMove(location);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool OnMouseDown(Float2 location, MouseButton button)
|
|
{
|
|
if (button == MouseButton.Left)
|
|
{
|
|
if (_splitterRect.Contains(location))
|
|
{
|
|
// Start moving splitter
|
|
StartTracking();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return base.OnMouseDown(location, button);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
|
{
|
|
if (_splitterClicked)
|
|
{
|
|
EndTracking();
|
|
return true;
|
|
}
|
|
|
|
return base.OnMouseUp(location, button);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnMouseLeave()
|
|
{
|
|
// Clear flag
|
|
_mouseOverSplitter = false;
|
|
|
|
if (_cursorChanged)
|
|
{
|
|
Cursor = CursorType.Default;
|
|
_cursorChanged = false;
|
|
}
|
|
|
|
base.OnMouseLeave();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnEndMouseCapture()
|
|
{
|
|
// Clear flag
|
|
_splitterClicked = false;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void OnSizeChanged()
|
|
{
|
|
base.OnSizeChanged();
|
|
|
|
// Refresh
|
|
UpdateSplitRect();
|
|
PerformLayout(true);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void PerformLayoutBeforeChildren()
|
|
{
|
|
base.PerformLayoutBeforeChildren();
|
|
|
|
// Pre-set width of all controls
|
|
float w = Width - _margin.Width;
|
|
for (int i = 0; i < _children.Count; i++)
|
|
{
|
|
Control c = _children[i];
|
|
if (!(c is PropertyNameLabel))
|
|
{
|
|
c.Width = w;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void PerformLayoutAfterChildren()
|
|
{
|
|
// Sort controls from up to down into two columns: one for labels and one for the rest of the stuff
|
|
|
|
float y = _margin.Top;
|
|
float w = Width - _margin.Width;
|
|
for (int i = 0; i < _children.Count; i++)
|
|
{
|
|
Control c = _children[i];
|
|
if (!(c is PropertyNameLabel))
|
|
{
|
|
var h = c.Height;
|
|
c.Bounds = new Rectangle(_margin.Left, y + _spacing, w, h);
|
|
if (c.Visible)
|
|
y = c.Bottom;
|
|
}
|
|
}
|
|
y += _margin.Bottom;
|
|
|
|
float namesWidth = _splitterValue * Width;
|
|
int count = _element.Labels.Count;
|
|
float[] yStarts = new float[count + 1];
|
|
for (int i = 1; i < count; i++)
|
|
{
|
|
var label = _element.Labels[i];
|
|
|
|
if (label.FirstChildControlIndex < 0)
|
|
yStarts[i] = 0;
|
|
else if (_children.Count <= label.FirstChildControlIndex)
|
|
yStarts[i] = y;
|
|
else
|
|
yStarts[i] = _children[label.FirstChildControlIndex].Top;
|
|
}
|
|
yStarts[count] = y;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var label = _element.Labels[i];
|
|
|
|
var rect = new Rectangle(0, yStarts[i] + 1, namesWidth, yStarts[i + 1] - yStarts[i] - 2);
|
|
//label.Parent = this;
|
|
label.Bounds = rect;
|
|
}
|
|
|
|
if (_autoSize)
|
|
Height = y;
|
|
}
|
|
}
|
|
}
|