Compare commits
445 Commits
work
...
d955303df7
| Author | SHA1 | Date | |
|---|---|---|---|
| d955303df7 | |||
| 6ca949e70b | |||
| afecf984e3 | |||
| 85fad2b6bf | |||
| aee1fd285e | |||
| 9b464c6530 | |||
| 3ea807468e | |||
| 0f135597fa | |||
|
|
8da0d2c4ce | ||
|
|
20f576783b | ||
|
|
ef2c551cee | ||
|
|
65cf59642c | ||
|
|
e2fc8a6283 | ||
|
|
73976f3ed9 | ||
|
|
7ab6bafe39 | ||
|
|
deb2ad7c8f | ||
|
|
cd7fc3242e | ||
|
|
31a0a77e7c | ||
|
|
f0ec4a901a | ||
|
|
3682159da6 | ||
|
|
e9f83f77bb | ||
|
|
8f3b80492e | ||
|
|
c61c013517 | ||
|
|
a2170ffd8a | ||
|
|
4f1f77fb32 | ||
|
|
6ae370f8fc | ||
|
|
da5c8555e5 | ||
|
|
c4c7ee941f | ||
|
|
4f45b3c1d0 | ||
|
|
4c640b915f | ||
|
|
1f3f1ea67e | ||
|
|
60c19303f6 | ||
|
|
adcfc50218 | ||
|
|
9b812ec34a | ||
|
|
7e1ac5e167 | ||
|
|
028b5fedec | ||
|
|
9cc2c1da40 | ||
|
|
5ed8564293 | ||
|
|
7c87ade12b | ||
|
|
7f87678282 | ||
|
|
e429d29d17 | ||
|
|
537d8b57ca | ||
|
|
4a3fb41035 | ||
|
|
797cb3c3f2 | ||
|
|
47670251ef | ||
|
|
cf1ef91246 | ||
|
|
7060cb5696 | ||
|
|
c449833d35 | ||
|
|
284aeca51a | ||
|
|
d8f7199c11 | ||
|
|
dc05bbbbcd | ||
|
|
3fcc9ed01f | ||
|
|
70ba750a5e | ||
|
|
50271199ac | ||
|
|
d1a99c9396 | ||
|
|
4ae3d57adc | ||
|
|
99b9967806 | ||
|
|
2f7e84253a | ||
|
|
2f55cb938f | ||
|
|
a86661a855 | ||
|
|
a68ce6633a | ||
|
|
5d57afe3aa | ||
|
|
6705138247 | ||
|
|
276caf771c | ||
|
|
5eea5a72c9 | ||
|
|
e9a7b1c8eb | ||
|
|
a151c78412 | ||
|
|
5f1e905e8f | ||
|
|
a0f764a774 | ||
|
|
7da5ce3ed4 | ||
|
|
1186833b2d | ||
|
|
bb180b0f59 | ||
|
|
5d1c79929a | ||
|
|
3f5a4cc4c9 | ||
|
|
62fd8ac967 | ||
|
|
d904b92f2e | ||
|
|
87e2b76ffa | ||
|
|
c5e11aed15 | ||
|
|
85d61b334b | ||
|
|
26e94f6f8e | ||
|
|
902744a0ce | ||
|
|
1c581bceaf | ||
|
|
9cc44825c6 | ||
|
|
92f4327fc2 | ||
|
|
47711ec5be | ||
|
|
d1fbc66cb9 | ||
|
|
7183a3306e | ||
|
|
fd191f7ffb | ||
|
|
2604d58687 | ||
|
|
01d1d634c2 | ||
|
|
c7e403661d | ||
|
|
1196db6d17 | ||
|
|
553a007508 | ||
|
|
de2ee36529 | ||
|
|
eea44ac897 | ||
|
|
c124713e99 | ||
|
|
364a523375 | ||
|
|
afdd264e63 | ||
|
|
8ec54f7b1c | ||
|
|
521518bde4 | ||
|
|
56077a268a | ||
|
|
a0ca000793 | ||
|
|
ae4ae7a638 | ||
|
|
e2a4c8ab03 | ||
|
|
7a40722964 | ||
|
|
1de8909d05 | ||
|
|
9749487e24 | ||
|
|
55968a8ddc | ||
|
|
e325b190ea | ||
|
|
ada6b9140f | ||
|
|
5582579173 | ||
|
|
88773e71e5 | ||
|
|
9e1f488f22 | ||
|
|
a471861e92 | ||
|
|
3d182c89f3 | ||
|
|
e3810a9938 | ||
| f0dea9d528 | |||
| d70a003617 | |||
| b443b74d18 | |||
|
|
eff5f84185 | ||
|
|
ad1163bccc | ||
|
|
1042ad4e7d | ||
|
|
8fdda1a71a | ||
|
|
3f7fe635d8 | ||
|
|
fcebc57ed0 | ||
|
|
c40e447bb7 | ||
|
|
e6f94bb154 | ||
|
|
adbc546978 | ||
|
|
2d171967e9 | ||
|
|
33a0e6ac7d | ||
|
|
848cc38bf1 | ||
|
|
91cd1e8065 | ||
|
|
9fafb47abb | ||
|
|
5222f1d35c | ||
|
|
785649f9d5 | ||
|
|
d47ac95681 | ||
|
|
703e0cb7ca | ||
|
|
ef7c7f2d30 | ||
|
|
f3d375e356 | ||
|
|
a3073321cf | ||
|
|
824b49dd88 | ||
|
|
d0e7bff03a | ||
|
|
d314d5b324 | ||
|
|
a027ed3b63 | ||
|
|
bcedb05a2c | ||
|
|
b36be95947 | ||
|
|
4ca399af71 | ||
|
|
c1f022520d | ||
|
|
2efd20f223 | ||
|
|
00dd432fbc | ||
|
|
f707508d70 | ||
|
|
b965ca6c8c | ||
|
|
6fea9eefaa | ||
|
|
47caa6af28 | ||
|
|
9aedb37ac2 | ||
|
|
e4bc2c69c7 | ||
|
|
6db9265112 | ||
|
|
21a84c5b84 | ||
|
|
633b5857c9 | ||
|
|
9f9dac1543 | ||
|
|
df6f8fd8ae | ||
|
|
4bd8ce37ac | ||
|
|
40e204839f | ||
|
|
d6c75b3f86 | ||
|
|
687c283533 | ||
|
|
70ee8501a5 | ||
|
|
2c34bd2308 | ||
| fc341a86e7 | |||
|
|
5023e3277b | ||
|
|
9003d855b3 | ||
|
|
75906719d4 | ||
|
|
9a59925a36 | ||
|
|
d5ca80c2c1 | ||
|
|
cb07ee77aa | ||
|
|
680783f2b0 | ||
|
|
4a28b4bd6c | ||
|
|
4bf36f3467 | ||
|
|
2c1713d300 | ||
|
|
8136691914 | ||
|
|
da23e287c0 | ||
|
|
e0825d870d | ||
|
|
86dbe6b93d | ||
|
|
e71b74c625 | ||
|
|
45f6ef29e9 | ||
|
|
0fabca19cd | ||
|
|
0bc242c738 | ||
|
|
8c548ceff2 | ||
|
|
46fda05000 | ||
|
|
1fee95be1c | ||
|
|
8c66ae99a3 | ||
|
|
1cf98e2188 | ||
|
|
6a8553a277 | ||
|
|
a02b7d4a1a | ||
|
|
606dfa4e2e | ||
|
|
b6f853a01c | ||
|
|
fc2112ec93 | ||
|
|
6ccfbfeff1 | ||
|
|
f21accd466 | ||
|
|
38b4ace1a8 | ||
|
|
774b6bd72c | ||
|
|
9c4606fefc | ||
|
|
0e50e47cce | ||
|
|
285fa870d0 | ||
|
|
69ed0bf56f | ||
|
|
237b9bccd5 | ||
|
|
f5fd7319e1 | ||
|
|
e04c0c4ace | ||
|
|
f6d0b073da | ||
|
|
68a7cf4f18 | ||
|
|
a91360529f | ||
|
|
bf9ca14deb | ||
|
|
cb92a2b8cb | ||
|
|
8a73d79936 | ||
|
|
2f7d7a0f2a | ||
|
|
d43c0c593f | ||
|
|
1087bd2445 | ||
|
|
5e19a9729b | ||
|
|
69e12d77be | ||
|
|
169d3e964d | ||
|
|
cf503cf921 | ||
|
|
6fd4ef735e | ||
|
|
303087c4c4 | ||
| 68da28ffe8 | |||
|
|
427e76e76e | ||
| b183b5bcfc | |||
|
|
e27880c1e6 | ||
|
|
6f15ef7690 | ||
|
|
0e3a22faa0 | ||
|
|
0dc1e04c89 | ||
|
|
5c7712daad | ||
|
|
f37b75df7b | ||
|
|
6132e45e25 | ||
|
|
753035c452 | ||
|
|
2dc44ac1a6 | ||
|
|
d6a33d5a1c | ||
|
|
6cbd40e6d8 | ||
|
|
b8e00f2ed1 | ||
|
|
b6e18ccae5 | ||
|
|
1fb6586dff | ||
|
|
e6265105b5 | ||
|
|
f8dadac453 | ||
|
|
fc46219a82 | ||
|
|
2546e19d65 | ||
|
|
bb37f980ed | ||
|
|
10e9aee8ce | ||
|
|
4af26a5516 | ||
|
|
3e82e550f3 | ||
|
|
33b540ed9e | ||
|
|
50871d8885 | ||
|
|
83374164db | ||
|
|
c5bfc6bc3d | ||
|
|
d698bf96cc | ||
|
|
b3f88e156c | ||
|
|
7418d60f24 | ||
|
|
3981d5090c | ||
| 51feaa0730 | |||
| 8e69aa8bd6 | |||
| 90b2fded48 | |||
| 9125ffeb9e | |||
| 28980e5fbf | |||
| aab0d772a4 | |||
| a93a940630 | |||
| 3e353db1fa | |||
|
|
75647d149a | ||
|
|
52b64540ab | ||
|
|
d7ab497b0e | ||
|
|
683a48a6e3 | ||
|
|
a41fc51f92 | ||
|
|
b9cfd054c1 | ||
|
|
53d4ea51af | ||
| 6296e1a9eb | |||
| 1586bb0702 | |||
| edeaf6af09 | |||
| 951edd95db | |||
| 9951211596 | |||
| 6d337464f7 | |||
| d0b552d74a | |||
| 83512822b1 | |||
| ccb78103ec | |||
| 6e79ffea34 | |||
| 4f03d37a17 | |||
|
|
c8622d1801 | ||
|
|
fdd22c3380 | ||
|
|
53761df85e | ||
|
|
ecaae2b458 | ||
| 6fb8419b3c | |||
| a4272d6ca9 | |||
| 4654117d5e | |||
| 6ff260d052 | |||
| 88d2b72822 | |||
|
|
a4d3ede368 | ||
|
|
92edb996f2 | ||
|
|
2e0c35e6e4 | ||
|
|
baba151d8a | ||
|
|
b44d4107c0 | ||
|
|
f40c67ddf0 | ||
|
|
ec154b4998 | ||
|
|
28eaac37dc | ||
|
|
6997cbeb47 | ||
| 29868531ad | |||
| 95dfb6fdc6 | |||
| 8df3999f85 | |||
|
|
6b78b498f7 | ||
|
|
c4130aa20f | ||
|
|
71991ff8c7 | ||
|
|
c9fe9213b3 | ||
|
|
608839b6a5 | ||
| 149b189629 | |||
| b1fd86e6b5 | |||
| 5e8fdf879d | |||
|
|
954cf3eb5b | ||
|
|
245d7de818 | ||
|
|
49e0cc937e | ||
|
|
f71bdd0962 | ||
|
|
47a6da9e40 | ||
|
|
1704cfba4d | ||
|
|
40dae18b76 | ||
| 84ada18299 | |||
| 635fe8017e | |||
| 834380ff05 | |||
| dd65fc2289 | |||
| 86125bb625 | |||
| 09a9d8b380 | |||
| 86b223ec93 | |||
| 78f6080321 | |||
| c7be6f6e0e | |||
| c40c31fbb7 | |||
| f5fbc1e32d | |||
| 0a20378acd | |||
| d4e87877b6 | |||
| 7eb7236b8a | |||
| dc0e4ffce2 | |||
| 86fe93943d | |||
| 3258113ef8 | |||
| ebf3999cfe | |||
| a1ccbbb5b9 | |||
| 35072c40d8 | |||
| 5c51021388 | |||
| 95fd527515 | |||
| 113d851cea | |||
| 72f0c460f9 | |||
| 13ddd15b44 | |||
| c9e24aaf5f | |||
| 68c0ac0ffc | |||
| bebda275a9 | |||
| 287eaae850 | |||
| 7e59c3b9a7 | |||
| 38658a5b8c | |||
| 88c75b8672 | |||
| 17ab1e6830 | |||
| 1f45110e5f | |||
| 5401166a07 | |||
| bfa8188782 | |||
| e777a71784 | |||
| c13e91a0d0 | |||
| aac5d57352 | |||
| ceca13c7b6 | |||
| 2905470330 | |||
| 2c17033822 | |||
| 0a4cb9e9b1 | |||
| 096651f4c1 | |||
| 275a08506b | |||
| 4cd61cb381 | |||
| 9ad1147581 | |||
| 35e09def1b | |||
| 0469607a71 | |||
| d115d22ee6 | |||
| 8120ae621d | |||
| f873c2fa9f | |||
| c3ffbe2f42 | |||
| 55a0a39881 | |||
| 592215dd30 | |||
| ef0c2a2785 | |||
| 37979e452a | |||
| e9671bb727 | |||
| aa328bd591 | |||
| 188e4313f9 | |||
| df02c70e31 | |||
| 257f54b323 | |||
| fb4b5b2575 | |||
| 4e1251276d | |||
| a7b200dc57 | |||
| 8cadbae80e | |||
| c3bae49aae | |||
|
|
6fa4fc6149 | ||
| f1ffe1acaa | |||
| 5c9ddf7f00 | |||
| 431767a150 | |||
| 8ba4e442a6 | |||
| 2f8d19c267 | |||
| f2fd98f742 | |||
| 3ce35d6e81 | |||
| 796dbaa836 | |||
| 84b209ec7f | |||
| fa976b34dc | |||
| e7cda362b7 | |||
| a41a09c162 | |||
| e82f84f0ab | |||
| f1d387ceea | |||
| 3893d4d1f8 | |||
| cf7ac50faf | |||
| 3f6bf15554 | |||
| dac74829b4 | |||
| 94d6f213a0 | |||
| 8f2550ef61 | |||
| b622a1cc5e | |||
| c83a3c32c7 | |||
| e7dcf7f7e7 | |||
| 398785a2be | |||
| 05dba0f1f5 | |||
| 39dbd32b2e | |||
| 23a34a455a | |||
| 4308328b48 | |||
|
|
4e44831bbe | ||
|
|
0c1e0e48d4 | ||
|
|
6884df02fd | ||
|
|
63655d18c5 | ||
|
|
4170699348 | ||
|
|
d58a9beb3d | ||
|
|
5049f3b2d8 | ||
|
|
6e44eebb9e | ||
|
|
aecbab5613 | ||
|
|
6bf90f29c5 | ||
|
|
19edce1770 | ||
|
|
e17b96b2d6 | ||
|
|
9b6feb9367 | ||
|
|
1dfd717093 | ||
|
|
68ef6f08c6 | ||
|
|
a0b80c6096 | ||
|
|
59ac8a3f60 | ||
|
|
dd281bbca8 | ||
|
|
2e48be97b6 | ||
|
|
0b7550e5ca | ||
|
|
391c67b1a9 | ||
|
|
975cc79085 | ||
|
|
468babae87 | ||
|
|
516d4263c9 | ||
|
|
ba35123420 | ||
|
|
409703d675 | ||
|
|
570c3f7462 | ||
|
|
42d02a9e63 | ||
|
|
819c93f6fb | ||
|
|
0c645cbc78 | ||
|
|
e665cc7500 | ||
|
|
0a516ac98d |
42
.github/ISSUE_TEMPLATE/1-bug.yaml
vendored
Normal file
42
.github/ISSUE_TEMPLATE/1-bug.yaml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Bug Report
|
||||
description: File a bug report.
|
||||
title: "[Bug]: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report! Please attach any minimal reproduction projects!
|
||||
- type: textarea
|
||||
id: description-area
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please provide a description of the bug and what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps-area
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Please provide reproduction steps if possible.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of Flax are you running?
|
||||
options:
|
||||
- '1.8'
|
||||
- '1.9'
|
||||
- '1.10'
|
||||
- '1.11'
|
||||
- master branch
|
||||
default: 2
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant logs
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
22
.github/ISSUE_TEMPLATE/2-feature-request.yaml
vendored
Normal file
22
.github/ISSUE_TEMPLATE/2-feature-request.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Feature Request
|
||||
description: File a feature request.
|
||||
title: "[Request]: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out a feature request!
|
||||
- type: textarea
|
||||
id: description-area
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please provide a description of the feature!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: benefits-area
|
||||
attributes:
|
||||
label: Benefits
|
||||
description: Please provide what benefits this feature would provide to the engine!
|
||||
validations:
|
||||
required: true
|
||||
4
.github/workflows/build_linux.yml
vendored
4
.github/workflows/build_linux.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev libwayland-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
|
||||
- name: Build
|
||||
run: |
|
||||
./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8
|
||||
|
||||
BIN
Content/Editor/Camera/M_Camera.flax
(Stored with Git LFS)
BIN
Content/Editor/Camera/M_Camera.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/CubeTexturePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/CubeTexturePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/DDGIDebugProbes.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/DDGIDebugProbes.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Decal.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Decal.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Particle.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Particle.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Surface.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Surface.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Terrain.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Terrain.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DefaultFontMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/DefaultFontMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/FoliageBrushMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/FoliageBrushMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialAxisLocked.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/MaterialAxisLocked.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialWire.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/MaterialWire.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/SelectionOutlineMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/SelectionOutlineMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Highlight Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Highlight Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Icons/IconsMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Icons/IconsMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/IconsAtlas.flax
(Stored with Git LFS)
BIN
Content/Editor/IconsAtlas.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/IesProfilePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/IesProfilePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
@@ -13,7 +13,7 @@
|
||||
META_CB_BEGIN(0, Data)
|
||||
float4x4 WorldMatrix;
|
||||
float4x4 InvWorld;
|
||||
float4x4 SVPositionToWorld;
|
||||
float4x4 SvPositionToWorld;
|
||||
@1META_CB_END
|
||||
|
||||
// Use depth buffer for per-pixel decal layering
|
||||
@@ -27,12 +27,63 @@ struct MaterialInput
|
||||
float3 WorldPosition;
|
||||
float TwoSidedSign;
|
||||
float2 TexCoord;
|
||||
float4 TexCoord_DDX_DDY;
|
||||
float3x3 TBN;
|
||||
float4 SvPosition;
|
||||
float3 PreSkinnedPosition;
|
||||
float3 PreSkinnedNormal;
|
||||
};
|
||||
|
||||
// Calculates decal texcoords for a given pixel position (sampels depth buffer and projects value to decal space).
|
||||
float2 SvPositionToDecalUV(float4 svPosition)
|
||||
{
|
||||
float2 screenUV = svPosition.xy * ScreenSize.zw;
|
||||
svPosition.z = SAMPLE_RT(DepthBuffer, screenUV).r;
|
||||
float4 positionHS = mul(float4(svPosition.xyz, 1), SvPositionToWorld);
|
||||
float3 positionWS = positionHS.xyz / positionHS.w;
|
||||
float3 positionOS = mul(float4(positionWS, 1), InvWorld).xyz;
|
||||
return positionOS.xz + 0.5f;
|
||||
}
|
||||
|
||||
// Manually compute ddx/ddy for decal texture cooordinates to avoid the 2x2 pixels artifacts on the edges of geometry under decal
|
||||
// [Reference: https://www.humus.name/index.php?page=3D&ID=84]
|
||||
float4 CalculateTextureDerivatives(float4 svPosition, float2 texCoord)
|
||||
{
|
||||
float4 svDiffX = float4(1, 0, 0, 0);
|
||||
float2 uvDiffX0 = texCoord - SvPositionToDecalUV(svPosition - svDiffX);
|
||||
float2 uvDiffX1 = SvPositionToDecalUV(svPosition + svDiffX) - texCoord;
|
||||
float2 dx = dot(uvDiffX0, uvDiffX0) < dot(uvDiffX1, uvDiffX1) ? uvDiffX0 : uvDiffX1;
|
||||
|
||||
float4 svDiffY = float4(0, 1, 0, 0);
|
||||
float2 uvDiffY0 = texCoord - SvPositionToDecalUV(svPosition - svDiffY);
|
||||
float2 uvDiffY1 = SvPositionToDecalUV(svPosition + svDiffY) - texCoord;
|
||||
float2 dy = dot(uvDiffY0, uvDiffY0) < dot(uvDiffY1, uvDiffY1) ? uvDiffY0 : uvDiffY1;
|
||||
|
||||
return float4(dx, dy);
|
||||
}
|
||||
|
||||
// Computes the mipmap level for a specific texture dimensions to be sampled at decal texture cooordinates.
|
||||
// [Reference: https://hugi.scene.org/online/coding/hugi%2014%20-%20comipmap.htm]
|
||||
float CalculateTextureMipmap(MaterialInput input, float2 textureSize)
|
||||
{
|
||||
float2 dx = input.TexCoord_DDX_DDY.xy * textureSize;
|
||||
float2 dy = input.TexCoord_DDX_DDY.zw * textureSize;
|
||||
float d = max(dot(dx, dx), dot(dy, dy));
|
||||
return (0.5 * 0.5) * log2(d); // Hardcoded half-mip rate reduction to avoid artifacts when decal is moved over dither texture
|
||||
}
|
||||
float CalculateTextureMipmap(MaterialInput input, Texture2D t)
|
||||
{
|
||||
float2 textureSize;
|
||||
t.GetDimensions(textureSize.x, textureSize.y);
|
||||
return CalculateTextureMipmap(input, textureSize);
|
||||
}
|
||||
float CalculateTextureMipmap(MaterialInput input, TextureCube t)
|
||||
{
|
||||
float2 textureSize;
|
||||
t.GetDimensions(textureSize.x, textureSize.y);
|
||||
return CalculateTextureMipmap(input, textureSize);
|
||||
}
|
||||
|
||||
// Transforms a vector from tangent space to world space
|
||||
float3 TransformTangentVectorToWorld(MaterialInput input, float3 tangentVector)
|
||||
{
|
||||
@@ -116,7 +167,6 @@ Material GetMaterialPS(MaterialInput input)
|
||||
}
|
||||
|
||||
// Input macro specified by the material: DECAL_BLEND_MODE
|
||||
|
||||
#define DECAL_BLEND_MODE_TRANSLUCENT 0
|
||||
#define DECAL_BLEND_MODE_STAIN 1
|
||||
#define DECAL_BLEND_MODE_NORMAL 2
|
||||
@@ -153,7 +203,7 @@ void PS_Decal(
|
||||
float2 screenUV = SvPosition.xy * ScreenSize.zw;
|
||||
SvPosition.z = SAMPLE_RT(DepthBuffer, screenUV).r;
|
||||
|
||||
float4 positionHS = mul(float4(SvPosition.xyz, 1), SVPositionToWorld);
|
||||
float4 positionHS = mul(float4(SvPosition.xyz, 1), SvPositionToWorld);
|
||||
float3 positionWS = positionHS.xyz / positionHS.w;
|
||||
float3 positionOS = mul(float4(positionWS, 1), InvWorld).xyz;
|
||||
|
||||
@@ -166,8 +216,9 @@ void PS_Decal(
|
||||
materialInput.TexCoord = decalUVs;
|
||||
materialInput.TwoSidedSign = 1;
|
||||
materialInput.SvPosition = SvPosition;
|
||||
|
||||
// Build tangent to world transformation matrix
|
||||
materialInput.TexCoord_DDX_DDY = CalculateTextureDerivatives(materialInput.SvPosition, materialInput.TexCoord);
|
||||
|
||||
// Calculate tangent-space
|
||||
float3 ddxWp = ddx(positionWS);
|
||||
float3 ddyWp = ddy(positionWS);
|
||||
materialInput.TBN[0] = normalize(ddyWp);
|
||||
|
||||
@@ -27,6 +27,7 @@ TextureCube EnvProbe : register(t__SRV__);
|
||||
TextureCube SkyLightTexture : register(t__SRV__);
|
||||
Buffer<float4> ShadowsBuffer : register(t__SRV__);
|
||||
Texture2D<float> ShadowMap : register(t__SRV__);
|
||||
Texture3D VolumetricFogTexture : register(t__SRV__);
|
||||
@4// Forward Shading: Utilities
|
||||
@5// Forward Shading: Shaders
|
||||
|
||||
@@ -147,6 +148,18 @@ void PS_Forward(
|
||||
// Calculate exponential height fog
|
||||
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0, gBuffer.ViewPos.z);
|
||||
|
||||
if (ExponentialHeightFog.VolumetricFogMaxDistance > 0)
|
||||
{
|
||||
// Sample volumetric fog and mix it in
|
||||
float2 screenUV = materialInput.SvPosition.xy * ScreenSize.zw;
|
||||
float3 viewVector = materialInput.WorldPosition - ViewPos;
|
||||
float sceneDepth = length(viewVector);
|
||||
float depthSlice = sceneDepth / ExponentialHeightFog.VolumetricFogMaxDistance;
|
||||
float3 volumeUV = float3(screenUV, depthSlice);
|
||||
float4 volumetricFog = VolumetricFogTexture.SampleLevel(SamplerLinearClamp, volumeUV, 0);
|
||||
fog = CombineVolumetricFog(fog, volumetricFog);
|
||||
}
|
||||
|
||||
// Apply fog to the output color
|
||||
#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE
|
||||
output = float4(output.rgb * fog.a + fog.rgb, output.a);
|
||||
|
||||
@@ -20,6 +20,8 @@ float TimeParam;
|
||||
float4 ViewInfo;
|
||||
float4 ScreenSize;
|
||||
float4 ViewSize;
|
||||
float3 ViewPadding0;
|
||||
float UnscaledTimeParam;
|
||||
@1META_CB_END
|
||||
|
||||
// Shader resources
|
||||
|
||||
@@ -645,7 +645,7 @@ VertexOutput VS_Ribbon(RibbonInput input, uint vertexIndex : SV_VertexID)
|
||||
materialInput.TBN = output.TBN;
|
||||
materialInput.TwoSidedSign = 1;
|
||||
materialInput.SvPosition = output.Position;
|
||||
materialInput.PreSkinnedPosition = Position;
|
||||
materialInput.PreSkinnedPosition = position;
|
||||
materialInput.PreSkinnedNormal = tangentToLocal[2].xyz;
|
||||
materialInput.InstanceOrigin = output.InstanceOrigin;
|
||||
materialInput.InstanceParams = output.InstanceParams;
|
||||
|
||||
@@ -19,6 +19,8 @@ float4 ViewInfo;
|
||||
float4 ScreenSize;
|
||||
float4 TemporalAAJitter;
|
||||
float4x4 InverseViewProjectionMatrix;
|
||||
float3 ViewPadding0;
|
||||
float UnscaledTimeParam;
|
||||
@1META_CB_END
|
||||
|
||||
// Shader resources
|
||||
|
||||
BIN
Content/Editor/Particles/Particle Material Color.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Particle Material Color.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Particles/Smoke Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Smoke Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/SpriteMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/SpriteMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Terrain/Circle Brush Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Terrain/Circle Brush Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Terrain/Highlight Terrain Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Terrain/Highlight Terrain Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/TexturePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/TexturePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Wires Debug Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Wires Debug Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultDeformableMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultDeformableMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultRadialMenu.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultRadialMenu.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultTerrainMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultTerrainMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/SingleColorMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/SingleColorMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/SkyboxMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/SkyboxMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/Fog.flax
(Stored with Git LFS)
BIN
Content/Shaders/Fog.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/SSAO.flax
(Stored with Git LFS)
BIN
Content/Shaders/SSAO.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/Sky.flax
(Stored with Git LFS)
BIN
Content/Shaders/Sky.flax
(Stored with Git LFS)
Binary file not shown.
@@ -13,6 +13,7 @@
|
||||
"Configuration": {
|
||||
"UseCSharp": true,
|
||||
"UseLargeWorlds": false,
|
||||
"UseDotNet": true
|
||||
"UseDotNet": true,
|
||||
"UseSDL": true
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,24 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=TYPEDEF/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=UNION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=UNION_005FMEMBER/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Abbreviations/=CCD/@EntryIndexedValue">CCD</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Abbreviations/=GPU/@EntryIndexedValue">GPU</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Abbreviations/=ID/@EntryIndexedValue">ID</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=175CE9C669E52F4D92FD2C07848740BD/@EntryIndexedValue"><NamingElement Priority="11" Title="Class and struct public fields"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="PUBLIC"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=32EB6D69783B3E4481A733193E338089/@EntryIndexedValue"><NamingElement Priority="9" Title="Class and struct methods"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=3C4E0D59F298854F9608A9B454E8FF5E/@EntryIndexedValue"><NamingElement Priority="17" Title="Typedefs"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="type alias" /><type Name="typedef" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=499C9026DADA2B448BCD0B2C54746A59/@EntryIndexedValue"><NamingElement Priority="14" Title="Other constants"><Descriptor Static="True" Constexpr="Indeterminate" Const="True" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="class field" /><type Name="local variable" /><type Name="struct field" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=50D2535711CE1A43A3B06EF841C36CFD/@EntryIndexedValue"><NamingElement Priority="13" Title="Enum members"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="scoped enumerator" /><type Name="unscoped enumerator" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=6AD3BADA1260CC4D840AB26323C51827/@EntryIndexedValue"><NamingElement Priority="15" Title="Global constants"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="True" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global variable" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=904CDCA174AACE4AA52660A247CDF9A0/@EntryIndexedValue"><NamingElement Priority="7" Title="Global variables"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global variable" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=95BCDE767C97B64DB3DAE800DBBBC758/@EntryIndexedValue"><NamingElement Priority="5" Title="Parameters"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="function parameter" /><type Name="lambda parameter" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=C03AE454FC2CBA43819AC75E4D6C9C8C/@EntryIndexedValue"><NamingElement Priority="1" Title="Classes and structs"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="__interface" /><type Name="class" /><type Name="struct" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=D49E31C610641E4CAD0407DB79ACC851/@EntryIndexedValue"><NamingElement Priority="8" Title="Global functions"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global function" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=D73FBB3529BC5449B6C85BB37B26A8D4/@EntryIndexedValue"><NamingElement Priority="16" Title="Namespaces"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="namespace" /><type Name="namespace alias" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=DA41807CE47AEB4CBE1724C44D0B786E/@EntryIndexedValue"><NamingElement Priority="6" Title="Local variables"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="local variable" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=DDF30C9A1DA74B4DBBC56D25FDF886AA/@EntryIndexedValue"><NamingElement Priority="10" Title="Class and struct fields"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="_" Suffix="" Style="aaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=EF70A6BF54ACE446971DDB32344C25A3/@EntryIndexedValue"><NamingElement Priority="12" Title="Union members"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="union member" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNamingOptions/Rules/=F37818C54C323A4A80B1A478629985AE/@EntryIndexedValue"><NamingElement Priority="2" Title="Enums"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="enum" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ARGB/@EntryIndexedValue">ARGB</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LO/@EntryIndexedValue">LO</s:String>
|
||||
@@ -213,6 +231,7 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=YZ/@EntryIndexedValue">YZ</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PublicFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/Environment/Hierarchy/PsiConfigurationSettingsKey/CustomLocation/@EntryValue">C:\Users\Wojtek\AppData\Local\JetBrains\Transient\ReSharperPlatformVs15\v08_f9eacea9\SolutionCaches</s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECpp_002ECodeStyle_002ENaming_002ECppNamingOptionsMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECpp_002ECodeStyle_002ESettingsUpgrade_002EFunctionReturnStyleSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECpp_002ECodeStyle_002ESettingsUpgrade_002ENamespaceIndentationSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<!-- Please search existing issues for potential duplicates before filing yours:
|
||||
https://github.com/flaxengine/FlaxEngine/issues?q=is%3Aissue
|
||||
-->
|
||||
|
||||
**Issue description:**
|
||||
<!-- What happened, and what was expected. -->
|
||||
<!-- Log file, can be found in the project directory's `Logs` folder (optional) -->
|
||||
|
||||
**Steps to reproduce:**
|
||||
<!-- Enter minimal reproduction steps if available. -->
|
||||
|
||||
|
||||
**Minimal reproduction project:**
|
||||
<!-- Recommended as it greatly speeds up debugging. Drag and drop a zip archive to upload it. -->
|
||||
|
||||
|
||||
**Flax version:**
|
||||
<!-- Specify version number. -->
|
||||
|
||||
@@ -57,9 +57,9 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Arch: `sudo pacman -S git git-lfs`
|
||||
* `git-lfs install`
|
||||
* Install the required packages:
|
||||
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev`
|
||||
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel`
|
||||
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib`
|
||||
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev zenity wayland-protocols libportal-dev`
|
||||
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel zenity wayland-protocols-devel libportal`
|
||||
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib zenity wayland-protocols libportal`
|
||||
* Install Clang compiler (version 6 or later):
|
||||
* Ubuntu: `sudo apt-get install clang lldb lld`
|
||||
* Fedora: `sudo dnf install clang llvm lldb lld`
|
||||
|
||||
@@ -117,7 +117,8 @@ namespace FlaxEditor.Content.Create
|
||||
|
||||
private static bool IsValid(Type type)
|
||||
{
|
||||
return (type.IsPublic || type.IsNestedPublic) && !type.IsAbstract && !type.IsGenericType;
|
||||
var controlTypes = Editor.Instance.CodeEditing.Controls.Get();
|
||||
return (type.IsPublic || type.IsNestedPublic) && !type.IsAbstract && !type.IsGenericType && controlTypes.Contains(new ScriptType(type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace FlaxEditor.Content
|
||||
|
||||
if (data is DragDataFiles)
|
||||
return DragDropEffect.Copy;
|
||||
return _dragOverItems.Effect;
|
||||
return _dragOverItems?.Effect ?? DragDropEffect.None;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string TypeDescription => Path.EndsWith(".h") ? "C++ Header File" : "C++ Source Code";
|
||||
public override string TypeDescription => Path.EndsWith(".h") || Path.EndsWith(".hpp") ? "C++ Header File" : "C++ Source Code";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CPPScript128;
|
||||
|
||||
@@ -18,8 +18,8 @@ namespace FlaxEditor
|
||||
private readonly CustomEditorPresenter _presenter;
|
||||
private CustomEditorWindow _customEditor;
|
||||
|
||||
public Win(CustomEditorWindow customEditor)
|
||||
: base(Editor.Instance, false, ScrollBars.Vertical)
|
||||
public Win(CustomEditorWindow customEditor, bool hideOnClose, ScrollBars scrollBars)
|
||||
: base(Editor.Instance, hideOnClose, scrollBars)
|
||||
{
|
||||
Title = customEditor.GetType().Name;
|
||||
_customEditor = customEditor;
|
||||
@@ -64,9 +64,9 @@ namespace FlaxEditor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CustomEditorWindow"/> class.
|
||||
/// </summary>
|
||||
protected CustomEditorWindow()
|
||||
protected CustomEditorWindow(bool hideOnClose = false, ScrollBars scrollBars = ScrollBars.Vertical)
|
||||
{
|
||||
_win = new Win(this);
|
||||
_win = new Win(this, hideOnClose, scrollBars);
|
||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||
}
|
||||
|
||||
|
||||
@@ -749,6 +749,15 @@ namespace FlaxEditor.CustomEditors
|
||||
}
|
||||
}
|
||||
|
||||
private Actor FindActor(CustomEditor editor)
|
||||
{
|
||||
if (editor.Values[0] is Actor actor)
|
||||
return actor;
|
||||
if (editor.ParentEditor != null)
|
||||
return FindActor(editor.ParentEditor);
|
||||
return null;
|
||||
}
|
||||
|
||||
private Actor FindPrefabRoot(CustomEditor editor)
|
||||
{
|
||||
if (editor.Values[0] is Actor actor)
|
||||
@@ -767,32 +776,35 @@ namespace FlaxEditor.CustomEditors
|
||||
return FindPrefabRoot(actor.Parent);
|
||||
}
|
||||
|
||||
private SceneObject FindObjectWithPrefabObjectId(Actor actor, ref Guid prefabObjectId)
|
||||
private SceneObject FindObjectWithPrefabObjectId(Actor actor, ref Guid prefabObjectId, Actor endPoint)
|
||||
{
|
||||
var visited = new HashSet<Actor>();
|
||||
return FindObjectWithPrefabObjectId(actor, ref prefabObjectId, endPoint, visited);
|
||||
}
|
||||
|
||||
private SceneObject FindObjectWithPrefabObjectId(Actor actor, ref Guid prefabObjectId, Actor endPoint, HashSet<Actor> visited)
|
||||
{
|
||||
if (visited.Contains(actor) || actor is Scene || actor == endPoint)
|
||||
return null;
|
||||
if (actor.PrefabObjectID == prefabObjectId)
|
||||
return actor;
|
||||
|
||||
for (int i = 0; i < actor.ScriptsCount; i++)
|
||||
{
|
||||
if (actor.GetScript(i).PrefabObjectID == prefabObjectId)
|
||||
{
|
||||
var a = actor.GetScript(i);
|
||||
if (a != null)
|
||||
return a;
|
||||
}
|
||||
var script = actor.GetScript(i);
|
||||
if (script != null && script.PrefabObjectID == prefabObjectId)
|
||||
return script;
|
||||
}
|
||||
|
||||
for (int i = 0; i < actor.ChildrenCount; i++)
|
||||
{
|
||||
if (actor.GetChild(i).PrefabObjectID == prefabObjectId)
|
||||
{
|
||||
var a = actor.GetChild(i);
|
||||
if (a != null)
|
||||
return a;
|
||||
}
|
||||
var child = actor.GetChild(i);
|
||||
if (child != null && child.PrefabObjectID == prefabObjectId)
|
||||
return child;
|
||||
}
|
||||
|
||||
return null;
|
||||
// Go up in the hierarchy
|
||||
return FindObjectWithPrefabObjectId(actor.Parent, ref prefabObjectId, endPoint, visited);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -826,7 +838,7 @@ namespace FlaxEditor.CustomEditors
|
||||
}
|
||||
|
||||
var prefabObjectId = referenceSceneObject.PrefabObjectID;
|
||||
var prefabInstanceRef = FindObjectWithPrefabObjectId(prefabInstanceRoot, ref prefabObjectId);
|
||||
var prefabInstanceRef = FindObjectWithPrefabObjectId(FindActor(this), ref prefabObjectId, prefabInstanceRoot);
|
||||
if (prefabInstanceRef == null)
|
||||
{
|
||||
Editor.LogWarning("Missing prefab instance reference in the prefab instance. Cannot revert to it.");
|
||||
|
||||
@@ -63,6 +63,11 @@ namespace FlaxEditor.CustomEditors
|
||||
/// Indication of if the properties window is locked on specific objects.
|
||||
/// </summary>
|
||||
public bool LockSelection { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scene editing context.
|
||||
/// </summary>
|
||||
public ISceneEditingContext SceneContext { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -87,8 +87,11 @@ namespace FlaxEditor.CustomEditors
|
||||
var targetTypeType = TypeUtils.GetType(targetType);
|
||||
if (canUseRefPicker)
|
||||
{
|
||||
// TODO: add generic way of CustomEditor for ref pickers (use it on AssetRefEditor/GPUTextureEditor/...)
|
||||
if (typeof(Asset).IsAssignableFrom(targetTypeType))
|
||||
return new AssetRefEditor();
|
||||
if (typeof(GPUTexture).IsAssignableFrom(targetTypeType))
|
||||
return new GPUTextureEditor();
|
||||
if (typeof(FlaxEngine.Object).IsAssignableFrom(targetTypeType))
|
||||
return new FlaxObjectRefEditor();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Actions;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
@@ -10,12 +7,14 @@ using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Tree;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using FlaxEngine.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -240,6 +239,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
node.TextColor = Color.OrangeRed;
|
||||
node.Text = Utilities.Utils.GetPropertyNameUI(removed.PrefabObject.GetType().Name);
|
||||
}
|
||||
// Removed Actor
|
||||
else if (editor is RemovedActorDummy removedActor)
|
||||
{
|
||||
node.TextColor = Color.OrangeRed;
|
||||
node.Text = $"{removedActor.PrefabObject.Name} ({Utilities.Utils.GetPropertyNameUI(removedActor.PrefabObject.GetType().Name)})";
|
||||
}
|
||||
// Actor or Script
|
||||
else if (editor.Values[0] is SceneObject sceneObject)
|
||||
{
|
||||
@@ -295,16 +300,40 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
// Not used
|
||||
}
|
||||
}
|
||||
|
||||
private class RemovedActorDummy : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The removed prefab object (from the prefab default instance).
|
||||
/// </summary>
|
||||
public Actor PrefabObject;
|
||||
|
||||
/// <summary>
|
||||
/// The prefab instance's parent.
|
||||
/// </summary>
|
||||
public Actor ParentActor;
|
||||
|
||||
/// <summary>
|
||||
/// The order of the removed actor in the parent.
|
||||
/// </summary>
|
||||
public int OrderInParent;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
// Not used
|
||||
}
|
||||
}
|
||||
|
||||
private TreeNode ProcessDiff(CustomEditor editor, bool skipIfNotModified = true)
|
||||
{
|
||||
// Special case for new Script added to actor
|
||||
if (editor.Values[0] is Script script && !script.HasPrefabLink)
|
||||
// Special case for new Script or child actor added to actor
|
||||
if ((editor.Values[0] is Script script && !script.HasPrefabLink) || (editor.Values[0] is Actor a && !a.HasPrefabLink))
|
||||
return CreateDiffNode(editor);
|
||||
|
||||
// Skip if no change detected
|
||||
var isRefEdited = editor.Values.IsReferenceValueModified;
|
||||
if (!isRefEdited && skipIfNotModified)
|
||||
if (!isRefEdited && skipIfNotModified && editor is not ScriptsEditor)
|
||||
return null;
|
||||
|
||||
TreeNode result = null;
|
||||
@@ -359,6 +388,44 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
}
|
||||
|
||||
// Compare child actors for removed actors.
|
||||
if (editor is ActorEditor && editor.Values.HasReferenceValue && editor.Values.ReferenceValue is Actor prefabObjectActor)
|
||||
{
|
||||
var thisActor = editor.Values[0] as Actor;
|
||||
for (int i = 0; i < prefabObjectActor.ChildrenCount; i++)
|
||||
{
|
||||
var prefabActorChild = prefabObjectActor.Children[i];
|
||||
if (thisActor == null)
|
||||
continue;
|
||||
bool isRemoved = true;
|
||||
for (int j = 0; j < thisActor.ChildrenCount; j++)
|
||||
{
|
||||
var actorChild = thisActor.Children[j];
|
||||
if (actorChild.PrefabObjectID == prefabActorChild.PrefabObjectID)
|
||||
{
|
||||
isRemoved = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isRemoved)
|
||||
{
|
||||
var dummy = new RemovedActorDummy
|
||||
{
|
||||
PrefabObject = prefabActorChild,
|
||||
ParentActor = thisActor,
|
||||
OrderInParent = prefabActorChild.OrderInParent,
|
||||
};
|
||||
var child = CreateDiffNode(dummy);
|
||||
if (result == null)
|
||||
result = CreateDiffNode(editor);
|
||||
result.AddChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (editor is ScriptsEditor && result != null && result.ChildrenCount == 0)
|
||||
return null;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -438,6 +505,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
Presenter.BuildLayoutOnUpdate();
|
||||
}
|
||||
|
||||
private static void GetAllPrefabObjects(List<object> objects, Actor actor)
|
||||
{
|
||||
objects.Add(actor);
|
||||
objects.AddRange(actor.Scripts);
|
||||
var children = actor.Children;
|
||||
foreach (var child in children)
|
||||
GetAllPrefabObjects(objects, child);
|
||||
}
|
||||
|
||||
private void OnDiffRevert(CustomEditor editor)
|
||||
{
|
||||
// Special case for removed Script from actor
|
||||
@@ -459,6 +535,22 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case for reverting removed Actors
|
||||
if (editor is RemovedActorDummy removedActor)
|
||||
{
|
||||
Editor.Log("Reverting removed actor changes to prefab (adding it)");
|
||||
|
||||
var parentActor = removedActor.ParentActor;
|
||||
var restored = parentActor.AddChild(removedActor.PrefabObject.GetType());
|
||||
var prefabId = parentActor.PrefabID;
|
||||
var prefabObjectId = removedActor.PrefabObject.PrefabObjectID;
|
||||
string data = JsonSerializer.Serialize(removedActor.PrefabObject);
|
||||
JsonSerializer.Deserialize(restored, data);
|
||||
Presenter.Owner.SceneContext.Spawn(restored, parentActor, removedActor.OrderInParent);
|
||||
Actor.Internal_LinkPrefab(FlaxEngine.Object.GetUnmanagedPtr(restored), ref prefabId, ref prefabObjectId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case for new Script added to actor
|
||||
if (editor.Values[0] is Script script && !script.HasPrefabLink)
|
||||
{
|
||||
@@ -470,8 +562,37 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case for new Actor added to actor
|
||||
if (editor.Values[0] is Actor a && !a.HasPrefabLink)
|
||||
{
|
||||
Editor.Log("Reverting added actor changes to prefab (removing it)");
|
||||
|
||||
editor.RevertToReferenceValue();
|
||||
// TODO: Keep previous selection.
|
||||
var context = Presenter.Owner.SceneContext;
|
||||
context.Select(SceneGraph.SceneGraphFactory.FindNode(a.ID));
|
||||
context.DeleteSelection();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Presenter.Undo != null && Presenter.Undo.Enabled)
|
||||
{
|
||||
var thisActor = (Actor)Values[0];
|
||||
var rootActor = thisActor.IsPrefabRoot ? thisActor : thisActor.GetPrefabRoot();
|
||||
var prefabObjects = new List<object>();
|
||||
GetAllPrefabObjects(prefabObjects, rootActor);
|
||||
using (new UndoMultiBlock(Presenter.Undo, prefabObjects, "Revert to Prefab"))
|
||||
{
|
||||
editor.RevertToReferenceValue();
|
||||
editor.RefreshInternal();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
editor.RevertToReferenceValue();
|
||||
editor.RefreshInternal();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
public class AudioSourceEditor : ActorEditor
|
||||
{
|
||||
private Label _infoLabel;
|
||||
private Slider _slider;
|
||||
private AudioSource.States _slideStartState;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -28,6 +30,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_infoLabel = playbackGroup.Label(string.Empty).Label;
|
||||
_infoLabel.AutoHeight = true;
|
||||
|
||||
// Play back slider
|
||||
var sliderElement = playbackGroup.CustomContainer<Slider>();
|
||||
_slider = sliderElement.CustomControl;
|
||||
_slider.ThumbSize = new Float2(_slider.ThumbSize.X * 0.5f, _slider.ThumbSize.Y);
|
||||
_slider.SlidingStart += OnSlidingStart;
|
||||
_slider.SlidingEnd += OnSlidingEnd;
|
||||
|
||||
var grid = playbackGroup.UniformGrid();
|
||||
var gridControl = grid.CustomControl;
|
||||
gridControl.ClipChildren = false;
|
||||
@@ -40,6 +49,38 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSlidingEnd()
|
||||
{
|
||||
foreach (var value in Values)
|
||||
{
|
||||
if (value is AudioSource audioSource && audioSource.Clip)
|
||||
{
|
||||
switch (_slideStartState)
|
||||
{
|
||||
case AudioSource.States.Playing:
|
||||
audioSource.Play();
|
||||
break;
|
||||
case AudioSource.States.Paused:
|
||||
case AudioSource.States.Stopped:
|
||||
audioSource.Pause();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSlidingStart()
|
||||
{
|
||||
foreach (var value in Values)
|
||||
{
|
||||
if (value is AudioSource audioSource && audioSource.Clip)
|
||||
{
|
||||
_slideStartState = audioSource.State;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
@@ -51,7 +92,29 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
foreach (var value in Values)
|
||||
{
|
||||
if (value is AudioSource audioSource && audioSource.Clip)
|
||||
{
|
||||
text += $"Time: {audioSource.Time:##0.0}s / {audioSource.Clip.Length:##0.0}s\n";
|
||||
_slider.Maximum = audioSource.Clip.Length;
|
||||
_slider.Minimum = 0;
|
||||
if (_slider.IsSliding)
|
||||
{
|
||||
if (audioSource.State != AudioSource.States.Playing)
|
||||
{
|
||||
// Play to move slider correctly
|
||||
audioSource.Play();
|
||||
audioSource.Time = _slider.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
audioSource.Time = _slider.Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_slider.Value = audioSource.Time;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
_infoLabel.Text = text;
|
||||
}
|
||||
|
||||
68
Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs
Normal file
68
Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic editor/viewer for <see cref="GPUTexture"/>.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(GPUTexture)), DefaultEditor]
|
||||
public class GPUTextureEditor : CustomEditor
|
||||
{
|
||||
private Image _image;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_image = new Image
|
||||
{
|
||||
Brush = new GPUTextureBrush(),
|
||||
Size = new Float2(200, 100),
|
||||
Parent = layout.ContainerControl,
|
||||
};
|
||||
_image.Clicked += OnImageClicked;
|
||||
}
|
||||
|
||||
private void OnImageClicked(Image image, MouseButton button)
|
||||
{
|
||||
var texture = Values[0] as GPUTexture;
|
||||
if (!texture || button != MouseButton.Right)
|
||||
return;
|
||||
var menu = new ContextMenu();
|
||||
menu.AddButton("Save...", () => Screenshot.Capture(Values[0] as GPUTexture));
|
||||
menu.AddButton("Enlarge", () => _image.Size *= 2);
|
||||
menu.AddButton("Shrink", () => _image.Size /= 2).Enabled = _image.Height > 32;
|
||||
var location = image.PointFromScreen(Input.MouseScreenPosition);
|
||||
menu.Show(image, location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
var texture = Values[0] as GPUTexture;
|
||||
((GPUTextureBrush)_image.Brush).Texture = texture;
|
||||
if (texture)
|
||||
{
|
||||
var desc = texture.Description;
|
||||
#if BUILD_RELEASE
|
||||
var name = string.Empty;
|
||||
#else
|
||||
var name = texture.Name;
|
||||
#endif
|
||||
_image.TooltipText = $"{name}\nType: {texture.ResourceType}\nSize: {desc.Width}x{desc.Height}\nFormat: {desc.Format}\nMemory: {Utilities.Utils.FormatBytesCount(texture.MemoryUsage)}";
|
||||
}
|
||||
else
|
||||
{
|
||||
_image.TooltipText = "None";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
ScriptName = scriptName;
|
||||
TooltipText = "Create a new script";
|
||||
DrawHighlights = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4;
|
||||
_addScriptsButton = new Button
|
||||
{
|
||||
TooltipText = "Add new scripts to the actor",
|
||||
TooltipText = "Add new scripts to the actor.",
|
||||
AnchorPreset = AnchorPresets.MiddleCenter,
|
||||
Text = buttonText,
|
||||
Parent = this,
|
||||
@@ -114,7 +115,16 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
cm.TextChanged += text =>
|
||||
{
|
||||
if (!IsValidScriptName(text))
|
||||
{
|
||||
// Remove NewScriptItems
|
||||
List<Control> newScriptItems = cm.ItemsPanel.Children.FindAll(c => c is NewScriptItem);
|
||||
foreach (var item in newScriptItems)
|
||||
{
|
||||
cm.ItemsPanel.RemoveChild(item);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (!cm.ItemsPanel.Children.Any(x => x.Visible && x is not NewScriptItem))
|
||||
{
|
||||
// If there are no visible items, that means the search failed so we can find the create script button or create one if it's the first time
|
||||
@@ -876,7 +886,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
// Add drag button to the group
|
||||
var scriptDrag = new DragImage
|
||||
{
|
||||
TooltipText = "Script reference",
|
||||
TooltipText = "Script reference.",
|
||||
AutoFocus = true,
|
||||
IsScrollable = false,
|
||||
Color = FlaxEngine.GUI.Style.Current.ForegroundGrey,
|
||||
|
||||
@@ -106,7 +106,6 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
_linkButton = new Button
|
||||
{
|
||||
BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Link32),
|
||||
Parent = LinkedLabel,
|
||||
Width = 18,
|
||||
Height = 18,
|
||||
@@ -189,6 +188,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
_linkButton.SetColors(backgroundColor);
|
||||
_linkButton.BorderColor = _linkButton.BorderColorSelected = _linkButton.BorderColorHighlighted = Color.Transparent;
|
||||
_linkButton.TooltipText = LinkValues ? "Unlinks scale components from uniform scaling" : "Links scale components for uniform scaling";
|
||||
_linkButton.BackgroundBrush = new SpriteBrush(LinkValues ? Editor.Instance.Icons.Link32 : Editor.Instance.Icons.BrokenLink32);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
menu.ItemsContainer.RemoveChildren();
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize;
|
||||
b = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
menu.AddSeparator();
|
||||
@@ -404,8 +406,10 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var menu = new ContextMenu();
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize;
|
||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
paste.Enabled = linkedEditor.CanPaste;
|
||||
paste.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
if (_canReorder)
|
||||
{
|
||||
@@ -418,7 +422,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
moveDownButton.Enabled = Index + 1 < Editor.Count;
|
||||
}
|
||||
|
||||
menu.AddButton("Remove", OnRemoveClicked);
|
||||
b = menu.AddButton("Remove", OnRemoveClicked);
|
||||
b.Enabled = !Editor._readOnly && Editor._canResize;
|
||||
|
||||
menu.Show(panel, location);
|
||||
}
|
||||
@@ -741,6 +746,34 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
cloned[srcIndex] = tmp;
|
||||
SetValue(cloned);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Duplicates the list item.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to duplicate.</param>
|
||||
public void Duplicate(int index)
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var count = Count;
|
||||
var newValues = Allocate(count + 1);
|
||||
var oldValues = (IList)Values[0];
|
||||
|
||||
for (int i = 0; i <= index; i++)
|
||||
{
|
||||
newValues[i] = oldValues[i];
|
||||
}
|
||||
|
||||
newValues[index + 1] = Utilities.Utils.CloneValue(oldValues[index]);
|
||||
|
||||
for (int i = index + 1; i < count; i++)
|
||||
{
|
||||
newValues[i + 1] = oldValues[i];
|
||||
}
|
||||
|
||||
SetValue(newValues);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shifts the specified item at the given index and moves it through the list to the other item. It supports undo.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
@@ -81,9 +82,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
private OptionType[] _options;
|
||||
private ScriptType _type;
|
||||
private Elements.PropertiesListElement _typeItem;
|
||||
|
||||
private ScriptType Type => Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RevertValueWithChildren => false; // Always revert value for a whole object
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
@@ -98,7 +103,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
_type = type;
|
||||
|
||||
// Type
|
||||
var typeEditor = layout.ComboBox(TypeComboBoxName, "Type of the object value. Use it to change the object.");
|
||||
_typeItem = layout.AddPropertyItem(TypeComboBoxName, "Type of the object value. Use it to change the object.");
|
||||
var typeEditor = _typeItem.ComboBox();
|
||||
for (int i = 0; i < _options.Length; i++)
|
||||
{
|
||||
typeEditor.ComboBox.AddItem(_options[i].Name);
|
||||
@@ -126,6 +132,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
// Value
|
||||
var values = new CustomValueContainer(type, (instance, index) => instance);
|
||||
if (Values.HasReferenceValue)
|
||||
values.SetReferenceValue(Values.ReferenceValue);
|
||||
values.AddRange(Values);
|
||||
var editor = CustomEditorsUtil.CreateEditor(type);
|
||||
var style = editor.Style;
|
||||
@@ -170,6 +178,12 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
// Show prefab diff when reference value type is different
|
||||
var color = Color.Transparent;
|
||||
if (Values.HasReferenceValue && CanRevertReferenceValue && Values[0]?.GetType() != Values.ReferenceValue?.GetType())
|
||||
color = FlaxEngine.GUI.Style.Current.BackgroundSelected;
|
||||
_typeItem.Labels[0].HighlightStripColor = color;
|
||||
|
||||
// Check if type has been modified outside the editor (eg. from code)
|
||||
if (Type != _type)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,9 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
public class PropertiesList : PanelWithMargins
|
||||
{
|
||||
// TODO: sync splitter for whole presenter
|
||||
|
||||
private const float SplitterPadding = 15;
|
||||
private const float EditorsMinWidthRatio = 0.4f;
|
||||
|
||||
/// <summary>
|
||||
/// The splitter size (in pixels).
|
||||
@@ -25,6 +28,7 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
private Rectangle _splitterRect;
|
||||
private bool _splitterClicked, _mouseOverSplitter;
|
||||
private bool _cursorChanged;
|
||||
private bool _hasCustomSplitterValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the splitter value (always in range [0; 1]).
|
||||
@@ -66,6 +70,26 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
UpdateSplitRect();
|
||||
}
|
||||
|
||||
private void AutoSizeSplitter()
|
||||
{
|
||||
if (_hasCustomSplitterValue || !Editor.Instance.Options.Options.Interface.AutoSizePropertiesPanelSplitter)
|
||||
return;
|
||||
|
||||
Font font = Style.Current.FontMedium;
|
||||
|
||||
float largestWidth = 0f;
|
||||
for (int i = 0; i < _element.Labels.Count; i++)
|
||||
{
|
||||
Label currentLabel = _element.Labels[i];
|
||||
Float2 dimensions = font.MeasureText(currentLabel.Text);
|
||||
float width = dimensions.X + currentLabel.Margin.Left + SplitterPadding;
|
||||
|
||||
largestWidth = Mathf.Max(largestWidth, width);
|
||||
}
|
||||
|
||||
SplitterValue = Mathf.Clamp(largestWidth / Width, 0, 1 - EditorsMinWidthRatio);
|
||||
}
|
||||
|
||||
private void UpdateSplitRect()
|
||||
{
|
||||
_splitterRect = new Rectangle(Mathf.Clamp(_splitterValue * Width - SplitterSize * 0.5f, 0.0f, Width), 0, SplitterSize, Height);
|
||||
@@ -122,6 +146,7 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
SplitterValue = location.X / Width;
|
||||
Cursor = CursorType.SizeWE;
|
||||
_cursorChanged = true;
|
||||
_hasCustomSplitterValue = true;
|
||||
}
|
||||
else if (_mouseOverSplitter)
|
||||
{
|
||||
@@ -195,6 +220,7 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
// Refresh
|
||||
UpdateSplitRect();
|
||||
PerformLayout(true);
|
||||
AutoSizeSplitter();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -51,6 +51,7 @@ namespace FlaxEditor
|
||||
private readonly List<EditorModule> _modules = new List<EditorModule>(16);
|
||||
private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode, _autoExit;
|
||||
private string _projectToOpen;
|
||||
private bool _projectIsNew;
|
||||
private float _lastAutoSaveTimer, _autoExitTimeout = 0.1f;
|
||||
private Button _saveNowButton;
|
||||
private Button _cancelSaveButton;
|
||||
@@ -528,7 +529,11 @@ namespace FlaxEditor
|
||||
var timeSinceLastSave = Time.UnscaledGameTime - _lastAutoSaveTimer;
|
||||
var timeToNextSave = options.AutoSaveFrequency * 60.0f - timeSinceLastSave;
|
||||
|
||||
if (timeToNextSave <= 0.0f || _autoSaveNow)
|
||||
if (timeToNextSave <= 0.0f && GetWindows().Any(x => x.GUI.Children.Any(c => c is GUI.ContextMenu.ContextMenuBase)))
|
||||
{
|
||||
// Skip aut-save if any context menu is opened to wait for user to end interaction
|
||||
}
|
||||
else if (timeToNextSave <= 0.0f || _autoSaveNow)
|
||||
{
|
||||
Log("Auto save");
|
||||
_lastAutoSaveTimer = Time.UnscaledGameTime;
|
||||
@@ -733,11 +738,12 @@ namespace FlaxEditor
|
||||
var procSettings = new CreateProcessSettings
|
||||
{
|
||||
FileName = Platform.ExecutableFilePath,
|
||||
Arguments = string.Format("-project \"{0}\"", _projectToOpen),
|
||||
Arguments = string.Format("-project \"{0}\"" + (_projectIsNew ? " -new" : string.Empty), _projectToOpen),
|
||||
ShellExecute = true,
|
||||
WaitForEnd = false,
|
||||
HiddenWindow = false,
|
||||
};
|
||||
_projectIsNew = false;
|
||||
_projectToOpen = null;
|
||||
Platform.CreateProcess(ref procSettings);
|
||||
}
|
||||
@@ -786,6 +792,24 @@ namespace FlaxEditor
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the given project. Afterwards closes this project with running editor and opens the given project.
|
||||
/// </summary>
|
||||
/// <param name="projectFilePath">The project file path.</param>
|
||||
public void NewProject(string projectFilePath)
|
||||
{
|
||||
if (projectFilePath == null)
|
||||
{
|
||||
MessageBox.Show("Missing project");
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache project path and start editor exit (it will open new instance on valid closing)
|
||||
_projectToOpen = StringUtils.NormalizePath(Path.GetDirectoryName(projectFilePath));
|
||||
_projectIsNew = true;
|
||||
Windows.MainWindow.Close(ClosingReason.User);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes this project with running editor and opens the given project.
|
||||
/// </summary>
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace FlaxEditor
|
||||
public SpriteHandle Globe32;
|
||||
public SpriteHandle CamSpeed32;
|
||||
public SpriteHandle Link32;
|
||||
public SpriteHandle BrokenLink32;
|
||||
public SpriteHandle Add32;
|
||||
public SpriteHandle Left32;
|
||||
public SpriteHandle Right32;
|
||||
@@ -94,6 +95,7 @@ namespace FlaxEditor
|
||||
public SpriteHandle Search64;
|
||||
public SpriteHandle Bone64;
|
||||
public SpriteHandle Link64;
|
||||
public SpriteHandle BrokenLink64;
|
||||
public SpriteHandle Build64;
|
||||
public SpriteHandle Add64;
|
||||
public SpriteHandle ShipIt64;
|
||||
|
||||
@@ -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;
|
||||
@@ -121,7 +124,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>
|
||||
@@ -256,7 +259,9 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
desc.AllowMaximize = false;
|
||||
desc.AllowDragAndDrop = false;
|
||||
desc.IsTopmost = true;
|
||||
desc.IsRegularWindow = false;
|
||||
desc.Type = WindowType.Popup;
|
||||
desc.Parent = parentWin.Window;
|
||||
desc.Title = "ContextMenu";
|
||||
desc.HasSizingFrame = false;
|
||||
OnWindowCreating(ref desc);
|
||||
_window = Platform.CreateWindow(ref desc);
|
||||
@@ -265,6 +270,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
_window.GotFocus += OnWindowGotFocus;
|
||||
_window.LostFocus += OnWindowLostFocus;
|
||||
}
|
||||
|
||||
#if USE_IS_FOREGROUND && USE_SDL_WORKAROUNDS
|
||||
// The focus between popup and parent windows doesn't change, force hide the popup when clicked on parent
|
||||
parentWin.Window.MouseDown += OnWindowMouseDown;
|
||||
_window.Closed += () => parentWin.Window.MouseDown -= OnWindowMouseDown;
|
||||
#endif
|
||||
|
||||
// Attach to the window
|
||||
_parentCM = parent as ContextMenuBase;
|
||||
@@ -440,6 +451,17 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_SDL_WORKAROUNDS
|
||||
private void OnWindowGotFocus()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnWindowMouseDown(ref Float2 mousePosition, MouseButton button, ref bool handled)
|
||||
{
|
||||
// The user clicked outside the popup window
|
||||
Hide();
|
||||
}
|
||||
#else
|
||||
private void OnWindowGotFocus()
|
||||
{
|
||||
var child = _childCM;
|
||||
@@ -453,6 +475,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnWindowLostFocus()
|
||||
{
|
||||
@@ -551,7 +574,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
// Let root context menu to check if none of the popup windows
|
||||
if (_parentCM == null && UseVisibilityControl && !IsForeground)
|
||||
{
|
||||
#if USE_SDL_WORKAROUNDS
|
||||
if (!IsMouseOver)
|
||||
Hide();
|
||||
#else
|
||||
Hide();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -326,8 +326,11 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
// Update eye dropper tool
|
||||
if (_activeEyedropper)
|
||||
{
|
||||
// Try reading the color under the cursor in realtime if supported by the platform
|
||||
Float2 mousePosition = Platform.MousePosition;
|
||||
SelectedColor = ScreenUtilities.GetColorAt(mousePosition);
|
||||
Color color = ScreenUtilities.GetColorAt(mousePosition);
|
||||
if (color != Color.Transparent)
|
||||
SelectedColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 += DockPanel.DefaultHeaderHeight;
|
||||
result.Size.Y -= DockPanel.DefaultHeaderHeight;
|
||||
break;
|
||||
case DockState.DockTop:
|
||||
result.Size.Y *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockLeft:
|
||||
result.Size.X *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockBottom:
|
||||
result.Location.Y += result.Size.Y * (1 - DockPanel.DefaultSplitterValue);
|
||||
result.Size.Y *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockRight:
|
||||
result.Location.X += result.Size.X * (1 - DockPanel.DefaultSplitterValue);
|
||||
result.Size.X *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void CalculateDragOffset(Float2 mouseScreenPosition)
|
||||
{
|
||||
var baseWinPos = _toMove.Window.Window.Position;
|
||||
_dragOffset = mouseScreenPosition - baseWinPos;
|
||||
}
|
||||
|
||||
private void UpdateRects()
|
||||
{
|
||||
// Cache mouse position
|
||||
_mouse = Platform.MousePosition;
|
||||
|
||||
// Check intersection with any dock panel
|
||||
var uiMouse = _mouse;
|
||||
_toDock = _toMove.MasterPanel.HitTest(ref uiMouse, _toMove);
|
||||
|
||||
// Check dock state to use
|
||||
bool showProxyHints = _toDock != null;
|
||||
bool showBorderHints = showProxyHints;
|
||||
bool showCenterHint = showProxyHints;
|
||||
if (showProxyHints)
|
||||
{
|
||||
// If moved window has not only tabs but also child panels disable docking as tab
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showCenterHint = false;
|
||||
|
||||
// Disable docking windows with one or more dock panels inside
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showBorderHints = false;
|
||||
|
||||
// Get dock area
|
||||
_rectDock = _toDock.DockAreaBounds;
|
||||
|
||||
// Cache dock rectangles
|
||||
var size = _rectDock.Size;
|
||||
var offset = _rectDock.Location;
|
||||
var borderMargin = 4.0f;
|
||||
var hintWindowsSize = Proxy.HintWindowsSize * Platform.DpiScale;
|
||||
var hintWindowsSize2 = hintWindowsSize * 0.5f;
|
||||
var centerX = size.X * 0.5f;
|
||||
var centerY = size.Y * 0.5f;
|
||||
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
|
||||
// Hit test
|
||||
DockState toSet = DockState.Float;
|
||||
if (showBorderHints)
|
||||
{
|
||||
if (_rUpper.Contains(_mouse))
|
||||
toSet = DockState.DockTop;
|
||||
else if (_rBottom.Contains(_mouse))
|
||||
toSet = DockState.DockBottom;
|
||||
else if (_rLeft.Contains(_mouse))
|
||||
toSet = DockState.DockLeft;
|
||||
else if (_rRight.Contains(_mouse))
|
||||
toSet = DockState.DockRight;
|
||||
}
|
||||
if (showCenterHint && _rCenter.Contains(_mouse))
|
||||
toSet = DockState.DockFill;
|
||||
_toSet = toSet;
|
||||
|
||||
// Show proxy hint windows
|
||||
Proxy.Down.Position = _rBottom.Location;
|
||||
Proxy.Left.Position = _rLeft.Location;
|
||||
Proxy.Right.Position = _rRight.Location;
|
||||
Proxy.Up.Position = _rUpper.Location;
|
||||
Proxy.Center.Position = _rCenter.Location;
|
||||
}
|
||||
else
|
||||
{
|
||||
_toSet = DockState.Float;
|
||||
}
|
||||
|
||||
// Update proxy hint windows visibility
|
||||
Proxy.Down.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Left.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Right.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Up.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Center.IsVisible = showProxyHints & showCenterHint;
|
||||
|
||||
// Calculate proxy/dock/window rectangles
|
||||
if (_toDock == null)
|
||||
{
|
||||
// Floating window over nothing
|
||||
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
// Floating window over dock panel
|
||||
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use only part of the dock panel to show hint
|
||||
_rectWindow = CalculateDockRect(_toSet, ref _rectDock);
|
||||
}
|
||||
}
|
||||
|
||||
// Update proxy window
|
||||
Proxy.Window.ClientBounds = _rectWindow;
|
||||
}
|
||||
|
||||
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseMove(ref Float2 mousePos)
|
||||
{
|
||||
// Recalculate the drag offset because the current mouse screen position was invalid when we initialized the window
|
||||
if (_lateDragOffsetUpdate)
|
||||
{
|
||||
// Calculate dragging offset and move window to the destination position
|
||||
CalculateDragOffset(mousePos);
|
||||
|
||||
// Reset state
|
||||
_lateDragOffsetUpdate = false;
|
||||
}
|
||||
|
||||
UpdateRects();
|
||||
}
|
||||
|
||||
private void OnLostFocus()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper proxy windows shared across docking panels. They are used to visualize docking window locations.
|
||||
/// </summary>
|
||||
public static class Proxy
|
||||
{
|
||||
/// <summary>
|
||||
/// The drag proxy window.
|
||||
/// </summary>
|
||||
public static Window Window;
|
||||
|
||||
/// <summary>
|
||||
/// The left hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Left;
|
||||
|
||||
/// <summary>
|
||||
/// The right hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Right;
|
||||
|
||||
/// <summary>
|
||||
/// The up hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Up;
|
||||
|
||||
/// <summary>
|
||||
/// The down hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Down;
|
||||
|
||||
/// <summary>
|
||||
/// The center hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Center;
|
||||
|
||||
/// <summary>
|
||||
/// The hint windows size.
|
||||
/// </summary>
|
||||
public const float HintWindowsSize = 32.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the hit proxy windows. Those windows are used to indicate drag target areas (left, right, top, bottom, etc.).
|
||||
/// </summary>
|
||||
public static void InitHitProxy()
|
||||
{
|
||||
CreateProxy(ref Left, "DockHint.Left");
|
||||
CreateProxy(ref Right, "DockHint.Right");
|
||||
CreateProxy(ref Up, "DockHint.Up");
|
||||
CreateProxy(ref Down, "DockHint.Down");
|
||||
CreateProxy(ref Center, "DockHint.Center");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the hint window.
|
||||
/// </summary>
|
||||
/// <param name="initSize">Initial size of the proxy window.</param>
|
||||
public static void Init(ref Float2 initSize)
|
||||
{
|
||||
if (Window == null)
|
||||
{
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = "DockHint.Window";
|
||||
settings.Size = initSize;
|
||||
settings.AllowInput = true;
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowMinimize = false;
|
||||
settings.HasBorder = false;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
settings.IsTopmost = true;
|
||||
|
||||
Window = Platform.CreateWindow(ref settings);
|
||||
Window.Opacity = 0.6f;
|
||||
Window.GUI.BackgroundColor = Style.Current.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
@@ -13,12 +14,12 @@ namespace FlaxEditor.GUI.Docking
|
||||
public class DockPanelProxy : ContainerControl
|
||||
{
|
||||
private DockPanel _panel;
|
||||
private InterfaceOptions.TabCloseButtonVisibility closeButtonVisibility;
|
||||
private double _dragEnterTime = -1;
|
||||
#if PLATFORM_WINDOWS
|
||||
private const bool HideTabForSingleTab = true;
|
||||
#else
|
||||
private const bool HideTabForSingleTab = false;
|
||||
#endif
|
||||
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;
|
||||
private readonly bool _hideTabForSingleTab = Utilities.Utils.HideSingleTabWindowTabBars();
|
||||
|
||||
/// <summary>
|
||||
/// The is mouse down flag (left button).
|
||||
@@ -49,14 +50,19 @@ 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.
|
||||
/// </summary>
|
||||
public DockWindow StartDragAsyncWindow;
|
||||
|
||||
private Rectangle HeaderRectangle => new Rectangle(0, 0, Width, DockPanel.DefaultHeaderHeight);
|
||||
private bool IsSingleFloatingWindow => HideTabForSingleTab && _panel.TabsCount == 1 && _panel.IsFloating && _panel.ChildPanelsCount == 0;
|
||||
private Rectangle HeaderRectangle => new Rectangle(0, 0, Width, _tabHeight);
|
||||
private bool IsSingleFloatingWindow => _hideTabForSingleTab && _panel.TabsCount == 1 && _panel.IsFloating && _panel.ChildPanelsCount == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DockPanelProxy"/> class.
|
||||
@@ -70,6 +76,14 @@ namespace FlaxEditor.GUI.Docking
|
||||
_panel = panel;
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
|
||||
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||
OnEditorOptionsChanged(Editor.Instance.Options.Options);
|
||||
}
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
closeButtonVisibility = options.Interface.ShowTabCloseButton;
|
||||
}
|
||||
|
||||
private DockWindow GetTabAtPos(Float2 position, out bool closeButton)
|
||||
@@ -80,11 +94,11 @@ namespace FlaxEditor.GUI.Docking
|
||||
var tabsCount = _panel.TabsCount;
|
||||
if (tabsCount == 1)
|
||||
{
|
||||
var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
if (HeaderRectangle.Contains(position))
|
||||
{
|
||||
closeButton = crossRect.Contains(position);
|
||||
result = _panel.GetTab(0);
|
||||
closeButton = crossRect.Contains(position) && IsCloseButtonVisible(result, closeButtonVisibility);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -93,15 +107,17 @@ namespace FlaxEditor.GUI.Docking
|
||||
for (int i = 0; i < tabsCount; i++)
|
||||
{
|
||||
var tab = _panel.GetTab(i);
|
||||
var titleSize = tab.TitleSize;
|
||||
var iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0;
|
||||
var width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth;
|
||||
var tabRect = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight);
|
||||
float width = CalculateTabWidth(tab, closeButtonVisibility);
|
||||
|
||||
if (_useMinimumTabWidth && width < _minimumTabWidth)
|
||||
width = _minimumTabWidth;
|
||||
|
||||
var tabRect = new Rectangle(x, 0, width, HeaderRectangle.Height);
|
||||
var isMouseOver = tabRect.Contains(position);
|
||||
if (isMouseOver)
|
||||
{
|
||||
var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
closeButton = crossRect.Contains(position);
|
||||
var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
closeButton = crossRect.Contains(position) && IsCloseButtonVisible(tab, closeButtonVisibility);
|
||||
result = tab;
|
||||
break;
|
||||
}
|
||||
@@ -112,6 +128,24 @@ namespace FlaxEditor.GUI.Docking
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool IsCloseButtonVisible(DockWindow win, InterfaceOptions.TabCloseButtonVisibility visibilityMode)
|
||||
{
|
||||
return visibilityMode != InterfaceOptions.TabCloseButtonVisibility.Never &&
|
||||
(visibilityMode == InterfaceOptions.TabCloseButtonVisibility.Always ||
|
||||
(visibilityMode == InterfaceOptions.TabCloseButtonVisibility.SelectedTab && _panel.SelectedTab == win));
|
||||
}
|
||||
|
||||
private float CalculateTabWidth(DockWindow win, InterfaceOptions.TabCloseButtonVisibility visibilityMode)
|
||||
{
|
||||
var iconWidth = win.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0;
|
||||
var width = win.TitleSize.X + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth;
|
||||
|
||||
if (IsCloseButtonVisible(win, visibilityMode))
|
||||
width += 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultButtonsSize;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
private void GetTabRect(DockWindow win, out Rectangle bounds)
|
||||
{
|
||||
FlaxEngine.Assertions.Assert.IsTrue(_panel.ContainsTab(win));
|
||||
@@ -129,10 +163,10 @@ namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
var tab = _panel.GetTab(i);
|
||||
var titleSize = tab.TitleSize;
|
||||
float width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin;
|
||||
float width = CalculateTabWidth(tab, closeButtonVisibility);
|
||||
if (tab == win)
|
||||
{
|
||||
bounds = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight);
|
||||
bounds = new Rectangle(x, 0, width, HeaderRectangle.Height);
|
||||
return;
|
||||
}
|
||||
x += width;
|
||||
@@ -165,7 +199,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (_panel.ChildPanelsCount == 0 && _panel.TabsCount == 1 && _panel.IsFloating)
|
||||
{
|
||||
// Create docking hint window but in an async manner
|
||||
DockHintWindow.Create(_panel as FloatWindowDockPanel);
|
||||
WindowDragHelper.StartDragging(_panel as FloatWindowDockPanel);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -176,7 +210,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
_panel.SelectTab(index - 1);
|
||||
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(win);
|
||||
WindowDragHelper.StartDragging(win, _panel.RootWindow.Window);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,7 +246,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
Render2D.DrawSprite(
|
||||
tab.Icon,
|
||||
new Rectangle(DockPanel.DefaultLeftTextMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize),
|
||||
new Rectangle(DockPanel.DefaultLeftTextMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize),
|
||||
style.Foreground);
|
||||
|
||||
}
|
||||
@@ -221,17 +255,20 @@ namespace FlaxEditor.GUI.Docking
|
||||
Render2D.DrawText(
|
||||
style.FontMedium,
|
||||
tab.Title,
|
||||
new Rectangle(DockPanel.DefaultLeftTextMargin + iconWidth, 0, Width - DockPanel.DefaultLeftTextMargin - DockPanel.DefaultButtonsSize - 2 * DockPanel.DefaultButtonsMargin, DockPanel.DefaultHeaderHeight),
|
||||
new Rectangle(DockPanel.DefaultLeftTextMargin + iconWidth, 0, Width - DockPanel.DefaultLeftTextMargin - DockPanel.DefaultButtonsSize - 2 * DockPanel.DefaultButtonsMargin, HeaderRectangle.Height),
|
||||
style.Foreground,
|
||||
TextAlignment.Near,
|
||||
TextAlignment.Center);
|
||||
|
||||
// Draw cross
|
||||
var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
bool isMouseOverCross = isMouseOver && crossRect.Contains(MousePosition);
|
||||
if (isMouseOverCross)
|
||||
Render2D.FillRectangle(crossRect, (containsFocus ? style.BackgroundSelected : style.LightBackground) * 1.3f);
|
||||
Render2D.DrawSprite(style.Cross, crossRect, isMouseOverCross ? style.Foreground : style.ForegroundGrey);
|
||||
if (IsCloseButtonVisible(tab, closeButtonVisibility))
|
||||
{
|
||||
// Draw cross
|
||||
var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
bool isMouseOverCross = isMouseOver && crossRect.Contains(MousePosition);
|
||||
if (isMouseOverCross)
|
||||
Render2D.FillRectangle(crossRect, (containsFocus ? style.BackgroundSelected : style.LightBackground) * 1.3f);
|
||||
Render2D.DrawSprite(style.Cross, crossRect, isMouseOverCross ? style.Foreground : style.ForegroundGrey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -245,10 +282,14 @@ namespace FlaxEditor.GUI.Docking
|
||||
// Cache data
|
||||
var tab = _panel.GetTab(i);
|
||||
var tabColor = Color.Black;
|
||||
var titleSize = tab.TitleSize;
|
||||
var iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0;
|
||||
var width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth;
|
||||
var tabRect = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight);
|
||||
|
||||
float width = CalculateTabWidth(tab, closeButtonVisibility);
|
||||
|
||||
if (_useMinimumTabWidth && width < _minimumTabWidth)
|
||||
width = _minimumTabWidth;
|
||||
|
||||
var tabRect = new Rectangle(x, 0, width, headerRect.Height);
|
||||
var isMouseOver = tabRect.Contains(MousePosition);
|
||||
var isSelected = _panel.SelectedTab == tab;
|
||||
|
||||
@@ -275,7 +316,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
Render2D.DrawSprite(
|
||||
tab.Icon,
|
||||
new Rectangle(x + DockPanel.DefaultLeftTextMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize),
|
||||
new Rectangle(x + DockPanel.DefaultLeftTextMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize),
|
||||
style.Foreground);
|
||||
|
||||
}
|
||||
@@ -284,27 +325,27 @@ namespace FlaxEditor.GUI.Docking
|
||||
Render2D.DrawText(
|
||||
style.FontMedium,
|
||||
tab.Title,
|
||||
new Rectangle(x + DockPanel.DefaultLeftTextMargin + iconWidth, 0, 10000, DockPanel.DefaultHeaderHeight),
|
||||
new Rectangle(x + DockPanel.DefaultLeftTextMargin + iconWidth, 0, 10000, HeaderRectangle.Height),
|
||||
style.Foreground,
|
||||
TextAlignment.Near,
|
||||
TextAlignment.Center);
|
||||
|
||||
// Draw cross
|
||||
if (isSelected || isMouseOver)
|
||||
if (IsCloseButtonVisible(tab, closeButtonVisibility))
|
||||
{
|
||||
var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
|
||||
bool isMouseOverCross = isMouseOver && crossRect.Contains(MousePosition);
|
||||
if (isMouseOverCross)
|
||||
Render2D.FillRectangle(crossRect, tabColor * 1.3f);
|
||||
Render2D.DrawSprite(style.Cross, crossRect, isMouseOverCross ? style.Foreground : style.ForegroundGrey);
|
||||
}
|
||||
|
||||
// Move
|
||||
// Set the start position for the next tab
|
||||
x += width;
|
||||
}
|
||||
|
||||
// Draw selected tab strip
|
||||
Render2D.FillRectangle(new Rectangle(0, DockPanel.DefaultHeaderHeight - 2, Width, 2), containsFocus ? style.BackgroundSelected : style.BackgroundNormal);
|
||||
Render2D.FillRectangle(new Rectangle(0, HeaderRectangle.Height - 2, Width, 2), containsFocus ? style.BackgroundSelected : style.BackgroundNormal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,6 +396,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (IsSingleFloatingWindow)
|
||||
return base.OnMouseDown(location, button);
|
||||
MouseDownWindow = GetTabAtPos(location, out IsMouseDownOverCross);
|
||||
MouseStartPosition = location;
|
||||
|
||||
// Check buttons
|
||||
if (button == MouseButton.Left)
|
||||
@@ -441,6 +483,20 @@ namespace FlaxEditor.GUI.Docking
|
||||
StartDrag(MouseDownWindow);
|
||||
MouseDownWindow = null;
|
||||
}
|
||||
// Check if single tab is tried to be moved
|
||||
else if (MouseDownWindow != null && _panel.TabsCount <= 1)
|
||||
{
|
||||
if ((MousePosition - MouseStartPosition).Length > 3)
|
||||
{
|
||||
// Clear flag
|
||||
IsMouseLeftButtonDown = false;
|
||||
|
||||
// Check tab under the mouse
|
||||
if (!IsMouseDownOverCross && MouseDownWindow != null)
|
||||
StartDrag(MouseDownWindow);
|
||||
MouseDownWindow = null;
|
||||
}
|
||||
}
|
||||
// Check if has more than one tab to change order
|
||||
else if (MouseDownWindow != null && _panel.TabsCount > 1)
|
||||
{
|
||||
@@ -522,7 +578,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (IsSingleFloatingWindow)
|
||||
rect = new Rectangle(0, 0, Width, Height);
|
||||
else
|
||||
rect = new Rectangle(0, DockPanel.DefaultHeaderHeight, Width, Height - DockPanel.DefaultHeaderHeight);
|
||||
rect = new Rectangle(0, HeaderRectangle.Height, Width, Height - HeaderRectangle.Height);
|
||||
}
|
||||
|
||||
private DragDropEffect TrySelectTabUnderLocation(ref Float2 location)
|
||||
|
||||
@@ -182,6 +182,25 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
|
||||
/// <param name="position">Window location.</param>
|
||||
public void ShowFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent)
|
||||
{
|
||||
CreateFloating(location, size, position, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the window in a floating state.
|
||||
/// </summary>
|
||||
public void CreateFloating()
|
||||
{
|
||||
CreateFloating(Float2.Zero, Float2.Zero);
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates the window in a floating state.
|
||||
/// </summary>
|
||||
/// <param name="location">Window location.</param>
|
||||
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
|
||||
/// <param name="position">Window location.</param>
|
||||
/// <param name="showWindow">Window visibility.</param>
|
||||
public void CreateFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent, bool showWindow = false)
|
||||
{
|
||||
Undock();
|
||||
|
||||
@@ -199,14 +218,17 @@ namespace FlaxEditor.GUI.Docking
|
||||
windowGUI.UnlockChildrenRecursive();
|
||||
windowGUI.PerformLayout();
|
||||
|
||||
// Show
|
||||
window.Show();
|
||||
window.BringToFront();
|
||||
window.Focus();
|
||||
OnShow();
|
||||
if (showWindow)
|
||||
{
|
||||
// Show
|
||||
window.Show();
|
||||
window.BringToFront();
|
||||
window.Focus();
|
||||
OnShow();
|
||||
|
||||
// Perform layout again
|
||||
windowGUI.PerformLayout();
|
||||
// Perform layout again
|
||||
windowGUI.PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,42 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// <seealso cref="DockPanel" />
|
||||
public class FloatWindowDockPanel : DockPanel
|
||||
{
|
||||
private class FloatWindowDecorations : WindowDecorations
|
||||
{
|
||||
private FloatWindowDockPanel _panel;
|
||||
|
||||
public FloatWindowDecorations(FloatWindowDockPanel panel)
|
||||
: base(panel.RootWindow)
|
||||
{
|
||||
_panel = panel;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
_panel.BeginDrag();
|
||||
return true;
|
||||
}
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
#if !PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
protected override WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
var hit = base.OnHitTest(ref mouse);
|
||||
if (hit == WindowHitCodes.Caption)
|
||||
{
|
||||
// Override the system behaviour when interacting with the caption area
|
||||
hit = WindowHitCodes.Client;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private MasterDockPanel _masterPanel;
|
||||
private WindowRootControl _window;
|
||||
|
||||
@@ -40,6 +76,26 @@ namespace FlaxEditor.GUI.Docking
|
||||
Parent = window;
|
||||
_window.Window.Closing += OnClosing;
|
||||
_window.Window.LeftButtonHit += OnLeftButtonHit;
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
var decorations = Parent.AddChild(new FloatWindowDecorations(this));
|
||||
decorations.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
base.PerformLayoutBeforeChildren();
|
||||
|
||||
var decorations = Parent.GetChild<FloatWindowDecorations>();
|
||||
if (decorations != null)
|
||||
{
|
||||
// Apply offset for the title bar
|
||||
foreach (var child in Children)
|
||||
child.Bounds = child.Bounds with { Y = decorations.Height, Height = Parent.Height - decorations.Height };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,7 +108,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
return;
|
||||
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(this);
|
||||
WindowDragHelper.StartDragging(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -71,22 +127,28 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.Title = title;
|
||||
settings.Size = size;
|
||||
settings.Position = location;
|
||||
settings.MinimumSize = new Float2(1);
|
||||
settings.MinimumSize = new Float2(200, 150);
|
||||
settings.MaximumSize = Float2.Zero; // Unlimited size
|
||||
settings.Fullscreen = false;
|
||||
settings.HasBorder = true;
|
||||
settings.SupportsTransparency = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ActivateWhenFirstShown = true;
|
||||
settings.AllowInput = true;
|
||||
settings.AllowMinimize = true;
|
||||
settings.AllowMaximize = true;
|
||||
settings.AllowDragAndDrop = true;
|
||||
settings.IsTopmost = false;
|
||||
settings.IsRegularWindow = true;
|
||||
settings.Type = WindowType.Regular;
|
||||
settings.HasSizingFrame = true;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
settings.ShowInTaskbar = true;
|
||||
settings.StartPosition = startPosition;
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
settings.HasBorder = false;
|
||||
//settings.HasSizingFrame = false;
|
||||
}
|
||||
|
||||
// Create window
|
||||
return Platform.CreateWindow(ref settings);
|
||||
|
||||
@@ -81,7 +81,6 @@ namespace FlaxEditor.GUI.Docking
|
||||
public DockPanel HitTest(ref Float2 position, FloatWindowDockPanel excluded)
|
||||
{
|
||||
// Check all floating windows
|
||||
// TODO: gather windows order and take it into account when performing test
|
||||
for (int i = 0; i < FloatingPanels.Count; i++)
|
||||
{
|
||||
var win = FloatingPanels[i];
|
||||
@@ -94,9 +93,44 @@ namespace FlaxEditor.GUI.Docking
|
||||
}
|
||||
|
||||
// Base
|
||||
//if (!Root?.RootWindow.Window.IsFocused ?? false)
|
||||
// return null;
|
||||
return base.HitTest(ref position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs hit test over dock panel.
|
||||
/// </summary>
|
||||
/// <param name="position">Window space position to test.</param>
|
||||
/// <param name="excluded">Floating window to omit during searching (and all docked to that one).</param>
|
||||
/// <param name="hitResults">Results of the hit test</param>
|
||||
/// <returns>True if any dock panels were hit, otherwise false.</returns>
|
||||
public bool HitTest(ref Float2 position, FloatWindowDockPanel excluded, out DockPanel[] hitResults)
|
||||
{
|
||||
// Check all floating windows
|
||||
List<DockPanel> results = new(FloatingPanels.Count);
|
||||
for (int i = 0; i < FloatingPanels.Count; i++)
|
||||
{
|
||||
var win = FloatingPanels[i];
|
||||
if (win.Visible && win != excluded)
|
||||
{
|
||||
var result = win.HitTest(ref position);
|
||||
if (result != null)
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Base
|
||||
//if (!Root?.RootWindow.Window.IsFocused ?? false)
|
||||
// return null;
|
||||
var baseResult = base.HitTest(ref position);
|
||||
if (baseResult != null)
|
||||
results.Add(baseResult);
|
||||
|
||||
hitResults = results.ToArray();
|
||||
return hitResults.Length > 0;
|
||||
}
|
||||
|
||||
internal void LinkWindow(DockWindow window)
|
||||
{
|
||||
// Add to the windows list
|
||||
|
||||
458
Source/Editor/GUI/Docking/WindowDragHelper.cs
Normal file
458
Source/Editor/GUI/Docking/WindowDragHelper.cs
Normal file
@@ -0,0 +1,458 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used to handle docking windows dragging and docking.
|
||||
/// </summary>
|
||||
public class WindowDragHelper
|
||||
{
|
||||
private FloatWindowDockPanel _toMove;
|
||||
|
||||
private Float2 _dragOffset;
|
||||
private Rectangle _rectDock;
|
||||
private Float2 _mouse;
|
||||
private DockState _toSet;
|
||||
private DockPanel _toDock;
|
||||
private Window _dragSourceWindow;
|
||||
|
||||
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
|
||||
private Control _dockHintDown, _dockHintUp, _dockHintLeft, _dockHintRight, _dockHintCenter;
|
||||
|
||||
/// <summary>
|
||||
/// The hint control size.
|
||||
/// </summary>
|
||||
public const float HintControlSize = 32.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The opacity of the dragged window when hint controls are shown.
|
||||
/// </summary>
|
||||
public const float DragWindowOpacity = 0.4f;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if any windows are being dragged.
|
||||
/// </summary>
|
||||
public static bool IsDragActive { get; private set; }
|
||||
|
||||
private WindowDragHelper(FloatWindowDockPanel toMove, Window dragSourceWindow)
|
||||
{
|
||||
IsDragActive = true;
|
||||
_toMove = toMove;
|
||||
_toSet = DockState.Float;
|
||||
var window = toMove.Window.Window;
|
||||
|
||||
// Bind events
|
||||
FlaxEngine.Scripting.Update += OnUpdate;
|
||||
window.MouseUp += OnMouseUp;
|
||||
|
||||
// Update rectangles
|
||||
UpdateRects(Platform.MousePosition);
|
||||
|
||||
// Ensure the dragged window stays on top of every other window
|
||||
window.IsAlwaysOnTop = true;
|
||||
|
||||
_dragSourceWindow = dragSourceWindow;
|
||||
if (_dragSourceWindow != null) // Detaching a tab from existing window
|
||||
{
|
||||
_dragOffset = new Float2(window.Size.X / 2, 10.0f);
|
||||
|
||||
_dragSourceWindow.MouseUp += OnMouseUp; // The mouse up event is sent to the source window on Windows
|
||||
|
||||
// TODO: when detaching tab in floating window (not main window), the drag source window is still main window?
|
||||
var dragSourceWindowWayland = toMove.MasterPanel?.RootWindow.Window ?? Editor.Instance.Windows.MainWindow;
|
||||
window.DoDragDrop(window.Title, _dragOffset, dragSourceWindowWayland);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dragOffset = window.MousePosition;
|
||||
window.DoDragDrop(window.Title, _dragOffset, window);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
IsDragActive = false;
|
||||
var window = _toMove?.Window?.Window;
|
||||
|
||||
// Unbind events
|
||||
FlaxEngine.Scripting.Update -= OnUpdate;
|
||||
if (window != null)
|
||||
window.MouseUp -= OnMouseUp;
|
||||
if (_dragSourceWindow != null)
|
||||
_dragSourceWindow.MouseUp -= OnMouseUp;
|
||||
|
||||
RemoveDockHints();
|
||||
|
||||
if (_toMove == null)
|
||||
return;
|
||||
|
||||
if (window != null)
|
||||
{
|
||||
window.Opacity = 1.0f;
|
||||
window.IsAlwaysOnTop = false;
|
||||
window.BringToFront();
|
||||
}
|
||||
|
||||
// Check if window won't be docked
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
if (window == null)
|
||||
return;
|
||||
|
||||
// Show base window
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
|
||||
|
||||
// Check if window has only single tab
|
||||
if (hasNoChildPanels && _toMove.TabsCount == 1)
|
||||
{
|
||||
// Dock window
|
||||
_toMove.GetTab(0).Show(_toSet, _toDock);
|
||||
}
|
||||
// Check if dock as tab and has no child panels
|
||||
else if (hasNoChildPanels && _toSet == DockState.DockFill)
|
||||
{
|
||||
// Dock all tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var selectedTab = _toMove.SelectedTab;
|
||||
|
||||
// Dock the first tab into the target location
|
||||
if (_toMove.TabsCount > 0)
|
||||
{
|
||||
var firstTab = _toMove.GetTab(0);
|
||||
firstTab.Show(_toSet, _toDock);
|
||||
|
||||
// Dock rest of the tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep selected tab being selected
|
||||
selectedTab?.SelectTab();
|
||||
}
|
||||
|
||||
// Focus target window
|
||||
_toDock.Root.Focus();
|
||||
}
|
||||
|
||||
_toMove = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start dragging a floating dock panel.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Floating dock panel to move.</param>
|
||||
/// <returns>The window drag helper object.</returns>
|
||||
public static WindowDragHelper StartDragging(FloatWindowDockPanel toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
return new WindowDragHelper(toMove, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start dragging a docked panel into a floating window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Dock window to move.</param>
|
||||
/// <param name="dragSourceWindow">The window where dragging started from.</param>
|
||||
/// <returns>The window drag helper object.</returns>
|
||||
public static WindowDragHelper StartDragging(DockWindow toMove, Window dragSourceWindow)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Create floating window
|
||||
toMove.CreateFloating();
|
||||
|
||||
// Get floating panel
|
||||
var window = (WindowRootControl)toMove.Root;
|
||||
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
|
||||
|
||||
return new WindowDragHelper(floatingPanelToMove, dragSourceWindow);
|
||||
}
|
||||
|
||||
private sealed class DragVisuals : Control
|
||||
{
|
||||
public DragVisuals()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.SelectionBorder);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDockHints()
|
||||
{
|
||||
if (_toDock == null)
|
||||
return;
|
||||
|
||||
if (_toDock.RootWindow.Window != _dragSourceWindow)
|
||||
_toDock.RootWindow.Window.MouseUp += OnMouseUp;
|
||||
|
||||
_dockHintDown = AddHintControl(new Float2(0.5f, 1));
|
||||
_dockHintUp = AddHintControl(new Float2(0.5f, 0));
|
||||
_dockHintLeft = AddHintControl(new Float2(0, 0.5f));
|
||||
_dockHintRight = AddHintControl(new Float2(1, 0.5f));
|
||||
_dockHintCenter = AddHintControl(new Float2(0.5f, 0.5f));
|
||||
|
||||
Control AddHintControl(Float2 pivot)
|
||||
{
|
||||
DragVisuals hintControl = _toDock.AddChild<DragVisuals>();
|
||||
hintControl.Size = new Float2(HintControlSize);
|
||||
hintControl.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
hintControl.Pivot = pivot;
|
||||
hintControl.PivotRelative = true;
|
||||
return hintControl;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveDockHints()
|
||||
{
|
||||
if (_toDock == null)
|
||||
return;
|
||||
|
||||
if (_toDock.RootWindow.Window != _dragSourceWindow)
|
||||
_toDock.RootWindow.Window.MouseUp -= OnMouseUp;
|
||||
|
||||
_dockHintDown?.Parent.RemoveChild(_dockHintDown);
|
||||
_dockHintUp?.Parent.RemoveChild(_dockHintUp);
|
||||
_dockHintLeft?.Parent.RemoveChild(_dockHintLeft);
|
||||
_dockHintRight?.Parent.RemoveChild(_dockHintRight);
|
||||
_dockHintCenter?.Parent.RemoveChild(_dockHintCenter);
|
||||
_dockHintDown = _dockHintUp = _dockHintLeft = _dockHintRight = _dockHintCenter = null;
|
||||
}
|
||||
|
||||
private void UpdateRects(Float2 mousePos)
|
||||
{
|
||||
// Cache mouse position
|
||||
_mouse = mousePos;
|
||||
|
||||
// Check intersection with any dock panel
|
||||
DockPanel dockPanel = null;
|
||||
if (_toMove.MasterPanel.HitTest(ref _mouse, _toMove, out var hitResults))
|
||||
{
|
||||
dockPanel = hitResults[0];
|
||||
|
||||
// Prefer panel which currently has focus
|
||||
foreach (var hit in hitResults)
|
||||
{
|
||||
if (hit.RootWindow.Window.IsFocused)
|
||||
{
|
||||
dockPanel = hit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer panel in the same window we hit earlier
|
||||
if (dockPanel?.RootWindow != _toDock?.RootWindow)
|
||||
{
|
||||
foreach (var hit in hitResults)
|
||||
{
|
||||
if (hit.RootWindow == _toDock?.RootWindow)
|
||||
{
|
||||
dockPanel = _toDock;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dockPanel != _toDock)
|
||||
{
|
||||
RemoveDockHints();
|
||||
_toDock = dockPanel;
|
||||
AddDockHints();
|
||||
|
||||
// Make sure the all the dock hint areas are not under other windows
|
||||
_toDock?.RootWindow.Window.BringToFront();
|
||||
//_toDock?.RootWindow.Window.Focus();
|
||||
|
||||
// Make the dragged window transparent when dock hints are visible
|
||||
_toMove.Window.Window.Opacity = _toDock == null ? 1.0f : DragWindowOpacity;
|
||||
}
|
||||
|
||||
// Check dock state to use
|
||||
bool showProxyHints = _toDock != null;
|
||||
bool showBorderHints = showProxyHints;
|
||||
bool showCenterHint = showProxyHints;
|
||||
Control hoveredHintControl = null;
|
||||
Float2 hoveredSizeOverride = Float2.Zero;
|
||||
if (showProxyHints)
|
||||
{
|
||||
// If moved window has not only tabs but also child panels disable docking as tab
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showCenterHint = false;
|
||||
|
||||
// Disable docking windows with one or more dock panels inside
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showBorderHints = false;
|
||||
|
||||
// Get dock area
|
||||
_rectDock = _toDock.DockAreaBounds;
|
||||
|
||||
// Cache dock rectangles
|
||||
var size = _rectDock.Size / Platform.DpiScale;
|
||||
var offset = _toDock.PointFromScreen(_rectDock.Location);
|
||||
var borderMargin = 4.0f;
|
||||
var hintWindowsSize = HintControlSize;
|
||||
var hintWindowsSize2 = hintWindowsSize * 0.5f;
|
||||
var hintPreviewSize = new Float2(Math.Max(HintControlSize * 2, size.X * 0.5f), Math.Max(HintControlSize * 2, size.Y * 0.5f));
|
||||
var centerX = size.X * 0.5f;
|
||||
var centerY = size.Y * 0.5f;
|
||||
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
|
||||
// Hit test, and calculate the approximation for filled area when hovered over the hint
|
||||
DockState toSet = DockState.Float;
|
||||
if (showBorderHints)
|
||||
{
|
||||
if (_rUpper.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockTop;
|
||||
hoveredHintControl = _dockHintUp;
|
||||
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
|
||||
}
|
||||
else if (_rBottom.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockBottom;
|
||||
hoveredHintControl = _dockHintDown;
|
||||
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
|
||||
}
|
||||
else if (_rLeft.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockLeft;
|
||||
hoveredHintControl = _dockHintLeft;
|
||||
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
|
||||
}
|
||||
else if (_rRight.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockRight;
|
||||
hoveredHintControl = _dockHintRight;
|
||||
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
|
||||
}
|
||||
}
|
||||
if (showCenterHint && _rCenter.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockFill;
|
||||
hoveredHintControl = _dockHintCenter;
|
||||
hoveredSizeOverride = new Float2(size.X, size.Y);
|
||||
}
|
||||
|
||||
_toSet = toSet;
|
||||
}
|
||||
else
|
||||
{
|
||||
_toSet = DockState.Float;
|
||||
}
|
||||
|
||||
// Update sizes and opacity of hint controls
|
||||
if (_toDock != null)
|
||||
{
|
||||
if (hoveredHintControl != _dockHintDown)
|
||||
{
|
||||
_dockHintDown.Size = new Float2(HintControlSize);
|
||||
_dockHintDown.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintLeft)
|
||||
{
|
||||
_dockHintLeft.Size = new Float2(HintControlSize);
|
||||
_dockHintLeft.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintRight)
|
||||
{
|
||||
_dockHintRight.Size = new Float2(HintControlSize);
|
||||
_dockHintRight.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintUp)
|
||||
{
|
||||
_dockHintUp.Size = new Float2(HintControlSize);
|
||||
_dockHintUp.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintCenter)
|
||||
{
|
||||
_dockHintCenter.Size = new Float2(HintControlSize);
|
||||
_dockHintCenter.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
|
||||
if (_toSet != DockState.Float)
|
||||
{
|
||||
if (hoveredHintControl != null)
|
||||
{
|
||||
hoveredHintControl.BackgroundColor = Style.Current.Selection.AlphaMultiplied(1.0f);
|
||||
hoveredHintControl.Size = hoveredSizeOverride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update hint controls visibility and location
|
||||
if (showProxyHints)
|
||||
{
|
||||
if (hoveredHintControl != _dockHintDown)
|
||||
_dockHintDown.Location = _rBottom.Location;
|
||||
if (hoveredHintControl != _dockHintLeft)
|
||||
_dockHintLeft.Location = _rLeft.Location;
|
||||
if (hoveredHintControl != _dockHintRight)
|
||||
_dockHintRight.Location = _rRight.Location;
|
||||
if (hoveredHintControl != _dockHintUp)
|
||||
_dockHintUp.Location = _rUpper.Location;
|
||||
if (hoveredHintControl != _dockHintCenter)
|
||||
_dockHintCenter.Location = _rCenter.Location;
|
||||
|
||||
_dockHintDown.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintLeft.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintRight.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintUp.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintCenter.Visible = showProxyHints & showCenterHint;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void OnUpdate()
|
||||
{
|
||||
var mousePos = Platform.MousePosition;
|
||||
|
||||
if (_mouse != mousePos)
|
||||
OnMouseMove(mousePos);
|
||||
}
|
||||
|
||||
private void OnMouseMove(Float2 mousePos)
|
||||
{
|
||||
if (_dragSourceWindow != null)
|
||||
_toMove.Window.Window.Position = mousePos - _dragOffset;
|
||||
|
||||
UpdateRects(mousePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,11 @@ namespace FlaxEditor.GUI
|
||||
/// </summary>
|
||||
public float SortScore;
|
||||
|
||||
/// <summary>
|
||||
/// Wether the query highlights should be draw.
|
||||
/// </summary>
|
||||
public bool DrawHighlights = true;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when items gets clicked by the user.
|
||||
/// </summary>
|
||||
@@ -165,7 +170,7 @@ namespace FlaxEditor.GUI
|
||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundHighlighted);
|
||||
|
||||
// Draw all highlights
|
||||
if (_highlights != null)
|
||||
if (DrawHighlights && _highlights != null)
|
||||
{
|
||||
var color = style.ProgressNormal * 0.6f;
|
||||
for (int i = 0; i < _highlights.Count; i++)
|
||||
@@ -514,20 +519,15 @@ namespace FlaxEditor.GUI
|
||||
var items = ItemsPanel.Children;
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (items[i] is Item item && item.Visible)
|
||||
var currentItem = items[i];
|
||||
if (currentItem is Item item && item.Visible)
|
||||
result.Add(item);
|
||||
}
|
||||
if (_categoryPanels != null)
|
||||
{
|
||||
for (int i = 0; i < _categoryPanels.Count; i++)
|
||||
else if (currentItem is DropPanel category && (!ignoreFoldedCategories || !category.IsClosed) && currentItem.Visible)
|
||||
{
|
||||
var category = _categoryPanels[i];
|
||||
if (!category.Visible || (ignoreFoldedCategories && category is DropPanel panel && panel.IsClosed))
|
||||
continue;
|
||||
for (int j = 0; j < category.Children.Count; j++)
|
||||
{
|
||||
if (category.Children[j] is Item item2 && item2.Visible)
|
||||
result.Add(item2);
|
||||
if (category.Children[j] is Item categoryItem && categoryItem.Visible)
|
||||
result.Add(categoryItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -591,10 +591,6 @@ namespace FlaxEditor.GUI
|
||||
var items = GetVisibleItems(!controlDown);
|
||||
var focusedIndex = items.IndexOf(focusedItem);
|
||||
|
||||
// If the user hasn't selected anything yet and is holding control, focus first folded item
|
||||
if (focusedIndex == -1 && controlDown)
|
||||
focusedIndex = GetVisibleItems(true).Count - 1;
|
||||
|
||||
int delta = key == KeyboardKeys.ArrowDown ? -1 : 1;
|
||||
int nextIndex = Mathf.Wrap(focusedIndex - delta, 0, items.Count - 1);
|
||||
var nextItem = items[nextIndex];
|
||||
|
||||
@@ -12,16 +12,6 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public sealed class MainMenu : ContainerControl
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
private bool _useCustomWindowSystem;
|
||||
private Image _icon;
|
||||
private Label _title;
|
||||
private Button _closeButton;
|
||||
private Button _minimizeButton;
|
||||
private Button _maximizeButton;
|
||||
private LocalizedString _charChromeRestore, _charChromeMaximize;
|
||||
private Window _window;
|
||||
#endif
|
||||
private MainMenuButton _selected;
|
||||
|
||||
/// <summary>
|
||||
@@ -60,200 +50,12 @@ namespace FlaxEditor.GUI
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MainMenu"/> class.
|
||||
/// </summary>
|
||||
/// <param name="mainWindow">The main window.</param>
|
||||
public MainMenu(RootControl mainWindow)
|
||||
public MainMenu()
|
||||
: base(0, 0, 0, 20)
|
||||
{
|
||||
AutoFocus = false;
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
_useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
Height = 28;
|
||||
|
||||
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
|
||||
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
|
||||
Font iconFont = windowIconsFont?.CreateFont(9);
|
||||
|
||||
_window = mainWindow.RootWindow.Window;
|
||||
_window.HitTest += OnHitTest;
|
||||
_window.Closed += OnWindowClosed;
|
||||
|
||||
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
|
||||
|
||||
_icon = new Image
|
||||
{
|
||||
Margin = new Margin(6, 6, 6, 6),
|
||||
Brush = new TextureBrush(windowIcon),
|
||||
Color = Style.Current.Foreground,
|
||||
KeepAspectRatio = false,
|
||||
TooltipText = string.Format("{0}\nVersion {1}\nConfiguration {3}\nGraphics {2}", _window.Title, Globals.EngineVersion, GPUDevice.Instance.RendererType, configuration),
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_title = new Label(0, 0, Width, Height)
|
||||
{
|
||||
Text = _window.Title,
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = TextAlignment.Center,
|
||||
ClipText = true,
|
||||
TextColor = Style.Current.ForegroundGrey,
|
||||
TextColorHighlighted = Style.Current.ForegroundGrey,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_closeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Color.Red,
|
||||
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
|
||||
|
||||
_minimizeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_minimizeButton.Clicked += () => _window.Minimize();
|
||||
|
||||
_maximizeButton = new Button
|
||||
{
|
||||
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_maximizeButton.Clicked += () =>
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
};
|
||||
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
|
||||
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
_maximizeButton.Text = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowClosed()
|
||||
{
|
||||
if (_window != null)
|
||||
{
|
||||
_window.HitTest = null;
|
||||
_window = null;
|
||||
}
|
||||
}
|
||||
|
||||
private WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
var dpiScale = _window.DpiScale;
|
||||
|
||||
if (_window.IsMinimized)
|
||||
return WindowHitCodes.NoWhere;
|
||||
|
||||
if (!_window.IsMaximized)
|
||||
{
|
||||
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
|
||||
var winSize = _window.Size;
|
||||
|
||||
// Distance from which the mouse is considered to be on the border/corner
|
||||
float distance = 5.0f * dpiScale;
|
||||
|
||||
if (pos.Y > winSize.Y - distance && pos.X < distance)
|
||||
return WindowHitCodes.BottomLeft;
|
||||
|
||||
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.BottomRight;
|
||||
|
||||
if (pos.Y < distance && pos.X < distance)
|
||||
return WindowHitCodes.TopLeft;
|
||||
|
||||
if (pos.Y < distance && pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.TopRight;
|
||||
|
||||
if (pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.Right;
|
||||
|
||||
if (pos.X < distance)
|
||||
return WindowHitCodes.Left;
|
||||
|
||||
if (pos.Y < distance)
|
||||
return WindowHitCodes.Top;
|
||||
|
||||
if (pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.Bottom;
|
||||
}
|
||||
|
||||
var mousePos = PointFromScreen(mouse * dpiScale);
|
||||
var controlUnderMouse = GetChildAt(mousePos);
|
||||
var isMouseOverSth = controlUnderMouse != null && controlUnderMouse != _title;
|
||||
var rb = GetRightButton();
|
||||
if (rb != null && _minimizeButton != null && new Rectangle(rb.UpperRight, _minimizeButton.BottomLeft - rb.UpperRight).Contains(ref mousePos) && !isMouseOverSth)
|
||||
return WindowHitCodes.Caption;
|
||||
|
||||
return WindowHitCodes.Client;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Return the rightmost button.
|
||||
/// </summary>
|
||||
/// <returns>Rightmost button, null if there is no <see cref="MainMenuButton"/></returns>
|
||||
private MainMenuButton GetRightButton()
|
||||
{
|
||||
MainMenuButton b = null;
|
||||
foreach (var control in Children)
|
||||
{
|
||||
if (b == null && control is MainMenuButton)
|
||||
b = (MainMenuButton)control;
|
||||
|
||||
if (control is MainMenuButton && control.Right > b.Right)
|
||||
b = (MainMenuButton)control;
|
||||
}
|
||||
return b;
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -298,26 +100,6 @@ namespace FlaxEditor.GUI
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseDoubleClick(location, button))
|
||||
return true;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
var child = GetChildAtRecursive(location);
|
||||
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
@@ -333,16 +115,8 @@ namespace FlaxEditor.GUI
|
||||
protected override void PerformLayoutAfterChildren()
|
||||
{
|
||||
float x = 0;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Icon
|
||||
_icon.X = x;
|
||||
_icon.Size = new Float2(Height);
|
||||
x += _icon.Width;
|
||||
}
|
||||
#endif
|
||||
WindowDecorations decorations = Parent.GetChild<WindowDecorations>();
|
||||
x += decorations?.Icon?.Width ?? 0;
|
||||
|
||||
// Arrange controls
|
||||
MainMenuButton rightMostButton = null;
|
||||
@@ -361,37 +135,21 @@ namespace FlaxEditor.GUI
|
||||
x += b.Width;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Buttons
|
||||
_closeButton.Height = Height;
|
||||
_closeButton.X = Width - _closeButton.Width;
|
||||
_maximizeButton.Height = Height;
|
||||
_maximizeButton.X = _closeButton.X - _maximizeButton.Width;
|
||||
_minimizeButton.Height = Height;
|
||||
_minimizeButton.X = _maximizeButton.X - _minimizeButton.Width;
|
||||
|
||||
// Title
|
||||
_title.Bounds = new Rectangle(x + 2, 0, _minimizeButton.Left - x - 4, Height);
|
||||
//_title.Text = _title.Width < 300.0f ? Editor.Instance.ProjectInfo.Name : _window.Title;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fill the right side if title and buttons are not present
|
||||
if (decorations?.Title == null)
|
||||
Width = Parent.Width;
|
||||
else
|
||||
Width = x;
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_window != null)
|
||||
{
|
||||
_window.Closed -= OnWindowClosed;
|
||||
OnWindowClosed();
|
||||
}
|
||||
|
||||
if (_selected != null)
|
||||
Selected = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,12 @@ namespace FlaxEditor.GUI
|
||||
Text = text;
|
||||
|
||||
var style = Style.Current;
|
||||
#if PLATFORM_WINDOWS
|
||||
if (Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
|
||||
if (!Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
BackgroundColorMouseOver = style.BackgroundHighlighted;
|
||||
BackgroundColorMouseOverOpened = style.Background;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BackgroundColorMouseOver = BackgroundColorMouseOverOpened = style.LightBackground * 1.3f;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="FlaxEngine.GUI.Panel" />
|
||||
public class NavigationBar : Panel
|
||||
{
|
||||
private float _toolstripHeight = 0;
|
||||
private Margin _toolstripMargin;
|
||||
|
||||
/// <summary>
|
||||
/// The default buttons margin.
|
||||
/// </summary>
|
||||
@@ -50,9 +53,42 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
if (toolstrip == null)
|
||||
return;
|
||||
|
||||
if (_toolstripHeight <= 0.0f)
|
||||
{
|
||||
// Cache initial toolstrip state
|
||||
_toolstripHeight = toolstrip.Height;
|
||||
_toolstripMargin = toolstrip.ItemsMargin;
|
||||
}
|
||||
|
||||
// Control toolstrip bottom margin to prevent navigation bar scroll going over the buttons
|
||||
var toolstripLocked = toolstrip.IsLayoutLocked;
|
||||
toolstrip.IsLayoutLocked = true;
|
||||
var toolstripHeight = _toolstripHeight;
|
||||
var toolstripMargin = _toolstripMargin;
|
||||
if (HScrollBar.Visible)
|
||||
{
|
||||
float scrollMargin = 8;
|
||||
toolstripHeight += scrollMargin;
|
||||
toolstripMargin.Bottom += scrollMargin;
|
||||
}
|
||||
toolstrip.Height = toolstripHeight;
|
||||
toolstrip.IsLayoutLocked = toolstripLocked;
|
||||
toolstrip.ItemsMargin = toolstripMargin;
|
||||
|
||||
var lastToolstripButton = toolstrip.LastButton;
|
||||
var parentSize = Parent.Size;
|
||||
Bounds = new Rectangle(lastToolstripButton.Right + 8.0f, 0, parentSize.X - X - 8.0f, toolstrip.Height);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PerformLayout(bool force = false)
|
||||
{
|
||||
base.PerformLayout(force);
|
||||
|
||||
// Stretch excluding toolstrip margin to fill the space
|
||||
if (Parent is ToolStrip toolStrip)
|
||||
Height = toolStrip.Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,10 @@ namespace FlaxEditor.GUI
|
||||
/// <returns>Created popup.</returns>
|
||||
public static RenamePopup Show(Control control, Rectangle area, string value, bool isMultiline)
|
||||
{
|
||||
// hardcoded flushing layout for tree controls
|
||||
if (control is Tree.TreeNode treeNode && treeNode.ParentTree != null)
|
||||
treeNode.ParentTree.FlushPendingPerformLayout();
|
||||
|
||||
// Calculate the control size in the window space to handle scaled controls
|
||||
var upperLeft = control.PointToWindow(area.UpperLeft);
|
||||
var bottomRight = control.PointToWindow(area.BottomRight);
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Text;
|
||||
using FlaxEditor.GUI.Timeline.Undo;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
@@ -54,7 +55,10 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
var paramTypeName = LoadName(stream);
|
||||
e.EventParamsTypes[i] = TypeUtils.GetManagedType(paramTypeName);
|
||||
if (e.EventParamsTypes[i] == null)
|
||||
{
|
||||
Editor.LogError($"Unknown type {paramTypeName}.");
|
||||
isInvalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInvalid)
|
||||
@@ -82,7 +86,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
for (int j = 0; j < paramsCount; j++)
|
||||
{
|
||||
stream.Read(dataBuffer, 0, e.EventParamsSizes[j]);
|
||||
key.Parameters[j] = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), e.EventParamsTypes[j]);
|
||||
key.Parameters[j] = Utilities.Utils.ByteArrayToStructure(handle.AddrOfPinnedObject(), e.EventParamsTypes[j], e.EventParamsSizes[j]);
|
||||
}
|
||||
|
||||
events[i] = new KeyframesEditor.Keyframe
|
||||
@@ -125,8 +129,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
|
||||
for (int j = 0; j < paramsCount; j++)
|
||||
{
|
||||
Marshal.StructureToPtr(key.Parameters[j], ptr, true);
|
||||
Marshal.Copy(ptr, dataBuffer, 0, e.EventParamsSizes[j]);
|
||||
Utilities.Utils.StructureToByteArray(key.Parameters[j], e.EventParamsSizes[j], ptr, dataBuffer);
|
||||
stream.Write(dataBuffer, 0, e.EventParamsSizes[j]);
|
||||
}
|
||||
}
|
||||
@@ -153,7 +156,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
/// <summary>
|
||||
/// The event key data.
|
||||
/// </summary>
|
||||
public struct EventKey
|
||||
public struct EventKey : ICloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// The parameters values.
|
||||
@@ -178,6 +181,26 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
sb.Append(')');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Clone()
|
||||
{
|
||||
if (Parameters == null)
|
||||
return new EventKey();
|
||||
|
||||
// Deep clone parameter values (especially boxed value types need to be duplicated to avoid referencing the same ones)
|
||||
var parameters = new object[Parameters.Length];
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var p = Parameters[i];
|
||||
if (p == null || p is FlaxEngine.Object)
|
||||
parameters[i] = Parameters[i];
|
||||
else
|
||||
parameters[i] = JsonSerializer.Deserialize(JsonSerializer.Serialize(p), p.GetType());
|
||||
}
|
||||
|
||||
return new EventKey { Parameters = parameters };
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -234,6 +257,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
var time = Timeline.CurrentTime;
|
||||
if (!TryGetValue(out var value))
|
||||
value = Events.Evaluate(time);
|
||||
value = ((ICloneable)value).Clone();
|
||||
|
||||
// Find event at the current location
|
||||
for (int i = Events.Keyframes.Count - 1; i >= 0; i--)
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
{
|
||||
var time = stream.ReadSingle();
|
||||
stream.Read(dataBuffer, 0, e.ValueSize);
|
||||
var value = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), propertyType);
|
||||
var value = Utilities.Utils.ByteArrayToStructure(handle.AddrOfPinnedObject(), propertyType, e.ValueSize);
|
||||
|
||||
keyframes[i] = new KeyframesEditor.Keyframe
|
||||
{
|
||||
@@ -142,8 +142,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
for (int i = 0; i < keyframes.Count; i++)
|
||||
{
|
||||
var keyframe = keyframes[i];
|
||||
Marshal.StructureToPtr(keyframe.Value, ptr, true);
|
||||
Marshal.Copy(ptr, dataBuffer, 0, e.ValueSize);
|
||||
Utilities.Utils.StructureToByteArray(keyframe.Value, e.ValueSize, ptr, dataBuffer);
|
||||
stream.Write(keyframe.Time);
|
||||
stream.Write(dataBuffer);
|
||||
}
|
||||
|
||||
@@ -13,15 +13,7 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public class ToolStrip : ContainerControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The default margin vertically.
|
||||
/// </summary>
|
||||
public const int DefaultMarginV = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The default margin horizontally.
|
||||
/// </summary>
|
||||
public const int DefaultMarginH = 2;
|
||||
private Margin _itemsMargin;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when button gets clicked with the primary mouse button.
|
||||
@@ -66,10 +58,26 @@ namespace FlaxEditor.GUI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the space around items.
|
||||
/// </summary>
|
||||
public Margin ItemsMargin
|
||||
{
|
||||
get => _itemsMargin;
|
||||
set
|
||||
{
|
||||
if (_itemsMargin != value)
|
||||
{
|
||||
_itemsMargin = value;
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height for the items.
|
||||
/// </summary>
|
||||
public float ItemsHeight => Height - 2 * DefaultMarginV;
|
||||
public float ItemsHeight => Height - _itemsMargin.Height;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ToolStrip"/> class.
|
||||
@@ -82,6 +90,7 @@ namespace FlaxEditor.GUI
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
Offsets = new Margin(0, 0, y, height * Editor.Instance.Options.Options.Interface.IconsScale);
|
||||
_itemsMargin = new Margin(2, 2, 1, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -161,7 +170,7 @@ namespace FlaxEditor.GUI
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
// Arrange controls
|
||||
float x = DefaultMarginH;
|
||||
float x = _itemsMargin.Left;
|
||||
float h = ItemsHeight;
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
@@ -169,8 +178,8 @@ namespace FlaxEditor.GUI
|
||||
if (c.Visible)
|
||||
{
|
||||
var w = c.Width;
|
||||
c.Bounds = new Rectangle(x, DefaultMarginV, w, h);
|
||||
x += w + DefaultMarginH;
|
||||
c.Bounds = new Rectangle(x, _itemsMargin.Top, w, h);
|
||||
x += w + _itemsMargin.Width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace FlaxEditor.GUI.Tree
|
||||
private Margin _margin;
|
||||
private bool _autoSize = true;
|
||||
private bool _deferLayoutUpdate = false;
|
||||
private TreeNode _lastSelectedNode;
|
||||
|
||||
/// <summary>
|
||||
/// The TreeNode that is being dragged over. This could have a value when not dragging.
|
||||
@@ -67,7 +68,7 @@ namespace FlaxEditor.GUI.Tree
|
||||
/// Gets the first selected node or null.
|
||||
/// </summary>
|
||||
public TreeNode SelectedNode => Selection.Count > 0 ? Selection[0] : null;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Allow nodes to Draw the root tree line.
|
||||
/// </summary>
|
||||
@@ -364,6 +365,19 @@ namespace FlaxEditor.GUI.Tree
|
||||
BulkSelectUpdateExpanded(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes any pending layout perming action that has been delayed until next update to optimize performance of the complex tree hierarchy.
|
||||
/// </summary>
|
||||
public void FlushPendingPerformLayout()
|
||||
{
|
||||
if (_deferLayoutUpdate)
|
||||
{
|
||||
base.PerformLayout();
|
||||
AfterDeferredLayout?.Invoke();
|
||||
_deferLayoutUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PerformLayout(bool force = false)
|
||||
{
|
||||
@@ -378,25 +392,31 @@ namespace FlaxEditor.GUI.Tree
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (_deferLayoutUpdate)
|
||||
{
|
||||
base.PerformLayout();
|
||||
AfterDeferredLayout?.Invoke();
|
||||
_deferLayoutUpdate = false;
|
||||
}
|
||||
FlushPendingPerformLayout();
|
||||
var window = Root;
|
||||
bool shiftDown = window.GetKey(KeyboardKeys.Shift);
|
||||
bool keyUpArrow = window.GetKey(KeyboardKeys.ArrowUp);
|
||||
bool keyDownArrow = window.GetKey(KeyboardKeys.ArrowDown);
|
||||
|
||||
var node = SelectedNode;
|
||||
// Use last selection for last selected node if sift is down
|
||||
if (Selection.Count < 2)
|
||||
_lastSelectedNode = null;
|
||||
else if (shiftDown)
|
||||
_lastSelectedNode ??= Selection[^1];
|
||||
|
||||
// Skip root to prevent blocking input
|
||||
if (_lastSelectedNode != null && _lastSelectedNode.IsRoot)
|
||||
_lastSelectedNode = null;
|
||||
|
||||
var node = _lastSelectedNode ?? SelectedNode;
|
||||
|
||||
// Check if has focus and if any node is focused and it isn't a root
|
||||
if (ContainsFocus && node != null && node.AutoFocus)
|
||||
{
|
||||
var window = Root;
|
||||
if (window.GetKeyDown(KeyboardKeys.ArrowUp) || window.GetKeyDown(KeyboardKeys.ArrowDown))
|
||||
_keyUpdateTime = KeyUpdateTimeout;
|
||||
if (_keyUpdateTime >= KeyUpdateTimeout && window is WindowRootControl windowRoot && windowRoot.Window.IsFocused)
|
||||
{
|
||||
bool keyUpArrow = window.GetKey(KeyboardKeys.ArrowUp);
|
||||
bool keyDownArrow = window.GetKey(KeyboardKeys.ArrowDown);
|
||||
|
||||
// Check if arrow flags are different
|
||||
if (keyDownArrow != keyUpArrow)
|
||||
{
|
||||
@@ -406,24 +426,38 @@ namespace FlaxEditor.GUI.Tree
|
||||
Assert.AreNotEqual(-1, myIndex);
|
||||
|
||||
// Up
|
||||
TreeNode toSelect = null;
|
||||
List<TreeNode> toSelect = new List<TreeNode>();
|
||||
if (shiftDown && _supportMultiSelect)
|
||||
{
|
||||
toSelect.AddRange(Selection);
|
||||
}
|
||||
if (keyUpArrow)
|
||||
{
|
||||
if (myIndex == 0)
|
||||
{
|
||||
// Select parent
|
||||
toSelect = parentNode;
|
||||
if (toSelect.Contains(parentNode))
|
||||
toSelect.Remove(node);
|
||||
else if (parentNode != null)
|
||||
toSelect.Add(parentNode);
|
||||
_lastSelectedNode = parentNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select previous parent child
|
||||
toSelect = nodeParent.GetChild(myIndex - 1) as TreeNode;
|
||||
var select = nodeParent.GetChild(myIndex - 1) as TreeNode;
|
||||
|
||||
// Select last child if is valid and expanded and has any children
|
||||
if (toSelect != null && toSelect.IsExpanded && toSelect.HasAnyVisibleChild)
|
||||
if (select != null && select.IsExpanded && select.HasAnyVisibleChild)
|
||||
{
|
||||
toSelect = toSelect.GetChild(toSelect.ChildrenCount - 1) as TreeNode;
|
||||
select = select.GetChild(select.ChildrenCount - 1) as TreeNode;
|
||||
}
|
||||
|
||||
if (select == null || toSelect.Contains(select))
|
||||
toSelect.Remove(node);
|
||||
else
|
||||
toSelect.Add(select);
|
||||
_lastSelectedNode = select;
|
||||
}
|
||||
}
|
||||
// Down
|
||||
@@ -432,32 +466,48 @@ namespace FlaxEditor.GUI.Tree
|
||||
if (node.IsExpanded && node.HasAnyVisibleChild)
|
||||
{
|
||||
// Select the first child
|
||||
toSelect = node.GetChild(0) as TreeNode;
|
||||
var select = node.GetChild(0) as TreeNode;
|
||||
if (select == null || toSelect.Contains(select))
|
||||
toSelect.Remove(node);
|
||||
else
|
||||
toSelect.Add(select);
|
||||
_lastSelectedNode = select;
|
||||
}
|
||||
else if (myIndex == nodeParent.ChildrenCount - 1)
|
||||
{
|
||||
// Select next node after parent
|
||||
while (parentNode != null && toSelect == null)
|
||||
TreeNode select = null;
|
||||
while (parentNode != null && select == null)
|
||||
{
|
||||
int parentIndex = parentNode.IndexInParent;
|
||||
if (parentIndex != -1 && parentIndex < parentNode.Parent.ChildrenCount - 1)
|
||||
{
|
||||
toSelect = parentNode.Parent.GetChild(parentIndex + 1) as TreeNode;
|
||||
select = parentNode.Parent.GetChild(parentIndex + 1) as TreeNode;
|
||||
}
|
||||
parentNode = parentNode.Parent as TreeNode;
|
||||
}
|
||||
if (select == null || toSelect.Contains(select))
|
||||
toSelect.Remove(node);
|
||||
else
|
||||
toSelect.Add(select);
|
||||
_lastSelectedNode = select;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select next parent child
|
||||
toSelect = nodeParent.GetChild(myIndex + 1) as TreeNode;
|
||||
var select = nodeParent.GetChild(myIndex + 1) as TreeNode;
|
||||
if (select == null || toSelect.Contains(select))
|
||||
toSelect.Remove(node);
|
||||
else
|
||||
toSelect.Add(select);
|
||||
_lastSelectedNode = select;
|
||||
}
|
||||
}
|
||||
if (toSelect != null && toSelect.AutoFocus)
|
||||
if (toSelect.Count > 0)
|
||||
{
|
||||
// Select
|
||||
Select(toSelect);
|
||||
toSelect.Focus();
|
||||
_lastSelectedNode?.Focus();
|
||||
}
|
||||
|
||||
// Reset time
|
||||
|
||||
342
Source/Editor/GUI/WindowDecorations.cs
Normal file
342
Source/Editor/GUI/WindowDecorations.cs
Normal file
@@ -0,0 +1,342 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.GUI.Docking;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the title bar of the window with buttons.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public class WindowDecorations : ContainerControl
|
||||
{
|
||||
private Image _icon;
|
||||
private Label _title;
|
||||
private Button _closeButton;
|
||||
private Button _minimizeButton;
|
||||
private Button _maximizeButton;
|
||||
private LocalizedString _charChromeRestore, _charChromeMaximize;
|
||||
private Window _window;
|
||||
|
||||
/// <summary>
|
||||
/// The title label in the title bar.
|
||||
/// </summary>
|
||||
public Label Title => _title;
|
||||
|
||||
/// <summary>
|
||||
/// The icon used in the title bar.
|
||||
/// </summary>
|
||||
public Image Icon => _icon;
|
||||
|
||||
/// <summary>
|
||||
/// The tooltip shown when hovering over the icon.
|
||||
/// </summary>
|
||||
public string IconTooltipText
|
||||
{
|
||||
get => _icon?.TooltipText ?? null;
|
||||
set
|
||||
{
|
||||
if (_icon != null)
|
||||
_icon.TooltipText = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WindowDecorations"/> class.
|
||||
/// </summary>
|
||||
/// <param name="window">The window.</param>
|
||||
/// <param name="iconOnly">When set, omit drawing title and buttons.</param>
|
||||
public WindowDecorations(RootControl window, bool iconOnly = false)
|
||||
: base(0, 0, 0, 20)
|
||||
{
|
||||
_window = window.RootWindow.Window;
|
||||
|
||||
AutoFocus = false;
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
BackgroundColor = Color.Transparent;
|
||||
|
||||
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
|
||||
|
||||
_icon = new Image
|
||||
{
|
||||
Margin = new Margin(4, 4, 4, 4),
|
||||
Brush = new TextureBrush(windowIcon),
|
||||
Color = Style.Current.Foreground,
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
KeepAspectRatio = false,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
if (!iconOnly)
|
||||
{
|
||||
_icon.Margin = new Margin(6, 6, 6, 6);
|
||||
Height = 28;
|
||||
|
||||
_window.HitTest += OnHitTest;
|
||||
_window.Closed += OnWindowClosed;
|
||||
|
||||
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
|
||||
Font iconFont = windowIconsFont?.CreateFont(9);
|
||||
|
||||
_title = new Label(0, 0, Width, Height)
|
||||
{
|
||||
Text = _window.Title,
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = TextAlignment.Center,
|
||||
ClipText = true,
|
||||
TextColor = Style.Current.ForegroundGrey,
|
||||
TextColorHighlighted = Style.Current.ForegroundGrey,
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_closeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Color.Red,
|
||||
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
|
||||
|
||||
_minimizeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_minimizeButton.Clicked += () => _window.Minimize();
|
||||
|
||||
_maximizeButton = new Button
|
||||
{
|
||||
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_maximizeButton.Clicked += () =>
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
};
|
||||
|
||||
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
|
||||
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
var maximizeText = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
|
||||
if (_maximizeButton.Text != maximizeText)
|
||||
_maximizeButton.Text = maximizeText;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowClosed()
|
||||
{
|
||||
if (_window != null)
|
||||
{
|
||||
_window.HitTest -= OnHitTest;
|
||||
_window = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform hit test on the window.
|
||||
/// </summary>
|
||||
/// <param name="mouse">The mouse position</param>
|
||||
/// <returns>The hit code for given position.</returns>
|
||||
protected virtual WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
if (_window.IsMinimized)
|
||||
return WindowHitCodes.NoWhere;
|
||||
|
||||
var dpiScale = _window.DpiScale;
|
||||
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
|
||||
if (!_window.IsMaximized)
|
||||
{
|
||||
var winSize = _window.Size;
|
||||
|
||||
// Distance from which the mouse is considered to be on the border/corner
|
||||
float distance = 5.0f * dpiScale;
|
||||
|
||||
if (pos.Y > winSize.Y - distance && pos.X < distance)
|
||||
return WindowHitCodes.BottomLeft;
|
||||
|
||||
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.BottomRight;
|
||||
|
||||
if (pos.Y < distance && pos.X < distance)
|
||||
return WindowHitCodes.TopLeft;
|
||||
|
||||
if (pos.Y < distance && pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.TopRight;
|
||||
|
||||
if (pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.Right;
|
||||
|
||||
if (pos.X < distance)
|
||||
return WindowHitCodes.Left;
|
||||
|
||||
if (pos.Y < distance)
|
||||
return WindowHitCodes.Top;
|
||||
|
||||
if (pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.Bottom;
|
||||
}
|
||||
|
||||
var controlUnderMouse = GetChildAt(pos, control => control != _title);
|
||||
if (_title.Bounds.Contains(pos) && controlUnderMouse == null)
|
||||
return WindowHitCodes.Caption;
|
||||
|
||||
return WindowHitCodes.Client;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
// These may not work with main window due to SDL not passing mouse events
|
||||
// when interacting with hit tests on caption area...
|
||||
|
||||
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
return true;
|
||||
}
|
||||
else if (Icon.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
_window.Close(ClosingReason.User);
|
||||
return true;
|
||||
}
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutAfterChildren()
|
||||
{
|
||||
// Calculate extents for title bounds area excluding the icon and main menu area
|
||||
float x = 0;
|
||||
|
||||
// Icon
|
||||
if (_icon != null)
|
||||
{
|
||||
_icon.X = x;
|
||||
_icon.Size = new Float2(Height);
|
||||
x += _icon.Width;
|
||||
}
|
||||
|
||||
// Main menu if present
|
||||
if (Parent.GetChild<MainMenu>() is MainMenu mainMenu)
|
||||
{
|
||||
for (int i = 0; i < mainMenu.Children.Count; i++)
|
||||
{
|
||||
var c = mainMenu.Children[i];
|
||||
if (c is MainMenuButton b && c.Visible)
|
||||
{
|
||||
b.Bounds = new Rectangle(x, 0, b.Width, Height);
|
||||
x += b.Width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons
|
||||
float rightMostButtonX = Width;
|
||||
if (_closeButton != null)
|
||||
{
|
||||
_closeButton.Height = Height;
|
||||
_closeButton.X = rightMostButtonX - _closeButton.Width;
|
||||
rightMostButtonX = _closeButton.X;
|
||||
}
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
_maximizeButton.Height = Height;
|
||||
_maximizeButton.X = rightMostButtonX - _maximizeButton.Width;
|
||||
rightMostButtonX = _maximizeButton.X;
|
||||
}
|
||||
if (_minimizeButton != null)
|
||||
{
|
||||
_minimizeButton.Height = Height;
|
||||
_minimizeButton.X = rightMostButtonX - _minimizeButton.Width;
|
||||
rightMostButtonX = _minimizeButton.X;
|
||||
}
|
||||
|
||||
// Title
|
||||
if (_title != null)
|
||||
{
|
||||
_title.Text = _window.Title;
|
||||
_title.Bounds = new Rectangle(x, 0, rightMostButtonX - x, Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
DrawBorders();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw borders around the window.
|
||||
/// </summary>
|
||||
public virtual void DrawBorders()
|
||||
{
|
||||
var win = RootWindow.Window;
|
||||
if (win.IsMaximized)
|
||||
return;
|
||||
|
||||
if (Editor.Instance.UI.StatusBar == null)
|
||||
return;
|
||||
|
||||
const float thickness = 1.0f;
|
||||
Color color = Editor.Instance.UI.StatusBar.StatusColor;
|
||||
Rectangle rect = new Rectangle(thickness * 0.5f, thickness * 0.5f, Parent.Width - thickness, Parent.Height - thickness);
|
||||
Render2D.DrawRectangle(rect, color);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_window != null)
|
||||
{
|
||||
_window.Closed -= OnWindowClosed;
|
||||
OnWindowClosed();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEngine;
|
||||
using System;
|
||||
|
||||
namespace FlaxEditor.Gizmo
|
||||
{
|
||||
@@ -21,12 +19,16 @@ namespace FlaxEditor.Gizmo
|
||||
private MaterialInstance _materialAxisY;
|
||||
private MaterialInstance _materialAxisZ;
|
||||
private MaterialInstance _materialAxisFocus;
|
||||
private MaterialInstance _materialAxisLocked;
|
||||
private MaterialBase _materialSphere;
|
||||
|
||||
// Material Parameter Names
|
||||
const String _brightnessParamName = "Brightness";
|
||||
const String _opacityParamName = "Opacity";
|
||||
private const string _brightnessParamName = "Brightness";
|
||||
private const string _opacityParamName = "Opacity";
|
||||
|
||||
/// <summary>
|
||||
/// Used for example when the selection can't be moved because one actor is static.
|
||||
/// </summary>
|
||||
private bool _isDisabled;
|
||||
|
||||
private void InitDrawing()
|
||||
{
|
||||
@@ -42,7 +44,6 @@ namespace FlaxEditor.Gizmo
|
||||
_materialAxisY = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisY");
|
||||
_materialAxisZ = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisZ");
|
||||
_materialAxisFocus = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisFocus");
|
||||
_materialAxisLocked = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisLocked");
|
||||
_materialSphere = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialSphere");
|
||||
|
||||
// Ensure that every asset was loaded
|
||||
@@ -67,17 +68,42 @@ namespace FlaxEditor.Gizmo
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
float brightness = options.Visual.TransformGizmoBrightness;
|
||||
_materialAxisX.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisY.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisZ.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisLocked.SetParameterValue(_brightnessParamName, brightness);
|
||||
|
||||
UpdateGizmoBrightness(options);
|
||||
|
||||
float opacity = options.Visual.TransformGizmoOpacity;
|
||||
_materialAxisX.SetParameterValue(_opacityParamName, opacity);
|
||||
_materialAxisY.SetParameterValue(_opacityParamName, opacity);
|
||||
_materialAxisZ.SetParameterValue(_opacityParamName, opacity);
|
||||
_materialAxisLocked.SetParameterValue(_opacityParamName, opacity);
|
||||
}
|
||||
|
||||
private void UpdateGizmoBrightness(EditorOptions options)
|
||||
{
|
||||
_isDisabled = ShouldGizmoBeLocked();
|
||||
|
||||
float brightness = _isDisabled ? options.Visual.TransformGizmoBrightnessDisabled : options.Visual.TransformGizmoBrightness;
|
||||
if (Mathf.NearEqual(brightness, (float)_materialAxisX.GetParameterValue(_brightnessParamName)))
|
||||
return;
|
||||
_materialAxisX.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisY.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisZ.SetParameterValue(_brightnessParamName, brightness);
|
||||
}
|
||||
|
||||
private bool ShouldGizmoBeLocked()
|
||||
{
|
||||
bool gizmoLocked = false;
|
||||
if (Editor.Instance.StateMachine.IsPlayMode && Owner is Viewport.EditorGizmoViewport)
|
||||
{
|
||||
// Block editing static scene objects in main view during play mode
|
||||
foreach (var obj in Editor.Instance.SceneEditing.Selection)
|
||||
{
|
||||
if (obj.CanTransform == false)
|
||||
{
|
||||
gizmoLocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return gizmoLocked;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -88,20 +114,8 @@ namespace FlaxEditor.Gizmo
|
||||
if (!_modelCube || !_modelCube.IsLoaded)
|
||||
return;
|
||||
|
||||
// Find out if any of the selected objects can not be moved
|
||||
bool gizmoLocked = false;
|
||||
if (Editor.Instance.StateMachine.IsPlayMode)
|
||||
{
|
||||
for (int i = 0; i < SelectionCount; i++)
|
||||
{
|
||||
var obj = GetSelectedObject(i);
|
||||
if (obj.CanTransform == false)
|
||||
{
|
||||
gizmoLocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update the gizmo brightness every frame to ensure it updates correctly
|
||||
UpdateGizmoBrightness(Editor.Instance.Options.Options);
|
||||
|
||||
// As all axisMesh have the same pivot, add a little offset to the x axisMesh, this way SortDrawCalls is able to sort the draw order
|
||||
// https://github.com/FlaxEngine/FlaxEngine/issues/680
|
||||
@@ -136,37 +150,37 @@ namespace FlaxEditor.Gizmo
|
||||
// X axis
|
||||
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance xAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
|
||||
MaterialInstance xAxisMaterialTransform = (isXAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisX;
|
||||
transAxisMesh.Draw(ref renderContext, xAxisMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Y axis
|
||||
Matrix.RotationX(Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance yAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
|
||||
MaterialInstance yAxisMaterialTransform = (isYAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisY;
|
||||
transAxisMesh.Draw(ref renderContext, yAxisMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Z axis
|
||||
Matrix.RotationX(Mathf.Pi, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance zAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
|
||||
MaterialInstance zAxisMaterialTransform = (isZAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisZ;
|
||||
transAxisMesh.Draw(ref renderContext, zAxisMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// XY plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance xyPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX);
|
||||
MaterialInstance xyPlaneMaterialTransform = (_activeAxis == Axis.XY && !_isDisabled) ? _materialAxisFocus : _materialAxisX;
|
||||
cubeMesh.Draw(ref renderContext, xyPlaneMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// ZX plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance zxPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisY);
|
||||
MaterialInstance zxPlaneMaterialTransform = (_activeAxis == Axis.ZX && !_isDisabled) ? _materialAxisFocus : _materialAxisY;
|
||||
cubeMesh.Draw(ref renderContext, zxPlaneMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// YZ plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance yzPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisZ);
|
||||
MaterialInstance yzPlaneMaterialTransform = (_activeAxis == Axis.YZ && !_isDisabled) ? _materialAxisFocus : _materialAxisZ;
|
||||
cubeMesh.Draw(ref renderContext, yzPlaneMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Center sphere
|
||||
@@ -186,17 +200,17 @@ namespace FlaxEditor.Gizmo
|
||||
// X axis
|
||||
Matrix.RotationZ(Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
|
||||
MaterialInstance xAxisMaterialRotate = (isXAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisX;
|
||||
rotationAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Y axis
|
||||
MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
|
||||
MaterialInstance yAxisMaterialRotate = (isYAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisY;
|
||||
rotationAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m1, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Z axis
|
||||
Matrix.RotationX(-Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
|
||||
MaterialInstance zAxisMaterialRotate = (isZAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisZ;
|
||||
rotationAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Center box
|
||||
@@ -216,37 +230,37 @@ namespace FlaxEditor.Gizmo
|
||||
// X axis
|
||||
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref mx1, out m3);
|
||||
MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
|
||||
MaterialInstance xAxisMaterialRotate = (isXAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisX;
|
||||
scaleAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Y axis
|
||||
Matrix.RotationX(Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
|
||||
MaterialInstance yAxisMaterialRotate = (isYAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisY;
|
||||
scaleAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Z axis
|
||||
Matrix.RotationX(Mathf.Pi, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
|
||||
MaterialInstance zAxisMaterialRotate = (isZAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisZ;
|
||||
scaleAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// XY plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance xyPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX);
|
||||
MaterialInstance xyPlaneMaterialScale = (_activeAxis == Axis.XY && !_isDisabled) ? _materialAxisFocus : _materialAxisX;
|
||||
cubeMesh.Draw(ref renderContext, xyPlaneMaterialScale, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// ZX plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance zxPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ);
|
||||
MaterialInstance zxPlaneMaterialScale = (_activeAxis == Axis.ZX && !_isDisabled) ? _materialAxisFocus : _materialAxisZ;
|
||||
cubeMesh.Draw(ref renderContext, zxPlaneMaterialScale, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// YZ plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance yzPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY);
|
||||
MaterialInstance yzPlaneMaterialScale = (_activeAxis == Axis.YZ && !_isDisabled) ? _materialAxisFocus : _materialAxisY;
|
||||
cubeMesh.Draw(ref renderContext, yzPlaneMaterialScale, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Center box
|
||||
|
||||
@@ -155,6 +155,16 @@ namespace FlaxEditor
|
||||
private List<Widget> _widgets;
|
||||
private Widget _activeWidget;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the view size.
|
||||
/// </summary>
|
||||
/// <param name="size">The new size.</param>
|
||||
public void SetViewSize(Float2 size)
|
||||
{
|
||||
_view.Size = size;
|
||||
_view.PerformLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if enable displaying UI editing background and grid elements.
|
||||
/// </summary>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MException.h"
|
||||
#include "Engine/Scripting/Internal/MainThreadManagedInvokeAction.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Content/Assets/VisualScript.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/CSG/CSGBuilder.h"
|
||||
@@ -622,6 +623,14 @@ void ManagedEditor::WipeOutLeftoverSceneObjects()
|
||||
ObjectsRemovalService::Flush();
|
||||
}
|
||||
|
||||
Array<Window*> ManagedEditor::GetWindows()
|
||||
{
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
auto result = WindowsManager::Windows;
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void ManagedEditor::OnEditorAssemblyLoaded(MAssembly* assembly)
|
||||
{
|
||||
ASSERT(!HasManagedInstance());
|
||||
|
||||
@@ -259,6 +259,7 @@ public:
|
||||
API_FUNCTION(Internal) static Array<VisualScriptLocal> GetVisualScriptLocals();
|
||||
API_FUNCTION(Internal) static bool EvaluateVisualScriptLocal(VisualScript* script, API_PARAM(Ref) VisualScriptLocal& local);
|
||||
API_FUNCTION(Internal) static void WipeOutLeftoverSceneObjects();
|
||||
API_FUNCTION(Internal) static Array<Window*> GetWindows();
|
||||
|
||||
private:
|
||||
void OnEditorAssemblyLoaded(MAssembly* assembly);
|
||||
|
||||
@@ -1013,7 +1013,7 @@ namespace FlaxEditor.Modules
|
||||
ContentItem item;
|
||||
if (path.EndsWith(".cs"))
|
||||
item = new CSharpScriptItem(path);
|
||||
else if (path.EndsWith(".cpp") || path.EndsWith(".h"))
|
||||
else if (path.EndsWith(".cpp") || path.EndsWith(".h") || path.EndsWith(".c") || path.EndsWith(".hpp"))
|
||||
item = new CppScriptItem(path);
|
||||
else if (path.EndsWith(".shader") || path.EndsWith(".hlsl"))
|
||||
item = new ShaderSourceItem(path);
|
||||
|
||||
@@ -222,7 +222,7 @@ namespace FlaxEditor.Modules
|
||||
outputExtension = extension;
|
||||
|
||||
// Check if can place source files here
|
||||
if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h"))
|
||||
if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h" || extension == ".c" || extension == ".hpp"))
|
||||
{
|
||||
// Error
|
||||
Editor.LogWarning(string.Format("Cannot import \'{0}\' to \'{1}\'. The target directory cannot have scripts.", inputPath, targetLocation.Node.Path));
|
||||
|
||||
@@ -254,18 +254,29 @@ namespace FlaxEditor.Modules
|
||||
PrefabApplying?.Invoke(prefab, instance);
|
||||
|
||||
// When applying changes to prefab from actor in level ignore it's root transformation (see ActorEditor.ProcessDiff)
|
||||
Actor prefabRoot = null;
|
||||
var originalTransform = instance.LocalTransform;
|
||||
var originalName = instance.Name;
|
||||
if (instance.IsPrefabRoot && instance.HasScene)
|
||||
if (instance.HasScene)
|
||||
{
|
||||
instance.LocalTransform = prefab.GetDefaultInstance().Transform;
|
||||
instance.Name = prefab.GetDefaultInstance().Name;
|
||||
prefabRoot = instance.GetPrefabRoot();
|
||||
if (prefabRoot != null && prefabRoot.IsPrefabRoot && instance.HasScene)
|
||||
{
|
||||
var defaultInstance = prefab.GetDefaultInstance();
|
||||
originalTransform = prefabRoot.LocalTransform;
|
||||
originalName = prefabRoot.Name;
|
||||
prefabRoot.LocalTransform = defaultInstance.Transform;
|
||||
prefabRoot.Name = defaultInstance.Name;
|
||||
}
|
||||
}
|
||||
|
||||
// Call backend
|
||||
var failed = PrefabManager.Internal_ApplyAll(FlaxEngine.Object.GetUnmanagedPtr(instance));
|
||||
instance.LocalTransform = originalTransform;
|
||||
instance.Name = originalName;
|
||||
if (prefabRoot != null)
|
||||
{
|
||||
prefabRoot.LocalTransform = originalTransform;
|
||||
prefabRoot.Name = originalName;
|
||||
}
|
||||
if (failed)
|
||||
throw new Exception("Failed to apply the prefab. See log to learn more.");
|
||||
|
||||
|
||||
@@ -673,6 +673,7 @@ namespace FlaxEditor.Modules
|
||||
pasteAction.Do(out _, out var nodeParents);
|
||||
|
||||
// Select spawned objects (parents only)
|
||||
newSelection.Clear();
|
||||
newSelection.AddRange(nodeParents);
|
||||
var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo);
|
||||
selectAction.Do();
|
||||
|
||||
@@ -568,6 +568,10 @@ namespace FlaxEditor.Modules
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if already added
|
||||
if (SceneGraphFactory.Nodes.ContainsKey(actor.ID))
|
||||
return;
|
||||
|
||||
var node = SceneGraphFactory.BuildActorNode(actor);
|
||||
if (node != null)
|
||||
{
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace FlaxEditor.Modules
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnPlayBegin()
|
||||
public override void OnPlayBeginning()
|
||||
{
|
||||
Editor.Windows.FlashMainWindow();
|
||||
|
||||
|
||||
@@ -54,6 +54,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
case CodeEditorTypes.VS2022:
|
||||
Name = "Visual Studio 2022";
|
||||
break;
|
||||
case CodeEditorTypes.VS2026:
|
||||
Name = "Visual Studio 2026";
|
||||
break;
|
||||
case CodeEditorTypes.VSCode:
|
||||
Name = "Visual Studio Code";
|
||||
break;
|
||||
@@ -110,6 +113,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
case CodeEditorTypes.VS2017:
|
||||
case CodeEditorTypes.VS2019:
|
||||
case CodeEditorTypes.VS2022:
|
||||
case CodeEditorTypes.VS2026:
|
||||
// TODO: finish dynamic files adding to the project
|
||||
//Editor.Instance.ProgressReporting.GenerateScriptsProjectFiles.RunAsync();
|
||||
break;
|
||||
|
||||
@@ -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;
|
||||
@@ -37,6 +70,53 @@ namespace FlaxEditor.Modules
|
||||
private bool _progressFailed;
|
||||
|
||||
ContextMenuSingleSelectGroup<int> _numberOfClientsGroup = new ContextMenuSingleSelectGroup<int>();
|
||||
|
||||
/// <summary>
|
||||
/// Defines a viewport scaling option.
|
||||
/// </summary>
|
||||
public class ViewportScaleOption
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the viewport scale type.
|
||||
/// </summary>
|
||||
public enum ViewportScaleType
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolution.
|
||||
/// </summary>
|
||||
Resolution = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Aspect Ratio.
|
||||
/// </summary>
|
||||
Aspect = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name.
|
||||
/// </summary>
|
||||
public string Label;
|
||||
|
||||
/// <summary>
|
||||
/// The Type of scaling to do.
|
||||
/// </summary>
|
||||
public ViewportScaleType ScaleType;
|
||||
|
||||
/// <summary>
|
||||
/// The width and height to scale by.
|
||||
/// </summary>
|
||||
public Int2 Size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default viewport scaling options.
|
||||
/// </summary>
|
||||
public List<ViewportScaleOption> DefaultViewportScaleOptions = new List<ViewportScaleOption>();
|
||||
|
||||
/// <summary>
|
||||
/// The user defined viewport scaling options.
|
||||
/// </summary>
|
||||
public List<ViewportScaleOption> CustomViewportScaleOptions = new List<ViewportScaleOption>();
|
||||
|
||||
private ContextMenuButton _menuFileSaveScenes;
|
||||
private ContextMenuButton _menuFileReloadScenes;
|
||||
@@ -97,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>
|
||||
@@ -371,25 +456,70 @@ namespace FlaxEditor.Modules
|
||||
|
||||
// Update window background
|
||||
mainWindow.BackgroundColor = Style.Current.Background;
|
||||
|
||||
InitViewportScaleOptions();
|
||||
|
||||
InitSharedMenus();
|
||||
InitMainMenu(mainWindow);
|
||||
InitToolstrip(mainWindow);
|
||||
InitStatusBar(mainWindow);
|
||||
InitDockPanel(mainWindow);
|
||||
InitWindowDecorations(mainWindow);
|
||||
|
||||
Editor.Options.OptionsChanged += OnOptionsChanged;
|
||||
|
||||
mainWindow.PerformLayout(true);
|
||||
}
|
||||
|
||||
// Add dummy control for drawing the main window borders if using a custom style
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Editor.Options.Options.Interface.UseNativeWindowSystem)
|
||||
#endif
|
||||
private void InitViewportScaleOptions()
|
||||
{
|
||||
if (DefaultViewportScaleOptions.Count == 0)
|
||||
{
|
||||
mainWindow.AddChild(new CustomWindowBorderControl
|
||||
DefaultViewportScaleOptions.Add(new ViewportScaleOption
|
||||
{
|
||||
Size = Float2.Zero,
|
||||
Label = "Free Aspect",
|
||||
ScaleType = ViewportScaleOption.ViewportScaleType.Aspect,
|
||||
Size = new Int2(1, 1),
|
||||
});
|
||||
DefaultViewportScaleOptions.Add(new ViewportScaleOption
|
||||
{
|
||||
Label = "16:9 Aspect",
|
||||
ScaleType = ViewportScaleOption.ViewportScaleType.Aspect,
|
||||
Size = new Int2(16, 9),
|
||||
});
|
||||
DefaultViewportScaleOptions.Add(new ViewportScaleOption
|
||||
{
|
||||
Label = "16:10 Aspect",
|
||||
ScaleType = ViewportScaleOption.ViewportScaleType.Aspect,
|
||||
Size = new Int2(16, 10),
|
||||
});
|
||||
DefaultViewportScaleOptions.Add(new ViewportScaleOption
|
||||
{
|
||||
Label = "1920x1080 Resolution (Full HD)",
|
||||
ScaleType = ViewportScaleOption.ViewportScaleType.Resolution,
|
||||
Size = new Int2(1920, 1080),
|
||||
});
|
||||
DefaultViewportScaleOptions.Add(new ViewportScaleOption
|
||||
{
|
||||
Label = "2560x1440 Resolution (2K)",
|
||||
ScaleType = ViewportScaleOption.ViewportScaleType.Resolution,
|
||||
Size = new Int2(2560, 1440),
|
||||
});
|
||||
}
|
||||
|
||||
if (Editor.Instance.ProjectCache.TryGetCustomData("CustomViewportScalingOptions", out string data))
|
||||
{
|
||||
CustomViewportScaleOptions = JsonSerializer.Deserialize<List<ViewportScaleOption>>(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the custom viewport scaling options.
|
||||
/// </summary>
|
||||
public void SaveCustomViewportScalingOptions()
|
||||
{
|
||||
var customOptions = JsonSerializer.Serialize(CustomViewportScaleOptions);
|
||||
Editor.Instance.ProjectCache.SetCustomData("CustomViewportScalingOptions", customOptions);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -410,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()
|
||||
{
|
||||
@@ -458,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);
|
||||
@@ -518,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;
|
||||
|
||||
@@ -538,6 +646,7 @@ namespace FlaxEditor.Modules
|
||||
_menuFileGenerateScriptsProjectFiles = cm.AddButton("Generate scripts project files", inputOptions.GenerateScriptsProject, Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync);
|
||||
_menuFileRecompileScripts = cm.AddButton("Recompile scripts", inputOptions.RecompileScripts, ScriptsBuilder.Compile);
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("New project", NewProject);
|
||||
cm.AddButton("Open project...", OpenProject);
|
||||
cm.AddButton("Reload project", ReloadProject);
|
||||
cm.AddButton("Open project folder", () => FileSystem.ShowFileExplorer(Editor.Instance.GameProject.ProjectFolderPath));
|
||||
@@ -568,7 +677,7 @@ namespace FlaxEditor.Modules
|
||||
if (item != null)
|
||||
Editor.ContentEditing.Open(item);
|
||||
});
|
||||
cm.AddButton("Editor Options", () => Editor.Windows.EditorOptionsWin.Show());
|
||||
cm.AddButton("Editor Options", inputOptions.EditorOptionsWindow, () => Editor.Windows.EditorOptionsWin.Show());
|
||||
|
||||
// Scene
|
||||
MenuScene = MainMenu.AddButton("Scene");
|
||||
@@ -661,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;
|
||||
@@ -829,6 +952,17 @@ namespace FlaxEditor.Modules
|
||||
MasterPanel.Offsets = new Margin(0, 0, ToolStrip.Bottom, StatusBar.Height);
|
||||
}
|
||||
|
||||
private void NewProject()
|
||||
{
|
||||
// Ask user to create project file
|
||||
if (FileSystem.ShowSaveFileDialog(Editor.Windows.MainWindow, null, "Project files (*.flaxproj)\0*.flaxproj\0All files (*.*)\0*.*\0", false, "Create project file", out var files))
|
||||
return;
|
||||
if (files != null && files.Length > 0)
|
||||
{
|
||||
Editor.NewProject(files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenProject()
|
||||
{
|
||||
// Ask user to select project file
|
||||
@@ -1070,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;
|
||||
@@ -1085,5 +1220,267 @@ namespace FlaxEditor.Modules
|
||||
MenuTools = null;
|
||||
MenuHelp = null;
|
||||
}
|
||||
|
||||
internal void CreateViewportSizingContextMenu(ContextMenu vsMenu, int defaultScaleActiveIndex, int customScaleActiveIndex, bool prefabViewport, Action<ViewportScaleOption> changeView, Action<int, int> changeActiveIndices)
|
||||
{
|
||||
// Add default viewport sizing options
|
||||
var defaultOptions = DefaultViewportScaleOptions;
|
||||
for (int i = 0; i < defaultOptions.Count; i++)
|
||||
{
|
||||
var viewportScale = defaultOptions[i];
|
||||
if (prefabViewport && viewportScale.ScaleType == ViewportScaleOption.ViewportScaleType.Aspect)
|
||||
continue; // Skip aspect ratio types in prefab
|
||||
var button = vsMenu.AddButton(viewportScale.Label);
|
||||
button.CloseMenuOnClick = false;
|
||||
button.Tag = viewportScale;
|
||||
|
||||
// No default index is active
|
||||
if (defaultScaleActiveIndex == -1)
|
||||
{
|
||||
button.Icon = SpriteHandle.Invalid;
|
||||
}
|
||||
// This is the active index
|
||||
else if (defaultScaleActiveIndex == i)
|
||||
{
|
||||
button.Icon = Style.Current.CheckBoxTick;
|
||||
changeView(viewportScale);
|
||||
}
|
||||
|
||||
button.Clicked += () =>
|
||||
{
|
||||
if (button.Tag == null)
|
||||
return;
|
||||
|
||||
// Reset selected icon on all buttons
|
||||
foreach (var child in vsMenu.Items)
|
||||
{
|
||||
if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v)
|
||||
{
|
||||
if (cmb == button)
|
||||
{
|
||||
button.Icon = Style.Current.CheckBoxTick;
|
||||
var index = defaultOptions.FindIndex(x => x == v);
|
||||
changeActiveIndices(index, -1); // Reset custom index because default was chosen
|
||||
changeView(v);
|
||||
}
|
||||
else if (cmb.Icon != SpriteHandle.Invalid)
|
||||
{
|
||||
cmb.Icon = SpriteHandle.Invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if (defaultOptions.Count != 0)
|
||||
vsMenu.AddSeparator();
|
||||
|
||||
// Add custom viewport options
|
||||
var customOptions = CustomViewportScaleOptions;
|
||||
for (int i = 0; i < customOptions.Count; i++)
|
||||
{
|
||||
var viewportScale = customOptions[i];
|
||||
if (prefabViewport && viewportScale.ScaleType == ViewportScaleOption.ViewportScaleType.Aspect)
|
||||
continue; // Skip aspect ratio types in prefab
|
||||
var childCM = vsMenu.AddChildMenu(viewportScale.Label);
|
||||
childCM.CloseMenuOnClick = false;
|
||||
childCM.Tag = viewportScale;
|
||||
|
||||
// No custom index is active
|
||||
if (customScaleActiveIndex == -1)
|
||||
{
|
||||
childCM.Icon = SpriteHandle.Invalid;
|
||||
}
|
||||
// This is the active index
|
||||
else if (customScaleActiveIndex == i)
|
||||
{
|
||||
childCM.Icon = Style.Current.CheckBoxTick;
|
||||
changeView(viewportScale);
|
||||
}
|
||||
|
||||
var applyButton = childCM.ContextMenu.AddButton("Apply");
|
||||
applyButton.Tag = childCM.Tag = viewportScale;
|
||||
applyButton.CloseMenuOnClick = false;
|
||||
applyButton.Clicked += () =>
|
||||
{
|
||||
if (childCM.Tag == null)
|
||||
return;
|
||||
|
||||
// Reset selected icon on all buttons
|
||||
foreach (var child in vsMenu.Items)
|
||||
{
|
||||
if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v)
|
||||
{
|
||||
if (child == childCM)
|
||||
{
|
||||
childCM.Icon = Style.Current.CheckBoxTick;
|
||||
var index = customOptions.FindIndex(x => x == v);
|
||||
changeActiveIndices(-1, index); // Reset default index because custom was chosen
|
||||
changeView(v);
|
||||
}
|
||||
else if (cmb.Icon != SpriteHandle.Invalid)
|
||||
{
|
||||
cmb.Icon = SpriteHandle.Invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var deleteButton = childCM.ContextMenu.AddButton("Delete");
|
||||
deleteButton.CloseMenuOnClick = false;
|
||||
deleteButton.Clicked += () =>
|
||||
{
|
||||
if (childCM.Tag == null)
|
||||
return;
|
||||
|
||||
var v = (ViewportScaleOption)childCM.Tag;
|
||||
if (childCM.Icon != SpriteHandle.Invalid)
|
||||
{
|
||||
changeActiveIndices(-1, 0);
|
||||
changeView(defaultOptions[0]);
|
||||
}
|
||||
customOptions.Remove(v);
|
||||
SaveCustomViewportScalingOptions();
|
||||
vsMenu.DisposeAllItems();
|
||||
CreateViewportSizingContextMenu(vsMenu, defaultScaleActiveIndex, customScaleActiveIndex, prefabViewport, changeView, changeActiveIndices);
|
||||
vsMenu.PerformLayout();
|
||||
};
|
||||
}
|
||||
if (customOptions.Count != 0)
|
||||
vsMenu.AddSeparator();
|
||||
|
||||
// Add button
|
||||
var add = vsMenu.AddButton("Add...");
|
||||
add.CloseMenuOnClick = false;
|
||||
add.Clicked += () =>
|
||||
{
|
||||
var popup = new ContextMenuBase
|
||||
{
|
||||
Size = new Float2(230, 125),
|
||||
ClipChildren = false,
|
||||
CullChildren = false,
|
||||
};
|
||||
popup.Show(add, new Float2(add.Width, 0));
|
||||
|
||||
var nameLabel = new Label
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Name",
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
};
|
||||
nameLabel.LocalX += 10;
|
||||
nameLabel.LocalY += 10;
|
||||
|
||||
var nameTextBox = new TextBox
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
IsMultiline = false,
|
||||
};
|
||||
nameTextBox.LocalX += 100;
|
||||
nameTextBox.LocalY += 10;
|
||||
|
||||
var typeLabel = new Label
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Type",
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
};
|
||||
typeLabel.LocalX += 10;
|
||||
typeLabel.LocalY += 35;
|
||||
|
||||
var typeDropdown = new Dropdown
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Items = { "Aspect", "Resolution" },
|
||||
SelectedItem = "Aspect",
|
||||
Visible = !prefabViewport,
|
||||
Width = nameTextBox.Width
|
||||
};
|
||||
typeDropdown.LocalY += 35;
|
||||
typeDropdown.LocalX += 100;
|
||||
|
||||
var whLabel = new Label
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Width & Height",
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
};
|
||||
whLabel.LocalX += 10;
|
||||
whLabel.LocalY += 60;
|
||||
|
||||
var wValue = new IntValueBox(16)
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
MinValue = 1,
|
||||
Width = 55,
|
||||
};
|
||||
wValue.LocalY += 60;
|
||||
wValue.LocalX += 100;
|
||||
|
||||
var hValue = new IntValueBox(9)
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
MinValue = 1,
|
||||
Width = 55,
|
||||
};
|
||||
hValue.LocalY += 60;
|
||||
hValue.LocalX += 165;
|
||||
|
||||
var submitButton = new Button
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Submit",
|
||||
Width = 70,
|
||||
};
|
||||
submitButton.LocalX += 40;
|
||||
submitButton.LocalY += 90;
|
||||
submitButton.Clicked += () =>
|
||||
{
|
||||
Enum.TryParse(typeDropdown.SelectedItem, out ViewportScaleOption.ViewportScaleType type);
|
||||
if (prefabViewport)
|
||||
type = ViewportScaleOption.ViewportScaleType.Resolution;
|
||||
|
||||
var combineString = type == ViewportScaleOption.ViewportScaleType.Aspect ? ":" : "x";
|
||||
var name = nameTextBox.Text + " (" + wValue.Value + combineString + hValue.Value + ") " + typeDropdown.SelectedItem;
|
||||
var newViewportOption = new ViewportScaleOption
|
||||
{
|
||||
ScaleType = type,
|
||||
Label = name,
|
||||
Size = new Int2(wValue.Value, hValue.Value),
|
||||
};
|
||||
|
||||
customOptions.Add(newViewportOption);
|
||||
SaveCustomViewportScalingOptions();
|
||||
vsMenu.DisposeAllItems();
|
||||
CreateViewportSizingContextMenu(vsMenu, defaultScaleActiveIndex, customScaleActiveIndex, prefabViewport, changeView, changeActiveIndices);
|
||||
vsMenu.PerformLayout();
|
||||
};
|
||||
|
||||
var cancelButton = new Button
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Cancel",
|
||||
Width = 70,
|
||||
};
|
||||
cancelButton.LocalX += 120;
|
||||
cancelButton.LocalY += 90;
|
||||
cancelButton.Clicked += () =>
|
||||
{
|
||||
nameTextBox.Clear();
|
||||
typeDropdown.SelectedItem = "Aspect";
|
||||
hValue.Value = 9;
|
||||
wValue.Value = 16;
|
||||
popup.Hide();
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -650,45 +650,53 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Windows"), EditorOrder(4020)]
|
||||
public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Control+Comma")]
|
||||
[EditorDisplay("Windows"), EditorOrder(4030)]
|
||||
public InputBinding EditorOptionsWindow = new InputBinding(KeyboardKeys.Comma, KeyboardKeys.Control);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Node editors
|
||||
#region Node Editors
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+W")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4500)]
|
||||
[EditorDisplay("Node Editors"), EditorOrder(4500)]
|
||||
public InputBinding NodesAlignTop = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+A")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4510)]
|
||||
[EditorDisplay("Node Editors"), EditorOrder(4510)]
|
||||
public InputBinding NodesAlignLeft = new InputBinding(KeyboardKeys.A, KeyboardKeys.Shift);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+S")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4520)]
|
||||
[EditorDisplay("Node Editors"), EditorOrder(4520)]
|
||||
public InputBinding NodesAlignBottom = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Shift+D")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4530)]
|
||||
[EditorDisplay("Node Editors"), EditorOrder(4530)]
|
||||
public InputBinding NodesAlignRight = new InputBinding(KeyboardKeys.D, KeyboardKeys.Shift);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Alt+Shift+W")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4540)]
|
||||
[EditorDisplay("Node Editors"), EditorOrder(4540)]
|
||||
public InputBinding NodesAlignMiddle = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift, KeyboardKeys.Alt);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Alt+Shift+S")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4550)]
|
||||
[EditorDisplay("Node Editors"), EditorOrder(4550)]
|
||||
public InputBinding NodesAlignCenter = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift, KeyboardKeys.Alt);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Q")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4560)]
|
||||
[EditorDisplay("Node Editors"), EditorOrder(4560)]
|
||||
public InputBinding NodesAutoFormat = new InputBinding(KeyboardKeys.Q);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4570)]
|
||||
public InputBinding NodesDistributeHorizontal = new InputBinding(KeyboardKeys.None);
|
||||
[DefaultValue(typeof(InputBinding), "Shift+Q")]
|
||||
[EditorDisplay("Node Editors"), EditorOrder(4560)]
|
||||
public InputBinding NodesStraightenConnections = new InputBinding(KeyboardKeys.Q, KeyboardKeys.Shift);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Node editors"), EditorOrder(4580)]
|
||||
public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.None);
|
||||
[DefaultValue(typeof(InputBinding), "Alt+W")]
|
||||
[EditorDisplay("Node Editors"), EditorOrder(4570)]
|
||||
public InputBinding NodesDistributeHorizontal = new InputBinding(KeyboardKeys.W, KeyboardKeys.Alt);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Alt+A")]
|
||||
[EditorDisplay("Node Editors"), EditorOrder(4580)]
|
||||
public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.A, KeyboardKeys.Alt);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user