Nav: Added a CTRL+TAB window list and changed the highlight system accordingly. (#787)
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 6c01be7..da8b6ee 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -37,11 +37,13 @@
- Removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor `io.OptResizeWindowsFromEdges=true` to enable the feature globally. (#1495)
The feature is not currently enabled by default because it is not satisfying enough.
+ - Style: Renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
Other Changes:
- ArrowButton: Fixed to honor PushButtonRepeat() setting (and internals' ImGuiItemFlags_ButtonRepeat).
- ArrowButton: Setup current line text baseline so that ArrowButton() + SameLine() + Text() are aligned properly.
+ - Nav: Added a CTRL+TAB window list and changed the highlight system accordingly. This is designed to allow CTRL+TAB between Tabs in the future.
- Window: Allow menu and popups windows from ignoring the style.WindowMinSize values so short menus/popups are not padded. (#1909)
- Window: Added global io.OptResizeWindowsFromEdges option to enable resizing windows from their edges and from the lower-left corner. (#1495)
- Drag and Drop: Fixed an incorrect assert when dropping a source that is submitted after the target (bug introduced with 1.62 changes
diff --git a/imgui.cpp b/imgui.cpp
index 79ba261..5a268e5 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -305,6 +305,7 @@
When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
You can read releases logs https://github.com/ocornut/imgui/releases for more details.
+ - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
- 2018/07/06 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.OptResizeWindowsFromEdges to enable the feature.
- 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
- 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
@@ -896,6 +897,7 @@
static void NavUpdate();
static void NavUpdateWindowing();
+static void NavUpdateWindowingList();
static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id);
static void UpdateMouseInputs();
@@ -3051,7 +3053,8 @@
ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
if (!window_target)
window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir);
- g.NavWindowingTarget = window_target;
+ if (window_target) // Don't reset windowing target if there's a single window in the list
+ g.NavWindowingTarget = window_target;
g.NavWindowingToggleLayer = false;
}
@@ -3136,7 +3139,7 @@
{
const float NAV_MOVE_SPEED = 800.0f;
const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well
- g.NavWindowingTarget->Pos += move_delta * move_speed;
+ g.NavWindowingTarget->RootWindow->Pos += move_delta * move_speed;
g.NavDisableMouseHover = true;
MarkIniSettingsDirty(g.NavWindowingTarget);
}
@@ -3178,6 +3181,46 @@
}
}
+// Window has already passed the IsWindowNavFocusable()
+static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
+{
+ if (window->Flags & ImGuiWindowFlags_Popup)
+ return "(Popup)";
+ if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
+ return "(Main menu bar)";
+ return "(Untitled)";
+}
+
+// Overlay displayed when using CTRL+TAB. Called by EndFrame().
+void ImGui::NavUpdateWindowingList()
+{
+ ImGuiContext& g = *GImGui;
+ if (!g.NavWindowingTarget)
+ {
+ g.NavWindowingList = NULL;
+ return;
+ }
+
+ if (g.NavWindowingList == NULL)
+ g.NavWindowingList = FindWindowByName("###NavWindowList");
+ SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
+ SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
+ PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
+ Begin("###NavWindowList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize);
+ for (int n = g.Windows.Size - 1; n >= 0; n--)
+ {
+ ImGuiWindow* window = g.Windows[n];
+ if (!IsWindowNavFocusable(window))
+ continue;
+ const char* label = window->Name;
+ if (label == FindRenderedTextEnd(label))
+ label = GetFallbackWindowNameForWindowingList(window);
+ Selectable(label, g.NavWindowingTarget == window);
+ }
+ End();
+ PopStyleVar();
+}
+
// Scroll to keep newly navigated item fully into view
// NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated.
static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect)
@@ -3590,6 +3633,11 @@
}
}
+static bool IsWindowActiveAndVisible(ImGuiWindow* window)
+{
+ return (window->HiddenFrames == 0) && (window->Active);
+}
+
static void ImGui::UpdateMouseInputs()
{
ImGuiContext& g = *GImGui;
@@ -3820,10 +3868,10 @@
UpdateHoveredWindowAndCaptureFlags();
// Background darkening/whitening
- if (GetFrontMostPopupModal() != NULL)
- g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f);
+ if (GetFrontMostPopupModal() != NULL || g.NavWindowingTarget != NULL)
+ g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
else
- g.ModalWindowDarkeningRatio = 0.0f;
+ g.DimBgRatio = 0.0f;
g.MouseCursor = ImGuiMouseCursor_Arrow;
g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
@@ -4333,6 +4381,8 @@
g.PlatformImeLastPos = g.PlatformImePos;
}
+ NavUpdateWindowingList();
+
// Hide implicit "Debug" window if it hasn't been used
IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls, did you forget to call end on g.CurrentWindow->Name?
if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
@@ -4421,15 +4471,18 @@
// Gather windows to render
g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0;
g.DrawDataBuilder.Clear();
- ImGuiWindow* window_to_render_front_most = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget : NULL;
+ ImGuiWindow* windows_to_render_front_most[2];
+ windows_to_render_front_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
+ windows_to_render_front_most[1] = (g.NavWindowingList);
for (int n = 0; n != g.Windows.Size; n++)
{
ImGuiWindow* window = g.Windows[n];
- if (window->Active && window->HiddenFrames == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != window_to_render_front_most)
+ if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_front_most[0] && window != windows_to_render_front_most[1])
AddWindowToDrawDataSelectLayer(window);
}
- if (window_to_render_front_most && window_to_render_front_most->Active && window_to_render_front_most->HiddenFrames == 0) // NavWindowingTarget is always temporarily displayed as the front-most window
- AddWindowToDrawDataSelectLayer(window_to_render_front_most);
+ for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_front_most); n++)
+ if (windows_to_render_front_most[n] && IsWindowActiveAndVisible(windows_to_render_front_most[n])) // NavWindowingTarget is always temporarily displayed as the front-most window
+ AddWindowToDrawDataSelectLayer(windows_to_render_front_most[n]);
g.DrawDataBuilder.FlattenIntoSingleLayer();
// Draw software mouse cursor if requested
@@ -6032,7 +6085,7 @@
PopID();
// Navigation resize (keyboard/gamepad)
- if (g.NavWindowingTarget == window)
+ if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
{
ImVec2 nav_resize_delta;
if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
@@ -6391,23 +6444,29 @@
else
PushClipRect(viewport_rect.Min, viewport_rect.Max, true);
- // Draw modal window background (darkens what is behind them)
- if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostPopupModal())
- window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio));
+ // Draw modal window background (darkens what is behind them, all viewports)
+ const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetFrontMostPopupModal() && window->HiddenFrames <= 0;
+ const bool dim_bg_for_window_list = g.NavWindowingTarget && (window == g.NavWindowingTarget->RootWindow);
+ if (dim_bg_for_modal || dim_bg_for_window_list)
+ {
+ const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowListDimBg, g.DimBgRatio);
+ window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
+ }
// Draw navigation selection/windowing rectangle background
- if (g.NavWindowingTarget == window)
+ if (dim_bg_for_window_list && window == g.NavWindowingTarget->RootWindow)
{
ImRect bb = window->Rect();
bb.Expand(g.FontSize);
if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
- window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
+ window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowListHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
}
// Draw window + handle manual resize
const float window_rounding = window->WindowRounding;
const float window_border_size = window->WindowBorderSize;
- const bool title_bar_is_highlight = want_focus || (g.NavWindow && window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
+ const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
+ const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
const ImRect title_bar_rect = window->TitleBarRect();
if (window->Collapsed)
{
@@ -6487,7 +6546,7 @@
bb.Expand(-g.FontSize - 1.0f);
rounding = window->WindowRounding;
}
- window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
+ window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowListHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
}
// Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
@@ -7181,10 +7240,10 @@
case ImGuiCol_PlotHistogram: return "PlotHistogram";
case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
- case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening";
case ImGuiCol_DragDropTarget: return "DragDropTarget";
case ImGuiCol_NavHighlight: return "NavHighlight";
- case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
+ case ImGuiCol_NavWindowListDimBg: return "NavWindowListDimBg";
+ case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
}
IM_ASSERT(0);
return "Unknown";
diff --git a/imgui.h b/imgui.h
index b9e3a6d..da21e83 100644
--- a/imgui.h
+++ b/imgui.h
@@ -892,15 +892,17 @@
ImGuiCol_PlotHistogram,
ImGuiCol_PlotHistogramHovered,
ImGuiCol_TextSelectedBg,
- ImGuiCol_ModalWindowDarkening, // Darken/colorize entire screen behind a modal window, when one is active
ImGuiCol_DragDropTarget,
ImGuiCol_NavHighlight, // Gamepad/keyboard: current highlighted item
- ImGuiCol_NavWindowingHighlight, // Gamepad/keyboard: when holding NavMenu to focus/move/resize windows
+ ImGuiCol_NavWindowListHighlight,// Highlight window when using CTRL+TAB
+ ImGuiCol_NavWindowListDimBg, // Darken/colorize entire screen behind the CTRL+TAB window list, when active
+ ImGuiCol_ModalWindowDimBg, // Darken/colorize entire screen behind a modal window, when one is active
ImGuiCol_COUNT
// Obsolete names (will be removed)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
, ImGuiCol_ChildWindowBg = ImGuiCol_ChildBg, ImGuiCol_Column = ImGuiCol_Separator, ImGuiCol_ColumnHovered = ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive = ImGuiCol_SeparatorActive
+ , ImGuiCol_ModalWindowDarkening = ImGuiCol_ModalWindowDimBg
//ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered, // [unused since 1.60+] the close button now uses regular button colors.
//ImGuiCol_ComboBg, // [unused since 1.53+] ComboBg has been merged with PopupBg, so a redirect isn't accurate.
#endif
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 99d6ab1..f33a3a4 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -1860,7 +1860,7 @@
ImGui::OpenPopup("Stacked 1");
if (ImGui::BeginPopupModal("Stacked 1"))
{
- ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDarkening] for darkening.");
+ ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it.");
static int item = 1;
ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
static float color[4] = { 0.4f,0.7f,0.0f,0.5f };
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index dfb8bb4..5d74181 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -194,10 +194,11 @@
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
- colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
- colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
+ colors[ImGuiCol_NavWindowListHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
+ colors[ImGuiCol_NavWindowListDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
+ colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
}
void ImGui::StyleColorsClassic(ImGuiStyle* dst)
@@ -243,10 +244,11 @@
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
- colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered];
- colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
+ colors[ImGuiCol_NavWindowListHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
+ colors[ImGuiCol_NavWindowListDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
+ colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
}
// Those light colors are better suited with a thicker font than the default one + FrameBorder
@@ -293,10 +295,11 @@
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.45f, 0.00f, 1.00f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
- colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered];
- colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f);
+ colors[ImGuiCol_NavWindowListHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f);
+ colors[ImGuiCol_NavWindowListDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f);
+ colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
}
//-----------------------------------------------------------------------------
diff --git a/imgui_internal.h b/imgui_internal.h
index b7c7f07..d336b15 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -659,6 +659,7 @@
ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring.
int NavScoringCount; // Metrics for debugging
ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most.
+ ImGuiWindow* NavWindowingList;
float NavWindowingHighlightTimer;
float NavWindowingHighlightAlpha;
bool NavWindowingToggleLayer;
@@ -686,7 +687,7 @@
// Render
ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user
ImDrawDataBuilder DrawDataBuilder;
- float ModalWindowDarkeningRatio;
+ float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list)
ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays
ImGuiMouseCursor MouseCursor;
@@ -787,7 +788,7 @@
NavInputSource = ImGuiInputSource_None;
NavScoringRectScreen = ImRect();
NavScoringCount = 0;
- NavWindowingTarget = NULL;
+ NavWindowingTarget = NavWindowingList = NULL;
NavWindowingHighlightTimer = NavWindowingHighlightAlpha = 0.0f;
NavWindowingToggleLayer = false;
NavLayer = 0;
@@ -806,7 +807,7 @@
NavMoveRequestForward = ImGuiNavForward_None;
NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None;
- ModalWindowDarkeningRatio = 0.0f;
+ DimBgRatio = 0.0f;
OverlayDrawList._Data = &DrawListSharedData;
OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging
MouseCursor = ImGuiMouseCursor_Arrow;