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

Pre-compute the size of widgets #3714

Open
mistahmikey opened this issue Jan 12, 2021 · 6 comments
Open

Pre-compute the size of widgets #3714

mistahmikey opened this issue Jan 12, 2021 · 6 comments
Labels

Comments

@mistahmikey
Copy link

My Issue/Question:

Is there a way to compute the size of a widget before it is drawn so you can affect it's layout intelligently? For example, if you want to call, say, InputInt, is there some way to precompute how much space it will take given a particular set of arguments/style settings? I searched through the demo code but was unable to find any such examples.

@thedmd
Copy link
Contributor

thedmd commented Jan 12, 2021

Right now there is no official method to measure widgets before they're drawn. Most of the time you can specify its size before it is drawn.

There are methods that can be applied if you don't mind running logic twice. Out of the box I see two possible solutions:

  • run a 'dry' frame where every widget you care about is rendered with it's default size, you can record it for use in next frame
  • remember cursor position, draw a widget*, record size, move cursor back, draw widget again

*Widget can be drawn with two methods:

  • with zero transparency (see ImGuiStyle::Alpha)
  • using ImDrawListSplitter with two channels, select second channel, after drawing select to first channel but do not merge splitter back, also ImDrawList vertex pointer can be moved back to reuse vertices used by "invisible" widget.

@ocornut
Copy link
Owner

ocornut commented Jan 12, 2021 via email

@ocornut
Copy link
Owner

ocornut commented Jan 13, 2021

Some clarification as I sent yesterday's message hastily from my phone:

Widget heights are almost always

  • either CalcTextSize(text).y for unframed items, == GetTextLineHeight() for single-line label.
  • either CalcTextSize(text).y + style.FramePadding.y * 2.0f for framed items, == GetFrameHeight() for single-line label.

Widget widths are not as clearly defined, but:

  • separate sub-component (e.g. frame vs label) are separated with style.ItemInnerSpacing.x worth of spacing.
  • small square items frames such as checkboxes or color button are GetFrameHeight()-wide and GetFrameHeight()-high (you can imagine why).
  • for large frame widgets with frame + separate label, the frame width is CalcItemWidth(), the label width is CalcTextSize(label).x and there have the regular spacing style.ItemInnerSpacing.x.

@ocornut ocornut added the layout label Jan 13, 2021
@ocornut
Copy link
Owner

ocornut commented Jan 13, 2021

We are currently missing - and we will eventually implement - a "null" layout mode which will allow submitting items for size measurement.

@gucio321
Copy link

gucio321 commented Sep 26, 2021

Hi guys! I also faced this issue. I'm trying to center a button using advices from the comment above (#3714 (comment)), but I met the following problem:
my first button (despite it shouldn't be rendered) is able to be clicked by user.
I'm using the following code:

        {

            ImGui::Begin("Hello, world!");

            ImDrawList* list =  ImGui::GetWindowDrawList();

            struct foo {
                    bool btn() { return ImGui::Button("btn",ImVec2{200,20}); };
            } btn;

            ImVec2 start = ImGui::GetCursorPos();
            ImVec2 size;

            list->ChannelsSplit(2);

            ImGui::PushID("someotherid");
            list->ChannelsSetCurrent(1);
            if (btn.btn() ) {
                    std::cout << "btn 1 (invisible) pressed\n";
            };
            ImGui::PopID();

            size = ImGui::GetItemRectSize();
            ImVec2 available = ImGui::GetContentRegionMax();
            start.x = (available.x-size.x)/2;
            ImGui::SetCursorPos(start);

            list->ChannelsSetCurrent(0);
            if (btn.btn()){
                    std::cout << "btn 1 pressed\n";
            };

            ImGui::End();
        }

Am I doing something wrong?

@rokups
Copy link
Contributor

rokups commented Oct 18, 2021

You should manually calculate expected width of a button before rendering it as described in #3714 (comment).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants