140 Commits

Author SHA1 Message Date
d0cc2b67a7 Fix building PhysX on Linux and macOS
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-10-14 04:21:04 +03:00
9b93bef983 Use OpenAL Git repository as source with all platforms 2025-10-14 03:11:44 +03:00
ee95a292a6 Fix building curl on Linux 2025-10-14 03:10:51 +03:00
a336b4966f Fix building Assimp on Linux
Versioned clang++ symlinks are not available on Arch
2025-10-14 03:10:33 +03:00
da1c112c92 Add support for building dependencies with specific architecture 2025-10-14 02:04:27 +03:00
9ffe206b81 Fix CMake compatibility errors with dependencies 2025-10-14 01:54:28 +03:00
a87a6898ae Support Visual Studio 2026 as a generator for CMake dependencies 2025-10-14 01:11:01 +03:00
ebf57ef9b0 Update SDL to 3.2.24 2025-10-14 01:10:44 +03:00
668a4dbb4d Merge remote-tracking branch 'origin/master' into sdl_platform 2025-10-14 01:09:26 +03:00
1d8f221f1b Merge remote-tracking branch 'origin/1.11' into sdl_platform_1.11
# Conflicts:
#	Source/Engine/Platform/Windows/WindowsPlatform.cpp
#	Source/Tools/Flax.Build/Build/ProjectTarget.cs
#	Source/Tools/Flax.Build/Configuration.cs
2025-09-07 19:31:16 +03:00
d70a003617 Improve tab-less dock window behavior in other platforms
Add checks for #3215 to hide the tab bar only if it doesn't prevent
window dragging in Wayland sessions with native decorations.
2025-09-07 19:27:24 +03:00
b443b74d18 Merge branch 'master' into sdl_platform
# Conflicts:
#	Source/Editor/GUI/Docking/DockHintWindow.cs
#	Source/Editor/Options/InterfaceOptions.cs
2025-09-07 19:02:09 +03:00
fc341a86e7 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-08-24 09:44:28 +03:00
68da28ffe8 Update SDL to 3.2.20 2025-08-12 15:39:19 +03:00
Wojtek Figat
427e76e76e Update volk to version 1.4.304 2025-08-12 15:24:48 +03:00
b183b5bcfc Merge remote-tracking branch 'origin/master' into sdl_platform 2025-08-12 15:22:34 +03:00
51feaa0730 Release mouse relative mode when focus changes during mouse tracking
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-07-03 23:21:42 +03:00
8e69aa8bd6 Fix error while closing Editor
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-07-03 22:57:07 +03:00
90b2fded48 Merge remote-tracking branch 'origin/master' into sdl_platform
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
# Conflicts:
#	Source/Editor/Windows/SceneTreeWindow.cs
2025-07-03 22:05:29 +03:00
9125ffeb9e Update third-party label in About dialog 2025-07-03 22:02:37 +03:00
28980e5fbf Cleanup 2025-07-03 22:02:36 +03:00
aab0d772a4 Implement client-side window decorations for Editor windows 2025-07-03 22:02:36 +03:00
6296e1a9eb Fix linker errors 2025-06-26 15:32:38 +03:00
1586bb0702 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-26 15:31:59 +03:00
edeaf6af09 Fix compilation without SDL 2025-06-24 22:57:49 +03:00
951edd95db Cleanup 2025-06-24 16:47:45 +03:00
9951211596 Fix mono glib.h wrapper compilation error 2025-06-24 16:47:20 +03:00
6d337464f7 Support XDG Desktop Portal as color picker provider
Wayland and XWayland fallback implementation uses XDP in
order to query picked color from desktop.
2025-06-24 16:41:32 +03:00
d0b552d74a Use logo as an icon in Linux windows 2025-06-23 18:58:02 +03:00
83512822b1 Support clipboard actions in Content Browser and Scene tree on Wayland
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-06-23 17:50:02 +03:00
ccb78103ec Support clipboard actions in Content Browser and Scene tree on X11 2025-06-23 17:07:10 +03:00
6e79ffea34 Fix clipboard not working on X11 2025-06-23 17:07:10 +03:00
4f03d37a17 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-23 12:03:41 +03:00
6fb8419b3c Disable window flashing on Wayland to fix focus issues 2025-06-17 14:23:20 +03:00
a4272d6ca9 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-17 14:19:46 +03:00
4654117d5e Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-16 17:09:22 +03:00
6ff260d052 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-16 14:09:29 +03:00
88d2b72822 Update SDL to 3.3.x
Fixes popup focus issues on Linux
2025-06-16 14:09:04 +03:00
29868531ad SDL workaround for window size and position not updating
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-06-08 18:57:57 +03:00
95dfb6fdc6 Reduce minimum default window size for CommandLineBox popup 2025-06-08 18:57:12 +03:00
8df3999f85 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-04 19:30:30 +03:00
149b189629 Update SDL to 3.2.16 2025-06-02 19:23:34 +03:00
b1fd86e6b5 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-02 18:15:16 +03:00
5e8fdf879d Fix logging in gamepad related events 2025-06-02 18:15:10 +03:00
84ada18299 Fix unlock mouse hotkey not unlocking CursorLockMode.Locked cursor 2025-04-21 23:13:23 +03:00
635fe8017e Fix EditorViewport buttons activating items on scene 2025-04-21 23:13:23 +03:00
834380ff05 Cleanup 2025-04-21 23:13:23 +03:00
dd65fc2289 Implement Wayland text clipboard data handling 2025-04-21 23:13:23 +03:00
86125bb625 Add support for VK_EXT_metal_surface extension 2025-04-21 23:13:23 +03:00
09a9d8b380 Update volk to 1.4.304 2025-04-21 23:13:23 +03:00
86b223ec93 Fix mouse relative mode activation triggering mouse move events on Mac 2025-04-21 23:13:23 +03:00
78f6080321 Initial support for building and running SDL platform on macOS 2025-04-21 23:13:23 +03:00
c7be6f6e0e Fix VC++ project file building on macOS Rider 2025-04-20 01:14:51 +03:00
c40c31fbb7 Reduce lock usage during window events 2025-04-19 21:14:13 +03:00
f5fbc1e32d Fix handling of WindowsManager locks 2025-04-19 21:14:13 +03:00
0a20378acd Handle Wayland dragging actions ending prematurely 2025-04-19 21:14:12 +03:00
d4e87877b6 Fix editor viewport activating while dragging a window 2025-04-19 21:14:12 +03:00
7eb7236b8a Fix occluded or hidden windows causing the engine to freeze on Wayland 2025-04-19 21:14:11 +03:00
dc0e4ffce2 Allow context menu to show when activating scene tree with right click 2025-04-19 21:14:11 +03:00
86fe93943d Fix crash when relative mode was left active on removed window 2025-04-19 21:14:10 +03:00
3258113ef8 Ensure mouse position is up-to-date during button down events 2025-04-19 21:14:10 +03:00
ebf3999cfe Fix rubber band selector activating outside of the viewport
This usually happens while trying to drag the window from window handle
2025-04-19 21:14:09 +03:00
a1ccbbb5b9 Use sensible window minimum size limits in editor windows 2025-04-19 21:14:09 +03:00
35072c40d8 Fix build on Linux 2025-04-19 21:14:08 +03:00
5c51021388 Fix merge 2025-04-19 21:14:08 +03:00
95fd527515 Apply drag and drop styling on refactored WindowDragHelper
Reapplies changes from commit 38b00f458c
2025-04-19 21:14:07 +03:00
113d851cea Fix cursor escaping constrained area when initially outside on Windows 2025-04-19 21:14:07 +03:00
72f0c460f9 Refix dragged maximized window generating wrong mouse position events 2025-04-19 21:14:06 +03:00
13ddd15b44 Focus viewports earlier to fix CursorLockMode not always activating 2025-04-19 21:14:06 +03:00
c9e24aaf5f Fix documentation generation 2025-04-19 21:14:05 +03:00
68c0ac0ffc Fix cursor locking bounds calculation 2025-04-19 21:14:05 +03:00
bebda275a9 Fix crash when Game viewport was hidden 2025-04-19 21:14:04 +03:00
287eaae850 Fix X11 external drag and drop not releasing consistently 2025-04-19 21:14:04 +03:00
7e59c3b9a7 Fix restoring locked cursor state when window gains focus again 2025-04-19 21:14:03 +03:00
38658a5b8c Fix CursorLockMode.Locked not working 2025-04-19 21:14:02 +03:00
88c75b8672 Fix dragged maximized window generating wrong mouse position events 2025-04-19 21:14:02 +03:00
17ab1e6830 Fix window handle clicking to not restore window from maximized state 2025-04-19 21:14:01 +03:00
1f45110e5f Fix rubber band selector activating when using Alt-key to orbit 2025-04-19 21:14:01 +03:00
5401166a07 Fix error when entering playmode while holding right mouse button 2025-04-19 21:14:00 +03:00
bfa8188782 Fix accepting drag and drop for files in Wayland 2025-04-19 21:14:00 +03:00
e777a71784 Fix error while dragging files around Content window 2025-04-19 21:13:59 +03:00
c13e91a0d0 Release rubber band selection when viewport starts controlling the view 2025-04-19 21:13:59 +03:00
aac5d57352 Update SDL to 3.2.10 2025-04-19 21:13:58 +03:00
ceca13c7b6 Avoid showing tooltips in inactive windows 2025-04-19 21:13:58 +03:00
2905470330 Update SDL3 to 3.2.4 2025-04-19 21:13:57 +03:00
2c17033822 Fix window dragging when not supported by Wayland compositor
(cherry picked from commit 3554747a67)
2025-04-19 21:13:57 +03:00
0a4cb9e9b1 Show current display server in Editor window tooltip
(cherry picked from commit 62968dd437)
2025-04-19 21:13:56 +03:00
096651f4c1 Properly mark floating windows with transparency support
(cherry picked from commit c660fac524)
2025-04-19 21:13:56 +03:00
275a08506b Enable transparency support in Vulkan swapchains
(cherry picked from commit 431a69e357)
2025-04-19 21:13:55 +03:00
4cd61cb381 Fix compilation for game builds
(cherry picked from commit f4fcc07288)
2025-04-19 21:13:54 +03:00
9ad1147581 Fix cloning SDL repository 2025-04-19 21:13:54 +03:00
35e09def1b Fix text input not working on X11 2025-04-19 21:13:53 +03:00
0469607a71 Fix button latching on Windows after drag and drop operation 2025-04-19 21:13:53 +03:00
d115d22ee6 Implement new window dragging system 2025-04-19 21:13:52 +03:00
8120ae621d Fix mouse resetting issues after ending relative mode 2025-04-19 21:13:52 +03:00
f873c2fa9f Fix frame stutter when window is focused 2025-04-19 21:13:51 +03:00
c3ffbe2f42 Fix error when docking to sides of tabbed panel 2025-04-19 21:13:51 +03:00
55a0a39881 Cleanup Linux SDL implementation 2025-04-19 21:13:50 +03:00
592215dd30 Support compiling third party library C files as C code 2025-04-19 21:13:50 +03:00
ef0c2a2785 Implement Wayland protocols module and file generation 2025-04-19 21:13:49 +03:00
37979e452a Fix mouse warping after ending relative mode 2025-04-19 21:13:49 +03:00
e9671bb727 Add git fetch method for dependencies 2025-04-19 21:13:48 +03:00
aa328bd591 Fix window ShowInTaskbar setting 2025-04-19 21:13:48 +03:00
188e4313f9 Fix various issues with child window positioning 2025-04-19 21:13:47 +03:00
df02c70e31 Add Window.IsAlwaysOnTop property 2025-04-19 21:13:47 +03:00
257f54b323 Use SDL locale 2025-04-19 21:13:46 +03:00
fb4b5b2575 Allow window with single tab to be dragged from tab area 2025-04-19 21:13:46 +03:00
4e1251276d Fix ValueBox mouse position resetting after releasing the button 2025-04-19 21:13:45 +03:00
a7b200dc57 Fix SDL build process on Linux 2025-04-19 21:13:44 +03:00
8cadbae80e Update SDL to 3.2.0 2025-04-19 21:13:44 +03:00
c3bae49aae Force cursor to center of Game Window when tab handle is clicked 2025-04-19 21:13:43 +03:00
Chandler Cox
6fa4fc6149 Fix rotation using SDL 2025-04-19 21:13:43 +03:00
f1ffe1acaa Fix Linux compilation without SDL 2025-04-19 21:13:42 +03:00
5c9ddf7f00 Fix compilation 2025-04-19 21:13:42 +03:00
431767a150 Update SDL3 2025-04-19 21:13:41 +03:00
8ba4e442a6 Fix compilation issues 2025-04-19 21:13:41 +03:00
2f8d19c267 Fix windows not being hidden initially 2025-04-19 21:13:40 +03:00
f2fd98f742 Fix parent window position handling with popup/tooltip windows 2025-04-19 21:13:40 +03:00
3ce35d6e81 Fix compilation errors in other platforms 2025-04-19 21:13:39 +03:00
796dbaa836 Fix CI for Linux 2025-04-19 21:13:39 +03:00
84b209ec7f Prevent building with SDL in unsupported platforms 2025-04-19 21:13:38 +03:00
fa976b34dc Fallback to X11 message box implementation when SDL fails 2025-04-19 21:13:38 +03:00
e7cda362b7 Fix popup and context menus not working on Wayland 2025-04-19 21:13:37 +03:00
a41a09c162 Hide warnings for unsupported SDL operations on Wayland 2025-04-19 21:13:37 +03:00
e82f84f0ab Log a warning for not implemented Wayland functionality 2025-04-19 21:13:36 +03:00
f1d387ceea Fix compilation in Linux 2025-04-19 21:13:36 +03:00
3893d4d1f8 Enable warning sound in question dialogs 2025-04-19 21:13:35 +03:00
cf7ac50faf Enable modern Windows dialog boxes 2025-04-19 21:13:35 +03:00
3f6bf15554 Implement relative mouse mode (raw input) for SDL platform 2025-04-19 21:13:34 +03:00
dac74829b4 Add flag for Window types 2025-04-19 21:13:34 +03:00
94d6f213a0 Enable native windowing system settings with SDL platform 2025-04-19 21:13:33 +03:00
8f2550ef61 Add command-line switches to force X11 and Wayland SDL drivers 2025-04-19 21:13:33 +03:00
b622a1cc5e Implement SDL platform, windowing and input handling 2025-04-19 21:13:32 +03:00
c83a3c32c7 Refactor application window class name 2025-04-19 21:13:32 +03:00
e7dcf7f7e7 Move Window related enums to separate header file 2025-04-19 21:13:31 +03:00
398785a2be Refactor Windows drag and drop implementation 2025-04-19 21:13:31 +03:00
05dba0f1f5 Refactor ScreenUtilities 2025-04-19 21:13:30 +03:00
39dbd32b2e Add more helper methods for managing Git repos 2025-04-19 21:13:30 +03:00
23a34a455a Fix centered window location on X11 2025-04-19 21:13:29 +03:00
4308328b48 Fix initial position of Tooltips 2025-04-19 21:13:29 +03:00
514 changed files with 98579 additions and 6149 deletions

View File

@@ -31,7 +31,7 @@ body:
- '1.10'
- '1.11'
- master branch
default: 3
default: 2
validations:
required: true
- type: textarea

View File

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

View File

@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v3
- name: Install dependencies
run: |
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
- name: Setup Vulkan
uses: ./.github/actions/vulkan
- name: Setup .NET
@@ -44,7 +44,7 @@ jobs:
uses: actions/checkout@v3
- name: Install dependencies
run: |
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev libwayland-dev
- name: Setup Vulkan
uses: ./.github/actions/vulkan
- name: Setup .NET

View File

@@ -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/Primitives/Cube.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/ColorGrading.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/GI/GlobalSurfaceAtlas.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

BIN
Content/Shaders/MotionBlur.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/SDF.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/VolumetricFog.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -4,7 +4,7 @@
"Major": 1,
"Minor": 11,
"Revision": 0,
"Build": 6805
"Build": 6801
},
"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
}
}

View File

@@ -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
@@ -60,7 +60,7 @@ Follow the instructions below to compile and run the engine from source.
* 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):
* 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`

View File

@@ -282,7 +282,7 @@ namespace FlaxEditor.Content
if (data is DragDataFiles)
return DragDropEffect.Copy;
return _dragOverItems.Effect;
return _dragOverItems?.Effect ?? DragDropEffect.None;
}
/// <inheritdoc />

View File

@@ -20,7 +20,7 @@ namespace FlaxEditor.Content
}
/// <inheritdoc />
public override string TypeDescription => Path.EndsWith(".h") ? "C++ Header File" : "C++ Source Code";
public override string TypeDescription => Path.EndsWith(".h") || Path.EndsWith(".hpp") ? "C++ Header File" : "C++ Source Code";
/// <inheritdoc />
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CPPScript128;

View File

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

View File

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

View File

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

View File

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

View File

@@ -526,7 +526,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 +1367,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, "");
}

View File

@@ -265,7 +265,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 (min {}) for the current host platform."), maxVer, minVer));
return true;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -123,8 +123,6 @@ namespace FlaxEditor.CustomEditors.Editors
{
base.Refresh();
if (Picker == null)
return;
var differentValues = HasDifferentValues;
Picker.DifferentValues = differentValues;
if (!differentValues)

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,10 @@
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
#define USE_IS_FOREGROUND
#else
#endif
#if PLATFORM_SDL
#define USE_SDL_WORKAROUNDS
#endif
// Copyright (c) Wojciech Figat. All rights reserved.
using System.Collections.Generic;
@@ -122,7 +125,7 @@ namespace FlaxEditor.GUI.ContextMenu
}
/// <summary>
/// Shows the empty menu popup o na screen.
/// Shows the empty menu popup on a screen.
/// </summary>
/// <param name="control">The target control.</param>
/// <param name="area">The target control area to cover.</param>
@@ -257,7 +260,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 +271,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 +452,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 +476,7 @@ namespace FlaxEditor.GUI.ContextMenu
});
}
}
#endif
private void OnWindowLostFocus()
{
@@ -552,7 +575,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

View File

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

View File

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

View File

@@ -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.
@@ -196,7 +197,7 @@ namespace FlaxEditor.GUI.Docking
if (_panel.ChildPanelsCount == 0 && _panel.TabsCount == 1 && _panel.IsFloating)
{
// Create docking hint window but in an async manner
DockHintWindow.Create(_panel as FloatWindowDockPanel);
WindowDragHelper.StartDragging(_panel as FloatWindowDockPanel);
}
else
{
@@ -207,7 +208,7 @@ namespace FlaxEditor.GUI.Docking
_panel.SelectTab(index - 1);
// Create docking hint window
DockHintWindow.Create(win);
WindowDragHelper.StartDragging(win, _panel.RootWindow.Window);
}
}
}
@@ -393,6 +394,7 @@ namespace FlaxEditor.GUI.Docking
if (IsSingleFloatingWindow)
return base.OnMouseDown(location, button);
MouseDownWindow = GetTabAtPos(location, out IsMouseDownOverCross);
MouseStartPosition = location;
// Check buttons
if (button == MouseButton.Left)
@@ -479,6 +481,20 @@ namespace FlaxEditor.GUI.Docking
StartDrag(MouseDownWindow);
MouseDownWindow = null;
}
// Check if single tab is tried to be moved
else if (MouseDownWindow != null && _panel.TabsCount <= 1)
{
if ((MousePosition - MouseStartPosition).Length > 3)
{
// Clear flag
IsMouseLeftButtonDown = false;
// Check tab under the mouse
if (!IsMouseDownOverCross && MouseDownWindow != null)
StartDrag(MouseDownWindow);
MouseDownWindow = null;
}
}
// Check if has more than one tab to change order
else if (MouseDownWindow != null && _panel.TabsCount > 1)
{

View File

@@ -182,6 +182,25 @@ namespace FlaxEditor.GUI.Docking
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
/// <param name="position">Window location.</param>
public void ShowFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent)
{
CreateFloating(location, size, position, true);
}
/// <summary>
/// Creates the window in a floating state.
/// </summary>
public void CreateFloating()
{
CreateFloating(Float2.Zero, Float2.Zero);
}
/// <summary>
/// Creates the window in a floating state.
/// </summary>
/// <param name="location">Window location.</param>
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
/// <param name="position">Window location.</param>
/// <param name="showWindow">Window visibility.</param>
public void CreateFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent, bool showWindow = false)
{
Undock();
@@ -199,14 +218,17 @@ namespace FlaxEditor.GUI.Docking
windowGUI.UnlockChildrenRecursive();
windowGUI.PerformLayout();
// Show
window.Show();
window.BringToFront();
window.Focus();
OnShow();
if (showWindow)
{
// Show
window.Show();
window.BringToFront();
window.Focus();
OnShow();
// Perform layout again
windowGUI.PerformLayout();
// Perform layout again
windowGUI.PerformLayout();
}
}
/// <summary>

View File

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

View File

@@ -81,7 +81,6 @@ namespace FlaxEditor.GUI.Docking
public DockPanel HitTest(ref Float2 position, FloatWindowDockPanel excluded)
{
// Check all floating windows
// TODO: gather windows order and take it into account when performing test
for (int i = 0; i < FloatingPanels.Count; i++)
{
var win = FloatingPanels[i];
@@ -94,9 +93,44 @@ namespace FlaxEditor.GUI.Docking
}
// Base
//if (!Root?.RootWindow.Window.IsFocused ?? false)
// return null;
return base.HitTest(ref position);
}
/// <summary>
/// Performs hit test over dock panel.
/// </summary>
/// <param name="position">Window space position to test.</param>
/// <param name="excluded">Floating window to omit during searching (and all docked to that one).</param>
/// <param name="hitResults">Results of the hit test</param>
/// <returns>True if any dock panels were hit, otherwise false.</returns>
public bool HitTest(ref Float2 position, FloatWindowDockPanel excluded, out DockPanel[] hitResults)
{
// Check all floating windows
List<DockPanel> results = new(FloatingPanels.Count);
for (int i = 0; i < FloatingPanels.Count; i++)
{
var win = FloatingPanels[i];
if (win.Visible && win != excluded)
{
var result = win.HitTest(ref position);
if (result != null)
results.Add(result);
}
}
// Base
//if (!Root?.RootWindow.Window.IsFocused ?? false)
// return null;
var baseResult = base.HitTest(ref position);
if (baseResult != null)
results.Add(baseResult);
hitResults = results.ToArray();
return hitResults.Length > 0;
}
internal void LinkWindow(DockWindow window)
{
// Add to the windows list

View File

@@ -0,0 +1,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);
}
}
}

View File

@@ -266,6 +266,7 @@ namespace FlaxEditor.GUI.Input
return base.OnMouseDown(location, button);
}
#if !PLATFORM_SDL
/// <inheritdoc />
public override void OnMouseMove(Float2 location)
{
@@ -292,13 +293,45 @@ namespace FlaxEditor.GUI.Input
base.OnMouseMove(location);
}
#else
/// <inheritdoc />
public override void OnMouseMoveRelative(Float2 mouseMotion)
{
var location = Root.TrackingMouseOffset;
if (_isSliding)
{
// Update sliding
ApplySliding(Root.TrackingMouseOffset.X * _slideSpeed);
return;
}
// Update cursor type so user knows they can slide value
if (CanUseSliding && SlideRect.Contains(location) && !_isSliding)
{
Cursor = CursorType.SizeWE;
_cursorChanged = true;
}
else if (_cursorChanged && !_isSliding)
{
Cursor = CursorType.Default;
_cursorChanged = false;
}
base.OnMouseMoveRelative(mouseMotion);
}
#endif
/// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (button == MouseButton.Left && _isSliding)
{
#if !PLATFORM_SDL
// End sliding and return mouse to original location
RootWindow.MousePosition = _mouseClickedPosition;
#endif
EndSliding();
return true;
}

View File

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

View File

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

View File

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

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

View File

@@ -402,10 +402,11 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
break;
}
}
WindowsManager::WindowsLocker.Unlock();
for (const auto& e : inputEvents)
{
auto window = e.Target ? e.Target : defaultWindow;
if (!window)
if (!window || window->IsClosed())
continue;
switch (e.Type)
{
@@ -435,12 +436,14 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
case InputDevice::EventType::MouseMove:
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
break;
case InputDevice::EventType::MouseMoveRelative:
window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative);
break;
case InputDevice::EventType::MouseLeave:
window->OnMouseLeave();
break;
}
}
WindowsManager::WindowsLocker.Unlock();
}
WindowsManager::WindowsLocker.Lock();
Array<Window*, InlinedAllocation<32>> windows;

View File

@@ -1013,7 +1013,7 @@ namespace FlaxEditor.Modules
ContentItem item;
if (path.EndsWith(".cs"))
item = new CSharpScriptItem(path);
else if (path.EndsWith(".cpp") || path.EndsWith(".h"))
else if (path.EndsWith(".cpp") || path.EndsWith(".h") || path.EndsWith(".c") || path.EndsWith(".hpp"))
item = new CppScriptItem(path);
else if (path.EndsWith(".shader") || path.EndsWith(".hlsl"))
item = new ShaderSourceItem(path);

View File

@@ -222,7 +222,7 @@ namespace FlaxEditor.Modules
outputExtension = extension;
// Check if can place source files here
if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h"))
if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h" || extension == ".c" || extension == ".hpp"))
{
// Error
Editor.LogWarning(string.Format("Cannot import \'{0}\' to \'{1}\'. The target directory cannot have scripts.", inputPath, targetLocation.Node.Path));

View File

@@ -267,7 +267,7 @@ namespace FlaxEditor.Modules
}
/// <inheritdoc />
public override void OnPlayBegin()
public override void OnPlayBeginning()
{
Editor.Windows.FlashMainWindow();

View File

@@ -16,7 +16,6 @@ using FlaxEditor.Windows;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Json;
using DockHintWindow = FlaxEditor.GUI.Docking.DockHintWindow;
using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel;
using FlaxEditor.Content.Settings;
using FlaxEditor.Options;
@@ -29,6 +28,40 @@ namespace FlaxEditor.Modules
/// <seealso cref="FlaxEditor.Modules.EditorModule" />
public sealed class UIModule : EditorModule
{
private class MainWindowDecorations : WindowDecorations
{
public MainWindowDecorations(RootControl window, bool iconOnly)
: base(window, iconOnly)
{
}
/// <inheritdoc />
public override bool OnKeyDown(KeyboardKeys key)
{
if (base.OnKeyDown(key))
return true;
// Fallback to the edit window for shortcuts
var editor = Editor.Instance;
return editor.Windows.EditWin.InputActions.Process(editor, this, key);
}
/// <inheritdoc />
public override void DrawBorders()
{
// Draw main window borders if using a custom style
var win = RootWindow.Window;
if (win.IsMaximized)
return;
var color = Editor.Instance.UI.StatusBar.StatusColor;
var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight);
Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color);
Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color);
Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color);
}
}
private Label _progressLabel;
private ProgressBar _progressBar;
private Button _outputLogButton;
@@ -144,6 +177,11 @@ namespace FlaxEditor.Modules
/// </summary>
public MainMenu MainMenu;
/// <summary>
/// The window decorations (title bar with buttons)
/// </summary>
public WindowDecorations WindowDecorations;
/// <summary>
/// The tool strip control.
/// </summary>
@@ -426,19 +464,11 @@ namespace FlaxEditor.Modules
InitToolstrip(mainWindow);
InitStatusBar(mainWindow);
InitDockPanel(mainWindow);
InitWindowDecorations(mainWindow);
Editor.Options.OptionsChanged += OnOptionsChanged;
// Add dummy control for drawing the main window borders if using a custom style
#if PLATFORM_WINDOWS
if (!Editor.Options.Options.Interface.UseNativeWindowSystem)
#endif
{
mainWindow.AddChild(new CustomWindowBorderControl
{
Size = Float2.Zero,
});
}
mainWindow.PerformLayout(true);
}
private void InitViewportScaleOptions()
@@ -510,23 +540,6 @@ namespace FlaxEditor.Modules
}
}
private class CustomWindowBorderControl : Control
{
/// <inheritdoc />
public override void Draw()
{
var win = RootWindow.Window;
if (win.IsMaximized)
return;
var color = Editor.Instance.UI.StatusBar.StatusColor;
var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight);
Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color);
Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color);
Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color);
}
}
/// <inheritdoc />
public override void OnEndInit()
{
@@ -558,13 +571,6 @@ namespace FlaxEditor.Modules
UpdateToolstrip();
}
/// <inheritdoc />
public override void OnExit()
{
// Cleanup dock panel hint proxy windows (Flax will destroy them by var but it's better to clear them earlier)
DockHintWindow.Proxy.Dispose();
}
private IColorPickerDialog ShowPickColorDialog(Control targetControl, Color initialValue, ColorValueBox.ColorPickerEvent colorChanged, ColorValueBox.ColorPickerClosedEvent pickerClosed, bool useDynamicEditing)
{
var dialog = new ColorPickerDialog(initialValue, colorChanged, pickerClosed, useDynamicEditing);
@@ -618,10 +624,12 @@ namespace FlaxEditor.Modules
private void InitMainMenu(RootControl mainWindow)
{
MainMenu = new MainMenu(mainWindow)
MainMenu = new MainMenu()
{
Parent = mainWindow
};
if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
MainMenu.Height = 28;
var inputOptions = Editor.Options.Options.Input;
@@ -714,7 +722,6 @@ namespace FlaxEditor.Modules
_menuToolsBuildCSGMesh = cm.AddButton("Build CSG mesh", inputOptions.BuildCSG, Editor.BuildCSG);
_menuToolsBuildNavMesh = cm.AddButton("Build Nav Mesh", inputOptions.BuildNav, Editor.BuildNavMesh);
_menuToolsBuildAllMeshesSDF = cm.AddButton("Build all meshes SDF", inputOptions.BuildSDF, Editor.BuildAllMeshesSDF);
_menuToolsBuildAllMeshesSDF.LinkTooltip("Generates Sign Distance Field texture for all meshes used in loaded scenes. Use with 'F' key pressed to force rebuild SDF for meshes with existing one.");
cm.AddSeparator();
cm.AddButton("Game Cooker", Editor.Windows.GameCookerWin.FocusOrShow);
_menuToolsCancelBuilding = cm.AddButton("Cancel building game", () => GameCooker.Cancel());
@@ -763,6 +770,20 @@ namespace FlaxEditor.Modules
cm.AddButton("Information about Flax", () => new AboutDialog().Show());
}
private void InitWindowDecorations(RootControl mainWindow)
{
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
var driver = Platform.DisplayServer;
if (!string.IsNullOrEmpty(driver))
driver = $" ({driver})";
WindowDecorations = new MainWindowDecorations(mainWindow, !Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
{
Parent = mainWindow,
IconTooltipText = $"{mainWindow.RootWindow.Title}\nVersion {Globals.EngineVersion}\nConfiguration {configuration}\nGraphics {GPUDevice.Instance.RendererType}{driver}",
};
}
private void OnOptionsChanged(EditorOptions options)
{
var inputOptions = options.Input;
@@ -1183,6 +1204,7 @@ namespace FlaxEditor.Modules
{
// Clear UI references (GUI cannot be used after window closing)
MainMenu = null;
WindowDecorations = null;
ToolStrip = null;
MasterPanel = null;
StatusBar = null;

View File

@@ -10,7 +10,6 @@ using System.Text;
using System.Xml;
using FlaxEditor.Content;
using FlaxEditor.GUI.Dialogs;
using FlaxEditor.GUI.Docking;
using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets;
using FlaxEditor.Windows.Profiler;
@@ -758,17 +757,19 @@ namespace FlaxEditor.Modules
var settings = CreateWindowSettings.Default;
settings.Title = "Flax Editor";
settings.Size = Platform.DesktopSize * 0.75f;
settings.MinimumSize = new Float2(200, 150);
settings.StartPosition = WindowStartPosition.CenterScreen;
settings.ShowAfterFirstPaint = true;
#if PLATFORM_WINDOWS
if (!Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
{
settings.HasBorder = false;
#if PLATFORM_WINDOWS && !PLATFORM_SDL
// Skip OS sizing frame and implement it using LeftButtonHit
settings.HasSizingFrame = false;
#endif
}
#elif PLATFORM_LINUX
#if PLATFORM_LINUX && !PLATFORM_SDL
settings.HasBorder = false;
#endif
MainWindow = Platform.CreateWindow(ref settings);
@@ -896,11 +897,9 @@ namespace FlaxEditor.Modules
if (type.IsAssignableTo(typeof(AssetEditorWindow)))
{
var ctor = type.GetConstructor(new Type[] { typeof(Editor), typeof(AssetItem) });
var assetItem = Editor.ContentDatabase.FindAsset(winData.AssetItemID);
var assetType = assetItem.GetType();
var ctor = type.GetConstructor(new Type[] { typeof(Editor), assetType });
var win = (AssetEditorWindow)ctor.Invoke(new object[] { Editor.Instance, assetItem });
win.Show(winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue);
if (winData.DockState == DockState.Float)
{

View File

@@ -179,6 +179,34 @@ namespace FlaxEditor.Options
GameWindowThenRestore,
}
/// <summary>
/// Options for type of window decorations to use.
/// </summary>
public enum WindowDecorationsType
{
/// <summary>
/// Determined automatically based on the system and any known compatibility issues with native decorations.
/// </summary>
Auto,
/// <summary>
/// Automatically choose most compatible window decorations for child windows, prefer custom decorations on main window.
/// </summary>
[EditorDisplay(Name = "Auto (Child Only)")]
AutoChildOnly,
/// <summary>
/// Use native system window decorations on all windows.
/// </summary>
Native,
/// <summary>
/// Use custom client-side window decorations on all windows.
/// </summary>
[EditorDisplay(Name = "Client-side")]
ClientSide,
}
/// <summary>
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
/// </summary>
@@ -268,7 +296,14 @@ namespace FlaxEditor.Options
[EditorDisplay("Interface"), EditorOrder(322)]
public bool ScrollToScriptOnAdd { get; set; } = true;
#if PLATFORM_WINDOWS
#if PLATFORM_SDL
/// <summary>
/// Gets or sets a value indicating whether use native window title bar decorations in child windows. Editor restart required.
/// </summary>
[DefaultValue(WindowDecorationsType.AutoChildOnly)]
[EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar decorations. Editor restart required.")]
public WindowDecorationsType WindowDecorations { get; set; } = WindowDecorationsType.AutoChildOnly;
#elif PLATFORM_WINDOWS
/// <summary>
/// Gets or sets a value indicating whether use native window title bar. Editor restart required.
/// </summary>
@@ -277,7 +312,7 @@ namespace FlaxEditor.Options
public bool UseNativeWindowSystem { get; set; } = false;
#endif
#if PLATFORM_WINDOWS
#if PLATFORM_SDL || PLATFORM_WINDOWS
/// <summary>
/// Gets or sets a value indicating whether a window containing a single tabs hides the tab bar. Editor restart recommended.
/// </summary>

View File

@@ -121,9 +121,13 @@ void ScriptsBuilderImpl::sourceDirEvent(const String& path, FileSystemAction act
// Discard non-source files or generated files
if ((!path.EndsWith(TEXT(".cs")) &&
!path.EndsWith(TEXT(".cpp")) &&
!path.EndsWith(TEXT(".c")) &&
!path.EndsWith(TEXT(".hpp")) &&
!path.EndsWith(TEXT(".h"))) ||
path.EndsWith(TEXT(".Gen.cs")))
{
return;
}
ScopeLock scopeLock(_locker);
_lastSourceCodeEdited = DateTime::Now();

View File

@@ -406,8 +406,6 @@ namespace FlaxEngine.Utilities
{
if (type == ScriptType.Null)
return null;
if (type.BaseType == null)
return type.Type;
while (type.Type == null)
type = type.BaseType;
return type.Type;

View File

@@ -726,7 +726,7 @@ namespace FlaxEditor.Surface.Archetypes
private void OnSurfaceMouseUp(ref Float2 mouse, MouseButton buttons, ref bool handled)
{
if (handled || Surface.Context != Context)
if (handled)
return;
// Check click over the connection
@@ -751,7 +751,7 @@ namespace FlaxEditor.Surface.Archetypes
private void OnSurfaceMouseDoubleClick(ref Float2 mouse, MouseButton buttons, ref bool handled)
{
if (handled || Surface.Context != Context)
if (handled)
return;
// Check double click over the connection

View File

@@ -2,8 +2,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Runtime.Serialization.Formatters.Binary;
using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.GUI.ContextMenu;
@@ -15,7 +18,6 @@ namespace FlaxEditor.Surface
class AttributesEditor : ContextMenuBase
{
private CustomEditorPresenter _presenter;
private Proxy _proxy;
private byte[] _oldData;
private class Proxy
@@ -70,11 +72,11 @@ namespace FlaxEditor.Surface
/// Initializes a new instance of the <see cref="AttributesEditor"/> class.
/// </summary>
/// <param name="attributes">The attributes list to edit.</param>
/// <param name="attributeTypes">The allowed attribute types to use.</param>
public AttributesEditor(Attribute[] attributes, IList<Type> attributeTypes)
/// <param name="attributeType">The allowed attribute types to use.</param>
public AttributesEditor(Attribute[] attributes, IList<Type> attributeType)
{
// Context menu dimensions
const float width = 375.0f;
const float width = 340.0f;
const float height = 370.0f;
Size = new Float2(width, height);
@@ -86,68 +88,61 @@ namespace FlaxEditor.Surface
Parent = this
};
// Ok and Cancel Buttons
float buttonsWidth = (width - 12.0f) * 0.5f;
// Buttons
float buttonsWidth = (width - 16.0f) * 0.5f;
float buttonsHeight = 20.0f;
var okButton = new Button(4.0f, Bottom - 4.0f - buttonsHeight, buttonsWidth, buttonsHeight)
{
Text = "Ok",
Parent = this
};
okButton.Clicked += OnOkButtonClicked;
var cancelButton = new Button(okButton.Right + 4.0f, okButton.Y, buttonsWidth, buttonsHeight)
var cancelButton = new Button(4.0f, title.Bottom + 4.0f, buttonsWidth, buttonsHeight)
{
Text = "Cancel",
Parent = this
};
cancelButton.Clicked += Hide;
var okButton = new Button(cancelButton.Right + 4.0f, cancelButton.Y, buttonsWidth, buttonsHeight)
{
Text = "OK",
Parent = this
};
okButton.Clicked += OnOkButtonClicked;
// Actual panel used to display attributes
// Actual panel
var panel1 = new Panel(ScrollBars.Vertical)
{
Bounds = new Rectangle(0, title.Bottom + 4.0f, width, height - buttonsHeight - title.Height - 14.0f),
Bounds = new Rectangle(0, okButton.Bottom + 4.0f, width, height - okButton.Bottom - 2.0f),
Parent = this
};
var editor = new CustomEditorPresenter(null);
editor.Panel.AnchorPreset = AnchorPresets.HorizontalStretchTop;
editor.Panel.IsScrollable = true;
editor.Panel.Parent = panel1;
editor.Panel.Tag = attributeTypes;
editor.Panel.Tag = attributeType;
_presenter = editor;
// Cache 'previous' state to check if attributes were edited after operation
_oldData = SurfaceMeta.GetAttributesData(attributes);
_proxy = new Proxy
editor.Select(new Proxy
{
Value = attributes,
};
editor.Select(_proxy);
_presenter.Modified += OnPresenterModified;
OnPresenterModified();
}
private void OnPresenterModified()
{
if (_proxy.Value.Length == 0)
{
var label = _presenter.Label("No attributes.\nPress the \"+\" button to add a new one and then select an attribute type using the \"Type\" dropdown.", TextAlignment.Center);
label.Label.Wrapping = TextWrapping.WrapWords;
label.Control.Height = 35f;
label.Label.Margin = new Margin(10f);
label.Label.TextColor = label.Label.TextColorHighlighted = Style.Current.ForegroundGrey;
}
});
}
private void OnOkButtonClicked()
{
var newValue = ((Proxy)_presenter.Selection[0]).Value;
newValue = newValue.Where(v => v != null).ToArray();
for (int i = 0; i < newValue.Length; i++)
{
if (newValue[i] == null)
{
MessageBox.Show("One of the attributes is null. Please set it to the valid object.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
var newData = SurfaceMeta.GetAttributesData(newValue);
if (!_oldData.SequenceEqual(newData))
{
Edited?.Invoke(newValue);
}
Hide();
}
@@ -188,9 +183,7 @@ namespace FlaxEditor.Surface
{
_presenter = null;
_oldData = null;
_proxy = null;
Edited = null;
_presenter.Modified -= OnPresenterModified;
base.OnDestroy();
}

View File

@@ -412,7 +412,7 @@ namespace FlaxEditor.Surface.ContextMenu
{
group.UnlockChildrenRecursive();
// TODO: Improve filtering to be based on boxes with the most common things instead of first box
if (_contextSensitiveSearchEnabled && _selectedBoxes.Count > 0 && _selectedBoxes[0] != null)
if (_contextSensitiveSearchEnabled && _selectedBoxes[0] != null)
UpdateFilters();
else
SortGroups();
@@ -424,7 +424,7 @@ namespace FlaxEditor.Surface.ContextMenu
OnSearchFilterChanged();
}
}
else if (_contextSensitiveSearchEnabled && _selectedBoxes.Count > 0)
else if (_contextSensitiveSearchEnabled)
{
// TODO: Filtering could be improved here as well
group.EvaluateVisibilityWithBox(_selectedBoxes[0]);
@@ -462,7 +462,7 @@ namespace FlaxEditor.Surface.ContextMenu
Parent = group
};
}
if (_contextSensitiveSearchEnabled && _selectedBoxes.Count > 0)
if (_contextSensitiveSearchEnabled)
group.EvaluateVisibilityWithBox(_selectedBoxes[0]);
group.SortChildren();
if (ShowExpanded)
@@ -476,7 +476,7 @@ namespace FlaxEditor.Surface.ContextMenu
if (!isLayoutLocked)
{
if (_contextSensitiveSearchEnabled && _selectedBoxes.Count != 0 && _selectedBoxes[0] != null)
if (_contextSensitiveSearchEnabled && _selectedBoxes[0] != null)
UpdateFilters();
else
SortGroups();

View File

@@ -214,26 +214,23 @@ namespace FlaxEditor.Surface
if (!_isRenaming)
Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center);
if (Surface.CanEdit)
// Close button
Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
// Color button
Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
// Check if is resizing
if (_isResizing)
{
// Close button
Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
// Color button
Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
// Check if is resizing
if (_isResizing)
{
// Draw overlay
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
}
// Resize button
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
// Draw overlay
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
}
// Resize button
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
// Selection outline
if (_isSelected)
{

View File

@@ -431,6 +431,27 @@ namespace FlaxEditor.Surface
/// </summary>
public bool HasIndependentBoxes => Archetype.IndependentBoxes != null;
/// <summary>
/// Gets a value indicating whether this node has dependent boxes with assigned valid types. Otherwise any box has no dependent type assigned.
/// </summary>
public bool HasDependentBoxesSetup
{
get
{
if (Archetype.DependentBoxes == null || Archetype.IndependentBoxes == null)
return true;
for (int i = 0; i < Archetype.DependentBoxes.Length; i++)
{
var b = GetBox(Archetype.DependentBoxes[i]);
if (b != null && b.CurrentType == b.DefaultType)
return false;
}
return true;
}
}
private static readonly List<SurfaceNode> UpdateStack = new List<SurfaceNode>();
/// <summary>

View File

@@ -400,7 +400,7 @@ namespace FlaxEditor.Surface
return scriptType.GetGenericTypeDefinition() == typeof(Dictionary<,>);
}
var managedType = TypeUtils.GetType(scriptType);
return managedType != null && !TypeUtils.IsDelegate(managedType);
return !TypeUtils.IsDelegate(managedType);
}
internal static bool IsValidVisualScriptFunctionType(ScriptType scriptType)
@@ -408,7 +408,7 @@ namespace FlaxEditor.Surface
if (scriptType.IsGenericType || scriptType.IsStatic || !scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true))
return false;
var managedType = TypeUtils.GetType(scriptType);
return managedType != null && !TypeUtils.IsDelegate(managedType);
return !TypeUtils.IsDelegate(managedType);
}
internal static string GetVisualScriptTypeDescription(ScriptType type)

View File

@@ -469,8 +469,7 @@ namespace FlaxEditor.Surface
bool handled = base.OnMouseDown(location, button);
if (!handled)
CustomMouseDown?.Invoke(ref location, button, ref handled);
var root = Root;
if (handled || root == null)
if (handled)
{
// Clear flags
_isMovingSelection = false;
@@ -524,11 +523,11 @@ namespace FlaxEditor.Surface
if (_leftMouseDown && controlUnderMouse.CanSelect(ref cLocation))
{
// Check if user is pressing control
if (root.GetKey(KeyboardKeys.Control))
if (Root.GetKey(KeyboardKeys.Control))
{
AddToSelection(controlUnderMouse);
}
else if (root.GetKey(KeyboardKeys.Shift))
else if (Root.GetKey(KeyboardKeys.Shift))
{
RemoveFromSelection(controlUnderMouse);
}
@@ -540,7 +539,7 @@ namespace FlaxEditor.Surface
}
// Start moving selected nodes
if (!root.GetKey(KeyboardKeys.Shift))
if (!Root.GetKey(KeyboardKeys.Shift))
{
StartMouseCapture();
_movingSelectionViewPos = _rootControl.Location;
@@ -560,7 +559,7 @@ namespace FlaxEditor.Surface
// Start selecting or commenting
StartMouseCapture();
if (!root.GetKey(KeyboardKeys.Control) && !root.GetKey(KeyboardKeys.Shift))
if (!Root.GetKey(KeyboardKeys.Control) && !Root.GetKey(KeyboardKeys.Shift))
{
ClearSelection();
}

View File

@@ -178,31 +178,19 @@ namespace FlaxEditor.Surface
// Update boxes types for nodes that dependant box types based on incoming connections
{
bool keepUpdating = true;
int updatesMin = 2, updatesMax = 100;
bool keepUpdating = false;
int updateLimit = 100;
do
{
keepUpdating = false;
for (int i = 0; i < RootControl.Children.Count; i++)
{
if (RootControl.Children[i] is SurfaceNode node)
if (RootControl.Children[i] is SurfaceNode node && !node.HasDependentBoxesSetup)
{
node.UpdateBoxesTypes();
var arch = node.Archetype;
if (arch.DependentBoxes != null && arch.IndependentBoxes != null)
{
foreach (var boxId in arch.DependentBoxes)
{
var b = node.GetBox(boxId);
if (b != null && b.CurrentType == b.DefaultType)
{
keepUpdating = true;
}
}
}
keepUpdating = true;
}
}
} while ((keepUpdating && --updatesMax > 0) || --updatesMin > 0);
} while (keepUpdating && updateLimit-- > 0);
}
Loaded?.Invoke(this);

View File

@@ -74,6 +74,11 @@ struct TextureDataResult
PixelFormat Format;
Int2 Mip0Size;
BytesContainer* Mip0DataPtr;
TextureDataResult()
: Lock(FlaxStorage::LockData::Invalid)
{
}
};
bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool hdr = false)

View File

@@ -1,131 +0,0 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#include "ScreenUtilities.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Log.h"
#include "Engine/Profiler/ProfilerCPU.h"
Delegate<Color32> ScreenUtilities::PickColorDone;
#if PLATFORM_WINDOWS
#include <Windows.h>
#pragma comment(lib, "Gdi32.lib")
static HHOOK MouseCallbackHook;
LRESULT CALLBACK OnScreenUtilsMouseCallback(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
if (nCode >= 0 && wParam == WM_LBUTTONDOWN)
{
UnhookWindowsHookEx(MouseCallbackHook);
// Push event with the picked color
const Float2 cursorPos = Platform::GetMousePosition();
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
ScreenUtilities::PickColorDone(colorPicked);
return 1;
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
{
PROFILE_CPU();
HDC deviceContext = GetDC(NULL);
COLORREF color = GetPixel(deviceContext, (int)pos.X, (int)pos.Y);
ReleaseDC(NULL, deviceContext);
return Color32(GetRValue(color), GetGValue(color), GetBValue(color), 255);
}
void ScreenUtilities::PickColor()
{
MouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, OnScreenUtilsMouseCallback, NULL, NULL);
if (MouseCallbackHook == NULL)
{
LOG(Warning, "Failed to set mouse hook.");
LOG(Warning, "Error: {0}", GetLastError());
}
}
#elif PLATFORM_LINUX
#include "Engine/Platform/Linux/LinuxPlatform.h"
#include "Engine/Platform/Linux/IncludeX11.h"
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
{
X11::XColor color;
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
int defaultScreen = X11::XDefaultScreen(display);
X11::XImage* image;
image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), (int)pos.X, (int)pos.Y, 1, 1, AllPlanes, XYPixmap);
color.pixel = XGetPixel(image, 0, 0);
X11::XFree(image);
X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color);
Color32 outputColor;
outputColor.R = color.red / 256;
outputColor.G = color.green / 256;
outputColor.B = color.blue / 256;
outputColor.A = 255;
return outputColor;
}
void OnScreenUtilsXEventCallback(void* eventPtr)
{
X11::XEvent* event = (X11::XEvent*) eventPtr;
X11::Display* display = (X11::Display*)LinuxPlatform::GetXDisplay();
if (event->type == ButtonPress)
{
const Float2 cursorPos = Platform::GetMousePosition();
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
X11::XUngrabPointer(display, CurrentTime);
ScreenUtilities::PickColorDone(colorPicked);
LinuxPlatform::xEventRecieved.Unbind(OnScreenUtilsXEventCallback);
}
}
void ScreenUtilities::PickColor()
{
PROFILE_CPU();
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
X11::Cursor cursor = XCreateFontCursor(display, 130);
int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime);
if (grabbedPointer != GrabSuccess)
{
LOG(Error, "Failed to grab cursor for events.");
X11::XFreeCursor(display, cursor);
return;
}
X11::XFreeCursor(display, cursor);
LinuxPlatform::xEventRecieved.Bind(OnScreenUtilsXEventCallback);
}
#elif PLATFORM_MAC
#include <Cocoa/Cocoa.h>
#include <AppKit/AppKit.h>
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
{
// TODO: implement ScreenUtilities for macOS
return { 0, 0, 0, 255 };
}
void ScreenUtilities::PickColor()
{
// This is what C# calls to start the color picking sequence
// This should stop mouse clicks from working for one click, and that click is on the selected color
// There is a class called NSColorSample that might implement that for you, but maybe not.
}
#endif

View File

@@ -2,32 +2,4 @@
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
/// <summary>
/// Platform-dependent screen utilities.
/// </summary>
API_CLASS(Static) class FLAXENGINE_API ScreenUtilities
{
DECLARE_SCRIPTING_TYPE_MINIMAL(ScreenUtilities);
/// <summary>
/// Gets the pixel color at the specified coordinates.
/// </summary>
/// <param name="pos">Screen-space coordinate to read.</param>
/// <returns>Pixel color at the specified coordinates.</returns>
API_FUNCTION() static Color32 GetColorAt(const Float2& pos);
/// <summary>
/// Starts async color picking. Color will be returned through PickColorDone event when the actions ends (user selected the final color with a mouse). When action is active, GetColorAt can be used to read the current value.
/// </summary>
API_FUNCTION() static void PickColor();
/// <summary>
/// Called when PickColor action is finished.
/// </summary>
API_EVENT() static Delegate<Color32> PickColorDone;
};
#include "Engine/Platform/ScreenUtilities.h"

View File

@@ -1583,5 +1583,31 @@ namespace FlaxEditor.Utilities
c = c.Parent;
return c as ISceneEditingContext;
}
internal static bool UseCustomWindowDecorations(bool isMainWindow = false)
{
return Editor.Instance.Options.Options.Interface.WindowDecorations switch
{
Options.InterfaceOptions.WindowDecorationsType.Auto => !Platform.SupportsNativeDecorations,
Options.InterfaceOptions.WindowDecorationsType.AutoChildOnly => !isMainWindow ? !Platform.SupportsNativeDecorations : true,
Options.InterfaceOptions.WindowDecorationsType.Native => false,
Options.InterfaceOptions.WindowDecorationsType.ClientSide => true,
_ => throw new ArgumentOutOfRangeException()
};
}
internal static bool HideSingleTabWindowTabBars()
{
#if PLATFORM_SDL
// We should not hide the tab bars if tab handle is the only way to dock the window
bool clientSideDecorations = UseCustomWindowDecorations(false);
bool draggableDecorations = clientSideDecorations || Platform.SupportsNativeDecorationDragging;
return draggableDecorations && Editor.Instance.Options.Options.Interface.HideSingleTabWindowTabBars;
#elif PLATFORM_WINDOWS
return Editor.Instance.Options.Options.Interface.HideSingleTabWindowTabBars;
#else
return false;
#endif
}
}
}

View File

@@ -77,7 +77,7 @@ namespace FlaxEditor.Viewport
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
/// <inheritdoc />
public Float2 MouseDelta => _mouseDelta;
public Float2 MouseDelta => FlaxEngine.Input.MousePositionDelta;
/// <inheritdoc />
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;

View File

@@ -4,6 +4,7 @@ using System;
using System.Linq;
using FlaxEditor.Content.Settings;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Docking;
using FlaxEditor.GUI.Input;
using FlaxEditor.Options;
using FlaxEditor.Viewport.Cameras;
@@ -158,18 +159,22 @@ namespace FlaxEditor.Viewport
private float _movementSpeed;
private float _minMovementSpeed;
private float _maxMovementSpeed;
#if !PLATFORM_SDL
private float _mouseAccelerationScale;
private bool _useMouseFiltering;
private bool _useMouseAcceleration;
#endif
// Input
internal bool _disableInputUpdate;
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
private int _deltaFilteringStep;
private Float2 _startPos;
#if !PLATFORM_SDL
private Float2 _mouseDeltaLast;
private int _deltaFilteringStep;
private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames];
#endif
/// <summary>
/// The previous input (from the previous update).
@@ -522,10 +527,11 @@ namespace FlaxEditor.Viewport
: base(task)
{
_editor = Editor.Instance;
#if !PLATFORM_SDL
_mouseAccelerationScale = 0.1f;
_useMouseFiltering = false;
_useMouseAcceleration = false;
#endif
_camera = camera;
if (_camera != null)
_camera.Viewport = this;
@@ -1525,7 +1531,9 @@ namespace FlaxEditor.Viewport
// Hide cursor and start tracking mouse movement
win.StartTrackingMouse(false);
win.Cursor = CursorType.Hidden;
win.MouseMoveRelative += OnMouseMoveRelative;
#if !PLATFORM_SDL
// Center mouse position if it's too close to the edge
var size = Size;
var center = Float2.Round(size * 0.5f);
@@ -1534,6 +1542,7 @@ namespace FlaxEditor.Viewport
_viewMousePos = center;
win.MousePosition = PointToWindow(_viewMousePos);
}
#endif
}
/// <summary>
@@ -1545,6 +1554,7 @@ namespace FlaxEditor.Viewport
// Restore cursor and stop tracking mouse movement
win.Cursor = CursorType.Default;
win.EndTrackingMouse();
win.MouseMoveRelative -= OnMouseMoveRelative;
}
/// <summary>
@@ -1626,18 +1636,15 @@ namespace FlaxEditor.Viewport
// Get parent window
var win = (WindowRootControl)Root;
// Get current mouse position in the view
if (win.IsFocused)
{
// When the window is not focused, the position in window does not return sane values
Float2 pos = PointFromWindow(win.MousePosition);
if (!float.IsInfinity(pos.LengthSquared))
_viewMousePos = pos;
// Get current mouse position in the view
_viewMousePos = PointFromWindow(win.MousePosition);
}
// Update input
var window = win.Window;
var canUseInput = window != null && window.IsFocused && window.IsForegroundWindow;
var canUseInput = window != null && window.IsFocused && window.IsForegroundWindow && !WindowDragHelper.IsDragActive;
{
// Get input buttons and keys (skip if viewport has no focus or mouse is over a child control)
var isViewportControllingMouse = canUseInput && IsControllingMouse;
@@ -1649,9 +1656,17 @@ namespace FlaxEditor.Viewport
else
EndMouseCapture();
}
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
_prevInput = _input;
#if PLATFORM_SDL
bool useMouse = IsControllingMouse || ContainsPoint(ref _viewMousePos) || _prevInput.IsControllingMouse;
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
if (_prevInput.IsControllingMouse)
hit = null;
#else
bool useMouse = IsControllingMouse || ContainsPoint(ref _viewMousePos);
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
#endif
if (canUseInput && ContainsFocus && hit == null)
_input.Gather(win.Window, useMouse, ref _prevInput);
else
@@ -1764,6 +1779,10 @@ namespace FlaxEditor.Viewport
if (_input.IsControlDown)
moveDelta *= 0.3f;
#if PLATFORM_SDL
var mouseDelta = _mouseDelta;
_mouseDelta = Float2.Zero;
#else
// Calculate smooth mouse delta not dependant on viewport size
var offset = _viewMousePos - _startPos;
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown)
@@ -1805,6 +1824,7 @@ namespace FlaxEditor.Viewport
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
_mouseDeltaLast = currentDelta;
}
#endif
// Update
moveDelta *= dt * (60.0f * 4.0f);
@@ -1813,12 +1833,14 @@ namespace FlaxEditor.Viewport
mouseDelta *= new Float2(1, -1);
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
#if !PLATFORM_SDL
// Move mouse back to the root position
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown))
{
var center = PointToWindow(_startPos);
win.MousePosition = center;
}
#endif
// Change Ortho size on mouse scroll
if (_isOrtho && !rmbWheel)
@@ -1830,6 +1852,8 @@ namespace FlaxEditor.Viewport
}
else
{
#if PLATFORM_SDL
#else
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
{
// Calculate smooth mouse delta not dependant on viewport size
@@ -1844,6 +1868,7 @@ namespace FlaxEditor.Viewport
_mouseDelta = Float2.Zero;
}
_mouseDeltaLast = Float2.Zero;
#endif
if (ContainsFocus)
{
@@ -1893,6 +1918,12 @@ namespace FlaxEditor.Viewport
_input.MouseWheelDelta = 0;
}
/// <inheritdoc />
public void OnMouseMoveRelative(ref Float2 mouseMotion)
{
_mouseDelta += mouseMotion;
}
/// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button)
{
@@ -1958,7 +1989,8 @@ namespace FlaxEditor.Viewport
if (_isControllingMouse)
{
OnControlMouseEnd(RootWindow.Window);
if (RootWindow?.Window != null)
OnControlMouseEnd(RootWindow.Window);
_isControllingMouse = false;
_isVirtualMouseRightDown = false;
}

View File

@@ -611,16 +611,25 @@ namespace FlaxEditor.Viewport
// Don't allow rubber band selection when gizmo is controlling mouse, vertex painting mode, or cloth painting is enabled
bool canStart = !(IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown) &&
Gizmos?.Active is TransformGizmo && !Gizmos.Active.IsControllingMouse;
Gizmos?.Active is TransformGizmo;
_rubberBandSelector.TryCreateRubberBand(canStart, _viewMousePos);
}
/// <inheritdoc />
protected override void OnControlMouseBegin(Window win)
{
_rubberBandSelector.ReleaseRubberBandSelection();
base.OnControlMouseBegin(win);
}
/// <inheritdoc />
protected override void OnLeftMouseButtonDown()
{
base.OnLeftMouseButtonDown();
_rubberBandSelector.TryStartingRubberBandSelection();
if (!IsAltKeyDown)
_rubberBandSelector.TryStartingRubberBandSelection();
}
/// <inheritdoc />

View File

@@ -74,7 +74,7 @@ namespace FlaxEditor.Viewport
private PrefabUIEditorRoot _uiRoot;
private bool _showUI = false;
private int _defaultScaleActiveIndex = -1;
private int _defaultScaleActiveIndex = 0;
private int _customScaleActiveIndex = -1;
private ContextMenuButton _uiModeButton;
private ContextMenuChildMenu _uiViewOptions;
@@ -257,23 +257,16 @@ namespace FlaxEditor.Viewport
/// </summary>
public void SaveActiveUIScalingOption()
{
if (!Prefab)
return;
var id = Prefab.ID;
var defaultKey = $"{id}:DefaultViewportScalingIndex";
var defaultKey = $"{Prefab.ID}:DefaultViewportScalingIndex";
Editor.Instance.ProjectCache.SetCustomData(defaultKey, _defaultScaleActiveIndex.ToString());
var customKey = $"{id}:CustomViewportScalingIndex";
var customKey = $"{Prefab.ID}:CustomViewportScalingIndex";
Editor.Instance.ProjectCache.SetCustomData(customKey, _customScaleActiveIndex.ToString());
}
private void LoadCustomUIScalingOption()
{
if (!Prefab)
return;
var id = Prefab.ID;
Prefab.WaitForLoaded();
var defaultKey = $"{id}:DefaultViewportScalingIndex";
var defaultKey = $"{Prefab.ID}:DefaultViewportScalingIndex";
if (Editor.Instance.ProjectCache.TryGetCustomData(defaultKey, out string defaultData))
{
if (int.TryParse(defaultData, out var index))
@@ -293,7 +286,7 @@ namespace FlaxEditor.Viewport
}
}
var customKey = $"{id}:CustomViewportScalingIndex";
var customKey = $"{Prefab.ID}:CustomViewportScalingIndex";
if (Editor.Instance.ProjectCache.TryGetCustomData(customKey, out string data))
{
if (int.TryParse(data, out var index))

View File

@@ -130,6 +130,9 @@ namespace FlaxEditor.Windows
"Mono Project - www.mono-project.com",
#if USE_NETCORE
".NET - www.dotnet.microsoft.com",
#endif
#if PLATFORM_SDL
"Simple DirectMedia Layer - www.libsdl.org",
#endif
"FreeType Project - www.freetype.org",
"Assimp - www.assimp.sourceforge.net",

View File

@@ -140,7 +140,7 @@ namespace FlaxEditor.Windows
}
private string _cacheFolder;
private AssetItem _item;
private Guid _assetId;
private Surface _surface;
private Label _loadingLabel;
private CancellationTokenSource _token;
@@ -163,13 +163,13 @@ namespace FlaxEditor.Windows
public AssetReferencesGraphWindow(Editor editor, AssetItem assetItem)
: base(editor, false, ScrollBars.None)
{
_item = assetItem;
Title = _item.ShortName + " References";
Title = assetItem.ShortName + " References";
_tempFolder = StringUtils.NormalizePath(Path.GetDirectoryName(Globals.TemporaryFolder));
_cacheFolder = Path.Combine(Globals.ProjectCacheFolder, "References");
if (!Directory.Exists(_cacheFolder))
Directory.CreateDirectory(_cacheFolder);
_assetId = assetItem.ID;
_surface = new Surface(this)
{
AnchorPreset = AnchorPresets.StretchAll,
@@ -194,7 +194,6 @@ namespace FlaxEditor.Windows
_nodesAssets.Add(assetId);
var node = new AssetNode((uint)_nodes.Count + 1, _surface.Context, GraphNodes[0], GraphGroups[0], assetId);
_nodes.Add(node);
return node;
}
@@ -393,7 +392,8 @@ namespace FlaxEditor.Windows
_nodesAssets = new HashSet<Guid>();
var searchLevel = 4; // TODO: make it as an option (somewhere in window UI)
// TODO: add option to filter assets by type (eg. show only textures as leaf nodes)
var assetNode = SpawnNode(_item.ID);
var assetNode = SpawnNode(_assetId);
// TODO: add some outline or tint color to the main node
BuildGraph(assetNode, searchLevel, false);
ArrangeGraph(assetNode, false);
BuildGraph(assetNode, searchLevel, true);
@@ -402,10 +402,6 @@ namespace FlaxEditor.Windows
return;
_progress = 100.0f;
var commentRect = assetNode.EditorBounds;
commentRect.Expand(80f);
_surface.Context.CreateComment(ref commentRect, _item.ShortName, Color.Green);
// Update UI
FlaxEngine.Scripting.InvokeOnUpdate(() =>
{

View File

@@ -14,6 +14,7 @@ using FlaxEditor.Surface;
using FlaxEditor.Viewport.Previews;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Utilities;
namespace FlaxEditor.Windows.Assets
{
@@ -429,7 +430,7 @@ namespace FlaxEditor.Windows.Assets
for (var i = 0; i < parameters.Length; i++)
{
var p = parameters[i];
if (p.IsOverride && p.IsPublic)
if (p.IsOverride)
{
p.IsOverride = false;
actions.Add(new EditParamOverrideAction

View File

@@ -90,15 +90,25 @@ namespace FlaxEditor.Windows.Assets
var gpu = group.Checkbox("Bake on GPU", "If checked, SDF generation will be calculated using GPU on Compute Shader, otherwise CPU will use Job System. GPU generation is fast but result in artifacts in various meshes (eg. foliage).");
gpu.CheckBox.Checked = sdfOptions.GPU;
gpu.CheckBox.StateChanged += c => { Window._sdfOptions.GPU = c.Checked; };
var backfacesThresholdProp = group.AddPropertyItem("Backfaces Threshold", "Custom threshold (in range 0-1) for adjusting mesh internals detection based on the percentage of test rays hit triangle backfaces. Use lower value for more dense mesh.");
var backfacesThreshold = backfacesThresholdProp.FloatValue();
var backfacesThresholdLabel = backfacesThresholdProp.Labels.Last();
backfacesThreshold.ValueBox.MinValue = 0.001f;
backfacesThreshold.ValueBox.MaxValue = 1.0f;
backfacesThreshold.ValueBox.Value = sdfOptions.BackfacesThreshold;
backfacesThreshold.ValueBox.BoxValueChanged += b => { Window._sdfOptions.BackfacesThreshold = b.Value; };
// Toggle Backfaces Threshold visibility (CPU-only option)
gpu.CheckBox.StateChanged += c =>
{
Window._sdfOptions.GPU = c.Checked;
backfacesThresholdLabel.Visible = !c.Checked;
backfacesThreshold.ValueBox.Visible = !c.Checked;
};
backfacesThresholdLabel.Visible = !gpu.CheckBox.Checked;
backfacesThreshold.ValueBox.Visible = !gpu.CheckBox.Checked;
var lodIndex = group.IntegerValue("LOD Index", "Index of the model Level of Detail to use for SDF data building. By default uses the lowest quality LOD for fast building.");
lodIndex.IntValue.MinValue = 0;
lodIndex.IntValue.MaxValue = Asset.LODsCount - 1;

View File

@@ -142,7 +142,6 @@ namespace FlaxEditor.Windows
{
Title = "Content";
Icon = editor.Icons.Folder32;
var style = Style.Current;
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
@@ -165,8 +164,6 @@ namespace FlaxEditor.Windows
_navigationBar = new NavigationBar
{
Parent = _toolStrip,
ScrollbarTrackColor = style.Background,
ScrollbarThumbColor = style.ForegroundGrey,
};
// Split panel
@@ -182,7 +179,7 @@ namespace FlaxEditor.Windows
var headerPanel = new ContainerControl
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
BackgroundColor = style.Background,
BackgroundColor = Style.Current.Background,
IsScrollable = false,
Offsets = new Margin(0, 0, 0, 18 + 6),
};

View File

@@ -10,117 +10,17 @@ using FlaxEditor.Modules;
using FlaxEditor.Options;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Json;
namespace FlaxEditor.Windows
{
/// <summary>
/// Render output control with content scaling support.
/// </summary>
public class ScaledRenderOutputControl : RenderOutputControl
{
/// <summary>
/// Custom scale.
/// </summary>
public float ContentScale = 1.0f;
/// <summary>
/// Actual bounds size for content (incl. scale).
/// </summary>
public Float2 ContentSize => Size / ContentScale;
/// <inheritdoc />
public ScaledRenderOutputControl(SceneRenderTask task)
: base(task)
{
}
/// <inheritdoc />
public override void Draw()
{
DrawSelf();
// Draw children with scale
var scaling = new Float3(ContentScale, ContentScale, 1);
Matrix3x3.Scaling(ref scaling, out Matrix3x3 scale);
Render2D.PushTransform(scale);
if (ClipChildren)
{
GetDesireClientArea(out var clientArea);
Render2D.PushClip(ref clientArea);
DrawChildren();
Render2D.PopClip();
}
else
{
DrawChildren();
}
Render2D.PopTransform();
}
/// <inheritdoc />
public override void GetDesireClientArea(out Rectangle rect)
{
// Scale the area for the client controls
rect = new Rectangle(Float2.Zero, Size / ContentScale);
}
/// <inheritdoc />
public override bool IntersectsContent(ref Float2 locationParent, out Float2 location)
{
// Skip local PointFromParent but use base code
location = base.PointFromParent(ref locationParent);
return ContainsPoint(ref location);
}
/// <inheritdoc />
public override bool IntersectsChildContent(Control child, Float2 location, out Float2 childSpaceLocation)
{
location /= ContentScale;
return base.IntersectsChildContent(child, location, out childSpaceLocation);
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise) // Ignore as utility-only element
return false;
return base.ContainsPoint(ref location, precise);
}
/// <inheritdoc />
public override bool RayCast(ref Float2 location, out Control hit)
{
var p = location / ContentScale;
if (RayCastChildren(ref p, out hit))
return true;
return base.RayCast(ref location, out hit);
}
/// <inheritdoc />
public override Float2 PointToParent(ref Float2 location)
{
var result = base.PointToParent(ref location);
result *= ContentScale;
return result;
}
/// <inheritdoc />
public override Float2 PointFromParent(ref Float2 location)
{
var result = base.PointFromParent(ref location);
result /= ContentScale;
return result;
}
}
/// <summary>
/// Provides in-editor play mode simulation.
/// </summary>
/// <seealso cref="FlaxEditor.Windows.EditorWindow" />
public class GameWindow : EditorWindow
{
private readonly ScaledRenderOutputControl _viewport;
private readonly RenderOutputControl _viewport;
private readonly GameRoot _guiRoot;
private bool _showGUI = true, _editGUI = true;
private bool _showDebugDraw = false;
@@ -177,7 +77,7 @@ namespace FlaxEditor.Windows
/// <summary>
/// Gets the viewport.
/// </summary>
public ScaledRenderOutputControl Viewport => _viewport;
public RenderOutputControl Viewport => _viewport;
/// <summary>
/// Gets or sets a value indicating whether show game GUI in the view or keep it hidden.
@@ -395,7 +295,7 @@ namespace FlaxEditor.Windows
var task = MainRenderTask.Instance;
// Setup viewport
_viewport = new ScaledRenderOutputControl(task)
_viewport = new RenderOutputControl(task)
{
AnchorPreset = AnchorPresets.StretchAll,
Offsets = Margin.Zero,
@@ -496,8 +396,11 @@ namespace FlaxEditor.Windows
{
if (v == null)
return;
if (v.Size.Y <= 0 || v.Size.X <= 0)
{
return;
}
if (string.Equals(v.Label, "Free Aspect", StringComparison.Ordinal) && v.Size == new Int2(1, 1))
{
@@ -545,7 +448,15 @@ namespace FlaxEditor.Windows
private void ResizeViewport()
{
_windowAspectRatio = _freeAspect ? 1 : Width / Height;
if (!_freeAspect)
{
_windowAspectRatio = Width / Height;
}
else
{
_windowAspectRatio = 1;
}
var scaleWidth = _viewportAspectRatio / _windowAspectRatio;
var scaleHeight = _windowAspectRatio / _viewportAspectRatio;
@@ -557,24 +468,6 @@ namespace FlaxEditor.Windows
{
_viewport.Bounds = new Rectangle(Width * (1 - scaleWidth) / 2, 0, Width * scaleWidth, Height);
}
if (_viewport.KeepAspectRatio)
{
var resolution = _viewport.CustomResolution.HasValue ? (Float2)_viewport.CustomResolution.Value : Size;
if (scaleHeight < 1)
{
_viewport.ContentScale = _viewport.Width / resolution.X;
}
else
{
_viewport.ContentScale = _viewport.Height / resolution.Y;
}
}
else
{
_viewport.ContentScale = 1;
}
_viewport.SyncBackbufferSize();
PerformLayout();
}
@@ -874,7 +767,7 @@ namespace FlaxEditor.Windows
_cursorVisible = Screen.CursorVisible;
_cursorLockMode = Screen.CursorLock;
Screen.CursorVisible = true;
if (Screen.CursorLock == CursorLockMode.Clipped)
if (Screen.CursorLock == CursorLockMode.Clipped || Screen.CursorLock == CursorLockMode.Locked)
Screen.CursorLock = CursorLockMode.None;
// Defocus
@@ -963,8 +856,11 @@ namespace FlaxEditor.Windows
if (Editor.StateMachine.IsPlayMode && !Editor.StateMachine.PlayingState.IsPaused)
{
// Make sure the cursor is always in the viewport when cursor is locked
bool forceCenter = _cursorLockMode != CursorLockMode.None && !IsMouseOver;
// Center mouse in play mode
if (CenterMouseOnFocus)
if (CenterMouseOnFocus || forceCenter)
{
var center = PointToWindow(Size * 0.5f);
Root.MousePosition = center;
@@ -989,15 +885,16 @@ namespace FlaxEditor.Windows
_cursorVisible = Screen.CursorVisible;
_cursorLockMode = Screen.CursorLock;
// Restore cursor visibility (could be hidden by the game)
// Restore cursor state, could be hidden or locked by the game
if (!_cursorVisible)
Screen.CursorVisible = true;
Screen.CursorLock = CursorLockMode.None;
}
if (Editor.IsPlayMode && IsDocked && IsSelected && RootWindow.FocusedControl == null)
{
// Game UI cleared focus so regain it to maintain UI navigation just like game window does
FlaxEngine.Scripting.InvokeOnUpdate(Focus);
}
if (Editor.IsPlayMode && IsDocked && IsSelected && RootWindow.FocusedControl == null)
{
// Game UI cleared focus so regain it to maintain UI navigation just like game window does
FlaxEngine.Scripting.InvokeOnUpdate(Focus);
}
}
@@ -1014,7 +911,6 @@ namespace FlaxEditor.Windows
return child.OnNavigate(direction, Float2.Zero, this, visited);
}
}
return null;
}
@@ -1065,7 +961,7 @@ namespace FlaxEditor.Windows
else
_defaultScaleActiveIndex = 0;
}
if (_customScaleActiveIndex != -1)
{
var options = Editor.UI.CustomViewportScaleOptions;

View File

@@ -296,15 +296,13 @@ namespace FlaxEditor.Windows.Profiler
var resources = _resources.Get(_memoryUsageChart.SelectedSampleIndex);
if (resources == null || resources.Length == 0)
return;
var resourcesOrdered = resources.OrderByDescending(x => x?.MemoryUsage ?? 0);
var resourcesOrdered = resources.OrderByDescending(x => x.MemoryUsage);
// Add rows
var rowColor2 = Style.Current.Background * 1.4f;
int rowIndex = 0;
foreach (var e in resourcesOrdered)
{
if (e == null)
continue;
ClickableRow row;
if (_tableRowsCache.Count != 0)
{

View File

@@ -26,7 +26,6 @@ namespace FlaxEditor.Windows
private Tree _tree;
private Panel _sceneTreePanel;
private bool _isUpdatingSelection;
private bool _isMouseDown;
private bool _blockSceneTreeScroll = false;
private DragAssets _dragAssets;
@@ -319,22 +318,22 @@ namespace FlaxEditor.Windows
{
if (assetItem.IsOfType<SceneAsset>())
return true;
return assetItem.OnEditorDrag(this) && Level.IsAnySceneLoaded;
return assetItem.OnEditorDrag(this);
}
private static bool ValidateDragActorType(ScriptType actorType)
{
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType) && Level.IsAnySceneLoaded;
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
}
private static bool ValidateDragControlType(ScriptType controlType)
{
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType) && Level.IsAnySceneLoaded;
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType);
}
private static bool ValidateDragScriptItem(ScriptItem script)
{
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null && Level.IsAnySceneLoaded;
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null;
}
/// <inheritdoc />
@@ -374,10 +373,7 @@ namespace FlaxEditor.Windows
return true;
if (buttons == MouseButton.Right)
{
_isMouseDown = true;
return true;
}
return false;
}
@@ -388,10 +384,8 @@ namespace FlaxEditor.Windows
if (base.OnMouseUp(location, buttons))
return true;
if (_isMouseDown && buttons == MouseButton.Right)
if (buttons == MouseButton.Right)
{
_isMouseDown = false;
if (Editor.StateMachine.CurrentState.CanEditScene)
{
// Show context menu
@@ -416,14 +410,6 @@ namespace FlaxEditor.Windows
return false;
}
/// <inheritdoc />
public override void OnLostFocus()
{
_isMouseDown = false;
base.OnLostFocus();
}
/// <inheritdoc />
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
{
@@ -490,7 +476,6 @@ namespace FlaxEditor.Windows
if (result == DragDropEffect.None)
{
_isDropping = true;
// Drag assets
if (_dragAssets != null && _dragAssets.HasValidDrag)
{
@@ -505,7 +490,7 @@ namespace FlaxEditor.Windows
}
var actor = item.OnEditorDrop(this);
actor.Name = item.ShortName;
Editor.SceneEditing.Spawn(actor);
Level.SpawnActor(actor);
var graphNode = Editor.Scene.GetActorNode(actor.ID);
if (graphNode != null)
graphNodes.Add(graphNode);
@@ -528,7 +513,7 @@ namespace FlaxEditor.Windows
continue;
}
actor.Name = item.Name;
Editor.SceneEditing.Spawn(actor);
Level.SpawnActor(actor);
Editor.Scene.MarkSceneEdited(actor.Scene);
}
result = DragDropEffect.Move;
@@ -550,7 +535,7 @@ namespace FlaxEditor.Windows
Control = control,
Name = item.Name,
};
Editor.SceneEditing.Spawn(uiControl);
Level.SpawnActor(uiControl);
Editor.Scene.MarkSceneEdited(uiControl.Scene);
}
result = DragDropEffect.Move;
@@ -572,7 +557,7 @@ namespace FlaxEditor.Windows
continue;
}
actor.Name = actorType.Name;
Editor.SceneEditing.Spawn(actor);
Level.SpawnActor(actor);
var graphNode = Editor.Scene.GetActorNode(actor.ID);
if (graphNode != null)
graphNodes.Add(graphNode);

View File

@@ -29,7 +29,7 @@ const Char* SplashScreenQuotes[] =
#elif PLATFORM_LINUX
TEXT("Try it on a Raspberry"),
TEXT("Trying to exit vim"),
TEXT("Sudo flax --loadproject"),
TEXT("sudo flax --project HelloWorld.flaxproj"),
#elif PLATFORM_MAC
TEXT("don't compare Macbooks to oranges."),
TEXT("Why does macbook heat up?\nBecause it doesn't have windows"),
@@ -105,6 +105,7 @@ const Char* SplashScreenQuotes[] =
TEXT("You have my bow.\nAnd my axe!"),
TEXT("To the bridge of Khazad-dum."),
TEXT("One ring to rule them all.\nOne ring to find them."),
TEXT("Where there's a whip, there's a way."),
TEXT("That's what she said"),
TEXT("We could be compiling shaders here"),
TEXT("Hello There"),
@@ -137,7 +138,6 @@ const Char* SplashScreenQuotes[] =
TEXT("Good Luck Have Fun"),
TEXT("GG Well Played"),
TEXT("Now with documentation."),
TEXT("We do this not because it is easy,\nbut because we thought it would be easy"),
};
SplashScreen::~SplashScreen()
@@ -166,7 +166,7 @@ void SplashScreen::Show()
settings.AllowMaximize = false;
settings.AllowDragAndDrop = false;
settings.IsTopmost = false;
settings.IsRegularWindow = false;
settings.Type = WindowType::Utility;
settings.HasSizingFrame = false;
settings.ShowAfterFirstPaint = true;
settings.StartPosition = WindowStartPosition::CenterScreen;

View File

@@ -246,19 +246,11 @@ void AnimGraphExecutor::ProcessAnimEvents(AnimGraphNode* node, bool loop, float
const float duration = k.Value.Duration > 1 ? k.Value.Duration : 0.0f;
#define ADD_OUTGOING_EVENT(type) context.Data->OutgoingEvents.Add({ k.Value.Instance, (AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime, AnimGraphInstanceData::OutgoingEvent::type })
if ((k.Time <= eventTimeMax && eventTimeMin <= k.Time + duration
&& (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration())
&& Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration())))
&& (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration()) && Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration())))
// Handle the edge case of an event on 0 or on max animation duration during looping
|| (!loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration()))
|| (loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == anim->GetDuration())
|| (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f)
|| (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f)
|| (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration()))
|| (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration()))
|| (Math::FloorToInt(animPos) == 1 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 1.0f)
|| (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 1 && k.Time == 1.0f)
|| (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f))
|| (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f))
|| (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 0.0f)
)
{
int32 stateIndex = -1;
@@ -2441,14 +2433,10 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
{
if (bucket.LoopsLeft == 0)
{
// End playing animation and reset bucket params
// End playing animation
value = tryGetValue(node->GetBox(1), Value::Null);
bucket.Index = -1;
slot.Animation = nullptr;
bucket.TimePosition = 0.0f;
bucket.BlendInPosition = 0.0f;
bucket.BlendOutPosition = 0.0f;
bucket.LoopsDone = 0;
return;
}
@@ -2557,15 +2545,9 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va
// Function Input
case 1:
{
// Skip when graph is too small (eg. preview) and fallback with default value from the function graph
if (context.GraphStack.Count() < 2)
{
value = tryGetValue(node->TryGetBox(1), Value::Zero);
break;
}
// Find the function call
AnimGraphNode* functionCallNode = nullptr;
ASSERT(context.GraphStack.Count() >= 2);
Graph* graph;
for (int32 i = context.CallStack.Count() - 1; i >= 0; i--)
{

View File

@@ -666,7 +666,7 @@ void Asset::onLoaded()
{
onLoaded_MainThread();
}
else if (OnLoaded.IsBinded() || _references.HasItems())
else if (OnLoaded.IsBinded())
{
Function<void()> action;
action.Bind<Asset, &Asset::onLoaded>(this);

View File

@@ -218,14 +218,10 @@ Asset::LoadResult MaterialInstance::load()
Guid baseMaterialId;
headerStream.Read(baseMaterialId);
auto baseMaterial = Content::LoadAsync<MaterialBase>(baseMaterialId);
if (baseMaterial)
baseMaterial->AddReference();
// Load parameters
if (Params.Load(&headerStream))
{
if (baseMaterial)
baseMaterial->RemoveReference();
LOG(Warning, "Cannot load material parameters.");
return LoadResult::CannotLoadData;
}
@@ -243,8 +239,6 @@ Asset::LoadResult MaterialInstance::load()
ParamsChanged();
}
if (baseMaterial)
baseMaterial->RemoveReference();
return LoadResult::Ok;
}

View File

@@ -262,7 +262,6 @@ bool Model::GenerateSDF(float resolutionScale, int32 lodIndex, bool cacheData, f
LOG(Warning, "Cannot generate SDF for virtual models on a main thread.");
return true;
}
auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData();
lodIndex = Math::Clamp(lodIndex, HighestResidentLODIndex(), LODs.Count() - 1);
// Generate SDF

View File

@@ -61,7 +61,7 @@ public:
model->GetLODData(_lodIndex, data);
if (data.IsInvalid())
{
LOG(Warning, "Missing data chunk with LOD{} for model '{}'", _lodIndex, model->ToString());
LOG(Warning, "Missing data chunk");
return true;
}
MemoryReadStream stream(data.Get(), data.Length());
@@ -234,7 +234,6 @@ bool ModelBase::Save(bool withMeshDataFromGpu, const StringView& path)
LOG(Error, "To save virtual model asset you need to specify 'withMeshDataFromGpu' (it has no other storage container to get data).");
return true;
}
auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData();
ScopeLock lock(Locker);
// Use a temporary chunks for data storage for virtual assets

View File

@@ -18,7 +18,7 @@ public:
/// <param name="id">The asset id.</param>
/// <returns>Loaded asset of null.</returns>
template<typename T>
T* Load(const Guid& id)
T* LoadAsync(const Guid& id)
{
for (auto& e : *this)
{
@@ -26,10 +26,8 @@ public:
return (T*)e.Get();
}
auto asset = (T*)::LoadAsset(id, T::TypeInitializer);
if (asset && !asset->WaitForLoaded())
if (asset)
Add(asset);
else
asset = nullptr;
return asset;
}

View File

@@ -30,7 +30,6 @@
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/Internal/InternalCalls.h"
#include "Engine/Scripting/Scripting.h"
#if USE_EDITOR
#include "Editor/Editor.h"
@@ -347,21 +346,17 @@ int32 LoadingThread::Run()
ContentLoadTask* task;
ThisLoadThread = this;
MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr;
while (Platform::AtomicRead(&_exitFlag) == 0)
{
if (LoadTasks.try_dequeue(task))
{
Run(task);
MONO_THREAD_INFO_GET(monoThreadInfo);
}
else
{
MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo);
LoadTasksMutex.Lock();
LoadTasksSignal.Wait(LoadTasksMutex);
LoadTasksMutex.Unlock();
MONO_EXIT_GC_SAFE_WITH_INFO;
}
}
@@ -705,7 +700,6 @@ Asset* Content::GetAsset(const StringView& outputPath)
{
if (outputPath.IsEmpty())
return nullptr;
PROFILE_CPU();
ScopeLock lock(AssetsLocker);
for (auto i = Assets.Begin(); i.IsNotEnd(); ++i)
{

View File

@@ -87,10 +87,6 @@ public:
/// </summary>
double LastAccessTime = 0.0;
/// <summary>
/// Flag set to indicate that chunk is during loading (atomic access to sync multiple reading threads).
/// </summary>
int64 IsLoading = 0;
/// <summary>
/// The chunk data.
/// </summary>
@@ -150,7 +146,7 @@ public:
/// </summary>
FORCE_INLINE bool IsLoaded() const
{
return Data.IsValid() && Platform::AtomicRead(&IsLoading) == 0;
return Data.IsValid();
}
/// <summary>
@@ -158,7 +154,7 @@ public:
/// </summary>
FORCE_INLINE bool IsMissing() const
{
return !IsLoaded();
return Data.IsInvalid();
}
/// <summary>

View File

@@ -5,7 +5,6 @@
#include "FlaxPackage.h"
#include "ContentStorageManager.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/ScopeExit.h"
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Platform/File.h"
#include "Engine/Profiler/ProfilerCPU.h"
@@ -75,6 +74,8 @@ FlaxChunk* FlaxChunk::Clone() const
const int32 FlaxStorage::MagicCode = 1180124739;
FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr);
struct Header
{
int32 MagicCode;
@@ -245,7 +246,6 @@ FlaxStorage::~FlaxStorage()
ASSERT(IsDisposed());
CHECK(_chunksLock == 0);
CHECK(_refCount == 0);
CHECK(_isUnloadingData == 0);
ASSERT(_chunks.IsEmpty());
#if USE_EDITOR
@@ -261,22 +261,6 @@ FlaxStorage::~FlaxStorage()
#endif
}
void FlaxStorage::LockChunks()
{
RETRY:
Platform::InterlockedIncrement(&_chunksLock);
if (Platform::AtomicRead(&_isUnloadingData) != 0)
{
// Someone else is closing file handles or freeing chunks so wait for it to finish and retry
Platform::InterlockedDecrement(&_chunksLock);
do
{
Platform::Sleep(1);
} while (Platform::AtomicRead(&_isUnloadingData) != 0);
goto RETRY;
}
}
FlaxStorage::LockData FlaxStorage::LockSafe()
{
auto lock = LockData(this);
@@ -705,6 +689,7 @@ bool FlaxStorage::LoadAssetHeader(const Guid& id, AssetInitData& data)
return true;
}
// Load header
return LoadAssetHeader(e, data);
}
@@ -714,10 +699,7 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
ASSERT(IsLoaded());
ASSERT(chunk != nullptr && _chunks.Contains(chunk));
// Protect against loading the same chunk from multiple threads at once
while (Platform::InterlockedCompareExchange(&chunk->IsLoading, 1, 0) != 0)
Platform::Sleep(1);
SCOPE_EXIT{ Platform::AtomicStore(&chunk->IsLoading, 0); };
// Check if already loaded
if (chunk->IsLoaded())
return false;
@@ -794,10 +776,12 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
// Raw data
chunk->Data.Read(stream, size);
}
ASSERT(chunk->IsLoaded());
chunk->RegisterUsage();
}
UnlockChunks();
return failed;
}
@@ -1436,12 +1420,10 @@ FileReadStream* FlaxStorage::OpenFile()
bool FlaxStorage::CloseFileHandles()
{
// Guard the whole process so if new thread wants to lock the chunks will need to wait for this to end
Platform::InterlockedIncrement(&_isUnloadingData);
SCOPE_EXIT{ Platform::InterlockedDecrement(&_isUnloadingData); };
if (Platform::AtomicRead(&_chunksLock) == 0 && Platform::AtomicRead(&_files) == 0)
return false; // Early out when no files are opened
{
return false;
}
PROFILE_CPU();
PROFILE_MEM(ContentFiles);
@@ -1514,21 +1496,9 @@ void FlaxStorage::Tick(double time)
{
auto chunk = _chunks.Get()[i];
const bool wasUsed = (time - chunk->LastAccessTime) < unusedDataChunksLifetime;
if (!wasUsed &&
chunk->IsLoaded() &&
EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory) &&
Platform::AtomicRead(&chunk->IsLoading) == 0)
if (!wasUsed && chunk->IsLoaded() && EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory))
{
// Guard the unloading so if other thread wants to lock the chunks will need to wait for this to end
Platform::InterlockedIncrement(&_isUnloadingData);
if (Platform::AtomicRead(&_chunksLock) != 0 || Platform::AtomicRead(&chunk->IsLoading) != 0)
{
// Someone started loading so skip ticking
Platform::InterlockedDecrement(&_isUnloadingData);
return;
}
chunk->Unload();
Platform::InterlockedDecrement(&_isUnloadingData);
}
wasAnyUsed |= wasUsed;
}

View File

@@ -90,7 +90,6 @@ protected:
int64 _refCount = 0;
int64 _chunksLock = 0;
int64 _files = 0;
int64 _isUnloadingData = 0;
double _lastRefLostTime;
CriticalSection _loadLocker;
@@ -130,7 +129,10 @@ public:
/// <summary>
/// Locks the storage chunks data to prevent disposing them. Also ensures that file handles won't be closed while chunks are locked.
/// </summary>
void LockChunks();
FORCE_INLINE void LockChunks()
{
Platform::InterlockedIncrement(&_chunksLock);
}
/// <summary>
/// Unlocks the storage chunks data.
@@ -146,6 +148,7 @@ public:
struct LockData
{
friend FlaxStorage;
static LockData Invalid;
private:
FlaxStorage* _storage;
@@ -158,11 +161,6 @@ public:
}
public:
LockData()
: _storage(nullptr)
{
}
LockData(const LockData& other)
: _storage(other._storage)
{

View File

@@ -658,10 +658,7 @@ public:
--_count;
T* data = _allocation.Get();
if (index < _count)
{
for (int32 i = index; i < _count; i++)
data[i] = MoveTemp(data[i + 1]);
}
Memory::MoveAssignItems(data + index, data + (index + 1), _count - index);
Memory::DestructItems(data + _count, 1);
}

View File

@@ -409,36 +409,27 @@ protected:
else
{
// Rebuild entire table completely
const int32 elementsCount = _elementsCount;
const int32 oldSize = _size;
AllocationData oldAllocation;
AllocationUtils::MoveToEmpty<BucketType, AllocationType>(oldAllocation, _allocation, oldSize, oldSize);
AllocationUtils::MoveToEmpty<BucketType, AllocationType>(oldAllocation, _allocation, _size, _size);
_allocation.Allocate(_size);
BucketType* data = _allocation.Get();
for (int32 i = 0; i < oldSize; ++i)
for (int32 i = 0; i < _size; ++i)
data[i]._state = HashSetBucketState::Empty;
BucketType* oldData = oldAllocation.Get();
FindPositionResult pos;
for (int32 i = 0; i < oldSize; ++i)
for (int32 i = 0; i < _size; ++i)
{
BucketType& oldBucket = oldData[i];
if (oldBucket.IsOccupied())
{
FindPosition(oldBucket.GetKey(), pos);
if (pos.FreeSlotIndex == -1)
{
// Grow and retry to handle pathological cases (eg. heavy collisions)
EnsureCapacity(_size + 1, true);
FindPosition(oldBucket.GetKey(), pos);
ASSERT(pos.FreeSlotIndex != -1);
}
ASSERT(pos.FreeSlotIndex != -1);
BucketType& bucket = _allocation.Get()[pos.FreeSlotIndex];
bucket = MoveTemp(oldBucket);
}
}
for (int32 i = 0; i < oldSize; ++i)
for (int32 i = 0; i < _size; ++i)
oldData[i].Free();
_elementsCount = elementsCount;
}
_deletedCount = 0;
}

View File

@@ -4,9 +4,8 @@
#if defined(__clang__)
#define DLLEXPORT __attribute__((__visibility__("default")))
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
#define DLLIMPORT
#define USED __attribute__((used))
#define THREADLOCAL __thread
#define STDCALL __attribute__((stdcall))
#define CDECL __attribute__((cdecl))
@@ -20,7 +19,7 @@
#define PACK_BEGIN()
#define PACK_END() __attribute__((__packed__))
#define ALIGN_BEGIN(_align)
#define ALIGN_END(_align) __attribute__((aligned(_align)))
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) )
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS \
_Pragma("clang diagnostic push") \
@@ -38,9 +37,8 @@
#elif defined(__GNUC__)
#define DLLEXPORT __attribute__((__visibility__("default")))
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
#define DLLIMPORT
#define USED __attribute__((used))
#define THREADLOCAL __thread
#define STDCALL __attribute__((stdcall))
#define CDECL __attribute__((cdecl))
@@ -54,7 +52,7 @@
#define PACK_BEGIN()
#define PACK_END() __attribute__((__packed__))
#define ALIGN_BEGIN(_align)
#define ALIGN_END(_align) __attribute__((aligned(_align)))
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) )
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS
@@ -69,7 +67,6 @@
#define DLLEXPORT __declspec(dllexport)
#define DLLIMPORT __declspec(dllimport)
#define USED
#define THREADLOCAL __declspec(thread)
#define STDCALL __stdcall
#define CDECL __cdecl

View File

@@ -620,9 +620,14 @@ bool Collision::RayIntersectsTriangle(const Ray& ray, const Vector3& a, const Ve
Real rayDistance = edge2.X * distanceCrossEdge1.X + edge2.Y * distanceCrossEdge1.Y + edge2.Z * distanceCrossEdge1.Z;
rayDistance *= inverseDeterminant;
// Check if the triangle is in front the ray origin
// Check if the triangle is behind the ray origin
if (rayDistance < 0.0f)
{
return false;
}
distance = rayDistance;
return rayDistance >= 0.0f;
return true;
}
bool CollisionsHelper::RayIntersectsTriangle(const Ray& ray, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Real& distance, Vector3& normal)

View File

@@ -12,6 +12,11 @@ String Ray::ToString() const
return String::Format(TEXT("{}"), *this);
}
Vector3 Ray::GetPoint(Real distance) const
{
return Position + Direction * distance;
}
Ray Ray::GetPickRay(float x, float y, const Viewport& viewport, const Matrix& vp)
{
Vector3 nearPoint(x, y, 0.0f);

View File

@@ -79,10 +79,7 @@ public:
/// </summary>
/// <param name="distance">The distance from ray origin.</param>
/// <returns>The calculated point.</returns>
FORCE_INLINE Vector3 GetPoint(Real distance) const
{
return Position + Direction * distance;
}
Vector3 GetPoint(Real distance) const;
/// <summary>
/// Determines if there is an intersection between ray and a point.

View File

@@ -18,7 +18,9 @@ namespace AllocationUtils
capacity |= capacity >> 8;
capacity |= capacity >> 16;
uint64 capacity64 = (uint64)(capacity + 1) * 2;
return capacity64 >= MAX_int32 ? MAX_int32 : (int32)capacity64 / 2;
if (capacity64 > MAX_int32)
capacity64 = MAX_int32;
return (int32)capacity64;
}
// Aligns the input value to the next power of 2 to be used as bigger memory allocation block.
@@ -206,7 +208,7 @@ public:
typedef typename FallbackAllocation::template Data<T> FallbackData;
bool _useFallback = false;
alignas(sizeof(void*)) byte _data[Capacity * sizeof(T)];
byte _data[Capacity * sizeof(T)];
FallbackData _fallback;
public:

View File

@@ -215,11 +215,6 @@ public:
return String(_data.Get(), _data.Count());
}
StringAnsi ToStringAnsi() const
{
return StringAnsi(_data.Get(), _data.Count());
}
StringView ToStringView() const;
};

View File

@@ -145,6 +145,12 @@ bool CommandLine::Parse(const Char* cmdLine)
PARSE_BOOL_SWITCH("-monolog ", MonoLog);
PARSE_BOOL_SWITCH("-mute ", Mute);
PARSE_BOOL_SWITCH("-lowdpi ", LowDPI);
#if PLATFORM_LINUX && PLATFORM_SDL
PARSE_BOOL_SWITCH("-wayland ", Wayland);
PARSE_BOOL_SWITCH("-x11 ", X11);
#endif
#if USE_EDITOR
PARSE_BOOL_SWITCH("-clearcache ", ClearCache);
PARSE_BOOL_SWITCH("-clearcooker ", ClearCookerCache);

View File

@@ -127,6 +127,20 @@ public:
/// </summary>
Nullable<bool> LowDPI;
#if PLATFORM_LINUX && PLATFORM_SDL
/// <summary>
/// -wayland (prefer Wayland over X11 as display server)
/// </summary>
Nullable<bool> Wayland;
/// <summary>
/// -x11 (prefer X11 over Wayland as display server)
/// </summary>
Nullable<bool> X11;
#endif
#if USE_EDITOR
/// <summary>
/// -project !path! (Startup project path)

View File

@@ -105,6 +105,8 @@ int32 Engine::Main(const Char* cmdLine)
CommandLine::Options.Std = true;
#endif
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
if (Platform::Init())
{
Platform::Fatal(TEXT("Cannot init platform."));
@@ -114,7 +116,6 @@ int32 Engine::Main(const Char* cmdLine)
InitProfilerMemory(cmdLine, 1);
#endif
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
Time::StartupTime = DateTime::Now();
Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory();
#if USE_EDITOR

View File

@@ -79,11 +79,10 @@ namespace FlaxEngine.Interop
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), NativeLibraryImportResolver);
// Change default culture to match with Mono runtime default culture
var culture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
System.Threading.Thread.CurrentThread.CurrentCulture = culture;
System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
}
@@ -426,8 +425,6 @@ namespace FlaxEngine.Interop
var fieldOffsetPtr = (IntPtr*)field.FieldHandle.Value; // Pointer to MonoClassField
fieldOffsetPtr += 3; // Skip three pointers (type, name, parent_and_flags)
return *(int*)fieldOffsetPtr - IntPtr.Size * 2; // Load the value of a pointer (4 bytes, int32), then subtracting 16 bytes from it (2 pointers for vtable and threadsync)
#else
throw new NotImplementedException();
#endif
}

View File

@@ -6,6 +6,8 @@
#include "Engine/Core/Types/Nullable.h"
#include "Engine/Platform/Window.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Input/Input.h"
#include "Engine/Input/Mouse.h"
#if USE_EDITOR
#include "Editor/Editor.h"
#include "Editor/Managed/ManagedEditor.h"
@@ -13,10 +15,14 @@
#include "Engine/Engine/Engine.h"
#endif
Nullable<bool> Fullscreen;
Nullable<Float2> Size;
bool CursorVisible = true;
CursorLockMode CursorLock = CursorLockMode::None;
namespace
{
Nullable<bool> Fullscreen;
Nullable<Float2> Size;
bool CursorVisible = true;
CursorLockMode CursorLock = CursorLockMode::None;
bool LastGameViewportFocus = false;
}
class ScreenService : public EngineService
{
@@ -101,9 +107,9 @@ void Screen::SetCursorVisible(const bool value)
const auto win = Engine::MainWindow;
#endif
if (win && Engine::HasGameViewportFocus())
{
win->SetCursor(value ? CursorType::Default : CursorType::Hidden);
}
else if (win)
win->SetCursor(CursorType::Default);
CursorVisible = value;
}
@@ -116,21 +122,31 @@ void Screen::SetCursorLock(CursorLockMode mode)
{
#if USE_EDITOR
const auto win = Editor::Managed->GetGameWindow(true);
Rectangle bounds(Editor::Managed->GameViewportToScreen(Float2::Zero), Editor::Managed->GetGameWindowSize());
if (win)
bounds = Rectangle(win->ScreenToClient(bounds.GetTopLeft()), bounds.Size);
#else
const auto win = Engine::MainWindow;
Rectangle bounds = win != nullptr ? win->GetClientBounds() : Rectangle();
#endif
if (win && mode == CursorLockMode::Clipped)
if (win)
{
#if USE_EDITOR
Rectangle bounds(Editor::Managed->GameViewportToScreen(Float2::Zero), Editor::Managed->GetGameWindowSize());
#else
Rectangle bounds = win->GetClientBounds();
#endif
win->StartClippingCursor(bounds);
}
else if (win && CursorLock == CursorLockMode::Clipped)
{
win->EndClippingCursor();
bool inRelativeMode = Input::Mouse->IsRelative();
if (mode == CursorLockMode::Clipped)
win->StartClippingCursor(bounds);
else if (mode == CursorLockMode::Locked)
{
// Use mouse clip region to restrict the cursor in one spot
win->StartClippingCursor(Rectangle(bounds.GetCenter(), Float2(1, 1)));
}
else if (CursorLock == CursorLockMode::Locked || CursorLock == CursorLockMode::Clipped)
win->EndClippingCursor();
// Enable relative mode when cursor is restricted
if (mode != CursorLockMode::None)
Input::Mouse->SetRelativeMode(true, win);
else if (mode == CursorLockMode::None && inRelativeMode)
Input::Mouse->SetRelativeMode(false, win);
}
CursorLock = mode;
}
@@ -190,7 +206,11 @@ void ScreenService::Update()
{
#if USE_EDITOR
// Sync current cursor state in Editor (eg. when viewport focus can change)
Screen::SetCursorVisible(CursorVisible);
const auto win = Editor::Managed->GetGameWindow(true);
bool gameViewportFocus = win && Engine::HasGameViewportFocus();
if (gameViewportFocus != LastGameViewportFocus)
Screen::SetCursorVisible(CursorVisible);
LastGameViewportFocus = gameViewportFocus;
#endif
}

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