@ -24,6 +24,7 @@ Index of this file:
// [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc.
// [SECTION] Widgets: BeginTabBar, EndTabBar, etc.
// [SECTION] Widgets: BeginTabItem, EndTabItem, etc.
// [SECTION] Widgets: Columns, BeginColumns, EndColumns, etc.
*/
@ -625,8 +626,6 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags
flags | = ImGuiButtonFlags_Repeat ;
bool hovered , held ;
bool pressed = ButtonBehavior ( bb , id , & hovered , & held , flags ) ;
if ( pressed )
MarkItemEdited ( id ) ;
// Render
const ImU32 col = GetColorU32 ( ( held & & hovered ) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button ) ;
@ -4849,9 +4848,6 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
if ( ! ( flags & ImGuiColorEditFlags_NoTooltip ) & & hovered )
ColorTooltip ( desc_id , & col . x , flags & ( ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf ) ) ;
if ( pressed )
MarkItemEdited ( id ) ;
return pressed ;
}
@ -7207,3 +7203,416 @@ bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
return close_button_pressed ;
}
//-------------------------------------------------------------------------
// [SECTION] Widgets: Columns, BeginColumns, EndColumns, etc.
// In the current version, Columns are very weak. Needs to be replaced with a more full-featured system.
//-------------------------------------------------------------------------
// - GetColumnIndex()
// - GetColumnCount()
// - GetColumnOffset()
// - GetColumnWidth()
// - SetColumnOffset()
// - SetColumnWidth()
// - PushColumnClipRect() [Internal]
// - PushColumnsBackground() [Internal]
// - PopColumnsBackground() [Internal]
// - FindOrCreateColumns() [Internal]
// - GetColumnsID() [Internal]
// - BeginColumns()
// - NextColumn()
// - EndColumns()
// - Columns()
//-------------------------------------------------------------------------
int ImGui : : GetColumnIndex ( )
{
ImGuiWindow * window = GetCurrentWindowRead ( ) ;
return window - > DC . CurrentColumns ? window - > DC . CurrentColumns - > Current : 0 ;
}
int ImGui : : GetColumnsCount ( )
{
ImGuiWindow * window = GetCurrentWindowRead ( ) ;
return window - > DC . CurrentColumns ? window - > DC . CurrentColumns - > Count : 1 ;
}
float ImGui : : GetColumnOffsetFromNorm ( const ImGuiColumns * columns , float offset_norm )
{
return offset_norm * ( columns - > OffMaxX - columns - > OffMinX ) ;
}
float ImGui : : GetColumnNormFromOffset ( const ImGuiColumns * columns , float offset )
{
return offset / ( columns - > OffMaxX - columns - > OffMinX ) ;
}
static const float COLUMNS_HIT_RECT_HALF_WIDTH = 4.0f ;
static float GetDraggedColumnOffset ( ImGuiColumns * columns , 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
// window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
ImGuiContext & g = * GImGui ;
ImGuiWindow * window = g . CurrentWindow ;
IM_ASSERT ( column_index > 0 ) ; // We are not supposed to drag column 0.
IM_ASSERT ( g . ActiveId = = columns - > ID + ImGuiID ( column_index ) ) ;
float x = g . IO . MousePos . x - g . ActiveIdClickOffset . x + COLUMNS_HIT_RECT_HALF_WIDTH - window - > Pos . x ;
x = ImMax ( x , ImGui : : GetColumnOffset ( column_index - 1 ) + g . Style . ColumnsMinSpacing ) ;
if ( ( columns - > Flags & ImGuiColumnsFlags_NoPreserveWidths ) )
x = ImMin ( x , ImGui : : GetColumnOffset ( column_index + 1 ) - g . Style . ColumnsMinSpacing ) ;
return x ;
}
float ImGui : : GetColumnOffset ( int column_index )
{
ImGuiWindow * window = GetCurrentWindowRead ( ) ;
ImGuiColumns * columns = window - > DC . CurrentColumns ;
IM_ASSERT ( columns ! = NULL ) ;
if ( column_index < 0 )
column_index = columns - > Current ;
IM_ASSERT ( column_index < columns - > Columns . Size ) ;
const float t = columns - > Columns [ column_index ] . OffsetNorm ;
const float x_offset = ImLerp ( columns - > OffMinX , columns - > OffMaxX , t ) ;
return x_offset ;
}
static float GetColumnWidthEx ( ImGuiColumns * columns , int column_index , bool before_resize = false )
{
if ( column_index < 0 )
column_index = columns - > Current ;
float offset_norm ;
if ( before_resize )
offset_norm = columns - > Columns [ column_index + 1 ] . OffsetNormBeforeResize - columns - > Columns [ column_index ] . OffsetNormBeforeResize ;
else
offset_norm = columns - > Columns [ column_index + 1 ] . OffsetNorm - columns - > Columns [ column_index ] . OffsetNorm ;
return ImGui : : GetColumnOffsetFromNorm ( columns , offset_norm ) ;
}
float ImGui : : GetColumnWidth ( int column_index )
{
ImGuiWindow * window = GetCurrentWindowRead ( ) ;
ImGuiColumns * columns = window - > DC . CurrentColumns ;
IM_ASSERT ( columns ! = NULL ) ;
if ( column_index < 0 )
column_index = columns - > Current ;
return GetColumnOffsetFromNorm ( columns , columns - > Columns [ column_index + 1 ] . OffsetNorm - columns - > Columns [ column_index ] . OffsetNorm ) ;
}
void ImGui : : SetColumnOffset ( int column_index , float offset )
{
ImGuiContext & g = * GImGui ;
ImGuiWindow * window = g . CurrentWindow ;
ImGuiColumns * columns = window - > DC . CurrentColumns ;
IM_ASSERT ( columns ! = NULL ) ;
if ( column_index < 0 )
column_index = columns - > Current ;
IM_ASSERT ( column_index < columns - > Columns . Size ) ;
const bool preserve_width = ! ( columns - > Flags & ImGuiColumnsFlags_NoPreserveWidths ) & & ( column_index < columns - > Count - 1 ) ;
const float width = preserve_width ? GetColumnWidthEx ( columns , column_index , columns - > IsBeingResized ) : 0.0f ;
if ( ! ( columns - > Flags & ImGuiColumnsFlags_NoForceWithinWindow ) )
offset = ImMin ( offset , columns - > OffMaxX - g . Style . ColumnsMinSpacing * ( columns - > Count - column_index ) ) ;
columns - > Columns [ column_index ] . OffsetNorm = GetColumnNormFromOffset ( columns , offset - columns - > OffMinX ) ;
if ( preserve_width )
SetColumnOffset ( column_index + 1 , offset + ImMax ( g . Style . ColumnsMinSpacing , width ) ) ;
}
void ImGui : : SetColumnWidth ( int column_index , float width )
{
ImGuiWindow * window = GetCurrentWindowRead ( ) ;
ImGuiColumns * columns = window - > DC . CurrentColumns ;
IM_ASSERT ( columns ! = NULL ) ;
if ( column_index < 0 )
column_index = columns - > Current ;
SetColumnOffset ( column_index + 1 , GetColumnOffset ( column_index ) + width ) ;
}
void ImGui : : PushColumnClipRect ( int column_index )
{
ImGuiWindow * window = GetCurrentWindowRead ( ) ;
ImGuiColumns * columns = window - > DC . CurrentColumns ;
if ( column_index < 0 )
column_index = columns - > Current ;
ImGuiColumnData * column = & columns - > Columns [ column_index ] ;
PushClipRect ( column - > ClipRect . Min , column - > ClipRect . Max , false ) ;
}
// Get into the columns background draw command (which is generally the same draw command as before we called BeginColumns)
void ImGui : : PushColumnsBackground ( )
{
ImGuiWindow * window = GetCurrentWindowRead ( ) ;
ImGuiColumns * columns = window - > DC . CurrentColumns ;
window - > DrawList - > ChannelsSetCurrent ( 0 ) ;
int cmd_size = window - > DrawList - > CmdBuffer . Size ;
PushClipRect ( columns - > HostClipRect . Min , columns - > HostClipRect . Max , false ) ;
IM_UNUSED ( cmd_size ) ;
IM_ASSERT ( cmd_size = = window - > DrawList - > CmdBuffer . Size ) ; // Being in channel 0 this should not have created an ImDrawCmd
}
void ImGui : : PopColumnsBackground ( )
{
ImGuiWindow * window = GetCurrentWindowRead ( ) ;
ImGuiColumns * columns = window - > DC . CurrentColumns ;
window - > DrawList - > ChannelsSetCurrent ( columns - > Current + 1 ) ;
PopClipRect ( ) ;
}
ImGuiColumns * ImGui : : FindOrCreateColumns ( ImGuiWindow * window , ImGuiID id )
{
// We have few columns per window so for now we don't need bother much with turning this into a faster lookup.
for ( int n = 0 ; n < window - > ColumnsStorage . Size ; n + + )
if ( window - > ColumnsStorage [ n ] . ID = = id )
return & window - > ColumnsStorage [ n ] ;
window - > ColumnsStorage . push_back ( ImGuiColumns ( ) ) ;
ImGuiColumns * columns = & window - > ColumnsStorage . back ( ) ;
columns - > ID = id ;
return columns ;
}
ImGuiID ImGui : : GetColumnsID ( const char * str_id , int columns_count )
{
ImGuiWindow * window = GetCurrentWindow ( ) ;
// 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 + ( str_id ? 0 : columns_count ) ) ;
ImGuiID id = window - > GetID ( str_id ? str_id : " columns " ) ;
PopID ( ) ;
return id ;
}
void ImGui : : BeginColumns ( const char * str_id , int columns_count , ImGuiColumnsFlags flags )
{
ImGuiContext & g = * GImGui ;
ImGuiWindow * window = GetCurrentWindow ( ) ;
IM_ASSERT ( columns_count > = 1 ) ;
IM_ASSERT ( window - > DC . CurrentColumns = = NULL ) ; // Nested columns are currently not supported
// Acquire storage for the columns set
ImGuiID id = GetColumnsID ( str_id , columns_count ) ;
ImGuiColumns * columns = FindOrCreateColumns ( window , id ) ;
IM_ASSERT ( columns - > ID = = id ) ;
columns - > Current = 0 ;
columns - > Count = columns_count ;
columns - > Flags = flags ;
window - > DC . CurrentColumns = columns ;
columns - > HostCursorPosY = window - > DC . CursorPos . y ;
columns - > HostCursorMaxPosX = window - > DC . CursorMaxPos . x ;
columns - > HostClipRect = window - > ClipRect ;
columns - > HostWorkRect = window - > WorkRect ;
// Set state for first column
// We aim so that the right-most column will have the same clipping width as other after being clipped by parent ClipRect
const float column_padding = g . Style . ItemSpacing . x ;
const float half_clip_extend_x = ImFloor ( ImMax ( window - > WindowPadding . x * 0.5f , window - > WindowBorderSize ) ) ;
columns - > OffMinX = window - > DC . Indent . x - column_padding + ImMax ( column_padding - window - > WindowPadding . x , 0.0f ) ;
columns - > OffMaxX = ImMin ( window - > WorkRect . Max . x + column_padding - ImMax ( column_padding - window - > WindowPadding . x , 0.0f ) , window - > WorkRect . Max . x + half_clip_extend_x ) - window - > Pos . x ;
columns - > OffMaxX = ImMax ( columns - > OffMaxX , columns - > OffMinX + 1.0f ) ;
columns - > LineMinY = columns - > LineMaxY = window - > DC . CursorPos . y ;
// Clear data if columns count changed
if ( columns - > Columns . Size ! = 0 & & columns - > Columns . Size ! = columns_count + 1 )
columns - > Columns . resize ( 0 ) ;
// Initialize default widths
columns - > IsFirstFrame = ( columns - > Columns . Size = = 0 ) ;
if ( columns - > Columns . Size = = 0 )
{
columns - > Columns . reserve ( columns_count + 1 ) ;
for ( int n = 0 ; n < columns_count + 1 ; n + + )
{
ImGuiColumnData column ;
column . OffsetNorm = n / ( float ) columns_count ;
columns - > Columns . push_back ( column ) ;
}
}
for ( int n = 0 ; n < columns_count ; n + + )
{
// Compute clipping rectangle
ImGuiColumnData * column = & columns - > Columns [ n ] ;
float clip_x1 = ImFloor ( 0.5f + window - > Pos . x + GetColumnOffset ( n ) ) ;
float clip_x2 = ImFloor ( 0.5f + window - > Pos . x + GetColumnOffset ( n + 1 ) - 1.0f ) ;
column - > ClipRect = ImRect ( clip_x1 , - FLT_MAX , clip_x2 , + FLT_MAX ) ;
column - > ClipRect . ClipWith ( window - > ClipRect ) ;
}
if ( columns - > Count > 1 )
{
window - > DrawList - > ChannelsSplit ( 1 + columns - > Count ) ;
window - > DrawList - > ChannelsSetCurrent ( 1 ) ;
PushColumnClipRect ( 0 ) ;
}
float offset_0 = GetColumnOffset ( columns - > Current ) ;
float offset_1 = GetColumnOffset ( columns - > Current + 1 ) ;
float width = offset_1 - offset_0 ;
PushItemWidth ( width * 0.65f ) ;
window - > DC . ColumnsOffset . x = columns - > OffMinX - window - > DC . Indent . x + column_padding ;
window - > DC . CursorPos . x = ( float ) ( int ) ( window - > Pos . x + window - > DC . Indent . x + window - > DC . ColumnsOffset . x ) ;
window - > WorkRect . Max . x = window - > Pos . x + offset_1 - column_padding ;
}
void ImGui : : NextColumn ( )
{
ImGuiWindow * window = GetCurrentWindow ( ) ;
if ( window - > SkipItems | | window - > DC . CurrentColumns = = NULL )
return ;
ImGuiContext & g = * GImGui ;
ImGuiColumns * columns = window - > DC . CurrentColumns ;
if ( columns - > Count = = 1 )
{
window - > DC . CursorPos . x = ( float ) ( int ) ( window - > Pos . x + window - > DC . Indent . x + window - > DC . ColumnsOffset . x ) ;
IM_ASSERT ( columns - > Current = = 0 ) ;
return ;
}
PopItemWidth ( ) ;
PopClipRect ( ) ;
const float column_padding = g . Style . ItemSpacing . x ;
columns - > LineMaxY = ImMax ( columns - > LineMaxY , window - > DC . CursorPos . y ) ;
if ( + + columns - > Current < columns - > Count )
{
// Columns 1+ ignore IndentX (by canceling it out)
// FIXME-COLUMNS: Unnecessary, could be locked?
window - > DC . ColumnsOffset . x = GetColumnOffset ( columns - > Current ) - window - > DC . Indent . x + column_padding ;
window - > DrawList - > ChannelsSetCurrent ( columns - > Current + 1 ) ;
}
else
{
// New row/line
// Column 0 honor IndentX
window - > DC . ColumnsOffset . x = columns - > OffMinX - window - > DC . Indent . x + column_padding ;
window - > DrawList - > ChannelsSetCurrent ( 1 ) ;
columns - > Current = 0 ;
columns - > LineMinY = columns - > LineMaxY ;
}
window - > DC . CursorPos . x = ( float ) ( int ) ( window - > Pos . x + window - > DC . Indent . x + window - > DC . ColumnsOffset . x ) ;
window - > DC . CursorPos . y = columns - > LineMinY ;
window - > DC . CurrLineSize = ImVec2 ( 0.0f , 0.0f ) ;
window - > DC . CurrLineTextBaseOffset = 0.0f ;
PushColumnClipRect ( columns - > Current ) ; // FIXME-COLUMNS: Could it be an overwrite?
// FIXME-COLUMNS: Share code with BeginColumns() - move code on columns setup.
float offset_0 = GetColumnOffset ( columns - > Current ) ;
float offset_1 = GetColumnOffset ( columns - > Current + 1 ) ;
float width = offset_1 - offset_0 ;
PushItemWidth ( width * 0.65f ) ;
window - > WorkRect . Max . x = window - > Pos . x + offset_1 - column_padding ;
}
void ImGui : : EndColumns ( )
{
ImGuiContext & g = * GImGui ;
ImGuiWindow * window = GetCurrentWindow ( ) ;
ImGuiColumns * columns = window - > DC . CurrentColumns ;
IM_ASSERT ( columns ! = NULL ) ;
PopItemWidth ( ) ;
if ( columns - > Count > 1 )
{
PopClipRect ( ) ;
window - > DrawList - > ChannelsMerge ( ) ;
}
const ImGuiColumnsFlags flags = columns - > Flags ;
columns - > LineMaxY = ImMax ( columns - > LineMaxY , window - > DC . CursorPos . y ) ;
window - > DC . CursorPos . y = columns - > LineMaxY ;
if ( ! ( flags & ImGuiColumnsFlags_GrowParentContentsSize ) )
window - > DC . CursorMaxPos . x = columns - > HostCursorMaxPosX ; // Restore cursor max pos, as columns don't grow parent
// Draw columns borders and handle resize
// The IsBeingResized flag ensure we preserve pre-resize columns width so back-and-forth are not lossy
bool is_being_resized = false ;
if ( ! ( flags & ImGuiColumnsFlags_NoBorder ) & & ! window - > SkipItems )
{
// We clip Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.
const float y1 = ImMax ( columns - > HostCursorPosY , window - > ClipRect . Min . y ) ;
const float y2 = ImMin ( window - > DC . CursorPos . y , window - > ClipRect . Max . y ) ;
int dragging_column = - 1 ;
for ( int n = 1 ; n < columns - > Count ; n + + )
{
ImGuiColumnData * column = & columns - > Columns [ n ] ;
float x = window - > Pos . x + GetColumnOffset ( n ) ;
const ImGuiID column_id = columns - > ID + ImGuiID ( n ) ;
const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH ;
const ImRect column_hit_rect ( ImVec2 ( x - column_hit_hw , y1 ) , ImVec2 ( x + column_hit_hw , y2 ) ) ;
KeepAliveID ( column_id ) ;
if ( IsClippedEx ( column_hit_rect , column_id , false ) )
continue ;
bool hovered = false , held = false ;
if ( ! ( flags & ImGuiColumnsFlags_NoResize ) )
{
ButtonBehavior ( column_hit_rect , column_id , & hovered , & held ) ;
if ( hovered | | held )
g . MouseCursor = ImGuiMouseCursor_ResizeEW ;
if ( held & & ! ( column - > Flags & ImGuiColumnsFlags_NoResize ) )
dragging_column = n ;
}
// Draw column
const ImU32 col = GetColorU32 ( held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator ) ;
const float xi = ( float ) ( int ) x ;
window - > DrawList - > AddLine ( ImVec2 ( xi , y1 + 1.0f ) , ImVec2 ( xi , y2 ) , col ) ;
}
// Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
if ( dragging_column ! = - 1 )
{
if ( ! columns - > IsBeingResized )
for ( int n = 0 ; n < columns - > Count + 1 ; n + + )
columns - > Columns [ n ] . OffsetNormBeforeResize = columns - > Columns [ n ] . OffsetNorm ;
columns - > IsBeingResized = is_being_resized = true ;
float x = GetDraggedColumnOffset ( columns , dragging_column ) ;
SetColumnOffset ( dragging_column , x ) ;
}
}
columns - > IsBeingResized = is_being_resized ;
window - > WorkRect = columns - > HostWorkRect ;
window - > DC . CurrentColumns = NULL ;
window - > DC . ColumnsOffset . x = 0.0f ;
window - > DC . CursorPos . x = ( float ) ( int ) ( window - > Pos . x + window - > DC . Indent . x + window - > DC . ColumnsOffset . x ) ;
}
// [2018-03: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
void ImGui : : Columns ( int columns_count , const char * id , bool border )
{
ImGuiWindow * window = GetCurrentWindow ( ) ;
IM_ASSERT ( columns_count > = 1 ) ;
ImGuiColumnsFlags flags = ( border ? 0 : ImGuiColumnsFlags_NoBorder ) ;
//flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
ImGuiColumns * columns = window - > DC . CurrentColumns ;
if ( columns ! = NULL & & columns - > Count = = columns_count & & columns - > Flags = = flags )
return ;
if ( columns ! = NULL )
EndColumns ( ) ;
if ( columns_count ! = 1 )
BeginColumns ( id , columns_count , flags ) ;
}
//-------------------------------------------------------------------------