Merge branch 'GoaLitiuM-sdl_platform' into 1.12
This commit is contained in:
4
.github/workflows/build_linux.yml
vendored
4
.github/workflows/build_linux.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev libwayland-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
|
||||
- name: Build
|
||||
run: |
|
||||
./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"Major": 1,
|
||||
"Minor": 12,
|
||||
"Revision": 0,
|
||||
"Build": 6904
|
||||
"Build": 6905
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2026 Wojciech Figat. All rights reserved.",
|
||||
@@ -13,6 +13,7 @@
|
||||
"Configuration": {
|
||||
"UseCSharp": true,
|
||||
"UseLargeWorlds": false,
|
||||
"UseDotNet": true
|
||||
"UseDotNet": true,
|
||||
"UseSDL": true
|
||||
}
|
||||
}
|
||||
@@ -188,7 +188,7 @@
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_CASE_STATEMENT_ON_SAME_LINE/@EntryValue">ALWAYS</s:String>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_EMBEDDED_STATEMENT_ON_SAME_LINE/@EntryValue">NEVER</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_CASE_STATEMENT_STYLE/@EntryValue">ON_SINGLE_LINE</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace FlaxEditor.Content
|
||||
|
||||
if (data is DragDataFiles)
|
||||
return DragDropEffect.Copy;
|
||||
return _dragOverItems.Effect;
|
||||
return _dragOverItems?.Effect ?? DragDropEffect.None;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string TypeDescription => Path.EndsWith(".h") ? "C++ Header File" : "C++ Source Code";
|
||||
public override string TypeDescription => Path.EndsWith(".h") || Path.EndsWith(".hpp") ? "C++ Header File" : "C++ Source Code";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CPPScript128;
|
||||
|
||||
@@ -190,12 +190,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
foreach (var file in files)
|
||||
FindNewKeysCSharp(file, newKeys, allKeys);
|
||||
|
||||
// C++
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories);
|
||||
// C/C++
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.c", SearchOption.AllDirectories)).ToArray();
|
||||
filesCount += files.Length;
|
||||
foreach (var file in files)
|
||||
FindNewKeysCpp(file, newKeys, allKeys);
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories);
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.hpp", SearchOption.AllDirectories)).ToArray();
|
||||
filesCount += files.Length;
|
||||
foreach (var file in files)
|
||||
FindNewKeysCpp(file, newKeys, allKeys);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
#define USE_IS_FOREGROUND
|
||||
#else
|
||||
#endif
|
||||
#if PLATFORM_SDL
|
||||
#define USE_SDL_WORKAROUNDS
|
||||
#endif
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
@@ -130,7 +133,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the empty menu popup o na screen.
|
||||
/// Shows the empty menu popup on a screen.
|
||||
/// </summary>
|
||||
/// <param name="control">The target control.</param>
|
||||
/// <param name="area">The target control area to cover.</param>
|
||||
@@ -265,7 +268,9 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
desc.AllowMaximize = false;
|
||||
desc.AllowDragAndDrop = false;
|
||||
desc.IsTopmost = true;
|
||||
desc.IsRegularWindow = false;
|
||||
desc.Type = WindowType.Popup;
|
||||
desc.Parent = parentWin.Window;
|
||||
desc.Title = "ContextMenu";
|
||||
desc.HasSizingFrame = false;
|
||||
OnWindowCreating(ref desc);
|
||||
_window = Platform.CreateWindow(ref desc);
|
||||
@@ -275,6 +280,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
_window.LostFocus += OnWindowLostFocus;
|
||||
}
|
||||
|
||||
#if USE_IS_FOREGROUND && USE_SDL_WORKAROUNDS
|
||||
// The focus between popup and parent windows doesn't change, force hide the popup when clicked on parent
|
||||
parentWin.Window.MouseDown += OnWindowMouseDown;
|
||||
_window.Closed += () => parentWin.Window.MouseDown -= OnWindowMouseDown;
|
||||
#endif
|
||||
|
||||
// Attach to the window
|
||||
_parentCM = parent as ContextMenuBase;
|
||||
Parent = _window.GUI;
|
||||
@@ -449,6 +460,17 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_SDL_WORKAROUNDS
|
||||
private void OnWindowGotFocus()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnWindowMouseDown(ref Float2 mousePosition, MouseButton button, ref bool handled)
|
||||
{
|
||||
// The user clicked outside the popup window
|
||||
Hide();
|
||||
}
|
||||
#else
|
||||
private void OnWindowGotFocus()
|
||||
{
|
||||
var child = _childCM;
|
||||
@@ -462,6 +484,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnWindowLostFocus()
|
||||
{
|
||||
@@ -560,7 +583,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
// Let root context menu to check if none of the popup windows
|
||||
if (_parentCM == null && UseVisibilityControl && !IsForeground)
|
||||
{
|
||||
#if USE_SDL_WORKAROUNDS
|
||||
if (!IsMouseOver)
|
||||
Hide();
|
||||
#else
|
||||
Hide();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -303,19 +303,22 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
if (_activeEyedropper)
|
||||
{
|
||||
_activeEyedropper = false;
|
||||
Color color = colorPicked;
|
||||
if (_linear)
|
||||
color = color.ToLinear();
|
||||
SelectedColor = color;
|
||||
ScreenUtilities.PickColorDone -= OnColorPicked;
|
||||
if (colorPicked != Color.Transparent)
|
||||
{
|
||||
Color color = colorPicked;
|
||||
if (_linear)
|
||||
color = color.ToLinear();
|
||||
SelectedColor = color;
|
||||
}
|
||||
Platform.PickScreenColorDone -= OnColorPicked;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEyedropStart()
|
||||
{
|
||||
_activeEyedropper = true;
|
||||
ScreenUtilities.PickColor();
|
||||
ScreenUtilities.PickColorDone += OnColorPicked;
|
||||
Platform.PickScreenColor();
|
||||
Platform.PickScreenColorDone += OnColorPicked;
|
||||
}
|
||||
|
||||
private void OnRGBAChanged()
|
||||
@@ -351,11 +354,15 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
// Update eye dropper tool
|
||||
if (_activeEyedropper)
|
||||
{
|
||||
// Try reading the color under the cursor in realtime if supported by the platform
|
||||
Float2 mousePosition = Platform.MousePosition;
|
||||
Color color = ScreenUtilities.GetColorAt(mousePosition);
|
||||
if (_linear)
|
||||
color = color.ToLinear();
|
||||
SelectedColor = color;
|
||||
Color color = Platform.GetScreenColorAt(mousePosition);
|
||||
if (color != Color.Transparent)
|
||||
{
|
||||
if (_linear)
|
||||
color = color.ToLinear();
|
||||
SelectedColor = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,7 +444,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
{
|
||||
// Cancel eye dropping
|
||||
_activeEyedropper = false;
|
||||
ScreenUtilities.PickColorDone -= OnColorPicked;
|
||||
Platform.PickScreenColorDone -= OnColorPicked;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,545 +0,0 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used to handle docking windows dragging and docking.
|
||||
/// </summary>
|
||||
public class DockHintWindow
|
||||
{
|
||||
private FloatWindowDockPanel _toMove;
|
||||
|
||||
private Float2 _dragOffset;
|
||||
private Float2 _defaultWindowSize;
|
||||
private Rectangle _rectDock;
|
||||
private Rectangle _rectWindow;
|
||||
private Float2 _mouse;
|
||||
private DockState _toSet;
|
||||
private DockPanel _toDock;
|
||||
private bool _lateDragOffsetUpdate;
|
||||
|
||||
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
|
||||
|
||||
private DockHintWindow(FloatWindowDockPanel toMove)
|
||||
{
|
||||
_toMove = toMove;
|
||||
_toSet = DockState.Float;
|
||||
var window = toMove.Window.Window;
|
||||
|
||||
// Remove focus from drag target
|
||||
_toMove.Focus();
|
||||
_toMove.Defocus();
|
||||
|
||||
// Focus window
|
||||
window.Focus();
|
||||
|
||||
// Check if window is maximized and restore window.
|
||||
if (window.IsMaximized)
|
||||
{
|
||||
// Restore window and set position to mouse.
|
||||
var mousePos = window.MousePosition;
|
||||
var previousSize = window.Size;
|
||||
window.Restore();
|
||||
window.Position = Platform.MousePosition - mousePos * window.Size / previousSize;
|
||||
}
|
||||
|
||||
// Calculate dragging offset and move window to the destination position
|
||||
var mouseScreenPosition = Platform.MousePosition;
|
||||
|
||||
// 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;
|
||||
|
||||
// Get initial size
|
||||
_defaultWindowSize = window.Size;
|
||||
|
||||
// Init proxy window
|
||||
Proxy.Init(ref _defaultWindowSize);
|
||||
|
||||
// Bind events
|
||||
Proxy.Window.MouseUp += OnMouseUp;
|
||||
Proxy.Window.MouseMove += OnMouseMove;
|
||||
Proxy.Window.LostFocus += OnLostFocus;
|
||||
|
||||
// Start tracking mouse
|
||||
Proxy.Window.StartTrackingMouse(false);
|
||||
|
||||
// Update window GUI
|
||||
Proxy.Window.GUI.PerformLayout();
|
||||
|
||||
// Update rectangles
|
||||
UpdateRects();
|
||||
|
||||
// Hide base window
|
||||
window.Hide();
|
||||
|
||||
// Enable hit window presentation
|
||||
Proxy.Window.RenderingEnabled = true;
|
||||
Proxy.Window.Show();
|
||||
Proxy.Window.Focus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
// End tracking mouse
|
||||
Proxy.Window.EndTrackingMouse();
|
||||
|
||||
// Disable rendering
|
||||
Proxy.Window.RenderingEnabled = false;
|
||||
|
||||
// Unbind events
|
||||
Proxy.Window.MouseUp -= OnMouseUp;
|
||||
Proxy.Window.MouseMove -= OnMouseMove;
|
||||
Proxy.Window.LostFocus -= OnLostFocus;
|
||||
|
||||
// Hide the proxy
|
||||
Proxy.Hide();
|
||||
|
||||
if (_toMove == null)
|
||||
return;
|
||||
|
||||
// Check if window won't be docked
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
var window = _toMove.Window?.Window;
|
||||
if (window == null)
|
||||
return;
|
||||
var mouse = Platform.MousePosition;
|
||||
|
||||
// Move base window
|
||||
window.Position = mouse - _dragOffset;
|
||||
|
||||
// Show base window
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
|
||||
|
||||
// Check if window has only single tab
|
||||
if (hasNoChildPanels && _toMove.TabsCount == 1)
|
||||
{
|
||||
// Dock window
|
||||
_toMove.GetTab(0).Show(_toSet, _toDock);
|
||||
}
|
||||
// Check if dock as tab and has no child panels
|
||||
else if (hasNoChildPanels && _toSet == DockState.DockFill)
|
||||
{
|
||||
// Dock all tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var selectedTab = _toMove.SelectedTab;
|
||||
|
||||
// Dock the first tab into the target location
|
||||
var firstTab = _toMove.GetTab(0);
|
||||
firstTab.Show(_toSet, _toDock);
|
||||
|
||||
// Dock rest of the tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
|
||||
}
|
||||
|
||||
// Keep selected tab being selected
|
||||
selectedTab?.SelectTab();
|
||||
}
|
||||
|
||||
// Focus target window
|
||||
_toDock.Root.Focus();
|
||||
}
|
||||
|
||||
_toMove = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the new dragging hit window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Floating dock panel to move.</param>
|
||||
/// <returns>The dock hint window object.</returns>
|
||||
public static DockHintWindow Create(FloatWindowDockPanel toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
return new DockHintWindow(toMove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the new dragging hit window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Dock window to move.</param>
|
||||
/// <returns>The dock hint window object.</returns>
|
||||
public static DockHintWindow Create(DockWindow toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Show floating
|
||||
toMove.ShowFloating();
|
||||
|
||||
// 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);
|
||||
|
||||
// Get floating panel
|
||||
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
|
||||
|
||||
return new DockHintWindow(floatingPanelToMove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates window rectangle in the dock window.
|
||||
/// </summary>
|
||||
/// <param name="state">Window dock state.</param>
|
||||
/// <param name="rect">Dock panel rectangle.</param>
|
||||
/// <returns>Calculated window rectangle.</returns>
|
||||
public static Rectangle CalculateDockRect(DockState state, ref Rectangle rect)
|
||||
{
|
||||
Rectangle result = rect;
|
||||
switch (state)
|
||||
{
|
||||
case DockState.DockFill:
|
||||
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;
|
||||
break;
|
||||
case DockState.DockLeft:
|
||||
result.Size.X *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockBottom:
|
||||
result.Location.Y += result.Size.Y * (1 - DockPanel.DefaultSplitterValue);
|
||||
result.Size.Y *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockRight:
|
||||
result.Location.X += result.Size.X * (1 - DockPanel.DefaultSplitterValue);
|
||||
result.Size.X *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void CalculateDragOffset(Float2 mouseScreenPosition)
|
||||
{
|
||||
var baseWinPos = _toMove.Window.Window.Position;
|
||||
_dragOffset = mouseScreenPosition - baseWinPos;
|
||||
}
|
||||
|
||||
private void UpdateRects()
|
||||
{
|
||||
// Cache mouse position
|
||||
_mouse = Platform.MousePosition;
|
||||
|
||||
// Check intersection with any dock panel
|
||||
var uiMouse = _mouse;
|
||||
_toDock = _toMove.MasterPanel.HitTest(ref uiMouse, _toMove);
|
||||
|
||||
// Check dock state to use
|
||||
bool showProxyHints = _toDock != null;
|
||||
bool showBorderHints = showProxyHints;
|
||||
bool showCenterHint = showProxyHints;
|
||||
if (showProxyHints)
|
||||
{
|
||||
// If moved window has not only tabs but also child panels disable docking as tab
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showCenterHint = false;
|
||||
|
||||
// Disable docking windows with one or more dock panels inside
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showBorderHints = false;
|
||||
|
||||
// Get dock area
|
||||
_rectDock = _toDock.DockAreaBounds;
|
||||
|
||||
// Cache dock rectangles
|
||||
var size = _rectDock.Size;
|
||||
var offset = _rectDock.Location;
|
||||
var borderMargin = 4.0f;
|
||||
var hintWindowsSize = Proxy.HintWindowsSize * Platform.DpiScale;
|
||||
var hintWindowsSize2 = hintWindowsSize * 0.5f;
|
||||
var centerX = size.X * 0.5f;
|
||||
var centerY = size.Y * 0.5f;
|
||||
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
|
||||
// Hit test
|
||||
DockState toSet = DockState.Float;
|
||||
if (showBorderHints)
|
||||
{
|
||||
if (_rUpper.Contains(_mouse))
|
||||
toSet = DockState.DockTop;
|
||||
else if (_rBottom.Contains(_mouse))
|
||||
toSet = DockState.DockBottom;
|
||||
else if (_rLeft.Contains(_mouse))
|
||||
toSet = DockState.DockLeft;
|
||||
else if (_rRight.Contains(_mouse))
|
||||
toSet = DockState.DockRight;
|
||||
}
|
||||
if (showCenterHint && _rCenter.Contains(_mouse))
|
||||
toSet = DockState.DockFill;
|
||||
_toSet = toSet;
|
||||
|
||||
// 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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
// Floating window over dock panel
|
||||
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use only part of the dock panel to show hint
|
||||
_rectWindow = CalculateDockRect(_toSet, ref _rectDock);
|
||||
}
|
||||
}
|
||||
|
||||
// Update proxy window
|
||||
Proxy.Window.ClientBounds = _rectWindow;
|
||||
}
|
||||
|
||||
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseMove(ref Float2 mousePos)
|
||||
{
|
||||
// Recalculate the drag offset because the current mouse screen position was invalid when we initialized the window
|
||||
if (_lateDragOffsetUpdate)
|
||||
{
|
||||
// Calculate dragging offset and move window to the destination position
|
||||
CalculateDragOffset(mousePos);
|
||||
|
||||
// Reset state
|
||||
_lateDragOffsetUpdate = false;
|
||||
}
|
||||
|
||||
UpdateRects();
|
||||
}
|
||||
|
||||
private void OnLostFocus()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper proxy windows shared across docking panels. They are used to visualize docking window locations.
|
||||
/// </summary>
|
||||
public static class Proxy
|
||||
{
|
||||
/// <summary>
|
||||
/// The drag proxy window.
|
||||
/// </summary>
|
||||
public static Window Window;
|
||||
|
||||
/// <summary>
|
||||
/// The left hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Left;
|
||||
|
||||
/// <summary>
|
||||
/// The right hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Right;
|
||||
|
||||
/// <summary>
|
||||
/// The up hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Up;
|
||||
|
||||
/// <summary>
|
||||
/// The down hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Down;
|
||||
|
||||
/// <summary>
|
||||
/// The center hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Center;
|
||||
|
||||
/// <summary>
|
||||
/// The hint windows size.
|
||||
/// </summary>
|
||||
public const float HintWindowsSize = 32.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the hit proxy windows. Those windows are used to indicate drag target areas (left, right, top, bottom, etc.).
|
||||
/// </summary>
|
||||
public static void InitHitProxy()
|
||||
{
|
||||
CreateProxy(ref Left, "DockHint.Left");
|
||||
CreateProxy(ref Right, "DockHint.Right");
|
||||
CreateProxy(ref Up, "DockHint.Up");
|
||||
CreateProxy(ref Down, "DockHint.Down");
|
||||
CreateProxy(ref Center, "DockHint.Center");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the hint window.
|
||||
/// </summary>
|
||||
/// <param name="initSize">Initial size of the proxy window.</param>
|
||||
public static void Init(ref Float2 initSize)
|
||||
{
|
||||
if (Window == null)
|
||||
{
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = "DockHint.Window";
|
||||
settings.Size = initSize;
|
||||
settings.AllowInput = true;
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowMinimize = false;
|
||||
settings.HasBorder = false;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
settings.IsTopmost = true;
|
||||
|
||||
Window = Platform.CreateWindow(ref settings);
|
||||
Window.Opacity = 0.6f;
|
||||
Window.GUI.BackgroundColor = Style.Current.Selection;
|
||||
Window.GUI.AddChild<DragVisuals>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resize proxy
|
||||
Window.ClientSize = initSize;
|
||||
}
|
||||
|
||||
InitHitProxy();
|
||||
}
|
||||
|
||||
private sealed class DragVisuals : Control
|
||||
{
|
||||
public DragVisuals()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.SelectionBorder);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateProxy(ref Window win, string name)
|
||||
{
|
||||
if (win != null)
|
||||
return;
|
||||
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = name;
|
||||
settings.Size = new Float2(HintWindowsSize * Platform.DpiScale);
|
||||
settings.AllowInput = false;
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowMinimize = false;
|
||||
settings.HasBorder = false;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ActivateWhenFirstShown = false;
|
||||
settings.IsTopmost = true;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
|
||||
win = Platform.CreateWindow(ref settings);
|
||||
win.Opacity = 0.6f;
|
||||
win.GUI.BackgroundColor = Style.Current.Selection;
|
||||
win.GUI.AddChild<DragVisuals>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides proxy windows.
|
||||
/// </summary>
|
||||
public static void Hide()
|
||||
{
|
||||
HideProxy(ref Window);
|
||||
HideProxy(ref Left);
|
||||
HideProxy(ref Right);
|
||||
HideProxy(ref Up);
|
||||
HideProxy(ref Down);
|
||||
HideProxy(ref Center);
|
||||
}
|
||||
|
||||
private static void HideProxy(ref Window win)
|
||||
{
|
||||
if (win)
|
||||
{
|
||||
win.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases proxy data and windows.
|
||||
/// </summary>
|
||||
public static void Dispose()
|
||||
{
|
||||
DisposeProxy(ref Window);
|
||||
DisposeProxy(ref Left);
|
||||
DisposeProxy(ref Right);
|
||||
DisposeProxy(ref Up);
|
||||
DisposeProxy(ref Down);
|
||||
DisposeProxy(ref Center);
|
||||
}
|
||||
|
||||
private static void DisposeProxy(ref Window win)
|
||||
{
|
||||
if (win)
|
||||
{
|
||||
win.Close(ClosingReason.User);
|
||||
win = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,11 +19,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
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
|
||||
private readonly bool _hideTabForSingleTab = Utilities.Utils.HideSingleTabWindowTabBars();
|
||||
|
||||
/// <summary>
|
||||
/// The is mouse down flag (left button).
|
||||
@@ -55,6 +51,11 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// </summary>
|
||||
public Float2 MousePosition = Float2.Minimum;
|
||||
|
||||
/// <summary>
|
||||
/// The mouse position.
|
||||
/// </summary>
|
||||
public Float2 MouseStartPosition = Float2.Minimum;
|
||||
|
||||
/// <summary>
|
||||
/// The start drag asynchronous window.
|
||||
/// </summary>
|
||||
@@ -196,7 +197,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (_panel.ChildPanelsCount == 0 && _panel.TabsCount == 1 && _panel.IsFloating)
|
||||
{
|
||||
// Create docking hint window but in an async manner
|
||||
DockHintWindow.Create(_panel as FloatWindowDockPanel);
|
||||
WindowDragHelper.StartDragging(_panel as FloatWindowDockPanel);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -207,7 +208,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
_panel.SelectTab(index - 1);
|
||||
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(win);
|
||||
WindowDragHelper.StartDragging(win, _panel.RootWindow.Window);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,6 +394,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (IsSingleFloatingWindow)
|
||||
return base.OnMouseDown(location, button);
|
||||
MouseDownWindow = GetTabAtPos(location, out IsMouseDownOverCross);
|
||||
MouseStartPosition = location;
|
||||
|
||||
// Check buttons
|
||||
if (button == MouseButton.Left)
|
||||
@@ -479,6 +481,20 @@ namespace FlaxEditor.GUI.Docking
|
||||
StartDrag(MouseDownWindow);
|
||||
MouseDownWindow = null;
|
||||
}
|
||||
// Check if single tab is tried to be moved
|
||||
else if (MouseDownWindow != null && _panel.TabsCount <= 1)
|
||||
{
|
||||
if ((MousePosition - MouseStartPosition).Length > 3)
|
||||
{
|
||||
// Clear flag
|
||||
IsMouseLeftButtonDown = false;
|
||||
|
||||
// Check tab under the mouse
|
||||
if (!IsMouseDownOverCross && MouseDownWindow != null)
|
||||
StartDrag(MouseDownWindow);
|
||||
MouseDownWindow = null;
|
||||
}
|
||||
}
|
||||
// Check if has more than one tab to change order
|
||||
else if (MouseDownWindow != null && _panel.TabsCount > 1)
|
||||
{
|
||||
|
||||
@@ -182,6 +182,26 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
|
||||
/// <param name="position">Window location.</param>
|
||||
public void ShowFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent)
|
||||
{
|
||||
CreateFloating(location, size, position, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the window in a floating state.
|
||||
/// </summary>
|
||||
public void CreateFloating()
|
||||
{
|
||||
CreateFloating(Float2.Zero, Float2.Zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the window in a floating state.
|
||||
/// </summary>
|
||||
/// <param name="location">Window location.</param>
|
||||
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
|
||||
/// <param name="position">Window location.</param>
|
||||
/// <param name="showWindow">Window visibility.</param>
|
||||
public void CreateFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent, bool showWindow = false)
|
||||
{
|
||||
Undock();
|
||||
|
||||
@@ -199,14 +219,17 @@ namespace FlaxEditor.GUI.Docking
|
||||
windowGUI.UnlockChildrenRecursive();
|
||||
windowGUI.PerformLayout();
|
||||
|
||||
// Show
|
||||
window.Show();
|
||||
window.BringToFront();
|
||||
window.Focus();
|
||||
OnShow();
|
||||
if (showWindow)
|
||||
{
|
||||
// Show
|
||||
window.Show();
|
||||
window.BringToFront();
|
||||
window.Focus();
|
||||
OnShow();
|
||||
|
||||
// Perform layout again
|
||||
windowGUI.PerformLayout();
|
||||
// Perform layout again
|
||||
windowGUI.PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,42 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// <seealso cref="DockPanel" />
|
||||
public class FloatWindowDockPanel : DockPanel
|
||||
{
|
||||
private class FloatWindowDecorations : WindowDecorations
|
||||
{
|
||||
private FloatWindowDockPanel _panel;
|
||||
|
||||
public FloatWindowDecorations(FloatWindowDockPanel panel)
|
||||
: base(panel.RootWindow)
|
||||
{
|
||||
_panel = panel;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
_panel.BeginDrag();
|
||||
return true;
|
||||
}
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
#if !PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
protected override WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
var hit = base.OnHitTest(ref mouse);
|
||||
if (hit == WindowHitCodes.Caption)
|
||||
{
|
||||
// Override the system behaviour when interacting with the caption area
|
||||
hit = WindowHitCodes.Client;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private MasterDockPanel _masterPanel;
|
||||
private WindowRootControl _window;
|
||||
|
||||
@@ -40,6 +76,26 @@ namespace FlaxEditor.GUI.Docking
|
||||
Parent = window;
|
||||
_window.Window.Closing += OnClosing;
|
||||
_window.Window.LeftButtonHit += OnLeftButtonHit;
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
var decorations = Parent.AddChild(new FloatWindowDecorations(this));
|
||||
decorations.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
base.PerformLayoutBeforeChildren();
|
||||
|
||||
var decorations = Parent.GetChild<FloatWindowDecorations>();
|
||||
if (decorations != null)
|
||||
{
|
||||
// Apply offset for the title bar
|
||||
foreach (var child in Children)
|
||||
child.Bounds = child.Bounds with { Y = decorations.Height, Height = Parent.Height - decorations.Height };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,7 +108,11 @@ namespace FlaxEditor.GUI.Docking
|
||||
return;
|
||||
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(this);
|
||||
Window dragSourceWindow = null;
|
||||
#if !PLATFORM_SDL
|
||||
dragSourceWindow = _window?.Window;
|
||||
#endif
|
||||
WindowDragHelper.StartDragging(this, dragSourceWindow);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -71,23 +131,33 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.Title = title;
|
||||
settings.Size = size;
|
||||
settings.Position = location;
|
||||
settings.MinimumSize = new Float2(1);
|
||||
settings.MinimumSize = new Float2(100, 100);
|
||||
settings.MaximumSize = Float2.Zero; // Unlimited size
|
||||
settings.Fullscreen = false;
|
||||
settings.HasBorder = true;
|
||||
#if PLATFORM_SDL
|
||||
settings.SupportsTransparency = true;
|
||||
#else
|
||||
settings.SupportsTransparency = false;
|
||||
#endif
|
||||
settings.ActivateWhenFirstShown = true;
|
||||
settings.AllowInput = true;
|
||||
settings.AllowMinimize = true;
|
||||
settings.AllowMaximize = true;
|
||||
settings.AllowDragAndDrop = true;
|
||||
settings.IsTopmost = false;
|
||||
settings.IsRegularWindow = true;
|
||||
settings.Type = WindowType.Regular;
|
||||
settings.HasSizingFrame = true;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
settings.ShowInTaskbar = true;
|
||||
settings.StartPosition = startPosition;
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
settings.HasBorder = false;
|
||||
//settings.HasSizingFrame = false;
|
||||
}
|
||||
|
||||
// Create window
|
||||
return Platform.CreateWindow(ref settings);
|
||||
}
|
||||
@@ -151,7 +221,12 @@ namespace FlaxEditor.GUI.Docking
|
||||
base.OnSelectedTabChanged();
|
||||
|
||||
if (_window != null && SelectedTab != null)
|
||||
{
|
||||
_window.Title = SelectedTab.Title;
|
||||
var decorations = Parent.GetChild<FloatWindowDecorations>();
|
||||
if (decorations != null)
|
||||
decorations.PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -81,7 +81,6 @@ namespace FlaxEditor.GUI.Docking
|
||||
public DockPanel HitTest(ref Float2 position, FloatWindowDockPanel excluded)
|
||||
{
|
||||
// Check all floating windows
|
||||
// TODO: gather windows order and take it into account when performing test
|
||||
for (int i = 0; i < FloatingPanels.Count; i++)
|
||||
{
|
||||
var win = FloatingPanels[i];
|
||||
@@ -94,9 +93,44 @@ namespace FlaxEditor.GUI.Docking
|
||||
}
|
||||
|
||||
// Base
|
||||
//if (!Root?.RootWindow.Window.IsFocused ?? false)
|
||||
// return null;
|
||||
return base.HitTest(ref position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs hit test over dock panel.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <param name="hitResults">Results of the hit test</param>
|
||||
/// <returns>True if any dock panels were hit, otherwise false.</returns>
|
||||
public bool HitTest(ref Float2 position, FloatWindowDockPanel excluded, out DockPanel[] hitResults)
|
||||
{
|
||||
// Check all floating windows
|
||||
List<DockPanel> results = new(FloatingPanels.Count);
|
||||
for (int i = 0; i < FloatingPanels.Count; i++)
|
||||
{
|
||||
var win = FloatingPanels[i];
|
||||
if (win.Visible && win != excluded)
|
||||
{
|
||||
var result = win.HitTest(ref position);
|
||||
if (result != null)
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Base
|
||||
//if (!Root?.RootWindow.Window.IsFocused ?? false)
|
||||
// return null;
|
||||
var baseResult = base.HitTest(ref position);
|
||||
if (baseResult != null)
|
||||
results.Add(baseResult);
|
||||
|
||||
hitResults = results.ToArray();
|
||||
return hitResults.Length > 0;
|
||||
}
|
||||
|
||||
internal void LinkWindow(DockWindow window)
|
||||
{
|
||||
// Add to the windows list
|
||||
|
||||
514
Source/Editor/GUI/Docking/WindowDragHelper.cs
Normal file
514
Source/Editor/GUI/Docking/WindowDragHelper.cs
Normal file
@@ -0,0 +1,514 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used to handle docking windows dragging and docking.
|
||||
/// </summary>
|
||||
public class WindowDragHelper
|
||||
{
|
||||
private FloatWindowDockPanel _toMove;
|
||||
|
||||
private Float2 _dragOffset;
|
||||
private Rectangle _rectDock;
|
||||
private Float2 _mouse;
|
||||
private DockState _toSet;
|
||||
private DockPanel _toDock;
|
||||
private Window _dragSourceWindow;
|
||||
|
||||
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
|
||||
private Control _dockHintDown, _dockHintUp, _dockHintLeft, _dockHintRight, _dockHintCenter;
|
||||
|
||||
/// <summary>
|
||||
/// The hint control size.
|
||||
/// </summary>
|
||||
public const float HintControlSize = 48.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The opacity of the dragged window when hint controls are shown.
|
||||
/// </summary>
|
||||
public const float DragWindowOpacity = 0.4f;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if any windows are being dragged.
|
||||
/// </summary>
|
||||
public static bool IsDragActive { get; private set; }
|
||||
|
||||
private WindowDragHelper(FloatWindowDockPanel toMove, Window dragSourceWindow)
|
||||
{
|
||||
IsDragActive = true;
|
||||
_toMove = toMove;
|
||||
_toSet = DockState.Float;
|
||||
var window = toMove.Window.Window;
|
||||
var mousePos = Platform.MousePosition;
|
||||
|
||||
// Check if window is maximized and restore window for correct dragging
|
||||
if (window.IsMaximized)
|
||||
{
|
||||
var windowMousePos = mousePos - window.Position;
|
||||
var previousSize = window.Size;
|
||||
window.Restore();
|
||||
window.Position = mousePos - windowMousePos * window.Size / previousSize;
|
||||
}
|
||||
|
||||
// When drag starts from a tabs the window might not be shown yet
|
||||
if (!window.IsVisible)
|
||||
{
|
||||
window.Show();
|
||||
window.Position = mousePos - new Float2(40, 10);
|
||||
}
|
||||
|
||||
// Bind events
|
||||
FlaxEngine.Scripting.Update += OnUpdate;
|
||||
window.MouseUp += OnMouseUp;
|
||||
#if !PLATFORM_SDL
|
||||
window.StartTrackingMouse(false);
|
||||
#endif
|
||||
|
||||
// Update rectangles
|
||||
UpdateRects(mousePos);
|
||||
|
||||
// Ensure the dragged window stays on top of every other window
|
||||
window.IsAlwaysOnTop = true;
|
||||
|
||||
_dragSourceWindow = dragSourceWindow;
|
||||
if (_dragSourceWindow != null) // Detaching a tab from existing window
|
||||
{
|
||||
#if PLATFORM_SDL
|
||||
_dragOffset = new Float2(window.Size.X / 2, 10.0f);
|
||||
#else
|
||||
_dragOffset = mousePos - window.Position;
|
||||
#endif
|
||||
|
||||
// The mouse up event is sent to the source window on Windows
|
||||
_dragSourceWindow.MouseUp += OnMouseUp;
|
||||
|
||||
// TODO: when detaching tab in floating window (not main window), the drag source window is still main window?
|
||||
var dragSourceWindowWayland = toMove.MasterPanel?.RootWindow.Window ?? Editor.Instance.Windows.MainWindow;
|
||||
window.DoDragDrop(window.Title, _dragOffset, dragSourceWindowWayland);
|
||||
#if !PLATFORM_SDL
|
||||
_dragSourceWindow.BringToFront();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
_dragOffset = window.MousePosition;
|
||||
window.DoDragDrop(window.Title, _dragOffset, window);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
IsDragActive = false;
|
||||
var window = _toMove?.Window?.Window;
|
||||
|
||||
// Unbind events
|
||||
FlaxEngine.Scripting.Update -= OnUpdate;
|
||||
if (window != null)
|
||||
{
|
||||
window.MouseUp -= OnMouseUp;
|
||||
#if !PLATFORM_SDL
|
||||
window.EndTrackingMouse();
|
||||
#endif
|
||||
}
|
||||
if (_dragSourceWindow != null)
|
||||
_dragSourceWindow.MouseUp -= OnMouseUp;
|
||||
|
||||
RemoveDockHints();
|
||||
|
||||
if (_toMove == null)
|
||||
return;
|
||||
|
||||
if (window != null)
|
||||
{
|
||||
window.Opacity = 1.0f;
|
||||
window.IsAlwaysOnTop = false;
|
||||
window.BringToFront();
|
||||
}
|
||||
|
||||
// Check if window won't be docked
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
if (window == null)
|
||||
return;
|
||||
|
||||
// Show base window
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
|
||||
|
||||
// Check if window has only single tab
|
||||
if (hasNoChildPanels && _toMove.TabsCount == 1)
|
||||
{
|
||||
// Dock window
|
||||
_toMove.GetTab(0).Show(_toSet, _toDock);
|
||||
}
|
||||
// Check if dock as tab and has no child panels
|
||||
else if (hasNoChildPanels && _toSet == DockState.DockFill)
|
||||
{
|
||||
// Dock all tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var selectedTab = _toMove.SelectedTab;
|
||||
|
||||
// Dock the first tab into the target location
|
||||
if (_toMove.TabsCount > 0)
|
||||
{
|
||||
var firstTab = _toMove.GetTab(0);
|
||||
firstTab.Show(_toSet, _toDock);
|
||||
|
||||
// Dock rest of the tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep selected tab being selected
|
||||
selectedTab?.SelectTab();
|
||||
}
|
||||
|
||||
// Focus target window
|
||||
_toDock.Root.Focus();
|
||||
}
|
||||
|
||||
_toMove = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start dragging a floating dock panel.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Floating dock panel to move.</param>
|
||||
/// <param name="dragSourceWindow">The window where dragging started from.</param>
|
||||
/// <returns>The window drag helper object.</returns>
|
||||
public static WindowDragHelper StartDragging(FloatWindowDockPanel toMove, Window dragSourceWindow = null)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
return new WindowDragHelper(toMove, dragSourceWindow);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start dragging a docked panel into a floating window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Dock window to move.</param>
|
||||
/// <param name="dragSourceWindow">The window where dragging started from.</param>
|
||||
/// <returns>The window drag helper object.</returns>
|
||||
public static WindowDragHelper StartDragging(DockWindow toMove, Window dragSourceWindow)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Create floating window
|
||||
toMove.CreateFloating();
|
||||
|
||||
// Get floating panel
|
||||
var window = (WindowRootControl)toMove.Root;
|
||||
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
|
||||
|
||||
return new WindowDragHelper(floatingPanelToMove, dragSourceWindow);
|
||||
}
|
||||
|
||||
private sealed class DragVisuals : Control
|
||||
{
|
||||
public DragVisuals()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.SelectionBorder);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDockHints()
|
||||
{
|
||||
if (_toDock == null)
|
||||
return;
|
||||
|
||||
if (_toDock.RootWindow.Window != _dragSourceWindow)
|
||||
_toDock.RootWindow.Window.MouseUp += OnMouseUp;
|
||||
|
||||
_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));
|
||||
|
||||
Control AddHintControl(Float2 pivot)
|
||||
{
|
||||
DragVisuals hintControl = _toDock.AddChild<DragVisuals>();
|
||||
hintControl.Size = new Float2(HintControlSize);
|
||||
hintControl.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
hintControl.Pivot = pivot;
|
||||
hintControl.PivotRelative = true;
|
||||
return hintControl;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveDockHints()
|
||||
{
|
||||
if (_toDock == null)
|
||||
return;
|
||||
|
||||
if (_toDock.RootWindow.Window != _dragSourceWindow)
|
||||
_toDock.RootWindow.Window.MouseUp -= OnMouseUp;
|
||||
|
||||
_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(Float2 mousePos)
|
||||
{
|
||||
// Cache mouse position
|
||||
_mouse = mousePos;
|
||||
|
||||
// Check intersection with any dock panel
|
||||
DockPanel dockPanel = null;
|
||||
if (_toMove.MasterPanel.HitTest(ref _mouse, _toMove, out var hitResults))
|
||||
{
|
||||
dockPanel = hitResults[0];
|
||||
|
||||
// Prefer panel which currently has focus
|
||||
foreach (var hit in hitResults)
|
||||
{
|
||||
if (hit.RootWindow.Window.IsFocused)
|
||||
{
|
||||
dockPanel = hit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer panel in the same window we hit earlier
|
||||
// TODO: this doesn't allow docking window into another floating window over the main window
|
||||
/*if (dockPanel?.RootWindow != _toDock?.RootWindow)
|
||||
{
|
||||
foreach (var hit in hitResults)
|
||||
{
|
||||
if (hit.RootWindow == _toDock?.RootWindow)
|
||||
{
|
||||
dockPanel = _toDock;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
if (dockPanel != _toDock)
|
||||
{
|
||||
RemoveDockHints();
|
||||
_toDock = dockPanel;
|
||||
AddDockHints();
|
||||
|
||||
// Make sure the all the dock hint areas are not under other windows
|
||||
if (_toDock != Editor.Instance.UI.MasterPanel)
|
||||
_toDock?.RootWindow.Window.BringToFront();
|
||||
//_toDock?.RootWindow.Window.Focus();
|
||||
|
||||
#if PLATFORM_SDL
|
||||
// Make the dragged window transparent when dock hints are visible
|
||||
_toMove.Window.Window.Opacity = _toDock == null ? 1.0f : DragWindowOpacity;
|
||||
#else
|
||||
// Bring the drop source always to the top
|
||||
if (_dragSourceWindow != null)
|
||||
_dragSourceWindow.BringToFront();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Check dock state to use
|
||||
bool showProxyHints = _toDock != null;
|
||||
bool showBorderHints = showProxyHints;
|
||||
bool showCenterHint = showProxyHints;
|
||||
Control hoveredHintControl = null;
|
||||
Float2 hoveredLocationOffset = Float2.Zero;
|
||||
Float2 hoveredSizeOverride = Float2.Zero;
|
||||
DockState prevToSet = _toSet;
|
||||
float hoveredMargin = 1.0f;
|
||||
if (showProxyHints)
|
||||
{
|
||||
// If moved window has not only tabs but also child panels disable docking as tab
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showCenterHint = false;
|
||||
|
||||
// Disable docking windows with one or more dock panels inside
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showBorderHints = false;
|
||||
|
||||
// Get dock area
|
||||
_rectDock = _toDock.DockAreaBounds;
|
||||
|
||||
// Cache dock rectangles
|
||||
var size = _rectDock.Size / Platform.DpiScale;
|
||||
var offset = _toDock.PointFromScreen(_rectDock.Location);
|
||||
var borderMargin = 10.0f;
|
||||
var hintWindowsSize = HintControlSize;
|
||||
var hintWindowsSize2 = hintWindowsSize * 0.5f;
|
||||
var hintPreviewSize = new Float2(Math.Max(HintControlSize * 2, size.X * 0.5f), Math.Max(HintControlSize * 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;
|
||||
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
|
||||
// Hit test, and calculate the approximation for filled area when hovered over the hint
|
||||
var toSet = DockState.Float;
|
||||
var hintTestPoint = _toDock.PointFromScreen(_mouse);
|
||||
if (showBorderHints)
|
||||
{
|
||||
if (_rUpper.Contains(ref hintTestPoint))
|
||||
{
|
||||
toSet = DockState.DockTop;
|
||||
hoveredHintControl = _dockHintUp;
|
||||
hoveredSizeOverride = new Float2(size.X, size.Y * DockPanel.DefaultSplitterValue);
|
||||
hoveredLocationOffset.Y -= borderMargin - hoveredMargin;
|
||||
}
|
||||
else if (_rBottom.Contains(ref hintTestPoint))
|
||||
{
|
||||
toSet = DockState.DockBottom;
|
||||
hoveredHintControl = _dockHintDown;
|
||||
hoveredSizeOverride = new Float2(size.X, size.Y * DockPanel.DefaultSplitterValue);
|
||||
hoveredLocationOffset.Y += borderMargin - hoveredMargin;
|
||||
}
|
||||
else if (_rLeft.Contains(ref hintTestPoint))
|
||||
{
|
||||
toSet = DockState.DockLeft;
|
||||
hoveredHintControl = _dockHintLeft;
|
||||
hoveredSizeOverride = new Float2(size.X * DockPanel.DefaultSplitterValue, size.Y);
|
||||
hoveredLocationOffset.X -= borderMargin - hoveredMargin;
|
||||
}
|
||||
else if (_rRight.Contains(ref hintTestPoint))
|
||||
{
|
||||
toSet = DockState.DockRight;
|
||||
hoveredHintControl = _dockHintRight;
|
||||
hoveredSizeOverride = new Float2(size.X * DockPanel.DefaultSplitterValue, size.Y);
|
||||
hoveredLocationOffset.X += borderMargin - hoveredMargin;
|
||||
}
|
||||
}
|
||||
if (showCenterHint && _rCenter.Contains(ref hintTestPoint))
|
||||
{
|
||||
toSet = DockState.DockFill;
|
||||
hoveredHintControl = _dockHintCenter;
|
||||
hoveredSizeOverride = new Float2(size.X, size.Y);
|
||||
}
|
||||
|
||||
_toSet = toSet;
|
||||
}
|
||||
else
|
||||
{
|
||||
_toSet = DockState.Float;
|
||||
}
|
||||
|
||||
// Update sizes and opacity of hint controls
|
||||
if (_toDock != null)
|
||||
{
|
||||
var mainColor = Style.Current.Selection;
|
||||
if (hoveredHintControl != _dockHintDown)
|
||||
{
|
||||
_dockHintDown.Size = new Float2(HintControlSize);
|
||||
_dockHintDown.BackgroundColor = mainColor.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintLeft)
|
||||
{
|
||||
_dockHintLeft.Size = new Float2(HintControlSize);
|
||||
_dockHintLeft.BackgroundColor = mainColor.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintRight)
|
||||
{
|
||||
_dockHintRight.Size = new Float2(HintControlSize);
|
||||
_dockHintRight.BackgroundColor = mainColor.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintUp)
|
||||
{
|
||||
_dockHintUp.Size = new Float2(HintControlSize);
|
||||
_dockHintUp.BackgroundColor = mainColor.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintCenter)
|
||||
{
|
||||
_dockHintCenter.Size = new Float2(HintControlSize);
|
||||
_dockHintCenter.BackgroundColor = mainColor.AlphaMultiplied(0.6f);
|
||||
}
|
||||
|
||||
if (_toSet != DockState.Float)
|
||||
{
|
||||
if (hoveredHintControl != null)
|
||||
{
|
||||
hoveredHintControl.BackgroundColor = mainColor;
|
||||
if (_toSet != prevToSet)
|
||||
hoveredHintControl.Location += hoveredLocationOffset;
|
||||
hoveredHintControl.Size = hoveredSizeOverride - hoveredMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update hint controls visibility and location
|
||||
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;
|
||||
|
||||
_dockHintDown.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintLeft.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintRight.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintUp.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintCenter.Visible = showProxyHints & showCenterHint;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void OnUpdate()
|
||||
{
|
||||
// If the engine lost focus during dragging, end the action
|
||||
if (!Engine.HasFocus)
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
var mousePos = Platform.MousePosition;
|
||||
if (_mouse != mousePos)
|
||||
{
|
||||
if (_dragSourceWindow != null)
|
||||
_toMove.Window.Window.Position = mousePos - _dragOffset;
|
||||
|
||||
UpdateRects(mousePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,6 +292,7 @@ namespace FlaxEditor.GUI.Input
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
@@ -318,13 +319,45 @@ namespace FlaxEditor.GUI.Input
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMoveRelative(Float2 motion)
|
||||
{
|
||||
var location = Root.TrackingMouseOffset;
|
||||
if (_isSliding)
|
||||
{
|
||||
// Update sliding
|
||||
ApplySliding(Root.TrackingMouseOffset.X * _slideSpeed);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update cursor type so user knows they can slide value
|
||||
if (CanUseSliding && SlideRect.Contains(location) && !_isSliding)
|
||||
{
|
||||
Cursor = CursorType.SizeWE;
|
||||
_cursorChanged = true;
|
||||
}
|
||||
else if (_cursorChanged && !_isSliding)
|
||||
{
|
||||
Cursor = CursorType.Default;
|
||||
_cursorChanged = false;
|
||||
}
|
||||
|
||||
base.OnMouseMoveRelative(motion);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && _isSliding)
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
// End sliding and return mouse to original location
|
||||
RootWindow.MousePosition = _mouseClickedPosition;
|
||||
#endif
|
||||
EndSliding();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -12,16 +12,6 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public sealed class MainMenu : ContainerControl
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
private bool _useCustomWindowSystem;
|
||||
private Image _icon;
|
||||
private Label _title;
|
||||
private Button _closeButton;
|
||||
private Button _minimizeButton;
|
||||
private Button _maximizeButton;
|
||||
private LocalizedString _charChromeRestore, _charChromeMaximize;
|
||||
private Window _window;
|
||||
#endif
|
||||
private MainMenuButton _selected;
|
||||
|
||||
/// <summary>
|
||||
@@ -60,200 +50,12 @@ namespace FlaxEditor.GUI
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MainMenu"/> class.
|
||||
/// </summary>
|
||||
/// <param name="mainWindow">The main window.</param>
|
||||
public MainMenu(RootControl mainWindow)
|
||||
public MainMenu()
|
||||
: base(0, 0, 0, 20)
|
||||
{
|
||||
AutoFocus = false;
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
_useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
Height = 28;
|
||||
|
||||
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
|
||||
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
|
||||
Font iconFont = windowIconsFont?.CreateFont(9);
|
||||
|
||||
_window = mainWindow.RootWindow.Window;
|
||||
_window.HitTest += OnHitTest;
|
||||
_window.Closed += OnWindowClosed;
|
||||
|
||||
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
|
||||
|
||||
_icon = new Image
|
||||
{
|
||||
Margin = new Margin(6, 6, 6, 6),
|
||||
Brush = new TextureBrush(windowIcon),
|
||||
Color = Style.Current.Foreground,
|
||||
KeepAspectRatio = false,
|
||||
TooltipText = string.Format("{0}\nVersion {1}\nConfiguration {3}\nGraphics {2}", _window.Title, Globals.EngineVersion, GPUDevice.Instance.RendererType, configuration),
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_title = new Label(0, 0, Width, Height)
|
||||
{
|
||||
Text = _window.Title,
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = TextAlignment.Center,
|
||||
ClipText = true,
|
||||
TextColor = Style.Current.ForegroundGrey,
|
||||
TextColorHighlighted = Style.Current.ForegroundGrey,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_closeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Color.Red,
|
||||
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
|
||||
|
||||
_minimizeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_minimizeButton.Clicked += () => _window.Minimize();
|
||||
|
||||
_maximizeButton = new Button
|
||||
{
|
||||
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_maximizeButton.Clicked += () =>
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
};
|
||||
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
|
||||
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
_maximizeButton.Text = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowClosed()
|
||||
{
|
||||
if (_window != null)
|
||||
{
|
||||
_window.HitTest = null;
|
||||
_window = null;
|
||||
}
|
||||
}
|
||||
|
||||
private WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
var dpiScale = _window.DpiScale;
|
||||
|
||||
if (_window.IsMinimized)
|
||||
return WindowHitCodes.NoWhere;
|
||||
|
||||
if (!_window.IsMaximized)
|
||||
{
|
||||
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
|
||||
var winSize = _window.Size;
|
||||
|
||||
// Distance from which the mouse is considered to be on the border/corner
|
||||
float distance = 5.0f * dpiScale;
|
||||
|
||||
if (pos.Y > winSize.Y - distance && pos.X < distance)
|
||||
return WindowHitCodes.BottomLeft;
|
||||
|
||||
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.BottomRight;
|
||||
|
||||
if (pos.Y < distance && pos.X < distance)
|
||||
return WindowHitCodes.TopLeft;
|
||||
|
||||
if (pos.Y < distance && pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.TopRight;
|
||||
|
||||
if (pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.Right;
|
||||
|
||||
if (pos.X < distance)
|
||||
return WindowHitCodes.Left;
|
||||
|
||||
if (pos.Y < distance)
|
||||
return WindowHitCodes.Top;
|
||||
|
||||
if (pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.Bottom;
|
||||
}
|
||||
|
||||
var mousePos = PointFromScreen(mouse * dpiScale);
|
||||
var controlUnderMouse = GetChildAt(mousePos);
|
||||
var isMouseOverSth = controlUnderMouse != null && controlUnderMouse != _title;
|
||||
var rb = GetRightButton();
|
||||
if (rb != null && _minimizeButton != null && new Rectangle(rb.UpperRight, _minimizeButton.BottomLeft - rb.UpperRight).Contains(ref mousePos) && !isMouseOverSth)
|
||||
return WindowHitCodes.Caption;
|
||||
|
||||
return WindowHitCodes.Client;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Return the rightmost button.
|
||||
/// </summary>
|
||||
/// <returns>Rightmost button, null if there is no <see cref="MainMenuButton"/></returns>
|
||||
private MainMenuButton GetRightButton()
|
||||
{
|
||||
MainMenuButton b = null;
|
||||
foreach (var control in Children)
|
||||
{
|
||||
if (b == null && control is MainMenuButton)
|
||||
b = (MainMenuButton)control;
|
||||
|
||||
if (control is MainMenuButton && control.Right > b.Right)
|
||||
b = (MainMenuButton)control;
|
||||
}
|
||||
return b;
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -298,26 +100,6 @@ namespace FlaxEditor.GUI
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseDoubleClick(location, button))
|
||||
return true;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
var child = GetChildAtRecursive(location);
|
||||
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
@@ -333,16 +115,8 @@ namespace FlaxEditor.GUI
|
||||
protected override void PerformLayoutAfterChildren()
|
||||
{
|
||||
float x = 0;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Icon
|
||||
_icon.X = x;
|
||||
_icon.Size = new Float2(Height);
|
||||
x += _icon.Width;
|
||||
}
|
||||
#endif
|
||||
WindowDecorations decorations = Parent.GetChild<WindowDecorations>();
|
||||
x += decorations?.Icon?.Width ?? 0;
|
||||
|
||||
// Arrange controls
|
||||
MainMenuButton rightMostButton = null;
|
||||
@@ -361,37 +135,21 @@ namespace FlaxEditor.GUI
|
||||
x += b.Width;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Buttons
|
||||
_closeButton.Height = Height;
|
||||
_closeButton.X = Width - _closeButton.Width;
|
||||
_maximizeButton.Height = Height;
|
||||
_maximizeButton.X = _closeButton.X - _maximizeButton.Width;
|
||||
_minimizeButton.Height = Height;
|
||||
_minimizeButton.X = _maximizeButton.X - _minimizeButton.Width;
|
||||
|
||||
// Title
|
||||
_title.Bounds = new Rectangle(x + 2, 0, _minimizeButton.Left - x - 4, Height);
|
||||
//_title.Text = _title.Width < 300.0f ? Editor.Instance.ProjectInfo.Name : _window.Title;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fill the right side if title and buttons are not present
|
||||
if (decorations?.Title == null)
|
||||
Width = Parent.Width;
|
||||
else
|
||||
Width = x;
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_window != null)
|
||||
{
|
||||
_window.Closed -= OnWindowClosed;
|
||||
OnWindowClosed();
|
||||
}
|
||||
|
||||
if (_selected != null)
|
||||
Selected = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,12 @@ namespace FlaxEditor.GUI
|
||||
Text = text;
|
||||
|
||||
var style = Style.Current;
|
||||
#if PLATFORM_WINDOWS
|
||||
if (Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
|
||||
if (!Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
BackgroundColorMouseOver = style.BackgroundHighlighted;
|
||||
BackgroundColorMouseOverOpened = style.Background;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BackgroundColorMouseOver = BackgroundColorMouseOverOpened = style.LightBackground * 1.3f;
|
||||
}
|
||||
|
||||
339
Source/Editor/GUI/WindowDecorations.cs
Normal file
339
Source/Editor/GUI/WindowDecorations.cs
Normal file
@@ -0,0 +1,339 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the title bar of the window with buttons.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public class WindowDecorations : ContainerControl
|
||||
{
|
||||
private Image _icon;
|
||||
private Label _title;
|
||||
private Button _closeButton;
|
||||
private Button _minimizeButton;
|
||||
private Button _maximizeButton;
|
||||
private LocalizedString _charChromeRestore, _charChromeMaximize;
|
||||
private Window _window;
|
||||
|
||||
/// <summary>
|
||||
/// The title label in the title bar.
|
||||
/// </summary>
|
||||
public Label Title => _title;
|
||||
|
||||
/// <summary>
|
||||
/// The icon used in the title bar.
|
||||
/// </summary>
|
||||
public Image Icon => _icon;
|
||||
|
||||
/// <summary>
|
||||
/// The tooltip shown when hovering over the icon.
|
||||
/// </summary>
|
||||
public string IconTooltipText
|
||||
{
|
||||
get => _icon?.TooltipText ?? null;
|
||||
set
|
||||
{
|
||||
if (_icon != null)
|
||||
_icon.TooltipText = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WindowDecorations"/> class.
|
||||
/// </summary>
|
||||
/// <param name="window">The window.</param>
|
||||
/// <param name="iconOnly">When set, omit drawing title and buttons.</param>
|
||||
public WindowDecorations(RootControl window, bool iconOnly = false)
|
||||
: base(0, 0, 0, 20)
|
||||
{
|
||||
_window = window.RootWindow.Window;
|
||||
|
||||
AutoFocus = false;
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
BackgroundColor = Color.Transparent;
|
||||
|
||||
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
|
||||
|
||||
_icon = new Image
|
||||
{
|
||||
Margin = new Margin(4, 4, 4, 4),
|
||||
Brush = new TextureBrush(windowIcon),
|
||||
Color = Style.Current.Foreground,
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
KeepAspectRatio = false,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
if (!iconOnly)
|
||||
{
|
||||
_icon.Margin = new Margin(6, 6, 6, 6);
|
||||
Height = 28;
|
||||
|
||||
_window.HitTest += OnHitTest;
|
||||
_window.Closed += OnWindowClosed;
|
||||
|
||||
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
|
||||
Font iconFont = windowIconsFont?.CreateFont(9);
|
||||
|
||||
_title = new Label(0, 0, Width, Height)
|
||||
{
|
||||
Text = _window.Title,
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = TextAlignment.Center,
|
||||
ClipText = true,
|
||||
TextColor = Style.Current.ForegroundGrey,
|
||||
TextColorHighlighted = Style.Current.ForegroundGrey,
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_closeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Color.Red,
|
||||
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
|
||||
|
||||
_minimizeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_minimizeButton.Clicked += () => _window.Minimize();
|
||||
|
||||
_maximizeButton = new Button
|
||||
{
|
||||
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_maximizeButton.Clicked += () =>
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
};
|
||||
|
||||
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
|
||||
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
var maximizeText = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
|
||||
if (_maximizeButton.Text != maximizeText)
|
||||
_maximizeButton.Text = maximizeText;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowClosed()
|
||||
{
|
||||
if (_window != null)
|
||||
{
|
||||
_window.HitTest -= OnHitTest;
|
||||
_window = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform hit test on the window.
|
||||
/// </summary>
|
||||
/// <param name="mouse">The mouse position</param>
|
||||
/// <returns>The hit code for given position.</returns>
|
||||
protected virtual WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
if (_window.IsMinimized)
|
||||
return WindowHitCodes.NoWhere;
|
||||
|
||||
var dpiScale = _window.DpiScale;
|
||||
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
|
||||
if (!_window.IsMaximized)
|
||||
{
|
||||
var winSize = _window.Size;
|
||||
|
||||
// Distance from which the mouse is considered to be on the border/corner
|
||||
float distance = 5.0f * dpiScale;
|
||||
|
||||
if (pos.Y > winSize.Y - distance && pos.X < distance)
|
||||
return WindowHitCodes.BottomLeft;
|
||||
|
||||
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.BottomRight;
|
||||
|
||||
if (pos.Y < distance && pos.X < distance)
|
||||
return WindowHitCodes.TopLeft;
|
||||
|
||||
if (pos.Y < distance && pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.TopRight;
|
||||
|
||||
if (pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.Right;
|
||||
|
||||
if (pos.X < distance)
|
||||
return WindowHitCodes.Left;
|
||||
|
||||
if (pos.Y < distance)
|
||||
return WindowHitCodes.Top;
|
||||
|
||||
if (pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.Bottom;
|
||||
}
|
||||
|
||||
var controlUnderMouse = GetChildAt(pos, control => control != _title);
|
||||
if (_title.Bounds.Contains(pos) && controlUnderMouse == null)
|
||||
return WindowHitCodes.Caption;
|
||||
|
||||
return WindowHitCodes.Client;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
// These may not work with main window due to SDL not passing mouse events
|
||||
// when interacting with hit tests on caption area...
|
||||
|
||||
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
return true;
|
||||
}
|
||||
else if (Icon.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
_window.Close(ClosingReason.User);
|
||||
return true;
|
||||
}
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutAfterChildren()
|
||||
{
|
||||
// Calculate extents for title bounds area excluding the icon and main menu area
|
||||
float x = 0;
|
||||
|
||||
// Icon
|
||||
if (_icon != null)
|
||||
{
|
||||
_icon.X = x;
|
||||
_icon.Size = new Float2(Height);
|
||||
x += _icon.Width;
|
||||
}
|
||||
|
||||
// Main menu if present
|
||||
if (Parent.GetChild<MainMenu>() is MainMenu mainMenu)
|
||||
{
|
||||
for (int i = 0; i < mainMenu.Children.Count; i++)
|
||||
{
|
||||
var c = mainMenu.Children[i];
|
||||
if (c is MainMenuButton b && c.Visible)
|
||||
{
|
||||
b.Bounds = new Rectangle(x, 0, b.Width, Height);
|
||||
x += b.Width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons
|
||||
float rightMostButtonX = Width;
|
||||
if (_closeButton != null)
|
||||
{
|
||||
_closeButton.Height = Height;
|
||||
_closeButton.X = rightMostButtonX - _closeButton.Width;
|
||||
rightMostButtonX = _closeButton.X;
|
||||
}
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
_maximizeButton.Height = Height;
|
||||
_maximizeButton.X = rightMostButtonX - _maximizeButton.Width;
|
||||
rightMostButtonX = _maximizeButton.X;
|
||||
}
|
||||
if (_minimizeButton != null)
|
||||
{
|
||||
_minimizeButton.Height = Height;
|
||||
_minimizeButton.X = rightMostButtonX - _minimizeButton.Width;
|
||||
rightMostButtonX = _minimizeButton.X;
|
||||
}
|
||||
|
||||
// Title
|
||||
if (_title != null)
|
||||
{
|
||||
_title.Text = _window.Title;
|
||||
_title.Bounds = new Rectangle(x, 0, rightMostButtonX - x, Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
DrawBorders();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw borders around the window.
|
||||
/// </summary>
|
||||
public virtual void DrawBorders()
|
||||
{
|
||||
var win = RootWindow.Window;
|
||||
if (win.IsMaximized)
|
||||
return;
|
||||
|
||||
if (Editor.Instance.UI.StatusBar == null)
|
||||
return;
|
||||
|
||||
const float thickness = 1.0f;
|
||||
Color color = Editor.Instance.UI.StatusBar.StatusColor;
|
||||
Rectangle rect = new Rectangle(thickness * 0.5f, thickness * 0.5f, Parent.Width - thickness, Parent.Height - thickness);
|
||||
Render2D.DrawRectangle(rect, color);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_window != null)
|
||||
{
|
||||
_window.Closed -= OnWindowClosed;
|
||||
OnWindowClosed();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,10 +402,11 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
|
||||
break;
|
||||
}
|
||||
}
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
for (const auto& e : inputEvents)
|
||||
{
|
||||
auto window = e.Target ? e.Target : defaultWindow;
|
||||
if (!window)
|
||||
if (!window || window->IsClosed())
|
||||
continue;
|
||||
switch (e.Type)
|
||||
{
|
||||
@@ -435,12 +436,14 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
|
||||
case InputDevice::EventType::MouseMove:
|
||||
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
|
||||
break;
|
||||
case InputDevice::EventType::MouseMoveRelative:
|
||||
window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative);
|
||||
break;
|
||||
case InputDevice::EventType::MouseLeave:
|
||||
window->OnMouseLeave();
|
||||
break;
|
||||
}
|
||||
}
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
}
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
Array<Window*, InlinedAllocation<32>> windows;
|
||||
|
||||
@@ -1013,7 +1013,7 @@ namespace FlaxEditor.Modules
|
||||
ContentItem item;
|
||||
if (path.EndsWith(".cs"))
|
||||
item = new CSharpScriptItem(path);
|
||||
else if (path.EndsWith(".cpp") || path.EndsWith(".h"))
|
||||
else if (path.EndsWith(".cpp") || path.EndsWith(".h") || path.EndsWith(".c") || path.EndsWith(".hpp"))
|
||||
item = new CppScriptItem(path);
|
||||
else if (path.EndsWith(".shader") || path.EndsWith(".hlsl"))
|
||||
item = new ShaderSourceItem(path);
|
||||
|
||||
@@ -222,7 +222,7 @@ namespace FlaxEditor.Modules
|
||||
outputExtension = extension;
|
||||
|
||||
// Check if can place source files here
|
||||
if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h"))
|
||||
if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h" || extension == ".c" || extension == ".hpp"))
|
||||
{
|
||||
// Error
|
||||
Editor.LogWarning(string.Format("Cannot import \'{0}\' to \'{1}\'. The target directory cannot have scripts.", inputPath, targetLocation.Node.Path));
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace FlaxEditor.Modules
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnPlayBegin()
|
||||
public override void OnPlayBeginning()
|
||||
{
|
||||
Editor.Windows.FlashMainWindow();
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using DockHintWindow = FlaxEditor.GUI.Docking.DockHintWindow;
|
||||
using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.Options;
|
||||
@@ -29,6 +28,40 @@ namespace FlaxEditor.Modules
|
||||
/// <seealso cref="FlaxEditor.Modules.EditorModule" />
|
||||
public sealed class UIModule : EditorModule
|
||||
{
|
||||
private class MainWindowDecorations : WindowDecorations
|
||||
{
|
||||
public MainWindowDecorations(RootControl window, bool iconOnly)
|
||||
: base(window, iconOnly)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (base.OnKeyDown(key))
|
||||
return true;
|
||||
|
||||
// Fallback to the edit window for shortcuts
|
||||
var editor = Editor.Instance;
|
||||
return editor.Windows.EditWin.InputActions.Process(editor, this, key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void DrawBorders()
|
||||
{
|
||||
// Draw main window borders if using a custom style
|
||||
var win = RootWindow.Window;
|
||||
if (win.IsMaximized)
|
||||
return;
|
||||
|
||||
var color = Editor.Instance.UI.StatusBar.StatusColor;
|
||||
var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight);
|
||||
Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color);
|
||||
Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color);
|
||||
Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color);
|
||||
}
|
||||
}
|
||||
|
||||
private struct Status
|
||||
{
|
||||
public int ID;
|
||||
@@ -45,7 +78,7 @@ namespace FlaxEditor.Modules
|
||||
private bool _progressFailed;
|
||||
|
||||
ContextMenuSingleSelectGroup<int> _numberOfClientsGroup = new ContextMenuSingleSelectGroup<int>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Defines a viewport scaling option.
|
||||
/// </summary>
|
||||
@@ -66,7 +99,7 @@ namespace FlaxEditor.Modules
|
||||
/// </summary>
|
||||
Aspect = 1,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The name.
|
||||
/// </summary>
|
||||
@@ -82,7 +115,7 @@ namespace FlaxEditor.Modules
|
||||
/// </summary>
|
||||
public Int2 Size;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The default viewport scaling options.
|
||||
/// </summary>
|
||||
@@ -153,6 +186,11 @@ namespace FlaxEditor.Modules
|
||||
/// </summary>
|
||||
public MainMenu MainMenu;
|
||||
|
||||
/// <summary>
|
||||
/// The window decorations (title bar with buttons)
|
||||
/// </summary>
|
||||
public WindowDecorations WindowDecorations;
|
||||
|
||||
/// <summary>
|
||||
/// The tool strip control.
|
||||
/// </summary>
|
||||
@@ -448,7 +486,7 @@ namespace FlaxEditor.Modules
|
||||
|
||||
// Update window background
|
||||
mainWindow.BackgroundColor = Style.Current.Background;
|
||||
|
||||
|
||||
InitViewportScaleOptions();
|
||||
|
||||
InitSharedMenus();
|
||||
@@ -456,19 +494,11 @@ namespace FlaxEditor.Modules
|
||||
InitToolstrip(mainWindow);
|
||||
InitStatusBar(mainWindow);
|
||||
InitDockPanel(mainWindow);
|
||||
InitWindowDecorations(mainWindow);
|
||||
|
||||
Editor.Options.OptionsChanged += OnOptionsChanged;
|
||||
|
||||
// Add dummy control for drawing the main window borders if using a custom style
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Editor.Options.Options.Interface.UseNativeWindowSystem)
|
||||
#endif
|
||||
{
|
||||
mainWindow.AddChild(new CustomWindowBorderControl
|
||||
{
|
||||
Size = Float2.Zero,
|
||||
});
|
||||
}
|
||||
mainWindow.PerformLayout(true);
|
||||
}
|
||||
|
||||
private void InitViewportScaleOptions()
|
||||
@@ -506,7 +536,7 @@ namespace FlaxEditor.Modules
|
||||
Size = new Int2(2560, 1440),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (Editor.Instance.ProjectCache.TryGetCustomData("CustomViewportScalingOptions", out string data))
|
||||
{
|
||||
CustomViewportScaleOptions = JsonSerializer.Deserialize<List<ViewportScaleOption>>(data);
|
||||
@@ -519,7 +549,7 @@ namespace FlaxEditor.Modules
|
||||
public void SaveCustomViewportScalingOptions()
|
||||
{
|
||||
var customOptions = JsonSerializer.Serialize(CustomViewportScaleOptions);
|
||||
Editor.Instance.ProjectCache.SetCustomData("CustomViewportScalingOptions", customOptions);
|
||||
Editor.Instance.ProjectCache.SetCustomData("CustomViewportScalingOptions", customOptions);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -540,23 +570,6 @@ namespace FlaxEditor.Modules
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomWindowBorderControl : Control
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
var win = RootWindow.Window;
|
||||
if (win.IsMaximized)
|
||||
return;
|
||||
|
||||
var color = Editor.Instance.UI.StatusBar.StatusColor;
|
||||
var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight);
|
||||
Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color);
|
||||
Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color);
|
||||
Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnEndInit()
|
||||
{
|
||||
@@ -588,13 +601,6 @@ namespace FlaxEditor.Modules
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnExit()
|
||||
{
|
||||
// Cleanup dock panel hint proxy windows (Flax will destroy them by var but it's better to clear them earlier)
|
||||
DockHintWindow.Proxy.Dispose();
|
||||
}
|
||||
|
||||
private IColorPickerDialog ShowPickColorDialog(Control targetControl, Color initialValue, ColorValueBox.ColorPickerEvent colorChanged, ColorValueBox.ColorPickerClosedEvent pickerClosed, bool useDynamicEditing)
|
||||
{
|
||||
var dialog = new ColorPickerDialog(initialValue, colorChanged, pickerClosed, useDynamicEditing);
|
||||
@@ -648,10 +654,12 @@ namespace FlaxEditor.Modules
|
||||
|
||||
private void InitMainMenu(RootControl mainWindow)
|
||||
{
|
||||
MainMenu = new MainMenu(mainWindow)
|
||||
MainMenu = new MainMenu()
|
||||
{
|
||||
Parent = mainWindow
|
||||
};
|
||||
if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
|
||||
MainMenu.Height = 28;
|
||||
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
@@ -770,7 +778,7 @@ namespace FlaxEditor.Modules
|
||||
MenuWindow = MainMenu.AddButton("Window");
|
||||
cm = MenuWindow.ContextMenu;
|
||||
cm.VisibleChanged += OnMenuWindowVisibleChanged;
|
||||
cm.AddButton("Content", inputOptions.ContentWindow,Editor.Windows.ContentWin.FocusOrShow);
|
||||
cm.AddButton("Content", inputOptions.ContentWindow, Editor.Windows.ContentWin.FocusOrShow);
|
||||
cm.AddButton("Scene", inputOptions.SceneWindow, Editor.Windows.SceneWin.FocusOrShow);
|
||||
cm.AddButton("Toolbox", inputOptions.ToolboxWindow, Editor.Windows.ToolboxWin.FocusOrShow);
|
||||
cm.AddButton("Properties", inputOptions.PropertiesWindow, Editor.Windows.PropertiesWin.FocusOrShow);
|
||||
@@ -803,6 +811,23 @@ namespace FlaxEditor.Modules
|
||||
cm.AddButton("Information about Flax", () => new AboutDialog().Show());
|
||||
}
|
||||
|
||||
private void InitWindowDecorations(RootControl mainWindow)
|
||||
{
|
||||
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
|
||||
string driver = string.Empty;
|
||||
#if PLATFORM_LINUX
|
||||
driver = LinuxPlatform.DisplayServer;
|
||||
if (!string.IsNullOrEmpty(driver))
|
||||
driver = $" ({driver})";
|
||||
#endif
|
||||
|
||||
WindowDecorations = new MainWindowDecorations(mainWindow, !Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
|
||||
{
|
||||
Parent = mainWindow,
|
||||
IconTooltipText = $"{mainWindow.RootWindow.Title}\nVersion {Globals.EngineVersion}\nConfiguration {configuration}\nGraphics {GPUDevice.Instance.RendererType}{driver}",
|
||||
};
|
||||
}
|
||||
|
||||
private void OnOptionsChanged(EditorOptions options)
|
||||
{
|
||||
var inputOptions = options.Input;
|
||||
@@ -1227,6 +1252,7 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
// Clear UI references (GUI cannot be used after window closing)
|
||||
MainMenu = null;
|
||||
WindowDecorations = null;
|
||||
ToolStrip = null;
|
||||
MasterPanel = null;
|
||||
StatusBar = null;
|
||||
|
||||
@@ -10,7 +10,6 @@ using System.Text;
|
||||
using System.Xml;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.GUI.Dialogs;
|
||||
using FlaxEditor.GUI.Docking;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEditor.Windows.Profiler;
|
||||
@@ -763,17 +762,19 @@ namespace FlaxEditor.Modules
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = "Flax Editor";
|
||||
settings.Size = Platform.DesktopSize * 0.75f;
|
||||
settings.MinimumSize = new Float2(200, 150);
|
||||
settings.StartPosition = WindowStartPosition.CenterScreen;
|
||||
settings.ShowAfterFirstPaint = true;
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
|
||||
{
|
||||
settings.HasBorder = false;
|
||||
|
||||
#if PLATFORM_WINDOWS && !PLATFORM_SDL
|
||||
// Skip OS sizing frame and implement it using LeftButtonHit
|
||||
settings.HasSizingFrame = false;
|
||||
#endif
|
||||
}
|
||||
#elif PLATFORM_LINUX
|
||||
#if PLATFORM_LINUX && !PLATFORM_SDL
|
||||
settings.HasBorder = false;
|
||||
#endif
|
||||
MainWindow = Platform.CreateWindow(ref settings);
|
||||
|
||||
@@ -85,10 +85,12 @@ namespace FlaxEditor.Options
|
||||
/// Never show the close button.
|
||||
/// </summary>
|
||||
Never,
|
||||
|
||||
/// <summary>
|
||||
/// Show the close button on tabs that are currently selected.
|
||||
/// </summary>
|
||||
SelectedTab,
|
||||
|
||||
/// <summary>
|
||||
/// Show the close button on all tabs that can be closed.
|
||||
/// </summary>
|
||||
@@ -179,6 +181,34 @@ namespace FlaxEditor.Options
|
||||
GameWindowThenRestore,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options for type of window decorations to use.
|
||||
/// </summary>
|
||||
public enum WindowDecorationsType
|
||||
{
|
||||
/// <summary>
|
||||
/// Determined automatically based on the system and any known compatibility issues with native decorations.
|
||||
/// </summary>
|
||||
Auto,
|
||||
|
||||
/// <summary>
|
||||
/// Automatically choose most compatible window decorations for child windows, prefer custom decorations on main window.
|
||||
/// </summary>
|
||||
[EditorDisplay(Name = "Auto (Child Only)")]
|
||||
AutoChildOnly,
|
||||
|
||||
/// <summary>
|
||||
/// Use native system window decorations on all windows.
|
||||
/// </summary>
|
||||
Native,
|
||||
|
||||
/// <summary>
|
||||
/// Use custom client-side window decorations on all windows.
|
||||
/// </summary>
|
||||
[EditorDisplay(Name = "Client-side")]
|
||||
ClientSide,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
|
||||
/// </summary>
|
||||
@@ -268,7 +298,14 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Interface"), EditorOrder(322)]
|
||||
public bool ScrollToScriptOnAdd { get; set; } = true;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_SDL
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether use native window title bar decorations in child windows. Editor restart required.
|
||||
/// </summary>
|
||||
[DefaultValue(WindowDecorationsType.AutoChildOnly)]
|
||||
[EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar decorations. Editor restart required.")]
|
||||
public WindowDecorationsType WindowDecorations { get; set; } = WindowDecorationsType.AutoChildOnly;
|
||||
#elif PLATFORM_WINDOWS
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether use native window title bar. Editor restart required.
|
||||
/// </summary>
|
||||
@@ -277,7 +314,7 @@ namespace FlaxEditor.Options
|
||||
public bool UseNativeWindowSystem { get; set; } = false;
|
||||
#endif
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_SDL || PLATFORM_WINDOWS
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a window containing a single tabs hides the tab bar. Editor restart recommended.
|
||||
/// </summary>
|
||||
@@ -287,7 +324,7 @@ namespace FlaxEditor.Options
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating wether the minum tab width should be used. Editor restart required.
|
||||
/// Gets or sets a value indicating whether the minimum tab width should be used. Editor restart required.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorDisplay("Tabs & Windows"), EditorOrder(99)]
|
||||
@@ -483,7 +520,7 @@ namespace FlaxEditor.Options
|
||||
[DefaultValue(1), Range(1, 4)]
|
||||
[EditorDisplay("Cook & Run"), EditorOrder(600)]
|
||||
public int NumberOfGameClientsToLaunch = 1;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the build configuration to use when using Cook and Run option in the editor.
|
||||
/// </summary>
|
||||
@@ -498,7 +535,7 @@ namespace FlaxEditor.Options
|
||||
public float ConnectionCurvature { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates wether the context menu description panel is shown or not.
|
||||
/// Gets or sets a value that indicates whether the context menu description panel is shown or not.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Visject"), EditorOrder(550), Tooltip("Shows/hides the description panel in visual scripting context menu.")]
|
||||
|
||||
@@ -121,9 +121,13 @@ void ScriptsBuilderImpl::sourceDirEvent(const String& path, FileSystemAction act
|
||||
// Discard non-source files or generated files
|
||||
if ((!path.EndsWith(TEXT(".cs")) &&
|
||||
!path.EndsWith(TEXT(".cpp")) &&
|
||||
!path.EndsWith(TEXT(".c")) &&
|
||||
!path.EndsWith(TEXT(".hpp")) &&
|
||||
!path.EndsWith(TEXT(".h"))) ||
|
||||
path.EndsWith(TEXT(".Gen.cs")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ScopeLock scopeLock(_locker);
|
||||
_lastSourceCodeEdited = DateTime::Now();
|
||||
|
||||
@@ -163,6 +163,7 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
}
|
||||
}
|
||||
surface.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "ScreenUtilities.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
|
||||
Delegate<Color32> ScreenUtilities::PickColorDone;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#pragma comment(lib, "Gdi32.lib")
|
||||
|
||||
static HHOOK MouseCallbackHook;
|
||||
|
||||
LRESULT CALLBACK OnScreenUtilsMouseCallback(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
|
||||
{
|
||||
if (nCode >= 0 && wParam == WM_LBUTTONDOWN)
|
||||
{
|
||||
UnhookWindowsHookEx(MouseCallbackHook);
|
||||
|
||||
// Push event with the picked color
|
||||
const Float2 cursorPos = Platform::GetMousePosition();
|
||||
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
|
||||
ScreenUtilities::PickColorDone(colorPicked);
|
||||
return 1;
|
||||
}
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
HDC deviceContext = GetDC(NULL);
|
||||
COLORREF color = GetPixel(deviceContext, (int)pos.X, (int)pos.Y);
|
||||
ReleaseDC(NULL, deviceContext);
|
||||
return Color32(GetRValue(color), GetGValue(color), GetBValue(color), 255);
|
||||
}
|
||||
|
||||
void ScreenUtilities::PickColor()
|
||||
{
|
||||
MouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, OnScreenUtilsMouseCallback, NULL, NULL);
|
||||
if (MouseCallbackHook == NULL)
|
||||
{
|
||||
LOG(Warning, "Failed to set mouse hook.");
|
||||
LOG(Warning, "Error: {0}", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
#elif PLATFORM_LINUX
|
||||
|
||||
#include "Engine/Platform/Linux/LinuxPlatform.h"
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
|
||||
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
X11::XColor color;
|
||||
|
||||
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
|
||||
int defaultScreen = X11::XDefaultScreen(display);
|
||||
|
||||
X11::XImage* image;
|
||||
image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), (int)pos.X, (int)pos.Y, 1, 1, AllPlanes, XYPixmap);
|
||||
color.pixel = XGetPixel(image, 0, 0);
|
||||
X11::XFree(image);
|
||||
|
||||
X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color);
|
||||
|
||||
Color32 outputColor;
|
||||
outputColor.R = color.red / 256;
|
||||
outputColor.G = color.green / 256;
|
||||
outputColor.B = color.blue / 256;
|
||||
outputColor.A = 255;
|
||||
return outputColor;
|
||||
}
|
||||
|
||||
void OnScreenUtilsXEventCallback(void* eventPtr)
|
||||
{
|
||||
X11::XEvent* event = (X11::XEvent*) eventPtr;
|
||||
X11::Display* display = (X11::Display*)LinuxPlatform::GetXDisplay();
|
||||
if (event->type == ButtonPress)
|
||||
{
|
||||
const Float2 cursorPos = Platform::GetMousePosition();
|
||||
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
|
||||
X11::XUngrabPointer(display, CurrentTime);
|
||||
ScreenUtilities::PickColorDone(colorPicked);
|
||||
LinuxPlatform::xEventRecieved.Unbind(OnScreenUtilsXEventCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenUtilities::PickColor()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
|
||||
X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
|
||||
|
||||
X11::Cursor cursor = XCreateFontCursor(display, 130);
|
||||
int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime);
|
||||
if (grabbedPointer != GrabSuccess)
|
||||
{
|
||||
LOG(Error, "Failed to grab cursor for events.");
|
||||
X11::XFreeCursor(display, cursor);
|
||||
return;
|
||||
}
|
||||
|
||||
X11::XFreeCursor(display, cursor);
|
||||
LinuxPlatform::xEventRecieved.Bind(OnScreenUtilsXEventCallback);
|
||||
}
|
||||
|
||||
#elif PLATFORM_MAC
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
// TODO: implement ScreenUtilities for macOS
|
||||
return { 0, 0, 0, 255 };
|
||||
}
|
||||
|
||||
void ScreenUtilities::PickColor()
|
||||
{
|
||||
// This is what C# calls to start the color picking sequence
|
||||
// This should stop mouse clicks from working for one click, and that click is on the selected color
|
||||
// There is a class called NSColorSample that might implement that for you, but maybe not.
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
#include "Engine/Core/Math/Color32.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
|
||||
/// <summary>
|
||||
/// Platform-dependent screen utilities.
|
||||
/// </summary>
|
||||
API_CLASS(Static) class FLAXENGINE_API ScreenUtilities
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(ScreenUtilities);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pixel color at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="pos">Screen-space coordinate to read.</param>
|
||||
/// <returns>Pixel color at the specified coordinates.</returns>
|
||||
API_FUNCTION() static Color32 GetColorAt(const Float2& pos);
|
||||
|
||||
/// <summary>
|
||||
/// Starts async color picking. Color will be returned through PickColorDone event when the actions ends (user selected the final color with a mouse). When action is active, GetColorAt can be used to read the current value.
|
||||
/// </summary>
|
||||
API_FUNCTION() static void PickColor();
|
||||
|
||||
/// <summary>
|
||||
/// Called when PickColor action is finished.
|
||||
/// </summary>
|
||||
API_EVENT() static Delegate<Color32> PickColorDone;
|
||||
};
|
||||
@@ -1583,5 +1583,37 @@ namespace FlaxEditor.Utilities
|
||||
c = c.Parent;
|
||||
return c as ISceneEditingContext;
|
||||
}
|
||||
|
||||
internal static bool UseCustomWindowDecorations(bool isMainWindow = false)
|
||||
{
|
||||
#if PLATFORM_SDL
|
||||
return Editor.Instance.Options.Options.Interface.WindowDecorations switch
|
||||
{
|
||||
Options.InterfaceOptions.WindowDecorationsType.Auto => !SDLPlatform.SupportsNativeDecorations,
|
||||
Options.InterfaceOptions.WindowDecorationsType.AutoChildOnly => !isMainWindow ? !SDLPlatform.SupportsNativeDecorations : true,
|
||||
Options.InterfaceOptions.WindowDecorationsType.Native => false,
|
||||
Options.InterfaceOptions.WindowDecorationsType.ClientSide => true,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
#elif PLATFORM_WINDOWS
|
||||
return !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static bool HideSingleTabWindowTabBars()
|
||||
{
|
||||
#if PLATFORM_SDL
|
||||
// We should not hide the tab bars if tab handle is the only way to dock the window
|
||||
bool clientSideDecorations = UseCustomWindowDecorations(false);
|
||||
bool draggableDecorations = clientSideDecorations || SDLPlatform.SupportsNativeDecorationDragging;
|
||||
return draggableDecorations && Editor.Instance.Options.Options.Interface.HideSingleTabWindowTabBars;
|
||||
#elif PLATFORM_WINDOWS
|
||||
return Editor.Instance.Options.Options.Interface.HideSingleTabWindowTabBars;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,13 @@ namespace FlaxEditor.Viewport
|
||||
/// <inheritdoc />
|
||||
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
|
||||
|
||||
#if PLATFORM_SDL
|
||||
/// <inheritdoc />
|
||||
public Float2 MouseDelta => FlaxEngine.Input.MousePositionDelta;
|
||||
#else
|
||||
/// <inheritdoc />
|
||||
public Float2 MouseDelta => _mouseDelta;
|
||||
#endif
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Docking;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
@@ -158,18 +159,22 @@ namespace FlaxEditor.Viewport
|
||||
private float _movementSpeed;
|
||||
private float _minMovementSpeed;
|
||||
private float _maxMovementSpeed;
|
||||
#if !PLATFORM_SDL
|
||||
private float _mouseAccelerationScale;
|
||||
private bool _useMouseFiltering;
|
||||
private bool _useMouseAcceleration;
|
||||
#endif
|
||||
|
||||
// Input
|
||||
|
||||
internal bool _disableInputUpdate;
|
||||
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
|
||||
private int _deltaFilteringStep;
|
||||
private Float2 _startPos;
|
||||
#if !PLATFORM_SDL
|
||||
private Float2 _mouseDeltaLast;
|
||||
private int _deltaFilteringStep;
|
||||
private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames];
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The previous input (from the previous update).
|
||||
@@ -522,10 +527,11 @@ namespace FlaxEditor.Viewport
|
||||
: base(task)
|
||||
{
|
||||
_editor = Editor.Instance;
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
_mouseAccelerationScale = 0.1f;
|
||||
_useMouseFiltering = false;
|
||||
_useMouseAcceleration = false;
|
||||
#endif
|
||||
_camera = camera;
|
||||
if (_camera != null)
|
||||
_camera.Viewport = this;
|
||||
@@ -1530,7 +1536,9 @@ namespace FlaxEditor.Viewport
|
||||
// Hide cursor and start tracking mouse movement
|
||||
win.StartTrackingMouse(false);
|
||||
win.Cursor = CursorType.Hidden;
|
||||
win.MouseMoveRelative += OnMouseMoveRelative;
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Center mouse position if it's too close to the edge
|
||||
var size = Size;
|
||||
var center = Float2.Round(size * 0.5f);
|
||||
@@ -1539,6 +1547,7 @@ namespace FlaxEditor.Viewport
|
||||
_viewMousePos = center;
|
||||
win.MousePosition = PointToWindow(_viewMousePos);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1550,6 +1559,7 @@ namespace FlaxEditor.Viewport
|
||||
// Restore cursor and stop tracking mouse movement
|
||||
win.Cursor = CursorType.Default;
|
||||
win.EndTrackingMouse();
|
||||
win.MouseMoveRelative -= OnMouseMoveRelative;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1631,18 +1641,15 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Get parent window
|
||||
var win = (WindowRootControl)Root;
|
||||
|
||||
// Get current mouse position in the view
|
||||
if (win.IsFocused)
|
||||
{
|
||||
// When the window is not focused, the position in window does not return sane values
|
||||
Float2 pos = PointFromWindow(win.MousePosition);
|
||||
if (!float.IsInfinity(pos.LengthSquared))
|
||||
_viewMousePos = pos;
|
||||
// Get current mouse position in the view
|
||||
_viewMousePos = PointFromWindow(win.MousePosition);
|
||||
}
|
||||
|
||||
// Update input
|
||||
var window = win.Window;
|
||||
var canUseInput = window != null && window.IsFocused && window.IsForegroundWindow;
|
||||
var canUseInput = window != null && window.IsFocused && window.IsForegroundWindow && !WindowDragHelper.IsDragActive;
|
||||
{
|
||||
// Get input buttons and keys (skip if viewport has no focus or mouse is over a child control)
|
||||
var isViewportControllingMouse = canUseInput && IsControllingMouse;
|
||||
@@ -1654,9 +1661,17 @@ namespace FlaxEditor.Viewport
|
||||
else
|
||||
EndMouseCapture();
|
||||
}
|
||||
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
|
||||
|
||||
_prevInput = _input;
|
||||
#if PLATFORM_SDL
|
||||
bool useMouse = IsControllingMouse || ContainsPoint(ref _viewMousePos) || _prevInput.IsControllingMouse;
|
||||
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
|
||||
if (_prevInput.IsControllingMouse)
|
||||
hit = null;
|
||||
#else
|
||||
bool useMouse = IsControllingMouse || ContainsPoint(ref _viewMousePos);
|
||||
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
|
||||
#endif
|
||||
if (canUseInput && ContainsFocus && hit == null)
|
||||
_input.Gather(win.Window, useMouse, ref _prevInput);
|
||||
else
|
||||
@@ -1769,6 +1784,10 @@ namespace FlaxEditor.Viewport
|
||||
if (_input.IsControlDown)
|
||||
moveDelta *= 0.3f;
|
||||
|
||||
#if PLATFORM_SDL
|
||||
var mouseDelta = _mouseDelta;
|
||||
_mouseDelta = Float2.Zero;
|
||||
#else
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
var offset = _viewMousePos - _startPos;
|
||||
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown)
|
||||
@@ -1810,6 +1829,7 @@ namespace FlaxEditor.Viewport
|
||||
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
|
||||
_mouseDeltaLast = currentDelta;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update
|
||||
moveDelta *= dt * (60.0f * 4.0f);
|
||||
@@ -1818,12 +1838,14 @@ namespace FlaxEditor.Viewport
|
||||
mouseDelta *= new Float2(1, -1);
|
||||
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Move mouse back to the root position
|
||||
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown))
|
||||
{
|
||||
var center = PointToWindow(_startPos);
|
||||
win.MousePosition = center;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Change Ortho size on mouse scroll
|
||||
if (_isOrtho && !rmbWheel)
|
||||
@@ -1835,6 +1857,8 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
else
|
||||
{
|
||||
#if PLATFORM_SDL
|
||||
#else
|
||||
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
|
||||
{
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
@@ -1849,6 +1873,7 @@ namespace FlaxEditor.Viewport
|
||||
_mouseDelta = Float2.Zero;
|
||||
}
|
||||
_mouseDeltaLast = Float2.Zero;
|
||||
#endif
|
||||
|
||||
if (ContainsFocus)
|
||||
{
|
||||
@@ -1898,6 +1923,11 @@ namespace FlaxEditor.Viewport
|
||||
_input.MouseWheelDelta = 0;
|
||||
}
|
||||
|
||||
private void OnMouseMoveRelative(ref Float2 motion)
|
||||
{
|
||||
_mouseDelta += motion;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
@@ -1963,7 +1993,8 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
if (_isControllingMouse)
|
||||
{
|
||||
OnControlMouseEnd(RootWindow.Window);
|
||||
if (RootWindow?.Window != null)
|
||||
OnControlMouseEnd(RootWindow.Window);
|
||||
_isControllingMouse = false;
|
||||
_isVirtualMouseRightDown = false;
|
||||
}
|
||||
|
||||
@@ -662,16 +662,25 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Don't allow rubber band selection when gizmo is controlling mouse, vertex painting mode, or cloth painting is enabled
|
||||
bool canStart = !(IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown) &&
|
||||
Gizmos?.Active is TransformGizmo && !Gizmos.Active.IsControllingMouse;
|
||||
Gizmos?.Active is TransformGizmo;
|
||||
_rubberBandSelector.TryCreateRubberBand(canStart, _viewMousePos);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnControlMouseBegin(Window win)
|
||||
{
|
||||
_rubberBandSelector.ReleaseRubberBandSelection();
|
||||
|
||||
base.OnControlMouseBegin(win);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnLeftMouseButtonDown()
|
||||
{
|
||||
base.OnLeftMouseButtonDown();
|
||||
|
||||
_rubberBandSelector.TryStartingRubberBandSelection(_viewMousePos);
|
||||
if (!IsAltKeyDown)
|
||||
_rubberBandSelector.TryStartingRubberBandSelection(_viewMousePos);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -130,6 +130,9 @@ namespace FlaxEditor.Windows
|
||||
"Mono Project - www.mono-project.com",
|
||||
#if USE_NETCORE
|
||||
".NET - www.dotnet.microsoft.com",
|
||||
#endif
|
||||
#if PLATFORM_SDL
|
||||
"Simple DirectMedia Layer - www.libsdl.org",
|
||||
#endif
|
||||
"FreeType Project - www.freetype.org",
|
||||
"Assimp - www.assimp.sourceforge.net",
|
||||
|
||||
@@ -874,7 +874,7 @@ namespace FlaxEditor.Windows
|
||||
_cursorVisible = Screen.CursorVisible;
|
||||
_cursorLockMode = Screen.CursorLock;
|
||||
Screen.CursorVisible = true;
|
||||
if (Screen.CursorLock == CursorLockMode.Clipped)
|
||||
if (Screen.CursorLock == CursorLockMode.Clipped || Screen.CursorLock == CursorLockMode.Locked)
|
||||
Screen.CursorLock = CursorLockMode.None;
|
||||
|
||||
// Defocus
|
||||
@@ -963,8 +963,11 @@ namespace FlaxEditor.Windows
|
||||
|
||||
if (Editor.StateMachine.IsPlayMode && !Editor.StateMachine.PlayingState.IsPaused)
|
||||
{
|
||||
// Make sure the cursor is always in the viewport when cursor is locked
|
||||
bool forceCenter = _cursorLockMode != CursorLockMode.None && !IsMouseOver;
|
||||
|
||||
// Center mouse in play mode
|
||||
if (CenterMouseOnFocus)
|
||||
if (CenterMouseOnFocus || forceCenter)
|
||||
{
|
||||
var center = PointToWindow(Size * 0.5f);
|
||||
Root.MousePosition = center;
|
||||
@@ -989,9 +992,10 @@ namespace FlaxEditor.Windows
|
||||
_cursorVisible = Screen.CursorVisible;
|
||||
_cursorLockMode = Screen.CursorLock;
|
||||
|
||||
// Restore cursor visibility (could be hidden by the game)
|
||||
// Restore cursor state, could be hidden or locked by the game
|
||||
if (!_cursorVisible)
|
||||
Screen.CursorVisible = true;
|
||||
Screen.CursorLock = CursorLockMode.None;
|
||||
|
||||
if (Editor.IsPlayMode && IsDocked && IsSelected && RootWindow.FocusedControl == null)
|
||||
{
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace FlaxEditor.Windows
|
||||
private Tree _tree;
|
||||
private Panel _sceneTreePanel;
|
||||
private bool _isUpdatingSelection;
|
||||
private bool _isMouseDown;
|
||||
private bool _blockSceneTreeScroll = false;
|
||||
|
||||
private DragAssets _dragAssets;
|
||||
@@ -374,10 +373,7 @@ namespace FlaxEditor.Windows
|
||||
return true;
|
||||
|
||||
if (buttons == MouseButton.Right)
|
||||
{
|
||||
_isMouseDown = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -388,10 +384,8 @@ namespace FlaxEditor.Windows
|
||||
if (base.OnMouseUp(location, buttons))
|
||||
return true;
|
||||
|
||||
if (_isMouseDown && buttons == MouseButton.Right)
|
||||
if (buttons == MouseButton.Right)
|
||||
{
|
||||
_isMouseDown = false;
|
||||
|
||||
if (Editor.StateMachine.CurrentState.CanEditScene)
|
||||
{
|
||||
// Show context menu
|
||||
@@ -416,14 +410,6 @@ namespace FlaxEditor.Windows
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLostFocus()
|
||||
{
|
||||
_isMouseDown = false;
|
||||
|
||||
base.OnLostFocus();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ const Char* SplashScreenQuotes[] =
|
||||
#elif PLATFORM_LINUX
|
||||
TEXT("Try it on a Raspberry"),
|
||||
TEXT("Trying to exit vim"),
|
||||
TEXT("Sudo flax --loadproject"),
|
||||
TEXT("sudo flax --project HelloWorld.flaxproj"),
|
||||
#elif PLATFORM_MAC
|
||||
TEXT("don't compare Macbooks to oranges."),
|
||||
TEXT("Why does macbook heat up?\nBecause it doesn't have windows"),
|
||||
@@ -105,6 +105,7 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("You have my bow.\nAnd my axe!"),
|
||||
TEXT("To the bridge of Khazad-dum."),
|
||||
TEXT("One ring to rule them all.\nOne ring to find them."),
|
||||
TEXT("Where there's a whip, there's a way."),
|
||||
TEXT("That's what she said"),
|
||||
TEXT("We could be compiling shaders here"),
|
||||
TEXT("Hello There"),
|
||||
@@ -166,7 +167,7 @@ void SplashScreen::Show()
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowDragAndDrop = false;
|
||||
settings.IsTopmost = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.Type = WindowType::Utility;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.ShowAfterFirstPaint = true;
|
||||
settings.StartPosition = WindowStartPosition::CenterScreen;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "CommandLine.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Utilities.h"
|
||||
#include "Engine/Core/Types/StringView.h"
|
||||
#include <iostream>
|
||||
|
||||
CommandLine::OptionsData CommandLine::Options;
|
||||
@@ -76,6 +77,8 @@ bool CommandLine::Parse(const Char* cmdLine)
|
||||
Char* argStart;
|
||||
Char* argEnd;
|
||||
int32 len;
|
||||
(void)argStart;
|
||||
(void)argEnd;
|
||||
|
||||
#define PARSE_BOOL_SWITCH(text, field) \
|
||||
pos = (Char*)StringUtils::FindIgnoreCase(buffer.Get(), TEXT(text)); \
|
||||
@@ -145,6 +148,12 @@ bool CommandLine::Parse(const Char* cmdLine)
|
||||
PARSE_BOOL_SWITCH("-monolog ", MonoLog);
|
||||
PARSE_BOOL_SWITCH("-mute ", Mute);
|
||||
PARSE_BOOL_SWITCH("-lowdpi ", LowDPI);
|
||||
|
||||
#if PLATFORM_LINUX && PLATFORM_SDL
|
||||
PARSE_BOOL_SWITCH("-wayland ", Wayland);
|
||||
PARSE_BOOL_SWITCH("-x11 ", X11);
|
||||
#endif
|
||||
|
||||
#if USE_EDITOR
|
||||
PARSE_BOOL_SWITCH("-clearcache ", ClearCache);
|
||||
PARSE_BOOL_SWITCH("-clearcooker ", ClearCookerCache);
|
||||
@@ -163,3 +172,63 @@ bool CommandLine::Parse(const Char* cmdLine)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CommandLine::ParseArguments(const StringView& cmdLine, Array<StringAnsi>& arguments)
|
||||
{
|
||||
int32 start = 0;
|
||||
int32 quotesStart = -1;
|
||||
int32 length = cmdLine.Length();
|
||||
for (int32 i = 0; i < length; i++)
|
||||
{
|
||||
if (cmdLine[i] == ' ' && quotesStart == -1)
|
||||
{
|
||||
int32 count = i - start;
|
||||
if (count > 0)
|
||||
arguments.Add(StringAnsi(cmdLine.Substring(start, count)));
|
||||
start = i + 1;
|
||||
}
|
||||
else if (cmdLine[i] == '\"')
|
||||
{
|
||||
if (quotesStart >= 0)
|
||||
{
|
||||
if (i + 1 < length && cmdLine[i + 1] != ' ')
|
||||
{
|
||||
// End quotes are in the middle of the current word,
|
||||
// continue until the end of the current word.
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 offset = 1;
|
||||
if (quotesStart == start && cmdLine[start] == '\"')
|
||||
{
|
||||
// Word starts and ends with quotes, only include the quoted content.
|
||||
quotesStart++;
|
||||
offset--;
|
||||
}
|
||||
else if (quotesStart != start)
|
||||
{
|
||||
// Start quotes in the middle of the word, include the whole word.
|
||||
quotesStart = start;
|
||||
}
|
||||
|
||||
int32 count = i - quotesStart + offset;
|
||||
if (count > 0)
|
||||
arguments.Add(StringAnsi(cmdLine.Substring(quotesStart, count)));
|
||||
start = i + 1;
|
||||
}
|
||||
quotesStart = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
quotesStart = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
const int32 count = length - start;
|
||||
if (count > 0)
|
||||
arguments.Add(StringAnsi(cmdLine.Substring(start, count)));
|
||||
if (quotesStart >= 0)
|
||||
return true; // Missing last closing quote
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Types/Nullable.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
|
||||
/// <summary>
|
||||
/// Command line options helper.
|
||||
@@ -127,6 +128,20 @@ public:
|
||||
/// </summary>
|
||||
Nullable<bool> LowDPI;
|
||||
|
||||
#if PLATFORM_LINUX && PLATFORM_SDL
|
||||
|
||||
/// <summary>
|
||||
/// -wayland (prefer Wayland over X11 as display server)
|
||||
/// </summary>
|
||||
Nullable<bool> Wayland;
|
||||
|
||||
/// <summary>
|
||||
/// -x11 (prefer X11 over Wayland as display server)
|
||||
/// </summary>
|
||||
Nullable<bool> X11;
|
||||
|
||||
#endif
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// -project !path! (Startup project path)
|
||||
@@ -200,4 +215,12 @@ public:
|
||||
/// <param name="cmdLine">The command line.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
static bool Parse(const Char* cmdLine);
|
||||
|
||||
/// <summary>
|
||||
/// Parses the command line arguments string into string list of arguments.
|
||||
/// </summary>
|
||||
/// <param name="cmdLine">The command line.</param>
|
||||
/// <param name="arguments">The parsed arguments</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
static bool ParseArguments(const StringView& cmdLine, Array<StringAnsi>& arguments);
|
||||
};
|
||||
|
||||
@@ -101,6 +101,8 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
CommandLine::Options.Std = true;
|
||||
#endif
|
||||
|
||||
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
|
||||
|
||||
if (Platform::Init())
|
||||
{
|
||||
Platform::Fatal(TEXT("Cannot init platform."));
|
||||
@@ -110,7 +112,6 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
InitProfilerMemory(cmdLine, 1);
|
||||
#endif
|
||||
|
||||
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
|
||||
Time::StartupTime = DateTime::Now();
|
||||
Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory();
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "Engine/Core/Types/Nullable.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/Managed/ManagedEditor.h"
|
||||
@@ -13,10 +15,14 @@
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#endif
|
||||
|
||||
Nullable<bool> Fullscreen;
|
||||
Nullable<Float2> Size;
|
||||
bool CursorVisible = true;
|
||||
CursorLockMode CursorLock = CursorLockMode::None;
|
||||
namespace
|
||||
{
|
||||
Nullable<bool> Fullscreen;
|
||||
Nullable<Float2> Size;
|
||||
bool CursorVisible = true;
|
||||
CursorLockMode CursorLock = CursorLockMode::None;
|
||||
bool LastGameViewportFocus = false;
|
||||
}
|
||||
|
||||
class ScreenService : public EngineService
|
||||
{
|
||||
@@ -101,9 +107,9 @@ void Screen::SetCursorVisible(const bool value)
|
||||
const auto win = Engine::MainWindow;
|
||||
#endif
|
||||
if (win && Engine::HasGameViewportFocus())
|
||||
{
|
||||
win->SetCursor(value ? CursorType::Default : CursorType::Hidden);
|
||||
}
|
||||
else if (win)
|
||||
win->SetCursor(CursorType::Default);
|
||||
CursorVisible = value;
|
||||
}
|
||||
|
||||
@@ -116,21 +122,31 @@ void Screen::SetCursorLock(CursorLockMode mode)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
const auto win = Editor::Managed->GetGameWindow(true);
|
||||
Rectangle bounds(Editor::Managed->GameViewportToScreen(Float2::Zero), Editor::Managed->GetGameWindowSize());
|
||||
if (win)
|
||||
bounds = Rectangle(win->ScreenToClient(bounds.GetTopLeft()), bounds.Size);
|
||||
#else
|
||||
const auto win = Engine::MainWindow;
|
||||
Rectangle bounds = win != nullptr ? win->GetClientBounds() : Rectangle();
|
||||
#endif
|
||||
if (win && mode == CursorLockMode::Clipped)
|
||||
if (win)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
Rectangle bounds(Editor::Managed->GameViewportToScreen(Float2::Zero), Editor::Managed->GetGameWindowSize());
|
||||
#else
|
||||
Rectangle bounds = win->GetClientBounds();
|
||||
#endif
|
||||
win->StartClippingCursor(bounds);
|
||||
}
|
||||
else if (win && CursorLock == CursorLockMode::Clipped)
|
||||
{
|
||||
win->EndClippingCursor();
|
||||
bool inRelativeMode = Input::Mouse->IsRelative();
|
||||
if (mode == CursorLockMode::Clipped)
|
||||
win->StartClippingCursor(bounds);
|
||||
else if (mode == CursorLockMode::Locked)
|
||||
{
|
||||
// Use mouse clip region to restrict the cursor in one spot
|
||||
win->StartClippingCursor(Rectangle(bounds.GetCenter(), Float2(1, 1)));
|
||||
}
|
||||
else if (CursorLock == CursorLockMode::Locked || CursorLock == CursorLockMode::Clipped)
|
||||
win->EndClippingCursor();
|
||||
|
||||
// Enable relative mode when cursor is restricted
|
||||
if (mode != CursorLockMode::None)
|
||||
Input::Mouse->SetRelativeMode(true, win);
|
||||
else if (mode == CursorLockMode::None && inRelativeMode)
|
||||
Input::Mouse->SetRelativeMode(false, win);
|
||||
}
|
||||
CursorLock = mode;
|
||||
}
|
||||
@@ -190,7 +206,11 @@ void ScreenService::Update()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
// Sync current cursor state in Editor (eg. when viewport focus can change)
|
||||
Screen::SetCursorVisible(CursorVisible);
|
||||
const auto win = Editor::Managed->GetGameWindow(true);
|
||||
bool gameViewportFocus = win && Engine::HasGameViewportFocus();
|
||||
if (gameViewportFocus != LastGameViewportFocus)
|
||||
Screen::SetCursorVisible(CursorVisible);
|
||||
LastGameViewportFocus = gameViewportFocus;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ API_CLASS(Static, Attributes="DebugCommand") class FLAXENGINE_API Time
|
||||
friend class Engine;
|
||||
friend class TimeService;
|
||||
friend class PhysicsSettings;
|
||||
friend Window;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "AndroidVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
|
||||
void AndroidVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
{
|
||||
@@ -17,8 +18,10 @@ void AndroidVulkanPlatform::GetDeviceExtensions(Array<const char*>& extensions,
|
||||
extensions.Add(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void AndroidVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void AndroidVulkanPlatform::CreateSurface(Window* window, GPUDeviceVulkan* device, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
ASSERT(window);
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
ASSERT(windowHandle);
|
||||
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR);
|
||||
|
||||
@@ -17,7 +17,7 @@ class AndroidVulkanPlatform : public VulkanPlatformBase
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void GetDeviceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface);
|
||||
static void CreateSurface(Window* window, GPUDeviceVulkan* device, VkInstance instance, VkSurfaceKHR* surface);
|
||||
};
|
||||
|
||||
typedef AndroidVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -217,7 +217,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
|
||||
ASSERT_LOW_LAYER(_backBuffers.Count() == 0);
|
||||
|
||||
// Create platform-dependent surface
|
||||
VulkanPlatform::CreateSurface(windowHandle, GPUDeviceVulkan::Instance, &_surface);
|
||||
VulkanPlatform::CreateSurface(_window, _device, GPUDeviceVulkan::Instance, &_surface);
|
||||
if (_surface == VK_NULL_HANDLE)
|
||||
{
|
||||
LOG(Warning, "Failed to create Vulkan surface.");
|
||||
@@ -356,13 +356,22 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
|
||||
LOG(Error, "Vulkan swapchain dimensions are invalid {}x{} (minImageExtent={}x{}, maxImageExtent={}x{})", width, height, surfProperties.minImageExtent.width, surfProperties.minImageExtent.height, surfProperties.maxImageExtent.width, surfProperties.maxImageExtent.height);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t backbuffersCount = VULKAN_BACK_BUFFERS_COUNT;
|
||||
#if PLATFORM_SDL && PLATFORM_LINUX && USE_EDITOR
|
||||
// Wayland compositor might block one of the backbuffers while the window is minimized or fully occluded,
|
||||
// make sure we have at least 3 backbuffers available so double-buffering can be used while we are blocked.
|
||||
if (Platform::UsesWayland())
|
||||
backbuffersCount = Math::Max<uint32_t>(backbuffersCount, 3);
|
||||
#endif
|
||||
|
||||
ASSERT(surfProperties.minImageCount <= VULKAN_BACK_BUFFERS_COUNT_MAX);
|
||||
VkSwapchainCreateInfoKHR swapChainInfo;
|
||||
RenderToolsVulkan::ZeroStruct(swapChainInfo, VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
|
||||
swapChainInfo.surface = _surface;
|
||||
swapChainInfo.minImageCount = surfProperties.maxImageCount > 0 // A value of 0 means that there is no limit on the number of image
|
||||
? Math::Min<uint32_t>(VULKAN_BACK_BUFFERS_COUNT, surfProperties.maxImageCount)
|
||||
: VULKAN_BACK_BUFFERS_COUNT;
|
||||
? Math::Min<uint32_t>(backbuffersCount, surfProperties.maxImageCount)
|
||||
: backbuffersCount;
|
||||
swapChainInfo.minImageCount = Math::Max<uint32_t>(swapChainInfo.minImageCount, surfProperties.minImageCount);
|
||||
swapChainInfo.minImageCount = Math::Min<uint32_t>(swapChainInfo.minImageCount, VULKAN_BACK_BUFFERS_COUNT_MAX);
|
||||
swapChainInfo.imageFormat = result.format;
|
||||
@@ -381,7 +390,9 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
|
||||
swapChainInfo.presentMode = presentMode;
|
||||
swapChainInfo.clipped = VK_TRUE;
|
||||
swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
|
||||
if (surfProperties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)
|
||||
if (_window->GetSettings().SupportsTransparency && surfProperties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
|
||||
swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
|
||||
else if (surfProperties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)
|
||||
swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
|
||||
// Create swap chain
|
||||
|
||||
@@ -4,62 +4,59 @@
|
||||
|
||||
#include "LinuxVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
|
||||
// Contents of vulkan\vulkan_xlib.h inlined here to prevent typename collisions with engine types due to X11 types
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define VK_KHR_xlib_surface 1
|
||||
#define VK_KHR_XLIB_SURFACE_SPEC_VERSION 6
|
||||
#define VK_KHR_XLIB_SURFACE_EXTENSION_NAME "VK_KHR_xlib_surface"
|
||||
typedef VkFlags VkXlibSurfaceCreateFlagsKHR;
|
||||
#define Display X11::Display
|
||||
#define Window X11::Window
|
||||
#define VisualID X11::VisualID
|
||||
#include "vulkan/vulkan_xlib.h"
|
||||
#undef Display
|
||||
#undef Window
|
||||
#undef VisualID
|
||||
#include "vulkan/vulkan_wayland.h"
|
||||
|
||||
typedef struct VkXlibSurfaceCreateInfoKHR
|
||||
{
|
||||
VkStructureType sType;
|
||||
const void* pNext;
|
||||
VkXlibSurfaceCreateFlagsKHR flags;
|
||||
X11::Display* dpy;
|
||||
X11::Window window;
|
||||
} VkXlibSurfaceCreateInfoKHR;
|
||||
|
||||
typedef VkResult (VKAPI_PTR *PFN_vkCreateXlibSurfaceKHR)(VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||
typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, X11::Display* dpy, X11::VisualID visualID);
|
||||
#ifndef VK_NO_PROTOTYPES
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkCreateXlibSurfaceKHR(
|
||||
VkInstance instance,
|
||||
const VkXlibSurfaceCreateInfoKHR* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkSurfaceKHR* pSurface);
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXlibPresentationSupportKHR(
|
||||
VkPhysicalDevice physicalDevice,
|
||||
uint32_t queueFamilyIndex,
|
||||
Display* dpy,
|
||||
VisualID visualID);
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
//
|
||||
|
||||
// Export X11 surface extension from volk
|
||||
// Export extension from volk
|
||||
extern PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
|
||||
extern PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR;
|
||||
extern PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
|
||||
extern PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR;
|
||||
|
||||
void LinuxVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
{
|
||||
extensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||
extensions.Add(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
||||
extensions.Add(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void LinuxVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void LinuxVulkanPlatform::CreateSurface(Window* window, GPUDeviceVulkan* device, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
VkXlibSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.dpy = (X11::Display*)LinuxPlatform::GetXDisplay();
|
||||
surfaceCreateInfo.dpy = (X11::Display*)Platform::GetXDisplay();
|
||||
surfaceCreateInfo.window = (X11::Window)windowHandle;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateXlibSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
#else
|
||||
SDLWindow* sdlWindow = static_cast<Window*>(window);
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
if (SDLPlatform::UsesWayland())
|
||||
{
|
||||
VkWaylandSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.display = (wl_display*)sdlWindow->GetWaylandDisplay();
|
||||
surfaceCreateInfo.surface = (wl_surface*)windowHandle;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateWaylandSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
else if (SDLPlatform::UsesX11())
|
||||
{
|
||||
VkXlibSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.dpy = (X11::Display*)sdlWindow->GetX11Display();
|
||||
surfaceCreateInfo.window = (X11::Window)windowHandle;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateXlibSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,7 @@ class LinuxVulkanPlatform : public VulkanPlatformBase
|
||||
{
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
static void CreateSurface(Window* window, GPUDeviceVulkan* device, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
};
|
||||
|
||||
typedef LinuxVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -4,21 +4,40 @@
|
||||
|
||||
#include "MacVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include "Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h"
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
void MacVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
{
|
||||
extensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||
extensions.Add(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
|
||||
extensions.Add(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
|
||||
extensions.Add(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void MacVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void MacVulkanPlatform::CreateSurface(Window* window, GPUDeviceVulkan* device, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
NSWindow* window = (NSWindow*)windowHandle;
|
||||
VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK);
|
||||
surfaceCreateInfo.pView = (void*)window.contentView;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
NSWindow* nswindow = (NSWindow*)windowHandle;
|
||||
#if PLATFORM_SDL
|
||||
nswindow.contentView.wantsLayer = YES;
|
||||
nswindow.contentView.layer = [CAMetalLayer layer];
|
||||
#endif
|
||||
if (device->InstanceExtensions.Contains(VK_EXT_METAL_SURFACE_EXTENSION_NAME))
|
||||
{
|
||||
VkMetalSurfaceCreateInfoEXT surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT);
|
||||
surfaceCreateInfo.pLayer = (CAMetalLayer*)nswindow.contentView.layer;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateMetalSurfaceEXT(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
else
|
||||
{
|
||||
VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK);
|
||||
surfaceCreateInfo.pView = (void*)nswindow.contentView;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,7 +18,7 @@ class MacVulkanPlatform : public VulkanPlatformBase
|
||||
{
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
static void CreateSurface(Window* window, GPUDeviceVulkan* device, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
};
|
||||
|
||||
typedef MacVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -17,6 +17,8 @@ enum class VulkanValidationLevel
|
||||
All = 5,
|
||||
};
|
||||
|
||||
class GPUDeviceVulkan;
|
||||
|
||||
/// <summary>
|
||||
/// The base implementation for the Vulkan API platform support.
|
||||
/// </summary>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
|
||||
void Win32VulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
{
|
||||
@@ -13,12 +14,12 @@ void Win32VulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions,
|
||||
extensions.Add(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void Win32VulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void Win32VulkanPlatform::CreateSurface(Window* window, GPUDeviceVulkan* device, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
VkWin32SurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.hinstance = GetModuleHandle(nullptr);
|
||||
surfaceCreateInfo.hwnd = static_cast<HWND>(windowHandle);
|
||||
surfaceCreateInfo.hwnd = static_cast<HWND>(window->GetNativePtr());
|
||||
VALIDATE_VULKAN_RESULT(vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class Win32VulkanPlatform : public VulkanPlatformBase
|
||||
{
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface);
|
||||
static void CreateSurface(Window* window, GPUDeviceVulkan* device, VkInstance instance, VkSurfaceKHR* surface);
|
||||
};
|
||||
|
||||
typedef Win32VulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "iOSVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include <UIKit/UIKit.h>
|
||||
|
||||
void iOSVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
@@ -13,8 +14,10 @@ void iOSVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Ar
|
||||
extensions.Add(VK_MVK_IOS_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void iOSVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void iOSVulkanPlatform::CreateSurface(Window* window, GPUDeviceVulkan* device, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
|
||||
// Create surface on a main UI Thread
|
||||
Function<void()> func = [&windowHandle, &instance, &surface]()
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ class iOSVulkanPlatform : public VulkanPlatformBase
|
||||
{
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
static void CreateSurface(Window* window, GPUDeviceVulkan* device, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
};
|
||||
|
||||
typedef iOSVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -79,6 +79,7 @@ Delegate<const Float2&, MouseButton> Input::MouseUp;
|
||||
Delegate<const Float2&, MouseButton> Input::MouseDoubleClick;
|
||||
Delegate<const Float2&, float> Input::MouseWheel;
|
||||
Delegate<const Float2&> Input::MouseMove;
|
||||
Delegate<const Float2&> Input::MouseMoveRelative;
|
||||
Action Input::MouseLeave;
|
||||
Delegate<InputGamepadIndex, GamepadButton> Input::GamepadButtonDown;
|
||||
Delegate<InputGamepadIndex, GamepadButton> Input::GamepadButtonUp;
|
||||
@@ -217,6 +218,14 @@ void Mouse::OnMouseMove(const Float2& position, Window* target)
|
||||
e.MouseData.Position = position;
|
||||
}
|
||||
|
||||
void Mouse::OnMouseMoveRelative(const Float2& positionRelative, Window* target)
|
||||
{
|
||||
Event& e = _queue.AddOne();
|
||||
e.Type = EventType::MouseMoveRelative;
|
||||
e.Target = target;
|
||||
e.MouseMovementData.PositionRelative = positionRelative;
|
||||
}
|
||||
|
||||
void Mouse::OnMouseLeave(Window* target)
|
||||
{
|
||||
PROFILE_MEM(Input);
|
||||
@@ -284,6 +293,11 @@ bool Mouse::Update(EventQueue& queue)
|
||||
_state.MousePosition = e.MouseData.Position;
|
||||
break;
|
||||
}
|
||||
case EventType::MouseMoveRelative:
|
||||
{
|
||||
_state.MousePosition += e.MouseMovementData.PositionRelative;
|
||||
break;
|
||||
}
|
||||
case EventType::MouseLeave:
|
||||
{
|
||||
break;
|
||||
@@ -924,11 +938,10 @@ void InputService::Update()
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
|
||||
// Send input events for the focused window
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
for (const auto& e : InputEvents)
|
||||
{
|
||||
auto window = e.Target ? e.Target : defaultWindow;
|
||||
if (!window || !WindowsManager::Windows.Contains(window))
|
||||
if (!window || window->IsClosed())
|
||||
continue;
|
||||
switch (e.Type)
|
||||
{
|
||||
@@ -958,6 +971,9 @@ void InputService::Update()
|
||||
case InputDevice::EventType::MouseMove:
|
||||
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
|
||||
break;
|
||||
case InputDevice::EventType::MouseMoveRelative:
|
||||
window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative);
|
||||
break;
|
||||
case InputDevice::EventType::MouseLeave:
|
||||
window->OnMouseLeave();
|
||||
break;
|
||||
@@ -973,7 +989,6 @@ void InputService::Update()
|
||||
break;
|
||||
}
|
||||
}
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
|
||||
// Skip if game has no focus to handle the input
|
||||
if (!Engine::HasGameViewportFocus())
|
||||
@@ -1014,6 +1029,9 @@ void InputService::Update()
|
||||
case InputDevice::EventType::MouseMove:
|
||||
Input::MouseMove(e.MouseData.Position);
|
||||
break;
|
||||
case InputDevice::EventType::MouseMoveRelative:
|
||||
Input::MouseMoveRelative(e.MouseMovementData.PositionRelative);
|
||||
break;
|
||||
case InputDevice::EventType::MouseLeave:
|
||||
Input::MouseLeave();
|
||||
break;
|
||||
@@ -1240,6 +1258,7 @@ void InputService::Update()
|
||||
}
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Lock mouse if need to
|
||||
const auto lockMode = Screen::GetCursorLock();
|
||||
if (lockMode == CursorLockMode::Locked)
|
||||
@@ -1248,6 +1267,7 @@ void InputService::Update()
|
||||
Screen::ScreenToGameViewport(Float2::Zero);
|
||||
Input::SetMousePosition(pos);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Send events for the active actions and axes (send events only in play mode)
|
||||
if (!Time::GetGamePaused())
|
||||
|
||||
@@ -108,6 +108,11 @@ public:
|
||||
/// </summary>
|
||||
API_EVENT() static Delegate<const Float2&> MouseMove;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when mouse moves while in relative mode.
|
||||
/// </summary>
|
||||
API_EVENT() static Delegate<const Float2&> MouseMoveRelative;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when mouse leaves window.
|
||||
/// </summary>
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
MouseDoubleClick,
|
||||
MouseWheel,
|
||||
MouseMove,
|
||||
MouseMoveRelative,
|
||||
MouseLeave,
|
||||
TouchDown,
|
||||
TouchMove,
|
||||
@@ -54,6 +55,11 @@ public:
|
||||
Float2 Position;
|
||||
} MouseData;
|
||||
|
||||
struct
|
||||
{
|
||||
Float2 PositionRelative;
|
||||
} MouseMovementData;
|
||||
|
||||
struct
|
||||
{
|
||||
float WheelDelta;
|
||||
|
||||
@@ -46,12 +46,14 @@ public:
|
||||
protected:
|
||||
State _state;
|
||||
State _prevState;
|
||||
bool _relativeMode;
|
||||
|
||||
explicit Mouse()
|
||||
: InputDevice(SpawnParams(Guid::New(), TypeInitializer), TEXT("Mouse"))
|
||||
{
|
||||
_state.Clear();
|
||||
_prevState.Clear();
|
||||
_relativeMode = false;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -114,6 +116,16 @@ public:
|
||||
return !_state.MouseButtons[static_cast<int32>(button)] && _prevState.MouseButtons[static_cast<int32>(button)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current state of mouse relative mode.
|
||||
/// </summary>
|
||||
/// <param name="window">The window to check against, or null to check for any window.</param>
|
||||
/// <returns>True if mouse is in relative mode, otherwise false.</returns>
|
||||
API_FUNCTION() virtual bool IsRelative(Window* window = nullptr) const
|
||||
{
|
||||
return _relativeMode;
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Sets the mouse position.
|
||||
@@ -121,6 +133,17 @@ public:
|
||||
/// <param name="newPosition">The new position.</param>
|
||||
virtual void SetMousePosition(const Float2& newPosition) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mouse relative mode state. While enabled, the mouse movement tracking becomes more accurate.
|
||||
/// The cursor will be hidden while in relative mode.
|
||||
/// </summary>
|
||||
/// <param name="relativeMode">The new relative mode state.</param>
|
||||
/// <param name="window">The window.</param>
|
||||
virtual void SetRelativeMode(bool relativeMode, Window* window)
|
||||
{
|
||||
_relativeMode = relativeMode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when mouse cursor gets moved by the application. Invalidates the previous cached mouse position to prevent mouse jitter when locking the cursor programmatically.
|
||||
/// </summary>
|
||||
@@ -158,6 +181,13 @@ public:
|
||||
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
|
||||
void OnMouseMove(const Float2& position, Window* target = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Called when mouse moves in relative mode.
|
||||
/// </summary>
|
||||
/// <param name="positionRelative">The mouse position change.</param>
|
||||
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
|
||||
void OnMouseMoveRelative(const Float2& positionRelative, Window* target = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Called when mouse leaves the input source area.
|
||||
/// </summary>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "android_native_app_glue.h"
|
||||
#include "android_native_app_glue.c"
|
||||
|
||||
void android_main(android_app* app)
|
||||
{
|
||||
|
||||
@@ -12,15 +12,20 @@ struct android_app;
|
||||
/// <summary>
|
||||
/// The Android platform implementation and application management utilities.
|
||||
/// </summary>
|
||||
API_CLASS(Static, Tag="NoTypeInitializer")
|
||||
class FLAXENGINE_API AndroidPlatform : public UnixPlatform
|
||||
{
|
||||
public:
|
||||
|
||||
static android_app* GetApp();
|
||||
static String GetAppPackageName();
|
||||
static String GetDeviceManufacturer();
|
||||
static String GetDeviceModel();
|
||||
static String GetDeviceBuildNumber();
|
||||
// Gets 'getPackageName()' value.
|
||||
API_PROPERTY() static String GetAppPackageName();
|
||||
// Gets 'android.os.Build.MANUFACTURER' value.
|
||||
API_PROPERTY() static String GetDeviceManufacturer();
|
||||
// Gets 'android.os.Build.MODEL' value.
|
||||
API_PROPERTY() static String GetDeviceModel();
|
||||
// Gets 'android.os.Build.DISPLAY' value.
|
||||
API_PROPERTY() static String GetDeviceBuildNumber();
|
||||
static void PreInit(android_app* app);
|
||||
|
||||
public:
|
||||
|
||||
251
Source/Engine/Platform/Base/Enums.h
Normal file
251
Source/Engine/Platform/Base/Enums.h
Normal file
@@ -0,0 +1,251 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Config.h"
|
||||
|
||||
/// <summary>
|
||||
/// Window closing reasons.
|
||||
/// </summary>
|
||||
API_ENUM() enum class ClosingReason
|
||||
{
|
||||
/// <summary>
|
||||
/// The unknown.
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The user.
|
||||
/// </summary>
|
||||
User,
|
||||
|
||||
/// <summary>
|
||||
/// The engine exit.
|
||||
/// </summary>
|
||||
EngineExit,
|
||||
|
||||
/// <summary>
|
||||
/// The close event.
|
||||
/// </summary>
|
||||
CloseEvent,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Types of default cursors.
|
||||
/// </summary>
|
||||
API_ENUM() enum class CursorType
|
||||
{
|
||||
/// <summary>
|
||||
/// The default.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The cross.
|
||||
/// </summary>
|
||||
Cross,
|
||||
|
||||
/// <summary>
|
||||
/// The hand.
|
||||
/// </summary>
|
||||
Hand,
|
||||
|
||||
/// <summary>
|
||||
/// The help icon
|
||||
/// </summary>
|
||||
Help,
|
||||
|
||||
/// <summary>
|
||||
/// The I beam.
|
||||
/// </summary>
|
||||
IBeam,
|
||||
|
||||
/// <summary>
|
||||
/// The blocking image.
|
||||
/// </summary>
|
||||
No,
|
||||
|
||||
/// <summary>
|
||||
/// The wait.
|
||||
/// </summary>
|
||||
Wait,
|
||||
|
||||
/// <summary>
|
||||
/// The size all sides.
|
||||
/// </summary>
|
||||
SizeAll,
|
||||
|
||||
/// <summary>
|
||||
/// The size NE-SW.
|
||||
/// </summary>
|
||||
SizeNESW,
|
||||
|
||||
/// <summary>
|
||||
/// The size NS.
|
||||
/// </summary>
|
||||
SizeNS,
|
||||
|
||||
/// <summary>
|
||||
/// The size NW-SE.
|
||||
/// </summary>
|
||||
SizeNWSE,
|
||||
|
||||
/// <summary>
|
||||
/// The size WE.
|
||||
/// </summary>
|
||||
SizeWE,
|
||||
|
||||
/// <summary>
|
||||
/// The cursor is hidden.
|
||||
/// </summary>
|
||||
Hidden,
|
||||
|
||||
MAX
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Data drag and drop effects.
|
||||
/// </summary>
|
||||
API_ENUM() enum class DragDropEffect
|
||||
{
|
||||
/// <summary>
|
||||
/// The none.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The copy.
|
||||
/// </summary>
|
||||
Copy,
|
||||
|
||||
/// <summary>
|
||||
/// The move.
|
||||
/// </summary>
|
||||
Move,
|
||||
|
||||
/// <summary>
|
||||
/// The link.
|
||||
/// </summary>
|
||||
Link,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Window hit test codes. Note: they are 1:1 mapping for Win32 values.
|
||||
/// </summary>
|
||||
API_ENUM() enum class WindowHitCodes
|
||||
{
|
||||
/// <summary>
|
||||
/// The transparent area.
|
||||
/// </summary>
|
||||
Transparent = -1,
|
||||
|
||||
/// <summary>
|
||||
/// The no hit.
|
||||
/// </summary>
|
||||
NoWhere = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The client area.
|
||||
/// </summary>
|
||||
Client = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The caption area.
|
||||
/// </summary>
|
||||
Caption = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The system menu.
|
||||
/// </summary>
|
||||
SystemMenu = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The grow box
|
||||
/// </summary>
|
||||
GrowBox = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The menu.
|
||||
/// </summary>
|
||||
Menu = 5,
|
||||
|
||||
/// <summary>
|
||||
/// The horizontal scroll.
|
||||
/// </summary>
|
||||
HScroll = 6,
|
||||
|
||||
/// <summary>
|
||||
/// The vertical scroll.
|
||||
/// </summary>
|
||||
VScroll = 7,
|
||||
|
||||
/// <summary>
|
||||
/// The minimize button.
|
||||
/// </summary>
|
||||
MinButton = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The maximize button.
|
||||
/// </summary>
|
||||
MaxButton = 9,
|
||||
|
||||
/// <summary>
|
||||
/// The left side;
|
||||
/// </summary>
|
||||
Left = 10,
|
||||
|
||||
/// <summary>
|
||||
/// The right side.
|
||||
/// </summary>
|
||||
Right = 11,
|
||||
|
||||
/// <summary>
|
||||
/// The top side.
|
||||
/// </summary>
|
||||
Top = 12,
|
||||
|
||||
/// <summary>
|
||||
/// The top left corner.
|
||||
/// </summary>
|
||||
TopLeft = 13,
|
||||
|
||||
/// <summary>
|
||||
/// The top right corner.
|
||||
/// </summary>
|
||||
TopRight = 14,
|
||||
|
||||
/// <summary>
|
||||
/// The bottom side.
|
||||
/// </summary>
|
||||
Bottom = 15,
|
||||
|
||||
/// <summary>
|
||||
/// The bottom left corner.
|
||||
/// </summary>
|
||||
BottomLeft = 16,
|
||||
|
||||
/// <summary>
|
||||
/// The bottom right corner.
|
||||
/// </summary>
|
||||
BottomRight = 17,
|
||||
|
||||
/// <summary>
|
||||
/// The border.
|
||||
/// </summary>
|
||||
Border = 18,
|
||||
|
||||
/// <summary>
|
||||
/// The object.
|
||||
/// </summary>
|
||||
Object = 19,
|
||||
|
||||
/// <summary>
|
||||
/// The close button.
|
||||
/// </summary>
|
||||
Close = 20,
|
||||
|
||||
/// <summary>
|
||||
/// The help button.
|
||||
/// </summary>
|
||||
Help = 21,
|
||||
};
|
||||
@@ -46,6 +46,7 @@ static_assert(sizeof(double) == 8, "Invalid double type size.");
|
||||
static_assert((PLATFORM_THREADS_LIMIT & (PLATFORM_THREADS_LIMIT - 1)) == 0, "Threads limit must be power of two.");
|
||||
static_assert(PLATFORM_THREADS_LIMIT % 4 == 0, "Threads limit must be multiple of 4.");
|
||||
|
||||
const Char* PlatformBase::ApplicationClassName = TEXT("FlaxWindow");
|
||||
float PlatformBase::CustomDpiScale = 1.0f;
|
||||
Array<User*, FixedAllocation<8>> PlatformBase::Users;
|
||||
Delegate<User*> PlatformBase::UserAdded;
|
||||
@@ -773,6 +774,26 @@ void PlatformBase::CollectCrashData(const String& crashDataFolder, void* context
|
||||
{
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
#include "Engine/Core/Math/Color32.h"
|
||||
|
||||
Delegate<Color32> PlatformBase::PickScreenColorDone;
|
||||
|
||||
Color32 PlatformBase::GetScreenColorAt(const Float2& pos)
|
||||
{
|
||||
// No supported
|
||||
return Color32::Transparent;
|
||||
}
|
||||
|
||||
void PlatformBase::PickScreenColor()
|
||||
{
|
||||
// Just return transparent color when not implemented/supported
|
||||
PickScreenColorDone(Color32::Transparent);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const Char* ToString(PlatformType type)
|
||||
{
|
||||
switch (type)
|
||||
|
||||
@@ -190,6 +190,13 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(PlatformBase);
|
||||
/// </summary>
|
||||
static void Exit();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Application windows class name.
|
||||
/// </summary>
|
||||
static const Char* ApplicationClassName;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Copy memory region
|
||||
@@ -866,6 +873,26 @@ public:
|
||||
|
||||
// Crash dump data handling
|
||||
static void CollectCrashData(const String& crashDataFolder, void* context = nullptr);
|
||||
|
||||
public:
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// Gets the pixel color at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="pos">Screen-space coordinate to read.</param>
|
||||
/// <returns>Pixel color at the specified coordinates, or transparent color when color couldn't be picked up.</returns>
|
||||
API_FUNCTION() static Color32 GetScreenColorAt(const Float2& pos);
|
||||
|
||||
/// <summary>
|
||||
/// Starts async color picking. Color will be returned through PickColorDone event when the action ends (user selected the final color with a mouse). When action is active, GetColorAt can be used to read the current value.
|
||||
/// </summary>
|
||||
API_FUNCTION() static void PickScreenColor();
|
||||
|
||||
/// <summary>
|
||||
/// Called when PickColor action is finished.
|
||||
/// </summary>
|
||||
API_EVENT() static Delegate<Color32> PickScreenColorDone;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern FLAXENGINE_API const Char* ToString(PlatformType type);
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Engine/Core/Math/Color32.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/IGuiData.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Graphics/GPUSwapChain.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Platform/IGuiData.h"
|
||||
#include "Engine/Scripting/ScriptingType.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
@@ -107,7 +108,7 @@ WindowBase::WindowBase(const CreateWindowSettings& settings)
|
||||
if (settings.StartPosition == WindowStartPosition::CenterParent
|
||||
|| settings.StartPosition == WindowStartPosition::CenterScreen)
|
||||
{
|
||||
Rectangle parentBounds = Rectangle(Float2::Zero, Platform::GetDesktopSize());
|
||||
Rectangle parentBounds = Platform::GetMonitorBounds(Float2::Minimum);
|
||||
if (settings.Parent != nullptr && settings.StartPosition == WindowStartPosition::CenterParent)
|
||||
parentBounds = settings.Parent->GetClientBounds();
|
||||
|
||||
@@ -165,6 +166,15 @@ void WindowBase::SetIsVisible(bool isVisible)
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowBase::IsAlwaysOnTop() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void WindowBase::SetIsAlwaysOnTop(bool isAlwaysOnTop)
|
||||
{
|
||||
}
|
||||
|
||||
String WindowBase::ToString() const
|
||||
{
|
||||
return GetTitle();
|
||||
@@ -266,6 +276,13 @@ void WindowBase::OnMouseMove(const Float2& mousePosition)
|
||||
INVOKE_EVENT_PARAMS_1(OnMouseMove, (void*)&mousePosition);
|
||||
}
|
||||
|
||||
void WindowBase::OnMouseMoveRelative(const Float2& mousePositionRelative)
|
||||
{
|
||||
PROFILE_CPU_NAMED("GUI.OnMouseMoveRelative");
|
||||
MouseMoveRelative(mousePositionRelative);
|
||||
INVOKE_EVENT_PARAMS_1(OnMouseMoveRelative, (void*)&mousePositionRelative);
|
||||
}
|
||||
|
||||
void WindowBase::OnMouseLeave()
|
||||
{
|
||||
PROFILE_CPU_NAMED("GUI.OnMouseLeave");
|
||||
@@ -583,6 +600,11 @@ void WindowBase::Close(ClosingReason reason)
|
||||
OnClosed();
|
||||
}
|
||||
|
||||
bool WindowBase::IsClosed() const
|
||||
{
|
||||
return _isClosing || EnumHasAnyFlags(Flags, ObjectFlags::WasMarkedToDelete);
|
||||
}
|
||||
|
||||
bool WindowBase::IsForegroundWindow() const
|
||||
{
|
||||
return _focused;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Input/KeyboardKeys.h"
|
||||
#include "Engine/Input/Enums.h"
|
||||
#include "Enums.h"
|
||||
|
||||
class Input;
|
||||
class Engine;
|
||||
@@ -17,252 +18,6 @@ class GPUSwapChain;
|
||||
class TextureData;
|
||||
class IGuiData;
|
||||
|
||||
/// <summary>
|
||||
/// Window closing reasons.
|
||||
/// </summary>
|
||||
API_ENUM() enum class ClosingReason
|
||||
{
|
||||
/// <summary>
|
||||
/// The unknown.
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The user.
|
||||
/// </summary>
|
||||
User,
|
||||
|
||||
/// <summary>
|
||||
/// The engine exit.
|
||||
/// </summary>
|
||||
EngineExit,
|
||||
|
||||
/// <summary>
|
||||
/// The close event.
|
||||
/// </summary>
|
||||
CloseEvent,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Types of default cursors.
|
||||
/// </summary>
|
||||
API_ENUM() enum class CursorType
|
||||
{
|
||||
/// <summary>
|
||||
/// The default.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The cross.
|
||||
/// </summary>
|
||||
Cross,
|
||||
|
||||
/// <summary>
|
||||
/// The hand.
|
||||
/// </summary>
|
||||
Hand,
|
||||
|
||||
/// <summary>
|
||||
/// The help icon
|
||||
/// </summary>
|
||||
Help,
|
||||
|
||||
/// <summary>
|
||||
/// The I beam.
|
||||
/// </summary>
|
||||
IBeam,
|
||||
|
||||
/// <summary>
|
||||
/// The blocking image.
|
||||
/// </summary>
|
||||
No,
|
||||
|
||||
/// <summary>
|
||||
/// The wait.
|
||||
/// </summary>
|
||||
Wait,
|
||||
|
||||
/// <summary>
|
||||
/// The size all sides.
|
||||
/// </summary>
|
||||
SizeAll,
|
||||
|
||||
/// <summary>
|
||||
/// The size NE-SW.
|
||||
/// </summary>
|
||||
SizeNESW,
|
||||
|
||||
/// <summary>
|
||||
/// The size NS.
|
||||
/// </summary>
|
||||
SizeNS,
|
||||
|
||||
/// <summary>
|
||||
/// The size NW-SE.
|
||||
/// </summary>
|
||||
SizeNWSE,
|
||||
|
||||
/// <summary>
|
||||
/// The size WE.
|
||||
/// </summary>
|
||||
SizeWE,
|
||||
|
||||
/// <summary>
|
||||
/// The cursor is hidden.
|
||||
/// </summary>
|
||||
Hidden,
|
||||
|
||||
MAX
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Data drag and drop effects.
|
||||
/// </summary>
|
||||
API_ENUM() enum class DragDropEffect
|
||||
{
|
||||
/// <summary>
|
||||
/// The none.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The copy.
|
||||
/// </summary>
|
||||
Copy,
|
||||
|
||||
/// <summary>
|
||||
/// The move.
|
||||
/// </summary>
|
||||
Move,
|
||||
|
||||
/// <summary>
|
||||
/// The link.
|
||||
/// </summary>
|
||||
Link,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Window hit test codes. Note: they are 1:1 mapping for Win32 values.
|
||||
/// </summary>
|
||||
API_ENUM() enum class WindowHitCodes
|
||||
{
|
||||
/// <summary>
|
||||
/// The transparent area.
|
||||
/// </summary>
|
||||
Transparent = -1,
|
||||
|
||||
/// <summary>
|
||||
/// The no hit.
|
||||
/// </summary>
|
||||
NoWhere = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The client area.
|
||||
/// </summary>
|
||||
Client = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The caption area.
|
||||
/// </summary>
|
||||
Caption = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The system menu.
|
||||
/// </summary>
|
||||
SystemMenu = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The grow box
|
||||
/// </summary>
|
||||
GrowBox = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The menu.
|
||||
/// </summary>
|
||||
Menu = 5,
|
||||
|
||||
/// <summary>
|
||||
/// The horizontal scroll.
|
||||
/// </summary>
|
||||
HScroll = 6,
|
||||
|
||||
/// <summary>
|
||||
/// The vertical scroll.
|
||||
/// </summary>
|
||||
VScroll = 7,
|
||||
|
||||
/// <summary>
|
||||
/// The minimize button.
|
||||
/// </summary>
|
||||
MinButton = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The maximize button.
|
||||
/// </summary>
|
||||
MaxButton = 9,
|
||||
|
||||
/// <summary>
|
||||
/// The left side;
|
||||
/// </summary>
|
||||
Left = 10,
|
||||
|
||||
/// <summary>
|
||||
/// The right side.
|
||||
/// </summary>
|
||||
Right = 11,
|
||||
|
||||
/// <summary>
|
||||
/// The top side.
|
||||
/// </summary>
|
||||
Top = 12,
|
||||
|
||||
/// <summary>
|
||||
/// The top left corner.
|
||||
/// </summary>
|
||||
TopLeft = 13,
|
||||
|
||||
/// <summary>
|
||||
/// The top right corner.
|
||||
/// </summary>
|
||||
TopRight = 14,
|
||||
|
||||
/// <summary>
|
||||
/// The bottom side.
|
||||
/// </summary>
|
||||
Bottom = 15,
|
||||
|
||||
/// <summary>
|
||||
/// The bottom left corner.
|
||||
/// </summary>
|
||||
BottomLeft = 16,
|
||||
|
||||
/// <summary>
|
||||
/// The bottom right corner.
|
||||
/// </summary>
|
||||
BottomRight = 17,
|
||||
|
||||
/// <summary>
|
||||
/// The border.
|
||||
/// </summary>
|
||||
Border = 18,
|
||||
|
||||
/// <summary>
|
||||
/// The object.
|
||||
/// </summary>
|
||||
Object = 19,
|
||||
|
||||
/// <summary>
|
||||
/// The close button.
|
||||
/// </summary>
|
||||
Close = 20,
|
||||
|
||||
/// <summary>
|
||||
/// The help button.
|
||||
/// </summary>
|
||||
Help = 21,
|
||||
};
|
||||
|
||||
API_INJECT_CODE(cpp, "#include \"Engine/Platform/Window.h\"");
|
||||
|
||||
/// <summary>
|
||||
@@ -290,6 +45,7 @@ protected:
|
||||
bool _isHorizontalFlippingMouse = false;
|
||||
bool _isVerticalFlippingMouse = false;
|
||||
bool _isClippingCursor = false;
|
||||
bool _restoreRelativeMode = false;
|
||||
|
||||
explicit WindowBase(const CreateWindowSettings& settings);
|
||||
virtual ~WindowBase();
|
||||
@@ -402,6 +158,17 @@ public:
|
||||
return _maximized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that indicates whether a window is always on top of other windows.
|
||||
/// </summary>
|
||||
API_PROPERTY() virtual bool IsAlwaysOnTop() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets a value that indicates whether a window is always on top of other windows.
|
||||
/// </summary>
|
||||
/// <param name="isAlwaysOnTop">True if always on top.</param>
|
||||
API_PROPERTY() virtual void SetIsAlwaysOnTop(bool isAlwaysOnTop);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the native window handle.
|
||||
/// </summary>
|
||||
@@ -475,10 +242,7 @@ public:
|
||||
/// <summary>
|
||||
/// Checks if window is closed.
|
||||
/// </summary>
|
||||
API_PROPERTY() virtual bool IsClosed() const
|
||||
{
|
||||
return _isClosing;
|
||||
}
|
||||
API_PROPERTY() virtual bool IsClosed() const;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if window is foreground (the window with which the user is currently working).
|
||||
@@ -663,7 +427,7 @@ public:
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Starts drag and drop operation
|
||||
/// Starts a drag and drop operation.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>The result.</returns>
|
||||
@@ -672,6 +436,18 @@ public:
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a window drag and drop operation.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="offset">The offset for positioning the window from cursor.</param>
|
||||
/// <param name="dragSourceWindow">The window where dragging started.</param>
|
||||
/// <returns>The result.</returns>
|
||||
API_FUNCTION() virtual DragDropEffect DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow)
|
||||
{
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the mouse tracking.
|
||||
/// </summary>
|
||||
@@ -742,6 +518,17 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mouse position in window coordinates.
|
||||
/// </summary>
|
||||
API_PROPERTY() virtual Float2 GetMousePosition() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mouse position in window coordinates.
|
||||
/// </summary>
|
||||
/// <param name="position">Mouse position to set on</param>
|
||||
API_PROPERTY() virtual void SetMousePosition(const Float2& position) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mouse cursor.
|
||||
/// </summary>
|
||||
@@ -837,6 +624,12 @@ public:
|
||||
MouseDelegate MouseMove;
|
||||
void OnMouseMove(const Float2& mousePosition);
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when mouse moves in relative mode.
|
||||
/// </summary>
|
||||
MouseDelegate MouseMoveRelative;
|
||||
void OnMouseMoveRelative(const Float2& mousePositionRelative);
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when mouse leaves window.
|
||||
/// </summary>
|
||||
@@ -932,16 +725,6 @@ public:
|
||||
API_FUNCTION() bool GetKeyUp(KeyboardKeys key) const;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the mouse position in window coordinates.
|
||||
/// </summary>
|
||||
API_PROPERTY() Float2 GetMousePosition() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mouse position in window coordinates.
|
||||
/// </summary>
|
||||
/// <param name="position">Mouse position to set on</param>
|
||||
API_PROPERTY() void SetMousePosition(const Float2& position) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mouse position change during the last frame.
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_SDL && PLATFORM_LINUX
|
||||
#include "SDL/SDLClipboard.h"
|
||||
#elif PLATFORM_WINDOWS
|
||||
#include "Windows/WindowsClipboard.h"
|
||||
#elif PLATFORM_LINUX
|
||||
#include "Linux/LinuxClipboard.h"
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace FlaxEngine
|
||||
{
|
||||
Position = new Float2(100, 100),
|
||||
Size = new Float2(640, 480),
|
||||
MinimumSize = Float2.One,
|
||||
MinimumSize = new Float2(8, 8),
|
||||
MaximumSize = Float2.Zero, // Unlimited size
|
||||
StartPosition = WindowStartPosition.CenterParent,
|
||||
HasBorder = true,
|
||||
@@ -21,7 +21,7 @@ namespace FlaxEngine
|
||||
AllowMinimize = true,
|
||||
AllowMaximize = true,
|
||||
AllowDragAndDrop = true,
|
||||
IsRegularWindow = true,
|
||||
Type = WindowType.Regular,
|
||||
HasSizingFrame = true,
|
||||
ShowAfterFirstPaint = true,
|
||||
};
|
||||
|
||||
@@ -26,6 +26,32 @@ API_ENUM() enum class WindowStartPosition
|
||||
Manual,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the type of the window.
|
||||
/// </summary>
|
||||
API_ENUM() enum class WindowType
|
||||
{
|
||||
/// <summary>
|
||||
/// Regular window.
|
||||
/// </summary>
|
||||
Regular,
|
||||
|
||||
/// <summary>
|
||||
/// Utility window.
|
||||
/// </summary>
|
||||
Utility,
|
||||
|
||||
/// <summary>
|
||||
/// Tooltip window.
|
||||
/// </summary>
|
||||
Tooltip,
|
||||
|
||||
/// <summary>
|
||||
/// Popup window.
|
||||
/// </summary>
|
||||
Popup,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Settings for new window.
|
||||
/// </summary>
|
||||
@@ -119,9 +145,16 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(CreateWindowSettings);
|
||||
API_FIELD() bool IsTopmost = false;
|
||||
|
||||
/// <summary>
|
||||
/// True if it's a regular window, false for tooltips, contextmenu and other utility windows.
|
||||
/// True if it's a regular window, false for tooltips, context menu and other utility windows.
|
||||
/// [Deprecated in v1.12]
|
||||
/// </summary>
|
||||
API_FIELD() bool IsRegularWindow = true;
|
||||
API_FIELD() DEPRECATED("Use Type instead") bool IsRegularWindow = true;
|
||||
|
||||
/// <summary>
|
||||
/// The type of window. The type affects the behaviour of the window in system level.
|
||||
/// Note: Tooltip and Popup windows require Parent to be set.
|
||||
/// </summary>
|
||||
API_FIELD() WindowType Type = WindowType::Regular;
|
||||
|
||||
/// <summary>
|
||||
/// Enable/disable window sizing frame.
|
||||
|
||||
@@ -140,6 +140,9 @@ API_ENUM() enum class ArchitectureType
|
||||
#if !defined(PLATFORM_SWITCH)
|
||||
#define PLATFORM_SWITCH 0
|
||||
#endif
|
||||
#if !defined(PLATFORM_SDL)
|
||||
#define PLATFORM_SDL 0
|
||||
#endif
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "Windows/WindowsDefines.h"
|
||||
|
||||
@@ -30,7 +30,6 @@ inline bool operator==(const APP_LOCAL_DEVICE_ID& l, const APP_LOCAL_DEVICE_ID&
|
||||
return Platform::MemoryCompare(&l, &r, sizeof(APP_LOCAL_DEVICE_ID)) == 0;
|
||||
}
|
||||
|
||||
const Char* GDKPlatform::ApplicationWindowClass = TEXT("FlaxWindow");
|
||||
void* GDKPlatform::Instance = nullptr;
|
||||
Delegate<> GDKPlatform::Suspended;
|
||||
Delegate<> GDKPlatform::Resumed;
|
||||
@@ -317,7 +316,7 @@ void GDKPlatform::PreInit(void* hInstance)
|
||||
windowsClass.style = CS_HREDRAW | CS_VREDRAW;
|
||||
windowsClass.lpfnWndProc = WndProc;
|
||||
windowsClass.hInstance = (HINSTANCE)Instance;
|
||||
windowsClass.lpszClassName = ApplicationWindowClass;
|
||||
windowsClass.lpszClassName = ApplicationClassName;
|
||||
if (!RegisterClassW(&windowsClass))
|
||||
{
|
||||
Error(TEXT("Window class registration failed!"));
|
||||
@@ -477,7 +476,7 @@ void GDKPlatform::Exit()
|
||||
if (PlmSignalResume)
|
||||
CloseHandle(PlmSignalResume);
|
||||
|
||||
UnregisterClassW(ApplicationWindowClass, nullptr);
|
||||
UnregisterClassW(ApplicationClassName, nullptr);
|
||||
|
||||
XGameRuntimeUninitialize();
|
||||
}
|
||||
|
||||
@@ -9,15 +9,11 @@
|
||||
/// <summary>
|
||||
/// The GDK platform implementation and application management utilities.
|
||||
/// </summary>
|
||||
API_CLASS(Static, Tag="NoTypeInitializer")
|
||||
class FLAXENGINE_API GDKPlatform : public Win32Platform
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Win32 application windows class name.
|
||||
/// </summary>
|
||||
static const Char* ApplicationWindowClass;
|
||||
|
||||
/// <summary>
|
||||
/// Handle to Win32 application instance.
|
||||
/// </summary>
|
||||
@@ -42,10 +38,13 @@ public:
|
||||
/// <param name="hInstance">The Win32 application instance.</param>
|
||||
static void PreInit(void* hInstance);
|
||||
|
||||
static bool IsRunningOnDevKit();
|
||||
|
||||
static void SignInSilently();
|
||||
static void SignInWithUI();
|
||||
// True, if game is running Xbox Devkit.
|
||||
API_PROPERTY() static bool IsRunningOnDevKit();
|
||||
// Signs in user without showing UI. If user is not signed in, it will fail and return false. Use SignInWithUI to show UI and let user sign in.
|
||||
API_FUNCTION() static void SignInSilently();
|
||||
// Signs in user with showing UI. If user is already signed in, it will succeed and return true. If user is not signed in, it will show UI and let user sign in.
|
||||
API_FUNCTION() static void SignInWithUI();
|
||||
// Searches for a user with a specific local ID.
|
||||
static User* FindUser(const struct XUserLocalId& id);
|
||||
|
||||
public:
|
||||
|
||||
@@ -71,7 +71,7 @@ GDKWindow::GDKWindow(const CreateWindowSettings& settings)
|
||||
// Creating the window
|
||||
_handle = CreateWindowExW(
|
||||
exStyle,
|
||||
Platform::ApplicationWindowClass,
|
||||
Platform::ApplicationClassName,
|
||||
settings.Title.GetText(),
|
||||
style,
|
||||
x,
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace X11
|
||||
#include <X11/Xresource.h>
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
}
|
||||
|
||||
// Helper macros
|
||||
|
||||
@@ -97,7 +97,9 @@ X11::Cursor Cursors[(int32)CursorType::MAX];
|
||||
X11::XcursorImage* CursorsImg[(int32)CursorType::MAX];
|
||||
Dictionary<StringAnsi, X11::KeyCode> KeyNameMap;
|
||||
Array<KeyboardKeys> KeyCodeMap;
|
||||
Delegate<void*> LinuxPlatform::xEventRecieved;
|
||||
#if !PLATFORM_SDL
|
||||
Delegate<void*> LinuxPlatform::xEventReceived;
|
||||
#endif
|
||||
Window* MouseTrackingWindow = nullptr;
|
||||
|
||||
// Message boxes configuration
|
||||
@@ -655,7 +657,11 @@ static int X11_MessageBoxLoop(MessageBoxData* data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||
#else
|
||||
DialogResult MessageBox::ShowFallback(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||
#endif
|
||||
{
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
return DialogResult::None;
|
||||
@@ -842,6 +848,8 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri
|
||||
return data.resultButtonIndex == -1 ? DialogResult::None : data.buttons[data.resultButtonIndex].result;
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
|
||||
int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event)
|
||||
{
|
||||
if (event->error_code == 5)
|
||||
@@ -852,6 +860,8 @@ int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int32 CalculateDpi()
|
||||
{
|
||||
int dpi = 96;
|
||||
@@ -1207,17 +1217,20 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
struct Property
|
||||
{
|
||||
unsigned char* data;
|
||||
int format, nitems;
|
||||
X11::Atom type;
|
||||
unsigned char* data;
|
||||
int format, nitems;
|
||||
X11::Atom type;
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace Impl
|
||||
{
|
||||
LinuxKeyboard* Keyboard;
|
||||
LinuxMouse* Mouse;
|
||||
#if !PLATFORM_SDL
|
||||
StringAnsi ClipboardText;
|
||||
|
||||
void ClipboardGetText(String& result, X11::Atom source, X11::Atom atom, X11::Window window)
|
||||
@@ -1332,6 +1345,7 @@ namespace Impl
|
||||
X11::XQueryPointer(display, w, &wtmp, &child, &tmp, &tmp, &tmp, &tmp, &utmp);
|
||||
return FindAppWindow(display, child);
|
||||
}
|
||||
#endif
|
||||
|
||||
Dictionary<String, String> LoadConfigFile(StringView path)
|
||||
{
|
||||
@@ -1361,6 +1375,7 @@ namespace Impl
|
||||
}
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
class LinuxDropFilesData : public IGuiData
|
||||
{
|
||||
public:
|
||||
@@ -1398,7 +1413,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
DragDropEffect Window::DoDragDrop(const StringView& data)
|
||||
{
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
return DragDropEffect::None;
|
||||
@@ -1412,13 +1427,14 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
StringAnsi dataAnsi(data);
|
||||
LinuxDropTextData dropData;
|
||||
dropData.Text = data;
|
||||
unsigned long mainWindow = _window;
|
||||
|
||||
// Begin dragging
|
||||
auto screen = X11::XDefaultScreen(xDisplay);
|
||||
auto rootWindow = X11::XRootWindow(xDisplay, screen);
|
||||
if (X11::XGrabPointer(xDisplay, _window, 1, Button1MotionMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, rootWindow, cursorWrong, CurrentTime) != GrabSuccess)
|
||||
if (X11::XGrabPointer(xDisplay, mainWindow, 1, Button1MotionMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, rootWindow, cursorWrong, CurrentTime) != GrabSuccess)
|
||||
return DragDropEffect::None;
|
||||
X11::XSetSelectionOwner(xDisplay, xAtomXdndSelection, _window, CurrentTime);
|
||||
X11::XSetSelectionOwner(xDisplay, xAtomXdndSelection, mainWindow, CurrentTime);
|
||||
|
||||
// Process events
|
||||
X11::XEvent event;
|
||||
@@ -1526,7 +1542,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
m.window = previousWindow;
|
||||
m.message_type = xAtomXdndLeave;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = 0;
|
||||
m.data.l[3] = 0;
|
||||
@@ -1555,7 +1571,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
m.window = window;
|
||||
m.message_type = xAtomXdndEnter;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = Math::Min(5, version) << 24 | (formats.Count() > 3);
|
||||
m.data.l[2] = formats.Count() > 0 ? formats[0] : 0;
|
||||
m.data.l[3] = formats.Count() > 1 ? formats[1] : 0;
|
||||
@@ -1590,7 +1606,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
m.window = window;
|
||||
m.message_type = xAtomXdndPosition;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = (x << 16) | y;
|
||||
m.data.l[3] = CurrentTime;
|
||||
@@ -1633,7 +1649,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
m.window = previousWindow;
|
||||
m.message_type = xAtomXdndDrop;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = CurrentTime;
|
||||
m.data.l[3] = 0;
|
||||
@@ -1680,7 +1696,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
m.window = previousWindow;
|
||||
m.message_type = xAtomXdndLeave;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = 0;
|
||||
m.data.l[3] = 0;
|
||||
@@ -1706,7 +1722,7 @@ void LinuxClipboard::SetText(const StringView& text)
|
||||
{
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
return;
|
||||
auto mainWindow = (LinuxWindow*)Engine::MainWindow;
|
||||
auto mainWindow = Engine::MainWindow;
|
||||
if (!mainWindow)
|
||||
return;
|
||||
X11::Window window = (X11::Window)mainWindow->GetNativePtr();
|
||||
@@ -1729,7 +1745,7 @@ String LinuxClipboard::GetText()
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
return String::Empty;
|
||||
String result;
|
||||
auto mainWindow = (LinuxWindow*)Engine::MainWindow;
|
||||
auto mainWindow = Engine::MainWindow;
|
||||
if (!mainWindow)
|
||||
return result;
|
||||
X11::Window window = (X11::Window)mainWindow->GetNativePtr();
|
||||
@@ -1758,9 +1774,11 @@ Array<String> LinuxClipboard::GetFiles()
|
||||
return Array<String>();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void* LinuxPlatform::GetXDisplay()
|
||||
{
|
||||
return xDisplay;
|
||||
return xDisplay;
|
||||
}
|
||||
|
||||
bool LinuxPlatform::CreateMutex(const Char* name)
|
||||
@@ -1783,6 +1801,11 @@ const String& LinuxPlatform::GetHomeDirectory()
|
||||
return HomeDir;
|
||||
}
|
||||
|
||||
String LinuxPlatform::GetDisplayServer()
|
||||
{
|
||||
return xDisplay ? TEXT("X11") : TEXT("");
|
||||
}
|
||||
|
||||
bool LinuxPlatform::Is64BitPlatform()
|
||||
{
|
||||
#ifdef PLATFORM_64BITS
|
||||
@@ -2095,6 +2118,7 @@ bool LinuxPlatform::Init()
|
||||
|
||||
UnixGetMacAddress(MacAddress);
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Get user locale string
|
||||
setlocale(LC_ALL, "");
|
||||
const char* locale = setlocale(LC_CTYPE, NULL);
|
||||
@@ -2104,6 +2128,7 @@ bool LinuxPlatform::Init()
|
||||
UserLocale.Replace('_', '-');
|
||||
if (UserLocale == TEXT("C"))
|
||||
UserLocale = TEXT("en");
|
||||
#endif
|
||||
|
||||
// Get computer name string
|
||||
gethostname(buffer, UNIX_APP_BUFF_SIZE);
|
||||
@@ -2147,7 +2172,11 @@ bool LinuxPlatform::Init()
|
||||
// Skip setup if running in headless mode (X11 might not be available on servers)
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
return false;
|
||||
|
||||
#if PLATFORM_SDL
|
||||
xDisplay = X11::XOpenDisplay(nullptr);
|
||||
#endif
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
X11::XInitThreads();
|
||||
|
||||
xDisplay = X11::XOpenDisplay(nullptr);
|
||||
@@ -2286,7 +2315,7 @@ bool LinuxPlatform::Init()
|
||||
Input::Mouse = Impl::Mouse = New<LinuxMouse>();
|
||||
Input::Keyboard = Impl::Keyboard = New<LinuxKeyboard>();
|
||||
LinuxInput::Init();
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2296,6 +2325,7 @@ void LinuxPlatform::BeforeRun()
|
||||
|
||||
void LinuxPlatform::Tick()
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
UnixPlatform::Tick();
|
||||
|
||||
LinuxInput::UpdateState();
|
||||
@@ -2312,9 +2342,9 @@ void LinuxPlatform::Tick()
|
||||
continue;
|
||||
|
||||
// External event handling
|
||||
xEventRecieved(&event);
|
||||
xEventReceived(&event);
|
||||
|
||||
LinuxWindow* window;
|
||||
Window* window;
|
||||
switch (event.type)
|
||||
{
|
||||
case ClientMessage:
|
||||
@@ -2646,6 +2676,7 @@ void LinuxPlatform::Tick()
|
||||
}
|
||||
|
||||
//X11::XFlush(xDisplay);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LinuxPlatform::BeforeExit()
|
||||
@@ -2654,6 +2685,7 @@ void LinuxPlatform::BeforeExit()
|
||||
|
||||
void LinuxPlatform::Exit()
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
for (int32 i = 0; i < (int32)CursorType::MAX; i++)
|
||||
{
|
||||
if (Cursors[i])
|
||||
@@ -2679,6 +2711,7 @@ void LinuxPlatform::Exit()
|
||||
X11::XCloseDisplay(xDisplay);
|
||||
xDisplay = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
String LinuxPlatform::GetSystemName()
|
||||
@@ -2700,6 +2733,7 @@ Version LinuxPlatform::GetSystemVersion()
|
||||
return Version(0, 0);
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
int32 LinuxPlatform::GetDpi()
|
||||
{
|
||||
return SystemDpi;
|
||||
@@ -2709,6 +2743,7 @@ String LinuxPlatform::GetUserLocaleName()
|
||||
{
|
||||
return UserLocale;
|
||||
}
|
||||
#endif
|
||||
|
||||
String LinuxPlatform::GetComputerName()
|
||||
{
|
||||
@@ -2916,10 +2951,12 @@ bool LinuxPlatform::SetWorkingDirectory(const String& path)
|
||||
return chdir(StringAsANSI<>(*path).Get()) != 0;
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
Window* LinuxPlatform::CreateWindow(const CreateWindowSettings& settings)
|
||||
{
|
||||
return New<LinuxWindow>(settings);
|
||||
}
|
||||
#endif
|
||||
|
||||
extern char **environ;
|
||||
|
||||
@@ -2961,6 +2998,7 @@ bool LinuxPlatform::SetEnvironmentVariable(const String& name, const String& val
|
||||
return setenv(StringAsANSI<>(*name).Get(), StringAsANSI<>(*value).Get(), true) != 0;
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
int32 LinuxPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
{
|
||||
LOG(Info, "Command: {0} {1}", settings.FileName, settings.Arguments);
|
||||
@@ -3074,6 +3112,7 @@ int32 LinuxPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
#endif
|
||||
|
||||
void* LinuxPlatform::LoadLibrary(const Char* filename)
|
||||
{
|
||||
@@ -3130,4 +3169,160 @@ Array<LinuxPlatform::StackFrame> LinuxPlatform::GetStackFrames(int32 skipCount,
|
||||
return result;
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Math/Vector4.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Platform/Linux/LinuxPlatform.h"
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
|
||||
#if PLATFORM_SDL
|
||||
#include <libportal/portal-enums.h>
|
||||
#include <libportal/screenshot.h>
|
||||
|
||||
namespace PortalImpl
|
||||
{
|
||||
XdpPortal* Portal = nullptr;
|
||||
int64 MainLoopReady = 0;
|
||||
|
||||
gpointer GLibMainLoop(gpointer data);
|
||||
void PickColorCallback(GObject* source, GAsyncResult* result, gpointer data);
|
||||
}
|
||||
#endif
|
||||
|
||||
Color32 LinuxPlatform::GetScreenColorAt(const Float2& pos)
|
||||
{
|
||||
X11::Display* display = (X11::Display*)Platform::GetXDisplay();
|
||||
if (display)
|
||||
{
|
||||
int defaultScreen = X11::XDefaultScreen(display);
|
||||
X11::Window rootWindow = X11::XRootWindow(display, defaultScreen);
|
||||
X11::XImage* image = X11::XGetImage(display, rootWindow, (int)pos.X, (int)pos.Y, 1, 1, AllPlanes, XYPixmap);
|
||||
if (image)
|
||||
{
|
||||
X11::XColor color;
|
||||
color.pixel = XGetPixel(image, 0, 0);
|
||||
X11::XFree(image);
|
||||
|
||||
X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color);
|
||||
|
||||
Color32 outputColor;
|
||||
outputColor.R = color.red / 256;
|
||||
outputColor.G = color.green / 256;
|
||||
outputColor.B = color.blue / 256;
|
||||
outputColor.A = 255;
|
||||
return outputColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
// XWayland doesn't support XGetImage
|
||||
}
|
||||
}
|
||||
|
||||
return Color32::Transparent;
|
||||
}
|
||||
|
||||
void OnScreenUtilsXEventCallback(void* eventPtr)
|
||||
{
|
||||
X11::XEvent* event = (X11::XEvent*)eventPtr;
|
||||
X11::Display* display = (X11::Display*)Platform::GetXDisplay();
|
||||
if (event->type == ButtonPress)
|
||||
{
|
||||
const Float2 cursorPos = Platform::GetMousePosition();
|
||||
const Color32 colorPicked = LinuxPlatform::GetScreenColorAt(cursorPos);
|
||||
X11::XUngrabPointer(display, CurrentTime);
|
||||
PlatformBase::PickScreenColorDone(colorPicked);
|
||||
LinuxPlatform::xEventReceived.Unbind(OnScreenUtilsXEventCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxPlatform::PickScreenColor()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
X11::Display* display = (X11::Display*)Platform::GetXDisplay();
|
||||
if (display)
|
||||
{
|
||||
X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
|
||||
X11::Cursor cursor = XCreateFontCursor(display, 130);
|
||||
int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime);
|
||||
if (grabbedPointer != GrabSuccess)
|
||||
{
|
||||
LOG(Error, "Failed to grab cursor for events.");
|
||||
X11::XFreeCursor(display, cursor);
|
||||
return;
|
||||
}
|
||||
|
||||
X11::XFreeCursor(display, cursor);
|
||||
LinuxPlatform::xEventReceived.Bind(OnScreenUtilsXEventCallback);
|
||||
return;
|
||||
}
|
||||
#if PLATFORM_SDL
|
||||
if (PortalImpl::MainLoopReady == 0)
|
||||
{
|
||||
// Initialize portal
|
||||
GError* error = nullptr;
|
||||
PortalImpl::Portal = xdp_portal_initable_new(&error);
|
||||
if (error != nullptr)
|
||||
{
|
||||
PortalImpl::MainLoopReady = 2;
|
||||
LOG(Error, "Failed to initialize XDP Portal");
|
||||
return;
|
||||
}
|
||||
|
||||
// Run the GLib main loop in other thread in order to process asynchronous callbacks
|
||||
g_thread_new(nullptr, PortalImpl::GLibMainLoop, nullptr);
|
||||
while (Platform::AtomicRead(&PortalImpl::MainLoopReady) != 1)
|
||||
Platform::Sleep(1);
|
||||
}
|
||||
|
||||
if (PortalImpl::Portal != nullptr)
|
||||
{
|
||||
// Enter color picking mode, the callback receives the final color
|
||||
xdp_portal_pick_color(PortalImpl::Portal, nullptr, nullptr, PortalImpl::PickColorCallback, nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
gpointer PortalImpl::GLibMainLoop(gpointer data)
|
||||
{
|
||||
GMainContext* mainContext = g_main_context_get_thread_default();
|
||||
GMainLoop* mainLoop = g_main_loop_new(mainContext, false);
|
||||
|
||||
Platform::AtomicStore(&PortalImpl::MainLoopReady, 1);
|
||||
|
||||
g_main_loop_run(mainLoop);
|
||||
g_main_loop_unref(mainLoop);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PortalImpl::PickColorCallback(GObject* source, GAsyncResult* result, gpointer data)
|
||||
{
|
||||
GError* error = nullptr;
|
||||
GVariant* variant = xdp_portal_pick_color_finish(PortalImpl::Portal, result, &error);
|
||||
if (error)
|
||||
{
|
||||
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
LOG(Info, "XDP Portal pick color cancelled");
|
||||
else
|
||||
LOG(Error, "XDP Portal pick color failed: {}", String(error->message));
|
||||
return;
|
||||
}
|
||||
|
||||
// The color is stored in a triple double variant, extract the values
|
||||
Double4 colorDouble;
|
||||
g_variant_get(variant, "(ddd)", &colorDouble.X, &colorDouble.Y, &colorDouble.Z);
|
||||
g_variant_unref(variant);
|
||||
colorDouble.W = 1.0f;
|
||||
Vector4 colorVector = colorDouble;
|
||||
Color32 color = Color32(colorVector);
|
||||
|
||||
PlatformBase::PickScreenColorDone(color);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
/// <summary>
|
||||
/// The Linux platform implementation and application management utilities.
|
||||
/// </summary>
|
||||
API_CLASS(Static, Tag="NoTypeInitializer")
|
||||
class FLAXENGINE_API LinuxPlatform : public UnixPlatform
|
||||
{
|
||||
public:
|
||||
@@ -30,13 +31,17 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the current user home directory.
|
||||
/// </summary>
|
||||
/// <returns>The user home directory.</returns>
|
||||
static const String& GetHomeDirectory();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the display server name on Linux (eg. X11, Wayland).
|
||||
/// </summary>
|
||||
API_PROPERTY() static String GetDisplayServer();
|
||||
|
||||
/// <summary>
|
||||
/// An event that is fired when an XEvent is received during platform tick.
|
||||
/// </summary>
|
||||
static Delegate<void*> xEventRecieved;
|
||||
static Delegate<void*> xEventReceived;
|
||||
|
||||
public:
|
||||
|
||||
@@ -124,8 +129,10 @@ public:
|
||||
static void Tick();
|
||||
static void BeforeExit();
|
||||
static void Exit();
|
||||
#if !PLATFORM_SDL
|
||||
static int32 GetDpi();
|
||||
static String GetUserLocaleName();
|
||||
#endif
|
||||
static String GetComputerName();
|
||||
static bool GetHasFocus();
|
||||
static bool CanOpenUrl(const StringView& url);
|
||||
@@ -140,15 +147,23 @@ public:
|
||||
static Guid GetUniqueDeviceId();
|
||||
static String GetWorkingDirectory();
|
||||
static bool SetWorkingDirectory(const String& path);
|
||||
#if !PLATFORM_SDL
|
||||
static Window* CreateWindow(const CreateWindowSettings& settings);
|
||||
#endif
|
||||
static void GetEnvironmentVariables(Dictionary<String, String, HeapAllocation>& result);
|
||||
static bool GetEnvironmentVariable(const String& name, String& value);
|
||||
static bool SetEnvironmentVariable(const String& name, const String& value);
|
||||
#if !PLATFORM_SDL
|
||||
static int32 CreateProcess(CreateProcessSettings& settings);
|
||||
#endif
|
||||
static void* LoadLibrary(const Char* filename);
|
||||
static void FreeLibrary(void* handle);
|
||||
static void* GetProcAddress(void* handle, const char* symbol);
|
||||
static Array<StackFrame, HeapAllocation> GetStackFrames(int32 skipCount = 0, int32 maxDepth = 60, void* context = nullptr);
|
||||
#if USE_EDITOR
|
||||
static Color32 GetScreenColorAt(const Float2& pos);
|
||||
static void PickScreenColor();
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
#if PLATFORM_LINUX && !PLATFORM_SDL
|
||||
|
||||
#include "../Window.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
@@ -112,7 +112,7 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
windowAttributes.border_pixel = XBlackPixel(display, screen);
|
||||
windowAttributes.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask;
|
||||
|
||||
if (!settings.IsRegularWindow)
|
||||
if (settings.Type != WindowType::Regular)
|
||||
{
|
||||
windowAttributes.save_under = true;
|
||||
windowAttributes.override_redirect = true;
|
||||
@@ -126,7 +126,7 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
*/
|
||||
|
||||
unsigned long valueMask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap;
|
||||
if (!settings.IsRegularWindow)
|
||||
if (settings.Type != WindowType::Regular)
|
||||
{
|
||||
valueMask |= CWOverrideRedirect | CWSaveUnder;
|
||||
}
|
||||
@@ -183,7 +183,7 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
X11::XClassHint* classHint = X11::XAllocClassHint();
|
||||
if (classHint)
|
||||
{
|
||||
const char* className = settings.IsRegularWindow ? "FlexEditor" : "FlaxPopup";
|
||||
const char* className = settings.Type == WindowType::Regular ? "FlaxEditor" : "FlaxPopup";
|
||||
|
||||
classHint->res_name = const_cast<char*>(className);
|
||||
classHint->res_class = const_cast<char*>(className);
|
||||
@@ -234,7 +234,7 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
}
|
||||
|
||||
// Adjust type for utility windows
|
||||
if (!settings.IsRegularWindow)
|
||||
if (settings.Type != WindowType::Regular)
|
||||
{
|
||||
X11::Atom value = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", 0);
|
||||
X11::Atom wmType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", 0);
|
||||
@@ -374,11 +374,6 @@ void LinuxWindow::BringToFront(bool force)
|
||||
X11::XFlush(display);
|
||||
}
|
||||
|
||||
bool LinuxWindow::IsClosed() const
|
||||
{
|
||||
return _isClosing;
|
||||
}
|
||||
|
||||
bool LinuxWindow::IsForegroundWindow() const
|
||||
{
|
||||
return _focused || _focusOnMapped;
|
||||
@@ -631,7 +626,7 @@ void LinuxWindow::OnButtonPress(void* event)
|
||||
}
|
||||
|
||||
// Handle double-click
|
||||
if (buttonEvent->button == Button1)
|
||||
if (buttonEvent->button == Button1 && !Input::Mouse->IsRelative())
|
||||
{
|
||||
if (
|
||||
buttonEvent->time < (MouseLastButtonPressTime + MouseDoubleClickTime) &&
|
||||
|
||||
@@ -72,7 +72,6 @@ public:
|
||||
void Minimize() override;
|
||||
void Maximize() override;
|
||||
void Restore() override;
|
||||
bool IsClosed() const override;
|
||||
bool IsForegroundWindow() const override;
|
||||
void BringToFront(bool force = false) override;
|
||||
void SetClientBounds(const Rectangle& clientArea) override;
|
||||
@@ -89,6 +88,7 @@ public:
|
||||
void Focus() override;
|
||||
void SetTitle(const StringView& title) override;
|
||||
DragDropEffect DoDragDrop(const StringView& data) override;
|
||||
using WindowBase::DoDragDrop;
|
||||
void StartTrackingMouse(bool useMouseScreenOffset) override;
|
||||
void EndTrackingMouse() override;
|
||||
void SetCursor(CursorType type) override;
|
||||
|
||||
@@ -276,6 +276,7 @@ bool MacPlatform::Init()
|
||||
CFRelease(computerName);
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Find the maximum scale of the display to handle high-dpi displays scaling factor
|
||||
{
|
||||
NSArray* screenArray = [NSScreen screens];
|
||||
@@ -300,6 +301,7 @@ bool MacPlatform::Init()
|
||||
|
||||
Input::Mouse = New<MacMouse>();
|
||||
Input::Keyboard = New<MacKeyboard>();
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -426,11 +428,16 @@ String MacPlatform::GetMainDirectory()
|
||||
return path;
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
|
||||
Window* MacPlatform::CreateWindow(const CreateWindowSettings& settings)
|
||||
{
|
||||
return New<MacWindow>(settings);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
int32 MacPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
{
|
||||
LOG(Info, "Command: {0} {1}", settings.FileName, settings.Arguments);
|
||||
@@ -570,5 +577,6 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -27,7 +27,9 @@ public:
|
||||
static Rectangle GetVirtualDesktopBounds();
|
||||
static String GetMainDirectory();
|
||||
static Window* CreateWindow(const CreateWindowSettings& settings);
|
||||
#if !PLATFORM_SDL
|
||||
static int32 CreateProcess(CreateProcessSettings& settings);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_MAC
|
||||
#if PLATFORM_MAC && !PLATFORM_SDL
|
||||
|
||||
#include "../Window.h"
|
||||
#include "Engine/Platform/Apple/AppleUtils.h"
|
||||
@@ -507,7 +507,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
Float2 mousePos = GetMousePosition(Window, event);
|
||||
mousePos = Window->ClientToScreen(mousePos);
|
||||
MouseButton mouseButton = MouseButton::Left;
|
||||
if ([event clickCount] == 2)
|
||||
if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
|
||||
Input::Mouse->OnMouseDoubleClick(mousePos, mouseButton, Window);
|
||||
else
|
||||
Input::Mouse->OnMouseDown(mousePos, mouseButton, Window);
|
||||
@@ -544,7 +544,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Float2 mousePos = GetMousePosition(Window, event);
|
||||
MouseButton mouseButton = MouseButton::Right;
|
||||
if ([event clickCount] == 2)
|
||||
if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
|
||||
Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window);
|
||||
else
|
||||
Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window);
|
||||
@@ -582,7 +582,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if ([event clickCount] == 2)
|
||||
if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
|
||||
Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window);
|
||||
else
|
||||
Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window);
|
||||
@@ -696,7 +696,7 @@ MacWindow::MacWindow(const CreateWindowSettings& settings)
|
||||
Float2 pos = AppleUtils::PosToCoca(settings.Position);
|
||||
NSRect frame = NSMakeRect(pos.X, pos.Y - settings.Size.Y, settings.Size.X, settings.Size.Y);
|
||||
NSUInteger styleMask = NSWindowStyleMaskClosable;
|
||||
if (settings.IsRegularWindow)
|
||||
if (settings.Type == WindowType::Regular)
|
||||
{
|
||||
styleMask |= NSWindowStyleMaskTitled;
|
||||
if (settings.AllowMinimize)
|
||||
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
void Focus() override;
|
||||
void SetTitle(const StringView& title) override;
|
||||
DragDropEffect DoDragDrop(const StringView& data) override;
|
||||
using WindowBase::DoDragDrop;
|
||||
void StartTrackingMouse(bool useMouseScreenOffset) override;
|
||||
void EndTrackingMouse() override;
|
||||
void SetCursor(CursorType type) override;
|
||||
|
||||
@@ -237,4 +237,9 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(MessageBox);
|
||||
/// <param name="icon">One of the MessageBoxIcon values that specifies which icon to display in the message box.</param>
|
||||
/// <returns>The message box dialog result.</returns>
|
||||
API_FUNCTION() static DialogResult Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon);
|
||||
|
||||
private:
|
||||
#if PLATFORM_SDL && PLATFORM_LINUX
|
||||
static DialogResult ShowFallback(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon);
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -89,6 +89,21 @@ public class Platform : EngineModule
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
|
||||
if (EngineConfiguration.WithSDL(options))
|
||||
{
|
||||
switch (options.Platform.Target)
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
case TargetPlatform.Linux:
|
||||
case TargetPlatform.Mac:
|
||||
options.PublicDependencies.Add("SDL");
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "SDL"));
|
||||
break;
|
||||
}
|
||||
if (options.Platform.Target == TargetPlatform.Linux)
|
||||
options.PublicDependencies.Add("Wayland");
|
||||
}
|
||||
if (options.Target.IsEditor)
|
||||
{
|
||||
// Include platform settings headers
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include "Types.h"
|
||||
#include "Defines.h"
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_SDL
|
||||
#include "SDL/SDLPlatform.h"
|
||||
#elif PLATFORM_WINDOWS
|
||||
#include "Windows/WindowsPlatform.h"
|
||||
#elif PLATFORM_UWP
|
||||
#include "UWP/UWPPlatform.h"
|
||||
|
||||
26
Source/Engine/Platform/SDL/SDLClipboard.h
Normal file
26
Source/Engine/Platform/SDL/SDLClipboard.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_LINUX
|
||||
|
||||
#include "Engine/Platform/Base/ClipboardBase.h"
|
||||
|
||||
/// <summary>
|
||||
/// SDL implementation of the clipboard service for Linux platform.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API SDLClipboard : public ClipboardBase
|
||||
{
|
||||
public:
|
||||
|
||||
// [ClipboardBase]
|
||||
static void Clear();
|
||||
static void SetText(const StringView& text);
|
||||
static void SetRawData(const Span<byte>& data);
|
||||
static void SetFiles(const Array<String>& files);
|
||||
static String GetText();
|
||||
static Array<byte> GetRawData();
|
||||
static Array<String> GetFiles();
|
||||
};
|
||||
|
||||
#endif
|
||||
836
Source/Engine/Platform/SDL/SDLInput.cpp
Normal file
836
Source/Engine/Platform/SDL/SDLInput.cpp
Normal file
@@ -0,0 +1,836 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
#include "SDLInput.h"
|
||||
#include "SDLWindow.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#include "Engine/Input/Keyboard.h"
|
||||
#include "Engine/Input/Gamepad.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Engine/Screen.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/Managed/ManagedEditor.h"
|
||||
#endif
|
||||
|
||||
#include <SDL3/SDL_events.h>
|
||||
|
||||
class SDLMouse;
|
||||
class SDLKeyboard;
|
||||
class SDLGamepad;
|
||||
|
||||
// TODO: Turn these into customizable values
|
||||
#define TRIGGER_THRESHOLD 30
|
||||
#define LEFT_STICK_THRESHOLD 7849
|
||||
#define RIGHT_STICK_THRESHOLD 8689
|
||||
|
||||
namespace SDLInputImpl
|
||||
{
|
||||
SDLMouse* Mouse = nullptr;
|
||||
SDLKeyboard* Keyboard = nullptr;
|
||||
Dictionary<SDL_JoystickID, SDLGamepad*> Gamepads;
|
||||
}
|
||||
|
||||
static const KeyboardKeys SDL_TO_FLAX_KEYS_MAP[] =
|
||||
{
|
||||
KeyboardKeys::None, // SDL_SCANCODE_UNKNOWN
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::A,
|
||||
KeyboardKeys::B,
|
||||
KeyboardKeys::C,
|
||||
KeyboardKeys::D,
|
||||
KeyboardKeys::E,
|
||||
KeyboardKeys::F,
|
||||
KeyboardKeys::G,
|
||||
KeyboardKeys::H,
|
||||
KeyboardKeys::I,
|
||||
KeyboardKeys::J,
|
||||
KeyboardKeys::K,
|
||||
KeyboardKeys::L,
|
||||
KeyboardKeys::M,
|
||||
KeyboardKeys::N,
|
||||
KeyboardKeys::O,
|
||||
KeyboardKeys::P,
|
||||
KeyboardKeys::Q,
|
||||
KeyboardKeys::R,
|
||||
KeyboardKeys::S,
|
||||
KeyboardKeys::T,
|
||||
KeyboardKeys::U,
|
||||
KeyboardKeys::V,
|
||||
KeyboardKeys::W,
|
||||
KeyboardKeys::X,
|
||||
KeyboardKeys::Y,
|
||||
KeyboardKeys::Z, // 29
|
||||
KeyboardKeys::Alpha1,
|
||||
KeyboardKeys::Alpha2,
|
||||
KeyboardKeys::Alpha3,
|
||||
KeyboardKeys::Alpha4,
|
||||
KeyboardKeys::Alpha5,
|
||||
KeyboardKeys::Alpha6,
|
||||
KeyboardKeys::Alpha7,
|
||||
KeyboardKeys::Alpha8,
|
||||
KeyboardKeys::Alpha9,
|
||||
KeyboardKeys::Alpha0, // 39
|
||||
KeyboardKeys::Return,
|
||||
KeyboardKeys::Escape,
|
||||
KeyboardKeys::Backspace,
|
||||
KeyboardKeys::Tab,
|
||||
KeyboardKeys::Spacebar,
|
||||
KeyboardKeys::Minus,
|
||||
KeyboardKeys::None, //KeyboardKeys::Equals, // ?
|
||||
KeyboardKeys::LeftBracket,
|
||||
KeyboardKeys::RightBracket,
|
||||
KeyboardKeys::Backslash, // SDL_SCANCODE_BACKSLASH ?
|
||||
KeyboardKeys::Oem102, // SDL_SCANCODE_NONUSHASH ?
|
||||
KeyboardKeys::Colon, // SDL_SCANCODE_SEMICOLON ?
|
||||
KeyboardKeys::Quote, // SDL_SCANCODE_APOSTROPHE
|
||||
KeyboardKeys::BackQuote, // SDL_SCANCODE_GRAVE
|
||||
KeyboardKeys::Comma,
|
||||
KeyboardKeys::Period,
|
||||
KeyboardKeys::Slash,
|
||||
KeyboardKeys::Capital,
|
||||
KeyboardKeys::F1,
|
||||
KeyboardKeys::F2,
|
||||
KeyboardKeys::F3,
|
||||
KeyboardKeys::F4,
|
||||
KeyboardKeys::F5,
|
||||
KeyboardKeys::F6,
|
||||
KeyboardKeys::F7,
|
||||
KeyboardKeys::F8,
|
||||
KeyboardKeys::F9,
|
||||
KeyboardKeys::F10,
|
||||
KeyboardKeys::F11,
|
||||
KeyboardKeys::F12,
|
||||
KeyboardKeys::PrintScreen,
|
||||
KeyboardKeys::Scroll,
|
||||
KeyboardKeys::Pause,
|
||||
KeyboardKeys::Insert,
|
||||
KeyboardKeys::Home,
|
||||
KeyboardKeys::PageUp,
|
||||
KeyboardKeys::Delete,
|
||||
KeyboardKeys::End,
|
||||
KeyboardKeys::PageDown,
|
||||
KeyboardKeys::ArrowRight,
|
||||
KeyboardKeys::ArrowLeft,
|
||||
KeyboardKeys::ArrowDown,
|
||||
KeyboardKeys::ArrowUp,
|
||||
KeyboardKeys::Numlock,
|
||||
KeyboardKeys::NumpadDivide,
|
||||
KeyboardKeys::NumpadMultiply,
|
||||
KeyboardKeys::NumpadSubtract,
|
||||
KeyboardKeys::NumpadAdd,
|
||||
KeyboardKeys::Return, // SDL_SCANCODE_KP_ENTER ?
|
||||
KeyboardKeys::Numpad1,
|
||||
KeyboardKeys::Numpad2,
|
||||
KeyboardKeys::Numpad3,
|
||||
KeyboardKeys::Numpad4,
|
||||
KeyboardKeys::Numpad5,
|
||||
KeyboardKeys::Numpad6,
|
||||
KeyboardKeys::Numpad7,
|
||||
KeyboardKeys::Numpad8,
|
||||
KeyboardKeys::Numpad9,
|
||||
KeyboardKeys::Numpad0, //98
|
||||
KeyboardKeys::NumpadDecimal, // SDL_SCANCODE_KP_PERIOD
|
||||
KeyboardKeys::Backslash, // SDL_SCANCODE_NONUSBACKSLASH ?
|
||||
KeyboardKeys::Applications,
|
||||
KeyboardKeys::Sleep, // SDL_SCANCODE_POWER ?
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_EQUALS ?
|
||||
KeyboardKeys::F13,
|
||||
KeyboardKeys::F14,
|
||||
KeyboardKeys::F15,
|
||||
KeyboardKeys::F16,
|
||||
KeyboardKeys::F17,
|
||||
KeyboardKeys::F18,
|
||||
KeyboardKeys::F19,
|
||||
KeyboardKeys::F20,
|
||||
KeyboardKeys::F21,
|
||||
KeyboardKeys::F22,
|
||||
KeyboardKeys::F23,
|
||||
KeyboardKeys::F24,
|
||||
KeyboardKeys::Execute,
|
||||
KeyboardKeys::Help,
|
||||
KeyboardKeys::LeftMenu, // SDL_SCANCODE_MENU ?
|
||||
KeyboardKeys::Select,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_STOP
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AGAIN
|
||||
KeyboardKeys::None, // SDL_SCANCODE_UNDO
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CUT
|
||||
KeyboardKeys::None, // SDL_SCANCODE_COPY
|
||||
KeyboardKeys::None, // SDL_SCANCODE_PASTE
|
||||
KeyboardKeys::None, // SDL_SCANCODE_FIND
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MUTE
|
||||
KeyboardKeys::None, // SDL_SCANCODE_VOLUMEUP
|
||||
KeyboardKeys::None, // SDL_SCANCODE_VOLUMEDOWN
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::NumpadSeparator, // SDL_SCANCODE_KP_COMMA ?
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_EQUALSAS400
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL1
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL2
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL3
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL4
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL5
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL6
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL7
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL8
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL9
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG1
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG2
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG3
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG4
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG5
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG6
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG7
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG8
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG9
|
||||
KeyboardKeys::None, // SDL_SCANCODE_ALTERASE
|
||||
KeyboardKeys::None, // SDL_SCANCODE_SYSREQ
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CANCEL
|
||||
KeyboardKeys::Clear, // SDL_SCANCODE_CLEAR
|
||||
KeyboardKeys::None, // SDL_SCANCODE_PRIOR
|
||||
KeyboardKeys::None, // SDL_SCANCODE_RETURN2
|
||||
KeyboardKeys::None, // SDL_SCANCODE_SEPARATOR
|
||||
KeyboardKeys::None, // SDL_SCANCODE_OUT
|
||||
KeyboardKeys::None, // SDL_SCANCODE_OPER
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CLEARAGAIN
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CRSEL
|
||||
KeyboardKeys::None, // SDL_SCANCODE_EXSEL
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_00
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_000
|
||||
KeyboardKeys::None, // SDL_SCANCODE_THOUSANDSSEPARATOR
|
||||
KeyboardKeys::None, // SDL_SCANCODE_DECIMALSEPARATOR
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CURRENCYUNIT
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CURRENCYSUBUNIT
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_LEFTPAREN = 182,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_RIGHTPAREN = 183,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_LEFTBRACE = 184,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_RIGHTBRACE = 185,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_TAB = 186,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_BACKSPACE = 187,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_A = 188,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_B = 189,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_C = 190,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_D = 191,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_E = 192,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_F = 193,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_XOR = 194,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_POWER = 195,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_PERCENT = 196,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_LESS = 197,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_GREATER = 198,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_AMPERSAND = 199,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_DBLAMPERSAND = 200,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_VERTICALBAR = 201,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_DBLVERTICALBAR = 202,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_COLON = 203,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_HASH = 204,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_SPACE = 205,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_AT = 206,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_EXCLAM = 207,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMSTORE = 208,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMRECALL = 209,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMCLEAR = 210,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMADD = 211,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMSUBTRACT = 212,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMMULTIPLY = 213,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMDIVIDE = 214,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_PLUSMINUS = 215,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_CLEAR = 216,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_CLEARENTRY = 217,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_BINARY = 218,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_OCTAL = 219,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_DECIMAL = 220,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_HEXADECIMAL = 221,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::Control, // SDL_SCANCODE_LCTRL = 224,
|
||||
KeyboardKeys::Shift, // SDL_SCANCODE_LSHIFT = 225,
|
||||
KeyboardKeys::Alt, // SDL_SCANCODE_LALT = 226,
|
||||
KeyboardKeys::LeftMenu, // SDL_SCANCODE_LGUI = 227,
|
||||
KeyboardKeys::Control, // SDL_SCANCODE_RCTRL = 228,
|
||||
KeyboardKeys::Shift, // SDL_SCANCODE_RSHIFT = 229,
|
||||
KeyboardKeys::Alt, // SDL_SCANCODE_RALT = 230,
|
||||
KeyboardKeys::RightMenu, // SDL_SCANCODE_RGUI = 231,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::Modechange, // SDL_SCANCODE_MODE
|
||||
KeyboardKeys::Sleep, // SDL_SCANCODE_SLEEP
|
||||
KeyboardKeys::None, // SDL_SCANCODE_WAKE
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CHANNEL_INCREMENT = 260,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CHANNEL_DECREMENT = 261,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_PLAY = 262,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_PAUSE = 263,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_RECORD = 264,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_FAST_FORWARD = 265,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_REWIND = 266,
|
||||
KeyboardKeys::MediaNextTrack, // SDL_SCANCODE_MEDIA_NEXT_TRACK = 267,
|
||||
KeyboardKeys::MediaPrevTrack, // SDL_SCANCODE_MEDIA_PREVIOUS_TRACK = 268,
|
||||
KeyboardKeys::MediaStop, // SDL_SCANCODE_MEDIA_STOP = 269,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_EJECT = 270,
|
||||
KeyboardKeys::MediaPlayPause, // SDL_SCANCODE_MEDIA_PLAY_PAUSE = 271,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_SELECT = 272,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_NEW = 273,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_OPEN = 274,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_CLOSE = 275,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_EXIT = 276,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_SAVE = 277,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_PRINT = 278,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_PROPERTIES = 279,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_SEARCH = 280,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_HOME = 281,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_BACK = 282,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_FORWARD = 283,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_STOP = 284,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_REFRESH = 285,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_BOOKMARKS = 286,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_SOFTLEFT = 287,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_SOFTRIGHT = 288,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CALL = 289,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_ENDCALL = 290
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the keyboard device for Windows platform.
|
||||
/// </summary>
|
||||
/// <seealso cref="Keyboard" />
|
||||
class SDLKeyboard : public Keyboard
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SDLKeyboard"/> class.
|
||||
/// </summary>
|
||||
explicit SDLKeyboard()
|
||||
: Keyboard()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the mouse device for SDL platform.
|
||||
/// </summary>
|
||||
/// <seealso cref="Mouse" />
|
||||
class SDLMouse : public Mouse
|
||||
{
|
||||
private:
|
||||
Float2 _oldPosition = Float2::Zero;
|
||||
Window* _relativeModeWindow = nullptr;
|
||||
const SDL_Rect* _oldScreenRect = nullptr;
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SDLMouse"/> class.
|
||||
/// </summary>
|
||||
explicit SDLMouse()
|
||||
: Mouse()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Returns the previous known position of the mouse before entering relative mode.
|
||||
/// </summary>
|
||||
Float2 GetOldMousePosition() const
|
||||
{
|
||||
ASSERT(_relativeModeWindow != nullptr);
|
||||
return _relativeModeWindow->ClientToScreen(_oldPosition);
|
||||
}
|
||||
|
||||
// [Mouse]
|
||||
void SetMousePosition(const Float2& screenPosition) final override
|
||||
{
|
||||
#if USE_EDITOR
|
||||
auto window = Editor::Managed->GetGameWindow();
|
||||
if (window == nullptr)
|
||||
window = Engine::MainWindow;
|
||||
#else
|
||||
auto window = Engine::MainWindow;
|
||||
#endif
|
||||
Float2 position = window->ScreenToClient(screenPosition);
|
||||
SDL_WarpMouseInWindow(static_cast<SDLWindow*>(window)->_window, position.X, position.Y);
|
||||
|
||||
OnMouseMoved(screenPosition);
|
||||
}
|
||||
|
||||
void SetRelativeMode(bool relativeMode, Window* window) final override
|
||||
{
|
||||
ASSERT(window != nullptr);
|
||||
if (relativeMode == _relativeMode)
|
||||
return;
|
||||
|
||||
auto windowHandle = static_cast<SDLWindow*>(window)->_window;
|
||||
if (relativeMode)
|
||||
{
|
||||
_relativeModeWindow = window;
|
||||
SDL_GetMouseState(&_oldPosition.X, &_oldPosition.Y);
|
||||
if (!SDL_CursorVisible())
|
||||
{
|
||||
// Trap the cursor in current location
|
||||
SDL_Rect clipRect = { (int)_oldPosition.X, (int)_oldPosition.Y, 1, 1 };
|
||||
_oldScreenRect = SDL_GetWindowMouseRect(windowHandle);
|
||||
SDL_SetWindowMouseRect(windowHandle, &clipRect);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_relativeModeWindow != window)
|
||||
{
|
||||
// FIXME: When floating game window is focused and editor viewport activated, the relative mode gets stuck
|
||||
return;
|
||||
}
|
||||
SDL_SetWindowMouseRect(windowHandle, nullptr);//oldScreenRect);
|
||||
SDL_WarpMouseInWindow(windowHandle, _oldPosition.X, _oldPosition.Y);
|
||||
_oldScreenRect = nullptr;
|
||||
_relativeModeWindow = nullptr;
|
||||
}
|
||||
|
||||
Mouse::SetRelativeMode(relativeMode, window);
|
||||
if (!SDL_SetWindowRelativeMouseMode(windowHandle, relativeMode))
|
||||
LOG(Error, "Failed to set mouse relative mode: {0}", String(SDL_GetError()));
|
||||
|
||||
#if PLATFORM_MAC
|
||||
// Warping right before entering relative mode seems to generate motion event for relative mode
|
||||
SDL_PumpEvents();
|
||||
SDL_FlushEvent(SDL_EVENT_MOUSE_MOTION);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsRelative(Window* window) const override
|
||||
{
|
||||
if (window == nullptr)
|
||||
return _relativeMode;
|
||||
return _relativeModeWindow == window && _relativeMode;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the gamepad device for SDL platform.
|
||||
/// </summary>
|
||||
/// <seealso cref="Gamepad" />
|
||||
class SDLGamepad : public Gamepad
|
||||
{
|
||||
private:
|
||||
|
||||
SDL_Gamepad* _gamepad = nullptr;
|
||||
SDL_JoystickID _instanceId;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SDLGamepad"/> class.
|
||||
/// </summary>
|
||||
/// <param name="userIndex">The joystick.</param>
|
||||
explicit SDLGamepad(SDL_JoystickID instanceId);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="SDLGamepad"/> class.
|
||||
/// </summary>
|
||||
~SDLGamepad();
|
||||
|
||||
private:
|
||||
|
||||
SDLGamepad(SDL_Gamepad* gamepad, SDL_JoystickID instanceId);
|
||||
|
||||
public:
|
||||
|
||||
static SDLGamepad* GetGamepadById(SDL_JoystickID id)
|
||||
{
|
||||
SDLGamepad* gamepad = nullptr;
|
||||
SDLInputImpl::Gamepads.TryGet(id, gamepad);
|
||||
return gamepad;
|
||||
}
|
||||
|
||||
SDL_JoystickID GetJoystickInstanceId() const
|
||||
{
|
||||
return _instanceId;
|
||||
}
|
||||
|
||||
void OnAxisMotion(SDL_GamepadAxis axis, int16 value);
|
||||
|
||||
void OnButtonState(SDL_GamepadButton axis, bool pressed);
|
||||
|
||||
// [Gamepad]
|
||||
void SetVibration(const GamepadVibrationState& state) override;
|
||||
|
||||
protected:
|
||||
|
||||
// [Gamepad]
|
||||
bool UpdateState() override;
|
||||
};
|
||||
|
||||
void SDLInput::Init()
|
||||
{
|
||||
Input::Mouse = SDLInputImpl::Mouse = New<SDLMouse>();
|
||||
Input::Keyboard = SDLInputImpl::Keyboard = New<SDLKeyboard>();
|
||||
}
|
||||
|
||||
void SDLInput::Update()
|
||||
{
|
||||
}
|
||||
|
||||
float NormalizeAxisValue(const int16 axisVal)
|
||||
{
|
||||
// Normalize [-32768..32767] -> [-1..1]
|
||||
const float norm = axisVal <= 0 ? 32768.0f : 32767.0f;
|
||||
return float(axisVal) / norm;
|
||||
}
|
||||
|
||||
bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
{
|
||||
if (Input::Mouse->IsRelative())
|
||||
{
|
||||
const Float2 mouseDelta(event.motion.xrel, event.motion.yrel);
|
||||
Input::Mouse->OnMouseMoveRelative(mouseDelta, window);
|
||||
}
|
||||
else
|
||||
{
|
||||
const Float2 mousePos = window->ClientToScreen({ event.motion.x, event.motion.y });
|
||||
Input::Mouse->OnMouseMove(mousePos, window);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
||||
{
|
||||
Input::Mouse->OnMouseLeave(window);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
{
|
||||
Float2 mousePos = window->ClientToScreen({ event.button.x, event.button.y });
|
||||
|
||||
// In case of activating the window from non-focused state, we might not have received the motion event yet
|
||||
if (!Input::Mouse->IsRelative() && event.button.down)
|
||||
Input::Mouse->OnMouseMove(mousePos, window);
|
||||
|
||||
MouseButton button = MouseButton::None;
|
||||
if (event.button.button == SDL_BUTTON_LEFT)
|
||||
button = MouseButton::Left;
|
||||
else if (event.button.button == SDL_BUTTON_RIGHT)
|
||||
button = MouseButton::Right;
|
||||
else if (event.button.button == SDL_BUTTON_MIDDLE)
|
||||
button = MouseButton::Middle;
|
||||
else if (event.button.button == SDL_BUTTON_X1)
|
||||
button = MouseButton::Extended1;
|
||||
else if (event.button.button == SDL_BUTTON_X2)
|
||||
button = MouseButton::Extended2;
|
||||
|
||||
if (Input::Mouse->IsRelative())
|
||||
{
|
||||
// Use the previous visible mouse position here, the event or global
|
||||
// mouse position would cause input to trigger in other editor windows.
|
||||
mousePos = SDLInputImpl::Mouse->GetOldMousePosition();
|
||||
}
|
||||
|
||||
if (!event.button.down)
|
||||
Input::Mouse->OnMouseUp(mousePos, button, window);
|
||||
// Prevent sending multiple mouse down event when double-clicking UI elements
|
||||
else if (event.button.clicks % 2 == 1)
|
||||
Input::Mouse->OnMouseDown(mousePos, button, window);
|
||||
else
|
||||
Input::Mouse->OnMouseDoubleClick(mousePos, button, window);
|
||||
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_WHEEL:
|
||||
{
|
||||
Float2 mousePos = window->ClientToScreen({ event.wheel.mouse_x, event.wheel.mouse_y });
|
||||
const float delta = event.wheel.y;
|
||||
|
||||
if (Input::Mouse->IsRelative())
|
||||
{
|
||||
// Use the previous visible mouse position here, the event or global
|
||||
// mouse position would cause input to trigger in other editor windows.
|
||||
mousePos = SDLInputImpl::Mouse->GetOldMousePosition();
|
||||
}
|
||||
|
||||
Input::Mouse->OnMouseWheel(mousePos, delta, window);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP:
|
||||
{
|
||||
// TODO: scancode support
|
||||
KeyboardKeys key = SDL_TO_FLAX_KEYS_MAP[event.key.scancode];
|
||||
//event.key.mod
|
||||
if (!event.key.down)
|
||||
Input::Keyboard->OnKeyUp(key, window);
|
||||
else
|
||||
Input::Keyboard->OnKeyDown(key, window);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_TEXT_EDITING:
|
||||
{
|
||||
auto edit = event.edit;
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_TEXT_INPUT:
|
||||
{
|
||||
String text(event.text.text);
|
||||
for (int i = 0; i < text.Length(); i++)
|
||||
{
|
||||
Input::Keyboard->OnCharInput(text[i], window);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||
{
|
||||
SDLGamepad* gamepad = SDLGamepad::GetGamepadById(event.gaxis.which);
|
||||
SDL_GamepadAxis axis = (SDL_GamepadAxis)event.gaxis.axis;
|
||||
gamepad->OnAxisMotion(axis, event.gaxis.value);
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||
{
|
||||
SDLGamepad* gamepad = SDLGamepad::GetGamepadById(event.gbutton.which);
|
||||
SDL_GamepadButton button = (SDL_GamepadButton)event.gbutton.button;
|
||||
gamepad->OnButtonState(button, event.gbutton.down);
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
{
|
||||
Input::Gamepads.Add(New<SDLGamepad>(event.gdevice.which));
|
||||
Input::OnGamepadsChanged();
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
{
|
||||
for (int i = 0; i < Input::Gamepads.Count(); i++)
|
||||
{
|
||||
SDLGamepad* gamepad = static_cast<SDLGamepad*>(Input::Gamepads[i]);
|
||||
if (gamepad->GetJoystickInstanceId() == event.gdevice.which)
|
||||
{
|
||||
Input::Gamepads[i]->DeleteObject();
|
||||
Input::Gamepads.RemoveAtKeepOrder(i);
|
||||
Input::OnGamepadsChanged();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_REMAPPED:
|
||||
{
|
||||
auto ev = event.gdevice;
|
||||
LOG(Info, "TODO: SDL_EVENT_GAMEPAD_REMAPPED");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
|
||||
{
|
||||
LOG(Info, "TODO: SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
|
||||
{
|
||||
LOG(Info, "TODO: SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
|
||||
{
|
||||
LOG(Info, "TODO: SDL_EVENT_GAMEPAD_TOUCHPAD_UP");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
|
||||
{
|
||||
LOG(Info, "TODO: SDL_EVENT_GAMEPAD_SENSOR_UPDATE");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED:
|
||||
{
|
||||
auto ev = event.gdevice;
|
||||
LOG(Info, "TODO: SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Guid GetGamepadGuid(SDL_JoystickID instanceId)
|
||||
{
|
||||
SDL_GUID joystickGuid = SDL_GetGamepadGUIDForID(instanceId);
|
||||
Guid guid;
|
||||
Platform::MemoryCopy(&guid.Raw, joystickGuid.data, sizeof(uint8) * 16);
|
||||
return guid;
|
||||
}
|
||||
|
||||
SDLGamepad::SDLGamepad(SDL_JoystickID instanceId)
|
||||
: SDLGamepad(SDL_OpenGamepad(instanceId), instanceId)
|
||||
{
|
||||
LOG(Info, "Gamepad connected: {}", String(SDL_GetGamepadName(_gamepad)));
|
||||
}
|
||||
|
||||
SDLGamepad::SDLGamepad(SDL_Gamepad* gamepad, SDL_JoystickID instanceId)
|
||||
: Gamepad(GetGamepadGuid(instanceId), String(SDL_GetGamepadName(gamepad)))
|
||||
, _gamepad(gamepad)
|
||||
, _instanceId(instanceId)
|
||||
{
|
||||
SDLInputImpl::Gamepads.Add(_instanceId, this);
|
||||
}
|
||||
|
||||
SDLGamepad::~SDLGamepad()
|
||||
{
|
||||
SDL_CloseGamepad(_gamepad);
|
||||
SDLInputImpl::Gamepads.Remove(_instanceId);
|
||||
}
|
||||
|
||||
void SDLGamepad::SetVibration(const GamepadVibrationState& state)
|
||||
{
|
||||
Gamepad::SetVibration(state);
|
||||
LOG(Info, "TODO: SDLGamepad::SetVibration");
|
||||
}
|
||||
|
||||
bool SDLGamepad::UpdateState()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDLGamepad::OnAxisMotion(SDL_GamepadAxis sdlAxis, int16 value)
|
||||
{
|
||||
GamepadAxis axis;
|
||||
int16 deadzone = 1; // SDL reports -1 for centered axis?
|
||||
float valueNormalized = NormalizeAxisValue(value);
|
||||
switch (sdlAxis)
|
||||
{
|
||||
case SDL_GAMEPAD_AXIS_LEFTX:
|
||||
axis = GamepadAxis::LeftStickX;
|
||||
deadzone = LEFT_STICK_THRESHOLD;
|
||||
_state.Buttons[(int32)GamepadButton::LeftStickLeft] = value > LEFT_STICK_THRESHOLD;
|
||||
_state.Buttons[(int32)GamepadButton::LeftStickRight] = value < -LEFT_STICK_THRESHOLD;
|
||||
break;
|
||||
case SDL_GAMEPAD_AXIS_LEFTY:
|
||||
axis = GamepadAxis::LeftStickY;
|
||||
deadzone = LEFT_STICK_THRESHOLD;
|
||||
_state.Buttons[(int32)GamepadButton::LeftStickUp] = value < -LEFT_STICK_THRESHOLD;
|
||||
_state.Buttons[(int32)GamepadButton::LeftStickDown] = value > LEFT_STICK_THRESHOLD;
|
||||
valueNormalized = -valueNormalized;
|
||||
break;
|
||||
case SDL_GAMEPAD_AXIS_RIGHTX:
|
||||
deadzone = RIGHT_STICK_THRESHOLD;
|
||||
axis = GamepadAxis::RightStickX;
|
||||
_state.Buttons[(int32)GamepadButton::RightStickLeft] = value > RIGHT_STICK_THRESHOLD;
|
||||
_state.Buttons[(int32)GamepadButton::RightStickRight] = value < -RIGHT_STICK_THRESHOLD;
|
||||
break;
|
||||
case SDL_GAMEPAD_AXIS_RIGHTY:
|
||||
deadzone = RIGHT_STICK_THRESHOLD;
|
||||
axis = GamepadAxis::RightStickY;
|
||||
_state.Buttons[(int32)GamepadButton::RightStickUp] = value < -RIGHT_STICK_THRESHOLD;
|
||||
_state.Buttons[(int32)GamepadButton::RightStickDown] = value > RIGHT_STICK_THRESHOLD;
|
||||
valueNormalized = -valueNormalized;
|
||||
break;
|
||||
case SDL_GAMEPAD_AXIS_LEFT_TRIGGER:
|
||||
deadzone = TRIGGER_THRESHOLD;
|
||||
axis = GamepadAxis::LeftTrigger;
|
||||
_state.Buttons[(int32)GamepadButton::LeftTrigger] = value > TRIGGER_THRESHOLD;
|
||||
break;
|
||||
case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER:
|
||||
deadzone = TRIGGER_THRESHOLD;
|
||||
axis = GamepadAxis::RightTrigger;
|
||||
_state.Buttons[(int32)GamepadButton::RightTrigger] = value > TRIGGER_THRESHOLD;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (value <= deadzone && value >= -deadzone)
|
||||
valueNormalized = 0.0f;
|
||||
_state.Axis[(int32)axis] = valueNormalized;
|
||||
}
|
||||
|
||||
void SDLGamepad::OnButtonState(SDL_GamepadButton sdlButton, bool pressed)
|
||||
{
|
||||
switch (sdlButton)
|
||||
{
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_SOUTH:
|
||||
_state.Buttons[(int32)GamepadButton::A] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_EAST:
|
||||
_state.Buttons[(int32)GamepadButton::B] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_WEST:
|
||||
_state.Buttons[(int32)GamepadButton::X] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_NORTH:
|
||||
_state.Buttons[(int32)GamepadButton::Y] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
||||
_state.Buttons[(int32)GamepadButton::LeftShoulder] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||
_state.Buttons[(int32)GamepadButton::RightShoulder] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_BACK:
|
||||
_state.Buttons[(int32)GamepadButton::Back] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_START:
|
||||
_state.Buttons[(int32)GamepadButton::Start] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_LEFT_STICK:
|
||||
_state.Buttons[(int32)GamepadButton::LeftThumb] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_RIGHT_STICK:
|
||||
_state.Buttons[(int32)GamepadButton::RightThumb] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||
_state.Buttons[(int32)GamepadButton::DPadUp] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||
_state.Buttons[(int32)GamepadButton::DPadDown] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||
_state.Buttons[(int32)GamepadButton::DPadLeft] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||
_state.Buttons[(int32)GamepadButton::DPadRight] = pressed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
22
Source/Engine/Platform/SDL/SDLInput.h
Normal file
22
Source/Engine/Platform/SDL/SDLInput.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
class SDLWindow;
|
||||
union SDL_Event;
|
||||
|
||||
/// <summary>
|
||||
/// SDL specific implementation of the input system parts.
|
||||
/// </summary>
|
||||
class SDLInput
|
||||
{
|
||||
public:
|
||||
|
||||
static void Init();
|
||||
static void Update();
|
||||
static bool HandleEvent(SDLWindow* window, SDL_Event& event);
|
||||
};
|
||||
|
||||
#endif
|
||||
1931
Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp
Normal file
1931
Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp
Normal file
File diff suppressed because it is too large
Load Diff
82
Source/Engine/Platform/SDL/SDLPlatform.Mac.cpp
Normal file
82
Source/Engine/Platform/SDL/SDLPlatform.Mac.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_MAC
|
||||
|
||||
#include "SDLWindow.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#include "Engine/Platform/IGuiData.h"
|
||||
#include "Engine/Platform/MessageBox.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/Base/DragDropHelper.h"
|
||||
#include "Engine/Platform/SDL/SDLClipboard.h"
|
||||
#include "Engine/Platform/Unix/UnixFile.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_messagebox.h>
|
||||
#include <SDL3/SDL_mouse.h>
|
||||
#include <SDL3/SDL_system.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
#include <SDL3/SDL_video.h>
|
||||
|
||||
bool SDLPlatform::InitInternal()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesWindows()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesWayland()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesX11()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDLPlatform::PreHandleEvents()
|
||||
{
|
||||
}
|
||||
|
||||
void SDLPlatform::PostHandleEvents()
|
||||
{
|
||||
}
|
||||
|
||||
bool SDLWindow::HandleEventInternal(SDL_Event& event)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
|
||||
{
|
||||
// TODO: This is now called before Platform::Init, ensure the scaling is changed accordingly during Platform::Init (see ApplePlatform::SetHighDpiAwarenessEnabled)
|
||||
}
|
||||
|
||||
DragDropEffect SDLWindow::DoDragDrop(const StringView& data)
|
||||
{
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
|
||||
DragDropEffect SDLWindow::DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow)
|
||||
{
|
||||
Show();
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
|
||||
#endif
|
||||
259
Source/Engine/Platform/SDL/SDLPlatform.Windows.cpp
Normal file
259
Source/Engine/Platform/SDL/SDLPlatform.Windows.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_WINDOWS
|
||||
|
||||
#include "SDLPlatform.h"
|
||||
#include "SDLInput.h"
|
||||
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_system.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
|
||||
#if USE_EDITOR
|
||||
#include <oleidl.h>
|
||||
#endif
|
||||
|
||||
namespace WinImpl
|
||||
{
|
||||
Window* DraggedWindow;
|
||||
Float2 DraggedWindowStartPosition = Float2::Zero;
|
||||
Float2 DraggedWindowMousePosition = Float2::Zero;
|
||||
Float2 DraggedWindowSize = Float2::Zero;
|
||||
}
|
||||
|
||||
// The events for releasing the mouse during window dragging are missing, handle the mouse release event here
|
||||
bool SDLCALL SDLPlatform::EventMessageHook(void* userdata, MSG* msg)
|
||||
{
|
||||
if (msg->message == WM_NCLBUTTONDOWN)
|
||||
{
|
||||
Window* window = WindowsManager::GetByNativePtr(msg->hwnd);
|
||||
Float2 mousePosition(static_cast<float>(static_cast<LONG>(WINDOWS_GET_X_LPARAM(msg->lParam))), static_cast<float>(static_cast<LONG>(WINDOWS_GET_Y_LPARAM(msg->lParam))));
|
||||
|
||||
WinImpl::DraggedWindow = window;
|
||||
WinImpl::DraggedWindowStartPosition = window->GetClientPosition();
|
||||
WinImpl::DraggedWindowMousePosition = mousePosition - WinImpl::DraggedWindowStartPosition;
|
||||
WinImpl::DraggedWindowSize = window->GetClientSize();
|
||||
|
||||
bool result = false;
|
||||
WindowHitCodes hit = static_cast<WindowHitCodes>(msg->wParam);
|
||||
window->OnHitTest(mousePosition, hit, result);
|
||||
//if (result && hit != WindowHitCodes::Caption)
|
||||
// return false;
|
||||
|
||||
if (hit == WindowHitCodes::Caption)
|
||||
{
|
||||
SDL_Event event{ 0 };
|
||||
event.button.type = SDL_EVENT_MOUSE_BUTTON_DOWN;
|
||||
event.button.down = true;
|
||||
event.button.timestamp = SDL_GetTicksNS();
|
||||
event.button.windowID = SDL_GetWindowID(window->GetSDLWindow());
|
||||
event.button.button = SDL_BUTTON_LEFT;
|
||||
event.button.clicks = 1;
|
||||
event.button.x = WinImpl::DraggedWindowMousePosition.X;
|
||||
event.button.y = WinImpl::DraggedWindowMousePosition.Y;
|
||||
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLPlatform::InitInternal()
|
||||
{
|
||||
// Workaround required for handling window dragging events properly
|
||||
SDL_SetWindowsMessageHook(&EventMessageHook, nullptr);
|
||||
|
||||
if (WindowsPlatform::Init())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::EventFilterCallback(void* userdata, SDL_Event* event)
|
||||
{
|
||||
Window* draggedWindow = *(Window**)userdata;
|
||||
if (draggedWindow == nullptr)
|
||||
return true;
|
||||
|
||||
// When the window is being dragged on Windows, the internal message loop is blocking
|
||||
// the SDL event queue. We need to handle all relevant events in this event watch callback
|
||||
// to ensure dragging related functionality doesn't break due to engine not getting updated.
|
||||
// This also happens to fix the engine freezing during the dragging operation.
|
||||
|
||||
SDLWindow* window = SDLWindow::GetWindowFromEvent(*event);
|
||||
if (event->type == SDL_EVENT_WINDOW_EXPOSED)
|
||||
{
|
||||
// The internal timer is sending exposed events every ~16ms
|
||||
Engine::OnUpdate(); // For docking updates
|
||||
Engine::OnDraw();
|
||||
return false;
|
||||
}
|
||||
else if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN)
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
bool result = false;
|
||||
window->OnLeftButtonHit(WindowHitCodes::Caption, result);
|
||||
//if (result)
|
||||
// return false;
|
||||
window->HandleEvent(*event);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (event->type == SDL_EVENT_WINDOW_MOVED)
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
window->HandleEvent(*event);
|
||||
|
||||
Float2 windowSize = window->GetClientSize();
|
||||
if (WinImpl::DraggedWindowSize != windowSize)
|
||||
{
|
||||
// The window size changed while dragging, most likely due to maximized window restoring back to previous size.
|
||||
WinImpl::DraggedWindowMousePosition = WinImpl::DraggedWindowStartPosition + WinImpl::DraggedWindowMousePosition - window->GetClientPosition();
|
||||
WinImpl::DraggedWindowStartPosition = window->GetClientPosition();
|
||||
WinImpl::DraggedWindowSize = windowSize;
|
||||
}
|
||||
Float2 windowPosition = Float2(static_cast<float>(event->window.data1), static_cast<float>(event->window.data2));
|
||||
Float2 mousePosition = WinImpl::DraggedWindowMousePosition;
|
||||
|
||||
// Generate mouse movement events while dragging the window around
|
||||
SDL_Event mouseMovedEvent{ 0 };
|
||||
mouseMovedEvent.motion.type = SDL_EVENT_MOUSE_MOTION;
|
||||
mouseMovedEvent.motion.windowID = SDL_GetWindowID(WinImpl::DraggedWindow->GetSDLWindow());
|
||||
mouseMovedEvent.motion.timestamp = SDL_GetTicksNS();
|
||||
mouseMovedEvent.motion.state = SDL_BUTTON_LEFT;
|
||||
mouseMovedEvent.motion.x = mousePosition.X;
|
||||
mouseMovedEvent.motion.y = mousePosition.Y;
|
||||
window->HandleEvent(mouseMovedEvent);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (window)
|
||||
window->HandleEvent(*event);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDLPlatform::PreHandleEvents()
|
||||
{
|
||||
SDL_AddEventWatch(EventFilterCallback, &WinImpl::DraggedWindow);
|
||||
}
|
||||
|
||||
void SDLPlatform::PostHandleEvents()
|
||||
{
|
||||
SDL_RemoveEventWatch(EventFilterCallback, &WinImpl::DraggedWindow);
|
||||
|
||||
// Handle window dragging release here
|
||||
if (WinImpl::DraggedWindow != nullptr)
|
||||
{
|
||||
Float2 mousePosition;
|
||||
auto buttons = SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y);
|
||||
|
||||
// Send simulated mouse up event
|
||||
SDL_Event buttonUpEvent { 0 };
|
||||
buttonUpEvent.motion.type = SDL_EVENT_MOUSE_BUTTON_UP;
|
||||
buttonUpEvent.button.down = false;
|
||||
buttonUpEvent.motion.windowID = SDL_GetWindowID(WinImpl::DraggedWindow->GetSDLWindow());
|
||||
buttonUpEvent.motion.timestamp = SDL_GetTicksNS();
|
||||
buttonUpEvent.motion.state = SDL_BUTTON_LEFT;
|
||||
buttonUpEvent.button.clicks = 1;
|
||||
buttonUpEvent.motion.x = mousePosition.X;
|
||||
buttonUpEvent.motion.y = mousePosition.Y;
|
||||
WinImpl::DraggedWindow->HandleEvent(buttonUpEvent);
|
||||
WinImpl::DraggedWindow = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool SDLWindow::HandleEventInternal(SDL_Event& event)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_EVENT_WINDOW_DESTROYED:
|
||||
{
|
||||
#if USE_EDITOR
|
||||
// Disable file dropping
|
||||
if (_settings.AllowDragAndDrop)
|
||||
{
|
||||
const auto result = RevokeDragDrop((HWND)_handle);
|
||||
if (result != S_OK)
|
||||
LOG(Warning, "Window drag and drop service error: 0x{0:x}:{1}", result, 2);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
{
|
||||
if (WinImpl::DraggedWindow != nullptr && WinImpl::DraggedWindow->_windowId != event.button.windowID)
|
||||
{
|
||||
// Send the button event to dragged window as well
|
||||
Float2 mousePos = ClientToScreen({ event.button.x, event.button.y });
|
||||
Float2 clientPos = WinImpl::DraggedWindow->ScreenToClient(mousePos);
|
||||
|
||||
SDL_Event event2 = event;
|
||||
event2.button.windowID = WinImpl::DraggedWindow->_windowId;
|
||||
event2.button.x = clientPos.X;
|
||||
event2.button.y = clientPos.Y;
|
||||
|
||||
SDLInput::HandleEvent(WinImpl::DraggedWindow, event2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesWindows()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesWayland()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesX11()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDLWindow::Focus()
|
||||
{
|
||||
auto activateWhenRaised = SDL_GetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED);
|
||||
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, "1");
|
||||
|
||||
// Forcing the window to focus causes issues with opening context menus while window is maximized
|
||||
//auto forceRaiseWindow = SDL_GetHint(SDL_HINT_FORCE_RAISEWINDOW);
|
||||
//SDL_SetHint(SDL_HINT_FORCE_RAISEWINDOW, "1");
|
||||
|
||||
SDL_RaiseWindow(_window);
|
||||
|
||||
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, activateWhenRaised);
|
||||
//SDL_SetHint(SDL_HINT_FORCE_RAISEWINDOW, forceRaiseWindow);
|
||||
}
|
||||
|
||||
void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
|
||||
{
|
||||
// Other supported values: "permonitor", "permonitorv2"
|
||||
SDL_SetHint("SDL_WINDOWS_DPI_AWARENESS", enable ? "system" : "unaware");
|
||||
}
|
||||
|
||||
DragDropEffect SDLWindow::DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow)
|
||||
{
|
||||
Show();
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
|
||||
#endif
|
||||
487
Source/Engine/Platform/SDL/SDLPlatform.cpp
Normal file
487
Source/Engine/Platform/SDL/SDLPlatform.cpp
Normal file
@@ -0,0 +1,487 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
#include "SDLPlatform.h"
|
||||
#include "SDLWindow.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#include "Engine/Platform/BatteryInfo.h"
|
||||
#include "Engine/Platform/CreateProcessSettings.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/SDL/SDLInput.h"
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_locale.h>
|
||||
#include <SDL3/SDL_misc.h>
|
||||
#include <SDL3/SDL_power.h>
|
||||
#include <SDL3/SDL_process.h>
|
||||
#include <SDL3/SDL_revision.h>
|
||||
#include <SDL3/SDL_system.h>
|
||||
#include <SDL3/SDL_version.h>
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#endif
|
||||
|
||||
#define DefaultDPI 96
|
||||
|
||||
namespace SDLImpl
|
||||
{
|
||||
int32 SystemDpi = 96;
|
||||
#if PLATFORM_LINUX
|
||||
String UserLocale("en");
|
||||
#endif
|
||||
bool WindowDecorationsSupported = true;
|
||||
bool SupportsDecorationDragging = true;
|
||||
String WaylandDisplayEnv;
|
||||
String XDGCurrentDesktop;
|
||||
}
|
||||
|
||||
bool SDLPlatform::Init()
|
||||
{
|
||||
#if PLATFORM_LINUX
|
||||
bool waylandSession = false;
|
||||
if (!GetEnvironmentVariable(String("WAYLAND_DISPLAY"), SDLImpl::WaylandDisplayEnv))
|
||||
waylandSession = true;
|
||||
GetEnvironmentVariable(String("XDG_CURRENT_DESKTOP"), SDLImpl::XDGCurrentDesktop);
|
||||
|
||||
if (CommandLine::Options.X11.IsTrue())
|
||||
{
|
||||
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "x11", SDL_HINT_OVERRIDE);
|
||||
waylandSession = false;
|
||||
}
|
||||
else if (CommandLine::Options.Wayland.IsTrue())
|
||||
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
|
||||
else if (waylandSession)
|
||||
{
|
||||
// Override the X11 preference when running in Wayland session
|
||||
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
|
||||
}
|
||||
|
||||
// Workaround for libdecor in Gnome+Wayland causing freezes when interacting with the native decorations
|
||||
if (waylandSession && SDLImpl::XDGCurrentDesktop.Compare(String("GNOME"), StringSearchCase::IgnoreCase) == 0)
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_VIDEO_WAYLAND_ALLOW_LIBDECOR, "0");
|
||||
SDLImpl::WindowDecorationsSupported = false;
|
||||
}
|
||||
if (waylandSession)
|
||||
SDLImpl::SupportsDecorationDragging = false;
|
||||
#endif
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
// The name follows the .desktop entry specification, this is used to get a fallback icon on Wayland:
|
||||
// https://specifications.freedesktop.org/desktop-entry-spec/latest/file-naming.html
|
||||
#if USE_EDITOR
|
||||
SDL_SetHint(SDL_HINT_APP_ID, StringAnsi("com.FlaxEngine.FlaxEditor").Get());
|
||||
#else
|
||||
// TODO: This should be read from the platform configuration (needed for desktop icon handling)
|
||||
SDL_SetHint(SDL_HINT_APP_ID, StringAnsi("com.FlaxEngine.FlaxGame").Get());
|
||||
#endif
|
||||
#else
|
||||
SDL_SetHint(SDL_HINT_APP_ID, StringAnsi(ApplicationClassName).Get());
|
||||
#endif
|
||||
|
||||
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, "0");
|
||||
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, "0");
|
||||
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); // Fixes context menu focus issues when clicking unfocused menus
|
||||
SDL_SetHint(SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE, "0");
|
||||
SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "0"); // Already handled during platform initialization
|
||||
SDL_SetHint("SDL_BORDERLESS_RESIZABLE_STYLE", "1"); // Allow borderless windows to be resizable on Windows
|
||||
//SDL_SetHint("SDL_BORDERLESS_WINDOWED_STYLE", "1");
|
||||
|
||||
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION, "0");
|
||||
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, "1"); // Needed for tracking mode
|
||||
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, "0"); // Relative mode can be active when cursor is shown and clipped
|
||||
SDL_SetHint(SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS, "8"); // Reduce the default mouse double-click radius
|
||||
|
||||
//SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1"); // Disables raw mouse input
|
||||
SDL_SetHint(SDL_HINT_WINDOWS_RAW_KEYBOARD, "1");
|
||||
|
||||
SDL_SetHint(SDL_HINT_VIDEO_WAYLAND_SCALE_TO_DISPLAY, "1");
|
||||
|
||||
//if (InitInternal())
|
||||
// return true;
|
||||
|
||||
if (!SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD))
|
||||
Platform::Fatal(String::Format(TEXT("Failed to initialize SDL: {0}."), String(SDL_GetError())));
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
int localesCount = 0;
|
||||
auto locales = SDL_GetPreferredLocales(&localesCount);
|
||||
for (int i = 0; i < localesCount; i++)
|
||||
{
|
||||
auto language = StringAnsiView(locales[i]->language);
|
||||
auto country = StringAnsiView(locales[i]->country);
|
||||
if (language.StartsWith("en"))
|
||||
{
|
||||
if (country != nullptr)
|
||||
SDLImpl::UserLocale = String::Format(TEXT("{0}-{1}"), String(language), String(locales[i]->country));
|
||||
else
|
||||
SDLImpl::UserLocale = String(language);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDL_free(locales);
|
||||
#endif
|
||||
|
||||
if (InitInternal())
|
||||
return true;
|
||||
|
||||
#if !PLATFORM_MAC
|
||||
if (!UsesWayland())
|
||||
{
|
||||
// Disable SDL clipboard support
|
||||
SDL_SetEventEnabled(SDL_EVENT_CLIPBOARD_UPDATE, false);
|
||||
|
||||
// Disable SDL drag and drop support
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, false);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_TEXT, false);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_BEGIN, false);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_COMPLETE, false);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_POSITION, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
SDLInput::Init();
|
||||
SDLWindow::Init();
|
||||
|
||||
SDLImpl::SystemDpi = (int)(SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()) * DefaultDPI);
|
||||
|
||||
//SDL_StartTextInput(); // TODO: Call this only when text input is expected (shows virtual keyboard in some cases)
|
||||
|
||||
return SDLPlatformBase::Init();
|
||||
}
|
||||
|
||||
void SDLPlatform::LogInfo()
|
||||
{
|
||||
SDLPlatformBase::LogInfo();
|
||||
|
||||
const int32 runtimeVersion = SDL_GetVersion();
|
||||
LOG(Info, "Using SDL version {}.{}.{} ({}), runtime: {}.{}.{} ({})",
|
||||
SDL_VERSIONNUM_MAJOR(SDL_VERSION), SDL_VERSIONNUM_MINOR(SDL_VERSION), SDL_VERSIONNUM_MICRO(SDL_VERSION), String(SDL_REVISION),
|
||||
SDL_VERSIONNUM_MAJOR(runtimeVersion), SDL_VERSIONNUM_MINOR(runtimeVersion), SDL_VERSIONNUM_MICRO(runtimeVersion), String(SDL_GetRevision()));
|
||||
|
||||
LOG(Info, "SDL video driver: {}", String(SDL_GetCurrentVideoDriver()));
|
||||
}
|
||||
|
||||
void SDLPlatform::Tick()
|
||||
{
|
||||
SDLInput::Update();
|
||||
|
||||
PreHandleEvents();
|
||||
|
||||
SDL_PumpEvents();
|
||||
SDL_Event events[32];
|
||||
int count = SDL_PeepEvents(events, SDL_arraysize(events), SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
SDLWindow* window = SDLWindow::GetWindowFromEvent(events[i]);
|
||||
if (window)
|
||||
window->HandleEvent(events[i]);
|
||||
else if (events[i].type >= SDL_EVENT_JOYSTICK_AXIS_MOTION && events[i].type <= SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED)
|
||||
SDLInput::HandleEvent(nullptr, events[i]);
|
||||
else
|
||||
HandleEvent(events[i]);
|
||||
}
|
||||
|
||||
PostHandleEvents();
|
||||
}
|
||||
|
||||
bool SDLPlatform::HandleEvent(SDL_Event& event)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLPlatform::SupportsNativeDecorations()
|
||||
{
|
||||
return SDLImpl::WindowDecorationsSupported;
|
||||
}
|
||||
|
||||
bool SDLPlatform::SupportsNativeDecorationDragging()
|
||||
{
|
||||
return SDLImpl::SupportsDecorationDragging;
|
||||
}
|
||||
|
||||
#if !PLATFORM_WINDOWS
|
||||
BatteryInfo SDLPlatform::GetBatteryInfo()
|
||||
{
|
||||
BatteryInfo info;
|
||||
int percentage;
|
||||
SDL_PowerState powerState = SDL_GetPowerInfo(nullptr, &percentage);
|
||||
|
||||
if (percentage < 0)
|
||||
info.BatteryLifePercent = 1.0f;
|
||||
else
|
||||
info.BatteryLifePercent = (float)percentage / 100.0f;
|
||||
|
||||
switch (powerState)
|
||||
{
|
||||
case SDL_POWERSTATE_CHARGING:
|
||||
info.State = BatteryInfo::States::BatteryCharging;
|
||||
break;
|
||||
case SDL_POWERSTATE_ON_BATTERY:
|
||||
info.State = BatteryInfo::States::BatteryDischarging;
|
||||
break;
|
||||
case SDL_POWERSTATE_CHARGED:
|
||||
info.State = BatteryInfo::States::Connected;
|
||||
break;
|
||||
default:
|
||||
info.State = BatteryInfo::States::Unknown;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
#endif
|
||||
|
||||
int32 SDLPlatform::GetDpi()
|
||||
{
|
||||
return SDLImpl::SystemDpi;
|
||||
}
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
String SDLPlatform::GetUserLocaleName()
|
||||
{
|
||||
return SDLImpl::UserLocale;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool SDLPlatform::CanOpenUrl(const StringView& url)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLPlatform::OpenUrl(const StringView& url)
|
||||
{
|
||||
StringAnsi urlStr(url);
|
||||
SDL_OpenURL(urlStr.GetText());
|
||||
}
|
||||
|
||||
Float2 SDLPlatform::GetMousePosition()
|
||||
{
|
||||
#if PLATFORM_LINUX
|
||||
if (UsesWayland())
|
||||
{
|
||||
// Wayland doesn't support reporting global mouse position,
|
||||
// use the last known reported position we got from received window events.
|
||||
return Input::GetMouseScreenPosition();
|
||||
}
|
||||
#endif
|
||||
Float2 pos;
|
||||
SDL_GetGlobalMouseState(&pos.X, &pos.Y);
|
||||
return pos;
|
||||
}
|
||||
|
||||
void SDLPlatform::SetMousePosition(const Float2& pos)
|
||||
{
|
||||
SDL_WarpMouseGlobal(pos.X, pos.Y);
|
||||
}
|
||||
|
||||
Float2 SDLPlatform::GetDesktopSize()
|
||||
{
|
||||
SDL_Rect rect;
|
||||
SDL_GetDisplayBounds(SDL_GetPrimaryDisplay(), &rect);
|
||||
return Float2(static_cast<float>(rect.w), static_cast<float>(rect.h));
|
||||
}
|
||||
|
||||
Rectangle SDLPlatform::GetMonitorBounds(const Float2& screenPos)
|
||||
{
|
||||
SDL_Point point{ (int32)screenPos.X, (int32)screenPos.Y };
|
||||
SDL_DisplayID display = SDL_GetDisplayForPoint(&point);
|
||||
SDL_Rect rect;
|
||||
SDL_GetDisplayBounds(display, &rect);
|
||||
return Rectangle(static_cast<float>(rect.x), static_cast<float>(rect.y), static_cast<float>(rect.w), static_cast<float>(rect.h));
|
||||
}
|
||||
|
||||
Rectangle SDLPlatform::GetVirtualDesktopBounds()
|
||||
{
|
||||
int count;
|
||||
const SDL_DisplayID* displays = SDL_GetDisplays(&count);
|
||||
if (displays == nullptr)
|
||||
return Rectangle::Empty;
|
||||
|
||||
Rectangle bounds = Rectangle::Empty;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
SDL_DisplayID display = displays[i];
|
||||
SDL_Rect rect;
|
||||
SDL_GetDisplayBounds(display, &rect);
|
||||
bounds = Rectangle::Union(bounds, Rectangle(static_cast<float>(rect.x), static_cast<float>(rect.y), static_cast<float>(rect.w), static_cast<float>(rect.h)));
|
||||
}
|
||||
SDL_free((void*)displays);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
Window* SDLPlatform::CreateWindow(const CreateWindowSettings& settings)
|
||||
{
|
||||
return New<SDLWindow>(settings);
|
||||
}
|
||||
|
||||
#if !PLATFORM_WINDOWS
|
||||
|
||||
bool ReadStream(SDL_IOStream*& stream, char* buffer, int64 bufferLength, int64& bufferPosition, LogType logType, CreateProcessSettings& settings)
|
||||
{
|
||||
bool flushBuffer = false;
|
||||
bool success = true;
|
||||
auto read = SDL_ReadIO(stream, buffer + bufferPosition, bufferLength - bufferPosition - 1);
|
||||
if (read == 0)
|
||||
{
|
||||
SDL_IOStatus status = SDL_GetIOStatus(stream);
|
||||
if (status != SDL_IO_STATUS_NOT_READY && status != SDL_IO_STATUS_EOF)
|
||||
success = false;
|
||||
if (status != SDL_IO_STATUS_NOT_READY)
|
||||
{
|
||||
stream = nullptr;
|
||||
flushBuffer = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int64 startPosition = bufferPosition;
|
||||
bufferPosition += (int64)read;
|
||||
if (bufferPosition == bufferLength - 1)
|
||||
{
|
||||
flushBuffer = true;
|
||||
buffer[bufferPosition++] = '\n'; // Make sure to flush fully filled buffer
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int64 i = startPosition; i < bufferPosition; ++i)
|
||||
{
|
||||
if (buffer[i] == '\n')
|
||||
{
|
||||
flushBuffer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flushBuffer)
|
||||
{
|
||||
int64 start = 0;
|
||||
for (int64 i = 0; i < bufferPosition; ++i)
|
||||
{
|
||||
if (buffer[i] != '\n')
|
||||
continue;
|
||||
|
||||
String str(&buffer[start], (int32)(i - start + 1));
|
||||
#if LOG_ENABLE
|
||||
if (settings.LogOutput)
|
||||
Log::Logger::Write(logType, StringView(str.Get(), str.Length() - 1));
|
||||
#endif
|
||||
if (settings.SaveOutput)
|
||||
settings.Output.Add(str.Get(), str.Length());
|
||||
start = i + 1;
|
||||
}
|
||||
int64 length = bufferPosition - start;
|
||||
if (length > 0)
|
||||
{
|
||||
// TODO: Use memmove here? Overlapped memory regions with memcpy is undefined behaviour
|
||||
char temp[2048];
|
||||
Platform::MemoryCopy(temp, buffer + start, length);
|
||||
Platform::MemoryCopy(buffer, temp, length);
|
||||
bufferPosition = length;
|
||||
}
|
||||
else
|
||||
bufferPosition = 0;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
{
|
||||
LOG(Info, "Command: {0} {1}", settings.FileName, settings.Arguments);
|
||||
if (settings.WorkingDirectory.HasChars())
|
||||
LOG(Info, "Working directory: {0}", settings.WorkingDirectory);
|
||||
|
||||
int32 result = 0;
|
||||
const bool captureStdOut = settings.LogOutput || settings.SaveOutput;
|
||||
const StringAnsi cmdLine = StringAnsi::Format("\"{0}\" {1}", StringAnsi(settings.FileName), StringAnsi(settings.Arguments));
|
||||
StringAnsi workingDirectory(settings.WorkingDirectory);
|
||||
|
||||
// Populate environment with current values from parent environment.
|
||||
// SDL does not populate the environment with the latest values but with a snapshot captured during initialization.
|
||||
Dictionary<String, String> envDictionary;
|
||||
GetEnvironmentVariables(envDictionary);
|
||||
SDL_Environment* env = SDL_CreateEnvironment(false);
|
||||
for (auto iter = envDictionary.Begin(); iter != envDictionary.End(); ++iter)
|
||||
SDL_SetEnvironmentVariable(env, StringAnsi(iter->Key).Get(), StringAnsi(iter->Value).Get(), true);
|
||||
for (auto iter = settings.Environment.Begin(); iter != settings.Environment.End(); ++iter)
|
||||
SDL_SetEnvironmentVariable(env, StringAnsi(iter->Key).Get(), StringAnsi(iter->Value).Get(), true);
|
||||
|
||||
// Parse argument list with possible quotes included
|
||||
Array<StringAnsi> arguments;
|
||||
arguments.Add(StringAnsi(settings.FileName));
|
||||
if (CommandLine::ParseArguments(settings.Arguments, arguments))
|
||||
{
|
||||
LOG(Error, "Failed to parse arguments for process {}: '{}'", settings.FileName.Get(), settings.Arguments.Get());
|
||||
return -1;
|
||||
}
|
||||
Array<const char*> cmd;
|
||||
for (const StringAnsi& str : arguments)
|
||||
cmd.Add(str.Get());
|
||||
cmd.Add((const char*)0);
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
bool background = !settings.WaitForEnd || settings.HiddenWindow; // This also hides the window on Windows
|
||||
#else
|
||||
bool background = !settings.WaitForEnd;
|
||||
#endif
|
||||
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, cmd.Get());
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, env);
|
||||
SDL_SetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN, background);
|
||||
if (workingDirectory.HasChars())
|
||||
SDL_SetStringProperty(props, SDL_PROP_PROCESS_CREATE_WORKING_DIRECTORY_STRING, workingDirectory.Get());
|
||||
if (captureStdOut)
|
||||
{
|
||||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_APP);
|
||||
}
|
||||
SDL_Process* process = SDL_CreateProcessWithProperties(props);
|
||||
SDL_DestroyProperties(props);
|
||||
SDL_DestroyEnvironment(env);
|
||||
if (process == nullptr)
|
||||
{
|
||||
LOG(Error, "Failed to run process {}: {}", settings.FileName.Get(), String(SDL_GetError()));
|
||||
return -1;
|
||||
}
|
||||
|
||||
props = SDL_GetProcessProperties(process);
|
||||
int64 pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0);
|
||||
SDL_IOStream* stdoutStream = nullptr;
|
||||
SDL_IOStream* stderrStream = nullptr;
|
||||
if (captureStdOut)
|
||||
{
|
||||
stdoutStream = static_cast<SDL_IOStream*>(SDL_GetPointerProperty(props, SDL_PROP_PROCESS_STDOUT_POINTER, nullptr));
|
||||
stderrStream = static_cast<SDL_IOStream*>(SDL_GetPointerProperty(props, SDL_PROP_PROCESS_STDERR_POINTER, nullptr));
|
||||
}
|
||||
|
||||
// Handle process output in realtime
|
||||
char stdoutBuffer[2049];
|
||||
int64 stdoutPosition = 0;
|
||||
char stderrBuffer[2049];
|
||||
int64 stderrPosition = 0;
|
||||
while (stdoutStream != nullptr && stderrStream != nullptr)
|
||||
{
|
||||
if (stdoutStream != nullptr && !ReadStream(stdoutStream, stdoutBuffer, sizeof(stdoutBuffer), stdoutPosition, LogType::Info, settings))
|
||||
LOG(Warning, "Failed to read process {} stdout: {}", pid, String(SDL_GetError()));
|
||||
if (stderrStream != nullptr && !ReadStream(stderrStream, stderrBuffer, sizeof(stderrBuffer), stderrPosition, LogType::Error, settings))
|
||||
LOG(Warning, "Failed to read process {} stderr: {}", pid, String(SDL_GetError()));
|
||||
Sleep(1);
|
||||
}
|
||||
|
||||
if (settings.WaitForEnd)
|
||||
SDL_WaitProcess(process, true, &result);
|
||||
|
||||
SDL_DestroyProcess(process);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
103
Source/Engine/Platform/SDL/SDLPlatform.h
Normal file
103
Source/Engine/Platform/SDL/SDLPlatform.h
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
#include "Engine/Platform/Base/Enums.h"
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "Engine/Platform/Windows/WindowsPlatform.h"
|
||||
typedef struct tagMSG MSG;
|
||||
#elif PLATFORM_LINUX
|
||||
#include "Engine/Platform/Linux/LinuxPlatform.h"
|
||||
union _XEvent;
|
||||
#elif PLATFORM_MAC
|
||||
#include "Engine/Platform/Mac/MacPlatform.h"
|
||||
#else
|
||||
static_assert(false, "Unsupported Platform");
|
||||
#endif
|
||||
|
||||
class SDLWindow;
|
||||
union SDL_Event;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
typedef WindowsPlatform SDLPlatformBase;
|
||||
#elif PLATFORM_LINUX
|
||||
typedef LinuxPlatform SDLPlatformBase;
|
||||
#elif PLATFORM_MAC
|
||||
typedef MacPlatform SDLPlatformBase;
|
||||
#else
|
||||
static_assert(false, "Unsupported SDL platform.");
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The SDL platform implementation and application management utilities.
|
||||
/// </summary>
|
||||
API_CLASS(Static, Tag="NoTypeInitializer")
|
||||
class FLAXENGINE_API SDLPlatform : public SDLPlatformBase
|
||||
{
|
||||
friend SDLWindow;
|
||||
|
||||
private:
|
||||
static bool InitInternal();
|
||||
#if PLATFORM_LINUX
|
||||
static bool InitX11(void* display);
|
||||
#endif
|
||||
static bool HandleEvent(SDL_Event& event);
|
||||
#if PLATFORM_WINDOWS
|
||||
static bool EventMessageHook(void* userdata, MSG* msg);
|
||||
static bool EventFilterCallback(void* userdata, SDL_Event* event);
|
||||
#elif PLATFORM_LINUX
|
||||
static bool X11EventHook(void* userdata, _XEvent* xevent);
|
||||
#elif PLATFORM_MAC
|
||||
static bool EventFilterCallback(void* userdata, SDL_Event* event);
|
||||
#endif
|
||||
static void PreHandleEvents();
|
||||
static void PostHandleEvents();
|
||||
|
||||
public:
|
||||
#if PLATFORM_LINUX
|
||||
static void* GetXDisplay();
|
||||
static String GetDisplayServer();
|
||||
#endif
|
||||
static bool UsesWindows();
|
||||
static bool UsesWayland();
|
||||
static bool UsesX11();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if system provides decorations for windows.
|
||||
/// </summary>
|
||||
API_PROPERTY() static bool SupportsNativeDecorations();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if system provides support for native window dragging events.
|
||||
/// </summary>
|
||||
API_PROPERTY() static bool SupportsNativeDecorationDragging();
|
||||
|
||||
public:
|
||||
// [PlatformBase]
|
||||
static bool Init();
|
||||
static void LogInfo();
|
||||
static void Tick();
|
||||
static void SetHighDpiAwarenessEnabled(bool enable);
|
||||
#if !PLATFORM_WINDOWS
|
||||
static BatteryInfo GetBatteryInfo();
|
||||
#endif
|
||||
static int32 GetDpi();
|
||||
#if PLATFORM_LINUX
|
||||
static String GetUserLocaleName();
|
||||
#endif
|
||||
static bool CanOpenUrl(const StringView& url);
|
||||
static void OpenUrl(const StringView& url);
|
||||
static Float2 GetMousePosition();
|
||||
static void SetMousePosition(const Float2& pos);
|
||||
static Float2 GetDesktopSize();
|
||||
static Rectangle GetMonitorBounds(const Float2& screenPos);
|
||||
static Rectangle GetVirtualDesktopBounds();
|
||||
static Window* CreateWindow(const CreateWindowSettings& settings);
|
||||
#if !PLATFORM_WINDOWS
|
||||
static int32 CreateProcess(CreateProcessSettings& settings);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
1023
Source/Engine/Platform/SDL/SDLWindow.cpp
Normal file
1023
Source/Engine/Platform/SDL/SDLWindow.cpp
Normal file
File diff suppressed because it is too large
Load Diff
130
Source/Engine/Platform/SDL/SDLWindow.h
Normal file
130
Source/Engine/Platform/SDL/SDLWindow.h
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
#include "Engine/Platform/Base/WindowBase.h"
|
||||
|
||||
struct SDL_Window;
|
||||
union SDL_Event;
|
||||
#if PLATFORM_LINUX
|
||||
class MessageBox;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the window class for SDL platform
|
||||
/// </summary>
|
||||
class FLAXENGINE_API SDLWindow : public WindowBase
|
||||
#if USE_EDITOR && PLATFORM_WINDOWS
|
||||
, public Windows::IDropTarget
|
||||
#endif
|
||||
{
|
||||
friend SDLPlatform;
|
||||
friend class SDLMouse;
|
||||
#if PLATFORM_LINUX
|
||||
friend LinuxPlatform;
|
||||
friend MessageBox;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void* _handle; // Opaque, platform specific window handle
|
||||
#if USE_EDITOR && PLATFORM_WINDOWS
|
||||
Windows::ULONG _refCount;
|
||||
#endif
|
||||
#if PLATFORM_LINUX
|
||||
bool _dragOver;
|
||||
#endif
|
||||
SDL_Window* _window;
|
||||
uint32 _windowId;
|
||||
Rectangle _clipCursorRect;
|
||||
Rectangle _cachedClientRectangle;
|
||||
|
||||
public:
|
||||
static void Init();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SDLWindow"/> class.
|
||||
/// </summary>
|
||||
/// <param name="settings">The initial window settings.</param>
|
||||
SDLWindow(const CreateWindowSettings& settings);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="SDLWindow"/> class.
|
||||
/// </summary>
|
||||
~SDLWindow();
|
||||
|
||||
private:
|
||||
static SDLWindow* GetWindowFromEvent(const SDL_Event& event);
|
||||
static SDLWindow* GetWindowWithSDLWindow(SDL_Window* window);
|
||||
void HandleEvent(SDL_Event& event);
|
||||
bool HandleEventInternal(SDL_Event& event);
|
||||
void CheckForWindowResize();
|
||||
void UpdateCursor();
|
||||
|
||||
public:
|
||||
SDL_Window* GetSDLWindow() const;
|
||||
#if PLATFORM_LINUX
|
||||
void* GetWaylandDisplay() const;
|
||||
void* GetX11Display() const;
|
||||
#endif
|
||||
WindowHitCodes OnWindowHit(const Float2 point);
|
||||
|
||||
// [WindowBase]
|
||||
void* GetNativePtr() const override;
|
||||
void Show() override;
|
||||
void Hide() override;
|
||||
void Minimize() override;
|
||||
void Maximize() override;
|
||||
void SetBorderless(bool isBorderless, bool maximized = false) override;
|
||||
void Restore() override;
|
||||
bool IsClosed() const override;
|
||||
bool IsForegroundWindow() const override;
|
||||
void BringToFront(bool force = false) override;
|
||||
void SetClientBounds(const Rectangle& clientArea) override;
|
||||
void SetPosition(const Float2& position) override;
|
||||
void SetClientPosition(const Float2& position) override;
|
||||
void SetIsFullscreen(bool isFullscreen) override;
|
||||
bool IsAlwaysOnTop() const override;
|
||||
void SetIsAlwaysOnTop(bool isAlwaysOnTop) override;
|
||||
Float2 GetPosition() const override;
|
||||
Float2 GetSize() const override;
|
||||
Float2 GetClientSize() const override;
|
||||
Float2 ScreenToClient(const Float2& screenPos) const override;
|
||||
Float2 ClientToScreen(const Float2& clientPos) const override;
|
||||
void FlashWindow() override;
|
||||
float GetOpacity() const override;
|
||||
void SetOpacity(float opacity) override;
|
||||
void Focus() override;
|
||||
String GetTitle() const override;
|
||||
void SetTitle(const StringView& title) override;
|
||||
DragDropEffect DoDragDrop(const StringView& data) override;
|
||||
DragDropEffect DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow) override;
|
||||
void StartTrackingMouse(bool useMouseScreenOffset) override;
|
||||
void EndTrackingMouse() override;
|
||||
void StartClippingCursor(const Rectangle& bounds) override;
|
||||
void EndClippingCursor() override;
|
||||
void SetMousePosition(const Float2& position) const override;
|
||||
void SetCursor(CursorType type) override;
|
||||
void SetIcon(TextureData& icon) override;
|
||||
|
||||
#if USE_EDITOR && PLATFORM_WINDOWS
|
||||
// [IUnknown]
|
||||
Windows::HRESULT __stdcall QueryInterface(const Windows::IID& id, void** ppvObject) override;
|
||||
Windows::ULONG __stdcall AddRef() override;
|
||||
Windows::ULONG __stdcall Release() override;
|
||||
|
||||
// [Windows::IDropTarget]
|
||||
Windows::HRESULT __stdcall DragEnter(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) override;
|
||||
Windows::HRESULT __stdcall DragOver(Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) override;
|
||||
Windows::HRESULT __stdcall DragLeave() override;
|
||||
Windows::HRESULT __stdcall Drop(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) override;
|
||||
#endif
|
||||
#if PLATFORM_LINUX
|
||||
DragDropEffect DoDragDropWayland(const StringView& data, Window* dragSourceWindow = nullptr, Float2 dragOffset = Float2::Zero);
|
||||
DragDropEffect DoDragDropX11(const StringView& data);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
class WindowsClipboard;
|
||||
typedef WindowsClipboard Clipboard;
|
||||
class Win32CriticalSection;
|
||||
typedef Win32CriticalSection CriticalSection;
|
||||
class Win32ReadWriteLock;
|
||||
@@ -18,12 +16,16 @@ class WindowsFileSystemWatcher;
|
||||
typedef WindowsFileSystemWatcher FileSystemWatcher;
|
||||
class Win32File;
|
||||
typedef Win32File File;
|
||||
class WindowsPlatform;
|
||||
typedef WindowsPlatform Platform;
|
||||
class Win32Thread;
|
||||
typedef Win32Thread Thread;
|
||||
class WindowsClipboard;
|
||||
typedef WindowsClipboard Clipboard;
|
||||
#if !PLATFORM_SDL
|
||||
class WindowsPlatform;
|
||||
typedef WindowsPlatform Platform;
|
||||
class WindowsWindow;
|
||||
typedef WindowsWindow Window;
|
||||
#endif
|
||||
class Win32Network;
|
||||
typedef Win32Network Network;
|
||||
class UserBase;
|
||||
@@ -31,8 +33,6 @@ typedef UserBase User;
|
||||
|
||||
#elif PLATFORM_UWP
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class Win32CriticalSection;
|
||||
typedef Win32CriticalSection CriticalSection;
|
||||
class Win32ReadWriteLock;
|
||||
@@ -45,10 +45,12 @@ class FileSystemWatcherBase;
|
||||
typedef FileSystemWatcherBase FileSystemWatcher;
|
||||
class Win32File;
|
||||
typedef Win32File File;
|
||||
class UWPPlatform;
|
||||
typedef UWPPlatform Platform;
|
||||
class Win32Thread;
|
||||
typedef Win32Thread Thread;
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class UWPPlatform;
|
||||
typedef UWPPlatform Platform;
|
||||
class UWPWindow;
|
||||
typedef UWPWindow Window;
|
||||
class Win32Network;
|
||||
@@ -58,8 +60,6 @@ typedef UserBase User;
|
||||
|
||||
#elif PLATFORM_LINUX
|
||||
|
||||
class LinuxClipboard;
|
||||
typedef LinuxClipboard Clipboard;
|
||||
class UnixCriticalSection;
|
||||
typedef UnixCriticalSection CriticalSection;
|
||||
class UnixReadWriteLock;
|
||||
@@ -72,12 +72,16 @@ class LinuxFileSystemWatcher;
|
||||
typedef LinuxFileSystemWatcher FileSystemWatcher;
|
||||
class UnixFile;
|
||||
typedef UnixFile File;
|
||||
class LinuxPlatform;
|
||||
typedef LinuxPlatform Platform;
|
||||
class LinuxThread;
|
||||
typedef LinuxThread Thread;
|
||||
#if !PLATFORM_SDL
|
||||
class LinuxClipboard;
|
||||
typedef LinuxClipboard Clipboard;
|
||||
class LinuxPlatform;
|
||||
typedef LinuxPlatform Platform;
|
||||
class LinuxWindow;
|
||||
typedef LinuxWindow Window;
|
||||
#endif
|
||||
class UnixNetwork;
|
||||
typedef UnixNetwork Network;
|
||||
class UserBase;
|
||||
@@ -85,8 +89,6 @@ typedef UserBase User;
|
||||
|
||||
#elif PLATFORM_PS4
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class UnixCriticalSection;
|
||||
typedef UnixCriticalSection CriticalSection;
|
||||
class UnixReadWriteLock;
|
||||
@@ -99,10 +101,12 @@ class FileSystemWatcherBase;
|
||||
typedef FileSystemWatcherBase FileSystemWatcher;
|
||||
class UnixFile;
|
||||
typedef UnixFile File;
|
||||
class PS4Platform;
|
||||
typedef PS4Platform Platform;
|
||||
class PS4Thread;
|
||||
typedef PS4Thread Thread;
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class PS4Platform;
|
||||
typedef PS4Platform Platform;
|
||||
class PS4Window;
|
||||
typedef PS4Window Window;
|
||||
class PS4Network;
|
||||
@@ -112,8 +116,6 @@ typedef PS4User User;
|
||||
|
||||
#elif PLATFORM_PS5
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class UnixCriticalSection;
|
||||
typedef UnixCriticalSection CriticalSection;
|
||||
class UnixReadWriteLock;
|
||||
@@ -126,10 +128,12 @@ class FileSystemWatcherBase;
|
||||
typedef FileSystemWatcherBase FileSystemWatcher;
|
||||
class UnixFile;
|
||||
typedef UnixFile File;
|
||||
class PS5Platform;
|
||||
typedef PS5Platform Platform;
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class PS5Thread;
|
||||
typedef PS5Thread Thread;
|
||||
class PS5Platform;
|
||||
typedef PS5Platform Platform;
|
||||
class PS5Window;
|
||||
typedef PS5Window Window;
|
||||
class PS5Network;
|
||||
@@ -139,8 +143,6 @@ typedef PS5User User;
|
||||
|
||||
#elif PLATFORM_XBOX_ONE
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class Win32CriticalSection;
|
||||
typedef Win32CriticalSection CriticalSection;
|
||||
class Win32ReadWriteLock;
|
||||
@@ -153,10 +155,12 @@ class FileSystemWatcherBase;
|
||||
typedef FileSystemWatcherBase FileSystemWatcher;
|
||||
class Win32File;
|
||||
typedef Win32File File;
|
||||
class XboxOnePlatform;
|
||||
typedef XboxOnePlatform Platform;
|
||||
class Win32Thread;
|
||||
typedef Win32Thread Thread;
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class XboxOnePlatform;
|
||||
typedef XboxOnePlatform Platform;
|
||||
class GDKWindow;
|
||||
typedef GDKWindow Window;
|
||||
class Win32Network;
|
||||
@@ -166,8 +170,6 @@ typedef GDKUser User;
|
||||
|
||||
#elif PLATFORM_XBOX_SCARLETT
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class Win32CriticalSection;
|
||||
typedef Win32CriticalSection CriticalSection;
|
||||
class Win32ReadWriteLock;
|
||||
@@ -180,10 +182,12 @@ class FileSystemWatcherBase;
|
||||
typedef FileSystemWatcherBase FileSystemWatcher;
|
||||
class Win32File;
|
||||
typedef Win32File File;
|
||||
class XboxScarlettPlatform;
|
||||
typedef XboxScarlettPlatform Platform;
|
||||
class Win32Thread;
|
||||
typedef Win32Thread Thread;
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class XboxScarlettPlatform;
|
||||
typedef XboxScarlettPlatform Platform;
|
||||
class GDKWindow;
|
||||
typedef GDKWindow Window;
|
||||
class Win32Network;
|
||||
@@ -193,8 +197,6 @@ typedef GDKUser User;
|
||||
|
||||
#elif PLATFORM_ANDROID
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class UnixCriticalSection;
|
||||
typedef UnixCriticalSection CriticalSection;
|
||||
class UnixReadWriteLock;
|
||||
@@ -207,10 +209,12 @@ class FileSystemWatcherBase;
|
||||
typedef FileSystemWatcherBase FileSystemWatcher;
|
||||
class AndroidFile;
|
||||
typedef AndroidFile File;
|
||||
class AndroidPlatform;
|
||||
typedef AndroidPlatform Platform;
|
||||
class AndroidThread;
|
||||
typedef AndroidThread Thread;
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class AndroidPlatform;
|
||||
typedef AndroidPlatform Platform;
|
||||
class AndroidWindow;
|
||||
typedef AndroidWindow Window;
|
||||
class UnixNetwork;
|
||||
@@ -220,8 +224,6 @@ typedef UserBase User;
|
||||
|
||||
#elif PLATFORM_SWITCH
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class SwitchCriticalSection;
|
||||
typedef SwitchCriticalSection CriticalSection;
|
||||
class SwitchReadWriteLock;
|
||||
@@ -234,10 +236,12 @@ class FileSystemWatcherBase;
|
||||
typedef FileSystemWatcherBase FileSystemWatcher;
|
||||
class SwitchFile;
|
||||
typedef SwitchFile File;
|
||||
class SwitchPlatform;
|
||||
typedef SwitchPlatform Platform;
|
||||
class SwitchThread;
|
||||
typedef SwitchThread Thread;
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class SwitchPlatform;
|
||||
typedef SwitchPlatform Platform;
|
||||
class SwitchWindow;
|
||||
typedef SwitchWindow Window;
|
||||
class SwitchNetwork;
|
||||
@@ -247,8 +251,6 @@ typedef SwitchUser User;
|
||||
|
||||
#elif PLATFORM_MAC
|
||||
|
||||
class MacClipboard;
|
||||
typedef MacClipboard Clipboard;
|
||||
class UnixCriticalSection;
|
||||
typedef UnixCriticalSection CriticalSection;
|
||||
class UnixReadWriteLock;
|
||||
@@ -261,12 +263,16 @@ class MacFileSystemWatcher;
|
||||
typedef MacFileSystemWatcher FileSystemWatcher;
|
||||
class UnixFile;
|
||||
typedef UnixFile File;
|
||||
class MacPlatform;
|
||||
typedef MacPlatform Platform;
|
||||
class AppleThread;
|
||||
typedef AppleThread Thread;
|
||||
class MacClipboard;
|
||||
typedef MacClipboard Clipboard;
|
||||
#if !PLATFORM_SDL
|
||||
class MacPlatform;
|
||||
typedef MacPlatform Platform;
|
||||
class MacWindow;
|
||||
typedef MacWindow Window;
|
||||
#endif
|
||||
class UnixNetwork;
|
||||
typedef UnixNetwork Network;
|
||||
class UserBase;
|
||||
@@ -274,8 +280,6 @@ typedef UserBase User;
|
||||
|
||||
#elif PLATFORM_IOS
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class UnixCriticalSection;
|
||||
typedef UnixCriticalSection CriticalSection;
|
||||
class UnixReadWriteLock;
|
||||
@@ -288,10 +292,12 @@ class FileSystemWatcherBase;
|
||||
typedef FileSystemWatcherBase FileSystemWatcher;
|
||||
class iOSFile;
|
||||
typedef iOSFile File;
|
||||
class iOSPlatform;
|
||||
typedef iOSPlatform Platform;
|
||||
class AppleThread;
|
||||
typedef AppleThread Thread;
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class iOSPlatform;
|
||||
typedef iOSPlatform Platform;
|
||||
class iOSWindow;
|
||||
typedef iOSWindow Window;
|
||||
class UnixNetwork;
|
||||
@@ -304,3 +310,14 @@ typedef UserBase User;
|
||||
#error Missing Types implementation!
|
||||
|
||||
#endif
|
||||
|
||||
#if PLATFORM_SDL
|
||||
#if PLATFORM_LINUX
|
||||
class SDLClipboard;
|
||||
typedef SDLClipboard Clipboard;
|
||||
#endif
|
||||
class SDLPlatform;
|
||||
typedef SDLPlatform Platform;
|
||||
class SDLWindow;
|
||||
typedef SDLWindow Window;
|
||||
#endif
|
||||
|
||||
@@ -364,11 +364,6 @@ void UWPWindow::Restore()
|
||||
// Not supported
|
||||
}
|
||||
|
||||
bool UWPWindow::IsClosed() const
|
||||
{
|
||||
return _isClosing;
|
||||
}
|
||||
|
||||
void UWPWindow::BringToFront(bool force)
|
||||
{
|
||||
Focus();
|
||||
|
||||
@@ -160,7 +160,6 @@ public:
|
||||
void Minimize() override;
|
||||
void Maximize() override;
|
||||
void Restore() override;
|
||||
bool IsClosed() const override;
|
||||
void BringToFront(bool force = false) override;
|
||||
void SetClientBounds(const Rectangle& clientArea) override;
|
||||
void SetPosition(const Float2& position) override;
|
||||
@@ -183,6 +182,7 @@ public:
|
||||
void Focus() override;
|
||||
void SetTitle(const StringView& title) override;
|
||||
DragDropEffect DoDragDrop(const StringView& data) override;
|
||||
using WindowBase::DoDragDrop;
|
||||
void StartTrackingMouse(bool useMouseScreenOffset) override;
|
||||
void EndTrackingMouse() override;
|
||||
void SetCursor(CursorType type) override;
|
||||
|
||||
@@ -17,31 +17,37 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Perform window hit test delegate.
|
||||
/// </summary>
|
||||
/// <param name="mouse">The mouse position. The coordinate is relative to the upper-left corner of the screen. Use <see cref="ScreenToClient"/> to convert position into client space coordinates.</param>
|
||||
/// <param name="mousePosition">The mouse position. The coordinate is relative to the upper-left corner of the screen. Use <see cref="ScreenToClient"/> to convert position into client space coordinates.</param>
|
||||
/// <returns>Hit result.</returns>
|
||||
public delegate WindowHitCodes HitTestDelegate(ref Float2 mouse);
|
||||
public delegate WindowHitCodes HitTestDelegate(ref Float2 mousePosition);
|
||||
|
||||
/// <summary>
|
||||
/// Perform mouse buttons action.
|
||||
/// </summary>
|
||||
/// <param name="mouse">The mouse position.</param>
|
||||
/// <param name="mousePosition">The mouse position.</param>
|
||||
/// <param name="button">The mouse buttons state.</param>
|
||||
/// <param name="handled">The flag that indicated that event has been handled by the custom code and should not be passed further. By default it is set to false.</param>
|
||||
public delegate void MouseButtonDelegate(ref Float2 mouse, MouseButton button, ref bool handled);
|
||||
public delegate void MouseButtonDelegate(ref Float2 mousePosition, MouseButton button, ref bool handled);
|
||||
|
||||
/// <summary>
|
||||
/// Perform mouse move action.
|
||||
/// </summary>
|
||||
/// <param name="mouse">The mouse position.</param>
|
||||
public delegate void MouseMoveDelegate(ref Float2 mouse);
|
||||
/// <param name="mousePosition">The mouse position.</param>
|
||||
public delegate void MouseMoveDelegate(ref Float2 mousePosition);
|
||||
|
||||
/// <summary>
|
||||
/// Perform mouse move action in relative mode.
|
||||
/// </summary>
|
||||
/// <param name="motion">The relative mouse motion.</param>
|
||||
public delegate void MouseMoveRelativeDelegate(ref Float2 motion);
|
||||
|
||||
/// <summary>
|
||||
/// Perform mouse wheel action.
|
||||
/// </summary>
|
||||
/// <param name="mouse">The mouse position.</param>
|
||||
/// <param name="mousePosition">The mouse position.</param>
|
||||
/// <param name="delta">The mouse wheel move delta (can be positive or negative; normalized to [-1;1] range).</param>
|
||||
/// <param name="handled">The flag that indicated that event has been handled by the custom code and should not be passed further. By default it is set to false.</param>
|
||||
public delegate void MouseWheelDelegate(ref Float2 mouse, float delta, ref bool handled);
|
||||
public delegate void MouseWheelDelegate(ref Float2 mousePosition, float delta, ref bool handled);
|
||||
|
||||
/// <summary>
|
||||
/// Perform touch action.
|
||||
@@ -99,9 +105,14 @@ namespace FlaxEngine
|
||||
public event MouseWheelDelegate MouseWheel;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when mouse moves
|
||||
/// Event fired when mouse moves.
|
||||
/// </summary>
|
||||
public event MouseMoveDelegate MouseMove;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when mouse moves in relative mode.
|
||||
/// </summary>
|
||||
public event MouseMoveRelativeDelegate MouseMoveRelative;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when mouse leaves window.
|
||||
@@ -273,6 +284,12 @@ namespace FlaxEngine
|
||||
MouseMove?.Invoke(ref pos);
|
||||
GUI.OnMouseMove(pos);
|
||||
}
|
||||
|
||||
internal void Internal_OnMouseMoveRelative(ref Float2 motion)
|
||||
{
|
||||
MouseMoveRelative?.Invoke(ref motion);
|
||||
GUI.OnMouseMoveRelative(motion);
|
||||
}
|
||||
|
||||
internal void Internal_OnMouseLeave()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_SDL
|
||||
#include "SDL/SDLWindow.h"
|
||||
#elif PLATFORM_WINDOWS
|
||||
#include "Windows/WindowsWindow.h"
|
||||
#elif PLATFORM_UWP
|
||||
#include "UWP/UWPWindow.h"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user