Docking: Tabs use their own identifier (in order to make window->ID refer to whole window in test engine). Also prevents Tab ID from clashing with "" which was common.

This commit is contained in:
ocornut 2022-02-02 23:23:13 +01:00
parent c1ab3c406f
commit 8eb8689391
4 changed files with 29 additions and 21 deletions

View File

@ -232,6 +232,8 @@ Other Changes:
Docking+Viewports Branch: Docking+Viewports Branch:
- Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792) - Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792)
- Docking: Tabs use their own identifier instead of the Window identifier.
(This will invalidate some stored .ini data such as last selected tab, sorry!)
- Docking: Fixed size constraints not working on single window holding on a dock id (still doesn't work on docked windows). - Docking: Fixed size constraints not working on single window holding on a dock id (still doesn't work on docked windows).
- Viewports, IO: Added io.AddMouseViewportEvent() function to queue hovered viewport change (when known by backend). - Viewports, IO: Added io.AddMouseViewportEvent() function to queue hovered viewport change (when known by backend).
- Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_ - Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_

View File

@ -3264,6 +3264,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst
ViewportAllowPlatformMonitorExtend = -1; ViewportAllowPlatformMonitorExtend = -1;
ViewportPos = ImVec2(FLT_MAX, FLT_MAX); ViewportPos = ImVec2(FLT_MAX, FLT_MAX);
MoveId = GetID("#MOVE"); MoveId = GetID("#MOVE");
TabId = GetID("#TAB");
ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
AutoFitFramesX = AutoFitFramesY = -1; AutoFitFramesX = AutoFitFramesY = -1;
@ -3541,8 +3542,9 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
// Test if another item is active (e.g. being dragged) // Test if another item is active (e.g. being dragged)
if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap)
return false; if (g.ActiveId != window->MoveId && g.ActiveId != window->TabId)
return false;
// Test if interactions on this window are blocked by an active popup or modal. // Test if interactions on this window are blocked by an active popup or modal.
// The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
@ -3554,7 +3556,8 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
return false; return false;
// Special handling for calling after Begin() which represent the title bar or tab. // Special handling for calling after Begin() which represent the title bar or tab.
// When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin)
// will never be overwritten so we need to detect the case.
if (g.LastItemData.ID == window->MoveId && window->WriteAccessed) if (g.LastItemData.ID == window->MoveId && window->WriteAccessed)
return false; return false;
} }
@ -7299,7 +7302,7 @@ void ImGui::FocusWindow(ImGuiWindow* window)
// Select in dock node // Select in dock node
if (dock_node && dock_node->TabBar) if (dock_node && dock_node->TabBar)
dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->ID; dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->TabId;
// Bring to front // Bring to front
BringWindowToFocusFront(focus_front_window); BringWindowToFocusFront(focus_front_window);
@ -12456,6 +12459,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
buf->appendf("Collapsed=%d\n", settings->Collapsed); buf->appendf("Collapsed=%d\n", settings->Collapsed);
if (settings->DockId != 0) if (settings->DockId != 0)
{ {
//buf->appendf("TabId=0x%08X\n", ImHashStr("#TAB", 4, settings->ID)); // window->TabId: this is not read back but writing it makes "debugging" the .ini data easier.
if (settings->DockOrder == -1) if (settings->DockOrder == -1)
buf->appendf("DockId=0x%08X\n", settings->DockId); buf->appendf("DockId=0x%08X\n", settings->DockId);
else else
@ -14063,7 +14067,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
if (payload_node && payload_node->IsLeafNode()) if (payload_node && payload_node->IsLeafNode())
next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId;
if (payload_node == NULL) if (payload_node == NULL)
next_selected_id = payload_window->ID; next_selected_id = payload_window->TabId;
} }
// FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well
@ -14340,7 +14344,7 @@ int ImGui::DockNodeGetTabOrder(ImGuiWindow* window)
ImGuiTabBar* tab_bar = window->DockNode->TabBar; ImGuiTabBar* tab_bar = window->DockNode->TabBar;
if (tab_bar == NULL) if (tab_bar == NULL)
return -1; return -1;
ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->ID); ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->TabId);
return tab ? tab_bar->GetTabOrder(tab) : -1; return tab ? tab_bar->GetTabOrder(tab) : -1;
} }
@ -14444,7 +14448,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window
node->WantHiddenTabBarUpdate = true; node->WantHiddenTabBarUpdate = true;
if (node->TabBar) if (node->TabBar)
{ {
TabBarRemoveTab(node->TabBar, window->ID); TabBarRemoveTab(node->TabBar, window->TabId);
const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2; const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2;
if (node->Windows.Size < tab_count_threshold_for_tab_bar) if (node->Windows.Size < tab_count_threshold_for_tab_bar)
DockNodeRemoveTabBar(node); DockNodeRemoveTabBar(node);
@ -14627,7 +14631,7 @@ static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node)
bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
bool remove = false; bool remove = false;
remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount); remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount);
remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame
remove |= (window->DockTabWantClose); remove |= (window->DockTabWantClose);
if (remove) if (remove)
{ {
@ -15159,7 +15163,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
if (is_focused || root_node->VisibleWindow == NULL) if (is_focused || root_node->VisibleWindow == NULL)
root_node->VisibleWindow = node->VisibleWindow; root_node->VisibleWindow = node->VisibleWindow;
if (node->TabBar) if (node->TabBar)
node->TabBar->VisibleTabId = node->VisibleWindow->ID; node->TabBar->VisibleTabId = node->VisibleWindow->TabId;
} }
return; return;
} }
@ -15210,7 +15214,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
for (int window_n = 0; window_n < node->Windows.Size; window_n++) for (int window_n = 0; window_n < node->Windows.Size; window_n++)
{ {
ImGuiWindow* window = node->Windows[window_n]; ImGuiWindow* window = node->Windows[window_n];
if (TabBarFindTabByID(tab_bar, window->ID) == NULL) if (TabBarFindTabByID(tab_bar, window->TabId) == NULL)
TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window); TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window);
} }
@ -15255,7 +15259,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL) if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL)
tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId; tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId;
else if (tab_bar->Tabs.Size > tabs_count_old) else if (tab_bar->Tabs.Size > tabs_count_old)
tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->ID; tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->TabId;
// Begin tab bar // Begin tab bar
ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons); ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons);
@ -15275,7 +15279,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
for (int window_n = 0; window_n < node->Windows.Size; window_n++) for (int window_n = 0; window_n < node->Windows.Size; window_n++)
{ {
ImGuiWindow* window = node->Windows[window_n]; ImGuiWindow* window = node->Windows[window_n];
if ((closed_all || closed_one == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument)) if ((closed_all || closed_one == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument))
continue; continue;
if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active) if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active)
{ {
@ -15290,11 +15294,12 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]); g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]);
// Note that TabItemEx() calls TabBarCalcTabID() so our tab item ID will ignore the current ID stack (rightly so)
bool tab_open = true; bool tab_open = true;
TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window); TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window);
if (!tab_open) if (!tab_open)
node->WantCloseTabId = window->ID; node->WantCloseTabId = window->TabId;
if (tab_bar->VisibleTabId == window->ID) if (tab_bar->VisibleTabId == window->TabId)
node->VisibleWindow = window; node->VisibleWindow = window;
// Store last item data so it can be queried with IsItemXXX functions after the user Begin() call // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call
@ -15303,7 +15308,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
// Update navigation ID on menu layer // Update navigation ID on menu layer
if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0) if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0)
host_window->NavLastIds[1] = window->ID; host_window->NavLastIds[1] = window->TabId;
} }
} }
@ -15317,7 +15322,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
root_node->VisibleWindow = node->VisibleWindow; root_node->VisibleWindow = node->VisibleWindow;
// Close button (after VisibleWindow was updated) // Close button (after VisibleWindow was updated)
// Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->ID may be != from tab_bar->SelectedTabId // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->TabId may be != from tab_bar->SelectedTabId
const bool close_button_is_enabled = node->HasCloseButton && node->VisibleWindow && node->VisibleWindow->HasCloseButton; const bool close_button_is_enabled = node->HasCloseButton && node->VisibleWindow && node->VisibleWindow->HasCloseButton;
const bool close_button_is_visible = node->HasCloseButton; const bool close_button_is_visible = node->HasCloseButton;
//const bool close_button_is_visible = close_button_is_enabled; // Most people would expect this behavior of not even showing the button (leaving a hole since we can't claim that space as other windows in the tba bar have one) //const bool close_button_is_visible = close_button_is_enabled; // Most people would expect this behavior of not even showing the button (leaving a hole since we can't claim that space as other windows in the tba bar have one)
@ -16931,7 +16936,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open)
if (node->TabBar && window->WasActive) if (node->TabBar && window->WasActive)
window->DockOrder = (short)DockNodeGetTabOrder(window); window->DockOrder = (short)DockNodeGetTabOrder(window);
if ((node->WantCloseAll || node->WantCloseTabId == window->ID) && p_open != NULL) if ((node->WantCloseAll || node->WantCloseTabId == window->TabId) && p_open != NULL)
*p_open = false; *p_open = false;
// Update ChildId to allow returning from Child to Parent with Escape // Update ChildId to allow returning from Child to Parent with Escape

View File

@ -2231,6 +2231,7 @@ struct IMGUI_API ImGuiWindow
float WindowBorderSize; // Window border size at the time of Begin(). float WindowBorderSize; // Window border size at the time of Begin().
int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)! int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)!
ImGuiID MoveId; // == window->GetID("#MOVE") ImGuiID MoveId; // == window->GetID("#MOVE")
ImGuiID TabId; // == window->GetID("#TAB")
ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window)
ImVec2 Scroll; ImVec2 Scroll;
ImVec2 ScrollMax; ImVec2 ScrollMax;

View File

@ -7584,7 +7584,7 @@ static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, I
{ {
IM_UNUSED(tab_bar); IM_UNUSED(tab_bar);
IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode); IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode);
ImGuiID id = ImHashStr(label); ImGuiID id = docked_window->TabId;
KeepAliveID(id); KeepAliveID(id);
return id; return id;
} }
@ -7629,14 +7629,14 @@ ImGuiTabItem* ImGui::TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBa
void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window) void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
IM_ASSERT(TabBarFindTabByID(tab_bar, window->ID) == NULL); IM_ASSERT(TabBarFindTabByID(tab_bar, window->TabId) == NULL);
IM_ASSERT(g.CurrentTabBar != tab_bar); // Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar->CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame) IM_ASSERT(g.CurrentTabBar != tab_bar); // Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar->CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame)
if (!window->HasCloseButton) if (!window->HasCloseButton)
tab_flags |= ImGuiTabItemFlags_NoCloseButton; // Set _NoCloseButton immediately because it will be used for first-frame width calculation. tab_flags |= ImGuiTabItemFlags_NoCloseButton; // Set _NoCloseButton immediately because it will be used for first-frame width calculation.
ImGuiTabItem new_tab; ImGuiTabItem new_tab;
new_tab.ID = window->ID; new_tab.ID = window->TabId;
new_tab.Flags = tab_flags; new_tab.Flags = tab_flags;
new_tab.LastFrameVisible = tab_bar->CurrFrameVisible; // Required so BeginTabBar() doesn't ditch the tab new_tab.LastFrameVisible = tab_bar->CurrFrameVisible; // Required so BeginTabBar() doesn't ditch the tab
if (new_tab.LastFrameVisible == -1) if (new_tab.LastFrameVisible == -1)
@ -8209,7 +8209,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
// Render tab label, process close button // Render tab label, process close button
const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, id) : 0; const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0;
bool just_closed; bool just_closed;
bool text_clipped; bool text_clipped;
TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped);