mirror of
				https://github.com/Drezil/imgui.git
				synced 2025-11-03 22:51:06 +01:00 
			
		
		
		
	Refactor: Moved Menu functions from imgui.cpp to imgui_widgets.cpp (#2036)
This commit is contained in:
		
							
								
								
									
										349
									
								
								imgui.cpp
									
									
									
									
									
								
							
							
						
						
									
										349
									
								
								imgui.cpp
									
									
									
									
									
								
							@@ -1996,51 +1996,6 @@ void ImGuiTextBuffer::appendf(const char* fmt, ...)
 | 
				
			|||||||
    va_end(args);
 | 
					    va_end(args);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//-----------------------------------------------------------------------------
 | 
					 | 
				
			||||||
// ImGuiSimpleColumns (internal use only)
 | 
					 | 
				
			||||||
//-----------------------------------------------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ImGuiMenuColumns::ImGuiMenuColumns()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    Count = 0;
 | 
					 | 
				
			||||||
    Spacing = Width = NextWidth = 0.0f;
 | 
					 | 
				
			||||||
    memset(Pos, 0, sizeof(Pos));
 | 
					 | 
				
			||||||
    memset(NextWidths, 0, sizeof(NextWidths));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ImGuiMenuColumns::Update(int count, float spacing, bool clear)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
 | 
					 | 
				
			||||||
    Count = count;
 | 
					 | 
				
			||||||
    Width = NextWidth = 0.0f;
 | 
					 | 
				
			||||||
    Spacing = spacing;
 | 
					 | 
				
			||||||
    if (clear) memset(NextWidths, 0, sizeof(NextWidths));
 | 
					 | 
				
			||||||
    for (int i = 0; i < Count; i++)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        if (i > 0 && NextWidths[i] > 0.0f)
 | 
					 | 
				
			||||||
            Width += Spacing;
 | 
					 | 
				
			||||||
        Pos[i] = (float)(int)Width;
 | 
					 | 
				
			||||||
        Width += NextWidths[i];
 | 
					 | 
				
			||||||
        NextWidths[i] = 0.0f;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    NextWidth = 0.0f;
 | 
					 | 
				
			||||||
    NextWidths[0] = ImMax(NextWidths[0], w0);
 | 
					 | 
				
			||||||
    NextWidths[1] = ImMax(NextWidths[1], w1);
 | 
					 | 
				
			||||||
    NextWidths[2] = ImMax(NextWidths[2], w2);
 | 
					 | 
				
			||||||
    for (int i = 0; i < 3; i++)
 | 
					 | 
				
			||||||
        NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
 | 
					 | 
				
			||||||
    return ImMax(Width, NextWidth);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
float ImGuiMenuColumns::CalcExtraSpace(float avail_w)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    return ImMax(0.0f, avail_w - Width);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//-----------------------------------------------------------------------------
 | 
					//-----------------------------------------------------------------------------
 | 
				
			||||||
// ImGuiListClipper
 | 
					// ImGuiListClipper
 | 
				
			||||||
//-----------------------------------------------------------------------------
 | 
					//-----------------------------------------------------------------------------
 | 
				
			||||||
@@ -11848,310 +11803,6 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags
 | 
				
			|||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    ImGuiWindow* window = GetCurrentWindow();
 | 
					 | 
				
			||||||
    if (window->SkipItems)
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ImGuiContext& g = *GImGui;
 | 
					 | 
				
			||||||
    ImGuiStyle& style = g.Style;
 | 
					 | 
				
			||||||
    ImVec2 pos = window->DC.CursorPos;
 | 
					 | 
				
			||||||
    ImVec2 label_size = CalcTextSize(label, NULL, true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ImGuiSelectableFlags flags = ImGuiSelectableFlags_PressedOnRelease | (enabled ? 0 : ImGuiSelectableFlags_Disabled);
 | 
					 | 
				
			||||||
    bool pressed;
 | 
					 | 
				
			||||||
    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
 | 
					 | 
				
			||||||
        // Note that in this situation we render neither the shortcut neither the selected tick mark
 | 
					 | 
				
			||||||
        float w = label_size.x;
 | 
					 | 
				
			||||||
        window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
 | 
					 | 
				
			||||||
        PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
 | 
					 | 
				
			||||||
        pressed = Selectable(label, false, flags, ImVec2(w, 0.0f));
 | 
					 | 
				
			||||||
        PopStyleVar();
 | 
					 | 
				
			||||||
        window->DC.CursorPos.x += (float)(int)(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().
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
 | 
					 | 
				
			||||||
        float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
 | 
					 | 
				
			||||||
        float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
 | 
					 | 
				
			||||||
        pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f));
 | 
					 | 
				
			||||||
        if (shortcut_size.x > 0.0f)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
 | 
					 | 
				
			||||||
            RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
 | 
					 | 
				
			||||||
            PopStyleColor();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (selected)
 | 
					 | 
				
			||||||
            RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize  * 0.866f);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return pressed;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        if (p_selected)
 | 
					 | 
				
			||||||
            *p_selected = !*p_selected;
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
 | 
					 | 
				
			||||||
bool ImGui::BeginMainMenuBar()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    ImGuiContext& g = *GImGui;
 | 
					 | 
				
			||||||
    g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f));
 | 
					 | 
				
			||||||
    SetNextWindowPos(g.Viewports[0]->Pos);
 | 
					 | 
				
			||||||
    SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y));
 | 
					 | 
				
			||||||
    PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
 | 
					 | 
				
			||||||
    PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
 | 
					 | 
				
			||||||
    ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;
 | 
					 | 
				
			||||||
    bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar();
 | 
					 | 
				
			||||||
    PopStyleVar(2);
 | 
					 | 
				
			||||||
    g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);
 | 
					 | 
				
			||||||
    if (!is_open)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        End();
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ImGui::EndMainMenuBar()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    EndMenuBar();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
 | 
					 | 
				
			||||||
    ImGuiContext& g = *GImGui;
 | 
					 | 
				
			||||||
    if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0)
 | 
					 | 
				
			||||||
        FocusFrontMostActiveWindowIgnoringOne(g.NavWindow);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    End();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool ImGui::BeginMenuBar()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    ImGuiWindow* window = GetCurrentWindow();
 | 
					 | 
				
			||||||
    if (window->SkipItems)
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    if (!(window->Flags & ImGuiWindowFlags_MenuBar))
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    IM_ASSERT(!window->DC.MenuBarAppending);
 | 
					 | 
				
			||||||
    BeginGroup(); // Backup position on layer 0
 | 
					 | 
				
			||||||
    PushID("##menubar");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect.
 | 
					 | 
				
			||||||
    // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy.
 | 
					 | 
				
			||||||
    ImRect bar_rect = window->MenuBarRect();
 | 
					 | 
				
			||||||
    ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f));
 | 
					 | 
				
			||||||
    clip_rect.ClipWith(window->OuterRectClipped);
 | 
					 | 
				
			||||||
    PushClipRect(clip_rect.Min, clip_rect.Max, false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y);
 | 
					 | 
				
			||||||
    window->DC.LayoutType = ImGuiLayoutType_Horizontal;
 | 
					 | 
				
			||||||
    window->DC.NavLayerCurrent++;
 | 
					 | 
				
			||||||
    window->DC.NavLayerCurrentMask <<= 1;
 | 
					 | 
				
			||||||
    window->DC.MenuBarAppending = true;
 | 
					 | 
				
			||||||
    AlignTextToFramePadding();
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ImGui::EndMenuBar()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    ImGuiWindow* window = GetCurrentWindow();
 | 
					 | 
				
			||||||
    if (window->SkipItems)
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    ImGuiContext& g = *GImGui;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.
 | 
					 | 
				
			||||||
    if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        ImGuiWindow* nav_earliest_child = g.NavWindow;
 | 
					 | 
				
			||||||
        while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))
 | 
					 | 
				
			||||||
            nav_earliest_child = nav_earliest_child->ParentWindow;
 | 
					 | 
				
			||||||
        if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // To do so we claim focus back, restore NavId and then process the movement request for yet another frame.
 | 
					 | 
				
			||||||
            // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost)
 | 
					 | 
				
			||||||
            IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check
 | 
					 | 
				
			||||||
            FocusWindow(window);
 | 
					 | 
				
			||||||
            SetNavIDWithRectRel(window->NavLastIds[1], 1, window->NavRectRel[1]);
 | 
					 | 
				
			||||||
            g.NavLayer = 1;
 | 
					 | 
				
			||||||
            g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection.
 | 
					 | 
				
			||||||
            g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
 | 
					 | 
				
			||||||
            NavMoveRequestCancel();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
 | 
					 | 
				
			||||||
    IM_ASSERT(window->DC.MenuBarAppending);
 | 
					 | 
				
			||||||
    PopClipRect();
 | 
					 | 
				
			||||||
    PopID();
 | 
					 | 
				
			||||||
    window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.
 | 
					 | 
				
			||||||
    window->DC.GroupStack.back().AdvanceCursor = false;
 | 
					 | 
				
			||||||
    EndGroup(); // Restore position on layer 0
 | 
					 | 
				
			||||||
    window->DC.LayoutType = ImGuiLayoutType_Vertical;
 | 
					 | 
				
			||||||
    window->DC.NavLayerCurrent--;
 | 
					 | 
				
			||||||
    window->DC.NavLayerCurrentMask >>= 1;
 | 
					 | 
				
			||||||
    window->DC.MenuBarAppending = false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool ImGui::BeginMenu(const char* label, bool enabled)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    ImGuiWindow* window = GetCurrentWindow();
 | 
					 | 
				
			||||||
    if (window->SkipItems)
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ImGuiContext& g = *GImGui;
 | 
					 | 
				
			||||||
    const ImGuiStyle& style = g.Style;
 | 
					 | 
				
			||||||
    const ImGuiID id = window->GetID(label);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ImVec2 label_size = CalcTextSize(label, NULL, true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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].OpenParentId == window->IDStack.back());
 | 
					 | 
				
			||||||
    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)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestWindowPosForPopup).
 | 
					 | 
				
			||||||
    ImVec2 popup_pos, pos = window->DC.CursorPos;
 | 
					 | 
				
			||||||
    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        // Menu inside an horizontal menu bar
 | 
					 | 
				
			||||||
        // Selectable extend their highlight by half ItemSpacing in each direction.
 | 
					 | 
				
			||||||
        // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
 | 
					 | 
				
			||||||
        popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
 | 
					 | 
				
			||||||
        window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
 | 
					 | 
				
			||||||
        PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
 | 
					 | 
				
			||||||
        float w = label_size.x;
 | 
					 | 
				
			||||||
        pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
 | 
					 | 
				
			||||||
        PopStyleVar();
 | 
					 | 
				
			||||||
        window->DC.CursorPos.x += (float)(int)(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().
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        // Menu inside a menu
 | 
					 | 
				
			||||||
        popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
 | 
					 | 
				
			||||||
        float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
 | 
					 | 
				
			||||||
        float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
 | 
					 | 
				
			||||||
        pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
 | 
					 | 
				
			||||||
        if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
 | 
					 | 
				
			||||||
        RenderArrow(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right);
 | 
					 | 
				
			||||||
        if (!enabled) PopStyleColor();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id);
 | 
					 | 
				
			||||||
    if (menuset_is_open)
 | 
					 | 
				
			||||||
        g.NavWindow = backed_nav_window;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool want_open = false, want_close = false;
 | 
					 | 
				
			||||||
    if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
 | 
					 | 
				
			||||||
        bool moving_within_opened_triangle = false;
 | 
					 | 
				
			||||||
        if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar))
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ImRect next_window_rect = next_window->Rect();
 | 
					 | 
				
			||||||
                ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
 | 
					 | 
				
			||||||
                ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
 | 
					 | 
				
			||||||
                ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
 | 
					 | 
				
			||||||
                float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
 | 
					 | 
				
			||||||
                ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f;   // to avoid numerical issues
 | 
					 | 
				
			||||||
                tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f);            // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
 | 
					 | 
				
			||||||
                tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
 | 
					 | 
				
			||||||
                moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
 | 
					 | 
				
			||||||
                //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
 | 
					 | 
				
			||||||
        want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (g.NavActivateId == id)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            want_close = menu_is_open;
 | 
					 | 
				
			||||||
            want_open = !menu_is_open;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            want_open = true;
 | 
					 | 
				
			||||||
            NavMoveRequestCancel();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        // Menu bar
 | 
					 | 
				
			||||||
        if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            want_close = true;
 | 
					 | 
				
			||||||
            want_open = menu_is_open = false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            want_open = true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            want_open = true;
 | 
					 | 
				
			||||||
            NavMoveRequestCancel();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
 | 
					 | 
				
			||||||
        want_close = true;
 | 
					 | 
				
			||||||
    if (want_close && IsPopupOpen(id))
 | 
					 | 
				
			||||||
        ClosePopupToLevel(g.CurrentPopupStack.Size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
 | 
					 | 
				
			||||||
        OpenPopup(label);
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    menu_is_open |= want_open;
 | 
					 | 
				
			||||||
    if (want_open)
 | 
					 | 
				
			||||||
        OpenPopup(label);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (menu_is_open)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        // 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)
 | 
					 | 
				
			||||||
        SetNextWindowPos(popup_pos, ImGuiCond_Always);
 | 
					 | 
				
			||||||
        ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
 | 
					 | 
				
			||||||
        if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
 | 
					 | 
				
			||||||
            flags |= ImGuiWindowFlags_ChildWindow;
 | 
					 | 
				
			||||||
        menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return menu_is_open;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ImGui::EndMenu()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    // Nav: When a left move request _within our child menu_ failed, close the menu.
 | 
					 | 
				
			||||||
    // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs.
 | 
					 | 
				
			||||||
    // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction.
 | 
					 | 
				
			||||||
    ImGuiContext& g = *GImGui;
 | 
					 | 
				
			||||||
    ImGuiWindow* window = g.CurrentWindow;
 | 
					 | 
				
			||||||
    if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        ClosePopupToLevel(g.OpenPopupStack.Size - 1);
 | 
					 | 
				
			||||||
        NavMoveRequestCancel();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    EndPopup();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
 | 
					// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
 | 
				
			||||||
void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
 | 
					void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1372,6 +1372,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
//-------------------------------------------------------------------------
 | 
					//-------------------------------------------------------------------------
 | 
				
			||||||
// WIDGETS: Menus
 | 
					// WIDGETS: Menus
 | 
				
			||||||
 | 
					// - ImGuiMenuColumns
 | 
				
			||||||
// - BeginMainMenuBar()
 | 
					// - BeginMainMenuBar()
 | 
				
			||||||
// - EndMainMenuBar()
 | 
					// - EndMainMenuBar()
 | 
				
			||||||
// - BeginMenuBar()
 | 
					// - BeginMenuBar()
 | 
				
			||||||
@@ -1381,3 +1382,348 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v
 | 
				
			|||||||
// - MenuItem()
 | 
					// - MenuItem()
 | 
				
			||||||
//-------------------------------------------------------------------------
 | 
					//-------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Helpers for internal use
 | 
				
			||||||
 | 
					ImGuiMenuColumns::ImGuiMenuColumns()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Count = 0;
 | 
				
			||||||
 | 
					    Spacing = Width = NextWidth = 0.0f;
 | 
				
			||||||
 | 
					    memset(Pos, 0, sizeof(Pos));
 | 
				
			||||||
 | 
					    memset(NextWidths, 0, sizeof(NextWidths));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ImGuiMenuColumns::Update(int count, float spacing, bool clear)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    IM_ASSERT(Count <= IM_ARRAYSIZE(Pos));
 | 
				
			||||||
 | 
					    Count = count;
 | 
				
			||||||
 | 
					    Width = NextWidth = 0.0f;
 | 
				
			||||||
 | 
					    Spacing = spacing;
 | 
				
			||||||
 | 
					    if (clear) memset(NextWidths, 0, sizeof(NextWidths));
 | 
				
			||||||
 | 
					    for (int i = 0; i < Count; i++)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (i > 0 && NextWidths[i] > 0.0f)
 | 
				
			||||||
 | 
					            Width += Spacing;
 | 
				
			||||||
 | 
					        Pos[i] = (float)(int)Width;
 | 
				
			||||||
 | 
					        Width += NextWidths[i];
 | 
				
			||||||
 | 
					        NextWidths[i] = 0.0f;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    NextWidth = 0.0f;
 | 
				
			||||||
 | 
					    NextWidths[0] = ImMax(NextWidths[0], w0);
 | 
				
			||||||
 | 
					    NextWidths[1] = ImMax(NextWidths[1], w1);
 | 
				
			||||||
 | 
					    NextWidths[2] = ImMax(NextWidths[2], w2);
 | 
				
			||||||
 | 
					    for (int i = 0; i < 3; i++)
 | 
				
			||||||
 | 
					        NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f);
 | 
				
			||||||
 | 
					    return ImMax(Width, NextWidth);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float ImGuiMenuColumns::CalcExtraSpace(float avail_w)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return ImMax(0.0f, avail_w - Width);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
 | 
				
			||||||
 | 
					bool ImGui::BeginMainMenuBar()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ImGuiContext& g = *GImGui;
 | 
				
			||||||
 | 
					    g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f));
 | 
				
			||||||
 | 
					    SetNextWindowPos(g.Viewports[0]->Pos);
 | 
				
			||||||
 | 
					    SetNextWindowSize(ImVec2(g.Viewports[0]->Size.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y));
 | 
				
			||||||
 | 
					    PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
 | 
				
			||||||
 | 
					    PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
 | 
				
			||||||
 | 
					    ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;
 | 
				
			||||||
 | 
					    bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar();
 | 
				
			||||||
 | 
					    PopStyleVar(2);
 | 
				
			||||||
 | 
					    g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);
 | 
				
			||||||
 | 
					    if (!is_open)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        End();
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ImGui::EndMainMenuBar()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EndMenuBar();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
 | 
				
			||||||
 | 
					    ImGuiContext& g = *GImGui;
 | 
				
			||||||
 | 
					    if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0)
 | 
				
			||||||
 | 
					        FocusFrontMostActiveWindowIgnoringOne(g.NavWindow);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    End();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool ImGui::BeginMenuBar()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ImGuiWindow* window = GetCurrentWindow();
 | 
				
			||||||
 | 
					    if (window->SkipItems)
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    if (!(window->Flags & ImGuiWindowFlags_MenuBar))
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IM_ASSERT(!window->DC.MenuBarAppending);
 | 
				
			||||||
 | 
					    BeginGroup(); // Backup position on layer 0
 | 
				
			||||||
 | 
					    PushID("##menubar");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect.
 | 
				
			||||||
 | 
					    // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy.
 | 
				
			||||||
 | 
					    ImRect bar_rect = window->MenuBarRect();
 | 
				
			||||||
 | 
					    ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f));
 | 
				
			||||||
 | 
					    clip_rect.ClipWith(window->OuterRectClipped);
 | 
				
			||||||
 | 
					    PushClipRect(clip_rect.Min, clip_rect.Max, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y);
 | 
				
			||||||
 | 
					    window->DC.LayoutType = ImGuiLayoutType_Horizontal;
 | 
				
			||||||
 | 
					    window->DC.NavLayerCurrent++;
 | 
				
			||||||
 | 
					    window->DC.NavLayerCurrentMask <<= 1;
 | 
				
			||||||
 | 
					    window->DC.MenuBarAppending = true;
 | 
				
			||||||
 | 
					    AlignTextToFramePadding();
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ImGui::EndMenuBar()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ImGuiWindow* window = GetCurrentWindow();
 | 
				
			||||||
 | 
					    if (window->SkipItems)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    ImGuiContext& g = *GImGui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.
 | 
				
			||||||
 | 
					    if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ImGuiWindow* nav_earliest_child = g.NavWindow;
 | 
				
			||||||
 | 
					        while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))
 | 
				
			||||||
 | 
					            nav_earliest_child = nav_earliest_child->ParentWindow;
 | 
				
			||||||
 | 
					        if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // To do so we claim focus back, restore NavId and then process the movement request for yet another frame.
 | 
				
			||||||
 | 
					            // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost)
 | 
				
			||||||
 | 
					            IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check
 | 
				
			||||||
 | 
					            FocusWindow(window);
 | 
				
			||||||
 | 
					            SetNavIDWithRectRel(window->NavLastIds[1], 1, window->NavRectRel[1]);
 | 
				
			||||||
 | 
					            g.NavLayer = 1;
 | 
				
			||||||
 | 
					            g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection.
 | 
				
			||||||
 | 
					            g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
 | 
				
			||||||
 | 
					            NavMoveRequestCancel();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
 | 
				
			||||||
 | 
					    IM_ASSERT(window->DC.MenuBarAppending);
 | 
				
			||||||
 | 
					    PopClipRect();
 | 
				
			||||||
 | 
					    PopID();
 | 
				
			||||||
 | 
					    window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.
 | 
				
			||||||
 | 
					    window->DC.GroupStack.back().AdvanceCursor = false;
 | 
				
			||||||
 | 
					    EndGroup(); // Restore position on layer 0
 | 
				
			||||||
 | 
					    window->DC.LayoutType = ImGuiLayoutType_Vertical;
 | 
				
			||||||
 | 
					    window->DC.NavLayerCurrent--;
 | 
				
			||||||
 | 
					    window->DC.NavLayerCurrentMask >>= 1;
 | 
				
			||||||
 | 
					    window->DC.MenuBarAppending = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool ImGui::BeginMenu(const char* label, bool enabled)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ImGuiWindow* window = GetCurrentWindow();
 | 
				
			||||||
 | 
					    if (window->SkipItems)
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ImGuiContext& g = *GImGui;
 | 
				
			||||||
 | 
					    const ImGuiStyle& style = g.Style;
 | 
				
			||||||
 | 
					    const ImGuiID id = window->GetID(label);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ImVec2 label_size = CalcTextSize(label, NULL, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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].OpenParentId == window->IDStack.back());
 | 
				
			||||||
 | 
					    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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestWindowPosForPopup).
 | 
				
			||||||
 | 
					    ImVec2 popup_pos, pos = window->DC.CursorPos;
 | 
				
			||||||
 | 
					    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Menu inside an horizontal menu bar
 | 
				
			||||||
 | 
					        // Selectable extend their highlight by half ItemSpacing in each direction.
 | 
				
			||||||
 | 
					        // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
 | 
				
			||||||
 | 
					        popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
 | 
				
			||||||
 | 
					        window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
 | 
				
			||||||
 | 
					        PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
 | 
				
			||||||
 | 
					        float w = label_size.x;
 | 
				
			||||||
 | 
					        pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
 | 
				
			||||||
 | 
					        PopStyleVar();
 | 
				
			||||||
 | 
					        window->DC.CursorPos.x += (float)(int)(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().
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Menu inside a menu
 | 
				
			||||||
 | 
					        popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
 | 
				
			||||||
 | 
					        float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
 | 
				
			||||||
 | 
					        float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
 | 
				
			||||||
 | 
					        pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
 | 
				
			||||||
 | 
					        if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
 | 
				
			||||||
 | 
					        RenderArrow(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right);
 | 
				
			||||||
 | 
					        if (!enabled) PopStyleColor();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id);
 | 
				
			||||||
 | 
					    if (menuset_is_open)
 | 
				
			||||||
 | 
					        g.NavWindow = backed_nav_window;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool want_open = false, want_close = false;
 | 
				
			||||||
 | 
					    if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
 | 
				
			||||||
 | 
					        bool moving_within_opened_triangle = false;
 | 
				
			||||||
 | 
					        if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ImRect next_window_rect = next_window->Rect();
 | 
				
			||||||
 | 
					                ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
 | 
				
			||||||
 | 
					                ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
 | 
				
			||||||
 | 
					                ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
 | 
				
			||||||
 | 
					                float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
 | 
				
			||||||
 | 
					                ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f;   // to avoid numerical issues
 | 
				
			||||||
 | 
					                tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f);            // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
 | 
				
			||||||
 | 
					                tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
 | 
				
			||||||
 | 
					                moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
 | 
				
			||||||
 | 
					                //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
 | 
				
			||||||
 | 
					        want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (g.NavActivateId == id)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            want_close = menu_is_open;
 | 
				
			||||||
 | 
					            want_open = !menu_is_open;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            want_open = true;
 | 
				
			||||||
 | 
					            NavMoveRequestCancel();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Menu bar
 | 
				
			||||||
 | 
					        if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            want_close = true;
 | 
				
			||||||
 | 
					            want_open = menu_is_open = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            want_open = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            want_open = true;
 | 
				
			||||||
 | 
					            NavMoveRequestCancel();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
 | 
				
			||||||
 | 
					        want_close = true;
 | 
				
			||||||
 | 
					    if (want_close && IsPopupOpen(id))
 | 
				
			||||||
 | 
					        ClosePopupToLevel(g.CurrentPopupStack.Size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
 | 
				
			||||||
 | 
					        OpenPopup(label);
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    menu_is_open |= want_open;
 | 
				
			||||||
 | 
					    if (want_open)
 | 
				
			||||||
 | 
					        OpenPopup(label);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (menu_is_open)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // 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)
 | 
				
			||||||
 | 
					        SetNextWindowPos(popup_pos, ImGuiCond_Always);
 | 
				
			||||||
 | 
					        ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
 | 
				
			||||||
 | 
					        if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
 | 
				
			||||||
 | 
					            flags |= ImGuiWindowFlags_ChildWindow;
 | 
				
			||||||
 | 
					        menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return menu_is_open;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ImGui::EndMenu()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // Nav: When a left move request _within our child menu_ failed, close the menu.
 | 
				
			||||||
 | 
					    // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs.
 | 
				
			||||||
 | 
					    // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction.
 | 
				
			||||||
 | 
					    ImGuiContext& g = *GImGui;
 | 
				
			||||||
 | 
					    ImGuiWindow* window = g.CurrentWindow;
 | 
				
			||||||
 | 
					    if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ClosePopupToLevel(g.OpenPopupStack.Size - 1);
 | 
				
			||||||
 | 
					        NavMoveRequestCancel();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EndPopup();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ImGuiWindow* window = GetCurrentWindow();
 | 
				
			||||||
 | 
					    if (window->SkipItems)
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ImGuiContext& g = *GImGui;
 | 
				
			||||||
 | 
					    ImGuiStyle& style = g.Style;
 | 
				
			||||||
 | 
					    ImVec2 pos = window->DC.CursorPos;
 | 
				
			||||||
 | 
					    ImVec2 label_size = CalcTextSize(label, NULL, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ImGuiSelectableFlags flags = ImGuiSelectableFlags_PressedOnRelease | (enabled ? 0 : ImGuiSelectableFlags_Disabled);
 | 
				
			||||||
 | 
					    bool pressed;
 | 
				
			||||||
 | 
					    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
 | 
				
			||||||
 | 
					        // Note that in this situation we render neither the shortcut neither the selected tick mark
 | 
				
			||||||
 | 
					        float w = label_size.x;
 | 
				
			||||||
 | 
					        window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
 | 
				
			||||||
 | 
					        PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
 | 
				
			||||||
 | 
					        pressed = Selectable(label, false, flags, ImVec2(w, 0.0f));
 | 
				
			||||||
 | 
					        PopStyleVar();
 | 
				
			||||||
 | 
					        window->DC.CursorPos.x += (float)(int)(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().
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
 | 
				
			||||||
 | 
					        float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
 | 
				
			||||||
 | 
					        float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
 | 
				
			||||||
 | 
					        pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f));
 | 
				
			||||||
 | 
					        if (shortcut_size.x > 0.0f)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
 | 
				
			||||||
 | 
					            RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
 | 
				
			||||||
 | 
					            PopStyleColor();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (selected)
 | 
				
			||||||
 | 
					            RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize  * 0.866f);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return pressed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (p_selected)
 | 
				
			||||||
 | 
					            *p_selected = !*p_selected;
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user