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

Began improvements to columns API by splitting the Columns() function #913

Merged
merged 1 commit into from
Aug 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 129 additions & 75 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3507,7 +3507,7 @@ static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags)

bool ImGui::BeginPopup(const char* str_id)
{
if (GImGui->OpenPopupStack.Size <= GImGui->CurrentPopupStack.Size) // Early out for performance
if (GImGui->OpenPopupStack.Size <= GImGui->CurrentPopupStack.Size) // Early out for performance
{
ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
return false;
Expand Down Expand Up @@ -9175,7 +9175,7 @@ void ImGui::EndGroup()
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();

IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls

ImGuiGroupData& group_data = window->DC.GroupStack.back();

Expand Down Expand Up @@ -9295,6 +9295,18 @@ int ImGui::GetColumnsCount()
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)
{
// Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
Expand All @@ -9305,7 +9317,9 @@ static float GetDraggedColumnOffset(int column_index)
IM_ASSERT(g.ActiveId == window->DC.ColumnsSetId + ImGuiID(column_index));

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;
}
Expand All @@ -9332,16 +9346,26 @@ float ImGui::GetColumnOffset(int column_index)

void ImGui::SetColumnOffset(int column_index, float offset)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
if (column_index < 0)
column_index = window->DC.ColumnsCurrent;

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);
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)
Expand All @@ -9350,8 +9374,16 @@ float ImGui::GetColumnWidth(int column_index)
if (column_index < 0)
column_index = window->DC.ColumnsCurrent;

float w = GetColumnOffset(column_index+1) - GetColumnOffset(column_index);
return w;
return OffsetNormToPixels(window->DC.ColumnsData[column_index+1].OffsetNorm - window->DC.ColumnsData[column_index].OffsetNorm);
}

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)
Expand All @@ -9365,56 +9397,12 @@ static void PushColumnClipRect(int column_index)
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;
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(columns_count >= 1);

if (window->DC.ColumnsCount != 1)
{
if (window->DC.ColumnsCurrent != 0)
ItemSize(ImVec2(0,0)); // Advance to column 0
PopItemWidth();
PopClipRect();
window->DrawList->ChannelsMerge();

window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
window->DC.CursorPos.y = window->DC.ColumnsCellMaxY;
}

// Draw columns borders and handle resize at the time of "closing" a columns set
if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders && !window->SkipItems)
{
const float y1 = window->DC.ColumnsStartPosY;
const float y2 = window->DC.CursorPos.y;
for (int i = 1; i < window->DC.ColumnsCount; i++)
{
float x = window->Pos.x + GetColumnOffset(i);
const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i);
const ImRect column_rect(ImVec2(x-4,y1),ImVec2(x+4,y2));
if (IsClippedEx(column_rect, &column_id, false))
continue;

bool hovered, held;
ButtonBehavior(column_rect, column_id, &hovered, &held);
if (hovered || held)
g.MouseCursor = ImGuiMouseCursor_ResizeEW;

// 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 float xi = (float)(int)x;
window->DrawList->AddLine(ImVec2(xi, y1+1.0f), ImVec2(xi, y2), col);

if (held)
{
if (g.ActiveIdIsJustActivated)
g.ActiveIdClickOffset.x -= 4; // Store from center of column line (we used a 8 wide rect for columns clicking)
x = GetDraggedColumnOffset(i);
SetColumnOffset(i, x);
}
}
}
IM_ASSERT(columns_count > 1);
IM_ASSERT(window->DC.ColumnsCount == 1); // Nested columns are currently not supported

// 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.
Expand All @@ -9425,7 +9413,7 @@ void ImGui::Columns(int columns_count, const char* id, bool border)
// Set state for first column
window->DC.ColumnsCurrent = 0;
window->DC.ColumnsCount = columns_count;
window->DC.ColumnsShowBorders = border;
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
Expand All @@ -9435,26 +9423,92 @@ void ImGui::Columns(int columns_count, const char* id, bool border)
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
// Cache column offsets
window->DC.ColumnsData.resize(columns_count + 1);
for (int column_index = 0; column_index < columns_count + 1; column_index++)
{
window->DC.ColumnsData.resize(0);
}
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)
ItemSize(ImVec2(0, 0)); // Advance to column 0
PopItemWidth();
PopClipRect();
window->DrawList->ChannelsMerge();

window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
window->DC.CursorPos.y = window->DC.ColumnsCellMaxY;

// Draw columns borders and handle resize
if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
{
const float y1 = window->DC.ColumnsStartPosY;
const float y2 = window->DC.CursorPos.y;
for (int i = 1; i < window->DC.ColumnsCount; i++)
{
float x = window->Pos.x + GetColumnOffset(i);
const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i);
const ImRect column_rect(ImVec2(x - 4, y1), ImVec2(x + 4, y2));
if (IsClippedEx(column_rect, &column_id, false))
continue;

bool hovered, held;
ButtonBehavior(column_rect, column_id, &hovered, &held);
if (hovered || held)
g.MouseCursor = ImGuiMouseCursor_ResizeEW;

// 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 float xi = (float)(int)x;
window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col);

if (held)
{
if (g.ActiveIdIsJustActivated)
g.ActiveIdClickOffset.x -= 4; // Store from center of column line (we used a 8 wide rect for columns clicking)
x = GetDraggedColumnOffset(i);
SetColumnOffset(i, x);
}
}
}

window->DC.ColumnsSetId = 0;
window->DC.ColumnsCurrent = 0;
window->DC.ColumnsCount = 1;
window->DC.ColumnsFlags = 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)
Expand Down
33 changes: 23 additions & 10 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 ImGuiMouseCursor; // a mouse cursor identifier // enum ImGuiMouseCursor_
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 ImGuiInputTextFlags; // flags for InputText*() // enum ImGuiInputTextFlags_
typedef int ImGuiSelectableFlags; // flags for Selectable() // enum ImGuiSelectableFlags_
Expand Down Expand Up @@ -221,13 +222,16 @@ namespace ImGui

// Columns
// 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 NextColumn(); // next column
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 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(); // number of columns (what was passed to Columns())
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 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 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 int GetColumnsCount();
IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true);

// ID scopes
// If you are creating widgets in a loop you most likely want to push a unique identifier so ImGui can differentiate them.
Expand Down Expand Up @@ -418,7 +422,7 @@ namespace ImGui
IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);
IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // calculate coarse clipping for large list of evenly sized items. Prefer using the ImGuiListClipper higher-level helper if you can.

IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame
IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame
IMGUI_API void EndChildFrame();

IMGUI_API ImVec4 ColorConvertU32ToFloat4(ImU32 in);
Expand Down Expand Up @@ -506,6 +510,15 @@ enum ImGuiWindowFlags_
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()
enum ImGuiInputTextFlags_
{
Expand Down Expand Up @@ -1029,8 +1042,8 @@ struct ImGuiTextEditCallbackData
struct ImGuiSizeConstraintCallbackData
{
void* UserData; // Read-only. What user passed to SetNextWindowSizeConstraints()
ImVec2 Pos; // Read-only. Window position, for reference.
ImVec2 CurrentSize; // Read-only. Current window size.
ImVec2 Pos; // Read-only. Window position, for reference.
ImVec2 CurrentSize; // Read-only. Current window size.
ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing.
};

Expand Down
Loading