Began improvements to columns API by splitting the Columns() function into BeginColumns() and EndColumns() and adding additional flags. The columns data still needs to be placed into a stack.

This commit is contained in:
Geoffrey Tucker 2016-10-29 15:44:08 -07:00
parent 43e6c46c8d
commit 66c4281290
5 changed files with 199 additions and 89 deletions

158
imgui.cpp
View File

@ -9295,6 +9295,18 @@ int ImGui::GetColumnsCount()
return window->DC.ColumnsCount; return window->DC.ColumnsCount;
} }
static float OffsetNormToPixels(float offsetNorm)
{
ImGuiWindow* window = ImGui::GetCurrentWindowRead();
return offsetNorm * (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
}
static float PixelsToOffsetNorm(float offset)
{
ImGuiWindow* window = ImGui::GetCurrentWindowRead();
return (offset - window->DC.ColumnsMinX) / (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
}
static float GetDraggedColumnOffset(int column_index) static float GetDraggedColumnOffset(int column_index)
{ {
// Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
@ -9305,7 +9317,9 @@ static float GetDraggedColumnOffset(int column_index)
IM_ASSERT(g.ActiveId == window->DC.ColumnsSetId + ImGuiID(column_index)); IM_ASSERT(g.ActiveId == window->DC.ColumnsSetId + ImGuiID(column_index));
float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x - window->Pos.x; float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x - window->Pos.x;
x = ImClamp(x, ImGui::GetColumnOffset(column_index-1)+g.Style.ColumnsMinSpacing, ImGui::GetColumnOffset(column_index+1)-g.Style.ColumnsMinSpacing); x = ImMax(x, ImGui::GetColumnOffset(column_index-1) + g.Style.ColumnsMinSpacing);
if ((window->DC.ColumnsFlags & ImGuiColumnsFlags_NoPreserveWidths))
x = ImMin(x, ImGui::GetColumnOffset(column_index+1) - g.Style.ColumnsMinSpacing);
return (float)(int)x; return (float)(int)x;
} }
@ -9332,16 +9346,26 @@ float ImGui::GetColumnOffset(int column_index)
void ImGui::SetColumnOffset(int column_index, float offset) void ImGui::SetColumnOffset(int column_index, float offset)
{ {
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
if (column_index < 0) if (column_index < 0)
column_index = window->DC.ColumnsCurrent; column_index = window->DC.ColumnsCurrent;
IM_ASSERT(column_index < window->DC.ColumnsData.Size); IM_ASSERT(column_index < window->DC.ColumnsData.Size);
const float t = (offset - window->DC.ColumnsMinX) / (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
window->DC.ColumnsData[column_index].OffsetNorm = t; const bool preserveWidth = !(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < window->DC.ColumnsCount-1);
const float width = preserveWidth ? GetColumnWidth(column_index) : 0.0f;
if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoForceWithinWindow))
offset = ImMin((float)(int)offset, window->DC.ColumnsMaxX - g.Style.ColumnsMinSpacing * (window->DC.ColumnsCount - column_index));
const float offsetNorm = PixelsToOffsetNorm(offset);
const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index); const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
window->DC.StateStorage->SetFloat(column_id, t); window->DC.StateStorage->SetFloat(column_id, offsetNorm);
window->DC.ColumnsData[column_index].OffsetNorm = offsetNorm;
if (preserveWidth)
SetColumnOffset(column_index+1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
} }
float ImGui::GetColumnWidth(int column_index) float ImGui::GetColumnWidth(int column_index)
@ -9350,8 +9374,16 @@ float ImGui::GetColumnWidth(int column_index)
if (column_index < 0) if (column_index < 0)
column_index = window->DC.ColumnsCurrent; column_index = window->DC.ColumnsCurrent;
float w = GetColumnOffset(column_index+1) - GetColumnOffset(column_index); return OffsetNormToPixels(window->DC.ColumnsData[column_index+1].OffsetNorm - window->DC.ColumnsData[column_index].OffsetNorm);
return w; }
void ImGui::SetColumnWidth(int column_index, float width)
{
ImGuiWindow* window = GetCurrentWindowRead();
if (column_index < 0)
column_index = window->DC.ColumnsCurrent;
SetColumnOffset(column_index+1, GetColumnOffset(column_index) + width);
} }
static void PushColumnClipRect(int column_index) static void PushColumnClipRect(int column_index)
@ -9365,26 +9397,66 @@ static void PushColumnClipRect(int column_index)
ImGui::PushClipRect(ImVec2(x1,-FLT_MAX), ImVec2(x2,+FLT_MAX), true); ImGui::PushClipRect(ImVec2(x1,-FLT_MAX), ImVec2(x2,+FLT_MAX), true);
} }
void ImGui::Columns(int columns_count, const char* id, bool border) void ImGui::BeginColumns(const char* id, int columns_count, ImGuiColumnsFlags flags)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(columns_count >= 1); IM_ASSERT(columns_count > 1);
IM_ASSERT(window->DC.ColumnsCount == 1); // Nested columns are currently not supported
if (window->DC.ColumnsCount != 1) // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
// In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
PushID(0x11223347 + (id ? 0 : columns_count));
window->DC.ColumnsSetId = window->GetID(id ? id : "columns");
PopID();
// Set state for first column
window->DC.ColumnsCurrent = 0;
window->DC.ColumnsCount = columns_count;
window->DC.ColumnsFlags = flags;
const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : window->Size.x;
window->DC.ColumnsMinX = window->DC.IndentX; // Lock our horizontal range
window->DC.ColumnsMaxX = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x;
window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y;
window->DC.ColumnsOffsetX = 0.0f;
window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
// Cache column offsets
window->DC.ColumnsData.resize(columns_count + 1);
for (int column_index = 0; column_index < columns_count + 1; column_index++)
{ {
const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
KeepAliveID(column_id);
const float default_t = column_index / (float)window->DC.ColumnsCount;
float t = window->DC.StateStorage->GetFloat(column_id, default_t); // Cheaply store our floating point value inside the integer (could store a union into the map?)
if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoForceWithinWindow))
t = ImMin(t, PixelsToOffsetNorm(window->DC.ColumnsMaxX - g.Style.ColumnsMinSpacing * (window->DC.ColumnsCount - column_index)));
window->DC.ColumnsData[column_index].OffsetNorm = t;
}
window->DrawList->ChannelsSplit(window->DC.ColumnsCount);
PushColumnClipRect();
PushItemWidth(GetColumnWidth() * 0.65f);
}
void ImGui::EndColumns()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(window->DC.ColumnsCount > 1);
if (window->DC.ColumnsCurrent != 0) if (window->DC.ColumnsCurrent != 0)
ItemSize(ImVec2(0,0)); // Advance to column 0 ItemSize(ImVec2(0, 0)); // Advance to column 0
PopItemWidth(); PopItemWidth();
PopClipRect(); PopClipRect();
window->DrawList->ChannelsMerge(); window->DrawList->ChannelsMerge();
window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y); window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
window->DC.CursorPos.y = window->DC.ColumnsCellMaxY; window->DC.CursorPos.y = window->DC.ColumnsCellMaxY;
}
// Draw columns borders and handle resize at the time of "closing" a columns set // Draw columns borders and handle resize
if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders && !window->SkipItems) if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
{ {
const float y1 = window->DC.ColumnsStartPosY; const float y1 = window->DC.ColumnsStartPosY;
const float y2 = window->DC.CursorPos.y; const float y2 = window->DC.CursorPos.y;
@ -9392,7 +9464,7 @@ void ImGui::Columns(int columns_count, const char* id, bool border)
{ {
float x = window->Pos.x + GetColumnOffset(i); float x = window->Pos.x + GetColumnOffset(i);
const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i); const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i);
const ImRect column_rect(ImVec2(x-4,y1),ImVec2(x+4,y2)); const ImRect column_rect(ImVec2(x - 4, y1), ImVec2(x + 4, y2));
if (IsClippedEx(column_rect, &column_id, false)) if (IsClippedEx(column_rect, &column_id, false))
continue; continue;
@ -9404,7 +9476,7 @@ void ImGui::Columns(int columns_count, const char* id, bool border)
// Draw before resize so our items positioning are in sync with the line being drawn // Draw before resize so our items positioning are in sync with the line being drawn
const ImU32 col = GetColorU32(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column); const ImU32 col = GetColorU32(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column);
const float xi = (float)(int)x; const float xi = (float)(int)x;
window->DrawList->AddLine(ImVec2(xi, y1+1.0f), ImVec2(xi, y2), col); window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col);
if (held) if (held)
{ {
@ -9416,45 +9488,27 @@ void ImGui::Columns(int columns_count, const char* id, bool border)
} }
} }
// Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget. window->DC.ColumnsSetId = 0;
// In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
PushID(0x11223347 + (id ? 0 : columns_count));
window->DC.ColumnsSetId = window->GetID(id ? id : "columns");
PopID();
// Set state for first column
window->DC.ColumnsCurrent = 0; window->DC.ColumnsCurrent = 0;
window->DC.ColumnsCount = columns_count; window->DC.ColumnsCount = 1;
window->DC.ColumnsShowBorders = border; window->DC.ColumnsFlags = 0;
const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : window->Size.x;
window->DC.ColumnsMinX = window->DC.IndentX; // Lock our horizontal range
window->DC.ColumnsMaxX = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x;
window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y;
window->DC.ColumnsOffsetX = 0.0f;
window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
if (window->DC.ColumnsCount != 1)
{
// Cache column offsets
window->DC.ColumnsData.resize(columns_count + 1);
for (int column_index = 0; column_index < columns_count + 1; column_index++)
{
const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
KeepAliveID(column_id);
const float default_t = column_index / (float)window->DC.ColumnsCount;
const float t = window->DC.StateStorage->GetFloat(column_id, default_t); // Cheaply store our floating point value inside the integer (could store a union into the map?)
window->DC.ColumnsData[column_index].OffsetNorm = t;
}
window->DrawList->ChannelsSplit(window->DC.ColumnsCount);
PushColumnClipRect();
PushItemWidth(GetColumnWidth() * 0.65f);
}
else
{
window->DC.ColumnsData.resize(0); window->DC.ColumnsData.resize(0);
} }
void ImGui::Columns(int columns_count, const char* id, bool border)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(columns_count >= 1);
ImGuiColumnsFlags flags = 0;
flags |= (border ? 0 : ImGuiColumnsFlags_NoBorder);
if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1)
EndColumns();
if (columns_count != 1)
BeginColumns(id, columns_count, flags);
} }
void ImGui::Indent(float indent_w) void ImGui::Indent(float indent_w)

23
imgui.h
View File

@ -73,6 +73,7 @@ typedef int ImGuiKey; // a key identifier (ImGui-side enum) // e
typedef int ImGuiColorEditMode; // color edit mode for ColorEdit*() // enum ImGuiColorEditMode_ typedef int ImGuiColorEditMode; // color edit mode for ColorEdit*() // enum ImGuiColorEditMode_
typedef int ImGuiMouseCursor; // a mouse cursor identifier // enum ImGuiMouseCursor_ typedef int ImGuiMouseCursor; // a mouse cursor identifier // enum ImGuiMouseCursor_
typedef int ImGuiWindowFlags; // window flags for Begin*() // enum ImGuiWindowFlags_ typedef int ImGuiWindowFlags; // window flags for Begin*() // enum ImGuiWindowFlags_
typedef int ImGuiColumnsFlags; // column flags for Columns() // enum ImGuiColumnsFlags_
typedef int ImGuiSetCond; // condition flags for Set*() // enum ImGuiSetCond_ typedef int ImGuiSetCond; // condition flags for Set*() // enum ImGuiSetCond_
typedef int ImGuiInputTextFlags; // flags for InputText*() // enum ImGuiInputTextFlags_ typedef int ImGuiInputTextFlags; // flags for InputText*() // enum ImGuiInputTextFlags_
typedef int ImGuiSelectableFlags; // flags for Selectable() // enum ImGuiSelectableFlags_ typedef int ImGuiSelectableFlags; // flags for Selectable() // enum ImGuiSelectableFlags_
@ -221,13 +222,16 @@ namespace ImGui
// Columns // Columns
// You can also use SameLine(pos_x) for simplified columning. The columns API is still work-in-progress and rather lacking. // You can also use SameLine(pos_x) for simplified columning. The columns API is still work-in-progress and rather lacking.
IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); // setup number of columns. use an identifier to distinguish multiple column sets. close with Columns(1). IMGUI_API void BeginColumns(const char* id, int count, ImGuiColumnsFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns().
IMGUI_API void NextColumn(); // next column IMGUI_API void EndColumns(); // close columns
IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished
IMGUI_API int GetColumnIndex(); // get current column index IMGUI_API int GetColumnIndex(); // get current column index
IMGUI_API float GetColumnOffset(int column_index = -1); // get position of column line (in pixels, from the left side of the contents region). pass -1 to use current column, otherwise 0..GetcolumnsCount() inclusive. column 0 is usually 0.0f and not resizable unless you call this IMGUI_API float GetColumnWidth(int column_index = -1); // get column width (in pixels). pass -1 to use current column
IMGUI_API void SetColumnWidth(int column_index, float width); // set column width (in pixels). pass -1 to use current column
IMGUI_API float GetColumnOffset(int column_index = -1); // get position of column line (in pixels, from the left side of the contents region). pass -1 to use current column, otherwise 0..GetColumnsCount() inclusive. column 0 is usually 0.0f and not resizable unless you call this
IMGUI_API void SetColumnOffset(int column_index, float offset_x); // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column IMGUI_API void SetColumnOffset(int column_index, float offset_x); // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column
IMGUI_API float GetColumnWidth(int column_index = -1); // column width (== GetColumnOffset(GetColumnIndex()+1) - GetColumnOffset(GetColumnOffset()) IMGUI_API int GetColumnsCount();
IMGUI_API int GetColumnsCount(); // number of columns (what was passed to Columns()) IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true);
// ID scopes // ID scopes
// If you are creating widgets in a loop you most likely want to push a unique identifier so ImGui can differentiate them. // If you are creating widgets in a loop you most likely want to push a unique identifier so ImGui can differentiate them.
@ -506,6 +510,15 @@ enum ImGuiWindowFlags_
ImGuiWindowFlags_ChildMenu = 1 << 27 // Don't use! For internal use by BeginMenu() ImGuiWindowFlags_ChildMenu = 1 << 27 // Don't use! For internal use by BeginMenu()
}; };
// Flags for ImGui::Columns()
enum ImGuiColumnsFlags_
{
// Default: 0
ImGuiColumnsFlags_NoBorder = 1 << 0, // Disable column dividers
ImGuiColumnsFlags_NoPreserveWidths = 1 << 1, // Disable column width preservation when adjusting columns
ImGuiColumnsFlags_NoForceWithinWindow = 1 << 2 // Disable forcing columns to fit within window
};
// Flags for ImGui::InputText() // Flags for ImGui::InputText()
enum ImGuiInputTextFlags_ enum ImGuiInputTextFlags_
{ {

View File

@ -1400,6 +1400,49 @@ void ImGui::ShowTestWindow(bool* p_open)
} }
*/ */
if (ImGui::TreeNode("Advanced settings"))
{
static bool border = true;
static bool preserveWidths = true;
static bool forceWithinWindow = true;
ImGui::Checkbox("Border", &border);
ImGui::SameLine();
ImGui::Checkbox("Preserve widths", &preserveWidths);
ImGui::SameLine();
ImGui::Checkbox("Force within window", &forceWithinWindow);
ImGuiColumnsFlags flags = 0;
flags |= (border ? 0 : ImGuiColumnsFlags_NoBorder);
flags |= (preserveWidths ? 0 : ImGuiColumnsFlags_NoPreserveWidths);
flags |= (forceWithinWindow ? 0 : ImGuiColumnsFlags_NoForceWithinWindow);
ImGui::BeginColumns("AdvancedColumns", 4, flags);
ImGui::Separator();
ImGui::Text("ID"); ImGui::NextColumn();
ImGui::Text("Name"); ImGui::NextColumn();
ImGui::Text("Path"); ImGui::NextColumn();
ImGui::Text("Flags"); ImGui::NextColumn();
ImGui::Separator();
const char* names[3] = { "One", "Two", "Three" };
const char* paths[3] = { "/path/one", "/path/two", "/path/three" };
static int selected = -1;
for (int i = 0; i < 3; i++)
{
char label[32];
sprintf(label, "%04d", i);
if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns))
selected = i;
ImGui::NextColumn();
ImGui::Text(names[i]); ImGui::NextColumn();
ImGui::Text(paths[i]); ImGui::NextColumn();
ImGui::Text("...."); ImGui::NextColumn();
}
ImGui::EndColumns();
ImGui::Separator();
ImGui::TreePop();
}
// Create multiple items in a same cell before switching to next column // Create multiple items in a same cell before switching to next column
if (ImGui::TreeNode("Mixed items")) if (ImGui::TreeNode("Mixed items"))
{ {

View File

@ -571,7 +571,7 @@ struct IMGUI_API ImGuiDrawContext
float ColumnsStartPosY; float ColumnsStartPosY;
float ColumnsCellMinY; float ColumnsCellMinY;
float ColumnsCellMaxY; float ColumnsCellMaxY;
bool ColumnsShowBorders; ImGuiColumnsFlags ColumnsFlags;
ImGuiID ColumnsSetId; ImGuiID ColumnsSetId;
ImVector<ImGuiColumnData> ColumnsData; ImVector<ImGuiColumnData> ColumnsData;
@ -603,7 +603,7 @@ struct IMGUI_API ImGuiDrawContext
ColumnsMinX = ColumnsMaxX = 0.0f; ColumnsMinX = ColumnsMaxX = 0.0f;
ColumnsStartPosY = 0.0f; ColumnsStartPosY = 0.0f;
ColumnsCellMinY = ColumnsCellMaxY = 0.0f; ColumnsCellMinY = ColumnsCellMaxY = 0.0f;
ColumnsShowBorders = true; ColumnsFlags = 0;
ColumnsSetId = 0; ColumnsSetId = 0;
} }
}; };