Compare commits
91 Commits
d4d3933187
...
a38cc4d5cb
| Author | SHA1 | Date | |
|---|---|---|---|
| a38cc4d5cb | |||
| 1528d458d9 | |||
| 9e937776ac | |||
| a8aebdd01f | |||
| 998ed87029 | |||
| 279b3f8d9a | |||
| 738a506b6d | |||
| 0ddecdda1f | |||
| 94a22907e2 | |||
| d22a0aad0c | |||
| 9e035f3818 | |||
| 99a810458a | |||
| 2806578126 | |||
| 882e9a6623 | |||
| 14c156e351 | |||
| cd2151332e | |||
| 53bbca3779 | |||
| 51f4ac467e | |||
| 81427a7f9e | |||
| faadfb28b3 | |||
| 6bfe0bed35 | |||
| 6e4d5b853f | |||
| 0ebe23b482 | |||
| abbde5861a | |||
| b78d4349a7 | |||
| 3c1d3234b8 | |||
|
|
79baaae8e7 | ||
| 79289662ed | |||
| 89a2985ad8 | |||
| 1f0a521a87 | |||
| 8a0e43c38c | |||
| ac9022f585 | |||
| 314dbbbeaf | |||
| f13e09910d | |||
| 67b9511afe | |||
| 510c477468 | |||
| 697fcc18ab | |||
| 89ff08b3f2 | |||
| 9417585cd9 | |||
| f794bb5455 | |||
| bb7a5326cd | |||
| a998b2a44e | |||
| a69eb4296f | |||
| 727af5a5b9 | |||
| d545dd8219 | |||
| 29e385ab4b | |||
| 5c63e30b84 | |||
| 5187c677f1 | |||
| 1d43314efd | |||
| 91ef8c69db | |||
| f24251af82 | |||
| ed6e0138e6 | |||
| 4362cdb396 | |||
| 8146680b53 | |||
| 27aa2cfa1f | |||
|
|
1475075b00 | ||
|
|
702564366d | ||
|
|
e2fd3891d1 | ||
| f54e961f11 | |||
|
|
aabd70fbe7 | ||
| 428ebf7fd7 | |||
|
|
72f16d738f | ||
|
|
475818554d | ||
|
|
8ebb3a3215 | ||
|
|
2085a0bf25 | ||
|
|
d829461def | ||
|
|
2d169fdcd8 | ||
|
|
53ba5968fd | ||
|
|
cc735a1b6a | ||
|
|
970af7bdc8 | ||
|
|
2ff6a6dd9a | ||
|
|
ae3149c9c0 | ||
|
|
1a1bd56802 | ||
|
|
25b566b348 | ||
|
|
4fb95a704a | ||
|
|
5f09ef4c1f | ||
|
|
7bc099c32c | ||
|
|
9a59d0c5ed | ||
|
|
275ca296fa | ||
|
|
57af076c8d | ||
|
|
f905c49f0b | ||
|
|
c72fac335f | ||
|
|
030befdcaa | ||
|
|
3a591577ad | ||
|
|
8f9eaddbe5 | ||
|
|
ec11a79f55 | ||
|
|
55fd198102 | ||
|
|
425699c4b0 | ||
| af3badaeef | |||
|
|
873491eca2 | ||
|
|
3ba1ebb847 |
4
.github/workflows/build_linux.yml
vendored
4
.github/workflows/build_linux.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install 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
|
||||
@@ -44,7 +44,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
|
||||
|
||||
13
.github/workflows/cd.yml
vendored
13
.github/workflows/cd.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
env:
|
||||
DOTNET_NOLOGO: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: false
|
||||
GIT_LFS_PULL_OPTIONS: '-c lfs.concurrenttransfers=10 -c lfs.transfer.maxretries=2'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -20,7 +21,7 @@ jobs:
|
||||
- name: Checkout LFS
|
||||
run: |
|
||||
git lfs version
|
||||
git lfs pull
|
||||
git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
@@ -53,7 +54,7 @@ jobs:
|
||||
- name: Checkout LFS
|
||||
run: |
|
||||
git lfs version
|
||||
git lfs pull
|
||||
git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
@@ -83,7 +84,7 @@ jobs:
|
||||
- name: Checkout LFS
|
||||
run: |
|
||||
git lfs version
|
||||
git lfs pull
|
||||
GIT_TRACE=1 GIT_TRANSFER_TRACE=1 git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
@@ -114,7 +115,7 @@ jobs:
|
||||
- name: Checkout LFS
|
||||
run: |
|
||||
git lfs version
|
||||
git lfs pull
|
||||
git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
@@ -147,7 +148,7 @@ jobs:
|
||||
- name: Checkout LFS
|
||||
run: |
|
||||
git lfs version
|
||||
git lfs pull
|
||||
git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
@@ -175,7 +176,7 @@ jobs:
|
||||
- name: Checkout LFS
|
||||
run: |
|
||||
git lfs version
|
||||
git lfs pull
|
||||
git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Redirect to our own Git LFS server
|
||||
[lfs]
|
||||
url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
|
||||
#url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
|
||||
locksverify = false
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"Configuration": {
|
||||
"UseCSharp": true,
|
||||
"UseLargeWorlds": false,
|
||||
"UseDotNet": true
|
||||
"UseDotNet": true,
|
||||
"UseSDL": true
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Install Visual Studio 2022 or newer
|
||||
* Install Windows 8.1 SDK or newer (via Visual Studio Installer)
|
||||
* Install Microsoft Visual C++ 2015 v140 toolset or newer (via Visual Studio Installer)
|
||||
* Install .NET 8 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
|
||||
* Install .NET 8 or 9 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
|
||||
* Install Git with LFS
|
||||
* Clone repo (with LFS)
|
||||
* Run **GenerateProjectFiles.bat**
|
||||
@@ -44,8 +44,9 @@ Follow the instructions below to compile and run the engine from source.
|
||||
## Linux
|
||||
|
||||
* Install Visual Studio Code
|
||||
* Install .NET 8 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
|
||||
* Install .NET 8 or 9 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
|
||||
* Ubuntu: `sudo apt install dotnet-sdk-8.0`
|
||||
* Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host`
|
||||
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
|
||||
* Ubuntu: `sudo apt install vulkan-sdk`
|
||||
* Arch: `sudo pacman -S spirv-tools vulkan-headers vulkan-tools vulkan-validation-layers`
|
||||
@@ -67,12 +68,12 @@ Follow the instructions below to compile and run the engine from source.
|
||||
## Mac
|
||||
|
||||
* Install XCode
|
||||
* Install .NET 8 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
|
||||
* Install .NET 8 or 9 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0))
|
||||
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
|
||||
* Clone repo (with LFS)
|
||||
* Run `GenerateProjectFiles.command`
|
||||
* Open workspace with XCode or Visual Studio Code
|
||||
* Build and run (configuration `Editor.Mac.Development`)
|
||||
* Build and run (configuration `Editor.Mac.Development`)
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
|
||||
@@ -750,7 +750,8 @@ namespace FlaxEditor.Content
|
||||
|
||||
// Draw short name
|
||||
Render2D.PushClip(ref textRect);
|
||||
Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f);
|
||||
var scale = 0.95f * view.ViewScale;
|
||||
Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, scale);
|
||||
Render2D.PopClip();
|
||||
|
||||
if (IsBeingCut)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -883,7 +883,7 @@ namespace FlaxEditor.CustomEditors
|
||||
/// </summary>
|
||||
protected virtual void ClearToken()
|
||||
{
|
||||
ParentEditor.ClearToken();
|
||||
ParentEditor?.ClearToken();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,12 +186,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);
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -156,6 +157,17 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
private Rectangle DropdownRect => new Rectangle(Width - DropdownIconSize - DropdownIconMargin, DropdownIconMargin, DropdownIconSize, DropdownIconSize);
|
||||
|
||||
public Action ShowPicker;
|
||||
public Action<ContentItem> OnAssetDropped;
|
||||
|
||||
private DragItems _dragItems;
|
||||
private DragHandlers _dragHandlers;
|
||||
private bool _hasValidDragOver;
|
||||
private Func<ContentItem, bool> _validate;
|
||||
|
||||
public void SetValidationMethod(Func<ContentItem, bool> validate)
|
||||
{
|
||||
_validate = validate;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
@@ -164,6 +176,14 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var style = FlaxEngine.GUI.Style.Current;
|
||||
var dropdownRect = DropdownRect;
|
||||
Render2D.DrawSprite(style.ArrowDown, dropdownRect, Enabled ? (DropdownRect.Contains(PointFromWindow(RootWindow.MousePosition)) ? style.BorderSelected : style.Foreground) : style.ForegroundDisabled);
|
||||
|
||||
// Check if drag is over
|
||||
if (IsDragOver && _hasValidDragOver)
|
||||
{
|
||||
var bounds = new Rectangle(Float2.Zero, Size);
|
||||
Render2D.FillRectangle(bounds, style.Selection);
|
||||
Render2D.DrawRectangle(bounds, style.SelectionBorder);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
@@ -207,6 +227,68 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private DragDropEffect DragEffect => _hasValidDragOver ? DragDropEffect.Move : DragDropEffect.None;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||
{
|
||||
base.OnDragEnter(ref location, data);
|
||||
|
||||
// Ensure to have valid drag helpers (uses lazy init)
|
||||
if (_dragItems == null)
|
||||
_dragItems = new DragItems(ValidateDragAsset);
|
||||
if (_dragHandlers == null)
|
||||
{
|
||||
_dragHandlers = new DragHandlers
|
||||
{
|
||||
_dragItems,
|
||||
};
|
||||
}
|
||||
|
||||
_hasValidDragOver = _dragHandlers.OnDragEnter(data) != DragDropEffect.None;
|
||||
|
||||
|
||||
return DragEffect;
|
||||
}
|
||||
|
||||
private bool ValidateDragAsset(ContentItem contentItem)
|
||||
{
|
||||
// Load or get asset
|
||||
return _validate?.Invoke(contentItem) ?? false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||
{
|
||||
base.OnDragMove(ref location, data);
|
||||
|
||||
return DragEffect;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_hasValidDragOver = false;
|
||||
_dragHandlers.OnDragLeave();
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
|
||||
{
|
||||
var result = DragEffect;
|
||||
|
||||
base.OnDragDrop(ref location, data);
|
||||
|
||||
if (_dragItems.HasValidDrag)
|
||||
{
|
||||
OnAssetDropped(_dragItems.Objects[0]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private TextBoxWithPicker _textBox;
|
||||
@@ -221,13 +303,21 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
if (HasDifferentTypes)
|
||||
return;
|
||||
|
||||
_validator = new AssetPickerValidator(ScriptType.Null);
|
||||
_textBox = layout.Custom<TextBoxWithPicker>().CustomControl;
|
||||
_textBox.ShowPicker = OnShowPicker;
|
||||
_textBox.OnAssetDropped = OnItemDropped;
|
||||
_textBox.EditEnd += OnEditEnd;
|
||||
_validator = new AssetPickerValidator(ScriptType.Null);
|
||||
_textBox.SetValidationMethod(_validator.IsValid);
|
||||
AssetRefEditor.ApplyAssetReferenceAttribute(Values, out _, _validator);
|
||||
}
|
||||
|
||||
private void OnItemDropped(ContentItem item)
|
||||
{
|
||||
SetPickerPath(item);
|
||||
}
|
||||
|
||||
private void OnShowPicker()
|
||||
{
|
||||
if (_validator.AssetType != ScriptType.Null)
|
||||
|
||||
@@ -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) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
@@ -111,7 +114,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>
|
||||
@@ -215,21 +218,27 @@ 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);
|
||||
_window.GotFocus += OnWindowGotFocus;
|
||||
_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;
|
||||
|
||||
// Show
|
||||
Visible = true;
|
||||
if (_window == null)
|
||||
return;
|
||||
_window.Show();
|
||||
PerformLayout();
|
||||
_previouslyFocused = parentWin.FocusedControl;
|
||||
@@ -378,6 +387,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;
|
||||
@@ -391,6 +411,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnWindowLostFocus()
|
||||
{
|
||||
@@ -489,7 +510,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
// Let root context menu to check if none of the popup windows
|
||||
if (_parentCM == null && !IsForeground)
|
||||
{
|
||||
#if USE_SDL_WORKAROUNDS
|
||||
if (!IsMouseOver)
|
||||
Hide();
|
||||
#else
|
||||
Hide();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,529 +0,0 @@
|
||||
// Copyright (c) 2012-2024 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 += DockPanel.DefaultHeaderHeight;
|
||||
result.Size.Y -= DockPanel.DefaultHeaderHeight;
|
||||
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.DragWindow;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resize proxy
|
||||
Window.ClientSize = initSize;
|
||||
}
|
||||
|
||||
InitHitProxy();
|
||||
}
|
||||
|
||||
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.DragWindow;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,11 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// The mouse position.
|
||||
/// </summary>
|
||||
public Float2 MousePosition = Float2.Minimum;
|
||||
|
||||
/// <summary>
|
||||
/// The mouse position.
|
||||
/// </summary>
|
||||
public Float2 MouseStartPosition = Float2.Minimum;
|
||||
|
||||
/// <summary>
|
||||
/// The start drag asynchronous window.
|
||||
@@ -165,7 +170,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
|
||||
{
|
||||
@@ -176,7 +181,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
_panel.SelectTab(index - 1);
|
||||
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(win);
|
||||
WindowDragHelper.StartDragging(win, _panel.RootWindow.Window);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -355,6 +360,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)
|
||||
@@ -441,6 +447,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,25 @@ 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 +218,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>
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
return;
|
||||
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(this);
|
||||
WindowDragHelper.StartDragging(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,7 +82,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
|
||||
448
Source/Editor/GUI/Docking/WindowDragHelper.cs
Normal file
448
Source/Editor/GUI/Docking/WindowDragHelper.cs
Normal file
@@ -0,0 +1,448 @@
|
||||
// Copyright (c) 2012-2024 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 = 32.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The opacity of the dragged window when hint controls are shown.
|
||||
/// </summary>
|
||||
public const float DragWindowOpacity = 0.4f;
|
||||
|
||||
private WindowDragHelper(FloatWindowDockPanel toMove, Window dragSourceWindow)
|
||||
{
|
||||
_toMove = toMove;
|
||||
_toSet = DockState.Float;
|
||||
var window = toMove.Window.Window;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Bind events
|
||||
FlaxEngine.Scripting.Update += OnUpdate;
|
||||
window.MouseUp += OnMouseUp;
|
||||
|
||||
// Update rectangles
|
||||
UpdateRects(Platform.MousePosition);
|
||||
|
||||
_dragSourceWindow = dragSourceWindow;
|
||||
if (_dragSourceWindow != null) // Detaching a tab from existing window
|
||||
{
|
||||
_dragOffset = new Float2(window.Size.X / 2, 10.0f);
|
||||
|
||||
// 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);
|
||||
|
||||
_dragSourceWindow.MouseUp += OnMouseUp; // The mouse up event is sent to the source window on Windows
|
||||
}
|
||||
else
|
||||
{
|
||||
_dragOffset = window.MousePosition;
|
||||
window.DoDragDrop(window.Title, _dragOffset, window);
|
||||
}
|
||||
|
||||
// Ensure the dragged window stays on top of every other window
|
||||
window.IsAlwaysOnTop = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
var window = _toMove?.Window?.Window;
|
||||
|
||||
// Unbind events
|
||||
FlaxEngine.Scripting.Update -= OnUpdate;
|
||||
if (window != null)
|
||||
window.MouseUp -= OnMouseUp;
|
||||
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>
|
||||
/// <returns>The window drag helper object.</returns>
|
||||
public static WindowDragHelper StartDragging(FloatWindowDockPanel toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
return new WindowDragHelper(toMove, null);
|
||||
}
|
||||
|
||||
/// <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 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)
|
||||
{
|
||||
Control hintControl = _toDock.AddChild<Control>();
|
||||
hintControl.AnchorPreset = AnchorPresets.StretchAll;
|
||||
hintControl.Offsets = Margin.Zero;
|
||||
hintControl.Size = new Float2(HintControlSize);
|
||||
hintControl.BackgroundColor = Style.Current.DragWindow.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
|
||||
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
|
||||
_toDock?.RootWindow.Window.BringToFront();
|
||||
//_toDock?.RootWindow.Window.Focus();
|
||||
|
||||
// Make the dragged window transparent when dock hints are visible
|
||||
_toMove.Window.Window.Opacity = _toDock == null ? 1.0f : DragWindowOpacity;
|
||||
}
|
||||
|
||||
// Check dock state to use
|
||||
bool showProxyHints = _toDock != null;
|
||||
bool showBorderHints = showProxyHints;
|
||||
bool showCenterHint = showProxyHints;
|
||||
Control hoveredHintControl = null;
|
||||
Float2 hoveredSizeOverride = Float2.Zero;
|
||||
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 = 4.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
|
||||
DockState toSet = DockState.Float;
|
||||
if (showBorderHints)
|
||||
{
|
||||
if (_rUpper.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockTop;
|
||||
hoveredHintControl = _dockHintUp;
|
||||
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
|
||||
}
|
||||
else if (_rBottom.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockBottom;
|
||||
hoveredHintControl = _dockHintDown;
|
||||
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
|
||||
}
|
||||
else if (_rLeft.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockLeft;
|
||||
hoveredHintControl = _dockHintLeft;
|
||||
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
|
||||
}
|
||||
else if (_rRight.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockRight;
|
||||
hoveredHintControl = _dockHintRight;
|
||||
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
|
||||
}
|
||||
}
|
||||
if (showCenterHint && _rCenter.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (hoveredHintControl != _dockHintDown)
|
||||
{
|
||||
_dockHintDown.Size = new Float2(HintControlSize);
|
||||
_dockHintDown.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintLeft)
|
||||
{
|
||||
_dockHintLeft.Size = new Float2(HintControlSize);
|
||||
_dockHintLeft.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintRight)
|
||||
{
|
||||
_dockHintRight.Size = new Float2(HintControlSize);
|
||||
_dockHintRight.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintUp)
|
||||
{
|
||||
_dockHintUp.Size = new Float2(HintControlSize);
|
||||
_dockHintUp.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintCenter)
|
||||
{
|
||||
_dockHintCenter.Size = new Float2(HintControlSize);
|
||||
_dockHintCenter.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
|
||||
}
|
||||
|
||||
if (_toSet != DockState.Float)
|
||||
{
|
||||
if (hoveredHintControl != null)
|
||||
{
|
||||
hoveredHintControl.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(1.0f);
|
||||
hoveredHintControl.Size = hoveredSizeOverride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
{
|
||||
var mousePos = Platform.MousePosition;
|
||||
|
||||
if (_mouse != mousePos)
|
||||
OnMouseMove(mousePos);
|
||||
}
|
||||
|
||||
private void OnMouseMove(Float2 mousePos)
|
||||
{
|
||||
if (_dragSourceWindow != null)
|
||||
_toMove.Window.Window.Position = mousePos - _dragOffset;
|
||||
|
||||
UpdateRects(mousePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,6 +266,7 @@ namespace FlaxEditor.GUI.Input
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
@@ -292,13 +293,45 @@ namespace FlaxEditor.GUI.Input
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMoveRelative(Float2 mouseMotion)
|
||||
{
|
||||
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(mouseMotion);
|
||||
}
|
||||
|
||||
#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,7 +12,7 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public sealed class MainMenu : ContainerControl
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
private bool _useCustomWindowSystem;
|
||||
private Image _icon;
|
||||
private Label _title;
|
||||
@@ -67,7 +67,7 @@ namespace FlaxEditor.GUI
|
||||
AutoFocus = false;
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
_useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
@@ -166,7 +166,7 @@ namespace FlaxEditor.GUI
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
@@ -291,7 +291,7 @@ namespace FlaxEditor.GUI
|
||||
if (base.OnMouseDoubleClick(location, button))
|
||||
return true;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
var child = GetChildAtRecursive(location);
|
||||
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
|
||||
{
|
||||
@@ -321,7 +321,7 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
float x = 0;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Icon
|
||||
@@ -349,7 +349,7 @@ namespace FlaxEditor.GUI
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Buttons
|
||||
@@ -367,7 +367,7 @@ namespace FlaxEditor.GUI
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace FlaxEditor.GUI
|
||||
Text = text;
|
||||
|
||||
var style = Style.Current;
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
if (Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
|
||||
{
|
||||
BackgroundColorMouseOver = style.BackgroundHighlighted;
|
||||
|
||||
@@ -641,7 +641,68 @@ namespace FlaxEditor
|
||||
DrawControlWidget(uiControl, ref eu, ref mousePos, ref widgetHandleSize, viewScale, new Float2(0, -1), CursorType.SizeNS);
|
||||
DrawControlWidget(uiControl, ref eb, ref mousePos, ref widgetHandleSize, viewScale, new Float2(0, 1), CursorType.SizeNS);
|
||||
|
||||
// TODO: draw anchors
|
||||
// Draw pivot
|
||||
var pivotSize = 8.0f;
|
||||
if (viewScale < 0.7f)
|
||||
pivotSize *= viewScale;
|
||||
var pivotX = Mathf.Remap(control.Pivot.X, 0, 1, bounds.Location.X, bounds.Location.X + bounds.Width);
|
||||
var pivotY = Mathf.Remap(control.Pivot.Y, 0, 1, bounds.Location.Y, bounds.Location.Y + bounds.Height);
|
||||
var pivotLoc = control.PointToParent(this, new Float2(pivotX, pivotY));
|
||||
var pivotRect = new Rectangle(pivotLoc - pivotSize * 0.5f, new Float2(pivotSize));
|
||||
var pivotColor = options.UIPivotColor;
|
||||
Render2D.FillRectangle(pivotRect, pivotColor);
|
||||
|
||||
// Draw anchors
|
||||
var controlParent = control.Parent;
|
||||
if (controlParent != null)
|
||||
{
|
||||
var parentBounds = controlParent.EditorBounds;
|
||||
var anchorMin = control.AnchorMin;
|
||||
var anchorMax = control.AnchorMax;
|
||||
var newMinX = Mathf.Remap(anchorMin.X, 0, 1, parentBounds.UpperLeft.X, parentBounds.UpperRight.X);
|
||||
var newMinY = Mathf.Remap(anchorMin.Y, 0, 1, parentBounds.UpperLeft.Y, parentBounds.LowerLeft.Y);
|
||||
var newMaxX = Mathf.Remap(anchorMax.X, 0, 1, parentBounds.UpperLeft.X, parentBounds.UpperRight.X);
|
||||
var newMaxY = Mathf.Remap(anchorMax.Y, 0, 1, parentBounds.UpperLeft.Y, parentBounds.LowerLeft.Y);
|
||||
|
||||
var anchorUpperLeft = controlParent.PointToParent(this, new Float2(newMinX, newMinY));
|
||||
var anchorUpperRight = controlParent.PointToParent(this, new Float2(newMaxX, newMinY));
|
||||
var anchorLowerLeft = controlParent.PointToParent(this, new Float2(newMinX, newMaxY));
|
||||
var anchorLowerRight = controlParent.PointToParent(this, new Float2(newMaxX, newMaxY));
|
||||
|
||||
var anchorRectSize = 8.0f;
|
||||
if (viewScale < 0.7f)
|
||||
anchorRectSize *= viewScale;
|
||||
|
||||
// Make anchor rects and rotate if parent is rotated.
|
||||
var parentRotation = controlParent.Rotation * Mathf.DegreesToRadians;
|
||||
|
||||
var rect1Axis = new Float2(-1, -1);
|
||||
var rect1 = new Rectangle(anchorUpperLeft +
|
||||
new Float2(anchorRectSize * rect1Axis.X * Mathf.Cos(parentRotation) - anchorRectSize * rect1Axis.Y * Mathf.Sin(parentRotation),
|
||||
anchorRectSize * rect1Axis.Y * Mathf.Cos(parentRotation) + anchorRectSize * rect1Axis.X * Mathf.Sin(parentRotation)) - anchorRectSize * 0.5f, new Float2(anchorRectSize));
|
||||
var rect2Axis = new Float2(1, -1);
|
||||
var rect2 = new Rectangle(anchorUpperRight +
|
||||
new Float2(anchorRectSize * rect2Axis.X * Mathf.Cos(parentRotation) - anchorRectSize * rect2Axis.Y * Mathf.Sin(parentRotation),
|
||||
anchorRectSize * rect2Axis.Y * Mathf.Cos(parentRotation) + anchorRectSize * rect2Axis.X * Mathf.Sin(parentRotation)) - anchorRectSize * 0.5f, new Float2(anchorRectSize));
|
||||
var rect3Axis = new Float2(-1, 1);
|
||||
var rect3 = new Rectangle(anchorLowerLeft +
|
||||
new Float2(anchorRectSize * rect3Axis.X * Mathf.Cos(parentRotation) - anchorRectSize * rect3Axis.Y * Mathf.Sin(parentRotation),
|
||||
anchorRectSize * rect3Axis.Y * Mathf.Cos(parentRotation) + anchorRectSize * rect3Axis.X * Mathf.Sin(parentRotation)) - anchorRectSize * 0.5f, new Float2(anchorRectSize));
|
||||
var rect4Axis = new Float2(1, 1);
|
||||
var rect4 = new Rectangle(anchorLowerRight +
|
||||
new Float2(anchorRectSize * rect4Axis.X * Mathf.Cos(parentRotation) - anchorRectSize * rect4Axis.Y * Mathf.Sin(parentRotation),
|
||||
anchorRectSize * rect4Axis.Y * Mathf.Cos(parentRotation) + anchorRectSize * rect4Axis.X * Mathf.Sin(parentRotation)) - anchorRectSize * 0.5f, new Float2(anchorRectSize));
|
||||
|
||||
var rectColor = options.UIAnchorColor;
|
||||
Render2D.DrawLine(anchorUpperLeft, anchorUpperRight, rectColor);
|
||||
Render2D.DrawLine(anchorUpperRight, anchorLowerRight, rectColor);
|
||||
Render2D.DrawLine(anchorLowerRight, anchorLowerLeft, rectColor);
|
||||
Render2D.DrawLine(anchorLowerLeft, anchorUpperLeft, rectColor);
|
||||
Render2D.FillRectangle(rect1, rectColor);
|
||||
Render2D.FillRectangle(rect2, rectColor);
|
||||
Render2D.FillRectangle(rect3, rectColor);
|
||||
Render2D.FillRectangle(rect4, rectColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,8 +712,7 @@ namespace FlaxEditor
|
||||
var control = uiControl.Control;
|
||||
var rotation = control.Rotation;
|
||||
var rotationInRadians = rotation * Mathf.DegreesToRadians;
|
||||
var rect = new Rectangle(
|
||||
(pos +
|
||||
var rect = new Rectangle((pos +
|
||||
new Float2(resizeAxis.X * Mathf.Cos(rotationInRadians) - resizeAxis.Y * Mathf.Sin(rotationInRadians),
|
||||
resizeAxis.Y * Mathf.Cos(rotationInRadians) + resizeAxis.X * Mathf.Sin(rotationInRadians)) * 10 * scale) - size * 0.5f,
|
||||
size);
|
||||
@@ -714,16 +774,15 @@ namespace FlaxEditor
|
||||
|
||||
private bool RayCastControl(ref Float2 location, out Control hit)
|
||||
{
|
||||
#if false
|
||||
// Raycast only controls with content (eg. skips transparent panels)
|
||||
return RayCastChildren(ref location, out hit);
|
||||
#else
|
||||
// Find any control under mouse (hierarchical)
|
||||
hit = GetChildAtRecursive(location);
|
||||
// First, raycast only controls with content (eg. skips transparent panels)
|
||||
RayCastChildren(ref location, out hit);
|
||||
|
||||
// If raycast failed, then find any control under mouse (hierarchical)
|
||||
hit = hit ?? GetChildAtRecursive(location);
|
||||
if (hit is View || hit is CanvasContainer)
|
||||
hit = null;
|
||||
|
||||
return hit != null;
|
||||
#endif
|
||||
}
|
||||
|
||||
private UIControlNode FindUIControlNode(Control control)
|
||||
|
||||
@@ -435,6 +435,9 @@ 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;
|
||||
|
||||
@@ -1008,7 +1008,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));
|
||||
|
||||
@@ -16,7 +16,7 @@ using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using DockHintWindow = FlaxEditor.GUI.Docking.DockHintWindow;
|
||||
using WindowDragHelper = FlaxEditor.GUI.Docking.WindowDragHelper;
|
||||
using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.Options;
|
||||
@@ -381,7 +381,7 @@ namespace FlaxEditor.Modules
|
||||
Editor.Options.OptionsChanged += OnOptionsChanged;
|
||||
|
||||
// Add dummy control for drawing the main window borders if using a custom style
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
if (!Editor.Options.Options.Interface.UseNativeWindowSystem)
|
||||
#endif
|
||||
{
|
||||
@@ -456,13 +456,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);
|
||||
|
||||
@@ -760,8 +760,10 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
settings.HasBorder = false;
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Skip OS sizing frame and implement it using LeftButtonHit
|
||||
settings.HasSizingFrame = false;
|
||||
#endif
|
||||
}
|
||||
#elif PLATFORM_LINUX
|
||||
settings.HasBorder = false;
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Interface"), EditorOrder(10), Tooltip("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.")]
|
||||
public float InterfaceScale { get; set; } = 1.0f;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether use native window title bar. Editor restart required.
|
||||
/// </summary>
|
||||
|
||||
@@ -39,6 +39,20 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("UI Gizmo", "UI Control Outline Size"), EditorOrder(103), Tooltip("The size of the selection outline for UI controls.")]
|
||||
public float UISelectionOutlineSize { get; set; } = 2.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pivot color for the UI Gizmo.
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(Color), "0.0,0.5725,0.8,0.5")]
|
||||
[EditorDisplay("UI Gizmo", "Pivot Color"), EditorOrder(103), Tooltip("The color of the pivot for the UI Gizmo.")]
|
||||
public Color UIPivotColor { get; set; } = new Color(0.0f, 0.5725f, 0.8f, 0.5f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the anchor color for the UI Gizmo.
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(Color), "0.8392,0.8471,0.8706,0.5")]
|
||||
[EditorDisplay("UI Gizmo", "Anchor Color"), EditorOrder(103), Tooltip("The color of the anchors for the UI Gizmo.")]
|
||||
public Color UIAnchorColor { get; set; } = new Color(0.8392f, 0.8471f, 0.8706f, 0.5f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transform gizmo size.
|
||||
/// </summary>
|
||||
|
||||
@@ -215,10 +215,10 @@ namespace FlaxEditor.SceneGraph
|
||||
public override bool CanDuplicate => (_actor.HideFlags & HideFlags.HideInHierarchy) == 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsActive => _actor.IsActive;
|
||||
public override bool IsActive => _actor?.IsActive ?? false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsActiveInHierarchy => _actor.IsActiveInHierarchy;
|
||||
public override bool IsActiveInHierarchy => _actor?.IsActiveInHierarchy ?? false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int OrderInParent
|
||||
|
||||
@@ -120,9 +120,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();
|
||||
|
||||
@@ -924,6 +924,25 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
(int)ModuleType.Initialize,
|
||||
},
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 216,
|
||||
Create = CreateParticleModuleNode,
|
||||
Title = "Rotate Position Shape",
|
||||
Description = "Rotate the shape.",
|
||||
Flags = DefaultModuleFlags,
|
||||
Size = new Float2(200, 1 * Surface.Constants.LayoutOffsetY),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
true,
|
||||
(int)ModuleType.Initialize,
|
||||
Quaternion.Identity,
|
||||
},
|
||||
Elements = new []
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(-0.5f, "Rotation", true, typeof(Quaternion), 0, 2),
|
||||
}
|
||||
},
|
||||
GetParticleAttribute(ModuleType.Initialize, 250, "Set Position", "Sets the particle position", typeof(Float3), Float3.Zero),
|
||||
GetParticleAttribute(ModuleType.Initialize, 251, "Set Lifetime", "Sets the particle lifetime (in seconds)", typeof(float), 10.0f),
|
||||
GetParticleAttribute(ModuleType.Initialize, 252, "Set Age", "Sets the particle age (in seconds)", typeof(float), 0.0f),
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
// Copyright (c) 2012-2024 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
|
||||
@@ -2,32 +2,4 @@
|
||||
|
||||
#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;
|
||||
};
|
||||
#include "Engine/Platform/ScreenUtilities.h"
|
||||
|
||||
@@ -135,6 +135,8 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
float a = Mathf.Saturate(progress);
|
||||
a = a * a * a;
|
||||
var targetTransform = Transform.Lerp(_startMove, _endMove, a);
|
||||
if (progress >= 1.0f)
|
||||
targetTransform = _endMove; // Be precise
|
||||
targetTransform.Scale = Vector3.Zero;
|
||||
Viewport.ViewPosition = targetTransform.Translation;
|
||||
Viewport.ViewOrientation = targetTransform.Orientation;
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace FlaxEditor.Viewport
|
||||
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Float2 MouseDelta => _mouseDelta;
|
||||
public Float2 MouseDelta => FlaxEngine.Input.MousePositionDelta;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;
|
||||
|
||||
@@ -158,18 +158,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).
|
||||
@@ -229,13 +233,11 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
if (Mathf.Abs(_movementSpeed - _maxMovementSpeed) < Mathf.Epsilon || Mathf.Abs(_movementSpeed - _minMovementSpeed) < Mathf.Epsilon)
|
||||
return "{0:0.##}";
|
||||
|
||||
if (_movementSpeed < 10.0f)
|
||||
return "{0:0.00}";
|
||||
else if (_movementSpeed < 100.0f)
|
||||
if (_movementSpeed < 100.0f)
|
||||
return "{0:0.0}";
|
||||
else
|
||||
return "{0:#}";
|
||||
return "{0:#}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,11 +288,6 @@ namespace FlaxEditor.Viewport
|
||||
/// </summary>
|
||||
public Float2 MousePositionDelta => _mouseDelta;
|
||||
|
||||
/// <summary>
|
||||
/// Camera's pitch angle clamp range (in degrees).
|
||||
/// </summary>
|
||||
public Float2 CamPitchAngles = new Float2(-88, 88);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view transform.
|
||||
/// </summary>
|
||||
@@ -326,7 +323,7 @@ namespace FlaxEditor.Viewport
|
||||
get => Float3.Forward * ViewOrientation;
|
||||
set
|
||||
{
|
||||
var right = Float3.Cross(value, Float3.Up);
|
||||
var right = Mathf.Abs(Float3.Dot(value, Float3.Up)) < 1.0f - Mathf.Epsilon ? Float3.Cross(value, Float3.Up) : Float3.Forward;
|
||||
var up = Float3.Cross(right, value);
|
||||
ViewOrientation = Quaternion.LookRotation(value, up);
|
||||
}
|
||||
@@ -376,7 +373,11 @@ namespace FlaxEditor.Viewport
|
||||
public float Pitch
|
||||
{
|
||||
get => _pitch;
|
||||
set => _pitch = Mathf.Clamp(value, CamPitchAngles.X, CamPitchAngles.Y);
|
||||
set
|
||||
{
|
||||
var pitchLimit = _isOrtho ? new Float2(-90, 90) : new Float2(-88, 88);
|
||||
_pitch = Mathf.Clamp(value, pitchLimit.X, pitchLimit.Y);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -525,10 +526,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;
|
||||
@@ -1155,8 +1157,7 @@ namespace FlaxEditor.Viewport
|
||||
/// <param name="orientation">The orientation.</param>
|
||||
protected void OrientViewport(Quaternion orientation)
|
||||
{
|
||||
var quat = orientation;
|
||||
OrientViewport(ref quat);
|
||||
OrientViewport(ref orientation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1367,7 +1368,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
var direction = ViewDirection;
|
||||
var target = position + direction;
|
||||
var right = Float3.Normalize(Float3.Cross(Float3.Up, direction));
|
||||
var right = Mathf.Abs(Float3.Dot(direction, Float3.Up)) < 1.0f - Mathf.Epsilon ? Float3.Normalize(Float3.Cross(Float3.Up, direction)) : Float3.Forward;
|
||||
var up = Float3.Normalize(Float3.Cross(direction, right));
|
||||
Matrix.LookAt(ref position, ref target, ref up, out result);
|
||||
}
|
||||
@@ -1456,7 +1457,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);
|
||||
@@ -1465,6 +1468,7 @@ namespace FlaxEditor.Viewport
|
||||
_viewMousePos = center;
|
||||
win.MousePosition = PointToWindow(_viewMousePos);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1476,6 +1480,7 @@ namespace FlaxEditor.Viewport
|
||||
// Restore cursor and stop tracking mouse movement
|
||||
win.Cursor = CursorType.Default;
|
||||
win.EndTrackingMouse();
|
||||
win.MouseMoveRelative -= OnMouseMoveRelative;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1580,6 +1585,14 @@ namespace FlaxEditor.Viewport
|
||||
else
|
||||
EndMouseCapture();
|
||||
}
|
||||
#if PLATFORM_SDL
|
||||
bool useMouse = IsControllingMouse || true;
|
||||
_prevInput = _input;
|
||||
if (canUseInput && ContainsFocus)
|
||||
_input.Gather(win.Window, useMouse, ref _prevInput);
|
||||
else
|
||||
_input.Clear();
|
||||
#else
|
||||
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
|
||||
_prevInput = _input;
|
||||
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
|
||||
@@ -1587,6 +1600,7 @@ namespace FlaxEditor.Viewport
|
||||
_input.Gather(win.Window, useMouse, ref _prevInput);
|
||||
else
|
||||
_input.Clear();
|
||||
#endif
|
||||
|
||||
// Track controlling mouse state change
|
||||
bool wasControllingMouse = _prevInput.IsControllingMouse;
|
||||
@@ -1695,6 +1709,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)
|
||||
@@ -1736,6 +1754,7 @@ namespace FlaxEditor.Viewport
|
||||
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
|
||||
_mouseDeltaLast = currentDelta;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update
|
||||
moveDelta *= dt * (60.0f * 4.0f);
|
||||
@@ -1744,12 +1763,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)
|
||||
@@ -1761,6 +1782,8 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
else
|
||||
{
|
||||
#if PLATFORM_SDL
|
||||
#else
|
||||
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
|
||||
{
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
@@ -1775,6 +1798,7 @@ namespace FlaxEditor.Viewport
|
||||
_mouseDelta = Float2.Zero;
|
||||
}
|
||||
_mouseDeltaLast = Float2.Zero;
|
||||
#endif
|
||||
|
||||
if (ContainsFocus)
|
||||
{
|
||||
@@ -1824,6 +1848,12 @@ namespace FlaxEditor.Viewport
|
||||
_input.MouseWheelDelta = 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnMouseMoveRelative(ref Float2 mouseMotion)
|
||||
{
|
||||
_mouseDelta += mouseMotion;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
|
||||
@@ -1068,8 +1068,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;
|
||||
|
||||
@@ -28,7 +28,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"),
|
||||
@@ -104,6 +104,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"),
|
||||
@@ -164,7 +165,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;
|
||||
|
||||
@@ -145,6 +145,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);
|
||||
|
||||
@@ -127,6 +127,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)
|
||||
|
||||
@@ -95,13 +95,14 @@ 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."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
@@ -100,11 +106,25 @@ void Screen::SetCursorVisible(const bool value)
|
||||
#else
|
||||
const auto win = Engine::MainWindow;
|
||||
#endif
|
||||
bool focused = false;
|
||||
if (win && Engine::HasGameViewportFocus())
|
||||
{
|
||||
win->SetCursor(value ? CursorType::Default : CursorType::Hidden);
|
||||
focused = true;
|
||||
}
|
||||
else if (win)
|
||||
win->SetCursor(CursorType::Default);
|
||||
CursorVisible = value;
|
||||
|
||||
// Just enable relative mode when cursor is constrained and not visible
|
||||
if (CursorLock != CursorLockMode::None && !CursorVisible && focused)
|
||||
{
|
||||
Input::Mouse->SetRelativeMode(true, win);
|
||||
}
|
||||
else if (CursorLock == CursorLockMode::None || CursorVisible || !focused)
|
||||
{
|
||||
Input::Mouse->SetRelativeMode(false, win);
|
||||
}
|
||||
}
|
||||
|
||||
CursorLockMode Screen::GetCursorLock()
|
||||
@@ -133,6 +153,17 @@ void Screen::SetCursorLock(CursorLockMode mode)
|
||||
win->EndClippingCursor();
|
||||
}
|
||||
CursorLock = mode;
|
||||
|
||||
// Just enable relative mode when cursor is constrained and not visible
|
||||
bool focused = win && Engine::HasGameViewportFocus();
|
||||
if (CursorLock != CursorLockMode::None && !CursorVisible && focused)
|
||||
{
|
||||
Input::Mouse->SetRelativeMode(true, win);
|
||||
}
|
||||
else if (CursorLock == CursorLockMode::None || CursorVisible || !focused)
|
||||
{
|
||||
Input::Mouse->SetRelativeMode(false, win);
|
||||
}
|
||||
}
|
||||
|
||||
GameWindowMode Screen::GetGameWindowMode()
|
||||
@@ -190,7 +221,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) 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, 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, VkInstance instance, VkSurfaceKHR* surface);
|
||||
};
|
||||
|
||||
typedef AndroidVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -192,7 +192,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, GPUDeviceVulkan::Instance, &_surface);
|
||||
if (_surface == VK_NULL_HANDLE)
|
||||
{
|
||||
LOG(Warning, "Failed to create Vulkan surface.");
|
||||
|
||||
@@ -4,62 +4,62 @@
|
||||
|
||||
#include "LinuxVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
|
||||
// Contents of vulkan\vulkan_xlib.h inlined here to prevent typename collisions with engine types due to X11 types
|
||||
#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
|
||||
|
||||
typedef struct VkXlibSurfaceCreateInfoKHR
|
||||
{
|
||||
VkStructureType sType;
|
||||
const void* pNext;
|
||||
VkXlibSurfaceCreateFlagsKHR flags;
|
||||
X11::Display* dpy;
|
||||
X11::Window window;
|
||||
} VkXlibSurfaceCreateInfoKHR;
|
||||
#include "vulkan/vulkan_wayland.h"
|
||||
|
||||
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, 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);
|
||||
X11::Window x11Window = (X11::Window)sdlWindow->GetX11WindowHandle();
|
||||
wl_surface* waylandSurface = (wl_surface*)sdlWindow->GetWaylandSurfacePtr();
|
||||
if (waylandSurface != nullptr)
|
||||
{
|
||||
VkWaylandSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.display = (wl_display*)sdlWindow->GetWaylandDisplay();
|
||||
surfaceCreateInfo.surface = waylandSurface;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateWaylandSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
else if (x11Window != 0)
|
||||
{
|
||||
VkXlibSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.dpy = (X11::Display*)sdlWindow->GetX11Display();
|
||||
surfaceCreateInfo.window = x11Window;
|
||||
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, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
};
|
||||
|
||||
typedef LinuxVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "MacVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
void MacVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
@@ -12,12 +13,13 @@ void MacVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Ar
|
||||
extensions.Add(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void MacVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void MacVulkanPlatform::CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
NSWindow* window = (NSWindow*)windowHandle;
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
NSWindow* nswindow = (NSWindow*)windowHandle;
|
||||
VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK);
|
||||
surfaceCreateInfo.pView = (void*)window.contentView;
|
||||
surfaceCreateInfo.pView = (void*)nswindow.contentView;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
|
||||
|
||||
@@ -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, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
};
|
||||
|
||||
typedef MacVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -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,8 +14,9 @@ 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, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
VkWin32SurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.hinstance = GetModuleHandle(nullptr);
|
||||
|
||||
@@ -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, 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,9 @@ 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, 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, 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<const Float2&, int32> Input::TouchDown;
|
||||
Delegate<const Float2&, int32> Input::TouchMove;
|
||||
@@ -209,6 +210,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)
|
||||
{
|
||||
Event& e = _queue.AddOne();
|
||||
@@ -274,6 +283,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;
|
||||
@@ -731,6 +745,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;
|
||||
@@ -787,6 +804,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;
|
||||
@@ -1000,12 +1020,14 @@ void InputService::Update()
|
||||
}
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Lock mouse if need to
|
||||
const auto lockMode = Screen::GetCursorLock();
|
||||
if (lockMode == CursorLockMode::Locked)
|
||||
{
|
||||
Input::SetMousePosition(Screen::GetSize() * 0.5f);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Send events for the active actions and axes (send events only in play mode)
|
||||
if (!Time::GetGamePaused())
|
||||
|
||||
@@ -107,6 +107,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,14 @@ public:
|
||||
return !_state.MouseButtons[static_cast<int32>(button)] && _prevState.MouseButtons[static_cast<int32>(button)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current state of mouse relative mode.
|
||||
/// </summary>
|
||||
API_FUNCTION() FORCE_INLINE bool IsRelative() const
|
||||
{
|
||||
return _relativeMode;
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Sets the mouse position.
|
||||
@@ -121,6 +131,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 +179,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>
|
||||
|
||||
@@ -476,9 +476,9 @@ void Spline::GetKeyframes(MArray* data)
|
||||
Platform::MemoryCopy(MCore::Array::GetAddress(data), Curve.GetKeyframes().Get(), sizeof(Keyframe) * Curve.GetKeyframes().Count());
|
||||
}
|
||||
|
||||
void Spline::SetKeyframes(MArray* data)
|
||||
void Spline::SetKeyframes(MArray* data, int32 keySize)
|
||||
{
|
||||
Curve = Span<byte>((const byte*)MCore::Array::GetAddress(data), MCore::Array::GetLength(data));
|
||||
Curve = Span<byte>(MCore::Array::GetAddress<byte>(data), keySize * MCore::Array::GetLength(data));
|
||||
UpdateSpline();
|
||||
}
|
||||
|
||||
|
||||
@@ -372,7 +372,7 @@ private:
|
||||
// Internal bindings
|
||||
#if !COMPILE_WITHOUT_CSHARP
|
||||
API_FUNCTION(NoProxy) void GetKeyframes(MArray* data);
|
||||
API_FUNCTION(NoProxy) void SetKeyframes(MArray* data);
|
||||
API_FUNCTION(NoProxy) void SetKeyframes(MArray* data, int32 keySize);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace FlaxEngine
|
||||
if (value == null)
|
||||
value = Utils.GetEmptyArray<BezierCurve<Transform>.Keyframe>();
|
||||
_keyframes = null;
|
||||
Internal_SetKeyframes(__unmanagedPtr, value);
|
||||
Internal_SetKeyframes(__unmanagedPtr, value, System.Runtime.CompilerServices.Unsafe.SizeOf<BezierCurve<Transform>.Keyframe>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1150,6 +1150,47 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode*
|
||||
// Not supported
|
||||
break;
|
||||
}
|
||||
// Rotate Position Shape
|
||||
case 216:
|
||||
{
|
||||
PARTICLE_EMITTER_MODULE("Rotate Position Shape");
|
||||
auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]];
|
||||
|
||||
byte* positionPtr = start + positionAttr.Offset;
|
||||
|
||||
auto quatBox = node->GetBox(0);
|
||||
|
||||
#define INPUTS_FETCH() \
|
||||
const Quaternion quat = (Quaternion)GetValue(quatBox, 2);
|
||||
#define LOGIC() \
|
||||
Quaternion nq = Quaternion::Invert(quat); \
|
||||
Float3 v3 = *((Float3*)positionPtr); \
|
||||
Quaternion q = Quaternion(v3.X, v3.Y, v3.Z, 0.0f); \
|
||||
Quaternion rq = quat * (q * nq); \
|
||||
*(Float3*)positionPtr = Float3(rq.X, rq.Y, rq.Z); \
|
||||
positionPtr += stride;
|
||||
|
||||
if (node->UsePerParticleDataResolve())
|
||||
{
|
||||
for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++)
|
||||
{
|
||||
context.ParticleIndex = particleIndex;
|
||||
INPUTS_FETCH();
|
||||
LOGIC();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
INPUTS_FETCH();
|
||||
for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++)
|
||||
{
|
||||
LOGIC();
|
||||
}
|
||||
}
|
||||
#undef INPUTS_FETCH
|
||||
#undef LOGIC
|
||||
break;
|
||||
}
|
||||
|
||||
// Helper macros for collision modules to share the code
|
||||
#define COLLISION_BEGIN() \
|
||||
|
||||
@@ -632,6 +632,20 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node)
|
||||
), position.Value, param.ShaderName, wsPos);
|
||||
break;
|
||||
}
|
||||
// Rotate position shape
|
||||
case 216:
|
||||
{
|
||||
auto positionAttr = AccessParticleAttribute(node, nodeGpu->Attributes[0], AccessMode::Write);
|
||||
const Value quaternion = GetValue(node->GetBox(0), 2).Cast(VariantType::Quaternion);
|
||||
_writer.Write(
|
||||
TEXT(
|
||||
" {{\n"
|
||||
" // Rotate position shape\n"
|
||||
" {0} = QuatRotateVector({1}, {0});\n"
|
||||
" }}\n"
|
||||
), positionAttr.Value, quaternion.Value);
|
||||
break;
|
||||
}
|
||||
|
||||
// Helper macros for collision modules to share the code
|
||||
#define COLLISION_BEGIN() \
|
||||
|
||||
251
Source/Engine/Platform/Base/Enums.h
Normal file
251
Source/Engine/Platform/Base/Enums.h
Normal file
@@ -0,0 +1,251 @@
|
||||
// Copyright (c) 2012-2024 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,
|
||||
};
|
||||
@@ -45,6 +45,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;
|
||||
|
||||
@@ -166,6 +166,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
|
||||
|
||||
42
Source/Engine/Platform/Base/ScreenUtilitiesBase.h
Normal file
42
Source/Engine/Platform/Base/ScreenUtilitiesBase.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2012-2024 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"
|
||||
|
||||
API_INJECT_CODE(cpp, "#include \"Engine/Platform/ScreenUtilities.h\"");
|
||||
|
||||
/// <summary>
|
||||
/// Platform-dependent screen utilities.
|
||||
/// </summary>
|
||||
API_CLASS(Static, Name="ScreenUtilities", Tag="NativeInvokeUseName")
|
||||
class FLAXENGINE_API ScreenUtilitiesBase
|
||||
{
|
||||
public:
|
||||
static struct FLAXENGINE_API ScriptingTypeInitializer TypeInitializer;
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return Color32::Transparent;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
};
|
||||
@@ -106,7 +106,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();
|
||||
|
||||
@@ -164,6 +164,15 @@ void WindowBase::SetIsVisible(bool isVisible)
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowBase::IsAlwaysOnTop() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void WindowBase::SetIsAlwaysOnTop(bool isAlwaysOnTop)
|
||||
{
|
||||
}
|
||||
|
||||
String WindowBase::ToString() const
|
||||
{
|
||||
return GetTitle();
|
||||
@@ -257,6 +266,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");
|
||||
|
||||
@@ -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>
|
||||
@@ -402,6 +157,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>
|
||||
@@ -663,7 +429,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 +438,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>
|
||||
@@ -837,6 +615,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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,15 @@ 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.
|
||||
/// </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;
|
||||
@@ -316,7 +315,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!"));
|
||||
@@ -474,7 +473,7 @@ void GDKPlatform::Exit()
|
||||
if (PlmSignalResume)
|
||||
CloseHandle(PlmSignalResume);
|
||||
|
||||
UnregisterClassW(ApplicationWindowClass, nullptr);
|
||||
UnregisterClassW(ApplicationClassName, nullptr);
|
||||
|
||||
XGameRuntimeUninitialize();
|
||||
}
|
||||
|
||||
@@ -13,11 +13,6 @@ 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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -93,7 +93,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
|
||||
@@ -651,7 +653,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)
|
||||
return DialogResult::None;
|
||||
@@ -838,6 +844,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)
|
||||
@@ -848,6 +856,8 @@ int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int32 CalculateDpi()
|
||||
{
|
||||
int dpi = 96;
|
||||
@@ -1203,17 +1213,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)
|
||||
@@ -1328,8 +1341,10 @@ namespace Impl
|
||||
X11::XQueryPointer(display, w, &wtmp, &child, &tmp, &tmp, &tmp, &tmp, &utmp);
|
||||
return FindAppWindow(display, child);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
class LinuxDropFilesData : public IGuiData
|
||||
{
|
||||
public:
|
||||
@@ -1367,7 +1382,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
DragDropEffect Window::DoDragDrop(const StringView& data)
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
return DragDropEffect::None;
|
||||
@@ -1381,13 +1396,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;
|
||||
@@ -1495,7 +1511,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;
|
||||
@@ -1524,7 +1540,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;
|
||||
@@ -1559,7 +1575,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;
|
||||
@@ -1602,7 +1618,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;
|
||||
@@ -1649,7 +1665,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;
|
||||
@@ -1675,7 +1691,7 @@ void LinuxClipboard::SetText(const StringView& text)
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
return;
|
||||
auto mainWindow = (LinuxWindow*)Engine::MainWindow;
|
||||
auto mainWindow = Engine::MainWindow;
|
||||
if (!mainWindow)
|
||||
return;
|
||||
X11::Window window = (X11::Window)mainWindow->GetNativePtr();
|
||||
@@ -1698,7 +1714,7 @@ String LinuxClipboard::GetText()
|
||||
if (CommandLine::Options.Headless)
|
||||
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();
|
||||
@@ -1727,9 +1743,11 @@ Array<String> LinuxClipboard::GetFiles()
|
||||
return Array<String>();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void* LinuxPlatform::GetXDisplay()
|
||||
{
|
||||
return xDisplay;
|
||||
return xDisplay;
|
||||
}
|
||||
|
||||
bool LinuxPlatform::CreateMutex(const Char* name)
|
||||
@@ -2090,6 +2108,7 @@ bool LinuxPlatform::Init()
|
||||
DeviceId.D = (uint32)UnixCpu.ClockSpeed * UnixCpu.LogicalProcessorCount * UnixCpu.ProcessorCoreCount * UnixCpu.CacheLineSize;
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Get user locale string
|
||||
setlocale(LC_ALL, "");
|
||||
const char* locale = setlocale(LC_CTYPE, NULL);
|
||||
@@ -2099,6 +2118,7 @@ bool LinuxPlatform::Init()
|
||||
UserLocale.Replace('_', '-');
|
||||
if (UserLocale == TEXT("C"))
|
||||
UserLocale = TEXT("en");
|
||||
#endif
|
||||
|
||||
// Get computer name string
|
||||
gethostname(buffer, UNIX_APP_BUFF_SIZE);
|
||||
@@ -2117,10 +2137,15 @@ bool LinuxPlatform::Init()
|
||||
Platform::MemoryClear(Cursors, sizeof(Cursors));
|
||||
Platform::MemoryClear(CursorsImg, sizeof(CursorsImg));
|
||||
|
||||
|
||||
// Skip setup if running in headless mode (X11 might not be available on servers)
|
||||
if (CommandLine::Options.Headless)
|
||||
return false;
|
||||
|
||||
#if PLATFORM_SDL
|
||||
xDisplay = X11::XOpenDisplay(nullptr);
|
||||
#endif
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
X11::XInitThreads();
|
||||
|
||||
xDisplay = X11::XOpenDisplay(nullptr);
|
||||
@@ -2259,7 +2284,7 @@ bool LinuxPlatform::Init()
|
||||
Input::Mouse = Impl::Mouse = New<LinuxMouse>();
|
||||
Input::Keyboard = Impl::Keyboard = New<LinuxKeyboard>();
|
||||
LinuxInput::Init();
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2269,6 +2294,7 @@ void LinuxPlatform::BeforeRun()
|
||||
|
||||
void LinuxPlatform::Tick()
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
UnixPlatform::Tick();
|
||||
|
||||
LinuxInput::UpdateState();
|
||||
@@ -2285,9 +2311,9 @@ void LinuxPlatform::Tick()
|
||||
continue;
|
||||
|
||||
// External event handling
|
||||
xEventRecieved(&event);
|
||||
xEventReceived(&event);
|
||||
|
||||
LinuxWindow* window;
|
||||
Window* window;
|
||||
switch (event.type)
|
||||
{
|
||||
case ClientMessage:
|
||||
@@ -2619,6 +2645,7 @@ void LinuxPlatform::Tick()
|
||||
}
|
||||
|
||||
//X11::XFlush(xDisplay);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LinuxPlatform::BeforeExit()
|
||||
@@ -2627,6 +2654,7 @@ void LinuxPlatform::BeforeExit()
|
||||
|
||||
void LinuxPlatform::Exit()
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
for (int32 i = 0; i < (int32)CursorType::MAX; i++)
|
||||
{
|
||||
if (Cursors[i])
|
||||
@@ -2652,8 +2680,10 @@ void LinuxPlatform::Exit()
|
||||
X11::XCloseDisplay(xDisplay);
|
||||
xDisplay = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
int32 LinuxPlatform::GetDpi()
|
||||
{
|
||||
return SystemDpi;
|
||||
@@ -2663,6 +2693,7 @@ String LinuxPlatform::GetUserLocaleName()
|
||||
{
|
||||
return UserLocale;
|
||||
}
|
||||
#endif
|
||||
|
||||
String LinuxPlatform::GetComputerName()
|
||||
{
|
||||
@@ -2870,10 +2901,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;
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
/// <summary>
|
||||
/// An event that is fired when an XEvent is received during platform tick.
|
||||
/// </summary>
|
||||
static Delegate<void*> xEventRecieved;
|
||||
static Delegate<void*> xEventReceived;
|
||||
|
||||
public:
|
||||
|
||||
@@ -122,8 +122,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);
|
||||
@@ -138,7 +140,9 @@ 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);
|
||||
|
||||
94
Source/Engine/Platform/Linux/LinuxScreenUtilities.cpp
Normal file
94
Source/Engine/Platform/Linux/LinuxScreenUtilities.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if USE_EDITOR && PLATFORM_LINUX
|
||||
|
||||
#include "Engine/Platform/Types.h"
|
||||
#include "Engine/Platform/ScreenUtilities.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Platform/Linux/LinuxPlatform.h"
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
|
||||
Delegate<Color32> ScreenUtilitiesBase::PickColorDone;
|
||||
|
||||
Color32 LinuxScreenUtilities::GetColorAt(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...
|
||||
// TODO: Fallback to Wayland implementation here?
|
||||
return Color32::Black;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Wayland
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
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 = ScreenUtilities::GetColorAt(cursorPos);
|
||||
X11::XUngrabPointer(display, CurrentTime);
|
||||
ScreenUtilities::PickColorDone(colorPicked);
|
||||
LinuxPlatform::xEventReceived.Unbind(OnScreenUtilsXEventCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxScreenUtilities::PickColor()
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Wayland
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
25
Source/Engine/Platform/Linux/LinuxScreenUtilities.h
Normal file
25
Source/Engine/Platform/Linux/LinuxScreenUtilities.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if USE_EDITOR && PLATFORM_LINUX
|
||||
|
||||
#include "Engine/Platform/Base/ScreenUtilitiesBase.h"
|
||||
#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>
|
||||
class FLAXENGINE_API LinuxScreenUtilities : public ScreenUtilitiesBase
|
||||
{
|
||||
public:
|
||||
|
||||
// [ScreenUtilitiesBase]
|
||||
static Color32 GetColorAt(const Float2& pos);
|
||||
static void PickColor();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
#if PLATFORM_LINUX && !PLATFORM_SDL
|
||||
|
||||
#include "../Window.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
@@ -617,7 +617,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) &&
|
||||
|
||||
29
Source/Engine/Platform/Mac/MacScreenUtilities.cpp
Normal file
29
Source/Engine/Platform/Mac/MacScreenUtilities.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if USE_EDITOR && PLATFORM_MAC
|
||||
|
||||
#include "Engine/Platform/Types.h"
|
||||
#include "Engine/Platform/ScreenUtilities.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
Delegate<Color32> ScreenUtilitiesBase::PickColorDone;
|
||||
|
||||
Color32 MacScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
// TODO: implement ScreenUtilities for macOS
|
||||
return { 0, 0, 0, 255 };
|
||||
}
|
||||
|
||||
void MacScreenUtilities::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
|
||||
25
Source/Engine/Platform/Mac/MacScreenUtilities.h
Normal file
25
Source/Engine/Platform/Mac/MacScreenUtilities.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if USE_EDITOR && PLATFORM_MAC
|
||||
|
||||
#include "Engine/Platform/Base/ScreenUtilitiesBase.h"
|
||||
#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>
|
||||
class FLAXENGINE_API MacScreenUtilities : public ScreenUtilitiesBase
|
||||
{
|
||||
public:
|
||||
|
||||
// [ScreenUtilitiesBase]
|
||||
static Color32 GetColorAt(const Float2& pos);
|
||||
static void PickColor();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -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);
|
||||
|
||||
@@ -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) 2012-2024 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
|
||||
823
Source/Engine/Platform/SDL/SDLInput.cpp
Normal file
823
Source/Engine/Platform/SDL/SDLInput.cpp
Normal file
@@ -0,0 +1,823 @@
|
||||
// Copyright (c) 2012-2024 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(position);
|
||||
}
|
||||
|
||||
void SetRelativeMode(bool relativeMode, Window* window) final override
|
||||
{
|
||||
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()));
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the gamepad device for SDL platform.
|
||||
/// </summary>
|
||||
/// <seealso cref="Gamepad" />
|
||||
class SDLGamepad : public Gamepad
|
||||
{
|
||||
private:
|
||||
|
||||
SDL_Gamepad* _gamepad;
|
||||
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 });
|
||||
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);
|
||||
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_AXIS_MOTION");
|
||||
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);
|
||||
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_BUTTON_");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
{
|
||||
Input::Gamepads.Add(New<SDLGamepad>(event.gdevice.which));
|
||||
Input::OnGamepadsChanged();
|
||||
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_ADDED");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_REMOVED");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_REMAPPED:
|
||||
{
|
||||
auto ev = event.gdevice;
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_REMAPPED");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
|
||||
{
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
|
||||
{
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
|
||||
{
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_TOUCHPAD_UP");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
|
||||
{
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_SENSOR_UPDATE");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED:
|
||||
{
|
||||
auto ev = event.gdevice;
|
||||
LOG(Info, "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)
|
||||
{
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) 2012-2024 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
|
||||
1601
Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp
Normal file
1601
Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp
Normal file
File diff suppressed because it is too large
Load Diff
12
Source/Engine/Platform/SDL/SDLPlatform.Mac.cpp
Normal file
12
Source/Engine/Platform/SDL/SDLPlatform.Mac.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_MAC
|
||||
|
||||
static_assert(false, "TODO");
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
#endif
|
||||
292
Source/Engine/Platform/SDL/SDLPlatform.Windows.cpp
Normal file
292
Source/Engine/Platform/SDL/SDLPlatform.Windows.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_WINDOWS
|
||||
|
||||
#define BORDERLESS_MAXIMIZE_WORKAROUND 2
|
||||
|
||||
#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
|
||||
|
||||
#define STYLE_RESIZABLE (WS_THICKFRAME | WS_MAXIMIZEBOX)
|
||||
|
||||
namespace WinImpl
|
||||
{
|
||||
Window* DraggedWindow;
|
||||
Float2 DraggedWindowStartPosition = Float2::Zero;
|
||||
Float2 DraggedWindowMousePosition = 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)
|
||||
{
|
||||
#define GET_WINDOW_WITH_HWND(window, hwnd) \
|
||||
do { \
|
||||
(window) = nullptr; \
|
||||
WindowsManager::WindowsLocker.Lock(); \
|
||||
for (int32 i = 0; i < WindowsManager::Windows.Count(); i++) \
|
||||
{ \
|
||||
if (WindowsManager::Windows[i]->GetNativePtr() == (hwnd)) \
|
||||
{ \
|
||||
(window) = WindowsManager::Windows[i]; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
WindowsManager::WindowsLocker.Unlock(); \
|
||||
ASSERT((window) != nullptr); \
|
||||
} while (false)
|
||||
|
||||
if (msg->message == WM_NCLBUTTONDOWN)
|
||||
{
|
||||
Window* window;
|
||||
GET_WINDOW_WITH_HWND(window, msg->hwnd);
|
||||
WinImpl::DraggedWindow = window;
|
||||
|
||||
WinImpl::DraggedWindowStartPosition = WinImpl::DraggedWindow->GetClientPosition();
|
||||
Float2 mousePos(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::DraggedWindowMousePosition = mousePos;
|
||||
WinImpl::DraggedWindowMousePosition -= WinImpl::DraggedWindowStartPosition;
|
||||
|
||||
bool result = false;
|
||||
WindowHitCodes hit = static_cast<WindowHitCodes>(msg->wParam);
|
||||
window->OnHitTest(mousePos, 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);
|
||||
}
|
||||
}
|
||||
/*else if (msg->message == WM_NCLBUTTONUP || msg->message == WM_CAPTURECHANGED)
|
||||
{
|
||||
windowDragging = false;
|
||||
Window* window;
|
||||
GET_WINDOW_WITH_HWND(window, msg->hwnd);
|
||||
|
||||
SDL_Event event{ 0 };
|
||||
event.button.type = SDL_EVENT_MOUSE_BUTTON_UP;
|
||||
event.button.down = false;
|
||||
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 = static_cast<float>(static_cast<LONG>(WINDOWS_GET_X_LPARAM(msg->lParam)));
|
||||
event.button.y = static_cast<float>(static_cast<LONG>(WINDOWS_GET_Y_LPARAM(msg->lParam)));
|
||||
|
||||
SDL_PushEvent(&event);
|
||||
}*/
|
||||
return true;
|
||||
#undef GET_WINDOW_WITH_HWND
|
||||
}
|
||||
|
||||
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();//Scripting::Update(); // For docking updates
|
||||
Engine::OnDraw();
|
||||
return false;
|
||||
}
|
||||
else if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN)
|
||||
{
|
||||
SDLWindow* window = SDLWindow::GetWindowFromEvent(*event);
|
||||
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)
|
||||
{
|
||||
Float2 start = WinImpl::DraggedWindowStartPosition;
|
||||
Float2 newPos = Float2(static_cast<float>(event->window.data1), static_cast<float>(event->window.data2));
|
||||
Float2 offset = newPos - start;
|
||||
Float2 mousePos = WinImpl::DraggedWindowMousePosition;
|
||||
|
||||
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 = mousePos.X;
|
||||
mouseMovedEvent.motion.y = mousePos.Y;
|
||||
if (window)
|
||||
window->HandleEvent(mouseMovedEvent);
|
||||
if (window)
|
||||
window->HandleEvent(*event);
|
||||
|
||||
return false;
|
||||
}
|
||||
if (window)
|
||||
window->HandleEvent(*event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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
|
||||
276
Source/Engine/Platform/SDL/SDLPlatform.cpp
Normal file
276
Source/Engine/Platform/SDL/SDLPlatform.cpp
Normal file
@@ -0,0 +1,276 @@
|
||||
// Copyright (c) 2012-2024 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/WindowsManager.h"
|
||||
#include "Engine/Platform/SDL/SDLInput.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_misc.h>
|
||||
#include <SDL3/SDL_power.h>
|
||||
#include <SDL3/SDL_revision.h>
|
||||
#include <SDL3/SDL_system.h>
|
||||
#include <SDL3/SDL_version.h>
|
||||
#include <SDL3/SDL_locale.h>
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#endif
|
||||
|
||||
#define DefaultDPI 96
|
||||
|
||||
namespace
|
||||
{
|
||||
int32 SystemDpi = 96;
|
||||
String UserLocale("en");
|
||||
}
|
||||
|
||||
bool SDLPlatform::Init()
|
||||
{
|
||||
#if PLATFORM_LINUX
|
||||
if (CommandLine::Options.X11)
|
||||
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "x11", SDL_HINT_OVERRIDE);
|
||||
else if (CommandLine::Options.Wayland)
|
||||
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
|
||||
else
|
||||
{
|
||||
// Override the X11 preference when running in Wayland session
|
||||
String waylandDisplayEnv;
|
||||
if (!GetEnvironmentVariable(String("WAYLAND_DISPLAY"), waylandDisplayEnv))
|
||||
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
// TODO: This should be read from the platform configuration (needed for desktop icon handling)
|
||||
#if USE_EDITOR
|
||||
SDL_SetHint(SDL_HINT_APP_ID, StringAnsi("com.FlaxEngine.FlaxEditor").Get());
|
||||
#else
|
||||
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"); //
|
||||
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())));
|
||||
|
||||
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)
|
||||
UserLocale = String::Format(TEXT("{0}-{1}"), String(language), String(locales[i]->country));
|
||||
else
|
||||
UserLocale = String(language);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDL_free(locales);
|
||||
|
||||
if (InitInternal())
|
||||
return true;
|
||||
|
||||
if (UsesWindows() || UsesX11())
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
SDLInput::Init();
|
||||
|
||||
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 base::Init();
|
||||
}
|
||||
|
||||
void SDLPlatform::LogInfo()
|
||||
{
|
||||
base::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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int32 SDLPlatform::GetDpi()
|
||||
{
|
||||
return SystemDpi;
|
||||
}
|
||||
|
||||
String SDLPlatform::GetUserLocaleName()
|
||||
{
|
||||
return UserLocale;
|
||||
}
|
||||
|
||||
void SDLPlatform::OpenUrl(const StringView& url)
|
||||
{
|
||||
StringAnsi urlStr(url);
|
||||
SDL_OpenURL(urlStr.GetText());
|
||||
}
|
||||
|
||||
Float2 SDLPlatform::GetMousePosition()
|
||||
{
|
||||
Float2 pos;
|
||||
if (UsesWayland())
|
||||
{
|
||||
// Wayland doesn't support reporting global mouse position,
|
||||
// use the last known reported position we got from received window events.
|
||||
pos = Input::GetMouseScreenPosition();
|
||||
//if (!SDL_GetGlobalMouseState(&pos.X, &pos.Y))
|
||||
// LOG(Error, "SDL_GetGlobalMouseState() failed");
|
||||
}
|
||||
else if (UsesX11())
|
||||
SDL_GetGlobalMouseState(&pos.X, &pos.Y);
|
||||
else
|
||||
pos = Input::GetMouseScreenPosition();
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
||||
79
Source/Engine/Platform/SDL/SDLPlatform.h
Normal file
79
Source/Engine/Platform/SDL/SDLPlatform.h
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2012-2024 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;
|
||||
#else
|
||||
#endif
|
||||
|
||||
class SDLWindow;
|
||||
union SDL_Event;
|
||||
|
||||
/// <summary>
|
||||
/// The SDL platform implementation and application management utilities.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API SDLPlatform
|
||||
#if PLATFORM_WINDOWS
|
||||
: public WindowsPlatform
|
||||
{
|
||||
using base = WindowsPlatform;
|
||||
#elif PLATFORM_LINUX
|
||||
: public LinuxPlatform
|
||||
{
|
||||
using base = LinuxPlatform;
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
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 SDLPlatform::EventFilterCallback(void* userdata, SDL_Event* event);
|
||||
#elif PLATFORM_LINUX
|
||||
static bool X11EventHook(void* userdata, _XEvent* xevent);
|
||||
#endif
|
||||
static void PreHandleEvents();
|
||||
static void PostHandleEvents();
|
||||
|
||||
public:
|
||||
#if PLATFORM_LINUX
|
||||
static void* GetXDisplay();
|
||||
#endif
|
||||
static bool UsesWindows();
|
||||
static bool UsesWayland();
|
||||
static bool UsesX11();
|
||||
|
||||
public:
|
||||
|
||||
// [PlatformBase]
|
||||
static bool Init();
|
||||
static void LogInfo();
|
||||
static void Tick();
|
||||
static void SetHighDpiAwarenessEnabled(bool enable);
|
||||
static BatteryInfo GetBatteryInfo();
|
||||
static int32 GetDpi();
|
||||
static String GetUserLocaleName();
|
||||
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);
|
||||
};
|
||||
|
||||
#endif
|
||||
917
Source/Engine/Platform/SDL/SDLWindow.cpp
Normal file
917
Source/Engine/Platform/SDL/SDLWindow.cpp
Normal file
@@ -0,0 +1,917 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
#include "SDLWindow.h"
|
||||
#include "SDLInput.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Math/Math.h"
|
||||
#include "Engine/Core/Math/Rectangle.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/GPUSwapChain.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Keyboard.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
|
||||
#define NOGDI
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_properties.h>
|
||||
#include <SDL3/SDL_video.h>
|
||||
#undef CreateWindow
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
|
||||
#if USE_EDITOR
|
||||
#include <oleidl.h>
|
||||
#endif
|
||||
#elif PLATFORM_LINUX
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
#endif
|
||||
|
||||
#define DefaultDPI 96
|
||||
|
||||
namespace WindowImpl
|
||||
{
|
||||
SDLWindow* LastEventWindow = nullptr;
|
||||
static SDL_Cursor* Cursors[SDL_SYSTEM_CURSOR_COUNT] = { nullptr };
|
||||
}
|
||||
using namespace WindowImpl;
|
||||
|
||||
SDL_HitTestResult OnWindowHitTest(SDL_Window* win, const SDL_Point* area, void* data);
|
||||
void GetRelativeWindowOffset(WindowType type, SDLWindow* parentWindow, Int2& positionOffset);
|
||||
Int2 GetSDLWindowScreenPosition(const SDLWindow* window);
|
||||
void SetSDLWindowScreenPosition(const SDLWindow* window, const Int2 position);
|
||||
|
||||
bool IsPopupWindow(WindowType type)
|
||||
{
|
||||
return type == WindowType::Popup || type == WindowType::Tooltip;
|
||||
}
|
||||
|
||||
void* GetNativeWindowPointer(SDL_Window* window)
|
||||
{
|
||||
void* windowPtr;
|
||||
auto props = SDL_GetWindowProperties(window);
|
||||
#if PLATFORM_WINDOWS
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
|
||||
#elif PLATFORM_LINUX
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
|
||||
if (windowPtr == nullptr)
|
||||
windowPtr = (void*)SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
||||
#elif PLATFORM_MAC
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
|
||||
#elif PLATFORM_ANDROID
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER, nullptr);
|
||||
#elif PLATFORM_IOS
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, nullptr);
|
||||
#else
|
||||
static_assert(false, "unsupported platform");
|
||||
#endif
|
||||
return windowPtr;
|
||||
}
|
||||
|
||||
SDLWindow::SDLWindow(const CreateWindowSettings& settings)
|
||||
: WindowBase(settings)
|
||||
, _handle(nullptr)
|
||||
, _cachedClientRectangle(Rectangle())
|
||||
#if PLATFORM_LINUX
|
||||
, _dragOver(false)
|
||||
#endif
|
||||
{
|
||||
Int2 clientSize(Math::TruncToInt(settings.Size.X), Math::TruncToInt(settings.Size.Y));
|
||||
_clientSize = Float2(clientSize);
|
||||
|
||||
if (SDLPlatform::UsesWayland())
|
||||
{
|
||||
// The compositor seems to crash when something is rendered to the hidden popup window surface
|
||||
_settings.ShowAfterFirstPaint = _showAfterFirstPaint = false;
|
||||
}
|
||||
|
||||
uint32 flags = SDL_WINDOW_HIDDEN;
|
||||
if (_settings.Type == WindowType::Utility)
|
||||
flags |= SDL_WINDOW_UTILITY;
|
||||
else if (_settings.Type == WindowType::Regular && !_settings.ShowInTaskbar)
|
||||
flags |= SDL_WINDOW_UTILITY;
|
||||
else if (_settings.Type == WindowType::Tooltip)
|
||||
flags |= SDL_WINDOW_TOOLTIP;
|
||||
else if (_settings.Type == WindowType::Popup)
|
||||
flags |= SDL_WINDOW_POPUP_MENU;
|
||||
|
||||
if (!_settings.HasBorder)
|
||||
flags |= SDL_WINDOW_BORDERLESS;
|
||||
if (_settings.AllowInput)
|
||||
flags |= SDL_WINDOW_INPUT_FOCUS;
|
||||
else
|
||||
flags |= SDL_WINDOW_NOT_FOCUSABLE;
|
||||
if (_settings.HasSizingFrame)
|
||||
flags |= SDL_WINDOW_RESIZABLE;
|
||||
if (_settings.IsTopmost)
|
||||
flags |= SDL_WINDOW_ALWAYS_ON_TOP;
|
||||
if (_settings.SupportsTransparency)
|
||||
flags |= SDL_WINDOW_TRANSPARENT;
|
||||
|
||||
// Disable parenting of child windows as those are always on top of the parent window and never show up in taskbar
|
||||
if (_settings.Parent != nullptr && (_settings.Type != WindowType::Tooltip && _settings.Type != WindowType::Popup))
|
||||
_settings.Parent = nullptr;
|
||||
|
||||
// The window position needs to be relative to the parent window
|
||||
Int2 relativePosition(Math::TruncToInt(settings.Position.X), Math::TruncToInt(settings.Position.Y));
|
||||
GetRelativeWindowOffset(_settings.Type, _settings.Parent, relativePosition);
|
||||
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags);
|
||||
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, settings.Title.ToStringAnsi().Get());
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, relativePosition.X);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, relativePosition.Y);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, clientSize.X);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, clientSize.Y);
|
||||
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN, true);
|
||||
if ((flags & SDL_WINDOW_TOOLTIP) != 0)
|
||||
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN, true);
|
||||
else if ((flags & SDL_WINDOW_POPUP_MENU) != 0)
|
||||
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN, true);
|
||||
if (_settings.Parent != nullptr)
|
||||
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, _settings.Parent->_window);
|
||||
|
||||
_window = SDL_CreateWindowWithProperties(props);
|
||||
SDL_DestroyProperties(props);
|
||||
if (_window == nullptr)
|
||||
Platform::Fatal(String::Format(TEXT("Cannot create SDL window: {0}"), String(SDL_GetError())));
|
||||
|
||||
_windowId = SDL_GetWindowID(_window);
|
||||
_handle = GetNativeWindowPointer(_window);
|
||||
ASSERT(_handle != nullptr);
|
||||
|
||||
SDL_DisplayID display = SDL_GetDisplayForWindow(_window);
|
||||
_dpiScale = SDL_GetWindowDisplayScale(_window);
|
||||
_dpi = Math::TruncToInt(_dpiScale * DefaultDPI);
|
||||
|
||||
SDL_SetWindowMinimumSize(_window, Math::TruncToInt(_settings.MinimumSize.X), Math::TruncToInt(_settings.MinimumSize.Y));
|
||||
SDL_SetWindowMaximumSize(_window, Math::TruncToInt(_settings.MaximumSize.X), Math::TruncToInt(_settings.MaximumSize.Y));
|
||||
|
||||
SDL_SetWindowHitTest(_window, &OnWindowHitTest, this);
|
||||
InitSwapChain();
|
||||
|
||||
#if USE_EDITOR
|
||||
// Enable file drag & drop support
|
||||
if (_settings.AllowDragAndDrop)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
const auto result = RegisterDragDrop((HWND)_handle, (LPDROPTARGET)(Windows::IDropTarget*)this);
|
||||
if (result != S_OK)
|
||||
{
|
||||
LOG(Warning, "Window drag and drop service error: 0x{0:x}:{1}", result, 1);
|
||||
}
|
||||
#elif PLATFORM_LINUX
|
||||
auto xDisplay = (X11::Display*)GetX11Display();
|
||||
if (xDisplay)
|
||||
{
|
||||
auto xdndVersion = 5;
|
||||
auto xdndAware = X11::XInternAtom(xDisplay, "XdndAware", 0);
|
||||
if (xdndAware != 0)
|
||||
X11::XChangeProperty(xDisplay, (X11::Window)_handle, xdndAware, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)&xdndVersion, 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
LastEventWindow = this;
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
// Initialize using the shared Display instance from SDL
|
||||
if (SDLPlatform::UsesX11() && SDLPlatform::GetXDisplay() == nullptr)
|
||||
SDLPlatform::InitX11(GetX11Display());
|
||||
|
||||
// Window focus changes breaks the text input for some reason, just keep it enabled for good
|
||||
if (SDLPlatform::UsesX11() && _settings.AllowInput)
|
||||
SDL_StartTextInput(_window);
|
||||
#endif
|
||||
}
|
||||
|
||||
SDL_Window* SDLWindow::GetSDLWindow() const
|
||||
{
|
||||
return _window;
|
||||
}
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
|
||||
void* SDLWindow::GetWaylandSurfacePtr() const
|
||||
{
|
||||
return SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
|
||||
}
|
||||
|
||||
void* SDLWindow::GetWaylandDisplay() const
|
||||
{
|
||||
return SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, nullptr);
|
||||
}
|
||||
|
||||
uintptr SDLWindow::GetX11WindowHandle() const
|
||||
{
|
||||
return (uintptr)SDL_GetNumberProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
||||
}
|
||||
|
||||
void* SDLWindow::GetX11Display() const
|
||||
{
|
||||
return SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_X11_DISPLAY_POINTER, nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
SDLWindow::~SDLWindow()
|
||||
{
|
||||
if (LastEventWindow == this)
|
||||
LastEventWindow = nullptr;
|
||||
|
||||
if (_window == nullptr)
|
||||
return;
|
||||
|
||||
SDL_StopTextInput(_window);
|
||||
SDL_DestroyWindow(_window);
|
||||
|
||||
_window = nullptr;
|
||||
_handle = nullptr;
|
||||
_windowId = 0;
|
||||
_visible = false;
|
||||
}
|
||||
|
||||
SDL_HitTestResult OnWindowHitTest(SDL_Window* win, const SDL_Point* area, void* data)
|
||||
{
|
||||
SDLWindow* window = (SDLWindow*)data;
|
||||
if (window->IsFullscreen())
|
||||
return SDL_HITTEST_NORMAL;
|
||||
|
||||
Float2 clientPosition = Float2(static_cast<float>(area->x), static_cast<float>(area->y));
|
||||
Float2 screenPosition = window->ClientToScreen(clientPosition);
|
||||
|
||||
WindowHitCodes hit = WindowHitCodes::Client;
|
||||
bool handled = false;
|
||||
window->OnHitTest(screenPosition, hit, handled);
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
int margin = window->GetSettings().HasBorder ? 0 : 0;
|
||||
auto size = window->GetClientSize();
|
||||
//if (clientPosition.Y < 0)
|
||||
// return SDL_HITTEST_DRAGGABLE;
|
||||
if (clientPosition.Y < margin && clientPosition.X < margin)
|
||||
return SDL_HITTEST_RESIZE_TOPLEFT;
|
||||
else if (clientPosition.Y < margin && clientPosition.X > size.X - margin)
|
||||
return SDL_HITTEST_RESIZE_TOPRIGHT;
|
||||
else if (clientPosition.Y < margin)
|
||||
return SDL_HITTEST_RESIZE_TOP;
|
||||
else if (clientPosition.X < margin && clientPosition.Y > size.Y - margin)
|
||||
return SDL_HITTEST_RESIZE_BOTTOMLEFT;
|
||||
else if (clientPosition.X < margin)
|
||||
return SDL_HITTEST_RESIZE_LEFT;
|
||||
else if (clientPosition.X > size.X - margin && clientPosition.Y > size.Y - margin)
|
||||
return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
|
||||
else if (clientPosition.X > size.X - margin)
|
||||
return SDL_HITTEST_RESIZE_RIGHT;
|
||||
else if (clientPosition.Y > size.Y - margin)
|
||||
return SDL_HITTEST_RESIZE_BOTTOM;
|
||||
else
|
||||
return SDL_HITTEST_NORMAL;
|
||||
}
|
||||
|
||||
switch (hit)
|
||||
{
|
||||
case WindowHitCodes::Caption:
|
||||
return SDL_HITTEST_DRAGGABLE;
|
||||
case WindowHitCodes::TopLeft:
|
||||
return SDL_HITTEST_RESIZE_TOPLEFT;
|
||||
case WindowHitCodes::Top:
|
||||
return SDL_HITTEST_RESIZE_TOP;
|
||||
case WindowHitCodes::TopRight:
|
||||
return SDL_HITTEST_RESIZE_TOPRIGHT;
|
||||
case WindowHitCodes::Right:
|
||||
return SDL_HITTEST_RESIZE_RIGHT;
|
||||
case WindowHitCodes::BottomRight:
|
||||
return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
|
||||
case WindowHitCodes::Bottom:
|
||||
return SDL_HITTEST_RESIZE_BOTTOM;
|
||||
case WindowHitCodes::BottomLeft:
|
||||
return SDL_HITTEST_RESIZE_BOTTOMLEFT;
|
||||
case WindowHitCodes::Left:
|
||||
return SDL_HITTEST_RESIZE_LEFT;
|
||||
default:
|
||||
return SDL_HITTEST_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
SDLWindow* SDLWindow::GetWindowFromEvent(const SDL_Event& event)
|
||||
{
|
||||
SDL_Window* window = SDL_GetWindowFromEvent(&event);
|
||||
if (window == nullptr)
|
||||
return nullptr;
|
||||
if (LastEventWindow == nullptr || window != LastEventWindow->_window)
|
||||
LastEventWindow = GetWindowWithSDLWindow(window);
|
||||
return LastEventWindow;
|
||||
}
|
||||
|
||||
SDLWindow* SDLWindow::GetWindowWithSDLWindow(SDL_Window* window)
|
||||
{
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
for (auto win : WindowsManager::Windows)
|
||||
{
|
||||
if (win->_window == window)
|
||||
return win;
|
||||
}
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SDLWindow::HandleEvent(SDL_Event& event)
|
||||
{
|
||||
if (_isClosing)
|
||||
return;
|
||||
|
||||
// Platform specific event handling
|
||||
if (HandleEventInternal(event))
|
||||
return;
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
{
|
||||
Close(ClosingReason::User);
|
||||
return;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_DESTROYED:
|
||||
{
|
||||
// Quit
|
||||
#if PLATFORM_WINDOWS
|
||||
PostQuitMessage(0);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
{
|
||||
if (_isTrackingMouse && _isUsingMouseOffset)
|
||||
{
|
||||
Float2 delta(event.motion.xrel, event.motion.yrel);
|
||||
_trackingMouseOffset += delta;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
{
|
||||
if (event.key.scancode == SDL_SCANCODE_RETURN && Input::Keyboard->GetKey(KeyboardKeys::Alt))
|
||||
{
|
||||
LOG(Info, "Alt+Enter pressed");
|
||||
SetIsFullscreen(!IsFullscreen());
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_MOVED:
|
||||
{
|
||||
_cachedClientRectangle.Location = Float2(static_cast<float>(event.window.data1), static_cast<float>(event.window.data2));
|
||||
return;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_HIT_TEST:
|
||||
{
|
||||
return;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_MINIMIZED:
|
||||
{
|
||||
_minimized = true;
|
||||
_maximized = false;
|
||||
return;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_MAXIMIZED:
|
||||
{
|
||||
_minimized = false;
|
||||
_maximized = true;
|
||||
|
||||
CheckForWindowResize();
|
||||
return;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_RESTORED:
|
||||
{
|
||||
if (_maximized)
|
||||
{
|
||||
_maximized = false;
|
||||
// We assume SDL_EVENT_WINDOW_RESIZED is called right afterwards, no need to check for resize here
|
||||
//CheckForWindowResize();
|
||||
}
|
||||
else if (_minimized)
|
||||
{
|
||||
_minimized = false;
|
||||
CheckForWindowResize();
|
||||
}
|
||||
return;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_RESIZED:
|
||||
{
|
||||
int32 width = event.window.data1;
|
||||
int32 height = event.window.data2;
|
||||
|
||||
_clientSize = Float2(static_cast<float>(width), static_cast<float>(height));
|
||||
_cachedClientRectangle.Size = _clientSize;
|
||||
|
||||
// Check if window size has been changed
|
||||
if (width > 0 && height > 0 && (_swapChain == nullptr || width != _swapChain->GetWidth() || height != _swapChain->GetHeight()))
|
||||
OnResize(width, height);
|
||||
return;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
||||
{
|
||||
OnGotFocus();
|
||||
if (IsPopupWindow(_settings.Type))
|
||||
_window = _window;
|
||||
if (_settings.AllowInput && !SDLPlatform::UsesX11())
|
||||
SDL_StartTextInput(_window);
|
||||
const SDL_Rect* currentClippingRect = SDL_GetWindowMouseRect(_window);
|
||||
if (_isClippingCursor && currentClippingRect == nullptr)
|
||||
{
|
||||
SDL_Rect rect{ (int)_clipCursorRect.GetX(), (int)_clipCursorRect.GetY(), (int)_clipCursorRect.GetWidth(), (int)_clipCursorRect.GetHeight() };
|
||||
SDL_SetWindowMouseRect(_window, &rect);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
||||
{
|
||||
if (IsPopupWindow(_settings.Type))
|
||||
_window = _window;
|
||||
if (_settings.AllowInput && !SDLPlatform::UsesX11())
|
||||
SDL_StopTextInput(_window);
|
||||
const SDL_Rect* currentClippingRect = SDL_GetWindowMouseRect(_window);
|
||||
if (currentClippingRect != nullptr)
|
||||
SDL_SetWindowMouseRect(_window, nullptr);
|
||||
OnLostFocus();
|
||||
return;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
|
||||
{
|
||||
float scale = SDL_GetWindowDisplayScale(_window);
|
||||
if (scale > 0.0f && _dpiScale != scale)
|
||||
{
|
||||
float oldScale = _dpiScale;
|
||||
_dpiScale = scale;
|
||||
_dpi = static_cast<int>(_dpiScale * DefaultDPI);
|
||||
int w = static_cast<int>(_cachedClientRectangle.GetWidth() * (scale / oldScale));
|
||||
int h = static_cast<int>(_cachedClientRectangle.GetHeight() * (scale / oldScale));
|
||||
_cachedClientRectangle.Size = Float2(static_cast<float>(w), static_cast<float>(h));
|
||||
SDL_SetWindowSize(_window, w, h);
|
||||
// TODO: Recalculate fonts
|
||||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (_settings.AllowInput)
|
||||
{
|
||||
if (SDLInput::HandleEvent(this, event))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void* SDLWindow::GetNativePtr() const
|
||||
{
|
||||
return _handle;
|
||||
}
|
||||
|
||||
void SDLWindow::Show()
|
||||
{
|
||||
if (_visible)
|
||||
return;
|
||||
|
||||
if (_showAfterFirstPaint)
|
||||
{
|
||||
if (RenderTask)
|
||||
RenderTask->Enabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_ShowWindow(_window);
|
||||
if (_settings.AllowInput && _settings.ActivateWhenFirstShown)
|
||||
Focus();
|
||||
else if (_settings.Parent == nullptr)
|
||||
BringToFront();
|
||||
|
||||
// Reused top-most windows doesn't stay on top for some reason
|
||||
if (_settings.IsTopmost && !IsPopupWindow(_settings.Type))
|
||||
SetIsAlwaysOnTop(true);
|
||||
|
||||
if (_isTrackingMouse)
|
||||
{
|
||||
if (!SDL_CaptureMouse(true))
|
||||
{
|
||||
if (!SDLPlatform::UsesWayland()) // Suppress "That operation is not supported" errors
|
||||
LOG(Warning, "SDL_CaptureMouse: {0}", String(SDL_GetError()));
|
||||
}
|
||||
}
|
||||
|
||||
WindowBase::Show();
|
||||
}
|
||||
|
||||
void SDLWindow::Hide()
|
||||
{
|
||||
if (!_visible)
|
||||
return;
|
||||
|
||||
SDL_HideWindow(_window);
|
||||
|
||||
WindowBase::Hide();
|
||||
}
|
||||
|
||||
void SDLWindow::Minimize()
|
||||
{
|
||||
if (!_settings.AllowMinimize)
|
||||
return;
|
||||
|
||||
SDL_MinimizeWindow(_window);
|
||||
}
|
||||
|
||||
void SDLWindow::Maximize()
|
||||
{
|
||||
if (!_settings.AllowMaximize)
|
||||
return;
|
||||
|
||||
SDL_MaximizeWindow(_window);
|
||||
}
|
||||
|
||||
void SDLWindow::SetBorderless(bool isBorderless, bool maximized)
|
||||
{
|
||||
if (IsFullscreen())
|
||||
SetIsFullscreen(false);
|
||||
|
||||
// Fixes issue of borderless window not going full screen
|
||||
if (IsMaximized())
|
||||
Restore();
|
||||
|
||||
_settings.HasBorder = !isBorderless;
|
||||
|
||||
BringToFront();
|
||||
|
||||
SDL_SetWindowBordered(_window, !isBorderless ? true : false);
|
||||
if (maximized)
|
||||
Maximize();
|
||||
else
|
||||
Focus();
|
||||
|
||||
CheckForWindowResize();
|
||||
}
|
||||
|
||||
void SDLWindow::Restore()
|
||||
{
|
||||
SDL_RestoreWindow(_window);
|
||||
}
|
||||
|
||||
bool SDLWindow::IsClosed() const
|
||||
{
|
||||
return _handle == nullptr;
|
||||
}
|
||||
|
||||
bool SDLWindow::IsForegroundWindow() const
|
||||
{
|
||||
SDL_WindowFlags flags = SDL_GetWindowFlags(_window);
|
||||
return (flags & SDL_WINDOW_INPUT_FOCUS) != 0;
|
||||
}
|
||||
|
||||
void SDLWindow::BringToFront(bool force)
|
||||
{
|
||||
SDL_RaiseWindow(_window);
|
||||
}
|
||||
|
||||
void SDLWindow::SetClientBounds(const Rectangle& clientArea)
|
||||
{
|
||||
Int2 newPos = Int2(clientArea.GetTopLeft());
|
||||
int newW = static_cast<int>(clientArea.GetWidth());
|
||||
int newH = static_cast<int>(clientArea.GetHeight());
|
||||
|
||||
SetSDLWindowScreenPosition(this, newPos);
|
||||
SDL_SetWindowSize(_window, newW, newH);
|
||||
}
|
||||
|
||||
void GetRelativeWindowOffset(WindowType type, SDLWindow* parentWindow, Int2& positionOffset)
|
||||
{
|
||||
if (!IsPopupWindow(type))
|
||||
return;
|
||||
|
||||
SDLWindow* window = parentWindow;
|
||||
while (window != nullptr)
|
||||
{
|
||||
Int2 parentPosition;
|
||||
SDL_GetWindowPosition(window->GetSDLWindow(), &parentPosition.X, &parentPosition.Y);
|
||||
positionOffset -= parentPosition;
|
||||
|
||||
if (!IsPopupWindow(window->GetSettings().Type))
|
||||
break;
|
||||
|
||||
window = window->GetSettings().Parent;
|
||||
}
|
||||
}
|
||||
|
||||
Int2 GetSDLWindowScreenPosition(const SDLWindow* window)
|
||||
{
|
||||
Int2 relativeOffset(0, 0);
|
||||
GetRelativeWindowOffset(window->GetSettings().Type, window->GetSettings().Parent, relativeOffset);
|
||||
|
||||
Int2 position;
|
||||
SDL_GetWindowPosition(window->GetSDLWindow(), &position.X, &position.Y);
|
||||
|
||||
return position - relativeOffset;
|
||||
}
|
||||
|
||||
void SetSDLWindowScreenPosition(const SDLWindow* window, const Int2 position)
|
||||
{
|
||||
Int2 relativePosition = position;
|
||||
GetRelativeWindowOffset(window->GetSettings().Type, window->GetSettings().Parent, relativePosition);
|
||||
SDL_SetWindowPosition(window->GetSDLWindow(), relativePosition.X, relativePosition.Y);
|
||||
}
|
||||
|
||||
void SDLWindow::SetPosition(const Float2& position)
|
||||
{
|
||||
Int2 topLeftBorder;
|
||||
SDL_GetWindowBordersSize(_window, &topLeftBorder.Y, &topLeftBorder.X, nullptr, nullptr);
|
||||
|
||||
Int2 screenPosition(static_cast<int>(position.X), static_cast<int>(position.Y));
|
||||
screenPosition += topLeftBorder;
|
||||
|
||||
if (false && SDLPlatform::UsesX11())
|
||||
{
|
||||
// TODO: is this needed?
|
||||
auto monitorBounds = Platform::GetMonitorBounds(Float2::Minimum);
|
||||
screenPosition += Int2(monitorBounds.GetTopLeft());
|
||||
}
|
||||
|
||||
SetSDLWindowScreenPosition(this, screenPosition);
|
||||
}
|
||||
|
||||
void SDLWindow::SetClientPosition(const Float2& position)
|
||||
{
|
||||
SetSDLWindowScreenPosition(this, Int2(position));
|
||||
}
|
||||
|
||||
void SDLWindow::SetIsFullscreen(bool isFullscreen)
|
||||
{
|
||||
SDL_SetWindowFullscreen(_window, isFullscreen ? true : false);
|
||||
if (!isFullscreen)
|
||||
{
|
||||
// The window is set to always-on-top for some reason when leaving fullscreen
|
||||
SetIsAlwaysOnTop(false);
|
||||
}
|
||||
|
||||
WindowBase::SetIsFullscreen(isFullscreen);
|
||||
}
|
||||
|
||||
bool SDLWindow::IsAlwaysOnTop() const
|
||||
{
|
||||
SDL_WindowFlags flags = SDL_GetWindowFlags(_window);
|
||||
return (flags & SDL_WINDOW_ALWAYS_ON_TOP) != 0;
|
||||
}
|
||||
|
||||
void SDLWindow::SetIsAlwaysOnTop(bool isAlwaysOnTop)
|
||||
{
|
||||
if (!SDL_SetWindowAlwaysOnTop(_window, isAlwaysOnTop))
|
||||
LOG(Warning, "SDL_SetWindowAlwaysOnTop failed: {0}", String(SDL_GetError()));
|
||||
// Not sure if this should change _settings.IsTopmost to reflect the new value?
|
||||
}
|
||||
|
||||
Float2 SDLWindow::GetPosition() const
|
||||
{
|
||||
Int2 topLeftBorder;
|
||||
SDL_GetWindowBordersSize(_window, &topLeftBorder.Y, &topLeftBorder.X, nullptr, nullptr);
|
||||
|
||||
Int2 position = GetSDLWindowScreenPosition(this);
|
||||
position -= topLeftBorder;
|
||||
|
||||
return Float2(static_cast<float>(position.X), static_cast<float>(position.Y));
|
||||
}
|
||||
|
||||
Float2 SDLWindow::GetSize() const
|
||||
{
|
||||
int top, left, bottom, right;
|
||||
SDL_GetWindowBordersSize(_window, &top, &left, &bottom, &right);
|
||||
|
||||
int w, h;
|
||||
SDL_GetWindowSizeInPixels(_window, &w, &h);
|
||||
return Float2(static_cast<float>(w + left + right), static_cast<float>(h + top + bottom));
|
||||
}
|
||||
|
||||
Float2 SDLWindow::GetClientSize() const
|
||||
{
|
||||
int w, h;
|
||||
SDL_GetWindowSizeInPixels(_window, &w, &h);
|
||||
|
||||
return Float2(static_cast<float>(w), static_cast<float>(h));;
|
||||
}
|
||||
|
||||
Float2 SDLWindow::ScreenToClient(const Float2& screenPos) const
|
||||
{
|
||||
Int2 position = GetSDLWindowScreenPosition(this);
|
||||
return screenPos - Float2(static_cast<float>(position.X), static_cast<float>(position.Y));
|
||||
}
|
||||
|
||||
Float2 SDLWindow::ClientToScreen(const Float2& clientPos) const
|
||||
{
|
||||
Int2 position = GetSDLWindowScreenPosition(this);
|
||||
return clientPos + Float2(static_cast<float>(position.X), static_cast<float>(position.Y));
|
||||
}
|
||||
|
||||
void SDLWindow::FlashWindow()
|
||||
{
|
||||
SDL_FlashWindow(_window, SDL_FLASH_UNTIL_FOCUSED);
|
||||
}
|
||||
|
||||
float SDLWindow::GetOpacity() const
|
||||
{
|
||||
float opacity = SDL_GetWindowOpacity(_window);
|
||||
if (opacity < 0.0f)
|
||||
{
|
||||
LOG(Warning, "SDL_GetWindowOpacity failed: {0}", String(SDL_GetError()));
|
||||
opacity = 1.0f;
|
||||
}
|
||||
return opacity;
|
||||
}
|
||||
|
||||
void SDLWindow::SetOpacity(const float opacity)
|
||||
{
|
||||
if (!SDL_SetWindowOpacity(_window, opacity))
|
||||
LOG(Warning, "SDL_SetWindowOpacity failed: {0}", String(SDL_GetError()));
|
||||
}
|
||||
|
||||
#if !PLATFORM_WINDOWS
|
||||
|
||||
void SDLWindow::Focus()
|
||||
{
|
||||
SDL_RaiseWindow(_window);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
String SDLWindow::GetTitle() const
|
||||
{
|
||||
return String(SDL_GetWindowTitle(_window));
|
||||
}
|
||||
|
||||
void SDLWindow::SetTitle(const StringView& title)
|
||||
{
|
||||
SDL_SetWindowTitle(_window, title.ToStringAnsi().Get());
|
||||
}
|
||||
|
||||
void SDLWindow::StartTrackingMouse(bool useMouseScreenOffset)
|
||||
{
|
||||
if (_isTrackingMouse)
|
||||
return;
|
||||
|
||||
_isTrackingMouse = true;
|
||||
_trackingMouseOffset = Float2::Zero;
|
||||
_isUsingMouseOffset = useMouseScreenOffset;
|
||||
|
||||
if (_visible)
|
||||
{
|
||||
if (!SDL_CaptureMouse(true))
|
||||
{
|
||||
if (!SDLPlatform::UsesWayland()) // Suppress "That operation is not supported" errors
|
||||
LOG(Warning, "SDL_CaptureMouse: {0}", String(SDL_GetError()));
|
||||
}
|
||||
|
||||
// For viewport camera mouse tracking we want to use relative mode for best precision
|
||||
if (_cursor == CursorType::Hidden)
|
||||
Input::Mouse->SetRelativeMode(true, this);
|
||||
}
|
||||
}
|
||||
|
||||
void SDLWindow::EndTrackingMouse()
|
||||
{
|
||||
if (!_isTrackingMouse)
|
||||
return;
|
||||
|
||||
_isTrackingMouse = false;
|
||||
_isHorizontalFlippingMouse = false;
|
||||
_isVerticalFlippingMouse = false;
|
||||
|
||||
if (!SDL_CaptureMouse(false))
|
||||
{
|
||||
if (!SDLPlatform::UsesWayland()) // Suppress "That operation is not supported" errors
|
||||
LOG(Warning, "SDL_CaptureMouse: {0}", String(SDL_GetError()));
|
||||
}
|
||||
|
||||
Input::Mouse->SetRelativeMode(false, this);
|
||||
}
|
||||
|
||||
void SDLWindow::StartClippingCursor(const Rectangle& bounds)
|
||||
{
|
||||
if (!IsFocused())
|
||||
return;
|
||||
|
||||
_isClippingCursor = true;
|
||||
SDL_Rect rect{ (int)bounds.GetX(), (int)bounds.GetY(), (int)bounds.GetWidth(), (int)bounds.GetHeight() };
|
||||
SDL_SetWindowMouseRect(_window, &rect);
|
||||
}
|
||||
|
||||
void SDLWindow::EndClippingCursor()
|
||||
{
|
||||
if (!_isClippingCursor)
|
||||
return;
|
||||
|
||||
_isClippingCursor = false;
|
||||
SDL_SetWindowMouseRect(_window, nullptr);
|
||||
}
|
||||
|
||||
void SDLWindow::SetCursor(CursorType type)
|
||||
{
|
||||
CursorType oldCursor = _cursor;
|
||||
WindowBase::SetCursor(type);
|
||||
if (oldCursor != type)
|
||||
SDLWindow::UpdateCursor();
|
||||
}
|
||||
|
||||
void SDLWindow::CheckForWindowResize()
|
||||
{
|
||||
return;
|
||||
// Cache client size
|
||||
_clientSize = GetClientSize();
|
||||
int32 width = (int32)(_clientSize.X);
|
||||
int32 height = (int32)(_clientSize.Y);
|
||||
|
||||
// Check for windows maximized size and see if it needs to adjust position if needed
|
||||
if (_maximized)
|
||||
{
|
||||
// Pick the current monitor data for sizing
|
||||
SDL_Rect rect;
|
||||
auto displayId = SDL_GetDisplayForWindow(_window);
|
||||
SDL_GetDisplayUsableBounds(displayId, &rect);
|
||||
|
||||
if (width > rect.w && height > rect.h)
|
||||
{
|
||||
width = rect.w;
|
||||
height = rect.h;
|
||||
SDL_SetWindowSize(_window, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if window size has been changed
|
||||
if (width > 0 && height > 0 && (_swapChain == nullptr || width != _swapChain->GetWidth() || height != _swapChain->GetHeight()))
|
||||
OnResize(width, height);
|
||||
}
|
||||
|
||||
void SDLWindow::UpdateCursor()
|
||||
{
|
||||
if (_cursor == CursorType::Hidden)
|
||||
{
|
||||
SDL_HideCursor();
|
||||
|
||||
if (_isTrackingMouse)
|
||||
Input::Mouse->SetRelativeMode(true, this);
|
||||
return;
|
||||
}
|
||||
SDL_ShowCursor();
|
||||
//if (_isTrackingMouse)
|
||||
// Input::Mouse->SetRelativeMode(false, this);
|
||||
|
||||
int32 index = SDL_SYSTEM_CURSOR_DEFAULT;
|
||||
switch (_cursor)
|
||||
{
|
||||
case CursorType::Cross:
|
||||
index = SDL_SYSTEM_CURSOR_CROSSHAIR;
|
||||
break;
|
||||
case CursorType::Hand:
|
||||
index = SDL_SYSTEM_CURSOR_POINTER;
|
||||
break;
|
||||
case CursorType::Help:
|
||||
//index = SDL_SYSTEM_CURSOR_DEFAULT;
|
||||
break;
|
||||
case CursorType::IBeam:
|
||||
index = SDL_SYSTEM_CURSOR_TEXT;
|
||||
break;
|
||||
case CursorType::No:
|
||||
index = SDL_SYSTEM_CURSOR_NOT_ALLOWED;
|
||||
break;
|
||||
case CursorType::Wait:
|
||||
index = SDL_SYSTEM_CURSOR_WAIT;
|
||||
break;
|
||||
case CursorType::SizeAll:
|
||||
index = SDL_SYSTEM_CURSOR_MOVE;
|
||||
break;
|
||||
case CursorType::SizeNESW:
|
||||
index = SDL_SYSTEM_CURSOR_NESW_RESIZE;
|
||||
break;
|
||||
case CursorType::SizeNS:
|
||||
index = SDL_SYSTEM_CURSOR_NS_RESIZE;
|
||||
break;
|
||||
case CursorType::SizeNWSE:
|
||||
index = SDL_SYSTEM_CURSOR_NWSE_RESIZE;
|
||||
break;
|
||||
case CursorType::SizeWE:
|
||||
index = SDL_SYSTEM_CURSOR_EW_RESIZE;
|
||||
break;
|
||||
case CursorType::Default:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (Cursors[index] == nullptr)
|
||||
Cursors[index] = SDL_CreateSystemCursor(static_cast<SDL_SystemCursor>(index));
|
||||
SDL_SetCursor(Cursors[index]);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
129
Source/Engine/Platform/SDL/SDLWindow.h
Normal file
129
Source/Engine/Platform/SDL/SDLWindow.h
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2012-2024 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:
|
||||
/// <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();
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
DragDropEffect DoDragDropWayland(const StringView& data, Window* dragSourceWindow = nullptr, Float2 dragOffset = Float2::Zero);
|
||||
DragDropEffect DoDragDropX11(const StringView& data);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
SDL_Window* GetSDLWindow() const;
|
||||
#if PLATFORM_LINUX
|
||||
void* GetWaylandSurfacePtr() const;
|
||||
void* GetWaylandDisplay() const;
|
||||
uintptr GetX11WindowHandle() const;
|
||||
void* GetX11Display() const;
|
||||
#endif
|
||||
|
||||
// [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 SetCursor(CursorType type) 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
|
||||
};
|
||||
|
||||
#endif
|
||||
15
Source/Engine/Platform/ScreenUtilities.h
Normal file
15
Source/Engine/Platform/ScreenUtilities.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "Windows/WindowsScreenUtilities.h"
|
||||
#elif PLATFORM_LINUX
|
||||
#include "Linux/LinuxScreenUtilities.h"
|
||||
#elif PLATFORM_MAC
|
||||
#include "Mac/MacScreenUtilities.h"
|
||||
#else
|
||||
#include "Base/ScreenUtilitiesBase.h"
|
||||
#endif
|
||||
|
||||
#include "Types.h"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user