-
-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
Column headers #513
Comments
A more elaborate code snippet for headers.
|
Ok, after 1 coffee (I'm sick today...), here is a cleaner helper for column headers. Considering you have two user files imgui_user.h and imgui_user.inl, here is the code for the helper : In imgui_user.h : namespace ImGui
{
// ColumnHeader
struct ColumnHeader
{
const char* label = NULL; // Label of the header
float size = -1.0f; // Negative value will calculate the size to fit label
};
// Draw column header
IMGUI_API void ColumnHeaders(const char* columnsId, ColumnHeader* headers, int count, bool setWidths, bool border=true, bool separator=true);
} // namespace ImGui In imgui_user.inl : namespace ImGui
{
// Draw column header
void ColumnHeaders(const char* columnsId, ColumnHeader* headers, int count, bool setWidths, bool border, bool separator)
{
ImGui::Columns(count, columnsId, border);
if(separator)
ImGui::Separator();
float offset = 0.0f;
ImGuiStyle & style = ImGui::GetStyle();
for(int i=0; i < count; i++)
{
const ColumnHeader & header = headers[i];
if(setWidths)
{
ImGui::SetColumnOffset(-1, offset);
if(header.size >= 0)
{
offset += header.size;
}
else
{
ImVec2 textsize = ImGui::CalcTextSize(header.label, NULL, true);
offset += (textsize.x + 2 * style.ItemSpacing.x);
}
}
ImGui::Text(header.label);
ImGui::NextColumn();
}
if(separator)
ImGui::Separator();
}
} // namespace ImGui And the code you put in your GUI : // Column headers
const bool makeColumsWidthFixed = false;
static bool s_firstTime = true;
ImGui::ColumnHeader headers[] =
{
{ "Idx", -1 },
{ "Name", 500 },
{ "RT", -1 },
{ "Depth", -1 },
{ "Full", -1 },
{ "ClearColor", -1 },
{ "Width", 150 },
{ "Height", 150 },
{ "Filter", 150 },
{ "WrapU", 150 },
{ "WrapV", 150 },
{ "Flip", -1 },
{ "Filename", 450 }
};
ImGui::ColumnHeaders("WinTextureColumns", headers, EASE_ARRAYSIZE(headers), s_firstTime || makeColumsWidthFixed, true, true);
if(s_firstTime)
s_firstTime = false; Ok, the "s_firstTime" flag could be handled internally the same way it is done for window's sizing... But I'm sick today... |
I'm still wondering how to make the column-headers fixed (like a top row in excel). |
Thanks. We may want to use that for inspiration as the columns API develops. To answer your question, it can be done by duplicating the column set outside and inside a child window, but the columns offset needs to be synchronized across two sets. It's possible right now but a bit messy - figuring out minor offsetting issues due to child window padding and the presence of a scrollbar. That's the sort of thing that once we figure out we should include as part of a helper, maybe BeginColumns()/EndColumns(), so it's good to experiment with it. Some other consideration: (some of them are discussed in #125)
We don't need all those features ready but I want to be confident that we can tackle all of them properly before introducing a new BeginColumns() api. So any research in those area is useful. PS: You don't have to user imgui_user files anyway since you can include |
Ok, I followed your advice, and it works pretty well when syncing both set of columns. imgui_user.h : namespace ImGui
{
// ColumnHeader
struct ColumnHeader
{
const char* label = NULL; // Label of the header
float size = -1.0f; // Negative value will calculate the size to fit label
// Internal
float syncOffset = -1.0f; // Internal offset used for sync purpose
};
// Draw column headers
IMGUI_API void ColumnHeaders(const char* columnsId, ColumnHeader* headers, int count, bool border=true);
// Synchronize with column headers
IMGUI_API void BeginColumnHeadersSync(const char* columnsId, ColumnHeader* headers, int count, bool border=true);
IMGUI_API void EndColumnHeadersSync(ColumnHeader* headers, int count);
} // namespace ImGui imgui_user.inl : // Draw column headers
void ColumnHeaders(const char* columnsId, ColumnHeader* headers, int count, bool border)
{
if(count<=0)
return;
ImGuiStyle & style = ImGui::GetStyle();
const ImVec2 firstTextSize = ImGui::CalcTextSize(headers[0].label, NULL, true);
ImGui::BeginChild(columnsId, ImVec2(0,firstTextSize.y + 2 * style.ItemSpacing.y), true);
char str_id[256];
ImFormatString(str_id, IM_ARRAYSIZE(str_id), "col_%s", columnsId);
ImGui::Columns(count, str_id, border);
float offset = 0.0f;
for(int i=0; i < count; i++)
{
ColumnHeader & header = headers[i];
printf("SetColumnOffset %d -> %d\n", i, int(header.syncOffset));
if(header.syncOffset < 0.0f)
{
ImGui::SetColumnOffset(i, offset);
if(header.size >= 0)
{
offset += header.size;
}
else
{
const ImVec2 textsize = ImGui::CalcTextSize(header.label, NULL, true);
offset += (textsize.x + 2 * style.ItemSpacing.x);
}
}
else
{
ImGui::SetColumnOffset(i, header.syncOffset);
}
header.syncOffset = ImGui::GetColumnOffset(i);
printf("Header %d -> %d\n", i, int(header.syncOffset));
ImGui::Text(header.label);
ImGui::NextColumn();
}
ImGui::Columns(1);
ImGui::EndChild();
}
// Synchronize with column headers
void BeginColumnHeadersSync(const char* columnsId, ColumnHeader* headers, int count, bool border)
{
if(count<=0)
return;
ImGui::BeginChild(columnsId, ImVec2(0,0), true);
ImGui::Columns(count, columnsId, border);
float offset = 0.0f;
ImGuiStyle & style = ImGui::GetStyle();
for(int i=0; i < count; i++)
{
ColumnHeader & header = headers[i];
ImGui::SetColumnOffset(i, header.syncOffset);
header.syncOffset = ImGui::GetColumnOffset(i);
printf("Content %d -> %d\n", i, int(header.syncOffset));
}
}
void EndColumnHeadersSync(ColumnHeader* headers, int count)
{
if(count<=0)
return;
ImGui::Columns(1);
ImGui::EndChild();
} And the usage becomes a lot easier : // Header definition must be STATIC in order to keep internal data alive. Else, columns will not be dragable.
static ImGui::ColumnHeader headers[] =
{
{ "Idx", -1 },
{ "Name", 500 },
{ "RT", -1 },
{ "Depth", -1 },
{ "Full", -1 },
{ "ClearColor", -1 },
{ "Width", 150 },
{ "Height", 150 },
{ "Filter", 150 },
{ "WrapU", 150 },
{ "WrapV", 150 },
{ "Flip", -1 },
{ "Filename", 450 }
};
ImGui::ColumnHeaders("WinTextureHeader", headers, IM_ARRAYSIZE(headers), true);
// Table content
ImGui::BeginColumnHeadersSync("WinTextureContent", headers, IM_ARRAYSIZE(headers), true);
// DRAW ALL YOUR ROWS HERE
ImGui::EndColumnHeadersSync(headers, EASE_ARRAYSIZE(headers)); The only 2 things I still dislike is :
Ok. Next, I would like to handle the double-click on a column-bar in order to resize the column to come back to its original size (same than Excel). |
That fix is definitively desirable. We should definitively find a way to fix that. However, when using headers it may also be acceptable to restrict resizing to the headers section only and then it's easy to solve.
That's also something that could be considered as part of the "how do we express the column width". Your current system has width values (or -1) only, perhaps columns can be marked as "use remaining space" or have a finer way to express the constraints. Again this needs a little research. Note that I currently store width as % of the total width to handle this sort of thing in the basic.
That interaction is currently handled in Columns()
This ties with my earlier comment "allowing the user to create finer interaction with columns header, perhaps add a context menu, might lead to an api more consistent with other ImGui where headers are declared one by one instead of packed into a table.". We either need to code in this feature in that code, either expose the interaction somehow. If we have an api were columns are declared separately, it'd be easier to allow for the user to handle custom interactions. Lots of open problems, thanks for trying those things. It is worth pushing those problems for now, and then we can design better api that would handle all/most of them properly. Minor tips:
|
Disabling the sync from bottom to top Columns solves both issues : no more lag (obviously), and when resizing the window all the columns follow the sizes of the top ones. I'll look at a way to disable dragging for bottom-columns only. Thanks for the AlignFirstTextHeightToWidgets() tip ! I didn't see the misalignment at all, too much focused on the columns ! Great that you gave me the tip for clipping, because that would have been my next question :) |
Mmmh... The clipper has side effects : the first line is not correctly detected, and when I scroll by dragging the scrollbar it is flickering. I'll look at what is wrong with it.. Edit : I found that issue #512 may be related. |
You probably aren't passing the right height for your items. There is a helper that gives you If you aren't sure of your height try seeking position +1 manually (using SetCursorPosY()) when eg. io.KeyCtrl is held for an easy comparison.
|
Here are the sources from yesterday evening (yes, I discovered Gist... ah ah) : imgui_column_headers.h : https://gist.github.com/itamago/27f09c429d625a0d31ec Works quite well. In the following capture, you will recognise the wip color-picker : Having ideas lot more clear than yesterday (no more fever), I have some questions which may be totally newbie ones...
Sorry if they are newbie question, and I would totally understand that I should more carefully RTFM, but I'm still sick lol :) |
Thanks. We should aim to make all this code invisible eventually. I am wondering if the performances are holding up with so much columns switches. In particular NextColumn() does a PopClipRect()/PushColumnClipRect() which will always end up in a merged command, I think with recent changes to ImDrawList I should be able to remove those calls and only do them once at the begining/end of the set. Same for PushItemWidth which ideally should be preserved on a per-column basis. It's all fairly small/fast stuff but it may add up when you are drawing thousands of calls. Hope to remove those 4 calls from NextColumns() at least. Typically I wouldn't even use so many columns, note how most of your elements don't really need to be resized. I'm always a little concerned when people try to push ImGui in a direction of trying to mimic well known UI systems and losing on the usage simplicity benefits of ImGui.. You can't turn a blind eye on the core design/benefits/limitations of ImGui and expect mimic to happen. You may end up disappointed! I am not sure I want to turn ImGui into pretty-face options because that would defeat lots of its benefits. And frankly I don't have sufficient bandwidth to handle it, every micro visual decision has side-effects on code and performance (if I had the time I would consider finding the best spot between those two opposites).
You can't, or you set the position yourself.
You can't, and yes it'd be pretty simple to change. Probably best set within the Style?
I plan to try to finish it and get it working first, I haven't figured out how to parametrize it yet.
There is a font scale but the best way is to bake a smaller font and call PushFont. |
You're right, I need only 4 columns (and 2 resizable only). Thanks for your advices, I will implement that tomorrow ! |
I did a test by reducing from 13 columns to only 4. The widgets were the same, and I kept row-clipping disabled in order to maximise the number of vertices. The gain is negligible :
It means that Columns have a very low overhead, that's great :) |
Updated source code for column-headers, making them more configurable and easier to manage :
imgui_column_headers.h : https://gist.github.com/itamago/27f09c429d625a0d31ec |
Nothing new on this topic, so I close it. |
Lots of ideas to filter and process. We still need official columns headers, etc. I'd rather keep topics open when they talk about stuff that I intend to do! |
Hey there, simplified header-only snippet because I wanted it shorter and more ImGui styled. /* // [src] https://github.com/ocornut/imgui/issues/513
// Usage:
static const char *headers[] = {
"Index", "Color", "Flip?", "Filename"
};
static float widths[ IM_ARRAYSIZE(headers) ] = {};
if( ImGui::BeginTable("WinTextureContent", headers, widths, IM_ARRAYSIZE(headers)) ) {
// Draw as many rows as needed
for( int i = 0; i < 10; ++i ) {
ImGui::Text("%d", i); ImGui::NextColumn();
ImGui::ColorButton( ImVec4(0.5f,0.2f,i*0.3f,1.f)); ImGui::NextColumn();
ImGui::Text("%s", i % 2 ? "yes" : "no"); ImGui::NextColumn();
ImGui::Text(__FILE__); ImGui::NextColumn();
}
ImGui::EndTable();
}
*/
// .h
namespace ImGui {
IMGUI_API int BeginTable(const char* columnsId, const char** headers, float *widths, int count, bool border=true);
IMGUI_API void EndTable();
}
// .cpp
namespace ImGui {
static inline IMGUI_API
int BeginTable(const char* columnsId, const char** headers, float* widths, int count, bool draw_border)
{
if(count<=0)
return 0;
// Draw column headers
ImGuiStyle & style = ImGui::GetStyle();
const ImVec2 firstTextSize = ImGui::CalcTextSize(headers[0], NULL, true);
ImGui::BeginChild(columnsId, ImVec2(0,firstTextSize.y + 2 * style.ItemSpacing.y), true);
char str_id[256];
sprintf(str_id, "tbl0_%s", columnsId);
ImGui::Columns(count, str_id, draw_border);
float offset = 0.0f;
for(int i=0; i < count; i++)
{
ImGui::SetColumnOffset(i, offset);
if(widths[i] <= 0)
{
const ImVec2 textsize = ImGui::CalcTextSize(headers[i], NULL, true);
const float colSizeX = (textsize.x + 2 * style.ItemSpacing.x);
widths[i] = colSizeX + 1;
}
if(i < (count-1))
{
float curOffset = offset;
offset = ImGui::GetColumnOffset(i+1);
widths[i] = offset - curOffset + 1;
}
ImGui::Text(headers[i]);
ImGui::NextColumn();
}
ImGui::Columns(1);
ImGui::EndChild();
// Draw body
str_id[3] = '1';
columnsId = str_id;
ImGui::BeginChild(columnsId, ImVec2(0,0), true);
ImGui::Columns(count, columnsId, draw_border);
offset = 0.0f;
for(int i=0; i < count; i++)
{
ImGui::SetColumnOffset(i, offset);
offset += widths[i] - 1;
}
return 1;
}
static inline IMGUI_API
void EndTable()
{
ImGui::Columns(1);
ImGui::EndChild();
}
} |
Hello, there is a severe bug when the window hosting the control is resized, the columns lose spacing and its not possibile to reset to their original size. |
With which code? Please provide a repro. |
With the code from r-lyeh , i basically copy pasted into a plain window and it showed the problem as soon as i resized the window, it takes 30 seconds to reproduce, i do not have a gif encoder in this computer right now. |
This code doesn't seem to work with latest ImGUI. In the case of the header-only snippet I can use |
Hello @itamago and all, |
…ColumnFlags_*. (#125, #513, #913, #1204, #1444, #2142, #2707) Affected: ImGuiColumnsFlags_None, ImGuiColumnsFlags_NoBorder, ImGuiColumnsFlags_NoResize, ImGuiColumnsFlags_NoPreserveWidths, ImGuiColumnsFlags_NoForceWithinWindow, ImGuiColumnsFlags_GrowParentContentsSize. Added redirection enums. Did not add redirection type.
Tables API now merged in master. |
Hello,
In case you are creating tables with lot of columns, here is a short code snippet to quickly manage the column headers :
I'm wondering if there is a simple way to make the headers "fixed", like in Excel when the top row is fixed ?
Because you obviously want to see the headers while scrolling in your table.
Thx
The text was updated successfully, but these errors were encountered: