131 Commits

Author SHA1 Message Date
a1c77cc41a Apply patches to rapidjson
Some checks are pending
Build Android / Game (Android, Release ARM64) (push) Waiting to run
Build iOS / Game (iOS, Release ARM64) (push) Waiting to run
Build Linux / Editor (Linux, Development x64) (push) Waiting to run
Build Linux / Game (Linux, Release x64) (push) Waiting to run
Build macOS / Editor (Mac, Development ARM64) (push) Waiting to run
Build macOS / Game (Mac, Release ARM64) (push) Waiting to run
Build Windows / Editor (Windows, Development x64) (push) Waiting to run
Build Windows / Game (Windows, Release x64) (push) Waiting to run
Cooker / Cook (Mac) (push) Waiting to run
Tests / Tests (Linux) (push) Waiting to run
Tests / Tests (Windows) (push) Waiting to run
2025-01-23 08:32:01 +02:00
ba271d025c Update rapidjson to latest version
Commit d621dc9e9c77f81e5c8a35b8dcc16dcd63351321 in Tencent/rapidjson
2025-01-23 08:32:00 +02:00
115c0b9adc Fix button latching after finishing drag and drop operation 2025-01-23 08:32:00 +02:00
445ca44ad9 Implement new window dragging system 2025-01-23 08:32:00 +02:00
6f289f7bb2 Fix mouse resetting issues after ending relative mode 2025-01-23 08:31:59 +02:00
c842e131bb Fix frame stutter when window is focused 2025-01-23 08:31:59 +02:00
25b4a081d4 Fix error when docking to sides of tabbed panel 2025-01-23 08:31:59 +02:00
26f8fb3cf3 Cleanup Linux SDL implementation 2025-01-23 08:31:59 +02:00
c1ed32baa8 Support compiling third party library C files as C code 2025-01-23 08:31:58 +02:00
80cd58e1be Implement Wayland protocols module and file generation 2025-01-23 08:31:58 +02:00
05e0162638 Fix mouse warping after ending relative mode 2025-01-23 08:31:58 +02:00
057ba88eab Add git fetch method for dependencies 2025-01-23 08:31:57 +02:00
59c70df58d Fix window ShowInTaskbar setting 2025-01-23 08:31:57 +02:00
297cd2c7af Fix various issues with child window positioning 2025-01-23 08:31:57 +02:00
fbdbf1bdec Add Window.IsAlwaysOnTop property 2025-01-23 08:25:20 +02:00
be1370f66e Use SDL locale 2025-01-23 08:25:19 +02:00
4ccb2b536c Allow window with single tab to be dragged from tab area 2025-01-23 08:25:19 +02:00
ccbea2ca10 Fix ValueBox mouse position resetting after releasing the button 2025-01-21 19:53:34 +02:00
a6804cfbfd Update SDL3 2025-01-21 19:53:34 +02:00
e0bd0df0d5 Fix SDL build process on Linux 2025-01-21 19:53:34 +02:00
435979ddd2 Update SDL to 3.1.3 preview 2025-01-21 19:53:33 +02:00
0318ad6d5f Update SDL3 binaries 2025-01-21 19:53:33 +02:00
dffde4bf6c _lfsconfig 2025-01-21 19:53:32 +02:00
5f2a1dab47 Force cursor to center of Game Window when tab handle is clicked 2025-01-21 19:53:32 +02:00
Chandler Cox
8efb52da64 Fix rotation using SDL 2025-01-21 19:53:32 +02:00
ae3c9d6fea Fix Linux compilation without SDL 2025-01-21 19:17:11 +02:00
4b514802ad Fix compilation 2025-01-21 19:17:11 +02:00
66bb90e7ea Update SDL3 2025-01-21 19:17:10 +02:00
02a6f38932 Fix compilation issues 2025-01-21 19:17:10 +02:00
0f0913cb77 Fix windows not being hidden initially 2025-01-21 19:17:10 +02:00
1f413c5776 Fix parent window position handling with popup/tooltip windows 2025-01-21 19:17:09 +02:00
81137fc7fe Fix compilation errors in other platforms 2025-01-21 19:17:09 +02:00
b9dd139c97 Fix CI for Linux 2025-01-21 19:17:09 +02:00
8e9171e9c3 Prevent building with SDL in unsupported platforms 2025-01-21 19:17:08 +02:00
e759e128c7 Fallback to X11 message box implementation when SDL fails 2025-01-21 19:17:08 +02:00
e96ef49f09 Fix popup and context menus not working on Wayland 2025-01-21 19:17:08 +02:00
f4f756e310 Hide warnings for unsupported SDL operations on Wayland 2025-01-21 19:17:07 +02:00
bb53710523 Log a warning for not implemented Wayland functionality 2025-01-21 19:17:07 +02:00
568569a09c Fix compilation in Linux 2025-01-21 19:17:07 +02:00
f0ad18fb82 Enable warning sound in question dialogs 2025-01-21 19:17:06 +02:00
a8f5e19ca6 Enable modern Windows dialog boxes 2025-01-21 19:17:06 +02:00
08bbb1707e Implement relative mouse mode (raw input) for SDL platform 2025-01-21 19:17:06 +02:00
ff4b92320d Add flag for Window types 2025-01-21 19:17:05 +02:00
8d65a1ec60 Enable native windowing system settings with SDL platform 2025-01-21 19:17:05 +02:00
c7b2bd8d24 Add command-line switches to force X11 and Wayland SDL drivers 2025-01-21 19:17:05 +02:00
9fe3e92dd6 Implement SDL platform, windowing and input handling 2025-01-21 19:17:04 +02:00
05109ca74f Refactor application window class name 2025-01-21 19:17:04 +02:00
1387a48da9 Move Window related enums to separate header file 2025-01-21 19:17:04 +02:00
45524d0fb0 Refactor Windows drag and drop implementation 2025-01-21 19:17:03 +02:00
ae962cbdc5 Refactor ScreenUtilities 2025-01-21 19:17:03 +02:00
8673d52a64 Add more helper methods for managing Git repos 2025-01-21 19:17:03 +02:00
fea009a034 Fix centered window location on X11 2025-01-21 19:17:03 +02:00
bac8277b2a Fix initial position of Tooltips 2025-01-21 19:17:02 +02:00
Wojtek Figat
f17d6f62ab Merge branch 'Tryibion-use-version' 2025-01-13 17:32:03 +01:00
Wojtek Figat
ae3d437539 Merge branch 'use-version' of https://github.com/Tryibion/FlaxEngine into Tryibion-use-version 2025-01-13 17:31:53 +01:00
Wojtek Figat
99b737eb14 Merge branch 'Tryibion-aab-building' 2025-01-13 17:31:38 +01:00
Wojtek Figat
4add5dcf49 Add missing default value and fix doc comment
#3150
2025-01-13 17:31:34 +01:00
Wojtek Figat
a204ada6d3 Merge branch 'aab-building' of https://github.com/Tryibion/FlaxEngine into Tryibion-aab-building 2025-01-13 17:18:35 +01:00
Wojtek Figat
bdc7f0760f Merge branch 'Tryibion-re-enable-scale-center' 2025-01-13 17:17:12 +01:00
Wojtek Figat
07ef2f03d5 Merge branch 're-enable-scale-center' of https://github.com/Tryibion/FlaxEngine into Tryibion-re-enable-scale-center 2025-01-13 17:12:20 +01:00
Wojtek Figat
b5437a9097 Merge branch 'Tryibion-fix-actor-drop-focus' 2025-01-13 17:11:48 +01:00
Wojtek Figat
890c3e75c3 Merge branch 'fix-actor-drop-focus' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-actor-drop-focus 2025-01-13 16:08:56 +01:00
Wojtek Figat
7fcf8c2d4c Improve #3138 to pass over setting value to null and via proper cast so value type stays the same 2025-01-13 16:08:43 +01:00
Wojtek Figat
3796b0ccae Revert #3117 and do it different way as some types can be valid even if different thus use internal parameter setter for type validation 2025-01-13 16:07:47 +01:00
Wojtek Figat
bc18ddc04b Merge branch 'Tryibion-invert-channels' 2025-01-13 15:46:49 +01:00
Wojtek Figat
90316dfa52 Minor code changes to PR 2025-01-13 15:46:35 +01:00
Chandler Cox
55fa372197 re-add returns to aab 2025-01-13 07:30:01 -06:00
Chandler Cox
563f0b9ab4 Move Build .aab to Android platform settings and make it side-by-side 2025-01-13 07:24:50 -06:00
Chandler Cox
30ea3bc9c2 Add check for empty array and use IsInRange for version check. 2025-01-13 07:16:31 -06:00
Wojtek Figat
07b9e5bfdd Merge branch 'invert-channels' of https://github.com/Tryibion/FlaxEngine into Tryibion-invert-channels 2025-01-13 09:20:22 +01:00
Chandler Cox
f274639e94 Fix path bug. 2025-01-12 17:17:59 -06:00
Wojtek Figat
7ac2376231 Simplify code 2025-01-12 23:57:52 +01:00
Chandler Cox
01d1dbba6c Add aditional check for if custom define of BuildAAB already exists. 2025-01-12 16:57:19 -06:00
Chandler Cox
ca995093b6 Add .aab bundling for Android. 2025-01-12 16:50:16 -06:00
Wojtek Figat
f36d3a11ab Merge branch 'Tryibion-handle-multi-linked' 2025-01-12 22:13:11 +01:00
Wojtek Figat
06a04167c7 Merge branch 'handle-multi-linked' of https://github.com/Tryibion/FlaxEngine into Tryibion-handle-multi-linked 2025-01-12 22:13:07 +01:00
Chandler Cox
e2df7a1e85 Handle linked values in Float3Editor 2025-01-12 15:07:43 -06:00
Wojtek Figat
80f5b554cd Merge branch 'Tryibion-fix-run-cooked-game' 2025-01-12 22:01:34 +01:00
Wojtek Figat
f10404a948 Merge branch 'fix-run-cooked-game' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-run-cooked-game 2025-01-12 22:01:29 +01:00
Wojtek Figat
a9580b9b46 Merge branch 'Tryibion-anim-param-warn-type' 2025-01-12 22:00:35 +01:00
Wojtek Figat
3d76a99c9b Merge branch 'anim-param-warn-type' of https://github.com/Tryibion/FlaxEngine into Tryibion-anim-param-warn-type 2025-01-12 22:00:30 +01:00
Wojtek Figat
eaeeea6a81 Merge branch 'Tryibion-import-ui-spacing' 2025-01-12 21:59:43 +01:00
Wojtek Figat
b546e4a332 Merge branch 'import-ui-spacing' of https://github.com/Tryibion/FlaxEngine into Tryibion-import-ui-spacing 2025-01-12 21:59:28 +01:00
Wojtek Figat
281e13b42c Merge branch 'xxSeys1-EditPrefabNoContentPanelChange' 2025-01-12 21:58:23 +01:00
Wojtek Figat
8451439abb Merge branch 'EditPrefabNoContentPanelChange' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-EditPrefabNoContentPanelChange 2025-01-12 21:58:19 +01:00
Wojtek Figat
95c9df2581 Merge branch 'Tryibion-no-params-ui' 2025-01-12 21:57:17 +01:00
Wojtek Figat
2699755581 Merge branch 'no-params-ui' of https://github.com/Tryibion/FlaxEngine into Tryibion-no-params-ui 2025-01-12 21:57:12 +01:00
Wojtek Figat
99b0fbee07 Merge branch 'Tryibion-fix-multiblend-exception' 2025-01-12 21:56:32 +01:00
Wojtek Figat
f153c67c0e Merge branch 'fix-multiblend-exception' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-multiblend-exception 2025-01-12 21:56:28 +01:00
Wojtek Figat
fa8857be0e Merge branch 'Tryibion-fix-cpu-profiler-crash' 2025-01-12 21:55:44 +01:00
Chandler Cox
2260236fa6 Fix CPU profiler crash on conversion to ulong. 2025-01-09 20:55:30 -06:00
Chandler Cox
cb7fc6141d Fix crash in multiblend 2d node when first created. 2025-01-09 19:40:36 -06:00
Chandler Cox
77c48c037e Add No parameters UI to particle effect and animated model 2025-01-09 19:19:30 -06:00
Chandler Cox
ccbcab9793 Fix android build on .net9 and use DotNet version that is found by build tool path. 2025-01-08 22:11:21 -06:00
Chandler Cox
e87bb2325b Standardize spacing for import path ui in assets. add import path ui to model prefab. 2025-01-07 20:43:57 -06:00
Chandler Cox
13863344d2 Warn if value is the wrong type for the animation graph parameter trying to be set. 2025-01-07 20:26:25 -06:00
Wojtek Figat
3133ebbd83 Merge branch 'Tryibion-mat-warn-wrong-type' 2025-01-06 23:58:04 +01:00
Wojtek Figat
bff25a8dd8 Merge branch 'mat-warn-wrong-type' of https://github.com/Tryibion/FlaxEngine into Tryibion-mat-warn-wrong-type 2025-01-06 23:57:59 +01:00
Wojtek Figat
a9d0f62039 Merge branch 'Tryibion-import-path-ui' 2025-01-06 23:55:09 +01:00
Wojtek Figat
5335594cb5 Merge branch 'import-path-ui' of https://github.com/Tryibion/FlaxEngine into Tryibion-import-path-ui 2025-01-06 23:55:04 +01:00
Wojtek Figat
3f201c3863 Resave terrain material
#3115
2025-01-06 23:53:27 +01:00
Wojtek Figat
3dc3406230 Merge branch 'Tryibion-fix-size-handles-rotation' 2025-01-06 23:47:25 +01:00
Wojtek Figat
0b99cb6de5 Merge branch 'fix-size-handles-rotation' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-size-handles-rotation 2025-01-06 23:47:17 +01:00
Wojtek Figat
deab78d6b1 Merge branch 'Tryibion-text-box-ui' 2025-01-06 23:46:35 +01:00
Wojtek Figat
45c01e7ada Merge branch 'text-box-ui' of https://github.com/Tryibion/FlaxEngine into Tryibion-text-box-ui 2025-01-06 23:46:28 +01:00
Wojtek Figat
bcc8544c46 Merge branch 'Duroxxigar-add-confirmation-dialog-for-editor' 2025-01-06 23:46:21 +01:00
Wojtek Figat
597bbf1f30 Merge branch 'add-confirmation-dialog-for-editor' of https://github.com/Duroxxigar/FlaxEngine into Duroxxigar-add-confirmation-dialog-for-editor 2025-01-06 23:46:03 +01:00
Wojtek Figat
67b5b74588 Merge branch 'Duroxxigar-load-product-typo-fix' 2025-01-06 23:45:44 +01:00
Wojtek Figat
39f2f0ccdf Merge branch 'load-product-typo-fix' of https://github.com/Duroxxigar/FlaxEngine into Duroxxigar-load-product-typo-fix 2025-01-06 23:45:39 +01:00
Wojtek Figat
6ad2cd0697 Merge branch 'Tryibion-gameplugin-template' 2025-01-06 23:45:15 +01:00
Wojtek Figat
ef01a4afeb Merge branch 'gameplugin-template' of https://github.com/Tryibion/FlaxEngine into Tryibion-gameplugin-template 2025-01-06 23:45:10 +01:00
Wojtek Figat
12b98f2df1 Migrate to actions/upload-artifact@v4
https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/
2025-01-06 23:44:34 +01:00
Chandler Cox
99ba59d02a Change to use static method for import path ui. 2025-01-06 15:58:09 -06:00
Chandler Cox
7eb2088af0 Use Variant::CanCast() for type check. 2025-01-06 15:40:38 -06:00
Jake Young
a66cb4d1b2 Added a confirmation dialog for editors when the reset button is clicked. 2025-01-03 19:18:10 -05:00
Jake Young
22914dc232 Fix typo for error message when trying to load a project file that does not exist. 2025-01-03 17:31:21 -05:00
Chandler Cox
5af13bcc93 Re-enable scale center gizmo. 2025-01-03 13:04:52 -06:00
Chandler Cox
867f7d0143 Add local variables to simplify math. 2024-12-31 14:12:23 -06:00
Chandler Cox
26d0b9a42c Fix rotation of UI size handles. 2024-12-31 14:06:17 -06:00
Chandler Cox
f2e4fe300b Change color for readonly text. Fix selection still showing when text box is not focused. 2024-12-31 12:56:43 -06:00
Chandler Cox
a313db22d2 Add tooltip text for path to let user know it is not editable. 2024-12-31 12:52:54 -06:00
Chandler Cox
e53eddaba5 Add import path UI and button to open folder in asset import settings. 2024-12-31 12:38:29 -06:00
Chandler Cox
f71e731e54 Add saving and loading. 2024-12-30 23:42:32 -06:00
Chandler Cox
cb6ab6a647 Fix comment 2024-12-30 22:14:18 -06:00
Chandler Cox
899528e087 Add invert options for texture imports for Red, Blue, and Alpha channels. 2024-12-30 21:27:03 -06:00
Chandler Cox
6a8814c99a Fix .exe being deleted when using the "Run cooked build" option. 2024-12-29 17:08:10 -06:00
Chandler Cox
88703d721b Warn if wrong type while setting material parameter. 2024-12-23 16:51:37 -06:00
Chandler Cox
36d4417905 Add game plugin template. 2024-12-20 09:29:36 -06:00
Chandler Cox
f9e125f795 Select dropped actors that are reparented. 2024-12-03 17:59:30 -06:00
xxSeys1
1fca41b31a code style fix 2024-10-09 18:29:48 +02:00
xxSeys1
8b3f1ca019 make "Edit Prefab" not change content panel file path
If someone still wants to change the Content panel to the file path of the prefab, they can use the "Select Prefab" button
2024-10-09 18:25:04 +02:00
288 changed files with 96670 additions and 3283 deletions

View File

@@ -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

View File

@@ -35,12 +35,12 @@ jobs:
run: |
.\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Windows-Editor
path: Output/Editor.zip
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Windows-EditorDebugSymbols
path: Output/EditorDebugSymbols.zip
@@ -68,7 +68,7 @@ jobs:
run: |
.\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Windows-Game
path: Output/Windows.zip
@@ -101,7 +101,7 @@ jobs:
run: |
./PackageEditor.sh -arch=x64 -platform=Linux -deployOutput=Output
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Linux-Editor
path: Output/FlaxEditorLinux.zip
@@ -132,7 +132,7 @@ jobs:
run: |
./PackagePlatforms.sh -arch=x64 -platform=Linux -deployOutput=Output
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Linux-Game
path: Output/Linux.zip
@@ -162,7 +162,7 @@ jobs:
run: |
./PackageEditor.command -arch=ARM64 -platform=Mac -deployOutput=Output
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Mac-Editor
path: Output/FlaxEditorMac.zip
@@ -190,7 +190,7 @@ jobs:
run: |
./PackagePlatforms.command -arch=ARM64 -platform=Mac -deployOutput=Output
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Mac-Game
path: Output/Mac.zip

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,25 @@
%copyright%using System;
using System.Collections.Generic;
using FlaxEngine;
namespace %namespace%;
/// <summary>
/// %class% GamePlugin.
/// </summary>
public class %class% : GamePlugin
{
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
}
/// <inheritdoc/>
public override void Deinitialize()
{
base.Deinitialize();
}
}

BIN
Content/Engine/DefaultTerrainMaterial.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -13,6 +13,7 @@
"Configuration": {
"UseCSharp": true,
"UseLargeWorlds": false,
"UseDotNet": true
"UseDotNet": true,
"UseSDL": true
}
}

View File

@@ -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;

View File

@@ -106,6 +106,23 @@ namespace FlaxEditor.Content
}
}
/// <summary>
/// Context proxy object for C# GamePlugin files.
/// </summary>
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
[ContentContextMenu("New/C#/C# GamePlugin")]
public class CSharpGamePluginProxy : CSharpProxy
{
/// <inheritdoc />
public override string Name => "C# GamePlugin";
/// <inheritdoc />
protected override void GetTemplatePath(out string path)
{
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/GamePluginTemplate.cs");
}
}
/// <summary>
/// Context proxy object for empty C# files.
/// </summary>

View File

@@ -671,11 +671,14 @@ bool GameCookerImpl::Build()
MCore::Thread::Attach();
// Build Started
CallEvent(GameCooker::EventType::BuildStarted);
data.Tools->OnBuildStarted(data);
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
Steps[stepIndex]->OnBuildStarted(data);
data.InitProgress(Steps.Count());
if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook))
{
CallEvent(GameCooker::EventType::BuildStarted);
data.Tools->OnBuildStarted(data);
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
Steps[stepIndex]->OnBuildStarted(data);
data.InitProgress(Steps.Count());
}
// Execute all steps in a sequence
bool failed = false;
@@ -741,10 +744,13 @@ bool GameCookerImpl::Build()
}
IsRunning = false;
CancelFlag = 0;
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
Steps[stepIndex]->OnBuildEnded(data, failed);
data.Tools->OnBuildEnded(data, failed);
CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone);
if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook))
{
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
Steps[stepIndex]->OnBuildEnded(data, failed);
data.Tools->OnBuildEnded(data, failed);
CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone);
}
Delete(Data);
Data = nullptr;

View File

@@ -364,6 +364,33 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
}
#endif
const bool distributionPackage = buildSettings->ForDistribution || data.Configuration == BuildConfiguration::Release;
if (platformSettings->BuildAAB)
{
// .aab
{
CreateProcessSettings procSettings;
procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT(":app:bundle") : TEXT(":app:bundleDebug"));
procSettings.WorkingDirectory = data.OriginalOutputPath;
const int32 result = Platform::CreateProcess(procSettings);
if (result != 0)
{
data.Error(String::Format(TEXT("Failed to build Gradle project into .aab package (result code: {0}). See log for more info."), result));
return true;
}
}
// Copy result package
const String aab = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/bundle/release/app-release.aab") : TEXT("app/build/outputs/bundle/debug/app-debug.aab"));
const String outputAab = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".aab");
if (FileSystem::CopyFile(outputAab, aab))
{
LOG(Error, "Failed to copy .aab package from {0} to {1}", aab, outputAab);
return true;
}
LOG(Info, "Output Android AAB application package: {0} (size: {1} MB)", outputAab, FileSystem::GetFileSize(outputAab) / 1024 / 1024);
}
// .apk
{
CreateProcessSettings procSettings;
procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug"));
@@ -371,20 +398,20 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
const int32 result = Platform::CreateProcess(procSettings);
if (result != 0)
{
data.Error(String::Format(TEXT("Failed to build Gradle project into package (result code: {0}). See log for more info."), result));
data.Error(String::Format(TEXT("Failed to build Gradle project into .apk package (result code: {0}). See log for more info."), result));
return true;
}
}
// Copy result package
const String apk = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/apk/release/app-release-unsigned.apk") : TEXT("app/build/outputs/apk/debug/app-debug.apk"));
const String outputApk = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".apk");
if (FileSystem::CopyFile(outputApk, apk))
{
LOG(Error, "Failed to copy package from {0} to {1}", apk, outputApk);
LOG(Error, "Failed to copy .apk package from {0} to {1}", apk, outputApk);
return true;
}
LOG(Info, "Output Android application package: {0} (size: {1} MB)", outputApk, FileSystem::GetFileSize(outputApk) / 1024 / 1024);
LOG(Info, "Output Android APK application package: {0} (size: {1} MB)", outputApk, FileSystem::GetFileSize(outputApk) / 1024 / 1024);
return false;
}

View File

@@ -214,6 +214,33 @@ bool DeployDataStep::Perform(CookingData& data)
FileSystem::NormalizePath(srcDotnet);
LOG(Info, "Using .NET Runtime {} at {}", TEXT("Host"), srcDotnet);
// Get major Version
Array<String> pathParts;
srcDotnet.Split('/', pathParts);
String version;
for (int i = 0; i < pathParts.Count(); i++)
{
if (pathParts[i] == TEXT("runtimes"))
{
Array<String> versionParts;
pathParts[i - 1].Split('.', versionParts);
if (!versionParts.IsEmpty())
{
const String majorVersion = versionParts[0].TrimTrailing();
int32 versionNum;
StringUtils::Parse(*majorVersion, majorVersion.Length(), &versionNum);
if (Math::IsInRange(versionNum, GAME_BUILD_DOTNET_RUNTIME_MIN_VER, GAME_BUILD_DOTNET_RUNTIME_MAX_VER)) // Check for major part
version = majorVersion;
}
}
}
if (version.IsEmpty())
{
data.Error(TEXT("Failed to find supported .NET version for the current host platform."));
return true;
}
// Deploy runtime files
const Char* corlibPrivateName = TEXT("System.Private.CoreLib.dll");
const bool srcDotnetFromEngine = srcDotnet.Contains(TEXT("Source/Platforms"));
@@ -226,14 +253,14 @@ bool DeployDataStep::Perform(CookingData& data)
{
// AOT runtime files inside Engine Platform folder
packFolder /= TEXT("Dotnet");
dstDotnetLibs /= TEXT("lib/net8.0");
srcDotnetLibs = packFolder / TEXT("lib/net8.0");
dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
srcDotnetLibs = packFolder / String::Format(TEXT("lib/net{}.0"), version);
}
else
{
// Runtime files inside Dotnet SDK folder but placed for AOT
dstDotnetLibs /= TEXT("lib/net8.0");
srcDotnetLibs /= TEXT("../lib/net8.0");
dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
srcDotnetLibs /= String::Format(TEXT("../lib/net{}.0"), version);
}
}
else
@@ -241,14 +268,14 @@ bool DeployDataStep::Perform(CookingData& data)
if (srcDotnetFromEngine)
{
// Runtime files inside Engine Platform folder
dstDotnetLibs /= TEXT("lib/net8.0");
srcDotnetLibs /= TEXT("lib/net8.0");
dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
srcDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
}
else
{
// Runtime files inside Dotnet SDK folder
dstDotnetLibs /= TEXT("shared/Microsoft.NETCore.App");
srcDotnetLibs /= TEXT("../lib/net8.0");
srcDotnetLibs /= String::Format(TEXT("../lib/net{}.0"), version);
}
}
LOG(Info, "Copying .NET files from {} to {}", packFolder, dstDotnet);
@@ -273,6 +300,7 @@ bool DeployDataStep::Perform(CookingData& data)
DEPLOY_NATIVE_FILE("libmonosgen-2.0.so");
DEPLOY_NATIVE_FILE("libSystem.IO.Compression.Native.so");
DEPLOY_NATIVE_FILE("libSystem.Native.so");
DEPLOY_NATIVE_FILE("libSystem.Globalization.Native.so");
DEPLOY_NATIVE_FILE("libSystem.Security.Cryptography.Native.Android.so");
break;
case BuildPlatform::iOSARM64:

View File

@@ -9,6 +9,7 @@ using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.GUI;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Tree;
using FlaxEditor.Modules;
using FlaxEditor.Scripting;
using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets;
@@ -85,12 +86,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Edit selected prefab asset
var editPrefab = panel.Button("Edit Prefab");
editPrefab.Button.Clicked += () =>
{
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
Editor.Instance.Windows.ContentWin.Select(prefab);
Editor.Instance.Windows.ContentWin.Open(Editor.Instance.Windows.ContentWin.View.Selection[0]);
};
editPrefab.Button.Clicked += () => Editor.Instance.Windows.ContentWin.Open(Editor.Instance.ContentDatabase.FindAsset(prefab.ID));
// Viewing changes applied to this actor
var viewChanges = panel.Button("View Changes");

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.Linq;
using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.Surface;
using FlaxEngine;
@@ -35,6 +36,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
(instance, parameter, tag) => ((AnimatedModel)instance).GetParameterValue(parameter.Identifier),
(instance, value, parameter, tag) => ((AnimatedModel)instance).SetParameterValue(parameter.Identifier, value),
Values);
if (!parameters.Any())
group.Label("No parameters", TextAlignment.Center);
_parametersAdded = true;
}
}

View File

@@ -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);

View File

@@ -53,6 +53,9 @@ public class ModelPrefabEditor : GenericEditor
}
}
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, modelPrefab.ImportPath, false);
var button = layout.Button("Reimport", "Reimports the source asset as prefab.");
_reimportButton = button.Button;
_reimportButton.Clicked += OnReimport;

View File

@@ -117,6 +117,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
var data = SurfaceUtils.InitGraphParameters(parametersGroup);
SurfaceUtils.DisplayGraphParameters(group, data, ParameterGet, ParameterSet, Values, ParameterDefaultValue);
}
if (!parameters.Any())
groups.Label("No parameters", TextAlignment.Center);
}
/// <inheritdoc />

View File

@@ -228,7 +228,6 @@ namespace FlaxEditor.CustomEditors.Editors
// Handle Sliding
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
{
// TODO: handle linked values
Float3 average = Float3.Zero;
for (int i = 0; i < Values.Count; i++)
{
@@ -251,12 +250,24 @@ namespace FlaxEditor.CustomEditors.Editors
for (int i = 0; i < Values.Count; i++)
{
var v = Values[i];
if (v is Vector3 asVector3)
v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
else if (v is Float3 asFloat3)
v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
else if (v is Double3 asDouble3)
v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
if (LinkValues)
{
if (v is Vector3 asVector3)
v = asVector3 + new Vector3(newValue.X, newValue.Y, newValue.Z);
else if (v is Float3 asFloat3)
v = asFloat3 + new Float3(newValue.X, newValue.Y, newValue.Z);
else if (v is Double3 asDouble3)
v = asDouble3 + new Double3(newValue.X, newValue.Y, newValue.Z);
}
else
{
if (v is Vector3 asVector3)
v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
else if (v is Float3 asFloat3)
v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
else if (v is Double3 asDouble3)
v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
}
newObjects[i] = v;
}
@@ -267,16 +278,27 @@ namespace FlaxEditor.CustomEditors.Editors
}
else
{
// TODO: handle linked values
for (int i = 0; i < Values.Count; i++)
{
object v = Values[i];
if (v is Vector3 asVector3)
v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z);
else if (v is Float3 asFloat3)
v = new Float3(_valueChanged == ValueChanged.X ? xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? zValue : asFloat3.Z);
else if (v is Double3 asDouble3)
v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z);
if (LinkValues)
{
if (v is Vector3 asVector3)
v = asVector3 + new Vector3(xValue, yValue, zValue);
else if (v is Float3 asFloat3)
v = asFloat3 + new Float3(xValue, yValue, zValue);
else if (v is Double3 asDouble3)
v = asDouble3 + new Double3(xValue, yValue, zValue);
}
else
{
if (v is Vector3 asVector3)
v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z);
else if (v is Float3 asFloat3)
v = new Float3(_valueChanged == ValueChanged.X ? xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? zValue : asFloat3.Z);
else if (v is Double3 asDouble3)
v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z);
}
newObjects[i] = v;
}

View File

@@ -550,7 +550,7 @@ int32 Editor::LoadProduct()
}
if (!FileSystem::FileExists(files[0]))
{
Platform::Fatal(TEXT("Cannot opoen selected project file because it doesn't exist."));
Platform::Fatal(TEXT("Cannot open selected project file because it doesn't exist."));
return -1;
}
projectPath = StringUtils::GetDirectoryName(files[0]);

View File

@@ -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

View File

@@ -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;
}
}
}
}
}

View File

@@ -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)
{

View File

@@ -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>

View File

@@ -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;

View File

@@ -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

View File

@@ -0,0 +1,445 @@
// 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();
_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("", _dragOffset, dragSourceWindowWayland);
_dragSourceWindow.MouseUp += OnMouseUp; // The mouse up event is sent to the source window on Windows
}
else
{
window.DoDragDrop("", _dragOffset, window);
}
// Ensure the dragged window stays on top of every other window
//window.Show();
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 dock hint window 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>
/// <returns>The dock hint window 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()
{
// Cache mouse position
_mouse = Platform.MousePosition;
// 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();
// 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();
}
}
}

View File

@@ -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;
}

View File

@@ -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()
{

View File

@@ -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;

View File

@@ -169,12 +169,12 @@ namespace FlaxEditor.Gizmo
closestIntersection = intersection;
}
/*// Center
if (CenterBoxRaw.Intersects(ref localRay, out intersection) && intersection < closestIntersection)
// Center
if (CenterBoxRaw.Intersects(ref localRay, out intersection) && intersection > closestIntersection)
{
_activeAxis = Axis.Center;
closestIntersection = intersection;
}*/
}
break;
}

View File

@@ -20,7 +20,7 @@ namespace FlaxEditor.Gizmo
/// <summary>
/// Offset to move axis away from center
/// </summary>
private const float AxisOffset = 0.8f;
private const float AxisOffset = 1.2f;
/// <summary>
/// How thick the axis should be

View File

@@ -501,7 +501,7 @@ namespace FlaxEditor.Gizmo
_scaleDelta = Vector3.Zero;
if (ActiveAxis == Axis.Center)
scaleDelta = new Vector3(scaleDelta.AvgValue);
scaleDelta = new Vector3(scaleDelta.ValuesSum);
}
// Apply transformation (but to the parents, not whole selection pool)

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Gizmo;
@@ -647,7 +648,36 @@ namespace FlaxEditor
private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size, float scale, Float2 resizeAxis, CursorType cursor)
{
var style = Style.Current;
var rect = new Rectangle((pos + resizeAxis * 10 * scale) - size * 0.5f, size);
var control = uiControl.Control;
var rotation = control.Rotation;
var rotationInRadians = rotation * Mathf.DegreesToRadians;
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);
// Find more correct cursor at different angles
var unwindRotation = Mathf.UnwindDegrees(rotation);
if (unwindRotation is (>= 45 and < 135) or (> -135 and <= -45) )
{
switch (cursor)
{
case CursorType.SizeNESW:
cursor = CursorType.SizeNWSE;
break;
case CursorType.SizeNS:
cursor = CursorType.SizeWE;
break;
case CursorType.SizeNWSE:
cursor = CursorType.SizeNESW;
break;
case CursorType.SizeWE:
cursor = CursorType.SizeNS;
break;
default: break;
}
}
if (rect.Contains(ref mousePos))
{
Render2D.FillRectangle(rect, style.Foreground);

View File

@@ -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;

View File

@@ -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);
@@ -1140,6 +1140,7 @@ namespace FlaxEditor.Modules
Proxy.Add(new CSharpEmptyStructProxy());
Proxy.Add(new CSharpEmptyInterfaceProxy());
Proxy.Add(new CSharpActorProxy());
Proxy.Add(new CSharpGamePluginProxy());
Proxy.Add(new CppAssetProxy());
Proxy.Add(new CppStaticClassProxy());
Proxy.Add(new CppScriptProxy());

View File

@@ -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));

View File

@@ -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);

View File

@@ -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;

View File

@@ -1,7 +1,9 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.Text;
using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Editors;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Json;
@@ -31,11 +33,33 @@ namespace FlaxEditor.Options
private void OnResetButtonClicked()
{
var obj = new T();
var str = JsonSerializer.Serialize(obj);
JsonSerializer.Deserialize(Values[0], str);
SetValue(Values[0]);
Refresh();
var editorClassName = typeof(T).Name;
var editorName = new StringBuilder();
editorName.Append(editorClassName[0]);
for (var i = 1; i < editorClassName.Length; i++)
{
// Whenever there is an uppercase letter, add a space to make it more pretty for the end user
if (char.IsUpper(editorClassName[i]))
{
editorName.Append(' ');
}
editorName.Append(editorClassName[i]);
}
var result = MessageBox.Show($"Are you sure you want to reset \"{editorName}\" to default values?",
"Reset values?",
MessageBoxButtons.YesNo
);
if (result == DialogResult.Yes || result == DialogResult.OK)
{
var obj = new T();
var str = JsonSerializer.Serialize(obj);
JsonSerializer.Deserialize(Values[0], str);
SetValue(Values[0]);
Refresh();
}
}
}
}

View File

@@ -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>

View File

@@ -596,11 +596,17 @@ namespace FlaxEditor.SceneGraph.GUI
{
bool worldPositionsStays = Root.GetKey(KeyboardKeys.Control) == false;
var objects = new SceneObject[_dragActors.Objects.Count];
var treeNodes = new TreeNode[_dragActors.Objects.Count];
for (int i = 0; i < objects.Length; i++)
{
objects[i] = _dragActors.Objects[i].Actor;
treeNodes[i] = _dragActors.Objects[i].TreeNode;
}
var action = new ParentActorsAction(objects, newParent, newOrder, worldPositionsStays);
ActorNode.Root.Undo?.AddAction(action);
action.Do();
ParentTree.Focus();
ParentTree.Select(treeNodes.ToList());
result = DragDropEffect.Move;
}
// Drag scripts

View File

@@ -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();

View File

@@ -1205,13 +1205,16 @@ namespace FlaxEditor.Surface.Archetypes
// Highlight selected blend point
var style = Style.Current;
var selectedIndex = _selectedAnimation.SelectedIndex;
if (selectedIndex != -1 && (ContainsFocus || IsMouseOver))
if (selectedIndex != -1 && selectedIndex < _editor.BlendPoints.Count && (ContainsFocus || IsMouseOver))
{
var point = _editor.BlendPoints[selectedIndex];
var highlightColor = point.IsMouseDown ? style.SelectionBorder : style.BackgroundSelected;
Render2D.PushTint(ref highlightColor);
Render2D.DrawTriangles(_selectedTriangles, _selectedColors);
Render2D.PopTint();
if (point != null)
{
var highlightColor = point.IsMouseDown ? style.SelectionBorder : style.BackgroundSelected;
Render2D.PushTint(ref highlightColor);
Render2D.DrawTriangles(_selectedTriangles, _selectedColors);
Render2D.PopTint();
}
}
}

View File

@@ -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

View File

@@ -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"

View File

@@ -383,6 +383,40 @@ namespace FlaxEditor.Utilities
File.Delete(file);
}
/// <summary>
/// Creates an Import path ui that show the asset import path and adds a button to show the folder in the file system.
/// </summary>
/// <param name="parentLayout">The parent layout container.</param>
/// <param name="assetItem">The asset item to get the import path of.</param>
public static void CreateImportPathUI(CustomEditors.LayoutElementsContainer parentLayout, Content.BinaryAssetItem assetItem)
{
assetItem.GetImportPath(out var path);
CreateImportPathUI(parentLayout, path);
}
/// <summary>
/// Creates an Import path ui that show the import path and adds a button to show the folder in the file system.
/// </summary>
/// <param name="parentLayout">The parent layout container.</param>
/// <param name="path">The import path.</param>
/// <param name="useInitialSpacing">Whether to use an initial layout space of 5 for separation.</param>
public static void CreateImportPathUI(CustomEditors.LayoutElementsContainer parentLayout, string path, bool useInitialSpacing = true)
{
if (!string.IsNullOrEmpty(path))
{
if (useInitialSpacing)
parentLayout.Space(5);
parentLayout.Label("Import Path:").Label.TooltipText = "Source asset path (can be relative or absolute to the project)";
var textBox = parentLayout.TextBox().TextBox;
textBox.TooltipText = "Path is not editable here.";
textBox.IsReadOnly = true;
textBox.Text = path;
parentLayout.Space(2);
var button = parentLayout.Button(Constants.ShowInExplorer).Button;
button.Clicked += () => FileSystem.ShowFileExplorer(Path.GetDirectoryName(path));
}
}
/// <summary>
/// Copies the directory. Supports subdirectories copy with files override option.
/// </summary>

View File

@@ -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;

View File

@@ -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).
@@ -525,10 +529,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;
@@ -1456,7 +1461,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 +1472,7 @@ namespace FlaxEditor.Viewport
_viewMousePos = center;
win.MousePosition = PointToWindow(_viewMousePos);
}
#endif
}
/// <summary>
@@ -1476,6 +1484,7 @@ namespace FlaxEditor.Viewport
// Restore cursor and stop tracking mouse movement
win.Cursor = CursorType.Default;
win.EndTrackingMouse();
win.MouseMoveRelative -= OnMouseMoveRelative;
}
/// <summary>
@@ -1580,6 +1589,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 +1604,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 +1713,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 +1758,7 @@ namespace FlaxEditor.Viewport
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
_mouseDeltaLast = currentDelta;
}
#endif
// Update
moveDelta *= dt * (60.0f * 4.0f);
@@ -1744,12 +1767,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 +1786,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 +1802,7 @@ namespace FlaxEditor.Viewport
_mouseDelta = Float2.Zero;
}
_mouseDeltaLast = Float2.Zero;
#endif
if (ContainsFocus)
{
@@ -1824,6 +1852,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)
{

View File

@@ -2,6 +2,7 @@
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml;
using FlaxEditor.Content;
@@ -210,8 +211,11 @@ namespace FlaxEditor.Windows.Assets
var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings };
group.Object(importSettingsValues);
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, proxy.Window.Item as BinaryAssetItem);
layout.Space(5);
var reimportButton = group.Button("Reimport");
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport();
}
}

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.IO;
using System.Xml;
using FlaxEditor.Content;
using FlaxEditor.Content.Import;
@@ -100,7 +101,10 @@ namespace FlaxEditor.Windows.Assets
base.Initialize(layout);
layout.Space(10);
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, window.Item as BinaryAssetItem);
layout.Space(5);
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport();
}

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.IO;
using System.Xml;
using FlaxEditor.Content;
using FlaxEditor.Content.Import;
@@ -53,7 +54,10 @@ namespace FlaxEditor.Windows.Assets
base.Initialize(layout);
layout.Space(10);
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, window.Item as BinaryAssetItem);
layout.Space(5);
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport();
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@@ -758,8 +759,11 @@ namespace FlaxEditor.Windows.Assets
var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings };
group.Object(importSettingsValues);
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, proxy.Window.Item as BinaryAssetItem);
layout.Space(5);
var reimportButton = group.Button("Reimport");
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((ImportPropertiesProxy)Values[0]).Reimport();
}
}

View File

@@ -1023,8 +1023,11 @@ namespace FlaxEditor.Windows.Assets
var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings };
group.Object(importSettingsValues);
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, proxy.Window.Item as BinaryAssetItem);
layout.Space(5);
var reimportButton = group.Button("Reimport");
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((ImportPropertiesProxy)Values[0]).Reimport();
}
}

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.IO;
using System.Linq;
using System.Xml;
using FlaxEditor.Content;
@@ -110,9 +111,19 @@ namespace FlaxEditor.Windows.Assets
{
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (PropertiesProxy)Values[0];
if (proxy._window == null)
{
layout.Label("Loading...", TextAlignment.Center);
return;
}
base.Initialize(layout);
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, proxy._window.Item as BinaryAssetItem);
layout.Space(10);
layout.Space(5);
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport();
}

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.IO;
using System.Xml;
using FlaxEditor.Content;
using FlaxEditor.Content.Import;
@@ -134,11 +135,21 @@ namespace FlaxEditor.Windows.Assets
{
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (ImportPropertiesProxy)Values[0];
if (proxy._window == null)
{
layout.Label("Loading...", TextAlignment.Center);
return;
}
// Import settings
base.Initialize(layout);
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, proxy._window.Item as BinaryAssetItem);
// Reimport
layout.Space(10);
layout.Space(5);
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((ImportPropertiesProxy)Values[0]).Reimport();
}

View File

@@ -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;

View File

@@ -175,7 +175,7 @@ namespace FlaxEditor.Windows.Profiler
private string FormatCellBytes(object x)
{
return Utilities.Utils.FormatBytesCount((ulong)x);
return Utilities.Utils.FormatBytesCount(Convert.ToUInt64(x));
}
/// <inheritdoc />

View File

@@ -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;

View File

@@ -35,7 +35,7 @@ void MaterialBase::SetParameterValue(const StringView& name, const Variant& valu
}
else if (warnIfMissing)
{
LOG(Warning, "Missing material parameter '{0}' in material {1}", String(name), ToString());
LOG(Warning, "Missing material parameter '{0}' in material {1}", name, ToString());
}
}

View File

@@ -65,7 +65,7 @@ public:
/// </summary>
/// <param name="name">The parameter name.</param>
/// <param name="value">The value to set.</param>
/// <param name="warnIfMissing">True if warn if parameter is missing, otherwise will do nothing.</param>
/// <param name="warnIfMissing">True to warn if parameter is missing, otherwise will do nothing.</param>
API_FUNCTION() void SetParameterValue(const StringView& name, const Variant& value, bool warnIfMissing = true);
/// <summary>

View File

@@ -3439,6 +3439,16 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
default:
return false;
}
case VariantType::Null:
switch (to.Type)
{
case VariantType::Asset:
case VariantType::ManagedObject:
case VariantType::Object:
return true;
default:
return false;
}
default:
return false;
}
@@ -3912,6 +3922,23 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
default: ;
}
break;
case VariantType::Null:
switch (to.Type)
{
case VariantType::Asset:
return Variant((Asset*)nullptr);
case VariantType::Object:
return Variant((ScriptingObject*)nullptr);
case VariantType::ManagedObject:
{
Variant result;
result.SetType(VariantType(VariantType::ManagedObject));
result.MANAGED_GC_HANDLE = 0;
return result;
}
default:
return false;
}
default: ;
}
LOG(Error, "Cannot cast Variant from {0} to {1}", v.Type, to);

View File

@@ -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);

View File

@@ -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)

View File

@@ -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

View File

@@ -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
}

View File

@@ -214,9 +214,11 @@ void MaterialParameter::SetValue(const Variant& value)
break;
case VariantType::Object:
_asAsset = Cast<TextureBase>(value.AsObject);
invalidType = _asAsset == nullptr && value.AsObject != nullptr;
break;
case VariantType::Asset:
_asAsset = Cast<TextureBase>(value.AsAsset);
invalidType = _asAsset == nullptr && value.AsAsset != nullptr;
break;
default:
invalidType = true;
@@ -239,6 +241,7 @@ void MaterialParameter::SetValue(const Variant& value)
break;
case VariantType::Object:
_asGPUTexture = Cast<GPUTexture>(value.AsObject);
invalidType = _asGPUTexture == nullptr && value.AsObject != nullptr;
break;
default:
invalidType = true;
@@ -258,9 +261,11 @@ void MaterialParameter::SetValue(const Variant& value)
break;
case VariantType::Object:
_asAsset = Cast<GameplayGlobals>(value.AsObject);
invalidType = _asAsset == nullptr && value.AsObject != nullptr;
break;
case VariantType::Asset:
_asAsset = Cast<GameplayGlobals>(value.AsAsset);
invalidType = _asAsset == nullptr && value.AsAsset != nullptr;
break;
default:
invalidType = true;
@@ -273,7 +278,7 @@ void MaterialParameter::SetValue(const Variant& value)
}
if (invalidType)
{
LOG(Error, "Invalid material parameter value type {0} to set (param type: {1})", value.Type, ScriptingEnum::ToString(_type));
LOG(Error, "Invalid material parameter value '{}' of type '{}' to set (expected type: {})", value.ToString(), value.Type, ScriptingEnum::ToString(_type));
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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.");

View File

@@ -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

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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]()
{

View File

@@ -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;

View File

@@ -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())

View File

@@ -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>

View File

@@ -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;

View File

@@ -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>

View File

@@ -292,7 +292,12 @@ void AnimatedModel::SetParameterValue(const StringView& name, const Variant& val
{
if (param.Name == name)
{
param.Value = value;
if (param.Value.Type == value.Type)
param.Value = value;
else if (Variant::CanCast(value, param.Value.Type))
param.Value = Variant::Cast(value, param.Value.Type);
else
LOG(Warning, "Animation Graph parameter '{0}' in AnimatedModel {1} is type '{2}' and not type '{3}'.", name, ToString(), param.Value.Type, value.Type);
return;
}
}

View File

@@ -19,7 +19,7 @@ API_CLASS(Attributes="HideInEditor") class FLAXENGINE_API ModelPrefab : public S
/// <summary>
/// Source model file path (absolute or relative to the project).
/// </summary>
API_FIELD(Attributes="ReadOnly") String ImportPath;
API_FIELD(Attributes="ReadOnly, HideInEditor") String ImportPath;
/// <summary>
/// Model file import settings.

View File

@@ -108,6 +108,12 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API
API_FIELD(Attributes="EditorOrder(500), EditorDisplay(\"General\")")
TextureQuality TexturesQuality = TextureQuality::ASTC_Medium;
/// <summary>
/// Whether to build Android App Bundle (aab) side by side with apk.
/// </summary>
API_FIELD(Attributes="EditorOrder(500), EditorDisplay(\"General\", \"Build .aab\")")
bool BuildAAB = true;
/// <summary>
/// Custom icon texture to use for the application (overrides the default one).
/// </summary>

View 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,
};

View File

@@ -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;

View File

@@ -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

View 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;
};

View File

@@ -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");

View File

@@ -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>

View File

@@ -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"

View File

@@ -21,7 +21,7 @@ namespace FlaxEngine
AllowMinimize = true,
AllowMaximize = true,
AllowDragAndDrop = true,
IsRegularWindow = true,
Type = WindowType.Regular,
HasSizingFrame = true,
ShowAfterFirstPaint = true,
};

View File

@@ -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.

View File

@@ -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"

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -71,7 +71,7 @@ GDKWindow::GDKWindow(const CreateWindowSettings& settings)
// Creating the window
_handle = CreateWindowExW(
exStyle,
Platform::ApplicationWindowClass,
Platform::ApplicationClassName,
settings.Title.GetText(),
style,
x,

View File

@@ -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

View File

@@ -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,18 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
StringAnsi dataAnsi(data);
LinuxDropTextData dropData;
dropData.Text = data;
#if !PLATFORM_SDL
unsigned long mainWindow = _window;
#else
unsigned long mainWindow = (unsigned long)_handle;
#endif
// 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 +1515,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 +1544,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 +1579,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 +1622,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 +1669,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 +1695,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 +1718,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 +1747,11 @@ Array<String> LinuxClipboard::GetFiles()
return Array<String>();
}
#endif
void* LinuxPlatform::GetXDisplay()
{
return xDisplay;
return xDisplay;
}
bool LinuxPlatform::CreateMutex(const Char* name)
@@ -2090,6 +2112,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 +2122,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 +2141,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 +2288,7 @@ bool LinuxPlatform::Init()
Input::Mouse = Impl::Mouse = New<LinuxMouse>();
Input::Keyboard = Impl::Keyboard = New<LinuxKeyboard>();
LinuxInput::Init();
#endif
return false;
}
@@ -2269,6 +2298,7 @@ void LinuxPlatform::BeforeRun()
void LinuxPlatform::Tick()
{
#if !PLATFORM_SDL
UnixPlatform::Tick();
LinuxInput::UpdateState();
@@ -2285,9 +2315,9 @@ void LinuxPlatform::Tick()
continue;
// External event handling
xEventRecieved(&event);
xEventReceived(&event);
LinuxWindow* window;
Window* window;
switch (event.type)
{
case ClientMessage:
@@ -2619,6 +2649,7 @@ void LinuxPlatform::Tick()
}
//X11::XFlush(xDisplay);
#endif
}
void LinuxPlatform::BeforeExit()
@@ -2627,6 +2658,7 @@ void LinuxPlatform::BeforeExit()
void LinuxPlatform::Exit()
{
#if !PLATFORM_SDL
for (int32 i = 0; i < (int32)CursorType::MAX; i++)
{
if (Cursors[i])
@@ -2652,8 +2684,10 @@ void LinuxPlatform::Exit()
X11::XCloseDisplay(xDisplay);
xDisplay = nullptr;
}
#endif
}
#if !PLATFORM_SDL
int32 LinuxPlatform::GetDpi()
{
return SystemDpi;
@@ -2663,6 +2697,7 @@ String LinuxPlatform::GetUserLocaleName()
{
return UserLocale;
}
#endif
String LinuxPlatform::GetComputerName()
{
@@ -2870,10 +2905,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;

View File

@@ -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);

Some files were not shown because too many files have changed in this diff Show More