@ -6868,6 +6868,23 @@ void ImGui::EndMainMenuBar()
End ( ) ;
End ( ) ;
}
}
static bool IsRootOfOpenMenuSet ( )
{
ImGuiContext & g = * GImGui ;
ImGuiWindow * window = g . CurrentWindow ;
if ( ( g . OpenPopupStack . Size < = g . BeginPopupStack . Size ) | | ( window - > Flags & ImGuiWindowFlags_ChildMenu ) )
return false ;
// Initially we used 'OpenParentId' to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) based on parent ID.
// This would however prevent the use of e.g. PuhsID() user code submitting menus.
// Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag,
// making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects.
// Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup
// doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first chilld menu.
const ImGuiPopupData * upper_popup = & g . OpenPopupStack [ g . BeginPopupStack . Size ] ;
return ( upper_popup & & /*upper_popup->OpenParentId == window->IDStack.back() &&*/ upper_popup - > Window & & ( upper_popup - > Window - > Flags & ImGuiWindowFlags_ChildMenu ) ) ;
}
bool ImGui : : BeginMenuEx ( const char * label , const char * icon , bool enabled )
bool ImGui : : BeginMenuEx ( const char * label , const char * icon , bool enabled )
{
{
ImGuiWindow * window = GetCurrentWindow ( ) ;
ImGuiWindow * window = GetCurrentWindow ( ) ;
@ -6880,8 +6897,9 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
bool menu_is_open = IsPopupOpen ( id , ImGuiPopupFlags_None ) ;
bool menu_is_open = IsPopupOpen ( id , ImGuiPopupFlags_None ) ;
// Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)
// Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)
// The first menu in a hierarchy isn't so hovering doesn't get accross (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation.
ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus ;
ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus ;
if ( window - > Flags & ( ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu ) )
if ( window - > Flags & ImGuiWindowFlags_ChildMenu )
flags | = ImGuiWindowFlags_ChildWindow ;
flags | = ImGuiWindowFlags_ChildWindow ;
// If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin().
// If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin().
@ -6900,11 +6918,12 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
g . MenusIdSubmittedThisFrame . push_back ( id ) ;
g . MenusIdSubmittedThisFrame . push_back ( id ) ;
ImVec2 label_size = CalcTextSize ( label , NULL , true ) ;
ImVec2 label_size = CalcTextSize ( label , NULL , true ) ;
bool pressed ;
bool menuset_is_open = ( window - > Flags & ImGuiWindowFlags_MenuBar ) & & ( g . OpenPopupStack . Size > g . BeginPopupStack . Size & & g . OpenPopupStack [ g . BeginPopupStack . Size ] . OpenParentId = = window - > IDStack . back ( ) ) ;
// Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window)
const bool menuset_is_open = IsRootOfOpenMenuSet ( ) ;
ImGuiWindow * backed_nav_window = g . NavWindow ;
ImGuiWindow * backed_nav_window = g . NavWindow ;
if ( menuset_is_open )
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)
g . NavWindow = window ;
// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
// However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
// However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
@ -6914,6 +6933,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
if ( ! enabled )
if ( ! enabled )
BeginDisabled ( ) ;
BeginDisabled ( ) ;
const ImGuiMenuColumns * offsets = & window - > DC . MenuColumns ;
const ImGuiMenuColumns * offsets = & window - > DC . MenuColumns ;
bool pressed ;
if ( window - > DC . LayoutType = = ImGuiLayoutType_Horizontal )
if ( window - > DC . LayoutType = = ImGuiLayoutType_Horizontal )
{
{
// Menu inside an horizontal menu bar
// Menu inside an horizontal menu bar
@ -7073,13 +7093,19 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
ImVec2 pos = window - > DC . CursorPos ;
ImVec2 pos = window - > DC . CursorPos ;
ImVec2 label_size = CalcTextSize ( label , NULL , true ) ;
ImVec2 label_size = CalcTextSize ( label , NULL , true ) ;
const bool menuset_is_open = IsRootOfOpenMenuSet ( ) ;
ImGuiWindow * backed_nav_window = g . NavWindow ;
if ( menuset_is_open )
g . NavWindow = window ;
// We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),
// We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),
// but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.
// but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.
bool pressed ;
bool pressed ;
PushID ( label ) ;
PushID ( label ) ;
if ( ! enabled )
if ( ! enabled )
BeginDisabled ( ) ;
BeginDisabled ( ) ;
const ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover ;
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover ;
const ImGuiMenuColumns * offsets = & window - > DC . MenuColumns ;
const ImGuiMenuColumns * offsets = & window - > DC . MenuColumns ;
if ( window - > DC . LayoutType = = ImGuiLayoutType_Horizontal )
if ( window - > DC . LayoutType = = ImGuiLayoutType_Horizontal )
{
{
@ -7089,7 +7115,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
window - > DC . CursorPos . x + = IM_FLOOR ( style . ItemSpacing . x * 0.5f ) ;
window - > DC . CursorPos . x + = IM_FLOOR ( style . ItemSpacing . x * 0.5f ) ;
ImVec2 text_pos ( window - > DC . CursorPos . x + offsets - > OffsetLabel , window - > DC . CursorPos . y + window - > DC . CurrLineTextBaseOffset ) ;
ImVec2 text_pos ( window - > DC . CursorPos . x + offsets - > OffsetLabel , window - > DC . CursorPos . y + window - > DC . CurrLineTextBaseOffset ) ;
PushStyleVar ( ImGuiStyleVar_ItemSpacing , ImVec2 ( style . ItemSpacing . x * 2.0f , style . ItemSpacing . y ) ) ;
PushStyleVar ( ImGuiStyleVar_ItemSpacing , ImVec2 ( style . ItemSpacing . x * 2.0f , style . ItemSpacing . y ) ) ;
pressed = Selectable ( " " , selected , flags, ImVec2 ( w , 0.0f ) ) ;
pressed = Selectable ( " " , selected , selectable_ flags, ImVec2 ( w , 0.0f ) ) ;
PopStyleVar ( ) ;
PopStyleVar ( ) ;
RenderText ( text_pos , label ) ;
RenderText ( text_pos , label ) ;
window - > DC . CursorPos . x + = IM_FLOOR ( style . ItemSpacing . x * ( - 1.0f + 0.5f ) ) ; // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
window - > DC . CursorPos . x + = IM_FLOOR ( style . ItemSpacing . x * ( - 1.0f + 0.5f ) ) ; // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
@ -7104,7 +7130,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
float checkmark_w = IM_FLOOR ( g . FontSize * 1.20f ) ;
float checkmark_w = IM_FLOOR ( g . FontSize * 1.20f ) ;
float min_w = window - > DC . MenuColumns . DeclColumns ( icon_w , label_size . x , shortcut_w , checkmark_w ) ; // Feedback for next frame
float min_w = window - > DC . MenuColumns . DeclColumns ( icon_w , label_size . x , shortcut_w , checkmark_w ) ; // Feedback for next frame
float stretch_w = ImMax ( 0.0f , GetContentRegionAvail ( ) . x - min_w ) ;
float stretch_w = ImMax ( 0.0f , GetContentRegionAvail ( ) . x - min_w ) ;
pressed = Selectable ( " " , false , flags | ImGuiSelectableFlags_SpanAvailWidth , ImVec2 ( min_w , 0.0f ) ) ;
pressed = Selectable ( " " , false , selectable_ flags | ImGuiSelectableFlags_SpanAvailWidth , ImVec2 ( min_w , 0.0f ) ) ;
RenderText ( pos + ImVec2 ( offsets - > OffsetLabel , 0.0f ) , label ) ;
RenderText ( pos + ImVec2 ( offsets - > OffsetLabel , 0.0f ) , label ) ;
if ( icon_w > 0.0f )
if ( icon_w > 0.0f )
RenderText ( pos + ImVec2 ( offsets - > OffsetIcon , 0.0f ) , icon ) ;
RenderText ( pos + ImVec2 ( offsets - > OffsetIcon , 0.0f ) , icon ) ;
@ -7121,6 +7147,8 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
if ( ! enabled )
if ( ! enabled )
EndDisabled ( ) ;
EndDisabled ( ) ;
PopID ( ) ;
PopID ( ) ;
if ( menuset_is_open )
g . NavWindow = backed_nav_window ;
return pressed ;
return pressed ;
}
}