diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 63ccfa8a6..17c798819 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -69,6 +69,8 @@ Other changes: but in the meanwhile it works better now. (#2701, #8138, #1018) - Windows, Style: Fixed small rendering issues with menu bar, resize grip and scrollbar when using thick border sizes. (#8267, #7887) +- Windows: Fixed IsItemXXXX() functions not working on append-version of EndChild(). (#8350) + Also made some of the fields accessible after BeginChild() to match Begin() logic. - ColorEdit, ColorPicker: Fixed alpha preview broken in 1.91.7. (#8336, #8241). [@PathogenDavid] - Tabs, Style: reworked selected overline rendering to better accommodate for rounded tabs. Reduced default thickness (style.TabBarOverlineSize), diff --git a/imgui.cpp b/imgui.cpp index dd644e5e7..5fa42b213 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1270,6 +1270,7 @@ static void RenderWindowTitleBarContents(ImGuiWindow* window, const static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); static void RenderDimmedBackgrounds(); static void SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect); +static void SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect); // Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. @@ -4802,15 +4803,30 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) // This is also inlined in ItemAdd() // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect. -void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) +void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect) { ImGuiContext& g = *GImGui; g.LastItemData.ID = item_id; - g.LastItemData.ItemFlags = in_flags; - g.LastItemData.StatusFlags = item_flags; + g.LastItemData.ItemFlags = item_flags; + g.LastItemData.StatusFlags = status_flags; g.LastItemData.Rect = g.LastItemData.NavRect = item_rect; } +static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect) +{ + ImGuiContext& g = *GImGui; + if (window->DockIsActive) + SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.DockTabItemStatusFlags, window->DC.DockTabItemRect); + else + SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.WindowItemStatusFlags, rect); +} + +static void ImGui::SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect) +{ + ImGuiContext& g = *GImGui; + SetLastItemData(window->ChildId, g.CurrentItemFlags, window->DC.ChildItemStatusFlags, rect); +} + float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) { if (wrap_pos_x < 0.0f) @@ -6434,7 +6450,14 @@ void ImGui::EndChild() } if (g.HoveredWindow == child_window) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + child_window->DC.ChildItemStatusFlags = g.LastItemData.StatusFlags; + //SetLastItemDataForChildWindowItem(child_window, child_window->Rect()); // Not needed, effectively done by ItemAdd() } + else + { + SetLastItemDataForChildWindowItem(child_window, child_window->Rect()); + } + g.WithinEndChildID = backup_within_end_child_id; g.LogLinePosY = -FLT_MAX; // To enforce a carriage return } @@ -8175,6 +8198,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). // This is useful to allow creating context menus on title bar only, etc. + window->DC.WindowItemStatusFlags = ImGuiItemStatusFlags_None; + window->DC.WindowItemStatusFlags |= IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; SetLastItemDataForWindow(window, title_bar_rect); // [DEBUG] @@ -8333,15 +8358,6 @@ void ImGui::DrawOverlappingDecorators() } } -static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect) -{ - ImGuiContext& g = *GImGui; - if (window->DockIsActive) - SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DockTabItemStatusFlags, window->DockTabItemRect); - else - SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(rect.Min, rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, rect); -} - void ImGui::End() { ImGuiContext& g = *GImGui; @@ -18577,8 +18593,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w node->VisibleWindow = window; // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call - window->DockTabItemStatusFlags = g.LastItemData.StatusFlags; - window->DockTabItemRect = g.LastItemData.Rect; + window->DC.DockTabItemStatusFlags = g.LastItemData.StatusFlags; + window->DC.DockTabItemRect = g.LastItemData.Rect; // Update navigation ID on menu layer if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0) diff --git a/imgui_internal.h b/imgui_internal.h index 204351f05..527b98050 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2669,6 +2669,12 @@ struct IMGUI_API ImGuiWindowTempData ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() ImU32 ModalDimBgColor; + // Status flags + ImGuiItemStatusFlags WindowItemStatusFlags; + ImGuiItemStatusFlags ChildItemStatusFlags; + ImGuiItemStatusFlags DockTabItemStatusFlags; + ImRect DockTabItemRect; + // Local parameters stacks // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window). @@ -2843,8 +2849,6 @@ struct IMGUI_API ImGuiWindow ImGuiDockNode* DockNode; // Which node are we docked into. Important: Prefer testing DockIsActive in many cases as this will still be set when the dock node is hidden. ImGuiDockNode* DockNodeAsHost; // Which node are we owning (for parent windows) ImGuiID DockId; // Backup of last valid DockNode->ID, so single window remember their dock node id even when they are not bound any more - ImGuiItemStatusFlags DockTabItemStatusFlags; - ImRect DockTabItemRect; public: ImGuiWindow(ImGuiContext* context, const char* name); @@ -2881,7 +2885,8 @@ enum ImGuiTabItemFlagsPrivate_ ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing, ImGuiTabItemFlags_NoCloseButton = 1 << 20, // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout) ImGuiTabItemFlags_Button = 1 << 21, // Used by TabItemButton, change the tab item behavior to mimic a button - ImGuiTabItemFlags_Unsorted = 1 << 22, // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window. + ImGuiTabItemFlags_Invisible = 1 << 22, // To reserve space e.g. with ImGuiTabItemFlags_Leading + ImGuiTabItemFlags_Unsorted = 1 << 23, // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window. }; // Storage for one active tab item (sizeof() 48 bytes) @@ -3373,7 +3378,7 @@ namespace ImGui IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags); IMGUI_API bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); - IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); + IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); @@ -3711,6 +3716,7 @@ namespace ImGui IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImVec2 mouse_pos); IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); + IMGUI_API void TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float width); IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker); IMGUI_API ImVec2 TabItemCalcSize(ImGuiWindow* window); IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e34df611e..58bdabeff 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -9981,7 +9981,7 @@ bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags f IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!"); return false; } - IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! + IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL); if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) @@ -10027,6 +10027,23 @@ bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags) return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL); } +void ImGui::TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float width) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); + return; + } + SetNextItemWidth(width); + TabItemEx(tab_bar, str_id, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder | ImGuiTabItemFlags_Invisible, NULL); +} + bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window) { // Layout whole tab bar if not already done @@ -10174,8 +10191,11 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap); if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW)) // FIXME: May be an opt-in property of the payload to disable this button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + bool hovered, held, pressed; + if (flags & ImGuiTabItemFlags_Invisible) + hovered = held = pressed = false; + else + pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); if (pressed && !is_tab_button) TabBarQueueFocus(tab_bar, tab); @@ -10260,67 +10280,71 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, #endif // Render tab shape - ImDrawList* display_draw_list = window->DrawList; - const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed)); - TabItemBackground(display_draw_list, bb, flags, tab_col); - if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f) + const bool is_visible = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) && !(flags & ImGuiTabItemFlags_Invisible); + if (is_visible) { - // Might be moved to TabItemBackground() ? - ImVec2 tl = bb.GetTL() + ImVec2(0, 1.0f * g.CurrentDpiScale); - ImVec2 tr = bb.GetTR() + ImVec2(0, 1.0f * g.CurrentDpiScale); - ImU32 overline_col = GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline); - if (style.TabRounding > 0.0f) + ImDrawList* display_draw_list = window->DrawList; + const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed)); + TabItemBackground(display_draw_list, bb, flags, tab_col); + if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f) { - float rounding = style.TabRounding; - display_draw_list->PathArcToFast(tl + ImVec2(+rounding, +rounding), rounding, 7, 9); - display_draw_list->PathArcToFast(tr + ImVec2(-rounding, +rounding), rounding, 9, 11); - display_draw_list->PathStroke(overline_col, 0, style.TabBarOverlineSize); + // Might be moved to TabItemBackground() ? + ImVec2 tl = bb.GetTL() + ImVec2(0, 1.0f * g.CurrentDpiScale); + ImVec2 tr = bb.GetTR() + ImVec2(0, 1.0f * g.CurrentDpiScale); + ImU32 overline_col = GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline); + if (style.TabRounding > 0.0f) + { + float rounding = style.TabRounding; + display_draw_list->PathArcToFast(tl + ImVec2(+rounding, +rounding), rounding, 7, 9); + display_draw_list->PathArcToFast(tr + ImVec2(-rounding, +rounding), rounding, 9, 11); + display_draw_list->PathStroke(overline_col, 0, style.TabBarOverlineSize); + } + else + { + display_draw_list->AddLine(tl - ImVec2(0.5f, 0.5f), tr - ImVec2(0.5f, 0.5f), overline_col, style.TabBarOverlineSize); + } } - else + RenderNavCursor(bb, id); + + // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. + const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) + TabBarQueueFocus(tab_bar, tab); + + if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) + flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; + + // Render tab label, process close button + const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0; + bool just_closed; + bool text_clipped; + TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); + if (just_closed && p_open != NULL) { - display_draw_list->AddLine(tl - ImVec2(0.5f, 0.5f), tr - ImVec2(0.5f, 0.5f), overline_col, style.TabBarOverlineSize); + *p_open = false; + TabBarCloseTab(tab_bar, tab); } + + // Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent) + // That state is copied to window->DockTabItemStatusFlags by our caller. + if (docked_window && (hovered || g.HoveredId == close_button_id)) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + + // Tooltip + // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) + // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) + // FIXME: This is a mess. + // FIXME: We may want disabled tab to still display the tooltip? + if (text_clipped && g.HoveredId == id && !held) + if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) + SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); } - RenderNavCursor(bb, id); - - // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. - const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); - if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) - TabBarQueueFocus(tab_bar, tab); - - if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) - flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; - - // Render tab label, process close button - const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0; - bool just_closed; - bool text_clipped; - TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); - if (just_closed && p_open != NULL) - { - *p_open = false; - TabBarCloseTab(tab_bar, tab); - } - - // Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent) - // That state is copied to window->DockTabItemStatusFlags by our caller. - if (docked_window && (hovered || g.HoveredId == close_button_id)) - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; // Restore main window position so user can draw there if (want_clip_rect) PopClipRect(); window->DC.CursorPos = backup_main_cursor_pos; - // Tooltip - // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) - // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) - // FIXME: This is a mess. - // FIXME: We may want disabled tab to still display the tooltip? - if (text_clipped && g.HoveredId == id && !held) - if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) - SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); - IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected if (is_tab_button) return pressed;