class
#include <Magnum/ImGuiIntegration/Context.h>
Context Dear ImGui context.
Handles initialization and destruction of a Dear ImGui context and implements a Magnum-based rendering backend.
Usage
Creating the Context instance will create the Dear ImGui context and make it current. From that point on you can use ImGui calls.
ImGuiIntegration::Context imgui{{640, 480}}; // ...
Rendering
Use newFrame() to initialize a ImGui frame and finally draw it with drawFrame() to the currently bound framebuffer. Dear ImGui requires scissor test to be enabled and depth test to be disabled. Blending should be enabled and set up as below. The following snippet sets up all required renderer state and then resets it back to default values. Adapt the state changes based on what else you are rendering. Right before drawFrame() you can call updateApplicationCursor() if your application implementation supports setting cursors.
GL::Renderer::setBlendEquation(GL::Renderer::BlendEquation::Add, GL::Renderer::BlendEquation::Add); GL::Renderer::setBlendFunction(GL::Renderer::BlendFunction::SourceAlpha, GL::Renderer::BlendFunction::OneMinusSourceAlpha); _imgui.newFrame(); // ImGui widget calls here ... _imgui.updateApplicationCursor(*this); GL::Renderer::enable(GL::Renderer::Feature::Blending); GL::Renderer::enable(GL::Renderer::Feature::ScissorTest); GL::Renderer::disable(GL::Renderer::Feature::DepthTest); GL::Renderer::disable(GL::Renderer::Feature::FaceCulling); _imgui.drawFrame(); GL::Renderer::enable(GL::Renderer::Feature::DepthTest); GL::Renderer::enable(GL::Renderer::Feature::FaceCulling); GL::Renderer::disable(GL::Renderer::Feature::ScissorTest); GL::Renderer::disable(GL::Renderer::Feature::Blending); // ... redraw();
Event handling
The templated handleMousePressEvent(), handleMouseReleaseEvent() etc. functions are meant to be used inside event handlers of application classes such as Platform::event
parameter to them. The returned value is then true
if ImGui used the event (and thus it shouldn't be propagated further) and false
otherwise.
#include <Magnum/ImGuiIntegration/Context.hpp> // ... void MyApp::pointerPressEvent(PointerEvent& event) { if(_imgui.handlePointerPressEvent(event)) return; // event not handled by ImGui, handle it for the app itself } void MyApp::pointerReleaseEvent(PointerEvent& event) { if(_imgui.handlePointerReleaseEvent(event)) return; // ... } // ...
Text input
UTF-8 text input is handled via handleTextInputEvent() but the application implementations only call textInputEvent() when text input is enabled. This is done because some platforms require explicit action in order to start a text input (for example, to open an on-screen keyboard on touch devices, or IME for complex alphabets). ImGui exposes its desire to capture text input during a call to newFrame(). Based on that, you can toggle the text input in the application, for example using startTextInput() / stopTextInput() in Platform::
_imgui.newFrame(); if(ImGui::GetIO().WantTextInput && !isTextInputActive()) startTextInput(); else if(!ImGui::GetIO().WantTextInput && isTextInputActive()) stopTextInput();
The above snippet also means that ImGui's InputQueueCharacters
will be empty unless an text input box is focused — so if you want to handle text input through ImGui manually, you need to explicitly call startTextInput() / stopTextInput() when desired.
Loading custom fonts
The Context class does additional adjustments to ImGui font setup in order to make their scaling DPI-aware. If you load custom fonts, it's recommended to do that before the Context class is created, in which case it picks up the custom font as default. Create the ImGui context first, add the font and then construct the integration using the Context(ImGuiContext&, const Vector2i&) constructor, passing the already created ImGui context to it:
ImGui::CreateContext(); ImGui::GetIO().Fonts->AddFontFromFileTTF("SourceSansPro-Regular.ttf", 16.0f); ImGuiIntegration::Context imgui(*ImGui::GetCurrentContext(), {640, 480}); // ...
It's possible to load custom fonts after the Context instance been constructed as well, but you first need to clear the default font added during Context construction and finally call relayout() to make it pick up the updated glyph cache. Alternatively, if you don't call Clear()
, you need to explicitly call PushFont()
to switch to a non-default one. Compared to loading fonts before the Context is created, this is the less efficient option, as the glyph cache is unnecessarily built and discarded one more time.
ImGuiIntegration::Context imgui({640, 480}); // ... ImGui::GetIO().Fonts->Clear(); ImGui::GetIO().Fonts->AddFontFromFileTTF("SourceSansPro-Regular.ttf", 16.0f); imgui.relayout(windowSize());
See the DPI awareness section below for more information about configuring the fonts for HiDPI screens.
DPI awareness
There are three separate concepts for DPI-aware UI rendering:
- UI size — size of the user interface to which all widgets are positioned
- Window size — size of the window to which all input events are related
- Framebuffer size — size of the framebuffer the UI is being rendered to
Depending on the platform and use case, each of these three values can be different. For example, a game menu screen can have the UI size the same regardless of window size. Or on Retina macOS you can have different window and framebuffer size and the UI size might be related to window size but independent on the framebuffer size.
When using for example Platform::*Application
implementations, you usually have three values at your disposal — windowSize(), framebufferSize() and dpiScaling(). ImGui interfaces are usually positioned with pixel units, getting more room on bigger windows. A non-DPI-aware setup would be simply this:
ImGuiIntegration::Context imgui{windowSize()};
If you want the UI to keep a reasonable physical size and stay crisp with different pixel densities, pass a ratio of window size and DPI scaling to the UI size:
ImGuiIntegration::Context imgui{ Vector2{windowSize()}/dpiScaling(), windowSize(), framebufferSize()};
Finally, by clamping the first size
parameter you can achieve various other results like limiting it to a minimal / maximal area or have it fully scaled with window size. When window size, framebuffer size or DPI scaling changes (usually as a response to viewportEvent()), call relayout() with the new values. If the pixel density is changed, this will result in the font caches being rebuilt.
HiDPI fonts
There are further important steps for DPI awareness if you are supplying custom fonts. Use the Context(ImGuiContext&, const Vector2&, const Vector2i&, const Vector2i&) constructor and pre-scale their size by the ratio of size
and framebufferSize
. If you don't do that, the fonts will appear tiny on HiDPI screens. Example:
ImGui::CreateContext(); const Vector2 size = Vector2{windowSize()}/dpiScaling(); ImGui::GetIO().Fonts->AddFontFromFileTTF("SourceSansPro-Regular.ttf", 16.0f*framebufferSize().x()/size.x()); ImGuiIntegration::Context imgui(*ImGui::GetCurrentContext(), size, windowSize(), framebufferSize()); // ...
If you supplied custom fonts and pixel density changed, in order to regenerate them you have to clear the font atlas and re-add all fonts again with a different scaling before calling relayout(), for example:
void MyApp::viewportEvent(ViewportEvent& event) { // ... const Vector2 size = Vector2{event.windowSize()}/event.dpiScaling(); /* Reload fonts if pixel density changed */ const Float supersamplingRatio = Float(event.framebufferSize().x())/size.x(); if(supersamplingRatio != _supersamplingRatio) { _supersamplingRatio = supersamplingRatio; ImGui::GetIO().Fonts->Clear(); // important ImGui::GetIO().Fonts->AddFontFromFileTTF("SourceSansPro-Regular.ttf", 16.0f*supersamplingRatio); } _imgui.relayout(size, event.windowSize(), event.framebufferSize()); }
If you don't do that, the fonts stay at the original scale, not matching the new UI scaling anymore. If you didn't supply any custom font, the function will reconfigure the builtin font automatically.
Large meshes
Complex user interfaces or widgets like ImPlot may end up creating large meshes with more than 65k vertices. Because ImGui defaults to 16-bit index buffers this can lead to asserts or visual errors.
If the underlying GL context supports setting the base vertex for indexed meshes the rendering backend sets the ImGuiBackendFlags_RendererHasVtxOffset
flag. This lets ImGui know the backend can handle per-draw vertex offsets, removing the 65k limitation altogether. Support for that requires one of the following:
If you can't guarantee that the required GL versions or extensions will be available at runtime (especially relevant on WebGL), the next best option is to change ImGui's index type to 32-bit by adding the following line to the ImGui user config:
#define ImDrawIdx unsigned int
This doubles the size of the index buffer, resulting in potentially reduced draw performance, but is guaranteed to work on all GL versions.
Drawing custom textures
In order to draw a GL::ImTextureID
, use the textureId() helper to create an ImGui texture ID from a GL::
Multiple contexts
Each instance of Context creates a new ImGui context. You can also pass an existing context to the Context(ImGuiContext&, const Vector2i&) constructor, which will then take ownership (and thus delete it on destruction). Switching between various ImGui
contexts wrapped in Context instances is done automatically when calling any of the relayout(), newFrame(), drawFrame() APIs or the event handling functions. You can also query the instance-specific context with context() and call ImGui::SetCurrentContext()
manually on that.
It's also possible to create a context-less instance using the Context(NoCreateT) constructor and release context ownership using release(). Such instances, together with moved-out instances are empty and calling any API that interacts with ImGui is not allowed on these.
Constructors, destructors, conversion operators
- Context(const Vector2& size, const Vector2i& windowSize, const Vector2i& framebufferSize) explicit
- Constructor.
- Context(const Vector2i& size) explicit
- Construct without DPI awareness.
- Context(ImGuiContext& context, const Vector2& size, const Vector2i& windowSize, const Vector2i& framebufferSize) explicit
- Construct from an existing context.
- Context(ImGuiContext& context, const Vector2i& size) explicit
- Construct from an existing context without DPI awareness.
- Context(NoCreateT) explicit noexcept
- Construct without creating the underlying ImGui context.
- Context(const Context&) deleted
- Copying is not allowed.
- Context(Context&& other) noexcept
- Move constructor.
- ~Context()
- Destructor.
Public functions
- auto operator=(const Context&) -> Context& deleted
- Copying is not allowed.
- auto operator=(Context&& other) -> Context& noexcept
- Move assignment.
- auto context() -> ImGuiContext*
- Underlying ImGui context.
- auto release() -> ImGuiContext*
- Release the underlying ImGui context.
-
auto atlasTexture() -> GL::
Texture2D& new in 2020.06 - Font texture used in
ImFontAtlas
- void relayout(const Vector2& size, const Vector2i& windowSize, const Vector2i& framebufferSize)
- Relayout the context.
- void relayout(const Vector2i& size)
- Relayout the context.
- void newFrame()
- Start a new frame.
- void drawFrame()
- Draw a frame.
-
template<class PointerEvent>auto handlePointerPressEvent(PointerEvent& event) -> bool new in Git master
- Handle pointer press event.
-
template<class MouseEvent>auto handleMousePressEvent(MouseEvent& event) -> bool deprecated in Git master
- Handle mouse press event.
-
template<class PointerEvent>auto handlePointerReleaseEvent(PointerEvent& event) -> bool new in Git master
- Handle pointer release event.
-
template<class MouseEvent>auto handleMouseReleaseEvent(MouseEvent& event) -> bool deprecated in Git master
- Handle mouse release event.
-
template<class ScrollEvent>auto handleScrollEvent(ScrollEvent& event) -> bool new in Git master
- Handle scroll event.
-
template<class MouseScrollEvent>auto handleMouseScrollEvent(MouseScrollEvent& event) -> bool deprecated in Git master
- Handle mouse scroll event.
-
template<class PointerMoveEvent>auto handlePointerMoveEvent(PointerMoveEvent& event) -> bool new in Git master
- Handle pointer move event.
-
template<class MouseMoveEvent>auto handleMouseMoveEvent(MouseMoveEvent& event) -> bool deprecated in Git master
- Handle mouse move event.
-
template<class KeyEvent>auto handleKeyPressEvent(KeyEvent& event) -> bool
- Handle key press event.
-
template<class KeyEvent>auto handleKeyReleaseEvent(KeyEvent& event) -> bool
- Handle key release event.
-
template<class TextInputEvent>auto handleTextInputEvent(TextInputEvent& event) -> bool
- Handle text input event.
-
template<class Application>void updateApplicationCursor(Application& application) new in 2020.06
- Update application mouse cursor.
Function documentation
Magnum:: ImGuiIntegration:: Context:: Context(const Vector2& size,
const Vector2i& windowSize,
const Vector2i& framebufferSize) explicit
Constructor.
Parameters | |
---|---|
size | Size of the user interface to which all widgets are positioned |
windowSize | Size of the window to which all input events are related |
framebufferSize | Size of the window framebuffer. On some platforms with HiDPI screens may be different from window size. |
This function creates the ImGui context using ImGui::CreateContext()
and then queries the font glyph cache from ImGui, uploading it to the GPU. If you need to do some extra work on the context and before the font texture gets uploaded, use Context(ImGuiContext&, const Vector2&, const Vector2i&, const Vector2i&) instead.
The sizes are allowed to be zero in any dimension, but note that specifying a concrete value later in relayout() may trigger an unnecessary rebuild of the font glyph cache due to different calculated pixel density. See DPI awareness for more information about the different size arguments. If you don't need DPI awareness, you can use the simpler Context(const Vector2i&) constructor instead.
Magnum:: ImGuiIntegration:: Context:: Context(const Vector2i& size) explicit
Construct without DPI awareness.
Equivalent to calling Context(const Vector2&, const Vector2i&, const Vector2i&) with size
passed to all three parameters.
Magnum:: ImGuiIntegration:: Context:: Context(ImGuiContext& context,
const Vector2& size,
const Vector2i& windowSize,
const Vector2i& framebufferSize) explicit
Construct from an existing context.
Parameters | |
---|---|
context | Existing ImGui context |
size | Size of the user interface to which all widgets are positioned |
windowSize | Size of the window to which all inputs events are related |
framebufferSize | Size of the window framebuffer. On some platforms with HiDPI screens may be different from window size. |
Expects that no instance is created yet; takes ownership of the passed context, deleting it on destruction. In comparison to Context(const Vector2&, const Vector2i&, const Vector2i&) this constructor is useful if you need to do some work before the font glyph cache gets uploaded to the GPU, for example adding custom fonts.
See DPI awareness for more information about the different size arguments. If you don't need DPI awareness, you can use the simpler Context(ImGuiContext&, const Vector2i&) constructor instead. Note that, in order to have the custom fonts crisp also on HiDPI screens, you have to pre-scale their size by the ratio of size
and framebufferSize
. See HiDPI fonts for more information.
Magnum:: ImGuiIntegration:: Context:: Context(ImGuiContext& context,
const Vector2i& size) explicit
Construct from an existing context without DPI awareness.
Equivalent to calling Context(ImGuiContext&, const Vector2&, const Vector2i&, const Vector2i&) with size
passed to the last three parameters. In comparison to Context(const Vector2i&) this constructor is useful if you need to do some work before the font glyph cache gets uploaded to the GPU, for example adding custom fonts.
Magnum:: ImGuiIntegration:: Context:: Context(NoCreateT) explicit noexcept
Construct without creating the underlying ImGui context.
This constructor also doesn't create any internal OpenGL objects, meaning it can be used without an active OpenGL context. Calling any APIs that interact with ImGui on such instance is not allowed. Move a non-empty instance over to make it useful.
ImGuiContext* Magnum:: ImGuiIntegration:: Context:: release()
Release the underlying ImGui context.
Returns the underlying ImGui context and sets the internal context pointer to nullptr
, making the instance equivalent to a moved-out state. Calling APIs that interact with ImGui is not allowed on the instance anymore.
void Magnum:: ImGuiIntegration:: Context:: relayout(const Vector2& size,
const Vector2i& windowSize,
const Vector2i& framebufferSize)
Relayout the context.
Parameters | |
---|---|
size | Size of the user interface to which all widgets are positioned |
windowSize | Size of the window to which all input events are related |
framebufferSize | Size of the window framebuffer. On some platforms with HiDPI screens may be different from window size. |
Calls ImGui::SetCurrentContext()
on context() and adapts the internal state for a new window size or pixel density. In case the pixel density gets changed, font glyph caches are rebuilt to match the new pixel density.
The sizes are allowed to be zero in any dimension, but note that it may trigger an unwanted rebuild of the font glyph cache due to different calculated pixel density. See DPI awareness for more information about the different size arguments. If you don't need DPI awareness, you can use the simpler relayout(const Vector2i&) function instead.
void Magnum:: ImGuiIntegration:: Context:: relayout(const Vector2i& size)
Relayout the context.
Equivalent to calling relayout(const Vector2&, const Vector2i&, const Vector2i&) with size
passed to all three parameters.
void Magnum:: ImGuiIntegration:: Context:: newFrame()
Start a new frame.
Calls ImGui::SetCurrentContext()
on context() and initializes a new ImGui frame using ImGui::NewFrame()
. This function also decides if a text input needs to be enabled, see Text input for more information.
void Magnum:: ImGuiIntegration:: Context:: drawFrame()
Draw a frame.
Calls ImGui::SetCurrentContext()
on context(), ImGui::Render()
and then draws the frame created by ImGui calls since last call to newFrame() to currently bound framebuffer.
See Rendering for more information on which rendering states to set before and after calling this method.
template<class PointerEvent>
bool Magnum:: ImGuiIntegration:: Context:: handlePointerPressEvent(PointerEvent& event) new in Git master
Handle pointer press event.
Calls ImGui::SetCurrentContext()
on context() first and then propagates the event, such as the one coming from Platform::true
if ImGui wants to capture the mouse (so the event shouldn't be further propagated to the rest of the application), false
otherwise.
If the event isn't primary (such as a second and following finger press in a multi-touch scenario), the function does nothing and returns false
.
template<class MouseEvent>
bool Magnum:: ImGuiIntegration:: Context:: handleMousePressEvent(MouseEvent& event)
Handle mouse press event.
Calls ImGui::SetCurrentContext()
on context() first and then propagates the event, such as the one coming from Platform::true
if ImGui wants to capture the mouse (so the event shouldn't be further propagated to the rest of the application), false
otherwise.
template<class PointerEvent>
bool Magnum:: ImGuiIntegration:: Context:: handlePointerReleaseEvent(PointerEvent& event) new in Git master
Handle pointer release event.
Calls ImGui::SetCurrentContext()
on context() first and then propagates the event, such as the one coming from Platform::true
if ImGui wants to capture the mouse (so the event shouldn't be further propagated to the rest of the application), false
otherwise.
If the event isn't primary (such as a second and following finger press in a multi-touch scenario), the function does nothing and returns false
.
template<class MouseEvent>
bool Magnum:: ImGuiIntegration:: Context:: handleMouseReleaseEvent(MouseEvent& event)
Handle mouse release event.
Calls ImGui::SetCurrentContext()
on context() first and then propagates the event, such as the one coming from Platform::true
if ImGui wants to capture the mouse (so the event shouldn't be further propagated to the rest of the application), false
otherwise.
template<class ScrollEvent>
bool Magnum:: ImGuiIntegration:: Context:: handleScrollEvent(ScrollEvent& event) new in Git master
Handle scroll event.
Calls ImGui::SetCurrentContext()
on context() first and then propagates the event, such as the one coming from Platform::true
if ImGui wants to capture the mouse (so the event shouldn't be further propagated to the rest of the application), false
otherwise.
template<class MouseScrollEvent>
bool Magnum:: ImGuiIntegration:: Context:: handleMouseScrollEvent(MouseScrollEvent& event)
Handle mouse scroll event.
Calls ImGui::SetCurrentContext()
on context() first and then propagates the event, such as the one coming from Platform::true
if ImGui wants to capture the mouse (so the event shouldn't be further propagated to the rest of the application), false
otherwise.
template<class PointerMoveEvent>
bool Magnum:: ImGuiIntegration:: Context:: handlePointerMoveEvent(PointerMoveEvent& event) new in Git master
Handle pointer move event.
Calls ImGui::SetCurrentContext()
on context() first and then propagates the event, such as the one coming from Platform::true
if ImGui wants to capture the mouse (so the event shouldn't be further propagated to the rest of the application), false
otherwise.
If the event isn't primary (such as a second and following finger press in a multi-touch scenario), the function does nothing and returns false
.
template<class MouseMoveEvent>
bool Magnum:: ImGuiIntegration:: Context:: handleMouseMoveEvent(MouseMoveEvent& event)
Handle mouse move event.
Calls ImGui::SetCurrentContext()
on context() first and then propagates the event, such as the one coming from Platform::true
if ImGui wants to capture the mouse (so the event shouldn't be further propagated to the rest of the application), false
otherwise.
template<class KeyEvent>
bool Magnum:: ImGuiIntegration:: Context:: handleKeyPressEvent(KeyEvent& event)
Handle key press event.
Calls ImGui::SetCurrentContext()
on context() first and then propagates the event, such as the one coming from Platform::true
if ImGui wants to capture the keyboard (so the event shouldn't be further propagated to the rest of the application), false
otherwise.
template<class KeyEvent>
bool Magnum:: ImGuiIntegration:: Context:: handleKeyReleaseEvent(KeyEvent& event)
Handle key release event.
Calls ImGui::SetCurrentContext()
on context() first and then propagates the event, such as the one coming from Platform::true
if ImGui wants to capture the keyboard (so the event shouldn't be further propagated to the rest of the application), false
otherwise.
template<class TextInputEvent>
bool Magnum:: ImGuiIntegration:: Context:: handleTextInputEvent(TextInputEvent& event)
Handle text input event.
Calls ImGui::SetCurrentContext()
on context() first and then propagates the event, such as the one coming from Platform::true
if ImGui wants to capture the keyboard (so the event shouldn't be further propagated to the rest of the application), false
otherwise.
template<class Application>
void Magnum:: ImGuiIntegration:: Context:: updateApplicationCursor(Application& application) new in 2020.06
Update application mouse cursor.
Calls ImGui::SetCurrentContext()
on context() first and then queries ImGui::GetMouseCursor()
, propagating that to the application via setCursor(). If the application doesn't implement a corresponding cursor, falls back to Cursor::