Instantly share code, notes, and snippets.
Last active
July 29, 2018 13:28
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save alexsr/fe926a36dc87f796711310b7c8a24d57 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
bool ImGui::BeginBarMenu(const char* label, const bool enabled) { | |
auto window = GetCurrentWindow(); | |
if (window->SkipItems) { | |
return false; | |
} | |
auto& g = *GImGui; | |
const auto& style = g.Style; | |
const auto id = window->GetID(label); | |
const char* str = "##barmenubar"; | |
const char* str_end = nullptr; | |
IM_ASSERT(window->IDStack.Size > 1); | |
const auto seed = window->IDStack.Data[window->IDStack.Size-2]; | |
const ImGuiID barmenubar_id = ImHash(str, str_end ? static_cast<int>(str_end - str) : 0, seed); | |
if (barmenubar_id != window->IDStack.back()) { | |
return false; | |
} | |
const auto label_size = CalcTextSize(label, nullptr, true); | |
auto menu_is_open = IsPopupOpen(id); | |
const auto menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) | |
&& (g.OpenPopupStack.Size > g.CurrentPopupStack.Size | |
&& g.OpenPopupStack[g.CurrentPopupStack.Size].OpenParentId | |
== window->IDStack.back()); | |
const auto 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) | |
const auto pos = window->DC.CursorPos; | |
// 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() | |
const auto popup_pos = ImVec2(pos.x - style.FramePadding.x, | |
pos.y - style.FramePadding.y + window->DC.MenuBarOffset.y + window->CalcFontSize() + | |
GImGui->Style.FramePadding.y * 2.0f); | |
window->DC.CursorPos.x += static_cast<float>(static_cast<int>(style.ItemSpacing.x * 0.5f)); | |
PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); | |
const auto w = label_size.x; | |
const auto 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 += static_cast<float>(static_cast<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(). | |
const auto hovered = enabled && ItemHoverable(window->DC.LastItemRect, id); | |
if (menuset_is_open) { | |
g.NavWindow = backed_nav_window; | |
} | |
bool want_open = false; | |
bool want_close = false; | |
// 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)) { } | |
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); | |
const auto flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | | |
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | | |
(window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu) | |
? ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_ChildWindow | |
: ImGuiWindowFlags_ChildMenu); | |
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; | |
} | |
bool ImGui::BeginBarMenuBar() { | |
Spacing(); | |
const auto window = GetCurrentWindow(); | |
if (window->SkipItems) { | |
return false; | |
} | |
window->DC.LayoutType = ImGuiLayoutType_Horizontal; | |
window->DC.NavLayerCurrent++; | |
window->DC.NavLayerCurrentMask <<= 1; | |
BeginGroup(); // Backup position on layer 0 | |
PushID("##barmenubar"); | |
return true; | |
} | |
void ImGui::EndBarMenuBar() { | |
auto window = GetCurrentWindow(); | |
if (window->SkipItems) { | |
return; | |
} | |
PopID(); | |
EndGroup(); // Restore position on layer 0 | |
window->DC.LayoutType = ImGuiLayoutType_Vertical; | |
window->DC.NavLayerCurrent--; | |
window->DC.NavLayerCurrentMask >>= 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment