4848
4949#include < algorithm>
5050
51+ #include < cmath>
52+
5153namespace dart {
5254namespace gui {
5355
@@ -201,6 +203,29 @@ ConvertedKey convertFromOSGKey(int key)
201203 }
202204}
203205
206+ // ==============================================================================
207+ void applyImGuiScale (float scale)
208+ {
209+ if (!std::isfinite (scale) || scale <= 0 .f )
210+ return ;
211+
212+ static ImGuiStyle baseStyle
213+ = ImGui::GetStyle (); // capture styled defaults (dark palette)
214+ auto & style = ImGui::GetStyle ();
215+ ImGuiIO& io = ImGui::GetIO ();
216+
217+ // Reset to the captured base style before applying the requested scale so
218+ // repeated calls are idempotent and preserve the configured palette.
219+ style = baseStyle;
220+ io.FontGlobalScale = 1 .f ;
221+
222+ if (std::abs (scale - 1 .f ) < 1e-6f )
223+ return ;
224+
225+ style.ScaleAllSizes (scale);
226+ io.FontGlobalScale = scale;
227+ }
228+
204229// ==============================================================================
205230struct ImGuiNewFrameCallback : public ::osg::Camera::DrawCallback
206231{
@@ -237,7 +262,10 @@ struct ImGuiDrawCallback : public ::osg::Camera::DrawCallback
237262
238263// ==============================================================================
239264ImGuiHandler::ImGuiHandler ()
240- : mTime {0.0 }, mMousePressed {false , false , false }, mMouseWheel {0 .0f }
265+ : mTime {0.0 },
266+ mMousePressed {false , false , false },
267+ mMouseWheel {0 .0f },
268+ mFramebufferScale {1 .0f , 1 .0f }
241269{
242270 ImGui::CreateContext ();
243271
@@ -344,6 +372,18 @@ bool ImGuiHandler::handle(
344372 const bool wantCaptureMouse = io.WantCaptureMouse ;
345373 const bool wantCaptureKeyboard = io.WantCaptureKeyboard ;
346374
375+ const float invScaleX
376+ = mFramebufferScale [0 ] > 0 .0f ? 1 .0f / mFramebufferScale [0 ] : 1 .0f ;
377+ const float invScaleY
378+ = mFramebufferScale [1 ] > 0 .0f ? 1 .0f / mFramebufferScale [1 ] : 1 .0f ;
379+
380+ const auto getScaledMousePosition
381+ = [&](const osgGA::GUIEventAdapter& adapter) {
382+ const float scaledX = static_cast <float >(adapter.getX ()) * invScaleX;
383+ const float scaledY = static_cast <float >(adapter.getY ()) * invScaleY;
384+ return ImVec2 (scaledX, io.DisplaySize .y - scaledY);
385+ };
386+
347387 switch (eventAdapter.getEventType ()) {
348388 case osgGA::GUIEventAdapter::KEYDOWN: {
349389 const int key = eventAdapter.getUnmodifiedKey ();
@@ -417,8 +457,7 @@ bool ImGuiHandler::handle(
417457 return wantCaptureKeyboard;
418458 }
419459 case osgGA::GUIEventAdapter::PUSH: {
420- io.MousePos
421- = ImVec2 (eventAdapter.getX (), io.DisplaySize .y - eventAdapter.getY ());
460+ io.MousePos = getScaledMousePosition (eventAdapter);
422461
423462 if (eventAdapter.getButtonMask ()
424463 == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON) {
@@ -440,8 +479,7 @@ bool ImGuiHandler::handle(
440479 }
441480 case osgGA::GUIEventAdapter::DRAG:
442481 case osgGA::GUIEventAdapter::MOVE: {
443- io.MousePos
444- = ImVec2 (eventAdapter.getX (), io.DisplaySize .y - eventAdapter.getY ());
482+ io.MousePos = getScaledMousePosition (eventAdapter);
445483
446484 return wantCaptureMouse;
447485 }
@@ -489,9 +527,39 @@ void ImGuiHandler::newFrame(::osg::RenderInfo& renderInfo)
489527 ImGui_ImplOpenGL2_NewFrame ();
490528
491529 auto & io = ImGui::GetIO ();
492- auto * viewport = renderInfo.getCurrentCamera ()->getViewport ();
530+ auto * camera = renderInfo.getCurrentCamera ();
531+ auto * viewport = camera ? camera->getViewport () : nullptr ;
532+
533+ ImVec2 displaySize (1 .0f , 1 .0f );
534+ ImVec2 framebufferScale (1 .0f , 1 .0f );
535+
536+ if (viewport) {
537+ // Use framebuffer-to-window ratio to honor OS display scaling.
538+ displaySize = ImVec2 (viewport->width (), viewport->height ());
539+
540+ auto * graphicsContext = camera->getGraphicsContext ();
541+ const auto * traits
542+ = graphicsContext ? graphicsContext->getTraits () : nullptr ;
543+ if (traits && traits->width > 0 && traits->height > 0 ) {
544+ framebufferScale.x = static_cast <float >(viewport->width ())
545+ / static_cast <float >(traits->width );
546+ framebufferScale.y = static_cast <float >(viewport->height ())
547+ / static_cast <float >(traits->height );
548+
549+ framebufferScale.x
550+ = framebufferScale.x > 0 .0f ? framebufferScale.x : 1 .0f ;
551+ framebufferScale.y
552+ = framebufferScale.y > 0 .0f ? framebufferScale.y : 1 .0f ;
553+
554+ displaySize = ImVec2 (
555+ static_cast <float >(traits->width ),
556+ static_cast <float >(traits->height ));
557+ }
558+ }
493559
494- io.DisplaySize = ImVec2 (viewport->width (), viewport->height ());
560+ io.DisplaySize = displaySize;
561+ io.DisplayFramebufferScale = framebufferScale;
562+ mFramebufferScale = {framebufferScale.x , framebufferScale.y };
495563
496564 const auto currentTime
497565 = renderInfo.getView ()->getFrameStamp ()->getSimulationTime ();
0 commit comments