imgui icon indicating copy to clipboard operation
imgui copied to clipboard

Consistent look across scale changes

Open wolfpld opened this issue 3 months ago • 3 comments

Version/Branch of Dear ImGui:

master

Back-ends:

n/a

Compiler, OS:

n/a

Full config/build information:

No response

Details:

The FAQ says that the indented way to scale the UI is to change the font size and apply a scaling factor to the style. This works reasonably well, but has it's limitations.

For example, here is a demo application with 1x scaling: obraz However, if the UI scale is changed at runtime, window sizes and positions are not affected: obraz This can be quite annoying, as the contents of the window no longer fit in the space previously allocated to them. The same is true when you scale down, and you end up with a huge window with very little content.

The use cases for runtime scale changes could be:

  • The user is presenting a UI to an audience and finds that the details are hard to see. An application-defined way to scale things up just for the duration of the presentation would be appreciated. Note that the display area remains the same in this case, so there is less space for everything.
  • The user moves the application window to a monitor with a different DPI scale. The application adapts to this change and provides a larger display area, but as far as UI rendering is concerned, the display area divided by the scaling factor remains the same (with some rounding errors that are negligible).

I have hacked a solution that makes things behave as expected in these cases.

    const auto scaleAdjust = scale / prevScale;
    prevScale = scale;

    auto ctx = ImGui::GetCurrentContext();
    for( auto& w : ctx->Windows )
    {
        w->Pos.x = round( w->Pos.x * scaleAdjust );
        w->Pos.y = round( w->Pos.y * scaleAdjust );
        w->Size.x = round( w->Size.x * scaleAdjust );
        w->Size.y = round( w->Size.y * scaleAdjust );
        w->SizeFull.x = round( w->SizeFull.x * scaleAdjust );
        w->SizeFull.y = round( w->SizeFull.y * scaleAdjust );
    }

obraz There are some more or less obvious drawbacks to this.

  • The application code digs through ImGui internals, which can change at any time.
  • It's hard to be completely sure which window properties should be changed. For example, what is the relationship between Size and SizeFull? What about other properties that may also need to be changed? Application developers should not have to know these things.
  • The scaling is not 1:1, as can be seen with the 2x scale in the screenshot above. This can be caused by some style paddings or borders. Ideally, this should be fixed, but it's now more of a cosmetic issue than a usability one.
  • Some code may do things like ImGui::SameLine( ImGui::GetWindowContentRegionMax().x - ImGui::CalcTextSize( ICON_FA_WRENCH ).x - ImGui::GetStyle().FramePadding.x * 2 ); to align UI elements to the right, and due to the way auto-fit window sizing is handled, this will break when the scale factor decreases. A hacky solution is to skip rendering these UI elements until things settle down, which is acceptable if UI resizing is controlled by the application.
  • There's probably a lot of interaction with the multi-viewport branch that I'm ignoring since I don't use it. However, if the scaling is to be (at least partially) driven by ImGui internals, then the code in the previous bullet point breaks.
  • I noticed that UI elements such as scrollable lists jump to a "random" place after a resize. A proper solution would need to identify places where this can happen and suggest a solution.

I would like to ask for an official API to adjust window positions and sizes at the request of the application. It might be worth considering if the position and size adjustment should only be done if the appropriate flags are specified. In case of a DPI change, both adjustments would be expected. However, if the UI scale is changed manually by the user, the application may not want to adjust the window positions because the display area remains the same.

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

No response

wolfpld avatar Mar 12 '24 13:03 wolfpld

I'm mostly away from keyboard for a few weeks so won't be able to dig, but there's machinery currently wired in ImGuiConfigFlags_DpiEnableScaleViewports to scale/position windows by calling ImGui::ScaleWindowsInViewport(), and I believe this machinery could probably be rewired to behave based on another source of data.

ocornut avatar Mar 15 '24 04:03 ocornut

Ah, it seems that ScaleWindow() is doing pretty much what I came up with. It is static though, but making it a public API should be straightforward.

It also exhibits the problems I have listed (right align, scroll position), so that's something to maybe take a look into.

wolfpld avatar Mar 15 '24 10:03 wolfpld

It's not clear to me how DPI scaling is handled when ImGui window sizes and positions are saved and loaded from imgui.ini. An application can save window sizes when it's at one scale and load them when it's at another scale, but there is no conversion step that would normalize these values so that the sizes remain consistent no matter what scale was used during saving and loading.

Users can handle scale normalization for the save part by manually resizing all windows to a scale of 1. But when the application's DPI setup is done at startup, the window list is not yet loaded (calling LoadIniSettingsFromDisk doesn't help), so nothing is updated on load because the DPI scaling is already set when windows are being loaded.

wolfpld avatar Mar 15 '24 20:03 wolfpld