_wayland toplevel drag WIP

This commit is contained in:
2025-01-07 17:57:32 +02:00
parent 4204e7f571
commit 75c44122c6
15 changed files with 4771 additions and 62 deletions

View File

@@ -21,21 +21,22 @@ namespace FlaxEditor.GUI.Docking
private DockState _toSet;
private DockPanel _toDock;
private bool _lateDragOffsetUpdate;
private float _lateDragStartTimer;
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
private DockHintWindow(FloatWindowDockPanel toMove)
private DockHintWindow(FloatWindowDockPanel toMove, bool lateDragStart)
{
_toMove = toMove;
_toSet = DockState.Float;
var window = toMove.Window.Window;
// Remove focus from drag target
_toMove.Focus();
_toMove.Defocus();
//_toMove.Focus();
//_toMove.Defocus();
// Focus window
window.Focus();
//window.Focus();
// Check if window is maximized and restore window.
if (window.IsMaximized)
@@ -48,43 +49,62 @@ namespace FlaxEditor.GUI.Docking
}
// Calculate dragging offset and move window to the destination position
var mouseScreenPosition = Platform.MousePosition;
var mouseClientPosition = Platform.MousePosition;
CalculateDragOffset(mouseClientPosition);
// If the _toMove window was not focused when initializing this window, the result vector only contains zeros
// and to prevent a failure, we need to perform an update for the drag offset at later time which will be done in the OnMouseMove event handler.
if (mouseScreenPosition != Float2.Zero)
CalculateDragOffset(mouseScreenPosition);
else
_lateDragOffsetUpdate = true;
//if (mouseScreenPosition != Float2.Zero)
// CalculateDragOffset(mouseScreenPosition);
//else
// _lateDragOffsetUpdate = true;
// Get initial size
_defaultWindowSize = window.Size;
// Init proxy window
Proxy.Init(ref _defaultWindowSize);
/*Proxy.Init(ref _defaultWindowSize);
// Bind events
*/
//FlaxEngine.Input.MouseMove += OnMouseMove;
FlaxEngine.Scripting.Update += OnUpdate;
_toMove.Window.Window.MouseUp += OnMouseUp;
/*
Proxy.Window.MouseUp += OnMouseUp;
Proxy.Window.MouseMove += OnMouseMove;
Proxy.Window.LostFocus += OnLostFocus;
_toMove.Window.Window.MouseUp += OnMouseUp; // Intercept the drag release mouse event from source window
*/
// Update window GUI
Proxy.Window.GUI.PerformLayout();
//Proxy.Window.GUI.PerformLayout();
// Update rectangles
UpdateRects();
// Enable hit window presentation
Proxy.Window.RenderingEnabled = true;
/*Proxy.Window.RenderingEnabled = true;
Proxy.Window.Show();
Proxy.Window.Focus();
Proxy.Window.Focus();*/
// Hide base window
window.Hide();
//window.Hide();
if (lateDragStart)
{
// The window needs some time to be fully ready for dragging
//_lateDragStartTimer = 1.5f;
window.StartDragging(_dragOffset);
}
else
window.StartDragging(_dragOffset);
// Start tracking mouse
Proxy.Window.StartTrackingMouse(false);
//Proxy.Window.StartTrackingMouse(false);
}
/// <summary>
@@ -93,7 +113,7 @@ namespace FlaxEditor.GUI.Docking
public void Dispose()
{
// End tracking mouse
Proxy.Window.EndTrackingMouse();
/*Proxy.Window.EndTrackingMouse();
// Disable rendering
Proxy.Window.RenderingEnabled = false;
@@ -106,11 +126,20 @@ namespace FlaxEditor.GUI.Docking
_toMove.Window.Window.MouseUp -= OnMouseUp;
// Hide the proxy
Proxy.Hide();
Proxy.Hide();*/
RemoveDockHints();
//FlaxEngine.Input.MouseMove -= OnMouseMove;
FlaxEngine.Scripting.Update -= OnUpdate;
if (_toMove?.Window?.Window)
_toMove.Window.Window.MouseUp -= OnMouseUp;
if (_toMove == null)
return;
_toMove.Window?.Window.StopDragging();
// Check if window won't be docked
if (_toSet == DockState.Float)
{
@@ -182,7 +211,7 @@ namespace FlaxEditor.GUI.Docking
if (toMove == null)
throw new ArgumentNullException();
return new DockHintWindow(toMove);
return new DockHintWindow(toMove, false);
}
/// <summary>
@@ -200,13 +229,13 @@ namespace FlaxEditor.GUI.Docking
// Move window to the mouse position (with some offset for caption bar)
var window = (WindowRootControl)toMove.Root;
var mouse = Platform.MousePosition;
window.Window.Position = mouse - new Float2(8, 8);
/*var mouse = Platform.MousePosition;
window.Window.Position = mouse - new Float2(8, 8);*/
// Get floating panel
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
return new DockHintWindow(floatingPanelToMove);
return new DockHintWindow(floatingPanelToMove, true);
}
/// <summary>
@@ -250,6 +279,47 @@ namespace FlaxEditor.GUI.Docking
Editor.Log($"_dragOffset: {_dragOffset}, mouse: {mouseScreenPosition}, basewinpos: {baseWinPos}");
}
DockHintControl _dockHintDown;
DockHintControl _dockHintUp;
DockHintControl _dockHintLeft;
DockHintControl _dockHintRight;
DockHintControl _dockHintCenter;
private void AddDockHints()
{
if (_toDock == null)
return;
_dockHintDown = AddHintControl(new Float2(0.5f, 1));
_dockHintUp = AddHintControl(new Float2(0.5f, 0));
_dockHintLeft = AddHintControl(new Float2(0, 0.5f));
_dockHintRight = AddHintControl(new Float2(1, 0.5f));
_dockHintCenter = AddHintControl(new Float2(0.5f, 0.5f));
DockHintControl AddHintControl(Float2 pivot)
{
DockHintControl hintControl = _toDock.AddChild<DockHintControl>();
hintControl.Size = new Float2(Proxy.HintWindowsSize);
hintControl.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
hintControl.Pivot = pivot;
hintControl.PivotRelative = true;
//.SetAnchorPreset(AnchorPresets.StretchAll, true);
return hintControl;
}
}
private void RemoveDockHints()
{
if (_toDock == null)
return;
_dockHintDown?.Parent.RemoveChild(_dockHintDown);
_dockHintUp?.Parent.RemoveChild(_dockHintUp);
_dockHintLeft?.Parent.RemoveChild(_dockHintLeft);
_dockHintRight?.Parent.RemoveChild(_dockHintRight);
_dockHintCenter?.Parent.RemoveChild(_dockHintCenter);
_dockHintDown = _dockHintUp = _dockHintLeft = _dockHintRight = _dockHintCenter = null;
}
private void UpdateRects()
{
// Cache mouse position
@@ -257,12 +327,26 @@ namespace FlaxEditor.GUI.Docking
// Check intersection with any dock panel
var uiMouse = _mouse;
_toDock = _toMove.MasterPanel.HitTest(ref uiMouse, _toMove);
var dockPanel = _toMove.MasterPanel.HitTest(ref uiMouse, _toMove);
if (dockPanel != _toDock)
{
//Editor.Log($"UpdateRects: {_mouse}, panel: {dockPanel?.RootWindow?.Window?.Title}");
_toDock?.RootWindow.Window.RemoveDockHints();
RemoveDockHints();
_toDock = dockPanel;
_toDock?.RootWindow.Window.CreateDockHints();
AddDockHints();
}
// Check dock state to use
bool showProxyHints = _toDock != null;
bool showBorderHints = showProxyHints;
bool showCenterHint = showProxyHints;
DockHintControl hoveredHintControl = null;
Float2 hoveredSizeOverride = Float2.Zero;
if (showProxyHints)
{
// If moved window has not only tabs but also child panels disable docking as tab
@@ -277,11 +361,12 @@ namespace FlaxEditor.GUI.Docking
_rectDock = _toDock.DockAreaBounds;
// Cache dock rectangles
var size = _rectDock.Size;
var offset = _rectDock.Location;
var size = _rectDock.Size / Platform.DpiScale;
var offset = _toDock.PointFromScreen(_rectDock.Location);
var borderMargin = 4.0f;
var hintWindowsSize = Proxy.HintWindowsSize * Platform.DpiScale;
var hintWindowsSize = Proxy.HintWindowsSize;
var hintWindowsSize2 = hintWindowsSize * 0.5f;
var hintPreviewSize = new Float2(Math.Max(Proxy.HintWindowsSize * 2, size.X * 0.5f), Math.Max(Proxy.HintWindowsSize * 2, size.Y * 0.5f));
var centerX = size.X * 0.5f;
var centerY = size.Y * 0.5f;
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
@@ -294,71 +379,150 @@ namespace FlaxEditor.GUI.Docking
DockState toSet = DockState.Float;
if (showBorderHints)
{
if (_rUpper.Contains(_mouse))
if (_rUpper.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockTop;
else if (_rBottom.Contains(_mouse))
hoveredHintControl = _dockHintUp;
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
}
else if (_rBottom.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockBottom;
else if (_rLeft.Contains(_mouse))
hoveredHintControl = _dockHintDown;
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
}
else if (_rLeft.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockLeft;
else if (_rRight.Contains(_mouse))
hoveredHintControl = _dockHintLeft;
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
}
else if (_rRight.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockRight;
hoveredHintControl = _dockHintRight;
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
}
}
if (showCenterHint && _rCenter.Contains(_mouse))
if (showCenterHint && _rCenter.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockFill;
_toSet = toSet;
hoveredHintControl = _dockHintCenter;
hoveredSizeOverride = new Float2(size.X, size.Y);
}
// Show proxy hint windows
Proxy.Down.Position = _rBottom.Location;
Proxy.Left.Position = _rLeft.Location;
Proxy.Right.Position = _rRight.Location;
Proxy.Up.Position = _rUpper.Location;
Proxy.Center.Position = _rCenter.Location;
if (toSet != DockState.Float)
Editor.Log($"docking: {toSet}");
_toSet = toSet;
}
else
{
_toSet = DockState.Float;
}
// Update proxy hint windows visibility
Proxy.Down.IsVisible = showProxyHints & showBorderHints;
Proxy.Left.IsVisible = showProxyHints & showBorderHints;
Proxy.Right.IsVisible = showProxyHints & showBorderHints;
Proxy.Up.IsVisible = showProxyHints & showBorderHints;
Proxy.Center.IsVisible = showProxyHints & showCenterHint;
// Calculate proxy/dock/window rectangles
if (_toDock == null)
{
// Floating window over nothing
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
//_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
if (hoveredHintControl != null)
hoveredHintControl.BackgroundColor = Color.Green;
}
else
{
if (hoveredHintControl != _dockHintDown)
{
_dockHintDown.Size = new Float2(Proxy.HintWindowsSize);
_dockHintDown.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
}
if (hoveredHintControl != _dockHintLeft)
{
_dockHintLeft.Size = new Float2(Proxy.HintWindowsSize);
_dockHintLeft.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
}
if (hoveredHintControl != _dockHintRight)
{
_dockHintRight.Size = new Float2(Proxy.HintWindowsSize);
_dockHintRight.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
}
if (hoveredHintControl != _dockHintUp)
{
_dockHintUp.Size = new Float2(Proxy.HintWindowsSize);
_dockHintUp.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
}
if (hoveredHintControl != _dockHintCenter)
{
_dockHintCenter.Size = new Float2(Proxy.HintWindowsSize);
_dockHintCenter.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
}
if (_toSet == DockState.Float)
{
// Floating window over dock panel
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
if (hoveredHintControl != null)
hoveredHintControl.BackgroundColor = Color.Red;
}
else
{
// Use only part of the dock panel to show hint
_rectWindow = CalculateDockRect(_toSet, ref _rectDock);
if (hoveredHintControl != null)
{
hoveredHintControl.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(1.0f);
hoveredHintControl.Size = hoveredSizeOverride;
}
}
}
if (showProxyHints)
{
if (hoveredHintControl != _dockHintDown)
_dockHintDown.Location = _rBottom.Location;
if (hoveredHintControl != _dockHintLeft)
_dockHintLeft.Location = _rLeft.Location;
if (hoveredHintControl != _dockHintRight)
_dockHintRight.Location = _rRight.Location;
if (hoveredHintControl != _dockHintUp)
_dockHintUp.Location = _rUpper.Location;
if (hoveredHintControl != _dockHintCenter)
_dockHintCenter.Location = _rCenter.Location;
// Update proxy hint windows visibility
_dockHintDown.Visible = showProxyHints & showBorderHints;
_dockHintLeft.Visible = showProxyHints & showBorderHints;
_dockHintRight.Visible = showProxyHints & showBorderHints;
_dockHintUp.Visible = showProxyHints & showBorderHints;
_dockHintCenter.Visible = showProxyHints & showCenterHint;
}
// Update proxy window
Proxy.Window.ClientBounds = _rectWindow;
//Proxy.Window.ClientBounds = _rectWindow;
}
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
{
Editor.Log("DockHintWindow.OnMouseUp");
if (button == MouseButton.Left)
{
Dispose();
}
}
private void OnMouseMove(ref Float2 mousePos)
private void OnUpdate()
{
if (_lateDragStartTimer > 0)
{
_lateDragStartTimer -= Time.UnscaledDeltaTime;
if (_lateDragStartTimer <= 0)
_toMove.Window.Window.StartDragging(_dragOffset);
}
var mousePos = Platform.MousePosition;
if (_mouse != mousePos)
{
OnMouseMove(mousePos);
}
}
private void OnMouseMove(Float2 mousePos)
{
// Recalculate the drag offset because the current mouse screen position was invalid when we initialized the window
if (_lateDragOffsetUpdate)
@@ -377,6 +541,15 @@ namespace FlaxEditor.GUI.Docking
{
Dispose();
}
public class DockHintControl : Control
{
public DockHintControl()
{
AnchorPreset = AnchorPresets.StretchAll;
Offsets = Margin.Zero;
}
}
/// <summary>
/// Contains helper proxy windows shared across docking panels. They are used to visualize docking window locations.

View File

@@ -200,13 +200,16 @@ namespace FlaxEditor.GUI.Docking
windowGUI.PerformLayout();
// Show
window.Show();
window.BringToFront();
window.Focus();
OnShow();
FlaxEngine.Scripting.InvokeOnUpdate(() =>
{
window.Show();
window.BringToFront();
window.Focus();
OnShow();
// Perform layout again
windowGUI.PerformLayout();
// Perform layout again
windowGUI.PerformLayout();
});
}
/// <summary>

View File

@@ -51,8 +51,9 @@ namespace FlaxEditor.GUI.Docking
if (_window == null)
return;
_window.Window.StartDragging(Float2.Zero);
// Create docking hint window
DockHintWindow.Create(this);
//DockHintWindow.Create(this);
}
/// <summary>

View File

@@ -75,7 +75,7 @@ namespace FlaxEditor.GUI.Docking
/// <summary>
/// Performs hit test over dock panel.
/// </summary>
/// <param name="position">Screen space position to test.</param>
/// <param name="position">Window space position to test.</param>
/// <param name="excluded">Floating window to omit during searching (and all docked to that one).</param>
/// <returns>Dock panel that has been hit or null if nothing found.</returns>
public DockPanel HitTest(ref Float2 position, FloatWindowDockPanel excluded)
@@ -87,13 +87,39 @@ namespace FlaxEditor.GUI.Docking
var win = FloatingPanels[i];
if (win.Visible && win != excluded)
{
var result = win.HitTest(ref position);
if (result != null)
return result;
if (win.Window.Window.IsFocused) // We can't use screen space position in some platforms, only check windows under the cursor
{
var result = win.HitTest(ref position);
if (result != null)
{
Editor.Log($"hit: {win.Window.Window.Title}");
result = result;
}
else
Editor.Log($"no hit: {win.Window.Window.Title}");
}
else
Editor.Log($"no focus: {win.Window.Window.Title}");
}
}
for (int i = 0; i < FloatingPanels.Count; i++)
{
var win = FloatingPanels[i];
if (win.Visible && win != excluded)
{
if (win.Window.Window.IsFocused) // We can't use screen space position in some platforms, only check windows under the cursor
{
var result = win.HitTest(ref position);
if (result != null)
return result;
}
}
}
// Base
if (!Root?.RootWindow.Window.IsFocused ?? false)
return null;
return base.HitTest(ref position);
}