Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImGui: Add support for ImGuiBackendFlags_HasSetMousePos and cleaner drawFrame() code #102

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/Magnum/ImGuiIntegration/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Context::Context(ImGuiContext& context, const Vector2& size, const Vector2i& win

/* Tell ImGui that changing mouse cursors is supported */
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;

/* Check if we can support base vertex > 0 in draw commands */
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
if(
Expand Down Expand Up @@ -312,17 +312,17 @@ void Context::drawFrame() {

ImGui::Render();

ImGuiIO& io = ImGui::GetIO();
const Vector2 fbSize = Vector2{io.DisplaySize}*Vector2{io.DisplayFramebufferScale};
if(!fbSize.product()) return;

ImDrawData* drawData = ImGui::GetDrawData();
CORRADE_INTERNAL_ASSERT(drawData); /* This is always valid after Render() */
drawData->ScaleClipRects(io.DisplayFramebufferScale);

const Vector2 fbSize = Vector2{drawData->DisplaySize}*Vector2{drawData->FramebufferScale};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I can't tell if this is a good or a bad change as my knowledge of ImGui is rather bad 😅 How does drawData->FramebufferScale and io.DisplayFramebufferScale relate?

Cc: @pezcode, you're responsible for most of the code here I think, and I bet you know better than me :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the ImGui comments:

ImVec2          DisplayPos;             // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications)
ImVec2          DisplaySize;            // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications)
ImVec2          FramebufferScale;       // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's mainly to aid with multi-window rendering 😉

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, that makes sense then :)

if(!fbSize.product()) return;

drawData->ScaleClipRects(drawData->FramebufferScale);

const Matrix3 projection =
Matrix3::translation({-1.0f, 1.0f})*
Matrix3::scaling({2.0f/Vector2(io.DisplaySize)})*
Matrix3::scaling({2.0f/Vector2(drawData->DisplaySize)})*
Matrix3::scaling({1.0f, -1.0f});
_shader.setTransformationProjectionMatrix(projection);

Expand Down
14 changes: 14 additions & 0 deletions src/Magnum/ImGuiIntegration/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,20 @@ class MAGNUM_IMGUIINTEGRATION_EXPORT Context {
*/
explicit Context(ImGuiContext& context, const Vector2i& size);

/**
* @brief TODO
*
* Implicitly fetches framebuffer and window size from the application instance
* Using this template constructor is preferable as it enables additional features based on given Application capabilities
*/
template<class Application> explicit Context(const Vector2& size, const Application& application);

/**
* @brief TODO
*
*/
template<class Application> explicit Context(ImGuiContext& context, const Vector2& size, const Application& application);

/**
* @brief Construct without creating the underlying ImGui context
*
Expand Down
26 changes: 26 additions & 0 deletions src/Magnum/ImGuiIntegration/Context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@
#include "Magnum/ImGuiIntegration/Context.h"

namespace Magnum { namespace ImGuiIntegration {
namespace Implementation {
template<class Application, class = decltype(&Application::warpCursor)>
constexpr static bool hasWarpCursor(const Application&) { return true; }

template<class... T> constexpr static bool hasWarpCursor(const T&...) { return false; }
}

template<class Application> Context::Context(const Vector2& size, const Application& application): Context{*ImGui::CreateContext(), size, application} {}

template<class Application> Context::Context(ImGuiContext& context, const Vector2& size, const Application& application): Context{context, size, application.windowSize(), application.framebufferSize()} {
/* We can honor io.WantSetMousePos requests if application type supports it */
if(Implementation::hasWarpCursor(application))
ImGui::GetIO().BackendFlags |= ImGuiBackendFlags_HasSetMousePos;
}

template<class KeyEvent> bool Context::handleKeyEvent(KeyEvent& event, bool value) {
/* Ensure we use the context we're linked to */
Expand Down Expand Up @@ -337,12 +351,24 @@ MAGNUM_IMGUIINTEGRATION_OPTIONAL_CURSOR(No)
#undef MAGNUM_IMGUIINTEGRATION_OPTIONAL_CURSOR
#endif

template<class... T> static void callWarpCursor(const T&...) {}

template<class Application, class = decltype(&Application::warpCursor)>
static void callWarpCursor(Application& application, const Vector2i& position) {
application.warpCursor(position);
}
}

template<class Application> void Context::updateApplicationCursor(Application& application) {
/* Ensure we use the context we're linked to */
ImGui::SetCurrentContext(_context);

ImGuiIO& io = ImGui::GetIO();

if(io.WantSetMousePos) {
Implementation::callWarpCursor(application, Vector2i(Vector2(io.MousePos)/_eventScaling));
}

switch(ImGui::GetMouseCursor()) {
case ImGuiMouseCursor_TextInput:
application.setCursor(Application::Cursor::TextInput);
Expand Down
27 changes: 25 additions & 2 deletions src/Magnum/ImGuiIntegration/Test/ContextGLTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,11 @@ struct Application {
None = 999
};

void setCursor(Cursor cursor) { currentCursor = cursor; }
Cursor currentCursor = Cursor::None;
Vector2i mousePos;

void setCursor(Cursor cursor) { currentCursor = cursor; }
void warpCursor(const Vector2i& pos) { mousePos = pos; }
};

struct KeyEvent: public InputEvent {
Expand Down Expand Up @@ -752,7 +755,7 @@ void ContextGLTest::textInput() {
}

void ContextGLTest::updateCursor() {
Context c{{}};
Context c{{200, 200}, {400, 400}, {300, 300}};

Application app;

Expand Down Expand Up @@ -780,6 +783,26 @@ void ContextGLTest::updateCursor() {
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNESW);
c.updateApplicationCursor(app);
CORRADE_VERIFY(app.currentCursor == Application::Cursor::Arrow);

/* Change to imgui mouse pos and mark it as changed
Account for 2x DPI scaling in equality check */
ImGuiIO& io = ImGui::GetIO();
io.MousePos = ImVec2(10, 15);
io.WantSetMousePos = true;
c.updateApplicationCursor(app);
CORRADE_VERIFY(app.mousePos == Vector2i(20, 30));

/* Change to imgui mouse pos without marking it as changed */
io.MousePos = ImVec2(50, 0);
io.WantSetMousePos = false;
c.updateApplicationCursor(app);
CORRADE_VERIFY(app.mousePos == Vector2i(20, 30));

/* Mark mouse pos changed */
io.WantSetMousePos = true;
c.updateApplicationCursor(app);
CORRADE_VERIFY(app.mousePos == Vector2i(100, 0));

}

void ContextGLTest::multipleContexts() {
Expand Down