Skip to content

Commit 892dfb1

Browse files
committed
InputText, Nav: Fixed Home/End key broken when activating Keyboard Navigation. (#787)
Small refactor of ActiveIdUsingXXX inputs flags toward a little more consistent system. (#2637)
1 parent a2f3dcf commit 892dfb1

File tree

5 files changed

+51
-35
lines changed

5 files changed

+51
-35
lines changed

docs/CHANGELOG.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ HOW TO UPDATE?
3434
-----------------------------------------------------------------------
3535

3636
Breaking Changes:
37-
-
37+
-
3838

3939
Other Changes:
40-
-
40+
- InputText, Nav: Fixed Home/End key broken when activating Keyboard Navigation. (#787)
4141

4242

4343
-----------------------------------------------------------------------

imgui.cpp

+25-18
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,7 @@ static void NavUpdate();
10641064
static void NavUpdateWindowing();
10651065
static void NavUpdateWindowingOverlay();
10661066
static void NavUpdateMoveResult();
1067-
static float NavUpdatePageUpPageDown(int allowed_dir_flags);
1067+
static float NavUpdatePageUpPageDown();
10681068
static inline void NavUpdateAnyRequestFlag();
10691069
static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
10701070
static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
@@ -2867,8 +2867,6 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
28672867
}
28682868
}
28692869
g.ActiveId = id;
2870-
g.ActiveIdAllowNavDirFlags = 0;
2871-
g.ActiveIdBlockNavInputFlags = 0;
28722870
g.ActiveIdAllowOverlap = false;
28732871
g.ActiveIdWindow = window;
28742872
g.ActiveIdHasBeenEditedThisFrame = false;
@@ -2877,6 +2875,12 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
28772875
g.ActiveIdIsAlive = id;
28782876
g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
28792877
}
2878+
2879+
// Clear declaration of inputs claimed by the widget
2880+
// (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
2881+
g.ActiveIdUsingNavDirMask = 0x00;
2882+
g.ActiveIdUsingNavInputMask = 0x00;
2883+
g.ActiveIdUsingKeyInputMask = 0x00;
28802884
}
28812885

28822886
// FIXME-NAV: The existence of SetNavID/SetNavIDWithRectRel/SetFocusID is incredibly messy and confusing and needs some explanation or refactoring.
@@ -3157,7 +3161,7 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
31573161

31583162
// Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
31593163
// (Note that we can always TAB out of a widget that doesn't allow tabbing in)
3160-
if (g.ActiveId == id && g.FocusTabPressed && !(g.ActiveIdBlockNavInputFlags & (1 << ImGuiNavInput_KeyTab_)) && g.FocusRequestNextWindow == NULL)
3164+
if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL)
31613165
{
31623166
g.FocusRequestNextWindow = window;
31633167
g.FocusRequestNextCounterTab = window->DC.FocusCounterTab + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
@@ -3782,6 +3786,11 @@ void ImGui::NewFrame()
37823786
g.ActiveIdIsJustActivated = false;
37833787
if (g.TempInputTextId != 0 && g.ActiveId != g.TempInputTextId)
37843788
g.TempInputTextId = 0;
3789+
if (g.ActiveId == 0)
3790+
{
3791+
g.ActiveIdUsingNavDirMask = g.ActiveIdUsingNavInputMask = 0;
3792+
g.ActiveIdUsingKeyInputMask = 0;
3793+
}
37853794

37863795
// Drag and drop
37873796
g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
@@ -8271,7 +8280,6 @@ static void ImGui::NavUpdate()
82718280
NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
82728281
NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
82738282
NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
8274-
NAV_MAP_KEY(ImGuiKey_Tab, ImGuiNavInput_KeyTab_ );
82758283
if (g.IO.KeyCtrl)
82768284
g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
82778285
if (g.IO.KeyShift)
@@ -8349,7 +8357,7 @@ static void ImGui::NavUpdate()
83498357
{
83508358
if (g.ActiveId != 0)
83518359
{
8352-
if (!(g.ActiveIdBlockNavInputFlags & (1 << ImGuiNavInput_Cancel)))
8360+
if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
83538361
ClearActiveID();
83548362
}
83558363
else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
@@ -8411,17 +8419,16 @@ static void ImGui::NavUpdate()
84118419
g.NavNextActivateId = 0;
84128420

84138421
// Initiate directional inputs request
8414-
const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags;
84158422
if (g.NavMoveRequestForward == ImGuiNavForward_None)
84168423
{
84178424
g.NavMoveDir = ImGuiDir_None;
84188425
g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
8419-
if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8426+
if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
84208427
{
8421-
if ((allowed_dir_flags & (1 << ImGuiDir_Left)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Left; }
8422-
if ((allowed_dir_flags & (1 << ImGuiDir_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Right; }
8423-
if ((allowed_dir_flags & (1 << ImGuiDir_Up)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp, ImGuiNavInput_KeyUp_, ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Up; }
8424-
if ((allowed_dir_flags & (1 << ImGuiDir_Down)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Down; }
8428+
if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Left; }
8429+
if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Right; }
8430+
if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp, ImGuiNavInput_KeyUp_, ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Up; }
8431+
if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) { g.NavMoveDir = ImGuiDir_Down; }
84258432
}
84268433
g.NavMoveClipDir = g.NavMoveDir;
84278434
}
@@ -8438,7 +8445,7 @@ static void ImGui::NavUpdate()
84388445
// FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
84398446
float nav_scoring_rect_offset_y = 0.0f;
84408447
if (nav_keyboard_active)
8441-
nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags);
8448+
nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
84428449

84438450
// If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match
84448451
if (g.NavMoveDir != ImGuiDir_None)
@@ -8585,7 +8592,7 @@ static void ImGui::NavUpdateMoveResult()
85858592
}
85868593

85878594
// Handle PageUp/PageDown/Home/End keys
8588-
static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
8595+
static float ImGui::NavUpdatePageUpPageDown()
85898596
{
85908597
ImGuiContext& g = *GImGui;
85918598
if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
@@ -8594,10 +8601,10 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
85948601
return 0.0f;
85958602

85968603
ImGuiWindow* window = g.NavWindow;
8597-
const bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up));
8598-
const bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down));
8599-
const bool home_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_Home]) && (allowed_dir_flags & (1 << ImGuiDir_Up));
8600-
const bool end_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_End]) && (allowed_dir_flags & (1 << ImGuiDir_Down));
8604+
const bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
8605+
const bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
8606+
const bool home_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
8607+
const bool end_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
86018608
if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
86028609
{
86038610
if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)

imgui.h

-1
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,6 @@ enum ImGuiNavInput_
986986
// [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them.
987987
// Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[].
988988
ImGuiNavInput_KeyMenu_, // toggle menu // = io.KeyAlt
989-
ImGuiNavInput_KeyTab_, // tab // = Tab key
990989
ImGuiNavInput_KeyLeft_, // move left // = Arrow keys
991990
ImGuiNavInput_KeyRight_, // move right
992991
ImGuiNavInput_KeyUp_, // move up

imgui_internal.h

+12-6
Original file line numberDiff line numberDiff line change
@@ -900,8 +900,9 @@ struct ImGuiContext
900900
bool ActiveIdHasBeenPressedBefore; // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch.
901901
bool ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state.
902902
bool ActiveIdHasBeenEditedThisFrame;
903-
int ActiveIdAllowNavDirFlags; // Active widget allows using directional navigation (e.g. can activate a button and move away from it)
904-
int ActiveIdBlockNavInputFlags;
903+
ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those directional navigation requests (e.g. can activate a button and move away from it)
904+
ImU32 ActiveIdUsingNavInputMask; // Active widget will want to read those nav inputs.
905+
ImU64 ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array.
905906
ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)
906907
ImGuiWindow* ActiveIdWindow;
907908
ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard)
@@ -1092,8 +1093,9 @@ struct ImGuiContext
10921093
ActiveIdHasBeenPressedBefore = false;
10931094
ActiveIdHasBeenEditedBefore = false;
10941095
ActiveIdHasBeenEditedThisFrame = false;
1095-
ActiveIdAllowNavDirFlags = 0x00;
1096-
ActiveIdBlockNavInputFlags = 0x00;
1096+
ActiveIdUsingNavDirMask = 0x00;
1097+
ActiveIdUsingNavInputMask = 0x00;
1098+
ActiveIdUsingKeyInputMask = 0x00;
10971099
ActiveIdClickOffset = ImVec2(-1,-1);
10981100
ActiveIdWindow = NULL;
10991101
ActiveIdSource = ImGuiInputSource_None;
@@ -1584,9 +1586,13 @@ namespace ImGui
15841586
IMGUI_API void SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel);
15851587

15861588
// Inputs
1589+
// FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
1590+
inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; }
1591+
inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; }
1592+
inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; IM_ASSERT(key < 64); return (g.ActiveIdUsingKeyInputMask & ((ImU64)1 << key)) != 0; }
15871593
IMGUI_API bool IsMouseDragPastThreshold(int button, float lock_threshold = -1.0f);
1588-
inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { const int key_index = GImGui->IO.KeyMap[key]; return (key_index >= 0) ? IsKeyPressed(key_index, repeat) : false; }
1589-
inline bool IsNavInputDown(ImGuiNavInput n) { return GImGui->IO.NavInputs[n] > 0.0f; }
1594+
inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { ImGuiContext& g = *GImGui; const int key_index = g.IO.KeyMap[key]; return (key_index >= 0) ? IsKeyPressed(key_index, repeat) : false; }
1595+
inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; }
15901596
inline bool IsNavInputPressed(ImGuiNavInput n, ImGuiInputReadMode mode) { return GetNavInputAmount(n, mode) > 0.0f; }
15911597
inline bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiInputReadMode mode) { return (GetNavInputAmount(n1, mode) + GetNavInputAmount(n2, mode)) > 0.0f; }
15921598

imgui_widgets.cpp

+12-8
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,6 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
557557
SetActiveID(id, window);
558558
if ((nav_activated_by_code || nav_activated_by_inputs) && !(flags & ImGuiButtonFlags_NoNavFocus))
559559
SetFocusID(id, window);
560-
g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
561560
}
562561
}
563562

@@ -2093,7 +2092,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, floa
20932092
SetActiveID(id, window);
20942093
SetFocusID(id, window);
20952094
FocusWindow(window);
2096-
g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
2095+
g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
20972096
if (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavInputId == id)
20982097
{
20992098
temp_input_start = true;
@@ -2542,7 +2541,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co
25422541
SetActiveID(id, window);
25432542
SetFocusID(id, window);
25442543
FocusWindow(window);
2545-
g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
2544+
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
25462545
if (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id)
25472546
{
25482547
temp_input_start = true;
@@ -2696,7 +2695,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
26962695
SetActiveID(id, window);
26972696
SetFocusID(id, window);
26982697
FocusWindow(window);
2699-
g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
2698+
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
27002699
}
27012700

27022701
// Draw frame
@@ -3505,12 +3504,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
35053504
SetActiveID(id, window);
35063505
SetFocusID(id, window);
35073506
FocusWindow(window);
3507+
3508+
// Declare our inputs
35083509
IM_ASSERT(ImGuiNavInput_COUNT < 32);
3509-
g.ActiveIdBlockNavInputFlags = (1 << ImGuiNavInput_Cancel);
3510+
if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory))
3511+
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
3512+
g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);
3513+
g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Home) | ((ImU64)1 << ImGuiKey_End);
3514+
if (is_multiline)
3515+
g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_PageUp) | ((ImU64)1 << ImGuiKey_PageDown); // FIXME-NAV: Page up/down actually not supported yet by widget, but claim them ahead.
35103516
if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character.
3511-
g.ActiveIdBlockNavInputFlags |= (1 << ImGuiNavInput_KeyTab_);
3512-
if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory))
3513-
g.ActiveIdAllowNavDirFlags = ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down));
3517+
g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Tab);
35143518
}
35153519

35163520
// We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)

0 commit comments

Comments
 (0)