mirror of
https://github.com/kevinporetti/UnrealImGui.git
synced 2025-04-07 07:20:32 +00:00
Compare commits
150 Commits
Author | SHA1 | Date | |
---|---|---|---|
7df161dead | |||
03f5d09ab4 | |||
c8e465ce2d | |||
b58d17177e | |||
ba26c95c94 | |||
![]() |
b7f9cb9e5a | ||
![]() |
487cd08df9 | ||
![]() |
f662f8b504 | ||
![]() |
59645dbf9a | ||
![]() |
25846675cd | ||
![]() |
5dc3b1f222 | ||
![]() |
8f6180969e | ||
![]() |
097719426a | ||
![]() |
cff6ff6f66 | ||
![]() |
9496087f9c | ||
![]() |
cb89223375 | ||
![]() |
b346fa894d | ||
![]() |
91825ac883 | ||
![]() |
48b1abd1e1 | ||
![]() |
2b8f3e55c3 | ||
![]() |
14749e50bb | ||
![]() |
7b055e4040 | ||
![]() |
fa7134f553 | ||
![]() |
e96a11abcf | ||
![]() |
d0034be488 | ||
![]() |
ea84ccf468 | ||
![]() |
56987d244f | ||
![]() |
e00a133617 | ||
![]() |
9f41d0906e | ||
![]() |
39c3eced41 | ||
![]() |
47473c3eeb | ||
![]() |
ea74c3c872 | ||
![]() |
860b922206 | ||
![]() |
db77175e66 | ||
![]() |
16056820e9 | ||
![]() |
a76f4bc451 | ||
![]() |
9eed8f93b9 | ||
![]() |
bbc3101a58 | ||
![]() |
a18e2e8dfb | ||
![]() |
5b737e111e | ||
![]() |
c7d36a802b | ||
![]() |
5d5bd8a284 | ||
![]() |
b94bb7b5ef | ||
![]() |
d55af9db11 | ||
![]() |
d4ffe9443f | ||
![]() |
ac2b80bd63 | ||
![]() |
04e6f9d844 | ||
![]() |
bbd2c01071 | ||
![]() |
f8d8704a67 | ||
![]() |
48be56de64 | ||
![]() |
9d4eb74bf0 | ||
![]() |
8de65f3d34 | ||
![]() |
253eff0525 | ||
![]() |
26f8729604 | ||
![]() |
4c3905b348 | ||
![]() |
003eed34d7 | ||
![]() |
62a739ac9f | ||
![]() |
5a7c81b4e3 | ||
![]() |
174e38cfc6 | ||
![]() |
73cdfe8d83 | ||
![]() |
a35585f26a | ||
![]() |
91080c3988 | ||
![]() |
848e7294c8 | ||
![]() |
05d7c5c48b | ||
![]() |
9e5975c9bb | ||
![]() |
95d27334fe | ||
![]() |
71dd082f69 | ||
![]() |
5f1dc964cf | ||
![]() |
e651b0f88f | ||
![]() |
5e4b7084de | ||
![]() |
10ce6386d4 | ||
![]() |
979903722a | ||
![]() |
852a501022 | ||
![]() |
22c5b42387 | ||
![]() |
2d728f1f26 | ||
![]() |
505eb166d5 | ||
![]() |
59dc8decf8 | ||
![]() |
4668d5efad | ||
![]() |
15658fa917 | ||
![]() |
ca4001343e | ||
![]() |
4a9d66889b | ||
![]() |
dc516f052e | ||
![]() |
867a34e640 | ||
![]() |
2b5d871609 | ||
![]() |
c144658f37 | ||
![]() |
6c6e766a20 | ||
![]() |
6643958586 | ||
![]() |
eba312e20b | ||
![]() |
ffe532f6b3 | ||
![]() |
dcbf229263 | ||
![]() |
6eb66bf021 | ||
![]() |
09572a8ef9 | ||
![]() |
0bf940de81 | ||
![]() |
51031d7d86 | ||
![]() |
f4718e404a | ||
![]() |
0c69e6ddfc | ||
![]() |
c662869820 | ||
![]() |
e05cea2789 | ||
![]() |
b1d000f696 | ||
![]() |
62d26f4ee5 | ||
![]() |
a8ba10d074 | ||
![]() |
209a594616 | ||
![]() |
70db3c7b20 | ||
![]() |
e00c1ff9e3 | ||
![]() |
c5f3759664 | ||
![]() |
f67cabbe93 | ||
![]() |
fc6fd2e498 | ||
![]() |
20d1490bd3 | ||
![]() |
b492932055 | ||
![]() |
b4e959ccd3 | ||
![]() |
36cdd0c337 | ||
![]() |
6b97a7b724 | ||
![]() |
590f0819ab | ||
![]() |
588428507a | ||
![]() |
dd06fcbbdf | ||
![]() |
9c181463ce | ||
![]() |
d9220ad536 | ||
![]() |
74c794b53e | ||
![]() |
20a7675787 | ||
![]() |
9ab12850fb | ||
![]() |
a90f217ab3 | ||
![]() |
eabff7b839 | ||
![]() |
daa18fcf25 | ||
![]() |
5968c3ce84 | ||
![]() |
6ddc7f2805 | ||
![]() |
f57a73b91b | ||
![]() |
ec5bc3e738 | ||
![]() |
82729a12c8 | ||
![]() |
e83f37d518 | ||
![]() |
413b4f407a | ||
![]() |
fa32fd95e3 | ||
![]() |
d152b4193c | ||
![]() |
cfc76ea286 | ||
![]() |
f8e689561f | ||
![]() |
2ccd3252ec | ||
![]() |
8c1c710646 | ||
![]() |
052ae0a201 | ||
![]() |
f18e1f0b68 | ||
![]() |
f3a5e8e6b9 | ||
![]() |
ab103ac65f | ||
![]() |
2e994ac124 | ||
![]() |
b2f4f43d7e | ||
![]() |
c260a3d824 | ||
![]() |
f40efc265f | ||
![]() |
933b7f1b3b | ||
![]() |
19b9502e24 | ||
![]() |
f01cefc30f | ||
![]() |
068ac2ebd0 | ||
![]() |
c7d152cc22 | ||
![]() |
f0e3f7e82e |
150
CHANGES.md
Normal file
150
CHANGES.md
Normal file
@ -0,0 +1,150 @@
|
||||
This is only a summary provided to give a quick overview of changes. It does not list details which can be found in commit messages. If you think that more detailed changelog would be beneficiary, please raise it as an issue.
|
||||
|
||||
Versions marked as 'unofficial' are labelled only for the needs of this changelog. Officially I maintain version numbers since 2019/04, starting from version 1.14. If you have any of the earlier commits then you will see plugin signed as a version 1.0.
|
||||
|
||||
Change History
|
||||
--------------
|
||||
|
||||
Version: 1.22 (2021/04)
|
||||
- Fixed potential for initialization fiasco when using delegates container.
|
||||
- Fixed bug in code protecting redirecting handles from self-referencing.
|
||||
- Fixed cached resource handles getting invalid after reloading texture resources.
|
||||
|
||||
Version: 1.21 (2020/07-09)
|
||||
Improving stability
|
||||
- Fixed a crash in the input handler caused by invalidated by hot-reload instance trying to unregister a delegate.
|
||||
- Improved hot-reload stability and support for reloading after recompiling outside of the editor. Both methods should be equally supported and work together.
|
||||
- Improved behaviour of delegates when hot-reloading.
|
||||
- Changed context index mapping to fix issues with multi-PIE debugging in 4.25.
|
||||
- Fixed Linux crash caused by wrong mapping of key codes.
|
||||
|
||||
Version: 1.20 (2020/06)
|
||||
Transition to IWYU and maintenance:
|
||||
- Replaced includes of monolithic headers.
|
||||
- Removed explicit PCH and switched to IWYU-style PCH model.
|
||||
- Fixed includes to compile without explicit PCH in non-unity mode.
|
||||
- Fixed a few issues causing compilation errors in older engine versions.
|
||||
- Fixed debug code to compile on platforms using other than char or wchar_t characters.
|
||||
- Fixed issues in recently added DPI scaling.
|
||||
- Cleaned obsolete and unused code.
|
||||
- Replaced custom scope guards with the template provided by the engine.
|
||||
|
||||
Version: 1.19 (2020/03-06)
|
||||
- Integrated fix for issue with ImGui popup/modal windows not being able to be closed in transparent mouse input mode.
|
||||
- Integrated first version of Adaptive Canvas Size.
|
||||
- Added different options to define canvas size, with Adaptive Canvas Size being one of the options (viewport).
|
||||
- Added option for DPI scaling.
|
||||
|
||||
Version: 1.18 (2020/01)
|
||||
- Updated to engine version 4.24.
|
||||
- Updated to ImGui version 1.74.
|
||||
|
||||
Version: 1.17 (2019/04)
|
||||
- Added experimental support for touch input.
|
||||
- Integrated fixes allowing to build this as an engine plugin:
|
||||
- Added support for sharing with game mouse input.
|
||||
- Refactorization of input handling, with changes in SImGuiWidget and compatibility breaking changes in UImGuiInputHandler.
|
||||
|
||||
Version: 1.16 (2019/05)
|
||||
- Fixed issue with SImGuiLayout blocking mouse input for other Slate widgets, which was introduced by refactorization of widgets (version 1.14, commit c144658f).
|
||||
|
||||
Version: 1.15 (2019/04)
|
||||
- Added new FImGuiDelegates interface for ImGui debug delegates.
|
||||
- Added code preserving delegates during hot-reloading and moving them to a new module.
|
||||
- DEPRECIATED old FImGuiModule delegates interface and FImGuiDelegateHandle.
|
||||
- Delegates registered with depreciated interface are redirected and get benefit of being preserved during hot-reloading. This can be controlled with IMGUI_REDIRECT_OBSOLETE_DELEGATES.
|
||||
- Added IMGUI_WITH_OBSOLETE_DELEGATES allowing to strip depreciated interface from builds (that interface will be officially removed in one of later releases).
|
||||
- Added new ImGui early debug delegates called during world tick start.
|
||||
- Delegates are called in fixed order: multi-context early debug, world early debug (called during world tick start), world debug, multi-context debug (called during world post actor tick or if not available, during world tick start).
|
||||
- Removed from build script configuration of debug delegates.
|
||||
|
||||
Version: 1.14 (2019/03)
|
||||
- Added SImGuiLayout to resets layout for SImGuiWidget.
|
||||
- Refactored rendering in SImGuiWidget to take advantage of layout reset.
|
||||
- Reworked ImGui canvas dragging and scaling and moved it to SImGuiCanvasControl.
|
||||
- Removed dependency on ImGui internal cursor data.
|
||||
|
||||
Version: 1.13 (unofficial) (2019/03)
|
||||
- Fixed mapping from FKey to ImGui key index to gracefully handle unsupported keys and work on platforms that do not support all the keys defined in ImGui key map.
|
||||
- Fixed non-unity compile warnings and errors.
|
||||
|
||||
Version: 1.12 (unofficial) (2018/12)
|
||||
- Added support for sharing with game keyboard and gamepad input.
|
||||
- Added FImGuiModuleSettings to handle delayed loading of UImGuiSettings and serve as settings proxy for other classes.
|
||||
|
||||
Version: 1.11 (unofficial) (2018/10-11)
|
||||
- Moved ImGui Draw events to be called at the end of the world update during post actor tick event. Only available in UE 4.18 or later, with old behaviour available as an option.
|
||||
- Replaced console variable based configuration of ImGui Draw events with macros.
|
||||
- Replaced console variable based configuration of software cursor with a setting.
|
||||
- Console variables and logging that are primarily focused on module development are hidden by default and can be enabled by setting IMGUI_MODULE_DEVELOPER to 1.
|
||||
- Replaced console variables with module properties and settings.
|
||||
- Added console commands to control module properties.
|
||||
- Added support to preserve and move module properties to hot-reloaded module.
|
||||
- Moved properties to public interface.
|
||||
- Added FImGuiModule interface to access properties instance.
|
||||
- DEPRECIATED FImGuiModule functions to modify single properties.
|
||||
|
||||
Version: 1.10 (unofficial) (2018/10)
|
||||
- Changed module type to 'Developer' to make it easier strip it from shipping or other builds.
|
||||
- Added runtime loader to allow automatically load developer module in runtime builds.
|
||||
|
||||
Version: 1.09 (unofficial) (2018/08-09)
|
||||
- Added interface to register user textures for use in ImGui.
|
||||
- Fixed bad deallocation in Texture Manager.
|
||||
- Updated to ImGui 1.61.
|
||||
- Updated to UE 4.20.
|
||||
|
||||
Version: 1.08 (unofficial) (2018/07-08)
|
||||
- Added ImGui Input Handler to allow to customization of input handling.
|
||||
- Added ImGui settings with editor page in project properties.
|
||||
- Added command to switch input mode with configurable key binding.
|
||||
- Added backward compatibility macros.
|
||||
- Fixed hot-reloading issues with using ImGui implementation.
|
||||
|
||||
Version: 1.07 (unofficial) (2018/05)
|
||||
- Improved initialization to allow loading module in any loading phase.
|
||||
|
||||
Version: 1.06 (unofficial) (2018/05)
|
||||
- Updated to ImGui 1.61
|
||||
- Added support for gamepad and keyboard navigation.
|
||||
|
||||
Version: 1.05 (unofficial) (2018/04)
|
||||
- Added mode to scale and drag ImGui canvas.
|
||||
- Using ImGui internal cursor data to draw drag icon.
|
||||
|
||||
Version: 1.04 (unofficial) (2018/03)
|
||||
- Minimised lag between ending ImGui frame and rendering draw data in Slate.
|
||||
- Moved ImGui Draw event to be called during world tick start with configuration to use old behaviour.
|
||||
|
||||
Version: 1.03 (unofficial) (2018/01-03)
|
||||
- Fixed warnings and errors found in non-unity, Linux, PS4 or XBox builds.
|
||||
- Added configuration to choose between hardware and software cursor with hardware cursor used by default.
|
||||
|
||||
Version: 1.02 (unofficial) (2018/01)
|
||||
- Updated to ImGui 1.53.
|
||||
- Fixed problems with ImGui Demo working in multi-context environment.
|
||||
- Added FImGuiModule interface to change input mode and demo visibility.
|
||||
- Fixed input state issues after application loses focus.
|
||||
- Added input state debugging and `ImGui.Debug.Input` console variable to switch it.
|
||||
|
||||
Version: 1.01 (unofficial) (2017/10)
|
||||
- Added `ImGui.ShowDemo` console variable to show/hide ImGui demo.
|
||||
- Added automatic switching to right ImGui context at the beginning of the world tick.
|
||||
- Updated to UE 4.18
|
||||
|
||||
Version: <=1.00 (unofficial) (2017/04-10)
|
||||
- Added ImGui source code as an external module to expose it in IDE.
|
||||
- Integrated ImGui source code to build as part of the ImGui module.
|
||||
- Added FImGuiModule to implement ImGui module interface.
|
||||
- Added FImGuiModuleManager to control other module components.
|
||||
- Added FTextureManager to manage texture resources and map them to ImTextureID.
|
||||
- Added SImGuiWidget to handle Slate input and render in Slate ImGui draw data.
|
||||
- Added FImGuiInputState to collect and store input before it can be copied to ImGui IO.
|
||||
- Added FContextProxy to represent a single ImGui context.
|
||||
- Added FImGuiContextManager to create and manage ImGui context proxies.
|
||||
- Added Multi-PIE support with each PIE instance getting a dedicated ImGui context proxy, input state and widget.
|
||||
- Added `ImGui.InputEnabled` console variable to control whether input mode is enabled.
|
||||
- Added widget debugging and `ImGui.Debug.Widget` console variable to switch it.
|
||||
- Added ImGui software cursor.
|
||||
- Added support for session reloading with ini file names based on world type and PIE index.
|
||||
- Added FImGuiModule interface to register ImGui delegates called to draw ImGui controls.
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "1.0",
|
||||
"VersionName": "1.22",
|
||||
"FriendlyName": "ImGui",
|
||||
"Description": "",
|
||||
"Category": "Debug",
|
||||
@ -10,14 +10,20 @@
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "",
|
||||
"CanContainContent": true,
|
||||
"CanContainContent": false,
|
||||
"IsBetaVersion": false,
|
||||
"Installed": false,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "ImGui",
|
||||
"Type": "RuntimeNoCommandlet",
|
||||
"Type": "DeveloperTool",
|
||||
"LoadingPhase": "PreDefault"
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "EnhancedInput",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Sebastian Gross
|
||||
Copyright (c) 2017-2021 Sebastian Gross
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
193
README.md
193
README.md
@ -1,63 +1,190 @@
|
||||
Unreal ImGui
|
||||
============
|
||||
|
||||
[](LICENSE.md)
|
||||
|
||||
Unreal ImGui is an Unreal Engine 4 plug-in which integrates [Dear ImGui](https://github.com/ocornut/imgui) framework developed by Omar Cornut.
|
||||
Unreal ImGui is an Unreal Engine 5 plug-in that integrates [Dear ImGui](https://github.com/ocornut/imgui) developed by Omar Cornut.
|
||||
|
||||
Dear ImGui simplifies and helps with creation of quality visualisation and debugging tools for your Unreal projects.
|
||||
Dear ImGui is an immediate-mode graphical user interface library that is very lightweight and easy to use. It can be very useful when creating debugging tools.
|
||||
|
||||
:stop_button: Read Me First
|
||||
---------------------------
|
||||
This is a fork of [https://github.com/benui-dev/UnrealImGui](https://github.com/benui-dev/UnrealImGui), which itself is a fork of the original UnrealImGui project by [https://github.com/segross/UnrealImGui](https://github.com/segross/UnrealImGui).
|
||||
|
||||
About
|
||||
You can view the original readme.md, please see this link: [UnrealImGui ReadMe.md](https://github.com/benui-dev/UnrealImGui/blob/master/README.md).
|
||||
|
||||
Fork Additions/Fixes
|
||||
--------------------
|
||||
- Updated Dear ImGui to 1.91.5-docking
|
||||
- Added configurable docking-enabled setting
|
||||
|
||||
TO-DO
|
||||
-----
|
||||
- [X] Update Dear ImGui to latest 'docking' branch release (which as of writing is 1.91.5-docking)
|
||||
- [ ] Update ImPlot to latest (as of writing is 0.16)
|
||||
- [X] Update input state to use new ImGuiKey API
|
||||
- [ ] Update gamepad navigation to use new ImGuiKey API
|
||||
- [X] Enable docking
|
||||
- [ ] Support full-screen dock space
|
||||
- [ ] Add NetImGui support
|
||||
|
||||
This plug-in adds to Unreal project ImGui module. Adding it as as a dependency to other modules enables them to use Dear ImGui framework.
|
||||
Status
|
||||
------
|
||||
UnrealImGui Version: 1.22
|
||||
|
||||
The base aim of the project is to provide a basic integration of Dear ImGui, without imposing any patters how it should be used in projects. Right now it doesn't extend ImGui API for Unreal types, but that will change in near future.
|
||||
ImGui version: 1.91.5-docking
|
||||
|
||||
This is a work in progress but it supports key Unreal features, like Multi-PIE sessions etc. When running Multi-PIE session, each world gets its own ImGui context where world specific data can be visualised.
|
||||
ImPlot version: v0.13 WIP
|
||||
|
||||
The upcoming feature is the ability to invisibly switch contexts when calling ImGui functions from different worlds.
|
||||
Supported Unreal Engine version: 5.4*
|
||||
|
||||
After that I plan to add more usability features, better documentation and integration of Remote ImGui which enables using ImGui from a browser and to investigate possibility of opening ImGui for Blueprints.
|
||||
\* *The original repository and Ben's fork that this project is based on has support for Unreal 4.26 to 5.3. As of writing I am currently using this plugin on Unreal 5.4 with no issues. I cannot guarantee compatibility with versions other than the ones previously listed, so use this plugin with other versions of Unreal at your own risk.*
|
||||
|
||||
|
||||
How to use it
|
||||
How to Set up
|
||||
-------------
|
||||
On top of reading the base repository's [How to Set up](https://github.com/segross/UnrealImGui/blob/master/README.md#how-to-set-up) segment, you'll need to add the following line to your `[GameName].Build.cs` file otherwise you'll get linking errors:
|
||||
|
||||
To use this plug-in, you will need a C++ Unreal project.
|
||||
|
||||
Content of this repository needs to be placed in *Plugins* directory under project's root: */Plugins/ImGui*. After you compile and run you should notice that *ImGui* module is now available.
|
||||
|
||||
To use that in other modules you will need to declare it as a public or private dependency in those modules' Build.cs files:
|
||||
|
||||
```c#
|
||||
PublicDependencyModuleNames.AddRange(new string[] { "ImGui" });
|
||||
```
|
||||
or
|
||||
|
||||
```c#
|
||||
PrivateDependencyModuleNames.AddRange(new string[] { "ImGui" });
|
||||
```cpp
|
||||
// Tell the compiler we want to import the ImPlot symbols when linking against ImGui plugin
|
||||
PrivateDefinitions.Add(string.Format("IMPLOT_API=DLLIMPORT"));
|
||||
```
|
||||
|
||||
You should now be able to use ImGui.
|
||||
# Additional Knowledge
|
||||
|
||||
## Using ImPlot
|
||||
|
||||
*Console variables:*
|
||||
It's pretty easy to use ImPlot, it's pretty much the same drill as using Dear ImGui with the UnrealImGui plugin. You can see documentation on how to use ImPlot here: [ImPlot](https://github.com/epezent/implot).
|
||||
|
||||
- **ImGui.InputEnabled** - Allows to enable or disable ImGui input mode. 0: disabled (default); 1: enabled, input is routed to ImGui and with a few exceptions is consumed. Note: this is going to be supported by a keyboard short-cut, but in the meantime ImGui input can be enabled/disabled using console.
|
||||
- **ImGui.ShowDemo** - Show ImGui demo. 0: disabled (default); 1: enabled.
|
||||
- **ImGui.Debug.Widget** - Show self-debug for the widget that renders ImGui output. 0: disabled (default); 1: enabled.
|
||||
The only thing you won't need to do is call the `ImPlot::CreateContext()` and `ImPlot::DestroyContext` routines as they're already called when ImGui's context is created within UnrealImGui's guts.
|
||||
|
||||
## Drawing a UTextureRenderTarget2D
|
||||
|
||||
One might want to render viewports into the world in an ImGui window. You can do this pretty simply by generating a `UTextureRenderTarget2D` then assigning that to a `ASceneCapture2D` actor in your world. Here's some sample code for generating an correctly managing the `UTextureRenderTarget2D`:
|
||||
```cpp
|
||||
void Init()
|
||||
{
|
||||
TextureRenderTarget = NewObject<UTextureRenderTarget2D>();
|
||||
if(IsValid(TextureRenderTarget))
|
||||
{
|
||||
TextureRenderTarget->InitAutoFormat(512, 512);
|
||||
TextureRenderTarget->UpdateResourceImmediate(true);
|
||||
}
|
||||
|
||||
// ... Generate a unique TextureName here
|
||||
// Register this render target as an ImGui interop handled texture
|
||||
ImGuiTextureHandle = FImGuiModule::Get().FindTextureHandle(TextureName);
|
||||
if(!ImGuiTextureHandle.IsValid())
|
||||
{
|
||||
if(IsValid(TextureRenderTarget))
|
||||
{
|
||||
ImGuiTextureHandle = FImGuiModule::Get().RegisterTexture(TextureName, TextureRenderTarget, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~Class()
|
||||
{
|
||||
// Requires releasing to avoid memory leak
|
||||
FImGuiModule::Get().ReleaseTexture(ImGuiTextureHandle);
|
||||
}
|
||||
|
||||
void Render()
|
||||
{
|
||||
// Actually submit the draw command to ImGui to render the quad with the texture
|
||||
if(ImGuiTextureHandle.IsValid())
|
||||
{
|
||||
ImGui::Image(ImGuiTextureHandle.GetTextureId(), {512.f, 512.f});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then generating the `ASceneCapture2D`:
|
||||
```cpp
|
||||
void Init()
|
||||
{
|
||||
FActorSpawnParameters SpawnInfo;
|
||||
SceneCapture2D = World->SpawnActor<ASceneCapture2D>(FVector::ZeroVector, FRotator::ZeroRotator, SpawnInfo);
|
||||
SceneCaptureComponent2D->TextureTarget = TextureRenderTarget;
|
||||
SceneCaptureComponent2D->UpdateContent();
|
||||
|
||||
// Need to use this in order to force capture to use tone curve and also set alpha to scene alpha (1)
|
||||
SceneCaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_FinalToneCurveHDR;
|
||||
}
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
If you're using a scene capture and your quad is not drawing at all, make sure your scene capture "Capture Source" is set to "Final Color (with tone curve) in Linear sRGB gamut" to avoid alpha being set to 0 (since there's no way to instruct ImGui to ignore alpha without modding the core UnrealImGui plugin).
|
||||
|
||||
If you're getting crashes or seg faults during rendering, make sure you're using `UPROPERTY()` on your class variables!
|
||||
|
||||
## Adding custom fonts
|
||||
### FontAwesome
|
||||
Adding custom fonts is fairly simple. As a more complex and more commonly done, we're going to embed and build FontAwesome 6 into the font atlas. First thing you'll need is a binary C version of the FontAwesome font along with the necessary [companion descriptors](https://github.com/juliettef/IconFontCppHeaders/blob/main/IconsFontAwesome6.h). The descriptors are pre-generated, however if you have a new version of FA you wish to use, then use the Python script in that repository. As for the binary C, you'll need to compile Dear ImGui's [binary_to_compressed_c.cpp](https://github.com/ocornut/imgui/blob/master/misc/fonts/binary_to_compressed_c.cpp).
|
||||
|
||||
Once you have the necessary files, you'll need to encode your FontAwesome font using the command:
|
||||
```
|
||||
binary_to_compressed_c.exe -nocompress fa-solid-900.ttf FontAwesomeFont > FontAwesomeFont.h
|
||||
```
|
||||
The only mandatory field here is `-nocompress` as this instructs the encoder to create an uncompressed binary file (1:1) since currently there's no immediate support for compressed fonts.
|
||||
|
||||
Move over your descriptors and your binary C font file to an appropriate location for inclusion in your Unreal Engine project. The following code is how to instruct ImGui to build the font atlas with your FontAwesome font:
|
||||
|
||||
```cpp
|
||||
#include "FontAwesomeFont.h"
|
||||
#include "IconsFontAwesome6.h"
|
||||
|
||||
// Add FontAwesome font glyphs from memory
|
||||
if(TSharedPtr<ImFontConfig> FAFontConfig = MakeShareable(new ImFontConfig()))
|
||||
{
|
||||
static const ImWchar IconRange[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
|
||||
|
||||
FAFontConfig->FontDataOwnedByAtlas = false; // Global font data lifetime
|
||||
FAFontConfig->FontData = (void*)FontAwesomeData; // Declared in binary C .h file
|
||||
FAFontConfig->FontDataSize = FontAwesomeSize; // Declared in binary C .h file
|
||||
FAFontConfig->SizePixels = 16;
|
||||
FAFontConfig->MergeMode = true; // Forces ImGui to place this font into the same atlas as the previous font
|
||||
FAFontConfig->GlyphRanges = IconRange; // Required; instructs ImGui to use these glyphs
|
||||
FAFontConfig->GlyphMinAdvanceX = 16.f; // Use for monospaced icons
|
||||
FAFontConfig->PixelSnapH = true; // Better rendering (align to pixel grid)
|
||||
FAFontConfig->GlyphOffset = {0, 3}; // Moves icons around, for alignment with general typesets
|
||||
|
||||
FImGuiModule::Get().GetProperties().AddCustomFont("FontAwesome", FAFontConfig);
|
||||
FImGuiModule::Get().RebuildFontAtlas();
|
||||
}
|
||||
```
|
||||
|
||||
That's it. Make sure you execute the above code once at the beginning of the first ImGui frame (or at any point of your framework where the ImGui context has been initialized correctly) and it should build the main atlas with FontAwesome inside it. ImFontConfig lifetime is currently managed via reference counting (`TSharedPtr`).
|
||||
|
||||
### Using the icons
|
||||
```cpp
|
||||
#include "IconsFontAwesome6.h"
|
||||
|
||||
void OnPaint()
|
||||
{
|
||||
ImGui::Text(ICON_FA_AWARD);
|
||||
}
|
||||
```
|
||||
|
||||
Pretty simple. Building FStrings that incorporate FontAwesome icons however gets a little trickier:
|
||||
```cpp
|
||||
FString Str = "Hello " + FString(UTF8_TO_TCHAR(ICON_FA_WAVE_SQUARE)) " World";
|
||||
ImGui::TextUnformatted(TCHAR_TO_UTF8(*Str));
|
||||
```
|
||||
|
||||
### More info
|
||||
- [Dear ImGui: Using Fonts](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md)
|
||||
- [IconFontCppHeaders](https://github.com/juliettef/IconFontCppHeaders)
|
||||
- [FontAwesome with general Dear ImGui](https://pixtur.github.io/mkdocs-for-imgui/site/FONTS/)
|
||||
|
||||
# Misc
|
||||
|
||||
See also
|
||||
--------
|
||||
|
||||
- [Fork this project is based on](https://github.com/benui-dev/UnrealImGui)
|
||||
- [Original project by segross](https://github.com/segross/UnrealImGui)
|
||||
- [Dear ImGui](https://github.com/ocornut/imgui)
|
||||
- [An Introduction to UE4 Plugins](https://wiki.unrealengine.com/An_Introduction_to_UE4_Plugins).
|
||||
- [ImPlot](https://github.com/epezent/implot)
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Unreal ImGui is licensed under the MIT License, see LICENSE for more information.
|
||||
Unreal ImGui (and this fork) is licensed under the MIT License, see LICENSE for more information.
|
||||
|
@ -1,5 +1,6 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnrealBuildTool;
|
||||
|
||||
@ -12,10 +13,27 @@ public class ImGui : ModuleRules
|
||||
#endif
|
||||
{
|
||||
|
||||
#if WITH_FORWARDED_MODULE_RULES_CTOR
|
||||
bool bBuildEditor = Target.bBuildEditor;
|
||||
#else
|
||||
bool bBuildEditor = (Target.Type == TargetRules.TargetType.Editor);
|
||||
#endif
|
||||
|
||||
// Developer modules are automatically loaded only in editor builds but can be stripped out from other builds.
|
||||
// Enable runtime loader, if you want this module to be automatically loaded in runtime builds (monolithic).
|
||||
bool bEnableRuntimeLoader = true;
|
||||
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
#if UE_4_24_OR_LATER
|
||||
bLegacyPublicIncludePaths = false;
|
||||
ShadowVariableWarningLevel = WarningLevel.Error;
|
||||
bTreatAsEngineModule = true;
|
||||
#endif
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"ImGui/Public",
|
||||
Path.Combine(ModuleDirectory, "../ThirdParty/ImGuiLibrary/Include")
|
||||
Path.Combine(ModuleDirectory, "../ThirdParty/ImGuiLibrary/Include"),
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
@ -24,7 +42,7 @@ public class ImGui : ModuleRules
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"ImGui/Private",
|
||||
"ThirdParty/ImGuiLibrary/Private"
|
||||
"ThirdParty/ImGuiLibrary/Private",
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
@ -44,6 +62,7 @@ public class ImGui : ModuleRules
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"EnhancedInput",
|
||||
"Engine",
|
||||
"InputCore",
|
||||
"Slate",
|
||||
@ -53,11 +72,31 @@ public class ImGui : ModuleRules
|
||||
);
|
||||
|
||||
|
||||
if (bBuildEditor)
|
||||
{
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"EditorStyle",
|
||||
"Settings",
|
||||
"UnrealEd",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
#if !UE_4_19_OR_LATER
|
||||
List<string> PrivateDefinitions = Definitions;
|
||||
#endif
|
||||
|
||||
PrivateDefinitions.Add(string.Format("RUNTIME_LOADER_ENABLED={0}", bEnableRuntimeLoader ? 1 : 0));
|
||||
}
|
||||
}
|
||||
|
158
Source/ImGui/Private/Editor/ImGuiCanvasSizeInfoCustomization.cpp
Normal file
158
Source/ImGui/Private/Editor/ImGuiCanvasSizeInfoCustomization.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiCanvasSizeInfoCustomization.h"
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
#include "ImGuiModuleSettings.h"
|
||||
|
||||
#include <PropertyCustomizationHelpers.h>
|
||||
|
||||
|
||||
#define LOCTEXT_NAMESPACE "ImGuiEditor"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
EImGuiCanvasSizeType GetCanvasSizeTypeEnumValue(const TSharedPtr<IPropertyHandle>& TypeHandle)
|
||||
{
|
||||
uint8 ValueAsByte = 0;
|
||||
if (TypeHandle.IsValid())
|
||||
{
|
||||
TypeHandle->GetValue(ValueAsByte);
|
||||
}
|
||||
return static_cast<EImGuiCanvasSizeType>(ValueAsByte);
|
||||
}
|
||||
|
||||
bool IsAny(const TSharedPtr<IPropertyHandle>& TypeHandle, EImGuiCanvasSizeType Value)
|
||||
{
|
||||
return GetCanvasSizeTypeEnumValue(TypeHandle) == Value;
|
||||
}
|
||||
|
||||
template<typename... TRest>
|
||||
bool IsAny(const TSharedPtr<IPropertyHandle>& TypeHandle, EImGuiCanvasSizeType First, TRest... Rest)
|
||||
{
|
||||
return IsAny(TypeHandle, First) || IsAny(TypeHandle, Rest...);
|
||||
}
|
||||
|
||||
float ShowToHeight(bool bShow)
|
||||
{
|
||||
return bShow ? 0.f /* Infinity */ : SMALL_NUMBER;
|
||||
}
|
||||
|
||||
EVisibility ShowToVisibility(bool bShow)
|
||||
{
|
||||
return bShow ? EVisibility::SelfHitTestInvisible : EVisibility::Hidden;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// FImGuiKeyInfoCustomization implementation
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
TSharedRef<IPropertyTypeCustomization> FImGuiCanvasSizeInfoCustomization::MakeInstance()
|
||||
{
|
||||
return MakeShareable(new FImGuiCanvasSizeInfoCustomization);
|
||||
}
|
||||
|
||||
void FImGuiCanvasSizeInfoCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
|
||||
{
|
||||
TSharedPtr<IPropertyHandle> TypeHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiCanvasSizeInfo, SizeType));
|
||||
TSharedPtr<IPropertyHandle> WidthHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiCanvasSizeInfo, Width));
|
||||
TSharedPtr<IPropertyHandle> HeightHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiCanvasSizeInfo, Height));
|
||||
TSharedPtr<IPropertyHandle> ExtendToViewportHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiCanvasSizeInfo, bExtendToViewport));
|
||||
|
||||
#define ADD_DIMENSION_SLOT(Handle, LPadding) \
|
||||
+ SHorizontalBox::Slot()\
|
||||
.Padding(LPadding, 0.f, 0.f, 0.f)\
|
||||
.HAlign(HAlign_Fill)\
|
||||
.MaxWidth(80.f)\
|
||||
[\
|
||||
SNew(SVerticalBox)\
|
||||
+ SVerticalBox::Slot()\
|
||||
.AutoHeight()\
|
||||
[\
|
||||
Handle->CreatePropertyNameWidget()\
|
||||
]\
|
||||
+ SVerticalBox::Slot()\
|
||||
.HAlign(HAlign_Fill)\
|
||||
.AutoHeight()\
|
||||
[\
|
||||
Handle->CreatePropertyValueWidget()\
|
||||
]\
|
||||
]
|
||||
|
||||
auto SizeRowHeight = TAttribute<float>::Create([TypeHandle]()
|
||||
{
|
||||
return ShowToHeight(IsAny(TypeHandle, EImGuiCanvasSizeType::Custom));
|
||||
});
|
||||
|
||||
auto SizeRowVisibility = TAttribute<EVisibility>::Create([TypeHandle]()
|
||||
{
|
||||
return ShowToVisibility(IsAny(TypeHandle, EImGuiCanvasSizeType::Custom));
|
||||
});
|
||||
|
||||
auto ExtendRowHeight = TAttribute<float>::Create([TypeHandle]()
|
||||
{
|
||||
return ShowToHeight(IsAny(TypeHandle, EImGuiCanvasSizeType::Custom, EImGuiCanvasSizeType::Desktop));
|
||||
});
|
||||
|
||||
auto ExtendRowVisibility = TAttribute<EVisibility>::Create([TypeHandle]()
|
||||
{
|
||||
return ShowToVisibility(IsAny(TypeHandle, EImGuiCanvasSizeType::Custom, EImGuiCanvasSizeType::Desktop));
|
||||
});
|
||||
|
||||
HeaderRow
|
||||
.NameContent()
|
||||
[
|
||||
InStructPropertyHandle->CreatePropertyNameWidget()
|
||||
]
|
||||
.ValueContent()
|
||||
.MinDesiredWidth(168.f)
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
TypeHandle->CreatePropertyValueWidget()
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.MaxHeight(SizeRowHeight)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
.Visibility(SizeRowVisibility)
|
||||
ADD_DIMENSION_SLOT(WidthHandle, 0.f)
|
||||
ADD_DIMENSION_SLOT(HeightHandle, 6.f)
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.MaxHeight(ExtendRowHeight)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
.Visibility(ExtendRowVisibility)
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
[
|
||||
ExtendToViewportHandle->CreatePropertyValueWidget()
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(4.f, 0.f, 0.f, 0.f)
|
||||
.AutoWidth()
|
||||
[
|
||||
ExtendToViewportHandle->CreatePropertyNameWidget()
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
#undef ADD_DIMENSION_SLOT
|
||||
}
|
||||
|
||||
void FImGuiCanvasSizeInfoCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
#endif // WITH_EDITOR
|
@ -0,0 +1,22 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
#include <IPropertyTypeCustomization.h>
|
||||
#include <PropertyHandle.h>
|
||||
|
||||
|
||||
// Property type customization for FImGuiCanvasSizeInfo.
|
||||
class FImGuiCanvasSizeInfoCustomization : public IPropertyTypeCustomization
|
||||
{
|
||||
public:
|
||||
static TSharedRef<IPropertyTypeCustomization> MakeInstance();
|
||||
|
||||
// IPropertyTypeCustomization interface
|
||||
virtual void CustomizeHeader(TSharedRef<IPropertyHandle> InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
|
||||
virtual void CustomizeChildren(TSharedRef<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
|
||||
};
|
||||
|
||||
#endif // WITH_EDITOR
|
136
Source/ImGui/Private/Editor/ImGuiEditor.cpp
Normal file
136
Source/ImGui/Private/Editor/ImGuiEditor.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiEditor.h"
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
#include "ImGuiCanvasSizeInfoCustomization.h"
|
||||
#include "ImGuiKeyInfoCustomization.h"
|
||||
#include "ImGuiModuleSettings.h"
|
||||
|
||||
#include <ISettingsModule.h>
|
||||
#include <Modules/ModuleManager.h>
|
||||
|
||||
|
||||
#define LOCTEXT_NAMESPACE "ImGuiEditor"
|
||||
|
||||
#define SETTINGS_CONTAINER TEXT("Project"), TEXT("Plugins"), TEXT("ImGui")
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
ISettingsModule* GetSettingsModule()
|
||||
{
|
||||
return FModuleManager::GetModulePtr<ISettingsModule>("Settings");
|
||||
}
|
||||
|
||||
FPropertyEditorModule* GetPropertyEditorModule()
|
||||
{
|
||||
return FModuleManager::GetModulePtr<FPropertyEditorModule>("PropertyEditor");
|
||||
}
|
||||
}
|
||||
|
||||
FImGuiEditor::FImGuiEditor()
|
||||
{
|
||||
Register();
|
||||
|
||||
// As a side effect of being part of the ImGui module, we need to support deferred registration (only executed if
|
||||
// module is loaded manually at the very early stage).
|
||||
if (!IsRegistrationCompleted())
|
||||
{
|
||||
CreateRegistrator();
|
||||
}
|
||||
}
|
||||
|
||||
FImGuiEditor::~FImGuiEditor()
|
||||
{
|
||||
Unregister();
|
||||
}
|
||||
|
||||
void FImGuiEditor::Register()
|
||||
{
|
||||
// Only register after UImGuiSettings class is initialized (necessary to check in early loading stages).
|
||||
if (!bSettingsRegistered && UImGuiSettings::Get())
|
||||
{
|
||||
if (ISettingsModule* SettingsModule = GetSettingsModule())
|
||||
{
|
||||
bSettingsRegistered = true;
|
||||
|
||||
SettingsModule->RegisterSettings(SETTINGS_CONTAINER,
|
||||
LOCTEXT("ImGuiSettingsName", "ImGui"),
|
||||
LOCTEXT("ImGuiSettingsDescription", "Configure the Unreal ImGui plugin."),
|
||||
UImGuiSettings::Get());
|
||||
}
|
||||
}
|
||||
|
||||
if (!bCustomPropertyTypeLayoutsRegistered)
|
||||
{
|
||||
if (FPropertyEditorModule* PropertyModule = GetPropertyEditorModule())
|
||||
{
|
||||
bCustomPropertyTypeLayoutsRegistered = true;
|
||||
|
||||
PropertyModule->RegisterCustomPropertyTypeLayout("ImGuiCanvasSizeInfo",
|
||||
FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FImGuiCanvasSizeInfoCustomization::MakeInstance));
|
||||
PropertyModule->RegisterCustomPropertyTypeLayout("ImGuiKeyInfo",
|
||||
FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FImGuiKeyInfoCustomization::MakeInstance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiEditor::Unregister()
|
||||
{
|
||||
if (bSettingsRegistered)
|
||||
{
|
||||
bSettingsRegistered = false;
|
||||
|
||||
if (ISettingsModule* SettingsModule = GetSettingsModule())
|
||||
{
|
||||
SettingsModule->UnregisterSettings(SETTINGS_CONTAINER);
|
||||
}
|
||||
}
|
||||
|
||||
if (bCustomPropertyTypeLayoutsRegistered)
|
||||
{
|
||||
bCustomPropertyTypeLayoutsRegistered = false;
|
||||
|
||||
if (FPropertyEditorModule* PropertyModule = GetPropertyEditorModule())
|
||||
{
|
||||
PropertyModule->UnregisterCustomPropertyTypeLayout("ImGuiCanvasSizeInfo");
|
||||
PropertyModule->UnregisterCustomPropertyTypeLayout("ImGuiKeyInfo");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiEditor::CreateRegistrator()
|
||||
{
|
||||
if (!RegistratorHandle.IsValid())
|
||||
{
|
||||
RegistratorHandle = FModuleManager::Get().OnModulesChanged().AddLambda([this](FName Name, EModuleChangeReason Reason)
|
||||
{
|
||||
if (Reason == EModuleChangeReason::ModuleLoaded)
|
||||
{
|
||||
Register();
|
||||
}
|
||||
|
||||
if (IsRegistrationCompleted())
|
||||
{
|
||||
ReleaseRegistrator();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiEditor::ReleaseRegistrator()
|
||||
{
|
||||
if (RegistratorHandle.IsValid())
|
||||
{
|
||||
FModuleManager::Get().OnModulesChanged().Remove(RegistratorHandle);
|
||||
RegistratorHandle.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#undef SETTINGS_CONTAINER
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
#endif // WITH_EDITOR
|
34
Source/ImGui/Private/Editor/ImGuiEditor.h
Normal file
34
Source/ImGui/Private/Editor/ImGuiEditor.h
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
#include <Delegates/IDelegateInstance.h>
|
||||
|
||||
|
||||
// Registers module's settings in editor (due to a small size of this code we don't use a separate editor module).
|
||||
class FImGuiEditor
|
||||
{
|
||||
public:
|
||||
|
||||
FImGuiEditor();
|
||||
~FImGuiEditor();
|
||||
|
||||
private:
|
||||
|
||||
bool IsRegistrationCompleted() const { return bSettingsRegistered && bCustomPropertyTypeLayoutsRegistered; }
|
||||
|
||||
void Register();
|
||||
void Unregister();
|
||||
|
||||
void CreateRegistrator();
|
||||
void ReleaseRegistrator();
|
||||
|
||||
FDelegateHandle RegistratorHandle;
|
||||
|
||||
bool bSettingsRegistered = false;
|
||||
bool bCustomPropertyTypeLayoutsRegistered = false;
|
||||
};
|
||||
|
||||
#endif // WITH_EDITOR
|
209
Source/ImGui/Private/Editor/ImGuiKeyInfoCustomization.cpp
Normal file
209
Source/ImGui/Private/Editor/ImGuiKeyInfoCustomization.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiKeyInfoCustomization.h"
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
#include "ImGuiModuleSettings.h"
|
||||
|
||||
#include <PropertyCustomizationHelpers.h>
|
||||
#include <SKeySelector.h>
|
||||
#include <Widgets/SCompoundWidget.h>
|
||||
#include <Widgets/Input/SCheckBox.h>
|
||||
|
||||
|
||||
#define LOCTEXT_NAMESPACE "ImGuiEditor"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Helper widgets
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace
|
||||
{
|
||||
class SPropertyKeySelector : public SCompoundWidget
|
||||
{
|
||||
public:
|
||||
SLATE_BEGIN_ARGS(SPropertyKeySelector)
|
||||
{}
|
||||
SLATE_ARGUMENT(TSharedPtr<IPropertyHandle>, KeyHandle)
|
||||
SLATE_ATTRIBUTE(FSlateFontInfo, Font)
|
||||
SLATE_END_ARGS()
|
||||
|
||||
void Construct(const FArguments& InArgs)
|
||||
{
|
||||
KeyHandle = InArgs._KeyHandle;
|
||||
|
||||
ChildSlot
|
||||
[
|
||||
SNew(SKeySelector)
|
||||
.CurrentKey(this, &SPropertyKeySelector::GetCurrentKey)
|
||||
.OnKeyChanged(this, &SPropertyKeySelector::OnKeyChanged)
|
||||
.Font(InArgs._Font)
|
||||
];
|
||||
}
|
||||
|
||||
TOptional<FKey> GetCurrentKey() const
|
||||
{
|
||||
TArray<void*> RawPtrs;
|
||||
KeyHandle->AccessRawData(RawPtrs);
|
||||
|
||||
if (RawPtrs.Num())
|
||||
{
|
||||
FKey* KeyPtr = static_cast<FKey*>(RawPtrs[0]);
|
||||
|
||||
if (KeyPtr)
|
||||
{
|
||||
for (int32 Index = 1; Index < RawPtrs.Num(); Index++)
|
||||
{
|
||||
if (*static_cast<FKey*>(RawPtrs[Index]) != *KeyPtr)
|
||||
{
|
||||
return TOptional<FKey>();
|
||||
}
|
||||
}
|
||||
|
||||
return *KeyPtr;
|
||||
}
|
||||
}
|
||||
|
||||
return FKey();
|
||||
}
|
||||
|
||||
void OnKeyChanged(TSharedPtr<FKey> SelectedKey)
|
||||
{
|
||||
KeyHandle->SetValueFromFormattedString(SelectedKey->ToString());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
TSharedPtr<IPropertyHandle> KeyHandle;
|
||||
};
|
||||
|
||||
class SPropertyTriStateCheckBox : public SCompoundWidget
|
||||
{
|
||||
public:
|
||||
|
||||
using FCheckBoxStateRaw = std::underlying_type<ECheckBoxState>::type;
|
||||
|
||||
SLATE_BEGIN_ARGS(SPropertyTriStateCheckBox)
|
||||
{}
|
||||
SLATE_ARGUMENT(TSharedPtr<IPropertyHandle>, PropertyHandle)
|
||||
SLATE_END_ARGS()
|
||||
|
||||
void Construct(const FArguments& InArgs)
|
||||
{
|
||||
PropertyHandle = InArgs._PropertyHandle;
|
||||
|
||||
// We are abusing SCheckBox a bit but our GetPropertyValue with custom OnCheckBoxStateChanged implementation
|
||||
// gives a checkbox that allows to cycle between all three states.
|
||||
ChildSlot
|
||||
[
|
||||
SNew(SCheckBox)
|
||||
.IsChecked(this, &SPropertyTriStateCheckBox::GetPropertyValue)
|
||||
.OnCheckStateChanged(this, &SPropertyTriStateCheckBox::OnCheckBoxStateChanged)
|
||||
];
|
||||
}
|
||||
|
||||
FCheckBoxStateRaw GetPropertyRawValue() const
|
||||
{
|
||||
FCheckBoxStateRaw Value;
|
||||
PropertyHandle.Get()->GetValue(Value);
|
||||
return Value;
|
||||
}
|
||||
|
||||
ECheckBoxState GetPropertyValue() const
|
||||
{
|
||||
return static_cast<ECheckBoxState>(GetPropertyRawValue());
|
||||
}
|
||||
|
||||
void OnCheckBoxStateChanged(ECheckBoxState State)
|
||||
{
|
||||
const FCheckBoxStateRaw PrevEnumValue = (GetPropertyRawValue() + 2) % 3;
|
||||
PropertyHandle.Get()->SetValue(PrevEnumValue);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
TSharedPtr<IPropertyHandle> PropertyHandle;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// FImGuiKeyInfoCustomization implementation
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace InputConstants
|
||||
{
|
||||
static const FMargin PropertyPadding(0.0f, 0.0f, 4.0f, 0.0f);
|
||||
}
|
||||
|
||||
TSharedRef<IPropertyTypeCustomization> FImGuiKeyInfoCustomization::MakeInstance()
|
||||
{
|
||||
return MakeShareable(new FImGuiKeyInfoCustomization);
|
||||
}
|
||||
|
||||
void FImGuiKeyInfoCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
|
||||
{
|
||||
TSharedPtr<IPropertyHandle> KeyHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiKeyInfo, Key));
|
||||
TSharedPtr<IPropertyHandle> ShiftHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiKeyInfo, Shift));
|
||||
TSharedPtr<IPropertyHandle> CtrlHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiKeyInfo, Ctrl));
|
||||
TSharedPtr<IPropertyHandle> AltHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiKeyInfo, Alt));
|
||||
TSharedPtr<IPropertyHandle> CmdHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiKeyInfo, Cmd));
|
||||
|
||||
#define ADD_CHECK_BOX_SLOT(Handle) \
|
||||
+ SHorizontalBox::Slot()\
|
||||
.Padding(InputConstants::PropertyPadding)\
|
||||
.HAlign(HAlign_Left)\
|
||||
.VAlign(VAlign_Center)\
|
||||
.AutoWidth()\
|
||||
[\
|
||||
Handle->CreatePropertyNameWidget()\
|
||||
]\
|
||||
+ SHorizontalBox::Slot()\
|
||||
.Padding(InputConstants::PropertyPadding)\
|
||||
.HAlign(HAlign_Left)\
|
||||
.VAlign(VAlign_Center)\
|
||||
.AutoWidth()\
|
||||
[\
|
||||
SNew(SPropertyTriStateCheckBox).PropertyHandle(Handle)\
|
||||
]
|
||||
|
||||
HeaderRow
|
||||
.NameContent()
|
||||
[
|
||||
InStructPropertyHandle->CreatePropertyNameWidget()
|
||||
]
|
||||
.ValueContent()
|
||||
.MaxDesiredWidth(400.f)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(InputConstants::PropertyPadding)
|
||||
.AutoWidth()
|
||||
[
|
||||
SNew(SBox)
|
||||
.WidthOverride(200.f)
|
||||
[
|
||||
SNew(SPropertyKeySelector)
|
||||
.KeyHandle(KeyHandle)
|
||||
.Font(StructCustomizationUtils.GetRegularFont())
|
||||
]
|
||||
]
|
||||
ADD_CHECK_BOX_SLOT(ShiftHandle)
|
||||
ADD_CHECK_BOX_SLOT(CtrlHandle)
|
||||
ADD_CHECK_BOX_SLOT(AltHandle)
|
||||
ADD_CHECK_BOX_SLOT(CmdHandle)
|
||||
];
|
||||
|
||||
#undef ADD_CHECK_BOX_SLOT
|
||||
}
|
||||
|
||||
void FImGuiKeyInfoCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
#endif // WITH_EDITOR
|
22
Source/ImGui/Private/Editor/ImGuiKeyInfoCustomization.h
Normal file
22
Source/ImGui/Private/Editor/ImGuiKeyInfoCustomization.h
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
#include <IPropertyTypeCustomization.h>
|
||||
#include <PropertyHandle.h>
|
||||
|
||||
|
||||
// Property type customization for FImGuiKeyInfo.
|
||||
class FImGuiKeyInfoCustomization : public IPropertyTypeCustomization
|
||||
{
|
||||
public:
|
||||
static TSharedRef<IPropertyTypeCustomization> MakeInstance();
|
||||
|
||||
// IPropertyTypeCustomization interface
|
||||
virtual void CustomizeHeader(TSharedRef<IPropertyHandle> InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
|
||||
virtual void CustomizeChildren(TSharedRef<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
|
||||
};
|
||||
|
||||
#endif // WITH_EDITOR
|
@ -1,16 +1,19 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
|
||||
#include "ImGuiContextManager.h"
|
||||
|
||||
#include "ImGuiDelegatesContainer.h"
|
||||
#include "ImGuiImplementation.h"
|
||||
#include "ImGuiModuleSettings.h"
|
||||
#include "ImGuiModule.h"
|
||||
#include "Utilities/WorldContext.h"
|
||||
#include "Utilities/WorldContextIndex.h"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
|
||||
// TODO: Refactor ImGui Context Manager, to handle different types of worlds.
|
||||
|
||||
namespace
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
@ -55,21 +58,29 @@ namespace
|
||||
#endif // WITH_EDITOR
|
||||
}
|
||||
|
||||
FImGuiContextManager::FImGuiContextManager()
|
||||
FImGuiContextManager::FImGuiContextManager(FImGuiModuleSettings& InSettings)
|
||||
: Settings(InSettings)
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
Settings.OnDPIScaleChangedDelegate.AddRaw(this, &FImGuiContextManager::SetDPIScale);
|
||||
|
||||
SetDPIScale(Settings.GetDPIScaleInfo());
|
||||
BuildFontAtlas();
|
||||
|
||||
FWorldDelegates::OnWorldTickStart.AddRaw(this, &FImGuiContextManager::OnWorldTickStart);
|
||||
#if ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK
|
||||
FWorldDelegates::OnWorldPostActorTick.AddRaw(this, &FImGuiContextManager::OnWorldPostActorTick);
|
||||
#endif
|
||||
}
|
||||
|
||||
FImGuiContextManager::~FImGuiContextManager()
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
Settings.OnDPIScaleChangedDelegate.RemoveAll(this);
|
||||
|
||||
// Order matters because contexts can be created during World Tick Start events.
|
||||
FWorldDelegates::OnWorldTickStart.RemoveAll(this);
|
||||
#if ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK
|
||||
FWorldDelegates::OnWorldPostActorTick.RemoveAll(this);
|
||||
#endif
|
||||
Contexts.Empty();
|
||||
ImGui::Shutdown();
|
||||
}
|
||||
|
||||
void FImGuiContextManager::Tick(float DeltaSeconds)
|
||||
@ -82,21 +93,57 @@ void FImGuiContextManager::Tick(float DeltaSeconds)
|
||||
auto& ContextData = Pair.Value;
|
||||
if (ContextData.CanTick())
|
||||
{
|
||||
ContextData.ContextProxy.SetAsCurrent();
|
||||
ContextData.ContextProxy.Tick(DeltaSeconds, &DrawMultiContextEvent);
|
||||
ContextData.ContextProxy->Tick(DeltaSeconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear to make sure that we don't store objects registered for world that is no longer valid.
|
||||
FImGuiDelegatesContainer::Get().OnWorldDebug(Pair.Key).Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Once all context tick they should use new fonts and we can release the old resources. Extra countdown is added
|
||||
// wait for contexts that ticked outside of this function, before rebuilding fonts.
|
||||
if (FontResourcesReleaseCountdown > 0 && !--FontResourcesReleaseCountdown)
|
||||
{
|
||||
FontResourcesToRelease.Empty();
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
#if ENGINE_COMPATIBILITY_LEGACY_WORLD_ACTOR_TICK
|
||||
void FImGuiContextManager::OnWorldTickStart(ELevelTick TickType, float DeltaSeconds)
|
||||
{
|
||||
if (GWorld)
|
||||
OnWorldTickStart(GWorld, TickType, DeltaSeconds);
|
||||
}
|
||||
#endif
|
||||
|
||||
void FImGuiContextManager::OnWorldTickStart(UWorld* World, ELevelTick TickType, float DeltaSeconds)
|
||||
{
|
||||
if (World && (World->WorldType == EWorldType::Game || World->WorldType == EWorldType::PIE
|
||||
|| World->WorldType == EWorldType::Editor))
|
||||
{
|
||||
GetWorldContextProxy(*GWorld).SetAsCurrent();
|
||||
FImGuiContextProxy& ContextProxy = GetWorldContextProxy(*World);
|
||||
|
||||
// Set as current, so we have right context ready when updating world objects.
|
||||
ContextProxy.SetAsCurrent();
|
||||
|
||||
ContextProxy.DrawEarlyDebug();
|
||||
#if !ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK
|
||||
ContextProxy.DrawDebug();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
#if ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK
|
||||
void FImGuiContextManager::OnWorldPostActorTick(UWorld* World, ELevelTick TickType, float DeltaSeconds)
|
||||
{
|
||||
if (World && (World->WorldType == EWorldType::Game || World->WorldType == EWorldType::PIE
|
||||
|| World->WorldType == EWorldType::Editor))
|
||||
{
|
||||
GetWorldContextProxy(*World).DrawDebug();
|
||||
}
|
||||
}
|
||||
#endif // ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK
|
||||
|
||||
#if WITH_EDITOR
|
||||
FImGuiContextManager::FContextData& FImGuiContextManager::GetEditorContextData()
|
||||
@ -105,7 +152,8 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetEditorContextData()
|
||||
|
||||
if (UNLIKELY(!Data))
|
||||
{
|
||||
Data = &Contexts.Emplace(Utilities::EDITOR_CONTEXT_INDEX, FContextData{ GetEditorContextName(), ImGuiDemo });
|
||||
Data = &Contexts.Emplace(Utilities::EDITOR_CONTEXT_INDEX, FContextData{ GetEditorContextName(), Utilities::EDITOR_CONTEXT_INDEX, FontAtlas, DPIScale, -1 });
|
||||
OnContextProxyCreated.Broadcast(Utilities::EDITOR_CONTEXT_INDEX, *Data->ContextProxy);
|
||||
}
|
||||
|
||||
return *Data;
|
||||
@ -119,7 +167,8 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetStandaloneWorldCont
|
||||
|
||||
if (UNLIKELY(!Data))
|
||||
{
|
||||
Data = &Contexts.Emplace(Utilities::STANDALONE_GAME_CONTEXT_INDEX, FContextData{ GetWorldContextName(), ImGuiDemo });
|
||||
Data = &Contexts.Emplace(Utilities::STANDALONE_GAME_CONTEXT_INDEX, FContextData{ GetWorldContextName(), Utilities::STANDALONE_GAME_CONTEXT_INDEX, FontAtlas, DPIScale });
|
||||
OnContextProxyCreated.Broadcast(Utilities::STANDALONE_GAME_CONTEXT_INDEX, *Data->ContextProxy);
|
||||
}
|
||||
|
||||
return *Data;
|
||||
@ -131,7 +180,8 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetWorldContextData(co
|
||||
using namespace Utilities;
|
||||
|
||||
#if WITH_EDITOR
|
||||
if (World.WorldType == EWorldType::Editor)
|
||||
// Default to editor context for anything other than a game world.
|
||||
if (World.WorldType != EWorldType::Game && World.WorldType != EWorldType::PIE)
|
||||
{
|
||||
if (OutIndex)
|
||||
{
|
||||
@ -159,7 +209,8 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetWorldContextData(co
|
||||
#if WITH_EDITOR
|
||||
if (UNLIKELY(!Data))
|
||||
{
|
||||
Data = &Contexts.Emplace(Index, FContextData{ GetWorldContextName(World), ImGuiDemo, WorldContext->PIEInstance });
|
||||
Data = &Contexts.Emplace(Index, FContextData{ GetWorldContextName(World), Index, FontAtlas, DPIScale, WorldContext->PIEInstance });
|
||||
OnContextProxyCreated.Broadcast(Index, *Data->ContextProxy);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -169,7 +220,8 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetWorldContextData(co
|
||||
#else
|
||||
if (UNLIKELY(!Data))
|
||||
{
|
||||
Data = &Contexts.Emplace(Index, FContextData{ GetWorldContextName(World), ImGuiDemo });
|
||||
Data = &Contexts.Emplace(Index, FContextData{ GetWorldContextName(World), Index, FontAtlas, DPIScale });
|
||||
OnContextProxyCreated.Broadcast(Index, *Data->ContextProxy);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -179,3 +231,73 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetWorldContextData(co
|
||||
}
|
||||
return *Data;
|
||||
}
|
||||
|
||||
void FImGuiContextManager::SetDPIScale(const FImGuiDPIScaleInfo& ScaleInfo)
|
||||
{
|
||||
const float Scale = ScaleInfo.GetImGuiScale();
|
||||
if (DPIScale != Scale)
|
||||
{
|
||||
DPIScale = Scale;
|
||||
|
||||
// Only rebuild font atlas if it is already built. Otherwise allow the other logic to pick a moment.
|
||||
if (FontAtlas.IsBuilt())
|
||||
{
|
||||
RebuildFontAtlas();
|
||||
}
|
||||
|
||||
for (auto& Pair : Contexts)
|
||||
{
|
||||
if (Pair.Value.ContextProxy)
|
||||
{
|
||||
Pair.Value.ContextProxy->SetDPIScale(DPIScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiContextManager::BuildFontAtlas(const TMap<FName, TSharedPtr<ImFontConfig>>& CustomFontConfigs)
|
||||
{
|
||||
if (!FontAtlas.IsBuilt())
|
||||
{
|
||||
ImFontConfig FontConfig = {};
|
||||
FontConfig.SizePixels = FMath::RoundFromZero(13.f * DPIScale);
|
||||
FontAtlas.AddFontDefault(&FontConfig);
|
||||
|
||||
// Build custom fonts
|
||||
for (const TPair<FName, TSharedPtr<ImFontConfig>>& CustomFontPair : CustomFontConfigs)
|
||||
{
|
||||
FName CustomFontName = CustomFontPair.Key;
|
||||
TSharedPtr<ImFontConfig> CustomFontConfig = CustomFontPair.Value;
|
||||
|
||||
// Set font name for debugging
|
||||
if (CustomFontConfig.IsValid())
|
||||
{
|
||||
strcpy_s(CustomFontConfig->Name, 40, TCHAR_TO_ANSI(*CustomFontName.ToString()));
|
||||
}
|
||||
|
||||
FontAtlas.AddFont(CustomFontConfig.Get());
|
||||
}
|
||||
|
||||
unsigned char* Pixels;
|
||||
int Width, Height, Bpp;
|
||||
FontAtlas.GetTexDataAsRGBA32(&Pixels, &Width, &Height, &Bpp);
|
||||
|
||||
OnFontAtlasBuilt.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiContextManager::RebuildFontAtlas()
|
||||
{
|
||||
if (FontAtlas.IsBuilt())
|
||||
{
|
||||
// Keep the old resources alive for a few frames to give all contexts a chance to bind to new ones.
|
||||
FontResourcesToRelease.Add(TUniquePtr<ImFontAtlas>(new ImFontAtlas()));
|
||||
Swap(*FontResourcesToRelease.Last(), FontAtlas);
|
||||
|
||||
// Typically, one frame should be enough but since we allow for custom ticking, we need at least to frames to
|
||||
// wait for contexts that already ticked and will not do that before the end of the next tick of this manager.
|
||||
FontResourcesReleaseCountdown = 3;
|
||||
}
|
||||
|
||||
BuildFontAtlas(FImGuiModule::Get().GetProperties().GetCustomFonts());
|
||||
}
|
||||
|
@ -3,15 +3,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "ImGuiContextProxy.h"
|
||||
#include "ImGuiDemo.h"
|
||||
#include "VersionCompatibility.h"
|
||||
|
||||
|
||||
class FImGuiModuleSettings;
|
||||
struct FImGuiDPIScaleInfo;
|
||||
|
||||
// TODO: It might be useful to broadcast FContextProxyCreatedDelegate to users, to support similar cases to our ImGui
|
||||
// demo, but we would need to remove from that interface internal classes.
|
||||
|
||||
// Delegate called when new context proxy is created.
|
||||
// @param ContextIndex - Index for that world
|
||||
// @param ContextProxy - Created context proxy
|
||||
DECLARE_MULTICAST_DELEGATE_TwoParams(FContextProxyCreatedDelegate, int32, FImGuiContextProxy&);
|
||||
|
||||
// Manages ImGui context proxies.
|
||||
class FImGuiContextManager
|
||||
{
|
||||
public:
|
||||
|
||||
FImGuiContextManager();
|
||||
FImGuiContextManager(FImGuiModuleSettings& InSettings);
|
||||
|
||||
FImGuiContextManager(const FImGuiContextManager&) = delete;
|
||||
FImGuiContextManager& operator=(const FImGuiContextManager&) = delete;
|
||||
@ -21,74 +32,66 @@ public:
|
||||
|
||||
~FImGuiContextManager();
|
||||
|
||||
ImFontAtlas& GetFontAtlas() { return FontAtlas; }
|
||||
const ImFontAtlas& GetFontAtlas() const { return FontAtlas; }
|
||||
|
||||
#if WITH_EDITOR
|
||||
// Get or create editor ImGui context proxy.
|
||||
FORCEINLINE FImGuiContextProxy& GetEditorContextProxy() { return GetEditorContextData().ContextProxy; }
|
||||
FORCEINLINE FImGuiContextProxy& GetEditorContextProxy() { return *GetEditorContextData().ContextProxy; }
|
||||
#endif
|
||||
|
||||
#if !WITH_EDITOR
|
||||
// Get or create standalone game ImGui context proxy.
|
||||
FORCEINLINE FImGuiContextProxy& GetWorldContextProxy() { return GetStandaloneWorldContextData().ContextProxy; }
|
||||
FORCEINLINE FImGuiContextProxy& GetWorldContextProxy() { return *GetStandaloneWorldContextData().ContextProxy; }
|
||||
#endif
|
||||
|
||||
// Get or create ImGui context proxy for given world.
|
||||
FORCEINLINE FImGuiContextProxy& GetWorldContextProxy(const UWorld& World) { return GetWorldContextData(World).ContextProxy; }
|
||||
FORCEINLINE FImGuiContextProxy& GetWorldContextProxy(const UWorld& World) { return *GetWorldContextData(World).ContextProxy; }
|
||||
|
||||
// Get or create ImGui context proxy for given world. Additionally get context index for that proxy.
|
||||
FORCEINLINE FImGuiContextProxy& GetWorldContextProxy(const UWorld& World, int32& OutContextIndex) { return GetWorldContextData(World, &OutContextIndex).ContextProxy; }
|
||||
FORCEINLINE FImGuiContextProxy& GetWorldContextProxy(const UWorld& World, int32& OutContextIndex) { return *GetWorldContextData(World, &OutContextIndex).ContextProxy; }
|
||||
|
||||
// Get context proxy by index, or null if context with that index doesn't exist.
|
||||
FORCEINLINE FImGuiContextProxy* GetContextProxy(int32 ContextIndex)
|
||||
{
|
||||
FContextData* Data = Contexts.Find(ContextIndex);
|
||||
return Data ? &(Data->ContextProxy) : nullptr;
|
||||
return Data ? Data->ContextProxy.Get() : nullptr;
|
||||
}
|
||||
|
||||
// Delegate called for all contexts in manager, right after calling context specific draw event. Allows listeners
|
||||
// draw the same content to multiple contexts.
|
||||
FSimpleMulticastDelegate& OnDrawMultiContext() { return DrawMultiContextEvent; }
|
||||
// Delegate called when a new context proxy is created.
|
||||
FContextProxyCreatedDelegate OnContextProxyCreated;
|
||||
|
||||
// Delegate called after font atlas is built.
|
||||
FSimpleMulticastDelegate OnFontAtlasBuilt;
|
||||
|
||||
void Tick(float DeltaSeconds);
|
||||
|
||||
private:
|
||||
void RebuildFontAtlas();
|
||||
|
||||
#if WITH_EDITOR
|
||||
private:
|
||||
|
||||
struct FContextData
|
||||
{
|
||||
FContextData(const FString& ContextName, FImGuiDemo& Demo, int32 InPIEInstance = -1)
|
||||
FContextData(const FString& ContextName, int32 ContextIndex, ImFontAtlas& FontAtlas, float DPIScale, int32 InPIEInstance = -1)
|
||||
: PIEInstance(InPIEInstance)
|
||||
, ContextProxy(ContextName)
|
||||
, ContextProxy(new FImGuiContextProxy(ContextName, ContextIndex, &FontAtlas, DPIScale))
|
||||
{
|
||||
ContextProxy.OnDraw().AddRaw(&Demo, &FImGuiDemo::DrawControls);
|
||||
}
|
||||
|
||||
FORCEINLINE bool CanTick() const { return PIEInstance < 0 || GEngine->GetWorldContextFromPIEInstance(PIEInstance); }
|
||||
|
||||
int32 PIEInstance = -1;
|
||||
FImGuiContextProxy ContextProxy;
|
||||
TUniquePtr<FImGuiContextProxy> ContextProxy;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
struct FContextData
|
||||
{
|
||||
FContextData(const FString& ContextName, FImGuiDemo& Demo)
|
||||
: ContextProxy(ContextName)
|
||||
{
|
||||
ContextProxy.OnDraw().AddRaw(&Demo, &FImGuiDemo::DrawControls);
|
||||
}
|
||||
|
||||
FORCEINLINE bool CanTick() const { return true; }
|
||||
|
||||
FImGuiContextProxy ContextProxy;
|
||||
};
|
||||
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
#if WITH_EDITOR
|
||||
#if ENGINE_COMPATIBILITY_LEGACY_WORLD_ACTOR_TICK
|
||||
void OnWorldTickStart(ELevelTick TickType, float DeltaSeconds);
|
||||
#endif
|
||||
void OnWorldTickStart(UWorld* World, ELevelTick TickType, float DeltaSeconds);
|
||||
|
||||
#if ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK
|
||||
void OnWorldPostActorTick(UWorld* World, ELevelTick TickType, float DeltaSeconds);
|
||||
#endif
|
||||
|
||||
#if WITH_EDITOR
|
||||
FContextData& GetEditorContextData();
|
||||
@ -100,9 +103,16 @@ private:
|
||||
|
||||
FContextData& GetWorldContextData(const UWorld& World, int32* OutContextIndex = nullptr);
|
||||
|
||||
void SetDPIScale(const FImGuiDPIScaleInfo& ScaleInfo);
|
||||
void BuildFontAtlas(const TMap<FName, TSharedPtr<ImFontConfig>>& CustomFontConfigs = {});
|
||||
|
||||
TMap<int32, FContextData> Contexts;
|
||||
|
||||
FImGuiDemo ImGuiDemo;
|
||||
ImFontAtlas FontAtlas;
|
||||
TArray<TUniquePtr<ImFontAtlas>> FontResourcesToRelease;
|
||||
|
||||
FSimpleMulticastDelegate DrawMultiContextEvent;
|
||||
FImGuiModuleSettings& Settings;
|
||||
|
||||
float DPIScale = -1.f;
|
||||
int32 FontResourcesReleaseCountdown = 0;
|
||||
};
|
||||
|
@ -1,13 +1,15 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
|
||||
#include "ImGuiContextProxy.h"
|
||||
|
||||
#include "ImGuiDelegatesContainer.h"
|
||||
#include "ImGuiImplementation.h"
|
||||
#include "ImGuiInteroperability.h"
|
||||
#include "Utilities/Arrays.h"
|
||||
#include "VersionCompatibility.h"
|
||||
|
||||
#include <Runtime/Launch/Resources/Version.h>
|
||||
#include <GenericPlatform/GenericPlatformFile.h>
|
||||
#include <Misc/Paths.h>
|
||||
|
||||
|
||||
static constexpr float DEFAULT_CANVAS_WIDTH = 3840.f;
|
||||
@ -18,10 +20,10 @@ namespace
|
||||
{
|
||||
FString GetSaveDirectory()
|
||||
{
|
||||
#if (ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 18))
|
||||
const FString SavedDir = FPaths::ProjectSavedDir();
|
||||
#else
|
||||
#if ENGINE_COMPATIBILITY_LEGACY_SAVED_DIR
|
||||
const FString SavedDir = FPaths::GameSavedDir();
|
||||
#else
|
||||
const FString SavedDir = FPaths::ProjectSavedDir();
|
||||
#endif
|
||||
|
||||
FString Directory = FPaths::Combine(*SavedDir, TEXT("ImGui"));
|
||||
@ -37,14 +39,47 @@ namespace
|
||||
static FString SaveDirectory = GetSaveDirectory();
|
||||
return FPaths::Combine(SaveDirectory, Name + TEXT(".ini"));
|
||||
}
|
||||
|
||||
struct FGuardCurrentContext
|
||||
{
|
||||
FGuardCurrentContext()
|
||||
: OldContext(ImGui::GetCurrentContext())
|
||||
{
|
||||
}
|
||||
|
||||
~FGuardCurrentContext()
|
||||
{
|
||||
if (bRestore)
|
||||
{
|
||||
ImGui::SetCurrentContext(OldContext);
|
||||
}
|
||||
}
|
||||
|
||||
FGuardCurrentContext(FGuardCurrentContext&& Other)
|
||||
: OldContext(MoveTemp(Other.OldContext))
|
||||
{
|
||||
Other.bRestore = false;
|
||||
}
|
||||
|
||||
FGuardCurrentContext& operator=(FGuardCurrentContext&&) = delete;
|
||||
|
||||
FGuardCurrentContext(const FGuardCurrentContext&) = delete;
|
||||
FGuardCurrentContext& operator=(const FGuardCurrentContext&) = delete;
|
||||
|
||||
private:
|
||||
|
||||
ImGuiContext* OldContext = nullptr;
|
||||
bool bRestore = true;
|
||||
};
|
||||
}
|
||||
|
||||
FImGuiContextProxy::FImGuiContextProxy(const FString& InName)
|
||||
FImGuiContextProxy::FImGuiContextProxy(const FString& InName, int32 InContextIndex, ImFontAtlas* InFontAtlas, float InDPIScale)
|
||||
: Name(InName)
|
||||
, ContextIndex(InContextIndex)
|
||||
, IniFilename(TCHAR_TO_ANSI(*GetIniFile(InName)))
|
||||
{
|
||||
// Create context.
|
||||
Context = ImGui::CreateContext();
|
||||
Context = ImGui::CreateContext(InFontAtlas);
|
||||
|
||||
// Set this context in ImGui for initialization (any allocations will be tracked in this context).
|
||||
SetAsCurrent();
|
||||
@ -55,22 +90,12 @@ FImGuiContextProxy::FImGuiContextProxy(const FString& InName)
|
||||
// Set session data storage.
|
||||
IO.IniFilename = IniFilename.c_str();
|
||||
|
||||
// Use pre-defined canvas size.
|
||||
IO.DisplaySize = { DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT };
|
||||
// Start with the default canvas size.
|
||||
ResetDisplaySize();
|
||||
IO.DisplaySize = {(float)DisplaySize.X, (float)DisplaySize.Y};
|
||||
|
||||
// When GetTexData is called for the first time it builds atlas texture and copies mouse cursor data to context.
|
||||
// When multiple contexts share atlas then only the first one will get mouse data. A simple workaround is to use
|
||||
// a temporary atlas if shared one is already built.
|
||||
unsigned char* Pixels;
|
||||
const bool bIsAltasBuilt = IO.Fonts->TexPixelsAlpha8 != nullptr;
|
||||
if (bIsAltasBuilt)
|
||||
{
|
||||
ImFontAtlas().GetTexDataAsRGBA32(&Pixels, nullptr, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
IO.Fonts->GetTexDataAsRGBA32(&Pixels, nullptr, nullptr);
|
||||
}
|
||||
// Set the initial DPI scale.
|
||||
SetDPIScale(InDPIScale);
|
||||
|
||||
// Initialize key mapping, so context can correctly interpret input state.
|
||||
ImGuiInterops::SetUnrealKeyMap(IO);
|
||||
@ -80,75 +105,100 @@ FImGuiContextProxy::FImGuiContextProxy(const FString& InName)
|
||||
BeginFrame();
|
||||
}
|
||||
|
||||
FImGuiContextProxy::FImGuiContextProxy(FImGuiContextProxy&& Other)
|
||||
: Context(std::move(Other.Context))
|
||||
, bHasActiveItem(Other.bHasActiveItem)
|
||||
, DrawEvent(std::move(Other.DrawEvent))
|
||||
, InputState(std::move(Other.InputState))
|
||||
, DrawLists(std::move(Other.DrawLists))
|
||||
, Name(std::move(Other.Name))
|
||||
, IniFilename(std::move(Other.IniFilename))
|
||||
{
|
||||
Other.Context = nullptr;
|
||||
}
|
||||
|
||||
FImGuiContextProxy& FImGuiContextProxy::operator=(FImGuiContextProxy&& Other)
|
||||
{
|
||||
// Swapping context so it can be destroyed with the other object.
|
||||
using std::swap;
|
||||
swap(Context, Other.Context);
|
||||
|
||||
// Just moving remaining data that doesn't affect cleanup.
|
||||
bHasActiveItem = Other.bHasActiveItem;
|
||||
DrawEvent = std::move(Other.DrawEvent);
|
||||
InputState = std::move(Other.InputState);
|
||||
DrawLists = std::move(Other.DrawLists);
|
||||
Name = std::move(Other.Name);
|
||||
IniFilename = std::move(Other.IniFilename);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
FImGuiContextProxy::~FImGuiContextProxy()
|
||||
{
|
||||
if (Context)
|
||||
{
|
||||
// Set this context in ImGui for de-initialization (any de-allocations will be tracked in this context).
|
||||
// It seems that to properly shutdown context we need to set it as the current one (at least in this framework
|
||||
// version), even though we can pass it to the destroy function.
|
||||
SetAsCurrent();
|
||||
|
||||
// Save context data and destroy.
|
||||
ImGuiImplementation::SaveCurrentContextIniSettings(IniFilename.c_str());
|
||||
ImGui::DestroyContext(Context);
|
||||
|
||||
// Set default context in ImGui to keep global context pointer valid.
|
||||
ImGui::SetCurrentContext(&ImGuiImplementation::GetDefaultContext());
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiContextProxy::Tick(float DeltaSeconds, FSimpleMulticastDelegate* SharedDrawEvent)
|
||||
void FImGuiContextProxy::ResetDisplaySize()
|
||||
{
|
||||
DisplaySize = { DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT };
|
||||
}
|
||||
|
||||
void FImGuiContextProxy::SetDPIScale(float Scale)
|
||||
{
|
||||
if (DPIScale != Scale)
|
||||
{
|
||||
DPIScale = Scale;
|
||||
|
||||
ImGuiStyle NewStyle = ImGuiStyle();
|
||||
NewStyle.ScaleAllSizes(Scale);
|
||||
|
||||
FGuardCurrentContext GuardContext;
|
||||
SetAsCurrent();
|
||||
ImGui::GetStyle() = MoveTemp(NewStyle);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiContextProxy::DrawEarlyDebug()
|
||||
{
|
||||
if (bIsFrameStarted && !bIsDrawEarlyDebugCalled)
|
||||
{
|
||||
bIsDrawEarlyDebugCalled = true;
|
||||
|
||||
SetAsCurrent();
|
||||
|
||||
// Delegates called in order specified in FImGuiDelegates.
|
||||
BroadcastMultiContextEarlyDebug();
|
||||
BroadcastWorldEarlyDebug();
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiContextProxy::DrawDebug()
|
||||
{
|
||||
if (bIsFrameStarted && !bIsDrawDebugCalled)
|
||||
{
|
||||
bIsDrawDebugCalled = true;
|
||||
|
||||
// Make sure that early debug is always called first to guarantee order specified in FImGuiDelegates.
|
||||
DrawEarlyDebug();
|
||||
|
||||
SetAsCurrent();
|
||||
|
||||
// Delegates called in order specified in FImGuiDelegates.
|
||||
BroadcastWorldDebug();
|
||||
BroadcastMultiContextDebug();
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiContextProxy::Tick(float DeltaSeconds)
|
||||
{
|
||||
// Making sure that we tick only once per frame.
|
||||
if (LastFrameNumber < GFrameNumber)
|
||||
{
|
||||
LastFrameNumber = GFrameNumber;
|
||||
|
||||
SetAsCurrent();
|
||||
|
||||
if (bIsFrameStarted)
|
||||
{
|
||||
// Broadcast draw event to allow listeners to draw their controls to this context.
|
||||
if (DrawEvent.IsBound())
|
||||
{
|
||||
DrawEvent.Broadcast();
|
||||
}
|
||||
if (SharedDrawEvent && SharedDrawEvent->IsBound())
|
||||
{
|
||||
SharedDrawEvent->Broadcast();
|
||||
}
|
||||
// Make sure that draw events are called before the end of the frame.
|
||||
DrawDebug();
|
||||
|
||||
// Ending frame will produce render output that we capture and store for later use. This also puts context to
|
||||
// state in which it does not allow to draw controls, so we want to immediately start a new frame.
|
||||
EndFrame();
|
||||
}
|
||||
|
||||
// Update context information (some data need to be collected before starting a new frame while some other data
|
||||
// may need to be collected after).
|
||||
bHasActiveItem = ImGui::IsAnyItemActive();
|
||||
MouseCursor = ImGuiInterops::ToSlateMouseCursor(ImGui::GetMouseCursor());
|
||||
|
||||
// Begin a new frame and set the context back to a state in which it allows to draw controls.
|
||||
BeginFrame(DeltaSeconds);
|
||||
|
||||
// Update context information.
|
||||
bHasActiveItem = ImGui::IsAnyItemActive();
|
||||
// Update remaining context information.
|
||||
bWantsMouseCapture = ImGui::GetIO().WantCaptureMouse;
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiContextProxy::BeginFrame(float DeltaTime)
|
||||
@ -158,14 +208,16 @@ void FImGuiContextProxy::BeginFrame(float DeltaTime)
|
||||
ImGuiIO& IO = ImGui::GetIO();
|
||||
IO.DeltaTime = DeltaTime;
|
||||
|
||||
if (InputState)
|
||||
{
|
||||
ImGuiInterops::CopyInput(IO, *InputState);
|
||||
}
|
||||
ImGuiInterops::CopyInput(IO, InputState);
|
||||
InputState.ClearUpdateState();
|
||||
|
||||
IO.DisplaySize = { (float)DisplaySize.X, (float)DisplaySize.Y };
|
||||
|
||||
ImGui::NewFrame();
|
||||
|
||||
bIsFrameStarted = true;
|
||||
bIsDrawEarlyDebugCalled = false;
|
||||
bIsDrawDebugCalled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,3 +253,50 @@ void FImGuiContextProxy::UpdateDrawData(ImDrawData* DrawData)
|
||||
DrawLists.Empty();
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiContextProxy::BroadcastWorldEarlyDebug()
|
||||
{
|
||||
if (ContextIndex != Utilities::INVALID_CONTEXT_INDEX)
|
||||
{
|
||||
FSimpleMulticastDelegate& WorldEarlyDebugEvent = FImGuiDelegatesContainer::Get().OnWorldEarlyDebug(ContextIndex);
|
||||
if (WorldEarlyDebugEvent.IsBound())
|
||||
{
|
||||
WorldEarlyDebugEvent.Broadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiContextProxy::BroadcastMultiContextEarlyDebug()
|
||||
{
|
||||
FSimpleMulticastDelegate& MultiContextEarlyDebugEvent = FImGuiDelegatesContainer::Get().OnMultiContextEarlyDebug();
|
||||
if (MultiContextEarlyDebugEvent.IsBound())
|
||||
{
|
||||
MultiContextEarlyDebugEvent.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiContextProxy::BroadcastWorldDebug()
|
||||
{
|
||||
if (DrawEvent.IsBound())
|
||||
{
|
||||
DrawEvent.Broadcast();
|
||||
}
|
||||
|
||||
if (ContextIndex != Utilities::INVALID_CONTEXT_INDEX)
|
||||
{
|
||||
FSimpleMulticastDelegate& WorldDebugEvent = FImGuiDelegatesContainer::Get().OnWorldDebug(ContextIndex);
|
||||
if (WorldDebugEvent.IsBound())
|
||||
{
|
||||
WorldDebugEvent.Broadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiContextProxy::BroadcastMultiContextDebug()
|
||||
{
|
||||
FSimpleMulticastDelegate& MultiContextDebugEvent = FImGuiDelegatesContainer::Get().OnMultiContextDebug();
|
||||
if (MultiContextDebugEvent.IsBound())
|
||||
{
|
||||
MultiContextDebugEvent.Broadcast();
|
||||
}
|
||||
}
|
||||
|
@ -3,28 +3,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "ImGuiDrawData.h"
|
||||
#include "ImGuiInputState.h"
|
||||
#include "Utilities/WorldContextIndex.h"
|
||||
|
||||
#include <GenericPlatform/ICursor.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
class FImGuiInputState;
|
||||
|
||||
// Represents a single ImGui context. All the context updates should be done through this proxy. During update it
|
||||
// broadcasts draw events to allow listeners draw their controls. After update it stores draw data.
|
||||
class FImGuiContextProxy
|
||||
{
|
||||
public:
|
||||
|
||||
FImGuiContextProxy(const FString& Name);
|
||||
FImGuiContextProxy(const FString& Name, int32 InContextIndex, ImFontAtlas* InFontAtlas, float InDPIScale);
|
||||
~FImGuiContextProxy();
|
||||
|
||||
FImGuiContextProxy(const FImGuiContextProxy&) = delete;
|
||||
FImGuiContextProxy& operator=(const FImGuiContextProxy&) = delete;
|
||||
|
||||
FImGuiContextProxy(FImGuiContextProxy&& Other);
|
||||
FImGuiContextProxy& operator=(FImGuiContextProxy&& Other);
|
||||
FImGuiContextProxy(FImGuiContextProxy&&) = delete;
|
||||
FImGuiContextProxy& operator=(FImGuiContextProxy&&) = delete;
|
||||
|
||||
// Get the name of this context.
|
||||
const FString& GetName() const { return Name; }
|
||||
@ -33,13 +35,8 @@ public:
|
||||
const TArray<FImGuiDrawList>& GetDrawData() const { return DrawLists; }
|
||||
|
||||
// Get input state used by this context.
|
||||
const FImGuiInputState* GetInputState() const { return InputState; }
|
||||
|
||||
// Set input state to be used by this context.
|
||||
void SetInputState(const FImGuiInputState* SourceInputState) { InputState = SourceInputState; }
|
||||
|
||||
// If context is currently using input state to remove then remove that binding.
|
||||
void RemoveInputState(const FImGuiInputState* InputStateToRemove) { if (InputState == InputStateToRemove) InputState = nullptr; }
|
||||
FImGuiInputState& GetInputState() { return InputState; }
|
||||
const FImGuiInputState& GetInputState() const { return InputState; }
|
||||
|
||||
// Is this context the current ImGui context.
|
||||
bool IsCurrentContext() const { return ImGui::GetCurrentContext() == Context; }
|
||||
@ -47,14 +44,42 @@ public:
|
||||
// Set this context as current ImGui context.
|
||||
void SetAsCurrent() { ImGui::SetCurrentContext(Context); }
|
||||
|
||||
// Get the desired context display size.
|
||||
const FVector2D& GetDisplaySize() const { return DisplaySize; }
|
||||
|
||||
// Set the desired context display size.
|
||||
void SetDisplaySize(const FVector2D& Size) { DisplaySize = Size; }
|
||||
|
||||
// Reset the desired context display size to default size.
|
||||
void ResetDisplaySize();
|
||||
|
||||
// Get the DPI scale set for this context.
|
||||
float GetDPIScale() const { return DPIScale; }
|
||||
|
||||
// Set the DPI scale for this context.
|
||||
void SetDPIScale(float Scale);
|
||||
|
||||
// Whether this context has an active item (read once per frame during context update).
|
||||
bool HasActiveItem() const { return bHasActiveItem; }
|
||||
|
||||
// Delegate called right before ending the frame to allows listeners draw their controls.
|
||||
// Whether ImGui will use the mouse inputs (read once per frame during context update).
|
||||
bool WantsMouseCapture() const { return bWantsMouseCapture; }
|
||||
|
||||
// Cursor type desired by this context (updated once per frame during context update).
|
||||
EMouseCursor::Type GetMouseCursor() const { return MouseCursor; }
|
||||
|
||||
// Internal draw event used to draw module's examples and debug widgets. Unlike the delegates container, it is not
|
||||
// passed when the module is reloaded, so all objects that are unloaded with the module should register here.
|
||||
FSimpleMulticastDelegate& OnDraw() { return DrawEvent; }
|
||||
|
||||
// Tick to advance context to the next frame.
|
||||
// @param SharedDrawEvent - Shared draw event provided from outside to be called right after context own event
|
||||
void Tick(float DeltaSeconds, FSimpleMulticastDelegate* SharedDrawEvent = nullptr);
|
||||
// Call early debug events to allow listeners draw their debug widgets.
|
||||
void DrawEarlyDebug();
|
||||
|
||||
// Call debug events to allow listeners draw their debug widgets.
|
||||
void DrawDebug();
|
||||
|
||||
// Tick to advance context to the next frame. Only one call per frame will be processed.
|
||||
void Tick(float DeltaSeconds);
|
||||
|
||||
private:
|
||||
|
||||
@ -63,16 +88,35 @@ private:
|
||||
|
||||
void UpdateDrawData(ImDrawData* DrawData);
|
||||
|
||||
ImGuiContext* Context = nullptr;
|
||||
void BroadcastWorldEarlyDebug();
|
||||
void BroadcastMultiContextEarlyDebug();
|
||||
|
||||
void BroadcastWorldDebug();
|
||||
void BroadcastMultiContextDebug();
|
||||
|
||||
ImGuiContext* Context;
|
||||
|
||||
FVector2D DisplaySize = FVector2D::ZeroVector;
|
||||
float DPIScale = 1.f;
|
||||
|
||||
EMouseCursor::Type MouseCursor = EMouseCursor::None;
|
||||
bool bHasActiveItem = false;
|
||||
bool bWantsMouseCapture = false;
|
||||
|
||||
bool bIsFrameStarted = false;
|
||||
FSimpleMulticastDelegate DrawEvent;
|
||||
const FImGuiInputState* InputState = nullptr;
|
||||
bool bIsDrawEarlyDebugCalled = false;
|
||||
bool bIsDrawDebugCalled = false;
|
||||
|
||||
FImGuiInputState InputState;
|
||||
|
||||
TArray<FImGuiDrawList> DrawLists;
|
||||
|
||||
FString Name;
|
||||
int32 ContextIndex = Utilities::INVALID_CONTEXT_INDEX;
|
||||
|
||||
uint32 LastFrameNumber = 0;
|
||||
|
||||
FSimpleMulticastDelegate DrawEvent;
|
||||
|
||||
std::string IniFilename;
|
||||
};
|
||||
|
37
Source/ImGui/Private/ImGuiDelegates.cpp
Normal file
37
Source/ImGui/Private/ImGuiDelegates.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiDelegates.h"
|
||||
#include "ImGuiDelegatesContainer.h"
|
||||
|
||||
#include <Engine/World.h>
|
||||
|
||||
|
||||
FSimpleMulticastDelegate& FImGuiDelegates::OnWorldEarlyDebug()
|
||||
{
|
||||
return OnWorldEarlyDebug(GWorld);
|
||||
}
|
||||
|
||||
FSimpleMulticastDelegate& FImGuiDelegates::OnWorldEarlyDebug(UWorld* World)
|
||||
{
|
||||
return FImGuiDelegatesContainer::Get().OnWorldEarlyDebug(World);
|
||||
}
|
||||
|
||||
FSimpleMulticastDelegate& FImGuiDelegates::OnMultiContextEarlyDebug()
|
||||
{
|
||||
return FImGuiDelegatesContainer::Get().OnMultiContextEarlyDebug();
|
||||
}
|
||||
|
||||
FSimpleMulticastDelegate& FImGuiDelegates::OnWorldDebug()
|
||||
{
|
||||
return OnWorldDebug(GWorld);
|
||||
}
|
||||
|
||||
FSimpleMulticastDelegate& FImGuiDelegates::OnWorldDebug(UWorld* World)
|
||||
{
|
||||
return FImGuiDelegatesContainer::Get().OnWorldDebug(World);
|
||||
}
|
||||
|
||||
FSimpleMulticastDelegate& FImGuiDelegates::OnMultiContextDebug()
|
||||
{
|
||||
return FImGuiDelegatesContainer::Get().OnMultiContextDebug();
|
||||
}
|
88
Source/ImGui/Private/ImGuiDelegatesContainer.cpp
Normal file
88
Source/ImGui/Private/ImGuiDelegatesContainer.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiDelegatesContainer.h"
|
||||
|
||||
#include "ImGuiModule.h"
|
||||
#include "Utilities/WorldContextIndex.h"
|
||||
|
||||
|
||||
#if !WITH_EDITOR
|
||||
//
|
||||
// Non-editor version without container redirection
|
||||
//
|
||||
|
||||
FImGuiDelegatesContainer& FImGuiDelegatesContainer::Get()
|
||||
{
|
||||
static FImGuiDelegatesContainer* Container = new FImGuiDelegatesContainer();
|
||||
return *Container;
|
||||
}
|
||||
|
||||
#endif // !WITH_EDITOR
|
||||
|
||||
|
||||
#if WITH_EDITOR
|
||||
//
|
||||
// Editor version supporting container redirection needed for hot-reloading
|
||||
//
|
||||
|
||||
#include "Utilities/RedirectingHandle.h"
|
||||
|
||||
// Redirecting handle which will always bind to a container from the currently loaded module.
|
||||
struct FImGuiDelegatesContainerHandle : Utilities::TRedirectingHandle<FImGuiDelegatesContainer>
|
||||
{
|
||||
FImGuiDelegatesContainerHandle(FImGuiDelegatesContainer& InDefaultContainer)
|
||||
: Utilities::TRedirectingHandle<FImGuiDelegatesContainer>(InDefaultContainer)
|
||||
{
|
||||
if (FImGuiModule* Module = FModuleManager::GetModulePtr<FImGuiModule>("ImGui"))
|
||||
{
|
||||
SetParent(Module->DelegatesContainerHandle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FImGuiDelegatesContainer& FImGuiDelegatesContainer::Get()
|
||||
{
|
||||
return GetHandle().Get();
|
||||
}
|
||||
|
||||
FImGuiDelegatesContainerHandle& FImGuiDelegatesContainer::GetHandle()
|
||||
{
|
||||
struct FContainerInstance
|
||||
{
|
||||
FContainerInstance() : Container(), Handle(Container) {}
|
||||
FImGuiDelegatesContainer Container;
|
||||
FImGuiDelegatesContainerHandle Handle;
|
||||
};
|
||||
static FContainerInstance* Instance = new FContainerInstance();
|
||||
return Instance->Handle;
|
||||
}
|
||||
|
||||
void FImGuiDelegatesContainer::MoveContainer(FImGuiDelegatesContainerHandle& OtherContainerHandle)
|
||||
{
|
||||
// Only move data if pointer points to default instance, otherwise our data has already been moved and we only
|
||||
// keep pointer to a more recent version.
|
||||
if (GetHandle().IsDefault())
|
||||
{
|
||||
OtherContainerHandle.Get() = MoveTemp(GetHandle().Get());
|
||||
GetHandle().Get().Clear();
|
||||
}
|
||||
|
||||
// Update pointer to the most recent version.
|
||||
GetHandle().SetParent(&OtherContainerHandle);
|
||||
}
|
||||
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
|
||||
int32 FImGuiDelegatesContainer::GetContextIndex(UWorld* World)
|
||||
{
|
||||
return Utilities::GetWorldContextIndex(*World);
|
||||
}
|
||||
|
||||
void FImGuiDelegatesContainer::Clear()
|
||||
{
|
||||
WorldEarlyDebugDelegates.Empty();
|
||||
WorldDebugDelegates.Empty();
|
||||
MultiContextEarlyDebugDelegate.Clear();
|
||||
MultiContextDebugDelegate.Clear();
|
||||
}
|
56
Source/ImGui/Private/ImGuiDelegatesContainer.h
Normal file
56
Source/ImGui/Private/ImGuiDelegatesContainer.h
Normal file
@ -0,0 +1,56 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Containers/Map.h>
|
||||
#include <Delegates/Delegate.h>
|
||||
|
||||
|
||||
#if WITH_EDITOR
|
||||
struct FImGuiDelegatesContainerHandle;
|
||||
#endif
|
||||
|
||||
struct FImGuiDelegatesContainer
|
||||
{
|
||||
public:
|
||||
|
||||
// Get the current instance (can change during hot-reloading).
|
||||
static FImGuiDelegatesContainer& Get();
|
||||
|
||||
#if WITH_EDITOR
|
||||
// Get the handle to the container instance (can attach to other handles in hot-reloaded modules).
|
||||
static FImGuiDelegatesContainerHandle& GetHandle();
|
||||
|
||||
// Redirect to the other container and if this one is still active move its data to the other one.
|
||||
static void MoveContainer(FImGuiDelegatesContainerHandle& OtherContainerHandle);
|
||||
#endif
|
||||
|
||||
// Get delegate to ImGui world early debug event from known world instance.
|
||||
FSimpleMulticastDelegate& OnWorldEarlyDebug(UWorld* World) { return OnWorldEarlyDebug(GetContextIndex(World)); }
|
||||
|
||||
// Get delegate to ImGui world early debug event from known context index.
|
||||
FSimpleMulticastDelegate& OnWorldEarlyDebug(int32 ContextIndex) { return WorldEarlyDebugDelegates.FindOrAdd(ContextIndex); }
|
||||
|
||||
// Get delegate to ImGui multi-context early debug event.
|
||||
FSimpleMulticastDelegate& OnMultiContextEarlyDebug() { return MultiContextEarlyDebugDelegate; }
|
||||
|
||||
// Get delegate to ImGui world debug event from known world instance.
|
||||
FSimpleMulticastDelegate& OnWorldDebug(UWorld* World) { return OnWorldDebug(GetContextIndex(World)); }
|
||||
|
||||
// Get delegate to ImGui world debug event from known context index.
|
||||
FSimpleMulticastDelegate& OnWorldDebug(int32 ContextIndex) { return WorldDebugDelegates.FindOrAdd(ContextIndex); }
|
||||
|
||||
// Get delegate to ImGui multi-context debug event.
|
||||
FSimpleMulticastDelegate& OnMultiContextDebug() { return MultiContextDebugDelegate; }
|
||||
|
||||
private:
|
||||
|
||||
int32 GetContextIndex(UWorld* World);
|
||||
|
||||
void Clear();
|
||||
|
||||
TMap<int32, FSimpleMulticastDelegate> WorldEarlyDebugDelegates;
|
||||
TMap<int32, FSimpleMulticastDelegate> WorldDebugDelegates;
|
||||
FSimpleMulticastDelegate MultiContextEarlyDebugDelegate;
|
||||
FSimpleMulticastDelegate MultiContextDebugDelegate;
|
||||
};
|
@ -1,25 +1,19 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
|
||||
#include "ImGuiDemo.h"
|
||||
#include "ImGuiModuleManager.h"
|
||||
|
||||
#include "ImGuiModuleProperties.h"
|
||||
|
||||
#include <CoreGlobals.h>
|
||||
|
||||
|
||||
namespace CVars
|
||||
// Demo copied (with minor modifications) from ImGui examples. See https://github.com/ocornut/imgui.
|
||||
void FImGuiDemo::DrawControls(int32 ContextIndex)
|
||||
{
|
||||
TAutoConsoleVariable<int> ShowDemo(TEXT("ImGui.ShowDemo"), 0,
|
||||
TEXT("Show ImGui demo.\n")
|
||||
TEXT("0: disabled (default)\n")
|
||||
TEXT("1: enabled."),
|
||||
ECVF_Default);
|
||||
}
|
||||
|
||||
// Demo copied from ImGui examples. See https://github.com/ocornut/imgui.
|
||||
void FImGuiDemo::DrawControls()
|
||||
{
|
||||
if (CVars::ShowDemo.GetValueOnGameThread() > 0)
|
||||
if (Properties.ShowDemo())
|
||||
{
|
||||
const int32 ContextBit = ContextIndex < 0 ? 0 : 1 << ContextIndex;
|
||||
|
||||
// 1. Show a simple window
|
||||
// Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets appears in a window automatically called "Debug"
|
||||
{
|
||||
@ -27,25 +21,63 @@ void FImGuiDemo::DrawControls()
|
||||
ImGui::Text("Hello, world!");
|
||||
ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
|
||||
ImGui::ColorEdit3("clear color", (float*)&ClearColor);
|
||||
if (ImGui::Button("Test Window")) bDemoShowTestWindow = !bDemoShowTestWindow;
|
||||
if (ImGui::Button("Another Window")) bDemoShowAnotherTestWindow = !bDemoShowAnotherTestWindow;
|
||||
|
||||
if (ContextBit)
|
||||
{
|
||||
if (ImGui::Button("Demo Window")) ShowDemoWindowMask ^= ContextBit;
|
||||
if (ImGui::Button("Another Window")) ShowAnotherWindowMask ^= ContextBit;
|
||||
}
|
||||
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
|
||||
}
|
||||
|
||||
// 2. Show another simple window, this time using an explicit Begin/End pair
|
||||
if (bDemoShowAnotherTestWindow)
|
||||
if (ShowAnotherWindowMask & ContextBit)
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(200, 100), ImGuiSetCond_FirstUseEver);
|
||||
ImGui::Begin("Another Window", &bDemoShowAnotherTestWindow);
|
||||
ImGui::SetNextWindowSize(ImVec2(200, 100), ImGuiCond_FirstUseEver);
|
||||
ImGui::Begin("Another Window");
|
||||
ImGui::Text("Hello");
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowTestWindow()
|
||||
if (bDemoShowTestWindow)
|
||||
if (ShowDemoWindowMask & ContextBit)
|
||||
{
|
||||
ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiSetCond_FirstUseEver);
|
||||
ImGui::ShowTestWindow(&bDemoShowTestWindow);
|
||||
// If more than one demo window is opened display warning about running ImGui examples in multiple contexts.
|
||||
|
||||
// For everything, but the first windows in this frame we assume warning.
|
||||
bool bWarning = true;
|
||||
if (GFrameNumber > LastDemoWindowFrameNumber)
|
||||
{
|
||||
// If this is the first window in this frame, then we need to look at the last frame to see whether
|
||||
// there were more than one windows. Higher frame distance automatically means that there were not.
|
||||
bWarning = ((GFrameNumber - LastDemoWindowFrameNumber) == 1) && (DemoWindowCounter > 1);
|
||||
|
||||
LastDemoWindowFrameNumber = GFrameNumber;
|
||||
DemoWindowCounter = 0;
|
||||
}
|
||||
|
||||
DemoWindowCounter++;
|
||||
|
||||
if (bWarning)
|
||||
{
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, { 1.f, 1.f, 0.5f, 1.f });
|
||||
ImGui::TextWrapped("Demo Window is opened in more than one context, some of the ImGui examples may not work correctly.");
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetTooltip(
|
||||
"Some of the ImGui examples that use static variables may not work correctly\n"
|
||||
"when run concurrently in multiple contexts.\n"
|
||||
"If you have a problem with an example try to run it in one context only.");
|
||||
}
|
||||
}
|
||||
|
||||
// Draw demo window.
|
||||
ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver);
|
||||
ImGui::ShowDemoWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,21 +2,33 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <CoreMinimal.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
class FImGuiModuleProperties;
|
||||
|
||||
// Widget drawing ImGui demo.
|
||||
class FImGuiDemo
|
||||
{
|
||||
public:
|
||||
|
||||
void DrawControls();
|
||||
FImGuiDemo(FImGuiModuleProperties& InProperties)
|
||||
: Properties(InProperties)
|
||||
{
|
||||
}
|
||||
|
||||
void DrawControls(int32 ContextIndex);
|
||||
|
||||
private:
|
||||
|
||||
FImGuiModuleProperties& Properties;
|
||||
|
||||
ImVec4 ClearColor = ImColor{ 114, 144, 154 };
|
||||
|
||||
bool bShowDemo = false;
|
||||
bool bDemoShowTestWindow = true;
|
||||
bool bDemoShowAnotherTestWindow = false;
|
||||
int32 ShowDemoWindowMask = 0;
|
||||
int32 ShowAnotherWindowMask = 0;
|
||||
|
||||
int32 DemoWindowCounter = 0;
|
||||
uint32 LastDemoWindowFrameNumber = 0;
|
||||
};
|
||||
|
@ -1,15 +1,13 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
|
||||
#include "ImGuiDrawData.h"
|
||||
|
||||
|
||||
#if WITH_OBSOLETE_CLIPPING_API
|
||||
void FImGuiDrawList::CopyVertexData(TArray<FSlateVertex>& OutVertexBuffer, const FVector2D VertexPositionOffset, const FSlateRotatedRect& VertexClippingRect) const
|
||||
#if ENGINE_COMPATIBILITY_LEGACY_CLIPPING_API
|
||||
void FImGuiDrawList::CopyVertexData(TArray<FSlateVertex>& OutVertexBuffer, const FTransform2D& Transform, const FSlateRotatedRect& VertexClippingRect) const
|
||||
#else
|
||||
void FImGuiDrawList::CopyVertexData(TArray<FSlateVertex>& OutVertexBuffer, const FVector2D VertexPositionOffset) const
|
||||
#endif // WITH_OBSOLETE_CLIPPING_API
|
||||
void FImGuiDrawList::CopyVertexData(TArray<FSlateVertex>& OutVertexBuffer, const FTransform2D& Transform) const
|
||||
#endif // ENGINE_COMPATIBILITY_LEGACY_CLIPPING_API
|
||||
{
|
||||
// Reset and reserve space in destination buffer.
|
||||
OutVertexBuffer.SetNumUninitialized(ImGuiVertexBuffer.Size, false);
|
||||
@ -25,14 +23,18 @@ void FImGuiDrawList::CopyVertexData(TArray<FSlateVertex>& OutVertexBuffer, const
|
||||
SlateVertex.TexCoords[1] = ImGuiVertex.uv.y;
|
||||
SlateVertex.TexCoords[2] = SlateVertex.TexCoords[3] = 1.f;
|
||||
|
||||
// Copy ImGui position and add offset.
|
||||
SlateVertex.Position[0] = ImGuiVertex.pos.x + VertexPositionOffset.X;
|
||||
SlateVertex.Position[1] = ImGuiVertex.pos.y + VertexPositionOffset.Y;
|
||||
|
||||
#if WITH_OBSOLETE_CLIPPING_API
|
||||
// Set clipping rectangle.
|
||||
#if ENGINE_COMPATIBILITY_LEGACY_CLIPPING_API
|
||||
const FVector2D VertexPosition = Transform.TransformPoint(ImGuiInterops::ToVector2D(ImGuiVertex.pos));
|
||||
SlateVertex.Position[0] = VertexPosition.X;
|
||||
SlateVertex.Position[1] = VertexPosition.Y;
|
||||
SlateVertex.ClipRect = VertexClippingRect;
|
||||
#endif // WITH_OBSOLETE_CLIPPING_API
|
||||
#else
|
||||
#if ENGINE_COMPATIBILITY_LEGACY_VECTOR2F
|
||||
SlateVertex.Position = Transform.TransformPoint(ImGuiInterops::ToVector2D(ImGuiVertex.pos));
|
||||
#else
|
||||
SlateVertex.Position = (FVector2f)Transform.TransformPoint(ImGuiInterops::ToVector2D(ImGuiVertex.pos));
|
||||
#endif // ENGINE_COMPATIBILITY_LEGACY_VECTOR2F
|
||||
#endif // ENGINE_COMPATIBILITY_LEGACY_CLIPPING_API
|
||||
|
||||
// Unpack ImU32 color.
|
||||
SlateVertex.Color = ImGuiInterops::UnpackImU32Color(ImGuiVertex.col);
|
||||
@ -58,8 +60,4 @@ void FImGuiDrawList::TransferDrawData(ImDrawList& Src)
|
||||
Src.CmdBuffer.swap(ImGuiCommandBuffer);
|
||||
Src.IdxBuffer.swap(ImGuiIndexBuffer);
|
||||
Src.VtxBuffer.swap(ImGuiVertexBuffer);
|
||||
|
||||
// ImGui seems to clear draw lists in every frame, but since source list can contain pointers to buffers that
|
||||
// we just swapped, it is better to clear explicitly here.
|
||||
Src.Clear();
|
||||
}
|
||||
|
@ -3,17 +3,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "ImGuiInteroperability.h"
|
||||
#include "VersionCompatibility.h"
|
||||
|
||||
#include <Runtime/Launch/Resources/Version.h>
|
||||
#include <SlateCore.h>
|
||||
#include <Rendering/RenderingCommon.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
|
||||
// Starting from version 4.17 Slate doesn't have per-vertex clipping rectangle and GSlateScissorRect. Use this to
|
||||
// support older engine versions.
|
||||
#define WITH_OBSOLETE_CLIPPING_API (ENGINE_MAJOR_VERSION < 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION < 17))
|
||||
|
||||
// ImGui draw command data transformed for Slate.
|
||||
struct FImGuiDrawCommand
|
||||
{
|
||||
@ -32,25 +28,27 @@ public:
|
||||
|
||||
// Get the draw command by number.
|
||||
// @param CommandNb - Number of draw command
|
||||
// @param Transform - Transform to apply to clipping rectangle
|
||||
// @returns Draw command data
|
||||
FORCEINLINE FImGuiDrawCommand GetCommand(int CommandNb) const
|
||||
FImGuiDrawCommand GetCommand(int CommandNb, const FTransform2D& Transform) const
|
||||
{
|
||||
const ImDrawCmd& ImGuiCommand = ImGuiCommandBuffer[CommandNb];
|
||||
return{ ImGuiCommand.ElemCount, ImGuiInterops::ToSlateRect(ImGuiCommand.ClipRect), ImGuiInterops::ToTextureIndex(ImGuiCommand.TextureId) };
|
||||
return { ImGuiCommand.ElemCount, TransformRect(Transform, ImGuiInterops::ToSlateRect(ImGuiCommand.ClipRect)),
|
||||
ImGuiInterops::ToTextureIndex(ImGuiCommand.TextureId) };
|
||||
}
|
||||
|
||||
#if WITH_OBSOLETE_CLIPPING_API
|
||||
#if ENGINE_COMPATIBILITY_LEGACY_CLIPPING_API
|
||||
// Transform and copy vertex data to target buffer (old data in the target buffer are replaced).
|
||||
// @param OutVertexBuffer - Destination buffer
|
||||
// @param VertexPositionOffset - Position offset added to every vertex to transform it to different space
|
||||
// @param VertexClippingRect - Clipping rectangle for Slate vertices
|
||||
void CopyVertexData(TArray<FSlateVertex>& OutVertexBuffer, const FVector2D VertexPositionOffset, const FSlateRotatedRect& VertexClippingRect) const;
|
||||
// @param Transform - Transform to apply to all vertices
|
||||
// @param VertexClippingRect - Clipping rectangle for transformed Slate vertices
|
||||
void CopyVertexData(TArray<FSlateVertex>& OutVertexBuffer, const FTransform2D& Transform, const FSlateRotatedRect& VertexClippingRect) const;
|
||||
#else
|
||||
// Transform and copy vertex data to target buffer (old data in the target buffer are replaced).
|
||||
// @param OutVertexBuffer - Destination buffer
|
||||
// @param VertexPositionOffset - Position offset added to every vertex to transform it to different space
|
||||
void CopyVertexData(TArray<FSlateVertex>& OutVertexBuffer, const FVector2D VertexPositionOffset) const;
|
||||
#endif // WITH_OBSOLETE_CLIPPING_API
|
||||
// @param Transform - Transform to apply to all vertices
|
||||
void CopyVertexData(TArray<FSlateVertex>& OutVertexBuffer, const FTransform2D& Transform) const;
|
||||
#endif // ENGINE_COMPATIBILITY_LEGACY_CLIPPING_API
|
||||
|
||||
// Transform and copy index data to target buffer (old data in the target buffer are replaced).
|
||||
// Internal index buffer contains enough data to match the sum of NumElements from all draw commands.
|
||||
|
@ -1,38 +1,75 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
#include "ImGuiImplementation.h"
|
||||
|
||||
// We build ImGui source code as part of this module. This is for convenience (no need to manually build libraries for
|
||||
// different target platforms) but it also exposes the whole ImGui source for inspection, which can be pretty handy.
|
||||
// Source files are included from Third Party directory, so we can wrap them in required by Unreal Build System headers
|
||||
// without modifications in ImGui source code.
|
||||
//
|
||||
#include <CoreMinimal.h>
|
||||
|
||||
// For convenience and easy access to the ImGui source code, we build it as part of this module.
|
||||
// We don't need to define IMGUI_API manually because it is already done for this module.
|
||||
// UE 5.1 stopped defining PLATFORM_XBOXONE, so be safe if not defined
|
||||
#if !defined(PLATFORM_XBOXONE)
|
||||
#define PLATFORM_XBOXONE 0
|
||||
#endif
|
||||
#if PLATFORM_XBOXONE
|
||||
// Disable Win32 functions used in ImGui and not supported on XBox.
|
||||
#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
|
||||
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
|
||||
#endif // PLATFORM_XBOXONE
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include <AllowWindowsPlatformTypes.h>
|
||||
#include <Windows/AllowWindowsPlatformTypes.h>
|
||||
#endif // PLATFORM_WINDOWS
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
#include "ImGuiModule.h"
|
||||
#include "Utilities/RedirectingHandle.h"
|
||||
|
||||
// Redirecting handle which will automatically bind to another one, if a different instance of the module is loaded.
|
||||
struct FImGuiContextHandle : public Utilities::TRedirectingHandle<ImGuiContext*>
|
||||
{
|
||||
FImGuiContextHandle(ImGuiContext*& InDefaultContext)
|
||||
: Utilities::TRedirectingHandle<ImGuiContext*>(InDefaultContext)
|
||||
{
|
||||
if (FImGuiModule* Module = FModuleManager::GetModulePtr<FImGuiModule>("ImGui"))
|
||||
{
|
||||
SetParent(Module->ImGuiContextHandle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static ImGuiContext* ImGuiContextPtr = nullptr;
|
||||
static FImGuiContextHandle ImGuiContextPtrHandle(ImGuiContextPtr);
|
||||
|
||||
// Get the global ImGui context pointer (GImGui) indirectly to allow redirections in obsolete modules.
|
||||
#define GImGui (ImGuiContextPtrHandle.Get())
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
#include "imgui.cpp"
|
||||
#include "imgui_demo.cpp"
|
||||
#include "imgui_draw.cpp"
|
||||
#include "imgui_widgets.cpp"
|
||||
|
||||
#include "imgui_tables.cpp"
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include <HideWindowsPlatformTypes.h>
|
||||
#include <Windows/HideWindowsPlatformTypes.h>
|
||||
#endif // PLATFORM_WINDOWS
|
||||
|
||||
#include "ImGuiInteroperability.h"
|
||||
|
||||
|
||||
namespace ImGuiImplementation
|
||||
{
|
||||
// This is exposing ImGui default context for the whole module.
|
||||
// This is assuming that we don't define custom GImGui and therefore have GImDefaultContext defined in imgui.cpp.
|
||||
ImGuiContext& GetDefaultContext()
|
||||
#if WITH_EDITOR
|
||||
FImGuiContextHandle& GetContextHandle()
|
||||
{
|
||||
return GImDefaultContext;
|
||||
return ImGuiContextPtrHandle;
|
||||
}
|
||||
|
||||
void SaveCurrentContextIniSettings(const char* Filename)
|
||||
void SetParentContextHandle(FImGuiContextHandle& Parent)
|
||||
{
|
||||
SaveIniSettingsToDisk(Filename);
|
||||
ImGuiContextPtrHandle.SetParent(&Parent);
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
}
|
@ -2,15 +2,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
struct FImGuiContextHandle;
|
||||
|
||||
// Gives access to selected ImGui implementation features.
|
||||
namespace ImGuiImplementation
|
||||
{
|
||||
// Get default context created by ImGui framework.
|
||||
ImGuiContext& GetDefaultContext();
|
||||
#if WITH_EDITOR
|
||||
// Get the handle to the ImGui Context pointer.
|
||||
FImGuiContextHandle& GetContextHandle();
|
||||
|
||||
// Save current context settings.
|
||||
void SaveCurrentContextIniSettings(const char* Filename);
|
||||
// Set the ImGui Context pointer handle.
|
||||
void SetParentContextHandle(FImGuiContextHandle& Parent);
|
||||
#endif // WITH_EDITOR
|
||||
}
|
||||
|
379
Source/ImGui/Private/ImGuiInputHandler.cpp
Normal file
379
Source/ImGui/Private/ImGuiInputHandler.cpp
Normal file
@ -0,0 +1,379 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiInputHandler.h"
|
||||
|
||||
#include "ImGuiContextProxy.h"
|
||||
#include "ImGuiInputState.h"
|
||||
#include "ImGuiModuleDebug.h"
|
||||
#include "ImGuiModuleManager.h"
|
||||
#include "ImGuiModuleSettings.h"
|
||||
#include "VersionCompatibility.h"
|
||||
|
||||
#include <Engine/Console.h>
|
||||
#include <Framework/Application/SlateApplication.h>
|
||||
#include <GameFramework/InputSettings.h>
|
||||
#include <InputCoreTypes.h>
|
||||
#include <Input/Events.h>
|
||||
|
||||
#if WITH_EDITOR
|
||||
#include <Framework/Commands/InputBindingManager.h>
|
||||
#include <Framework/Commands/InputChord.h>
|
||||
#include <Kismet2/DebuggerCommands.h>
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogImGuiInputHandler);
|
||||
|
||||
namespace
|
||||
{
|
||||
FReply ToReply(bool bConsume)
|
||||
{
|
||||
return bConsume ? FReply::Handled() : FReply::Unhandled();
|
||||
}
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnKeyChar(const struct FCharacterEvent& CharacterEvent)
|
||||
{
|
||||
InputState->AddCharacter(CharacterEvent.GetCharacter());
|
||||
return ToReply(!ModuleManager->GetProperties().IsKeyboardInputShared());
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnKeyDown(const FKeyEvent& KeyEvent)
|
||||
{
|
||||
if (KeyEvent.GetKey().IsGamepadKey())
|
||||
{
|
||||
bool bConsume = false;
|
||||
if (InputState->IsGamepadNavigationEnabled())
|
||||
{
|
||||
// TODO: Update gamepad navigation to use new ImGuiKey API
|
||||
//InputState->SetGamepadNavigationKey(KeyEvent, true);
|
||||
bConsume = !ModuleManager->GetProperties().IsGamepadInputShared();
|
||||
}
|
||||
|
||||
return ToReply(bConsume);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore console events, so we don't block it from opening.
|
||||
if (IsConsoleEvent(KeyEvent))
|
||||
{
|
||||
return ToReply(false);
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
// If there is no active ImGui control that would get precedence and this key event is bound to a stop play session
|
||||
// command, then ignore that event and let the command execute.
|
||||
if (!HasImGuiActiveItem() && IsStopPlaySessionEvent(KeyEvent))
|
||||
{
|
||||
return ToReply(false);
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
const bool bConsume = !ModuleManager->GetProperties().IsKeyboardInputShared();
|
||||
|
||||
// With shared input we can leave command bindings for DebugExec to handle, otherwise we need to do it here.
|
||||
if (bConsume && IsToggleInputEvent(KeyEvent))
|
||||
{
|
||||
ModuleManager->GetProperties().ToggleInput();
|
||||
}
|
||||
|
||||
InputState->SetKeyDown(KeyEvent, true);
|
||||
CopyModifierKeys(KeyEvent);
|
||||
|
||||
InputState->KeyDownEvents.Add(KeyEvent.GetKeyCode(), KeyEvent);
|
||||
|
||||
return ToReply(bConsume);
|
||||
}
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnKeyUp(const FKeyEvent& KeyEvent)
|
||||
{
|
||||
InputState->KeyUpEvents.Add(KeyEvent.GetKeyCode(), KeyEvent);
|
||||
|
||||
if (KeyEvent.GetKey().IsGamepadKey())
|
||||
{
|
||||
bool bConsume = false;
|
||||
if (InputState->IsGamepadNavigationEnabled())
|
||||
{
|
||||
// TODO: Update gamepad navigation to use new ImGuiKey API
|
||||
//InputState->SetGamepadNavigationKey(KeyEvent, false);
|
||||
bConsume = !ModuleManager->GetProperties().IsGamepadInputShared();
|
||||
}
|
||||
|
||||
return ToReply(bConsume);
|
||||
}
|
||||
else
|
||||
{
|
||||
InputState->SetKeyDown(KeyEvent, false);
|
||||
CopyModifierKeys(KeyEvent);
|
||||
|
||||
return ToReply(!ModuleManager->GetProperties().IsKeyboardInputShared());
|
||||
}
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnAnalogValueChanged(const FAnalogInputEvent& AnalogInputEvent)
|
||||
{
|
||||
bool bConsume = false;
|
||||
|
||||
if (AnalogInputEvent.GetKey().IsGamepadKey() && InputState->IsGamepadNavigationEnabled())
|
||||
{
|
||||
// TODO: Update gamepad navigation to use new ImGuiKey API
|
||||
//InputState->SetGamepadNavigationAxis(AnalogInputEvent, AnalogInputEvent.GetAnalogValue());
|
||||
bConsume = !ModuleManager->GetProperties().IsGamepadInputShared();
|
||||
}
|
||||
|
||||
return ToReply(bConsume);
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnMouseButtonDown(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
if (MouseEvent.IsTouchEvent())
|
||||
{
|
||||
return ToReply(false);
|
||||
}
|
||||
|
||||
InputState->SetMouseDown(MouseEvent, true);
|
||||
InputState->MouseButtonDownEvents.Add(ImGuiInterops::GetMouseIndex(MouseEvent.GetEffectingButton()), MouseEvent);
|
||||
if (ModuleManager)
|
||||
{
|
||||
FImGuiContextProxy* Proxy = ModuleManager->GetContextManager().GetContextProxy(0);
|
||||
if (Proxy)
|
||||
{
|
||||
//GEngine->AddOnScreenDebugMessage(15, 10, Proxy->WantsMouseCapture() ? FColor::Green : FColor::Red, TEXT("Handler Down"));
|
||||
return ToReply(Proxy->WantsMouseCapture());
|
||||
}
|
||||
}
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnMouseButtonDoubleClick(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState->SetMouseDown(MouseEvent, true);
|
||||
InputState->MouseButtonDownEvents.Add(ImGuiInterops::GetMouseIndex(MouseEvent.GetEffectingButton()), MouseEvent);
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnMouseButtonUp(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
if (MouseEvent.IsTouchEvent())
|
||||
{
|
||||
return ToReply(false);
|
||||
}
|
||||
|
||||
InputState->SetMouseDown(MouseEvent, false);
|
||||
InputState->MouseButtonUpEvents.Add(ImGuiInterops::GetMouseIndex(MouseEvent.GetEffectingButton()), MouseEvent);
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnMouseWheel(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState->AddMouseWheelDelta(MouseEvent.GetWheelDelta());
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnMouseMove(const FVector2D& MousePosition, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
if (MouseEvent.IsTouchEvent())
|
||||
{
|
||||
return ToReply(false);
|
||||
}
|
||||
|
||||
return OnMouseMove(MousePosition);
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnMouseMove(const FVector2D& MousePosition)
|
||||
{
|
||||
InputState->SetMousePosition(MousePosition);
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnTouchStarted(const FVector2D& CursorPosition, const FPointerEvent& TouchEvent)
|
||||
{
|
||||
InputState->SetTouchDown(true);
|
||||
InputState->SetTouchPosition(CursorPosition);
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnTouchMoved(const FVector2D& CursorPosition, const FPointerEvent& TouchEvent)
|
||||
{
|
||||
InputState->SetTouchPosition(CursorPosition);
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
FReply UImGuiInputHandler::OnTouchEnded(const FVector2D& CursorPosition, const FPointerEvent& TouchEvent)
|
||||
{
|
||||
InputState->SetTouchDown(false);
|
||||
return ToReply(true);
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnKeyboardInputEnabled()
|
||||
{
|
||||
bKeyboardInputEnabled = true;
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnKeyboardInputDisabled()
|
||||
{
|
||||
if (bKeyboardInputEnabled)
|
||||
{
|
||||
bKeyboardInputEnabled = false;
|
||||
InputState->ResetKeyboard();
|
||||
}
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnGamepadInputEnabled()
|
||||
{
|
||||
bGamepadInputEnabled = true;
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnGamepadInputDisabled()
|
||||
{
|
||||
if (bGamepadInputEnabled)
|
||||
{
|
||||
bGamepadInputEnabled = false;
|
||||
// TODO: Update gamepad navigation to use new ImGuiKey API
|
||||
//InputState->ResetGamepadNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnMouseInputEnabled()
|
||||
{
|
||||
if (!bMouseInputEnabled)
|
||||
{
|
||||
bMouseInputEnabled = true;
|
||||
UpdateInputStatePointer();
|
||||
}
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnMouseInputDisabled()
|
||||
{
|
||||
if (bMouseInputEnabled)
|
||||
{
|
||||
bMouseInputEnabled = false;
|
||||
InputState->ResetMouse();
|
||||
UpdateInputStatePointer();
|
||||
}
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::CopyModifierKeys(const FInputEvent& InputEvent)
|
||||
{
|
||||
InputState->SetControlDown(InputEvent.IsControlDown());
|
||||
InputState->SetShiftDown(InputEvent.IsShiftDown());
|
||||
InputState->SetAltDown(InputEvent.IsAltDown());
|
||||
}
|
||||
|
||||
bool UImGuiInputHandler::IsConsoleEvent(const FKeyEvent& KeyEvent) const
|
||||
{
|
||||
// Checking modifiers is based on console implementation.
|
||||
const bool bModifierDown = KeyEvent.IsControlDown() || KeyEvent.IsShiftDown() || KeyEvent.IsAltDown() || KeyEvent.IsCommandDown();
|
||||
return !bModifierDown && GetDefault<UInputSettings>()->ConsoleKeys.Contains(KeyEvent.GetKey());
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
bool UImGuiInputHandler::IsStopPlaySessionEvent(const FKeyEvent& KeyEvent) const
|
||||
{
|
||||
if (StopPlaySessionCommandInfo.IsValid())
|
||||
{
|
||||
const FInputChord InputChord(KeyEvent.GetKey(), KeyEvent.IsShiftDown(), KeyEvent.IsControlDown(), KeyEvent.IsAltDown(), KeyEvent.IsCommandDown());
|
||||
#if ENGINE_COMPATIBILITY_SINGLE_KEY_BINDING
|
||||
const bool bHasActiveChord = (InputChord == StopPlaySessionCommandInfo->GetActiveChord().Get());
|
||||
#else
|
||||
const bool bHasActiveChord = StopPlaySessionCommandInfo->HasActiveChord(InputChord);
|
||||
#endif
|
||||
return bHasActiveChord && FPlayWorldCommands::GlobalPlayWorldActions->CanExecuteAction(StopPlaySessionCommandInfo.ToSharedRef());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
namespace
|
||||
{
|
||||
bool IsMatching(ECheckBoxState CheckBoxState, bool bValue)
|
||||
{
|
||||
return (CheckBoxState == ECheckBoxState::Undetermined) || ((CheckBoxState == ECheckBoxState::Checked) == bValue);
|
||||
}
|
||||
|
||||
bool IsMatchingEvent(const FKeyEvent& KeyEvent, const FImGuiKeyInfo& KeyInfo)
|
||||
{
|
||||
return (KeyInfo.Key == KeyEvent.GetKey())
|
||||
&& IsMatching(KeyInfo.Shift, KeyEvent.IsShiftDown())
|
||||
&& IsMatching(KeyInfo.Ctrl, KeyEvent.IsControlDown())
|
||||
&& IsMatching(KeyInfo.Alt, KeyEvent.IsAltDown())
|
||||
&& IsMatching(KeyInfo.Cmd, KeyEvent.IsCommandDown());
|
||||
}
|
||||
}
|
||||
|
||||
bool UImGuiInputHandler::IsToggleInputEvent(const FKeyEvent& KeyEvent) const
|
||||
{
|
||||
return IsMatchingEvent(KeyEvent, ModuleManager->GetSettings().GetToggleInputKey());
|
||||
}
|
||||
|
||||
bool UImGuiInputHandler::HasImGuiActiveItem() const
|
||||
{
|
||||
FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
||||
return ContextProxy && ContextProxy->HasActiveItem();
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::UpdateInputStatePointer()
|
||||
{
|
||||
InputState->SetMousePointer(bMouseInputEnabled && ModuleManager->GetSettings().UseSoftwareCursor());
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnSoftwareCursorChanged(bool)
|
||||
{
|
||||
UpdateInputStatePointer();
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::OnPostImGuiUpdate()
|
||||
{
|
||||
InputState->ClearUpdateState();
|
||||
|
||||
// TODO Replace with delegates after adding property change events.
|
||||
InputState->SetKeyboardNavigationEnabled(ModuleManager->GetProperties().IsKeyboardNavigationEnabled());
|
||||
InputState->SetGamepadNavigationEnabled(ModuleManager->GetProperties().IsGamepadNavigationEnabled());
|
||||
|
||||
const auto& PlatformApplication = FSlateApplication::Get().GetPlatformApplication();
|
||||
InputState->SetGamepad(PlatformApplication.IsValid() && PlatformApplication->IsGamepadAttached());
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::Initialize(FImGuiModuleManager* InModuleManager, UGameViewportClient* InGameViewport, int32 InContextIndex)
|
||||
{
|
||||
ModuleManager = InModuleManager;
|
||||
GameViewport = InGameViewport;
|
||||
ContextIndex = InContextIndex;
|
||||
|
||||
auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
||||
checkf(ContextProxy, TEXT("Missing context during initialization of input handler: ContextIndex = %d"), ContextIndex);
|
||||
InputState = &ContextProxy->GetInputState();
|
||||
|
||||
// Register to get post-update notifications, so we can clean frame updates.
|
||||
ModuleManager->OnPostImGuiUpdate().AddUObject(this, &UImGuiInputHandler::OnPostImGuiUpdate);
|
||||
|
||||
auto& Settings = ModuleManager->GetSettings();
|
||||
if (!Settings.OnUseSoftwareCursorChanged.IsBoundToObject(this))
|
||||
{
|
||||
Settings.OnUseSoftwareCursorChanged.AddUObject(this, &UImGuiInputHandler::OnSoftwareCursorChanged);
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
StopPlaySessionCommandInfo = FInputBindingManager::Get().FindCommandInContext("PlayWorld", "StopPlaySession");
|
||||
if (!StopPlaySessionCommandInfo.IsValid())
|
||||
{
|
||||
UE_LOG(LogImGuiInputHandler, Warning, TEXT("Couldn't find 'StopPlaySession' in context 'PlayWorld'. ")
|
||||
TEXT("PIE feature allowing execution of stop command in ImGui input mode will be disabled."));
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
}
|
||||
|
||||
void UImGuiInputHandler::BeginDestroy()
|
||||
{
|
||||
Super::BeginDestroy();
|
||||
|
||||
// To catch leftovers from modules shutdown during PIE session.
|
||||
extern FImGuiModuleManager* ImGuiModuleManager;
|
||||
if (ModuleManager && ModuleManager == ImGuiModuleManager)
|
||||
{
|
||||
ModuleManager->GetSettings().OnUseSoftwareCursorChanged.RemoveAll(this);
|
||||
}
|
||||
}
|
||||
|
50
Source/ImGui/Private/ImGuiInputHandlerFactory.cpp
Normal file
50
Source/ImGui/Private/ImGuiInputHandlerFactory.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiInputHandlerFactory.h"
|
||||
#include "ImGuiInputHandler.h"
|
||||
|
||||
#include "ImGuiModuleDebug.h"
|
||||
|
||||
#include <Engine/GameViewportClient.h>
|
||||
#include <InputCoreTypes.h>
|
||||
|
||||
|
||||
UImGuiInputHandler* FImGuiInputHandlerFactory::NewHandler(const FSoftClassPath& HandlerClassReference, FImGuiModuleManager* ModuleManager, UGameViewportClient* GameViewport, int32 ContextIndex)
|
||||
{
|
||||
UClass* HandlerClass = nullptr;
|
||||
if (HandlerClassReference.IsValid())
|
||||
{
|
||||
HandlerClass = HandlerClassReference.TryLoadClass<UImGuiInputHandler>();
|
||||
|
||||
if (!HandlerClass)
|
||||
{
|
||||
UE_LOG(LogImGuiInputHandler, Error, TEXT("Couldn't load ImGui Input Handler class '%s'."), *HandlerClassReference.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
if (!HandlerClass)
|
||||
{
|
||||
HandlerClass = UImGuiInputHandler::StaticClass();
|
||||
}
|
||||
|
||||
UImGuiInputHandler* Handler = NewObject<UImGuiInputHandler>(GameViewport, HandlerClass);
|
||||
if (Handler)
|
||||
{
|
||||
Handler->Initialize(ModuleManager, GameViewport, ContextIndex);
|
||||
Handler->AddToRoot();
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogImGuiInputHandler, Error, TEXT("Failed attempt to create Input Handler: HandlerClass = %s."), *GetNameSafe(HandlerClass));
|
||||
}
|
||||
|
||||
return Handler;
|
||||
}
|
||||
|
||||
void FImGuiInputHandlerFactory::ReleaseHandler(UImGuiInputHandler* Handler)
|
||||
{
|
||||
if (Handler)
|
||||
{
|
||||
Handler->RemoveFromRoot();
|
||||
}
|
||||
}
|
19
Source/ImGui/Private/ImGuiInputHandlerFactory.h
Normal file
19
Source/ImGui/Private/ImGuiInputHandlerFactory.h
Normal file
@ -0,0 +1,19 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ImGuiModuleSettings.h"
|
||||
|
||||
|
||||
class FImGuiModuleManager;
|
||||
class UGameViewportClient;
|
||||
class UImGuiInputHandler;
|
||||
|
||||
class FImGuiInputHandlerFactory
|
||||
{
|
||||
public:
|
||||
|
||||
static UImGuiInputHandler* NewHandler(const FSoftClassPath& HandlerClassReference, FImGuiModuleManager* ModuleManager, UGameViewportClient* GameViewport, int32 ContextIndex);
|
||||
|
||||
static void ReleaseHandler(UImGuiInputHandler* Handler);
|
||||
};
|
@ -1,29 +1,23 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
|
||||
#include "ImGuiInputState.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
FImGuiInputState::FImGuiInputState()
|
||||
{
|
||||
ResetState();
|
||||
Reset();
|
||||
}
|
||||
|
||||
void FImGuiInputState::AddCharacter(TCHAR Char)
|
||||
{
|
||||
static_assert(sizeof(TCHAR) <= sizeof(InputCharacters[0]), "Size of elements in Input Characters buffer is smaller than size of 'TCHAR'. Possible truncation.");
|
||||
|
||||
if (InputCharactersNum < Utilities::GetArraySize(InputCharacters))
|
||||
{
|
||||
InputCharacters[InputCharactersNum++] = static_cast<ImWchar>(Char);
|
||||
InputCharacters[InputCharactersNum] = 0;
|
||||
}
|
||||
InputCharacters.Add(Char);
|
||||
}
|
||||
|
||||
void FImGuiInputState::SetKeyDown(uint32 KeyIndex, bool bIsDown)
|
||||
void FImGuiInputState::SetKeyDown(ImGuiKey KeyIndex, bool bIsDown)
|
||||
{
|
||||
if (KeyIndex < Utilities::GetArraySize(KeysDown))
|
||||
{
|
||||
@ -47,44 +41,26 @@ void FImGuiInputState::SetMouseDown(uint32 MouseIndex, bool bIsDown)
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiInputState::Reset(bool bKeyboard, bool bMouse)
|
||||
{
|
||||
if (bKeyboard)
|
||||
{
|
||||
ClearCharacters();
|
||||
ClearKeys();
|
||||
}
|
||||
|
||||
if (bMouse)
|
||||
{
|
||||
ClearMouseButtons();
|
||||
ClearMouseAnalogue();
|
||||
}
|
||||
|
||||
if (bKeyboard && bMouse)
|
||||
{
|
||||
ClearModifierKeys();
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiInputState::ClearUpdateState()
|
||||
{
|
||||
if (InputCharactersNum > 0)
|
||||
{
|
||||
ClearCharacters();
|
||||
}
|
||||
|
||||
KeyDownEvents.Reset();
|
||||
KeyUpEvents.Reset();
|
||||
MouseButtonDownEvents.Reset();
|
||||
MouseButtonUpEvents.Reset();
|
||||
|
||||
KeysUpdateRange.SetEmpty();
|
||||
MouseButtonsUpdateRange.SetEmpty();
|
||||
|
||||
MouseWheelDelta = 0.f;
|
||||
|
||||
bTouchProcessed = bTouchDown;
|
||||
}
|
||||
|
||||
void FImGuiInputState::ClearCharacters()
|
||||
{
|
||||
using std::fill;
|
||||
fill(InputCharacters, &InputCharacters[Utilities::GetArraySize(InputCharacters)], 0);
|
||||
InputCharactersNum = 0;
|
||||
InputCharacters.Empty();
|
||||
}
|
||||
|
||||
void FImGuiInputState::ClearKeys()
|
||||
@ -92,7 +68,7 @@ void FImGuiInputState::ClearKeys()
|
||||
using std::fill;
|
||||
fill(KeysDown, &KeysDown[Utilities::GetArraySize(KeysDown)], false);
|
||||
|
||||
// Expand update range because keys array has been updated.
|
||||
// Mark the whole array as dirty because potentially each entry could be affected.
|
||||
KeysUpdateRange.SetFull();
|
||||
}
|
||||
|
||||
@ -101,7 +77,7 @@ void FImGuiInputState::ClearMouseButtons()
|
||||
using std::fill;
|
||||
fill(MouseButtonsDown, &MouseButtonsDown[Utilities::GetArraySize(MouseButtonsDown)], false);
|
||||
|
||||
// Expand update range because mouse buttons array has been updated.
|
||||
// Mark the whole array as dirty because potentially each entry could be affected.
|
||||
MouseButtonsUpdateRange.SetFull();
|
||||
}
|
||||
|
||||
@ -117,3 +93,4 @@ void FImGuiInputState::ClearModifierKeys()
|
||||
bIsShiftDown = false;
|
||||
bIsAltDown = false;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "ImGuiInteroperability.h"
|
||||
#include "Utilities/Arrays.h"
|
||||
|
||||
#include <Containers/Array.h>
|
||||
|
||||
|
||||
// Collects and stores input state and updates for ImGui IO.
|
||||
class FImGuiInputState
|
||||
@ -12,7 +14,7 @@ class FImGuiInputState
|
||||
public:
|
||||
|
||||
// Characters buffer.
|
||||
using FCharactersBuffer = ImGuiInterops::ImGuiTypes::FInputCharactersBuffer;
|
||||
using FCharactersBuffer = TArray<TCHAR, TInlineAllocator<8>>;
|
||||
|
||||
// Array for mouse button states.
|
||||
using FMouseButtonsArray = ImGuiInterops::ImGuiTypes::FMouseButtonsArray;
|
||||
@ -32,9 +34,6 @@ public:
|
||||
// Get reference to input characters buffer.
|
||||
const FCharactersBuffer& GetCharacters() const { return InputCharacters; }
|
||||
|
||||
// Get number of characters in input characters buffer.
|
||||
int32 GetCharactersNum() const { return InputCharactersNum; }
|
||||
|
||||
// Add a character to the characters buffer. We can store and send to ImGui up to 16 characters per frame. Any
|
||||
// character beyond that limit will be discarded.
|
||||
// @param Char - Character to add
|
||||
@ -47,9 +46,14 @@ public:
|
||||
const FKeysIndexRange& GetKeysUpdateRange() const { return KeysUpdateRange; }
|
||||
|
||||
// Change state of the key in the keys array and expand range bounding dirty part of the array.
|
||||
// @param KeyIndex - Index of the key
|
||||
// @param KeyEvent - Key event representing the key
|
||||
// @param bIsDown - True, if key is down
|
||||
void SetKeyDown(uint32 KeyIndex, bool bIsDown);
|
||||
void SetKeyDown(const FKeyEvent& KeyEvent, bool bIsDown) { SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), bIsDown); }
|
||||
|
||||
// Change state of the key in the keys array and expand range bounding dirty part of the array.
|
||||
// @param Key - Keyboard key
|
||||
// @param bIsDown - True, if key is down
|
||||
void SetKeyDown(const FKey& Key, bool bIsDown) { SetKeyDown(ImGuiInterops::GetKeyIndex(Key), bIsDown); }
|
||||
|
||||
// Get reference to the array with mouse button down states.
|
||||
const FMouseButtonsArray& GetMouseButtons() const { return MouseButtonsDown; }
|
||||
@ -58,9 +62,14 @@ public:
|
||||
const FMouseButtonsIndexRange& GetMouseButtonsUpdateRange() const { return MouseButtonsUpdateRange; }
|
||||
|
||||
// Change state of the button in the mouse buttons array and expand range bounding dirty part of the array.
|
||||
// @param MouseIndex - Index of the mouse button
|
||||
// @param MouseEvent - Mouse event representing mouse button
|
||||
// @param bIsDown - True, if button is down
|
||||
void SetMouseDown(uint32 MouseIndex, bool IsDown);
|
||||
void SetMouseDown(const FPointerEvent& MouseEvent, bool bIsDown) { SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), bIsDown); }
|
||||
|
||||
// Change state of the button in the mouse buttons array and expand range bounding dirty part of the array.
|
||||
// @param MouseButton - Mouse button key
|
||||
// @param bIsDown - True, if button is down
|
||||
void SetMouseDown(const FKey& MouseButton, bool bIsDown) { SetMouseDown(ImGuiInterops::GetMouseIndex(MouseButton), bIsDown); }
|
||||
|
||||
// Get mouse wheel delta accumulated during the last frame.
|
||||
float GetMouseWheelDelta() const { return MouseWheelDelta; }
|
||||
@ -69,11 +78,11 @@ public:
|
||||
// @param DeltaValue - Mouse wheel delta to add
|
||||
void AddMouseWheelDelta(float DeltaValue) { MouseWheelDelta += DeltaValue; }
|
||||
|
||||
// Get the current mouse position.
|
||||
// Get the mouse position.
|
||||
const FVector2D& GetMousePosition() const { return MousePosition; }
|
||||
|
||||
// Set mouse position.
|
||||
// @param Position - New mouse position
|
||||
// Set the mouse position.
|
||||
// @param Position - Mouse position
|
||||
void SetMousePosition(const FVector2D& Position) { MousePosition = Position; }
|
||||
|
||||
// Check whether input has active mouse pointer.
|
||||
@ -81,7 +90,25 @@ public:
|
||||
|
||||
// Set whether input has active mouse pointer.
|
||||
// @param bHasPointer - True, if input has active mouse pointer
|
||||
void SetMousePointer(bool bHasPointer) { bHasMousePointer = bHasPointer; }
|
||||
void SetMousePointer(bool bInHasMousePointer) { bHasMousePointer = bInHasMousePointer; }
|
||||
|
||||
// Check whether touch input is in progress. True, after touch is started until one frame after it has ended.
|
||||
// One frame delay is used to process mouse release in ImGui since touch-down is simulated with mouse-down.
|
||||
bool IsTouchActive() const { return bTouchDown || bTouchProcessed; }
|
||||
|
||||
// Check whether touch input is down.
|
||||
bool IsTouchDown() const { return bTouchDown; }
|
||||
|
||||
// Set whether touch input is down.
|
||||
// @param bIsDown - True, if touch is down (or started) and false, if touch is up (or ended)
|
||||
void SetTouchDown(bool bIsDown) { bTouchDown = bIsDown; }
|
||||
|
||||
// Get the touch position.
|
||||
const FVector2D& GetTouchPosition() const { return TouchPosition; }
|
||||
|
||||
// Set the touch position.
|
||||
// @param Position - Touch position
|
||||
void SetTouchPosition(const FVector2D& Position) { TouchPosition = Position; }
|
||||
|
||||
// Get Control down state.
|
||||
bool IsControlDown() const { return bIsControlDown; }
|
||||
@ -104,22 +131,86 @@ public:
|
||||
// @param bIsDown - True, if Alt is down
|
||||
void SetAltDown(bool bIsDown) { bIsAltDown = bIsDown; }
|
||||
|
||||
// Reset state and mark as dirty.
|
||||
void ResetState() { Reset(true, true); }
|
||||
// TODO: Update gamepad navigation to use new ImGuiKey API
|
||||
/*
|
||||
// Change state of the navigation input associated with this gamepad key.
|
||||
// @param KeyEvent - Key event with gamepad key input
|
||||
// @param bIsDown - True, if key is down
|
||||
void SetGamepadNavigationKey(const FKeyEvent& KeyEvent, bool bIsDown) { ImGuiInterops::SetGamepadNavigationKey(NavigationInputs, KeyEvent.GetKey(), bIsDown); }
|
||||
|
||||
// Reset keyboard state and mark as dirty.
|
||||
void ResetKeyboardState() { Reset(true, false); }
|
||||
// Change state of the navigation input associated with this gamepad axis.
|
||||
// @param AnalogInputEvent - Analogue input event with gamepad axis input
|
||||
// @param Value - Analogue value that should be set for this axis
|
||||
void SetGamepadNavigationAxis(const FAnalogInputEvent& AnalogInputEvent, float Value) { ImGuiInterops::SetGamepadNavigationAxis(NavigationInputs, AnalogInputEvent.GetKey(), Value); }
|
||||
*/
|
||||
|
||||
// Reset mouse state and mark as dirty.
|
||||
void ResetMouseState() { Reset(false, true); }
|
||||
// Check whether keyboard navigation is enabled.
|
||||
bool IsKeyboardNavigationEnabled() const { return bKeyboardNavigationEnabled; }
|
||||
|
||||
// Clear part of the state that is meant to be updated in every frame like: accumulators, buffers and information
|
||||
// about dirty parts of keys or mouse buttons arrays.
|
||||
// Set whether keyboard navigation is enabled.
|
||||
// @param bEnabled - True, if navigation is enabled
|
||||
void SetKeyboardNavigationEnabled(bool bEnabled) { bKeyboardNavigationEnabled = bEnabled; }
|
||||
|
||||
// Check whether gamepad navigation is enabled.
|
||||
bool IsGamepadNavigationEnabled() const { return bGamepadNavigationEnabled; }
|
||||
|
||||
// Set whether gamepad navigation is enabled.
|
||||
// @param bEnabled - True, if navigation is enabled
|
||||
void SetGamepadNavigationEnabled(bool bEnabled) { bGamepadNavigationEnabled = bEnabled; }
|
||||
|
||||
// Check whether gamepad is attached.
|
||||
bool HasGamepad() const { return bHasGamepad; }
|
||||
|
||||
// Set whether gamepad is attached.
|
||||
// @param bInHasGamepad - True, if gamepad is attached
|
||||
void SetGamepad(bool bInHasGamepad) { bHasGamepad = bInHasGamepad; }
|
||||
|
||||
// Reset the whole input state and mark it as dirty.
|
||||
void Reset()
|
||||
{
|
||||
ResetKeyboard();
|
||||
ResetMouse();
|
||||
// TODO: Update gamepad navigation to use new ImGuiKey API
|
||||
//ResetGamepadNavigation();
|
||||
}
|
||||
|
||||
// Reset the keyboard input state and mark it as dirty.
|
||||
void ResetKeyboard()
|
||||
{
|
||||
ClearCharacters();
|
||||
ClearKeys();
|
||||
ClearModifierKeys();
|
||||
}
|
||||
|
||||
// Reset the mouse input state and mark it as dirty.
|
||||
void ResetMouse()
|
||||
{
|
||||
ClearMouseButtons();
|
||||
ClearMouseAnalogue();
|
||||
}
|
||||
|
||||
// TODO: Update gamepad navigation to use new ImGuiKey API
|
||||
/*
|
||||
// Reset the gamepad navigation state.
|
||||
void ResetGamepadNavigation()
|
||||
{
|
||||
ClearNavigationInputs();
|
||||
}
|
||||
*/
|
||||
|
||||
// Clear part of the state that is meant to be updated in every frame like: accumulators, buffers, navigation data
|
||||
// and information about dirty parts of keys or mouse buttons arrays.
|
||||
void ClearUpdateState();
|
||||
|
||||
TMap<uint32, FKeyEvent> KeyDownEvents;
|
||||
TMap<uint32, FKeyEvent> KeyUpEvents;
|
||||
TMap<uint32, FPointerEvent> MouseButtonDownEvents;
|
||||
TMap<uint32, FPointerEvent> MouseButtonUpEvents;
|
||||
|
||||
private:
|
||||
|
||||
void Reset(bool bKeyboard, bool bMouse);
|
||||
void SetKeyDown(ImGuiKey KeyIndex, bool bIsDown);
|
||||
void SetMouseDown(uint32 MouseIndex, bool IsDown);
|
||||
|
||||
void ClearCharacters();
|
||||
void ClearKeys();
|
||||
@ -128,20 +219,26 @@ private:
|
||||
void ClearModifierKeys();
|
||||
|
||||
FVector2D MousePosition = FVector2D::ZeroVector;
|
||||
FVector2D TouchPosition = FVector2D::ZeroVector;
|
||||
float MouseWheelDelta = 0.f;
|
||||
|
||||
FMouseButtonsArray MouseButtonsDown;
|
||||
FMouseButtonsIndexRange MouseButtonsUpdateRange;
|
||||
|
||||
FCharactersBuffer InputCharacters;
|
||||
uint32 InputCharactersNum = 0;
|
||||
|
||||
FKeysArray KeysDown;
|
||||
FKeysIndexRange KeysUpdateRange;
|
||||
|
||||
bool bHasMousePointer = false;
|
||||
bool bTouchDown = false;
|
||||
bool bTouchProcessed = false;
|
||||
|
||||
bool bIsControlDown = false;
|
||||
bool bIsShiftDown = false;
|
||||
bool bIsAltDown = false;
|
||||
|
||||
bool bKeyboardNavigationEnabled = false;
|
||||
bool bGamepadNavigationEnabled = false;
|
||||
bool bHasGamepad = false;
|
||||
};
|
||||
|
@ -1,14 +1,50 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
|
||||
#include "ImGuiInteroperability.h"
|
||||
|
||||
#include "ImGuiInputState.h"
|
||||
#include "ImGuiModule.h"
|
||||
#include "Utilities/Arrays.h"
|
||||
|
||||
|
||||
// If TCHAR is wider than ImWchar, enable or disable validation of input character before conversions.
|
||||
#define VALIDATE_INPUT_CHARACTERS 1
|
||||
|
||||
#if VALIDATE_INPUT_CHARACTERS
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogImGuiInput, Warning, All);
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
//====================================================================================================
|
||||
// Character conversion
|
||||
//====================================================================================================
|
||||
|
||||
template<typename T, std::enable_if_t<(sizeof(T) <= sizeof(ImWchar)), T>* = nullptr>
|
||||
ImWchar CastInputChar(T Char)
|
||||
{
|
||||
return static_cast<ImWchar>(Char);
|
||||
}
|
||||
|
||||
template<typename T, std::enable_if_t<!(sizeof(T) <= sizeof(ImWchar)), T>* = nullptr>
|
||||
ImWchar CastInputChar(T Char)
|
||||
{
|
||||
#if VALIDATE_INPUT_CHARACTERS
|
||||
// We only need a runtime validation if TCHAR is wider than ImWchar.
|
||||
// Signed and unsigned integral types with the same size as ImWchar should be safely converted. As long as the
|
||||
// char value is in that range we can safely use it, otherwise we should log an error to notify about possible
|
||||
// truncations.
|
||||
static constexpr auto MinLimit = (std::numeric_limits<std::make_signed_t<ImWchar>>::min)();
|
||||
static constexpr auto MaxLimit = (std::numeric_limits<std::make_unsigned_t<ImWchar>>::max)();
|
||||
UE_CLOG(!(Char >= MinLimit && Char <= MaxLimit), LogImGuiInput, Error,
|
||||
TEXT("TCHAR value '%c' (%#x) is out of range %d (%#x) to %u (%#x) that can be safely converted to ImWchar. ")
|
||||
TEXT("If you wish to disable this validation, please set VALIDATE_INPUT_CHARACTERS in ImGuiInputState.cpp to 0."),
|
||||
Char, Char, MinLimit, MinLimit, MaxLimit, MaxLimit);
|
||||
#endif
|
||||
|
||||
return static_cast<ImWchar>(Char);
|
||||
}
|
||||
|
||||
//====================================================================================================
|
||||
// Copying Utilities
|
||||
//====================================================================================================
|
||||
@ -50,61 +86,139 @@ namespace ImGuiInterops
|
||||
// Input Mapping
|
||||
//====================================================================================================
|
||||
|
||||
static TMap<FKey, ImGuiKey> UnrealToImGuiKeyMap;
|
||||
|
||||
void SetUnrealKeyMap(ImGuiIO& IO)
|
||||
{
|
||||
struct FUnrealToImGuiMapping
|
||||
{
|
||||
FUnrealToImGuiMapping()
|
||||
{
|
||||
KeyMap[ImGuiKey_Tab] = GetKeyIndex(EKeys::Tab);
|
||||
KeyMap[ImGuiKey_LeftArrow] = GetKeyIndex(EKeys::Left);
|
||||
KeyMap[ImGuiKey_RightArrow] = GetKeyIndex(EKeys::Right);
|
||||
KeyMap[ImGuiKey_UpArrow] = GetKeyIndex(EKeys::Up);
|
||||
KeyMap[ImGuiKey_DownArrow] = GetKeyIndex(EKeys::Down);
|
||||
KeyMap[ImGuiKey_PageUp] = GetKeyIndex(EKeys::PageUp);
|
||||
KeyMap[ImGuiKey_PageDown] = GetKeyIndex(EKeys::PageDown);
|
||||
KeyMap[ImGuiKey_Home] = GetKeyIndex(EKeys::Home);
|
||||
KeyMap[ImGuiKey_End] = GetKeyIndex(EKeys::End);
|
||||
KeyMap[ImGuiKey_Delete] = GetKeyIndex(EKeys::Delete);
|
||||
KeyMap[ImGuiKey_Backspace] = GetKeyIndex(EKeys::BackSpace);
|
||||
KeyMap[ImGuiKey_Enter] = GetKeyIndex(EKeys::Enter);
|
||||
KeyMap[ImGuiKey_Escape] = GetKeyIndex(EKeys::Escape);
|
||||
KeyMap[ImGuiKey_A] = GetKeyIndex(EKeys::A);
|
||||
KeyMap[ImGuiKey_C] = GetKeyIndex(EKeys::C);
|
||||
KeyMap[ImGuiKey_V] = GetKeyIndex(EKeys::V);
|
||||
KeyMap[ImGuiKey_X] = GetKeyIndex(EKeys::X);
|
||||
KeyMap[ImGuiKey_Y] = GetKeyIndex(EKeys::Y);
|
||||
KeyMap[ImGuiKey_Z] = GetKeyIndex(EKeys::Z);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Tab, ImGuiKey_Tab);
|
||||
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Left, ImGuiKey_LeftArrow);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Right, ImGuiKey_RightArrow);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Up, ImGuiKey_UpArrow);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Down, ImGuiKey_DownArrow);
|
||||
|
||||
UnrealToImGuiKeyMap.Add(EKeys::PageUp, ImGuiKey_PageUp);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::PageDown, ImGuiKey_PageDown);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Home, ImGuiKey_Home);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::End, ImGuiKey_End);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Insert, ImGuiKey_Insert);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Delete, ImGuiKey_Delete);
|
||||
|
||||
UnrealToImGuiKeyMap.Add(EKeys::NumLock, ImGuiKey_NumLock);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::ScrollLock, ImGuiKey_ScrollLock);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Pause, ImGuiKey_Pause);
|
||||
|
||||
UnrealToImGuiKeyMap.Add(EKeys::BackSpace, ImGuiKey_Backspace);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::SpaceBar, ImGuiKey_Space);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Enter, ImGuiKey_Enter);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Escape, ImGuiKey_Escape);
|
||||
|
||||
UnrealToImGuiKeyMap.Add(EKeys::A, ImGuiKey_A);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::B, ImGuiKey_B);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::C, ImGuiKey_C);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::D, ImGuiKey_D);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::E, ImGuiKey_E);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F, ImGuiKey_F);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::G, ImGuiKey_G);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::H, ImGuiKey_H);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::I, ImGuiKey_I);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::J, ImGuiKey_J);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::K, ImGuiKey_K);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::L, ImGuiKey_L);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::M, ImGuiKey_M);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::N, ImGuiKey_N);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::O, ImGuiKey_O);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::P, ImGuiKey_P);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Q, ImGuiKey_Q);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::R, ImGuiKey_R);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::S, ImGuiKey_S);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::T, ImGuiKey_T);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::U, ImGuiKey_U);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::V, ImGuiKey_V);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::W, ImGuiKey_W);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::X, ImGuiKey_X);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Y, ImGuiKey_Y);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Z, ImGuiKey_Z);
|
||||
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F1, ImGuiKey_F1);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F2, ImGuiKey_F2);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F3, ImGuiKey_F3);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F4, ImGuiKey_F4);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F5, ImGuiKey_F5);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F6, ImGuiKey_F6);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F7, ImGuiKey_F7);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F8, ImGuiKey_F8);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F9, ImGuiKey_F9);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F10, ImGuiKey_F10);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F11, ImGuiKey_F11);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::F12, ImGuiKey_F12);
|
||||
|
||||
UnrealToImGuiKeyMap.Add(EKeys::One, ImGuiKey_0);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Two, ImGuiKey_1);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Three, ImGuiKey_2);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Four, ImGuiKey_3);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Five, ImGuiKey_4);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Six, ImGuiKey_5);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Seven, ImGuiKey_6);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Eight, ImGuiKey_7);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Nine, ImGuiKey_8);
|
||||
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Equals, ImGuiKey_Equal);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Comma, ImGuiKey_Comma);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Period, ImGuiKey_Period);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Slash, ImGuiKey_Slash);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::LeftBracket, ImGuiKey_LeftBracket);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::RightBracket, ImGuiKey_RightBracket);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Apostrophe, ImGuiKey_Apostrophe);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Semicolon, ImGuiKey_Semicolon);
|
||||
|
||||
UnrealToImGuiKeyMap.Add(EKeys::NumPadZero, ImGuiKey_Keypad0);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::NumPadOne, ImGuiKey_Keypad1);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::NumPadTwo, ImGuiKey_Keypad2);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::NumPadThree, ImGuiKey_Keypad3);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::NumPadFour, ImGuiKey_Keypad4);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::NumPadFive, ImGuiKey_Keypad5);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::NumPadSix, ImGuiKey_Keypad6);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::NumPadSeven, ImGuiKey_Keypad7);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::NumPadEight, ImGuiKey_Keypad8);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::NumPadNine, ImGuiKey_Keypad9);
|
||||
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Multiply, ImGuiKey_KeypadMultiply);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Add, ImGuiKey_KeypadAdd);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Subtract, ImGuiKey_KeypadSubtract);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Decimal, ImGuiKey_KeypadDecimal);
|
||||
UnrealToImGuiKeyMap.Add(EKeys::Divide, ImGuiKey_KeypadDivide);
|
||||
}
|
||||
|
||||
ImGuiTypes::FKeyMap KeyMap;
|
||||
};
|
||||
|
||||
static const FUnrealToImGuiMapping Mapping;
|
||||
|
||||
Copy(Mapping.KeyMap, IO.KeyMap);
|
||||
// Simple transform mapping key codes to 0-511 range used in ImGui.
|
||||
// From what I can tell, on most supported platforms key codes should comfortably fit in that range anyway
|
||||
// but the SDL key-codes used on Linux can go way out of this range (because of the extra flag). However,
|
||||
// after this transform they should fit in the range without conflicts.
|
||||
// NOTE: Should any of the platforms have other conflicts or any trouble with inputs, this is the likely
|
||||
// candidate for change.
|
||||
static ImGuiKey MapKeyCode(uint32 KeyCode)
|
||||
{
|
||||
return static_cast<ImGuiKey>((KeyCode < 512) ? KeyCode : 256 + (KeyCode % 256));
|
||||
}
|
||||
|
||||
uint32 GetKeyIndex(const FKey& Key)
|
||||
ImGuiKey GetKeyIndex(const FKey& Key)
|
||||
{
|
||||
const uint32* pKeyCode = nullptr;
|
||||
const uint32* pCharCode = nullptr;
|
||||
|
||||
FInputKeyManager::Get().GetCodesFromKey(Key, pKeyCode, pCharCode);
|
||||
|
||||
if (pKeyCode)
|
||||
{
|
||||
return *pKeyCode;
|
||||
const uint32 KeyCode =
|
||||
pKeyCode ? *pKeyCode
|
||||
: pCharCode ? *pCharCode
|
||||
: 0;
|
||||
|
||||
return MapKeyCode(KeyCode);
|
||||
}
|
||||
|
||||
if (pCharCode)
|
||||
ImGuiKey GetKeyIndex(const FKeyEvent& KeyEvent)
|
||||
{
|
||||
return *pCharCode;
|
||||
}
|
||||
|
||||
checkf(false, TEXT("Couldn't find a Key Code for key '%s'. Expecting that all keys should have a Key Code."), *Key.GetDisplayName().ToString());
|
||||
|
||||
return -1;
|
||||
return MapKeyCode(KeyEvent.GetKeyCode());
|
||||
}
|
||||
|
||||
uint32 GetMouseIndex(const FKey& MouseButton)
|
||||
@ -133,10 +247,114 @@ namespace ImGuiInterops
|
||||
return -1;
|
||||
}
|
||||
|
||||
EMouseCursor::Type ToSlateMouseCursor(ImGuiMouseCursor MouseCursor)
|
||||
{
|
||||
switch (MouseCursor)
|
||||
{
|
||||
case ImGuiMouseCursor_Arrow:
|
||||
return EMouseCursor::Default;
|
||||
case ImGuiMouseCursor_TextInput:
|
||||
return EMouseCursor::TextEditBeam;
|
||||
case ImGuiMouseCursor_ResizeAll:
|
||||
return EMouseCursor::CardinalCross;
|
||||
case ImGuiMouseCursor_ResizeNS:
|
||||
return EMouseCursor::ResizeUpDown;
|
||||
case ImGuiMouseCursor_ResizeEW:
|
||||
return EMouseCursor::ResizeLeftRight;
|
||||
case ImGuiMouseCursor_ResizeNESW:
|
||||
return EMouseCursor::ResizeSouthWest;
|
||||
case ImGuiMouseCursor_ResizeNWSE:
|
||||
return EMouseCursor::ResizeSouthEast;
|
||||
case ImGuiMouseCursor_None:
|
||||
default:
|
||||
return EMouseCursor::None;
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
inline void UpdateKey(const FKey& Key, const FKey& KeyCondition, float& Value, bool bIsDown)
|
||||
{
|
||||
if (Key == KeyCondition)
|
||||
{
|
||||
Value = (bIsDown) ? 1.f : 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
inline void UpdateAxisValues(float& Axis, float& Opposite, float Value)
|
||||
{
|
||||
constexpr float AxisInputThreshold = 0.166f;
|
||||
|
||||
// Filter out small values to avoid false positives (helpful in case of worn controllers).
|
||||
Axis = FMath::Max(0.f, Value - AxisInputThreshold);
|
||||
Opposite = 0.f;
|
||||
}
|
||||
|
||||
inline void UpdateSymmetricAxis(const FKey& Key, const FKey& KeyCondition, float& Negative, float& Positive, float Value)
|
||||
{
|
||||
if (Key == KeyCondition)
|
||||
{
|
||||
if (Value < 0.f)
|
||||
{
|
||||
UpdateAxisValues(Negative, Positive, -Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateAxisValues(Positive, Negative, Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Update gamepad navigation to use new ImGuiKey API
|
||||
/*
|
||||
void SetGamepadNavigationKey(ImGuiTypes::FNavInputArray& NavInputs, const FKey& Key, bool bIsDown)
|
||||
{
|
||||
#define MAP_KEY(KeyCondition, NavIndex) UpdateKey(Key, KeyCondition, NavInputs[NavIndex], bIsDown)
|
||||
|
||||
if (Key.IsGamepadKey())
|
||||
{
|
||||
MAP_KEY(EKeys::Gamepad_FaceButton_Bottom, ImGuiNavInput_Activate);
|
||||
MAP_KEY(EKeys::Gamepad_FaceButton_Right, ImGuiNavInput_Cancel);
|
||||
MAP_KEY(EKeys::Gamepad_FaceButton_Top, ImGuiNavInput_Input);
|
||||
MAP_KEY(EKeys::Gamepad_FaceButton_Left, ImGuiNavInput_Menu);
|
||||
MAP_KEY(EKeys::Gamepad_DPad_Left, ImGuiNavInput_DpadLeft);
|
||||
MAP_KEY(EKeys::Gamepad_DPad_Right, ImGuiNavInput_DpadRight);
|
||||
MAP_KEY(EKeys::Gamepad_DPad_Up, ImGuiNavInput_DpadUp);
|
||||
MAP_KEY(EKeys::Gamepad_DPad_Down, ImGuiNavInput_DpadDown);
|
||||
MAP_KEY(EKeys::Gamepad_LeftShoulder, ImGuiNavInput_FocusPrev);
|
||||
MAP_KEY(EKeys::Gamepad_RightShoulder, ImGuiNavInput_FocusNext);
|
||||
MAP_KEY(EKeys::Gamepad_LeftShoulder, ImGuiNavInput_TweakSlow);
|
||||
MAP_KEY(EKeys::Gamepad_RightShoulder, ImGuiNavInput_TweakFast);
|
||||
}
|
||||
|
||||
#undef MAP_KEY
|
||||
}
|
||||
|
||||
void SetGamepadNavigationAxis(ImGuiTypes::FNavInputArray& NavInputs, const FKey& Key, float Value)
|
||||
{
|
||||
#define MAP_SYMMETRIC_AXIS(KeyCondition, NegNavIndex, PosNavIndex) UpdateSymmetricAxis(Key, KeyCondition, NavInputs[NegNavIndex], NavInputs[PosNavIndex], Value)
|
||||
|
||||
if (Key.IsGamepadKey())
|
||||
{
|
||||
MAP_SYMMETRIC_AXIS(EKeys::Gamepad_LeftX, ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight);
|
||||
MAP_SYMMETRIC_AXIS(EKeys::Gamepad_LeftY, ImGuiKey_GamepadLStickDown, ImGuiKey_GamepadLStickUp);
|
||||
}
|
||||
|
||||
#undef MAP_SYMMETRIC_AXIS
|
||||
}
|
||||
*/
|
||||
|
||||
//====================================================================================================
|
||||
// Input State Copying
|
||||
//====================================================================================================
|
||||
|
||||
template<typename TFlags, typename TFlag>
|
||||
static inline constexpr void SetFlag(TFlags& Flags, TFlag Flag, bool bSet)
|
||||
{
|
||||
Flags = bSet ? Flags | Flag : Flags & ~Flag;
|
||||
}
|
||||
|
||||
void CopyInput(ImGuiIO& IO, const FImGuiInputState& InputState)
|
||||
{
|
||||
static const uint32 LeftControl = GetKeyIndex(EKeys::LeftControl);
|
||||
@ -146,16 +364,6 @@ namespace ImGuiInterops
|
||||
static const uint32 LeftAlt = GetKeyIndex(EKeys::LeftAlt);
|
||||
static const uint32 RightAlt = GetKeyIndex(EKeys::RightAlt);
|
||||
|
||||
// Check whether we need to draw cursor.
|
||||
IO.MouseDrawCursor = InputState.HasMousePointer();
|
||||
|
||||
// Copy mouse position.
|
||||
IO.MousePos.x = InputState.GetMousePosition().X;
|
||||
IO.MousePos.y = InputState.GetMousePosition().Y;
|
||||
|
||||
// Copy mouse wheel delta.
|
||||
IO.MouseWheel += InputState.GetMouseWheelDelta();
|
||||
|
||||
// Copy key modifiers.
|
||||
IO.KeyCtrl = InputState.IsControlDown();
|
||||
IO.KeyShift = InputState.IsShiftDown();
|
||||
@ -165,17 +373,84 @@ namespace ImGuiInterops
|
||||
// Copy buffers.
|
||||
if (!InputState.GetKeysUpdateRange().IsEmpty())
|
||||
{
|
||||
Copy(InputState.GetKeys(), IO.KeysDown, InputState.GetKeysUpdateRange());
|
||||
// Key down events
|
||||
for(const auto& Pair : InputState.KeyDownEvents)
|
||||
{
|
||||
if(UnrealToImGuiKeyMap.Contains(Pair.Value.GetKey()))
|
||||
{
|
||||
IO.AddKeyEvent(UnrealToImGuiKeyMap[Pair.Value.GetKey()], true);
|
||||
}
|
||||
}
|
||||
|
||||
// Key up events
|
||||
for(const auto& Pair : InputState.KeyUpEvents)
|
||||
{
|
||||
if(UnrealToImGuiKeyMap.Contains(Pair.Value.GetKey()))
|
||||
{
|
||||
IO.AddKeyEvent(UnrealToImGuiKeyMap[Pair.Value.GetKey()], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!InputState.GetMouseButtonsUpdateRange().IsEmpty())
|
||||
{
|
||||
Copy(InputState.GetMouseButtons(), IO.MouseDown, InputState.GetMouseButtonsUpdateRange());
|
||||
for(const auto& Pair : InputState.MouseButtonDownEvents)
|
||||
{
|
||||
uint32 MouseIdx = GetMouseIndex(Pair.Value.GetEffectingButton());
|
||||
if(MouseIdx != -1)
|
||||
{
|
||||
IO.AddMouseButtonEvent(MouseIdx, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (InputState.GetCharactersNum() > 0)
|
||||
|
||||
for(const auto& Pair : InputState.MouseButtonUpEvents)
|
||||
{
|
||||
Copy(InputState.GetCharacters(), IO.InputCharacters);
|
||||
uint32 MouseIdx = GetMouseIndex(Pair.Value.GetEffectingButton());
|
||||
if(MouseIdx != -1)
|
||||
{
|
||||
IO.AddMouseButtonEvent(MouseIdx, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const TCHAR Char : InputState.GetCharacters())
|
||||
{
|
||||
IO.AddInputCharacter(CastInputChar(Char));
|
||||
}
|
||||
|
||||
// TODO: Update gamepad navigation to use new ImGuiKey API
|
||||
/*
|
||||
if (InputState.IsGamepadNavigationEnabled() && InputState.HasGamepad())
|
||||
{
|
||||
Copy(InputState.GetNavigationInputs(), IO.NavInputs);
|
||||
}
|
||||
*/
|
||||
|
||||
SetFlag(IO.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard, InputState.IsKeyboardNavigationEnabled());
|
||||
SetFlag(IO.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad, InputState.IsGamepadNavigationEnabled());
|
||||
SetFlag(IO.BackendFlags, ImGuiBackendFlags_HasGamepad, InputState.HasGamepad());
|
||||
SetFlag(IO.ConfigFlags, ImGuiConfigFlags_DockingEnable, FImGuiModule::Get().GetProperties().IsDockingEnabled());
|
||||
|
||||
// Check whether we need to draw cursor.
|
||||
IO.MouseDrawCursor = InputState.HasMousePointer();
|
||||
|
||||
// If touch is enabled and active, give it a precedence.
|
||||
if (InputState.IsTouchActive())
|
||||
{
|
||||
// Copy the touch position to mouse position.
|
||||
IO.AddMousePosEvent(InputState.GetTouchPosition().X, InputState.GetTouchPosition().Y);
|
||||
|
||||
// With touch active one frame longer than it is down, we have one frame to processed touch up.
|
||||
IO.AddMouseButtonEvent(0, InputState.IsTouchDown());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy the mouse position.
|
||||
IO.AddMousePosEvent(InputState.GetMousePosition().X, InputState.GetMousePosition().Y);
|
||||
|
||||
// Copy mouse wheel delta.
|
||||
IO.AddMouseWheelEvent(0, IO.MouseWheel + InputState.GetMouseWheelDelta());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
#include "TextureManager.h"
|
||||
|
||||
#include <GenericPlatform/ICursor.h>
|
||||
#include <Input/Events.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
|
||||
@ -19,11 +22,8 @@ namespace ImGuiInterops
|
||||
namespace ImGuiTypes
|
||||
{
|
||||
using FMouseButtonsArray = decltype(ImGuiIO::MouseDown);
|
||||
using FKeysArray = decltype(ImGuiIO::KeysDown);
|
||||
|
||||
using FInputCharactersBuffer = decltype(ImGuiIO::InputCharacters);
|
||||
|
||||
using FKeyMap = decltype(ImGuiIO::KeyMap);
|
||||
// ImGuiKeyData type keeps track of down state, duration, etc. but we only care about down state
|
||||
using FKeysArray = bool[ImGuiKey_NamedKey_COUNT];
|
||||
}
|
||||
|
||||
|
||||
@ -35,17 +35,37 @@ namespace ImGuiInterops
|
||||
void SetUnrealKeyMap(ImGuiIO& IO);
|
||||
|
||||
// Map FKey to index in keys buffer.
|
||||
uint32 GetKeyIndex(const FKey& Key);
|
||||
ImGuiKey GetKeyIndex(const FKey& Key);
|
||||
|
||||
// Map key event to index in keys buffer.
|
||||
uint32 GetKeyIndex(const FKeyEvent& KeyEvent) { return KeyEvent.GetKeyCode(); }
|
||||
ImGuiKey GetKeyIndex(const FKeyEvent& KeyEvent);
|
||||
|
||||
// Map mouse FKey to index in mouse buttons buffer.
|
||||
uint32 GetMouseIndex(const FKey& MouseButton);
|
||||
|
||||
// Map pointer event to index in mouse buttons buffer.
|
||||
uint32 GetMouseIndex(const FPointerEvent& MouseEvent) { return GetMouseIndex(MouseEvent.GetEffectingButton()); }
|
||||
FORCEINLINE uint32 GetMouseIndex(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
return GetMouseIndex(MouseEvent.GetEffectingButton());
|
||||
}
|
||||
|
||||
// Convert from ImGuiMouseCursor type to EMouseCursor.
|
||||
EMouseCursor::Type ToSlateMouseCursor(ImGuiMouseCursor MouseCursor);
|
||||
|
||||
// TODO: Update gamepad navigation to use new ImGuiKey API
|
||||
/*
|
||||
// Set in the target array navigation input corresponding to gamepad key.
|
||||
// @param NavInputs - Target array
|
||||
// @param Key - Gamepad key mapped to navigation input (non-mapped keys will be ignored)
|
||||
// @param bIsDown - True, if key is down
|
||||
void SetGamepadNavigationKey(ImGuiTypes::FNavInputArray& NavInputs, const FKey& Key, bool bIsDown);
|
||||
|
||||
// Set in the target array navigation input corresponding to gamepad axis.
|
||||
// @param NavInputs - Target array
|
||||
// @param Key - Gamepad axis key mapped to navigation input (non-axis or non-mapped inputs will be ignored)
|
||||
// @param Value - Axis value (-1..1 values from Unreal are mapped to separate ImGui axes with values in range 0..1)
|
||||
void SetGamepadNavigationAxis(ImGuiTypes::FNavInputArray& NavInputs, const FKey& Key, float Value);
|
||||
*/
|
||||
|
||||
//====================================================================================================
|
||||
// Input State Copying
|
||||
@ -75,15 +95,21 @@ namespace ImGuiInterops
|
||||
return FSlateRect{ ImGuiRect.x, ImGuiRect.y, ImGuiRect.z, ImGuiRect.w };
|
||||
}
|
||||
|
||||
// Convert from ImVec2 rectangle to FVector2D.
|
||||
FORCEINLINE FVector2D ToVector2D(const ImVec2& ImGuiVector)
|
||||
{
|
||||
return FVector2D{ ImGuiVector.x, ImGuiVector.y };
|
||||
}
|
||||
|
||||
// Convert from ImGui Texture Id to Texture Index that we use for texture resources.
|
||||
FORCEINLINE TextureIndex ToTextureIndex(ImTextureID Index)
|
||||
{
|
||||
return static_cast<TextureIndex>(reinterpret_cast<intptr_t>(Index));
|
||||
return static_cast<TextureIndex>(static_cast<intptr_t>(Index));
|
||||
}
|
||||
|
||||
// Convert from Texture Index to ImGui Texture Id that we pass to ImGui.
|
||||
FORCEINLINE ImTextureID ToImTextureID(TextureIndex Index)
|
||||
{
|
||||
return reinterpret_cast<ImTextureID>(static_cast<intptr_t>(Index));
|
||||
return static_cast<ImTextureID>(static_cast<intptr_t>(Index));
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,19 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
#include "ImGuiModule.h"
|
||||
|
||||
#include "ImGuiDelegatesContainer.h"
|
||||
#include "ImGuiModuleManager.h"
|
||||
#include "TextureManager.h"
|
||||
#include "Utilities/WorldContext.h"
|
||||
#include "Utilities/WorldContextIndex.h"
|
||||
|
||||
#include <IPluginManager.h>
|
||||
#if WITH_EDITOR
|
||||
#include "ImGuiImplementation.h"
|
||||
#include "Editor/ImGuiEditor.h"
|
||||
#endif
|
||||
|
||||
#include <Interfaces/IPluginManager.h>
|
||||
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FImGuiModule"
|
||||
@ -24,82 +31,294 @@ struct EDelegateCategory
|
||||
};
|
||||
};
|
||||
|
||||
static FImGuiModuleManager* ModuleManager = nullptr;
|
||||
FImGuiModuleManager* ImGuiModuleManager = nullptr;
|
||||
|
||||
#if WITH_EDITOR
|
||||
static FImGuiEditor* ImGuiEditor = nullptr;
|
||||
#endif
|
||||
|
||||
#if IMGUI_WITH_OBSOLETE_DELEGATES
|
||||
|
||||
#if WITH_EDITOR
|
||||
FImGuiDelegateHandle FImGuiModule::AddEditorImGuiDelegate(const FImGuiDelegate& Delegate)
|
||||
{
|
||||
checkf(ModuleManager, TEXT("Null pointer to internal module implementation. Is module available?"));
|
||||
|
||||
return { ModuleManager->GetContextManager().GetEditorContextProxy().OnDraw().Add(Delegate),
|
||||
return { FImGuiDelegatesContainer::Get().OnWorldDebug(Utilities::EDITOR_CONTEXT_INDEX).Add(Delegate),
|
||||
EDelegateCategory::Default, Utilities::EDITOR_CONTEXT_INDEX };
|
||||
}
|
||||
#endif
|
||||
|
||||
FImGuiDelegateHandle FImGuiModule::AddWorldImGuiDelegate(const FImGuiDelegate& Delegate)
|
||||
{
|
||||
checkf(ModuleManager, TEXT("Null pointer to internal module implementation. Is module available?"));
|
||||
const int32 ContextIndex = Utilities::GetWorldContextIndex((UWorld*)GWorld);
|
||||
return { FImGuiDelegatesContainer::Get().OnWorldDebug(ContextIndex).Add(Delegate), EDelegateCategory::Default, ContextIndex };
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
checkf(GEngine, TEXT("Null GEngine. AddWorldImGuiDelegate should be only called with GEngine initialized."));
|
||||
|
||||
const FWorldContext* WorldContext = Utilities::GetWorldContext(GEngine->GameViewport);
|
||||
if (!WorldContext)
|
||||
{
|
||||
WorldContext = Utilities::GetWorldContextFromNetMode(ENetMode::NM_DedicatedServer);
|
||||
}
|
||||
|
||||
checkf(WorldContext, TEXT("Couldn't find current world. AddWorldImGuiDelegate should be only called from a valid world."));
|
||||
|
||||
int32 Index;
|
||||
FImGuiContextProxy& Proxy = ModuleManager->GetContextManager().GetWorldContextProxy(*WorldContext->World(), Index);
|
||||
#else
|
||||
const int32 Index = Utilities::STANDALONE_GAME_CONTEXT_INDEX;
|
||||
FImGuiContextProxy& Proxy = ModuleManager->GetContextManager().GetWorldContextProxy();
|
||||
#endif
|
||||
|
||||
return{ Proxy.OnDraw().Add(Delegate), EDelegateCategory::Default, Index };
|
||||
FImGuiDelegateHandle FImGuiModule::AddWorldImGuiDelegate(const UWorld* World, const FImGuiDelegate& Delegate)
|
||||
{
|
||||
const int32 ContextIndex = Utilities::GetWorldContextIndex(World);
|
||||
return { FImGuiDelegatesContainer::Get().OnWorldDebug(ContextIndex).Add(Delegate), EDelegateCategory::Default, ContextIndex };
|
||||
}
|
||||
|
||||
FImGuiDelegateHandle FImGuiModule::AddMultiContextImGuiDelegate(const FImGuiDelegate& Delegate)
|
||||
{
|
||||
checkf(ModuleManager, TEXT("Null pointer to internal module implementation. Is module available?"));
|
||||
|
||||
return { ModuleManager->GetContextManager().OnDrawMultiContext().Add(Delegate), EDelegateCategory::MultiContext };
|
||||
return { FImGuiDelegatesContainer::Get().OnMultiContextDebug().Add(Delegate), EDelegateCategory::MultiContext };
|
||||
}
|
||||
|
||||
void FImGuiModule::RemoveImGuiDelegate(const FImGuiDelegateHandle& Handle)
|
||||
{
|
||||
if (ModuleManager)
|
||||
{
|
||||
if (Handle.Category == EDelegateCategory::MultiContext)
|
||||
{
|
||||
ModuleManager->GetContextManager().OnDrawMultiContext().Remove(Handle.Handle);
|
||||
FImGuiDelegatesContainer::Get().OnMultiContextDebug().Remove(Handle.Handle);
|
||||
}
|
||||
else if (auto* Proxy = ModuleManager->GetContextManager().GetContextProxy(Handle.Index))
|
||||
else
|
||||
{
|
||||
Proxy->OnDraw().Remove(Handle.Handle);
|
||||
FImGuiDelegatesContainer::Get().OnWorldDebug(Handle.Index).Remove(Handle.Handle);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // IMGUI_WITH_OBSOLETE_DELEGATES
|
||||
|
||||
FImGuiTextureHandle FImGuiModule::FindTextureHandle(const FName& Name)
|
||||
{
|
||||
const TextureIndex Index = ImGuiModuleManager->GetTextureManager().FindTextureIndex(Name);
|
||||
return (Index != INDEX_NONE) ? FImGuiTextureHandle{ Name, ImGuiInterops::ToImTextureID(Index) } : FImGuiTextureHandle{};
|
||||
}
|
||||
|
||||
FImGuiTextureHandle FImGuiModule::RegisterTexture(const FName& Name, class UTexture* Texture, bool bMakeUnique)
|
||||
{
|
||||
FTextureManager& TextureManager = ImGuiModuleManager->GetTextureManager();
|
||||
|
||||
checkf(!bMakeUnique || TextureManager.FindTextureIndex(Name) == INDEX_NONE,
|
||||
TEXT("Trying to register a texture with a name '%s' that is already used. Chose a different name ")
|
||||
TEXT("or use bMakeUnique false, to update existing texture resources."), *Name.ToString());
|
||||
|
||||
const TextureIndex Index = TextureManager.CreateTextureResources(Name, Texture);
|
||||
return FImGuiTextureHandle{ Name, ImGuiInterops::ToImTextureID(Index) };
|
||||
}
|
||||
|
||||
void FImGuiModule::ReleaseTexture(const FImGuiTextureHandle& Handle)
|
||||
{
|
||||
if (Handle.IsValid())
|
||||
{
|
||||
ImGuiModuleManager->GetTextureManager().ReleaseTextureResources(ImGuiInterops::ToTextureIndex(Handle.GetTextureId()));
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModule::RebuildFontAtlas()
|
||||
{
|
||||
if (ImGuiModuleManager)
|
||||
{
|
||||
ImGuiModuleManager->RebuildFontAtlas();
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModule::StartupModule()
|
||||
{
|
||||
checkf(!ModuleManager, TEXT("Instance of Module Manager already exists. Instance should be created only during module startup."));
|
||||
// Initialize handles to allow cross-module redirections. Other handles will always look for parents in the active
|
||||
// module, which means that we can only redirect to started modules. We don't have to worry about self-referencing
|
||||
// as local handles are guaranteed to be constructed before initializing pointers.
|
||||
// This supports in-editor recompilation and hot-reloading after compiling from the command line. The latter method
|
||||
// theoretically doesn't support plug-ins and will not load re-compiled module, but its handles will still redirect
|
||||
// to the active one.
|
||||
|
||||
// Create module manager that implements modules logic.
|
||||
ModuleManager = new FImGuiModuleManager();
|
||||
#if WITH_EDITOR
|
||||
ImGuiContextHandle = &ImGuiImplementation::GetContextHandle();
|
||||
DelegatesContainerHandle = &FImGuiDelegatesContainer::GetHandle();
|
||||
#endif
|
||||
|
||||
// Create managers that implements module logic.
|
||||
|
||||
checkf(!ImGuiModuleManager, TEXT("Instance of the ImGui Module Manager already exists. Instance should be created only during module startup."));
|
||||
ImGuiModuleManager = new FImGuiModuleManager();
|
||||
|
||||
#if WITH_EDITOR
|
||||
checkf(!ImGuiEditor, TEXT("Instance of the ImGui Editor already exists. Instance should be created only during module startup."));
|
||||
ImGuiEditor = new FImGuiEditor();
|
||||
#endif
|
||||
}
|
||||
|
||||
void FImGuiModule::ShutdownModule()
|
||||
{
|
||||
checkf(ModuleManager, TEXT("Null Module Manager. Manager instance should be deleted during module shutdown."));
|
||||
// In editor store data that we want to move to hot-reloaded module.
|
||||
|
||||
// Before we shutdown we need to delete manager that will do all necessary cleanup.
|
||||
delete ModuleManager;
|
||||
ModuleManager = nullptr;
|
||||
#if WITH_EDITOR
|
||||
static bool bMoveProperties = true;
|
||||
static FImGuiModuleProperties PropertiesToMove = ImGuiModuleManager->GetProperties();
|
||||
#endif
|
||||
|
||||
// Before we shutdown we need to delete managers that will do all the necessary cleanup.
|
||||
|
||||
#if WITH_EDITOR
|
||||
checkf(ImGuiEditor, TEXT("Null ImGui Editor. ImGui editor instance should be deleted during module shutdown."));
|
||||
delete ImGuiEditor;
|
||||
ImGuiEditor = nullptr;
|
||||
#endif
|
||||
|
||||
checkf(ImGuiModuleManager, TEXT("Null ImGui Module Manager. Module manager instance should be deleted during module shutdown."));
|
||||
delete ImGuiModuleManager;
|
||||
ImGuiModuleManager = nullptr;
|
||||
|
||||
#if WITH_EDITOR
|
||||
// When shutting down we leave the global ImGui context pointer and handle pointing to resources that are already
|
||||
// deleted. This can cause troubles after hot-reload when code in other modules calls ImGui interface functions
|
||||
// which are statically bound to the obsolete module. To keep ImGui code functional we can redirect context handle
|
||||
// to point to the new module.
|
||||
|
||||
// When shutting down during hot-reloading, we might want to rewire handles used in statically bound functions
|
||||
// or move data to a new module.
|
||||
|
||||
FModuleManager::Get().OnModulesChanged().AddLambda([this] (FName Name, EModuleChangeReason Reason)
|
||||
{
|
||||
if (Reason == EModuleChangeReason::ModuleLoaded && Name == "ImGui")
|
||||
{
|
||||
FImGuiModule& LoadedModule = FImGuiModule::Get();
|
||||
if (&LoadedModule != this)
|
||||
{
|
||||
// Statically bound functions can be bound to the obsolete module, so we need to manually redirect.
|
||||
|
||||
if (LoadedModule.ImGuiContextHandle)
|
||||
{
|
||||
ImGuiImplementation::SetParentContextHandle(*LoadedModule.ImGuiContextHandle);
|
||||
}
|
||||
|
||||
if (LoadedModule.DelegatesContainerHandle)
|
||||
{
|
||||
FImGuiDelegatesContainer::MoveContainer(*LoadedModule.DelegatesContainerHandle);
|
||||
}
|
||||
|
||||
if (bMoveProperties)
|
||||
{
|
||||
bMoveProperties = false;
|
||||
LoadedModule.SetProperties(PropertiesToMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
#endif // WITH_EDITOR
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void FImGuiModule::SetProperties(const FImGuiModuleProperties& Properties)
|
||||
{
|
||||
ImGuiModuleManager->GetProperties() = Properties;
|
||||
}
|
||||
#endif
|
||||
|
||||
FImGuiModuleProperties& FImGuiModule::GetProperties()
|
||||
{
|
||||
return ImGuiModuleManager->GetProperties();
|
||||
}
|
||||
|
||||
const FImGuiModuleProperties& FImGuiModule::GetProperties() const
|
||||
{
|
||||
return ImGuiModuleManager->GetProperties();
|
||||
}
|
||||
|
||||
bool FImGuiModule::IsInputMode() const
|
||||
{
|
||||
return ImGuiModuleManager && ImGuiModuleManager->GetProperties().IsInputEnabled();
|
||||
}
|
||||
|
||||
void FImGuiModule::SetInputMode(bool bEnabled)
|
||||
{
|
||||
if (ImGuiModuleManager)
|
||||
{
|
||||
ImGuiModuleManager->GetProperties().SetInputEnabled(bEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModule::ToggleInputMode()
|
||||
{
|
||||
if (ImGuiModuleManager)
|
||||
{
|
||||
ImGuiModuleManager->GetProperties().ToggleInput();
|
||||
}
|
||||
}
|
||||
|
||||
bool FImGuiModule::IsShowingDemo() const
|
||||
{
|
||||
return ImGuiModuleManager && ImGuiModuleManager->GetProperties().ShowDemo();
|
||||
}
|
||||
|
||||
void FImGuiModule::SetShowDemo(bool bShow)
|
||||
{
|
||||
if (ImGuiModuleManager)
|
||||
{
|
||||
ImGuiModuleManager->GetProperties().SetShowDemo(bShow);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModule::ToggleShowDemo()
|
||||
{
|
||||
if (ImGuiModuleManager)
|
||||
{
|
||||
ImGuiModuleManager->GetProperties().ToggleDemo();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Runtime loader
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
#if !WITH_EDITOR && RUNTIME_LOADER_ENABLED
|
||||
|
||||
class FImGuiModuleLoader
|
||||
{
|
||||
FImGuiModuleLoader()
|
||||
{
|
||||
if (!Load())
|
||||
{
|
||||
FModuleManager::Get().OnModulesChanged().AddRaw(this, &FImGuiModuleLoader::LoadAndRelease);
|
||||
}
|
||||
}
|
||||
|
||||
// For different engine versions.
|
||||
static FORCEINLINE bool IsValid(const TSharedPtr<IModuleInterface>& Ptr) { return Ptr.IsValid(); }
|
||||
static FORCEINLINE bool IsValid(const IModuleInterface* const Ptr) { return Ptr != nullptr; }
|
||||
|
||||
bool Load()
|
||||
{
|
||||
return IsValid(FModuleManager::Get().LoadModule(ModuleName));
|
||||
}
|
||||
|
||||
void LoadAndRelease(FName Name, EModuleChangeReason Reason)
|
||||
{
|
||||
// Avoid handling own load event.
|
||||
if (Name != ModuleName)
|
||||
{
|
||||
// Try loading until success and then release.
|
||||
if (Load())
|
||||
{
|
||||
FModuleManager::Get().OnModulesChanged().RemoveAll(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static FName ModuleName;
|
||||
|
||||
static FImGuiModuleLoader Instance;
|
||||
};
|
||||
|
||||
FName FImGuiModuleLoader::ModuleName = "ImGui";
|
||||
|
||||
// In monolithic builds this will start loading process.
|
||||
FImGuiModuleLoader FImGuiModuleLoader::Instance;
|
||||
|
||||
#endif // !WITH_EDITOR && RUNTIME_LOADER_ENABLED
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Partial implementations of other classes that needs access to ImGuiModuleManager
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
bool FImGuiTextureHandle::HasValidEntry() const
|
||||
{
|
||||
const TextureIndex Index = ImGuiInterops::ToTextureIndex(TextureId);
|
||||
return Index != INDEX_NONE && ImGuiModuleManager && ImGuiModuleManager->GetTextureManager().GetTextureName(Index) == Name;
|
||||
}
|
||||
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FImGuiModule, ImGui)
|
||||
|
104
Source/ImGui/Private/ImGuiModuleCommands.cpp
Normal file
104
Source/ImGui/Private/ImGuiModuleCommands.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiModuleCommands.h"
|
||||
|
||||
#include "ImGuiModuleProperties.h"
|
||||
#include "Utilities/DebugExecBindings.h"
|
||||
|
||||
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleInput = TEXT("ImGui.ToggleInput");
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleKeyboardNavigation = TEXT("ImGui.ToggleKeyboardNavigation");
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleGamepadNavigation = TEXT("ImGui.ToggleGamepadNavigation");
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleKeyboardInputSharing = TEXT("ImGui.ToggleKeyboardInputSharing");
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleGamepadInputSharing = TEXT("ImGui.ToggleGamepadInputSharing");
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleMouseInputSharing = TEXT("ImGui.ToggleMouseInputSharing");
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleDockingEnabled = TEXT("ImGui.ToggleDockingEnabled");
|
||||
const TCHAR* const FImGuiModuleCommands::SetMouseInputSharing = TEXT("ImGui.SetMouseInputSharing");
|
||||
const TCHAR* const FImGuiModuleCommands::ToggleDemo = TEXT("ImGui.ToggleDemo");
|
||||
|
||||
FImGuiModuleCommands::FImGuiModuleCommands(FImGuiModuleProperties& InProperties)
|
||||
: Properties(InProperties)
|
||||
, ToggleInputCommand(ToggleInput,
|
||||
TEXT("Toggle ImGui input mode."),
|
||||
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleInputImpl))
|
||||
, ToggleKeyboardNavigationCommand(ToggleKeyboardNavigation,
|
||||
TEXT("Toggle ImGui keyboard navigation."),
|
||||
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleKeyboardNavigationImpl))
|
||||
, ToggleGamepadNavigationCommand(ToggleGamepadNavigation,
|
||||
TEXT("Toggle ImGui gamepad navigation."),
|
||||
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleGamepadNavigationImpl))
|
||||
, ToggleKeyboardInputSharingCommand(ToggleKeyboardInputSharing,
|
||||
TEXT("Toggle ImGui keyboard input sharing."),
|
||||
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleKeyboardInputSharingImpl))
|
||||
, ToggleGamepadInputSharingCommand(ToggleGamepadInputSharing,
|
||||
TEXT("Toggle ImGui gamepad input sharing."),
|
||||
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleGamepadInputSharingImpl))
|
||||
, ToggleMouseInputSharingCommand(ToggleMouseInputSharing,
|
||||
TEXT("Toggle ImGui mouse input sharing."),
|
||||
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleMouseInputSharingImpl))
|
||||
, ToggleDockingEnabledCommand(ToggleDockingEnabled,
|
||||
TEXT("Toggle ImGui docking."),
|
||||
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleDockingEnabledImpl))
|
||||
, SetMouseInputSharingCommand(SetMouseInputSharing,
|
||||
TEXT("Toggle ImGui mouse input sharing."),
|
||||
FConsoleCommandWithArgsDelegate::CreateRaw(this, &FImGuiModuleCommands::SetMouseInputSharingImpl))
|
||||
, ToggleDemoCommand(ToggleDemo,
|
||||
TEXT("Toggle ImGui demo."),
|
||||
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleDemoImpl))
|
||||
{
|
||||
}
|
||||
|
||||
void FImGuiModuleCommands::SetKeyBinding(const TCHAR* CommandName, const FImGuiKeyInfo& KeyInfo)
|
||||
{
|
||||
DebugExecBindings::UpdatePlayerInputs(KeyInfo, CommandName);
|
||||
}
|
||||
|
||||
void FImGuiModuleCommands::ToggleInputImpl()
|
||||
{
|
||||
Properties.ToggleInput();
|
||||
}
|
||||
|
||||
void FImGuiModuleCommands::ToggleKeyboardNavigationImpl()
|
||||
{
|
||||
Properties.ToggleKeyboardNavigation();
|
||||
}
|
||||
|
||||
void FImGuiModuleCommands::ToggleGamepadNavigationImpl()
|
||||
{
|
||||
Properties.ToggleGamepadNavigation();
|
||||
}
|
||||
|
||||
void FImGuiModuleCommands::ToggleKeyboardInputSharingImpl()
|
||||
{
|
||||
Properties.ToggleKeyboardInputSharing();
|
||||
}
|
||||
|
||||
void FImGuiModuleCommands::ToggleGamepadInputSharingImpl()
|
||||
{
|
||||
Properties.ToggleGamepadInputSharing();
|
||||
}
|
||||
|
||||
void FImGuiModuleCommands::ToggleMouseInputSharingImpl()
|
||||
{
|
||||
Properties.ToggleMouseInputSharing();
|
||||
}
|
||||
|
||||
void FImGuiModuleCommands::ToggleDockingEnabledImpl()
|
||||
{
|
||||
Properties.ToggleDockingEnabled();
|
||||
}
|
||||
|
||||
void FImGuiModuleCommands::SetMouseInputSharingImpl(const TArray<FString>& Args)
|
||||
{
|
||||
bool bIsEnabled = false;
|
||||
if (Args.Num() > 0)
|
||||
{
|
||||
LexFromString(bIsEnabled, *Args[0]);
|
||||
}
|
||||
Properties.SetMouseInputShared(bIsEnabled);
|
||||
}
|
||||
|
||||
void FImGuiModuleCommands::ToggleDemoImpl()
|
||||
{
|
||||
Properties.ToggleDemo();
|
||||
}
|
53
Source/ImGui/Private/ImGuiModuleCommands.h
Normal file
53
Source/ImGui/Private/ImGuiModuleCommands.h
Normal file
@ -0,0 +1,53 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <HAL/IConsoleManager.h>
|
||||
|
||||
|
||||
struct FImGuiKeyInfo;
|
||||
class FImGuiModuleProperties;
|
||||
|
||||
// Manges ImGui module console commands.
|
||||
class FImGuiModuleCommands
|
||||
{
|
||||
public:
|
||||
|
||||
static const TCHAR* const ToggleInput;
|
||||
static const TCHAR* const ToggleKeyboardNavigation;
|
||||
static const TCHAR* const ToggleGamepadNavigation;
|
||||
static const TCHAR* const ToggleKeyboardInputSharing;
|
||||
static const TCHAR* const ToggleGamepadInputSharing;
|
||||
static const TCHAR* const ToggleMouseInputSharing;
|
||||
static const TCHAR* const ToggleDockingEnabled;
|
||||
static const TCHAR* const SetMouseInputSharing;
|
||||
static const TCHAR* const ToggleDemo;
|
||||
|
||||
FImGuiModuleCommands(FImGuiModuleProperties& InProperties);
|
||||
|
||||
void SetKeyBinding(const TCHAR* CommandName, const FImGuiKeyInfo& KeyInfo);
|
||||
|
||||
private:
|
||||
|
||||
void ToggleInputImpl();
|
||||
void ToggleKeyboardNavigationImpl();
|
||||
void ToggleGamepadNavigationImpl();
|
||||
void ToggleKeyboardInputSharingImpl();
|
||||
void ToggleGamepadInputSharingImpl();
|
||||
void ToggleMouseInputSharingImpl();
|
||||
void ToggleDockingEnabledImpl();
|
||||
void SetMouseInputSharingImpl(const TArray< FString >& Args);
|
||||
void ToggleDemoImpl();
|
||||
|
||||
FImGuiModuleProperties& Properties;
|
||||
|
||||
FAutoConsoleCommand ToggleInputCommand;
|
||||
FAutoConsoleCommand ToggleKeyboardNavigationCommand;
|
||||
FAutoConsoleCommand ToggleGamepadNavigationCommand;
|
||||
FAutoConsoleCommand ToggleKeyboardInputSharingCommand;
|
||||
FAutoConsoleCommand ToggleGamepadInputSharingCommand;
|
||||
FAutoConsoleCommand ToggleMouseInputSharingCommand;
|
||||
FAutoConsoleCommand ToggleDockingEnabledCommand;
|
||||
FAutoConsoleCommand SetMouseInputSharingCommand;
|
||||
FAutoConsoleCommand ToggleDemoCommand;
|
||||
};
|
16
Source/ImGui/Private/ImGuiModuleDebug.h
Normal file
16
Source/ImGui/Private/ImGuiModuleDebug.h
Normal file
@ -0,0 +1,16 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Logging/LogMacros.h>
|
||||
|
||||
|
||||
// Module-wide debug symbols and loggers.
|
||||
|
||||
|
||||
// If enabled, it activates debug code and console variables that in normal usage are hidden.
|
||||
#define IMGUI_MODULE_DEVELOPER 0
|
||||
|
||||
|
||||
// Input Handler logger (used also in non-developer mode to raise problems with handler extensions).
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogImGuiInputHandler, Warning, All);
|
@ -1,33 +1,53 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
|
||||
#include "ImGuiModuleManager.h"
|
||||
|
||||
#include "ImGuiInteroperability.h"
|
||||
#include "Utilities/WorldContextIndex.h"
|
||||
|
||||
#include <Framework/Application/SlateApplication.h>
|
||||
#include <Modules/ModuleManager.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
|
||||
// High enough z-order guarantees that ImGui output is rendered on top of the game UI.
|
||||
constexpr int32 IMGUI_WIDGET_Z_ORDER = 10000;
|
||||
|
||||
// Module texture names.
|
||||
const static FName PlainTextureName = "ImGuiModule_Plain";
|
||||
const static FName FontAtlasTextureName = "ImGuiModule_FontAtlas";
|
||||
|
||||
FImGuiModuleManager::FImGuiModuleManager()
|
||||
: Commands(Properties)
|
||||
, Settings(Properties, Commands)
|
||||
, ImGuiDemo(Properties)
|
||||
, ContextManager(Settings)
|
||||
{
|
||||
// Register in context manager to get information whenever a new context proxy is created.
|
||||
ContextManager.OnContextProxyCreated.AddRaw(this, &FImGuiModuleManager::OnContextProxyCreated);
|
||||
|
||||
// Typically we will use viewport created events to add widget to new game viewports.
|
||||
ViewportCreatedHandle = UGameViewportClient::OnViewportCreated().AddRaw(this, &FImGuiModuleManager::OnViewportCreated);
|
||||
|
||||
// Initialize resources and start ticking. Depending on loading phase, this may fail if Slate is not yet ready.
|
||||
Initialize();
|
||||
// Try to register tick delegate (it may fail if Slate application isn't yet ready).
|
||||
RegisterTick();
|
||||
|
||||
// If we failed to register, create an initializer that will do it later.
|
||||
if (!IsTickRegistered())
|
||||
{
|
||||
CreateTickInitializer();
|
||||
}
|
||||
|
||||
// We need to add widgets to active game viewports as they won't generate on-created events. This is especially
|
||||
// important during hot-reloading.
|
||||
if (bInitialized)
|
||||
{
|
||||
AddWidgetToAllViewports();
|
||||
}
|
||||
AddWidgetsToActiveViewports();
|
||||
}
|
||||
|
||||
FImGuiModuleManager::~FImGuiModuleManager()
|
||||
{
|
||||
ContextManager.OnFontAtlasBuilt.RemoveAll(this);
|
||||
|
||||
// We are no longer interested with adding widgets to viewports.
|
||||
if (ViewportCreatedHandle.IsValid())
|
||||
{
|
||||
@ -38,64 +58,66 @@ FImGuiModuleManager::~FImGuiModuleManager()
|
||||
// Remove still active widgets (important during hot-reloading).
|
||||
for (auto& Widget : Widgets)
|
||||
{
|
||||
if (Widget.IsValid())
|
||||
auto SharedWidget = Widget.Pin();
|
||||
if (SharedWidget.IsValid())
|
||||
{
|
||||
Widget.Pin()->Detach();
|
||||
auto& WidgetGameViewport = SharedWidget->GetGameViewport();
|
||||
if (WidgetGameViewport.IsValid())
|
||||
{
|
||||
WidgetGameViewport->RemoveViewportWidgetContent(SharedWidget.ToSharedRef());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deactivate this manager.
|
||||
Uninitialize();
|
||||
}
|
||||
|
||||
void FImGuiModuleManager::Initialize()
|
||||
{
|
||||
// We rely on Slate, so we can only continue if it is already initialized.
|
||||
if (!bInitialized && FSlateApplication::IsInitialized())
|
||||
{
|
||||
bInitialized = true;
|
||||
LoadTextures();
|
||||
RegisterTick();
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleManager::Uninitialize()
|
||||
{
|
||||
if (bInitialized)
|
||||
{
|
||||
bInitialized = false;
|
||||
ReleaseTickInitializer();
|
||||
UnregisterTick();
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleManager::RebuildFontAtlas()
|
||||
{
|
||||
ContextManager.RebuildFontAtlas();
|
||||
}
|
||||
|
||||
void FImGuiModuleManager::LoadTextures()
|
||||
{
|
||||
checkf(FSlateApplication::IsInitialized(), TEXT("Slate should be initialized before we can create textures."));
|
||||
|
||||
// Create an empty texture at index 0. We will use it for ImGui outputs with null texture id.
|
||||
TextureManager.CreatePlainTexture(FName{ "ImGuiModule_Null" }, 2, 2, FColor::White);
|
||||
if (!bTexturesLoaded)
|
||||
{
|
||||
bTexturesLoaded = true;
|
||||
|
||||
TextureManager.InitializeErrorTexture(FColor::Magenta);
|
||||
|
||||
// Create an empty texture at index 0. We will use it for ImGui outputs with null texture id.
|
||||
TextureManager.CreatePlainTexture(PlainTextureName, 2, 2, FColor::White);
|
||||
|
||||
// Register for atlas built events, so we can rebuild textures.
|
||||
ContextManager.OnFontAtlasBuilt.AddRaw(this, &FImGuiModuleManager::BuildFontAtlasTexture);
|
||||
|
||||
BuildFontAtlasTexture();
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleManager::BuildFontAtlasTexture()
|
||||
{
|
||||
// Create a font atlas texture.
|
||||
ImFontAtlas* Fonts = ImGui::GetIO().Fonts;
|
||||
checkf(Fonts, TEXT("Invalid font atlas."));
|
||||
ImFontAtlas& Fonts = ContextManager.GetFontAtlas();
|
||||
|
||||
unsigned char* Pixels;
|
||||
int Width, Height, Bpp;
|
||||
Fonts->GetTexDataAsRGBA32(&Pixels, &Width, &Height, &Bpp);
|
||||
Fonts.GetTexDataAsRGBA32(&Pixels, &Width, &Height, &Bpp);
|
||||
|
||||
TextureIndex FontsTexureIndex = TextureManager.CreateTexture(FName{ "ImGuiModule_FontAtlas" }, Width, Height, Bpp, Pixels, false);
|
||||
const TextureIndex FontsTexureIndex = TextureManager.CreateTexture(FontAtlasTextureName, Width, Height, Bpp, Pixels);
|
||||
|
||||
// Set font texture index in ImGui.
|
||||
Fonts->TexID = ImGuiInterops::ToImTextureID(FontsTexureIndex);
|
||||
// Set the font texture index in the ImGui.
|
||||
Fonts.TexID = ImGuiInterops::ToImTextureID(FontsTexureIndex);
|
||||
}
|
||||
|
||||
void FImGuiModuleManager::RegisterTick()
|
||||
{
|
||||
checkf(FSlateApplication::IsInitialized(), TEXT("Slate should be initialized before we can register tick listener."));
|
||||
|
||||
// We will tick on Slate Post-Tick events. They are quite convenient as they happen at the very end of the frame,
|
||||
// what helps to minimise tearing.
|
||||
if (!TickDelegateHandle.IsValid())
|
||||
// Slate Post-Tick is a good moment to end and advance ImGui frame as it minimises a tearing.
|
||||
if (!TickDelegateHandle.IsValid() && FSlateApplication::IsInitialized())
|
||||
{
|
||||
TickDelegateHandle = FSlateApplication::Get().OnPostTick().AddRaw(this, &FImGuiModuleManager::Tick);
|
||||
}
|
||||
@ -113,16 +135,39 @@ void FImGuiModuleManager::UnregisterTick()
|
||||
}
|
||||
}
|
||||
|
||||
bool FImGuiModuleManager::IsInUpdateThread()
|
||||
void FImGuiModuleManager::CreateTickInitializer()
|
||||
{
|
||||
// We can get ticks from the Game thread and Slate loading thread. In both cases IsInGameThread() is true, so we
|
||||
// need to make additional test to filter out loading thread.
|
||||
return IsInGameThread() && !IsInSlateThread();
|
||||
if (!TickInitializerHandle.IsValid())
|
||||
{
|
||||
// Try to register tick delegate until we finally succeed.
|
||||
|
||||
TickInitializerHandle = FModuleManager::Get().OnModulesChanged().AddLambda([this](FName Name, EModuleChangeReason Reason)
|
||||
{
|
||||
if (Reason == EModuleChangeReason::ModuleLoaded)
|
||||
{
|
||||
RegisterTick();
|
||||
}
|
||||
|
||||
if (IsTickRegistered())
|
||||
{
|
||||
ReleaseTickInitializer();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleManager::ReleaseTickInitializer()
|
||||
{
|
||||
if (TickInitializerHandle.IsValid())
|
||||
{
|
||||
FModuleManager::Get().OnModulesChanged().Remove(TickInitializerHandle);
|
||||
TickInitializerHandle.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleManager::Tick(float DeltaSeconds)
|
||||
{
|
||||
if (IsInUpdateThread())
|
||||
if (IsInGameThread())
|
||||
{
|
||||
// Update context manager to advance all ImGui contexts to the next frame.
|
||||
ContextManager.Tick(DeltaSeconds);
|
||||
@ -136,9 +181,6 @@ void FImGuiModuleManager::OnViewportCreated()
|
||||
{
|
||||
checkf(FSlateApplication::IsInitialized(), TEXT("We expect Slate to be initialized when game viewport is created."));
|
||||
|
||||
// Make sure that all resources are initialized to handle configurations where this module is loaded before Slate.
|
||||
Initialize();
|
||||
|
||||
// Create widget to viewport responsible for this event.
|
||||
AddWidgetToViewport(GEngine->GameViewport);
|
||||
}
|
||||
@ -148,28 +190,34 @@ void FImGuiModuleManager::AddWidgetToViewport(UGameViewportClient* GameViewport)
|
||||
checkf(GameViewport, TEXT("Null game viewport."));
|
||||
checkf(FSlateApplication::IsInitialized(), TEXT("Slate should be initialized before we can add widget to game viewports."));
|
||||
|
||||
// This makes sure that context for this world is created.
|
||||
auto& Proxy = ContextManager.GetWorldContextProxy(*GameViewport->GetWorld());
|
||||
// Make sure that we have a context for this viewport's world and get its index.
|
||||
int32 ContextIndex;
|
||||
auto& ContextProxy = ContextManager.GetWorldContextProxy(*GameViewport->GetWorld(), ContextIndex);
|
||||
|
||||
TSharedPtr<SImGuiWidget> Widget;
|
||||
SAssignNew(Widget, SImGuiWidget).ModuleManager(this).GameViewport(GameViewport)
|
||||
.ContextIndex(Utilities::GetWorldContextIndex(GameViewport));
|
||||
// Make sure that textures are loaded before the first Slate widget is created.
|
||||
LoadTextures();
|
||||
|
||||
if (TWeakPtr<SImGuiWidget>* Slot = Widgets.FindByPredicate([](auto& Widget) { return !Widget.IsValid(); }))
|
||||
// Create and initialize the widget.
|
||||
TSharedPtr<SImGuiLayout> SharedWidget;
|
||||
SAssignNew(SharedWidget, SImGuiLayout).ModuleManager(this).GameViewport(GameViewport).ContextIndex(ContextIndex);
|
||||
|
||||
GameViewport->AddViewportWidgetContent(SharedWidget.ToSharedRef(), IMGUI_WIDGET_Z_ORDER);
|
||||
|
||||
// We transfer widget ownerships to viewports but we keep weak references in case we need to manually detach active
|
||||
// widgets during module shutdown (important during hot-reloading).
|
||||
if (TWeakPtr<SImGuiLayout>* Slot = Widgets.FindByPredicate([](auto& Widget) { return !Widget.IsValid(); }))
|
||||
{
|
||||
*Slot = Widget;
|
||||
*Slot = SharedWidget;
|
||||
}
|
||||
else
|
||||
{
|
||||
Widgets.Emplace(Widget);
|
||||
Widgets.Emplace(SharedWidget);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleManager::AddWidgetToAllViewports()
|
||||
void FImGuiModuleManager::AddWidgetsToActiveViewports()
|
||||
{
|
||||
checkf(FSlateApplication::IsInitialized(), TEXT("Slate should be initialized before we can add widget to game viewports."));
|
||||
|
||||
if (GEngine)
|
||||
if (FSlateApplication::IsInitialized() && GEngine)
|
||||
{
|
||||
// Loop as long as we have a valid viewport or until we detect a cycle.
|
||||
UGameViewportClient* GameViewport = GEngine->GameViewport;
|
||||
@ -185,3 +233,8 @@ void FImGuiModuleManager::AddWidgetToAllViewports()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleManager::OnContextProxyCreated(int32 ContextIndex, FImGuiContextProxy& ContextProxy)
|
||||
{
|
||||
ContextProxy.OnDraw().AddLambda([this, ContextIndex]() { ImGuiDemo.DrawControls(ContextIndex); });
|
||||
}
|
||||
|
@ -3,8 +3,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "ImGuiContextManager.h"
|
||||
#include "SImGuiWidget.h"
|
||||
#include "ImGuiDemo.h"
|
||||
#include "ImGuiModuleCommands.h"
|
||||
#include "ImGuiModuleProperties.h"
|
||||
#include "ImGuiModuleSettings.h"
|
||||
#include "TextureManager.h"
|
||||
#include "Widgets/SImGuiLayout.h"
|
||||
|
||||
|
||||
// Central manager that implements module logic. It initializes and controls remaining module components.
|
||||
@ -15,6 +19,12 @@ class FImGuiModuleManager
|
||||
|
||||
public:
|
||||
|
||||
// Get interface to module settings.
|
||||
FImGuiModuleSettings& GetSettings() { return Settings; }
|
||||
|
||||
// Get interface to module state properties.
|
||||
FImGuiModuleProperties& GetProperties() { return Properties; }
|
||||
|
||||
// Get ImGui contexts manager.
|
||||
FImGuiContextManager& GetContextManager() { return ContextManager; }
|
||||
|
||||
@ -24,6 +34,8 @@ public:
|
||||
// Event called right after ImGui is updated, to give other subsystems chance to react.
|
||||
FSimpleMulticastDelegate& OnPostImGuiUpdate() { return PostImGuiUpdateEvent; }
|
||||
|
||||
void RebuildFontAtlas();
|
||||
|
||||
private:
|
||||
|
||||
FImGuiModuleManager();
|
||||
@ -35,26 +47,40 @@ private:
|
||||
FImGuiModuleManager(FImGuiModuleManager&&) = delete;
|
||||
FImGuiModuleManager& operator=(FImGuiModuleManager&&) = delete;
|
||||
|
||||
void Initialize();
|
||||
void Uninitialize();
|
||||
|
||||
void LoadTextures();
|
||||
void BuildFontAtlasTexture();
|
||||
|
||||
bool IsTickRegistered() { return TickDelegateHandle.IsValid(); }
|
||||
void RegisterTick();
|
||||
void UnregisterTick();
|
||||
|
||||
bool IsInUpdateThread();
|
||||
void CreateTickInitializer();
|
||||
void ReleaseTickInitializer();
|
||||
|
||||
void Tick(float DeltaSeconds);
|
||||
|
||||
void OnViewportCreated();
|
||||
|
||||
void AddWidgetToViewport(UGameViewportClient* GameViewport);
|
||||
void AddWidgetToAllViewports();
|
||||
void AddWidgetsToActiveViewports();
|
||||
|
||||
void OnContextProxyCreated(int32 ContextIndex, FImGuiContextProxy& ContextProxy);
|
||||
|
||||
// Event that we call after ImGui is updated.
|
||||
FSimpleMulticastDelegate PostImGuiUpdateEvent;
|
||||
|
||||
// Collection of module state properties.
|
||||
FImGuiModuleProperties Properties;
|
||||
|
||||
// Tying module console commands to life-cycle of this manager and module.
|
||||
FImGuiModuleCommands Commands;
|
||||
|
||||
// ImGui settings proxy (valid in every loading stage).
|
||||
FImGuiModuleSettings Settings;
|
||||
|
||||
// Widget that we add to all created contexts to draw ImGui demo.
|
||||
FImGuiDemo ImGuiDemo;
|
||||
|
||||
// Manager for ImGui contexts.
|
||||
FImGuiContextManager ContextManager;
|
||||
|
||||
@ -62,10 +88,11 @@ private:
|
||||
FTextureManager TextureManager;
|
||||
|
||||
// Slate widgets that we created.
|
||||
TArray<TWeakPtr<SImGuiWidget>> Widgets;
|
||||
TArray<TWeakPtr<SImGuiLayout>> Widgets;
|
||||
|
||||
FDelegateHandle TickInitializerHandle;
|
||||
FDelegateHandle TickDelegateHandle;
|
||||
FDelegateHandle ViewportCreatedHandle;
|
||||
|
||||
bool bInitialized = false;
|
||||
bool bTexturesLoaded = false;
|
||||
};
|
||||
|
224
Source/ImGui/Private/ImGuiModuleSettings.cpp
Normal file
224
Source/ImGui/Private/ImGuiModuleSettings.cpp
Normal file
@ -0,0 +1,224 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiModuleSettings.h"
|
||||
|
||||
#include "ImGuiModuleCommands.h"
|
||||
#include "ImGuiModuleProperties.h"
|
||||
|
||||
#include <Engine/Engine.h>
|
||||
#include <GameFramework/GameUserSettings.h>
|
||||
#include <Misc/ConfigCacheIni.h>
|
||||
|
||||
|
||||
//====================================================================================================
|
||||
// FImGuiDPIScaleInfo
|
||||
//====================================================================================================
|
||||
|
||||
FImGuiDPIScaleInfo::FImGuiDPIScaleInfo()
|
||||
{
|
||||
if (FRichCurve* Curve = DPICurve.GetRichCurve())
|
||||
{
|
||||
Curve->AddKey( 0.0f, 1.f);
|
||||
|
||||
Curve->AddKey(2159.5f, 1.f);
|
||||
Curve->AddKey(2160.0f, 2.f);
|
||||
|
||||
Curve->AddKey(4319.5f, 2.f);
|
||||
Curve->AddKey(4320.0f, 4.f);
|
||||
}
|
||||
}
|
||||
|
||||
float FImGuiDPIScaleInfo::CalculateResolutionBasedScale() const
|
||||
{
|
||||
float ResolutionBasedScale = 1.f;
|
||||
if (bScaleWithCurve && GEngine && GEngine->GameUserSettings)
|
||||
{
|
||||
if (const FRichCurve* Curve = DPICurve.GetRichCurveConst())
|
||||
{
|
||||
ResolutionBasedScale *= Curve->Eval((float)GEngine->GameUserSettings->GetDesktopResolution().Y, 1.f);
|
||||
}
|
||||
}
|
||||
return ResolutionBasedScale;
|
||||
}
|
||||
|
||||
//====================================================================================================
|
||||
// UImGuiSettings
|
||||
//====================================================================================================
|
||||
|
||||
UImGuiSettings* UImGuiSettings::DefaultInstance = nullptr;
|
||||
|
||||
FSimpleMulticastDelegate UImGuiSettings::OnSettingsLoaded;
|
||||
|
||||
void UImGuiSettings::PostInitProperties()
|
||||
{
|
||||
Super::PostInitProperties();
|
||||
|
||||
if (IsTemplate())
|
||||
{
|
||||
DefaultInstance = this;
|
||||
OnSettingsLoaded.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
void UImGuiSettings::BeginDestroy()
|
||||
{
|
||||
Super::BeginDestroy();
|
||||
|
||||
if (DefaultInstance == this)
|
||||
{
|
||||
DefaultInstance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================================================
|
||||
// FImGuiModuleSettings
|
||||
//====================================================================================================
|
||||
|
||||
FImGuiModuleSettings::FImGuiModuleSettings(FImGuiModuleProperties& InProperties, FImGuiModuleCommands& InCommands)
|
||||
: Properties(InProperties)
|
||||
, Commands(InCommands)
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
FCoreUObjectDelegates::OnObjectPropertyChanged.AddRaw(this, &FImGuiModuleSettings::OnPropertyChanged);
|
||||
#endif
|
||||
|
||||
// Delegate initializer to support settings loaded after this object creation (in stand-alone builds) and potential
|
||||
// reloading of settings.
|
||||
UImGuiSettings::OnSettingsLoaded.AddRaw(this, &FImGuiModuleSettings::InitializeAllSettings);
|
||||
|
||||
// Call initializer to support settings already loaded (editor).
|
||||
InitializeAllSettings();
|
||||
}
|
||||
|
||||
FImGuiModuleSettings::~FImGuiModuleSettings()
|
||||
{
|
||||
|
||||
UImGuiSettings::OnSettingsLoaded.RemoveAll(this);
|
||||
|
||||
#if WITH_EDITOR
|
||||
FCoreUObjectDelegates::OnObjectPropertyChanged.RemoveAll(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::InitializeAllSettings()
|
||||
{
|
||||
UpdateSettings();
|
||||
UpdateDPIScaleInfo();
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::UpdateSettings()
|
||||
{
|
||||
if (UImGuiSettings* SettingsObject = UImGuiSettings::Get())
|
||||
{
|
||||
SetImGuiInputHandlerClass(SettingsObject->ImGuiInputHandlerClass);
|
||||
SetShareKeyboardInput(SettingsObject->bShareKeyboardInput);
|
||||
SetShareGamepadInput(SettingsObject->bShareGamepadInput);
|
||||
SetShareMouseInput(SettingsObject->bShareMouseInput);
|
||||
SetUseSoftwareCursor(SettingsObject->bUseSoftwareCursor);
|
||||
SetToggleInputKey(SettingsObject->ToggleInput);
|
||||
SetCanvasSizeInfo(SettingsObject->CanvasSize);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::UpdateDPIScaleInfo()
|
||||
{
|
||||
if (UImGuiSettings* SettingsObject = UImGuiSettings::Get())
|
||||
{
|
||||
SetDPIScaleInfo(SettingsObject->DPIScale);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::SetImGuiInputHandlerClass(const FSoftClassPath& ClassReference)
|
||||
{
|
||||
if (ImGuiInputHandlerClass != ClassReference)
|
||||
{
|
||||
ImGuiInputHandlerClass = ClassReference;
|
||||
OnImGuiInputHandlerClassChanged.Broadcast(ClassReference);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::SetShareKeyboardInput(bool bShare)
|
||||
{
|
||||
if (bShareKeyboardInput != bShare)
|
||||
{
|
||||
bShareKeyboardInput = bShare;
|
||||
Properties.SetKeyboardInputShared(bShare);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::SetShareGamepadInput(bool bShare)
|
||||
{
|
||||
if (bShareGamepadInput != bShare)
|
||||
{
|
||||
bShareGamepadInput = bShare;
|
||||
Properties.SetGamepadInputShared(bShare);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::SetShareMouseInput(bool bShare)
|
||||
{
|
||||
if (bShareMouseInput != bShare)
|
||||
{
|
||||
bShareMouseInput = bShare;
|
||||
Properties.SetMouseInputShared(bShare);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::SetUseSoftwareCursor(bool bUse)
|
||||
{
|
||||
if (bUseSoftwareCursor != bUse)
|
||||
{
|
||||
bUseSoftwareCursor = bUse;
|
||||
OnUseSoftwareCursorChanged.Broadcast(bUse);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::SetToggleInputKey(const FImGuiKeyInfo& KeyInfo)
|
||||
{
|
||||
if (ToggleInputKey != KeyInfo)
|
||||
{
|
||||
ToggleInputKey = KeyInfo;
|
||||
Commands.SetKeyBinding(FImGuiModuleCommands::ToggleInput, ToggleInputKey);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::SetIsDockingEnabled(bool bDockingEnabled)
|
||||
{
|
||||
if (bIsDockingEnabled != bDockingEnabled)
|
||||
{
|
||||
bIsDockingEnabled = bDockingEnabled;
|
||||
Properties.SetDockingEnabled(bDockingEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::SetCanvasSizeInfo(const FImGuiCanvasSizeInfo& CanvasSizeInfo)
|
||||
{
|
||||
if (CanvasSize != CanvasSizeInfo)
|
||||
{
|
||||
CanvasSize = CanvasSizeInfo;
|
||||
OnCanvasSizeChangedDelegate.Broadcast(CanvasSize);
|
||||
}
|
||||
}
|
||||
|
||||
void FImGuiModuleSettings::SetDPIScaleInfo(const FImGuiDPIScaleInfo& ScaleInfo)
|
||||
{
|
||||
DPIScale = ScaleInfo;
|
||||
OnDPIScaleChangedDelegate.Broadcast(DPIScale);
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
void FImGuiModuleSettings::OnPropertyChanged(class UObject* ObjectBeingModified, struct FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
if (ObjectBeingModified == UImGuiSettings::Get())
|
||||
{
|
||||
UpdateSettings();
|
||||
if (PropertyChangedEvent.MemberProperty
|
||||
&& (PropertyChangedEvent.MemberProperty->GetFName() == GET_MEMBER_NAME_CHECKED(FImGuiModuleSettings, DPIScale)))
|
||||
{
|
||||
UpdateDPIScaleInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // WITH_EDITOR
|
320
Source/ImGui/Private/ImGuiModuleSettings.h
Normal file
320
Source/ImGui/Private/ImGuiModuleSettings.h
Normal file
@ -0,0 +1,320 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VersionCompatibility.h"
|
||||
|
||||
#include <Curves/CurveFloat.h>
|
||||
#include <Delegates/Delegate.h>
|
||||
#include <InputCoreTypes.h>
|
||||
#include <Styling/SlateTypes.h>
|
||||
#include <UObject/Object.h>
|
||||
|
||||
// We use FSoftClassPath, which is supported by older and newer engine versions. Starting from 4.18, it is
|
||||
// a typedef of FSoftClassPath, which is also recognized by UHT.
|
||||
#if ENGINE_COMPATIBILITY_LEGACY_STRING_CLASS_REF
|
||||
#include <StringClassReference.h>
|
||||
#else
|
||||
#include <UObject/SoftObjectPath.h>
|
||||
#endif
|
||||
|
||||
#include "ImGuiModuleSettings.generated.h"
|
||||
|
||||
|
||||
/**
|
||||
* Struct containing key information that can be used for key binding. Using 'Undetermined' value for modifier keys
|
||||
* means that those keys should be ignored when testing for a match.
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FImGuiKeyInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Input")
|
||||
FKey Key;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Input")
|
||||
ECheckBoxState Shift = ECheckBoxState::Undetermined;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Input")
|
||||
ECheckBoxState Ctrl = ECheckBoxState::Undetermined;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Input")
|
||||
ECheckBoxState Alt = ECheckBoxState::Undetermined;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Input")
|
||||
ECheckBoxState Cmd = ECheckBoxState::Undetermined;
|
||||
|
||||
friend bool operator==(const FImGuiKeyInfo& Lhs, const FImGuiKeyInfo& Rhs)
|
||||
{
|
||||
return Lhs.Key == Rhs.Key
|
||||
&& Lhs.Shift == Rhs.Shift
|
||||
&& Lhs.Ctrl == Rhs.Ctrl
|
||||
&& Lhs.Alt == Rhs.Alt
|
||||
&& Lhs.Cmd == Rhs.Cmd;
|
||||
}
|
||||
|
||||
friend bool operator!=(const FImGuiKeyInfo& Lhs, const FImGuiKeyInfo& Rhs)
|
||||
{
|
||||
return !(Lhs == Rhs);
|
||||
}
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EImGuiCanvasSizeType : uint8
|
||||
{
|
||||
Custom UMETA(ToolTip = "Canvas will have a custom width and height."),
|
||||
Desktop UMETA(ToolTip = "Canvas will have the same width and height as the desktop."),
|
||||
Viewport UMETA(ToolTip = "Canvas will always have the same width and height as the viewport."),
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct with information how to calculate canvas size.
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FImGuiCanvasSizeInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Select how to specify canvas size.
|
||||
UPROPERTY(EditAnywhere, Category = "Canvas Size")
|
||||
EImGuiCanvasSizeType SizeType = EImGuiCanvasSizeType::Desktop;
|
||||
|
||||
// Custom canvas width.
|
||||
UPROPERTY(EditAnywhere, Category = "Canvas Size", meta = (ClampMin = 0, UIMin = 0))
|
||||
int32 Width = 3840;
|
||||
|
||||
// Custom canvas height.
|
||||
UPROPERTY(EditAnywhere, Category = "Canvas Size", meta = (ClampMin = 0, UIMin = 0))
|
||||
int32 Height = 2160;
|
||||
|
||||
// If this is true, canvas width or height may be extended, if the viewport size is larger.
|
||||
UPROPERTY(EditAnywhere, Category = "Canvas Size", meta = (ClampMin = 0, UIMin = 0))
|
||||
bool bExtendToViewport = true;
|
||||
|
||||
bool operator==(const FImGuiCanvasSizeInfo& Other) const
|
||||
{
|
||||
return (SizeType == Other.SizeType) && (Width == Other.Width)
|
||||
&& (Height == Other.Height) && (bExtendToViewport == Other.bExtendToViewport);
|
||||
}
|
||||
|
||||
bool operator!=(const FImGuiCanvasSizeInfo& Other) const { return !(*this == Other); }
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EImGuiDPIScaleMethod : uint8
|
||||
{
|
||||
ImGui UMETA(DisplayName = "ImGui", ToolTip = "Scale ImGui fonts and styles."),
|
||||
Slate UMETA(ToolTip = "Scale in Slate. ImGui canvas size will be adjusted to get the screen size that is the same as defined in the Canvas Size property.")
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct with DPI scale data.
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FImGuiDPIScaleInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
|
||||
// Whether to scale in ImGui or in Slate. Scaling in ImGui gives better looking results but Slate might be a better
|
||||
// option when layouts do not account for different fonts and styles. When scaling in Slate, ImGui canvas size will
|
||||
// be adjusted to get the screen size that is the same as defined in the Canvas Size property.
|
||||
UPROPERTY(EditAnywhere, Category = "DPI Scale")
|
||||
EImGuiDPIScaleMethod ScalingMethod = EImGuiDPIScaleMethod::ImGui;
|
||||
|
||||
// An optional scale to apply on top or instead of the curve-based scale.
|
||||
UPROPERTY(EditAnywhere, Category = "DPI Scale", meta = (ClampMin = 0, UIMin = 0))
|
||||
float Scale = 1.f;
|
||||
|
||||
// Curve mapping resolution height to scale.
|
||||
UPROPERTY(config, EditAnywhere, Category = "DPI Scale", meta = (XAxisName = "Resolution Height", YAxisName = "Scale", EditCondition = "bScaleWithCurve"))
|
||||
FRuntimeFloatCurve DPICurve;
|
||||
|
||||
// Whether to use curve-based scaling. If enabled, Scale will be multiplied by a value read from the DPICurve.
|
||||
// If disabled, only the Scale property will be used.
|
||||
UPROPERTY(config, EditAnywhere, Category = "DPI Scale")
|
||||
bool bScaleWithCurve = true;
|
||||
|
||||
public:
|
||||
|
||||
FImGuiDPIScaleInfo();
|
||||
|
||||
float GetImGuiScale() const { return ShouldScaleInSlate() ? 1.f : CalculateScale(); }
|
||||
|
||||
float GetSlateScale() const { return ShouldScaleInSlate() ? CalculateScale() : 1.f; }
|
||||
|
||||
bool ShouldScaleInSlate() const { return ScalingMethod == EImGuiDPIScaleMethod::Slate; }
|
||||
|
||||
private:
|
||||
|
||||
float CalculateScale() const { return Scale * CalculateResolutionBasedScale(); }
|
||||
|
||||
float CalculateResolutionBasedScale() const;
|
||||
};
|
||||
|
||||
// UObject used for loading and saving ImGui settings. To access actual settings use FImGuiModuleSettings interface.
|
||||
UCLASS(config=ImGui, defaultconfig)
|
||||
class UImGuiSettings : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
// Get default instance or null if it is not loaded.
|
||||
static UImGuiSettings* Get() { return DefaultInstance; }
|
||||
|
||||
// Delegate raised when default instance is loaded.
|
||||
static FSimpleMulticastDelegate OnSettingsLoaded;
|
||||
|
||||
virtual void PostInitProperties() override;
|
||||
virtual void BeginDestroy() override;
|
||||
|
||||
protected:
|
||||
|
||||
// Path to own implementation of ImGui Input Handler allowing to customize handling of keyboard and gamepad input.
|
||||
// If not set then default handler is used.
|
||||
UPROPERTY(EditAnywhere, config, Category = "Extensions", meta = (MetaClass = "/Script/ImGui.ImGuiInputHandler"))
|
||||
FSoftClassPath ImGuiInputHandlerClass;
|
||||
|
||||
// Whether ImGui should share keyboard input with game.
|
||||
// This defines initial behaviour which can be later changed using 'ImGui.ToggleKeyboardInputSharing' command or
|
||||
// module properties interface.
|
||||
UPROPERTY(EditAnywhere, config, Category = "Input")
|
||||
bool bShareKeyboardInput = false;
|
||||
|
||||
// Whether ImGui should share gamepad input with game.
|
||||
// This defines initial behaviour which can be later changed using 'ImGui.ToggleGamepadInputSharing' command or
|
||||
// module properties interface.
|
||||
UPROPERTY(EditAnywhere, config, Category = "Input")
|
||||
bool bShareGamepadInput = false;
|
||||
|
||||
// Whether ImGui should share mouse input with game.
|
||||
// This defines initial behaviour which can be later changed using 'ImGui.ToggleMouseInputSharing' command or
|
||||
// module properties interface.
|
||||
UPROPERTY(EditAnywhere, config, Category = "Input")
|
||||
bool bShareMouseInput = false;
|
||||
|
||||
// Whether docking should be enabled.
|
||||
// This defines initial behaviour which can be later changed using 'ImGui.ToggleDockingEnabled' command or
|
||||
// module properties interface.
|
||||
UPROPERTY(EditAnywhere, config, Category = "Input", DisplayName = "Enable Docking")
|
||||
bool bIsDockingEnabled = true;
|
||||
|
||||
// If true, then in input mode ImGui will draw its own cursor in place of the hardware one.
|
||||
// When disabled (default) there is a noticeable difference between cursor position seen by ImGui and position on
|
||||
// the screen. Enabling this option removes that effect but with lower frame-rates UI becomes quickly unusable.
|
||||
UPROPERTY(EditAnywhere, config, Category = "Input", AdvancedDisplay)
|
||||
bool bUseSoftwareCursor = false;
|
||||
|
||||
// Define a shortcut key to 'ImGui.ToggleInput' command. Binding is only set if the key field is valid.
|
||||
// Note that modifier key properties can be set to one of the three values: undetermined means that state of the given
|
||||
// modifier is not important, checked means that it needs to be pressed and unchecked means that it cannot be pressed.
|
||||
//
|
||||
// This binding is using Player Input's DebugExecBindings which only works in non-shipment builds.
|
||||
UPROPERTY(EditAnywhere, config, Category = "Keyboard Shortcuts")
|
||||
FImGuiKeyInfo ToggleInput;
|
||||
|
||||
// Chose how to define the ImGui canvas size. Select between custom, desktop and viewport.
|
||||
UPROPERTY(EditAnywhere, config, Category = "Canvas Size")
|
||||
FImGuiCanvasSizeInfo CanvasSize;
|
||||
|
||||
// Setup DPI Scale.
|
||||
UPROPERTY(EditAnywhere, config, Category = "DPI Scale", Meta = (ShowOnlyInnerProperties))
|
||||
FImGuiDPIScaleInfo DPIScale;
|
||||
|
||||
static UImGuiSettings* DefaultInstance;
|
||||
|
||||
friend class FImGuiModuleSettings;
|
||||
};
|
||||
|
||||
|
||||
class FImGuiModuleCommands;
|
||||
class FImGuiModuleProperties;
|
||||
|
||||
// Interface for ImGui module settings. It shadows all the settings and keep them in sync after UImGuiSettings class is
|
||||
// loaded, but it can also work before that time what simplifies workflow in early-loading scenarios.
|
||||
// It binds to module properties and commands objects that need to be passed during construction.
|
||||
class FImGuiModuleSettings
|
||||
{
|
||||
public:
|
||||
|
||||
// Generic delegate used to notify changes of boolean properties.
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FBoolChangeDelegate, bool);
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FStringClassReferenceChangeDelegate, const FSoftClassPath&);
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FImGuiCanvasSizeInfoChangeDelegate, const FImGuiCanvasSizeInfo&);
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FImGuiDPIScaleInfoChangeDelegate, const FImGuiDPIScaleInfo&);
|
||||
|
||||
// Constructor for ImGui module settings. It will bind to instances of module properties and commands and will
|
||||
// update them every time when settings are changed.
|
||||
//
|
||||
// @param InProperties - Instance of module properties that will be bound and updated by this object.
|
||||
// @param InCommands - Instance of module commands that will be bound and updated by this object.
|
||||
FImGuiModuleSettings(FImGuiModuleProperties& InProperties, FImGuiModuleCommands& InCommands);
|
||||
~FImGuiModuleSettings();
|
||||
|
||||
// It doesn't offer interface for settings that define initial values for properties, as those are passed during
|
||||
// start-up and should be accessed trough properties interface. Remaining settings can have getter and/or change
|
||||
// event that are defined depending on needs.
|
||||
|
||||
// Get the path to custom implementation of ImGui Input Handler.
|
||||
const FSoftClassPath& GetImGuiInputHandlerClass() const { return ImGuiInputHandlerClass; }
|
||||
|
||||
// Get the software cursor configuration.
|
||||
bool UseSoftwareCursor() const { return bUseSoftwareCursor; }
|
||||
|
||||
// Get the shortcut configuration for 'ImGui.ToggleInput' command.
|
||||
const FImGuiKeyInfo& GetToggleInputKey() const { return ToggleInputKey; }
|
||||
|
||||
// Get the information how to calculate the canvas size.
|
||||
const FImGuiCanvasSizeInfo& GetCanvasSizeInfo() const { return CanvasSize; }
|
||||
|
||||
// Get the DPI Scale information.
|
||||
const FImGuiDPIScaleInfo& GetDPIScaleInfo() const { return DPIScale; }
|
||||
|
||||
// Delegate raised when ImGui Input Handle is changed.
|
||||
FStringClassReferenceChangeDelegate OnImGuiInputHandlerClassChanged;
|
||||
|
||||
// Delegate raised when software cursor configuration is changed.
|
||||
FBoolChangeDelegate OnUseSoftwareCursorChanged;
|
||||
|
||||
// Delegate raised when information how to calculate the canvas size is changed.
|
||||
FImGuiCanvasSizeInfoChangeDelegate OnCanvasSizeChangedDelegate;
|
||||
|
||||
// Delegate raised when the DPI scale is changed.
|
||||
FImGuiDPIScaleInfoChangeDelegate OnDPIScaleChangedDelegate;
|
||||
|
||||
private:
|
||||
|
||||
void InitializeAllSettings();
|
||||
void UpdateSettings();
|
||||
void UpdateDPIScaleInfo();
|
||||
|
||||
void SetImGuiInputHandlerClass(const FSoftClassPath& ClassReference);
|
||||
void SetShareKeyboardInput(bool bShare);
|
||||
void SetShareGamepadInput(bool bShare);
|
||||
void SetShareMouseInput(bool bShare);
|
||||
void SetUseSoftwareCursor(bool bUse);
|
||||
void SetToggleInputKey(const FImGuiKeyInfo& KeyInfo);
|
||||
void SetIsDockingEnabled(bool bDockingEnabled);
|
||||
void SetCanvasSizeInfo(const FImGuiCanvasSizeInfo& CanvasSizeInfo);
|
||||
void SetDPIScaleInfo(const FImGuiDPIScaleInfo& ScaleInfo);
|
||||
|
||||
#if WITH_EDITOR
|
||||
void OnPropertyChanged(class UObject* ObjectBeingModified, struct FPropertyChangedEvent& PropertyChangedEvent);
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
FImGuiModuleProperties& Properties;
|
||||
FImGuiModuleCommands& Commands;
|
||||
|
||||
FSoftClassPath ImGuiInputHandlerClass;
|
||||
FImGuiKeyInfo ToggleInputKey;
|
||||
FImGuiCanvasSizeInfo CanvasSize;
|
||||
FImGuiDPIScaleInfo DPIScale;
|
||||
bool bShareKeyboardInput = false;
|
||||
bool bShareGamepadInput = false;
|
||||
bool bShareMouseInput = false;
|
||||
bool bUseSoftwareCursor = false;
|
||||
bool bIsDockingEnabled = true;
|
||||
};
|
@ -1,11 +0,0 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
// Module
|
||||
#include "ImGuiModule.h"
|
||||
|
||||
// Engine
|
||||
#include <Core.h>
|
||||
#include <Engine.h>
|
||||
|
||||
// You should place include statements to your module's private header files here. You only need to
|
||||
// add includes for headers that are used in most of your module's source files though.
|
24
Source/ImGui/Private/ImGuiTextureHandle.cpp
Normal file
24
Source/ImGui/Private/ImGuiTextureHandle.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiTextureHandle.h"
|
||||
|
||||
#include "ImGuiInteroperability.h"
|
||||
|
||||
|
||||
FImGuiTextureHandle::FImGuiTextureHandle()
|
||||
: FImGuiTextureHandle(NAME_None, ImGuiInterops::ToImTextureID(INDEX_NONE))
|
||||
{
|
||||
}
|
||||
|
||||
FImGuiTextureHandle::FImGuiTextureHandle(const FName& InName, ImTextureID InTextureId)
|
||||
: Name(InName)
|
||||
, TextureId(InTextureId)
|
||||
{
|
||||
const TextureIndex Index = ImGuiInterops::ToTextureIndex(TextureId);
|
||||
checkf((Index == INDEX_NONE) == (Name == NAME_None),
|
||||
TEXT("Mismatch between Name and TextureId parameters, with only one indicating a null handle.")
|
||||
TEXT(" Name = '%s', TextureIndex(TextureId) = %d"), *Name.ToString(), Index);
|
||||
}
|
||||
|
||||
// FImGuiTextureHandle::HasValidEntry() is implemented in ImGuiModule.cpp to get access to FImGuiModuleManager instance
|
||||
// without referencing in this class.
|
@ -1,547 +0,0 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
|
||||
#include "SImGuiWidget.h"
|
||||
|
||||
#include "ImGuiContextManager.h"
|
||||
#include "ImGuiContextProxy.h"
|
||||
#include "ImGuiInteroperability.h"
|
||||
#include "ImGuiModuleManager.h"
|
||||
#include "TextureManager.h"
|
||||
#include "Utilities/ScopeGuards.h"
|
||||
|
||||
#include <Engine/Console.h>
|
||||
|
||||
|
||||
// High enough z-order guarantees that ImGui output is rendered on top of the game UI.
|
||||
constexpr int32 IMGUI_WIDGET_Z_ORDER = 10000;
|
||||
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogImGuiWidget, Warning, All);
|
||||
|
||||
#define TEXT_INPUT_MODE(Val) (\
|
||||
(Val) == EInputMode::MouseAndKeyboard ? TEXT("MouseAndKeyboard") :\
|
||||
(Val) == EInputMode::MousePointerOnly ? TEXT("MousePointerOnly") :\
|
||||
TEXT("None"))
|
||||
|
||||
#define TEXT_BOOL(Val) ((Val) ? TEXT("true") : TEXT("false"))
|
||||
|
||||
|
||||
namespace CVars
|
||||
{
|
||||
TAutoConsoleVariable<int> InputEnabled(TEXT("ImGui.InputEnabled"), 0,
|
||||
TEXT("Allows to enable or disable ImGui input mode.\n")
|
||||
TEXT("0: disabled (default)\n")
|
||||
TEXT("1: enabled, input is routed to ImGui and with a few exceptions is consumed."),
|
||||
ECVF_Default);
|
||||
|
||||
TAutoConsoleVariable<int> DebugWidget(TEXT("ImGui.Debug.Widget"), 0,
|
||||
TEXT("Show debug for SImGuiWidget.\n")
|
||||
TEXT("0: disabled (default)\n")
|
||||
TEXT("1: enabled."),
|
||||
ECVF_Default);
|
||||
}
|
||||
|
||||
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||
void SImGuiWidget::Construct(const FArguments& InArgs)
|
||||
{
|
||||
checkf(InArgs._ModuleManager, TEXT("Null Module Manager argument"));
|
||||
checkf(InArgs._GameViewport, TEXT("Null Game Viewport argument"));
|
||||
|
||||
ModuleManager = InArgs._ModuleManager;
|
||||
GameViewport = InArgs._GameViewport;
|
||||
ContextIndex = InArgs._ContextIndex;
|
||||
|
||||
// NOTE: We could allow null game viewports (for instance to attach to non-viewport widgets) but we would need
|
||||
// to modify a few functions that assume valid viewport pointer.
|
||||
GameViewport->AddViewportWidgetContent(SharedThis(this), IMGUI_WIDGET_Z_ORDER);
|
||||
|
||||
// Disable mouse cursor over this widget as we will use ImGui to draw it.
|
||||
SetCursor(EMouseCursor::None);
|
||||
|
||||
// Sync visibility with default input enabled state.
|
||||
SetVisibilityFromInputEnabled();
|
||||
|
||||
// Register to get post-update notifications, so we can clean frame updates.
|
||||
ModuleManager->OnPostImGuiUpdate().AddRaw(this, &SImGuiWidget::OnPostImGuiUpdate);
|
||||
|
||||
// Bind this widget to its context proxy.
|
||||
auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
||||
checkf(ContextProxy, TEXT("Missing context during widget construction: ContextIndex = %d"), ContextIndex);
|
||||
ContextProxy->OnDraw().AddRaw(this, &SImGuiWidget::OnDebugDraw);
|
||||
ContextProxy->SetInputState(&InputState);
|
||||
}
|
||||
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||
|
||||
SImGuiWidget::~SImGuiWidget()
|
||||
{
|
||||
// Remove binding between this widget and its context proxy.
|
||||
if (auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex))
|
||||
{
|
||||
ContextProxy->OnDraw().RemoveAll(this);
|
||||
ContextProxy->RemoveInputState(&InputState);
|
||||
}
|
||||
|
||||
// Unregister from post-update notifications.
|
||||
ModuleManager->OnPostImGuiUpdate().RemoveAll(this);
|
||||
}
|
||||
|
||||
void SImGuiWidget::Detach()
|
||||
{
|
||||
if (GameViewport.IsValid())
|
||||
{
|
||||
GameViewport->RemoveViewportWidgetContent(SharedThis(this));
|
||||
GameViewport.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void SImGuiWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
||||
{
|
||||
Super::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
||||
|
||||
UpdateMouseStatus();
|
||||
|
||||
// Note: Moving that update to console variable sink or callback might seem like a better alternative but input
|
||||
// setup in this function is better handled here.
|
||||
UpdateInputEnabled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent)
|
||||
{
|
||||
if (IsConsoleOpened())
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
InputState.AddCharacter(CharacterEvent.GetCharacter());
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
|
||||
{
|
||||
if (IsConsoleOpened() || IgnoreKeyEvent(KeyEvent))
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), true);
|
||||
CopyModifierKeys(KeyEvent);
|
||||
|
||||
// If this is tilde key then let input through and release the focus to allow console to process it.
|
||||
if (KeyEvent.GetKey() == EKeys::Tilde)
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
|
||||
{
|
||||
// Even if we don't send new keystrokes to ImGui, we still handle key up events, to make sure that we clear keys
|
||||
// pressed before suppressing keyboard input.
|
||||
InputState.SetKeyDown(ImGuiInterops::GetKeyIndex(KeyEvent), false);
|
||||
CopyModifierKeys(KeyEvent);
|
||||
|
||||
// If console is opened we notify key change but we also let event trough, so it can be handled by console.
|
||||
return IsConsoleOpened() ? FReply::Unhandled() : FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), true);
|
||||
CopyModifierKeys(MouseEvent);
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), true);
|
||||
CopyModifierKeys(MouseEvent);
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(MouseEvent), false);
|
||||
CopyModifierKeys(MouseEvent);
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState.AddMouseWheelDelta(MouseEvent.GetWheelDelta());
|
||||
CopyModifierKeys(MouseEvent);
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
InputState.SetMousePosition(MouseEvent.GetScreenSpacePosition() - MyGeometry.AbsolutePosition);
|
||||
CopyModifierKeys(MouseEvent);
|
||||
|
||||
// This event is called in every frame when we have a mouse, so we can use it to raise notifications.
|
||||
NotifyMouseEvent();
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SImGuiWidget::OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& FocusEvent)
|
||||
{
|
||||
Super::OnFocusReceived(MyGeometry, FocusEvent);
|
||||
|
||||
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Focus Received."), ContextIndex);
|
||||
|
||||
// If widget has a keyboard focus we always maintain mouse input. Technically, if mouse is outside of the widget
|
||||
// area it won't generate events but we freeze its state until it either comes back or input is completely lost.
|
||||
UpdateInputMode(true, IsDirectlyHovered());
|
||||
|
||||
FSlateApplication::Get().ResetToDefaultPointerInputSettings();
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
void SImGuiWidget::OnFocusLost(const FFocusEvent& FocusEvent)
|
||||
{
|
||||
Super::OnFocusLost(FocusEvent);
|
||||
|
||||
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Focus Lost."), ContextIndex);
|
||||
|
||||
UpdateInputMode(false, IsDirectlyHovered());
|
||||
}
|
||||
|
||||
void SImGuiWidget::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
Super::OnMouseEnter(MyGeometry, MouseEvent);
|
||||
|
||||
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Mouse Enter."), ContextIndex);
|
||||
|
||||
// If mouse enters while input is active then we need to update mouse buttons because there is a chance that we
|
||||
// missed some events.
|
||||
if (InputMode != EInputMode::None)
|
||||
{
|
||||
for (const FKey& Button : { EKeys::LeftMouseButton, EKeys::MiddleMouseButton, EKeys::RightMouseButton, EKeys::ThumbMouseButton, EKeys::ThumbMouseButton2 })
|
||||
{
|
||||
InputState.SetMouseDown(ImGuiInterops::GetMouseIndex(Button), MouseEvent.IsMouseButtonDown(Button));
|
||||
}
|
||||
}
|
||||
|
||||
UpdateInputMode(HasKeyboardFocus(), true);
|
||||
}
|
||||
|
||||
void SImGuiWidget::OnMouseLeave(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
Super::OnMouseLeave(MouseEvent);
|
||||
|
||||
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Mouse Leave."), ContextIndex);
|
||||
|
||||
UpdateInputMode(HasKeyboardFocus(), false);
|
||||
}
|
||||
|
||||
void SImGuiWidget::CopyModifierKeys(const FInputEvent& InputEvent)
|
||||
{
|
||||
InputState.SetControlDown(InputEvent.IsControlDown());
|
||||
InputState.SetShiftDown(InputEvent.IsShiftDown());
|
||||
InputState.SetAltDown(InputEvent.IsAltDown());
|
||||
}
|
||||
|
||||
void SImGuiWidget::CopyModifierKeys(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
if (InputMode == EInputMode::MousePointerOnly)
|
||||
{
|
||||
CopyModifierKeys(static_cast<const FInputEvent&>(MouseEvent));
|
||||
}
|
||||
}
|
||||
|
||||
bool SImGuiWidget::IsConsoleOpened() const
|
||||
{
|
||||
return GameViewport->ViewportConsole && GameViewport->ViewportConsole->ConsoleState != NAME_None;
|
||||
}
|
||||
|
||||
bool SImGuiWidget::IgnoreKeyEvent(const FKeyEvent& KeyEvent) const
|
||||
{
|
||||
// Ignore console open/close events.
|
||||
if (KeyEvent.GetKey() == EKeys::Tilde)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ignore escape keys unless they are needed to cancel operations in ImGui.
|
||||
if (KeyEvent.GetKey() == EKeys::Escape)
|
||||
{
|
||||
auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
||||
if (!ContextProxy || !ContextProxy->HasActiveItem())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SImGuiWidget::SetVisibilityFromInputEnabled()
|
||||
{
|
||||
// If we don't use input disable hit test to make this widget invisible for cursors hit detection.
|
||||
SetVisibility(bInputEnabled ? EVisibility::Visible : EVisibility::HitTestInvisible);
|
||||
|
||||
UE_LOG(LogImGuiWidget, VeryVerbose, TEXT("ImGui Widget %d - Visibility updated to '%s'."),
|
||||
ContextIndex, *GetVisibility().ToString());
|
||||
}
|
||||
|
||||
void SImGuiWidget::UpdateInputEnabled()
|
||||
{
|
||||
const bool bEnabled = CVars::InputEnabled.GetValueOnGameThread() > 0;
|
||||
if (bInputEnabled != bEnabled)
|
||||
{
|
||||
bInputEnabled = bEnabled;
|
||||
|
||||
UE_LOG(LogImGuiWidget, Log, TEXT("ImGui Widget %d - Input Enabled changed to '%s'."),
|
||||
ContextIndex, TEXT_BOOL(bInputEnabled));
|
||||
|
||||
SetVisibilityFromInputEnabled();
|
||||
|
||||
if (!bInputEnabled)
|
||||
{
|
||||
auto& Slate = FSlateApplication::Get();
|
||||
if (Slate.GetKeyboardFocusedWidget().Get() == this)
|
||||
{
|
||||
Slate.ResetToDefaultPointerInputSettings();
|
||||
Slate.SetUserFocus(Slate.GetUserIndexForKeyboard(),
|
||||
PreviousUserFocusedWidget.IsValid() ? PreviousUserFocusedWidget.Pin() : GameViewport->GetGameViewportWidget());
|
||||
}
|
||||
|
||||
PreviousUserFocusedWidget.Reset();
|
||||
|
||||
UpdateInputMode(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Some widgets, like console, can reset focus to viewport after we already grabbed it. If we detect that
|
||||
// viewport has a focus while input is enabled we will take it.
|
||||
if (bInputEnabled && !HasKeyboardFocus() && !IsConsoleOpened())
|
||||
{
|
||||
const auto& ViewportWidget = GameViewport->GetGameViewportWidget();
|
||||
if (ViewportWidget->HasKeyboardFocus() || ViewportWidget->HasFocusedDescendants())
|
||||
{
|
||||
auto& Slate = FSlateApplication::Get();
|
||||
PreviousUserFocusedWidget = Slate.GetUserFocusedWidget(Slate.GetUserIndexForKeyboard());
|
||||
Slate.SetKeyboardFocus(SharedThis(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SImGuiWidget::UpdateInputMode(bool bHasKeyboardFocus, bool bHasMousePointer)
|
||||
{
|
||||
const EInputMode NewInputMode =
|
||||
bHasKeyboardFocus ? EInputMode::MouseAndKeyboard :
|
||||
bHasMousePointer ? EInputMode::MousePointerOnly :
|
||||
EInputMode::None;
|
||||
|
||||
if (InputMode != NewInputMode)
|
||||
{
|
||||
UE_LOG(LogImGuiWidget, Verbose, TEXT("ImGui Widget %d - Input Mode changed from '%s' to '%s'."),
|
||||
ContextIndex, TEXT_INPUT_MODE(InputMode), TEXT_INPUT_MODE(NewInputMode));
|
||||
|
||||
// We need to reset input components if we are either fully shutting down or we are downgrading from full to
|
||||
// mouse-only input mode.
|
||||
if (NewInputMode == EInputMode::None)
|
||||
{
|
||||
InputState.ResetState();
|
||||
}
|
||||
else if (InputMode == EInputMode::MouseAndKeyboard)
|
||||
{
|
||||
InputState.ResetKeyboardState();
|
||||
}
|
||||
|
||||
InputMode = NewInputMode;
|
||||
|
||||
ClearMouseEventNotification();
|
||||
}
|
||||
|
||||
InputState.SetMousePointer(bHasMousePointer);
|
||||
}
|
||||
|
||||
void SImGuiWidget::UpdateMouseStatus()
|
||||
{
|
||||
// Note: Mouse leave events can get lost if other viewport takes mouse capture (for instance console is opened by
|
||||
// different viewport when this widget is hovered). With that we lose a chance to cleanup and hide ImGui pointer.
|
||||
// We could either update ImGui pointer in every frame or like below, use mouse events to catch when mouse is lost.
|
||||
|
||||
if (InputMode == EInputMode::MousePointerOnly)
|
||||
{
|
||||
if (!HasMouseEventNotification())
|
||||
{
|
||||
UpdateInputMode(false, IsDirectlyHovered());
|
||||
}
|
||||
ClearMouseEventNotification();
|
||||
}
|
||||
}
|
||||
|
||||
void SImGuiWidget::OnPostImGuiUpdate()
|
||||
{
|
||||
if (InputMode != EInputMode::None)
|
||||
{
|
||||
InputState.ClearUpdateState();
|
||||
}
|
||||
}
|
||||
|
||||
int32 SImGuiWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect,
|
||||
FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& WidgetStyle, bool bParentEnabled) const
|
||||
{
|
||||
if (FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex))
|
||||
{
|
||||
// Calculate offset that will transform vertex positions to screen space - rounded to avoid half pixel offsets.
|
||||
const FVector2D VertexPositionOffset{ FMath::RoundToFloat(MyClippingRect.Left), FMath::RoundToFloat(MyClippingRect.Top) };
|
||||
|
||||
// Convert clipping rectangle to format required by Slate vertex.
|
||||
const FSlateRotatedRect VertexClippingRect{ MyClippingRect };
|
||||
|
||||
for (const auto& DrawList : ContextProxy->GetDrawData())
|
||||
{
|
||||
#if WITH_OBSOLETE_CLIPPING_API
|
||||
DrawList.CopyVertexData(VertexBuffer, VertexPositionOffset, VertexClippingRect);
|
||||
|
||||
// Get access to the Slate scissor rectangle defined in Slate Core API, so we can customize elements drawing.
|
||||
extern SLATECORE_API TOptional<FShortRect> GSlateScissorRect;
|
||||
auto GSlateScissorRectSaver = ScopeGuards::MakeStateSaver(GSlateScissorRect);
|
||||
#else
|
||||
DrawList.CopyVertexData(VertexBuffer, VertexPositionOffset);
|
||||
#endif // WITH_OBSOLETE_CLIPPING_API
|
||||
|
||||
int IndexBufferOffset = 0;
|
||||
for (int CommandNb = 0; CommandNb < DrawList.NumCommands(); CommandNb++)
|
||||
{
|
||||
const auto& DrawCommand = DrawList.GetCommand(CommandNb);
|
||||
|
||||
DrawList.CopyIndexData(IndexBuffer, IndexBufferOffset, DrawCommand.NumElements);
|
||||
|
||||
// Advance offset by number of copied elements to position it for the next command.
|
||||
IndexBufferOffset += DrawCommand.NumElements;
|
||||
|
||||
// Get texture resource handle for this draw command (null index will be also mapped to a valid texture).
|
||||
const FSlateResourceHandle& Handle = ModuleManager->GetTextureManager().GetTextureHandle(DrawCommand.TextureId);
|
||||
|
||||
// Transform clipping rectangle to screen space and apply to elements that we draw.
|
||||
const FSlateRect ClippingRect = DrawCommand.ClippingRect.OffsetBy(MyClippingRect.GetTopLeft()).IntersectionWith(MyClippingRect);
|
||||
|
||||
#if WITH_OBSOLETE_CLIPPING_API
|
||||
GSlateScissorRect = FShortRect{ ClippingRect };
|
||||
#else
|
||||
OutDrawElements.PushClip(FSlateClippingZone{ ClippingRect });
|
||||
#endif // WITH_OBSOLETE_CLIPPING_API
|
||||
|
||||
// Add elements to the list.
|
||||
FSlateDrawElement::MakeCustomVerts(OutDrawElements, LayerId, Handle, VertexBuffer, IndexBuffer, nullptr, 0, 0);
|
||||
|
||||
#if !WITH_OBSOLETE_CLIPPING_API
|
||||
OutDrawElements.PopClip();
|
||||
#endif // WITH_OBSOLETE_CLIPPING_API
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LayerId;
|
||||
}
|
||||
|
||||
FVector2D SImGuiWidget::ComputeDesiredSize(float) const
|
||||
{
|
||||
return FVector2D{ 3840.f, 2160.f };
|
||||
}
|
||||
|
||||
// Controls tweaked for 2-columns layout.
|
||||
namespace TwoColumns
|
||||
{
|
||||
static void GroupName(const char* Name)
|
||||
{
|
||||
ImGui::TextColored({ 0.5f, 0.5f, 0.5f, 1.f }, Name); ImGui::NextColumn(); ImGui::NextColumn();
|
||||
}
|
||||
|
||||
static void Value(const char* Label, int Value)
|
||||
{
|
||||
ImGui::Text("%s:", Label); ImGui::NextColumn();
|
||||
ImGui::Text("%d", Value); ImGui::NextColumn();
|
||||
}
|
||||
|
||||
static void Value(const char* Label, bool bValue)
|
||||
{
|
||||
ImGui::Text("%s:", Label); ImGui::NextColumn();
|
||||
ImGui::Text("%ls", TEXT_BOOL(bValue)); ImGui::NextColumn();
|
||||
}
|
||||
|
||||
static void Value(const char* Label, const TCHAR* Value)
|
||||
{
|
||||
ImGui::Text("%s:", Label); ImGui::NextColumn();
|
||||
ImGui::Text("%ls", Value); ImGui::NextColumn();
|
||||
}
|
||||
}
|
||||
|
||||
void SImGuiWidget::OnDebugDraw()
|
||||
{
|
||||
bool bDebug = CVars::DebugWidget.GetValueOnGameThread() > 0;
|
||||
if (bDebug)
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(380, 360), ImGuiSetCond_Once);
|
||||
if (ImGui::Begin("ImGui Widget Debug", &bDebug))
|
||||
{
|
||||
ImGui::Columns(2, nullptr, false);
|
||||
|
||||
TwoColumns::Value("Context Index", ContextIndex);
|
||||
FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
||||
TwoColumns::Value("Context Name", ContextProxy ? *ContextProxy->GetName() : TEXT("< Null >"));
|
||||
TwoColumns::Value("Game Viewport", *GameViewport->GetName());
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
TwoColumns::Value("Input Enabled", bInputEnabled);
|
||||
TwoColumns::Value("Input Mode", TEXT_INPUT_MODE(InputMode));
|
||||
TwoColumns::Value("Input Has Mouse Pointer", InputState.HasMousePointer());
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
const float GroupIndent = 5.f;
|
||||
|
||||
TwoColumns::GroupName("Widget");
|
||||
ImGui::Indent(GroupIndent);
|
||||
{
|
||||
TwoColumns::Value("Visibility", *GetVisibility().ToString());
|
||||
TwoColumns::Value("Is Hovered", IsHovered());
|
||||
TwoColumns::Value("Is Directly Hovered", IsDirectlyHovered());
|
||||
TwoColumns::Value("Has Keyboard Input", HasKeyboardFocus());
|
||||
}
|
||||
ImGui::Unindent(GroupIndent);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
TwoColumns::GroupName("Viewport Widget");
|
||||
ImGui::Indent(GroupIndent);
|
||||
{
|
||||
const auto& ViewportWidget = GameViewport->GetGameViewportWidget();
|
||||
TwoColumns::Value("Is Hovered", ViewportWidget->IsHovered());
|
||||
TwoColumns::Value("Is Directly Hovered", ViewportWidget->IsDirectlyHovered());
|
||||
TwoColumns::Value("Has Mouse Capture", ViewportWidget->HasMouseCapture());
|
||||
TwoColumns::Value("Has Keyboard Input", ViewportWidget->HasKeyboardFocus());
|
||||
TwoColumns::Value("Has Focused Descendants", ViewportWidget->HasFocusedDescendants());
|
||||
auto Widget = PreviousUserFocusedWidget.Pin();
|
||||
TwoColumns::Value("Previous User Focused", Widget.IsValid() ? *Widget->GetTypeAsString() : TEXT("None"));
|
||||
}
|
||||
ImGui::Unindent(GroupIndent);
|
||||
|
||||
ImGui::Columns(1);
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (!bDebug)
|
||||
{
|
||||
CVars::DebugWidget->ClearFlags(ECVF_SetByConsole);
|
||||
CVars::DebugWidget->Set(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef TEXT_INPUT_MODE
|
||||
#undef TEXT_BOOL
|
@ -1,16 +1,50 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
|
||||
#include "TextureManager.h"
|
||||
#include "RHITypes.h"
|
||||
#include <Engine/Texture2D.h>
|
||||
#include <Framework/Application/SlateApplication.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
TextureIndex FTextureManager::CreateTexture(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, bool bDeleteSrcData)
|
||||
void FTextureManager::InitializeErrorTexture(const FColor& Color)
|
||||
{
|
||||
checkf(FindTextureIndex(Name) == INDEX_NONE, TEXT("Trying to create texture using resource name '%s' that is already registered."), *Name.ToString());
|
||||
CreatePlainTextureInternal(NAME_ErrorTexture, 2, 2, Color);
|
||||
}
|
||||
|
||||
TextureIndex FTextureManager::CreateTexture(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, TFunction<void(uint8*)> SrcDataCleanup)
|
||||
{
|
||||
checkf(Name != NAME_None, TEXT("Trying to create a texture with a name 'NAME_None' is not allowed."));
|
||||
|
||||
return CreateTextureInternal(Name, Width, Height, SrcBpp, SrcData, SrcDataCleanup);
|
||||
}
|
||||
|
||||
TextureIndex FTextureManager::CreatePlainTexture(const FName& Name, int32 Width, int32 Height, FColor Color)
|
||||
{
|
||||
checkf(Name != NAME_None, TEXT("Trying to create a texture with a name 'NAME_None' is not allowed."));
|
||||
|
||||
return CreatePlainTextureInternal(Name, Width, Height, Color);
|
||||
}
|
||||
|
||||
TextureIndex FTextureManager::CreateTextureResources(const FName& Name, UTexture* Texture)
|
||||
{
|
||||
checkf(Name != NAME_None, TEXT("Trying to create texture resources with a name 'NAME_None' is not allowed."));
|
||||
checkf(Texture, TEXT("Null Texture."));
|
||||
|
||||
// Create an entry for the texture.
|
||||
return AddTextureEntry(Name, Texture, false);
|
||||
}
|
||||
|
||||
void FTextureManager::ReleaseTextureResources(TextureIndex Index)
|
||||
{
|
||||
checkf(IsInRange(Index), TEXT("Invalid texture index %d. Texture resources array has %d entries total."), Index, TextureResources.Num());
|
||||
|
||||
TextureResources[Index] = {};
|
||||
}
|
||||
|
||||
TextureIndex FTextureManager::CreateTextureInternal(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, TFunction<void(uint8*)> SrcDataCleanup)
|
||||
{
|
||||
// Create a texture.
|
||||
UTexture2D* Texture = UTexture2D::CreateTransient(Width, Height);
|
||||
|
||||
@ -19,21 +53,26 @@ TextureIndex FTextureManager::CreateTexture(const FName& Name, int32 Width, int3
|
||||
|
||||
// Update texture data.
|
||||
FUpdateTextureRegion2D* TextureRegion = new FUpdateTextureRegion2D(0, 0, 0, 0, Width, Height);
|
||||
auto DataCleanup = [bDeleteSrcData](uint8* Data, const FUpdateTextureRegion2D* UpdateRegion)
|
||||
auto DataCleanup = [SrcDataCleanup](uint8* Data, const FUpdateTextureRegion2D* UpdateRegion)
|
||||
{
|
||||
if (bDeleteSrcData)
|
||||
{
|
||||
delete Data;
|
||||
}
|
||||
SrcDataCleanup(Data);
|
||||
delete UpdateRegion;
|
||||
};
|
||||
Texture->UpdateTextureRegions(0, 1u, TextureRegion, SrcBpp * Width, SrcBpp, SrcData, DataCleanup);
|
||||
|
||||
// Create a new entry for the texture.
|
||||
return TextureResources.Emplace(Name, Texture);
|
||||
// Create an entry for the texture.
|
||||
if (Name == NAME_ErrorTexture)
|
||||
{
|
||||
ErrorTexture = { Name, Texture, true };
|
||||
return INDEX_ErrorTexture;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AddTextureEntry(Name, Texture, true);
|
||||
}
|
||||
}
|
||||
|
||||
TextureIndex FTextureManager::CreatePlainTexture(const FName& Name, int32 Width, int32 Height, FColor Color)
|
||||
TextureIndex FTextureManager::CreatePlainTextureInternal(const FName& Name, int32 Width, int32 Height, const FColor& Color)
|
||||
{
|
||||
// Create buffer with raw data.
|
||||
const uint32 ColorPacked = Color.ToPackedARGB();
|
||||
@ -42,35 +81,108 @@ TextureIndex FTextureManager::CreatePlainTexture(const FName& Name, int32 Width,
|
||||
const uint32 SizeInBytes = SizeInPixels * Bpp;
|
||||
uint8* SrcData = new uint8[SizeInBytes];
|
||||
std::fill(reinterpret_cast<uint32*>(SrcData), reinterpret_cast<uint32*>(SrcData) + SizeInPixels, ColorPacked);
|
||||
auto SrcDataCleanup = [](uint8* Data) { delete[] Data; };
|
||||
|
||||
// Create new texture from raw data (we created the buffer, so mark it for delete).
|
||||
return CreateTexture(Name, Width, Height, Bpp, SrcData, true);
|
||||
// Create new texture from raw data.
|
||||
return CreateTextureInternal(Name, Width, Height, Bpp, SrcData, SrcDataCleanup);
|
||||
}
|
||||
|
||||
FTextureManager::FTextureEntry::FTextureEntry(const FName& InName, UTexture2D* InTexture)
|
||||
: Name{ InName }
|
||||
, Texture{ InTexture }
|
||||
TextureIndex FTextureManager::AddTextureEntry(const FName& Name, UTexture* Texture, bool bAddToRoot)
|
||||
{
|
||||
// Add texture to root to prevent garbage collection.
|
||||
Texture->AddToRoot();
|
||||
// Try to find an entry with that name.
|
||||
TextureIndex Index = FindTextureIndex(Name);
|
||||
|
||||
// If this is a new name, try to find an entry to reuse.
|
||||
if (Index == INDEX_NONE)
|
||||
{
|
||||
Index = FindTextureIndex(NAME_None);
|
||||
}
|
||||
|
||||
// Either update/reuse an entry or add a new one.
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
TextureResources[Index] = { Name, Texture, bAddToRoot };
|
||||
return Index;
|
||||
}
|
||||
else
|
||||
{
|
||||
return TextureResources.Emplace(Name, Texture, bAddToRoot);
|
||||
}
|
||||
}
|
||||
|
||||
FTextureManager::FTextureEntry::FTextureEntry(const FName& InName, UTexture* InTexture, bool bAddToRoot)
|
||||
: Name(InName)
|
||||
{
|
||||
checkf(InTexture, TEXT("Null texture."));
|
||||
|
||||
if (bAddToRoot)
|
||||
{
|
||||
// Get pointer only for textures that we added to root, so we can later release them.
|
||||
Texture = InTexture;
|
||||
// Add texture to the root to prevent garbage collection.
|
||||
InTexture->AddToRoot();
|
||||
}
|
||||
|
||||
// Create brush and resource handle for input texture.
|
||||
Brush.SetResourceObject(Texture);
|
||||
ResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(Brush);
|
||||
Brush.SetResourceObject(InTexture);
|
||||
CachedResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(Brush);
|
||||
}
|
||||
|
||||
FTextureManager::FTextureEntry::~FTextureEntry()
|
||||
{
|
||||
Reset(true);
|
||||
}
|
||||
|
||||
FTextureManager::FTextureEntry& FTextureManager::FTextureEntry::operator=(FTextureEntry&& Other)
|
||||
{
|
||||
// Release old resources if allocated.
|
||||
Reset(true);
|
||||
|
||||
// Move data and ownership to this instance.
|
||||
Name = MoveTemp(Other.Name);
|
||||
Texture = MoveTemp(Other.Texture);
|
||||
Brush = MoveTemp(Other.Brush);
|
||||
CachedResourceHandle = MoveTemp(Other.CachedResourceHandle);
|
||||
|
||||
// Reset the other entry (without releasing resources which are already moved to this instance) to remove tracks
|
||||
// of ownership and mark it as empty/reusable.
|
||||
Other.Reset(false);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const FSlateResourceHandle& FTextureManager::FTextureEntry::GetResourceHandle() const
|
||||
{
|
||||
if (!CachedResourceHandle.IsValid() && Brush.HasUObject())
|
||||
{
|
||||
CachedResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(Brush);
|
||||
}
|
||||
return CachedResourceHandle;
|
||||
}
|
||||
|
||||
void FTextureManager::FTextureEntry::Reset(bool bReleaseResources)
|
||||
{
|
||||
if (bReleaseResources)
|
||||
{
|
||||
// Release brush.
|
||||
if (Brush.HasUObject() && FSlateApplication::IsInitialized())
|
||||
{
|
||||
FSlateApplication::Get().GetRenderer()->ReleaseDynamicResource(Brush);
|
||||
}
|
||||
|
||||
// Remove texture from root to allow for garbage collection (it might be already invalid if this is application
|
||||
// shutdown).
|
||||
if (Texture && Texture->IsValidLowLevel())
|
||||
// Remove texture from root to allow for garbage collection (it might be invalid, if we never set it
|
||||
// or this is an application shutdown).
|
||||
if (Texture.IsValid())
|
||||
{
|
||||
Texture->RemoveFromRoot();
|
||||
}
|
||||
}
|
||||
|
||||
// We use empty name to mark unused entries.
|
||||
Name = NAME_None;
|
||||
|
||||
// Clean fields to make sure that we don't reference released or moved resources.
|
||||
Texture.Reset();
|
||||
Brush = FSlateNoResource();
|
||||
CachedResourceHandle = FSlateResourceHandle();
|
||||
}
|
||||
|
@ -2,11 +2,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Core.h>
|
||||
#include <Styling/SlateBrush.h>
|
||||
#include <Textures/SlateShaderResource.h>
|
||||
#include <UObject/WeakObjectPtr.h>
|
||||
|
||||
|
||||
class UTexture2D;
|
||||
|
||||
// Index type to be used as a texture handle.
|
||||
using TextureIndex = int32;
|
||||
|
||||
@ -24,44 +26,53 @@ public:
|
||||
FTextureManager& operator=(const FTextureManager&) = delete;
|
||||
|
||||
// Moving transfers ownership and leaves source empty.
|
||||
FTextureManager(FTextureManager&&) = default;
|
||||
FTextureManager& operator=(FTextureManager&&) = default;
|
||||
FTextureManager(FTextureManager&&) = delete;
|
||||
FTextureManager& operator=(FTextureManager&&) = delete;
|
||||
|
||||
// Initialize error texture that will be used for rendering textures without registered resources. Can be called
|
||||
// multiple time, if color needs to be changed.
|
||||
// Note: Because of any-time module loading and lazy resources initialization goals we can't simply call it from
|
||||
// constructor.
|
||||
// @param Color - The color of the error texture
|
||||
void InitializeErrorTexture(const FColor& Color);
|
||||
|
||||
// Find texture index by name.
|
||||
// @param Name - The name of a texture to find
|
||||
// @returns The index of a texture with given name or INDEX_NONE if there is no such texture
|
||||
TextureIndex FindTextureIndex(const FName& Name) const
|
||||
{
|
||||
return TextureResources.IndexOfByPredicate([&](const auto& Entry) { return Entry.Name == Name; });
|
||||
return TextureResources.IndexOfByPredicate([&](const auto& Entry) { return Entry.GetName() == Name; });
|
||||
}
|
||||
|
||||
// Get the name of a texture at given index. Throws exception if index is out of range.
|
||||
// Get the name of a texture at given index. Returns NAME_None, if index is out of range.
|
||||
// @param Index - Index of a texture
|
||||
// @returns The name of a texture at given index
|
||||
FORCEINLINE FName GetTextureName(TextureIndex Index) const
|
||||
// @returns The name of a texture at given index or NAME_None if index is out of range.
|
||||
FName GetTextureName(TextureIndex Index) const
|
||||
{
|
||||
return TextureResources[Index].Name;
|
||||
return IsInRange(Index) ? TextureResources[Index].GetName() : NAME_None;
|
||||
}
|
||||
|
||||
// Get the Slate Resource Handle to a texture at given index. Throws exception if index is out of range.
|
||||
// Get the Slate Resource Handle to a texture at given index. If index is out of range or resources are not valid
|
||||
// it returns a handle to the error texture.
|
||||
// @param Index - Index of a texture
|
||||
// @returns The Slate Resource Handle for a texture at given index
|
||||
FORCEINLINE const FSlateResourceHandle& GetTextureHandle(TextureIndex Index) const
|
||||
// @returns The Slate Resource Handle for a texture at given index or to error texture, if no valid resources were
|
||||
// found at given index
|
||||
const FSlateResourceHandle& GetTextureHandle(TextureIndex Index) const
|
||||
{
|
||||
return TextureResources[Index].ResourceHandle;
|
||||
return IsValidTexture(Index) ? TextureResources[Index].GetResourceHandle() : ErrorTexture.GetResourceHandle();
|
||||
}
|
||||
|
||||
// Create a texture from raw data. Throws exception if there is already a texture with that name.
|
||||
// Create a texture from raw data.
|
||||
// @param Name - The texture name
|
||||
// @param Width - The texture width
|
||||
// @param Height - The texture height
|
||||
// @param SrcBpp - The size in bytes of one pixel
|
||||
// @param SrcData - The source data
|
||||
// @param bDeleteSrcData - If true, we should delete source data after creating a texture
|
||||
// @param SrcDataCleanup - Optional function called to release source data after texture is created (only needed, if data need to be released)
|
||||
// @returns The index of a texture that was created
|
||||
TextureIndex CreateTexture(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, bool bDeleteSrc = false);
|
||||
TextureIndex CreateTexture(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, TFunction<void(uint8*)> SrcDataCleanup = [](uint8*) {});
|
||||
|
||||
// Create a plain texture. Throws exception if there is already a texture with that name.
|
||||
// Create a plain texture.
|
||||
// @param Name - The texture name
|
||||
// @param Width - The texture width
|
||||
// @param Height - The texture height
|
||||
@ -69,27 +80,79 @@ public:
|
||||
// @returns The index of a texture that was created
|
||||
TextureIndex CreatePlainTexture(const FName& Name, int32 Width, int32 Height, FColor Color);
|
||||
|
||||
// Create Slate resources to an existing texture, managed externally.
|
||||
// @param Name - The texture name
|
||||
// @param Texture - The texture
|
||||
// @returns The index to created/updated texture resources
|
||||
TextureIndex CreateTextureResources(const FName& Name, UTexture* Texture);
|
||||
|
||||
// Release resources for given texture. Ignores invalid indices.
|
||||
// @param Index - The index of a texture resources
|
||||
void ReleaseTextureResources(TextureIndex Index);
|
||||
|
||||
private:
|
||||
|
||||
// See CreateTexture for general description.
|
||||
// Internal implementations doesn't validate name or resource uniqueness. Instead it uses NAME_ErrorTexture
|
||||
// (aka NAME_None) and INDEX_ErrorTexture (aka INDEX_NONE) to identify ErrorTexture.
|
||||
TextureIndex CreateTextureInternal(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, TFunction<void(uint8*)> SrcDataCleanup = [](uint8*) {});
|
||||
|
||||
// See CreatePlainTexture for general description.
|
||||
// Internal implementations doesn't validate name or resource uniqueness. Instead it uses NAME_ErrorTexture
|
||||
// (aka NAME_None) and INDEX_ErrorTexture (aka INDEX_NONE) to identify ErrorTexture.
|
||||
TextureIndex CreatePlainTextureInternal(const FName& Name, int32 Width, int32 Height, const FColor& Color);
|
||||
|
||||
// Add or reuse texture entry.
|
||||
// @param Name - The texture name
|
||||
// @param Texture - The texture
|
||||
// @param bAddToRoot - If true, we should add texture to root to prevent garbage collection (use for own textures)
|
||||
// @returns The index of the entry that we created or reused
|
||||
TextureIndex AddTextureEntry(const FName& Name, UTexture* Texture, bool bAddToRoot);
|
||||
|
||||
// Check whether index is in range allocated for TextureResources (it doesn't mean that resources are valid).
|
||||
FORCEINLINE bool IsInRange(TextureIndex Index) const
|
||||
{
|
||||
return static_cast<uint32>(Index) < static_cast<uint32>(TextureResources.Num());
|
||||
}
|
||||
|
||||
// Check whether index is in range and whether texture resources are valid (using NAME_None sentinel).
|
||||
FORCEINLINE bool IsValidTexture(TextureIndex Index) const
|
||||
{
|
||||
return IsInRange(Index) && TextureResources[Index].GetName() != NAME_None;
|
||||
}
|
||||
|
||||
// Entry for texture resources. Only supports explicit construction.
|
||||
struct FTextureEntry
|
||||
{
|
||||
FTextureEntry(const FName& InName, UTexture2D* InTexture);
|
||||
FTextureEntry() = default;
|
||||
FTextureEntry(const FName& InName, UTexture* InTexture, bool bAddToRoot);
|
||||
~FTextureEntry();
|
||||
|
||||
// Copying is not supported.
|
||||
FTextureEntry(const FTextureEntry&) = delete;
|
||||
FTextureEntry& operator=(const FTextureEntry&) = delete;
|
||||
|
||||
// We rely on TArray and don't implement custom move semantics.
|
||||
// We rely on TArray and don't implement custom move constructor...
|
||||
FTextureEntry(FTextureEntry&&) = delete;
|
||||
FTextureEntry& operator=(FTextureEntry&&) = delete;
|
||||
// ... but we need move assignment to support reusing entries.
|
||||
FTextureEntry& operator=(FTextureEntry&& Other);
|
||||
|
||||
const FName& GetName() const { return Name; }
|
||||
const FSlateResourceHandle& GetResourceHandle() const;
|
||||
|
||||
private:
|
||||
|
||||
void Reset(bool bReleaseResources);
|
||||
|
||||
FName Name = NAME_None;
|
||||
UTexture2D* Texture = nullptr;
|
||||
mutable FSlateResourceHandle CachedResourceHandle;
|
||||
TWeakObjectPtr<UTexture> Texture;
|
||||
FSlateBrush Brush;
|
||||
FSlateResourceHandle ResourceHandle;
|
||||
};
|
||||
|
||||
TArray<FTextureEntry> TextureResources;
|
||||
FTextureEntry ErrorTexture;
|
||||
|
||||
static constexpr EName NAME_ErrorTexture = NAME_None;
|
||||
static constexpr TextureIndex INDEX_ErrorTexture = INDEX_NONE;
|
||||
};
|
||||
|
102
Source/ImGui/Private/Utilities/DebugExecBindings.cpp
Normal file
102
Source/ImGui/Private/Utilities/DebugExecBindings.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "DebugExecBindings.h"
|
||||
|
||||
#include "ImGuiModuleSettings.h"
|
||||
|
||||
#include <GameFramework/PlayerInput.h>
|
||||
#include <UObject/UObjectIterator.h>
|
||||
|
||||
#include "EnhancedPlayerInput.h"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
FKeyBind CreateKeyBind(const FImGuiKeyInfo& KeyInfo, const FString& Command)
|
||||
{
|
||||
FKeyBind KeyBind;
|
||||
KeyBind.Command = Command;
|
||||
KeyBind.Key = KeyInfo.Key;
|
||||
KeyBind.bDisabled = false;
|
||||
|
||||
#define FILL_MODIFIER_DATA(KeyInfoProperty, BindProperty, BindIgnoreProperty)\
|
||||
if (KeyInfo.KeyInfoProperty == ECheckBoxState::Undetermined)\
|
||||
{\
|
||||
KeyBind.BindProperty = KeyBind.BindIgnoreProperty = false;\
|
||||
}\
|
||||
else\
|
||||
{\
|
||||
KeyBind.BindProperty = (KeyInfo.KeyInfoProperty == ECheckBoxState::Checked);\
|
||||
KeyBind.BindIgnoreProperty = !KeyBind.BindProperty;\
|
||||
}
|
||||
|
||||
FILL_MODIFIER_DATA(Shift, Shift, bIgnoreShift);
|
||||
FILL_MODIFIER_DATA(Ctrl, Control, bIgnoreCtrl);
|
||||
FILL_MODIFIER_DATA(Alt, Alt, bIgnoreAlt);
|
||||
FILL_MODIFIER_DATA(Cmd, Cmd, bIgnoreCmd);
|
||||
|
||||
#undef FILL_MODIFIER_DATA
|
||||
|
||||
return KeyBind;
|
||||
}
|
||||
|
||||
bool IsBindable(const FKey& Key)
|
||||
{
|
||||
#if ENGINE_COMPATIBILITY_LEGACY_KEY_AXIS_API
|
||||
return Key.IsValid() && Key != EKeys::AnyKey && !Key.IsFloatAxis() && !Key.IsVectorAxis()
|
||||
&& !Key.IsGamepadKey() && !Key.IsModifierKey() && !Key.IsMouseButton();
|
||||
#else
|
||||
return Key.IsValid() && Key != EKeys::AnyKey && !Key.IsAxis1D() && !Key.IsAxis2D()
|
||||
&& !Key.IsAxis3D() && !Key.IsGamepadKey() && !Key.IsModifierKey() && !Key.IsMouseButton();
|
||||
#endif
|
||||
}
|
||||
|
||||
void UpdatePlayerInput(UEnhancedPlayerInput* PlayerInput, const FKeyBind& KeyBind)
|
||||
{
|
||||
const int32 Index = PlayerInput->DebugExecBindings.IndexOfByPredicate([&](const FKeyBind& PlayerKeyBind)
|
||||
{
|
||||
return PlayerKeyBind.Command.Equals(KeyBind.Command, ESearchCase::IgnoreCase);
|
||||
});
|
||||
|
||||
if (IsBindable(KeyBind.Key))
|
||||
{
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
PlayerInput->DebugExecBindings[Index] = KeyBind;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayerInput->DebugExecBindings.Add(KeyBind);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
PlayerInput->DebugExecBindings.RemoveAt(Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace DebugExecBindings
|
||||
{
|
||||
void UpdatePlayerInputs(const FImGuiKeyInfo& KeyInfo, const FString& Command)
|
||||
{
|
||||
checkf(!Command.IsEmpty(), TEXT("Empty command."));
|
||||
|
||||
const FKeyBind KeyBind = CreateKeyBind(KeyInfo, Command);
|
||||
|
||||
// Update default player input, so changes will be visible in all PIE sessions created after this point.
|
||||
if (UEnhancedPlayerInput* DefaultPlayerInput = GetMutableDefault<UEnhancedPlayerInput>())
|
||||
{
|
||||
UpdatePlayerInput(DefaultPlayerInput, KeyBind);
|
||||
}
|
||||
|
||||
// Update all existing player inputs to see changes in running PIE session.
|
||||
for (TObjectIterator<UEnhancedPlayerInput> It; It; ++It)
|
||||
{
|
||||
UpdatePlayerInput(*It, KeyBind);
|
||||
}
|
||||
}
|
||||
}
|
11
Source/ImGui/Private/Utilities/DebugExecBindings.h
Normal file
11
Source/ImGui/Private/Utilities/DebugExecBindings.h
Normal file
@ -0,0 +1,11 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
class FString;
|
||||
struct FImGuiKeyInfo;
|
||||
|
||||
namespace DebugExecBindings
|
||||
{
|
||||
void UpdatePlayerInputs(const FImGuiKeyInfo& KeyInfo, const FString& Command);
|
||||
}
|
96
Source/ImGui/Private/Utilities/RedirectingHandle.h
Normal file
96
Source/ImGui/Private/Utilities/RedirectingHandle.h
Normal file
@ -0,0 +1,96 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Delegates/Delegate.h>
|
||||
#include <Delegates/DelegateCombinations.h>
|
||||
|
||||
|
||||
namespace Utilities
|
||||
{
|
||||
// Handle initialized as a pointer to a default value, but can be redirected to follow other handles.
|
||||
// When detached, it will revert to the default value. Intended for cross-module redirections.
|
||||
template<typename T>
|
||||
struct TRedirectingHandle
|
||||
{
|
||||
TRedirectingHandle(T& InDefaultValue)
|
||||
: Handle(&InDefaultValue)
|
||||
, DefaultHandle(&InDefaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
~TRedirectingHandle()
|
||||
{
|
||||
// Broadcast null pointer as a request to detach.
|
||||
OnRedirectionUpdate.Broadcast(nullptr);
|
||||
}
|
||||
|
||||
// Check whether this handle points to the default value.
|
||||
bool IsDefault() const
|
||||
{
|
||||
return (Handle == DefaultHandle);
|
||||
}
|
||||
|
||||
// Get the current value.
|
||||
T& Get() const
|
||||
{
|
||||
return *Handle;
|
||||
}
|
||||
|
||||
// Set the other handle as a parent to this one. Passing null or itself will result with detaching from
|
||||
// the current parent (if any) and reverting back to the default value.
|
||||
void SetParent(TRedirectingHandle* InParent)
|
||||
{
|
||||
if (InParent != Parent)
|
||||
{
|
||||
if (Parent)
|
||||
{
|
||||
Parent->OnRedirectionUpdate.RemoveAll(this);
|
||||
}
|
||||
|
||||
// Protecting from setting itself as a parent.
|
||||
Parent = (InParent != this) ? InParent : nullptr;
|
||||
|
||||
if (Parent)
|
||||
{
|
||||
Parent->OnRedirectionUpdate.AddRaw(this, &TRedirectingHandle::UpdateRedirection);
|
||||
}
|
||||
|
||||
SetHandle((Parent) ? Parent->Handle : DefaultHandle);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void UpdateRedirection(T* InHandle)
|
||||
{
|
||||
if (InHandle)
|
||||
{
|
||||
SetHandle(InHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Interpreting null as a signal to detach.
|
||||
SetParent(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void SetHandle(T* InHandle)
|
||||
{
|
||||
if (InHandle != Handle)
|
||||
{
|
||||
Handle = InHandle;
|
||||
OnRedirectionUpdate.Broadcast(Handle);
|
||||
}
|
||||
}
|
||||
|
||||
T* Handle;
|
||||
T* DefaultHandle;
|
||||
|
||||
TRedirectingHandle* Parent = nullptr;
|
||||
|
||||
// Event with a new handle value or null pointer as a signal to detach.
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FRedirectionUpdateDelegate, T*);
|
||||
FRedirectionUpdateDelegate OnRedirectionUpdate;
|
||||
};
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ScopeGuards
|
||||
{
|
||||
// Saves snapshot of the object state and restores it during destruction.
|
||||
template<typename T>
|
||||
class TStateSaver
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructor taking target object in state that we want to save.
|
||||
TStateSaver(T& Target)
|
||||
: Ptr(&Target)
|
||||
, Value(Target)
|
||||
{
|
||||
}
|
||||
|
||||
// Move constructor allowing to transfer state out of scope.
|
||||
TStateSaver(TStateSaver&& Other)
|
||||
: Ptr(Other.Ptr)
|
||||
, Value(MoveTemp(Other.Value))
|
||||
{
|
||||
// Release responsibility from the other object (std::exchange currently not supported by all platforms).
|
||||
Other.Ptr = nullptr;
|
||||
}
|
||||
|
||||
// Non-assignable to enforce acquisition on construction.
|
||||
TStateSaver& operator=(TStateSaver&&) = delete;
|
||||
|
||||
// Non-copyable.
|
||||
TStateSaver(const TStateSaver&) = delete;
|
||||
TStateSaver& operator=(const TStateSaver&) = delete;
|
||||
|
||||
~TStateSaver()
|
||||
{
|
||||
if (Ptr)
|
||||
{
|
||||
*Ptr = Value;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
T* Ptr;
|
||||
T Value;
|
||||
};
|
||||
|
||||
// Create a state saver for target object. Unless saver is moved, state will be restored at the end of scope.
|
||||
// @param Target - Target object in state that we want to save
|
||||
// @returns State saver that unless moved, will restore target's state during scope exit
|
||||
template<typename T>
|
||||
TStateSaver<T> MakeStateSaver(T& Target)
|
||||
{
|
||||
return TStateSaver<T>{ Target };
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "ImGuiPrivatePCH.h"
|
||||
|
||||
#include "WorldContext.h"
|
||||
|
||||
|
||||
|
@ -2,8 +2,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Core.h>
|
||||
#include <Engine.h>
|
||||
#include <Engine/Engine.h>
|
||||
#include <Engine/GameInstance.h>
|
||||
#include <Engine/GameViewportClient.h>
|
||||
#include <UObject/WeakObjectPtr.h>
|
||||
|
||||
|
||||
// Utilities helping to get a World Context.
|
||||
@ -11,22 +13,19 @@
|
||||
namespace Utilities
|
||||
{
|
||||
template<typename T>
|
||||
FORCEINLINE const FWorldContext* GetWorldContext(const TWeakObjectPtr<T>& Obj)
|
||||
{
|
||||
return Obj.IsValid() ? GetWorldContext(*Obj.Get()) : nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE const FWorldContext* GetWorldContext(const T* Obj)
|
||||
{
|
||||
return Obj ? GetWorldContext(*Obj) : nullptr;
|
||||
}
|
||||
FORCEINLINE const FWorldContext* GetWorldContext(const T* Obj);
|
||||
|
||||
FORCEINLINE const FWorldContext* GetWorldContext(const UGameInstance& GameInstance)
|
||||
{
|
||||
return GameInstance.GetWorldContext();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE const FWorldContext* GetWorldContext(const TWeakObjectPtr<T>& Obj)
|
||||
{
|
||||
return Obj.IsValid() ? GetWorldContext(*Obj.Get()) : nullptr;
|
||||
}
|
||||
|
||||
FORCEINLINE const FWorldContext* GetWorldContext(const UGameViewportClient& GameViewportClient)
|
||||
{
|
||||
return GetWorldContext(GameViewportClient.GetGameInstance());
|
||||
@ -37,5 +36,11 @@ namespace Utilities
|
||||
return GetWorldContext(World.GetGameInstance());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE const FWorldContext* GetWorldContext(const T* Obj)
|
||||
{
|
||||
return Obj ? GetWorldContext(*Obj) : nullptr;
|
||||
}
|
||||
|
||||
const FWorldContext* GetWorldContextFromNetMode(ENetMode NetMode);
|
||||
}
|
||||
|
@ -6,42 +6,26 @@
|
||||
|
||||
|
||||
// Utilities mapping worlds to indices that we use to identify ImGui contexts.
|
||||
// Editor and standalone games have context index 0 while PIE worlds have indices starting from 1 for server and 2+ for
|
||||
// clients.
|
||||
|
||||
namespace Utilities
|
||||
{
|
||||
// Invalid context index for parameters that cannot be resolved to a valid world.
|
||||
static constexpr int32 INVALID_CONTEXT_INDEX = -1;
|
||||
static constexpr int32 INVALID_CONTEXT_INDEX = -10;
|
||||
|
||||
// Standalone context index.
|
||||
static constexpr int32 STANDALONE_GAME_CONTEXT_INDEX = 0;
|
||||
static constexpr int32 STANDALONE_GAME_CONTEXT_INDEX = -2;
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
// Editor context index.
|
||||
static constexpr int32 EDITOR_CONTEXT_INDEX = 0;
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE int32 GetWorldContextIndex(const T& Obj)
|
||||
{
|
||||
const FWorldContext* WorldContext = GetWorldContext(Obj);
|
||||
return WorldContext ? GetWorldContextIndex(*WorldContext) : INVALID_CONTEXT_INDEX;
|
||||
}
|
||||
// Editor context index. We are lacking flexibility here, so we might need to change it somehow.
|
||||
static constexpr int32 EDITOR_CONTEXT_INDEX = -1;
|
||||
|
||||
FORCEINLINE int32 GetWorldContextIndex(const FWorldContext& WorldContext)
|
||||
{
|
||||
// In standalone game (WorldType = Game) we have only one context with index 0 (see GAME_CONTEXT_INDEX).
|
||||
|
||||
// In editor, we keep 0 for editor and use PIEInstance to index worlds. In simulation or standalone single-PIE
|
||||
// sessions PIEInstance is 0, but since there is only one world we can change it without causing any conflicts.
|
||||
// In single-PIE with dedicated server or multi-PIE sessions worlds have PIEInstance starting from 1 for server
|
||||
// and 2+ for clients, what maps directly to our index.
|
||||
|
||||
switch (WorldContext.WorldType)
|
||||
{
|
||||
case EWorldType::PIE:
|
||||
return FMath::Max(WorldContext.PIEInstance, 1);
|
||||
return WorldContext.PIEInstance;
|
||||
case EWorldType::Game:
|
||||
return STANDALONE_GAME_CONTEXT_INDEX;
|
||||
case EWorldType::Editor:
|
||||
@ -51,11 +35,23 @@ namespace Utilities
|
||||
}
|
||||
}
|
||||
|
||||
int32 GetWorldContextIndex(const UWorld& World)
|
||||
template<typename T>
|
||||
FORCEINLINE int32 GetWorldContextIndex(const T& Obj)
|
||||
{
|
||||
const FWorldContext* WorldContext = GetWorldContext(Obj);
|
||||
return WorldContext ? GetWorldContextIndex(*WorldContext) : INVALID_CONTEXT_INDEX;
|
||||
}
|
||||
|
||||
FORCEINLINE int32 GetWorldContextIndex(const UWorld& World)
|
||||
{
|
||||
return (World.WorldType == EWorldType::Editor) ? EDITOR_CONTEXT_INDEX : GetWorldContextIndex(World.GetGameInstance());
|
||||
}
|
||||
|
||||
FORCEINLINE int32 GetWorldContextIndex(const UWorld* World)
|
||||
{
|
||||
return World ? GetWorldContextIndex(*World) : INVALID_CONTEXT_INDEX;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<typename T>
|
||||
|
37
Source/ImGui/Private/VersionCompatibility.h
Normal file
37
Source/ImGui/Private/VersionCompatibility.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include <Runtime/Launch/Resources/Version.h>
|
||||
|
||||
#define BELOW_ENGINE_VERSION(Major, Minor) (ENGINE_MAJOR_VERSION < (Major) || (ENGINE_MAJOR_VERSION == (Major) && ENGINE_MINOR_VERSION < (Minor)))
|
||||
#define FROM_ENGINE_VERSION(Major, Minor) !BELOW_ENGINE_VERSION(Major, Minor)
|
||||
|
||||
|
||||
// One place to define compatibility with older engine versions.
|
||||
|
||||
|
||||
// Starting from version 4.17, Slate has an improved clipping API. Old version required to specify per-vertex clipping
|
||||
// rectangle and unofficial GSlateScissorRect to correctly clip custom vertices made with FSlateDrawElement.
|
||||
#define ENGINE_COMPATIBILITY_LEGACY_CLIPPING_API BELOW_ENGINE_VERSION(4, 17)
|
||||
|
||||
// Starting from version 4.18, FPaths::GameSavedDir() has been superseded by FPaths::ProjectSavedDir().
|
||||
#define ENGINE_COMPATIBILITY_LEGACY_SAVED_DIR BELOW_ENGINE_VERSION(4, 18)
|
||||
|
||||
// Starting from version 4.18, we have support for dual key bindings.
|
||||
#define ENGINE_COMPATIBILITY_SINGLE_KEY_BINDING BELOW_ENGINE_VERSION(4, 18)
|
||||
|
||||
// Starting from version 4.18, FStringClassReference is replaced by FSoftClassPath. The new header contains a typedef
|
||||
// that renames FStringClassReference to FSoftClassPath, so it is still possible tu use the old type name in code.
|
||||
// The old header forwards to the new one but if used it outputs a warning, so we want to avoid it.
|
||||
#define ENGINE_COMPATIBILITY_LEGACY_STRING_CLASS_REF BELOW_ENGINE_VERSION(4, 18)
|
||||
|
||||
// Starting from version 4.18, engine has a world post actor tick event which if available, provides a good opportunity
|
||||
// to call debug delegates after world actors are already updated.
|
||||
#define ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK FROM_ENGINE_VERSION(4, 18)
|
||||
|
||||
// Starting from version 4.24, world actor tick event has additional world parameter.
|
||||
#define ENGINE_COMPATIBILITY_LEGACY_WORLD_ACTOR_TICK BELOW_ENGINE_VERSION(4, 24)
|
||||
|
||||
// Starting from version 4.26, FKey::IsFloatAxis and FKey::IsVectorAxis are deprecated and replaced with FKey::IsAxis[1|2|3]D methods.
|
||||
#define ENGINE_COMPATIBILITY_LEGACY_KEY_AXIS_API BELOW_ENGINE_VERSION(4, 26)
|
||||
|
||||
#define ENGINE_COMPATIBILITY_LEGACY_VECTOR2F BELOW_ENGINE_VERSION(5, 0)
|
352
Source/ImGui/Private/Widgets/SImGuiCanvasControl.cpp
Normal file
352
Source/ImGui/Private/Widgets/SImGuiCanvasControl.cpp
Normal file
@ -0,0 +1,352 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "SImGuiCanvasControl.h"
|
||||
|
||||
#include "VersionCompatibility.h"
|
||||
|
||||
#include <Rendering/DrawElements.h>
|
||||
#include <SlateOptMacros.h>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
// Mouse wheel to zoom ratio - how fast zoom changes with mouse wheel delta.
|
||||
const float ZoomScrollSpeed = 0.03125f;
|
||||
|
||||
// Speed of blending out - how much zoom scale and canvas offset fades in every frame.
|
||||
const float BlendOutSpeed = 0.25f;
|
||||
|
||||
// TODO: Move to settings
|
||||
namespace Colors
|
||||
{
|
||||
const FLinearColor CanvasMargin = FColor(0, 4, 32, 64);
|
||||
const FLinearColor CanvasBorder = FColor::Black.WithAlpha(127);
|
||||
const FLinearColor CanvasBorderHighlight = FColor(16, 24, 64, 160);
|
||||
const FLinearColor FrameBorder = FColor(222, 163, 9, 128);
|
||||
const FLinearColor FrameBorderHighlight = FColor(255, 180, 10, 160);
|
||||
}
|
||||
|
||||
// Defines type of drag operation.
|
||||
enum class EDragType
|
||||
{
|
||||
Content,
|
||||
Canvas
|
||||
};
|
||||
|
||||
// Data for drag & drop operations. Calculations are made in widget where we have more straightforward access to data
|
||||
// like geometry or scale.
|
||||
class FImGuiDragDropOperation : public FDragDropOperation
|
||||
{
|
||||
public:
|
||||
|
||||
DRAG_DROP_OPERATOR_TYPE(FImGuiDragDropOperation, FDragDropOperation)
|
||||
|
||||
FImGuiDragDropOperation(const FVector2D& InPosition, const FVector2D& InOffset, EDragType InDragType)
|
||||
: StartPosition(InPosition)
|
||||
, StartOffset(InOffset)
|
||||
, DragType(InDragType)
|
||||
{
|
||||
bCreateNewWindow = false;
|
||||
MouseCursor = EMouseCursor::GrabHandClosed;
|
||||
}
|
||||
|
||||
FVector2D StartPosition;
|
||||
FVector2D StartOffset;
|
||||
EDragType DragType;
|
||||
};
|
||||
}
|
||||
|
||||
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||
void SImGuiCanvasControl::Construct(const FArguments& InArgs)
|
||||
{
|
||||
OnTransformChanged = InArgs._OnTransformChanged;
|
||||
UpdateVisibility();
|
||||
}
|
||||
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||
|
||||
void SImGuiCanvasControl::SetActive(bool bInActive)
|
||||
{
|
||||
if (bActive != bInActive)
|
||||
{
|
||||
bActive = bInActive;
|
||||
bBlendingOut = !bInActive;
|
||||
if (bInActive)
|
||||
{
|
||||
Opacity = 1.f;
|
||||
}
|
||||
UpdateVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
void SImGuiCanvasControl::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
||||
{
|
||||
Super::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
||||
|
||||
if (bBlendingOut)
|
||||
{
|
||||
if (FMath::IsNearlyEqual(CanvasScale, 1.f, ZoomScrollSpeed) && CanvasOffset.IsNearlyZero(1.f))
|
||||
{
|
||||
CanvasOffset = FVector2D::ZeroVector;
|
||||
CanvasScale = 1.f;
|
||||
Opacity = 0.f;
|
||||
bBlendingOut = false;
|
||||
UpdateVisibility();
|
||||
}
|
||||
else
|
||||
{
|
||||
CanvasOffset = FMath::Lerp(CanvasOffset, FVector2D::ZeroVector, BlendOutSpeed);
|
||||
CanvasScale = FMath::Lerp(CanvasScale, 1.f, BlendOutSpeed);
|
||||
Opacity = FMath::Lerp(Opacity, 0.f, BlendOutSpeed);
|
||||
}
|
||||
|
||||
UpdateRenderTransform();
|
||||
}
|
||||
}
|
||||
|
||||
FReply SImGuiCanvasControl::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
if (DragRequest == EDragRequest::None)
|
||||
{
|
||||
if (MouseEvent.GetEffectingButton() == EKeys::RightMouseButton)
|
||||
{
|
||||
DragRequest = EDragRequest::Content;
|
||||
return FReply::Handled().DetectDrag(SharedThis(this), EKeys::RightMouseButton).CaptureMouse(SharedThis(this));
|
||||
}
|
||||
else if (MouseEvent.GetEffectingButton() == EKeys::MiddleMouseButton)
|
||||
{
|
||||
DragRequest = EDragRequest::Canvas;
|
||||
return FReply::Handled().DetectDrag(SharedThis(this), EKeys::MiddleMouseButton).CaptureMouse(SharedThis(this));
|
||||
}
|
||||
}
|
||||
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
FReply SImGuiCanvasControl::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
if (MouseEvent.GetEffectingButton() == EKeys::RightMouseButton)
|
||||
{
|
||||
if (DragRequest == EDragRequest::Content)
|
||||
{
|
||||
DragRequest = EDragRequest::None;
|
||||
}
|
||||
}
|
||||
else if (MouseEvent.GetEffectingButton() == EKeys::MiddleMouseButton)
|
||||
{
|
||||
if (DragRequest == EDragRequest::Canvas)
|
||||
{
|
||||
DragRequest = EDragRequest::None;
|
||||
}
|
||||
}
|
||||
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
FReply SImGuiCanvasControl::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
Zoom(MyGeometry, MouseEvent.GetWheelDelta() * ZoomScrollSpeed, MouseEvent.GetScreenSpacePosition());
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
FReply SImGuiCanvasControl::OnDragDetected(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
if (DragRequest == EDragRequest::Content)
|
||||
{
|
||||
return FReply::Handled()
|
||||
.BeginDragDrop(MakeShareable(new FImGuiDragDropOperation(
|
||||
MouseEvent.GetScreenSpacePosition(), ContentOffset, EDragType::Content)))
|
||||
.LockMouseToWidget(SharedThis(this));
|
||||
}
|
||||
else if (DragRequest == EDragRequest::Canvas)
|
||||
{
|
||||
return FReply::Handled()
|
||||
.BeginDragDrop(MakeShareable(new FImGuiDragDropOperation(
|
||||
MouseEvent.GetScreenSpacePosition(), CanvasOffset, EDragType::Canvas)))
|
||||
.LockMouseToWidget(SharedThis(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
}
|
||||
|
||||
FReply SImGuiCanvasControl::OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
|
||||
{
|
||||
auto Operation = DragDropEvent.GetOperationAs<FImGuiDragDropOperation>();
|
||||
if (Operation.IsValid())
|
||||
{
|
||||
const FSlateRenderTransform ScreenToWidget = MyGeometry.GetAccumulatedRenderTransform().Inverse();
|
||||
const FVector2D DragDelta = ScreenToWidget.TransformVector(FVector2D(DragDropEvent.GetScreenSpacePosition() - Operation->StartPosition));
|
||||
|
||||
if (Operation->DragType == EDragType::Content)
|
||||
{
|
||||
// Content offset is in ImGui space, so we need to scale drag calculated in widget space.
|
||||
ContentOffset = Operation->StartOffset + DragDelta / CanvasScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Canvas offset is in widget space, so we can apply drag calculated in widget space directly.
|
||||
CanvasOffset = Operation->StartOffset + DragDelta;
|
||||
}
|
||||
|
||||
UpdateRenderTransform();
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
else
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
}
|
||||
|
||||
FReply SImGuiCanvasControl::SImGuiCanvasControl::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
|
||||
{
|
||||
DragRequest = EDragRequest::None;
|
||||
return FReply::Handled().ReleaseMouseLock();
|
||||
}
|
||||
|
||||
FVector2D SImGuiCanvasControl::ComputeDesiredSize(float InScale) const
|
||||
{
|
||||
return FVector2D{ 3840.f, 2160.f } * InScale;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
FORCEINLINE FMargin CalculateInset(const FSlateRect& From, const FSlateRect& To)
|
||||
{
|
||||
return { To.Left - From.Left, To.Top - From.Top, From.Right - To.Right, From.Bottom - To.Bottom };
|
||||
}
|
||||
|
||||
FORCEINLINE FLinearColor ScaleAlpha(FLinearColor Color, float Scale)
|
||||
{
|
||||
Color.A *= Scale;
|
||||
return Color;
|
||||
}
|
||||
|
||||
FORCEINLINE FVector2D Round(const FVector2D& Vec)
|
||||
{
|
||||
return FVector2D{ FMath::FloorToFloat(Vec.X), FMath::FloorToFloat(Vec.Y) };
|
||||
}
|
||||
}
|
||||
|
||||
int32 SImGuiCanvasControl::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
|
||||
{
|
||||
const FPaintGeometry PaintGeometry = AllottedGeometry.ToPaintGeometry();
|
||||
|
||||
const FSlateRenderTransform& WidgetToScreen = AllottedGeometry.GetAccumulatedRenderTransform();
|
||||
const FSlateRenderTransform ImGuiToScreen = Transform.Concatenate(WidgetToScreen);
|
||||
|
||||
const FSlateRect CanvasRect = FSlateRect(
|
||||
ImGuiToScreen.TransformPoint(FVector2D::ZeroVector),
|
||||
ImGuiToScreen.TransformPoint(ComputeDesiredSize(1.f)));
|
||||
const FMargin CanvasMargin = CalculateInset(MyCullingRect, CanvasRect);
|
||||
|
||||
if (CanvasMargin.GetDesiredSize().SizeSquared() > 0.f)
|
||||
{
|
||||
CanvasBorderBrush.Margin = CanvasMargin;
|
||||
|
||||
const FLinearColor CanvasMarginColor = ScaleAlpha(Colors::CanvasMargin, Opacity);
|
||||
const FLinearColor CanvasBorderColor = ScaleAlpha(DragRequest == EDragRequest::Content
|
||||
? Colors::CanvasBorderHighlight : Colors::CanvasBorder, Opacity);
|
||||
|
||||
#if ENGINE_COMPATIBILITY_LEGACY_CLIPPING_API
|
||||
FSlateDrawElement::MakeBox(OutDrawElements, LayerId, PaintGeometry, &CanvasBorderBrush, MyCullingRect,
|
||||
ESlateDrawEffect::None, CanvasMarginColor);
|
||||
|
||||
FSlateDrawElement::MakeBox(OutDrawElements, LayerId, PaintGeometry, &CanvasBorderBrush,
|
||||
CanvasRect.ExtendBy(1).IntersectionWith(MyCullingRect), ESlateDrawEffect::None, CanvasBorderColor);
|
||||
|
||||
#else
|
||||
|
||||
FSlateDrawElement::MakeBox(OutDrawElements, LayerId, PaintGeometry, &CanvasBorderBrush, ESlateDrawEffect::None,
|
||||
CanvasMarginColor);
|
||||
|
||||
OutDrawElements.PushClip(FSlateClippingZone{ CanvasRect.ExtendBy(1) });
|
||||
FSlateDrawElement::MakeBox(OutDrawElements, LayerId, PaintGeometry, &CanvasBorderBrush, ESlateDrawEffect::None,
|
||||
CanvasBorderColor);
|
||||
OutDrawElements.PopClip();
|
||||
#endif
|
||||
}
|
||||
|
||||
const FSlateRect FrameRect = FSlateRect::FromPointAndExtent(
|
||||
WidgetToScreen.TransformPoint(Round(CanvasOffset)),
|
||||
Round(MyCullingRect.GetSize() * CanvasScale));
|
||||
const FMargin FrameMargin = CalculateInset(MyCullingRect, FrameRect);
|
||||
|
||||
if (FrameMargin.GetDesiredSize().SizeSquared() > 0.f)
|
||||
{
|
||||
FrameBorderBrush.Margin = FrameMargin;
|
||||
|
||||
const FLinearColor FrameBorderColor = ScaleAlpha(DragRequest == EDragRequest::Canvas
|
||||
? Colors::FrameBorderHighlight : Colors::FrameBorder, Opacity);
|
||||
|
||||
#if ENGINE_COMPATIBILITY_LEGACY_CLIPPING_API
|
||||
FSlateDrawElement::MakeBox(OutDrawElements, LayerId, PaintGeometry, &FrameBorderBrush,
|
||||
FrameRect.ExtendBy(1).IntersectionWith(MyCullingRect), ESlateDrawEffect::None, FrameBorderColor);
|
||||
#else
|
||||
OutDrawElements.PushClip(FSlateClippingZone{ FrameRect.ExtendBy(1) });
|
||||
FSlateDrawElement::MakeBox(OutDrawElements, LayerId, PaintGeometry, &FrameBorderBrush, ESlateDrawEffect::None,
|
||||
FrameBorderColor);
|
||||
OutDrawElements.PopClip();
|
||||
#endif
|
||||
}
|
||||
|
||||
return LayerId;
|
||||
}
|
||||
|
||||
void SImGuiCanvasControl::UpdateVisibility()
|
||||
{
|
||||
SetVisibility(bActive ? EVisibility::Visible : bBlendingOut ? EVisibility::HitTestInvisible : EVisibility::Hidden);
|
||||
}
|
||||
|
||||
void SImGuiCanvasControl::Zoom(const FGeometry& MyGeometry, const float Delta, const FVector2D& MousePosition)
|
||||
{
|
||||
// If blending out, then cancel.
|
||||
bBlendingOut = false;
|
||||
|
||||
float OldCanvasScale = CanvasScale;
|
||||
|
||||
// Normalize scale to make sure that it changes in fixed steps and that we don't accumulate rounding errors.
|
||||
// Normalizing before applying delta allows for scales that at the edges are not rounded to the closes step.
|
||||
CanvasScale = FMath::RoundToFloat(CanvasScale / ZoomScrollSpeed) * ZoomScrollSpeed;
|
||||
|
||||
// Update the scale.
|
||||
CanvasScale = FMath::Clamp(CanvasScale + Delta, GetMinScale(MyGeometry), 2.f);
|
||||
|
||||
// Update canvas offset to keep it fixed around pivot point.
|
||||
if (CanvasScale != OldCanvasScale && OldCanvasScale != 0.f)
|
||||
{
|
||||
// Pivot points (in screen space):
|
||||
// 1) Around mouse: MousePosition
|
||||
// 2) Fixed in top-left corner: MyGeometry.GetLayoutBoundingRect().GetTopLeft()
|
||||
// 3) Fixed in centre: MyGeometry.GetLayoutBoundingRect().GetCenter()
|
||||
const FVector2D PivotPoint = MyGeometry.GetAccumulatedRenderTransform().Inverse().TransformPoint(MousePosition);
|
||||
const FVector2D Pivot = PivotPoint - CanvasOffset;
|
||||
|
||||
CanvasOffset += Pivot * (OldCanvasScale - CanvasScale) / OldCanvasScale;
|
||||
}
|
||||
|
||||
UpdateRenderTransform();
|
||||
}
|
||||
|
||||
void SImGuiCanvasControl::UpdateRenderTransform()
|
||||
{
|
||||
const FVector2D RenderOffset = Round(ContentOffset * CanvasScale + CanvasOffset);
|
||||
Transform = FSlateRenderTransform(CanvasScale, RenderOffset);
|
||||
OnTransformChanged.ExecuteIfBound(Transform);
|
||||
}
|
||||
|
||||
float SImGuiCanvasControl::GetMinScale(const FGeometry& MyGeometry)
|
||||
{
|
||||
#if FROM_ENGINE_VERSION(4, 17)
|
||||
#define GET_BOUNDING_RECT GetLayoutBoundingRect
|
||||
#else
|
||||
#define GET_BOUNDING_RECT GetClippingRect
|
||||
#endif
|
||||
|
||||
const FVector2D DefaultCanvasSize = MyGeometry.GetAccumulatedRenderTransform().TransformVector(ComputeDesiredSize(1.f));
|
||||
const FVector2D WidgetSize = MyGeometry.GET_BOUNDING_RECT().GetSize();
|
||||
return FMath::Min(WidgetSize.X / DefaultCanvasSize.X, WidgetSize.Y / DefaultCanvasSize.Y);
|
||||
|
||||
#undef GET_BOUNDING_RECT
|
||||
}
|
108
Source/ImGui/Private/Widgets/SImGuiCanvasControl.h
Normal file
108
Source/ImGui/Private/Widgets/SImGuiCanvasControl.h
Normal file
@ -0,0 +1,108 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ImGuiInputState.h"
|
||||
#include "ImGuiModuleSettings.h"
|
||||
|
||||
#include <Brushes/SlateBorderBrush.h>
|
||||
#include <Widgets/DeclarativeSyntaxSupport.h>
|
||||
#include <Widgets/SLeafWidget.h>
|
||||
|
||||
|
||||
// Widget that controls transform of ImGui canvas/space.
|
||||
// When active, additionally it shows boundaries of ImGui canvas and default visible area.
|
||||
// TODO: Bind to ImGui context or properties to dynamically read canvas size.
|
||||
// TODO: Bind to properties to allow configure colors.
|
||||
class SImGuiCanvasControl : public SLeafWidget
|
||||
{
|
||||
typedef SLeafWidget Super;
|
||||
|
||||
public:
|
||||
|
||||
DECLARE_DELEGATE_OneParam(FOnTransformChanged, const FSlateRenderTransform&);
|
||||
|
||||
SLATE_BEGIN_ARGS(SImGuiCanvasControl)
|
||||
{}
|
||||
SLATE_EVENT(FOnTransformChanged, OnTransformChanged)
|
||||
SLATE_END_ARGS()
|
||||
|
||||
void Construct(const FArguments& InArgs);
|
||||
|
||||
bool IsActive() const { return bActive; }
|
||||
void SetActive(bool bInActive);
|
||||
|
||||
const FSlateRenderTransform& GetTransform() const { return Transform; }
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// SWidget overrides
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
|
||||
|
||||
virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
|
||||
virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
|
||||
virtual FReply OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
|
||||
virtual FReply OnDragDetected(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
|
||||
virtual FReply OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override;
|
||||
|
||||
virtual FReply OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override;
|
||||
|
||||
virtual FVector2D ComputeDesiredSize(float InScale) const override;
|
||||
|
||||
virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
|
||||
|
||||
private:
|
||||
|
||||
enum class EDragRequest : uint8
|
||||
{
|
||||
None,
|
||||
Content,
|
||||
Canvas
|
||||
};
|
||||
|
||||
void UpdateVisibility();
|
||||
|
||||
void Zoom(const FGeometry& MyGeometry, const float Delta, const FVector2D& MousePosition);
|
||||
|
||||
void UpdateRenderTransform();
|
||||
|
||||
float GetMinScale(const FGeometry& MyGeometry);
|
||||
|
||||
mutable FSlateBorderBrush CanvasBorderBrush = FSlateBorderBrush("SImGuiCanvasControl-CanvasBorder", FMargin(0.f, 0.f, 1.f, 1.f), FLinearColor::White);
|
||||
mutable FSlateBorderBrush FrameBorderBrush = FSlateBorderBrush("SImGuiCanvasControl-FrameBorder", FMargin(0.f, 0.f, 1.f, 1.f), FLinearColor::White);
|
||||
|
||||
FOnTransformChanged OnTransformChanged;
|
||||
|
||||
// Transform from ImGui space.
|
||||
FSlateRenderTransform Transform;
|
||||
|
||||
// Offset of the ImGui content in ImGui space.
|
||||
FVector2D ContentOffset = FVector2D::ZeroVector;
|
||||
|
||||
// Offset of the ImGui canvas in widget local space.
|
||||
FVector2D CanvasOffset = FVector2D::ZeroVector;
|
||||
|
||||
// Scale of the ImGui canvas in widget local space.
|
||||
float CanvasScale = 1.f;
|
||||
|
||||
// Opacity scaling visibility of elements during blending.
|
||||
float Opacity = 1.f;
|
||||
|
||||
// Whether this widget is active. While active, widget allows to modify transform of ImGui canvas, shows its
|
||||
// boundaries and default visible area.
|
||||
bool bActive = false;
|
||||
|
||||
// Whether we are blending out after widget was deactivated. While blending out, widget is visible but it doesn't
|
||||
// process inputs anymore.
|
||||
bool bBlendingOut = false;
|
||||
|
||||
// Request is set on mouse button press before drag operation is started. It remains valid until activating button
|
||||
// is released or until drag operation is finished or until it is replaced by alternative request.
|
||||
// Highlights are bound to requests, what means that they can also be activated before drag operation is started.
|
||||
EDragRequest DragRequest = EDragRequest::None;
|
||||
};
|
83
Source/ImGui/Private/Widgets/SImGuiLayout.cpp
Normal file
83
Source/ImGui/Private/Widgets/SImGuiLayout.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#include "SImGuiLayout.h"
|
||||
#include "SImGuiWidget.h"
|
||||
|
||||
#include "ImGuiModuleManager.h"
|
||||
#include "ImGuiModuleSettings.h"
|
||||
|
||||
#include <SlateOptMacros.h>
|
||||
#include <Widgets/Layout/SConstraintCanvas.h>
|
||||
#include <Widgets/Layout/SDPIScaler.h>
|
||||
#include <Widgets/Layout/SScaleBox.h>
|
||||
|
||||
|
||||
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||
void SImGuiLayout::Construct(const FArguments& InArgs)
|
||||
{
|
||||
checkf(InArgs._GameViewport, TEXT("Null Game Viewport argument"));
|
||||
|
||||
ModuleManager = InArgs._ModuleManager;
|
||||
GameViewport = InArgs._GameViewport;
|
||||
|
||||
if (ModuleManager)
|
||||
{
|
||||
auto& Settings = ModuleManager->GetSettings();
|
||||
SetDPIScale(Settings.GetDPIScaleInfo());
|
||||
if (!Settings.OnDPIScaleChangedDelegate.IsBoundToObject(this))
|
||||
{
|
||||
Settings.OnDPIScaleChangedDelegate.AddRaw(this, &SImGuiLayout::SetDPIScale);
|
||||
}
|
||||
}
|
||||
|
||||
ChildSlot
|
||||
[
|
||||
// Remove accumulated scale to manually control how we draw data.
|
||||
SNew(SScaleBox)
|
||||
.IgnoreInheritedScale(true)
|
||||
.HAlign(HAlign_Fill)
|
||||
.VAlign(VAlign_Fill)
|
||||
.Visibility(EVisibility::SelfHitTestInvisible)
|
||||
[
|
||||
// Apply custom scale if needed.
|
||||
SNew(SDPIScaler)
|
||||
.DPIScale(TAttribute<float>(this, &SImGuiLayout::GetDPIScale))
|
||||
.Visibility(EVisibility::SelfHitTestInvisible)
|
||||
[
|
||||
SNew(SConstraintCanvas)
|
||||
+ SConstraintCanvas::Slot()
|
||||
.Anchors(FAnchors(0.f, 0.f, 1.f, 1.f))
|
||||
.AutoSize(true)
|
||||
.Offset(FMargin(1.f, 1.f, 0.f, 1.f))
|
||||
.Alignment(FVector2D::ZeroVector)
|
||||
[
|
||||
SNew(SImGuiWidget)
|
||||
.ModuleManager(InArgs._ModuleManager)
|
||||
.GameViewport(InArgs._GameViewport)
|
||||
.ContextIndex(InArgs._ContextIndex)
|
||||
#if !ENGINE_COMPATIBILITY_LEGACY_CLIPPING_API
|
||||
// To correctly clip borders. Using SScissorRectBox in older versions seems to be not necessary.
|
||||
.Clipping(EWidgetClipping::ClipToBounds)
|
||||
#endif
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
SetVisibility(EVisibility::SelfHitTestInvisible);
|
||||
}
|
||||
|
||||
SImGuiLayout::~SImGuiLayout()
|
||||
{
|
||||
if (ModuleManager)
|
||||
{
|
||||
ModuleManager->GetSettings().OnDPIScaleChangedDelegate.RemoveAll(this);
|
||||
}
|
||||
}
|
||||
|
||||
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||
|
||||
void SImGuiLayout::SetDPIScale(const FImGuiDPIScaleInfo& ScaleInfo)
|
||||
{
|
||||
DPIScale = ScaleInfo.GetSlateScale();
|
||||
}
|
43
Source/ImGui/Private/Widgets/SImGuiLayout.h
Normal file
43
Source/ImGui/Private/Widgets/SImGuiLayout.h
Normal file
@ -0,0 +1,43 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <UObject/WeakObjectPtr.h>
|
||||
#include <Widgets/DeclarativeSyntaxSupport.h>
|
||||
#include <Widgets/SCompoundWidget.h>
|
||||
|
||||
|
||||
class FImGuiModuleManager;
|
||||
class UGameViewportClient;
|
||||
struct FImGuiDPIScaleInfo;
|
||||
|
||||
// Layout preset for ImGui Widget.
|
||||
class SImGuiLayout : public SCompoundWidget
|
||||
{
|
||||
typedef SCompoundWidget Super;
|
||||
|
||||
public:
|
||||
|
||||
SLATE_BEGIN_ARGS(SImGuiLayout)
|
||||
{}
|
||||
SLATE_ARGUMENT(FImGuiModuleManager*, ModuleManager)
|
||||
SLATE_ARGUMENT(UGameViewportClient*, GameViewport)
|
||||
SLATE_ARGUMENT(int32, ContextIndex)
|
||||
SLATE_END_ARGS()
|
||||
|
||||
void Construct(const FArguments& InArgs);
|
||||
|
||||
~SImGuiLayout();
|
||||
|
||||
const TWeakObjectPtr<UGameViewportClient>& GetGameViewport() const { return GameViewport; }
|
||||
|
||||
private:
|
||||
|
||||
float GetDPIScale() const { return DPIScale; }
|
||||
void SetDPIScale(const FImGuiDPIScaleInfo& ScaleInfo);
|
||||
|
||||
FImGuiModuleManager* ModuleManager = nullptr;
|
||||
TWeakObjectPtr<UGameViewportClient> GameViewport;
|
||||
|
||||
float DPIScale = 1.f;
|
||||
};
|
1010
Source/ImGui/Private/Widgets/SImGuiWidget.cpp
Normal file
1010
Source/ImGui/Private/Widgets/SImGuiWidget.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,17 +2,29 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ImGuiInputState.h"
|
||||
#include "ImGuiModuleDebug.h"
|
||||
#include "ImGuiModuleSettings.h"
|
||||
|
||||
#include <Widgets/SLeafWidget.h>
|
||||
#include <Rendering/RenderingCommon.h>
|
||||
#include <UObject/WeakObjectPtr.h>
|
||||
#include <Widgets/DeclarativeSyntaxSupport.h>
|
||||
#include <Widgets/SCompoundWidget.h>
|
||||
|
||||
|
||||
// Hide ImGui Widget debug in non-developer mode.
|
||||
#define IMGUI_WIDGET_DEBUG IMGUI_MODULE_DEVELOPER
|
||||
|
||||
class FImGuiModuleManager;
|
||||
class SImGuiCanvasControl;
|
||||
class UImGuiInputHandler;
|
||||
|
||||
class UGameViewportClient;
|
||||
class ULocalPlayer;
|
||||
|
||||
// Slate widget for rendering ImGui output and storing Slate inputs.
|
||||
class SImGuiWidget : public SLeafWidget
|
||||
class SImGuiWidget : public SCompoundWidget
|
||||
{
|
||||
typedef SLeafWidget Super;
|
||||
typedef SCompoundWidget Super;
|
||||
|
||||
public:
|
||||
|
||||
@ -30,22 +42,13 @@ public:
|
||||
// Get index of the context that this widget is targeting.
|
||||
int32 GetContextIndex() const { return ContextIndex; }
|
||||
|
||||
// Get input state associated with this widget.
|
||||
const FImGuiInputState& GetInputState() const { return InputState; }
|
||||
|
||||
// Get the game viewport to which this widget is attached.
|
||||
const TWeakObjectPtr<UGameViewportClient>& GetGameViewport() const { return GameViewport; }
|
||||
|
||||
// Detach widget from viewport assigned during construction (effectively allowing to dispose this widget).
|
||||
void Detach();
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// SWidget overrides
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
|
||||
|
||||
virtual bool SupportsKeyboardFocus() const override { return bInputEnabled; }
|
||||
virtual bool SupportsKeyboardFocus() const override { return bInputEnabled && !IsConsoleOpened(); }
|
||||
|
||||
virtual FReply OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent) override;
|
||||
|
||||
@ -53,6 +56,8 @@ public:
|
||||
|
||||
virtual FReply OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent) override;
|
||||
|
||||
virtual FReply OnAnalogValueChanged(const FGeometry& MyGeometry, const FAnalogInputEvent& AnalogInputEvent) override;
|
||||
|
||||
virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
|
||||
virtual FReply OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||
@ -71,60 +76,85 @@ public:
|
||||
|
||||
virtual void OnMouseLeave(const FPointerEvent& MouseEvent) override;
|
||||
|
||||
virtual FReply OnTouchStarted(const FGeometry& MyGeometry, const FPointerEvent& TouchEvent) override;
|
||||
|
||||
virtual FReply OnTouchMoved(const FGeometry& MyGeometry, const FPointerEvent& TouchEvent) override;
|
||||
|
||||
virtual FReply OnTouchEnded(const FGeometry& MyGeometry, const FPointerEvent& TouchEvent) override;
|
||||
|
||||
private:
|
||||
|
||||
enum class EInputMode : uint8
|
||||
{
|
||||
None,
|
||||
// Mouse pointer only without user focus
|
||||
MousePointerOnly,
|
||||
// Full input with user focus
|
||||
MouseAndKeyboard
|
||||
};
|
||||
void CreateInputHandler(const FSoftClassPath& HandlerClassReference);
|
||||
void ReleaseInputHandler();
|
||||
|
||||
FORCEINLINE void CopyModifierKeys(const FInputEvent& InputEvent);
|
||||
FORCEINLINE void CopyModifierKeys(const FPointerEvent& MouseEvent);
|
||||
void RegisterImGuiSettingsDelegates();
|
||||
void UnregisterImGuiSettingsDelegates();
|
||||
|
||||
void SetHideMouseCursor(bool bHide);
|
||||
|
||||
bool IsConsoleOpened() const;
|
||||
|
||||
bool IgnoreKeyEvent(const FKeyEvent& KeyEvent) const;
|
||||
// Update visibility based on input state.
|
||||
void UpdateVisibility();
|
||||
|
||||
// Update visibility based on input enabled state.
|
||||
void SetVisibilityFromInputEnabled();
|
||||
// Update cursor based on input state.
|
||||
void UpdateMouseCursor();
|
||||
|
||||
// Update input enabled state from console variable.
|
||||
void UpdateInputEnabled();
|
||||
ULocalPlayer* GetLocalPlayer() const;
|
||||
void TakeFocus();
|
||||
void ReturnFocus();
|
||||
|
||||
// Determine new input mode based on hints.
|
||||
void UpdateInputMode(bool bHasKeyboardFocus, bool bHasMousePointer);
|
||||
// Update input state.
|
||||
void UpdateInputState();
|
||||
void UpdateTransparentMouseInput(const FGeometry& AllottedGeometry);
|
||||
void HandleWindowFocusLost();
|
||||
|
||||
void UpdateMouseStatus();
|
||||
void SetDPIScale(const FImGuiDPIScaleInfo& ScaleInfo);
|
||||
|
||||
FORCEINLINE bool HasMouseEventNotification() const { return bReceivedMouseEvent; }
|
||||
FORCEINLINE void NotifyMouseEvent() { bReceivedMouseEvent = true; }
|
||||
FORCEINLINE void ClearMouseEventNotification() { bReceivedMouseEvent = false; }
|
||||
void SetCanvasSizeInfo(const FImGuiCanvasSizeInfo& CanvasSizeInfo);
|
||||
void UpdateCanvasSize();
|
||||
|
||||
void UpdateCanvasControlMode(const FInputEvent& InputEvent);
|
||||
|
||||
void OnPostImGuiUpdate();
|
||||
|
||||
FVector2D TransformScreenPointToImGui(const FGeometry& MyGeometry, const FVector2D& Point) const;
|
||||
|
||||
virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& WidgetStyle, bool bParentEnabled) const override;
|
||||
|
||||
virtual FVector2D ComputeDesiredSize(float) const override;
|
||||
|
||||
void SetImGuiTransform(const FSlateRenderTransform& Transform) { ImGuiTransform = Transform; }
|
||||
|
||||
#if IMGUI_WIDGET_DEBUG
|
||||
void OnDebugDraw();
|
||||
#endif // IMGUI_WIDGET_DEBUG
|
||||
|
||||
FImGuiModuleManager* ModuleManager = nullptr;
|
||||
TWeakObjectPtr<UGameViewportClient> GameViewport;
|
||||
TWeakObjectPtr<UImGuiInputHandler> InputHandler;
|
||||
|
||||
FSlateRenderTransform ImGuiTransform;
|
||||
FSlateRenderTransform ImGuiRenderTransform;
|
||||
|
||||
mutable TArray<FSlateVertex> VertexBuffer;
|
||||
mutable TArray<SlateIndex> IndexBuffer;
|
||||
|
||||
int32 ContextIndex = 0;
|
||||
|
||||
EInputMode InputMode = EInputMode::None;
|
||||
FVector2D MinCanvasSize = FVector2D::ZeroVector;
|
||||
FVector2D CanvasSize = FVector2D::ZeroVector;
|
||||
|
||||
float DPIScale = 1.f;
|
||||
|
||||
bool bInputEnabled = false;
|
||||
bool bReceivedMouseEvent = false;
|
||||
|
||||
FImGuiInputState InputState;
|
||||
bool bForegroundWindow = false;
|
||||
bool bHideMouseCursor = true;
|
||||
bool bTransparentMouseInput = false;
|
||||
bool bAdaptiveCanvasSize = false;
|
||||
bool bUpdateCanvasSize = false;
|
||||
bool bCanvasControlEnabled = false;
|
||||
|
||||
TSharedPtr<SImGuiCanvasControl> CanvasControlWidget;
|
||||
TWeakPtr<SWidget> PreviousUserFocusedWidget;
|
||||
};
|
@ -2,9 +2,69 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Core.h>
|
||||
#include <Delegates/Delegate.h>
|
||||
|
||||
|
||||
class UWorld;
|
||||
|
||||
/**
|
||||
* Delegates to ImGui debug events. World delegates are called once per frame during world updates and have invocation
|
||||
* lists cleared after their worlds become invalid. Multi-context delegates are called once for every updated world.
|
||||
* Early debug delegates are called during world tick start and debug delegates are called during world post actor tick
|
||||
* or in engine versions below 4.18 during world tick start.
|
||||
*
|
||||
* Order of events is defined in a way that multi-context delegates can be used to draw headers and/or footers:
|
||||
* multi-context early debug, world early debug, world debug, multi-context debug.
|
||||
*/
|
||||
class IMGUI_API FImGuiDelegates
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Get a delegate to ImGui world early debug event for current world (GWorld).
|
||||
* @returns Simple multicast delegate to debug events called once per frame to debug current world
|
||||
*/
|
||||
static FSimpleMulticastDelegate& OnWorldEarlyDebug();
|
||||
|
||||
/**
|
||||
* Get a delegate to ImGui world early debug event for given world.
|
||||
* @param World - World for which we need a delegate
|
||||
* @returns Simple multicast delegate to debug events called once per frame to debug given world
|
||||
*/
|
||||
static FSimpleMulticastDelegate& OnWorldEarlyDebug(UWorld* World);
|
||||
|
||||
/**
|
||||
* Get a delegate to ImGui multi-context early debug event.
|
||||
* @returns Simple multicast delegate to debug events called once per frame for every world to debug
|
||||
*/
|
||||
static FSimpleMulticastDelegate& OnMultiContextEarlyDebug();
|
||||
|
||||
/**
|
||||
* Get a delegate to ImGui world debug event for current world (GWorld).
|
||||
* @returns Simple multicast delegate to debug events called once per frame to debug current world
|
||||
*/
|
||||
static FSimpleMulticastDelegate& OnWorldDebug();
|
||||
|
||||
/**
|
||||
* Get a delegate to ImGui world debug event for given world.
|
||||
* @param World - World for which we need a delegate
|
||||
* @returns Simple multicast delegate to debug events called once per frame to debug given world
|
||||
*/
|
||||
static FSimpleMulticastDelegate& OnWorldDebug(UWorld* World);
|
||||
|
||||
/**
|
||||
* Get a delegate to ImGui multi-context debug event.
|
||||
* @returns Simple multicast delegate to debug events called once per frame for every world to debug
|
||||
*/
|
||||
static FSimpleMulticastDelegate& OnMultiContextDebug();
|
||||
};
|
||||
|
||||
|
||||
/** Enable to support legacy ImGui delegates API. */
|
||||
#define IMGUI_WITH_OBSOLETE_DELEGATES 1
|
||||
|
||||
#if IMGUI_WITH_OBSOLETE_DELEGATES
|
||||
|
||||
/** Delegate that allows to subscribe for ImGui events. */
|
||||
typedef FSimpleMulticastDelegate::FDelegate FImGuiDelegate;
|
||||
|
||||
@ -53,3 +113,5 @@ private:
|
||||
|
||||
friend class FImGuiModule;
|
||||
};
|
||||
|
||||
#endif // IMGUI_WITH_OBSOLETE_DELEGATES
|
||||
|
198
Source/ImGui/Public/ImGuiInputHandler.h
Normal file
198
Source/ImGui/Public/ImGuiInputHandler.h
Normal file
@ -0,0 +1,198 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <CoreMinimal.h>
|
||||
#include <Input/Reply.h>
|
||||
#include <UObject/Object.h>
|
||||
#include <UObject/WeakObjectPtr.h>
|
||||
|
||||
#include "ImGuiInputHandler.generated.h"
|
||||
|
||||
|
||||
class FImGuiModuleManager;
|
||||
class UGameViewportClient;
|
||||
|
||||
struct FAnalogInputEvent;
|
||||
struct FCharacterEvent;
|
||||
struct FKeyEvent;
|
||||
|
||||
#if WITH_EDITOR
|
||||
class FUICommandInfo;
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
|
||||
/**
|
||||
* Handles input and sends it to the input state, which is copied to the ImGui IO at the beginning of the frame.
|
||||
* Implementation of the input handler can be changed in the ImGui project settings by changing ImGuiInputHandlerClass.
|
||||
*/
|
||||
UCLASS()
|
||||
class IMGUI_API UImGuiInputHandler : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Called to handle character events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnKeyChar(const struct FCharacterEvent& CharacterEvent);
|
||||
|
||||
/**
|
||||
* Called to handle key down events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnKeyDown(const FKeyEvent& KeyEvent);
|
||||
|
||||
/**
|
||||
* Called to handle key up events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnKeyUp(const FKeyEvent& KeyEvent);
|
||||
|
||||
/**
|
||||
* Called to handle analog value change events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnAnalogValueChanged(const FAnalogInputEvent& AnalogInputEvent);
|
||||
|
||||
/**
|
||||
* Called to handle mouse button down events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnMouseButtonDown(const FPointerEvent& MouseEvent);
|
||||
|
||||
/**
|
||||
* Called to handle mouse button double-click events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnMouseButtonDoubleClick(const FPointerEvent& MouseEvent);
|
||||
|
||||
/**
|
||||
* Called to handle mouse button up events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnMouseButtonUp(const FPointerEvent& MouseEvent);
|
||||
|
||||
/**
|
||||
* Called to handle mouse wheel events.
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnMouseWheel(const FPointerEvent& MouseEvent);
|
||||
|
||||
/**
|
||||
* Called to handle mouse move events.
|
||||
* @param MousePosition Mouse position (in ImGui space)
|
||||
* @param MouseEvent Optional mouse event passed from Slate
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnMouseMove(const FVector2D& MousePosition, const FPointerEvent& MouseEvent);
|
||||
virtual FReply OnMouseMove(const FVector2D& MousePosition);
|
||||
|
||||
/**
|
||||
* Called to handle touch started event.
|
||||
* @param TouchPosition Touch position (in ImGui space)
|
||||
* @param TouchEvent Touch event passed from Slate
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnTouchStarted(const FVector2D& TouchPosition, const FPointerEvent& TouchEvent);
|
||||
|
||||
/**
|
||||
* Called to handle touch moved event.
|
||||
* @param TouchPosition Touch position (in ImGui space)
|
||||
* @param TouchEvent Touch event passed from Slate
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnTouchMoved(const FVector2D& TouchPosition, const FPointerEvent& TouchEvent);
|
||||
|
||||
/**
|
||||
* Called to handle touch ended event.
|
||||
* @param TouchPosition Touch position (in ImGui space)
|
||||
* @param TouchEvent Touch event passed from Slate
|
||||
* @returns Response whether the event was handled
|
||||
*/
|
||||
virtual FReply OnTouchEnded(const FVector2D& TouchPosition, const FPointerEvent& TouchEvent);
|
||||
|
||||
/** Called to handle activation of the keyboard input. */
|
||||
virtual void OnKeyboardInputEnabled();
|
||||
|
||||
/** Called to handle deactivation of the keyboard input. */
|
||||
virtual void OnKeyboardInputDisabled();
|
||||
|
||||
/** Called to handle activation of the gamepad input. */
|
||||
virtual void OnGamepadInputEnabled();
|
||||
|
||||
/** Called to handle deactivation of the gamepad input. */
|
||||
virtual void OnGamepadInputDisabled();
|
||||
|
||||
/** Called to handle activation of the mouse input. */
|
||||
virtual void OnMouseInputEnabled();
|
||||
|
||||
/** Called to handle deactivation of the mouse input. */
|
||||
virtual void OnMouseInputDisabled();
|
||||
|
||||
protected:
|
||||
|
||||
/** Copy state of modifier keys to input state. */
|
||||
void CopyModifierKeys(const FInputEvent& InputEvent);
|
||||
|
||||
/**
|
||||
* Checks whether this is a key event that can open console.
|
||||
* @param KeyEvent - Key event to test.
|
||||
* @returns True, if this key event can open console.
|
||||
*/
|
||||
bool IsConsoleEvent(const FKeyEvent& KeyEvent) const;
|
||||
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Checks whether this is a key event that can stop PIE session.
|
||||
* @param KeyEvent - Key event to test.
|
||||
* @returns True, if this key event can stop PIE session.
|
||||
*/
|
||||
bool IsStopPlaySessionEvent(const FKeyEvent& KeyEvent) const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Checks whether this key event can toggle ImGui input (as defined in settings).
|
||||
* @param KeyEvent - Key event to test.
|
||||
* @returns True, if this key is bound to 'ImGui.ToggleInput' command that switches ImGui input mode.
|
||||
*/
|
||||
bool IsToggleInputEvent(const FKeyEvent& KeyEvent) const;
|
||||
|
||||
/**
|
||||
* Checks whether corresponding ImGui context has an active item (holding cursor focus).
|
||||
* @returns True, if corresponding context has an active item.
|
||||
*/
|
||||
bool HasImGuiActiveItem() const;
|
||||
|
||||
private:
|
||||
|
||||
void UpdateInputStatePointer();
|
||||
|
||||
void OnSoftwareCursorChanged(bool);
|
||||
|
||||
void OnPostImGuiUpdate();
|
||||
|
||||
void Initialize(FImGuiModuleManager* InModuleManager, UGameViewportClient* InGameViewport, int32 InContextIndex);
|
||||
|
||||
virtual void BeginDestroy() override;
|
||||
|
||||
class FImGuiInputState* InputState = nullptr;
|
||||
|
||||
bool bMouseInputEnabled = false;
|
||||
bool bKeyboardInputEnabled = false;
|
||||
bool bGamepadInputEnabled = false;
|
||||
|
||||
FImGuiModuleManager* ModuleManager = nullptr;
|
||||
|
||||
TWeakObjectPtr<UGameViewportClient> GameViewport;
|
||||
|
||||
int32 ContextIndex = -1;
|
||||
|
||||
#if WITH_EDITOR
|
||||
TSharedPtr<FUICommandInfo> StopPlaySessionCommandInfo;
|
||||
#endif
|
||||
|
||||
friend class FImGuiInputHandlerFactory;
|
||||
};
|
@ -3,8 +3,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "ImGuiDelegates.h"
|
||||
#include "ImGuiModuleProperties.h"
|
||||
#include "ImGuiTextureHandle.h"
|
||||
|
||||
#include <ModuleManager.h>
|
||||
#include <Modules/ModuleManager.h>
|
||||
|
||||
|
||||
class FImGuiModule : public IModuleInterface
|
||||
@ -32,6 +34,8 @@ public:
|
||||
return FModuleManager::Get().IsModuleLoaded("ImGui");
|
||||
}
|
||||
|
||||
#if IMGUI_WITH_OBSOLETE_DELEGATES
|
||||
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Add a delegate called at the end of editor debug frame to draw debug controls in its ImGui context, creating
|
||||
@ -53,6 +57,16 @@ public:
|
||||
*/
|
||||
virtual FImGuiDelegateHandle AddWorldImGuiDelegate(const FImGuiDelegate& Delegate);
|
||||
|
||||
/**
|
||||
* Add a delegate called at the end of a specific world's debug frame to draw debug controls in its ImGui context,
|
||||
* creating that context on demand.
|
||||
*
|
||||
* @param World - A specific world to add the delegate to to
|
||||
* @param Delegate - Delegate that we want to add (@see FImGuiDelegate::Create...)
|
||||
* @returns Returns handle that can be used to remove delegate (@see RemoveImGuiDelegate)
|
||||
*/
|
||||
virtual FImGuiDelegateHandle AddWorldImGuiDelegate(const UWorld* World, const FImGuiDelegate& Delegate);
|
||||
|
||||
/**
|
||||
* Add shared delegate called for each ImGui context at the end of debug frame, after calling context specific
|
||||
* delegate. This delegate will be used for any ImGui context, created before or after it is registered.
|
||||
@ -69,7 +83,105 @@ public:
|
||||
*/
|
||||
virtual void RemoveImGuiDelegate(const FImGuiDelegateHandle& Handle);
|
||||
|
||||
#endif // #if IMGUI_WITH_OBSOLETE_DELEGATES
|
||||
|
||||
/**
|
||||
* If it exists, get a handle to the texture with given resource name.
|
||||
*
|
||||
* @param Name - Resource name of a texture to find
|
||||
* @returns Handle to a registered texture or invalid handle if resources could not be found or were not valid
|
||||
*/
|
||||
virtual FImGuiTextureHandle FindTextureHandle(const FName& Name);
|
||||
|
||||
/**
|
||||
* Register texture and create its Slate resources. If texture with that name already exists then it may be updated
|
||||
* or if bMakeUnique is true, exception will be thrown. Throws exception, if name argument is NAME_None or texture
|
||||
* is null.
|
||||
*
|
||||
* Note, that updating texture resources doesn't invalidate already existing handles and returned handle will have
|
||||
* the same value.
|
||||
*
|
||||
* @param Name - Resource name for the texture that needs to be registered or updated
|
||||
* @param Texture - Texture for which we want to create or update Slate resources
|
||||
* @param bMakeUnique - If false then existing resources are updated/overwritten (default). If true, then stricter
|
||||
* policy is applied and if resource with that name exists then exception is thrown.
|
||||
* @returns Handle to the texture resources, which can be used to release allocated resources and as an argument to
|
||||
* relevant ImGui functions
|
||||
*/
|
||||
virtual FImGuiTextureHandle RegisterTexture(const FName& Name, class UTexture* Texture, bool bMakeUnique = false);
|
||||
|
||||
/**
|
||||
* Unregister texture and release its Slate resources. If handle is null or not valid, this function fails silently
|
||||
* (for definition of 'valid' look @ FImGuiTextureHandle).
|
||||
*
|
||||
* @returns ImGui Texture Handle to texture that needs to be unregistered
|
||||
*/
|
||||
virtual void ReleaseTexture(const FImGuiTextureHandle& Handle);
|
||||
|
||||
virtual void RebuildFontAtlas();
|
||||
|
||||
/**
|
||||
* Get ImGui module properties.
|
||||
*
|
||||
* @returns Reference to an instance of ImGui module properties that allows to read and/or modify module state.
|
||||
*/
|
||||
virtual FImGuiModuleProperties& GetProperties();
|
||||
virtual const FImGuiModuleProperties& GetProperties() const;
|
||||
|
||||
/**
|
||||
* DEPRECIATED: Please use GetProperties() as this function is scheduled for removal.
|
||||
* Check whether Input Mode is enabled (tests ImGui.InputEnabled console variable).
|
||||
*
|
||||
* @returns True, if Input Mode is enabled (ImGui.InputEnabled != 0) and false otherwise.
|
||||
*/
|
||||
virtual bool IsInputMode() const;
|
||||
|
||||
/**
|
||||
* DEPRECIATED: Please use GetProperties() as this function is scheduled for removal.
|
||||
* Set Input Mode state (sets ImGui.InputEnabled console variable, so it can be used together with a console).
|
||||
*
|
||||
* @param bEnabled - Whether Input Mode should be enabled (ImGui.InputEnabled = 1) or not (ImGui.InputEnabled = 0).
|
||||
*/
|
||||
virtual void SetInputMode(bool bEnabled);
|
||||
|
||||
/**
|
||||
* DEPRECIATED: Please use GetProperties() as this function is scheduled for removal.
|
||||
* Toggle Input Mode state (changes ImGui.InputEnabled console variable).
|
||||
*/
|
||||
virtual void ToggleInputMode();
|
||||
|
||||
/**
|
||||
* DEPRECIATED: Please use GetProperties() as this function is scheduled for removal.
|
||||
* Check whether ImGui Demo is shown (tests ImGui.ShowDemo console variable).
|
||||
*
|
||||
* @returns True, if demo is shown (ImGui.ShowDemo != 0) and false otherwise.
|
||||
*/
|
||||
virtual bool IsShowingDemo() const;
|
||||
|
||||
/**
|
||||
* DEPRECIATED: Please use GetProperties() as this function is scheduled for removal.
|
||||
* Set whether to show ImGui Demo (sets ImGui.ShowDemo console variable, so it can be used together with a console).
|
||||
*
|
||||
* @param bShow - Whether to show ImGui Demo (ImGui.ShowDemo = 1) or not (ImGui.ShowDemo = 0).
|
||||
*/
|
||||
virtual void SetShowDemo(bool bShow);
|
||||
|
||||
/**
|
||||
* DEPRECIATED: Please use GetProperties() as this function is scheduled for removal.
|
||||
* Toggle ImGui Demo (changes ImGui.ShowDemo console variable).
|
||||
*/
|
||||
virtual void ToggleShowDemo();
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
private:
|
||||
#if WITH_EDITOR
|
||||
virtual void SetProperties(const FImGuiModuleProperties& Properties);
|
||||
struct FImGuiContextHandle* ImGuiContextHandle = nullptr;
|
||||
struct FImGuiDelegatesContainerHandle* DelegatesContainerHandle = nullptr;
|
||||
friend struct FImGuiContextHandle;
|
||||
friend struct FImGuiDelegatesContainerHandle;
|
||||
#endif
|
||||
};
|
||||
|
109
Source/ImGui/Public/ImGuiModuleProperties.h
Normal file
109
Source/ImGui/Public/ImGuiModuleProperties.h
Normal file
@ -0,0 +1,109 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
struct ImFontConfig;
|
||||
|
||||
/** Properties that define state of the ImGui module. */
|
||||
class IMGUI_API FImGuiModuleProperties
|
||||
{
|
||||
public:
|
||||
|
||||
/** Check whether input is enabled. */
|
||||
bool IsInputEnabled() const { return bInputEnabled; }
|
||||
|
||||
/** Enable or disable ImGui input. */
|
||||
void SetInputEnabled(bool bEnabled) { bInputEnabled = bEnabled; }
|
||||
|
||||
/** Toggle ImGui input. */
|
||||
void ToggleInput() { SetInputEnabled(!IsInputEnabled()); }
|
||||
|
||||
/** Check whether keyboard navigation is enabled. */
|
||||
bool IsKeyboardNavigationEnabled() const { return bKeyboardNavigationEnabled; }
|
||||
|
||||
/** Enable or disable keyboard navigation. */
|
||||
void SetKeyboardNavigationEnabled(bool bEnabled) { bKeyboardNavigationEnabled = bEnabled; }
|
||||
|
||||
/** Toggle keyboard navigation. */
|
||||
void ToggleKeyboardNavigation() { SetKeyboardNavigationEnabled(!IsKeyboardNavigationEnabled()); }
|
||||
|
||||
/** Check whether gamepad navigation is enabled. */
|
||||
bool IsGamepadNavigationEnabled() const { return bGamepadNavigationEnabled; }
|
||||
|
||||
/** Enable or disable gamepad navigation. */
|
||||
void SetGamepadNavigationEnabled(bool bEnabled) { bGamepadNavigationEnabled = bEnabled; }
|
||||
|
||||
/** Toggle gamepad navigation. */
|
||||
void ToggleGamepadNavigation() { SetGamepadNavigationEnabled(!IsGamepadNavigationEnabled()); }
|
||||
|
||||
/** Check whether keyboard input is shared with game. */
|
||||
bool IsKeyboardInputShared() const { return bKeyboardInputShared; }
|
||||
|
||||
/** Set whether keyboard input should be shared with game. */
|
||||
void SetKeyboardInputShared(bool bShared) { bKeyboardInputShared = bShared; }
|
||||
|
||||
/** Toggle whether keyboard input should be shared with game. */
|
||||
void ToggleKeyboardInputSharing() { SetKeyboardInputShared(!IsKeyboardInputShared()); }
|
||||
|
||||
/** Check whether gamepad input is shared with game. */
|
||||
bool IsGamepadInputShared() const { return bGamepadInputShared; }
|
||||
|
||||
/** Set whether gamepad input should be shared with game. */
|
||||
void SetGamepadInputShared(bool bShared) { bGamepadInputShared = bShared; }
|
||||
|
||||
/** Toggle whether gamepad input should be shared with game. */
|
||||
void ToggleGamepadInputSharing() { SetGamepadInputShared(!IsGamepadInputShared()); }
|
||||
|
||||
/** Check whether mouse input is shared with game. */
|
||||
bool IsMouseInputShared() const { return bMouseInputShared; }
|
||||
|
||||
/** Set whether mouse input should be shared with game. */
|
||||
void SetMouseInputShared(bool bShared) { bMouseInputShared = bShared; }
|
||||
|
||||
/** Toggle whether mouse input should be shared with game. */
|
||||
void ToggleMouseInputSharing() { SetMouseInputShared(!IsMouseInputShared()); }
|
||||
|
||||
/** Check whether ImGui demo is visible. */
|
||||
bool ShowDemo() const { return bShowDemo; }
|
||||
|
||||
/** Show or hide ImGui demo. */
|
||||
void SetShowDemo(bool bShow) { bShowDemo = bShow; }
|
||||
|
||||
/** Toggle ImGui demo. */
|
||||
void ToggleDemo() { SetShowDemo(!ShowDemo()); }
|
||||
|
||||
/** Check whether docking is enabled. */
|
||||
bool IsDockingEnabled() const { return bIsDockingEnabled; }
|
||||
|
||||
/** Set whether docking is enabled. */
|
||||
void SetDockingEnabled(bool bDockingEnabled) { bIsDockingEnabled = bDockingEnabled; }
|
||||
|
||||
/** Toggle whether docking is enabled. */
|
||||
void ToggleDockingEnabled() { SetDockingEnabled(!IsDockingEnabled()); }
|
||||
|
||||
/** Adds a new font to initialize */
|
||||
void AddCustomFont(FName FontName, TSharedPtr<ImFontConfig> Font) { CustomFonts.Emplace(FontName, Font); }
|
||||
|
||||
/** Removes a font from the custom font list */
|
||||
void RemoveCustomFont(FName FontName) { CustomFonts.Remove(FontName); }
|
||||
|
||||
/** Gets the map of registered custom fonts */
|
||||
TMap<FName, TSharedPtr<ImFontConfig>>& GetCustomFonts() { return CustomFonts; }
|
||||
|
||||
private:
|
||||
|
||||
bool bInputEnabled = false;
|
||||
|
||||
bool bKeyboardNavigationEnabled = false;
|
||||
bool bGamepadNavigationEnabled = false;
|
||||
|
||||
bool bKeyboardInputShared = false;
|
||||
bool bGamepadInputShared = false;
|
||||
bool bMouseInputShared = false;
|
||||
|
||||
bool bShowDemo = false;
|
||||
|
||||
bool bIsDockingEnabled = true;
|
||||
|
||||
TMap<FName, TSharedPtr<ImFontConfig>> CustomFonts;
|
||||
};
|
64
Source/ImGui/Public/ImGuiTextureHandle.h
Normal file
64
Source/ImGui/Public/ImGuiTextureHandle.h
Normal file
@ -0,0 +1,64 @@
|
||||
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <CoreMinimal.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
|
||||
/**
|
||||
* Handle to texture resources registered in module instance. Returned after successful texture registration.
|
||||
* Can be implicitly converted to ImTextureID making it possible to use it directly with ImGui interface.
|
||||
* Once texture is not needed handle can be used to release resources.
|
||||
*/
|
||||
class IMGUI_API FImGuiTextureHandle
|
||||
{
|
||||
public:
|
||||
|
||||
/** Creates an empty (null and not valid) texture handle. */
|
||||
FImGuiTextureHandle();
|
||||
|
||||
/**
|
||||
* Checks whether this handle is null. Can be used as a quick test whether it points to any resources but it does
|
||||
* not check whether those resources are valid (see @ IsValid).
|
||||
*
|
||||
* @returns True, if this handle is null (Name is NAME_None and TextureId is invalid) and false otherwise.
|
||||
*/
|
||||
bool IsNull() const { return Name == NAME_None; }
|
||||
|
||||
/**
|
||||
* Checks whether this handle is not null and valid. Valid handle points to valid texture resources.
|
||||
* It is slower but safer test, more useful when there is no guarantee that resources haven't been released.
|
||||
*
|
||||
* @returns True, if this handle is not null and valid, false otherwise.
|
||||
*/
|
||||
bool IsValid() const { return !IsNull() && HasValidEntry(); }
|
||||
|
||||
/** Get the name of the texture resources (NAME_None if handle is null). */
|
||||
const FName& GetName() const { return Name; }
|
||||
|
||||
/** Get the ImGui texture id for this texture (invalid if handle is null). */
|
||||
ImTextureID GetTextureId() const { return TextureId; }
|
||||
|
||||
/** Implicit conversion to ImTextureID. */
|
||||
operator ImTextureID() const { return GetTextureId(); }
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Creates a texture handle with known name and texture id.
|
||||
* @param InName - Name of the texture
|
||||
* @param InTextureId - ImGui id of texture
|
||||
*/
|
||||
FImGuiTextureHandle(const FName& InName, ImTextureID InTextureId);
|
||||
|
||||
/** Checks if texture manager has entry that matches this name and texture id index. */
|
||||
bool HasValidEntry() const;
|
||||
|
||||
FName Name;
|
||||
ImTextureID TextureId;
|
||||
|
||||
// Give module class a private access, so it can create valid handles.
|
||||
friend class FImGuiModule;
|
||||
};
|
146
Source/ThirdParty/ImGuiLibrary/Docs/BACKENDS.md
vendored
Normal file
146
Source/ThirdParty/ImGuiLibrary/Docs/BACKENDS.md
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
_(You may browse this at https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md or view this file with any Markdown viewer)_
|
||||
|
||||
## Dear ImGui: Backends
|
||||
|
||||
### Integrating backends
|
||||
|
||||
💡 The **[Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) wiki guide** has examples of how to integrate Dear ImGui into an existing application.
|
||||
<BR> The [EXAMPLES.MD](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md) documentation may also be worth a read.
|
||||
|
||||
### What are backends?
|
||||
|
||||
Dear ImGui is highly portable and only requires a few things to run and render, typically:
|
||||
|
||||
- Required: providing mouse/keyboard inputs (fed into the `ImGuiIO` structure).
|
||||
- Required: uploading the font atlas texture into graphics memory.
|
||||
- Required: rendering indexed textured triangles with a clipping rectangle.
|
||||
|
||||
Extra features are opt-in, our backends try to support as many as possible:
|
||||
|
||||
- Optional: custom texture binding support.
|
||||
- Optional: clipboard support.
|
||||
- Optional: gamepad support.
|
||||
- Optional: mouse cursor shape support.
|
||||
- Optional: IME support.
|
||||
- Optional: multi-viewports support.
|
||||
etc.
|
||||
|
||||
This is essentially what each backend is doing + obligatory portability cruft. Using standard backends ensure you can get all those features including the ones that would be harder to implement on your side (e.g. multi-viewports support).
|
||||
|
||||
It is important to understand the difference between the core Dear ImGui library (files in the root folder)
|
||||
and the backends which we are describing here (backends/ folder).
|
||||
|
||||
- Some issues may only be backend or platform specific.
|
||||
- You should be able to write backends for pretty much any platform and any 3D graphics API.
|
||||
e.g. you can get creative and use software rendering or render remotely on a different machine.
|
||||
|
||||
### Standard backends
|
||||
|
||||
**The [backends/](https://github.com/ocornut/imgui/blob/master/backends) folder contains backends for popular platforms/graphics API, which you can use in
|
||||
your application or engine to easily integrate Dear ImGui.** Each backend is typically self-contained in a pair of files: imgui_impl_XXXX.cpp + imgui_impl_XXXX.h.
|
||||
|
||||
- The 'Platform' backends are in charge of: mouse/keyboard/gamepad inputs, cursor shape, timing, and windowing.<BR>
|
||||
e.g. Windows ([imgui_impl_win32.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_win32.cpp)), GLFW ([imgui_impl_glfw.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_glfw.cpp)), SDL2 ([imgui_impl_sdl2.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_sdl2.cpp)), etc.
|
||||
|
||||
- The 'Renderer' backends are in charge of: creating atlas texture, and rendering imgui draw data.<BR>
|
||||
e.g. DirectX11 ([imgui_impl_dx11.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx11.cpp)), OpenGL/WebGL ([imgui_impl_opengl3.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_opengl3.cpp)), Vulkan ([imgui_impl_vulkan.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.cpp)), etc.
|
||||
|
||||
- For some high-level frameworks, a single backend usually handles both 'Platform' and 'Renderer' parts.<BR>
|
||||
e.g. Allegro 5 ([imgui_impl_allegro5.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_allegro5.cpp)). If you end up creating a custom backend for your engine, you may want to do the same.
|
||||
|
||||
An application usually combines one Platform backend + one Renderer backend + main Dear ImGui sources.
|
||||
For example, the [example_win32_directx11](https://github.com/ocornut/imgui/tree/master/examples/example_win32_directx11) application combines imgui_impl_win32.cpp + imgui_impl_dx11.cpp. There are 20+ examples in the [examples/](https://github.com/ocornut/imgui/blob/master/examples/) folder. See [EXAMPLES.MD](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md) for details.
|
||||
|
||||
**Once Dear ImGui is setup and running, run and refer to `ImGui::ShowDemoWindow()` in imgui_demo.cpp for usage of the end-user API.**
|
||||
|
||||
### List of backends
|
||||
|
||||
In the [backends/](https://github.com/ocornut/imgui/blob/master/backends) folder:
|
||||
|
||||
List of Platforms Backends:
|
||||
|
||||
imgui_impl_android.cpp ; Android native app API
|
||||
imgui_impl_glfw.cpp ; GLFW (Windows, macOS, Linux, etc.) http://www.glfw.org/
|
||||
imgui_impl_osx.mm ; macOS native API (not as feature complete as glfw/sdl backends)
|
||||
imgui_impl_sdl2.cpp ; SDL2 (Windows, macOS, Linux, iOS, Android) https://www.libsdl.org
|
||||
imgui_impl_sdl3.cpp ; SDL3 (Windows, macOS, Linux, iOS, Android) https://www.libsdl.org (*EXPERIMENTAL UNTIL SDL3 IS RELEASED*)
|
||||
imgui_impl_win32.cpp ; Win32 native API (Windows)
|
||||
imgui_impl_glut.cpp ; GLUT/FreeGLUT (this is prehistoric software and absolutely not recommended today!)
|
||||
|
||||
List of Renderer Backends:
|
||||
|
||||
imgui_impl_dx9.cpp ; DirectX9
|
||||
imgui_impl_dx10.cpp ; DirectX10
|
||||
imgui_impl_dx11.cpp ; DirectX11
|
||||
imgui_impl_dx12.cpp ; DirectX12
|
||||
imgui_impl_metal.mm ; Metal (with ObjC)
|
||||
imgui_impl_opengl2.cpp ; OpenGL 2 (legacy, fixed pipeline <- don't use with modern OpenGL context)
|
||||
imgui_impl_opengl3.cpp ; OpenGL 3/4, OpenGL ES 2, OpenGL ES 3 (modern programmable pipeline)
|
||||
imgui_impl_sdlrenderer2.cpp ; SDL_Renderer (optional component of SDL2 available from SDL 2.0.18+)
|
||||
imgui_impl_sdlrenderer3.cpp ; SDL_Renderer (optional component of SDL3 available from SDL 3.0.0+)
|
||||
imgui_impl_vulkan.cpp ; Vulkan
|
||||
imgui_impl_wgpu.cpp ; WebGPU (web and desktop)
|
||||
|
||||
List of high-level Frameworks Backends (combining Platform + Renderer):
|
||||
|
||||
imgui_impl_allegro5.cpp
|
||||
|
||||
Emscripten is also supported!
|
||||
The SDL+GL, GLFW+GL and GLFW+WebGPU examples are all ready to build and run with Emscripten.
|
||||
|
||||
### Backends for third-party frameworks, graphics API or other languages
|
||||
|
||||
See https://github.com/ocornut/imgui/wiki/Bindings for the full list (e.g. Adventure Game Studio, Cinder, Cocos2d-x, Game Maker Studio2, Godot, LÖVE+LUA, Magnum, Monogame, Ogre, openFrameworks, OpenSceneGraph, SFML, Sokol, Unity, Unreal Engine and many others).
|
||||
|
||||
### Recommended Backends
|
||||
|
||||
If you are not sure which backend to use, the recommended platform/frameworks for portable applications:
|
||||
|
||||
|Library |Website |Backend |Note |
|
||||
|--------|--------|--------|-----|
|
||||
| GLFW | https://github.com/glfw/glfw | imgui_impl_glfw.cpp | |
|
||||
| SDL2 | https://www.libsdl.org | imgui_impl_sdl2.cpp | |
|
||||
| Sokol | https://github.com/floooh/sokol | [util/sokol_imgui.h](https://github.com/floooh/sokol/blob/master/util/sokol_imgui.h) | Lower-level than GLFW/SDL |
|
||||
|
||||
|
||||
### Using a custom engine?
|
||||
|
||||
You will likely be tempted to start by rewrite your own backend using your own custom/high-level facilities...<BR>
|
||||
Think twice!
|
||||
|
||||
If you are new to Dear ImGui, first try using the existing backends as-is.
|
||||
You will save lots of time integrating the library.
|
||||
You can LATER decide to rewrite yourself a custom backend if you really need to.
|
||||
In most situations, custom backends have fewer features and more bugs than the standard backends we provide.
|
||||
If you want portability, you can use multiple backends and choose between them either at compile time
|
||||
or at runtime.
|
||||
|
||||
**Example A**: your engine is built over Windows + DirectX11 but you have your own high-level rendering
|
||||
system layered over DirectX11.<BR>
|
||||
Suggestion: try using imgui_impl_win32.cpp + imgui_impl_dx11.cpp first.
|
||||
Once it works, if you really need it, you can replace the imgui_impl_dx11.cpp code with a
|
||||
custom renderer using your own rendering functions, and keep using the standard Win32 code etc.
|
||||
|
||||
**Example B**: your engine runs on Windows, Mac, Linux and uses DirectX11, Metal, and Vulkan respectively.<BR>
|
||||
Suggestion: use multiple generic backends!
|
||||
Once it works, if you really need it, you can replace parts of backends with your own abstractions.
|
||||
|
||||
**Example C**: your engine runs on platforms we can't provide public backends for (e.g. PS4/PS5, Switch),
|
||||
and you have high-level systems everywhere.<BR>
|
||||
Suggestion: try using a non-portable backend first (e.g. win32 + underlying graphics API) to get
|
||||
your desktop builds working first. This will get you running faster and get your acquainted with
|
||||
how Dear ImGui works and is setup. You can then rewrite a custom backend using your own engine API...
|
||||
|
||||
Generally:
|
||||
It is unlikely you will add value to your project by creating your own backend.
|
||||
|
||||
Also:
|
||||
The [multi-viewports feature](https://github.com/ocornut/imgui/wiki/Multi-Viewports) of the 'docking' branch allows
|
||||
Dear ImGui windows to be seamlessly detached from the main application window. This is achieved using an
|
||||
extra layer to the Platform and Renderer backends, which allows Dear ImGui to communicate platform-specific
|
||||
requests such as: "create an additional OS window", "create a render context", "get the OS position of this
|
||||
window" etc. See 'ImGuiPlatformIO' for details.
|
||||
Supporting the multi-viewports feature correctly using 100% of your own abstractions is more difficult
|
||||
than supporting single-viewport.
|
||||
If you decide to use unmodified imgui_impl_XXXX.cpp files, you can automatically benefit from
|
||||
improvements and fixes related to viewports and platform windows without extra work on your side.
|
7252
Source/ThirdParty/ImGuiLibrary/Docs/CHANGELOG.txt
vendored
Normal file
7252
Source/ThirdParty/ImGuiLibrary/Docs/CHANGELOG.txt
vendored
Normal file
File diff suppressed because it is too large
Load Diff
81
Source/ThirdParty/ImGuiLibrary/Docs/CONTRIBUTING.md
vendored
Normal file
81
Source/ThirdParty/ImGuiLibrary/Docs/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
# Contributing Guidelines
|
||||
|
||||
## Index
|
||||
|
||||
- [Getting Started & General Advice](#getting-started--general-advice)
|
||||
- [Issues vs Discussions](#issues-vs-discussions)
|
||||
- [How to open an Issue](#how-to-open-an-issue)
|
||||
- [How to open a Pull Request](#how-to-open-a-pull-request)
|
||||
- [Copyright / Contributor License Agreement](#copyright--contributor-license-agreement)
|
||||
|
||||
## Getting Started & General Advice
|
||||
|
||||
- Article: [How To Ask Good Questions](https://bit.ly/3nwRnx1).
|
||||
- Please browse the [Wiki](https://github.com/ocornut/imgui/wiki) to find code snippets, links and other resources (e.g. [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started), [Useful extensions](https://github.com/ocornut/imgui/wiki/Useful-Extensions)).
|
||||
- Please read [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) if your question relates to setting up Dear ImGui.
|
||||
- Please read [docs/FAQ.md](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md).
|
||||
- Please read [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md) if your question relates to fonts or text.
|
||||
- Please run `ImGui::ShowDemoWindow()` to explore the demo and its sources.
|
||||
- Please use the search function of your IDE to search for symbols and comments related to your situation.
|
||||
- Please use the search function of GitHub to look for similar topics (always include 'Closed' issues/pr in your search).
|
||||
- You may [browse issues by Labels](https://github.com/ocornut/imgui/labels).
|
||||
- Please use a web search engine to look for similar issues.
|
||||
- If you get a crash or assert, use a debugger to locate the line triggering it and read the comments around.
|
||||
- Please don't be a [Help Vampire](https://slash7.com/2006/12/22/vampires/).
|
||||
|
||||
## 'Issues' vs 'Discussions'
|
||||
|
||||
We are happy to use 'Issues' for many type of open-ended questions. We are encouraging 'Issues' becoming an enormous, centralized and cross-referenced database of Dear ImGui contents.
|
||||
|
||||
Only if you:
|
||||
- Cannot BUILD or LINK examples.
|
||||
- Cannot BUILD, or LINK, or RUN Dear ImGui in your application or custom engine.
|
||||
- Cannot LOAD a font.
|
||||
|
||||
Then please [use the Discussions forums](https://github.com/ocornut/imgui/discussions) instead of opening an issue.
|
||||
|
||||
If Dear ImGui is successfully showing in your app and you have used Dear ImGui before, you can open an Issue. Any form of discussions is welcome as a new issue.
|
||||
|
||||
## How to open an issue
|
||||
|
||||
You may use the Issue Tracker to submit bug reports, feature requests or suggestions. You may ask for help or advice as well. But **PLEASE CAREFULLY READ THIS WALL OF TEXT. ISSUES IGNORING THOSE GUIDELINES MAY BE CLOSED. USERS IGNORING THOSE GUIDELINES MIGHT BE BLOCKED.**
|
||||
|
||||
Please do your best to clarify your request. The amount of incomplete or ambiguous requests due to people not following those guidelines is often overwhelming. Issues created without the requested information may be closed prematurely. Exceptionally entitled, impolite, or lazy requests may lead to bans.
|
||||
|
||||
**PLEASE UNDERSTAND THAT OPEN-SOURCE SOFTWARE LIVES OR DIES BY THE AMOUNT OF ENERGY MAINTAINERS CAN SPARE. WE HAVE LOTS OF STUFF TO DO. THIS IS AN ATTENTION ECONOMY AND MANY LAZY OR MINOR ISSUES ARE HOGGING OUR ATTENTION AND DRAINING ENERGY, TAKING US AWAY FROM MORE IMPORTANT WORK.**
|
||||
|
||||
Steps:
|
||||
|
||||
- Article: [How To Ask Good Questions](https://bit.ly/3nwRnx1).
|
||||
- **PLEASE DO FILL THE REQUESTED NEW ISSUE TEMPLATE.** Including Dear ImGui version number, branch name, platform/renderer back-ends (imgui_impl_XXX files), operating system.
|
||||
- **Try to be explicit with your GOALS, your EXPECTATIONS and what you have tried**. Be mindful of [The XY Problem](http://xyproblem.info/). What you have in mind or in your code is not obvious to other people. People frequently discuss problems and suggest incorrect solutions without first clarifying their goals. When requesting a new feature, please describe the usage context (how you intend to use it, why you need it, etc.). If you tried something and it failed, show us what you tried.
|
||||
- **Please INCLUDE CODE. Provide a Minimal, Complete, and Verifiable Example ([MCVE](https://stackoverflow.com/help/mcve)) to demonstrate your problem**. An ideal submission includes a small piece of code that anyone can paste into one of the examples applications (examples/../main.cpp) or demo (imgui_demo.cpp) to understand and reproduce it. **Narrowing your problem to its shortest and purest form is the easiest way to understand it, explain it and fix it**. Please test your shortened code to ensure it exhibits the problem. **Often while creating the MCVE you will solve the problem!** Many questions that are missing a standalone verifiable example are missing the actual cause of their issue in the description, which ends up wasting everyone's time.
|
||||
- **Attach screenshots (or GIF/video) to clarify the context**. They often convey useful information that is omitted by the description. You can drag pictures/files in the message edit box. Avoid using 3rd party image hosting services, prefer the long-term longevity of GitHub attachments (you can drag pictures into your post). On Windows, you can use [ScreenToGif](https://www.screentogif.com/) to easily capture .gif files.
|
||||
- **If you are discussing an assert or a crash, please provide a debugger callstack**. Never state "it crashes" without additional information. If you don't know how to use a debugger and retrieve a callstack, learning about it will be useful.
|
||||
- **Please make sure that your project has asserts enabled.** Calls to IM_ASSERT() are scattered in the code to help catch common issues. When an assert is triggered read the comments around it. By default IM_ASSERT() calls the standard assert() function. To verify that your asserts are enabled, add the line `IM_ASSERT(false);` in your main() function. Your application should display an error message and abort. If your application doesn't report an error, your asserts are disabled.
|
||||
- Please state if you have made substantial modifications to your copy of Dear ImGui or the back-end.
|
||||
- If you are not calling Dear ImGui directly from C++, please provide information about your Language and the wrapper/binding you are using.
|
||||
- Be mindful that messages are being sent to the mailbox of "Watching" users. Try to proofread your messages before sending them. Edits are not seen by those users unless they browse the site.
|
||||
|
||||
**Some unfortunate words of warning**
|
||||
- If you are involved in cheating schemes (e.g. DLL injection) for competitive online multiplayer games, please don't post here. We won't answer and you will be blocked. It doesn't matter if your question relates to said project. We've had too many of you and need to protect our time and sanity.
|
||||
- Due to frequent abuse of this service from the aforementioned users, if your GitHub account is anonymous and was created five minutes ago please understand that your post will receive more scrutiny and incomplete questions will be harshly dismissed.
|
||||
|
||||
If you have been using Dear ImGui for a while or have been using C/C++ for several years or have demonstrated good behavior here, it is ok to not fulfill every item to the letter. Those are guidelines and experienced users or members of the community will know which information is useful in a given context.
|
||||
|
||||
## How to open a Pull Request
|
||||
|
||||
- **Please understand that by submitting a PR you are also submitting a request for the maintainer to review your code and then take over its maintenance.** PR should be crafted both in the interest of the end-users and also to ease the maintainer into understanding and accepting it.
|
||||
- Many PRs are useful to demonstrate a need and a possible solution but aren't adequate for merging (causing other issues, not seeing other aspects of the big picture, etc.). In doubt, don't hesitate to push a PR because that is always the first step toward pointing toward a problem, and finding the mergeable solution! Even if a PR stays unmerged for a long time, its presence can be useful for other users and helps toward finding a general solution.
|
||||
- **When adding a feature,** please describe the usage context (how you intend to use it, why you need it, etc.). Be mindful of [The XY Problem](http://xyproblem.info/).
|
||||
- **When fixing a warning or compilation problem,** please post the compiler log and specify the compiler version and platform you are using.
|
||||
- **Attach screenshots (or GIF/video) to clarify the context and demonstrate the feature at a glance.** You can drag pictures/files in the message edit box. Prefer the long-term longevity of GitHub attachments over 3rd party hosting (you can drag pictures into your post).
|
||||
- **Make sure your code follows the coding style already used in the codebase:** 4 spaces indentations (no tabs), `local_variable`, `FunctionName()`, `MemberName`, `// Text Comment`, `//CodeComment();`, C-style casts, etc.. We don't use modern C++ idioms and tend to use only a minimum of C++11 features. The applications under examples/ are generally less consistent because they sometimes try to mimic the coding style often adopted by a certain ecosystem (e.g. DirectX-related code tend to use the style of their sample).
|
||||
- **Make sure you create a branch dedicated to the pull request**. In Git, 1 PR is associated to 1 branch. If you keep pushing to the same branch after you submitted the PR, your new commits will appear in the PR (we can still cherry-pick individual commits).
|
||||
|
||||
## Copyright / Contributor License Agreement
|
||||
|
||||
Any code you submit will become part of the repository and be distributed under the [Dear ImGui license](https://github.com/ocornut/imgui/blob/master/LICENSE.txt). By submitting code to the project you agree that the code is your work and that you can give it to the project.
|
||||
|
||||
You also agree by submitting your code that you grant all transferrable rights to the code to the project maintainer, including for example re-licensing the code, modifying the code, and distributing it in source or binary forms. Specifically, this includes a requirement that you assign copyright to the project maintainer. For this reason, do not modify any copyright statements in files in any PRs.
|
||||
|
212
Source/ThirdParty/ImGuiLibrary/Docs/EXAMPLES.md
vendored
Normal file
212
Source/ThirdParty/ImGuiLibrary/Docs/EXAMPLES.md
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
_(You may browse this at https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md or view this file with any Markdown viewer)_
|
||||
|
||||
## Dear ImGui: Examples
|
||||
|
||||
**The [examples/](https://github.com/ocornut/imgui/blob/master/examples) folder example applications (standalone, ready-to-build) for variety of
|
||||
platforms and graphics APIs.** They all use standard backends from the [backends/](https://github.com/ocornut/imgui/blob/master/backends) folder (see [BACKENDS.md](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md)).
|
||||
|
||||
The purpose of Examples is to showcase integration with backends, let you try Dear ImGui, and guide you toward
|
||||
integrating Dear ImGui in your own application/game/engine.
|
||||
**Once Dear ImGui is setup and running, run and refer to `ImGui::ShowDemoWindow()` in imgui_demo.cpp for usage of the end-user API.**
|
||||
|
||||
You can find Windows binaries for some of those example applications at:
|
||||
https://www.dearimgui.com/binaries
|
||||
|
||||
|
||||
### Getting Started
|
||||
|
||||
Integration in a typical existing application, should take <20 lines when using standard backends.
|
||||
|
||||
```cpp
|
||||
At initialization:
|
||||
call ImGui::CreateContext()
|
||||
call ImGui_ImplXXXX_Init() for each backend.
|
||||
|
||||
At the beginning of your frame:
|
||||
call ImGui_ImplXXXX_NewFrame() for each backend.
|
||||
call ImGui::NewFrame()
|
||||
|
||||
At the end of your frame:
|
||||
call ImGui::Render()
|
||||
call ImGui_ImplXXXX_RenderDrawData() for your Renderer backend.
|
||||
|
||||
At shutdown:
|
||||
call ImGui_ImplXXXX_Shutdown() for each backend.
|
||||
call ImGui::DestroyContext()
|
||||
```
|
||||
|
||||
Main resource:
|
||||
- Read **[Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) wiki guide** for detailed examples of how to integrate Dear ImGui in an existing application.
|
||||
|
||||
Additional resources:
|
||||
- Read FAQ at https://www.dearimgui.com/faq
|
||||
- Read 'PROGRAMMER GUIDE' section in imgui.cpp.
|
||||
- Read the comments and instruction at the top of each file.
|
||||
|
||||
If you are using any of the backends provided here, you can add the backends/imgui_impl_xxxx(.cpp,.h)
|
||||
files to your project and use as-in. Each imgui_impl_xxxx.cpp file comes with its own individual
|
||||
Changelog, so if you want to update them later it will be easier to catch up with what changed.
|
||||
|
||||
|
||||
### Examples Applications
|
||||
|
||||
[example_allegro5/](https://github.com/ocornut/imgui/blob/master/examples/example_allegro5/) <BR>
|
||||
Allegro 5 example. <BR>
|
||||
= main.cpp + imgui_impl_allegro5.cpp
|
||||
|
||||
[example_android_opengl3/](https://github.com/ocornut/imgui/blob/master/examples/example_android_opengl3/) <BR>
|
||||
Android + OpenGL3 (ES) example. <BR>
|
||||
= main.cpp + imgui_impl_android.cpp + imgui_impl_opengl3.cpp
|
||||
|
||||
[example_apple_metal/](https://github.com/ocornut/imgui/blob/master/examples/example_metal/) <BR>
|
||||
OSX & iOS + Metal example. <BR>
|
||||
= main.m + imgui_impl_osx.mm + imgui_impl_metal.mm <BR>
|
||||
It is based on the "cross-platform" game template provided with Xcode as of Xcode 9.
|
||||
(NB: imgui_impl_osx.mm is currently not as feature complete as other platforms backends.
|
||||
You may prefer to use the GLFW Or SDL backends, which will also support Windows and Linux.)
|
||||
|
||||
[example_apple_opengl2/](https://github.com/ocornut/imgui/blob/master/examples/example_apple_opengl2/) <BR>
|
||||
OSX + OpenGL2 example. <BR>
|
||||
= main.mm + imgui_impl_osx.mm + imgui_impl_opengl2.cpp <BR>
|
||||
(NB: imgui_impl_osx.mm is currently not as feature complete as other platforms backends.
|
||||
You may prefer to use the GLFW Or SDL backends, which will also support Windows and Linux.)
|
||||
|
||||
[example_glfw_wgpu/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_wgpu/) <BR>
|
||||
GLFW + WebGPU example. Supports Emscripten (web) or Dawn (desktop) <BR>
|
||||
= main.cpp + imgui_impl_glfw.cpp + imgui_impl_wgpu.cpp
|
||||
Note that the 'example_glfw_opengl3' and 'example_sdl2_opengl3' examples also supports Emscripten!
|
||||
|
||||
[example_glfw_metal/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_metal/) <BR>
|
||||
GLFW (Mac) + Metal example. <BR>
|
||||
= main.mm + imgui_impl_glfw.cpp + imgui_impl_metal.mm
|
||||
|
||||
[example_glfw_opengl2/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_opengl2/) <BR>
|
||||
GLFW + OpenGL2 example (legacy, fixed pipeline). <BR>
|
||||
= main.cpp + imgui_impl_glfw.cpp + imgui_impl_opengl2.cpp <BR>
|
||||
**DO NOT USE THIS IF YOUR CODE/ENGINE IS USING MODERN GL or WEBGL (SHADERS, VBO, VAO, etc.)** <BR>
|
||||
This code is mostly provided as a reference to learn about Dear ImGui integration, because it is shorter.
|
||||
If your code is using GL3+ context or any semi modern GL calls, using this renderer is likely to
|
||||
make things more complicated, will require your code to reset many GL attributes to their initial
|
||||
state, and might confuse your GPU driver. One star, not recommended.
|
||||
|
||||
[example_glfw_opengl3/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_opengl3/) <BR>
|
||||
GLFW (Win32, Mac, Linux) + OpenGL3+/ES2/ES3 example (modern, programmable pipeline). <BR>
|
||||
= main.cpp + imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp <BR>
|
||||
This uses more modern GL calls and custom shaders.<BR>
|
||||
This support building with Emscripten and targeting WebGL.<BR>
|
||||
Prefer using that if you are using modern GL or WebGL in your application.
|
||||
|
||||
[example_glfw_vulkan/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_vulkan/) <BR>
|
||||
GLFW (Win32, Mac, Linux) + Vulkan example. <BR>
|
||||
= main.cpp + imgui_impl_glfw.cpp + imgui_impl_vulkan.cpp <BR>
|
||||
This is quite long and tedious, because: Vulkan.
|
||||
For this example, the main.cpp file exceptionally use helpers function from imgui_impl_vulkan.h/cpp.
|
||||
|
||||
[example_glut_opengl2/](https://github.com/ocornut/imgui/blob/master/examples/example_glut_opengl2/) <BR>
|
||||
GLUT (e.g., FreeGLUT on Linux/Windows, GLUT framework on OSX) + OpenGL2 example. <BR>
|
||||
= main.cpp + imgui_impl_glut.cpp + imgui_impl_opengl2.cpp <BR>
|
||||
Note that GLUT/FreeGLUT is largely obsolete software, prefer using GLFW or SDL.
|
||||
|
||||
[example_null/](https://github.com/ocornut/imgui/blob/master/examples/example_null/) <BR>
|
||||
Null example, compile and link imgui, create context, run headless with no inputs and no graphics output. <BR>
|
||||
= main.cpp <BR>
|
||||
This is used to quickly test compilation of core imgui files in as many setups as possible.
|
||||
Because this application doesn't create a window nor a graphic context, there's no graphics output.
|
||||
|
||||
[example_sdl2_directx11/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl2_directx11/) <BR>
|
||||
SDL2 + DirectX11 example, Windows only. <BR>
|
||||
= main.cpp + imgui_impl_sdl2.cpp + imgui_impl_dx11.cpp <BR>
|
||||
This to demonstrate usage of DirectX with SDL2.
|
||||
|
||||
[example_sdl2_metal/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl2_metal/) <BR>
|
||||
SDL2 + Metal example, Mac only. <BR>
|
||||
= main.mm + imgui_impl_sdl2.cpp + imgui_impl_metal.mm
|
||||
|
||||
[example_sdl2_opengl2/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl2_opengl2/) <BR>
|
||||
SDL2 (Win32, Mac, Linux etc.) + OpenGL example (legacy, fixed pipeline). <BR>
|
||||
= main.cpp + imgui_impl_sdl2.cpp + imgui_impl_opengl2.cpp <BR>
|
||||
**DO NOT USE OPENGL2 CODE IF YOUR CODE/ENGINE IS USING GL OR WEBGL (SHADERS, VBO, VAO, etc.)** <BR>
|
||||
This code is mostly provided as a reference to learn about Dear ImGui integration, because it is shorter.
|
||||
If your code is using GL3+ context or any semi modern GL calls, using this renderer is likely to
|
||||
make things more complicated, will require your code to reset many GL attributes to their initial
|
||||
state, and might confuse your GPU driver. One star, not recommended.
|
||||
|
||||
[example_sdl2_opengl3/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl2_opengl3/) <BR>
|
||||
SDL2 (Win32, Mac, Linux, etc.) + OpenGL3+/ES2/ES3 example. <BR>
|
||||
= main.cpp + imgui_impl_sdl2.cpp + imgui_impl_opengl3.cpp <BR>
|
||||
This uses more modern GL calls and custom shaders. <BR>
|
||||
This support building with Emscripten and targeting WebGL.<BR>
|
||||
Prefer using that if you are using modern GL or WebGL in your application.
|
||||
|
||||
[example_sdl2_sdlrenderer2/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl2_sdlrenderer2/) <BR>
|
||||
SDL2 (Win32, Mac, Linux, etc.) + SDL_Renderer for SDL2 (most graphics backends are supported underneath) <BR>
|
||||
= main.cpp + imgui_impl_sdl2.cpp + imgui_impl_sdlrenderer.cpp <BR>
|
||||
This requires SDL 2.0.18+ (released November 2021) <BR>
|
||||
|
||||
[example_sdl2_vulkan/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl2_vulkan/) <BR>
|
||||
SDL2 (Win32, Mac, Linux, etc.) + Vulkan example. <BR>
|
||||
= main.cpp + imgui_impl_sdl2.cpp + imgui_impl_vulkan.cpp <BR>
|
||||
This is quite long and tedious, because: Vulkan. <BR>
|
||||
For this example, the main.cpp file exceptionally use helpers function from imgui_impl_vulkan.h/cpp.
|
||||
|
||||
[example_win32_directx9/](https://github.com/ocornut/imgui/blob/master/examples/example_win32_directx9/) <BR>
|
||||
DirectX9 example, Windows only. <BR>
|
||||
= main.cpp + imgui_impl_win32.cpp + imgui_impl_dx9.cpp
|
||||
|
||||
[example_win32_directx10/](https://github.com/ocornut/imgui/blob/master/examples/example_win32_directx10/) <BR>
|
||||
DirectX10 example, Windows only. <BR>
|
||||
= main.cpp + imgui_impl_win32.cpp + imgui_impl_dx10.cpp
|
||||
|
||||
[example_win32_directx11/](https://github.com/ocornut/imgui/blob/master/examples/example_win32_directx11/) <BR>
|
||||
DirectX11 example, Windows only. <BR>
|
||||
= main.cpp + imgui_impl_win32.cpp + imgui_impl_dx11.cpp
|
||||
|
||||
[example_win32_directx12/](https://github.com/ocornut/imgui/blob/master/examples/example_win32_directx12/) <BR>
|
||||
DirectX12 example, Windows only. <BR>
|
||||
= main.cpp + imgui_impl_win32.cpp + imgui_impl_dx12.cpp <BR>
|
||||
This is quite long and tedious, because: DirectX12.
|
||||
|
||||
[example_win32_opengl3/](https://github.com/ocornut/imgui/blob/master/examples/example_win32_opengl3/) <BR>
|
||||
Raw Windows + OpenGL3 + example (modern, programmable pipeline) <BR>
|
||||
= main.cpp + imgui_impl_win32.cpp + imgui_impl_opengl3.cpp <BR>
|
||||
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
**Building**
|
||||
|
||||
Unfortunately nowadays it is still tedious to create and maintain portable build files using external
|
||||
libraries (the kind we're using here to create a window and render 3D triangles) without relying on
|
||||
third party software and build systems. For most examples here we choose to provide:
|
||||
- Makefiles for Linux/OSX
|
||||
- Batch files for Visual Studio 2008+
|
||||
- A .sln project file for Visual Studio 2012+
|
||||
- Xcode project files for the Apple examples
|
||||
Please let us know if they don't work with your setup!
|
||||
You can probably just import the imgui_impl_xxx.cpp/.h files into your own codebase or compile those
|
||||
directly with a command-line compiler.
|
||||
|
||||
If you are interested in using Cmake to build and links examples, see:
|
||||
https://github.com/ocornut/imgui/pull/1713 and https://github.com/ocornut/imgui/pull/3027
|
||||
|
||||
**About mouse cursor latency**
|
||||
|
||||
Dear ImGui has no particular extra lag for most behaviors,
|
||||
e.g. the last value passed to 'io.AddMousePosEvent()' before NewFrame() will result in windows being moved
|
||||
to the right spot at the time of EndFrame()/Render(). At 60 FPS your experience should be pleasant.
|
||||
|
||||
However, consider that OS mouse cursors are typically drawn through a very specific hardware accelerated
|
||||
path and will feel smoother than the majority of contents rendered via regular graphics API (including,
|
||||
but not limited to Dear ImGui windows). Because UI rendering and interaction happens on the same plane
|
||||
as the mouse, that disconnect may be jarring to particularly sensitive users.
|
||||
You may experiment with enabling the io.MouseDrawCursor flag to request Dear ImGui to draw a mouse cursor
|
||||
using the regular graphics API, to help you visualize the difference between a "hardware" cursor and a
|
||||
regularly rendered software cursor.
|
||||
However, rendering a mouse cursor at 60 FPS will feel sluggish so you likely won't want to enable that at
|
||||
all times. It might be beneficial for the user experience to switch to a software rendered cursor _only_
|
||||
when an interactive drag is in progress.
|
||||
|
||||
Note that some setup or GPU drivers are likely to be causing extra display lag depending on their settings.
|
||||
If you feel that dragging windows feels laggy and you are not sure what the cause is: try to build a simple
|
||||
drawing a flat 2D shape directly under the mouse cursor!
|
||||
|
709
Source/ThirdParty/ImGuiLibrary/Docs/FAQ.md
vendored
Normal file
709
Source/ThirdParty/ImGuiLibrary/Docs/FAQ.md
vendored
Normal file
@ -0,0 +1,709 @@
|
||||
# FAQ (Frequently Asked Questions)
|
||||
|
||||
You may link to this document using short form:
|
||||
https://www.dearimgui.com/faq
|
||||
or its real address:
|
||||
https://github.com/ocornut/imgui/blob/master/docs/FAQ.md
|
||||
or view this file with any Markdown viewer.
|
||||
|
||||
|
||||
## Index
|
||||
|
||||
| **Q&A: Basics** |
|
||||
:---------------------------------------------------------- |
|
||||
| [Where is the documentation?](#q-where-is-the-documentation) |
|
||||
| [What is this library called?](#q-what-is-this-library-called) |
|
||||
| [Which version should I get?](#q-which-version-should-i-get) |
|
||||
| **Q&A: Integration** |
|
||||
| **[How to get started?](#q-how-to-get-started)** |
|
||||
| **[How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?](#q-how-can-i-tell-whether-to-dispatch-mousekeyboard-to-dear-imgui-or-my-application)** |
|
||||
| [How can I enable keyboard or gamepad controls?](#q-how-can-i-enable-keyboard-or-gamepad-controls) |
|
||||
| [How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)](#q-how-can-i-use-this-on-a-machine-without-mouse-keyboard-or-screen-input-share-remote-display) |
|
||||
| [I integrated Dear ImGui in my engine and little squares are showing instead of text...](#q-i-integrated-dear-imgui-in-my-engine-and-little-squares-are-showing-instead-of-text) |
|
||||
| [I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-clipping-or-disappearing-when-i-move-windows-around) |
|
||||
| [I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-displaying-outside-their-expected-windows-boundaries) |
|
||||
| **Q&A: Usage** |
|
||||
| **[About the ID Stack system..<br>Why is my widget not reacting when I click on it?<br>Why is the wrong widget reacting when I click on one?<br>How can I have widgets with an empty label?<br>How can I have multiple widgets with the same label?<br>How can I have multiple windows with the same label?](#q-about-the-id-stack-system)** |
|
||||
| [How can I display an image? What is ImTextureID, how does it work?](#q-how-can-i-display-an-image-what-is-imtextureid-how-does-it-work)|
|
||||
| [How can I use maths operators with ImVec2?](#q-how-can-i-use-maths-operators-with-imvec2) |
|
||||
| [How can I use my own maths types instead of ImVec2/ImVec4?](#q-how-can-i-use-my-own-maths-types-instead-of-imvec2imvec4) |
|
||||
| [How can I interact with standard C++ types (such as std::string and std::vector)?](#q-how-can-i-interact-with-standard-c-types-such-as-stdstring-and-stdvector) |
|
||||
| [How can I display custom shapes? (using low-level ImDrawList API)](#q-how-can-i-display-custom-shapes-using-low-level-imdrawlist-api) |
|
||||
| **Q&A: Fonts, Text** |
|
||||
| [How should I handle DPI in my application?](#q-how-should-i-handle-dpi-in-my-application) |
|
||||
| [How can I load a different font than the default?](#q-how-can-i-load-a-different-font-than-the-default) |
|
||||
| [How can I easily use icons in my application?](#q-how-can-i-easily-use-icons-in-my-application) |
|
||||
| [How can I load multiple fonts?](#q-how-can-i-load-multiple-fonts) |
|
||||
| [How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?](#q-how-can-i-display-and-input-non-latin-characters-such-as-chinese-japanese-korean-cyrillic) |
|
||||
| **Q&A: Concerns** |
|
||||
| [Who uses Dear ImGui?](#q-who-uses-dear-imgui) |
|
||||
| [Can you create elaborate/serious tools with Dear ImGui?](#q-can-you-create-elaborateserious-tools-with-dear-imgui) |
|
||||
| [Can you reskin the look of Dear ImGui?](#q-can-you-reskin-the-look-of-dear-imgui) |
|
||||
| [Why using C++ (as opposed to C)?](#q-why-using-c-as-opposed-to-c) |
|
||||
| **Q&A: Community** |
|
||||
| [How can I help?](#q-how-can-i-help) |
|
||||
|
||||
|
||||
# Q&A: Basics
|
||||
|
||||
### Q: Where is the documentation?
|
||||
|
||||
**This library is poorly documented at the moment and expects the user to be acquainted with C/C++.**
|
||||
- The [Wiki](https://github.com/ocornut/imgui/wiki) is a hub to many resources and links.
|
||||
- Handy [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide to integrate Dear ImGui in an existing application.
|
||||
- 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the [examples/](https://github.com/ocornut/imgui/blob/master/examples/) folder to explain how to integrate Dear ImGui with your own engine/application. You can run those applications and explore them.
|
||||
- See demo code in [imgui_demo.cpp](https://github.com/ocornut/imgui/blob/master/imgui_demo.cpp) and particularly the `ImGui::ShowDemoWindow()` function. The demo covers most features of Dear ImGui, so you can read the code and see its output.
|
||||
- See documentation: [Backends](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md), [Examples](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md), [Fonts](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md).
|
||||
- See documentation and comments at the top of [imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp) + general API comments in [imgui.h](https://github.com/ocornut/imgui/blob/master/imgui.h).
|
||||
- The [Glossary](https://github.com/ocornut/imgui/wiki/Glossary) page may be useful.
|
||||
- The [Issues](https://github.com/ocornut/imgui/issues) and [Discussions](https://github.com/ocornut/imgui/discussions) sections can be searched for past questions and issues.
|
||||
- Your programming IDE is your friend, find the type or function declaration to find comments associated with it.
|
||||
- The `ImGui::ShowMetricsWindow()` function exposes lots of internal information and tools. Although it is primarily designed as a debugging tool, having access to that information tends to help understands concepts.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q. What is this library called?
|
||||
|
||||
**This library is called Dear ImGui**. Please refer to it as Dear ImGui (not ImGui, not IMGUI).
|
||||
|
||||
(The library misleadingly started its life in 2014 as "ImGui" due to the fact that I didn't give it a proper name when I released 1.0, and had no particular expectation that it would take off. However, the term IMGUI (immediate-mode graphical user interface) was coined before and is being used in variety of other situations e.g. Unity uses it own implementation of the IMGUI paradigm. To reduce the ambiguity without affecting existing code bases, I have decided in December 2015 a fully qualified name "Dear ImGui" for this library.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: Which version should I get?
|
||||
I occasionally tag [Releases](https://github.com/ocornut/imgui/releases) but it is generally safe and recommended to sync to master/latest. The library is fairly stable and regressions tend to be fixed fast when reported.
|
||||
|
||||
You may use the ['docking'](https://github.com/ocornut/imgui/tree/docking) branch which includes:
|
||||
- [Docking features](https://github.com/ocornut/imgui/wiki/Docking)
|
||||
- [Multi-viewport features](https://github.com/ocornut/imgui/wiki/Multi-Viewports)
|
||||
|
||||
Many projects are using this branch and it is kept in sync with master regularly.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
----
|
||||
|
||||
# Q&A: Integration
|
||||
|
||||
### Q: How to get started?
|
||||
|
||||
Read [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started). <BR>
|
||||
Read [EXAMPLES.md](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md). <BR>
|
||||
Read [BACKENDS.md](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md). <BR>
|
||||
Read `PROGRAMMER GUIDE` section of [imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp). <BR>
|
||||
The [Wiki](https://github.com/ocornut/imgui/wiki) is a hub to many resources and links.
|
||||
|
||||
For first-time users having issues compiling/linking/running or issues loading fonts, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions).
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
|
||||
|
||||
You can read the `io.WantCaptureMouse`, `io.WantCaptureKeyboard` and `io.WantTextInput` flags from the ImGuiIO structure.
|
||||
- When `io.WantCaptureMouse` is set, you need to discard/hide the mouse inputs from your underlying application.
|
||||
- When `io.WantCaptureKeyboard` is set, you need to discard/hide the keyboard inputs from your underlying application.
|
||||
- When `io.WantTextInput` is set, you can notify your OS/engine to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS).
|
||||
|
||||
Important: you should always pass your mouse/keyboard inputs to Dear ImGui, regardless of the value `io.WantCaptureMouse`/`io.WantCaptureKeyboard`. This is because e.g. we need to detect that you clicked in the void to unfocus its own windows, and other reasons.
|
||||
|
||||
```cpp
|
||||
void MyLowLevelMouseButtonHandler(int button, bool down)
|
||||
{
|
||||
// (1) ALWAYS forward mouse data to ImGui! This is automatic with default backends. With your own backend:
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddMouseButtonEvent(button, down);
|
||||
|
||||
// (2) ONLY forward mouse data to your underlying app/game.
|
||||
if (!io.WantCaptureMouse)
|
||||
my_game->HandleMouseData(...);
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The `io.WantCaptureMouse` is more correct that any manual attempt to "check if the mouse is hovering a window" (don't do that!). It handles mouse dragging correctly (both dragging that started over your application or over a Dear ImGui window) and handle e.g. popup and modal windows blocking inputs.
|
||||
|
||||
**Note:** Text input widget releases focus on the "KeyDown" event of the Return key, so the subsequent "KeyUp" event that your application receive will typically have `io.WantCaptureKeyboard == false`. Depending on your application logic it may or not be inconvenient to receive that KeyUp event. You might want to track which key-downs were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: How can I enable keyboard or gamepad controls?
|
||||
- The gamepad/keyboard navigation is fairly functional and keeps being improved. The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. Gamepad support is particularly useful to use Dear ImGui on a game console (e.g. PS4, Switch, XB1) without a mouse connected!
|
||||
- Keyboard: set `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard` to enable.
|
||||
- Gamepad: set `io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad` to enable (with a supporting backend).
|
||||
- See [Control Sheets for Gamepads](https://www.dearimgui.com/controls_sheets) (reference PNG/PSD for PS4, XB1, Switch gamepads).
|
||||
- See `USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS` section of [imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp) for more details.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)
|
||||
- You can share your computer mouse seamlessly with your console/tablet/phone using solutions such as [Synergy](https://symless.com/synergy)
|
||||
This is the preferred solution for developer productivity.
|
||||
In particular, the [micro-synergy-client repository](https://github.com/symless/micro-synergy-client) has simple
|
||||
and portable source code (uSynergy.c/.h) for a small embeddable client that you can use on any platform to connect
|
||||
to your host computer, based on the Synergy 1.x protocol. Make sure you download the Synergy 1 server on your computer.
|
||||
Console SDK also sometimes provide equivalent tooling or wrapper for Synergy-like protocols.
|
||||
- Game console users: consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
|
||||
- You may also use a third party solution such as [netImgui](https://github.com/sammyfreg/netImgui), [Remote ImGui](https://github.com/JordiRos/remoteimgui) or [imgui-ws](https://github.com/ggerganov/imgui-ws) which sends the vertices to render over the local network, allowing you to use Dear ImGui even on a screen-less machine. See [Wiki](https://github.com/ocornut/imgui/wiki) index for most details.
|
||||
- For touch inputs, you can increase the hit box of widgets (via the `style.TouchPadding` setting) to accommodate for the lack of precision of touch inputs, but it is recommended you use a mouse or gamepad to allow optimizing for screen real-estate and precision.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
|
||||
Your renderer backend is not using the font texture correctly or it hasn't been uploaded to the GPU.
|
||||
- If this happens using the standard backends: A) have you modified the font atlas after `ImGui_ImplXXX_NewFrame()`? B) maybe the texture failed to upload, which **can if your texture atlas is too big**. Also see [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md).
|
||||
- If this happens with a custom backend: make sure you have uploaded the font texture to the GPU, that all shaders are rendering states are setup properly (e.g. texture is bound). Compare your code to existing backends and use a graphics debugger such as [RenderDoc](https://renderdoc.org) to debug your rendering states.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
|
||||
### Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
|
||||
You are probably mishandling the clipping rectangles in your render function.
|
||||
Each draw command needs the triangle rendered using the clipping rectangle provided in the ImDrawCmd structure (`ImDrawCmd->CllipRect`).
|
||||
Rectangles provided by Dear ImGui are defined as
|
||||
`(x1=left,y1=top,x2=right,y2=bottom)`
|
||||
and **NOT** as
|
||||
`(x1,y1,width,height)`.
|
||||
Refer to rendering backends in the [backends/](https://github.com/ocornut/imgui/tree/master/backends) folder for references of how to handle the `ClipRect` field.
|
||||
For example, the [DirectX11 backend](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx11.cpp) does this:
|
||||
```cpp
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
|
||||
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
|
||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||
continue;
|
||||
|
||||
// Apply scissor/clipping rectangle
|
||||
const D3D11_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
|
||||
ctx->RSSetScissorRects(1, &r);
|
||||
```
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
# Q&A: Usage
|
||||
|
||||
### Q: About the ID Stack system...
|
||||
### Q: Why is my widget not reacting when I click on it?
|
||||
### Q: Why is the wrong widget reacting when I click on one?
|
||||
### Q: How can I have widgets with an empty label?
|
||||
### Q: How can I have multiple widgets with the same label?
|
||||
### Q: How can I have multiple windows with the same label?
|
||||
|
||||
**USING THE SAME LABEL+ID IS THE MOST COMMON USER MISTAKE!**
|
||||
<br>**USING AN EMPTY LABEL IS THE SAME AS USING THE SAME LABEL AS YOUR PARENT WIDGET!**
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="https://github.com/user-attachments/assets/776a8315-1164-4178-9a8c-df52e0ff28aa"></td>
|
||||
<td>
|
||||
<pre lang="cpp">
|
||||
ImGui::Begin("Incorrect!");
|
||||
ImGui::DragFloat2("My value", &objects[0]->pos.x);
|
||||
ImGui::DragFloat2("My value", &objects[1]->pos.x);
|
||||
ImGui::DragFloat2("My value", &objects[2]->pos.x);
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("Correct!");
|
||||
ImGui::DragFloat2("My value", &objects[0]->pos.x);
|
||||
ImGui::DragFloat2("My value##2", &objects[1]->pos.x);
|
||||
ImGui::DragFloat2("My value##3", &objects[2]->pos.x);
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("Also Correct!");
|
||||
for (int n = 0; n < 3; n++)
|
||||
{
|
||||
ImGui::PushID(n);
|
||||
ImGui::DragFloat2("My value", &objects[n]->pos.x);
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::End();
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
A primer on labels and the ID Stack...
|
||||
|
||||
Dear ImGui internally needs to uniquely identify UI elements.
|
||||
Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
|
||||
Interactive widgets (such as calls to Button buttons) need a unique ID.
|
||||
|
||||
**Unique IDs are used internally to track active widgets and occasionally associate state to widgets.<BR>
|
||||
Unique IDs are implicitly built from the hash of multiple elements that identify the "path" to the UI element.**
|
||||
|
||||
Since Dear ImGui 1.85, you can use `Demo>Tools>ID Stack Tool` or call `ImGui::ShowIDStackToolWindow()`. The tool display intermediate values leading to the creation of a unique ID, making things easier to debug and understand.
|
||||
|
||||

|
||||
|
||||
- Unique ID are often derived from a string label and at minimum scoped within their host window:
|
||||
```cpp
|
||||
Begin("MyWindow");
|
||||
Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK")
|
||||
Button("Cancel"); // Label = "Cancel", ID = hash of ("MyWindow", "Cancel")
|
||||
End();
|
||||
```
|
||||
- Other elements such as tree nodes, etc. also pushes to the ID stack:
|
||||
```cpp
|
||||
Begin("MyWindow");
|
||||
if (TreeNode("MyTreeNode"))
|
||||
{
|
||||
Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "MyTreeNode", "OK")
|
||||
TreePop();
|
||||
}
|
||||
End();
|
||||
```
|
||||
- Two items labeled "OK" in different windows or different tree locations won't collide:
|
||||
```cpp
|
||||
Begin("MyFirstWindow");
|
||||
Button("OK"); // Label = "OK", ID = hash of ("MyFirstWindow", "OK")
|
||||
End();
|
||||
Begin("MyOtherWindow");
|
||||
Button("OK"); // Label = "OK", ID = hash of ("MyOtherWindow", "OK")
|
||||
End();
|
||||
```
|
||||
|
||||
- If you have a same ID twice in the same location, you'll have a conflict:
|
||||
```cpp
|
||||
Begin("MyWindow");
|
||||
Button("OK");
|
||||
Button("OK"); // ERROR: ID collision with the first button! Interacting with either button will trigger the first one.
|
||||
Button(""); // ERROR: ID collision with Begin("MyWindow")!
|
||||
End();
|
||||
```
|
||||
Fear not! This is easy to solve and there are many ways to solve it!
|
||||
|
||||
- Solving ID conflict in a simple/local context:
|
||||
When passing a label you can optionally specify extra ID information within the string itself.
|
||||
Use "##" to pass a complement to the ID that won't be visible to the end-user.
|
||||
This helps solve the simple collision cases when you know e.g. at compilation time which items
|
||||
are going to be created:
|
||||
```cpp
|
||||
Begin("MyWindow");
|
||||
Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play")
|
||||
Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from other buttons
|
||||
Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from other buttons
|
||||
Button("##foo"); // Label = "", ID = hash of ("MyWindow", "##foo") // Different from window
|
||||
End();
|
||||
```
|
||||
- If you want to completely hide the label, but still need an ID:
|
||||
```cpp
|
||||
Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox!
|
||||
```
|
||||
- Occasionally/rarely you might want to change a label while preserving a constant ID. This allows
|
||||
you to animate labels. For example, you may want to include varying information in a window title bar,
|
||||
but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
|
||||
```cpp
|
||||
Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "###ID")
|
||||
Button("World###ID"); // Label = "World", ID = hash of (..., "###ID") // Same ID, different label
|
||||
|
||||
sprintf(buf, "My game (%f FPS)###MyGame", fps);
|
||||
Begin(buf); // Variable title, ID = hash of "MyGame"
|
||||
```
|
||||
- Solving ID conflict in a more general manner:
|
||||
Use `PushID()` / `PopID()` to create scopes and manipulate the ID stack, as to avoid ID conflicts
|
||||
within the same window. This is the most convenient way of distinguishing ID when iterating and
|
||||
creating many UI elements programmatically.
|
||||
You can push a pointer, a string, or an integer value into the ID stack.
|
||||
Remember that IDs are formed from the concatenation of _everything_ pushed into the ID stack.
|
||||
At each level of the stack, we store the seed used for items at this level of the ID stack.
|
||||
```cpp
|
||||
Begin("Window");
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
PushID(i); // Push i to the id tack
|
||||
Button("Click"); // Label = "Click", ID = hash of ("Window", i, "Click")
|
||||
PopID();
|
||||
}
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
MyObject* obj = Objects[i];
|
||||
PushID(obj);
|
||||
Button("Click"); // Label = "Click", ID = hash of ("Window", obj pointer, "Click")
|
||||
PopID();
|
||||
}
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
MyObject* obj = Objects[i];
|
||||
PushID(obj->Name);
|
||||
Button("Click"); // Label = "Click", ID = hash of ("Window", obj->Name, "Click")
|
||||
PopID();
|
||||
}
|
||||
End();
|
||||
```
|
||||
- You can stack multiple prefixes into the ID stack:
|
||||
```cpp
|
||||
Button("Click"); // Label = "Click", ID = hash of (..., "Click")
|
||||
PushID("node");
|
||||
Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
|
||||
PushID(my_ptr);
|
||||
Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click")
|
||||
PopID();
|
||||
PopID();
|
||||
```
|
||||
- Tree nodes implicitly create a scope for you by calling `PushID()`:
|
||||
```cpp
|
||||
Button("Click"); // Label = "Click", ID = hash of (..., "Click")
|
||||
if (TreeNode("node")) // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag)
|
||||
{
|
||||
Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
|
||||
TreePop();
|
||||
}
|
||||
```
|
||||
|
||||
When working with trees, IDs are used to preserve the open/close state of each tree node.
|
||||
Depending on your use cases you may want to use strings, indices, or pointers as ID.
|
||||
- e.g. when following a single pointer that may change over time, using a static string as ID
|
||||
will preserve your node open/closed state when the targeted object change.
|
||||
- e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
|
||||
node open/closed state differently. See what makes more sense in your situation!
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: How can I display an image? What is ImTextureID, how does it work?
|
||||
|
||||
Short explanation:
|
||||
- Refer to [Image Loading and Displaying Examples](https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples) on the [Wiki](https://github.com/ocornut/imgui/wiki).
|
||||
- You may use functions such as `ImGui::Image()`, `ImGui::ImageButton()` or lower-level `ImDrawList::AddImage()` to emit draw calls that will use your own textures.
|
||||
- Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as an opaque ImTextureID value.
|
||||
- By default ImTextureID can store up to 64-bits. You may `#define` it to a custom type/structure if you need.
|
||||
- Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason), but the examples linked above may be useful references.
|
||||
|
||||
**Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.**
|
||||
|
||||
Long explanation:
|
||||
- Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices. At the end of the frame, those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code to render them is generally fairly short (a few dozen lines). In the examples/ folder, we provide functions for popular graphics APIs (OpenGL, DirectX, etc.).
|
||||
- Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API.
|
||||
We carry the information to identify a "texture" in the ImTextureID type.
|
||||
ImTextureID default to ImU64 aka 8 bytes worth of data: just enough to store one pointer or integer of your choice.
|
||||
Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely passes ImTextureID values until they reach your rendering function.
|
||||
- In the [examples/](https://github.com/ocornut/imgui/tree/master/examples) backends, for each graphics API we decided on a type that is likely to be a good representation for specifying an image from the end-user perspective. This is what the _examples_ rendering functions are using:
|
||||
```cpp
|
||||
OpenGL:
|
||||
- ImTextureID should contains 'GLuint' (GL texture identifier).
|
||||
- See ImGui_ImplOpenGL3_RenderDrawData() function in imgui_impl_opengl3.cpp
|
||||
```
|
||||
```cpp
|
||||
DirectX9:
|
||||
- ImTextureID should contain a 'LPDIRECT3DTEXTURE9' (pointer).
|
||||
- See ImGui_ImplDX9_RenderDrawData() function in imgui_impl_dx9.cpp
|
||||
```
|
||||
```cpp
|
||||
DirectX11:
|
||||
- ImTextureID should contain a 'ID3D11ShaderResourceView*' (pointer)
|
||||
- See ImGui_ImplDX11_RenderDrawData() function in imgui_impl_dx11.cpp
|
||||
```
|
||||
```cpp
|
||||
DirectX12:
|
||||
- ImTextureID should contain a 'D3D12_GPU_DESCRIPTOR_HANDLE' (always 64-bits)
|
||||
- See ImGui_ImplDX12_RenderDrawData() function in imgui_impl_dx12.cpp
|
||||
```
|
||||
For example, in the OpenGL example backend we store raw OpenGL texture identifier (GLuint) inside ImTextureID.
|
||||
Whereas in the DirectX11 example backend we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure tying together both the texture and information about its format and how to read it.
|
||||
|
||||
- If you have a custom engine built over e.g. OpenGL, instead of passing GLuint around you may decide to use a high-level data type to carry information about the texture as well as how to display it (shaders, etc.). The decision of what to use as ImTextureID can always be made better by knowing how your codebase is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them.
|
||||
If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID representation suggested by the example backends is probably the best choice.
|
||||
(Advanced users may also decide to keep a low-level type in ImTextureID, use ImDrawList callback and pass information to their renderer)
|
||||
|
||||
User code may do:
|
||||
```cpp
|
||||
// Cast our texture type to ImTextureID
|
||||
MyTexture* texture = g_CoffeeTableTexture;
|
||||
ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2(texture->Width, texture->Height));
|
||||
```
|
||||
The renderer function called after ImGui::Render() will receive that same value that the user code passed:
|
||||
```cpp
|
||||
// Cast ImTextureID stored in the draw command as our texture type
|
||||
MyTexture* texture = (MyTexture*)(intptr_t)pcmd->GetTexID();
|
||||
MyEngineBindTexture2D(texture);
|
||||
```
|
||||
Once you understand this design, you will understand that loading image files and turning them into displayable textures is not within the scope of Dear ImGui.
|
||||
This is by design and is a good thing because it means your code has full control over your data types and how you display them.
|
||||
If you want to display an image file (e.g. PNG file) on the screen, please refer to documentation and tutorials for the graphics API you are using.
|
||||
|
||||
Refer to [Image Loading and Displaying Examples](https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples) on the [Wiki](https://github.com/ocornut/imgui/wiki) to find simplified examples for loading textures with OpenGL, DirectX9 and DirectX11.
|
||||
|
||||
C/C++ tip: a u64 is 8 bytes. You may safely store any pointer or integer into it by casting your value to ImTextureID, and vice-versa.
|
||||
Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTextureID.
|
||||
Here are some examples:
|
||||
```cpp
|
||||
GLuint my_tex = XXX;
|
||||
ImTextureID my_imtexid;
|
||||
my_imtexid = (ImTextureID)(intptr_t)my_tex; // cast a GLuint into a ImTextureID (we don't take its address! we just copy the address)
|
||||
my_tex = (GLuint)(intptr_t)my_imtexid; // cast a ImTextureID into a GLuint
|
||||
|
||||
ID3D11ShaderResourceView* my_dx11_srv = XXX;
|
||||
ImTextureID my_imtexid;
|
||||
my_imtexid = (ImTextureID)(intptr_t)my_dx11_srv; // cast a ID3D11ShaderResourceView* into an opaque ImTextureID
|
||||
my_dx11_srv = (ID3D11ShaderResourceView*)(intptr_t)_my_imtexid; // cast a ImTextureID into a ID3D11ShaderResourceView*
|
||||
```
|
||||
Finally, you may call `ImGui::ShowMetricsWindow()` to explore/visualize/understand how the ImDrawList are generated.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: How can I use maths operators with ImVec2?
|
||||
|
||||
We do not export maths operators by default in imgui.h in order to not conflict with the use of your own maths types and maths operators. As a convenience, you may use `#define IMGUI_DEFINE_MATH_OPERATORS` + `#include "imgui.h"` to access our basic maths operators.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: How can I use my own maths types instead of ImVec2/ImVec4?
|
||||
|
||||
You can setup your [imconfig.h](https://github.com/ocornut/imgui/blob/master/imconfig.h) file with `IM_VEC2_CLASS_EXTRA`/`IM_VEC4_CLASS_EXTRA` macros to add implicit type conversions to our own maths types.
|
||||
This way you will be able to use your own types everywhere, e.g. passing `MyVector2` or `glm::vec2` to ImGui functions instead of `ImVec2`.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: How can I interact with standard C++ types (such as std::string and std::vector)?
|
||||
- Being highly portable (backends/bindings for several languages, frameworks, programming styles, obscure or older platforms/compilers), and aiming for compatibility & performance suitable for every modern real-time game engine, Dear ImGui does not use any of std C++ types. We use raw types (e.g. char* instead of std::string) because they adapt to more use cases.
|
||||
- To use ImGui::InputText() with a std::string or any resizable string class, see [misc/cpp/imgui_stdlib.h](https://github.com/ocornut/imgui/blob/master/misc/cpp/imgui_stdlib.h).
|
||||
- To use combo boxes and list boxes with `std::vector` or any other data structure: the `BeginCombo()/EndCombo()` API
|
||||
lets you iterate and submit items yourself, so does the `ListBoxHeader()/ListBoxFooter()` API.
|
||||
Prefer using them over the old and awkward `Combo()/ListBox()` api.
|
||||
- Generally for most high-level types you should be able to access the underlying data type.
|
||||
You may write your own one-liner wrappers to facilitate user code (tip: add new functions in ImGui:: namespace from your code).
|
||||
- Dear ImGui applications often need to make intensive use of strings. It is expected that many of the strings you will pass
|
||||
to the API are raw literals (free in C/C++) or allocated in a manner that won't incur a large cost on your application.
|
||||
Please bear in mind that using `std::string` on applications with a large amount of UI may incur unsatisfactory performances.
|
||||
Modern implementations of `std::string` often include small-string optimization (which is often a local buffer) but those
|
||||
are not configurable and not the same across implementations.
|
||||
- If you are finding your UI traversal cost to be too large, make sure your string usage is not leading to an excessive amount
|
||||
of heap allocations. Consider using literals, statically sized buffers, and your own helper functions. A common pattern
|
||||
is that you will need to build lots of strings on the fly, and their maximum length can be easily scoped ahead.
|
||||
One possible implementation of a helper to facilitate printf-style building of strings: https://github.com/ocornut/Str
|
||||
This is a small helper where you can instance strings with configurable local buffers length. Many game engines will
|
||||
provide similar or better string helpers.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: How can I display custom shapes? (using low-level ImDrawList API)
|
||||
|
||||
- You can use the low-level `ImDrawList` api to render shapes within a window.
|
||||
```cpp
|
||||
ImGui::Begin("My shapes");
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// Get the current ImGui cursor position
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
|
||||
// Draw a red circle
|
||||
draw_list->AddCircleFilled(ImVec2(p.x + 50, p.y + 50), 30.0f, IM_COL32(255, 0, 0, 255));
|
||||
|
||||
// Draw a 3 pixel thick yellow line
|
||||
draw_list->AddLine(ImVec2(p.x, p.y), ImVec2(p.x + 100.0f, p.y + 100.0f), IM_COL32(255, 255, 0, 255), 3.0f);
|
||||
|
||||
// Advance the ImGui cursor to claim space in the window (otherwise the window will appear small and needs to be resized)
|
||||
ImGui::Dummy(ImVec2(200, 200));
|
||||
|
||||
ImGui::End();
|
||||
```
|
||||

|
||||
|
||||
- Refer to "Demo > Examples > Custom Rendering" in the demo window and read the code of `ShowExampleAppCustomRendering()` in `imgui_demo.cpp` from more examples.
|
||||
- To generate colors: you can use the macro `IM_COL32(255,255,255,255)` to generate them at compile time, or use `ImGui::GetColorU32(IM_COL32(255,255,255,255))` or `ImGui::GetColorU32(ImVec4(1.0f,1.0f,1.0f,1.0f))` to generate a color that is multiplied by the current value of `style.Alpha`.
|
||||
- Math operators: if you have setup `IM_VEC2_CLASS_EXTRA` in `imconfig.h` to bind your own math types, you can use your own math types and their natural operators instead of ImVec2. ImVec2 by default doesn't export any math operators in the public API. You may use `#define IMGUI_DEFINE_MATH_OPERATORS` `#include "imgui.h"` to use our math operators, but instead prefer using your own math library and set it up in `imconfig.h`.
|
||||
- You can use `ImGui::GetBackgroundDrawList()` or `ImGui::GetForegroundDrawList()` to access draw lists which will be displayed behind and over every other Dear ImGui window (one bg/fg drawlist per viewport). This is very convenient if you need to quickly display something on the screen that is not associated with a Dear ImGui window.
|
||||
- You can also create your own empty window and draw inside it. Call Begin() with the NoBackground | NoDecoration | NoSavedSettings | NoInputs flags (The `ImGuiWindowFlags_NoDecoration` flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse). Then you can retrieve the ImDrawList* via `GetWindowDrawList()` and draw to it in any way you like.
|
||||
- You can create your own ImDrawList instance. You'll need to initialize them with `ImGui::GetDrawListSharedData()`, or create your own instancing `ImDrawListSharedData`, and then call your renderer function with your own ImDrawList or ImDrawData data.
|
||||
- Looking for fun? The [ImDrawList coding party 2020](https://github.com/ocornut/imgui/issues/3606) thread is full of "don't do this at home" extreme uses of the ImDrawList API.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
# Q&A: Fonts, Text
|
||||
|
||||
### Q: How should I handle DPI in my application?
|
||||
|
||||
The short answer is: obtain the desired DPI scale, load your fonts resized with that scale (always round down fonts size to the nearest integer), and scale your Style structure accordingly using `style.ScaleAllSizes()`.
|
||||
|
||||
Your application may want to detect DPI change and reload the fonts and reset style between frames.
|
||||
|
||||
Your ui code should avoid using hardcoded constants for size and positioning. Prefer to express values as multiple of reference values such as `ImGui::GetFontSize()` or `ImGui::GetFrameHeight()`. So e.g. instead of seeing a hardcoded height of 500 for a given item/window, you may want to use `30*ImGui::GetFontSize()` instead.
|
||||
|
||||
Down the line Dear ImGui will provide a variety of standardized reference values to facilitate using this.
|
||||
|
||||
Applications in the `examples/` folder are not DPI aware partly because they are unable to load a custom font from the file-system (may change that in the future).
|
||||
|
||||
The reason DPI is not auto-magically solved in stock examples is that we don't yet have a satisfying solution for the "multi-dpi" problem (using the `docking` branch: when multiple viewport windows are over multiple monitors using different DPI scales). The current way to handle this on the application side is:
|
||||
- Create and maintain one font atlas per active DPI scale (e.g. by iterating `platform_io.Monitors[]` before `NewFrame()`).
|
||||
- Hook `platform_io.OnChangedViewport()` to detect when a `Begin()` call makes a Dear ImGui window change monitor (and therefore DPI).
|
||||
- In the hook: swap atlas, swap style with correctly sized one, and remap the current font from one atlas to the other (you may need to maintain a remapping table of your fonts at varying DPI scales).
|
||||
|
||||
This approach is relatively easy and functional but comes with two issues:
|
||||
- It's not possibly to reliably size or position a window ahead of `Begin()` without knowing on which monitor it'll land.
|
||||
- Style override may be lost during the `Begin()` call crossing monitor boundaries. You may need to do some custom scaling mumbo-jumbo if you want your `OnChangedViewport()` handler to preserve style overrides.
|
||||
|
||||
Please note that if you are not using multi-viewports with multi-monitors using different DPI scales, you can ignore that and use the simpler technique recommended at the top.
|
||||
|
||||
On Windows, in addition to scaling the font size (make sure to round to an integer) and using `style.ScaleAllSizes()`, you will need to inform Windows that your application is DPI aware. If this is not done, Windows will scale the application window and the UI text will be blurry. Potential solutions to indicate DPI awareness on Windows are:
|
||||
|
||||
- For SDL: the flag `SDL_WINDOW_ALLOW_HIGHDPI` needs to be passed to `SDL_CreateWindow()``.
|
||||
- For GLFW: this is done automatically.
|
||||
- For other Windows projects with other backends, or wrapper projects:
|
||||
- We provide a `ImGui_ImplWin32_EnableDpiAwareness()` helper method in the Win32 backend.
|
||||
- Use an [application manifest file](https://learn.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process) to set the `<dpiAware>` property.
|
||||
|
||||
### Q: How can I load a different font than the default?
|
||||
Use the font atlas to load the TTF/OTF file you want:
|
||||
|
||||
```cpp
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
|
||||
io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
|
||||
```
|
||||
|
||||
Default is ProggyClean.ttf, monospace, rendered at size 13, embedded in dear imgui's source code.
|
||||
|
||||
(Tip: monospace fonts are convenient because they allow to facilitate horizontal alignment directly at the string level.)
|
||||
|
||||
(Read the [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md) file for more details about font loading.)
|
||||
|
||||
New programmers: remember that in C/C++ and most programming languages if you want to use a
|
||||
backslash \ within a string literal, you need to write it double backslash "\\":
|
||||
|
||||
```cpp
|
||||
io.Fonts->AddFontFromFileTTF("MyFolder\MyFont.ttf", size); // WRONG (you are escaping the M here!)
|
||||
io.Fonts->AddFontFromFileTTF("MyFolder\\MyFont.ttf", size); // CORRECT (Windows only)
|
||||
io.Fonts->AddFontFromFileTTF("MyFolder/MyFont.ttf", size); // ALSO CORRECT
|
||||
```
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: How can I easily use icons in my application?
|
||||
The most convenient and practical way is to merge an icon font such as FontAwesome inside your
|
||||
main font. Then you can refer to icons within your strings.
|
||||
Read the [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md) file for more details about icons font loading.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: How can I load multiple fonts?
|
||||
|
||||
Use the font atlas to pack them into a single texture. Read [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md) for more details.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
|
||||
When loading a font, pass custom Unicode ranges to specify the glyphs to load.
|
||||
|
||||
```cpp
|
||||
// Add default Japanese ranges
|
||||
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, nullptr, io.Fonts->GetGlyphRangesJapanese());
|
||||
|
||||
// Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need)
|
||||
ImVector<ImWchar> ranges;
|
||||
ImFontGlyphRangesBuilder builder;
|
||||
builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters)
|
||||
builder.AddChar(0x7262); // Add a specific character
|
||||
builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
|
||||
builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted)
|
||||
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", 16.0f, nullptr, ranges.Data);
|
||||
```
|
||||
|
||||
All your strings need to use UTF-8 encoding.
|
||||
You need to tell your compiler to use UTF-8, or in C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax.
|
||||
Specifying literal in your source code using a local code page (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
|
||||
See [About UTF-8 Encoding](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md#about-utf-8-encoding) section
|
||||
of [FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md) for details about UTF-8 Encoding.
|
||||
|
||||
Text input: it is up to your application to pass the right character code by calling `io.AddInputCharacter()`.
|
||||
The applications in examples/ are doing that.
|
||||
Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode).
|
||||
You may also use `MultiByteToWideChar()` or `ToUnicode()` to retrieve Unicode codepoints from MultiByte characters or keyboard state.
|
||||
Windows: if your language is relying on an Input Method Editor (IME), you can write your HWND to ImGui::GetMainViewport()->PlatformHandleRaw
|
||||
for the default implementation of GetPlatformIO().Platform_SetImeDataFn() to set your Microsoft IME position correctly.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
# Q&A: Concerns
|
||||
|
||||
### Q: Who uses Dear ImGui?
|
||||
|
||||
You may take a look at:
|
||||
|
||||
- [Quotes](https://github.com/ocornut/imgui/wiki/Quotes)
|
||||
- [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui)
|
||||
- [Funding & Sponsors](https://github.com/ocornut/imgui/wiki/Funding)
|
||||
- [Gallery](https://github.com/ocornut/imgui/issues?q=label%3Agallery)
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: Can you create elaborate/serious tools with Dear ImGui?
|
||||
|
||||
Yes. People have written game editors, data browsers, debuggers, profilers, and all sorts of non-trivial tools with the library. In my experience, the simplicity of the API is very empowering. Your UI runs close to your live data. Make the tools always-on and everybody in the team will be inclined to create new tools (as opposed to more "offline" UI toolkits where only a fraction of your team effectively creates tools). The list of sponsors below is also an indicator that serious game teams have been using the library.
|
||||
|
||||
Dear ImGui is very programmer centric and the immediate-mode GUI paradigm might require you to readjust some habits before you can realize its full potential. Dear ImGui is about making things that are simple, efficient, and powerful.
|
||||
|
||||
Dear ImGui is built to be efficient and scalable toward the needs for AAA-quality applications running all day. The IMGUI paradigm offers different opportunities for optimization than the more typical RMGUI paradigm.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: Can you reskin the look of Dear ImGui?
|
||||
|
||||
Somewhat. You can alter the look of the interface to some degree: changing colors, sizes, padding, rounding, and fonts. However, as Dear ImGui is designed and optimized to create debug tools, the amount of skinning you can apply is limited. There is only so much you can stray away from the default look and feel of the interface. Dear ImGui is NOT designed to create a user interface for games, although with ingenious use of the low-level API you can do it.
|
||||
|
||||
A reasonably skinned application may look like (screenshot from [#2529](https://github.com/ocornut/imgui/issues/2529#issuecomment-524281119)):
|
||||

|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
### Q: Why using C++ (as opposed to C)?
|
||||
|
||||
Dear ImGui takes advantage of a few C++ language features for convenience but nothing anywhere Boost insanity/quagmire. Dear ImGui doesn't use any C++ header file. Dear ImGui uses a very small subset of C++11 features. In particular, function overloading and default parameters are used to make the API easier to use and code terser. Doing so I believe the API is sitting on a sweet spot and giving up on those features would make the API more cumbersome. Other features such as namespace, constructors, and templates (in the case of the ImVector<> class) are also relied on as a convenience.
|
||||
|
||||
There is an auto-generated [c-api for Dear ImGui (cimgui)](https://github.com/cimgui/cimgui) by Sonoro1234 and Stephan Dilly. It is designed for creating bindings to other languages. If possible, I would suggest using your target language functionalities to try replicating the function overloading and default parameters used in C++ else the API may be harder to use. Also see [Bindings](https://github.com/ocornut/imgui/wiki/Bindings) for various third-party bindings.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---
|
||||
|
||||
# Q&A: Community
|
||||
|
||||
### Q: How can I help?
|
||||
- Businesses: please reach out to `omar AT dearimgui.com` if you work in a place using Dear ImGui! We can discuss ways for your company to fund development via invoiced technical support, maintenance, or sponsoring contacts. This is among the most useful thing you can do for Dear ImGui. With increased funding, we can hire more people to work on this project. Please see [Funding](https://github.com/ocornut/imgui/wiki/Funding) page.
|
||||
- Individuals: you can support continued maintenance and development via PayPal donations. See [README](https://github.com/ocornut/imgui/blob/master/docs/README.md).
|
||||
- If you are experienced with Dear ImGui and C++, look at [GitHub Issues](https://github.com/ocornut/imgui/issues), [GitHub Discussions](https://github.com/ocornut/imgui/discussions), the [Wiki](https://github.com/ocornut/imgui/wiki), read [docs/TODO.txt](https://github.com/ocornut/imgui/blob/master/docs/TODO.txt), and see how you want to help and can help!
|
||||
- Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere, etc.
|
||||
You may post screenshots or links in the [gallery threads](https://github.com/ocornut/imgui/issues?q=label%3Agallery). Visuals are ideal as they inspire other programmers. Disclosing your use of Dear ImGui helps the library grow credibility, and helps other teams and programmers with taking decisions.
|
||||
- If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues or sometimes incomplete PR.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
502
Source/ThirdParty/ImGuiLibrary/Docs/FONTS.md
vendored
Normal file
502
Source/ThirdParty/ImGuiLibrary/Docs/FONTS.md
vendored
Normal file
@ -0,0 +1,502 @@
|
||||
_(You may browse this at https://github.com/ocornut/imgui/blob/master/docs/FONTS.md or view this file with any Markdown viewer)_
|
||||
|
||||
## Dear ImGui: Using Fonts
|
||||
|
||||
The code in imgui.cpp embeds a copy of 'ProggyClean.ttf' (by Tristan Grimmer),
|
||||
a 13 pixels high, pixel-perfect font used by default. We embed it in the source code so you can use Dear ImGui without any file system access. ProggyClean does not scale smoothly, therefore it is recommended that you load your own file when using Dear ImGui in an application aiming to look nice and wanting to support multiple resolutions.
|
||||
|
||||
You may also load external .TTF/.OTF files.
|
||||
In the [misc/fonts/](https://github.com/ocornut/imgui/tree/master/misc/fonts) folder you can find a few suggested fonts, provided as a convenience.
|
||||
|
||||
**Also read the FAQ:** https://www.dearimgui.com/faq (there is a Fonts section!)
|
||||
|
||||
## Index
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [How should I handle DPI in my application?](#how-should-i-handle-dpi-in-my-application)
|
||||
- [Fonts Loading Instructions](#fonts-loading-instructions)
|
||||
- [Loading Font Data from Memory](#loading-font-data-from-memory)
|
||||
- [Loading Font Data Embedded In Source Code](#loading-font-data-embedded-in-source-code)
|
||||
- [Using Icon Fonts](#using-icon-fonts)
|
||||
- [Using FreeType Rasterizer (imgui_freetype)](#using-freetype-rasterizer-imgui_freetype)
|
||||
- [Using Colorful Glyphs/Emojis](#using-colorful-glyphsemojis)
|
||||
- [Using Custom Glyph Ranges](#using-custom-glyph-ranges)
|
||||
- [Using Custom Colorful Icons](#using-custom-colorful-icons)
|
||||
- [About Filenames](#about-filenames)
|
||||
- [About UTF-8 Encoding](#about-utf-8-encoding)
|
||||
- [Debug Tools](#debug-tools)
|
||||
- [Credits/Licenses For Fonts Included In Repository](#creditslicenses-for-fonts-included-in-repository)
|
||||
- [Font Links](#font-links)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**A vast majority of font and text related issues encountered comes from 4 things:**
|
||||
|
||||
### (1) Invalid filename due to use of `\` or unexpected working directory.
|
||||
|
||||
See [About Filenames](#about-filenames). AddFontXXX functions should assert if the filename is incorrect.
|
||||
|
||||
### (2) Invalid UTF-8 encoding of your non-ASCII strings.
|
||||
|
||||
See [About UTF-8 Encoding](#about-utf-8-encoding). Use the encoding viewer to confirm encoding of string literal in your source code is correct.
|
||||
|
||||
### (3) Missing glyph ranges.
|
||||
|
||||
You need to load a font with explicit glyph ranges if you want to use non-ASCII characters. See [Fonts Loading Instructions](#fonts-loading-instructions). Use [Debug Tools](#debug-tools) confirm loaded fonts and loaded glyph ranges.
|
||||
|
||||
This is a current constraint of Dear ImGui (which we will lift in the future): when loading a font you need to specify which characters glyphs to load.
|
||||
All loaded fonts glyphs are rendered into a single texture atlas ahead of time. Calling either of `io.Fonts->GetTexDataAsAlpha8()`, `io.Fonts->GetTexDataAsRGBA32()` or `io.Fonts->Build()` will build the atlas. This is generally called by the Renderer backend, e.g. `ImGui_ImplDX11_NewFrame()` calls it. **If you use custom glyphs ranges, make sure the array is persistent** and available during the calls to `GetTexDataAsAlpha8()/GetTexDataAsRGBA32()/Build()`.
|
||||
|
||||
### (4) Font atlas texture fails to upload to GPU.
|
||||
|
||||
This is often of byproduct of point 3. If you have large number of glyphs or multiple fonts, the texture may become too big for your graphics API. **The typical result of failing to upload a texture is if every glyph or everything appears as empty white rectangles.** Mind the fact that some graphics drivers have texture size limitation. If you are building a PC application, mind the fact that your users may use hardware with lower limitations than yours.
|
||||
|
||||

|
||||
|
||||
Some solutions:
|
||||
- You may reduce oversampling, e.g. `font_config.OversampleH = 1`, this will half your texture size for a quality loss.
|
||||
Note that while OversampleH = 2 looks visibly very close to 3 in most situations, with OversampleH = 1 the quality drop will be noticeable. Read about oversampling [here](https://github.com/nothings/stb/blob/master/tests/oversample).
|
||||
- Reduce glyphs ranges by calculating them from source localization data.
|
||||
You can use the `ImFontGlyphRangesBuilder` for this purpose and rebuilding your atlas between frames when new characters are needed. This will be the biggest win!
|
||||
- Set `io.Fonts.Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;` to disable rounding the texture height to the next power of two.
|
||||
- Set `io.Fonts.TexDesiredWidth` to specify a texture width to reduce maximum texture height (see comment in `ImFontAtlas::Build()` function).
|
||||
|
||||
Future versions of Dear ImGui should solve this problem.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## How should I handle DPI in my application?
|
||||
|
||||
See [FAQ entry](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-how-should-i-handle-dpi-in-my-application).
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Fonts Loading Instructions
|
||||
|
||||
**Load default font:**
|
||||
```cpp
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->AddFontDefault();
|
||||
```
|
||||
|
||||
**Load .TTF/.OTF file with:**
|
||||
```cpp
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels);
|
||||
```
|
||||
If you get an assert stating "Could not load font file!", your font filename is likely incorrect. Read [About filenames](#about-filenames) carefully.
|
||||
|
||||
**Load multiple fonts:**
|
||||
```cpp
|
||||
// Init
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImFont* font1 = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels);
|
||||
ImFont* font2 = io.Fonts->AddFontFromFileTTF("anotherfont.otf", size_pixels);
|
||||
```
|
||||
|
||||
In your application loop, select which font to use:
|
||||
```cpp
|
||||
ImGui::Text("Hello"); // use the default font (which is the first loaded font)
|
||||
ImGui::PushFont(font2);
|
||||
ImGui::Text("Hello with another font");
|
||||
ImGui::PopFont();
|
||||
```
|
||||
|
||||
**For advanced options create a ImFontConfig structure and pass it to the AddFont() function (it will be copied internally):**
|
||||
```cpp
|
||||
ImFontConfig config;
|
||||
config.OversampleH = 2;
|
||||
config.OversampleV = 1;
|
||||
config.GlyphExtraSpacing.x = 1.0f;
|
||||
ImFont* font = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, &config);
|
||||
```
|
||||
|
||||
**Combine multiple fonts into one:**
|
||||
```cpp
|
||||
// Load a first font
|
||||
ImFont* font = io.Fonts->AddFontDefault();
|
||||
|
||||
// Add character ranges and merge into the previous font
|
||||
// The ranges array is not copied by the AddFont* functions and is used lazily
|
||||
// so ensure it is available at the time of building or calling GetTexDataAsRGBA32().
|
||||
static const ImWchar icons_ranges[] = { 0xf000, 0xf3ff, 0 }; // Will not be copied by AddFont* so keep in scope.
|
||||
ImFontConfig config;
|
||||
config.MergeMode = true;
|
||||
io.Fonts->AddFontFromFileTTF("DroidSans.ttf", 18.0f, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge into first font
|
||||
io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 18.0f, &config, icons_ranges); // Merge into first font
|
||||
io.Fonts->Build();
|
||||
```
|
||||
|
||||
**Add a fourth parameter to bake specific font ranges only:**
|
||||
|
||||
```cpp
|
||||
// Basic Latin, Extended Latin
|
||||
io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, nullptr, io.Fonts->GetGlyphRangesDefault());
|
||||
|
||||
// Default + Selection of 2500 Ideographs used by Simplified Chinese
|
||||
io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, nullptr, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
|
||||
|
||||
// Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs
|
||||
io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, nullptr, io.Fonts->GetGlyphRangesJapanese());
|
||||
```
|
||||
See [Using Custom Glyph Ranges](#using-custom-glyph-ranges) section to create your own ranges.
|
||||
|
||||
**Example loading and using a Japanese font:**
|
||||
|
||||
```cpp
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->AddFontFromFileTTF("NotoSansCJKjp-Medium.otf", 20.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
|
||||
```
|
||||
```cpp
|
||||
ImGui::Text(u8"こんにちは!テスト %d", 123);
|
||||
if (ImGui::Button(u8"ロード"))
|
||||
{
|
||||
// do stuff
|
||||
}
|
||||
ImGui::InputText("string", buf, IM_ARRAYSIZE(buf));
|
||||
ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
|
||||
```
|
||||
|
||||

|
||||
<br>_(settings: Dark style (left), Light style (right) / Font: NotoSansCJKjp-Medium, 20px / Rounding: 5)_
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Loading Font Data from Memory
|
||||
|
||||
```cpp
|
||||
ImFont* font = io.Fonts->AddFontFromMemoryTTF(data, data_size, size_pixels, ...);
|
||||
```
|
||||
|
||||
IMPORTANT: `AddFontFromMemoryTTF()` by default transfer ownership of the data buffer to the font atlas, which will attempt to free it on destruction.
|
||||
This was to avoid an unnecessary copy, and is perhaps not a good API (a future version will redesign it).
|
||||
If you want to keep ownership of the data and free it yourself, you need to clear the `FontDataOwnedByAtlas` field:
|
||||
|
||||
```cpp
|
||||
ImFontConfig font_cfg;
|
||||
font_cfg.FontDataOwnedByAtlas = false;
|
||||
ImFont* font = io.Fonts->AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg);
|
||||
```
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Loading Font Data Embedded In Source Code
|
||||
|
||||
- Compile and use [binary_to_compressed_c.cpp](https://github.com/ocornut/imgui/blob/master/misc/fonts/binary_to_compressed_c.cpp) to create a compressed C style array that you can embed in source code.
|
||||
- See the documentation in [binary_to_compressed_c.cpp](https://github.com/ocornut/imgui/blob/master/misc/fonts/binary_to_compressed_c.cpp) for instructions on how to use the tool.
|
||||
- You may find a precompiled version binary_to_compressed_c.exe for Windows inside the demo binaries package (see [README](https://github.com/ocornut/imgui/blob/master/docs/README.md)).
|
||||
- The tool can optionally output Base85 encoding to reduce the size of _source code_ but the read-only arrays in the actual binary will be about 20% bigger.
|
||||
|
||||
Then load the font with:
|
||||
```cpp
|
||||
ImFont* font = io.Fonts->AddFontFromMemoryCompressedTTF(compressed_data, compressed_data_size, size_pixels, ...);
|
||||
```
|
||||
or
|
||||
```cpp
|
||||
ImFont* font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(compressed_data_base85, size_pixels, ...);
|
||||
```
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Using Icon Fonts
|
||||
|
||||
Using an icon font (such as [FontAwesome](http://fontawesome.io) or [OpenFontIcons](https://github.com/traverseda/OpenFontIcons)) is an easy and practical way to use icons in your Dear ImGui application.
|
||||
A common pattern is to merge the icon font within your main font, so you can embed icons directly from your strings without having to change fonts back and forth.
|
||||
|
||||
To refer to the icon UTF-8 codepoints from your C++ code, you may use those headers files created by Juliette Foucaut: https://github.com/juliettef/IconFontCppHeaders.
|
||||
|
||||
So you can use `ICON_FA_SEARCH` as a string that will render as a "Search" icon.
|
||||
|
||||
Example Setup:
|
||||
```cpp
|
||||
// Merge icons into default tool font
|
||||
#include "IconsFontAwesome.h"
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->AddFontDefault();
|
||||
|
||||
ImFontConfig config;
|
||||
config.MergeMode = true;
|
||||
config.GlyphMinAdvanceX = 13.0f; // Use if you want to make the icon monospaced
|
||||
static const ImWchar icon_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
|
||||
io.Fonts->AddFontFromFileTTF("fonts/fontawesome-webfont.ttf", 13.0f, &config, icon_ranges);
|
||||
```
|
||||
Example Usage:
|
||||
```cpp
|
||||
// Usage, e.g.
|
||||
ImGui::Text("%s among %d items", ICON_FA_SEARCH, count);
|
||||
ImGui::Button(ICON_FA_SEARCH " Search");
|
||||
// C string _literals_ can be concatenated at compilation time, e.g. "hello" " world"
|
||||
// ICON_FA_SEARCH is defined as a string literal so this is the same as "A" "B" becoming "AB"
|
||||
```
|
||||
See Links below for other icons fonts and related tools.
|
||||
|
||||
**Monospace Icons?**
|
||||
|
||||
To make your icon look more monospace and facilitate alignment, you may want to set the ImFontConfig::GlyphMinAdvanceX value when loading an icon font.
|
||||
|
||||
**Screenshot**
|
||||
|
||||
Here's an application using icons ("Avoyd", https://www.avoyd.com):
|
||||

|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Using FreeType Rasterizer (imgui_freetype)
|
||||
|
||||
- Dear ImGui uses imstb\_truetype.h to rasterize fonts (with optional oversampling). This technique and its implementation are not ideal for fonts rendered at small sizes, which may appear a little blurry or hard to read.
|
||||
- There is an implementation of the ImFontAtlas builder using FreeType that you can use in the [misc/freetype/](https://github.com/ocornut/imgui/tree/master/misc/freetype) folder.
|
||||
- FreeType supports auto-hinting which tends to improve the readability of small fonts.
|
||||
- Read documentation in the [misc/freetype/](https://github.com/ocornut/imgui/tree/master/misc/freetype) folder.
|
||||
- Correct sRGB space blending will have an important effect on your font rendering quality.
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Using Colorful Glyphs/Emojis
|
||||
|
||||
- Rendering of colored emojis is supported by imgui_freetype with FreeType 2.10+.
|
||||
- You will need to load fonts with the `ImGuiFreeTypeBuilderFlags_LoadColor` flag.
|
||||
- Emojis are frequently encoded in upper Unicode layers (character codes >0x10000) and will need dear imgui compiled with `IMGUI_USE_WCHAR32`.
|
||||
- Not all types of color fonts are supported by FreeType at the moment.
|
||||
- Stateful Unicode features such as skin tone modifiers are not supported by the text renderer.
|
||||
|
||||

|
||||
|
||||
```cpp
|
||||
io.Fonts->AddFontFromFileTTF("../../../imgui_dev/data/fonts/NotoSans-Regular.ttf", 16.0f);
|
||||
static ImWchar ranges[] = { 0x1, 0x1FFFF, 0 };
|
||||
static ImFontConfig cfg;
|
||||
cfg.OversampleH = cfg.OversampleV = 1;
|
||||
cfg.MergeMode = true;
|
||||
cfg.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_LoadColor;
|
||||
io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\seguiemj.ttf", 16.0f, &cfg, ranges);
|
||||
```
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Using Custom Glyph Ranges
|
||||
|
||||
You can use the `ImFontGlyphRangesBuilder` helper to create glyph ranges based on text input. For example: for a game where your script is known, if you can feed your entire script to it and only build the characters the game needs.
|
||||
```cpp
|
||||
ImVector<ImWchar> ranges;
|
||||
ImFontGlyphRangesBuilder builder;
|
||||
builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters)
|
||||
builder.AddChar(0x7262); // Add a specific character
|
||||
builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
|
||||
builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted)
|
||||
|
||||
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, nullptr, ranges.Data);
|
||||
io.Fonts->Build(); // Build the atlas while 'ranges' is still in scope and not deleted.
|
||||
```
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Using Custom Colorful Icons
|
||||
|
||||
As an alternative to rendering colorful glyphs using imgui_freetype with `ImGuiFreeTypeBuilderFlags_LoadColor`, you may allocate your own space in the texture atlas and write yourself into it. **(This is a BETA api, use if you are familiar with dear imgui and with your rendering backend)**
|
||||
|
||||
- You can use the `ImFontAtlas::AddCustomRect()` and `ImFontAtlas::AddCustomRectFontGlyph()` api to register rectangles that will be packed into the font atlas texture. Register them before building the atlas, then call Build()`.
|
||||
- You can then use `ImFontAtlas::GetCustomRectByIndex(int)` to query the position/size of your rectangle within the texture, and blit/copy any graphics data of your choice into those rectangles.
|
||||
- This API is beta because it is likely to change in order to support multi-dpi (multiple viewports on multiple monitors with varying DPI scale).
|
||||
|
||||
#### Pseudo-code:
|
||||
```cpp
|
||||
// Add font, then register two custom 13x13 rectangles mapped to glyph 'a' and 'b' of this font
|
||||
ImFont* font = io.Fonts->AddFontDefault();
|
||||
int rect_ids[2];
|
||||
rect_ids[0] = io.Fonts->AddCustomRectFontGlyph(font, 'a', 13, 13, 13+1);
|
||||
rect_ids[1] = io.Fonts->AddCustomRectFontGlyph(font, 'b', 13, 13, 13+1);
|
||||
|
||||
// Build atlas
|
||||
io.Fonts->Build();
|
||||
|
||||
// Retrieve texture in RGBA format
|
||||
unsigned char* tex_pixels = nullptr;
|
||||
int tex_width, tex_height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&tex_pixels, &tex_width, &tex_height);
|
||||
|
||||
for (int rect_n = 0; rect_n < IM_ARRAYSIZE(rect_ids); rect_n++)
|
||||
{
|
||||
int rect_id = rect_ids[rect_n];
|
||||
if (const ImFontAtlasCustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id))
|
||||
{
|
||||
// Fill the custom rectangle with red pixels (in reality you would draw/copy your bitmap data here!)
|
||||
for (int y = 0; y < rect->Height; y++)
|
||||
{
|
||||
ImU32* p = (ImU32*)tex_pixels + (rect->Y + y) * tex_width + (rect->X);
|
||||
for (int x = rect->Width; x > 0; x--)
|
||||
*p++ = IM_COL32(255, 0, 0, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## About Filenames
|
||||
|
||||
**Please note that many new C/C++ users have issues loading their files _because the filename they provide is wrong_ due to incorrect assumption of what is the current directory.**
|
||||
|
||||
Two things to watch for:
|
||||
|
||||
(1) In C/C++ and most programming languages if you want to use a backslash `\` within a string literal, you need to write it double backslash `\\`. At it happens, Windows uses backslashes as a path separator, so be mindful.
|
||||
```cpp
|
||||
io.Fonts->AddFontFromFileTTF("MyFiles\MyImage01.jpg", ...); // This is INCORRECT!!
|
||||
io.Fonts->AddFontFromFileTTF("MyFiles\\MyImage01.jpg", ...); // This is CORRECT
|
||||
```
|
||||
In some situations, you may also use `/` path separator under Windows.
|
||||
|
||||
(2) Make sure your IDE/debugger settings starts your executable from the right working (current) directory. In Visual Studio you can change your working directory in project `Properties > General > Debugging > Working Directory`. People assume that their execution will start from the root folder of the project, where by default it often starts from the folder where object or executable files are stored.
|
||||
```cpp
|
||||
io.Fonts->AddFontFromFileTTF("MyImage01.jpg", ...); // Relative filename depends on your Working Directory when running your program!
|
||||
io.Fonts->AddFontFromFileTTF("../MyImage01.jpg", ...); // Load from the parent folder of your Working Directory
|
||||
```
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## About UTF-8 Encoding
|
||||
|
||||
**For non-ASCII characters display, a common user issue is not passing correctly UTF-8 encoded strings.**
|
||||
|
||||
(1) We provide a function `ImGui::DebugTextEncoding(const char* text)` which you can call to verify the content of your UTF-8 strings.
|
||||
This is a convenient way to confirm that your encoding is correct.
|
||||
|
||||
```cpp
|
||||
ImGui::SeparatorText("CORRECT");
|
||||
ImGui::DebugTextEncoding(u8"こんにちは");
|
||||
|
||||
ImGui::SeparatorText("INCORRECT");
|
||||
ImGui::DebugTextEncoding("こんにちは");
|
||||
```
|
||||

|
||||
|
||||
You can also find this tool under `Metrics/Debuggers->Tools->UTF-8 Encoding viewer` if you want to paste from clipboard, but this won't validate the UTF-8 encoding done by your compiler.
|
||||
|
||||
(2) To encode in UTF-8:
|
||||
|
||||
There are also compiler-specific ways to enforce UTF-8 encoding by default:
|
||||
|
||||
- Visual Studio compiler: `/utf-8` command-line flag.
|
||||
- Visual Studio compiler: `#pragma execution_character_set("utf-8")` inside your code.
|
||||
- Since May 2023 we have changed the Visual Studio projects of all our examples to use `/utf-8` ([see commit](https://github.com/ocornut/imgui/commit/513af1efc9080857bbd10000d98f98f2a0c96803)).
|
||||
|
||||
Or, since C++11, you can use the `u8"my text"` syntax to encode literal strings as UTF-8. e.g.:
|
||||
```cpp
|
||||
ImGui::Text(u8"hello");
|
||||
ImGui::Text(u8"こんにちは"); // this will always be encoded as UTF-8
|
||||
ImGui::Text("こんにちは"); // the encoding of this is depending on compiler settings/flags and may be incorrect.
|
||||
```
|
||||
|
||||
Since C++20, because the C++ committee hate its users, they decided to change the `u8""` syntax to not return `const char*` but a new type `const char8_t*` which doesn't cast to `const char*`.
|
||||
Because of type usage of `u8""` in C++20 is a little more tedious:
|
||||
```cpp
|
||||
ImGui::Text((const char*)u8"こんにちは");
|
||||
```
|
||||
However, you can disable this behavior completely using the compiler option [`/Zc:char8_t-`](https://learn.microsoft.com/en-us/cpp/build/reference/zc-char8-t?view=msvc-170) for MSVC and [`-fno-char8_t`](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1423r3.html) for Clang and GCC.
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Debug Tools
|
||||
|
||||
#### Metrics/Debugger->Fonts
|
||||
You can use the `Metrics/Debugger` window (available in `Demo>Tools`) to browse your fonts and understand what's going on if you have an issue. You can also reach it in `Demo->Tools->Style Editor->Fonts`. The same information are also available in the Style Editor under Fonts.
|
||||
|
||||

|
||||
|
||||
#### UTF-8 Encoding Viewer**
|
||||
You can use the `UTF-8 Encoding viewer` in `Metrics/Debugger` to verify the content of your UTF-8 strings. From C/C++ code, you can call `ImGui::DebugTextEncoding("my string");` function to verify that your UTF-8 encoding is correct.
|
||||
|
||||

|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Credits/Licenses For Fonts Included In Repository
|
||||
|
||||
Some fonts files are available in the `misc/fonts/` folder:
|
||||
|
||||
**Roboto-Medium.ttf**, by Christian Robetson
|
||||
<br>Apache License 2.0
|
||||
<br>https://fonts.google.com/specimen/Roboto
|
||||
|
||||
**Cousine-Regular.ttf**, by Steve Matteson
|
||||
<br>Digitized data copyright (c) 2010 Google Corporation.
|
||||
<br>Licensed under the SIL Open Font License, Version 1.1
|
||||
<br>https://fonts.google.com/specimen/Cousine
|
||||
|
||||
**DroidSans.ttf**, by Steve Matteson
|
||||
<br>Apache License 2.0
|
||||
<br>https://www.fontsquirrel.com/fonts/droid-sans
|
||||
|
||||
**ProggyClean.ttf**, by Tristan Grimmer
|
||||
<br>MIT License
|
||||
<br>(recommended loading setting: Size = 13.0, GlyphOffset.y = +1)
|
||||
<br>http://www.proggyfonts.net/
|
||||
|
||||
**ProggyTiny.ttf**, by Tristan Grimmer
|
||||
<br>MIT License
|
||||
<br>(recommended loading setting: Size = 10.0, GlyphOffset.y = +1)
|
||||
<br>http://www.proggyfonts.net/
|
||||
|
||||
**Karla-Regular.ttf**, by Jonathan Pinhorn
|
||||
<br>SIL OPEN FONT LICENSE Version 1.1
|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
## Font Links
|
||||
|
||||
#### ICON FONTS
|
||||
|
||||
- C/C++ header for icon fonts (#define with code points to use in source code string literals) https://github.com/juliettef/IconFontCppHeaders
|
||||
- FontAwesome https://fortawesome.github.io/Font-Awesome
|
||||
- OpenFontIcons https://github.com/traverseda/OpenFontIcons
|
||||
- Google Icon Fonts https://design.google.com/icons/
|
||||
- Kenney Icon Font (Game Controller Icons) https://github.com/nicodinh/kenney-icon-font
|
||||
- IcoMoon - Custom Icon font builder https://icomoon.io/app
|
||||
|
||||
#### REGULAR FONTS
|
||||
|
||||
- Google Noto Fonts (worldwide languages) https://www.google.com/get/noto/
|
||||
- Open Sans Fonts https://fonts.google.com/specimen/Open+Sans
|
||||
- (Japanese) M+ fonts by Coji Morishita http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/index-en.html
|
||||
|
||||
#### MONOSPACE FONTS
|
||||
|
||||
Pixel Perfect:
|
||||
- Proggy Fonts, by Tristan Grimmer http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php
|
||||
- Sweet16, Sweet16 Mono, by Martin Sedlak (Latin + Supplemental + Extended A) https://github.com/kmar/Sweet16Font (also include an .inl file to use directly in dear imgui.)
|
||||
|
||||
Regular:
|
||||
- Google Noto Mono Fonts https://www.google.com/get/noto/
|
||||
- Typefaces for source code beautification https://github.com/chrissimpkins/codeface
|
||||
- Programmation fonts http://s9w.github.io/font_compare/
|
||||
- Inconsolata http://www.levien.com/type/myfonts/inconsolata.html
|
||||
- Adobe Source Code Pro: Monospaced font family for ui & coding environments https://github.com/adobe-fonts/source-code-pro
|
||||
- Monospace/Fixed Width Programmer's Fonts http://www.lowing.org/fonts/
|
||||
|
||||
Or use Arial Unicode or other Unicode fonts provided with Windows for full characters coverage (not sure of their licensing).
|
||||
|
||||
##### [Return to Index](#index)
|
222
Source/ThirdParty/ImGuiLibrary/Docs/README.md
vendored
Normal file
222
Source/ThirdParty/ImGuiLibrary/Docs/README.md
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
Dear ImGui
|
||||
=====
|
||||
|
||||
<center><b><i>"Give someone state and they'll have a bug one day, but teach them how to represent state in two separate locations that have to be kept in sync and they'll have bugs for a lifetime."</i></b></center> <a href="https://twitter.com/rygorous/status/1507178315886444544">-ryg</a>
|
||||
|
||||
----
|
||||
|
||||
[](https://github.com/ocornut/imgui/actions?workflow=build) [](https://github.com/ocornut/imgui/actions?workflow=static-analysis) [](https://github.com/ocornut/imgui_test_engine/actions?workflow=tests)
|
||||
|
||||
<sub>(This library is available under a free and permissive license, but needs financial support to sustain its continued improvements. In addition to maintenance and stability there are many desirable features yet to be added. If your company is using Dear ImGui, please consider reaching out.)</sub>
|
||||
|
||||
Businesses: support continued development and maintenance via invoiced sponsoring/support contracts:
|
||||
<br> _E-mail: contact @ dearimgui dot com_
|
||||
<br>Individuals: support continued development and maintenance [here](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WGHNC6MBFLZ2S). Also see [Funding](https://github.com/ocornut/imgui/wiki/Funding) page.
|
||||
|
||||
| [The Pitch](#the-pitch) - [Usage](#usage) - [How it works](#how-it-works) - [Releases & Changelogs](#releases--changelogs) - [Demo](#demo) - [Getting Started & Integration](#getting-started--integration) |
|
||||
:----------------------------------------------------------: |
|
||||
| [Gallery](#gallery) - [Support, FAQ](#support-frequently-asked-questions-faq) - [How to help](#how-to-help) - **[Funding & Sponsors](https://github.com/ocornut/imgui/wiki/Funding)** - [Credits](#credits) - [License](#license) |
|
||||
| [Wiki](https://github.com/ocornut/imgui/wiki) - [Extensions](https://github.com/ocornut/imgui/wiki/Useful-Extensions) - [Languages bindings & frameworks backends](https://github.com/ocornut/imgui/wiki/Bindings) - [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) - [User quotes](https://github.com/ocornut/imgui/wiki/Quotes) |
|
||||
|
||||
### The Pitch
|
||||
|
||||
Dear ImGui is a **bloat-free graphical user interface library for C++**. It outputs optimized vertex buffers that you can render anytime in your 3D-pipeline-enabled application. It is fast, portable, renderer agnostic, and self-contained (no external dependencies).
|
||||
|
||||
Dear ImGui is designed to **enable fast iterations** and to **empower programmers** to create **content creation tools and visualization / debug tools** (as opposed to UI for the average end-user). It favors simplicity and productivity toward this goal and lacks certain features commonly found in more high-level libraries. Among other things, full internationalization (right-to-left text, bidirectional text, text shaping etc.) and accessibility features are not supported.
|
||||
|
||||
Dear ImGui is particularly suited to integration in game engines (for tooling), real-time 3D applications, fullscreen applications, embedded applications, or any applications on console platforms where operating system features are non-standard.
|
||||
|
||||
- Minimize state synchronization.
|
||||
- Minimize UI-related state storage on user side.
|
||||
- Minimize setup and maintenance.
|
||||
- Easy to use to create dynamic UI which are the reflection of a dynamic data set.
|
||||
- Easy to use to create code-driven and data-driven tools.
|
||||
- Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
|
||||
- Easy to hack and improve.
|
||||
- Portable, minimize dependencies, run on target (consoles, phones, etc.).
|
||||
- Efficient runtime and memory consumption.
|
||||
- Battle-tested, used by [many major actors in the game industry](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui).
|
||||
|
||||
### Usage
|
||||
|
||||
**The core of Dear ImGui is self-contained within a few platform-agnostic files** which you can easily compile in your application/engine. They are all the files in the root folder of the repository (imgui*.cpp, imgui*.h). **No specific build process is required**. You can add the .cpp files into your existing project.
|
||||
|
||||
**Backends for a variety of graphics API and rendering platforms** are provided in the [backends/](https://github.com/ocornut/imgui/tree/master/backends) folder, along with example applications in the [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder. You may also create your own backend. Anywhere where you can render textured triangles, you can render Dear ImGui.
|
||||
|
||||
See the [Getting Started & Integration](#getting-started--integration) section of this document for more details.
|
||||
|
||||
After Dear ImGui is set up in your application, you can use it from \_anywhere\_ in your program loop:
|
||||
```cpp
|
||||
ImGui::Text("Hello, world %d", 123);
|
||||
if (ImGui::Button("Save"))
|
||||
MySaveFunction();
|
||||
ImGui::InputText("string", buf, IM_ARRAYSIZE(buf));
|
||||
ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
|
||||
```
|
||||

|
||||

|
||||
|
||||
```cpp
|
||||
// Create a window called "My First Tool", with a menu bar.
|
||||
ImGui::Begin("My First Tool", &my_tool_active, ImGuiWindowFlags_MenuBar);
|
||||
if (ImGui::BeginMenuBar())
|
||||
{
|
||||
if (ImGui::BeginMenu("File"))
|
||||
{
|
||||
if (ImGui::MenuItem("Open..", "Ctrl+O")) { /* Do stuff */ }
|
||||
if (ImGui::MenuItem("Save", "Ctrl+S")) { /* Do stuff */ }
|
||||
if (ImGui::MenuItem("Close", "Ctrl+W")) { my_tool_active = false; }
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
// Edit a color stored as 4 floats
|
||||
ImGui::ColorEdit4("Color", my_color);
|
||||
|
||||
// Generate samples and plot them
|
||||
float samples[100];
|
||||
for (int n = 0; n < 100; n++)
|
||||
samples[n] = sinf(n * 0.2f + ImGui::GetTime() * 1.5f);
|
||||
ImGui::PlotLines("Samples", samples, 100);
|
||||
|
||||
// Display contents in a scrolling region
|
||||
ImGui::TextColored(ImVec4(1,1,0,1), "Important Stuff");
|
||||
ImGui::BeginChild("Scrolling");
|
||||
for (int n = 0; n < 50; n++)
|
||||
ImGui::Text("%04d: Some text", n);
|
||||
ImGui::EndChild();
|
||||
ImGui::End();
|
||||
```
|
||||

|
||||
|
||||
Dear ImGui allows you to **create elaborate tools** as well as very short-lived ones. On the extreme side of short-livedness: using the Edit&Continue (hot code reload) feature of modern compilers you can add a few widgets to tweak variables while your application is running, and remove the code a minute later! Dear ImGui is not just for tweaking values. You can use it to trace a running algorithm by just emitting text commands. You can use it along with your own reflection data to browse your dataset live. You can use it to expose the internals of a subsystem in your engine, to create a logger, an inspection tool, a profiler, a debugger, an entire game-making editor/framework, etc.
|
||||
|
||||
### How it works
|
||||
|
||||
The IMGUI paradigm through its API tries to minimize superfluous state duplication, state synchronization, and state retention from the user's point of view. It is less error-prone (less code and fewer bugs) than traditional retained-mode interfaces, and lends itself to creating dynamic user interfaces. Check out the Wiki's [About the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm) section for more details.
|
||||
|
||||
Dear ImGui outputs vertex buffers and command lists that you can easily render in your application. The number of draw calls and state changes required to render them is fairly small. Because Dear ImGui doesn't know or touch graphics state directly, you can call its functions anywhere in your code (e.g. in the middle of a running algorithm, or in the middle of your own rendering process). Refer to the sample applications in the examples/ folder for instructions on how to integrate Dear ImGui with your existing codebase.
|
||||
|
||||
_A common misunderstanding is to mistake immediate mode GUI for immediate mode rendering, which usually implies hammering your driver/GPU with a bunch of inefficient draw calls and state changes as the GUI functions are called. This is NOT what Dear ImGui does. Dear ImGui outputs vertex buffers and a small list of draw calls batches. It never touches your GPU directly. The draw call batches are decently optimal and you can render them later, in your app or even remotely._
|
||||
|
||||
### Releases & Changelogs
|
||||
|
||||
See [Releases](https://github.com/ocornut/imgui/releases) page for decorated Changelogs.
|
||||
Reading the changelogs is a good way to keep up to date with the things Dear ImGui has to offer, and maybe will give you ideas of some features that you've been ignoring until now!
|
||||
|
||||
### Demo
|
||||
|
||||
Calling the `ImGui::ShowDemoWindow()` function will create a demo window showcasing a variety of features and examples. The code is always available for reference in `imgui_demo.cpp`. [Here's how the demo looks](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v167/v167-misc.png).
|
||||
|
||||
You should be able to build the examples from sources. If you don't, let us know! If you want to have a quick look at some Dear ImGui features, you can download Windows binaries of the demo app here:
|
||||
- [imgui-demo-binaries-20240105.zip](https://www.dearimgui.com/binaries/imgui-demo-binaries-20240105.zip) (Windows, 1.90.1 WIP, built 2024/01/05, master) or [older binaries](https://www.dearimgui.com/binaries).
|
||||
|
||||
The demo applications are not DPI aware so expect some blurriness on a 4K screen. For DPI awareness in your application, you can load/reload your font at a different scale and scale your style with `style.ScaleAllSizes()` (see [FAQ](https://www.dearimgui.com/faq)).
|
||||
|
||||
### Getting Started & Integration
|
||||
|
||||
See the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide for details.
|
||||
|
||||
On most platforms and when using C++, **you should be able to use a combination of the [imgui_impl_xxxx](https://github.com/ocornut/imgui/tree/master/backends) backends without modification** (e.g. `imgui_impl_win32.cpp` + `imgui_impl_dx11.cpp`). If your engine supports multiple platforms, consider using more imgui_impl_xxxx files instead of rewriting them: this will be less work for you, and you can get Dear ImGui running immediately. You can _later_ decide to rewrite a custom backend using your custom engine functions if you wish so.
|
||||
|
||||
Integrating Dear ImGui within your custom engine is a matter of 1) wiring mouse/keyboard/gamepad inputs 2) uploading a texture to your GPU/render engine 3) providing a render function that can bind textures and render textured triangles, which is essentially what Backends are doing. The [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder is populated with applications doing just that: setting up a window and using backends. If you follow the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide it should in theory takes you less than an hour to integrate Dear ImGui. **Make sure to spend time reading the [FAQ](https://www.dearimgui.com/faq), comments, and the examples applications!**
|
||||
|
||||
Officially maintained backends/bindings (in repository):
|
||||
- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, SDL_Renderer, Vulkan, WebGPU.
|
||||
- Platforms: GLFW, SDL2/SDL3, Win32, Glut, OSX, Android.
|
||||
- Frameworks: Allegro5, Emscripten.
|
||||
|
||||
[Third-party backends/bindings](https://github.com/ocornut/imgui/wiki/Bindings) wiki page:
|
||||
- Languages: C, C# and: Beef, ChaiScript, CovScript, Crystal, D, Go, Haskell, Haxe/hxcpp, Java, JavaScript, Julia, Kotlin, Lobster, Lua, Nim, Odin, Pascal, PureBasic, Python, ReaScript, Ruby, Rust, Swift, Zig...
|
||||
- Frameworks: AGS/Adventure Game Studio, Amethyst, Blender, bsf, Cinder, Cocos2d-x, Defold, Diligent Engine, Ebiten, Flexium, GML/Game Maker Studio, GLEQ, Godot, GTK3, Irrlicht Engine, JUCE, LÖVE+LUA, Mach Engine, Magnum, Marmalade, Monogame, NanoRT, nCine, Nim Game Lib, Nintendo 3DS/Switch/WiiU (homebrew), Ogre, openFrameworks, OSG/OpenSceneGraph, Orx, Photoshop, px_render, Qt/QtDirect3D, raylib, SFML, Sokol, Unity, Unreal Engine 4/5, UWP, vtk, VulkanHpp, VulkanSceneGraph, Win32 GDI, WxWidgets.
|
||||
- Many bindings are auto-generated (by good old [cimgui](https://github.com/cimgui/cimgui) or newer/experimental [dear_bindings](https://github.com/dearimgui/dear_bindings)), you can use their metadata output to generate bindings for other languages.
|
||||
|
||||
[Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page:
|
||||
- Automation/testing, Text editors, node editors, timeline editors, plotting, software renderers, remote network access, memory editors, gizmos, etc. Notable and well supported extensions include [ImPlot](https://github.com/epezent/implot) and [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine).
|
||||
|
||||
Also see [Wiki](https://github.com/ocornut/imgui/wiki) for more links and ideas.
|
||||
|
||||
### Gallery
|
||||
|
||||
Examples projects using Dear ImGui: [Tracy](https://github.com/wolfpld/tracy) (profiler), [ImHex](https://github.com/WerWolv/ImHex) (hex editor/data analysis), [RemedyBG](https://remedybg.itch.io/remedybg) (debugger) and [hundreds of others](https://github.com/ocornut/imgui/wiki/Software-using-Dear-ImGui).
|
||||
|
||||
For more user-submitted screenshots of projects using Dear ImGui, check out the [Gallery Threads](https://github.com/ocornut/imgui/issues?q=label%3Agallery)!
|
||||
|
||||
For a list of third-party widgets and extensions, check out the [Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page.
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Custom engine [erhe](https://github.com/tksuoran/erhe) (docking branch)<BR>[](https://user-images.githubusercontent.com/994606/147875067-a848991e-2ad2-4fd3-bf71-4aeb8a547bcf.png) | Custom engine for [Wonder Boy: The Dragon's Trap](http://www.TheDragonsTrap.com) (2017)<BR>[](https://cloud.githubusercontent.com/assets/8225057/20628927/33e14cac-b329-11e6-80f6-9524e93b048a.png) |
|
||||
| Custom engine (untitled)<BR>[](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v160/editor_white.png) | Tracy Profiler ([github](https://github.com/wolfpld/tracy))<BR>[](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v176/tracy_profiler.png) |
|
||||
|
||||
### Support, Frequently Asked Questions (FAQ)
|
||||
|
||||
See: [Frequently Asked Questions (FAQ)](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md) where common questions are answered.
|
||||
|
||||
See: [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) and [Wiki](https://github.com/ocornut/imgui/wiki) for many links, references, articles.
|
||||
|
||||
See: [Articles about the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm) to read/learn about the Immediate Mode GUI paradigm.
|
||||
|
||||
See: [Upcoming Changes](https://github.com/ocornut/imgui/wiki/Upcoming-Changes).
|
||||
|
||||
See: [Dear ImGui Test Engine + Test Suite](https://github.com/ocornut/imgui_test_engine) for Automation & Testing.
|
||||
|
||||
For the purposes of getting search engines to crawl the wiki, here's a link to the [Crawlable Wiki](https://github-wiki-see.page/m/ocornut/imgui/wiki) (not for humans, [here's why](https://github-wiki-see.page/)).
|
||||
|
||||
Getting started? For first-time users having issues compiling/linking/running or issues loading fonts, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions). For ANY other questions, bug reports, requests, feedback, please post on [GitHub Issues](https://github.com/ocornut/imgui/issues). Please read and fill the New Issue template carefully.
|
||||
|
||||
Private support is available for paying business customers (E-mail: _contact @ dearimgui dot com_).
|
||||
|
||||
**Which version should I get?**
|
||||
|
||||
We occasionally tag [Releases](https://github.com/ocornut/imgui/releases) (with nice releases notes) but it is generally safe and recommended to sync to latest `master` or `docking` branch. The library is fairly stable and regressions tend to be fixed fast when reported. Advanced users may want to use the `docking` branch with [Multi-Viewport](https://github.com/ocornut/imgui/wiki/Multi-Viewports) and [Docking](https://github.com/ocornut/imgui/wiki/Docking) features. This branch is kept in sync with master regularly.
|
||||
|
||||
**Who uses Dear ImGui?**
|
||||
|
||||
See the [Quotes](https://github.com/ocornut/imgui/wiki/Quotes), [Funding & Sponsors](https://github.com/ocornut/imgui/wiki/Funding), and [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) Wiki pages for an idea of who is using Dear ImGui. Please add your game/software if you can! Also, see the [Gallery Threads](https://github.com/ocornut/imgui/issues?q=label%3Agallery)!
|
||||
|
||||
How to help
|
||||
-----------
|
||||
|
||||
**How can I help?**
|
||||
|
||||
- See [GitHub Forum/Issues](https://github.com/ocornut/imgui/issues).
|
||||
- You may help with development and submit pull requests! Please understand that by submitting a PR you are also submitting a request for the maintainer to review your code and then take over its maintenance forever. PR should be crafted both in the interest of the end-users and also to ease the maintainer into understanding and accepting it.
|
||||
- See [Help wanted](https://github.com/ocornut/imgui/wiki/Help-Wanted) on the [Wiki](https://github.com/ocornut/imgui/wiki/) for some more ideas.
|
||||
- Be a [Funding Supporter](https://github.com/ocornut/imgui/wiki/Funding)! Have your company financially support this project via invoiced sponsors/maintenance or by buying a license for [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine) (please reach out: omar AT dearimgui DOT com).
|
||||
|
||||
Sponsors
|
||||
--------
|
||||
|
||||
Ongoing Dear ImGui development is and has been financially supported by users and private sponsors.
|
||||
<BR>Please see the **[detailed list of current and past Dear ImGui funding supporters and sponsors](https://github.com/ocornut/imgui/wiki/Funding)** for details.
|
||||
<BR>From November 2014 to December 2019, ongoing development has also been financially supported by its users on Patreon and through individual donations.
|
||||
|
||||
**THANK YOU to all past and present supporters for helping to keep this project alive and thriving!**
|
||||
|
||||
Dear ImGui is using software and services provided free of charge for open source projects:
|
||||
- [PVS-Studio](https://pvs-studio.com/en/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) for static analysis (supports C/C++/C#/Java).
|
||||
- [GitHub actions](https://github.com/features/actions) for continuous integration systems.
|
||||
- [OpenCppCoverage](https://github.com/OpenCppCoverage/OpenCppCoverage) for code coverage analysis.
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
Developed by [Omar Cornut](https://www.miracleworld.net) and every direct or indirect [contributors](https://github.com/ocornut/imgui/graphs/contributors) to the GitHub. The early version of this library was developed with the support of [Media Molecule](https://www.mediamolecule.com) and first used internally on the game [Tearaway](https://tearaway.mediamolecule.com) (PS Vita).
|
||||
|
||||
Recurring contributors include Rokas Kupstys [@rokups](https://github.com/rokups) (2020-2022): a good portion of work on automation system and regression tests now available in [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine).
|
||||
|
||||
Maintenance/support contracts, sponsoring invoices and other B2B transactions are hosted and handled by [Disco Hello](https://www.discohello.com).
|
||||
|
||||
Omar: "I first discovered the IMGUI paradigm at [Q-Games](https://www.q-games.com) where Atman Binstock had dropped his own simple implementation in the codebase, which I spent quite some time improving and thinking about. It turned out that Atman was exposed to the concept directly by working with Casey. When I moved to Media Molecule I rewrote a new library trying to overcome the flaws and limitations of the first one I've worked with. It became this library and since then I have spent an unreasonable amount of time iterating and improving it."
|
||||
|
||||
Embeds [ProggyClean.ttf](https://www.proggyfonts.net) font by Tristan Grimmer (MIT license).
|
||||
<br>Embeds [stb_textedit.h, stb_truetype.h, stb_rect_pack.h](https://github.com/nothings/stb/) by Sean Barrett (public domain).
|
||||
|
||||
Inspiration, feedback, and testing for early versions: Casey Muratori, Atman Binstock, Mikko Mononen, Emmanuel Briney, Stefan Kamoda, Anton Mikhailov, Matt Willis. Also thank you to everyone posting feedback, questions and patches on GitHub.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Dear ImGui is licensed under the MIT License, see [LICENSE.txt](https://github.com/ocornut/imgui/blob/master/LICENSE.txt) for more information.
|
383
Source/ThirdParty/ImGuiLibrary/Docs/TODO.txt
vendored
Normal file
383
Source/ThirdParty/ImGuiLibrary/Docs/TODO.txt
vendored
Normal file
@ -0,0 +1,383 @@
|
||||
dear imgui
|
||||
ISSUES & TODO LIST
|
||||
|
||||
Issue numbers (#) refer to GitHub issues listed at https://github.com/ocornut/imgui/issues/XXXX
|
||||
THIS LIST IS NOT WELL MAINTAINED. MOST OF THE WORK HAPPENS ON GITHUB NOWADAYS.
|
||||
The list below consist mostly of ideas noted down before they are requested/discussed by users (at which point they usually exist on the github issue tracker).
|
||||
It's mostly a bunch of personal notes, probably incomplete. Feel free to query if you have any questions.
|
||||
|
||||
- doc: add a proper documentation system (maybe relying on automation? #435)
|
||||
- doc: checklist app to verify backends/integration of imgui (test inputs, rendering, callback, etc.).
|
||||
- doc/tips: tips of the day: website? applet in imgui_club?
|
||||
|
||||
- window: preserve/restore relative focus ordering (persistent or not), and e.g. of multiple reappearing windows (#2304) -> also see docking reference to same #.
|
||||
- window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis). (#690)
|
||||
- window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. perhaps a lightweight explicit cleanup pass.
|
||||
- window: auto-fit feedback loop when user relies on any dynamic layout (window width multiplier, column) appears weird to end-user. clarify.
|
||||
- window: begin with *p_open == false could return false.
|
||||
- window: get size/pos helpers given names (see discussion in #249)
|
||||
- window: when window is very small, prioritize resize button over close button.
|
||||
- window: double-clicking on title bar to minimize isn't consistent interaction, perhaps move to single-click on left-most collapse icon?
|
||||
- window: expose contents size. (#1045)
|
||||
- window: using SetWindowPos() inside Begin() and moving the window with the mouse reacts a very ugly glitch. We should just defer the SetWindowPos() call.
|
||||
- window: GetWindowSize() returns (0,0) when not calculated? (#1045)
|
||||
- window: investigate better auto-positioning for new windows.
|
||||
- window: top most window flag? more z-order contrl? (#2574)
|
||||
- window/size: manually triggered auto-fit (double-click on grip) shouldn't resize window down to viewport size?
|
||||
- window/size: how to allow to e.g. auto-size vertically to fit contents, but be horizontally resizable? Assuming SetNextWindowSize() is modified to treat -1.0f on each axis as "keep as-is" (would be good but might break erroneous code): Problem is UpdateWindowManualResize() and lots of code treat (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) together.
|
||||
- window/opt: freeze window flag: if not focused/hovered, return false, render with previous ImDrawList. and/or reduce refresh rate. -> this may require enforcing that it is illegal to submit contents if Begin returns false.
|
||||
- window/child: background options for child windows, border option (disable rounding).
|
||||
- window/child: allow resizing of child windows (possibly given min/max for each axis?.)
|
||||
- window/child: allow SetNextWindowContentSize() to work on child windows.
|
||||
- window/clipping: some form of clipping when DisplaySize (or corresponding viewport) is zero.
|
||||
- window/tabbing: add a way to signify that a window or docked window requires attention (e.g. blinking title bar, trying to click behind a modal).
|
||||
- window/id_stack: add e.g. window->GetIDFromPath() with support for leading / and ../ (#1390, #331) -> model from test engine.
|
||||
! scrolling: exposing horizontal scrolling with Shift+Wheel even when scrollbar is disabled expose lots of issues (#2424, #1463)
|
||||
- scrolling: while holding down a scrollbar, try to keep the same contents visible (at least while not moving mouse)
|
||||
- scrolling: allow immediately effective change of scroll after Begin() if we haven't appended items yet.
|
||||
- scrolling: forward mouse wheel scrolling to parent window when at the edge of scrolling limits? (useful for listbox,tables?)
|
||||
- scrolling/style: shadows on scrollable areas to denote that there is more contents (see e.g. DaVinci Resolve ui)
|
||||
|
||||
- drawdata: make it easy to deep-copy (or swap?) a full ImDrawData so user can easily save that data if they use threaded rendering. (#1860 see ImDrawDataSnapshot)
|
||||
! drawlist: add CalcTextSize() func to facilitate consistent code from user pov (currently need to use ImGui or ImFont alternatives!)
|
||||
- drawlist: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command). (WIP branch)
|
||||
- drawlist: make it easier to toggle AA per primitive, so we can use e.g. non-AA fill + AA borders more naturally
|
||||
- drawlist: non-AA strokes have gaps between points (#593, #288), glitch especially on RenderCheckmark() and ColorPicker4().
|
||||
- drawlist: callback: add an extra void* in ImDrawCallback to expose render state instead of pulling from Renderer_RenderState (would break API).
|
||||
- drawlist: AddRect vs AddLine position confusing (#2441)
|
||||
- drawlist/opt: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding. (#1962)
|
||||
- drawlist/opt: AddRect() axis aligned pixel aligned (no-aa) could use 8 triangles instead of 16 and no normal calculation.
|
||||
- drawlist/opt: thick AA line could be doable in same number of triangles as 1.0 AA line by storing gradient+full color in atlas.
|
||||
|
||||
- items: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode?
|
||||
|
||||
- widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc. (#395)
|
||||
- widgets: clean up widgets internal toward exposing everything and stabilizing imgui_internals.h.
|
||||
- widgets: add always-allow-overlap mode. This should perhaps be the default? one problem is that highlight after mouse-wheel scrolling gets deferred, makes scrolling more flickery.
|
||||
- widgets: start exposing PushItemFlag() and ImGuiItemFlags
|
||||
- widgets: alignment options in style (e.g. center Selectable, Right-Align within Button, etc.) #1260
|
||||
- widgets: activate by identifier (trigger button, focus given id)
|
||||
- widgets: custom glyph/shapes replacements for stock sapes. (also #6090 #2431 #2235 #6517)
|
||||
- widgets: coloredit: keep reporting as active when picker is on?
|
||||
- widgets: group/scalarn functions: expose more per-component information. e.g. store NextItemData.ComponentIdx set by scalarn function, groups can expose them back somehow.
|
||||
- selectable: using (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported.
|
||||
- selectable: generic BeginSelectable()/EndSelectable() mechanism. (work out alongside range-select branch)
|
||||
- selectable: a way to visualize partial/mixed selection (e.g. parent tree node has children with mixed selection)
|
||||
|
||||
- input text: preserve scrolling when unfocused?
|
||||
- input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541)
|
||||
- input text: expose CursorPos in char filter event (#816)
|
||||
- input text: try usage idiom of using InputText with data only exposed through get/set accessors, without extraneous copy/alloc. (#3009)
|
||||
- input text: access public fields via a non-callback API e.g. InputTextGetState("xxx") that may return nullptr if not active (available in internals)
|
||||
- input text: flag to disable live update of the user buffer (also applies to float/int text input) (#701)
|
||||
- input text: hover tooltip could show unclamped text
|
||||
- input text: support for INSERT key to toggle overwrite mode. currently disabled because stb_textedit behavior is unsatisfactory on multi-line. (#2863)
|
||||
- input text: option to Tab after an Enter validation.
|
||||
- input text: add ImGuiInputTextFlags_EnterToApply? (off #218)
|
||||
- input text: easier ways to update buffer (from source char*) while owned. preserve some sort of cursor position for multi-line text.
|
||||
- input text: add discard flag (e.g. ImGuiInputTextFlags_DiscardActiveBuffer) or make it easier to clear active focus for text replacement during edition (#725)
|
||||
- input text: display bug when clicking a drag/slider after an input text in a different window has all-selected text (order dependent). actually a very old bug but no one appears to have noticed it.
|
||||
- input text: allow centering/positioning text so that ctrl+clicking Drag or Slider keeps the textual value at the same pixel position.
|
||||
- input text: decorrelate display layout from inputs with custom template - e.g. what's the easiest way to implement a nice IP/Mac address input editor?
|
||||
- input text: global callback system so user can plug in an expression evaluator easily. (#1691)
|
||||
- input text: force scroll to end or scroll to a given line/contents (so user can implement a log or a search feature)
|
||||
- input text: a way to preview completion (e.g. disabled text completing from the cursor)
|
||||
- input text: a side bar that could e.g. preview where errors are. probably left to the user to draw but we'd need to give them the info there.
|
||||
- input text: a way for the user to provide syntax coloring.
|
||||
- input text: Shift+TAB with ImGuiInputTextFlags_AllowTabInput could eat preceding blanks, up to tab_count.
|
||||
- input text multi-line: don't directly call AddText() which does an unnecessary vertex reserve for character count prior to clipping. and/or more line-based clipping to AddText(). and/or reorganize TextUnformatted/RenderText for more efficiency for large text (e.g TextUnformatted could clip and log separately, etc).
|
||||
- input text multi-line: support for copy/cut without selection (copy/cut current line?)
|
||||
- input text multi-line: line numbers? status bar? (follow up on #200)
|
||||
- input text multi-line: behave better when user changes input buffer while editing is active (even though it is illegal behavior). namely, the change of buffer can create a scrollbar glitch (#725)
|
||||
- input text multi-line: better horizontal scrolling support (#383, #1224)
|
||||
- input text multi-line: single call to AddText() should be coarse clipped on InputTextEx() end.
|
||||
- input number: optional range min/max for Input*() functions
|
||||
- input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled)
|
||||
- input number: use mouse wheel to step up/down
|
||||
|
||||
- layout: helper or a way to express ImGui::SameLine(ImGui::GetCursorStartPos().x + ImGui::CalcItemWidth() + ImGui::GetStyle().ItemInnerSpacing.x); in a simpler manner.
|
||||
- layout, font: horizontal tab support, A) text mode: forward only tabs (e.g. every 4 characters/N pixels from pos x1), B) manual mode: explicit tab stops acting as mini columns, no clipping (for menu items, many kind of uses, also vaguely relate to #267, #395)
|
||||
- layout: horizontal layout helper (#97)
|
||||
- layout: horizontal flow until no space left (#404)
|
||||
- layout: more generic alignment state (left/right/centered) for single items?
|
||||
- layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding.
|
||||
- layout: vertical alignment of mixed height items (e.g. buttons) within a same line (#1284)
|
||||
- layout: null layout mode were items are not rendered but user can query GetItemRectMin()/Max/Size.
|
||||
- layout: (R&D) local multi-pass layout mode.
|
||||
- layout: (R&D) bind authored layout data (created by an off-line tool), items fetch their pos/size at submission, self-optimize data structures to stable linear access.
|
||||
|
||||
- tables: see https://github.com/ocornut/imgui/issues/2957#issuecomment-569726095
|
||||
|
||||
- group: BeginGroup() needs a border option. (~#1496)
|
||||
- group: IsItemHovered() after EndGroup() covers whole AABB rather than the intersection of individual items. Is that desirable?
|
||||
- group: merge deactivation/activation within same group (fwd WasEdited flag). (#2550)
|
||||
|
||||
!- color: the color conversion helpers/types are a mess and needs sorting out.
|
||||
- color: (api breaking) ImGui::ColorConvertXXX functions should be loose ImColorConvertXX to match imgui_internals.h
|
||||
|
||||
- plot: full featured plot/graph api w/ scrolling, zooming etc. --> promote using ImPlot
|
||||
- (plot: deleted all other todo lines on 2023-06-28)
|
||||
|
||||
- clipper: ability to disable the clipping through a simple flag/bool.
|
||||
- clipper: ability to run without knowing full count in advance.
|
||||
- clipper: horizontal clipping support. (#2580)
|
||||
|
||||
- separator: expose flags (#759)
|
||||
- separator: take indent into consideration (optional)
|
||||
- separator: width, thickness, centering (#1643, #2657)
|
||||
- splitter: formalize the splitter idiom into an official api (we want to handle n-way split) (#319)
|
||||
|
||||
- docking: B: ordering currently held in tab bar should be implicitly held by windows themselves (also see #2304)
|
||||
- docking: B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8)
|
||||
- docking: B~ rework code to be able to lazily create tab bar instance in a single place. The _Unsorted tab flag could be replacing a trailing-counter in DockNode?
|
||||
- docking: B~ fully track windows/settings reference in dock nodes. perhaps find a representation that allows facilitate use of dock builder functions.
|
||||
- docking: B~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete). this is mostly working but the DockBuilderXXX api are not exposed/finished.
|
||||
- docking: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized.
|
||||
- docking: B~ central node resizing behavior incorrect.
|
||||
- docking: B: changing title font/style per-window is not supported as dock nodes are created in NewFrame.
|
||||
- docking: B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them (doesn't affect the user nor request platform windows to be created, but unnecessary)
|
||||
- docking: B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level?
|
||||
- docking: B- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows != NULL) should show a normal title bar (not a tab bar)
|
||||
- docking: B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All
|
||||
- docking: B~ tidy up tab list popup buttons features (available with manual tab-bar, see ImGuiTabBarFlags_NoTabListPopupButton code, not used by docking nodes)
|
||||
- docking: B- SetNextWindowDockId(0) with a second Begin() in the frame will asserts
|
||||
- docking: B: resize grip drawn in host window typically appears under scrollbar.
|
||||
- docking: B: resize grip auto-resize on multiple node hierarchy doesn't make much sense or should be improved?
|
||||
- docking: B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4)
|
||||
- docking: B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.)
|
||||
- docking: B- dpi: look at interaction with the hi-dpi and multi-dpi stuff.
|
||||
- docking: B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar()
|
||||
- docking: B- tab bar: make selected tab always shows its full title?
|
||||
- docking: B- hide close button on single tab bar?
|
||||
- docking: B- nav: design interactions so nav controls can dock/undock
|
||||
- docking: B- dockspace: flag to lock the dock tree and/or sizes (ImGuiDockNodeFlags_Locked?)
|
||||
- docking: B- reintroduce collapsing a floating dock node. also collapsing a docked dock node!
|
||||
- docking: B- allow dragging a non-floating dock node by clicking on the title-bar-looking section (not just the collapse/menu button)
|
||||
- docking: B- option to remember undocked window size? (instead of keeping their docked size) (relate to #2104)
|
||||
- docking: C- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs)
|
||||
- docking: C- after a dock/undock, the Scrollbar Status update in Begin() should use an updated e.g. size_y_for_scrollbars to avoid a 1 frame scrollbar flicker.
|
||||
|
||||
- tabs: "there is currently a problem because TabItem() will try to submit their own tooltip after 0.50 second, and this will have the effect of making your tooltip flicker once." -> tooltip priority work (WIP branch)
|
||||
- tabs: make EndTabBar fail if users doesn't respect BeginTabBar return value, for consistency/future-proofing.
|
||||
- tabs: persistent order/focus in BeginTabBar() api (#261, #351)
|
||||
- tabs: explicit api (even if internal) to cleanly manipulate tab order.
|
||||
|
||||
- image/image button: misalignment on padded/bordered button?
|
||||
- image/image button: parameters are confusing, image() has tint_col,border_col whereas imagebutton() has bg_col/tint_col. Even thou they are different parameters ordering could be more consistent. can we fix that?
|
||||
- slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt()
|
||||
- slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate.
|
||||
- slider: tint background based on value (e.g. v_min -> v_max, or use 0.0f either side of the sign)
|
||||
- slider: relative dragging? + precision dragging
|
||||
- slider: step option (#1183)
|
||||
- slider: style: fill % of the bar instead of positioning a drag.
|
||||
- knob: rotating knob widget (#942)
|
||||
- drag float: support for reversed drags (min > max) (removed is_locked, also see fdc526e)
|
||||
- drag float: up/down axis
|
||||
- drag float: power != 0.0f with current value being outside the range keeps the value stuck.
|
||||
- drag float: added leeway on edge (e.g. a few invisible steps past the clamp limits)
|
||||
|
||||
- combo: a way/helper to customize the combo preview (#1658) -> experimental BeginComboPreview()
|
||||
- combo/listbox: keyboard control. need InputText-like non-active focus + key handling. considering keyboard for custom listbox (pr #203)
|
||||
- listbox: multiple selection (WIP range-select branch)
|
||||
- listbox: unselect option (#1208)
|
||||
- listbox: make it easier/more natural to implement range-select (need some sort of info/ref about the last clicked/focused item that user can translate to an index?) (WIP range-select branch)
|
||||
- listbox: user may want to initial scroll to focus on the one selected value?
|
||||
- listbox: disable capturing mouse wheel if the listbox has no scrolling. (#1681)
|
||||
- listbox: scrolling should track modified selection.
|
||||
- listbox: future api should allow to enable horizontal scrolling (#2510)
|
||||
|
||||
!- popups/menus: clarify usage of popups id, how MenuItem/Selectable closing parent popups affects the ID, etc. this is quite fishy needs improvement! (#331, #402)
|
||||
- modals: make modal title bar blink when trying to click outside the modal
|
||||
- modals: technically speaking, we could make Begin() with ImGuiWindowFlags_Modal work without involving popup. May help untangle a few things, as modals are more like regular windows than popups.
|
||||
- popups: if the popup functions took explicit ImGuiID it would allow the user to manage the scope of those ID. (#331)
|
||||
- popups: clicking outside (to close popup) and holding shouldn't drag window below.
|
||||
- popups: add variant using global identifier similar to Begin/End (#402)
|
||||
- popups: border options. richer api like BeginChild() perhaps? (#197)
|
||||
- popups/modals: although it is sometimes convenient that popups/modals lifetime is owned by imgui, we could also a bool-owned-by-user api as long as Begin() return value testing is enforced.
|
||||
|
||||
- tooltip: drag and drop with tooltip near monitor edges lose/changes its last direction instead of locking one. The drag and drop tooltip should always follow without changing direction.
|
||||
- tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic.
|
||||
- tooltip: drag tooltip hovering over source widget with IsItemHovered/SetTooltip flickers (WIP branch)
|
||||
- tooltip: tooltip priorities to override a stock tooltip (e.g. shortcut tooltip)
|
||||
|
||||
- status-bar: add a per-window status bar helper similar to what menu-bar does. generalize concept of layer0 rect in window (can make _MenuBar window flag obsolete too).
|
||||
- shortcuts: store multiple keychords in ImGuiKeyChord
|
||||
- shortcuts: Hovered route (lower than Focused, higher than Global)
|
||||
- shortcuts: local-style shortcut api, e.g. parse "&Save"
|
||||
- shortcuts,menus: global-style shortcut api e.g. "Save (CTRL+S)" -> explicit flag for recursing into closed menu
|
||||
- menus: hovering from menu to menu on a menu-bar has 1 frame without any menu, which is a little annoying. ideally zero.
|
||||
- menus: would be nice if the Selectable() supported horizontal alignment (must be given the equivalent of WorkRect.Max.x matching the position of the shortcut column)
|
||||
|
||||
- tree node: add treenode/treepush int variants? not there because (void*) cast from int warns on some platforms/settings?
|
||||
- tree node: try to apply scrolling at time of TreePop() if node was just opened and end of node is past scrolling limits?
|
||||
- tree node / selectable render mismatch which is visible if you use them both next to each other (e.g. cf. property viewer)
|
||||
- tree node: tweak color scheme to distinguish headers from selected tree node (#581)
|
||||
- tree node: leaf/non-leaf highlight mismatch.
|
||||
- tree node/opt: could avoid formatting when clipped (flag assuming we don't care about width/height, assume single line height? format only %s/%c to be able to count height?)
|
||||
|
||||
- settings: write more decent code to allow saving/loading new fields: columns, selected tree nodes?
|
||||
- settings: facilitate extension lazily calling AddSettingsHandler() while running and still getting their data call the ReadXXX handlers immediately.
|
||||
- settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file (#437)
|
||||
- settings/persistence: helpers to make TreeNodeBehavior persist (even during dev!) - may need to store some semantic and/or data type in ImGuiStoragePair
|
||||
|
||||
- style: better default styles. (#707)
|
||||
- style: add a highlighted text color (for headers, etc.)
|
||||
- style: border types: out-screen, in-screen, etc. (#447)
|
||||
- style: add window shadow (fading away from the window. Paint-style calculation of vertices alpha after drawlist would be easier)
|
||||
- style: a concept of "compact style" that the end-user can easily rely on (e.g. PushStyleCompact()?) that maps to other settings? avoid implementing duplicate helpers such as SmallCheckbox(), etc.
|
||||
- style: try to make PushStyleVar() more robust to incorrect parameters (to be more friendly to edit & continues situation).
|
||||
- style: global scale setting.
|
||||
- style: FramePadding could be different for up vs down (#584)
|
||||
- style: WindowPadding needs to be EVEN as the 0.5 multiplier used on this value probably have a subtle effect on clip rectangle
|
||||
- style: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438, #707, #1223)
|
||||
- style: gradients fill (#1223) ~ 2 bg colors for each fill? tricky with rounded shapes and using textures for corners.
|
||||
- style editor: color child window height expressed in multiple of line height.
|
||||
|
||||
- log: improve logging of ArrowButton, ListBox, TabItem
|
||||
- log: carry on indent / tree depth when opening a child window
|
||||
- log: enabling log ends up pushing and growing vertices buffers because we don't distinguish layout vs render clipping
|
||||
- log: have more control over the log scope (e.g. stop logging when leaving current tree node scope)
|
||||
- log: be able to log anything (e.g. right-click on a window/tree-node, shows context menu? log into tty/file/clipboard)
|
||||
- log: let user copy any window content to clipboard easily (CTRL+C on windows? while moving it? context menu?). code is commented because it fails with multiple Begin/End pairs.
|
||||
- log: obsolete LogButtons().... (was: LogButtons() options for specifying depth and/or hiding depth slider)
|
||||
|
||||
- filters: set a current filter that certains items (e.g. tree node) can automatically query to hide themselves
|
||||
- filters: handle wild-cards (with implicit leading/trailing *), reg-exprs
|
||||
- filters: fuzzy matches (may use code at blog.forrestthewoods.com/4cffeed33fdb)
|
||||
|
||||
- drag and drop: focus drag target window on hold (even without open)
|
||||
- drag and drop: releasing a drop shows the "..." tooltip for one frame - since e13e598 (#1725)
|
||||
- drag and drop: drag source on a group object (would need e.g. an invisible button covering group in EndGroup) https://twitter.com/paniq/status/1121446364909535233
|
||||
- drag and drop: have some way to know when a drag begin from BeginDragDropSource() pov. (see 2018/01/11 post in #143)
|
||||
- drag and drop: allow preview tooltip to be submitted from a different place than the drag source. (#1725)
|
||||
- drag and drop: make it easier and provide a demo to have tooltip both are source and target site, with a more detailed one on target site (tooltip ordering problem)
|
||||
- drag and drop: demo with reordering nodes (in a list, or a tree node). (#143)
|
||||
- drag and drop: test integrating with os drag and drop (make it easy to do a naive WM_DROPFILE integration)
|
||||
- drag and drop: allow for multiple payload types. (#143)
|
||||
- drag and drop: make payload optional? payload promise? (see 2018/01/11 post in #143)
|
||||
- drag and drop: (#143) "both an in-process pointer and a promise to generate a serialized version, for whether the drag ends inside or outside the same process"
|
||||
- drag and drop: feedback when hovering a region blocked by modal (mouse cursor "NO"?)
|
||||
|
||||
- markup: simple markup language for color change? (#902, #3130)
|
||||
|
||||
- text: selectable text (for copy) as a generic feature (ItemFlags?)
|
||||
- text: proper alignment options in imgui_internal.h
|
||||
- text: provided a framed text helper, e.g. https://pastebin.com/1Laxy8bT
|
||||
- text: refactor TextUnformatted (or underlying function) to more explicitly request if we need width measurement or not
|
||||
- text/layout/tabs: \t pulling position from base pos + step, or offset array (e.g. could be used in text edit, menus for simple icon+text alignment, etc.)
|
||||
- text link/url button: underlined. should api expose an ID or use text contents as ID? which colors enum to use?
|
||||
- text/wrapped: should be a more first-class citizen, e.g. wrapped text within a Selectable with known width.
|
||||
- text/wrapped: custom separator for text wrapping. (#3002)
|
||||
- text/wrapped: figure out better way to use TextWrapped() in an always auto-resize context (tooltip, etc.) (#249)
|
||||
|
||||
- font: arbitrary line spacing. (#2945)
|
||||
- font: MergeMode: flags to select overwriting or not (this is now very easy with refactored ImFontAtlasBuildWithStbTruetype)
|
||||
- font: free the Alpha buffer if user only requested RGBA.
|
||||
!- font: better CalcTextSizeA() API, at least for simple use cases. current one is horrible (perhaps have simple vs extended versions).
|
||||
- font: for the purpose of RenderTextEllipsis(), it might be useful that CalcTextSizeA() can ignore the trailing padding?
|
||||
- font: a CalcTextHeight() helper could run faster than CalcTextSize().y
|
||||
- font: enforce monospace through ImFontConfig (for icons?) + create dual ImFont output from same input, reusing rasterized data but with different glyphs/AdvanceX
|
||||
- font: finish CustomRectRegister() to allow mapping Unicode codepoint to custom texture data
|
||||
- font: remove ID from CustomRect registration, it seems unnecessary!
|
||||
- font: make it easier to submit own bitmap font (same texture, another texture?). (#2127, #2575)
|
||||
- font: PushFontSize API (#1018)
|
||||
- font: MemoryTTF taking ownership confusing/not obvious, maybe default should be opposite?
|
||||
- font: storing MinAdvanceX per font would allow us to skip calculating line width (under a threshold of character count) in loops looking for block width
|
||||
- font/demo: add tools to show glyphs used by a text blob, display U16 value, list missing glyphs.
|
||||
- font/demo: demonstrate use of ImFontGlyphRangesBuilder.
|
||||
- font/atlas: add a missing Glyphs.reserve()
|
||||
- font/atlas: incremental updates
|
||||
- font/atlas: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier.
|
||||
- font/draw: vertical and/or rotated text renderer (#705) - vertical is easier clipping wise
|
||||
- font/draw: need to be able to specify wrap start position.
|
||||
- font/draw: better reserve policy for large horizontal block of text (shouldn't reserve for all clipped lines). also see #3349.
|
||||
- font/draw: fix for drawing 16k+ visible characters in same call.
|
||||
- font/draw: underline, squiggle line rendering helpers.
|
||||
- font: optimization: for monospace font (like the default one) we can trim IndexXAdvance as long as trailing value is == FallbackXAdvance (need to make sure TAB is still correct), would save on cache line.
|
||||
- font: add support for kerning, probably optional. A) perhaps default to (32..128)^2 matrix ~ 9K entries = 36KB, then hash for non-ascii?. B) or sparse lookup into per-char list?
|
||||
- font: add a simpler CalcTextSizeA() api? current one ok but not welcome if user needs to call it directly (without going through ImGui::CalcTextSize)
|
||||
- font: fix AddRemapChar() to work before atlas has been built.
|
||||
- font: (api breaking) remove "TTF" from symbol names. also because it now supports OTF.
|
||||
- font/opt: Considering storing standalone AdvanceX table as 16-bit fixed point integer?
|
||||
- font/opt: Glyph currently 40 bytes (2+9*4). Consider storing UV as 16-bits integer? (->32 bytes). X0/Y0/X1/Y1 as 16 fixed-point integers? Or X0/Y0 as float and X1/Y1 as fixed8_8?
|
||||
|
||||
- nav: some features such as PageUp/Down/Home/End should probably work without ImGuiConfigFlags_NavEnableKeyboard? (where do we draw the line? how about CTRL+Tab)
|
||||
- nav: Home/End behavior when navigable item is not fully visible at the edge of scrolling? should be backtrack to keep item into view?
|
||||
- nav: NavScrollToBringItemIntoView() with item bigger than view should focus top-right? Repro: using Nav in "About Window"
|
||||
- nav: expose wrap around flags/logic to allow e.g. grid based layout (pressing NavRight on the right-most element would go to the next row, etc.). see internal's NavMoveRequestTryWrapping().
|
||||
- nav: patterns to make it possible for arrows key to update selection (see JustMovedTo in range_select branch)
|
||||
- nav: restore/find nearest NavId when current one disappear (e.g. pressed a button that disappear, or perhaps auto restoring when current button change name)
|
||||
- nav: SetItemDefaultFocus() level of priority, so widget like Selectable when inside a popup could claim a low-priority default focus on the first selected iem
|
||||
- nav: holding space to repeat a button doesn't show button activated during hold.
|
||||
- nav: NavFlattened: init requests don't work properly on flattened siblings.
|
||||
- nav: NavFlattened: pageup/pagedown/home/end don't work properly on flattened siblings.
|
||||
- nav: NavFlattened: ESC on a flattened child should select something.
|
||||
- nav: NavFlattened: broken: in typical usage scenario, the items of a fully clipped child are currently not considered to enter into a NavFlattened child.
|
||||
- nav: NavFlattened: cannot access menu-bar of a flattened child window with Alt/menu key (not a very common use case..).
|
||||
- nav: simulate right-click or context activation? (SHIFT+F10, keyboard Menu key?)
|
||||
- nav/popup: esc/enter default behavior for popups, e.g. be able to mark an "ok" or "cancel" button that would get triggered by those keys, default validation button, etc.
|
||||
- nav/treenode: left within a tree node block as a fallback (ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default?)
|
||||
- nav/menus: pressing left-right on a vertically clipped menu bar tends to jump to the collapse/close buttons.
|
||||
- nav/menus: allow pressing Menu to leave a sub-menu.
|
||||
- nav/menus: a way to access the main menu bar with Alt? (currently needs CTRL+TAB) or last focused window menu bar?
|
||||
- nav/menus: when using the main menu bar, even though we restore focus after, the underlying window loses its title bar highlight during menu manipulation. could we prevent it?
|
||||
- nav/menus: main menu bar currently cannot restore a nullptr focus. Could save NavWindow at the time of being focused, similarly to what popup do?
|
||||
- nav/menus: Alt,Up could open the first menu (e.g. "File") currently it tends to nav into the window/collapse menu. Do do that we would need custom transition?
|
||||
- nav/windowing: when CTRL+Tab/windowing is active, the HoveredWindow detection doesn't take account of the window display re-ordering.
|
||||
- nav/windowing: Resizing window will currently fail with certain types of resizing constraints/callback applied
|
||||
- focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622)
|
||||
|
||||
- viewport: make it possible to have no main/hosting viewport
|
||||
- viewport: We set ImGuiViewportFlags_NoFocusOnAppearing in a way that is required for GLFW/SDL binding, but could be handled better without
|
||||
on a custom e.g. Win32 bindings. It prevents newly dragged-out viewports from taking the focus, which makes ALT+F4 more ambiguous.
|
||||
- viewport: not focusing newly undocked viewport means clicking back on previous one doesn't bring OS window to front.
|
||||
- viewport: with platform decoration enabled, platform may force constraint (e.g. minimum size)
|
||||
- viewport: use getfocus/setfocus api to synchronize imgui<>platform focus better (e.g imgui-side ctrl-tab can focus os window, OS initial setup and alt-tab can focus imgui window etc.)
|
||||
- viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for.
|
||||
- viewport: implicit/fallback Debug window can hog a zombie viewport (harmless, noisy?) > could at least clear out the reference on a per session basis?
|
||||
- viewport: need to clarify how to use GetMousePos() from a user point of view.
|
||||
- platform: glfw: no support for ImGuiBackendFlags_HasMouseHoveredViewport.
|
||||
- platform: sdl: no support for ImGuiBackendFlags_HasMouseHoveredViewport. maybe we could use SDL_GetMouseFocus() / SDL_WINDOW_MOUSE_FOCUS if imgui could fallback on its heuristic when NoInputs is set
|
||||
- platform: sdl: no refresh of monitor/display (SDL doesn't seem to have an event for it).
|
||||
- platform: sdl: multi-viewport + minimized window seems to break mouse wheel events (at least under Win32).
|
||||
|
||||
- inputs: support track pad style scrolling & slider edit.
|
||||
- inputs/io: backspace and arrows in the context of a text input could use system repeat rate.
|
||||
- inputs/io: clarify/standardize/expose repeat rate and repeat delays (#1808)
|
||||
- inputs/scrolling: support for smooth scrolling (#2462, #2569)
|
||||
|
||||
- misc: idle: expose "woken up" boolean (set by inputs) and/or animation time (for cursor blink) for backend to be able stop refreshing easily.
|
||||
- misc: idle: if cursor blink if the _only_ visible animation, core imgui could rewrite vertex alpha to avoid CPU pass on ImGui:: calls.
|
||||
- misc: idle: if cursor blink if the _only_ visible animation, could even expose a dirty rectangle that optionally can be leverage by some app to render in a smaller viewport, getting rid of much pixel shading cost.
|
||||
- misc: no way to run a root-most GetID() with ImGui:: api since there's always a Debug window in the stack. (mentioned in #2960)
|
||||
- misc: make the ImGuiCond values linear (non-power-of-two). internal storage for ImGuiWindow can use integers to combine into flags (Why?)
|
||||
- misc: PushItemFlag(): add a flag to disable keyboard capture when used with mouse? (#1682)
|
||||
- misc: use more size_t in public api?
|
||||
- misc: support for string view/range instead of char* would e.g. facilitate usage with Rust (#683, #3038, WIP string_view branch)
|
||||
|
||||
- demo: demonstrate using PushStyleVar() in more details.
|
||||
- demo: add vertical separator demo
|
||||
- demo: add virtual scrolling example?
|
||||
- demo: demonstrate Plot offset
|
||||
- demo: window size constraint: square demo is broken when resizing from edges (#1975), would need to rework the callback system to solve this
|
||||
|
||||
- examples: window minimize, maximize (#583)
|
||||
- examples: provide a zero frame-rate/idle example.
|
||||
- examples: dx11/dx12: try to use new swapchain blit models (#2970)
|
||||
- backends: report it better when not able to create texture?
|
||||
- backends: glfw: could go idle when minimized? if (glfwGetWindowAttrib(window, GLFW_ICONIFIED)) { glfwWaitEvents(); continue; } // issue: DeltaTime will be super high on resume, perhaps provide a way to let impl know (#440)
|
||||
- backends: opengl: rename imgui_impl_opengl2 to impl_opengl_legacy and imgui_impl_opengl3 to imgui_impl_opengl? (#1900)
|
||||
- backends: opengl: could use a single vertex buffer and glBufferSubData for uploads?
|
||||
- backends: opengl: explicitly disable GL_STENCIL_TEST in bindings.
|
||||
- backends: vulkan: viewport: support for synchronized swapping of multiple swap chains.
|
||||
- backends: bgfx: https://gist.github.com/RichardGale/6e2b74bc42b3005e08397236e4be0fd0
|
||||
- backends: emscriptem: with refactored examples, we could provide a direct imgui_impl_emscripten platform layer (see eg. https://github.com/floooh/sokol-samples/blob/master/html5/imgui-emsc.cc#L42)
|
||||
|
||||
- bindings: ways to use clang ast dump to generate bindings or helpers for bindings? (e.g. clang++ -Xclang -ast-dump=json imgui.h) (--> use https://github.com/dearimgui/dear_bindings)
|
||||
|
||||
- optimization: replace vsnprintf with stb_printf? using IMGUI_USE_STB_SPRINTF. (#1038 + needed for string_view)
|
||||
- optimization: add clipping for multi-component widgets (SliderFloatX, ColorEditX, etc.). one problem is that nav branch can't easily clip parent group when there is a move request.
|
||||
- optimization: add a flag to disable most of rendering, for the case where the user expect to skip it (#335)
|
||||
- optimization: fully covered window (covered by another with non-translucent bg + WindowRounding worth of padding) may want to clip rendering.
|
||||
- optimization: use another hash function than crc32, e.g. FNV1a
|
||||
- optimization: turn some the various stack vectors into statically-sized arrays
|
144
Source/ThirdParty/ImGuiLibrary/Include/imconfig.h
vendored
144
Source/ThirdParty/ImGuiLibrary/Include/imconfig.h
vendored
@ -1,54 +1,140 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// USER IMPLEMENTATION
|
||||
// This file contains compile-time options for ImGui.
|
||||
// Other options (memory allocation overrides, callbacks, etc.) can be set at runtime via the ImGuiIO structure - ImGui::GetIO().
|
||||
// DEAR IMGUI COMPILE-TIME OPTIONS
|
||||
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||
//-----------------------------------------------------------------------------
|
||||
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
|
||||
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
|
||||
//-----------------------------------------------------------------------------
|
||||
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
||||
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows.
|
||||
//#define IMGUI_API __declspec( dllexport )
|
||||
//#define IMGUI_API __declspec( dllimport )
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
|
||||
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
|
||||
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
|
||||
//---- Don't implement default handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions)
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS
|
||||
|
||||
//---- Don't implement help and test window functionality (ShowUserGuide()/ShowStyleEditor()/ShowTestWindow() methods will be empty)
|
||||
//#define IMGUI_DISABLE_TEST_WINDOWS
|
||||
|
||||
//---- Don't define obsolete functions names
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
|
||||
//---- Pack colors to BGRA instead of RGBA (remove need to post process vertex buffer in back ends)
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
|
||||
|
||||
//---- Don't implement some functions to reduce linkage requirements.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Enable Test Engine / Automation features.
|
||||
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Implement STB libraries in a namespace to avoid conflicts
|
||||
//#define IMGUI_STB_NAMESPACE ImGuiStb
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
|
||||
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
|
||||
|
||||
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
||||
//#define IMGUI_USE_STB_SPRINTF
|
||||
|
||||
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||
// - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg.
|
||||
// - plutosvg will support more fonts and may load them faster. It currently requires to be built manually but it is fairly easy. See misc/freetype/README for instructions.
|
||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
||||
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||
|
||||
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||
|
||||
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||
|
||||
//---- Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4.
|
||||
/*
|
||||
#define IM_VEC2_CLASS_EXTRA \
|
||||
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
|
||||
operator MyVec2() const { return MyVec2(x,y); }
|
||||
constexpr ImVec2(const FVector2D& f) : x(f.X), y(f.Y) {} \
|
||||
operator FVector2D() const { return FVector2D(x,y); }
|
||||
|
||||
#define IM_VEC4_CLASS_EXTRA \
|
||||
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
|
||||
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||
constexpr ImVec4(const FVector4& f) : x(f.X), y(f.Y), z(f.Z), w(f.W) {} \
|
||||
operator FVector4() const { return FVector4(x,y,z,w); }
|
||||
*/
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
|
||||
//---- e.g. create variants of the ImGui::Value() helper for your low-level math types, or your own widgets/helpers.
|
||||
//---- ...Or use Dear ImGui's own very basic math operators.
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
||||
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
|
||||
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
|
||||
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
|
||||
//#define ImDrawIdx unsigned int
|
||||
|
||||
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
|
||||
//struct ImDrawList;
|
||||
//struct ImDrawCmd;
|
||||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||
//#define ImDrawCallback MyImDrawCallback
|
||||
|
||||
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
|
||||
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
|
||||
/*
|
||||
namespace ImGui
|
||||
{
|
||||
void Value(const char* prefix, const MyMatrix44& v, const char* float_format = NULL);
|
||||
void MyFunction(const char* name, MyMatrix44* mtx);
|
||||
}
|
||||
*/
|
||||
|
||||
|
4428
Source/ThirdParty/ImGuiLibrary/Include/imgui.h
vendored
4428
Source/ThirdParty/ImGuiLibrary/Include/imgui.h
vendored
File diff suppressed because it is too large
Load Diff
3879
Source/ThirdParty/ImGuiLibrary/Include/imgui_internal.h
vendored
Normal file
3879
Source/ThirdParty/ImGuiLibrary/Include/imgui_internal.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,14 @@
|
||||
// [ImGui] this is a slightly modified version of stb_truetype.h 1.9. Those changes would need to be pushed into nothings/sb
|
||||
// [ImGui] - fixed linestart handler when over last character of multi-line buffer + simplified existing code (#588, #815)
|
||||
// [ImGui] - fixed a state corruption/crash bug in stb_text_redo and stb_textedit_discard_redo (#715)
|
||||
// [ImGui] - fixed a crash bug in stb_textedit_discard_redo (#681)
|
||||
// [ImGui] - fixed some minor warnings
|
||||
// [DEAR IMGUI]
|
||||
// This is a slightly modified version of stb_textedit.h 1.14.
|
||||
// Those changes would need to be pushed into nothings/stb:
|
||||
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
|
||||
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
|
||||
// - Added name to struct or it may be forward declared in our code.
|
||||
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
|
||||
|
||||
// stb_textedit.h - v1.9 - public domain - Sean Barrett
|
||||
// stb_textedit.h - v1.14 - public domain - Sean Barrett
|
||||
// Development of this library was sponsored by RAD Game Tools
|
||||
//
|
||||
// This C header file implements the guts of a multi-line text-editing
|
||||
@ -23,20 +27,23 @@
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// This software is dual-licensed to the public domain and under the following
|
||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
// publish, and distribute this file as you see fit.
|
||||
// See end of file for license information.
|
||||
//
|
||||
//
|
||||
// DEPENDENCIES
|
||||
//
|
||||
// Uses the C runtime function 'memmove', which you can override
|
||||
// by defining STB_TEXTEDIT_memmove before the implementation.
|
||||
// by defining IMSTB_TEXTEDIT_memmove before the implementation.
|
||||
// Uses no other functions. Performs no runtime allocations.
|
||||
//
|
||||
//
|
||||
// VERSION HISTORY
|
||||
//
|
||||
// 1.14 (2021-07-11) page up/down, various fixes
|
||||
// 1.13 (2019-02-07) fix bug in undo size management
|
||||
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
|
||||
// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
|
||||
// 1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
|
||||
// 1.9 (2016-08-27) customizable move-by-word
|
||||
// 1.8 (2016-04-02) better keyboard handling when mouse button is down
|
||||
// 1.7 (2015-09-13) change y range handling in case baseline is non-0
|
||||
@ -55,12 +62,14 @@
|
||||
//
|
||||
// Ulf Winklemann: move-by-word in 1.1
|
||||
// Fabian Giesen: secondary key inputs in 1.5
|
||||
// Martins Mozeiko: STB_TEXTEDIT_memmove
|
||||
// Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6
|
||||
// Louis Schnellbach: page up/down in 1.14
|
||||
//
|
||||
// Bugfixes:
|
||||
// Scott Graham
|
||||
// Daniel Keller
|
||||
// Omar Cornut
|
||||
// Dan Thompson
|
||||
//
|
||||
// USAGE
|
||||
//
|
||||
@ -90,8 +99,8 @@
|
||||
// moderate sizes. The undo system does no memory allocations, so
|
||||
// it grows STB_TexteditState by the worst-case storage which is (in bytes):
|
||||
//
|
||||
// [4 + sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT
|
||||
// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT
|
||||
// [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATECOUNT
|
||||
// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHARCOUNT
|
||||
//
|
||||
//
|
||||
// Implementation mode:
|
||||
@ -114,7 +123,7 @@
|
||||
// Symbols that must be the same in header-file and implementation mode:
|
||||
//
|
||||
// STB_TEXTEDIT_CHARTYPE the character type
|
||||
// STB_TEXTEDIT_POSITIONTYPE small type that a valid cursor position
|
||||
// STB_TEXTEDIT_POSITIONTYPE small type that is a valid cursor position
|
||||
// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow
|
||||
// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer
|
||||
//
|
||||
@ -145,6 +154,8 @@
|
||||
// STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right
|
||||
// STB_TEXTEDIT_K_UP keyboard input to move cursor up
|
||||
// STB_TEXTEDIT_K_DOWN keyboard input to move cursor down
|
||||
// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page
|
||||
// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page
|
||||
// STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME
|
||||
// STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END
|
||||
// STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME
|
||||
@ -167,14 +178,10 @@
|
||||
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
|
||||
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
|
||||
//
|
||||
// Todo:
|
||||
// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page
|
||||
// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page
|
||||
//
|
||||
// Keyboard input must be encoded as a single integer value; e.g. a character code
|
||||
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
|
||||
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
|
||||
// i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow.
|
||||
// i.e. (STB_TEXTEDIT_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow.
|
||||
//
|
||||
// You can encode other things, such as CONTROL or ALT, in additional bits, and
|
||||
// then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example,
|
||||
@ -203,7 +210,8 @@
|
||||
// void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
|
||||
// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key)
|
||||
// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
|
||||
// void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)
|
||||
//
|
||||
// Each of these functions potentially updates the string and updates the
|
||||
// state.
|
||||
@ -237,7 +245,14 @@
|
||||
// inputs, set a high bit to distinguish the two; then you can define the
|
||||
// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
|
||||
// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
|
||||
// clear.
|
||||
// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
|
||||
// anything other type you want before including.
|
||||
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
|
||||
// transformed into text and stb_textedit_text() is automatically called.
|
||||
//
|
||||
// text: [DEAR IMGUI] added 2024-09
|
||||
// call this to text inputs sent to the textfield.
|
||||
//
|
||||
//
|
||||
// When rendering, you can read the cursor position and selection state from
|
||||
// the STB_TexteditState.
|
||||
@ -268,8 +283,8 @@
|
||||
////
|
||||
////
|
||||
|
||||
#ifndef INCLUDE_STB_TEXTEDIT_H
|
||||
#define INCLUDE_STB_TEXTEDIT_H
|
||||
#ifndef INCLUDE_IMSTB_TEXTEDIT_H
|
||||
#define INCLUDE_IMSTB_TEXTEDIT_H
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -280,38 +295,38 @@
|
||||
// and undo state.
|
||||
//
|
||||
|
||||
#ifndef STB_TEXTEDIT_UNDOSTATECOUNT
|
||||
#define STB_TEXTEDIT_UNDOSTATECOUNT 99
|
||||
#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT
|
||||
#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_UNDOCHARCOUNT
|
||||
#define STB_TEXTEDIT_UNDOCHARCOUNT 999
|
||||
#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT
|
||||
#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_CHARTYPE
|
||||
#define STB_TEXTEDIT_CHARTYPE int
|
||||
#ifndef IMSTB_TEXTEDIT_CHARTYPE
|
||||
#define IMSTB_TEXTEDIT_CHARTYPE int
|
||||
#endif
|
||||
#ifndef STB_TEXTEDIT_POSITIONTYPE
|
||||
#define STB_TEXTEDIT_POSITIONTYPE int
|
||||
#ifndef IMSTB_TEXTEDIT_POSITIONTYPE
|
||||
#define IMSTB_TEXTEDIT_POSITIONTYPE int
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// private data
|
||||
STB_TEXTEDIT_POSITIONTYPE where;
|
||||
short insert_length;
|
||||
short delete_length;
|
||||
short char_storage;
|
||||
IMSTB_TEXTEDIT_POSITIONTYPE where;
|
||||
IMSTB_TEXTEDIT_POSITIONTYPE insert_length;
|
||||
IMSTB_TEXTEDIT_POSITIONTYPE delete_length;
|
||||
int char_storage;
|
||||
} StbUndoRecord;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// private data
|
||||
StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT];
|
||||
STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT];
|
||||
StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT];
|
||||
IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT];
|
||||
short undo_point, redo_point;
|
||||
short undo_char_point, redo_char_point;
|
||||
int undo_char_point, redo_char_point;
|
||||
} StbUndoState;
|
||||
|
||||
typedef struct
|
||||
typedef struct STB_TexteditState
|
||||
{
|
||||
/////////////////////
|
||||
//
|
||||
@ -332,6 +347,10 @@ typedef struct
|
||||
// each textfield keeps its own insert mode state. to keep an app-wide
|
||||
// insert mode, copy this value in/out of the app state
|
||||
|
||||
int row_count_per_page;
|
||||
// page size in number of row.
|
||||
// this value MUST be set to >0 for pageup or pagedown in multilines documents.
|
||||
|
||||
/////////////////////
|
||||
//
|
||||
// private data
|
||||
@ -361,7 +380,7 @@ typedef struct
|
||||
float ymin,ymax; // height of row above and below baseline
|
||||
int num_chars;
|
||||
} StbTexteditRow;
|
||||
#endif //INCLUDE_STB_TEXTEDIT_H
|
||||
#endif //INCLUDE_IMSTB_TEXTEDIT_H
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@ -374,11 +393,11 @@ typedef struct
|
||||
|
||||
// implementation isn't include-guarded, since it might have indirectly
|
||||
// included just the "header" portion
|
||||
#ifdef STB_TEXTEDIT_IMPLEMENTATION
|
||||
#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION
|
||||
|
||||
#ifndef STB_TEXTEDIT_memmove
|
||||
#ifndef IMSTB_TEXTEDIT_memmove
|
||||
#include <string.h>
|
||||
#define STB_TEXTEDIT_memmove memmove
|
||||
#define IMSTB_TEXTEDIT_memmove memmove
|
||||
#endif
|
||||
|
||||
|
||||
@ -388,7 +407,7 @@ typedef struct
|
||||
//
|
||||
|
||||
// traverse the layout to locate the nearest character to a display position
|
||||
static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
|
||||
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
{
|
||||
StbTexteditRow r;
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
@ -427,13 +446,13 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
|
||||
if (x < r.x1) {
|
||||
// search characters in row for one that straddles 'x'
|
||||
prev_x = r.x0;
|
||||
for (k=0; k < r.num_chars; ++k) {
|
||||
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
|
||||
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
|
||||
if (x < prev_x+w) {
|
||||
if (x < prev_x+w/2)
|
||||
return k+i;
|
||||
else
|
||||
return k+i+1;
|
||||
return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);
|
||||
}
|
||||
prev_x += w;
|
||||
}
|
||||
@ -448,8 +467,17 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
|
||||
}
|
||||
|
||||
// API click: on mouse down, move the cursor to the clicked location, and reset the selection
|
||||
static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
{
|
||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||
// goes off the top or bottom of the text
|
||||
if( state->single_line )
|
||||
{
|
||||
StbTexteditRow r;
|
||||
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
|
||||
y = r.ymin;
|
||||
}
|
||||
|
||||
state->cursor = stb_text_locate_coord(str, x, y);
|
||||
state->select_start = state->cursor;
|
||||
state->select_end = state->cursor;
|
||||
@ -457,11 +485,23 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
|
||||
}
|
||||
|
||||
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
|
||||
static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
|
||||
{
|
||||
int p = stb_text_locate_coord(str, x, y);
|
||||
int p = 0;
|
||||
|
||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||
// goes off the top or bottom of the text
|
||||
if( state->single_line )
|
||||
{
|
||||
StbTexteditRow r;
|
||||
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
|
||||
y = r.ymin;
|
||||
}
|
||||
|
||||
if (state->select_start == state->select_end)
|
||||
state->select_start = state->cursor;
|
||||
|
||||
p = stb_text_locate_coord(str, x, y);
|
||||
state->cursor = state->select_end = p;
|
||||
}
|
||||
|
||||
@ -471,11 +511,11 @@ static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state
|
||||
//
|
||||
|
||||
// forward declarations
|
||||
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
|
||||
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
|
||||
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
|
||||
static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);
|
||||
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
|
||||
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -487,36 +527,21 @@ typedef struct
|
||||
|
||||
// find the x/y location of a character, and remember info about the previous row in
|
||||
// case we get a move-up event (for page up, we'll have to rescan)
|
||||
static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line)
|
||||
static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line)
|
||||
{
|
||||
StbTexteditRow r;
|
||||
int prev_start = 0;
|
||||
int z = STB_TEXTEDIT_STRINGLEN(str);
|
||||
int i=0, first;
|
||||
|
||||
if (n == z) {
|
||||
// if it's at the end, then find the last line -- simpler than trying to
|
||||
// explicitly handle this case in the regular code
|
||||
if (single_line) {
|
||||
if (n == z && single_line) {
|
||||
// special case if it's at the end (may not be needed?)
|
||||
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
|
||||
find->y = 0;
|
||||
find->first_char = 0;
|
||||
find->length = z;
|
||||
find->height = r.ymax - r.ymin;
|
||||
find->x = r.x1;
|
||||
} else {
|
||||
find->y = 0;
|
||||
find->x = 0;
|
||||
find->height = 1;
|
||||
while (i < z) {
|
||||
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
||||
prev_start = i;
|
||||
i += r.num_chars;
|
||||
}
|
||||
find->first_char = i;
|
||||
find->length = 0;
|
||||
find->prev_first = prev_start;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -527,9 +552,16 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
|
||||
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
||||
if (n < i + r.num_chars)
|
||||
break;
|
||||
if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line
|
||||
break; // [DEAR IMGUI]
|
||||
prev_start = i;
|
||||
i += r.num_chars;
|
||||
find->y += r.baseline_y_delta;
|
||||
if (i == z) // [DEAR IMGUI]
|
||||
{
|
||||
r.num_chars = 0; // [DEAR IMGUI]
|
||||
break; // [DEAR IMGUI]
|
||||
}
|
||||
}
|
||||
|
||||
find->first_char = first = i;
|
||||
@ -539,15 +571,14 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
|
||||
|
||||
// now scan to find xpos
|
||||
find->x = r.x0;
|
||||
i = 0;
|
||||
for (i=0; first+i < n; ++i)
|
||||
for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first)
|
||||
find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
|
||||
}
|
||||
|
||||
#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
|
||||
|
||||
// make the selection/cursor state valid if client altered the string
|
||||
static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||
@ -561,7 +592,7 @@ static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
|
||||
}
|
||||
|
||||
// delete characters while updating undo
|
||||
static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
|
||||
static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
|
||||
{
|
||||
stb_text_makeundo_delete(str, state, where, len);
|
||||
STB_TEXTEDIT_DELETECHARS(str, where, len);
|
||||
@ -569,7 +600,7 @@ static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *sta
|
||||
}
|
||||
|
||||
// delete the section
|
||||
static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
stb_textedit_clamp(str, state);
|
||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||
@ -606,7 +637,7 @@ static void stb_textedit_move_to_first(STB_TexteditState *state)
|
||||
}
|
||||
|
||||
// move cursor to last character of selection
|
||||
static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||
stb_textedit_sortselection(state);
|
||||
@ -617,14 +648,25 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat
|
||||
}
|
||||
}
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// Functions must be implemented for UTF8 support
|
||||
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
||||
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
||||
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
|
||||
#endif
|
||||
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
|
||||
#endif
|
||||
|
||||
#ifdef STB_TEXTEDIT_IS_SPACE
|
||||
static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx )
|
||||
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
|
||||
{
|
||||
return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
|
||||
}
|
||||
|
||||
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
||||
static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
|
||||
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
--c; // always move at least one character
|
||||
while( c >= 0 && !is_word_boundary( str, c ) )
|
||||
@ -639,7 +681,7 @@ static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
|
||||
#endif
|
||||
|
||||
#ifndef STB_TEXTEDIT_MOVEWORDRIGHT
|
||||
static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c )
|
||||
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
const int len = STB_TEXTEDIT_STRINGLEN(str);
|
||||
++c; // always move at least one character
|
||||
@ -666,10 +708,10 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)
|
||||
}
|
||||
|
||||
// API cut: delete selection
|
||||
static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
if (STB_TEXT_HAS_SELECTION(state)) {
|
||||
stb_textedit_delete_selection(str,state); // implicity clamps
|
||||
stb_textedit_delete_selection(str,state); // implicitly clamps
|
||||
state->has_preferred_x = 0;
|
||||
return 1;
|
||||
}
|
||||
@ -677,9 +719,8 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
}
|
||||
|
||||
// API paste: replace existing selection with passed-in text
|
||||
static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)
|
||||
static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len)
|
||||
{
|
||||
STB_TEXTEDIT_CHARTYPE *text = (STB_TEXTEDIT_CHARTYPE *) ctext;
|
||||
// if there's a selection, the paste should delete it
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_delete_selection(str,state);
|
||||
@ -690,42 +731,52 @@ static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state
|
||||
state->has_preferred_x = 0;
|
||||
return 1;
|
||||
}
|
||||
// remove the undo since we didn't actually insert the characters
|
||||
if (state->undostate.undo_point)
|
||||
--state->undostate.undo_point;
|
||||
// note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// API key: process a keyboard input
|
||||
static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key)
|
||||
{
|
||||
retry:
|
||||
switch (key) {
|
||||
default: {
|
||||
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
||||
if (c > 0) {
|
||||
STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c;
|
||||
#ifndef STB_TEXTEDIT_KEYTYPE
|
||||
#define STB_TEXTEDIT_KEYTYPE int
|
||||
#endif
|
||||
|
||||
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
|
||||
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
|
||||
{
|
||||
// can't add newline in single-line mode
|
||||
if (c == '\n' && state->single_line)
|
||||
break;
|
||||
if (text[0] == '\n' && state->single_line)
|
||||
return;
|
||||
|
||||
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
||||
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
|
||||
++state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
} else {
|
||||
stb_textedit_delete_selection(str,state); // implicity clamps
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
|
||||
stb_text_makeundo_insert(state, state->cursor, 1);
|
||||
++state->cursor;
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
stb_textedit_delete_selection(str, state); // implicitly clamps
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
||||
stb_text_makeundo_insert(state, state->cursor, text_len);
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// API key: process a keyboard input
|
||||
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
|
||||
{
|
||||
retry:
|
||||
switch (key) {
|
||||
default: {
|
||||
#ifdef STB_TEXTEDIT_KEYTOTEXT
|
||||
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
||||
if (c > 0) {
|
||||
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
|
||||
stb_textedit_text(str, state, &ch, 1);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
@ -751,7 +802,7 @@ retry:
|
||||
stb_textedit_move_to_first(state);
|
||||
else
|
||||
if (state->cursor > 0)
|
||||
--state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
||||
@ -760,7 +811,7 @@ retry:
|
||||
if (STB_TEXT_HAS_SELECTION(state))
|
||||
stb_textedit_move_to_last(str, state);
|
||||
else
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
stb_textedit_clamp(str, state);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@ -770,7 +821,7 @@ retry:
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
// move selection left
|
||||
if (state->select_end > 0)
|
||||
--state->select_end;
|
||||
state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);
|
||||
state->cursor = state->select_end;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@ -820,19 +871,23 @@ retry:
|
||||
case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
// move selection right
|
||||
++state->select_end;
|
||||
state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);
|
||||
stb_textedit_clamp(str, state);
|
||||
state->cursor = state->select_end;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
||||
case STB_TEXTEDIT_K_DOWN:
|
||||
case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: {
|
||||
case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT:
|
||||
case STB_TEXTEDIT_K_PGDOWN:
|
||||
case STB_TEXTEDIT_K_PGDOWN | STB_TEXTEDIT_K_SHIFT: {
|
||||
StbFindState find;
|
||||
StbTexteditRow row;
|
||||
int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
|
||||
int i, j, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
|
||||
int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGDOWN;
|
||||
int row_count = is_page ? state->row_count_per_page : 1;
|
||||
|
||||
if (state->single_line) {
|
||||
if (!is_page && state->single_line) {
|
||||
// on windows, up&down in single-line behave like left&right
|
||||
key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT);
|
||||
goto retry;
|
||||
@ -841,30 +896,38 @@ retry:
|
||||
if (sel)
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
else if (STB_TEXT_HAS_SELECTION(state))
|
||||
stb_textedit_move_to_last(str,state);
|
||||
stb_textedit_move_to_last(str, state);
|
||||
|
||||
// compute current position of cursor point
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
|
||||
|
||||
// now find character position down a row
|
||||
if (find.length) {
|
||||
float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
|
||||
float x;
|
||||
for (j = 0; j < row_count; ++j) {
|
||||
float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x;
|
||||
int start = find.first_char + find.length;
|
||||
|
||||
if (find.length == 0)
|
||||
break;
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// going down while being on the last line shouldn't bring us to that line end
|
||||
if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
|
||||
break;
|
||||
|
||||
// now find character position down a row
|
||||
state->cursor = start;
|
||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
||||
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
#endif
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
@ -873,17 +936,25 @@ retry:
|
||||
|
||||
if (sel)
|
||||
state->select_end = state->cursor;
|
||||
|
||||
// go to next line
|
||||
find.first_char = find.first_char + find.length;
|
||||
find.length = row.num_chars;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STB_TEXTEDIT_K_UP:
|
||||
case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: {
|
||||
case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT:
|
||||
case STB_TEXTEDIT_K_PGUP:
|
||||
case STB_TEXTEDIT_K_PGUP | STB_TEXTEDIT_K_SHIFT: {
|
||||
StbFindState find;
|
||||
StbTexteditRow row;
|
||||
int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
|
||||
int i, j, prev_scan, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
|
||||
int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGUP;
|
||||
int row_count = is_page ? state->row_count_per_page : 1;
|
||||
|
||||
if (state->single_line) {
|
||||
if (!is_page && state->single_line) {
|
||||
// on windows, up&down become left&right
|
||||
key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT);
|
||||
goto retry;
|
||||
@ -898,24 +969,27 @@ retry:
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
|
||||
|
||||
for (j = 0; j < row_count; ++j) {
|
||||
float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x;
|
||||
|
||||
// can only go up if there's a previous row
|
||||
if (find.prev_first != find.first_char) {
|
||||
if (find.prev_first == find.first_char)
|
||||
break;
|
||||
|
||||
// now find character position up a row
|
||||
float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
|
||||
float x;
|
||||
state->cursor = find.prev_first;
|
||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
||||
#ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
#endif
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
@ -924,6 +998,14 @@ retry:
|
||||
|
||||
if (sel)
|
||||
state->select_end = state->cursor;
|
||||
|
||||
// go to previous line
|
||||
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
|
||||
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
|
||||
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
|
||||
--prev_scan;
|
||||
find.first_char = find.prev_first;
|
||||
find.prev_first = prev_scan;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -935,7 +1017,7 @@ retry:
|
||||
else {
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
if (state->cursor < n)
|
||||
stb_textedit_delete(str, state, state->cursor, 1);
|
||||
stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor);
|
||||
}
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@ -947,8 +1029,9 @@ retry:
|
||||
else {
|
||||
stb_textedit_clamp(str, state);
|
||||
if (state->cursor > 0) {
|
||||
stb_textedit_delete(str, state, state->cursor-1, 1);
|
||||
--state->cursor;
|
||||
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
|
||||
stb_textedit_delete(str, state, prev, state->cursor - prev);
|
||||
state->cursor = prev;
|
||||
}
|
||||
}
|
||||
state->has_preferred_x = 0;
|
||||
@ -1047,10 +1130,6 @@ retry:
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// @TODO:
|
||||
// STB_TEXTEDIT_K_PGUP - move cursor up a page
|
||||
// STB_TEXTEDIT_K_PGDOWN - move cursor down a page
|
||||
}
|
||||
}
|
||||
|
||||
@ -1062,8 +1141,8 @@ retry:
|
||||
|
||||
static void stb_textedit_flush_redo(StbUndoState *state)
|
||||
{
|
||||
state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
}
|
||||
|
||||
// discard the oldest entry in the undo list
|
||||
@ -1074,14 +1153,14 @@ static void stb_textedit_discard_undo(StbUndoState *state)
|
||||
if (state->undo_rec[0].char_storage >= 0) {
|
||||
int n = state->undo_rec[0].insert_length, i;
|
||||
// delete n characters from all other records
|
||||
state->undo_char_point = state->undo_char_point - (short) n; // vsnet05
|
||||
STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) ((size_t)state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE)));
|
||||
state->undo_char_point -= n;
|
||||
IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
|
||||
for (i=0; i < state->undo_point; ++i)
|
||||
if (state->undo_rec[i].char_storage >= 0)
|
||||
state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short) n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it
|
||||
state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it
|
||||
}
|
||||
--state->undo_point;
|
||||
STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) ((size_t)state->undo_point*sizeof(state->undo_rec[0])));
|
||||
IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1091,20 +1170,30 @@ static void stb_textedit_discard_undo(StbUndoState *state)
|
||||
// fill up even though the undo buffer didn't
|
||||
static void stb_textedit_discard_redo(StbUndoState *state)
|
||||
{
|
||||
int k = STB_TEXTEDIT_UNDOSTATECOUNT-1;
|
||||
int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1;
|
||||
|
||||
if (state->redo_point <= k) {
|
||||
// if the k'th undo state has characters, clean those up
|
||||
if (state->undo_rec[k].char_storage >= 0) {
|
||||
int n = state->undo_rec[k].insert_length, i;
|
||||
// delete n characters from all other records
|
||||
state->redo_char_point = state->redo_char_point + (short) n; // vsnet05
|
||||
STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((size_t)(STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE)));
|
||||
// move the remaining redo character data to the end of the buffer
|
||||
state->redo_char_point += n;
|
||||
IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
|
||||
// adjust the position of all the other records to account for above memmove
|
||||
for (i=state->redo_point; i < k; ++i)
|
||||
if (state->undo_rec[i].char_storage >= 0)
|
||||
state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short) n; // vsnet05
|
||||
state->undo_rec[i].char_storage += n;
|
||||
}
|
||||
STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point, state->undo_rec + state->redo_point-1, (size_t) ((size_t)(STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0])));
|
||||
// now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
|
||||
// [DEAR IMGUI]
|
||||
size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
|
||||
const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
|
||||
const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
|
||||
IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);
|
||||
IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);
|
||||
IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
|
||||
|
||||
// now move redo_point to point to the new one
|
||||
++state->redo_point;
|
||||
}
|
||||
}
|
||||
@ -1116,44 +1205,44 @@ static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numch
|
||||
|
||||
// if we have no free records, we have to make room, by sliding the
|
||||
// existing records down
|
||||
if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
stb_textedit_discard_undo(state);
|
||||
|
||||
// if the characters to store won't possibly fit in the buffer, we can't undo
|
||||
if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||
if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||
state->undo_point = 0;
|
||||
state->undo_char_point = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// if we don't have enough free characters in the buffer, we have to make room
|
||||
while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT)
|
||||
while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT)
|
||||
stb_textedit_discard_undo(state);
|
||||
|
||||
return &state->undo_rec[state->undo_point++];
|
||||
}
|
||||
|
||||
static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
|
||||
static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
|
||||
{
|
||||
StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
|
||||
r->where = pos;
|
||||
r->insert_length = (short) insert_len;
|
||||
r->delete_length = (short) delete_len;
|
||||
r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len;
|
||||
r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len;
|
||||
|
||||
if (insert_len == 0) {
|
||||
r->char_storage = -1;
|
||||
return NULL;
|
||||
} else {
|
||||
r->char_storage = state->undo_char_point;
|
||||
state->undo_char_point = state->undo_char_point + (short) insert_len;
|
||||
state->undo_char_point += insert_len;
|
||||
return &state->undo_char[r->char_storage];
|
||||
}
|
||||
}
|
||||
|
||||
static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
StbUndoState *s = &state->undostate;
|
||||
StbUndoRecord u, *r;
|
||||
@ -1180,7 +1269,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
// characters stored for *undoing* don't leave room for redo
|
||||
// if the last is true, we have to bail
|
||||
|
||||
if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||
if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
|
||||
// the undo records take up too much character space; there's no space to store the redo characters
|
||||
r->insert_length = 0;
|
||||
} else {
|
||||
@ -1188,16 +1277,16 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
|
||||
// there's definitely room to store the characters eventually
|
||||
while (s->undo_char_point + u.delete_length > s->redo_char_point) {
|
||||
// should never happen:
|
||||
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
return;
|
||||
// there's currently not enough room, so discard a redo record
|
||||
stb_textedit_discard_redo(s);
|
||||
// should never happen:
|
||||
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
return;
|
||||
}
|
||||
r = &s->undo_rec[s->redo_point-1];
|
||||
|
||||
r->char_storage = s->redo_char_point - u.delete_length;
|
||||
s->redo_char_point = s->redo_char_point - (short) u.delete_length;
|
||||
s->redo_char_point = s->redo_char_point - u.delete_length;
|
||||
|
||||
// now save the characters
|
||||
for (i=0; i < u.delete_length; ++i)
|
||||
@ -1221,11 +1310,11 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
s->redo_point--;
|
||||
}
|
||||
|
||||
static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
{
|
||||
StbUndoState *s = &state->undostate;
|
||||
StbUndoRecord *u, r;
|
||||
if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
|
||||
return;
|
||||
|
||||
// we need to do two things: apply the redo record, and create an undo record
|
||||
@ -1277,20 +1366,20 @@ static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int le
|
||||
stb_text_createundo(&state->undostate, where, 0, length);
|
||||
}
|
||||
|
||||
static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
|
||||
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
|
||||
{
|
||||
int i;
|
||||
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
|
||||
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
|
||||
if (p) {
|
||||
for (i=0; i < length; ++i)
|
||||
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
|
||||
}
|
||||
}
|
||||
|
||||
static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
|
||||
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
|
||||
{
|
||||
int i;
|
||||
STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
|
||||
IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
|
||||
if (p) {
|
||||
for (i=0; i < old_length; ++i)
|
||||
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
|
||||
@ -1302,8 +1391,8 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin
|
||||
{
|
||||
state->undostate.undo_point = 0;
|
||||
state->undostate.undo_char_point = 0;
|
||||
state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
|
||||
state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
|
||||
state->select_end = state->select_start = 0;
|
||||
state->cursor = 0;
|
||||
state->has_preferred_x = 0;
|
||||
@ -1312,6 +1401,7 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin
|
||||
state->initialized = 1;
|
||||
state->single_line = (unsigned char) is_single_line;
|
||||
state->insert_mode = 0;
|
||||
state->row_count_per_page = 0;
|
||||
}
|
||||
|
||||
// API initialize
|
||||
@ -1319,4 +1409,61 @@ static void stb_textedit_initialize_state(STB_TexteditState *state, int is_singl
|
||||
{
|
||||
stb_textedit_clear_state(state, is_single_line);
|
||||
}
|
||||
#endif//STB_TEXTEDIT_IMPLEMENTATION
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||
#endif
|
||||
|
||||
static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len)
|
||||
{
|
||||
return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len);
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif//IMSTB_TEXTEDIT_IMPLEMENTATION
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2015 Omar Cornut and ImGui contributors
|
||||
Copyright (c) 2014-2024 Omar Cornut
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
28351
Source/ThirdParty/ImGuiLibrary/Private/imgui.cpp
vendored
28351
Source/ThirdParty/ImGuiLibrary/Private/imgui.cpp
vendored
File diff suppressed because it is too large
Load Diff
10628
Source/ThirdParty/ImGuiLibrary/Private/imgui_demo.cpp
vendored
10628
Source/ThirdParty/ImGuiLibrary/Private/imgui_demo.cpp
vendored
File diff suppressed because it is too large
Load Diff
4366
Source/ThirdParty/ImGuiLibrary/Private/imgui_draw.cpp
vendored
4366
Source/ThirdParty/ImGuiLibrary/Private/imgui_draw.cpp
vendored
File diff suppressed because it is too large
Load Diff
@ -1,776 +0,0 @@
|
||||
// dear imgui, v1.50 WIP
|
||||
// (internals)
|
||||
|
||||
// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility!
|
||||
// Implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators)
|
||||
// #define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef IMGUI_VERSION
|
||||
#error Must include imgui.h before imgui_internal.h
|
||||
#endif
|
||||
|
||||
#include <stdio.h> // FILE*
|
||||
#include <math.h> // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport)
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward Declarations
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct ImRect;
|
||||
struct ImGuiColMod;
|
||||
struct ImGuiStyleMod;
|
||||
struct ImGuiGroupData;
|
||||
struct ImGuiSimpleColumns;
|
||||
struct ImGuiDrawContext;
|
||||
struct ImGuiTextEditState;
|
||||
struct ImGuiIniData;
|
||||
struct ImGuiMouseCursorData;
|
||||
struct ImGuiPopupRef;
|
||||
struct ImGuiWindow;
|
||||
|
||||
typedef int ImGuiLayoutType; // enum ImGuiLayoutType_
|
||||
typedef int ImGuiButtonFlags; // enum ImGuiButtonFlags_
|
||||
typedef int ImGuiTreeNodeFlags; // enum ImGuiTreeNodeFlags_
|
||||
typedef int ImGuiSliderFlags; // enum ImGuiSliderFlags_
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// STB libraries
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
namespace ImGuiStb
|
||||
{
|
||||
|
||||
#undef STB_TEXTEDIT_STRING
|
||||
#undef STB_TEXTEDIT_CHARTYPE
|
||||
#define STB_TEXTEDIT_STRING ImGuiTextEditState
|
||||
#define STB_TEXTEDIT_CHARTYPE ImWchar
|
||||
#define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f
|
||||
#include "stb_textedit.h"
|
||||
|
||||
} // namespace ImGuiStb
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Context
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef GImGui
|
||||
extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointer
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR)))
|
||||
#define IM_PI 3.14159265358979323846f
|
||||
#define IM_OFFSETOF(_TYPE,_ELM) ((size_t)&(((_TYPE*)0)->_ELM))
|
||||
|
||||
// Helpers: UTF-8 <> wchar
|
||||
IMGUI_API int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count
|
||||
IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count
|
||||
IMGUI_API int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count
|
||||
IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count)
|
||||
IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string as UTF-8 code-points
|
||||
|
||||
// Helpers: Misc
|
||||
IMGUI_API ImU32 ImHash(const void* data, int data_size, ImU32 seed = 0); // Pass data_size==0 for zero-terminated strings
|
||||
IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size = NULL, int padding_bytes = 0);
|
||||
IMGUI_API FILE* ImFileOpen(const char* filename, const char* file_open_mode);
|
||||
IMGUI_API bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c);
|
||||
static inline bool ImCharIsSpace(int c) { return c == ' ' || c == '\t' || c == 0x3000; }
|
||||
static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }
|
||||
|
||||
// Helpers: String
|
||||
IMGUI_API int ImStricmp(const char* str1, const char* str2);
|
||||
IMGUI_API int ImStrnicmp(const char* str1, const char* str2, int count);
|
||||
IMGUI_API char* ImStrdup(const char* str);
|
||||
IMGUI_API int ImStrlenW(const ImWchar* str);
|
||||
IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line
|
||||
IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);
|
||||
IMGUI_API int ImFormatString(char* buf, int buf_size, const char* fmt, ...) IM_PRINTFARGS(3);
|
||||
IMGUI_API int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args);
|
||||
|
||||
// Helpers: Math
|
||||
// We are keeping those not leaking to the user by default, in the case the user has implicit cast operators between ImVec2 and its own types (when IM_VEC2_CLASS_EXTRA is defined)
|
||||
#ifdef IMGUI_DEFINE_MATH_OPERATORS
|
||||
static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x*rhs, lhs.y*rhs); }
|
||||
static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x/rhs, lhs.y/rhs); }
|
||||
static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x+rhs.x, lhs.y+rhs.y); }
|
||||
static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x-rhs.x, lhs.y-rhs.y); }
|
||||
static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x*rhs.x, lhs.y*rhs.y); }
|
||||
static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x/rhs.x, lhs.y/rhs.y); }
|
||||
static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; }
|
||||
static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; }
|
||||
static inline ImVec2& operator*=(ImVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; }
|
||||
static inline ImVec2& operator/=(ImVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; }
|
||||
static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z, lhs.w-rhs.w); }
|
||||
#endif
|
||||
|
||||
static inline int ImMin(int lhs, int rhs) { return lhs < rhs ? lhs : rhs; }
|
||||
static inline int ImMax(int lhs, int rhs) { return lhs >= rhs ? lhs : rhs; }
|
||||
static inline float ImMin(float lhs, float rhs) { return lhs < rhs ? lhs : rhs; }
|
||||
static inline float ImMax(float lhs, float rhs) { return lhs >= rhs ? lhs : rhs; }
|
||||
static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMin(lhs.x,rhs.x), ImMin(lhs.y,rhs.y)); }
|
||||
static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMax(lhs.x,rhs.x), ImMax(lhs.y,rhs.y)); }
|
||||
static inline int ImClamp(int v, int mn, int mx) { return (v < mn) ? mn : (v > mx) ? mx : v; }
|
||||
static inline float ImClamp(float v, float mn, float mx) { return (v < mn) ? mn : (v > mx) ? mx : v; }
|
||||
static inline ImVec2 ImClamp(const ImVec2& f, const ImVec2& mn, ImVec2 mx) { return ImVec2(ImClamp(f.x,mn.x,mx.x), ImClamp(f.y,mn.y,mx.y)); }
|
||||
static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
|
||||
static inline float ImLerp(float a, float b, float t) { return a + (b - a) * t; }
|
||||
static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); }
|
||||
static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; }
|
||||
static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; }
|
||||
static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / sqrtf(d); return fail_value; }
|
||||
static inline float ImFloor(float f) { return (float)(int)f; }
|
||||
static inline ImVec2 ImFloor(ImVec2 v) { return ImVec2((float)(int)v.x, (float)(int)v.y); }
|
||||
|
||||
// We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax.
|
||||
// Defining a custom placement new() with a dummy parameter allows us to bypass including <new> which on some platforms complains when user has disabled exceptions.
|
||||
#ifdef IMGUI_DEFINE_PLACEMENT_NEW
|
||||
struct ImPlacementNewDummy {};
|
||||
inline void* operator new(size_t, ImPlacementNewDummy, void* ptr) { return ptr; }
|
||||
inline void operator delete(void*, ImPlacementNewDummy, void*) {}
|
||||
#define IM_PLACEMENT_NEW(_PTR) new(ImPlacementNewDummy(), _PTR)
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Types
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
enum ImGuiButtonFlags_
|
||||
{
|
||||
ImGuiButtonFlags_Repeat = 1 << 0, // hold to repeat
|
||||
ImGuiButtonFlags_PressedOnClickRelease = 1 << 1, // (default) return pressed on click+release on same item (default if no PressedOn** flag is set)
|
||||
ImGuiButtonFlags_PressedOnClick = 1 << 2, // return pressed on click (default requires click+release)
|
||||
ImGuiButtonFlags_PressedOnRelease = 1 << 3, // return pressed on release (default requires click+release)
|
||||
ImGuiButtonFlags_PressedOnDoubleClick = 1 << 4, // return pressed on double-click (default requires click+release)
|
||||
ImGuiButtonFlags_FlattenChilds = 1 << 5, // allow interaction even if a child window is overlapping
|
||||
ImGuiButtonFlags_DontClosePopups = 1 << 6, // disable automatically closing parent popup on press
|
||||
ImGuiButtonFlags_Disabled = 1 << 7, // disable interaction
|
||||
ImGuiButtonFlags_AlignTextBaseLine = 1 << 8, // vertically align button to match text baseline - ButtonEx() only
|
||||
ImGuiButtonFlags_NoKeyModifiers = 1 << 9, // disable interaction if a key modifier is held
|
||||
ImGuiButtonFlags_AllowOverlapMode = 1 << 10 // require previous frame HoveredId to either match id or be null before being usable
|
||||
};
|
||||
|
||||
enum ImGuiSliderFlags_
|
||||
{
|
||||
ImGuiSliderFlags_Vertical = 1 << 0
|
||||
};
|
||||
|
||||
enum ImGuiSelectableFlagsPrivate_
|
||||
{
|
||||
// NB: need to be in sync with last value of ImGuiSelectableFlags_
|
||||
ImGuiSelectableFlags_Menu = 1 << 3,
|
||||
ImGuiSelectableFlags_MenuItem = 1 << 4,
|
||||
ImGuiSelectableFlags_Disabled = 1 << 5,
|
||||
ImGuiSelectableFlags_DrawFillAvailWidth = 1 << 6
|
||||
};
|
||||
|
||||
// FIXME: this is in development, not exposed/functional as a generic feature yet.
|
||||
enum ImGuiLayoutType_
|
||||
{
|
||||
ImGuiLayoutType_Vertical,
|
||||
ImGuiLayoutType_Horizontal
|
||||
};
|
||||
|
||||
enum ImGuiPlotType
|
||||
{
|
||||
ImGuiPlotType_Lines,
|
||||
ImGuiPlotType_Histogram
|
||||
};
|
||||
|
||||
enum ImGuiDataType
|
||||
{
|
||||
ImGuiDataType_Int,
|
||||
ImGuiDataType_Float,
|
||||
ImGuiDataType_Float2,
|
||||
};
|
||||
|
||||
enum ImGuiCorner
|
||||
{
|
||||
ImGuiCorner_TopLeft = 1 << 0, // 1
|
||||
ImGuiCorner_TopRight = 1 << 1, // 2
|
||||
ImGuiCorner_BottomRight = 1 << 2, // 4
|
||||
ImGuiCorner_BottomLeft = 1 << 3, // 8
|
||||
ImGuiCorner_All = 0x0F
|
||||
};
|
||||
|
||||
// 2D axis aligned bounding-box
|
||||
// NB: we can't rely on ImVec2 math operators being available here
|
||||
struct IMGUI_API ImRect
|
||||
{
|
||||
ImVec2 Min; // Upper-left
|
||||
ImVec2 Max; // Lower-right
|
||||
|
||||
ImRect() : Min(FLT_MAX,FLT_MAX), Max(-FLT_MAX,-FLT_MAX) {}
|
||||
ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {}
|
||||
ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {}
|
||||
ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {}
|
||||
|
||||
ImVec2 GetCenter() const { return ImVec2((Min.x+Max.x)*0.5f, (Min.y+Max.y)*0.5f); }
|
||||
ImVec2 GetSize() const { return ImVec2(Max.x-Min.x, Max.y-Min.y); }
|
||||
float GetWidth() const { return Max.x-Min.x; }
|
||||
float GetHeight() const { return Max.y-Min.y; }
|
||||
ImVec2 GetTL() const { return Min; } // Top-left
|
||||
ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right
|
||||
ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left
|
||||
ImVec2 GetBR() const { return Max; } // Bottom-right
|
||||
bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; }
|
||||
bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x < Max.x && r.Max.y < Max.y; }
|
||||
bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; }
|
||||
void Add(const ImVec2& rhs) { if (Min.x > rhs.x) Min.x = rhs.x; if (Min.y > rhs.y) Min.y = rhs.y; if (Max.x < rhs.x) Max.x = rhs.x; if (Max.y < rhs.y) Max.y = rhs.y; }
|
||||
void Add(const ImRect& rhs) { if (Min.x > rhs.Min.x) Min.x = rhs.Min.x; if (Min.y > rhs.Min.y) Min.y = rhs.Min.y; if (Max.x < rhs.Max.x) Max.x = rhs.Max.x; if (Max.y < rhs.Max.y) Max.y = rhs.Max.y; }
|
||||
void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; }
|
||||
void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; }
|
||||
void Reduce(const ImVec2& amount) { Min.x += amount.x; Min.y += amount.y; Max.x -= amount.x; Max.y -= amount.y; }
|
||||
void Clip(const ImRect& clip) { if (Min.x < clip.Min.x) Min.x = clip.Min.x; if (Min.y < clip.Min.y) Min.y = clip.Min.y; if (Max.x > clip.Max.x) Max.x = clip.Max.x; if (Max.y > clip.Max.y) Max.y = clip.Max.y; }
|
||||
void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; }
|
||||
ImVec2 GetClosestPoint(ImVec2 p, bool on_edge) const
|
||||
{
|
||||
if (!on_edge && Contains(p))
|
||||
return p;
|
||||
if (p.x > Max.x) p.x = Max.x;
|
||||
else if (p.x < Min.x) p.x = Min.x;
|
||||
if (p.y > Max.y) p.y = Max.y;
|
||||
else if (p.y < Min.y) p.y = Min.y;
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
// Stacked color modifier, backup of modified data so we can restore it
|
||||
struct ImGuiColMod
|
||||
{
|
||||
ImGuiCol Col;
|
||||
ImVec4 BackupValue;
|
||||
};
|
||||
|
||||
// Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable.
|
||||
struct ImGuiStyleMod
|
||||
{
|
||||
ImGuiStyleVar VarIdx;
|
||||
union { int BackupInt[2]; float BackupFloat[2]; };
|
||||
ImGuiStyleMod(ImGuiStyleVar idx, int v) { VarIdx = idx; BackupInt[0] = v; }
|
||||
ImGuiStyleMod(ImGuiStyleVar idx, float v) { VarIdx = idx; BackupFloat[0] = v; }
|
||||
ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; }
|
||||
};
|
||||
|
||||
// Stacked data for BeginGroup()/EndGroup()
|
||||
struct ImGuiGroupData
|
||||
{
|
||||
ImVec2 BackupCursorPos;
|
||||
ImVec2 BackupCursorMaxPos;
|
||||
float BackupIndentX;
|
||||
float BackupGroupOffsetX;
|
||||
float BackupCurrentLineHeight;
|
||||
float BackupCurrentLineTextBaseOffset;
|
||||
float BackupLogLinePosY;
|
||||
bool BackupActiveIdIsAlive;
|
||||
bool AdvanceCursor;
|
||||
};
|
||||
|
||||
// Per column data for Columns()
|
||||
struct ImGuiColumnData
|
||||
{
|
||||
float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right)
|
||||
//float IndentX;
|
||||
};
|
||||
|
||||
// Simple column measurement currently used for MenuItem() only. This is very short-sighted/throw-away code and NOT a generic helper.
|
||||
struct IMGUI_API ImGuiSimpleColumns
|
||||
{
|
||||
int Count;
|
||||
float Spacing;
|
||||
float Width, NextWidth;
|
||||
float Pos[8], NextWidths[8];
|
||||
|
||||
ImGuiSimpleColumns();
|
||||
void Update(int count, float spacing, bool clear);
|
||||
float DeclColumns(float w0, float w1, float w2);
|
||||
float CalcExtraSpace(float avail_w);
|
||||
};
|
||||
|
||||
// Internal state of the currently focused/edited text input box
|
||||
struct IMGUI_API ImGuiTextEditState
|
||||
{
|
||||
ImGuiID Id; // widget id owning the text state
|
||||
ImVector<ImWchar> Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer.
|
||||
ImVector<char> InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered)
|
||||
ImVector<char> TempTextBuffer;
|
||||
int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format.
|
||||
int BufSizeA; // end-user buffer size
|
||||
float ScrollX;
|
||||
ImGuiStb::STB_TexteditState StbState;
|
||||
float CursorAnim;
|
||||
bool CursorFollow;
|
||||
bool SelectedAllMouseLock;
|
||||
|
||||
ImGuiTextEditState() { memset(this, 0, sizeof(*this)); }
|
||||
void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking
|
||||
void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); }
|
||||
bool HasSelection() const { return StbState.select_start != StbState.select_end; }
|
||||
void ClearSelection() { StbState.select_start = StbState.select_end = StbState.cursor; }
|
||||
void SelectAll() { StbState.select_start = 0; StbState.select_end = CurLenW; StbState.cursor = StbState.select_end; StbState.has_preferred_x = false; }
|
||||
void OnKeyPressed(int key);
|
||||
};
|
||||
|
||||
// Data saved in imgui.ini file
|
||||
struct ImGuiIniData
|
||||
{
|
||||
char* Name;
|
||||
ImGuiID Id;
|
||||
ImVec2 Pos;
|
||||
ImVec2 Size;
|
||||
bool Collapsed;
|
||||
};
|
||||
|
||||
// Mouse cursor data (used when io.MouseDrawCursor is set)
|
||||
struct ImGuiMouseCursorData
|
||||
{
|
||||
ImGuiMouseCursor Type;
|
||||
ImVec2 HotOffset;
|
||||
ImVec2 Size;
|
||||
ImVec2 TexUvMin[2];
|
||||
ImVec2 TexUvMax[2];
|
||||
};
|
||||
|
||||
// Storage for current popup stack
|
||||
struct ImGuiPopupRef
|
||||
{
|
||||
ImGuiID PopupId; // Set on OpenPopup()
|
||||
ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup()
|
||||
ImGuiWindow* ParentWindow; // Set on OpenPopup()
|
||||
ImGuiID ParentMenuSet; // Set on OpenPopup()
|
||||
ImVec2 MousePosOnOpen; // Copy of mouse position at the time of opening popup
|
||||
|
||||
ImGuiPopupRef(ImGuiID id, ImGuiWindow* parent_window, ImGuiID parent_menu_set, const ImVec2& mouse_pos) { PopupId = id; Window = NULL; ParentWindow = parent_window; ParentMenuSet = parent_menu_set; MousePosOnOpen = mouse_pos; }
|
||||
};
|
||||
|
||||
// Main state for ImGui
|
||||
struct ImGuiContext
|
||||
{
|
||||
bool Initialized;
|
||||
ImGuiIO IO;
|
||||
ImGuiStyle Style;
|
||||
ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back()
|
||||
float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize()
|
||||
float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Size of characters.
|
||||
ImVec2 FontTexUvWhitePixel; // (Shortcut) == Font->TexUvWhitePixel
|
||||
|
||||
float Time;
|
||||
int FrameCount;
|
||||
int FrameCountEnded;
|
||||
int FrameCountRendered;
|
||||
ImVector<ImGuiWindow*> Windows;
|
||||
ImVector<ImGuiWindow*> WindowsSortBuffer;
|
||||
ImGuiWindow* CurrentWindow; // Being drawn into
|
||||
ImVector<ImGuiWindow*> CurrentWindowStack;
|
||||
ImGuiWindow* FocusedWindow; // Will catch keyboard inputs
|
||||
ImGuiWindow* HoveredWindow; // Will catch mouse inputs
|
||||
ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only)
|
||||
ImGuiID HoveredId; // Hovered widget
|
||||
bool HoveredIdAllowOverlap;
|
||||
ImGuiID HoveredIdPreviousFrame;
|
||||
ImGuiID ActiveId; // Active widget
|
||||
ImGuiID ActiveIdPreviousFrame;
|
||||
bool ActiveIdIsAlive;
|
||||
bool ActiveIdIsJustActivated; // Set at the time of activation for one frame
|
||||
bool ActiveIdAllowOverlap; // Set only by active widget
|
||||
ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)
|
||||
ImGuiWindow* ActiveIdWindow;
|
||||
ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window.
|
||||
ImGuiID MovedWindowMoveId; // == MovedWindow->RootWindow->MoveId
|
||||
ImVector<ImGuiIniData> Settings; // .ini Settings
|
||||
float SettingsDirtyTimer; // Save .ini Settings on disk when time reaches zero
|
||||
ImVector<ImGuiColMod> ColorModifiers; // Stack for PushStyleColor()/PopStyleColor()
|
||||
ImVector<ImGuiStyleMod> StyleModifiers; // Stack for PushStyleVar()/PopStyleVar()
|
||||
ImVector<ImFont*> FontStack; // Stack for PushFont()/PopFont()
|
||||
ImVector<ImGuiPopupRef> OpenPopupStack; // Which popups are open (persistent)
|
||||
ImVector<ImGuiPopupRef> CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame)
|
||||
|
||||
// Storage for SetNexWindow** and SetNextTreeNode*** functions
|
||||
ImVec2 SetNextWindowPosVal;
|
||||
ImVec2 SetNextWindowSizeVal;
|
||||
ImVec2 SetNextWindowContentSizeVal;
|
||||
bool SetNextWindowCollapsedVal;
|
||||
ImGuiSetCond SetNextWindowPosCond;
|
||||
ImGuiSetCond SetNextWindowSizeCond;
|
||||
ImGuiSetCond SetNextWindowContentSizeCond;
|
||||
ImGuiSetCond SetNextWindowCollapsedCond;
|
||||
ImRect SetNextWindowSizeConstraintRect; // Valid if 'SetNextWindowSizeConstraint' is true
|
||||
ImGuiSizeConstraintCallback SetNextWindowSizeConstraintCallback;
|
||||
void* SetNextWindowSizeConstraintCallbackUserData;
|
||||
bool SetNextWindowSizeConstraint;
|
||||
bool SetNextWindowFocus;
|
||||
bool SetNextTreeNodeOpenVal;
|
||||
ImGuiSetCond SetNextTreeNodeOpenCond;
|
||||
|
||||
// Render
|
||||
ImDrawData RenderDrawData; // Main ImDrawData instance to pass render information to the user
|
||||
ImVector<ImDrawList*> RenderDrawLists[3];
|
||||
float ModalWindowDarkeningRatio;
|
||||
ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays
|
||||
ImGuiMouseCursor MouseCursor;
|
||||
ImGuiMouseCursorData MouseCursorData[ImGuiMouseCursor_Count_];
|
||||
|
||||
// Widget state
|
||||
ImGuiTextEditState InputTextState;
|
||||
ImFont InputTextPasswordFont;
|
||||
ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc.
|
||||
ImGuiStorage ColorEditModeStorage; // Store user selection of color edit mode
|
||||
float DragCurrentValue; // Currently dragged value, always float, not rounded by end-user precision settings
|
||||
ImVec2 DragLastMouseDelta;
|
||||
float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio
|
||||
float DragSpeedScaleSlow;
|
||||
float DragSpeedScaleFast;
|
||||
ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage?
|
||||
char Tooltip[1024];
|
||||
char* PrivateClipboard; // If no custom clipboard handler is defined
|
||||
ImVec2 OsImePosRequest, OsImePosSet; // Cursor position request & last passed to the OS Input Method Editor
|
||||
|
||||
// Logging
|
||||
bool LogEnabled;
|
||||
FILE* LogFile; // If != NULL log to stdout/ file
|
||||
ImGuiTextBuffer* LogClipboard; // Else log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators.
|
||||
int LogStartDepth;
|
||||
int LogAutoExpandMaxDepth;
|
||||
|
||||
// Misc
|
||||
float FramerateSecPerFrame[120]; // calculate estimate of framerate for user
|
||||
int FramerateSecPerFrameIdx;
|
||||
float FramerateSecPerFrameAccum;
|
||||
int CaptureMouseNextFrame; // explicit capture via CaptureInputs() sets those flags
|
||||
int CaptureKeyboardNextFrame;
|
||||
char TempBuffer[1024*3+1]; // temporary text buffer
|
||||
|
||||
ImGuiContext()
|
||||
{
|
||||
Initialized = false;
|
||||
Font = NULL;
|
||||
FontSize = FontBaseSize = 0.0f;
|
||||
FontTexUvWhitePixel = ImVec2(0.0f, 0.0f);
|
||||
|
||||
Time = 0.0f;
|
||||
FrameCount = 0;
|
||||
FrameCountEnded = FrameCountRendered = -1;
|
||||
CurrentWindow = NULL;
|
||||
FocusedWindow = NULL;
|
||||
HoveredWindow = NULL;
|
||||
HoveredRootWindow = NULL;
|
||||
HoveredId = 0;
|
||||
HoveredIdAllowOverlap = false;
|
||||
HoveredIdPreviousFrame = 0;
|
||||
ActiveId = 0;
|
||||
ActiveIdPreviousFrame = 0;
|
||||
ActiveIdIsAlive = false;
|
||||
ActiveIdIsJustActivated = false;
|
||||
ActiveIdAllowOverlap = false;
|
||||
ActiveIdClickOffset = ImVec2(-1,-1);
|
||||
ActiveIdWindow = NULL;
|
||||
MovedWindow = NULL;
|
||||
MovedWindowMoveId = 0;
|
||||
SettingsDirtyTimer = 0.0f;
|
||||
|
||||
SetNextWindowPosVal = ImVec2(0.0f, 0.0f);
|
||||
SetNextWindowSizeVal = ImVec2(0.0f, 0.0f);
|
||||
SetNextWindowCollapsedVal = false;
|
||||
SetNextWindowPosCond = 0;
|
||||
SetNextWindowSizeCond = 0;
|
||||
SetNextWindowContentSizeCond = 0;
|
||||
SetNextWindowCollapsedCond = 0;
|
||||
SetNextWindowSizeConstraintRect = ImRect();
|
||||
SetNextWindowSizeConstraintCallback = NULL;
|
||||
SetNextWindowSizeConstraintCallbackUserData = NULL;
|
||||
SetNextWindowSizeConstraint = false;
|
||||
SetNextWindowFocus = false;
|
||||
SetNextTreeNodeOpenVal = false;
|
||||
SetNextTreeNodeOpenCond = 0;
|
||||
|
||||
ScalarAsInputTextId = 0;
|
||||
DragCurrentValue = 0.0f;
|
||||
DragLastMouseDelta = ImVec2(0.0f, 0.0f);
|
||||
DragSpeedDefaultRatio = 1.0f / 100.0f;
|
||||
DragSpeedScaleSlow = 0.01f;
|
||||
DragSpeedScaleFast = 10.0f;
|
||||
ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f);
|
||||
memset(Tooltip, 0, sizeof(Tooltip));
|
||||
PrivateClipboard = NULL;
|
||||
OsImePosRequest = OsImePosSet = ImVec2(-1.0f, -1.0f);
|
||||
|
||||
ModalWindowDarkeningRatio = 0.0f;
|
||||
OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging
|
||||
MouseCursor = ImGuiMouseCursor_Arrow;
|
||||
memset(MouseCursorData, 0, sizeof(MouseCursorData));
|
||||
|
||||
LogEnabled = false;
|
||||
LogFile = NULL;
|
||||
LogClipboard = NULL;
|
||||
LogStartDepth = 0;
|
||||
LogAutoExpandMaxDepth = 2;
|
||||
|
||||
memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
|
||||
FramerateSecPerFrameIdx = 0;
|
||||
FramerateSecPerFrameAccum = 0.0f;
|
||||
CaptureMouseNextFrame = CaptureKeyboardNextFrame = -1;
|
||||
memset(TempBuffer, 0, sizeof(TempBuffer));
|
||||
}
|
||||
};
|
||||
|
||||
// Transient per-window data, reset at the beginning of the frame
|
||||
// FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiDrawContext is quite tenuous and could be reconsidered.
|
||||
struct IMGUI_API ImGuiDrawContext
|
||||
{
|
||||
ImVec2 CursorPos;
|
||||
ImVec2 CursorPosPrevLine;
|
||||
ImVec2 CursorStartPos;
|
||||
ImVec2 CursorMaxPos; // Implicitly calculate the size of our contents, always extending. Saved into window->SizeContents at the end of the frame
|
||||
float CurrentLineHeight;
|
||||
float CurrentLineTextBaseOffset;
|
||||
float PrevLineHeight;
|
||||
float PrevLineTextBaseOffset;
|
||||
float LogLinePosY;
|
||||
int TreeDepth;
|
||||
ImGuiID LastItemId;
|
||||
ImRect LastItemRect;
|
||||
bool LastItemHoveredAndUsable; // Item rectangle is hovered, and its window is currently interactable with (not blocked by a popup preventing access to the window)
|
||||
bool LastItemHoveredRect; // Item rectangle is hovered, but its window may or not be currently interactable with (might be blocked by a popup preventing access to the window)
|
||||
bool MenuBarAppending;
|
||||
float MenuBarOffsetX;
|
||||
ImVector<ImGuiWindow*> ChildWindows;
|
||||
ImGuiStorage* StateStorage;
|
||||
ImGuiLayoutType LayoutType;
|
||||
|
||||
// We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings.
|
||||
float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window
|
||||
float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f]
|
||||
bool AllowKeyboardFocus; // == AllowKeyboardFocusStack.back() [empty == true]
|
||||
bool ButtonRepeat; // == ButtonRepeatStack.back() [empty == false]
|
||||
ImVector<float> ItemWidthStack;
|
||||
ImVector<float> TextWrapPosStack;
|
||||
ImVector<bool> AllowKeyboardFocusStack;
|
||||
ImVector<bool> ButtonRepeatStack;
|
||||
ImVector<ImGuiGroupData>GroupStack;
|
||||
ImGuiColorEditMode ColorEditMode;
|
||||
int StackSizesBackup[6]; // Store size of various stacks for asserting
|
||||
|
||||
float IndentX; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.)
|
||||
float GroupOffsetX;
|
||||
float ColumnsOffsetX; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API.
|
||||
int ColumnsCurrent;
|
||||
int ColumnsCount;
|
||||
float ColumnsMinX;
|
||||
float ColumnsMaxX;
|
||||
float ColumnsStartPosY;
|
||||
float ColumnsCellMinY;
|
||||
float ColumnsCellMaxY;
|
||||
bool ColumnsShowBorders;
|
||||
ImGuiID ColumnsSetId;
|
||||
ImVector<ImGuiColumnData> ColumnsData;
|
||||
|
||||
ImGuiDrawContext()
|
||||
{
|
||||
CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f);
|
||||
CurrentLineHeight = PrevLineHeight = 0.0f;
|
||||
CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f;
|
||||
LogLinePosY = -1.0f;
|
||||
TreeDepth = 0;
|
||||
LastItemId = 0;
|
||||
LastItemRect = ImRect(0.0f,0.0f,0.0f,0.0f);
|
||||
LastItemHoveredAndUsable = LastItemHoveredRect = false;
|
||||
MenuBarAppending = false;
|
||||
MenuBarOffsetX = 0.0f;
|
||||
StateStorage = NULL;
|
||||
LayoutType = ImGuiLayoutType_Vertical;
|
||||
ItemWidth = 0.0f;
|
||||
ButtonRepeat = false;
|
||||
AllowKeyboardFocus = true;
|
||||
TextWrapPos = -1.0f;
|
||||
ColorEditMode = ImGuiColorEditMode_RGB;
|
||||
memset(StackSizesBackup, 0, sizeof(StackSizesBackup));
|
||||
|
||||
IndentX = 0.0f;
|
||||
GroupOffsetX = 0.0f;
|
||||
ColumnsOffsetX = 0.0f;
|
||||
ColumnsCurrent = 0;
|
||||
ColumnsCount = 1;
|
||||
ColumnsMinX = ColumnsMaxX = 0.0f;
|
||||
ColumnsStartPosY = 0.0f;
|
||||
ColumnsCellMinY = ColumnsCellMaxY = 0.0f;
|
||||
ColumnsShowBorders = true;
|
||||
ColumnsSetId = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Windows data
|
||||
struct IMGUI_API ImGuiWindow
|
||||
{
|
||||
char* Name;
|
||||
ImGuiID ID; // == ImHash(Name)
|
||||
ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_
|
||||
int IndexWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0.
|
||||
ImVec2 PosFloat;
|
||||
ImVec2 Pos; // Position rounded-up to nearest pixel
|
||||
ImVec2 Size; // Current size (==SizeFull or collapsed title bar size)
|
||||
ImVec2 SizeFull; // Size when non collapsed
|
||||
ImVec2 SizeContents; // Size of contents (== extents reach of the drawing cursor) from previous frame
|
||||
ImVec2 SizeContentsExplicit; // Size of contents explicitly set by the user via SetNextWindowContentSize()
|
||||
ImRect ContentsRegionRect; // Maximum visible content position in window coordinates. ~~ (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis
|
||||
ImVec2 WindowPadding; // Window padding at the time of begin. We need to lock it, in particular manipulation of the ShowBorder would have an effect
|
||||
ImGuiID MoveId; // == window->GetID("#MOVE")
|
||||
ImVec2 Scroll;
|
||||
ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change)
|
||||
ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered
|
||||
bool ScrollbarX, ScrollbarY;
|
||||
ImVec2 ScrollbarSizes;
|
||||
float BorderSize;
|
||||
bool Active; // Set to true on Begin()
|
||||
bool WasActive;
|
||||
bool Accessed; // Set to true when any widget access the current window
|
||||
bool Collapsed; // Set when collapsing window to become only title-bar
|
||||
bool SkipItems; // == Visible && !Collapsed
|
||||
int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs)
|
||||
ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling)
|
||||
int AutoFitFramesX, AutoFitFramesY;
|
||||
bool AutoFitOnlyGrows;
|
||||
int AutoPosLastDirection;
|
||||
int HiddenFrames;
|
||||
int SetWindowPosAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowPos() call will succeed with this particular flag.
|
||||
int SetWindowSizeAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowSize() call will succeed with this particular flag.
|
||||
int SetWindowCollapsedAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowCollapsed() call will succeed with this particular flag.
|
||||
bool SetWindowPosCenterWanted;
|
||||
|
||||
ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame
|
||||
ImVector<ImGuiID> IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack
|
||||
ImRect ClipRect; // = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2.
|
||||
ImRect WindowRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window.
|
||||
int LastFrameActive;
|
||||
float ItemWidthDefault;
|
||||
ImGuiSimpleColumns MenuColumns; // Simplified columns storage for menu items
|
||||
ImGuiStorage StateStorage;
|
||||
float FontWindowScale; // Scale multiplier per-window
|
||||
ImDrawList* DrawList;
|
||||
ImGuiWindow* RootWindow; // If we are a child window, this is pointing to the first non-child parent window. Else point to ourself.
|
||||
ImGuiWindow* RootNonPopupWindow; // If we are a child window, this is pointing to the first non-child non-popup parent window. Else point to ourself.
|
||||
ImGuiWindow* ParentWindow; // If we are a child window, this is pointing to our parent window. Else point to NULL.
|
||||
|
||||
// Navigation / Focus
|
||||
int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister()
|
||||
int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through)
|
||||
int FocusIdxAllRequestCurrent; // Item being requested for focus
|
||||
int FocusIdxTabRequestCurrent; // Tab-able item being requested for focus
|
||||
int FocusIdxAllRequestNext; // Item being requested for focus, for next update (relies on layout to be stable between the frame pressing TAB and the next frame)
|
||||
int FocusIdxTabRequestNext; // "
|
||||
|
||||
public:
|
||||
ImGuiWindow(const char* name);
|
||||
~ImGuiWindow();
|
||||
|
||||
ImGuiID GetID(const char* str, const char* str_end = NULL);
|
||||
ImGuiID GetID(const void* ptr);
|
||||
ImGuiID GetIDNoKeepAlive(const char* str, const char* str_end = NULL);
|
||||
|
||||
ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); }
|
||||
float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; }
|
||||
float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; }
|
||||
ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); }
|
||||
float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; }
|
||||
ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Internal API
|
||||
// No guarantee of forward compatibility here.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
// We should always have a CurrentWindow in the stack (there is an implicit "Debug" window)
|
||||
// If this ever crash because g.CurrentWindow is NULL it means that either
|
||||
// - ImGui::NewFrame() has never been called, which is illegal.
|
||||
// - You are calling ImGui functions after ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal.
|
||||
inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; }
|
||||
inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->Accessed = true; return g.CurrentWindow; }
|
||||
IMGUI_API ImGuiWindow* GetParentWindow();
|
||||
IMGUI_API ImGuiWindow* FindWindowByName(const char* name);
|
||||
IMGUI_API void FocusWindow(ImGuiWindow* window);
|
||||
|
||||
IMGUI_API void EndFrame(); // Ends the ImGui frame. Automatically called by Render()! you most likely don't need to ever call that yourself directly. If you don't need to render you can call EndFrame() but you'll have wasted CPU already. If you don't need to render, don't create any windows instead!
|
||||
|
||||
IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window);
|
||||
IMGUI_API void ClearActiveID();
|
||||
IMGUI_API void SetHoveredID(ImGuiID id);
|
||||
IMGUI_API void KeepAliveID(ImGuiID id);
|
||||
|
||||
IMGUI_API void ItemSize(const ImVec2& size, float text_offset_y = 0.0f);
|
||||
IMGUI_API void ItemSize(const ImRect& bb, float text_offset_y = 0.0f);
|
||||
IMGUI_API bool ItemAdd(const ImRect& bb, const ImGuiID* id);
|
||||
IMGUI_API bool IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged);
|
||||
IMGUI_API bool IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs = false);
|
||||
IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop = true); // Return true if focus is requested
|
||||
IMGUI_API void FocusableItemUnregister(ImGuiWindow* window);
|
||||
IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y);
|
||||
IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);
|
||||
|
||||
IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing);
|
||||
|
||||
// NB: All position are in absolute pixels coordinates (not window coordinates)
|
||||
// FIXME: All those functions are a mess and needs to be refactored into something decent. AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION.
|
||||
// We need: a sort of symbol library, preferably baked into font atlas when possible + decent text rendering helpers.
|
||||
IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true);
|
||||
IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);
|
||||
IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL);
|
||||
IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f);
|
||||
IMGUI_API void RenderCollapseTriangle(ImVec2 pos, bool is_open, float scale = 1.0f);
|
||||
IMGUI_API void RenderBullet(ImVec2 pos);
|
||||
IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col);
|
||||
IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text.
|
||||
|
||||
IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0);
|
||||
IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0);
|
||||
IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius);
|
||||
|
||||
IMGUI_API bool SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags = 0);
|
||||
IMGUI_API bool SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power);
|
||||
IMGUI_API bool SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format);
|
||||
|
||||
IMGUI_API bool DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power);
|
||||
IMGUI_API bool DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power);
|
||||
IMGUI_API bool DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format);
|
||||
|
||||
IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback = NULL, void* user_data = NULL);
|
||||
IMGUI_API bool InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags);
|
||||
IMGUI_API bool InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags);
|
||||
IMGUI_API bool InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags);
|
||||
IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision);
|
||||
|
||||
IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL);
|
||||
IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging
|
||||
IMGUI_API void TreePushRawID(ImGuiID id);
|
||||
|
||||
IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size);
|
||||
|
||||
IMGUI_API int ParseFormatPrecision(const char* fmt, int default_value);
|
||||
IMGUI_API float RoundScalar(float value, int decimal_precision);
|
||||
|
||||
} // namespace ImGui
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (pop)
|
||||
#endif
|
4467
Source/ThirdParty/ImGuiLibrary/Private/imgui_tables.cpp
vendored
Normal file
4467
Source/ThirdParty/ImGuiLibrary/Private/imgui_tables.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10434
Source/ThirdParty/ImGuiLibrary/Private/imgui_widgets.cpp
vendored
Normal file
10434
Source/ThirdParty/ImGuiLibrary/Private/imgui_widgets.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,19 @@
|
||||
// stb_rect_pack.h - v0.10 - public domain - rectangle packing
|
||||
// [DEAR IMGUI]
|
||||
// This is a slightly modified version of stb_rect_pack.h 1.01.
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
//
|
||||
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||
// Sean Barrett 2014
|
||||
//
|
||||
// Useful for e.g. packing rectangular textures into an atlas.
|
||||
// Does not do rotation.
|
||||
//
|
||||
// Before #including,
|
||||
//
|
||||
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||
//
|
||||
// in the file that you want to have the implementation.
|
||||
//
|
||||
// Not necessarily the awesomest packing method, but better than
|
||||
// the totally naive one in stb_truetype (which is primarily what
|
||||
// this is meant to replace).
|
||||
@ -27,11 +37,18 @@
|
||||
// Sean Barrett
|
||||
// Minor features
|
||||
// Martins Mozeiko
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bugfixes / warning fixes
|
||||
// Jeremy Jaussaud
|
||||
// Fabian Giesen
|
||||
//
|
||||
// Version history:
|
||||
//
|
||||
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||
// 0.99 (2019-02-07) warning fixes
|
||||
// 0.11 (2017-03-03) return packing success/fail result
|
||||
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||
// 0.09 (2016-08-27) fix compiler warnings
|
||||
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||
@ -43,9 +60,7 @@
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// This software is dual-licensed to the public domain and under the following
|
||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
// publish, and distribute this file as you see fit.
|
||||
// See end of file for license information.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -71,13 +86,12 @@ typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
typedef int stbrp_coord;
|
||||
#else
|
||||
typedef unsigned short stbrp_coord;
|
||||
#endif
|
||||
|
||||
STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
#define STBRP__MAXVAL 0x7fffffff
|
||||
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
// Assign packed locations to rectangles. The rectangles are of type
|
||||
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||
// are 'num_rects' many of them.
|
||||
@ -98,6 +112,9 @@ STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int
|
||||
// arrays will probably produce worse packing results than calling it
|
||||
// a single time with the full rectangle array, but the option is
|
||||
// available.
|
||||
//
|
||||
// The function returns 1 if all of the rectangles were successfully
|
||||
// packed and 0 otherwise.
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
@ -202,8 +219,10 @@ struct stbrp_context
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBRP__NOTUSED(v) (void)(v)
|
||||
#define STBRP__CDECL __cdecl
|
||||
#else
|
||||
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||
#define STBRP__CDECL
|
||||
#endif
|
||||
|
||||
enum
|
||||
@ -246,9 +265,6 @@ STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_ou
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
#ifndef STBRP_LARGE_RECTS
|
||||
STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
|
||||
#endif
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
@ -267,11 +283,7 @@ STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height,
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
context->extra[1].y = (1<<30);
|
||||
#else
|
||||
context->extra[1].y = 65535;
|
||||
#endif
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
@ -343,6 +355,13 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
// if it can't possibly fit, bail immediately
|
||||
if (width > c->width || height > c->height) {
|
||||
fr.prev_link = NULL;
|
||||
fr.x = fr.y = 0;
|
||||
return fr;
|
||||
}
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
@ -406,11 +425,11 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height < c->height) {
|
||||
if (y + height <= c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
STBRP_ASSERT(y <= best_y);
|
||||
//STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
@ -488,17 +507,14 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
stbrp_node *L1 = NULL, *L2 = NULL;
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
L1 = cur;
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
L2 = cur;
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
@ -509,7 +525,7 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i
|
||||
return res;
|
||||
}
|
||||
|
||||
static int rect_height_compare(const void *a, const void *b)
|
||||
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
@ -520,40 +536,20 @@ static int rect_height_compare(const void *a, const void *b)
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
static int rect_width_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->w > q->w)
|
||||
return -1;
|
||||
if (p->w < q->w)
|
||||
return 1;
|
||||
return (p->h > q->h) ? -1 : (p->h < q->h);
|
||||
}
|
||||
|
||||
static int rect_original_order(const void *a, const void *b)
|
||||
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
#define STBRP__MAXVAL 0xffffffff
|
||||
#else
|
||||
#define STBRP__MAXVAL 0xffff
|
||||
#endif
|
||||
|
||||
STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i;
|
||||
int i, all_rects_packed = 1;
|
||||
|
||||
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
#ifndef STBRP_LARGE_RECTS
|
||||
STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff);
|
||||
#endif
|
||||
}
|
||||
|
||||
// sort according to heuristic
|
||||
@ -576,8 +572,56 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n
|
||||
// unsort
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
// set was_packed flags
|
||||
for (i=0; i < num_rects; ++i)
|
||||
// set was_packed flags and all_rects_packed status
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
if (!rects[i].was_packed)
|
||||
all_rects_packed = 0;
|
||||
}
|
||||
|
||||
// return the all_rects_packed status
|
||||
return all_rects_packed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
File diff suppressed because it is too large
Load Diff
226
Source/ThirdParty/ImGuiLibrary/README.md
vendored
226
Source/ThirdParty/ImGuiLibrary/README.md
vendored
@ -1,226 +0,0 @@
|
||||
dear imgui,
|
||||
=====
|
||||
[](https://travis-ci.org/ocornut/imgui)
|
||||
[](https://scan.coverity.com/projects/4720)
|
||||
|
||||
(This library is free and will stay free, but needs your support to sustain its development. There are lots of desirable new features and maintenance to do. If you work for a company using ImGui or have the means to do so, please consider financial support)
|
||||
|
||||
[](http://www.patreon.com/imgui) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5Q73FPZ9C526U)
|
||||
|
||||
dear imgui (AKA ImGui), is a bloat-free graphical user interface library for C++. It outputs optimized vertex buffers that you can render anytime in your 3D-pipeline enabled application. It is fast, portable, renderer agnostic and self-contained (no external dependencies).
|
||||
|
||||
ImGui is designed to enable fast iteration and empower programmers to create content creation tools and visualization/ debug tools (as opposed to UI for the average end-user). It favors simplicity and productivity toward this goal, and thus lacks certain features normally found in more high-level libraries.
|
||||
|
||||
ImGui is particularly suited to integration in realtime 3D applications, fullscreen applications, embedded applications, games, or any applications on consoles platforms where operating system features are non-standard.
|
||||
|
||||
ImGui is self-contained within a few files that you can easily copy and compile into your application/engine:
|
||||
|
||||
- imgui.cpp
|
||||
- imgui.h
|
||||
- imgui_demo.cpp
|
||||
- imgui_draw.cpp
|
||||
- imgui_internal.h
|
||||
- imconfig.h (empty by default, user-editable)
|
||||
- stb_rect_pack.h
|
||||
- stb_textedit.h
|
||||
- stb_truetype.h
|
||||
|
||||
No specific build process is required. You can add the .cpp files to your project or #include them from an existing file.
|
||||
|
||||
Your code passes mouse/keyboard inputs and settings to ImGui (see example applications for more details). After ImGui is setup, you can use it like in this example:
|
||||
|
||||

|
||||
|
||||
ImGui outputs vertex buffers and simple command-lists that you can render in your application. The number of draw calls and state changes is typically very small. Because it doesn't know or touch graphics state directly, you can call ImGui commands anywhere in your code (e.g. in the middle of a running algorithm, or in the middle of your own rendering process). Refer to the sample applications in the examples/ folder for instructions on how to integrate ImGui with your existing codebase.
|
||||
|
||||
_A common misunderstanding is to think that immediate mode gui == immediate mode rendering, which usually implies hammering your driver/GPU with a bunch of inefficient draw calls and state changes, as the gui functions as called by the user. This is NOT what Dear ImGui does. Dear ImGui outputs vertex buffers and a small list of draw calls batches. It never touches your GPU directly. The draw call batches are decently optimal and you can render them later, in your app or even remotely._
|
||||
|
||||
ImGui allows you create elaborate tools as well as very short-lived ones. On the extreme side of short-liveness: using the Edit&Continue feature of modern compilers you can add a few widgets to tweaks variables while your application is running, and remove the code a minute later! ImGui is not just for tweaking values. You can use it to trace a running algorithm by just emitting text commands. You can use it along with your own reflection data to browse your dataset live. You can use it to expose the internals of a subsystem in your engine, to create a logger, an inspection tool, a profiler, a debugger, etc.
|
||||
|
||||
Binaries/Demo
|
||||
-------------
|
||||
|
||||
You should be able to build the examples from sources (tested on Windows/Mac/Linux). If you don't, let me know! If you want to have a quick look at the features of ImGui, you can download Windows binaries of the demo app here.
|
||||
- [imgui-demo-binaries-20161113.zip](http://www.miracleworld.net/imgui/binaries/imgui-demo-binaries-20161113.zip) (Windows binaries, ImGui 1.49+ 2016/11/13, 5 executables, 588 KB)
|
||||
|
||||
Bindings
|
||||
--------
|
||||
|
||||
_NB: those third-party bindings may be more or less maintained, more or less close to the spirit of original API and therefore I cannot give much guarantee about them. People who create language bindings sometimes haven't used the C++ API themselves (for the good reason that they aren't C++ users). ImGui was designed with C++ in mind and some of the subtleties may be lost in translation with other languages. If your language supports it, I would suggest replicating the function overloading and default parameters used in the original, else the API may be harder to use. In doubt, please check the original C++ version first!_
|
||||
|
||||
_Integrating Dear ImGui within your custom engine is a matter of wiring mouse/keyboard inputs and providing a render function that can bind a texture and render simple textured triangles. The examples/ folder is populated with applications doing just that. If you are an experienced programmer it should take you less than an hour to integrate Dear ImGui in your custom engine, but make sure to spend time reading the FAQ, the comments and other documentation!_
|
||||
|
||||
Languages:
|
||||
- cimgui: thin c-api wrapper for ImGui https://github.com/Extrawurst/cimgui
|
||||
- ImGui.NET: An ImGui wrapper for .NET Core https://github.com/mellinoe/ImGui.NET
|
||||
- imgui-rs: Rust bindings for dear imgui https://github.com/Gekkio/imgui-rs
|
||||
- DerelictImgui: Dynamic bindings for the D programming language: https://github.com/Extrawurst/DerelictImgui
|
||||
- CyImGui: Python bindings for dear imgui using Cython: https://github.com/chromy/cyimgui
|
||||
- pyimgui: Another Python bindings for dear imgui: https://github.com/swistakm/pyimgui
|
||||
- LUA: https://github.com/patrickriordan/imgui_lua_bindings
|
||||
|
||||
Frameworks:
|
||||
- Main ImGui repository include examples for DirectX9, DirectX10, DirectX11, OpenGL2/3, Vulkan, Allegro 5, SDL+GL2/3, iOS and Marmalade: https://github.com/ocornut/imgui/tree/master/examples
|
||||
- Unmerged PR: DirectX12 example (with issues) https://github.com/ocornut/imgui/pull/301
|
||||
- Unmerged PR: SDL2 + OpenGLES + Emscripten example https://github.com/ocornut/imgui/pull/336
|
||||
- Unmerged PR: FreeGlut + OpenGL2 example https://github.com/ocornut/imgui/pull/801
|
||||
- Unmerged PR: Native Win32 and OSX example https://github.com/ocornut/imgui/pull/281
|
||||
- Unmerged PR: Android Example https://github.com/ocornut/imgui/pull/421
|
||||
- Cinder backend for dear imgui https://github.com/simongeilfus/Cinder-ImGui
|
||||
- FlexGUI: Flexium/SFML backend for dear imgui https://github.com/DXsmiley/FlexGUI
|
||||
- IrrIMGUI: Irrlicht backend for dear imgui https://github.com/ZahlGraf/IrrIMGUI
|
||||
- LÖVE backend for dear imgui https://github.com/slages/love-imgui
|
||||
- Ogre backend for dear imgui https://bitbucket.org/LMCrashy/ogreimgui/src
|
||||
- ofxImGui: openFrameworks backend for dear imgui https://github.com/jvcleave/ofxImGui
|
||||
- SFML backend for dear imgui https://github.com/EliasD/imgui-sfml
|
||||
- SFML backend for dear imgui https://github.com/Mischa-Alff/imgui-backends
|
||||
- cocos2d-x with imgui https://github.com/c0i/imguix https://github.com/ocornut/imgui/issues/551
|
||||
- NanoRT: software raytraced version https://github.com/syoyo/imgui/tree/nanort/examples/raytrace_example
|
||||
|
||||
For other bindings: see [this page](https://github.com/ocornut/imgui/wiki/Links/).
|
||||
Please contact me with the Issues tracker or Twitter to fix/update this list.
|
||||
|
||||
Gallery
|
||||
-------
|
||||
|
||||
See the [Screenshots Thread](https://github.com/ocornut/imgui/issues/123) for some user creations.
|
||||
|
||||

|
||||
[](https://cloud.githubusercontent.com/assets/8225057/20628927/33e14cac-b329-11e6-80f6-9524e93b048a.png)
|
||||

|
||||
|
||||
[](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v148/profiler.png)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
ImGui can load TTF fonts. UTF-8 is supported for text display and input. Here using Arial Unicode font to display Japanese. Initialize custom font with:
|
||||
```
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->AddFontFromFileTTF("ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
|
||||
|
||||
// For Microsoft IME, pass your HWND to enable IME positioning:
|
||||
io.ImeWindowHandle = my_hwnd;
|
||||
```
|
||||

|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
The Immediate Mode GUI paradigm may at first appear unusual to some users. This is mainly because "Retained Mode" GUIs have been so widespread and predominant. The following links can give you a better understanding about how Immediate Mode GUIs works.
|
||||
- [Johannes 'johno' Norneby's article](http://www.johno.se/book/imgui.html).
|
||||
- [A presentation by Rickard Gustafsson and Johannes Algelind](http://www.cse.chalmers.se/edu/year/2011/course/TDA361/Advanced%20Computer%20Graphics/IMGUI.pdf).
|
||||
- [Jari Komppa's tutorial on building an ImGui library](http://iki.fi/sol/imgui/).
|
||||
- [Casey Muratori's original video that popularized the concept](https://mollyrocket.com/861).
|
||||
|
||||
See the [Links page](https://github.com/ocornut/imgui/wiki/Links) for third-party bindings to different languages and frameworks.
|
||||
|
||||
Frequently Asked Question (FAQ)
|
||||
-------------------------------
|
||||
|
||||
<b>Where is the documentation?</b>
|
||||
|
||||
- The documentation is at the top of imgui.cpp + effectively imgui.h.
|
||||
- Example code is in imgui_demo.cpp and particularly the ImGui::ShowTestWindow() function. It covers most features of ImGui so you can read the code and call the function itself to see its output.
|
||||
- Standalone example applications using e.g. OpenGL/DirectX are provided in the examples/ folder.
|
||||
- We obviously needs better documentation! Consider contributing or becoming a [Patron](http://www.patreon.com/imgui) to promote this effort.
|
||||
|
||||
<b>Why the odd dual naming, "dear imgui" vs "ImGui"?</b>
|
||||
|
||||
The library started its life and is best known as "ImGui" only due to the fact that I didn't give it a proper name when I released it. However, the term IMGUI (immediate-mode graphical user interface) was coined before and is being used in variety of other situations. It seemed confusing and unfair to hog the name. To reduce the ambiguity without affecting existing codebases, I have decided on an alternate, longer name "dear imgui" that people can use to refer to this specific library in ambiguous situations.
|
||||
|
||||
<b>How do I update to a newer version of ImGui?</b>
|
||||
<br><b>What is ImTextureID and how do I display an image?</b>
|
||||
<br><b>I integrated ImGui in my engine and the text or lines are blurry..</b>
|
||||
<br><b>I integrated ImGui in my engine and some elements are disappearing when I move windows around..</b>
|
||||
<br><b>How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on the purpose of labels/IDs.</b>
|
||||
<br><b>How can I tell when ImGui wants my mouse/keyboard inputs and when I can pass them to my application?</b>
|
||||
<br><b>How can I load a different font than the default?</b>
|
||||
<br><b>How can I easily use icons in my application?</b>
|
||||
<br><b>How can I load multiple fonts?</b>
|
||||
<br><b>How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?</b>
|
||||
<br><b>How can I use the drawing facilities without an ImGui window? (using ImDrawList API)</b>
|
||||
|
||||
See the FAQ in imgui.cpp for answers.
|
||||
|
||||
<b>How do you use ImGui on a platform that may not have a mouse or keyboard?</b>
|
||||
|
||||
I recommend using [Synergy](http://synergy-project.org) ([sources](https://github.com/symless/synergy)). In particular, the _src/micro/uSynergy.c_ file contains a small client that you can use on any platform to connect to your host PC. You can seamlessly use your PC input devices from a video game console or a tablet. ImGui allows to increase the hit box of widgets (via the _TouchPadding_ setting) to accommodate a little for the lack of precision of touch inputs, but it is recommended you use a mouse to allow optimising for screen real-estate.
|
||||
|
||||
<b>Can you create elaborate/serious tools with ImGui?</b>
|
||||
|
||||
Yes. I have written data browsers, debuggers, profilers and all sort of non-trivial tools with the library. In my experience the simplicity of the API is very empowering. Your UI runs close to your live data. Make the tools always-on and everybody in the team will be inclined to create new tools (as opposed to more "offline" UI toolkits where only a fraction of your team effectively creates tools).
|
||||
|
||||
ImGui is very programmer centric and the immediate-mode GUI paradigm might requires you to readjust some habits before you can realize its full potential. Many programmers have unfortunately been taught by their environment to make unnecessarily complicated things. ImGui is about making things that are simple, efficient and powerful.
|
||||
|
||||
<b>Is ImGui fast?</b>
|
||||
|
||||
Probably fast enough for most uses. Down to the foundation of its visual design, ImGui is engineered to be fairly performant both in term of CPU and GPU usage. Running elaborate code and creating elaborate UI will of course have a cost but ImGui aims to minimize it.
|
||||
|
||||
Mileage may vary but the following screenshot can give you a rough idea of the cost of running and rendering UI code (In the case of a trivial demo application like this one, your driver/os setup are likely to be the bottleneck. Testing performance as part of a real application is recommended).
|
||||
|
||||

|
||||
|
||||
This is showing framerate for the full application loop on my 2011 iMac running Windows 7, OpenGL, AMD Radeon HD 6700M with an optimized executable. In contrast, librairies featuring higher-quality rendering and layouting techniques may have a higher resources footprint.
|
||||
|
||||
If you intend to display large lists of items (say, 1000+) it can be beneficial for your code to perform clipping manually - one way is using helpers such as ImGuiListClipper - in order to avoid submitting them to ImGui in the first place. Even though ImGui will discard your clipped items it still needs to calculate their size and that overhead will add up if you have thousands of items. If you can handle clipping and height positionning yourself then browsing a list with millions of items isn't a problem.
|
||||
|
||||
<b>Can you reskin the look of ImGui?</b>
|
||||
|
||||
You can alter the look of the interface to some degree: changing colors, sizes, padding, rounding, fonts. However, as ImGui is designed and optimised to create debug tools, the amount of skinning you can apply is limited. There is only so much you can stray away from the default look and feel of the interface.
|
||||
|
||||
This is [LumixEngine](https://github.com/nem0/LumixEngine) with a minor skinning hack + a docking/tabs extension (both of which you can find in the Issues section and will eventually be merged).
|
||||
|
||||
[](https://cloud.githubusercontent.com/assets/8225057/13044612/59f07aec-d3cf-11e5-8ccb-39adf2e13e69.png)
|
||||
|
||||
<b>Why using C++ (as opposed to C)?</b>
|
||||
|
||||
ImGui takes advantage of a few C++ features for convenience but nothing anywhere Boost-insanity/quagmire. In particular, function overloading and default parameters are used to make the API easier to use and code more terse. Doing so I believe the API is sitting on a sweet spot and giving up on those features would make the API more cumbersome. Other features such as namespace, constructors and templates (in the case of the ImVector<> class) are also relied on as a convenience but could be removed.
|
||||
|
||||
There is an unofficial but reasonably maintained [c-api for ImGui](https://github.com/Extrawurst/cimgui) by Stephan Dilly. I would suggest using your target language functionality to try replicating the function overloading and default parameters used in C++ else the API may be harder to use. It was really designed with C++ in mind and may not make the same amount of sense with another language. Also see [Links](https://github.com/ocornut/imgui/wiki/Links) for third-party bindings to other languages.
|
||||
|
||||
Donate
|
||||
------
|
||||
|
||||
<b>Can I donate to support the development of ImGui?</b>
|
||||
|
||||
[](http://www.patreon.com/imgui) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5Q73FPZ9C526U)
|
||||
|
||||
I'm currently an independent developer and your contributions are useful. I have setup an [**ImGui Patreon page**](http://www.patreon.com/imgui) if you want to donate and enable me to spend more time improving the library. If your company uses ImGui please consider making a contribution. One-off donations are also greatly appreciated. I am available for hire to work on or with ImGui. Thanks!
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
Developed by [Omar Cornut](http://www.miracleworld.net) and every direct or indirect contributors to the GitHub. The early version of this library was developed with the support of [Media Molecule](http://www.mediamolecule.com) and first used internally on the game [Tearaway](http://tearaway.mediamolecule.com).
|
||||
|
||||
I first discovered imgui principles at [Q-Games](http://www.q-games.com) where Atman had dropped his own simple imgui implementation in the codebase, which I spent quite some time improving and thinking about. It turned out that Atman was exposed to the concept directly by working with Casey. When I moved to Media Molecule I rewrote a new library trying to overcome the flaws and limitations of the first one I've worked with. It became this library and since then I have spent an unreasonable amount of time iterating on it.
|
||||
|
||||
Embeds [ProggyClean.ttf](http://upperbounds.net) font by Tristan Grimmer (MIT license).
|
||||
|
||||
Embeds [stb_textedit.h, stb_truetype.h, stb_rectpack.h](https://github.com/nothings/stb/) by Sean Barrett (public domain).
|
||||
|
||||
Inspiration, feedback, and testing for early versions: Casey Muratori, Atman Binstock, Mikko Mononen, Emmanuel Briney, Stefan Kamoda, Anton Mikhailov, Matt Willis. And everybody posting feedback, questions and patches on the GitHub.
|
||||
|
||||
Ongoing ImGui development is financially supported on [**Patreon**](http://www.patreon.com/imgui).
|
||||
|
||||
Double-chocolate sponsors:
|
||||
- Media Molecule
|
||||
- Mobigame
|
||||
- Insomniac Games (sponsored the gamepad/keyboard navigation branch)
|
||||
- Aras Pranckevičius
|
||||
|
||||
Salty caramel supporters:
|
||||
- Jetha Chan, Wild Sheep Studio, Pastagames, Mārtiņš Možeiko, Daniel Collin, Recognition Robotics, Chris Genova, ikrima, Glenn Fiedler, Geoffrey Evans, Dakko Dakko.
|
||||
|
||||
Caramel supporters:
|
||||
- Michel Courtine, César Leblic, Dale Kim, Alex Evans, Rui Figueira, Paul Patrashcu, Jerome Lanquetot, Ctrl Alt Ninja, Paul Fleming, Neil Henning, Stephan Dilly, Neil Blakey-Milner, Aleksei, NeiloGD, Justin Paver, FiniteSol, Vincent Pancaldi, James Billot, Robin Hübner, furrtek, Eric, Simon Barratt, Game Atelier, Julian Bosch, Simon Lundmark, Vincent Hamm, Farhan Wali, Jeff Roberts, Matt Reyer, Colin Riley, Victor Martins, Josh Simmons, Garrett Hoofman, Sergio Gonzales, Andrew Berridge, Roy Eltham, Game Preservation Society, [Kit framework](http://svkonsult.se/kit), Josh Faust, Martin Donlon, Quinton, Felix.
|
||||
|
||||
And other supporters; thanks!
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Dear ImGui is licensed under the MIT License, see LICENSE for more information.
|
Loading…
Reference in New Issue
Block a user