Nav: Keyboard: Added CTRL+TAB (and CTRL+Shift+TAB) style window selection. (#787)
This commit is contained in:
parent
ed088b00be
commit
f2d5300408
1
TODO.txt
1
TODO.txt
@ -237,7 +237,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
|
|||||||
- focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame)
|
- focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame)
|
||||||
- focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787)
|
- focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787)
|
||||||
- inputs: rework IO system to be able to pass actual ordered/timestamped events. use an event queue? (~#335, #71)
|
- inputs: rework IO system to be able to pass actual ordered/timestamped events. use an event queue? (~#335, #71)
|
||||||
- inputs: allow to pass explicit double-clicks if that's the only thing the user's backend can get them. (e.g. for windows by the CS_DBLCLKS style).
|
|
||||||
- inputs: support track pad style scrolling & slider edit.
|
- inputs: support track pad style scrolling & slider edit.
|
||||||
|
|
||||||
- misc: idle refresh: expose cursor blink animation timer for backend to be able to lower framerate.
|
- misc: idle refresh: expose cursor blink animation timer for backend to be able to lower framerate.
|
||||||
|
119
imgui.cpp
119
imgui.cpp
@ -2700,42 +2700,52 @@ ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInput
|
|||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
|
||||||
|
{
|
||||||
|
ImGuiContext& g = *GImGui;
|
||||||
|
IM_ASSERT(g.NavWindowingTarget);
|
||||||
|
if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int i_current = FindWindowIndex(g.NavWindowingTarget);
|
||||||
|
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;
|
||||||
|
g.NavWindowingToggleLayer = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
|
// Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
|
||||||
static void ImGui::NavUpdateWindowing()
|
static void ImGui::NavUpdateWindowing()
|
||||||
{
|
{
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *GImGui;
|
||||||
bool toggle_layer = false;
|
ImGuiWindow* apply_focus_window = NULL;
|
||||||
|
bool apply_toggle_layer = false;
|
||||||
|
|
||||||
if (!g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiInputReadMode_Pressed))
|
bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiInputReadMode_Pressed);
|
||||||
{
|
bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard);
|
||||||
ImGuiWindow* window = g.NavWindow;
|
if (start_windowing_with_gamepad || start_windowing_with_keyboard)
|
||||||
if (!window)
|
if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavigable(g.Windows.Size - 1, -INT_MAX, -1))
|
||||||
window = FindWindowNavigable(g.Windows.Size - 1, -1, -1);
|
|
||||||
if (window)
|
|
||||||
{
|
{
|
||||||
g.NavWindowingTarget = window->RootNonPopupWindow;
|
g.NavWindowingTarget = window->RootNonPopupWindow;
|
||||||
g.NavWindowingDisplayAlpha = 0.0f;
|
g.NavWindowingHighlightTimer = g.NavWindowingHighlightAlpha = 0.0f;
|
||||||
g.NavWindowingToggleLayer = true;
|
g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
|
||||||
|
g.NavWindowingIsKeyboardMode = start_windowing_with_keyboard;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (g.NavWindowingTarget)
|
// Gamepad update
|
||||||
|
g.NavWindowingHighlightTimer += g.IO.DeltaTime;
|
||||||
|
if (g.NavWindowingTarget && !g.NavWindowingIsKeyboardMode)
|
||||||
{
|
{
|
||||||
// Visuals only appears after a brief time holding the button, so that a fast tap (to toggle NavLayer) doesn't add visual noise
|
// Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise
|
||||||
const float pressed_duration = g.IO.NavInputsDownDuration[ImGuiNavInput_PadMenu];
|
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.20f) / 0.05f));
|
||||||
g.NavWindowingDisplayAlpha = ImMax(g.NavWindowingDisplayAlpha, ImSaturate((pressed_duration - 0.20f) / 0.05f));
|
|
||||||
g.NavWindowingToggleLayer &= (g.NavWindowingDisplayAlpha < 1.0f); // Once button is held long enough we don't consider it a tag-to-toggle-layer press anymore.
|
|
||||||
|
|
||||||
// Select window to focus
|
// Select window to focus
|
||||||
const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_PadFocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_PadFocusNext, ImGuiInputReadMode_RepeatSlow);
|
const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_PadFocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_PadFocusNext, ImGuiInputReadMode_RepeatSlow);
|
||||||
if (focus_change_dir != 0 && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal))
|
if (focus_change_dir != 0)
|
||||||
{
|
{
|
||||||
const int i_current = FindWindowIndex(g.NavWindowingTarget);
|
NavUpdateWindowingHighlightWindow(focus_change_dir);
|
||||||
ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -1, focus_change_dir);
|
g.NavWindowingHighlightAlpha = 1.0f;
|
||||||
if (!window_target)
|
|
||||||
window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir);
|
|
||||||
g.NavWindowingTarget = window_target;
|
|
||||||
g.NavWindowingToggleLayer = false;
|
|
||||||
g.NavWindowingDisplayAlpha = 1.0f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move window
|
// Move window
|
||||||
@ -2751,34 +2761,51 @@ static void ImGui::NavUpdateWindowing()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most)
|
||||||
if (!IsNavInputDown(ImGuiNavInput_PadMenu))
|
if (!IsNavInputDown(ImGuiNavInput_PadMenu))
|
||||||
{
|
{
|
||||||
// Apply actual focus only when releasing the NavMenu button (until then the window was merely rendered front-most)
|
g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
|
||||||
if (g.NavWindowingTarget && !g.NavWindowingToggleLayer && (!g.NavWindow || g.NavWindowingTarget != g.NavWindow->RootNonPopupWindow))
|
|
||||||
{
|
|
||||||
FocusWindow(g.NavWindowingTarget);
|
|
||||||
g.NavDisableHighlight = false;
|
|
||||||
g.NavDisableMouseHover = true;
|
|
||||||
if (g.NavWindowingTarget->NavLastIds[0] == 0)
|
|
||||||
NavInitWindow(g.NavWindowingTarget, false);
|
|
||||||
|
|
||||||
// If the window only has a menu layer, select it directly
|
|
||||||
if (g.NavWindowingTarget->DC.NavLayerActiveMask == 0x02)
|
|
||||||
g.NavLayer = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single press toggles NavLayer
|
|
||||||
if (g.NavWindowingToggleLayer && g.NavWindow)
|
if (g.NavWindowingToggleLayer && g.NavWindow)
|
||||||
toggle_layer = true;
|
apply_toggle_layer = true;
|
||||||
|
else if (!g.NavWindowingToggleLayer)
|
||||||
|
apply_focus_window = g.NavWindowingTarget;
|
||||||
g.NavWindowingTarget = NULL;
|
g.NavWindowingTarget = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keyboard: Press and release ALT to toggle menu
|
|
||||||
if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiInputReadMode_Released))
|
|
||||||
toggle_layer = true;
|
|
||||||
|
|
||||||
if (toggle_layer && g.NavWindow)
|
// Keyboard: Focus
|
||||||
|
if (g.NavWindowingTarget && g.NavWindowingIsKeyboardMode)
|
||||||
|
{
|
||||||
|
// Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
|
||||||
|
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.15f) / 0.04f)); // 1.0f
|
||||||
|
if (IsKeyPressedMap(ImGuiKey_Tab, true))
|
||||||
|
NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
|
||||||
|
if (!g.IO.KeyCtrl)
|
||||||
|
apply_focus_window = g.NavWindowingTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keyboard: Press and release ALT to toggle menu layer
|
||||||
|
if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiInputReadMode_Released))
|
||||||
|
apply_toggle_layer = true;
|
||||||
|
|
||||||
|
// Apply final focus
|
||||||
|
if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootNonPopupWindow))
|
||||||
|
{
|
||||||
|
g.NavDisableHighlight = false;
|
||||||
|
g.NavDisableMouseHover = true;
|
||||||
|
FocusWindow(apply_focus_window);
|
||||||
|
if (apply_focus_window->NavLastIds[0] == 0)
|
||||||
|
NavInitWindow(apply_focus_window, false);
|
||||||
|
|
||||||
|
// If the window only has a menu layer, select it directly
|
||||||
|
if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1))
|
||||||
|
g.NavLayer = 1;
|
||||||
|
}
|
||||||
|
if (apply_focus_window)
|
||||||
|
g.NavWindowingTarget = NULL;
|
||||||
|
|
||||||
|
// Apply menu/layer toggle
|
||||||
|
if (apply_toggle_layer && g.NavWindow)
|
||||||
{
|
{
|
||||||
if ((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveMask & (1 << 1)) != 0)
|
if ((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveMask & (1 << 1)) != 0)
|
||||||
FocusWindow(g.NavWindow->RootWindow);
|
FocusWindow(g.NavWindow->RootWindow);
|
||||||
@ -5710,8 +5737,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
|||||||
{
|
{
|
||||||
ImRect bb = window->Rect();
|
ImRect bb = window->Rect();
|
||||||
bb.Expand(g.FontSize);
|
bb.Expand(g.FontSize);
|
||||||
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingDisplayAlpha * 0.25f), g.Style.WindowRounding);
|
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
|
||||||
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingDisplayAlpha), g.Style.WindowRounding, ~0, 3.0f);
|
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), g.Style.WindowRounding, ~0, 3.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw window + handle manual resize
|
// Draw window + handle manual resize
|
||||||
|
4
imgui.h
4
imgui.h
@ -730,8 +730,8 @@ enum ImGuiNavInput_
|
|||||||
// [BETA] Gamepad/Keyboard directional navigation options
|
// [BETA] Gamepad/Keyboard directional navigation options
|
||||||
enum ImGuiNavFlags_
|
enum ImGuiNavFlags_
|
||||||
{
|
{
|
||||||
ImGuiNavFlags_EnableGamepad = 1 << 0, // Master gamepad navigation enable flag. This is to instruct your imgui binding whether to fill in gamepad navigation inputs. imgui itself won't use this flag.
|
ImGuiNavFlags_EnableGamepad = 1 << 0, // Master gamepad navigation enable flag. This is mostly to instruct your imgui binding whether to fill in gamepad navigation inputs.
|
||||||
ImGuiNavFlags_EnableKeyboard = 1 << 1, // Master keyboard navigation enable flag. This is to instruct your imgui binding whether to fill in keyboard navigation inputs. imgui itself won't use this flag.
|
ImGuiNavFlags_EnableKeyboard = 1 << 1, // Master keyboard navigation enable flag. This is mostly to instruct your imgui binding whether to fill in keyboard navigation inputs.
|
||||||
ImGuiNavFlags_MoveMouse = 1 << 2 // Request navigation to allow move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth.
|
ImGuiNavFlags_MoveMouse = 1 << 2 // Request navigation to allow move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -591,8 +591,10 @@ struct ImGuiContext
|
|||||||
ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest)
|
ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest)
|
||||||
ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring.
|
ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring.
|
||||||
ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most.
|
ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most.
|
||||||
float NavWindowingDisplayAlpha;
|
float NavWindowingHighlightTimer;
|
||||||
|
float NavWindowingHighlightAlpha;
|
||||||
bool NavWindowingToggleLayer;
|
bool NavWindowingToggleLayer;
|
||||||
|
bool NavWindowingIsKeyboardMode; // Gamepad or keyboard mode
|
||||||
int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later.
|
int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later.
|
||||||
int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing
|
int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing
|
||||||
bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid
|
bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid
|
||||||
@ -710,8 +712,9 @@ struct ImGuiContext
|
|||||||
NavJustTabbedId = NavJustMovedToId = NavNextActivateId = 0;
|
NavJustTabbedId = NavJustMovedToId = NavNextActivateId = 0;
|
||||||
NavScoringRectScreen = ImRect();
|
NavScoringRectScreen = ImRect();
|
||||||
NavWindowingTarget = NULL;
|
NavWindowingTarget = NULL;
|
||||||
NavWindowingDisplayAlpha = 0.0f;
|
NavWindowingHighlightTimer = NavWindowingHighlightAlpha = 0.0f;
|
||||||
NavWindowingToggleLayer = false;
|
NavWindowingToggleLayer = false;
|
||||||
|
NavWindowingIsKeyboardMode = false;
|
||||||
NavLayer = 0;
|
NavLayer = 0;
|
||||||
NavIdTabCounter = INT_MAX;
|
NavIdTabCounter = INT_MAX;
|
||||||
NavIdIsAlive = false;
|
NavIdIsAlive = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user