// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. namespace FlaxEngine.GUI { /// /// GUI control that contains two child panels and the splitter between them. /// /// [HideInEditor] public class SplitPanel : ContainerControl { /// /// The splitter size (in pixels). /// public const int SplitterSize = 4; /// /// The splitter half size (in pixels). /// private const int SplitterSizeHalf = SplitterSize / 2; private Orientation _orientation; private float _splitterValue; private Rectangle _splitterRect; private bool _splitterClicked, _mouseOverSplitter; private bool _cursorChanged; /// /// The first panel (left or upper based on Orientation). /// public readonly Panel Panel1; /// /// The second panel. /// public readonly Panel Panel2; /// /// Gets or sets the panel orientation. /// /// /// The orientation. /// public Orientation Orientation { get => _orientation; set { if (_orientation != value) { _orientation = value; UpdateSplitRect(); PerformLayout(); } } } /// /// Gets or sets the splitter value (always in range [0; 1]). /// /// /// The splitter value (always in range [0; 1]). /// public float SplitterValue { get => _splitterValue; set { value = Mathf.Saturate(value); if (!Mathf.NearEqual(_splitterValue, value)) { // Set new value _splitterValue = value; // Calculate rectangle and update panels UpdateSplitRect(); PerformLayout(); } } } /// /// Initializes a new instance of the class. /// /// The orientation. /// The panel1 scroll bars. /// The panel2 scroll bars. public SplitPanel(Orientation orientation = Orientation.Horizontal, ScrollBars panel1Scroll = ScrollBars.Both, ScrollBars panel2Scroll = ScrollBars.Both) { AutoFocus = false; _orientation = orientation; _splitterValue = 0.5f; Panel1 = new Panel(panel1Scroll); Panel2 = new Panel(panel2Scroll); Panel1.Parent = this; Panel2.Parent = this; UpdateSplitRect(); } private void UpdateSplitRect() { if (_orientation == Orientation.Horizontal) { var split = Mathf.RoundToInt(_splitterValue * Width); _splitterRect = new Rectangle(Mathf.Clamp(split - SplitterSizeHalf, 0.0f, Width), 0, SplitterSize, Height); } else { var split = Mathf.RoundToInt(_splitterValue * Height); _splitterRect = new Rectangle(0, Mathf.Clamp(split - SplitterSizeHalf, 0.0f, Height), Width, SplitterSize); } } private void StartTracking() { // Start move _splitterClicked = true; // Start capturing mouse StartMouseCapture(); } private void EndTracking() { if (_splitterClicked) { // Clear flag _splitterClicked = false; // End capturing mouse EndMouseCapture(); } } /// public override void Draw() { base.Draw(); // Draw splitter var style = Style.Current; Render2D.FillRectangle(_splitterRect, _splitterClicked ? style.BackgroundSelected : _mouseOverSplitter ? style.BackgroundHighlighted : style.LightBackground); } /// public override void OnLostFocus() { EndTracking(); base.OnLostFocus(); } /// public override void OnMouseMove(Float2 location) { _mouseOverSplitter = _splitterRect.Contains(location); if (_splitterClicked) { SplitterValue = _orientation == Orientation.Horizontal ? location.X / Width : location.Y / Height; Cursor = _orientation == Orientation.Horizontal ? CursorType.SizeWE : CursorType.SizeNS; _cursorChanged = true; } else if (_mouseOverSplitter) { Cursor = _orientation == Orientation.Horizontal ? CursorType.SizeWE : CursorType.SizeNS; _cursorChanged = true; } else if (_cursorChanged) { Cursor = CursorType.Default; _cursorChanged = false; } base.OnMouseMove(location); } /// public override bool OnMouseDown(Float2 location, MouseButton button) { if (button == MouseButton.Left && _splitterRect.Contains(location)) { // Start moving splitter StartTracking(); Focus(); return false; } return base.OnMouseDown(location, button); } /// public override bool OnMouseUp(Float2 location, MouseButton button) { if (_splitterClicked) { EndTracking(); return true; } return base.OnMouseUp(location, button); } /// public override void OnMouseLeave() { // Clear flag _mouseOverSplitter = false; if (_cursorChanged) { Cursor = CursorType.Default; _cursorChanged = false; } base.OnMouseLeave(); } /// public override void OnEndMouseCapture() { EndTracking(); } /// protected override void OnSizeChanged() { base.OnSizeChanged(); UpdateSplitRect(); PerformLayout(); } /// protected override void PerformLayoutBeforeChildren() { base.PerformLayoutBeforeChildren(); if (_orientation == Orientation.Horizontal) { var split = Mathf.RoundToInt(_splitterValue * Width); Panel1.Bounds = new Rectangle(0, 0, split - SplitterSizeHalf, Height); Panel2.Bounds = new Rectangle(split + SplitterSizeHalf, 0, Width - split - SplitterSizeHalf, Height); } else { var split = Mathf.RoundToInt(_splitterValue * Height); Panel1.Bounds = new Rectangle(0, 0, Width, split - SplitterSizeHalf); Panel2.Bounds = new Rectangle(0, split + SplitterSizeHalf, Width, Height - split - SplitterSizeHalf); } } } }