@ -10732,213 +10732,6 @@ bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_fla
return InputScalarN ( label , ImGuiDataType_S32 , v , 4 , NULL , NULL , " %d " , extra_flags ) ;
}
static float CalcMaxPopupHeightFromItemCount ( int items_count )
{
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 )
{
// Always consume the SetNextWindowSizeConstraint() call in our early return paths
ImGuiContext & g = * GImGui ;
ImGuiCond backup_next_window_size_constraint = g . NextWindowData . SizeConstraintCond ;
g . NextWindowData . SizeConstraintCond = 0 ;
ImGuiWindow * window = GetCurrentWindow ( ) ;
if ( window - > SkipItems )
return false ;
IM_ASSERT ( ( flags & ( ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview ) ) ! = ( ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview ) ) ; // Can't use both flags together
const ImGuiStyle & style = g . Style ;
const ImGuiID id = window - > GetID ( label ) ;
const float arrow_size = ( flags & ImGuiComboFlags_NoArrowButton ) ? 0.0f : GetFrameHeight ( ) ;
const ImVec2 label_size = CalcTextSize ( label , NULL , true ) ;
const float w = ( flags & ImGuiComboFlags_NoPreview ) ? arrow_size : CalcItemWidth ( ) ;
const ImRect frame_bb ( window - > DC . CursorPos , window - > DC . CursorPos + ImVec2 ( w , label_size . y + style . FramePadding . y * 2.0f ) ) ;
const ImRect total_bb ( frame_bb . Min , frame_bb . Max + ImVec2 ( label_size . x > 0.0f ? style . ItemInnerSpacing . x + label_size . x : 0.0f , 0.0f ) ) ;
ItemSize ( total_bb , style . FramePadding . y ) ;
if ( ! ItemAdd ( total_bb , id , & frame_bb ) )
return false ;
bool hovered , held ;
bool pressed = ButtonBehavior ( frame_bb , id , & hovered , & held ) ;
bool popup_open = IsPopupOpen ( id ) ;
const ImRect value_bb ( frame_bb . Min , frame_bb . Max - ImVec2 ( arrow_size , 0.0f ) ) ;
const ImU32 frame_col = GetColorU32 ( hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg ) ;
RenderNavHighlight ( frame_bb , id ) ;
if ( ! ( flags & ImGuiComboFlags_NoPreview ) )
window - > DrawList - > AddRectFilled ( frame_bb . Min , ImVec2 ( frame_bb . Max . x - arrow_size , frame_bb . Max . y ) , frame_col , style . FrameRounding , ImDrawCornerFlags_Left ) ;
if ( ! ( flags & ImGuiComboFlags_NoArrowButton ) )
{
window - > DrawList - > AddRectFilled ( ImVec2 ( frame_bb . Max . x - arrow_size , frame_bb . Min . y ) , frame_bb . Max , GetColorU32 ( ( popup_open | | hovered ) ? ImGuiCol_ButtonHovered : ImGuiCol_Button ) , style . FrameRounding , ( w < = arrow_size ) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right ) ;
RenderArrow ( ImVec2 ( frame_bb . Max . x - arrow_size + style . FramePadding . y , frame_bb . Min . y + style . FramePadding . y ) , ImGuiDir_Down ) ;
}
RenderFrameBorder ( frame_bb . Min , frame_bb . Max , style . FrameRounding ) ;
if ( preview_value ! = NULL & & ! ( flags & ImGuiComboFlags_NoPreview ) )
RenderTextClipped ( frame_bb . Min + style . FramePadding , value_bb . Max , preview_value , NULL , NULL , ImVec2 ( 0.0f , 0.0f ) ) ;
if ( label_size . x > 0 )
RenderText ( ImVec2 ( frame_bb . Max . x + style . ItemInnerSpacing . x , frame_bb . Min . y + style . FramePadding . y ) , label ) ;
if ( ( pressed | | g . NavActivateId = = id ) & & ! popup_open )
{
if ( window - > DC . NavLayerCurrent = = 0 )
window - > NavLastIds [ 0 ] = id ;
OpenPopupEx ( id ) ;
popup_open = true ;
}
if ( ! popup_open )
return false ;
if ( backup_next_window_size_constraint )
{
g . NextWindowData . SizeConstraintCond = backup_next_window_size_constraint ;
g . NextWindowData . SizeConstraintRect . Min . x = ImMax ( g . NextWindowData . SizeConstraintRect . Min . x , w ) ;
}
else
{
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 ) )
if ( popup_window - > WasActive )
{
ImVec2 size_expected = CalcWindowExpectedSize ( popup_window ) ;
if ( flags & ImGuiComboFlags_PopupAlignLeft )
popup_window - > AutoPosLastDirection = ImGuiDir_Left ;
ImRect r_outer = GetWindowAllowedExtentRect ( popup_window ) ;
ImVec2 pos = FindBestWindowPosForPopupEx ( frame_bb . GetBL ( ) , size_expected , & popup_window - > AutoPosLastDirection , r_outer , frame_bb , ImGuiPopupPositionPolicy_ComboBox ) ;
SetNextWindowPos ( pos ) ;
}
// Horizontally align ourselves with the framed text
ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings ;
PushStyleVar ( ImGuiStyleVar_WindowPadding , ImVec2 ( style . FramePadding . x , style . WindowPadding . y ) ) ;
bool ret = Begin ( name , NULL , window_flags ) ;
PopStyleVar ( ) ;
if ( ! ret )
{
EndPopup ( ) ;
IM_ASSERT ( 0 ) ; // This should never happen as we tested for IsPopupOpen() above
return false ;
}
return true ;
}
void ImGui : : EndCombo ( )
{
EndPopup ( ) ;
}
// Getter for the old Combo() API: const char*[]
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 ;
}
// Getter for the old Combo() API: "item1\0item2\0item3\0"
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 ;
}
// Old API, prefer using BeginCombo() nowadays if you can.
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 ;
// Call the getter to obtain the preview string which is a parameter to BeginCombo()
const char * preview_value = NULL ;
if ( * current_item > = 0 & & * current_item < items_count )
items_getter ( data , * current_item , & preview_value ) ;
// The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here.
if ( popup_max_height_in_items ! = - 1 & & ! g . NextWindowData . SizeConstraintCond )
SetNextWindowSizeConstraints ( ImVec2 ( 0 , 0 ) , ImVec2 ( FLT_MAX , CalcMaxPopupHeightFromItemCount ( popup_max_height_in_items ) ) ) ;
if ( ! BeginCombo ( label , preview_value , ImGuiComboFlags_None ) )
return false ;
// Display items
// FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed)
bool value_changed = false ;
for ( int i = 0 ; i < items_count ; i + + )
{
PushID ( ( void * ) ( intptr_t ) i ) ;
const bool item_selected = ( i = = * current_item ) ;
const char * item_text ;
if ( ! items_getter ( data , i , & item_text ) )
item_text = " *Unknown item* " ;
if ( Selectable ( item_text , item_selected ) )
{
value_changed = true ;
* current_item = i ;
}
if ( item_selected )
SetItemDefaultFocus ( ) ;
PopID ( ) ;
}
EndCombo ( ) ;
return value_changed ;
}
// 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 literal holding multiple zero-terminated items "item1\0item2\0"
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 or use ##unique_id.
bool ImGui : : Selectable ( const char * label , bool selected , ImGuiSelectableFlags flags , const ImVec2 & size_arg )
@ -11042,108 +10835,6 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags
return false ;
}
// FIXME: Rename to BeginListBox()
// Helper to calculate the size of a listbox and display a label on the right.
// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty"
bool ImGui : : ListBoxHeader ( const char * label , const ImVec2 & size_arg )
{
ImGuiWindow * window = GetCurrentWindow ( ) ;
if ( window - > SkipItems )
return false ;
const ImGuiStyle & style = GetStyle ( ) ;
const ImGuiID id = GetID ( label ) ;
const ImVec2 label_size = CalcTextSize ( label , NULL , true ) ;
// Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
ImVec2 size = CalcItemSize ( size_arg , CalcItemWidth ( ) , GetTextLineHeightWithSpacing ( ) * 7.4f + style . ItemSpacing . y ) ;
ImVec2 frame_size = ImVec2 ( size . x , ImMax ( size . y , label_size . y ) ) ;
ImRect frame_bb ( window - > DC . CursorPos , window - > DC . CursorPos + frame_size ) ;
ImRect bb ( frame_bb . Min , frame_bb . Max + ImVec2 ( label_size . x > 0.0f ? style . ItemInnerSpacing . x + label_size . x : 0.0f , 0.0f ) ) ;
window - > DC . LastItemRect = bb ; // Forward storage for ListBoxFooter.. dodgy.
BeginGroup ( ) ;
if ( label_size . x > 0 )
RenderText ( ImVec2 ( frame_bb . Max . x + style . ItemInnerSpacing . x , frame_bb . Min . y + style . FramePadding . y ) , label ) ;
BeginChildFrame ( id , frame_bb . GetSize ( ) ) ;
return true ;
}
// FIXME: Rename to BeginListBox()
bool ImGui : : ListBoxHeader ( const char * label , int items_count , int height_in_items )
{
// Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
// We don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size.
// I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
if ( height_in_items < 0 )
height_in_items = ImMin ( items_count , 7 ) ;
float height_in_items_f = height_in_items < items_count ? ( height_in_items + 0.40f ) : ( height_in_items + 0.00f ) ;
// We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild().
ImVec2 size ;
size . x = 0.0f ;
size . y = GetTextLineHeightWithSpacing ( ) * height_in_items_f + GetStyle ( ) . ItemSpacing . y ;
return ListBoxHeader ( label , size ) ;
}
// FIXME: Rename to EndListBox()
void ImGui : : ListBoxFooter ( )
{
ImGuiWindow * parent_window = GetCurrentWindow ( ) - > ParentWindow ;
const ImRect bb = parent_window - > DC . LastItemRect ;
const ImGuiStyle & style = GetStyle ( ) ;
EndChildFrame ( ) ;
// Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
// We call SameLine() to restore DC.CurrentLine* data
SameLine ( ) ;
parent_window - > DC . CursorPos = bb . Min ;
ItemSize ( bb , style . FramePadding . y ) ;
EndGroup ( ) ;
}
bool ImGui : : ListBox ( const char * label , int * current_item , const char * const items [ ] , int items_count , int height_items )
{
const bool value_changed = ListBox ( label , current_item , Items_ArrayGetter , ( void * ) items , items_count , height_items ) ;
return value_changed ;
}
bool ImGui : : ListBox ( const char * label , int * current_item , bool ( * items_getter ) ( void * , int , const char * * ) , void * data , int items_count , int height_in_items )
{
if ( ! ListBoxHeader ( label , items_count , height_in_items ) )
return false ;
// Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper.
ImGuiContext & g = * GImGui ;
bool value_changed = false ;
ImGuiListClipper clipper ( items_count , GetTextLineHeightWithSpacing ( ) ) ; // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.
while ( clipper . Step ( ) )
for ( int i = clipper . DisplayStart ; i < clipper . DisplayEnd ; i + + )
{
const bool item_selected = ( i = = * current_item ) ;
const char * item_text ;
if ( ! items_getter ( data , i , & item_text ) )
item_text = " *Unknown item* " ;
PushID ( i ) ;
if ( Selectable ( item_text , item_selected ) )
{
* current_item = i ;
value_changed = true ;
}
if ( item_selected )
SetItemDefaultFocus ( ) ;
PopID ( ) ;
}
ListBoxFooter ( ) ;
if ( value_changed )
MarkItemEdited ( g . CurrentWindow - > DC . LastItemId ) ;
return value_changed ;
}
bool ImGui : : MenuItem ( const char * label , const char * shortcut , bool selected , bool enabled )
{
ImGuiWindow * window = GetCurrentWindow ( ) ;