diff --git a/.github/workflows/gdext.yml b/.github/workflows/gdext.yml index 2571a10..a5fa78b 100644 --- a/.github/workflows/gdext.yml +++ b/.github/workflows/gdext.yml @@ -230,6 +230,7 @@ jobs: pkgdir=~/out/imgui-godot-${plugin_ver} mkdir -p $pkgdir mv * $pkgdir + echo "pkgdir=$pkgdir" >> $GITHUB_OUTPUT - name: Upload package uses: actions/upload-artifact@v4 @@ -238,6 +239,12 @@ jobs: path: | ~/out + - name: Upload asset package + uses: actions/upload-artifact@v4 + with: + name: AssetLib-${{ steps.prep.outputs.pkgfn }} + path: ${{ steps.prep.outputs.pkgdir }}/addons + package_cs: name: 📦Package (C# only) runs-on: ubuntu-latest diff --git a/addons/imgui-godot/ImGuiGodot/ImGuiLayer.cs b/addons/imgui-godot/ImGuiGodot/ImGuiLayer.cs index dfdfa1d..47927ad 100644 --- a/addons/imgui-godot/ImGuiGodot/ImGuiLayer.cs +++ b/addons/imgui-godot/ImGuiGodot/ImGuiLayer.cs @@ -13,7 +13,6 @@ public partial class ImGuiLayer : CanvasLayer private Transform2D _finalTransform = Transform2D.Identity; private bool _visible = true; private Viewport _parentViewport = null!; - public Vector2I ViewportSize { get; private set; } public override void _EnterTree() { @@ -26,7 +25,7 @@ public override void _EnterTree() RenderingServer.CanvasItemSetParent(_canvasItem, GetCanvas()); State.Instance.Renderer.InitViewport(_subViewportRid); - State.Instance.Viewports.SetMainViewport(_parentViewport, _subViewportRid); + State.Instance.Viewports.SetMainWindow(GetWindow(), _subViewportRid); } public override void _Ready() @@ -59,7 +58,7 @@ private void OnChangeVisibility() public override void _Input(InputEvent @event) { - if (State.Instance.ProcessInput(@event)) + if (State.Instance.Input.ProcessInput(@event)) { _parentViewport.SetInputAsHandled(); } diff --git a/addons/imgui-godot/ImGuiGodot/Internal/BackendNative.cs b/addons/imgui-godot/ImGuiGodot/Internal/BackendNative.cs index e5d5bdb..f62a88e 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/BackendNative.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/BackendNative.cs @@ -16,6 +16,7 @@ private sealed class MethodName public static readonly StringName Connect = "Connect"; public static readonly StringName RebuildFontAtlas = "RebuildFontAtlas"; public static readonly StringName ResetFonts = "ResetFonts"; + public static readonly StringName SetMainViewport = "SetMainViewport"; public static readonly StringName SubViewport = "SubViewport"; public static readonly StringName ToolInit = "ToolInit"; } @@ -81,7 +82,7 @@ public void ResetFonts() } public void SetMainViewport(Viewport vp) { - throw new NotImplementedException(); + _gd.Call(MethodName.SetMainViewport, vp); } public bool SubViewportWidget(SubViewport svp) diff --git a/addons/imgui-godot/ImGuiGodot/Internal/Input.cs b/addons/imgui-godot/ImGuiGodot/Internal/Input.cs index fc74af1..576cd3e 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/Input.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/Input.cs @@ -119,7 +119,7 @@ protected void ProcessSubViewportWidget(InputEvent evt) } } - protected virtual bool HandleEvent(InputEvent evt) + protected bool HandleEvent(InputEvent evt) { var io = ImGui.GetIO(); bool consumed = false; @@ -236,7 +236,7 @@ protected virtual bool HandleEvent(InputEvent evt) return consumed; } - public bool ProcessInput(InputEvent evt) + public virtual bool ProcessInput(InputEvent evt) { ProcessSubViewportWidget(evt); return HandleEvent(evt); diff --git a/addons/imgui-godot/ImGuiGodot/Internal/InputLocal.cs b/addons/imgui-godot/ImGuiGodot/Internal/InputLocal.cs index fb9c534..f5a2af3 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/InputLocal.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/InputLocal.cs @@ -11,7 +11,7 @@ protected override void UpdateMousePos(ImGuiIOPtr io) // do not use global mouse position } - protected override bool HandleEvent(InputEvent evt) + public override bool ProcessInput(InputEvent evt) { // no support for SubViewport widgets @@ -23,7 +23,7 @@ protected override bool HandleEvent(InputEvent evt) mm.Dispose(); return io.WantCaptureMouse; } - return base.HandleEvent(evt); + return HandleEvent(evt); } } #endif diff --git a/addons/imgui-godot/ImGuiGodot/Internal/State.cs b/addons/imgui-godot/ImGuiGodot/Internal/State.cs index 0f7b8fd..4a8a37e 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/State.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/State.cs @@ -184,16 +184,5 @@ public void Render() ImGui.UpdatePlatformWindows(); Renderer.Render(); } - - /// - /// Send input event to ImGui - /// - /// - /// True if the InputEvent was consumed - /// - public bool ProcessInput(InputEvent evt) - { - return Input.ProcessInput(evt); - } } #endif diff --git a/addons/imgui-godot/ImGuiGodot/Internal/Viewports.cs b/addons/imgui-godot/ImGuiGodot/Internal/Viewports.cs index b33af29..b3e2afa 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/Viewports.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/Viewports.cs @@ -7,20 +7,7 @@ namespace ImGuiGodot.Internal; -internal interface IViewportWindow : IDisposable -{ - public void ShowWindow(); - public void SetWindowPos(Vector2I pos); - public Vector2I GetWindowPos(); - public void SetWindowSize(Vector2I size); - public Vector2I GetWindowSize(); - public void SetWindowFocus(); - public bool GetWindowFocus(); - public bool GetWindowMinimized(); - public void SetWindowTitle(string title); -} - -internal sealed class GodotImGuiWindow : IViewportWindow +internal sealed class GodotImGuiWindow { private readonly GCHandle _gcHandle; private readonly ImGuiViewportPtr _vp; @@ -150,68 +137,6 @@ public void SetWindowTitle(string title) } } -internal sealed class GodotImGuiSubViewport : IViewportWindow -{ - private readonly GCHandle _gcHandle; - private readonly SubViewport _svp; - - public GodotImGuiSubViewport(ImGuiViewportPtr vp, SubViewport svp, Rid mainSubViewport) - { - _gcHandle = GCHandle.Alloc(this); - vp.PlatformUserData = (IntPtr)_gcHandle; - vp.RendererUserData = (IntPtr)mainSubViewport.Id; - _svp = svp; - } - - public void Dispose() - { - if (_gcHandle.IsAllocated) - { - _gcHandle.Free(); - } - } - - public bool GetWindowFocus() - { - return true; - } - - public bool GetWindowMinimized() - { - return false; - } - - public Vector2I GetWindowPos() - { - return new(0, 0); - } - - public Vector2I GetWindowSize() - { - return _svp.Size; - } - - public void SetWindowFocus() - { - } - - public void SetWindowPos(Vector2I pos) - { - } - - public void SetWindowSize(Vector2I size) - { - } - - public void SetWindowTitle(string title) - { - } - - public void ShowWindow() - { - } -} - internal static class ViewportsExts { internal static Vector2 ToImVec2(this Vector2I v) @@ -283,7 +208,7 @@ private static readonly Platform_GetWindowMinimized _getWindowMinimized private delegate void Platform_SetWindowTitle(ImGuiViewportPtr vp, string title); private static readonly Platform_SetWindowTitle _setWindowTitle = Godot_SetWindowTitle; - private IViewportWindow _mainWindow = null!; + private GodotImGuiWindow _mainWindow = null!; private static void UpdateMonitors() { @@ -346,17 +271,10 @@ public Viewports() UpdateMonitors(); } - public void SetMainViewport(Viewport vp, Rid mainSubViewport) + public void SetMainWindow(Window window, Rid mainSubViewport) { _mainWindow?.Dispose(); - if (vp is Window window) - { - _mainWindow = new GodotImGuiWindow(ImGui.GetMainViewport(), window, mainSubViewport); - } - else if (vp is SubViewport svp) - { - _mainWindow = new GodotImGuiSubViewport(ImGui.GetMainViewport(), svp, mainSubViewport); - } + _mainWindow = new GodotImGuiWindow(ImGui.GetMainViewport(), window, mainSubViewport); } private static void Godot_CreateWindow(ImGuiViewportPtr vp) @@ -368,7 +286,7 @@ private static void Godot_DestroyWindow(ImGuiViewportPtr vp) { if (vp.PlatformUserData != IntPtr.Zero) { - var window = (IViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.Dispose(); vp.PlatformUserData = IntPtr.Zero; } @@ -376,55 +294,55 @@ private static void Godot_DestroyWindow(ImGuiViewportPtr vp) private static void Godot_ShowWindow(ImGuiViewportPtr vp) { - var window = (IViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.ShowWindow(); } private static void Godot_SetWindowPos(ImGuiViewportPtr vp, Vector2 pos) { - var window = (IViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.SetWindowPos(pos.ToVector2I()); } private static void Godot_GetWindowPos(ImGuiViewportPtr vp, out Vector2 pos) { - var window = (IViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; pos = window.GetWindowPos().ToImVec2(); } private static void Godot_SetWindowSize(ImGuiViewportPtr vp, Vector2 size) { - var window = (IViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.SetWindowSize(size.ToVector2I()); } private static void Godot_GetWindowSize(ImGuiViewportPtr vp, out Vector2 size) { - var window = (IViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; size = window.GetWindowSize().ToImVec2(); } private static void Godot_SetWindowFocus(ImGuiViewportPtr vp) { - var window = (IViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.SetWindowFocus(); } private static bool Godot_GetWindowFocus(ImGuiViewportPtr vp) { - var window = (IViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; return window.GetWindowFocus(); } private static bool Godot_GetWindowMinimized(ImGuiViewportPtr vp) { - var window = (IViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; return window.GetWindowMinimized(); } private static void Godot_SetWindowTitle(ImGuiViewportPtr vp, string title) { - var window = (IViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.SetWindowTitle(title); } } diff --git a/addons/imgui-godot/scripts/ImGuiPlugin.gd b/addons/imgui-godot/scripts/ImGuiPlugin.gd index cb2eb22..36b657b 100644 --- a/addons/imgui-godot/scripts/ImGuiPlugin.gd +++ b/addons/imgui-godot/scripts/ImGuiPlugin.gd @@ -2,17 +2,18 @@ extends EditorPlugin func _enter_tree(): + Engine.register_singleton("ImGuiPlugin", self) + add_autoload_singleton("ImGuiRoot", "res://addons/imgui-godot/data/ImGuiRoot.tscn") + + # remove obsolete ImGuiLayer autoload if ProjectSettings.has_setting("autoload/ImGuiLayer"): remove_autoload_singleton("ImGuiLayer") - add_autoload_singleton("ImGuiRoot", "res://addons/imgui-godot/data/ImGuiRoot.tscn") - Engine.register_singleton("ImGuiPlugin", self) + # warn user if csproj will fail to build if "C#" in ProjectSettings.get_setting("application/config/features"): var projPath: String = ProjectSettings.get_setting("dotnet/project/solution_directory") var fn: String = "%s.csproj" % ProjectSettings.get_setting("dotnet/project/assembly_name") - if projPath != "": - fn = "%s/%s" % [projPath, fn] - check_csproj(fn) + check_csproj(projPath.path_join(fn)) func check_csproj(fn): var fi := FileAccess.open(fn, FileAccess.READ) @@ -29,12 +30,10 @@ func check_csproj(fn): if netVer < 8: changesNeeded += "- Set target framework to .NET 8 or later\n" - idx = data.find("True") - if idx == -1: + if !data.contains("True"): changesNeeded += "- Allow unsafe blocks\n" - idx = data.find(",debug,release>.x86_64") + +set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/project") + +install(TARGETS gdexample + RUNTIME DESTINATION bin + ) diff --git a/doc/examples/GDExt/src/example.cpp b/doc/examples/GDExt/src/example.cpp index 9ab33ab..553c603 100644 --- a/doc/examples/GDExt/src/example.cpp +++ b/doc/examples/GDExt/src/example.cpp @@ -18,7 +18,6 @@ Example::~Example() void Example::_ready() { - ImGui::Godot::SyncImGuiPtrs(); _img = ResourceLoader::get_singleton()->load("res://robot_eye.tres"); } diff --git a/doc/examples/GDExt/src/register_types.cpp b/doc/examples/GDExt/src/register_types.cpp index 24f9ad8..a786728 100644 --- a/doc/examples/GDExt/src/register_types.cpp +++ b/doc/examples/GDExt/src/register_types.cpp @@ -5,15 +5,16 @@ #include #include #include +#include using namespace godot; void initialize_example_module(ModuleInitializationLevel p_level) { if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) - { return; - } + + ImGui::Godot::SyncImGuiPtrs(); GDREGISTER_CLASS(Example); } @@ -21,9 +22,7 @@ void initialize_example_module(ModuleInitializationLevel p_level) void uninitialize_example_module(ModuleInitializationLevel p_level) { if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) - { return; - } } extern "C" { diff --git a/doc/icon.png b/doc/icon.png new file mode 100644 index 0000000..b0f7be9 Binary files /dev/null and b/doc/icon.png differ diff --git a/gdext/godot-cpp b/gdext/godot-cpp index 98c143a..b28098e 160000 --- a/gdext/godot-cpp +++ b/gdext/godot-cpp @@ -1 +1 @@ -Subproject commit 98c143a48365f3f3bf5f99d6289a2cb25e6472d1 +Subproject commit b28098e76b84e8831b8ac68d490f4bca44678b2a diff --git a/gdext/include/imgui-godot.h b/gdext/include/imgui-godot.h index 8941067..a7f39a2 100644 --- a/gdext/include/imgui-godot.h +++ b/gdext/include/imgui-godot.h @@ -38,6 +38,7 @@ using godot::SubViewport; using godot::Texture2D; using godot::TypedArray; using godot::Vector2; +using godot::Viewport; using godot::Window; #else // module @@ -133,6 +134,13 @@ inline void SetVisible(bool vis) detail::ImGuiGD->set(sn, vis); } +inline void SetMainViewport(Viewport* vp) +{ + ERR_FAIL_COND(!detail::GET_IMGUIGD()); + static const StringName sn("SetMainViewport"); + detail::ImGuiGD->set(sn, vp); +} + inline bool ToolInit() { ERR_FAIL_COND_V(!detail::GET_IMGUIGD(), false); @@ -142,14 +150,16 @@ inline bool ToolInit() inline void SyncImGuiPtrs() { - ERR_FAIL_COND(!detail::GET_IMGUIGD()); + Object* obj = ClassDB::instantiate("ImGuiSync"); + ERR_FAIL_COND(!obj); + static const StringName sn("GetImGuiPtrs"); - TypedArray ptrs = detail::ImGuiGD->call(sn, - String(ImGui::GetVersion()), - (int32_t)sizeof(ImGuiIO), - (int32_t)sizeof(ImDrawVert), - (int32_t)sizeof(ImDrawIdx), - (int32_t)sizeof(ImWchar)); + TypedArray ptrs = obj->call(sn, + String(ImGui::GetVersion()), + (int32_t)sizeof(ImGuiIO), + (int32_t)sizeof(ImDrawVert), + (int32_t)sizeof(ImDrawIdx), + (int32_t)sizeof(ImWchar)); ERR_FAIL_COND(ptrs.size() != 3); @@ -157,6 +167,7 @@ inline void SyncImGuiPtrs() ImGuiMemAllocFunc alloc_func = reinterpret_cast((int64_t)ptrs[1]); ImGuiMemFreeFunc free_func = reinterpret_cast((int64_t)ptrs[2]); ImGui::SetAllocatorFunctions(alloc_func, free_func, nullptr); + memdelete(obj); } inline ImTextureID BindTexture(Texture2D* tex) diff --git a/gdext/src/CMakeLists.txt b/gdext/src/CMakeLists.txt index c1003e4..b96616c 100644 --- a/gdext/src/CMakeLists.txt +++ b/gdext/src/CMakeLists.txt @@ -11,14 +11,20 @@ target_sources(imgui-godot-native PRIVATE GdsCache.h ImGuiAPI.cpp ImGuiAPI.h + ImGuiController.cpp + ImGuiController.h + ImGuiControllerHelper.cpp + ImGuiControllerHelper.h ImGuiGD.cpp ImGuiGD.h ImGuiLayer.cpp ImGuiLayer.h - ImGuiLayerHelper.cpp - ImGuiLayerHelper.h + ImGuiSync.cpp + ImGuiSync.h Input.cpp Input.h + InputLocal.cpp + InputLocal.h main.cpp RdRenderer.cpp RdRenderer.h diff --git a/gdext/src/Context.cpp b/gdext/src/Context.cpp index 703bdf4..9b1537e 100644 --- a/gdext/src/Context.cpp +++ b/gdext/src/Context.cpp @@ -18,10 +18,10 @@ Context* GetContext() return ctx.get(); } -Context::Context(Window* mainWindow, RID mainSubViewport, std::unique_ptr r) +Context::Context(std::unique_ptr r) { renderer = std::move(r); - input = std::make_unique(mainWindow); + input = std::make_unique(); fonts = std::make_unique(); ImGuiIO& io = ImGui::GetIO(); @@ -32,7 +32,7 @@ Context::Context(Window* mainWindow, RID mainSubViewport, std::unique_ptrName(); - viewports = std::make_unique(mainWindow, mainSubViewport); + viewports = std::make_unique(); } Context::~Context() @@ -41,7 +41,7 @@ Context::~Context() ImGui::DestroyContext(); } -void Init(godot::Window* mainWindow, RID mainSubViewport, const Ref& cfg) +void Init(const Ref& cfg) { // re-init not allowed ERR_FAIL_COND(ctx); @@ -99,9 +99,8 @@ void Init(godot::Window* mainWindow, RID mainSubViewport, const Ref& c renderer->Init(); } - ctx = std::make_unique(mainWindow, mainSubViewport, std::move(renderer)); + ctx = std::make_unique(std::move(renderer)); ctx->scale = cfg->get("Scale"); - ctx->renderer->InitViewport(mainSubViewport); String iniFilename = cfg->get("IniFilename"); if (iniFilename.length() > 0) @@ -124,34 +123,23 @@ void Init(godot::Window* mainWindow, RID mainSubViewport, const Ref& c RebuildFontAtlas(); } -void Update(double delta, Vector2 displaySize) +void Context::Update(double delta, Vector2 displaySize) { ImGuiIO& io = ImGui::GetIO(); io.DisplaySize = displaySize; io.DeltaTime = static_cast(delta); - ctx->input->Update(); + input->Update(); gdscache->OnNewFrame(); ImGui::NewFrame(); } -bool ProcessInput(const Ref& evt, Window* window) -{ - return ctx->input->ProcessInput(evt, window); -} - -void ProcessNotification(int what) -{ - if (ctx) - ctx->input->ProcessNotification(what); -} - -void Render() +void Context::Render() { ImGui::Render(); ImGui::UpdatePlatformWindows(); - ctx->renderer->Render(); + renderer->Render(); } void Shutdown() @@ -163,9 +151,9 @@ void Shutdown() void Connect(const godot::Callable& callable) { - Object* igl = Engine::get_singleton()->get_singleton("ImGuiLayer"); - ERR_FAIL_COND(!igl); - igl->connect("imgui_layout", callable); + Object* igc = Engine::get_singleton()->get_singleton("ImGuiController"); + ERR_FAIL_COND(!igc); + igc->connect("imgui_layout", callable); } void ResetFonts() @@ -189,6 +177,8 @@ void AddFontDefault() void RebuildFontAtlas() { ERR_FAIL_COND(!ctx); + ERR_FAIL_COND(ctx->inProcessFrame); + bool scaleToDpi = ProjectSettings::get_singleton()->get_setting("display/window/dpi/allow_hidpi"); int dpiFactor = std::max(1, DisplayServer::get_singleton()->screen_get_dpi() / 96); ctx->fonts->RebuildFontAtlas(scaleToDpi ? dpiFactor * ctx->scale : ctx->scale); @@ -210,11 +200,6 @@ void SetIniFilename(const String& fn) io.IniFilename = nullptr; } -void OnFramePreDraw() -{ - ctx->renderer->OnFramePreDraw(); -} - bool SubViewportWidget(SubViewport* svp) { ImVec2 vpSize = svp->get_size(); diff --git a/gdext/src/Context.h b/gdext/src/Context.h index ab2a9ff..ead82d4 100644 --- a/gdext/src/Context.h +++ b/gdext/src/Context.h @@ -22,6 +22,7 @@ #include "Fonts.h" #include "GdsCache.h" #include "ImGuiGD.h" +#include "ImGuiLayer.h" #include "Input.h" #include "RdRenderer.h" #include "RdRendererThreadSafe.h" @@ -45,19 +46,24 @@ struct Context std::unique_ptr fonts; std::unique_ptr input; std::unique_ptr renderer; + float scale = 1.0f; + float joyAxisDeadZone = 0.15f; + int layerNum = 128; + Vector2i viewportSize; + ImGuiLayer* layer = nullptr; + bool inProcessFrame = false; - Context(Window* mainWindow, RID mainSubViewport, std::unique_ptr r); + Context(std::unique_ptr r); ~Context(); + + void Render(); + void Update(double delta, Vector2 displaySize); }; Context* GetContext(); -void Init(Window* mainWindow, RID mainSubViewport, const Ref& config); -void Update(double delta, Vector2 displaySize); -bool ProcessInput(const Ref& evt, Window* window); -void ProcessNotification(int what); -void Render(); +void Init(const Ref& config); void Shutdown(); void Connect(const Callable& callable); void ResetFonts(); @@ -69,6 +75,4 @@ void SetIniFilename(const String& fn); void SetVisible(bool visible); bool SubViewportWidget(SubViewport* svp); - -void OnFramePreDraw(); } // namespace ImGui::Godot diff --git a/gdext/src/ImGuiController.cpp b/gdext/src/ImGuiController.cpp new file mode 100644 index 0000000..20e1f22 --- /dev/null +++ b/gdext/src/ImGuiController.cpp @@ -0,0 +1,171 @@ +#include "ImGuiController.h" +#include "Context.h" +#include "ImGuiControllerHelper.h" +#include "ImGuiLayer.h" +#include "Input.h" +#include "InputLocal.h" +#include +#include +#include +#include + +namespace ImGui::Godot { + +namespace { +ImGuiController* instance = nullptr; +} + +struct ImGuiController::Impl +{ + Window* window; + ImGuiControllerHelper* helper = nullptr; + + void CheckContentScale() const; +}; + +void ImGuiController::Impl::CheckContentScale() const +{ + if (window->get_content_scale_mode() == Window::CONTENT_SCALE_MODE_VIEWPORT) + { + UtilityFunctions::printerr("imgui-godot: scale mode `viewport` is unsupported"); + } +} + + +ImGuiController* ImGuiController::Instance() +{ + return instance; +} + +void ImGuiController::_bind_methods() +{ + ADD_SIGNAL(MethodInfo("imgui_layout")); + ClassDB::bind_method(D_METHOD("on_frame_pre_draw"), &ImGuiController::on_frame_pre_draw); + ClassDB::bind_method(D_METHOD("OnLayerExiting"), &ImGuiController::OnLayerExiting); +} + +ImGuiController::ImGuiController() : impl(std::make_unique()) +{ +} + +ImGuiController::~ImGuiController() +{ +} + +void ImGuiController::_enter_tree() +{ + instance = this; + + set_name("ImGuiController"); + Engine::get_singleton()->register_singleton("ImGuiController", this); + impl->window = get_window(); + + impl->CheckContentScale(); + + Ref cfgPackedScene = ResourceLoader::get_singleton()->load("res://addons/imgui-godot/Config.tscn"); + Node* cfgScene = cfgPackedScene->instantiate(); + Ref cfg = cfgScene->get("Config"); + memdelete(cfgScene); + + if (cfg.is_null()) + { + Ref script = ResourceLoader::get_singleton()->load("res://addons/imgui-godot/scripts/ImGuiConfig.gd"); + cfg = script->new_(); + } + + ImGui::Godot::Init(cfg); + + impl->helper = memnew(ImGuiControllerHelper); + add_child(impl->helper); + + SetMainViewport(impl->window); +} + +void ImGuiController::_ready() +{ + set_process_priority(std::numeric_limits::max()); +} + +void ImGuiController::_exit_tree() +{ + ImGui::Godot::Shutdown(); +} + +void ImGuiController::_process(double delta) +{ +#ifdef DEBUG_ENABLED + if (Engine::get_singleton()->is_editor_hint()) + { + // verify signal connections + auto conns = get_signal_connection_list("imgui_layout"); + for (int i = 0; i < conns.size(); ++i) + { + const Dictionary& conn = conns[i]; + const Callable& cb = conn["callable"]; + if (!cb.is_valid()) + disconnect("imgui_layout", cb); + } + } +#endif + + Context* ctx = GetContext(); + ctx->layer->UpdateViewport(); + emit_signal("imgui_layout"); + ctx->Render(); + ctx->inProcessFrame = false; +} + +void ImGuiController::_notification(int p_what) +{ + Context* ctx = GetContext(); + if (ctx) + ctx->input->ProcessNotification(p_what); +} + +void ImGuiController::OnLayerExiting() +{ + // an ImGuiLayer is being destroyed without calling SetMainViewport + if (GetContext()->layer->get_viewport() != impl->window) + { + // revert to main window + SetMainViewport(impl->window); + } +} + +void ImGuiController::SetMainViewport(Viewport* vp) +{ + Context* ctx = GetContext(); + ImGuiLayer* oldLayer = ctx->layer; + if (oldLayer) + { + oldLayer->disconnect("tree_exiting", Callable(this, "OnLayerExiting")); + oldLayer->queue_free(); + } + + ImGuiLayer* newLayer = memnew(ImGuiLayer); + newLayer->connect("tree_exiting", Callable(this, "OnLayerExiting")); + + if (vp->get_class() == "Window") + { + ctx->input = std::make_unique(); + if (vp == impl->window) + add_child(newLayer); + else + vp->add_child(newLayer); + ImGui::GetIO().BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; + } + else + { + ctx->input = std::make_unique(); + vp->add_child(newLayer); + ImGui::GetIO().BackendFlags &= ~ImGuiBackendFlags_PlatformHasViewports; + } + ctx->layer = newLayer; +} + +void ImGuiController::on_frame_pre_draw() +{ + GetContext()->renderer->OnFramePreDraw(); +} + +} // namespace ImGui::Godot diff --git a/gdext/src/ImGuiController.h b/gdext/src/ImGuiController.h new file mode 100644 index 0000000..f508360 --- /dev/null +++ b/gdext/src/ImGuiController.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include + +using namespace godot; + +namespace ImGui::Godot { + +class ImGuiController : public Node +{ + GDCLASS(ImGuiController, Node); + +protected: + static void _bind_methods(); + +public: + static ImGuiController* Instance(); + + ImGuiController(); + ~ImGuiController(); + + void _ready() override; + void _enter_tree() override; + void _exit_tree() override; + void _process(double delta) override; + void _notification(int p_what); + void SetMainViewport(Viewport* vp); + + void OnLayerExiting(); + void on_frame_pre_draw(); + +private: + struct Impl; + std::unique_ptr impl; +}; + +} // namespace ImGui::Godot diff --git a/gdext/src/ImGuiControllerHelper.cpp b/gdext/src/ImGuiControllerHelper.cpp new file mode 100644 index 0000000..a670165 --- /dev/null +++ b/gdext/src/ImGuiControllerHelper.cpp @@ -0,0 +1,41 @@ +#include "ImGuiControllerHelper.h" +#include "Context.h" +using namespace godot; + +namespace ImGui::Godot { + +ImGuiControllerHelper::ImGuiControllerHelper() +{ +} + +ImGuiControllerHelper::~ImGuiControllerHelper() +{ +} + +void ImGuiControllerHelper::_bind_methods() +{ +} + +void ImGuiControllerHelper::_enter_tree() +{ +} + +void ImGuiControllerHelper::_ready() +{ + set_name("ImGuiControllerHelper"); + set_process_priority(std::numeric_limits::min()); + set_process_mode(Node::PROCESS_MODE_ALWAYS); +} + +void ImGuiControllerHelper::_exit_tree() +{ +} + +void ImGuiControllerHelper::_process(double delta) +{ + Context* ctx = GetContext(); + ctx->inProcessFrame = true; + ctx->Update(delta, ctx->viewportSize); +} + +} // namespace ImGui::Godot diff --git a/gdext/src/ImGuiLayerHelper.h b/gdext/src/ImGuiControllerHelper.h similarity index 56% rename from gdext/src/ImGuiLayerHelper.h rename to gdext/src/ImGuiControllerHelper.h index 8d0a562..2f77048 100644 --- a/gdext/src/ImGuiLayerHelper.h +++ b/gdext/src/ImGuiControllerHelper.h @@ -1,16 +1,13 @@ #pragma once #include -#include -using godot::InputEvent; using godot::Node; -using godot::Ref; namespace ImGui::Godot { -class ImGuiLayerHelper : public Node +class ImGuiControllerHelper : public Node { - GDCLASS(ImGuiLayerHelper, Node); + GDCLASS(ImGuiControllerHelper, Node); protected: static void _bind_methods(); @@ -21,12 +18,8 @@ class ImGuiLayerHelper : public Node void _exit_tree() override; void _process(double delta) override; - ImGuiLayerHelper(); - ~ImGuiLayerHelper(); - -private: - struct Impl; - std::unique_ptr impl; + ImGuiControllerHelper(); + ~ImGuiControllerHelper(); }; } // namespace ImGui::Godot diff --git a/gdext/src/ImGuiGD.cpp b/gdext/src/ImGuiGD.cpp index 48be0d4..bf5e715 100644 --- a/gdext/src/ImGuiGD.cpp +++ b/gdext/src/ImGuiGD.cpp @@ -1,5 +1,6 @@ #include "ImGuiGD.h" #include "Context.h" +#include "ImGuiController.h" #include "ImGuiLayer.h" #include "common.h" #include @@ -32,6 +33,8 @@ void ImGuiGD::_bind_methods() ClassDB::bind_method(D_METHOD("Connect", "callable"), &ImGuiGD::Connect); ClassDB::bind_method(D_METHOD("RebuildFontAtlas"), &ImGuiGD::RebuildFontAtlas); ClassDB::bind_method(D_METHOD("ResetFonts"), &ImGuiGD::ResetFonts); + ClassDB::bind_method(D_METHOD("SetMainViewport", "vp"), &ImGuiGD::SetMainViewport); + ClassDB::bind_method(D_METHOD("SubViewport", "svp"), &ImGuiGD::SubViewport); ClassDB::bind_method(D_METHOD("GetImGuiPtrs", "version", "ioSize", "vertSize", "idxSize", "charSize"), &ImGuiGD::GetImGuiPtrs); @@ -96,20 +99,26 @@ void ImGuiGD::RebuildFontAtlas() ImGui::Godot::RebuildFontAtlas(); } +void ImGuiGD::SetMainViewport(Viewport* vp) +{ + ImGuiController* igc = Object::cast_to(Engine::get_singleton()->get_singleton("ImGuiController")); + ERR_FAIL_COND(!igc); + igc->SetMainViewport(vp); +} + void ImGuiGD::_SetVisible(bool visible) { - CanvasLayer* igl = Object::cast_to(Engine::get_singleton()->get_singleton("ImGuiLayer")); - ERR_FAIL_COND(!igl); - igl->set_visible(visible); + Context* ctx = GetContext(); + ERR_FAIL_COND(!ctx); + ERR_FAIL_COND(!ctx->layer); + ctx->layer->set_visible(visible); } bool ImGuiGD::_GetVisible() { - CanvasLayer* igl = nullptr; - if (Engine::get_singleton()->has_singleton("ImGuiLayer")) - igl = Object::cast_to(Engine::get_singleton()->get_singleton("ImGuiLayer")); - if (igl) - return igl->is_visible(); + Context* ctx = GetContext(); + if (ctx && ctx->layer) + return ctx->layer->is_visible(); return false; } @@ -117,14 +126,14 @@ void ImGuiGD::_SetJoyAxisDeadZone(float zone) { Context* ctx = ImGui::Godot::GetContext(); ERR_FAIL_COND(!ctx); - ctx->input->SetJoyAxisDeadZone(zone); + ctx->joyAxisDeadZone = zone; } float ImGuiGD::_GetJoyAxisDeadZone() { Context* ctx = ImGui::Godot::GetContext(); if (ctx) - return ctx->input->GetJoyAxisDeadZone(); + return ctx->joyAxisDeadZone; return 0.15f; } diff --git a/gdext/src/ImGuiGD.h b/gdext/src/ImGuiGD.h index 02ab303..bbaedfb 100644 --- a/gdext/src/ImGuiGD.h +++ b/gdext/src/ImGuiGD.h @@ -37,6 +37,8 @@ class ImGuiGD : public Object void _SetScale(float scale); float _GetScale(); + void SetMainViewport(Viewport* vp); + PackedInt64Array GetFontPtrs(); PackedInt64Array GetImGuiPtrs(String version, int ioSize, int vertSize, int idxSize, int charSize); diff --git a/gdext/src/ImGuiLayer.cpp b/gdext/src/ImGuiLayer.cpp index baf89f7..3d61ba0 100644 --- a/gdext/src/ImGuiLayer.cpp +++ b/gdext/src/ImGuiLayer.cpp @@ -1,6 +1,6 @@ #include "ImGuiLayer.h" #include "Context.h" -#include "ImGuiLayerHelper.h" +#include "ImGuiControllerHelper.h" #include #include @@ -19,17 +19,14 @@ namespace ImGui::Godot { struct ImGuiLayer::Impl { - ImGuiLayerHelper* helper = nullptr; - Window* window = nullptr; RID subViewportRid; Vector2i subViewportSize{0, 0}; RID canvasItem; Transform2D finalTransform{1.f, 0.f, 0.f, 1.f, 0.f, 0.f}; // identity bool visible = true; - Ref cfg; + Viewport* parentViewport = nullptr; static RID AddLayerSubViewport(Node* parent); - void CheckContentScale() const; }; RID ImGuiLayer::Impl::AddLayerSubViewport(Node* parent) @@ -44,14 +41,6 @@ RID ImGuiLayer::Impl::AddLayerSubViewport(Node* parent) return svp; } -void ImGuiLayer::Impl::CheckContentScale() const -{ - if (window->get_content_scale_mode() == Window::CONTENT_SCALE_MODE_VIEWPORT) - { - UtilityFunctions::printerr("imgui-godot: scale mode `viewport` is unsupported"); - } -} - ImGuiLayer::ImGuiLayer() : impl(std::make_unique()) { } @@ -62,90 +51,75 @@ ImGuiLayer::~ImGuiLayer() void ImGuiLayer::_bind_methods() { - ADD_SIGNAL(MethodInfo("imgui_layout")); ClassDB::bind_method(D_METHOD("on_visibility_changed"), &ImGuiLayer::on_visibility_changed); - ClassDB::bind_method(D_METHOD("on_frame_pre_draw"), &ImGuiLayer::on_frame_pre_draw); } void ImGuiLayer::_enter_tree() { - Node* parent = get_parent(); - if (!parent) - return; - if (Engine::get_singleton()->has_singleton("ImGuiLayer")) - return; - + Context* ctx = GetContext(); set_name("ImGuiLayer"); - Engine::get_singleton()->register_singleton("ImGuiLayer", this); - impl->window = get_window(); + set_layer(ctx->layerNum); - impl->CheckContentScale(); + impl->parentViewport = get_viewport(); + impl->subViewportRid = Impl::AddLayerSubViewport(this); RenderingServer* RS = RenderingServer::get_singleton(); - - impl->subViewportRid = Impl::AddLayerSubViewport(this); impl->canvasItem = RS->canvas_item_create(); RS->canvas_item_set_parent(impl->canvasItem, get_canvas()); - Ref cfgPackedScene = ResourceLoader::get_singleton()->load("res://addons/imgui-godot/Config.tscn"); - Node* cfgScene = cfgPackedScene->instantiate(); - Ref cfg = cfgScene->get("Config"); - memdelete(cfgScene); - - if (cfg.is_null()) - { - Ref script = ResourceLoader::get_singleton()->load("res://addons/imgui-godot/scripts/ImGuiConfig.gd"); - cfg = script->new_(); - } - impl->cfg = cfg; - - set_layer(cfg->get("Layer")); - - impl->helper = memnew(ImGuiLayerHelper); - add_child(impl->helper); - - ImGui::Godot::Init(get_window(), impl->subViewportRid, cfg); + ctx->renderer->InitViewport(impl->subViewportRid); + ctx->viewports->SetMainWindow(get_window(), impl->subViewportRid); } void ImGuiLayer::_ready() { - set_process_priority(std::numeric_limits::max()); - set_process_mode(Node::PROCESS_MODE_ALWAYS); - connect("visibility_changed", Callable(this, "on_visibility_changed")); } void ImGuiLayer::_exit_tree() { - Engine::get_singleton()->unregister_singleton("ImGuiLayer"); - ImGui::Godot::Shutdown(); RenderingServer::get_singleton()->free_rid(impl->canvasItem); RenderingServer::get_singleton()->free_rid(impl->subViewportRid); } -void ImGuiLayer::_process(double delta) +void ImGuiLayer::on_visibility_changed() { -#ifdef DEBUG_ENABLED - if (Engine::get_singleton()->is_editor_hint()) + impl->visible = is_visible(); + if (impl->visible) { - // verify signal connections - auto conns = get_signal_connection_list("imgui_layout"); - for (int i = 0; i < conns.size(); ++i) - { - const Dictionary& conn = conns[i]; - const Callable& cb = conn["callable"]; - if (!cb.is_valid()) - disconnect("imgui_layout", cb); - } + set_process_input(true); } -#endif + else + { + set_process_input(false); + ImGui::Godot::GetContext()->renderer->OnHide(); + impl->subViewportSize = {0, 0}; + RenderingServer::get_singleton()->canvas_item_clear(impl->canvasItem); + } +} + +void ImGuiLayer::_input(const Ref& event) +{ + if (GetContext()->input->ProcessInput(event)) + { + impl->parentViewport->set_input_as_handled(); + } +} +void ImGuiLayer::UpdateViewport() +{ if (impl->visible) { - Vector2i winSize = impl->window->get_size(); - Transform2D ft = impl->window->get_final_transform(); + Vector2i vpSize; + if (Window* w = Object::cast_to(impl->parentViewport)) + vpSize = w->get_size(); + else + vpSize = Object::cast_to(impl->parentViewport)->get_size(); + GetContext()->viewportSize = vpSize; - if (impl->subViewportSize != winSize || + Transform2D ft = impl->parentViewport->get_final_transform(); + + if (impl->subViewportSize != vpSize || impl->finalTransform != ft #ifdef DEBUG_ENABLED // force redraw on every frame in editor @@ -154,7 +128,7 @@ void ImGuiLayer::_process(double delta) ) { // this is more or less how SubViewportContainer works - impl->subViewportSize = winSize; + impl->subViewportSize = vpSize; impl->finalTransform = ft; RenderingServer* RS = RenderingServer::get_singleton(); @@ -166,49 +140,7 @@ void ImGuiLayer::_process(double delta) Rect2(0, 0, impl->subViewportSize.x, impl->subViewportSize.y), vptex); } - - emit_signal("imgui_layout"); - } - - ImGui::Godot::Render(); -} - -void ImGuiLayer::_input(const Ref& event) -{ - if (ImGui::Godot::ProcessInput(event, impl->window)) - { - impl->window->set_input_as_handled(); - } -} - -void ImGuiLayer::_notification(int p_what) -{ - // quick filter - if (p_what > 2000) - { - ImGui::Godot::ProcessNotification(p_what); - } -} - -void ImGuiLayer::on_visibility_changed() -{ - impl->visible = is_visible(); - if (impl->visible) - { - set_process_input(true); - } - else - { - set_process_input(false); - ImGui::Godot::GetContext()->renderer->OnHide(); - impl->subViewportSize = {0, 0}; - RenderingServer::get_singleton()->canvas_item_clear(impl->canvasItem); } } -void ImGuiLayer::on_frame_pre_draw() -{ - ImGui::Godot::OnFramePreDraw(); -} - } // namespace ImGui::Godot diff --git a/gdext/src/ImGuiLayer.h b/gdext/src/ImGuiLayer.h index 91b24b3..3b93b77 100644 --- a/gdext/src/ImGuiLayer.h +++ b/gdext/src/ImGuiLayer.h @@ -21,11 +21,10 @@ class ImGuiLayer : public CanvasLayer void _ready() override; void _enter_tree() override; void _exit_tree() override; - void _process(double delta) override; void _input(const Ref& event) override; - void _notification(int p_what); + void on_visibility_changed(); - void on_frame_pre_draw(); + void UpdateViewport(); private: struct Impl; diff --git a/gdext/src/ImGuiLayerHelper.cpp b/gdext/src/ImGuiLayerHelper.cpp deleted file mode 100644 index 8188583..0000000 --- a/gdext/src/ImGuiLayerHelper.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "ImGuiLayerHelper.h" -#include "Context.h" -#include "imgui-godot.h" -#include -#include -#include -#include -using namespace godot; - -namespace ImGui::Godot { - -struct ImGuiLayerHelper::Impl -{ - Window* window; -}; - -ImGuiLayerHelper::ImGuiLayerHelper() : impl(std::make_unique()) -{ -} - -ImGuiLayerHelper::~ImGuiLayerHelper() -{ -} - -void ImGuiLayerHelper::_bind_methods() -{ -} - -void ImGuiLayerHelper::_enter_tree() -{ -} - -void ImGuiLayerHelper::_ready() -{ - set_name("ImGuiLayerHelper"); - set_process_priority(std::numeric_limits::min()); - set_process_mode(Node::PROCESS_MODE_ALWAYS); - impl->window = get_window(); -} - -void ImGuiLayerHelper::_exit_tree() -{ -} - -void ImGuiLayerHelper::_process(double delta) -{ - ImGui::Godot::Update(delta, impl->window->get_size()); -} - -} // namespace ImGui::Godot diff --git a/gdext/src/ImGuiSync.cpp b/gdext/src/ImGuiSync.cpp new file mode 100644 index 0000000..95f99af --- /dev/null +++ b/gdext/src/ImGuiSync.cpp @@ -0,0 +1,35 @@ +#include "ImGuiSync.h" +#include +#include + +namespace ImGui::Godot { + +void ImGuiSync::_bind_methods() +{ + ClassDB::bind_static_method("ImGuiSync", D_METHOD("GetImGuiPtrs"), &ImGuiSync::GetImGuiPtrs); +} + +PackedInt64Array ImGuiSync::GetImGuiPtrs(String version, int ioSize, int vertSize, int idxSize, int charSize) +{ + if (version != String(ImGui::GetVersion()) || ioSize != sizeof(ImGuiIO) || vertSize != sizeof(ImDrawVert) || + idxSize != sizeof(ImDrawIdx) || charSize != sizeof(ImWchar)) + { + UtilityFunctions::push_error("ImGui version mismatch, use v", ImGui::GetVersion(), "-docking"); + return {}; + } + + ImGuiMemAllocFunc alloc_func = nullptr; + ImGuiMemFreeFunc free_func = nullptr; + void* user_data = nullptr; + + ImGui::GetAllocatorFunctions(&alloc_func, &free_func, &user_data); + + PackedInt64Array rv; + rv.resize(3); + rv[0] = reinterpret_cast(ImGui::GetCurrentContext()); + rv[1] = reinterpret_cast(alloc_func); + rv[2] = reinterpret_cast(free_func); + return rv; +} + +} // namespace ImGui::Godot diff --git a/gdext/src/ImGuiSync.h b/gdext/src/ImGuiSync.h new file mode 100644 index 0000000..a1f4fb7 --- /dev/null +++ b/gdext/src/ImGuiSync.h @@ -0,0 +1,19 @@ +#pragma once +#include + +using namespace godot; + +namespace ImGui::Godot { + +class ImGuiSync : public Object +{ + GDCLASS(ImGuiSync, Object); + +protected: + static void _bind_methods(); + +public: + static PackedInt64Array GetImGuiPtrs(String version, int ioSize, int vertSize, int idxSize, int charSize); +}; + +} // namespace ImGui::Godot diff --git a/gdext/src/Input.cpp b/gdext/src/Input.cpp index 6244d60..adc1bb4 100644 --- a/gdext/src/Input.cpp +++ b/gdext/src/Input.cpp @@ -1,4 +1,5 @@ #include "Input.h" +#include "Context.h" #include "imgui-godot.h" #include @@ -21,16 +22,12 @@ namespace ImGui::Godot { struct Input::Impl { - Window* mainWindow = nullptr; godot::SubViewport* previousSubViewport = nullptr; godot::SubViewport* currentSubViewport = nullptr; Vector2 currentSubViewportPos; Vector2 mouseWheel; ImGuiMouseCursor currentCursor = ImGuiMouseCursor_None; bool hasMouse; - float deadZone = 0.15f; - - void UpdateMouse(); }; namespace { @@ -73,9 +70,8 @@ void UpdateKeyMods(ImGuiIO& io) } // namespace -Input::Input(Window* mainWindow) : impl(std::make_unique()) +Input::Input() : impl(std::make_unique()) { - impl->mainWindow = mainWindow; impl->hasMouse = DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_MOUSE); } @@ -83,7 +79,7 @@ Input::~Input() { } -void Input::Impl::UpdateMouse() +void Input::UpdateMousePos() { ImGuiIO& io = ImGui::GetIO(); @@ -119,44 +115,51 @@ void Input::Impl::UpdateMouse() } else { - Vector2i winPos = mainWindow->get_position(); + Vector2i winPos = GetContext()->layer->get_window()->get_position(); io.AddMousePosEvent(mousePos.x - winPos.x, mousePos.y - winPos.y); } } +} + +void Input::UpdateMouse() +{ + ImGuiIO& io = ImGui::GetIO(); + DisplayServer* DS = DisplayServer::get_singleton(); - if (mouseWheel != Vector2()) + UpdateMousePos(); + + // scrolling works better if we allow no more than one event per frame + if (impl->mouseWheel != Vector2()) { - io.AddMouseWheelEvent(mouseWheel.x, mouseWheel.y); - mouseWheel = Vector2(); + io.AddMouseWheelEvent(impl->mouseWheel.x, impl->mouseWheel.y); + impl->mouseWheel = Vector2(); } if (io.WantCaptureMouse && !(io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)) { ImGuiMouseCursor newCursor = ImGui::GetMouseCursor(); - if (newCursor != currentCursor) + if (newCursor != impl->currentCursor) { DS->cursor_set_shape(ToCursorShape(newCursor)); } } else { - currentCursor = ImGuiMouseCursor_None; + impl->currentCursor = ImGuiMouseCursor_None; } } void Input::Update() { if (impl->hasMouse) - impl->UpdateMouse(); + UpdateMouse(); impl->previousSubViewport = impl->currentSubViewport; impl->currentSubViewport = nullptr; } -bool Input::ProcessInput(const Ref& evt, Window* window) +void Input::ProcessSubViewportWidget(const Ref& evt) { - ImGuiIO& io = ImGui::GetIO(); - if (impl->currentSubViewport) { if (impl->currentSubViewport != impl->previousSubViewport) @@ -165,10 +168,11 @@ bool Input::ProcessInput(const Ref& evt, Window* window) Ref vpevt = evt->duplicate(); if (Ref me = vpevt; me.is_valid()) { + ImGuiIO& io = ImGui::GetIO(); Vector2i mousePos = DisplayServer::get_singleton()->mouse_get_position(); Vector2i windowPos{0, 0}; if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - windowPos = window->get_position(); + windowPos = GetContext()->layer->get_window()->get_position(); me->set_position(Vector2(mousePos.x - windowPos.x - impl->currentSubViewportPos.x, mousePos.y - windowPos.y - impl->currentSubViewportPos.y) @@ -180,6 +184,11 @@ bool Input::ProcessInput(const Ref& evt, Window* window) { impl->previousSubViewport->notification(Node::NOTIFICATION_VP_MOUSE_EXIT); } +} + +bool Input::HandleEvent(const Ref& evt) +{ + ImGuiIO& io = ImGui::GetIO(); bool consumed = false; @@ -259,7 +268,7 @@ bool Input::ProcessInput(const Ref& evt, Window* window) { bool pressed = true; float v = jm->get_axis_value(); - if (std::abs(v) < impl->deadZone) + if (std::abs(v) < GetContext()->joyAxisDeadZone) { v = 0; pressed = false; @@ -298,6 +307,12 @@ bool Input::ProcessInput(const Ref& evt, Window* window) return consumed; } +bool Input::ProcessInput(const Ref& evt) +{ + ProcessSubViewportWidget(evt); + return HandleEvent(evt); +} + void Input::ProcessNotification(int what) { switch (what) @@ -317,14 +332,4 @@ void Input::SetActiveSubViewport(godot::SubViewport* svp, Vector2 pos) impl->currentSubViewportPos = pos; } -void Input::SetJoyAxisDeadZone(float val) -{ - impl->deadZone = val; -} - -float Input::GetJoyAxisDeadZone() -{ - return impl->deadZone; -} - } // namespace ImGui::Godot diff --git a/gdext/src/Input.h b/gdext/src/Input.h index b513f31..16d89ef 100644 --- a/gdext/src/Input.h +++ b/gdext/src/Input.h @@ -12,18 +12,22 @@ namespace ImGui::Godot { class Input { public: - Input(Window* mainWindow); - ~Input(); + Input(); + virtual ~Input(); void Update(); - bool ProcessInput(const Ref& evt, Window* window); + virtual bool ProcessInput(const Ref& evt); void ProcessNotification(int what); void SetActiveSubViewport(godot::SubViewport* svp, Vector2 pos); - void SetJoyAxisDeadZone(float val); - float GetJoyAxisDeadZone(); +protected: + virtual void UpdateMousePos(); + void ProcessSubViewportWidget(const Ref& evt); + bool HandleEvent(const Ref& evt); private: + void UpdateMouse(); + struct Impl; std::unique_ptr impl; }; diff --git a/gdext/src/InputLocal.cpp b/gdext/src/InputLocal.cpp new file mode 100644 index 0000000..0825649 --- /dev/null +++ b/gdext/src/InputLocal.cpp @@ -0,0 +1,26 @@ +#include "InputLocal.h" +#include +#include + +namespace ImGui::Godot { + +void InputLocal::UpdateMousePos() +{ + // do not use global mouse position +} + +bool InputLocal::ProcessInput(const Ref& evt) +{ + // no support for SubViewport widgets + + if (Ref mm = evt; mm.is_valid()) + { + ImGuiIO& io = ImGui::GetIO(); + Vector2 mousePos = mm->get_position(); + io.AddMousePosEvent(mousePos.x, mousePos.y); + return io.WantCaptureMouse; + } + return HandleEvent(evt); +} + +} // namespace ImGui::Godot diff --git a/gdext/src/InputLocal.h b/gdext/src/InputLocal.h new file mode 100644 index 0000000..02f17ea --- /dev/null +++ b/gdext/src/InputLocal.h @@ -0,0 +1,15 @@ +#pragma once +#include "Input.h" + +using namespace godot; + +namespace ImGui::Godot { + +class InputLocal : public Input +{ +protected: + void UpdateMousePos() override; + bool ProcessInput(const Ref& evt) override; +}; + +} // namespace ImGui::Godot diff --git a/gdext/src/Viewports.cpp b/gdext/src/Viewports.cpp index b4b82ad..c674edd 100644 --- a/gdext/src/Viewports.cpp +++ b/gdext/src/Viewports.cpp @@ -47,7 +47,7 @@ static void Godot_CreateWindow(ImGuiViewport* vp) vd->window->set_transparent_background(true); vd->window->set_flag(Window::FLAG_TRANSPARENT, true); - Node* root = Object::cast_to(Engine::get_singleton()->get_singleton("ImGuiLayer")); + Node* root = Object::cast_to(Engine::get_singleton()->get_singleton("ImGuiController")); root->add_child(vd->window); // need to do this after add_child @@ -169,13 +169,16 @@ void Viewports::Impl::UpdateMonitors() } } -Viewports::Viewports(Window* mainWindow, RID mainSubViewport) : impl(std::make_unique()) +Viewports::Viewports() : impl(std::make_unique()) { auto& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; impl->InitPlatformInterface(); impl->UpdateMonitors(); +} +void Viewports::SetMainWindow(Window* mainWindow, RID mainSubViewport) +{ Godot_ViewportData* mainvd = IM_NEW(Godot_ViewportData); mainvd->window = mainWindow; ImGuiViewport* vp = ImGui::GetMainViewport(); @@ -189,7 +192,7 @@ Viewports::~Viewports() void ImGuiWindow::_input(const Ref& evt) { - ImGui::Godot::ProcessInput(evt, this); + GetContext()->input->ProcessInput(evt); } } // namespace ImGui::Godot diff --git a/gdext/src/Viewports.h b/gdext/src/Viewports.h index 9f20906..110cb18 100644 --- a/gdext/src/Viewports.h +++ b/gdext/src/Viewports.h @@ -40,9 +40,11 @@ class ImGuiWindow : public Window class Viewports { public: - Viewports(Window* mainWindow, RID mainSubViewport); + Viewports(); ~Viewports(); + void SetMainWindow(Window* mainWindow, RID mainSubViewport); + private: struct Impl; std::unique_ptr impl; diff --git a/gdext/src/main.cpp b/gdext/src/main.cpp index 80bf788..b84f60d 100644 --- a/gdext/src/main.cpp +++ b/gdext/src/main.cpp @@ -12,9 +12,11 @@ static_assert(GODOT_VERSION_MAJOR == 4 && GODOT_VERSION_MINOR >= 2); #include +#include "ImGuiController.h" +#include "ImGuiControllerHelper.h" #include "ImGuiGD.h" #include "ImGuiLayer.h" -#include "ImGuiLayerHelper.h" +#include "ImGuiSync.h" #include "Viewports.h" // avoid including cimgui.h elsewhere @@ -55,13 +57,18 @@ void sync_modules() void initialize_ign_module(ModuleInitializationLevel p_level) { + if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) + { + ImGui::CreateContext(); + ClassDB::register_internal_class(); + } + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) return; - ImGui::CreateContext(); - ClassDB::register_internal_class(); - ClassDB::register_internal_class(); + ClassDB::register_internal_class(); + ClassDB::register_internal_class(); ClassDB::register_class(); ClassDB::register_internal_class(); register_imgui_api();