Skip to content

Commit

Permalink
Docking: docked window honor tab and text colors by storing them. (#2771
Browse files Browse the repository at this point in the history
)

Later to lead into #2700 and #2539
Move tab submission block above in DockNodeUpdateTabBar(), not strictly necessary for this change as is, but useful if needing to apply override for TitleBg* as we'd need a value for node->VisibleWindow earlier than currently set.
  • Loading branch information
ocornut committed Jan 18, 2021
1 parent 9e4956d commit ebbb98d
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 29 deletions.
75 changes: 52 additions & 23 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2488,6 +2488,11 @@ struct ImGuiStyleVarInfo
void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
};

static const ImGuiCol GWindowDockStyleColors[ImGuiWindowDockStyleCol_COUNT] =
{
ImGuiCol_Text, ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive
};

static const ImGuiStyleVarInfo GStyleVarInfo[] =
{
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
Expand Down Expand Up @@ -4059,7 +4064,7 @@ void ImGui::NewFrame()

// Undocking
// (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame)
DockContextUpdateUndocking(&g);
DockContextNewFrameUpdateUndocking(&g);

// Find hovered window
// (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
Expand Down Expand Up @@ -4123,7 +4128,7 @@ void ImGui::NewFrame()
ClosePopupsOverWindow(g.NavWindow, false);

// Docking
DockContextUpdateDocking(&g);
DockContextNewFrameUpdateDocking(&g);

// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker();
Expand Down Expand Up @@ -11934,10 +11939,10 @@ void ImGui::DestroyPlatformWindows()
// Typical Docking call flow: (root level is generally public API):
//-----------------------------------------------------------------------------
// - NewFrame() new dear imgui frame
// | DockContextUpdateUndocking() - process queued undocking requests
// | DockContextNewFrameUpdateUndocking() - process queued undocking requests
// | - DockContextProcessUndockWindow() - process one window undocking request
// | - DockContextProcessUndockNode() - process one whole node undocking request
// | DockContextUpdateDocking() - process queue docking requests, create floating dock nodes
// | DockContextNewFrameUpdateUndocking() - process queue docking requests, create floating dock nodes
// | - update g.HoveredDockNode - [debug] update node hovered by mouse
// | - DockContextProcessDock() - process one docking request
// | - DockNodeUpdate()
Expand Down Expand Up @@ -12124,8 +12129,8 @@ namespace ImGui
// - DockContextShutdown()
// - DockContextClearNodes()
// - DockContextRebuildNodes()
// - DockContextUpdateUndocking()
// - DockContextUpdateDocking()
// - DockContextNewFrameUpdateUndocking()
// - DockContextNewFrameUpdateDocking()
// - DockContextFindNodeByID()
// - DockContextBindNodeToWindow()
// - DockContextGenNodeID()
Expand Down Expand Up @@ -12184,7 +12189,7 @@ void ImGui::DockContextRebuildNodes(ImGuiContext* ctx)
}

// Docking context update function, called by NewFrame()
void ImGui::DockContextUpdateUndocking(ImGuiContext* ctx)
void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx)
{
ImGuiContext& g = *ctx;
ImGuiDockContext* dc = &ctx->DockContext;
Expand Down Expand Up @@ -12228,7 +12233,7 @@ void ImGui::DockContextUpdateUndocking(ImGuiContext* ctx)
}

// Docking context update function, called by NewFrame()
void ImGui::DockContextUpdateDocking(ImGuiContext* ctx)
void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx)
{
ImGuiContext& g = *ctx;
ImGuiDockContext* dc = &ctx->DockContext;
Expand Down Expand Up @@ -13321,8 +13326,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node)
if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window)
node->LastFocusedNodeId = g.NavWindow->RootWindowDockStop->DockNode->ID;

// We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size after
// processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order!
// We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size
// _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order!
const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0;
if (render_dockspace_bg)
{
Expand Down Expand Up @@ -13561,6 +13566,17 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
ImVec2 window_menu_button_pos;
DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos);

// Submit new tabs and apply NavWindow focus back to the tab bar. They will be added as Unsorted and sorted below based on relative DockOrder value.
const int tabs_count_old = tab_bar->Tabs.Size;
for (int window_n = 0; window_n < node->Windows.Size; window_n++)
{
ImGuiWindow* window = node->Windows[window_n];
if (g.NavWindow && g.NavWindow->RootWindowDockStop == window)
tab_bar->SelectedTabId = window->ID;
if (TabBarFindTabByID(tab_bar, window->ID) == NULL)
TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window);
}

// Title bar
if (is_focused)
node->LastFrameFocused = g.FrameCount;
Expand All @@ -13576,17 +13592,6 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
focus_tab_id = tab_bar->SelectedTabId;
}

// Submit new tabs and apply NavWindow focus back to the tab bar. They will be added as Unsorted and sorted below based on relative DockOrder value.
const int tabs_count_old = tab_bar->Tabs.Size;
for (int window_n = 0; window_n < node->Windows.Size; window_n++)
{
ImGuiWindow* window = node->Windows[window_n];
if (g.NavWindow && g.NavWindow->RootWindowDockStop == window)
tab_bar->SelectedTabId = window->ID;
if (TabBarFindTabByID(tab_bar, window->ID) == NULL)
TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window);
}

// If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value
int tabs_unsorted_start = tab_bar->Tabs.Size;
for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--)
Expand Down Expand Up @@ -13618,6 +13623,11 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags, node);
//host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255));

// Backup style colors
ImVec4 backup_style_cols[ImGuiWindowDockStyleCol_COUNT];
for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
backup_style_cols[color_n] = g.Style.Colors[GWindowDockStyleColors[color_n]];

// Submit actual tabs
node->VisibleWindow = NULL;
for (int window_n = 0; window_n < node->Windows.Size; window_n++)
Expand All @@ -13634,6 +13644,10 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;

// Apply stored style overrides for the window
for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]);

bool tab_open = true;
TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window);
if (!tab_open)
Expand All @@ -13651,6 +13665,10 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
}
}

// Restore style colors
for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
g.Style.Colors[GWindowDockStyleColors[color_n]] = backup_style_cols[color_n];

// Notify root of visible window (used to display title in OS task bar)
if (node->VisibleWindow)
if (is_focused || root_node->VisibleWindow == NULL)
Expand Down Expand Up @@ -13999,7 +14017,6 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock
overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport);

// Draw main preview rectangle
const ImU32 overlay_col_tabs = GetColorU32(ImGuiCol_TabActive);
const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f);
const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f);
const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f);
Expand Down Expand Up @@ -14054,6 +14071,9 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock
ImVec2 tab_size = TabItemCalcSize(payload_window->Name, payload_window->HasCloseButton);
ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y);
tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x;
const ImU32 overlay_col_text = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_Text]);
const ImU32 overlay_col_tabs = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_TabActive]);
PushStyleColor(ImGuiCol_Text, overlay_col_text);
for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
{
ImGuiTabItemFlags tab_flags = ImGuiTabItemFlags_Preview | ((payload_window->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0);
Expand All @@ -14064,6 +14084,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock
if (!tab_bar_rect.Contains(tab_bb))
overlay_draw_lists[overlay_n]->PopClipRect();
}
PopStyleColor();
}
}

Expand Down Expand Up @@ -15142,7 +15163,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open)
// Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly.
if (node->LastFrameAlive < g.FrameCount)
{
// If the window has been orphaned, transition the docknode to an implicit node processed in DockContextUpdateDocking()
// If the window has been orphaned, transition the docknode to an implicit node processed in DockContextNewFrameUpdateDocking()
ImGuiDockNode* root_node = DockNodeGetRootNode(node);
if (root_node->LastFrameAlive < g.FrameCount)
{
Expand All @@ -15156,6 +15177,10 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open)
return;
}

// Store style overrides
for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
window->DockStyle.Colors[color_n] = ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]);

// Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window,
// and never create neither a host window neither a tab bar.
// FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test)
Expand Down Expand Up @@ -15227,6 +15252,10 @@ void ImGui::BeginDockableDragDropSource(ImGuiWindow* window)
{
SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window));
EndDragDropSource();

// Store style overrides
for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
window->DockStyle.Colors[color_n] = ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]);
}
}

Expand Down
33 changes: 27 additions & 6 deletions imgui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,26 @@ struct ImGuiDockNode
ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
};

// List of colors that are stored at the time of Begin() into Docked Windows.
// We currently store the packed colors in a simple array window->DockStyle.Colors[].
// A better solution may involve appending into a log of colors in ImGuiContext + store offsets into those arrays in ImGuiWindow,
// but it would be more complex as we'd need to double-buffer both as e.g. drop target may refer to window from last frame.
enum ImGuiWindowDockStyleCol
{
ImGuiWindowDockStyleCol_Text,
ImGuiWindowDockStyleCol_Tab,
ImGuiWindowDockStyleCol_TabHovered,
ImGuiWindowDockStyleCol_TabActive,
ImGuiWindowDockStyleCol_TabUnfocused,
ImGuiWindowDockStyleCol_TabUnfocusedActive,
ImGuiWindowDockStyleCol_COUNT
};

struct ImGuiWindowDockStyle
{
ImU32 Colors[ImGuiWindowDockStyleCol_COUNT];
};

struct ImGuiDockContext
{
ImGuiStorage Nodes; // Map ID -> ImGuiDockNode*: Active nodes
Expand Down Expand Up @@ -1970,15 +1990,16 @@ struct IMGUI_API ImGuiWindow
bool MemoryCompacted; // Set when window extraneous data have been garbage collected

// Docking
bool DockIsActive :1; // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1).
bool DockTabIsVisible :1; // Is our window visible this frame? ~~ is the corresponding tab selected?
bool DockTabWantClose :1;
short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible.
ImGuiWindowDockStyle DockStyle;
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;
short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible.
bool DockIsActive :1; // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1).
bool DockTabIsVisible :1; // Is our window visible this frame? ~~ is the corresponding tab selected?
bool DockTabWantClose :1;

public:
ImGuiWindow(ImGuiContext* context, const char* name);
Expand Down Expand Up @@ -2504,8 +2525,8 @@ namespace ImGui
IMGUI_API void DockContextShutdown(ImGuiContext* ctx);
IMGUI_API void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs); // Use root_id==0 to clear all
IMGUI_API void DockContextRebuildNodes(ImGuiContext* ctx);
IMGUI_API void DockContextUpdateUndocking(ImGuiContext* ctx);
IMGUI_API void DockContextUpdateDocking(ImGuiContext* ctx);
IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiContext* ctx);
IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiContext* ctx);
IMGUI_API ImGuiID DockContextGenNodeID(ImGuiContext* ctx);
IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer);
IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window);
Expand Down

0 comments on commit ebbb98d

Please sign in to comment.