Skip to content

Instantly share code, notes, and snippets.

@alexsr
Last active July 29, 2018 13:28
Show Gist options
  • Save alexsr/fe926a36dc87f796711310b7c8a24d57 to your computer and use it in GitHub Desktop.
Save alexsr/fe926a36dc87f796711310b7c8a24d57 to your computer and use it in GitHub Desktop.
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