@ -694,7 +694,7 @@ static ImRect GetVisibleRect();
static void CloseInactivePopups ( ImGuiWindow * ref_window ) ;
static void ClosePopupToLevel ( int remaining ) ;
static ImGuiWindow * GetFrontMostModalRootWindow ( ) ;
static ImVec2 FindBest Popup WindowPos( const ImVec2 & base_pos , const ImVec2 & size , ImGuiDir * last_dir , const ImRect & rect_to_avoid ) ;
static ImVec2 FindBest WindowPosForPopup ( const ImVec2 & base_pos , const ImVec2 & size , ImGuiDir * last_dir , const ImRect & rect_to_avoid ) ;
static bool InputTextFilterCharacter ( unsigned int * p_char , ImGuiInputTextFlags flags , ImGuiTextEditCallback callback , void * user_data ) ;
static int InputTextCalcTextLenAndLineCount ( const char * text_begin , const char * * out_text_end ) ;
@ -4508,14 +4508,14 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_
{
ImGuiContext & g = * GImGui ;
char window_name [ 16 ] ;
ImFormatString ( window_name , IM_ARRAYSIZE ( window_name ) , " ##Tooltip %02d" , g . TooltipOverrideCount ) ;
ImFormatString ( window_name , IM_ARRAYSIZE ( window_name ) , " ##Tooltip _ %02d" , g . TooltipOverrideCount ) ;
if ( override_previous_tooltip )
if ( ImGuiWindow * window = FindWindowByName ( window_name ) )
if ( window - > Active )
{
// Hide previous tooltips. We can't easily "reset" the content of a window so we create a new one.
window - > HiddenFrames = 1 ;
ImFormatString ( window_name , IM_ARRAYSIZE ( window_name ) , " ##Tooltip %02d" , + + g . TooltipOverrideCount ) ;
ImFormatString ( window_name , IM_ARRAYSIZE ( window_name ) , " ##Tooltip _ %02d" , + + g . TooltipOverrideCount ) ;
}
ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNavFocus ;
Begin ( window_name , NULL , flags | extra_flags ) ;
@ -4558,7 +4558,7 @@ void ImGui::OpenPopupEx(ImGuiID id, bool reopen_existing)
int current_stack_size = g . CurrentPopupStack . Size ;
ImVec2 mouse_pos = g . IO . MousePos ;
ImVec2 popup_pos = ( ! g . NavDisableHighlight & & g . NavDisableMouseHover ) ? NavCalcPreferredMousePos ( ) : mouse_pos ;
ImGuiPopupRef popup_ref = ImGuiPopupRef ( id , window , window - > GetID ( " ## m enus" ) , popup_pos , mouse_pos ) ; // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here)
ImGuiPopupRef popup_ref = ImGuiPopupRef ( id , window , window - > GetID ( " ## M enus" ) , popup_pos , mouse_pos ) ; // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here)
if ( g . OpenPopupStack . Size < current_stack_size + 1 )
g . OpenPopupStack . push_back ( popup_ref ) ;
else if ( reopen_existing | | g . OpenPopupStack [ current_stack_size ] . PopupId ! = id )
@ -4672,9 +4672,9 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
char name [ 20 ] ;
if ( flags & ImGuiWindowFlags_ChildMenu )
ImFormatString ( name , IM_ARRAYSIZE ( name ) , " ## menu_% d" , g . CurrentPopupStack . Size ) ; // Recycle windows based on depth
ImFormatString ( name , IM_ARRAYSIZE ( name ) , " ## Menu_%02 d" , g . CurrentPopupStack . Size ) ; // Recycle windows based on depth
else
ImFormatString ( name , IM_ARRAYSIZE ( name ) , " ## p opup_%08x" , id ) ; // Not recycling, so we can close/open during the same frame
ImFormatString ( name , IM_ARRAYSIZE ( name ) , " ## P opup_%08x" , id ) ; // Not recycling, so we can close/open during the same frame
bool is_open = Begin ( name , NULL , flags ) ;
if ( ! is_open ) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
@ -4931,7 +4931,7 @@ static void CheckStacksSize(ImGuiWindow* window, bool write)
IM_ASSERT ( p_backup = = window - > DC . StackSizesBackup + IM_ARRAYSIZE ( window - > DC . StackSizesBackup ) ) ;
}
static ImVec2 FindBest Popup WindowPos( const ImVec2 & base_pos , const ImVec2 & size , ImGuiDir * last_dir , const ImRect & r_avoid )
static ImVec2 FindBest WindowPosForPopup ( const ImVec2 & base_pos , const ImVec2 & size , ImGuiDir * last_dir , const ImRect & r_avoid , ImGuiWindowFlags flags )
{
const ImGuiStyle & style = GImGui - > Style ;
@ -4944,17 +4944,38 @@ static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size,
//GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
//GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
// Combo Box policy (we want a connecting edge)
if ( flags & ImGuiWindowFlags_ComboBox )
{
const ImGuiDir dir_prefered_order [ ImGuiDir_Count_ ] = { ImGuiDir_Down , ImGuiDir_Right , ImGuiDir_Left , ImGuiDir_Up } ;
for ( int n = ( * last_dir ! = ImGuiDir_None ) ? - 1 : 0 ; n < ImGuiDir_Count_ ; n + + )
{
const ImGuiDir dir = ( n = = - 1 ) ? * last_dir : dir_prefered_order [ n ] ;
if ( n ! = - 1 & & dir = = * last_dir ) // Already tried this direction?
continue ;
ImVec2 pos ;
if ( dir = = ImGuiDir_Down ) pos = ImVec2 ( r_avoid . Min . x , r_avoid . Max . y ) ; // Below, Toward Right (default)
if ( dir = = ImGuiDir_Right ) pos = ImVec2 ( r_avoid . Min . x , r_avoid . Min . y - size . y ) ; // Above, Toward Right
if ( dir = = ImGuiDir_Left ) pos = ImVec2 ( r_avoid . Max . x - size . x , r_avoid . Max . y ) ; // Below, Toward Left
if ( dir = = ImGuiDir_Up ) pos = ImVec2 ( r_avoid . Max . x - size . x , r_avoid . Min . y - size . y ) ; // Above, Toward Left
if ( ! r_outer . Contains ( ImRect ( pos , pos + size ) ) )
continue ;
* last_dir = dir ;
return pos ;
}
}
// Default popup policy
const ImGuiDir dir_prefered_order [ ImGuiDir_Count_ ] = { ImGuiDir_Right , ImGuiDir_Down , ImGuiDir_Up , ImGuiDir_Left } ;
for ( int n = ( * last_dir ! = ImGuiDir_None ) ? - 1 : 0 ; n < ImGuiDir_Count_ ; n + + )
{
const ImGuiDir dir = ( n = = - 1 ) ? * last_dir : dir_prefered_order [ n ] ;
float avail_w = ( dir = = ImGuiDir_Left ? r_avoid . Min . x : r_outer . Max . x ) - ( dir = = ImGuiDir_Right ? r_avoid . Max . x : r_outer . Min . x ) ;
if ( avail_w < size . x )
if ( n ! = - 1 & & dir = = * last_dir ) // Already tried this direction?
continue ;
float avail_w = ( dir = = ImGuiDir_Left ? r_avoid . Min . x : r_outer . Max . x ) - ( dir = = ImGuiDir_Right ? r_avoid . Max . x : r_outer . Min . x ) ;
float avail_h = ( dir = = ImGuiDir_Up ? r_avoid . Min . y : r_outer . Max . y ) - ( dir = = ImGuiDir_Down ? r_avoid . Max . y : r_outer . Min . y ) ;
if ( avail_h < size . y )
if ( avail_ w < size . x | | avail_ h < size . y )
continue ;
ImVec2 pos ;
pos . x = ( dir = = ImGuiDir_Left ) ? r_avoid . Min . x - size . x : ( dir = = ImGuiDir_Right ) ? r_avoid . Max . x : base_pos_clamped . x ;
pos . y = ( dir = = ImGuiDir_Up ) ? r_avoid . Min . y - size . y : ( dir = = ImGuiDir_Down ) ? r_avoid . Max . y : base_pos_clamped . y ;
@ -5035,7 +5056,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl
return window ;
}
static ImVec2 CalcSize FullWith Constraint( ImGuiWindow * window , ImVec2 new_size )
static ImVec2 CalcSize After Constraint( ImGuiWindow * window , ImVec2 new_size )
{
ImGuiContext & g = * GImGui ;
if ( g . SetNextWindowSizeConstraint )
@ -5055,6 +5076,8 @@ static ImVec2 CalcSizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
new_size = data . DesiredSize ;
}
}
// Minimum size
if ( ! ( window - > Flags & ( ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize ) ) )
{
new_size = ImMax ( new_size , g . Style . WindowMinSize ) ;
@ -5071,7 +5094,7 @@ static ImVec2 CalcSizeContents(ImGuiWindow* window)
return sz + window - > WindowPadding ;
}
static ImVec2 CalcSizeAutoFit ( ImGuiWindow * window )
static ImVec2 CalcSizeAutoFit ( ImGuiWindow * window , const ImVec2 & size_contents )
{
ImGuiContext & g = * GImGui ;
ImGuiStyle & style = g . Style ;
@ -5080,16 +5103,16 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window)
if ( ( flags & ImGuiWindowFlags_Tooltip ) ! = 0 )
{
// Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
size_auto_fit = window- > SizeC ontents;
size_auto_fit = size_c ontents;
}
else
{
// Handling case of auto fit window not fitting on the screen (on either axis ): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
size_auto_fit = ImClamp ( window- > SizeC ontents, style . WindowMinSize , ImMax ( style . WindowMinSize , g . IO . DisplaySize - g . Style . DisplaySafeAreaPadding ) ) ;
ImVec2 size_auto_fit_after_constraint = CalcSize FullWith Constraint( window , size_auto_fit ) ;
if ( size_auto_fit_after_constraint . x < window- > SizeC ontents. x & & ! ( flags & ImGuiWindowFlags_NoScrollbar ) & & ( flags & ImGuiWindowFlags_HorizontalScrollbar ) )
// When the window cannot fit all contents (either because of constraints, either because screen is too small ): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
size_auto_fit = ImClamp ( size_c ontents, style . WindowMinSize , ImMax ( style . WindowMinSize , g . IO . DisplaySize - g . Style . DisplaySafeAreaPadding ) ) ;
ImVec2 size_auto_fit_after_constraint = CalcSize After Constraint( window , size_auto_fit ) ;
if ( size_auto_fit_after_constraint . x < size_c ontents. x & & ! ( flags & ImGuiWindowFlags_NoScrollbar ) & & ( flags & ImGuiWindowFlags_HorizontalScrollbar ) )
size_auto_fit . y + = style . ScrollbarSize ;
if ( size_auto_fit_after_constraint . y < window- > SizeC ontents. y & & ! ( flags & ImGuiWindowFlags_NoScrollbar ) )
if ( size_auto_fit_after_constraint . y < size_c ontents. y & & ! ( flags & ImGuiWindowFlags_NoScrollbar ) )
size_auto_fit . x + = style . ScrollbarSize ;
}
return size_auto_fit ;
@ -5137,7 +5160,7 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co
ImVec2 pos_min = ImLerp ( corner_target , window - > Pos , corner_norm ) ; // Expected window upper-left
ImVec2 pos_max = ImLerp ( window - > Pos + window - > Size , corner_target , corner_norm ) ; // Expected window lower-right
ImVec2 size_expected = pos_max - pos_min ;
ImVec2 size_constrained = CalcSize FullWith Constraint( window , size_expected ) ;
ImVec2 size_constrained = CalcSize After Constraint( window , size_expected ) ;
* out_pos = pos_min ;
if ( corner_norm . x = = 0.0f )
out_pos - > x - = ( size_constrained . x - size_expected . x ) ;
@ -5375,30 +5398,30 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
const float window_border_size = window - > WindowBorderSize ;
// Calculate auto-fit size, handle automatic resize
const ImVec2 size_auto_fit = CalcSizeAutoFit ( window );
ImVec2 size_for_scrollbars_visibility = window - > SizeFullAtLastBegin ;
const ImVec2 size_auto_fit = CalcSizeAutoFit ( window , window - > SizeContents );
ImVec2 size_full_modified ( FLT_MAX , FLT_MAX ) ;
if ( flags & ImGuiWindowFlags_AlwaysAutoResize & & ! window - > Collapsed )
{
// Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
if ( ! window_size_x_set_by_api )
window - > SizeFull . x = size_f or_scrollbars_visibility . x = size_auto_fit . x ;
window - > SizeFull . x = size_f ull_modified . x = size_auto_fit . x ;
if ( ! window_size_y_set_by_api )
window - > SizeFull . y = size_f or_scrollbars_visibility . y = size_auto_fit . y ;
window - > SizeFull . y = size_f ull_modified . y = size_auto_fit . y ;
}
else if ( window - > AutoFitFramesX > 0 | | window - > AutoFitFramesY > 0 )
{
// Auto-fit only grows during the first few frames
// We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize ImGuiWindowFlags_AlwaysAutoResize .
// We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed .
if ( ! window_size_x_set_by_api & & window - > AutoFitFramesX > 0 )
window - > SizeFull . x = size_f or_scrollbars_visibility . x = window - > AutoFitOnlyGrows ? ImMax ( window - > SizeFull . x , size_auto_fit . x ) : size_auto_fit . x ;
window - > SizeFull . x = size_f ull_modified . x = window - > AutoFitOnlyGrows ? ImMax ( window - > SizeFull . x , size_auto_fit . x ) : size_auto_fit . x ;
if ( ! window_size_y_set_by_api & & window - > AutoFitFramesY > 0 )
window - > SizeFull . y = size_f or_scrollbars_visibility . y = window - > AutoFitOnlyGrows ? ImMax ( window - > SizeFull . y , size_auto_fit . y ) : size_auto_fit . y ;
window - > SizeFull . y = size_f ull_modified . y = window - > AutoFitOnlyGrows ? ImMax ( window - > SizeFull . y , size_auto_fit . y ) : size_auto_fit . y ;
if ( ! window - > Collapsed )
MarkIniSettingsDirty ( window ) ;
}
// Apply minimum/maximum window size constraints and final size
window - > SizeFull = CalcSize FullWith Constraint( window , window - > SizeFull ) ;
window - > SizeFull = CalcSize After Constraint( window , window - > SizeFull ) ;
window - > Size = window - > Collapsed ? window - > TitleBarRect ( ) . GetSize ( ) : window - > SizeFull ;
if ( ( flags & ImGuiWindowFlags_ChildWindow ) & & ! ( flags & ImGuiWindowFlags_Popup ) )
{
@ -5411,10 +5434,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
if ( ! window - > Collapsed )
{
window - > ScrollbarY = ( flags & ImGuiWindowFlags_AlwaysVerticalScrollbar ) | | ( ( window - > SizeContents . y > size_for_scrollbars_visibility . y ) & & ! ( flags & ImGuiWindowFlags_NoScrollbar ) ) ;
window - > ScrollbarX = ( flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar ) | | ( ( window - > SizeContents . x > size_for_scrollbars_visibility . x - ( window - > ScrollbarY ? style . ScrollbarSize : 0.0f ) - window - > WindowPadding . x ) & & ! ( flags & ImGuiWindowFlags_NoScrollbar ) & & ( flags & ImGuiWindowFlags_HorizontalScrollbar ) ) ;
// When reading the current size we need to read it after size constraints have been applied
float size_x_for_scrollbars = size_full_modified . x ! = FLT_MAX ? window - > SizeFull . x : window - > SizeFullAtLastBegin . x ;
float size_y_for_scrollbars = size_full_modified . y ! = FLT_MAX ? window - > SizeFull . y : window - > SizeFullAtLastBegin . y ;
window - > ScrollbarY = ( flags & ImGuiWindowFlags_AlwaysVerticalScrollbar ) | | ( ( window - > SizeContents . y > size_y_for_scrollbars ) & & ! ( flags & ImGuiWindowFlags_NoScrollbar ) ) ;
window - > ScrollbarX = ( flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar ) | | ( ( window - > SizeContents . x > size_x_for_scrollbars - ( window - > ScrollbarY ? style . ScrollbarSize : 0.0f ) - window - > WindowPadding . x ) & & ! ( flags & ImGuiWindowFlags_NoScrollbar ) & & ( flags & ImGuiWindowFlags_HorizontalScrollbar ) ) ;
if ( window - > ScrollbarX & & ! window - > ScrollbarY )
window - > ScrollbarY = ( window - > SizeContents . y > size_for_scrollbars_visibility . y + style . ScrollbarSize ) & & ! ( flags & ImGuiWindowFlags_NoScrollbar ) ;
window - > ScrollbarY = ( window - > SizeContents . y > size_ y_ for_scrollbars + style . ScrollbarSize ) & & ! ( flags & ImGuiWindowFlags_NoScrollbar ) ;
window - > ScrollbarSizes = ImVec2 ( window - > ScrollbarY ? style . ScrollbarSize : 0.0f , window - > ScrollbarX ? style . ScrollbarSize : 0.0f ) ;
}
@ -5446,12 +5472,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
rect_to_avoid = ImRect ( - FLT_MAX , parent_window - > Pos . y + parent_window - > TitleBarHeight ( ) , FLT_MAX , parent_window - > Pos . y + parent_window - > TitleBarHeight ( ) + parent_window - > MenuBarHeight ( ) ) ;
else
rect_to_avoid = ImRect ( parent_window - > Pos . x + horizontal_overlap , - FLT_MAX , parent_window - > Pos . x + parent_window - > Size . x - horizontal_overlap - parent_window - > ScrollbarSizes . x , FLT_MAX ) ;
window - > PosFloat = FindBest Popup WindowPos( window - > PosFloat , window - > Size , & window - > AutoPosLastDirection , rect_to_avoid ) ;
window - > PosFloat = FindBest WindowPosForPopup ( window - > PosFloat , window - > Size , & window - > AutoPosLastDirection , rect_to_avoid , flags ) ;
}
else if ( ( flags & ImGuiWindowFlags_Popup ) ! = 0 & & ! window_pos_set_by_api & & window_just_appearing_after_hidden_for_resize )
{
ImRect rect_to_avoid ( window - > PosFloat . x - 1 , window - > PosFloat . y - 1 , window - > PosFloat . x + 1 , window - > PosFloat . y + 1 ) ;
window - > PosFloat = FindBest Popup WindowPos( window - > PosFloat , window - > Size , & window - > AutoPosLastDirection , rect_to_avoid ) ;
window - > PosFloat = FindBest WindowPosForPopup ( window - > PosFloat , window - > Size , & window - > AutoPosLastDirection , rect_to_avoid , flags ) ;
}
// Position tooltip (always follows mouse)
@ -5459,7 +5485,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
{
ImVec2 ref_pos = ( ! g . NavDisableHighlight & & g . NavDisableMouseHover ) ? NavCalcPreferredMousePos ( ) : g . IO . MousePos ;
ImRect rect_to_avoid ( ref_pos . x - 16 , ref_pos . y - 8 , ref_pos . x + 24 , ref_pos . y + 24 ) ; // FIXME: Completely hard-coded. Store boxes in mouse cursor data? Scale? Center on cursor hit-point?
window - > PosFloat = FindBest Popup WindowPos( ref_pos , window - > Size , & window - > AutoPosLastDirection , rect_to_avoid ) ;
window - > PosFloat = FindBest WindowPosForPopup ( ref_pos , window - > Size , & window - > AutoPosLastDirection , rect_to_avoid , flags ) ;
if ( window - > AutoPosLastDirection = = ImGuiDir_None )
window - > PosFloat = ref_pos + ImVec2 ( 2 , 2 ) ; // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
}
@ -5555,7 +5581,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
if ( g . HoveredWindow = = window & & held & & g . IO . MouseDoubleClicked [ 0 ] & & resize_grip_n = = 0 )
{
// Manual auto-fit when double-clicking
size_target = CalcSize FullWith Constraint( window , size_auto_fit ) ;
size_target = CalcSize After Constraint( window , size_auto_fit ) ;
ClearActiveID ( ) ;
}
else if ( held )
@ -10150,63 +10176,25 @@ bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_fla
return InputIntN ( label , v , 4 , extra_flags ) ;
}
static bool Items_ArrayGetter ( void * data , int idx , const char * * out_text )
{
const char * const * items = ( const char * const * ) data ;
if ( out_text )
* out_text = items [ idx ] ;
return true ;
}
static bool Items_SingleStringGetter ( void * data , int idx , const char * * out_text )
static float CalcMaxPopupHeightFromItemCount ( int items_count )
{
// FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
const char * items_separated_by_zeros = ( const char * ) data ;
int items_count = 0 ;
const char * p = items_separated_by_zeros ;
while ( * p )
{
if ( idx = = items_count )
break ;
p + = strlen ( p ) + 1 ;
items_count + + ;
}
if ( ! * p )
return false ;
if ( out_text )
* out_text = p ;
return true ;
}
// Combo box helper allowing to pass an array of strings.
bool ImGui : : Combo ( const char * label , int * current_item , const char * const * items , int items_count , int height_in_items )
{
const bool value_changed = Combo ( label , current_item , Items_ArrayGetter , ( void * ) items , items_count , height_in_items ) ;
return value_changed ;
}
// Combo box helper allowing to pass all items in a single string.
bool ImGui : : Combo ( const char * label , int * current_item , const char * items_separated_by_zeros , int height_in_items )
{
int items_count = 0 ;
const char * p = items_separated_by_zeros ; // FIXME-OPT: Avoid computing this, or at least only when combo is open
while ( * p )
{
p + = strlen ( p ) + 1 ;
items_count + + ;
}
bool value_changed = Combo ( label , current_item , Items_SingleStringGetter , ( void * ) items_separated_by_zeros , items_count , height_in_items ) ;
return value_changed ;
ImGuiContext & g = * GImGui ;
if ( items_count < = 0 )
return FLT_MAX ;
return ( g . FontSize + g . Style . ItemSpacing . y ) * items_count - g . Style . ItemSpacing . y + ( g . Style . WindowPadding . y * 2 ) ;
}
bool ImGui : : BeginCombo ( const char * label , const char * preview_value , ImGuiComboFlags flags , ImVec2 popup_size )
bool ImGui : : BeginCombo ( const char * label , const char * preview_value , ImGuiComboFlags flags )
{
( void ) flags ; // Unused
// Always consume the SetNextWindowSizeConstraint() call in our early return paths
ImGuiContext & g = * GImGui ;
bool backup_has_next_window_size_constraint = g . SetNextWindowSizeConstraint ;
g . SetNextWindowSizeConstraint = false ;
ImGuiWindow * window = GetCurrentWindow ( ) ;
if ( window - > SkipItems )
return false ;
ImGuiContext & g = * GImGui ;
const ImGuiStyle & style = g . Style ;
const ImGuiID id = window - > GetID ( label ) ;
const float w = CalcItemWidth ( ) ;
@ -10248,58 +10236,75 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
if ( ! popup_open )
return false ;
if ( popup_size . x = = 0.0f )
popup_size . x = w ;
float popup_y1 = frame_bb . Max . y ;
float popup_y2 = ImClamp ( popup_y1 + popup_size . y , popup_y1 , g . IO . DisplaySize . y - style . DisplaySafeAreaPadding . y ) ;
if ( ( popup_y2 - popup_y1 ) < ImMin ( popup_size . y , frame_bb . Min . y - style . DisplaySafeAreaPadding . y ) )
if ( backup_has_next_window_size_constraint )
{
// Position our combo ABOVE because there's more space to fit! (FIXME: Handle in Begin() or use a shared helper. We have similar code in Begin() for popup placement)
popup_y1 = ImClamp ( frame_bb . Min . y - popup_size . y , style . DisplaySafeAreaPadding . y , frame_bb . Min . y ) ;
popup_y2 = frame_bb . Min . y ;
SetNextWindowPos ( ImVec2 ( frame_bb . Min . x , frame_bb . Min . y + style . FrameBorderSize ) , ImGuiCond_Always , ImVec2 ( 0.0f , 1.0f ) ) ;
g . SetNextWindowSizeConstraint = true ;
g . SetNextWindowSizeConstraintRect . Min . x = ImMax ( g . SetNextWindowSizeConstraintRect . Min . x , w ) ;
}
else
{
// Position our combo below
SetNextWindowPos ( ImVec2 ( frame_bb . Min . x , frame_bb . Max . y - style . FrameBorderSize ) , ImGuiCond_Always , ImVec2 ( 0.0f , 0.0f ) ) ;
if ( ( flags & ImGuiComboFlags_HeightMask_ ) = = 0 )
flags | = ImGuiComboFlags_HeightRegular ;
IM_ASSERT ( ImIsPowerOfTwo ( flags & ImGuiComboFlags_HeightMask_ ) ) ; // Only one
int popup_max_height_in_items = - 1 ;
if ( flags & ImGuiComboFlags_HeightRegular ) popup_max_height_in_items = 8 ;
else if ( flags & ImGuiComboFlags_HeightSmall ) popup_max_height_in_items = 4 ;
else if ( flags & ImGuiComboFlags_HeightLarge ) popup_max_height_in_items = 20 ;
SetNextWindowSizeConstraints ( ImVec2 ( w , 0.0f ) , ImVec2 ( FLT_MAX , CalcMaxPopupHeightFromItemCount ( popup_max_height_in_items ) ) ) ;
}
char name [ 16 ] ;
ImFormatString ( name , IM_ARRAYSIZE ( name ) , " ##Combo_%02d " , g . CurrentPopupStack . Size ) ; // Recycle windows based on depth
// Peak into expected window size so we can position it
if ( ImGuiWindow * popup_window = FindWindowByName ( name ) )
{
ImVec2 size_contents = CalcSizeContents ( popup_window ) ;
ImVec2 size_expected = CalcSizeAfterConstraint ( popup_window , CalcSizeAutoFit ( popup_window , size_contents ) ) ;
if ( flags & ImGuiComboFlags_PopupAlignLeft )
popup_window - > AutoPosLastDirection = ImGuiDir_Left ;
ImVec2 pos = FindBestWindowPosForPopup ( frame_bb . GetBL ( ) , size_expected , & popup_window - > AutoPosLastDirection , frame_bb , ImGuiWindowFlags_ComboBox ) ;
SetNextWindowPos ( pos ) ;
}
SetNextWindowSize ( ImVec2 ( popup_size . x , popup_y2 - popup_y1 ) , ImGuiCond_Appearing ) ;
PushStyleVar ( ImGuiStyleVar_WindowPadding , style . FramePadding ) ;
if ( ! BeginPopupEx ( id , ImGuiWindowFlags_ComboBox ) )
ImGuiWindowFlags window_flags = ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings ;
if ( ! Begin ( name , NULL , window_flags ) )
{
EndPopup ( ) ;
IM_ASSERT ( 0 ) ; // This should never happen as we tested for IsPopupOpen() above
return false ;
}
Spacing ( ) ;
// Horizontally align ourselves with the framed text
if ( style . FramePadding . x ! = style . WindowPadding . x )
Indent ( style . FramePadding . x - style . WindowPadding . x ) ;
return true ;
}
void ImGui : : EndCombo ( )
{
const ImGuiStyle & style = GImGui - > Style ;
if ( style . FramePadding . x ! = style . WindowPadding . x )
Unindent ( style . FramePadding . x - style . WindowPadding . x ) ;
EndPopup ( ) ;
PopStyleVar ( ) ;
}
// Combo box function.
bool ImGui : : Combo ( const char * label , int * current_item , bool ( * items_getter ) ( void * , int , const char * * ) , void * data , int items_count , int height_in_items)
bool ImGui : : Combo ( const char * label , int * current_item , bool ( * items_getter ) ( void * , int , const char * * ) , void * data , int items_count , int popup_max_ height_in_items)
{
ImGuiContext & g = * GImGui ;
const ImGuiStyle & style = g . Style ;
const char * preview_text = NULL ;
if ( * current_item > = 0 & & * current_item < items_count )
items_getter ( data , * current_item , & preview_text ) ;
// Size default to hold ~7 items
if ( height_in_items < 0 )
height_in_items = 7 ;
float popup_height = ( g . FontSize + style . ItemSpacing . y ) * ImMin ( items_count , height_in_items ) + ( style . FramePadding . y * 3 ) ;
if ( ! BeginCombo ( label , preview_text , 0 , ImVec2 ( 0.0f , popup_height ) ))
if ( popup_max_height_in_items ! = - 1 & & ! g . SetNextWindowSizeConstraint )
{
float popup_max_height = CalcMaxPopupHeightFromItemCount ( popup_max_height_in_items ) ;
SetNextWindowSizeConstraints ( ImVec2 ( 0 , 0 ) , ImVec2 ( FLT_MAX , popup_max_height ) ) ;
}
if ( ! BeginCombo ( label , preview_text , 0 ))
return false ;
// Display items
@ -10326,6 +10331,55 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi
return value_changed ;
}
static bool Items_ArrayGetter ( void * data , int idx , const char * * out_text )
{
const char * const * items = ( const char * const * ) data ;
if ( out_text )
* out_text = items [ idx ] ;
return true ;
}
static bool Items_SingleStringGetter ( void * data , int idx , const char * * out_text )
{
// FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
const char * items_separated_by_zeros = ( const char * ) data ;
int items_count = 0 ;
const char * p = items_separated_by_zeros ;
while ( * p )
{
if ( idx = = items_count )
break ;
p + = strlen ( p ) + 1 ;
items_count + + ;
}
if ( ! * p )
return false ;
if ( out_text )
* out_text = p ;
return true ;
}
// Combo box helper allowing to pass an array of strings.
bool ImGui : : Combo ( const char * label , int * current_item , const char * const * items , int items_count , int height_in_items )
{
const bool value_changed = Combo ( label , current_item , Items_ArrayGetter , ( void * ) items , items_count , height_in_items ) ;
return value_changed ;
}
// Combo box helper allowing to pass all items in a single string.
bool ImGui : : Combo ( const char * label , int * current_item , const char * items_separated_by_zeros , int height_in_items )
{
int items_count = 0 ;
const char * p = items_separated_by_zeros ; // FIXME-OPT: Avoid computing this, or at least only when combo is open
while ( * p )
{
p + = strlen ( p ) + 1 ;
items_count + + ;
}
bool value_changed = Combo ( label , current_item , Items_SingleStringGetter , ( void * ) items_separated_by_zeros , items_count , height_in_items ) ;
return value_changed ;
}
// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID.
bool ImGui : : Selectable ( const char * label , bool selected , ImGuiSelectableFlags flags , const ImVec2 & size_arg )
@ -10686,7 +10740,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
bool pressed ;
bool menu_is_open = IsPopupOpen ( id ) ;
bool menuset_is_open = ! ( window - > Flags & ImGuiWindowFlags_Popup ) & & ( g . OpenPopupStack . Size > g . CurrentPopupStack . Size & & g . OpenPopupStack [ g . CurrentPopupStack . Size ] . ParentMenuSet = = window - > GetID ( " ## m enus" ) ) ;
bool menuset_is_open = ! ( window - > Flags & ImGuiWindowFlags_Popup ) & & ( g . OpenPopupStack . Size > g . CurrentPopupStack . Size & & g . OpenPopupStack [ g . CurrentPopupStack . Size ] . ParentMenuSet = = window - > GetID ( " ## M enus" ) ) ;
ImGuiWindow * backed_nav_window = g . NavWindow ;
if ( menuset_is_open )
g . NavWindow = window ; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)