Compare commits
138 Commits
master_fix
...
d955303df7
| Author | SHA1 | Date | |
|---|---|---|---|
| d955303df7 | |||
| 6ca949e70b | |||
| afecf984e3 | |||
| 85fad2b6bf | |||
| aee1fd285e | |||
| 9b464c6530 | |||
| 3ea807468e | |||
| 0f135597fa | |||
| d70a003617 | |||
| b443b74d18 | |||
| fc341a86e7 | |||
| 68da28ffe8 | |||
|
|
427e76e76e | ||
| b183b5bcfc | |||
| 51feaa0730 | |||
| 8e69aa8bd6 | |||
| 90b2fded48 | |||
| 9125ffeb9e | |||
| 28980e5fbf | |||
| aab0d772a4 | |||
| 6296e1a9eb | |||
| 1586bb0702 | |||
| edeaf6af09 | |||
| 951edd95db | |||
| 9951211596 | |||
| 6d337464f7 | |||
| d0b552d74a | |||
| 83512822b1 | |||
| ccb78103ec | |||
| 6e79ffea34 | |||
| 4f03d37a17 | |||
| 6fb8419b3c | |||
| a4272d6ca9 | |||
| 4654117d5e | |||
| 6ff260d052 | |||
| 88d2b72822 | |||
| 29868531ad | |||
| 95dfb6fdc6 | |||
| 8df3999f85 | |||
| 149b189629 | |||
| b1fd86e6b5 | |||
| 5e8fdf879d | |||
| 84ada18299 | |||
| 635fe8017e | |||
| 834380ff05 | |||
| dd65fc2289 | |||
| 86125bb625 | |||
| 09a9d8b380 | |||
| 86b223ec93 | |||
| 78f6080321 | |||
| c7be6f6e0e | |||
| c40c31fbb7 | |||
| f5fbc1e32d | |||
| 0a20378acd | |||
| d4e87877b6 | |||
| 7eb7236b8a | |||
| dc0e4ffce2 | |||
| 86fe93943d | |||
| 3258113ef8 | |||
| ebf3999cfe | |||
| a1ccbbb5b9 | |||
| 35072c40d8 | |||
| 5c51021388 | |||
| 95fd527515 | |||
| 113d851cea | |||
| 72f0c460f9 | |||
| 13ddd15b44 | |||
| c9e24aaf5f | |||
| 68c0ac0ffc | |||
| bebda275a9 | |||
| 287eaae850 | |||
| 7e59c3b9a7 | |||
| 38658a5b8c | |||
| 88c75b8672 | |||
| 17ab1e6830 | |||
| 1f45110e5f | |||
| 5401166a07 | |||
| bfa8188782 | |||
| e777a71784 | |||
| c13e91a0d0 | |||
| aac5d57352 | |||
| ceca13c7b6 | |||
| 2905470330 | |||
| 2c17033822 | |||
| 0a4cb9e9b1 | |||
| 096651f4c1 | |||
| 275a08506b | |||
| 4cd61cb381 | |||
| 9ad1147581 | |||
| 35e09def1b | |||
| 0469607a71 | |||
| d115d22ee6 | |||
| 8120ae621d | |||
| f873c2fa9f | |||
| c3ffbe2f42 | |||
| 55a0a39881 | |||
| 592215dd30 | |||
| ef0c2a2785 | |||
| 37979e452a | |||
| e9671bb727 | |||
| aa328bd591 | |||
| 188e4313f9 | |||
| df02c70e31 | |||
| 257f54b323 | |||
| fb4b5b2575 | |||
| 4e1251276d | |||
| a7b200dc57 | |||
| 8cadbae80e | |||
| c3bae49aae | |||
|
|
6fa4fc6149 | ||
| f1ffe1acaa | |||
| 5c9ddf7f00 | |||
| 431767a150 | |||
| 8ba4e442a6 | |||
| 2f8d19c267 | |||
| f2fd98f742 | |||
| 3ce35d6e81 | |||
| 796dbaa836 | |||
| 84b209ec7f | |||
| fa976b34dc | |||
| e7cda362b7 | |||
| a41a09c162 | |||
| e82f84f0ab | |||
| f1d387ceea | |||
| 3893d4d1f8 | |||
| cf7ac50faf | |||
| 3f6bf15554 | |||
| dac74829b4 | |||
| 94d6f213a0 | |||
| 8f2550ef61 | |||
| b622a1cc5e | |||
| c83a3c32c7 | |||
| e7dcf7f7e7 | |||
| 398785a2be | |||
| 05dba0f1f5 | |||
| 39dbd32b2e | |||
| 23a34a455a | |||
| 4308328b48 |
2
.github/ISSUE_TEMPLATE/1-bug.yaml
vendored
2
.github/ISSUE_TEMPLATE/1-bug.yaml
vendored
@@ -31,7 +31,7 @@ body:
|
||||
- '1.10'
|
||||
- '1.11'
|
||||
- master branch
|
||||
default: 3
|
||||
default: 2
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
4
.github/workflows/build_ios.yml
vendored
4
.github/workflows/build_ios.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- name: Setup .NET Workload
|
||||
run: |
|
||||
dotnet workload install ios
|
||||
@@ -33,4 +33,4 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Build
|
||||
run: |
|
||||
./Development/Scripts/Mac/CallBuildTool.sh -build -log -dotnet=9 -arch=ARM64 -platform=iOS -configuration=Release -buildtargets=FlaxGame
|
||||
./Development/Scripts/Mac/CallBuildTool.sh -build -log -dotnet=8 -arch=ARM64 -platform=iOS -configuration=Release -buildtargets=FlaxGame
|
||||
|
||||
4
.github/workflows/build_linux.yml
vendored
4
.github/workflows/build_linux.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev libwayland-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
|
||||
- name: Build
|
||||
run: |
|
||||
./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8
|
||||
|
||||
BIN
Content/Editor/Camera/M_Camera.flax
(Stored with Git LFS)
BIN
Content/Editor/Camera/M_Camera.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/CubeTexturePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/CubeTexturePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/DDGIDebugProbes.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/DDGIDebugProbes.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Decal.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Decal.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Particle.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Particle.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Surface.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Surface.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Terrain.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Terrain.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DefaultFontMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/DefaultFontMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/FoliageBrushMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/FoliageBrushMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialWire.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/MaterialWire.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/SelectionOutlineMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/SelectionOutlineMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Highlight Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Highlight Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Icons/IconsMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Icons/IconsMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/IesProfilePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/IesProfilePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
@@ -6,7 +6,6 @@
|
||||
@3
|
||||
|
||||
#include "./Flax/Common.hlsl"
|
||||
#include "./Flax/Stencil.hlsl"
|
||||
#include "./Flax/MaterialCommon.hlsl"
|
||||
#include "./Flax/GBufferCommon.hlsl"
|
||||
@7
|
||||
@@ -15,13 +14,10 @@ META_CB_BEGIN(0, Data)
|
||||
float4x4 WorldMatrix;
|
||||
float4x4 InvWorld;
|
||||
float4x4 SvPositionToWorld;
|
||||
float3 Padding0;
|
||||
uint RenderLayersMask;
|
||||
@1META_CB_END
|
||||
|
||||
// Use depth buffer for per-pixel decal layering
|
||||
Texture2D DepthBuffer : register(t0);
|
||||
Texture2D<uint2> StencilBuffer : register(t1);
|
||||
|
||||
// Material shader resources
|
||||
@2
|
||||
@@ -204,14 +200,6 @@ void PS_Decal(
|
||||
#endif
|
||||
)
|
||||
{
|
||||
// Stencil masking
|
||||
uint stencilObjectLayer = STENCIL_BUFFER_OBJECT_LAYER(STENCIL_BUFFER_LOAD(StencilBuffer, SvPosition.xy));
|
||||
if ((RenderLayersMask & (1 << stencilObjectLayer)) == 0)
|
||||
{
|
||||
clip(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
float2 screenUV = SvPosition.xy * ScreenSize.zw;
|
||||
SvPosition.z = SAMPLE_RT(DepthBuffer, screenUV).r;
|
||||
|
||||
|
||||
@@ -29,13 +29,6 @@ Buffer<float4> ShadowsBuffer : register(t__SRV__);
|
||||
Texture2D<float> ShadowMap : register(t__SRV__);
|
||||
Texture3D VolumetricFogTexture : register(t__SRV__);
|
||||
@4// Forward Shading: Utilities
|
||||
// Public accessors for lighting data, use them as data binding might change but those methods will remain.
|
||||
LightData GetDirectionalLight() { return DirectionalLight; }
|
||||
LightData GetSkyLight() { return SkyLight; }
|
||||
ProbeData GetEnvironmentProbe() { return EnvironmentProbe; }
|
||||
ExponentialHeightFogData GetExponentialHeightFog() { return ExponentialHeightFog; }
|
||||
uint GetLocalLightsCount() { return LocalLightsCount; }
|
||||
LightData GetLocalLight(uint i) { return LocalLights[i]; }
|
||||
@5// Forward Shading: Shaders
|
||||
|
||||
// Pixel Shader function for Forward Pass
|
||||
@@ -84,8 +77,9 @@ void PS_Forward(
|
||||
gBuffer.ShadingModel = MATERIAL_SHADING_MODEL;
|
||||
|
||||
// Calculate lighting from a single directional light
|
||||
float4 shadowMask = 1.0f;
|
||||
ShadowSample shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, gBuffer);
|
||||
float4 shadowMask = GetShadowMask(shadow);
|
||||
shadowMask = GetShadowMask(shadow);
|
||||
float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false);
|
||||
|
||||
// Calculate lighting from sky light
|
||||
@@ -152,13 +146,7 @@ void PS_Forward(
|
||||
|
||||
#if USE_FOG && MATERIAL_SHADING_MODEL != SHADING_MODEL_UNLIT
|
||||
// Calculate exponential height fog
|
||||
#if DIRECTX && FEATURE_LEVEL < FEATURE_LEVEL_SM6
|
||||
// TODO: fix D3D11/D3D10 bug with incorrect distance
|
||||
float fogSceneDistance = distance(materialInput.WorldPosition, ViewPos);
|
||||
#else
|
||||
float fogSceneDistance = gBuffer.ViewPos.z;
|
||||
#endif
|
||||
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0, fogSceneDistance);
|
||||
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0, gBuffer.ViewPos.z);
|
||||
|
||||
if (ExponentialHeightFog.VolumetricFogMaxDistance > 0)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ float4 ViewInfo;
|
||||
float4 ScreenSize;
|
||||
float4 ViewSize;
|
||||
float3 ViewPadding0;
|
||||
float ScaledTimeParam;
|
||||
float UnscaledTimeParam;
|
||||
@1META_CB_END
|
||||
|
||||
// Shader resources
|
||||
|
||||
@@ -20,7 +20,7 @@ float4 ScreenSize;
|
||||
float4 TemporalAAJitter;
|
||||
float4x4 InverseViewProjectionMatrix;
|
||||
float3 ViewPadding0;
|
||||
float ScaledTimeParam;
|
||||
float UnscaledTimeParam;
|
||||
@1META_CB_END
|
||||
|
||||
// Shader resources
|
||||
|
||||
BIN
Content/Editor/Particles/Particle Material Color.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Particle Material Color.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Particles/Smoke Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Smoke Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Primitives/Cube.flax
(Stored with Git LFS)
BIN
Content/Editor/Primitives/Cube.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/SpriteMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/SpriteMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Terrain/Circle Brush Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Terrain/Circle Brush Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Terrain/Highlight Terrain Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Terrain/Highlight Terrain Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/TexturePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/TexturePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Wires Debug Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Wires Debug Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultDeformableMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultDeformableMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultRadialMenu.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultRadialMenu.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultTerrainMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultTerrainMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/SingleColorMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/SingleColorMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/SkyboxMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/SkyboxMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/Textures/PreIntegratedGF.flax
(Stored with Git LFS)
BIN
Content/Engine/Textures/PreIntegratedGF.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/BitonicSort.flax
(Stored with Git LFS)
BIN
Content/Shaders/BitonicSort.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/ColorGrading.flax
(Stored with Git LFS)
BIN
Content/Shaders/ColorGrading.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/GI/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
BIN
Content/Shaders/GI/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/GPUParticlesSorting.flax
(Stored with Git LFS)
BIN
Content/Shaders/GPUParticlesSorting.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/GlobalSignDistanceField.flax
(Stored with Git LFS)
BIN
Content/Shaders/GlobalSignDistanceField.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/MotionBlur.flax
(Stored with Git LFS)
BIN
Content/Shaders/MotionBlur.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/PostProcessing.flax
(Stored with Git LFS)
BIN
Content/Shaders/PostProcessing.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/ProbesFilter.flax
(Stored with Git LFS)
BIN
Content/Shaders/ProbesFilter.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/Reflections.flax
(Stored with Git LFS)
BIN
Content/Shaders/Reflections.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/SDF.flax
(Stored with Git LFS)
BIN
Content/Shaders/SDF.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/SSR.flax
(Stored with Git LFS)
BIN
Content/Shaders/SSR.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/VolumetricFog.flax
(Stored with Git LFS)
BIN
Content/Shaders/VolumetricFog.flax
(Stored with Git LFS)
Binary file not shown.
@@ -2,9 +2,9 @@
|
||||
"Name": "Flax",
|
||||
"Version": {
|
||||
"Major": 1,
|
||||
"Minor": 11,
|
||||
"Minor": 10,
|
||||
"Revision": 0,
|
||||
"Build": 6805
|
||||
"Build": 6705
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.",
|
||||
@@ -13,6 +13,7 @@
|
||||
"Configuration": {
|
||||
"UseCSharp": true,
|
||||
"UseLargeWorlds": false,
|
||||
"UseDotNet": true
|
||||
"UseDotNet": true,
|
||||
"UseSDL": true
|
||||
}
|
||||
}
|
||||
10
README.md
10
README.md
@@ -49,7 +49,7 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Fedora: `sudo dnf install dotnet-sdk-8.0`
|
||||
* Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host`
|
||||
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
|
||||
* Ubuntu: `sudo apt install vulkan-sdk` (deprecated, follow official docs)
|
||||
* Ubuntu: `sudo apt install vulkan-sdk`
|
||||
* Fedora: `sudo dnf install vulkan-headers vulkan-tools vulkan-validation-layers`
|
||||
* Arch: `sudo pacman -S vulkan-headers vulkan-tools vulkan-validation-layers`
|
||||
* Install Git with LFS
|
||||
@@ -57,10 +57,10 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Arch: `sudo pacman -S git git-lfs`
|
||||
* `git-lfs install`
|
||||
* Install the required packages:
|
||||
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev`
|
||||
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel`
|
||||
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib`
|
||||
* Install Clang compiler (version 14 or later):
|
||||
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev zenity wayland-protocols libportal-dev`
|
||||
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel zenity wayland-protocols-devel libportal`
|
||||
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib zenity wayland-protocols libportal`
|
||||
* Install Clang compiler (version 6 or later):
|
||||
* Ubuntu: `sudo apt-get install clang lldb lld`
|
||||
* Fedora: `sudo dnf install clang llvm lldb lld`
|
||||
* Arch: `sudo pacman -S clang lldb lld`
|
||||
|
||||
@@ -174,9 +174,7 @@ void EditorAnalytics::StartSession()
|
||||
// Bind events
|
||||
GameCooker::OnEvent.Bind<RegisterGameCookingStart>();
|
||||
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Bind<RegisterLightmapsBuildingStart>();
|
||||
#if LOG_ENABLE
|
||||
Log::Logger::OnError.Bind<RegisterError>();
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditorAnalytics::EndSession()
|
||||
@@ -189,9 +187,7 @@ void EditorAnalytics::EndSession()
|
||||
// Unbind events
|
||||
GameCooker::OnEvent.Unbind<RegisterGameCookingStart>();
|
||||
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Unbind<RegisterLightmapsBuildingStart>();
|
||||
#if LOG_ENABLE
|
||||
Log::Logger::OnError.Unbind<RegisterError>();
|
||||
#endif
|
||||
|
||||
// End session
|
||||
{
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace FlaxEditor.Content
|
||||
|
||||
if (data is DragDataFiles)
|
||||
return DragDropEffect.Copy;
|
||||
return _dragOverItems.Effect;
|
||||
return _dragOverItems?.Effect ?? DragDropEffect.None;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string TypeDescription => Path.EndsWith(".h") ? "C++ Header File" : "C++ Source Code";
|
||||
public override string TypeDescription => Path.EndsWith(".h") || Path.EndsWith(".hpp") ? "C++ Header File" : "C++ Source Code";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CPPScript128;
|
||||
|
||||
@@ -281,13 +281,6 @@ namespace FlaxEditor.Content
|
||||
|
||||
private void CacheData()
|
||||
{
|
||||
if (!_asset)
|
||||
{
|
||||
_parameters = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
_methods = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
_attributes = Utils.GetEmptyArray<Attribute>();
|
||||
return;
|
||||
}
|
||||
if (_parameters != null)
|
||||
return;
|
||||
if (_asset.WaitForLoaded())
|
||||
@@ -351,13 +344,13 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => _asset ? Path.GetFileNameWithoutExtension(_asset.Path) : null;
|
||||
public string Name => Path.GetFileNameWithoutExtension(_asset.Path);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Namespace => string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string TypeName => _asset ? JsonSerializer.GetStringID(_asset.ID) : null;
|
||||
public string TypeName => JsonSerializer.GetStringID(_asset.ID);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPublic => true;
|
||||
|
||||
@@ -130,11 +130,6 @@ namespace FlaxEditor.Content
|
||||
eyeAdaptation.Mode = EyeAdaptationMode.None;
|
||||
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
|
||||
preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
|
||||
|
||||
var antiAliasing = preview.PostFxVolume.AntiAliasing;
|
||||
antiAliasing.Mode = AntialiasingMode.FastApproximateAntialiasing;
|
||||
antiAliasing.OverrideFlags |= AntiAliasingSettingsOverride.Mode;
|
||||
preview.PostFxVolume.AntiAliasing = antiAliasing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,13 @@ class PlatformTools;
|
||||
#define GAME_BUILD_DOTNET_RUNTIME_MAX_VER 9
|
||||
#endif
|
||||
|
||||
#if OFFICIAL_BUILD
|
||||
// Use the fixed .NET SDK version in packaged builds for compatibility (FlaxGame is precompiled with it)
|
||||
#define GAME_BUILD_DOTNET_VER TEXT("-dotnet=" MACRO_TO_STR(GAME_BUILD_DOTNET_RUNTIME_MIN_VER))
|
||||
#else
|
||||
#define GAME_BUILD_DOTNET_VER TEXT("")
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Game building options. Used as flags.
|
||||
/// </summary>
|
||||
@@ -367,8 +374,6 @@ public:
|
||||
/// </summary>
|
||||
void GetBuildPlatformName(const Char*& platform, const Char*& architecture) const;
|
||||
|
||||
String GetDotnetCommandArg() const;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
|
||||
#include "Engine/Content/JsonAsset.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#if PLATFORM_TOOLS_WINDOWS
|
||||
#include "Platform/Windows/WindowsPlatformTools.h"
|
||||
#include "Engine/Platform/Windows/WindowsPlatformSettings.h"
|
||||
@@ -312,14 +311,6 @@ void CookingData::GetBuildPlatformName(const Char*& platform, const Char*& archi
|
||||
}
|
||||
}
|
||||
|
||||
String CookingData::GetDotnetCommandArg() const
|
||||
{
|
||||
int32 version = Tools->GetDotnetVersion();
|
||||
if (version == 0)
|
||||
return String::Empty;
|
||||
return String::Format(TEXT("-dotnet={}"), version);
|
||||
}
|
||||
|
||||
void CookingData::StepProgress(const String& info, const float stepProgress) const
|
||||
{
|
||||
const float singleStepProgress = 1.0f / (StepsCount + 1);
|
||||
@@ -389,7 +380,6 @@ bool GameCooker::IsCancelRequested()
|
||||
|
||||
PlatformTools* GameCooker::GetTools(BuildPlatform platform)
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
PlatformTools* result = nullptr;
|
||||
if (!Tools.TryGet(platform, result))
|
||||
{
|
||||
@@ -481,7 +471,6 @@ bool GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration,
|
||||
LOG(Error, "Build platform {0} is not supported.", ::ToString(platform));
|
||||
return true;
|
||||
}
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
// Setup
|
||||
CancelFlag = 0;
|
||||
@@ -635,7 +624,6 @@ void GameCookerImpl::ReportProgress(const String& info, float totalProgress)
|
||||
|
||||
void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
if (Internal_OnCollectAssets == nullptr)
|
||||
{
|
||||
auto c = GameCooker::GetStaticClass();
|
||||
@@ -663,7 +651,6 @@ void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
|
||||
|
||||
bool GameCookerImpl::Build()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
CookingData& data = *Data;
|
||||
LOG(Info, "Starting Game Cooker...");
|
||||
LOG(Info, "Platform: {0}, Configuration: {2}, Options: {1}", ::ToString(data.Platform), (int32)data.Options, ::ToString(data.Configuration));
|
||||
@@ -683,7 +670,8 @@ bool GameCookerImpl::Build()
|
||||
|
||||
MCore::Thread::Attach();
|
||||
|
||||
// Build start
|
||||
// Build Started
|
||||
if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook))
|
||||
{
|
||||
CallEvent(GameCooker::EventType::BuildStarted);
|
||||
data.Tools->OnBuildStarted(data);
|
||||
@@ -756,8 +744,8 @@ bool GameCookerImpl::Build()
|
||||
}
|
||||
IsRunning = false;
|
||||
CancelFlag = 0;
|
||||
if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook))
|
||||
{
|
||||
// Build end
|
||||
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
|
||||
Steps[stepIndex]->OnBuildEnded(data, failed);
|
||||
data.Tools->OnBuildEnded(data, failed);
|
||||
@@ -790,8 +778,6 @@ int32 GameCookerImpl::ThreadFunction()
|
||||
|
||||
bool GameCookerService::Init()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
auto editorAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
|
||||
editorAssembly->Unloading.Bind(OnEditorAssemblyUnloading);
|
||||
GameCooker::OnCollectAssets.Bind(OnCollectAssets);
|
||||
@@ -803,7 +789,6 @@ void GameCookerService::Update()
|
||||
{
|
||||
if (IsRunning)
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
ScopeLock lock(ProgressLocker);
|
||||
|
||||
if (ProgressMsg.HasChars())
|
||||
|
||||
@@ -15,32 +15,26 @@
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Editor/Utilities/EditorUtilities.h"
|
||||
|
||||
String GetGDK()
|
||||
GDKPlatformTools::GDKPlatformTools()
|
||||
{
|
||||
String gdk;
|
||||
Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), gdk);
|
||||
if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk))
|
||||
// Find GDK
|
||||
Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), _gdkPath);
|
||||
if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath))
|
||||
{
|
||||
gdk.Clear();
|
||||
Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), gdk);
|
||||
if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk))
|
||||
_gdkPath.Clear();
|
||||
Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), _gdkPath);
|
||||
if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath))
|
||||
{
|
||||
gdk.Clear();
|
||||
_gdkPath.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gdk.EndsWith(TEXT("GRDK\\")))
|
||||
gdk.Remove(gdk.Length() - 6);
|
||||
else if (gdk.EndsWith(TEXT("GRDK")))
|
||||
gdk.Remove(gdk.Length() - 5);
|
||||
if (_gdkPath.EndsWith(TEXT("GRDK\\")))
|
||||
_gdkPath.Remove(_gdkPath.Length() - 6);
|
||||
else if (_gdkPath.EndsWith(TEXT("GRDK")))
|
||||
_gdkPath.Remove(_gdkPath.Length() - 5);
|
||||
}
|
||||
}
|
||||
return gdk;
|
||||
}
|
||||
|
||||
GDKPlatformTools::GDKPlatformTools()
|
||||
{
|
||||
_gdkPath = GetGDK();
|
||||
}
|
||||
|
||||
DotNetAOTModes GDKPlatformTools::UseAOT() const
|
||||
@@ -127,7 +121,7 @@ bool GDKPlatformTools::OnPostProcess(CookingData& data, GDKPlatformSettings* pla
|
||||
validName.Add('\0');
|
||||
|
||||
sb.Append(TEXT("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"));
|
||||
sb.Append(TEXT("<Game configVersion=\"1\">\n"));
|
||||
sb.Append(TEXT("<Game configVersion=\"0\">\n"));
|
||||
sb.AppendFormat(TEXT(" <Identity Name=\"{0}\" Publisher=\"{1}\" Version=\"{2}\"/>\n"),
|
||||
validName.Get(),
|
||||
platformSettings->PublisherName.HasChars() ? platformSettings->PublisherName : TEXT("CN=") + gameSettings->CompanyName,
|
||||
@@ -201,9 +195,4 @@ bool GDKPlatformTools::OnPostProcess(CookingData& data, GDKPlatformSettings* pla
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 GDKPlatformTools::GetDotnetVersion() const
|
||||
{
|
||||
return GAME_BUILD_DOTNET_RUNTIME_MIN_VER;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,7 +26,6 @@ public:
|
||||
public:
|
||||
|
||||
// [PlatformTools]
|
||||
int32 GetDotnetVersion() const override;
|
||||
DotNetAOTModes UseAOT() const override;
|
||||
bool OnDeployBinaries(CookingData& data) override;
|
||||
};
|
||||
|
||||
@@ -186,7 +186,7 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
|
||||
ADD_ENTRY("CFBundlePackageType", "APPL");
|
||||
ADD_ENTRY("NSPrincipalClass", "NSApplication");
|
||||
ADD_ENTRY("LSApplicationCategoryType", "public.app-category.games");
|
||||
ADD_ENTRY("LSMinimumSystemVersion", "13");
|
||||
ADD_ENTRY("LSMinimumSystemVersion", "10.15");
|
||||
ADD_ENTRY("CFBundleIconFile", "icon.icns");
|
||||
ADD_ENTRY_STR("CFBundleExecutable", executableName);
|
||||
ADD_ENTRY_STR("CFBundleIdentifier", appIdentifier);
|
||||
@@ -231,8 +231,6 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
|
||||
LOG(Info, "Building app package...");
|
||||
{
|
||||
const String dmgPath = data.OriginalOutputPath / appName + TEXT(".dmg");
|
||||
if (FileSystem::FileExists(dmgPath))
|
||||
FileSystem::DeleteFile(dmgPath);
|
||||
CreateProcessSettings procSettings;
|
||||
procSettings.HiddenWindow = true;
|
||||
procSettings.WorkingDirectory = data.OriginalOutputPath;
|
||||
|
||||
@@ -528,9 +528,6 @@ bool WindowsPlatformTools::OnDeployBinaries(CookingData& data)
|
||||
|
||||
void WindowsPlatformTools::OnBuildStarted(CookingData& data)
|
||||
{
|
||||
if (EnumHasAllFlags(data.Options, BuildOptions::NoCook))
|
||||
return;
|
||||
|
||||
// Remove old executable
|
||||
Array<String> files;
|
||||
FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*.exe"), DirectorySearchOption::TopDirectoryOnly);
|
||||
|
||||
@@ -70,20 +70,6 @@ public:
|
||||
/// </summary>
|
||||
virtual ArchitectureType GetArchitecture() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the .Net version to use for the cooked game.
|
||||
/// </summary>
|
||||
virtual int32 GetDotnetVersion() const
|
||||
{
|
||||
#if OFFICIAL_BUILD
|
||||
// Use the fixed .NET SDK version in packaged builds for compatibility (FlaxGame is precompiled with it)
|
||||
return GAME_BUILD_DOTNET_RUNTIME_MIN_VER;
|
||||
#else
|
||||
// Use the highest version found on a system (Flax.Build will decide)
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value indicating whenever platform requires AOT (needs C# assemblies to be precompiled).
|
||||
/// </summary>
|
||||
|
||||
@@ -10,10 +10,9 @@
|
||||
#include "Engine/Serialization/JsonTools.h"
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
#include "Editor/Cooker/PlatformTools.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Editor/Utilities/EditorUtilities.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#if PLATFORM_MAC
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
@@ -128,7 +127,7 @@ bool CompileScriptsStep::DeployBinaries(CookingData& data, const String& path, c
|
||||
const String dst = dstPath / StringUtils::GetFileName(file);
|
||||
if (dst == file)
|
||||
continue;
|
||||
if (EditorUtilities::CopyFileIfNewer(dst, file))
|
||||
if (FileSystem::CopyFile(dst, file))
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to copy file from {0} to {1}."), file, dst));
|
||||
return true;
|
||||
@@ -190,7 +189,7 @@ bool CompileScriptsStep::Perform(CookingData& data)
|
||||
const String logFile = data.CacheDirectory / TEXT("CompileLog.txt");
|
||||
auto args = String::Format(
|
||||
TEXT("-log -logfile=\"{4}\" -build -mutex -buildtargets={0} -platform={1} -arch={2} -configuration={3} -aotMode={5} {6}"),
|
||||
target, platform, architecture, configuration, logFile, ToString(data.Tools->UseAOT()), data.GetDotnetCommandArg());
|
||||
target, platform, architecture, configuration, logFile, ToString(data.Tools->UseAOT()), GAME_BUILD_DOTNET_VER);
|
||||
#if PLATFORM_WINDOWS
|
||||
if (data.Platform == BuildPlatform::LinuxX64)
|
||||
#elif PLATFORM_LINUX
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#include "Engine/Engine/Base/GameBase.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/Tools/TextureTool/TextureTool.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Scripting/Enums.h"
|
||||
#if PLATFORM_TOOLS_WINDOWS
|
||||
@@ -526,7 +525,6 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
|
||||
#if PLATFORM_TOOLS_XBOX_SCARLETT
|
||||
case BuildPlatform::XboxScarlett:
|
||||
{
|
||||
options.Platform = PlatformType::XboxScarlett;
|
||||
const char* platformDefineName = "PLATFORM_XBOX_SCARLETT";
|
||||
COMPILE_PROFILE(DirectX_SM6, SHADER_FILE_CHUNK_INTERNAL_D3D_SM6_CACHE);
|
||||
break;
|
||||
@@ -1368,10 +1366,7 @@ bool CookAssetsStep::Perform(CookingData& data)
|
||||
{
|
||||
typeName = e.TypeName;
|
||||
}
|
||||
if (e.Count == 1)
|
||||
LOG(Info, "{0}: 1 asset of total size {1}", typeName, Utilities::BytesToText(e.ContentSize));
|
||||
else
|
||||
LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize));
|
||||
LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize));
|
||||
}
|
||||
LOG(Info, "");
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
{
|
||||
// Ask Flax.Build to provide .NET SDK location for the current platform
|
||||
String sdks;
|
||||
bool failed = ScriptsBuilder::RunBuildTool(String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printSDKs {}"), data.GetDotnetCommandArg()), data.CacheDirectory);
|
||||
bool failed = ScriptsBuilder::RunBuildTool(String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printSDKs {}"), GAME_BUILD_DOTNET_VER), data.CacheDirectory);
|
||||
failed |= File::ReadAllText(data.CacheDirectory / TEXT("SDKs.txt"), sdks);
|
||||
int32 idx = sdks.Find(TEXT("DotNetSdk, "), StringSearchCase::CaseSensitive);
|
||||
if (idx != -1)
|
||||
@@ -200,7 +200,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
String sdks;
|
||||
const Char *platformName, *archName;
|
||||
data.GetBuildPlatformName(platformName, archName);
|
||||
String args = String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printDotNetRuntime -platform={} -arch={} {}"), platformName, archName, data.GetDotnetCommandArg());
|
||||
String args = String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printDotNetRuntime -platform={} -arch={} {}"), platformName, archName, GAME_BUILD_DOTNET_VER);
|
||||
bool failed = ScriptsBuilder::RunBuildTool(args, data.CacheDirectory);
|
||||
failed |= File::ReadAllText(data.CacheDirectory / TEXT("SDKs.txt"), sdks);
|
||||
Array<String> parts;
|
||||
@@ -244,13 +244,10 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
}
|
||||
if (version.IsEmpty())
|
||||
{
|
||||
int32 minVer = GAME_BUILD_DOTNET_RUNTIME_MIN_VER, maxVer = GAME_BUILD_DOTNET_RUNTIME_MAX_VER;
|
||||
if (srcDotnetFromEngine)
|
||||
{
|
||||
// Detect version from runtime files inside Engine Platform folder
|
||||
if (data.Tools->GetDotnetVersion() != 0)
|
||||
minVer = maxVer = data.Tools->GetDotnetVersion();
|
||||
for (int32 i = maxVer; i >= minVer; i--)
|
||||
for (int32 i = GAME_BUILD_DOTNET_RUNTIME_MAX_VER; i >= GAME_BUILD_DOTNET_RUNTIME_MIN_VER; i--)
|
||||
{
|
||||
// Check runtime files inside Engine Platform folder
|
||||
String testPath1 = srcDotnet / String::Format(TEXT("lib/net{}.0"), i);
|
||||
@@ -265,7 +262,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
}
|
||||
if (version.IsEmpty())
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for {} platform."), maxVer, minVer, platformName));
|
||||
data.Error(String::Format(TEXT("Failed to find supported .NET {} version for the current host platform."), GAME_BUILD_DOTNET_RUNTIME_MIN_VER));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -367,7 +364,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
const String logFile = data.CacheDirectory / TEXT("StripDotnetLibs.txt");
|
||||
String args = String::Format(
|
||||
TEXT("-log -logfile=\"{}\" -runDotNetClassLibStripping -mutex -binaries=\"{}\" {}"),
|
||||
logFile, data.DataOutputPath, data.GetDotnetCommandArg());
|
||||
logFile, data.DataOutputPath, GAME_BUILD_DOTNET_VER);
|
||||
for (const String& define : data.CustomDefines)
|
||||
{
|
||||
args += TEXT(" -D");
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
void PrecompileAssembliesStep::OnBuildStarted(CookingData& data)
|
||||
{
|
||||
const DotNetAOTModes aotMode = data.Tools->UseAOT();
|
||||
if (aotMode == DotNetAOTModes::None || EnumHasAllFlags(data.Options, BuildOptions::NoCook))
|
||||
if (aotMode == DotNetAOTModes::None)
|
||||
return;
|
||||
const auto& buildSettings = *BuildSettings::Get();
|
||||
|
||||
@@ -59,7 +59,6 @@ bool PrecompileAssembliesStep::Perform(CookingData& data)
|
||||
data.StepProgress(infoMsg, 0);
|
||||
|
||||
// Override Newtonsoft.Json with AOT-version (one that doesn't use System.Reflection.Emit)
|
||||
// TODO: remove it since EngineModule does properly reference AOT lib now
|
||||
EditorUtilities::CopyFileIfNewer(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.dll"), Globals::StartupFolder / TEXT("Source/Platforms/DotNet/AOT/Newtonsoft.Json.dll"));
|
||||
FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.xml"));
|
||||
FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.pdb"));
|
||||
@@ -70,7 +69,7 @@ bool PrecompileAssembliesStep::Perform(CookingData& data)
|
||||
const String logFile = data.CacheDirectory / TEXT("AOTLog.txt");
|
||||
String args = String::Format(
|
||||
TEXT("-log -logfile=\"{}\" -runDotNetAOT -mutex -platform={} -arch={} -configuration={} -aotMode={} -binaries=\"{}\" -intermediate=\"{}\" {}"),
|
||||
logFile, platform, architecture, configuration, ToString(aotMode), data.DataOutputPath, data.ManagedCodeOutputPath, data.GetDotnetCommandArg());
|
||||
logFile, platform, architecture, configuration, ToString(aotMode), data.DataOutputPath, data.ManagedCodeOutputPath, GAME_BUILD_DOTNET_VER);
|
||||
if (!buildSettings.SkipUnusedDotnetLibsPackaging)
|
||||
args += TEXT(" -skipUnusedDotnetLibs=false"); // Run AOT on whole class library (not just used libs)
|
||||
for (const String& define : data.CustomDefines)
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include "Engine/Core/Types/TimeSpan.h"
|
||||
#include "Engine/Core/Types/Stopwatch.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Scripting/BinaryModule.h"
|
||||
@@ -71,7 +69,6 @@ MTypeObject* CustomEditorsUtil::GetCustomEditor(MTypeObject* refType)
|
||||
|
||||
bool CustomEditorsUtilService::Init()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
TRACK_ASSEMBLY(((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly);
|
||||
Scripting::BinaryModuleLoaded.Bind(&OnBinaryModuleLoaded);
|
||||
|
||||
@@ -80,8 +77,6 @@ bool CustomEditorsUtilService::Init()
|
||||
|
||||
void OnAssemblyLoaded(MAssembly* assembly)
|
||||
{
|
||||
PROFILE_CPU_NAMED("CustomEditors.OnAssemblyLoaded");
|
||||
PROFILE_MEM(Editor);
|
||||
Stopwatch stopwatch;
|
||||
|
||||
// Prepare FlaxEngine
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -12,7 +11,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
[CustomEditor(typeof(EnvironmentProbe)), DefaultEditor]
|
||||
public class EnvironmentProbeEditor : ActorEditor
|
||||
{
|
||||
private Button _bake;
|
||||
private FlaxEngine.GUI.Button _bake;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -21,9 +20,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
if (Values.HasDifferentTypes == false)
|
||||
{
|
||||
var group = layout.Group("Bake");
|
||||
group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2);
|
||||
_bake = group.Button("Bake").Button;
|
||||
layout.Space(10);
|
||||
_bake = layout.Button("Bake").Button;
|
||||
_bake.Clicked += BakeButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,12 +190,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
foreach (var file in files)
|
||||
FindNewKeysCSharp(file, newKeys, allKeys);
|
||||
|
||||
// C++
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories);
|
||||
// C/C++
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.c", SearchOption.AllDirectories)).ToArray();
|
||||
filesCount += files.Length;
|
||||
foreach (var file in files)
|
||||
FindNewKeysCpp(file, newKeys, allKeys);
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories);
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.hpp", SearchOption.AllDirectories)).ToArray();;
|
||||
filesCount += files.Length;
|
||||
foreach (var file in files)
|
||||
FindNewKeysCpp(file, newKeys, allKeys);
|
||||
|
||||
@@ -914,11 +914,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
// Remove drop down arrows and containment lines if no objects in the group
|
||||
if (group.Children.Count == 0)
|
||||
{
|
||||
group.Panel.Close();
|
||||
group.Panel.ArrowImageOpened = null;
|
||||
group.Panel.ArrowImageClosed = null;
|
||||
group.Panel.EnableContainmentLines = false;
|
||||
group.Panel.CanOpenClose = false;
|
||||
}
|
||||
|
||||
// Scripts arrange bar
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -20,9 +19,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
if (Values.HasDifferentTypes == false)
|
||||
{
|
||||
// Add 'Bake' button
|
||||
var group = layout.Group("Bake");
|
||||
group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2);
|
||||
var button = group.Button("Bake");
|
||||
layout.Space(10);
|
||||
var button = layout.Button("Bake");
|
||||
button.Button.Clicked += BakeButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,8 +123,6 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (Picker == null)
|
||||
return;
|
||||
var differentValues = HasDifferentValues;
|
||||
Picker.DifferentValues = differentValues;
|
||||
if (!differentValues)
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = !Editor._readOnly && Editor._canResize;
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize;
|
||||
b = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
@@ -407,7 +407,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = !Editor._readOnly && Editor._canResize;
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize;
|
||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
paste.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
@@ -650,7 +650,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
panel.Panel.Size = new Float2(0, 18);
|
||||
panel.Panel.Margin = new Margin(0, 0, Utilities.Constants.UIMargin, 0);
|
||||
|
||||
var removeButton = panel.Button("-", "Remove the last item.");
|
||||
var removeButton = panel.Button("-", "Remove the last item");
|
||||
removeButton.Button.Size = new Float2(16, 16);
|
||||
removeButton.Button.Enabled = size > _minCount;
|
||||
removeButton.Button.AnchorPreset = AnchorPresets.TopRight;
|
||||
@@ -661,7 +661,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
Resize(Count - 1);
|
||||
};
|
||||
|
||||
var addButton = panel.Button("+", "Add a new item.");
|
||||
var addButton = panel.Button("+", "Add a new item");
|
||||
addButton.Button.Size = new Float2(16, 16);
|
||||
addButton.Button.Enabled = (!NotNullItems || size > 0) && size < _maxCount;
|
||||
addButton.Button.AnchorPreset = AnchorPresets.TopRight;
|
||||
|
||||
@@ -22,7 +22,7 @@ internal class UIControlRefPickerControl : FlaxObjectRefPickerControl
|
||||
/// <inheritdoc />
|
||||
protected override bool IsValid(Object obj)
|
||||
{
|
||||
return obj == null || (obj is UIControl control && ControlType.IsAssignableFrom(control.Control.GetType()));
|
||||
return obj == null || (obj is UIControl control && control.Control.GetType() == ControlType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public event Action<TypePickerControl> TypePickerValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The custom callback for types validation. Can be used to implement a rule for types to pick.
|
||||
/// The custom callback for types validation. Cane be used to implement a rule for types to pick.
|
||||
/// </summary>
|
||||
public Func<ScriptType, bool> CheckValid;
|
||||
|
||||
@@ -353,13 +353,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
if (!string.IsNullOrEmpty(typeReference.CheckMethod))
|
||||
{
|
||||
var parentEditor = ParentEditor;
|
||||
// Find actual parent editor if parent editor is collection editor
|
||||
while (parentEditor.GetType().IsAssignableTo(typeof(CollectionEditor)))
|
||||
parentEditor = parentEditor.ParentEditor;
|
||||
|
||||
var parentType = parentEditor.Values[0].GetType();
|
||||
|
||||
var parentType = ParentEditor.Values[0].GetType();
|
||||
var method = parentType.GetMethod(typeReference.CheckMethod, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (method != null)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/ShadowsOfMordor/Builder.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
#if PLATFORM_LINUX
|
||||
#include "Engine/Tools/TextureTool/TextureTool.h"
|
||||
@@ -48,7 +47,6 @@ void Editor::CloseSplashScreen()
|
||||
|
||||
bool Editor::CheckProjectUpgrade()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
const auto versionFilePath = Globals::ProjectCacheFolder / TEXT("version");
|
||||
|
||||
// Load version cache file
|
||||
@@ -268,8 +266,8 @@ bool Editor::CheckProjectUpgrade()
|
||||
// Check if last version was older
|
||||
else if (lastVersion.Major < FLAXENGINE_VERSION_MAJOR || (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor < FLAXENGINE_VERSION_MINOR))
|
||||
{
|
||||
LOG(Warning, "The project was last opened with an older editor version");
|
||||
const auto result = MessageBox::Show(TEXT("The project was last opened with an older editor version.\nLoading it may modify existing data, which can result in older editor versions being unable to open it.\n\nDo you want to perform a backup before or cancel the operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question);
|
||||
LOG(Warning, "The project was opened with the older editor version last time");
|
||||
const auto result = MessageBox::Show(TEXT("The project was opened with the older editor version last time. Loading it may modify existing data so older editor version won't open it. Do you want to perform a backup before or cancel operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question);
|
||||
if (result == DialogResult::Yes)
|
||||
{
|
||||
if (BackupProject())
|
||||
@@ -291,8 +289,8 @@ bool Editor::CheckProjectUpgrade()
|
||||
// Check if last version was newer
|
||||
else if (lastVersion.Major > FLAXENGINE_VERSION_MAJOR || (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor > FLAXENGINE_VERSION_MINOR))
|
||||
{
|
||||
LOG(Warning, "The project was last opened with a newer editor version");
|
||||
const auto result = MessageBox::Show(TEXT("The project was last opened with a newer editor version.\nLoading it may fail and corrupt existing data.\n\nDo you want to perform a backup before loading or cancel the operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "The project was opened with the newer editor version last time");
|
||||
const auto result = MessageBox::Show(TEXT("The project was opened with the newer editor version last time. Loading it may fail and corrupt existing data. Do you want to perform a backup before or cancel operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Warning);
|
||||
if (result == DialogResult::Yes)
|
||||
{
|
||||
if (BackupProject())
|
||||
@@ -368,8 +366,6 @@ bool Editor::BackupProject()
|
||||
|
||||
int32 Editor::LoadProduct()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
// Flax Editor product
|
||||
Globals::ProductName = TEXT("Flax Editor");
|
||||
Globals::CompanyName = TEXT("Flax");
|
||||
@@ -630,7 +626,6 @@ int32 Editor::LoadProduct()
|
||||
|
||||
Window* Editor::CreateMainWindow()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
Window* window = Managed->GetMainWindow();
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
@@ -667,7 +662,6 @@ bool Editor::Init()
|
||||
return true;
|
||||
}
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
// If during last lightmaps baking engine crashed we could try to restore the progress
|
||||
ShadowsOfMordor::Builder::Instance()->CheckIfRestoreState();
|
||||
@@ -699,13 +693,11 @@ bool Editor::Init()
|
||||
|
||||
void Editor::BeforeRun()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
Managed->BeforeRun();
|
||||
}
|
||||
|
||||
void Editor::BeforeExit()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
CloseSplashScreen();
|
||||
|
||||
Managed->Exit();
|
||||
@@ -716,8 +708,6 @@ void Editor::BeforeExit()
|
||||
|
||||
void EditorImpl::OnUpdate()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
// Update c# editor
|
||||
Editor::Managed->Update();
|
||||
|
||||
|
||||
@@ -1390,7 +1390,6 @@ namespace FlaxEditor
|
||||
public void BuildAllMeshesSDF()
|
||||
{
|
||||
var models = new List<Model>();
|
||||
var forceRebuild = Input.GetKey(KeyboardKeys.F);
|
||||
Scene.ExecuteOnGraph(node =>
|
||||
{
|
||||
if (node is StaticModelNode staticModelNode && staticModelNode.Actor is StaticModel staticModel)
|
||||
@@ -1400,7 +1399,7 @@ namespace FlaxEditor
|
||||
model != null &&
|
||||
!models.Contains(model) &&
|
||||
!model.IsVirtual &&
|
||||
(forceRebuild || model.SDF.Texture == null))
|
||||
model.SDF.Texture == null)
|
||||
{
|
||||
models.Add(model);
|
||||
}
|
||||
@@ -1413,17 +1412,7 @@ namespace FlaxEditor
|
||||
{
|
||||
var model = models[i];
|
||||
Log($"[{i}/{models.Count}] Generating SDF for {model}");
|
||||
float resolutionScale = 1.0f, backfacesThreshold = 0.6f;
|
||||
int lodIndex = 6;
|
||||
bool useGPU = true;
|
||||
var sdf = model.SDF;
|
||||
if (sdf.Texture != null)
|
||||
{
|
||||
// Preserve options set on this model
|
||||
resolutionScale = sdf.ResolutionScale;
|
||||
lodIndex = sdf.LOD;
|
||||
}
|
||||
if (!model.GenerateSDF(resolutionScale, lodIndex, true, backfacesThreshold, useGPU))
|
||||
if (!model.GenerateSDF())
|
||||
model.Save();
|
||||
}
|
||||
});
|
||||
@@ -1598,7 +1587,7 @@ namespace FlaxEditor
|
||||
if (dockedTo != null && dockedTo.SelectedTab != gameWin && dockedTo.SelectedTab != null)
|
||||
result = dockedTo.SelectedTab.Size;
|
||||
else
|
||||
result = gameWin.Viewport.ContentSize;
|
||||
result = gameWin.Viewport.Size;
|
||||
|
||||
result *= root.DpiScale;
|
||||
result = Float2.Round(result);
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace FlaxEditor.GUI
|
||||
/// <summary>
|
||||
/// The column title horizontal text alignment
|
||||
/// </summary>
|
||||
public TextAlignment TitleAlignment = TextAlignment.Center;
|
||||
public TextAlignment TitleAlignment = TextAlignment.Near;
|
||||
|
||||
/// <summary>
|
||||
/// The column title margin.
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
#define USE_IS_FOREGROUND
|
||||
#else
|
||||
#endif
|
||||
#if PLATFORM_SDL
|
||||
#define USE_SDL_WORKAROUNDS
|
||||
#endif
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
@@ -114,15 +117,14 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
public ContextMenuBase()
|
||||
: base(0, 0, 120, 32)
|
||||
{
|
||||
Visible = false;
|
||||
AutoFocus = true;
|
||||
|
||||
_direction = ContextMenuDirection.RightDown;
|
||||
Visible = false;
|
||||
|
||||
_isSubMenu = true;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
@@ -257,7 +259,9 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
desc.AllowMaximize = false;
|
||||
desc.AllowDragAndDrop = false;
|
||||
desc.IsTopmost = true;
|
||||
desc.IsRegularWindow = false;
|
||||
desc.Type = WindowType.Popup;
|
||||
desc.Parent = parentWin.Window;
|
||||
desc.Title = "ContextMenu";
|
||||
desc.HasSizingFrame = false;
|
||||
OnWindowCreating(ref desc);
|
||||
_window = Platform.CreateWindow(ref desc);
|
||||
@@ -266,6 +270,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
_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;
|
||||
@@ -441,6 +451,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;
|
||||
@@ -454,6 +475,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnWindowLostFocus()
|
||||
{
|
||||
@@ -552,7 +574,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
// Let root context menu to check if none of the popup windows
|
||||
if (_parentCM == null && UseVisibilityControl && !IsForeground)
|
||||
{
|
||||
#if USE_SDL_WORKAROUNDS
|
||||
if (!IsMouseOver)
|
||||
Hide();
|
||||
#else
|
||||
Hide();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -326,8 +326,11 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
// Update eye dropper tool
|
||||
if (_activeEyedropper)
|
||||
{
|
||||
// Try reading the color under the cursor in realtime if supported by the platform
|
||||
Float2 mousePosition = Platform.MousePosition;
|
||||
SelectedColor = ScreenUtilities.GetColorAt(mousePosition);
|
||||
Color color = ScreenUtilities.GetColorAt(mousePosition);
|
||||
if (color != Color.Transparent)
|
||||
SelectedColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,8 +76,6 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
public ColorSelector(float wheelSize)
|
||||
: base(0, 0, wheelSize, wheelSize)
|
||||
{
|
||||
AutoFocus = true;
|
||||
|
||||
_colorWheelSprite = Editor.Instance.Icons.ColorWheel128;
|
||||
_wheelRect = new Rectangle(0, 0, wheelSize, wheelSize);
|
||||
}
|
||||
|
||||
@@ -1,545 +0,0 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used to handle docking windows dragging and docking.
|
||||
/// </summary>
|
||||
public class DockHintWindow
|
||||
{
|
||||
private FloatWindowDockPanel _toMove;
|
||||
|
||||
private Float2 _dragOffset;
|
||||
private Float2 _defaultWindowSize;
|
||||
private Rectangle _rectDock;
|
||||
private Rectangle _rectWindow;
|
||||
private Float2 _mouse;
|
||||
private DockState _toSet;
|
||||
private DockPanel _toDock;
|
||||
private bool _lateDragOffsetUpdate;
|
||||
|
||||
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
|
||||
|
||||
private DockHintWindow(FloatWindowDockPanel toMove)
|
||||
{
|
||||
_toMove = toMove;
|
||||
_toSet = DockState.Float;
|
||||
var window = toMove.Window.Window;
|
||||
|
||||
// Remove focus from drag target
|
||||
_toMove.Focus();
|
||||
_toMove.Defocus();
|
||||
|
||||
// Focus window
|
||||
window.Focus();
|
||||
|
||||
// Check if window is maximized and restore window.
|
||||
if (window.IsMaximized)
|
||||
{
|
||||
// Restore window and set position to mouse.
|
||||
var mousePos = window.MousePosition;
|
||||
var previousSize = window.Size;
|
||||
window.Restore();
|
||||
window.Position = Platform.MousePosition - mousePos * window.Size / previousSize;
|
||||
}
|
||||
|
||||
// Calculate dragging offset and move window to the destination position
|
||||
var mouseScreenPosition = Platform.MousePosition;
|
||||
|
||||
// If the _toMove window was not focused when initializing this window, the result vector only contains zeros
|
||||
// and to prevent a failure, we need to perform an update for the drag offset at later time which will be done in the OnMouseMove event handler.
|
||||
if (mouseScreenPosition != Float2.Zero)
|
||||
CalculateDragOffset(mouseScreenPosition);
|
||||
else
|
||||
_lateDragOffsetUpdate = true;
|
||||
|
||||
// Get initial size
|
||||
_defaultWindowSize = window.Size;
|
||||
|
||||
// Init proxy window
|
||||
Proxy.Init(ref _defaultWindowSize);
|
||||
|
||||
// Bind events
|
||||
Proxy.Window.MouseUp += OnMouseUp;
|
||||
Proxy.Window.MouseMove += OnMouseMove;
|
||||
Proxy.Window.LostFocus += OnLostFocus;
|
||||
|
||||
// Start tracking mouse
|
||||
Proxy.Window.StartTrackingMouse(false);
|
||||
|
||||
// Update window GUI
|
||||
Proxy.Window.GUI.PerformLayout();
|
||||
|
||||
// Update rectangles
|
||||
UpdateRects();
|
||||
|
||||
// Hide base window
|
||||
window.Hide();
|
||||
|
||||
// Enable hit window presentation
|
||||
Proxy.Window.RenderingEnabled = true;
|
||||
Proxy.Window.Show();
|
||||
Proxy.Window.Focus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
// End tracking mouse
|
||||
Proxy.Window.EndTrackingMouse();
|
||||
|
||||
// Disable rendering
|
||||
Proxy.Window.RenderingEnabled = false;
|
||||
|
||||
// Unbind events
|
||||
Proxy.Window.MouseUp -= OnMouseUp;
|
||||
Proxy.Window.MouseMove -= OnMouseMove;
|
||||
Proxy.Window.LostFocus -= OnLostFocus;
|
||||
|
||||
// Hide the proxy
|
||||
Proxy.Hide();
|
||||
|
||||
if (_toMove == null)
|
||||
return;
|
||||
|
||||
// Check if window won't be docked
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
var window = _toMove.Window?.Window;
|
||||
if (window == null)
|
||||
return;
|
||||
var mouse = Platform.MousePosition;
|
||||
|
||||
// Move base window
|
||||
window.Position = mouse - _dragOffset;
|
||||
|
||||
// Show base window
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
|
||||
|
||||
// Check if window has only single tab
|
||||
if (hasNoChildPanels && _toMove.TabsCount == 1)
|
||||
{
|
||||
// Dock window
|
||||
_toMove.GetTab(0).Show(_toSet, _toDock);
|
||||
}
|
||||
// Check if dock as tab and has no child panels
|
||||
else if (hasNoChildPanels && _toSet == DockState.DockFill)
|
||||
{
|
||||
// Dock all tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var selectedTab = _toMove.SelectedTab;
|
||||
|
||||
// Dock the first tab into the target location
|
||||
var firstTab = _toMove.GetTab(0);
|
||||
firstTab.Show(_toSet, _toDock);
|
||||
|
||||
// Dock rest of the tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
|
||||
}
|
||||
|
||||
// Keep selected tab being selected
|
||||
selectedTab?.SelectTab();
|
||||
}
|
||||
|
||||
// Focus target window
|
||||
_toDock.Root.Focus();
|
||||
}
|
||||
|
||||
_toMove = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the new dragging hit window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Floating dock panel to move.</param>
|
||||
/// <returns>The dock hint window object.</returns>
|
||||
public static DockHintWindow Create(FloatWindowDockPanel toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
return new DockHintWindow(toMove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the new dragging hit window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Dock window to move.</param>
|
||||
/// <returns>The dock hint window object.</returns>
|
||||
public static DockHintWindow Create(DockWindow toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Show floating
|
||||
toMove.ShowFloating();
|
||||
|
||||
// Move window to the mouse position (with some offset for caption bar)
|
||||
var window = (WindowRootControl)toMove.Root;
|
||||
var mouse = Platform.MousePosition;
|
||||
window.Window.Position = mouse - new Float2(8, 8);
|
||||
|
||||
// Get floating panel
|
||||
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
|
||||
|
||||
return new DockHintWindow(floatingPanelToMove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates window rectangle in the dock window.
|
||||
/// </summary>
|
||||
/// <param name="state">Window dock state.</param>
|
||||
/// <param name="rect">Dock panel rectangle.</param>
|
||||
/// <returns>Calculated window rectangle.</returns>
|
||||
public static Rectangle CalculateDockRect(DockState state, ref Rectangle rect)
|
||||
{
|
||||
Rectangle result = rect;
|
||||
switch (state)
|
||||
{
|
||||
case DockState.DockFill:
|
||||
result.Location.Y += Editor.Instance.Options.Options.Interface.TabHeight;
|
||||
result.Size.Y -= Editor.Instance.Options.Options.Interface.TabHeight;
|
||||
break;
|
||||
case DockState.DockTop:
|
||||
result.Size.Y *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockLeft:
|
||||
result.Size.X *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockBottom:
|
||||
result.Location.Y += result.Size.Y * (1 - DockPanel.DefaultSplitterValue);
|
||||
result.Size.Y *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockRight:
|
||||
result.Location.X += result.Size.X * (1 - DockPanel.DefaultSplitterValue);
|
||||
result.Size.X *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void CalculateDragOffset(Float2 mouseScreenPosition)
|
||||
{
|
||||
var baseWinPos = _toMove.Window.Window.Position;
|
||||
_dragOffset = mouseScreenPosition - baseWinPos;
|
||||
}
|
||||
|
||||
private void UpdateRects()
|
||||
{
|
||||
// Cache mouse position
|
||||
_mouse = Platform.MousePosition;
|
||||
|
||||
// Check intersection with any dock panel
|
||||
var uiMouse = _mouse;
|
||||
_toDock = _toMove.MasterPanel.HitTest(ref uiMouse, _toMove);
|
||||
|
||||
// Check dock state to use
|
||||
bool showProxyHints = _toDock != null;
|
||||
bool showBorderHints = showProxyHints;
|
||||
bool showCenterHint = showProxyHints;
|
||||
if (showProxyHints)
|
||||
{
|
||||
// If moved window has not only tabs but also child panels disable docking as tab
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showCenterHint = false;
|
||||
|
||||
// Disable docking windows with one or more dock panels inside
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showBorderHints = false;
|
||||
|
||||
// Get dock area
|
||||
_rectDock = _toDock.DockAreaBounds;
|
||||
|
||||
// Cache dock rectangles
|
||||
var size = _rectDock.Size;
|
||||
var offset = _rectDock.Location;
|
||||
var borderMargin = 4.0f;
|
||||
var hintWindowsSize = Proxy.HintWindowsSize * Platform.DpiScale;
|
||||
var hintWindowsSize2 = hintWindowsSize * 0.5f;
|
||||
var centerX = size.X * 0.5f;
|
||||
var centerY = size.Y * 0.5f;
|
||||
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
|
||||
// Hit test
|
||||
DockState toSet = DockState.Float;
|
||||
if (showBorderHints)
|
||||
{
|
||||
if (_rUpper.Contains(_mouse))
|
||||
toSet = DockState.DockTop;
|
||||
else if (_rBottom.Contains(_mouse))
|
||||
toSet = DockState.DockBottom;
|
||||
else if (_rLeft.Contains(_mouse))
|
||||
toSet = DockState.DockLeft;
|
||||
else if (_rRight.Contains(_mouse))
|
||||
toSet = DockState.DockRight;
|
||||
}
|
||||
if (showCenterHint && _rCenter.Contains(_mouse))
|
||||
toSet = DockState.DockFill;
|
||||
_toSet = toSet;
|
||||
|
||||
// Show proxy hint windows
|
||||
Proxy.Down.Position = _rBottom.Location;
|
||||
Proxy.Left.Position = _rLeft.Location;
|
||||
Proxy.Right.Position = _rRight.Location;
|
||||
Proxy.Up.Position = _rUpper.Location;
|
||||
Proxy.Center.Position = _rCenter.Location;
|
||||
}
|
||||
else
|
||||
{
|
||||
_toSet = DockState.Float;
|
||||
}
|
||||
|
||||
// Update proxy hint windows visibility
|
||||
Proxy.Down.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Left.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Right.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Up.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Center.IsVisible = showProxyHints & showCenterHint;
|
||||
|
||||
// Calculate proxy/dock/window rectangles
|
||||
if (_toDock == null)
|
||||
{
|
||||
// Floating window over nothing
|
||||
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
// Floating window over dock panel
|
||||
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use only part of the dock panel to show hint
|
||||
_rectWindow = CalculateDockRect(_toSet, ref _rectDock);
|
||||
}
|
||||
}
|
||||
|
||||
// Update proxy window
|
||||
Proxy.Window.ClientBounds = _rectWindow;
|
||||
}
|
||||
|
||||
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseMove(ref Float2 mousePos)
|
||||
{
|
||||
// Recalculate the drag offset because the current mouse screen position was invalid when we initialized the window
|
||||
if (_lateDragOffsetUpdate)
|
||||
{
|
||||
// Calculate dragging offset and move window to the destination position
|
||||
CalculateDragOffset(mousePos);
|
||||
|
||||
// Reset state
|
||||
_lateDragOffsetUpdate = false;
|
||||
}
|
||||
|
||||
UpdateRects();
|
||||
}
|
||||
|
||||
private void OnLostFocus()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper proxy windows shared across docking panels. They are used to visualize docking window locations.
|
||||
/// </summary>
|
||||
public static class Proxy
|
||||
{
|
||||
/// <summary>
|
||||
/// The drag proxy window.
|
||||
/// </summary>
|
||||
public static Window Window;
|
||||
|
||||
/// <summary>
|
||||
/// The left hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Left;
|
||||
|
||||
/// <summary>
|
||||
/// The right hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Right;
|
||||
|
||||
/// <summary>
|
||||
/// The up hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Up;
|
||||
|
||||
/// <summary>
|
||||
/// The down hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Down;
|
||||
|
||||
/// <summary>
|
||||
/// The center hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Center;
|
||||
|
||||
/// <summary>
|
||||
/// The hint windows size.
|
||||
/// </summary>
|
||||
public const float HintWindowsSize = 32.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the hit proxy windows. Those windows are used to indicate drag target areas (left, right, top, bottom, etc.).
|
||||
/// </summary>
|
||||
public static void InitHitProxy()
|
||||
{
|
||||
CreateProxy(ref Left, "DockHint.Left");
|
||||
CreateProxy(ref Right, "DockHint.Right");
|
||||
CreateProxy(ref Up, "DockHint.Up");
|
||||
CreateProxy(ref Down, "DockHint.Down");
|
||||
CreateProxy(ref Center, "DockHint.Center");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the hint window.
|
||||
/// </summary>
|
||||
/// <param name="initSize">Initial size of the proxy window.</param>
|
||||
public static void Init(ref Float2 initSize)
|
||||
{
|
||||
if (Window == null)
|
||||
{
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = "DockHint.Window";
|
||||
settings.Size = initSize;
|
||||
settings.AllowInput = true;
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowMinimize = false;
|
||||
settings.HasBorder = false;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
settings.IsTopmost = true;
|
||||
|
||||
Window = Platform.CreateWindow(ref settings);
|
||||
Window.Opacity = 0.6f;
|
||||
Window.GUI.BackgroundColor = Style.Current.Selection;
|
||||
Window.GUI.AddChild<DragVisuals>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resize proxy
|
||||
Window.ClientSize = initSize;
|
||||
}
|
||||
|
||||
InitHitProxy();
|
||||
}
|
||||
|
||||
private sealed class DragVisuals : Control
|
||||
{
|
||||
public DragVisuals()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.SelectionBorder);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateProxy(ref Window win, string name)
|
||||
{
|
||||
if (win != null)
|
||||
return;
|
||||
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = name;
|
||||
settings.Size = new Float2(HintWindowsSize * Platform.DpiScale);
|
||||
settings.AllowInput = false;
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowMinimize = false;
|
||||
settings.HasBorder = false;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ActivateWhenFirstShown = false;
|
||||
settings.IsTopmost = true;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
|
||||
win = Platform.CreateWindow(ref settings);
|
||||
win.Opacity = 0.6f;
|
||||
win.GUI.BackgroundColor = Style.Current.Selection;
|
||||
win.GUI.AddChild<DragVisuals>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides proxy windows.
|
||||
/// </summary>
|
||||
public static void Hide()
|
||||
{
|
||||
HideProxy(ref Window);
|
||||
HideProxy(ref Left);
|
||||
HideProxy(ref Right);
|
||||
HideProxy(ref Up);
|
||||
HideProxy(ref Down);
|
||||
HideProxy(ref Center);
|
||||
}
|
||||
|
||||
private static void HideProxy(ref Window win)
|
||||
{
|
||||
if (win)
|
||||
{
|
||||
win.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases proxy data and windows.
|
||||
/// </summary>
|
||||
public static void Dispose()
|
||||
{
|
||||
DisposeProxy(ref Window);
|
||||
DisposeProxy(ref Left);
|
||||
DisposeProxy(ref Right);
|
||||
DisposeProxy(ref Up);
|
||||
DisposeProxy(ref Down);
|
||||
DisposeProxy(ref Center);
|
||||
}
|
||||
|
||||
private static void DisposeProxy(ref Window win)
|
||||
{
|
||||
if (win)
|
||||
{
|
||||
win.Close(ClosingReason.User);
|
||||
win = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,11 +19,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
private float _tabHeight = Editor.Instance.Options.Options.Interface.TabHeight;
|
||||
private bool _useMinimumTabWidth = Editor.Instance.Options.Options.Interface.UseMinimumTabWidth;
|
||||
private float _minimumTabWidth = Editor.Instance.Options.Options.Interface.MinimumTabWidth;
|
||||
#if PLATFORM_WINDOWS
|
||||
private readonly bool _hideTabForSingleTab = Editor.Instance.Options.Options.Interface.HideSingleTabWindowTabBars;
|
||||
#else
|
||||
private readonly bool _hideTabForSingleTab = false;
|
||||
#endif
|
||||
private readonly bool _hideTabForSingleTab = Utilities.Utils.HideSingleTabWindowTabBars();
|
||||
|
||||
/// <summary>
|
||||
/// The is mouse down flag (left button).
|
||||
@@ -54,6 +50,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.
|
||||
@@ -70,6 +71,8 @@ namespace FlaxEditor.GUI.Docking
|
||||
internal DockPanelProxy(DockPanel panel)
|
||||
: base(0, 0, 64, 64)
|
||||
{
|
||||
AutoFocus = false;
|
||||
|
||||
_panel = panel;
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
@@ -196,7 +199,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (_panel.ChildPanelsCount == 0 && _panel.TabsCount == 1 && _panel.IsFloating)
|
||||
{
|
||||
// Create docking hint window but in an async manner
|
||||
DockHintWindow.Create(_panel as FloatWindowDockPanel);
|
||||
WindowDragHelper.StartDragging(_panel as FloatWindowDockPanel);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -207,7 +210,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
_panel.SelectTab(index - 1);
|
||||
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(win);
|
||||
WindowDragHelper.StartDragging(win, _panel.RootWindow.Window);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,6 +396,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (IsSingleFloatingWindow)
|
||||
return base.OnMouseDown(location, button);
|
||||
MouseDownWindow = GetTabAtPos(location, out IsMouseDownOverCross);
|
||||
MouseStartPosition = location;
|
||||
|
||||
// Check buttons
|
||||
if (button == MouseButton.Left)
|
||||
@@ -479,6 +483,20 @@ namespace FlaxEditor.GUI.Docking
|
||||
StartDrag(MouseDownWindow);
|
||||
MouseDownWindow = null;
|
||||
}
|
||||
// Check if single tab is tried to be moved
|
||||
else if (MouseDownWindow != null && _panel.TabsCount <= 1)
|
||||
{
|
||||
if ((MousePosition - MouseStartPosition).Length > 3)
|
||||
{
|
||||
// Clear flag
|
||||
IsMouseLeftButtonDown = false;
|
||||
|
||||
// Check tab under the mouse
|
||||
if (!IsMouseDownOverCross && MouseDownWindow != null)
|
||||
StartDrag(MouseDownWindow);
|
||||
MouseDownWindow = null;
|
||||
}
|
||||
}
|
||||
// Check if has more than one tab to change order
|
||||
else if (MouseDownWindow != null && _panel.TabsCount > 1)
|
||||
{
|
||||
|
||||
@@ -182,6 +182,25 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
|
||||
/// <param name="position">Window location.</param>
|
||||
public void ShowFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent)
|
||||
{
|
||||
CreateFloating(location, size, position, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the window in a floating state.
|
||||
/// </summary>
|
||||
public void CreateFloating()
|
||||
{
|
||||
CreateFloating(Float2.Zero, Float2.Zero);
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates the window in a floating state.
|
||||
/// </summary>
|
||||
/// <param name="location">Window location.</param>
|
||||
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
|
||||
/// <param name="position">Window location.</param>
|
||||
/// <param name="showWindow">Window visibility.</param>
|
||||
public void CreateFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent, bool showWindow = false)
|
||||
{
|
||||
Undock();
|
||||
|
||||
@@ -199,14 +218,17 @@ namespace FlaxEditor.GUI.Docking
|
||||
windowGUI.UnlockChildrenRecursive();
|
||||
windowGUI.PerformLayout();
|
||||
|
||||
// Show
|
||||
window.Show();
|
||||
window.BringToFront();
|
||||
window.Focus();
|
||||
OnShow();
|
||||
if (showWindow)
|
||||
{
|
||||
// Show
|
||||
window.Show();
|
||||
window.BringToFront();
|
||||
window.Focus();
|
||||
OnShow();
|
||||
|
||||
// Perform layout again
|
||||
windowGUI.PerformLayout();
|
||||
// Perform layout again
|
||||
windowGUI.PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,42 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// <seealso cref="DockPanel" />
|
||||
public class FloatWindowDockPanel : DockPanel
|
||||
{
|
||||
private class FloatWindowDecorations : WindowDecorations
|
||||
{
|
||||
private FloatWindowDockPanel _panel;
|
||||
|
||||
public FloatWindowDecorations(FloatWindowDockPanel panel)
|
||||
: base(panel.RootWindow)
|
||||
{
|
||||
_panel = panel;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
_panel.BeginDrag();
|
||||
return true;
|
||||
}
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
#if !PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
protected override WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
var hit = base.OnHitTest(ref mouse);
|
||||
if (hit == WindowHitCodes.Caption)
|
||||
{
|
||||
// Override the system behaviour when interacting with the caption area
|
||||
hit = WindowHitCodes.Client;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private MasterDockPanel _masterPanel;
|
||||
private WindowRootControl _window;
|
||||
|
||||
@@ -40,6 +76,26 @@ namespace FlaxEditor.GUI.Docking
|
||||
Parent = window;
|
||||
_window.Window.Closing += OnClosing;
|
||||
_window.Window.LeftButtonHit += OnLeftButtonHit;
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
var decorations = Parent.AddChild(new FloatWindowDecorations(this));
|
||||
decorations.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
base.PerformLayoutBeforeChildren();
|
||||
|
||||
var decorations = Parent.GetChild<FloatWindowDecorations>();
|
||||
if (decorations != null)
|
||||
{
|
||||
// Apply offset for the title bar
|
||||
foreach (var child in Children)
|
||||
child.Bounds = child.Bounds with { Y = decorations.Height, Height = Parent.Height - decorations.Height };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,7 +108,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
return;
|
||||
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(this);
|
||||
WindowDragHelper.StartDragging(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -71,22 +127,28 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.Title = title;
|
||||
settings.Size = size;
|
||||
settings.Position = location;
|
||||
settings.MinimumSize = new Float2(1);
|
||||
settings.MinimumSize = new Float2(200, 150);
|
||||
settings.MaximumSize = Float2.Zero; // Unlimited size
|
||||
settings.Fullscreen = false;
|
||||
settings.HasBorder = true;
|
||||
settings.SupportsTransparency = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ActivateWhenFirstShown = true;
|
||||
settings.AllowInput = true;
|
||||
settings.AllowMinimize = true;
|
||||
settings.AllowMaximize = true;
|
||||
settings.AllowDragAndDrop = true;
|
||||
settings.IsTopmost = false;
|
||||
settings.IsRegularWindow = true;
|
||||
settings.Type = WindowType.Regular;
|
||||
settings.HasSizingFrame = true;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
settings.ShowInTaskbar = true;
|
||||
settings.StartPosition = startPosition;
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
settings.HasBorder = false;
|
||||
//settings.HasSizingFrame = false;
|
||||
}
|
||||
|
||||
// Create window
|
||||
return Platform.CreateWindow(ref settings);
|
||||
|
||||
@@ -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
|
||||
|
||||
458
Source/Editor/GUI/Docking/WindowDragHelper.cs
Normal file
458
Source/Editor/GUI/Docking/WindowDragHelper.cs
Normal file
@@ -0,0 +1,458 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used to handle docking windows dragging and docking.
|
||||
/// </summary>
|
||||
public class WindowDragHelper
|
||||
{
|
||||
private FloatWindowDockPanel _toMove;
|
||||
|
||||
private Float2 _dragOffset;
|
||||
private Rectangle _rectDock;
|
||||
private Float2 _mouse;
|
||||
private DockState _toSet;
|
||||
private DockPanel _toDock;
|
||||
private Window _dragSourceWindow;
|
||||
|
||||
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
|
||||
private Control _dockHintDown, _dockHintUp, _dockHintLeft, _dockHintRight, _dockHintCenter;
|
||||
|
||||
/// <summary>
|
||||
/// The hint control size.
|
||||
/// </summary>
|
||||
public const float HintControlSize = 32.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The opacity of the dragged window when hint controls are shown.
|
||||
/// </summary>
|
||||
public const float DragWindowOpacity = 0.4f;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if any windows are being dragged.
|
||||
/// </summary>
|
||||
public static bool IsDragActive { get; private set; }
|
||||
|
||||
private WindowDragHelper(FloatWindowDockPanel toMove, Window dragSourceWindow)
|
||||
{
|
||||
IsDragActive = true;
|
||||
_toMove = toMove;
|
||||
_toSet = DockState.Float;
|
||||
var window = toMove.Window.Window;
|
||||
|
||||
// Bind events
|
||||
FlaxEngine.Scripting.Update += OnUpdate;
|
||||
window.MouseUp += OnMouseUp;
|
||||
|
||||
// Update rectangles
|
||||
UpdateRects(Platform.MousePosition);
|
||||
|
||||
// Ensure the dragged window stays on top of every other window
|
||||
window.IsAlwaysOnTop = true;
|
||||
|
||||
_dragSourceWindow = dragSourceWindow;
|
||||
if (_dragSourceWindow != null) // Detaching a tab from existing window
|
||||
{
|
||||
_dragOffset = new Float2(window.Size.X / 2, 10.0f);
|
||||
|
||||
_dragSourceWindow.MouseUp += OnMouseUp; // The mouse up event is sent to the source window on Windows
|
||||
|
||||
// TODO: when detaching tab in floating window (not main window), the drag source window is still main window?
|
||||
var dragSourceWindowWayland = toMove.MasterPanel?.RootWindow.Window ?? Editor.Instance.Windows.MainWindow;
|
||||
window.DoDragDrop(window.Title, _dragOffset, dragSourceWindowWayland);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dragOffset = window.MousePosition;
|
||||
window.DoDragDrop(window.Title, _dragOffset, window);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
IsDragActive = false;
|
||||
var window = _toMove?.Window?.Window;
|
||||
|
||||
// Unbind events
|
||||
FlaxEngine.Scripting.Update -= OnUpdate;
|
||||
if (window != null)
|
||||
window.MouseUp -= OnMouseUp;
|
||||
if (_dragSourceWindow != null)
|
||||
_dragSourceWindow.MouseUp -= OnMouseUp;
|
||||
|
||||
RemoveDockHints();
|
||||
|
||||
if (_toMove == null)
|
||||
return;
|
||||
|
||||
if (window != null)
|
||||
{
|
||||
window.Opacity = 1.0f;
|
||||
window.IsAlwaysOnTop = false;
|
||||
window.BringToFront();
|
||||
}
|
||||
|
||||
// Check if window won't be docked
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
if (window == null)
|
||||
return;
|
||||
|
||||
// Show base window
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
|
||||
|
||||
// Check if window has only single tab
|
||||
if (hasNoChildPanels && _toMove.TabsCount == 1)
|
||||
{
|
||||
// Dock window
|
||||
_toMove.GetTab(0).Show(_toSet, _toDock);
|
||||
}
|
||||
// Check if dock as tab and has no child panels
|
||||
else if (hasNoChildPanels && _toSet == DockState.DockFill)
|
||||
{
|
||||
// Dock all tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var selectedTab = _toMove.SelectedTab;
|
||||
|
||||
// Dock the first tab into the target location
|
||||
if (_toMove.TabsCount > 0)
|
||||
{
|
||||
var firstTab = _toMove.GetTab(0);
|
||||
firstTab.Show(_toSet, _toDock);
|
||||
|
||||
// Dock rest of the tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep selected tab being selected
|
||||
selectedTab?.SelectTab();
|
||||
}
|
||||
|
||||
// Focus target window
|
||||
_toDock.Root.Focus();
|
||||
}
|
||||
|
||||
_toMove = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start dragging a floating dock panel.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Floating dock panel to move.</param>
|
||||
/// <returns>The window drag helper object.</returns>
|
||||
public static WindowDragHelper StartDragging(FloatWindowDockPanel toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
return new WindowDragHelper(toMove, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start dragging a docked panel into a floating window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Dock window to move.</param>
|
||||
/// <param name="dragSourceWindow">The window where dragging started from.</param>
|
||||
/// <returns>The window drag helper object.</returns>
|
||||
public static WindowDragHelper StartDragging(DockWindow toMove, Window dragSourceWindow)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Create floating window
|
||||
toMove.CreateFloating();
|
||||
|
||||
// Get floating panel
|
||||
var window = (WindowRootControl)toMove.Root;
|
||||
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
|
||||
|
||||
return new WindowDragHelper(floatingPanelToMove, dragSourceWindow);
|
||||
}
|
||||
|
||||
private sealed class DragVisuals : Control
|
||||
{
|
||||
public DragVisuals()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.SelectionBorder);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDockHints()
|
||||
{
|
||||
if (_toDock == null)
|
||||
return;
|
||||
|
||||
if (_toDock.RootWindow.Window != _dragSourceWindow)
|
||||
_toDock.RootWindow.Window.MouseUp += OnMouseUp;
|
||||
|
||||
_dockHintDown = AddHintControl(new Float2(0.5f, 1));
|
||||
_dockHintUp = AddHintControl(new Float2(0.5f, 0));
|
||||
_dockHintLeft = AddHintControl(new Float2(0, 0.5f));
|
||||
_dockHintRight = AddHintControl(new Float2(1, 0.5f));
|
||||
_dockHintCenter = AddHintControl(new Float2(0.5f, 0.5f));
|
||||
|
||||
Control AddHintControl(Float2 pivot)
|
||||
{
|
||||
DragVisuals hintControl = _toDock.AddChild<DragVisuals>();
|
||||
hintControl.Size = new Float2(HintControlSize);
|
||||
hintControl.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
hintControl.Pivot = pivot;
|
||||
hintControl.PivotRelative = true;
|
||||
return hintControl;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveDockHints()
|
||||
{
|
||||
if (_toDock == null)
|
||||
return;
|
||||
|
||||
if (_toDock.RootWindow.Window != _dragSourceWindow)
|
||||
_toDock.RootWindow.Window.MouseUp -= OnMouseUp;
|
||||
|
||||
_dockHintDown?.Parent.RemoveChild(_dockHintDown);
|
||||
_dockHintUp?.Parent.RemoveChild(_dockHintUp);
|
||||
_dockHintLeft?.Parent.RemoveChild(_dockHintLeft);
|
||||
_dockHintRight?.Parent.RemoveChild(_dockHintRight);
|
||||
_dockHintCenter?.Parent.RemoveChild(_dockHintCenter);
|
||||
_dockHintDown = _dockHintUp = _dockHintLeft = _dockHintRight = _dockHintCenter = null;
|
||||
}
|
||||
|
||||
private void UpdateRects(Float2 mousePos)
|
||||
{
|
||||
// Cache mouse position
|
||||
_mouse = mousePos;
|
||||
|
||||
// Check intersection with any dock panel
|
||||
DockPanel dockPanel = null;
|
||||
if (_toMove.MasterPanel.HitTest(ref _mouse, _toMove, out var hitResults))
|
||||
{
|
||||
dockPanel = hitResults[0];
|
||||
|
||||
// Prefer panel which currently has focus
|
||||
foreach (var hit in hitResults)
|
||||
{
|
||||
if (hit.RootWindow.Window.IsFocused)
|
||||
{
|
||||
dockPanel = hit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer panel in the same window we hit earlier
|
||||
if (dockPanel?.RootWindow != _toDock?.RootWindow)
|
||||
{
|
||||
foreach (var hit in hitResults)
|
||||
{
|
||||
if (hit.RootWindow == _toDock?.RootWindow)
|
||||
{
|
||||
dockPanel = _toDock;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dockPanel != _toDock)
|
||||
{
|
||||
RemoveDockHints();
|
||||
_toDock = dockPanel;
|
||||
AddDockHints();
|
||||
|
||||
// Make sure the all the dock hint areas are not under other windows
|
||||
_toDock?.RootWindow.Window.BringToFront();
|
||||
//_toDock?.RootWindow.Window.Focus();
|
||||
|
||||
// Make the dragged window transparent when dock hints are visible
|
||||
_toMove.Window.Window.Opacity = _toDock == null ? 1.0f : DragWindowOpacity;
|
||||
}
|
||||
|
||||
// Check dock state to use
|
||||
bool showProxyHints = _toDock != null;
|
||||
bool showBorderHints = showProxyHints;
|
||||
bool showCenterHint = showProxyHints;
|
||||
Control hoveredHintControl = null;
|
||||
Float2 hoveredSizeOverride = Float2.Zero;
|
||||
if (showProxyHints)
|
||||
{
|
||||
// If moved window has not only tabs but also child panels disable docking as tab
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showCenterHint = false;
|
||||
|
||||
// Disable docking windows with one or more dock panels inside
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showBorderHints = false;
|
||||
|
||||
// Get dock area
|
||||
_rectDock = _toDock.DockAreaBounds;
|
||||
|
||||
// Cache dock rectangles
|
||||
var size = _rectDock.Size / Platform.DpiScale;
|
||||
var offset = _toDock.PointFromScreen(_rectDock.Location);
|
||||
var borderMargin = 4.0f;
|
||||
var hintWindowsSize = HintControlSize;
|
||||
var hintWindowsSize2 = hintWindowsSize * 0.5f;
|
||||
var hintPreviewSize = new Float2(Math.Max(HintControlSize * 2, size.X * 0.5f), Math.Max(HintControlSize * 2, size.Y * 0.5f));
|
||||
var centerX = size.X * 0.5f;
|
||||
var centerY = size.Y * 0.5f;
|
||||
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
|
||||
// Hit test, and calculate the approximation for filled area when hovered over the hint
|
||||
DockState toSet = DockState.Float;
|
||||
if (showBorderHints)
|
||||
{
|
||||
if (_rUpper.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockTop;
|
||||
hoveredHintControl = _dockHintUp;
|
||||
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
|
||||
}
|
||||
else if (_rBottom.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockBottom;
|
||||
hoveredHintControl = _dockHintDown;
|
||||
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
|
||||
}
|
||||
else if (_rLeft.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockLeft;
|
||||
hoveredHintControl = _dockHintLeft;
|
||||
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
|
||||
}
|
||||
else if (_rRight.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockRight;
|
||||
hoveredHintControl = _dockHintRight;
|
||||
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
|
||||
}
|
||||
}
|
||||
if (showCenterHint && _rCenter.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockFill;
|
||||
hoveredHintControl = _dockHintCenter;
|
||||
hoveredSizeOverride = new Float2(size.X, size.Y);
|
||||
}
|
||||
|
||||
_toSet = toSet;
|
||||
}
|
||||
else
|
||||
{
|
||||
_toSet = DockState.Float;
|
||||
}
|
||||
|
||||
// Update sizes and opacity of hint controls
|
||||
if (_toDock != null)
|
||||
{
|
||||
if (hoveredHintControl != _dockHintDown)
|
||||
{
|
||||
_dockHintDown.Size = new Float2(HintControlSize);
|
||||
_dockHintDown.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintLeft)
|
||||
{
|
||||
_dockHintLeft.Size = new Float2(HintControlSize);
|
||||
_dockHintLeft.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintRight)
|
||||
{
|
||||
_dockHintRight.Size = new Float2(HintControlSize);
|
||||
_dockHintRight.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintUp)
|
||||
{
|
||||
_dockHintUp.Size = new Float2(HintControlSize);
|
||||
_dockHintUp.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintCenter)
|
||||
{
|
||||
_dockHintCenter.Size = new Float2(HintControlSize);
|
||||
_dockHintCenter.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
|
||||
if (_toSet != DockState.Float)
|
||||
{
|
||||
if (hoveredHintControl != null)
|
||||
{
|
||||
hoveredHintControl.BackgroundColor = Style.Current.Selection.AlphaMultiplied(1.0f);
|
||||
hoveredHintControl.Size = hoveredSizeOverride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update hint controls visibility and location
|
||||
if (showProxyHints)
|
||||
{
|
||||
if (hoveredHintControl != _dockHintDown)
|
||||
_dockHintDown.Location = _rBottom.Location;
|
||||
if (hoveredHintControl != _dockHintLeft)
|
||||
_dockHintLeft.Location = _rLeft.Location;
|
||||
if (hoveredHintControl != _dockHintRight)
|
||||
_dockHintRight.Location = _rRight.Location;
|
||||
if (hoveredHintControl != _dockHintUp)
|
||||
_dockHintUp.Location = _rUpper.Location;
|
||||
if (hoveredHintControl != _dockHintCenter)
|
||||
_dockHintCenter.Location = _rCenter.Location;
|
||||
|
||||
_dockHintDown.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintLeft.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintRight.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintUp.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintCenter.Visible = showProxyHints & showCenterHint;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void OnUpdate()
|
||||
{
|
||||
var mousePos = Platform.MousePosition;
|
||||
|
||||
if (_mouse != mousePos)
|
||||
OnMouseMove(mousePos);
|
||||
}
|
||||
|
||||
private void OnMouseMove(Float2 mousePos)
|
||||
{
|
||||
if (_dragSourceWindow != null)
|
||||
_toMove.Window.Window.Position = mousePos - _dragOffset;
|
||||
|
||||
UpdateRects(mousePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,39 +129,11 @@ namespace FlaxEditor.GUI.Input
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
bool isTransparent = _value.A < 1;
|
||||
|
||||
var style = Style.Current;
|
||||
var fullRect = new Rectangle(0, 0, Width, Height);
|
||||
var colorRect = new Rectangle(0, 0, isTransparent ? Width * 0.7f : Width, Height);
|
||||
var r = new Rectangle(0, 0, Width, Height);
|
||||
|
||||
if (isTransparent)
|
||||
{
|
||||
var alphaRect = new Rectangle(colorRect.Right, 0, Width - colorRect.Right, Height);
|
||||
|
||||
// Draw checkerboard pattern to part of the color value box
|
||||
Render2D.FillRectangle(alphaRect, Color.White);
|
||||
var smallRectSize = 7.9f;
|
||||
var numHor = Mathf.CeilToInt(alphaRect.Width / smallRectSize);
|
||||
var numVer = Mathf.CeilToInt(alphaRect.Height / smallRectSize);
|
||||
for (int i = 0; i < numHor; i++)
|
||||
{
|
||||
for (int j = 0; j < numVer; j++)
|
||||
{
|
||||
if ((i + j) % 2 == 0)
|
||||
{
|
||||
var rect = new Rectangle(alphaRect.X + smallRectSize * i, alphaRect.Y + smallRectSize * j, new Float2(smallRectSize));
|
||||
Render2D.PushClip(alphaRect);
|
||||
Render2D.FillRectangle(rect, Color.Gray);
|
||||
Render2D.PopClip();
|
||||
}
|
||||
}
|
||||
}
|
||||
Render2D.FillRectangle(alphaRect, _value);
|
||||
}
|
||||
|
||||
Render2D.FillRectangle(colorRect, _value with { A = 1 });
|
||||
Render2D.DrawRectangle(fullRect, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black);
|
||||
Render2D.FillRectangle(r, _value);
|
||||
Render2D.DrawRectangle(r, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -368,8 +368,6 @@ namespace FlaxEditor.GUI.Input
|
||||
public SliderControl(float value, float x = 0, float y = 0, float width = 120, float min = float.MinValue, float max = float.MaxValue)
|
||||
: base(x, y, width, TextBox.DefaultHeight)
|
||||
{
|
||||
AutoFocus = true;
|
||||
|
||||
_min = min;
|
||||
_max = max;
|
||||
_value = Mathf.Clamp(value, min, max);
|
||||
|
||||
@@ -266,6 +266,7 @@ namespace FlaxEditor.GUI.Input
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
@@ -292,13 +293,45 @@ namespace FlaxEditor.GUI.Input
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMoveRelative(Float2 mouseMotion)
|
||||
{
|
||||
var location = Root.TrackingMouseOffset;
|
||||
if (_isSliding)
|
||||
{
|
||||
// Update sliding
|
||||
ApplySliding(Root.TrackingMouseOffset.X * _slideSpeed);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update cursor type so user knows they can slide value
|
||||
if (CanUseSliding && SlideRect.Contains(location) && !_isSliding)
|
||||
{
|
||||
Cursor = CursorType.SizeWE;
|
||||
_cursorChanged = true;
|
||||
}
|
||||
else if (_cursorChanged && !_isSliding)
|
||||
{
|
||||
Cursor = CursorType.Default;
|
||||
_cursorChanged = false;
|
||||
}
|
||||
|
||||
base.OnMouseMoveRelative(mouseMotion);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && _isSliding)
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
// End sliding and return mouse to original location
|
||||
RootWindow.MousePosition = _mouseClickedPosition;
|
||||
#endif
|
||||
EndSliding();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -12,16 +12,6 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public sealed class MainMenu : ContainerControl
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
private bool _useCustomWindowSystem;
|
||||
private Image _icon;
|
||||
private Label _title;
|
||||
private Button _closeButton;
|
||||
private Button _minimizeButton;
|
||||
private Button _maximizeButton;
|
||||
private LocalizedString _charChromeRestore, _charChromeMaximize;
|
||||
private Window _window;
|
||||
#endif
|
||||
private MainMenuButton _selected;
|
||||
|
||||
/// <summary>
|
||||
@@ -60,200 +50,12 @@ namespace FlaxEditor.GUI
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MainMenu"/> class.
|
||||
/// </summary>
|
||||
/// <param name="mainWindow">The main window.</param>
|
||||
public MainMenu(RootControl mainWindow)
|
||||
public MainMenu()
|
||||
: base(0, 0, 0, 20)
|
||||
{
|
||||
AutoFocus = false;
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
_useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
Height = 28;
|
||||
|
||||
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
|
||||
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
|
||||
Font iconFont = windowIconsFont?.CreateFont(9);
|
||||
|
||||
_window = mainWindow.RootWindow.Window;
|
||||
_window.HitTest += OnHitTest;
|
||||
_window.Closed += OnWindowClosed;
|
||||
|
||||
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
|
||||
|
||||
_icon = new Image
|
||||
{
|
||||
Margin = new Margin(6, 6, 6, 6),
|
||||
Brush = new TextureBrush(windowIcon),
|
||||
Color = Style.Current.Foreground,
|
||||
KeepAspectRatio = false,
|
||||
TooltipText = string.Format("{0}\nVersion {1}\nConfiguration {3}\nGraphics {2}", _window.Title, Globals.EngineVersion, GPUDevice.Instance.RendererType, configuration),
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_title = new Label(0, 0, Width, Height)
|
||||
{
|
||||
Text = _window.Title,
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = TextAlignment.Center,
|
||||
ClipText = true,
|
||||
TextColor = Style.Current.ForegroundGrey,
|
||||
TextColorHighlighted = Style.Current.ForegroundGrey,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_closeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Color.Red,
|
||||
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
|
||||
|
||||
_minimizeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_minimizeButton.Clicked += () => _window.Minimize();
|
||||
|
||||
_maximizeButton = new Button
|
||||
{
|
||||
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_maximizeButton.Clicked += () =>
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
};
|
||||
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
|
||||
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
_maximizeButton.Text = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowClosed()
|
||||
{
|
||||
if (_window != null)
|
||||
{
|
||||
_window.HitTest = null;
|
||||
_window = null;
|
||||
}
|
||||
}
|
||||
|
||||
private WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
var dpiScale = _window.DpiScale;
|
||||
|
||||
if (_window.IsMinimized)
|
||||
return WindowHitCodes.NoWhere;
|
||||
|
||||
if (!_window.IsMaximized)
|
||||
{
|
||||
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
|
||||
var winSize = _window.Size;
|
||||
|
||||
// Distance from which the mouse is considered to be on the border/corner
|
||||
float distance = 5.0f * dpiScale;
|
||||
|
||||
if (pos.Y > winSize.Y - distance && pos.X < distance)
|
||||
return WindowHitCodes.BottomLeft;
|
||||
|
||||
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.BottomRight;
|
||||
|
||||
if (pos.Y < distance && pos.X < distance)
|
||||
return WindowHitCodes.TopLeft;
|
||||
|
||||
if (pos.Y < distance && pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.TopRight;
|
||||
|
||||
if (pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.Right;
|
||||
|
||||
if (pos.X < distance)
|
||||
return WindowHitCodes.Left;
|
||||
|
||||
if (pos.Y < distance)
|
||||
return WindowHitCodes.Top;
|
||||
|
||||
if (pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.Bottom;
|
||||
}
|
||||
|
||||
var mousePos = PointFromScreen(mouse * dpiScale);
|
||||
var controlUnderMouse = GetChildAt(mousePos);
|
||||
var isMouseOverSth = controlUnderMouse != null && controlUnderMouse != _title;
|
||||
var rb = GetRightButton();
|
||||
if (rb != null && _minimizeButton != null && new Rectangle(rb.UpperRight, _minimizeButton.BottomLeft - rb.UpperRight).Contains(ref mousePos) && !isMouseOverSth)
|
||||
return WindowHitCodes.Caption;
|
||||
|
||||
return WindowHitCodes.Client;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Return the rightmost button.
|
||||
/// </summary>
|
||||
/// <returns>Rightmost button, null if there is no <see cref="MainMenuButton"/></returns>
|
||||
private MainMenuButton GetRightButton()
|
||||
{
|
||||
MainMenuButton b = null;
|
||||
foreach (var control in Children)
|
||||
{
|
||||
if (b == null && control is MainMenuButton)
|
||||
b = (MainMenuButton)control;
|
||||
|
||||
if (control is MainMenuButton && control.Right > b.Right)
|
||||
b = (MainMenuButton)control;
|
||||
}
|
||||
return b;
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -298,26 +100,6 @@ namespace FlaxEditor.GUI
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseDoubleClick(location, button))
|
||||
return true;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
var child = GetChildAtRecursive(location);
|
||||
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
@@ -333,16 +115,8 @@ namespace FlaxEditor.GUI
|
||||
protected override void PerformLayoutAfterChildren()
|
||||
{
|
||||
float x = 0;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Icon
|
||||
_icon.X = x;
|
||||
_icon.Size = new Float2(Height);
|
||||
x += _icon.Width;
|
||||
}
|
||||
#endif
|
||||
WindowDecorations decorations = Parent.GetChild<WindowDecorations>();
|
||||
x += decorations?.Icon?.Width ?? 0;
|
||||
|
||||
// Arrange controls
|
||||
MainMenuButton rightMostButton = null;
|
||||
@@ -361,37 +135,21 @@ namespace FlaxEditor.GUI
|
||||
x += b.Width;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Buttons
|
||||
_closeButton.Height = Height;
|
||||
_closeButton.X = Width - _closeButton.Width;
|
||||
_maximizeButton.Height = Height;
|
||||
_maximizeButton.X = _closeButton.X - _maximizeButton.Width;
|
||||
_minimizeButton.Height = Height;
|
||||
_minimizeButton.X = _maximizeButton.X - _minimizeButton.Width;
|
||||
|
||||
// Title
|
||||
_title.Bounds = new Rectangle(x + 2, 0, _minimizeButton.Left - x - 4, Height);
|
||||
//_title.Text = _title.Width < 300.0f ? Editor.Instance.ProjectInfo.Name : _window.Title;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fill the right side if title and buttons are not present
|
||||
if (decorations?.Title == null)
|
||||
Width = Parent.Width;
|
||||
else
|
||||
Width = x;
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_window != null)
|
||||
{
|
||||
_window.Closed -= OnWindowClosed;
|
||||
OnWindowClosed();
|
||||
}
|
||||
|
||||
if (_selected != null)
|
||||
Selected = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,12 @@ namespace FlaxEditor.GUI
|
||||
Text = text;
|
||||
|
||||
var style = Style.Current;
|
||||
#if PLATFORM_WINDOWS
|
||||
if (Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
|
||||
if (!Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
BackgroundColorMouseOver = style.BackgroundHighlighted;
|
||||
BackgroundColorMouseOverOpened = style.Background;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BackgroundColorMouseOver = BackgroundColorMouseOverOpened = style.LightBackground * 1.3f;
|
||||
}
|
||||
|
||||
@@ -447,8 +447,8 @@ namespace FlaxEditor.GUI.Tree
|
||||
// Select previous parent child
|
||||
var select = nodeParent.GetChild(myIndex - 1) as TreeNode;
|
||||
|
||||
// Get bottom most child node
|
||||
while (select != null && select.IsExpanded && select.HasAnyVisibleChild)
|
||||
// Select last child if is valid and expanded and has any children
|
||||
if (select != null && select.IsExpanded && select.HasAnyVisibleChild)
|
||||
{
|
||||
select = select.GetChild(select.ChildrenCount - 1) as TreeNode;
|
||||
}
|
||||
|
||||
@@ -319,8 +319,6 @@ namespace FlaxEditor.GUI.Tree
|
||||
public TreeNode(bool canChangeOrder, SpriteHandle iconCollapsed, SpriteHandle iconOpened)
|
||||
: base(0, 0, 64, 16)
|
||||
{
|
||||
AutoFocus = true;
|
||||
|
||||
_canChangeOrder = canChangeOrder;
|
||||
_animationProgress = 1.0f;
|
||||
_cachedHeight = _headerHeight;
|
||||
|
||||
342
Source/Editor/GUI/WindowDecorations.cs
Normal file
342
Source/Editor/GUI/WindowDecorations.cs
Normal file
@@ -0,0 +1,342 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.GUI.Docking;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the title bar of the window with buttons.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public class WindowDecorations : ContainerControl
|
||||
{
|
||||
private Image _icon;
|
||||
private Label _title;
|
||||
private Button _closeButton;
|
||||
private Button _minimizeButton;
|
||||
private Button _maximizeButton;
|
||||
private LocalizedString _charChromeRestore, _charChromeMaximize;
|
||||
private Window _window;
|
||||
|
||||
/// <summary>
|
||||
/// The title label in the title bar.
|
||||
/// </summary>
|
||||
public Label Title => _title;
|
||||
|
||||
/// <summary>
|
||||
/// The icon used in the title bar.
|
||||
/// </summary>
|
||||
public Image Icon => _icon;
|
||||
|
||||
/// <summary>
|
||||
/// The tooltip shown when hovering over the icon.
|
||||
/// </summary>
|
||||
public string IconTooltipText
|
||||
{
|
||||
get => _icon?.TooltipText ?? null;
|
||||
set
|
||||
{
|
||||
if (_icon != null)
|
||||
_icon.TooltipText = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WindowDecorations"/> class.
|
||||
/// </summary>
|
||||
/// <param name="window">The window.</param>
|
||||
/// <param name="iconOnly">When set, omit drawing title and buttons.</param>
|
||||
public WindowDecorations(RootControl window, bool iconOnly = false)
|
||||
: base(0, 0, 0, 20)
|
||||
{
|
||||
_window = window.RootWindow.Window;
|
||||
|
||||
AutoFocus = false;
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
BackgroundColor = Color.Transparent;
|
||||
|
||||
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
|
||||
|
||||
_icon = new Image
|
||||
{
|
||||
Margin = new Margin(4, 4, 4, 4),
|
||||
Brush = new TextureBrush(windowIcon),
|
||||
Color = Style.Current.Foreground,
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
KeepAspectRatio = false,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
if (!iconOnly)
|
||||
{
|
||||
_icon.Margin = new Margin(6, 6, 6, 6);
|
||||
Height = 28;
|
||||
|
||||
_window.HitTest += OnHitTest;
|
||||
_window.Closed += OnWindowClosed;
|
||||
|
||||
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
|
||||
Font iconFont = windowIconsFont?.CreateFont(9);
|
||||
|
||||
_title = new Label(0, 0, Width, Height)
|
||||
{
|
||||
Text = _window.Title,
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = TextAlignment.Center,
|
||||
ClipText = true,
|
||||
TextColor = Style.Current.ForegroundGrey,
|
||||
TextColorHighlighted = Style.Current.ForegroundGrey,
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_closeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Color.Red,
|
||||
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
|
||||
|
||||
_minimizeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_minimizeButton.Clicked += () => _window.Minimize();
|
||||
|
||||
_maximizeButton = new Button
|
||||
{
|
||||
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_maximizeButton.Clicked += () =>
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
};
|
||||
|
||||
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
|
||||
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
var maximizeText = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
|
||||
if (_maximizeButton.Text != maximizeText)
|
||||
_maximizeButton.Text = maximizeText;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowClosed()
|
||||
{
|
||||
if (_window != null)
|
||||
{
|
||||
_window.HitTest -= OnHitTest;
|
||||
_window = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform hit test on the window.
|
||||
/// </summary>
|
||||
/// <param name="mouse">The mouse position</param>
|
||||
/// <returns>The hit code for given position.</returns>
|
||||
protected virtual WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
if (_window.IsMinimized)
|
||||
return WindowHitCodes.NoWhere;
|
||||
|
||||
var dpiScale = _window.DpiScale;
|
||||
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
|
||||
if (!_window.IsMaximized)
|
||||
{
|
||||
var winSize = _window.Size;
|
||||
|
||||
// Distance from which the mouse is considered to be on the border/corner
|
||||
float distance = 5.0f * dpiScale;
|
||||
|
||||
if (pos.Y > winSize.Y - distance && pos.X < distance)
|
||||
return WindowHitCodes.BottomLeft;
|
||||
|
||||
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.BottomRight;
|
||||
|
||||
if (pos.Y < distance && pos.X < distance)
|
||||
return WindowHitCodes.TopLeft;
|
||||
|
||||
if (pos.Y < distance && pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.TopRight;
|
||||
|
||||
if (pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.Right;
|
||||
|
||||
if (pos.X < distance)
|
||||
return WindowHitCodes.Left;
|
||||
|
||||
if (pos.Y < distance)
|
||||
return WindowHitCodes.Top;
|
||||
|
||||
if (pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.Bottom;
|
||||
}
|
||||
|
||||
var controlUnderMouse = GetChildAt(pos, control => control != _title);
|
||||
if (_title.Bounds.Contains(pos) && controlUnderMouse == null)
|
||||
return WindowHitCodes.Caption;
|
||||
|
||||
return WindowHitCodes.Client;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
// These may not work with main window due to SDL not passing mouse events
|
||||
// when interacting with hit tests on caption area...
|
||||
|
||||
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
return true;
|
||||
}
|
||||
else if (Icon.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
_window.Close(ClosingReason.User);
|
||||
return true;
|
||||
}
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutAfterChildren()
|
||||
{
|
||||
// Calculate extents for title bounds area excluding the icon and main menu area
|
||||
float x = 0;
|
||||
|
||||
// Icon
|
||||
if (_icon != null)
|
||||
{
|
||||
_icon.X = x;
|
||||
_icon.Size = new Float2(Height);
|
||||
x += _icon.Width;
|
||||
}
|
||||
|
||||
// Main menu if present
|
||||
if (Parent.GetChild<MainMenu>() is MainMenu mainMenu)
|
||||
{
|
||||
for (int i = 0; i < mainMenu.Children.Count; i++)
|
||||
{
|
||||
var c = mainMenu.Children[i];
|
||||
if (c is MainMenuButton b && c.Visible)
|
||||
{
|
||||
b.Bounds = new Rectangle(x, 0, b.Width, Height);
|
||||
x += b.Width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons
|
||||
float rightMostButtonX = Width;
|
||||
if (_closeButton != null)
|
||||
{
|
||||
_closeButton.Height = Height;
|
||||
_closeButton.X = rightMostButtonX - _closeButton.Width;
|
||||
rightMostButtonX = _closeButton.X;
|
||||
}
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
_maximizeButton.Height = Height;
|
||||
_maximizeButton.X = rightMostButtonX - _maximizeButton.Width;
|
||||
rightMostButtonX = _maximizeButton.X;
|
||||
}
|
||||
if (_minimizeButton != null)
|
||||
{
|
||||
_minimizeButton.Height = Height;
|
||||
_minimizeButton.X = rightMostButtonX - _minimizeButton.Width;
|
||||
rightMostButtonX = _minimizeButton.X;
|
||||
}
|
||||
|
||||
// Title
|
||||
if (_title != null)
|
||||
{
|
||||
_title.Text = _window.Title;
|
||||
_title.Bounds = new Rectangle(x, 0, rightMostButtonX - x, Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
DrawBorders();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw borders around the window.
|
||||
/// </summary>
|
||||
public virtual void DrawBorders()
|
||||
{
|
||||
var win = RootWindow.Window;
|
||||
if (win.IsMaximized)
|
||||
return;
|
||||
|
||||
if (Editor.Instance.UI.StatusBar == null)
|
||||
return;
|
||||
|
||||
const float thickness = 1.0f;
|
||||
Color color = Editor.Instance.UI.StatusBar.StatusColor;
|
||||
Rectangle rect = new Rectangle(thickness * 0.5f, thickness * 0.5f, Parent.Width - thickness, Parent.Height - thickness);
|
||||
Render2D.DrawRectangle(rect, color);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_window != null)
|
||||
{
|
||||
_window.Closed -= OnWindowClosed;
|
||||
OnWindowClosed();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,7 +155,6 @@ namespace FlaxEditor.Gizmo
|
||||
// Ensure player is not moving objects
|
||||
if (ActiveAxis != Axis.None)
|
||||
return;
|
||||
Profiler.BeginEvent("Pick");
|
||||
|
||||
// Get mouse ray and try to hit any object
|
||||
var ray = Owner.MouseRay;
|
||||
@@ -244,8 +243,6 @@ namespace FlaxEditor.Gizmo
|
||||
{
|
||||
sceneEditing.Deselect();
|
||||
}
|
||||
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user