1
0
mirror of https://github.com/ocornut/imgui.git synced 2024-11-28 09:30:56 +01:00

Nav: refactor SetKeyboardFocusHere() into using Nav facility. Fix it for clipped items. (#343, #4079, #2352, #432)

+ Removed references to counter used by previous implementation of SetKeyboardFocus functions (the TabStop ones will be removed after)
This commit is contained in:
ocornut 2021-10-06 18:33:01 +02:00
parent fbe78b1a3a
commit 31d033c9d8
5 changed files with 113 additions and 57 deletions

View File

@ -57,6 +57,13 @@ Other Changes:
- TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform - TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform
to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615)
- Disabled: Added assert guard for mismatching BeginDisabled()/EndDisabled() blocks. (#211) - Disabled: Added assert guard for mismatching BeginDisabled()/EndDisabled() blocks. (#211)
- Nav: Fixed using SetKeyboardFocusHere() on non-visible/clipped items. It now works and will scroll
toward the item. When called during a frame where the parent window is appearing, scrolling will
aim to center the item in the window. When calling during a frame where the parent window is already
visible, scrolling will aim to scroll as little as possible to make the item visible. We will later
expose scroll functions and flags in public API to select those behaviors. (#343, #4079, #2352)
- Nav: Fixed using SetKeyboardFocusHere() from activating a different item on the next frame if
submitted items have changed during that frame. (#432)
- Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when - Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when
the NavEnableSetMousePos config flag is set. the NavEnableSetMousePos config flag is set.
- Nav: Fixed a few widgets from not setting reference keyboard/gamepad navigation ID when - Nav: Fixed a few widgets from not setting reference keyboard/gamepad navigation ID when

View File

@ -3303,7 +3303,6 @@ void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id)
// Increment counters // Increment counters
// FIXME: ImGuiItemFlags_Disabled should disable more. // FIXME: ImGuiItemFlags_Disabled should disable more.
const bool is_tab_stop = (g.LastItemData.InFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; const bool is_tab_stop = (g.LastItemData.InFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
window->DC.FocusCounterRegular++;
if (is_tab_stop) if (is_tab_stop)
{ {
window->DC.FocusCounterTabStop++; window->DC.FocusCounterTabStop++;
@ -3322,11 +3321,6 @@ void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id)
// Handle focus requests // Handle focus requests
if (g.TabFocusRequestCurrWindow == window) if (g.TabFocusRequestCurrWindow == window)
{ {
if (window->DC.FocusCounterRegular == g.TabFocusRequestCurrCounterRegular)
{
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByCode;
return;
}
if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop) if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop)
{ {
g.NavJustTabbedId = id; // FIXME-NAV: aim to eventually set in NavUpdate() once we finish the refactor g.NavJustTabbedId = id; // FIXME-NAV: aim to eventually set in NavUpdate() once we finish the refactor
@ -3843,7 +3837,6 @@ void ImGui::UpdateTabFocus()
// - Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also // - Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
// manipulate the Next fields here even though they will be turned into Curr fields below. // manipulate the Next fields here even though they will be turned into Curr fields below.
g.TabFocusRequestNextWindow = g.NavWindow; g.TabFocusRequestNextWindow = g.NavWindow;
g.TabFocusRequestNextCounterRegular = INT_MAX;
if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + (g.IO.KeyShift ? -1 : 0); g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + (g.IO.KeyShift ? -1 : 0);
else else
@ -3852,17 +3845,15 @@ void ImGui::UpdateTabFocus()
// Turn queued focus request into current one // Turn queued focus request into current one
g.TabFocusRequestCurrWindow = NULL; g.TabFocusRequestCurrWindow = NULL;
g.TabFocusRequestCurrCounterRegular = g.TabFocusRequestCurrCounterTabStop = INT_MAX; g.TabFocusRequestCurrCounterTabStop = INT_MAX;
if (g.TabFocusRequestNextWindow != NULL) if (g.TabFocusRequestNextWindow != NULL)
{ {
ImGuiWindow* window = g.TabFocusRequestNextWindow; ImGuiWindow* window = g.TabFocusRequestNextWindow;
g.TabFocusRequestCurrWindow = window; g.TabFocusRequestCurrWindow = window;
if (g.TabFocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
g.TabFocusRequestCurrCounterRegular = ImModPositive(g.TabFocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1) if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1); g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
g.TabFocusRequestNextWindow = NULL; g.TabFocusRequestNextWindow = NULL;
g.TabFocusRequestNextCounterRegular = g.TabFocusRequestNextCounterTabStop = INT_MAX; g.TabFocusRequestNextCounterTabStop = INT_MAX;
} }
g.NavIdTabCounter = INT_MAX; g.NavIdTabCounter = INT_MAX;
@ -6316,7 +6307,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->DC.CurrentColumns = NULL; window->DC.CurrentColumns = NULL;
window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.LayoutType = ImGuiLayoutType_Vertical;
window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1; window->DC.FocusCounterTabStop = -1;
window->DC.ItemWidth = window->ItemWidthDefault; window->DC.ItemWidth = window->ItemWidthDefault;
window->DC.TextWrapPos = -1.0f; // disabled window->DC.TextWrapPos = -1.0f; // disabled
@ -7082,12 +7073,16 @@ void ImGui::PopFocusScope()
void ImGui::SetKeyboardFocusHere(int offset) void ImGui::SetKeyboardFocusHere(int offset)
{ {
IM_ASSERT(offset >= -1); // -1 is allowed but not below
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
g.TabFocusRequestNextWindow = window; IM_ASSERT(offset >= -1); // -1 is allowed but not below
g.TabFocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset; g.NavWindow = window;
g.TabFocusRequestNextCounterTabStop = INT_MAX; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_None, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
if (offset == -1)
NavMoveRequestResolveWithLastItem();
else
g.NavTabbingInputableRemaining = offset + 1;
} }
void ImGui::SetItemDefaultFocus() void ImGui::SetItemDefaultFocus()
@ -8980,6 +8975,7 @@ static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
result->Window = window; result->Window = window;
result->ID = g.LastItemData.ID; result->ID = g.LastItemData.ID;
result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
result->InFlags = g.LastItemData.InFlags;
result->RectRel = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos); result->RectRel = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos);
} }
@ -9014,9 +9010,20 @@ static void ImGui::NavProcessItem()
// FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy) // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
if (g.NavMoveScoringItems) if (g.NavMoveScoringItems)
{ {
if (item_flags & ImGuiItemFlags_Inputable)
g.NavTabbingInputableRemaining--;
if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
{ {
ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing)
{
if (g.NavTabbingInputableRemaining == 0)
NavMoveRequestResolveWithLastItem();
}
else
{
if (NavScoreItem(result)) if (NavScoreItem(result))
NavApplyItemToResult(result); NavApplyItemToResult(result);
@ -9028,6 +9035,7 @@ static void ImGui::NavProcessItem()
NavApplyItemToResult(&g.NavMoveResultLocalVisible); NavApplyItemToResult(&g.NavMoveResultLocalVisible);
} }
} }
}
// Update window-relative bounding box of navigated item // Update window-relative bounding box of navigated item
if (g.NavId == id) if (g.NavId == id)
@ -9051,6 +9059,10 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
IM_ASSERT(g.NavWindow != NULL); IM_ASSERT(g.NavWindow != NULL);
if (move_flags & ImGuiNavMoveFlags_Tabbing)
move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;
g.NavMoveSubmitted = g.NavMoveScoringItems = true; g.NavMoveSubmitted = g.NavMoveScoringItems = true;
g.NavMoveDir = move_dir; g.NavMoveDir = move_dir;
g.NavMoveDirForDebug = move_dir; g.NavMoveDirForDebug = move_dir;
@ -9059,12 +9071,21 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM
g.NavMoveScrollFlags = scroll_flags; g.NavMoveScrollFlags = scroll_flags;
g.NavMoveForwardToNextFrame = false; g.NavMoveForwardToNextFrame = false;
g.NavMoveKeyMods = g.IO.KeyMods; g.NavMoveKeyMods = g.IO.KeyMods;
g.NavTabbingInputableRemaining = 0;
g.NavMoveResultLocal.Clear(); g.NavMoveResultLocal.Clear();
g.NavMoveResultLocalVisible.Clear(); g.NavMoveResultLocalVisible.Clear();
g.NavMoveResultOther.Clear(); g.NavMoveResultOther.Clear();
NavUpdateAnyRequestFlag(); NavUpdateAnyRequestFlag();
} }
void ImGui::NavMoveRequestResolveWithLastItem()
{
ImGuiContext& g = *GImGui;
g.NavMoveScoringItems = false; // Ensure request doesn't need more processing
NavApplyItemToResult(&g.NavMoveResultLocal);
NavUpdateAnyRequestFlag();
}
void ImGui::NavMoveRequestCancel() void ImGui::NavMoveRequestCancel()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
@ -9284,6 +9305,7 @@ static void ImGui::NavUpdate()
// Process navigation move request // Process navigation move request
if (g.NavMoveSubmitted) if (g.NavMoveSubmitted)
NavMoveRequestApplyResult(); NavMoveRequestApplyResult();
g.NavTabbingInputableRemaining = 0;
g.NavMoveSubmitted = g.NavMoveScoringItems = false; g.NavMoveSubmitted = g.NavMoveScoringItems = false;
// Apply application mouse position movement, after we had a chance to process move request result. // Apply application mouse position movement, after we had a chance to process move request result.
@ -9535,7 +9557,9 @@ void ImGui::NavMoveRequestApplyResult()
// In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
if (result == NULL) if (result == NULL)
{ {
if (g.NavId != 0) if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing)
g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight;
if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0)
{ {
g.NavDisableHighlight = false; g.NavDisableHighlight = false;
g.NavDisableMouseHover = true; g.NavDisableMouseHover = true;
@ -9590,9 +9614,27 @@ void ImGui::NavMoveRequestApplyResult()
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name); IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
// Tabbing: Activates Inputable or Focus non-Inputable
if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && (result->InFlags & ImGuiItemFlags_Inputable))
{
g.NavNextActivateId = result->ID;
g.NavNextActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState;
g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight;
}
// Activate
if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)
{
g.NavNextActivateId = result->ID;
g.NavNextActivateFlags = ImGuiActivateFlags_None;
}
// Enable nav highlight // Enable nav highlight
if ((g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0)
{
g.NavDisableHighlight = false; g.NavDisableHighlight = false;
g.NavDisableMouseHover = g.NavMousePosDirty = true; g.NavDisableMouseHover = g.NavMousePosDirty = true;
}
} }
// Process NavCancel input (to close a popup, get back to parent, clear focus) // Process NavCancel input (to close a popup, get back to parent, clear focus)

View File

@ -64,7 +64,7 @@ Index of this file:
// Version // Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
#define IMGUI_VERSION "1.85 WIP" #define IMGUI_VERSION "1.85 WIP"
#define IMGUI_VERSION_NUM 18419 #define IMGUI_VERSION_NUM 18420
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
#define IMGUI_HAS_TABLE #define IMGUI_HAS_TABLE

View File

@ -759,9 +759,7 @@ enum ImGuiItemStatusFlags_
ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag.
ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set.
ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing. ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing.
ImGuiItemStatusFlags_FocusedByCode = 1 << 8, // Set when the Focusable item just got focused from code. ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8 // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon)
ImGuiItemStatusFlags_FocusedByTabbing = 1 << 9, // Set when the Focusable item just got focused by Tabbing.
ImGuiItemStatusFlags_Focused = ImGuiItemStatusFlags_FocusedByCode | ImGuiItemStatusFlags_FocusedByTabbing
#ifdef IMGUI_ENABLE_TEST_ENGINE #ifdef IMGUI_ENABLE_TEST_ENGINE
, // [imgui_tests only] , // [imgui_tests only]
@ -1219,7 +1217,10 @@ enum ImGuiNavMoveFlags_
ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown)
ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary
ImGuiNavMoveFlags_Forwarded = 1 << 7, ImGuiNavMoveFlags_Forwarded = 1 << 7,
ImGuiNavMoveFlags_DebugNoResult = 1 << 8 ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result
ImGuiNavMoveFlags_Tabbing = 1 << 9, // == Focus + Activate if item is Inputable + DontChangeNavHighlight
ImGuiNavMoveFlags_Activate = 1 << 10,
ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 11 // Do not alter the visible state of keyboard vs mouse nav highlight
}; };
enum ImGuiNavLayer enum ImGuiNavLayer
@ -1235,12 +1236,13 @@ struct ImGuiNavItemData
ImGuiID ID; // Init,Move // Best candidate item ID ImGuiID ID; // Init,Move // Best candidate item ID
ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID
ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space
ImGuiItemFlags InFlags; // ????,Move // Best candidate item flags
float DistBox; // Move // Best candidate box distance to current NavId float DistBox; // Move // Best candidate box distance to current NavId
float DistCenter; // Move // Best candidate center distance to current NavId float DistCenter; // Move // Best candidate center distance to current NavId
float DistAxial; // Move // Best candidate axial distance to current NavId float DistAxial; // Move // Best candidate axial distance to current NavId
ImGuiNavItemData() { Clear(); } ImGuiNavItemData() { Clear(); }
void Clear() { Window = NULL; ID = FocusScopeId = 0; RectRel = ImRect(); DistBox = DistCenter = DistAxial = FLT_MAX; } void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; DistBox = DistCenter = DistAxial = FLT_MAX; }
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -1582,6 +1584,7 @@ struct ImGuiContext
ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename?
ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring.
int NavScoringDebugCount; // Metrics for debugging int NavScoringDebugCount; // Metrics for debugging
int NavTabbingInputableRemaining; // >0 when counting items for tabbing
ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow
ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag)
ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag)
@ -1597,9 +1600,7 @@ struct ImGuiContext
// Legacy Focus/Tabbing system (older than Nav, active even if Nav is disabled, misnamed. FIXME-NAV: This needs a redesign!) // Legacy Focus/Tabbing system (older than Nav, active even if Nav is disabled, misnamed. FIXME-NAV: This needs a redesign!)
ImGuiWindow* TabFocusRequestCurrWindow; // ImGuiWindow* TabFocusRequestCurrWindow; //
ImGuiWindow* TabFocusRequestNextWindow; // ImGuiWindow* TabFocusRequestNextWindow; //
int TabFocusRequestCurrCounterRegular; // Any item being requested for focus, stored as an index (we on layout to be stable between the frame pressing TAB and the next frame, semi-ouch)
int TabFocusRequestCurrCounterTabStop; // Tab item being requested for focus, stored as an index int TabFocusRequestCurrCounterTabStop; // Tab item being requested for focus, stored as an index
int TabFocusRequestNextCounterRegular; // Stored for next frame
int TabFocusRequestNextCounterTabStop; // " int TabFocusRequestNextCounterTabStop; // "
bool TabFocusPressed; // Set in NewFrame() when user pressed Tab bool TabFocusPressed; // Set in NewFrame() when user pressed Tab
@ -1787,14 +1788,15 @@ struct ImGuiContext
NavMoveKeyMods = ImGuiKeyModFlags_None; NavMoveKeyMods = ImGuiKeyModFlags_None;
NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
NavScoringDebugCount = 0; NavScoringDebugCount = 0;
NavTabbingInputableRemaining = 0;
NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
NavWindowingToggleLayer = false; NavWindowingToggleLayer = false;
TabFocusRequestCurrWindow = TabFocusRequestNextWindow = NULL; TabFocusRequestCurrWindow = TabFocusRequestNextWindow = NULL;
TabFocusRequestCurrCounterRegular = TabFocusRequestCurrCounterTabStop = INT_MAX; TabFocusRequestCurrCounterTabStop = INT_MAX;
TabFocusRequestNextCounterRegular = TabFocusRequestNextCounterTabStop = INT_MAX; TabFocusRequestNextCounterTabStop = INT_MAX;
TabFocusPressed = false; TabFocusPressed = false;
DimBgRatio = 0.0f; DimBgRatio = 0.0f;
@ -1901,7 +1903,6 @@ struct IMGUI_API ImGuiWindowTempData
int CurrentTableIdx; // Current table index (into g.Tables) int CurrentTableIdx; // Current table index (into g.Tables)
ImGuiLayoutType LayoutType; ImGuiLayoutType LayoutType;
ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin()
int FocusCounterRegular; // (Legacy Focus/Tabbing system) Sequential counter, start at -1 and increase when ImGuiItemFlags_Inputable (FIXME-NAV: Needs redesign)
int FocusCounterTabStop; // (Legacy Focus/Tabbing system) Same, but only count widgets which you can Tab through. int FocusCounterTabStop; // (Legacy Focus/Tabbing system) Same, but only count widgets which you can Tab through.
// Local parameters stacks // Local parameters stacks
@ -2478,9 +2479,9 @@ namespace ImGui
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// Currently refactoring focus/nav/tabbing system // Currently refactoring focus/nav/tabbing system
// If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): // If you have old/custom copy-and-pasted widgets that used FocusableItemRegister():
// (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool focused = FocusableItemRegister(...)' // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)'
// (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0'
// (New) IMGUI_VERSION_NUM >= 18411: using 'ItemAdd(..., ImGuiItemAddFlags_Inputable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedTabbing) != 0 || g.NavActivateInputId == id' (WIP)
// Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText() // Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText()
inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd()
inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem
@ -2522,6 +2523,7 @@ namespace ImGui
IMGUI_API bool NavMoveRequestButNoResultYet(); IMGUI_API bool NavMoveRequestButNoResultYet();
IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
IMGUI_API void NavMoveRequestResolveWithLastItem();
IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestCancel();
IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestApplyResult();
IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);

View File

@ -2407,17 +2407,17 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
if (!temp_input_is_active) if (!temp_input_is_active)
{ {
const bool focus_requested = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0; const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
const bool clicked = (hovered && g.IO.MouseClicked[0]); const bool clicked = (hovered && g.IO.MouseClicked[0]);
const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]); const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]);
if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id) if (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id)
{ {
SetActiveID(id, window); SetActiveID(id, window);
SetFocusID(id, window); SetFocusID(id, window);
FocusWindow(window); FocusWindow(window);
g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
if (temp_input_allowed) if (temp_input_allowed)
if (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id)
temp_input_is_active = true; temp_input_is_active = true;
} }
@ -3025,15 +3025,15 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
if (!temp_input_is_active) if (!temp_input_is_active)
{ {
const bool focus_requested = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0; const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
const bool clicked = (hovered && g.IO.MouseClicked[0]); const bool clicked = (hovered && g.IO.MouseClicked[0]);
if (focus_requested || clicked || g.NavActivateId == id || g.NavActivateInputId == id) if (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id)
{ {
SetActiveID(id, window); SetActiveID(id, window);
SetFocusID(id, window); SetFocusID(id, window);
FocusWindow(window); FocusWindow(window);
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)) if (temp_input_allowed && (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id))
temp_input_is_active = true; temp_input_is_active = true;
} }
} }
@ -4024,21 +4024,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// We are only allowed to access the state if we are already the active widget. // We are only allowed to access the state if we are already the active widget.
ImGuiInputTextState* state = GetInputTextState(id); ImGuiInputTextState* state = GetInputTextState(id);
const bool focus_requested_by_code = (item_status_flags & ImGuiItemStatusFlags_FocusedByCode) != 0; const bool input_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
const bool focus_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard));
const bool user_clicked = hovered && io.MouseClicked[0]; const bool user_clicked = hovered && io.MouseClicked[0];
const bool user_nav_input_start = (g.ActiveId != id) && (g.NavActivateInputId == id || g.NavActivateId == id);
const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);
const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);
bool clear_active_id = false; bool clear_active_id = false;
bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); bool select_all = false;
float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;
const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline);
const bool init_make_active = (user_clicked || user_scroll_finish || user_nav_input_start || focus_requested_by_code || focus_requested_by_tabbing); const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_tabbing);
const bool init_state = (init_make_active || user_scroll_active); const bool init_state = (init_make_active || user_scroll_active);
if ((init_state && g.ActiveId != id) || init_changed_specs) if ((init_state && g.ActiveId != id) || init_changed_specs)
{ {
@ -4074,13 +4072,20 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
state->ID = id; state->ID = id;
state->ScrollX = 0.0f; state->ScrollX = 0.0f;
stb_textedit_initialize_state(&state->Stb, !is_multiline); stb_textedit_initialize_state(&state->Stb, !is_multiline);
if (!is_multiline && focus_requested_by_code) }
if (!is_multiline)
{
if (flags & ImGuiInputTextFlags_AutoSelectAll)
select_all = true;
if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState)))
select_all = true;
if (input_requested_by_tabbing || (user_clicked && io.KeyCtrl))
select_all = true; select_all = true;
} }
if (flags & ImGuiInputTextFlags_AlwaysOverwrite) if (flags & ImGuiInputTextFlags_AlwaysOverwrite)
state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863) state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863)
if (!is_multiline && (focus_requested_by_tabbing || (user_clicked && io.KeyCtrl)))
select_all = true;
} }
if (g.ActiveId != id && init_make_active) if (g.ActiveId != id && init_make_active)
@ -4214,7 +4219,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters.
if (io.InputQueueCharacters.Size > 0) if (io.InputQueueCharacters.Size > 0)
{ {
if (!ignore_char_inputs && !is_readonly && !user_nav_input_start) if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav)
for (int n = 0; n < io.InputQueueCharacters.Size; n++) for (int n = 0; n < io.InputQueueCharacters.Size; n++)
{ {
// Insert character if they pass filtering // Insert character if they pass filtering