Internal: Nav,MultiSelect: import ImGuiSelectionUserData, SetNextItemSelectionUserData() from MultiSelect. Track NavLastValidSelectionUserData as a convenience.

This commit is contained in:
ocornut 2023-09-12 16:48:00 +02:00
parent f336e639e9
commit c86ce70968
4 changed files with 64 additions and 10 deletions

View File

@ -55,7 +55,7 @@ Other changes:
- Tooltips: made using SetItemTooltip()/IsItemHovered(ImGuiHoveredFlags_ForTooltip) defaults to
activate tooltips on disabled items. This is done by adding ImGuiHoveredFlags_AllowWhenDisabled
to the default value of style.HoverFlagsForTooltipMouse/HoverFlagsForTooltipNav. (##1485)
to the default value of style.HoverFlagsForTooltipMouse/HoverFlagsForTooltipNav. (#1485)
- Nav: Tabbing always enable nav highlight when ImGuiConfigFlags_NavEnableKeyboard is set.
Previously was inconsistent and only enabled when stepping through a non-input item.
(#6802, #3092, #5759, #787)

View File

@ -7065,6 +7065,7 @@ void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)
g.NavLayer = ImGuiNavLayer_Main;
g.NavFocusScopeId = window ? window->NavRootFocusScopeId : 0;
g.NavIdIsAlive = false;
g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
// Close popups if any
ClosePopupsOverWindow(window, false);
@ -9489,6 +9490,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu
g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
g.LastItemData.InFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;
g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
// Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
// Directional navigation processing
if (id != 0)
@ -10800,6 +10802,7 @@ void ImGui::SetNavWindow(ImGuiWindow* window)
{
IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : "<NULL>");
g.NavWindow = window;
g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
}
g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
NavUpdateAnyRequestFlag();
@ -11019,6 +11022,11 @@ static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
result->FocusScopeId = g.CurrentFocusScopeId;
result->InFlags = g.LastItemData.InFlags;
result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);
if (result->InFlags & ImGuiItemFlags_HasSelectionUserData)
{
IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
}
}
// True when current work location may be scrolled horizontally when moving left / right.
@ -11031,7 +11039,7 @@ void ImGui::NavUpdateCurrentWindowIsScrollPushableX()
}
// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
// This is called after LastItemData is set.
// This is called after LastItemData is set, but NextItemData is also still valid.
static void ImGui::NavProcessItem()
{
ImGuiContext& g = *GImGui;
@ -11095,6 +11103,11 @@ static void ImGui::NavProcessItem()
g.NavLayer = window->DC.NavLayerCurrent;
g.NavFocusScopeId = g.CurrentFocusScopeId;
g.NavIdIsAlive = true;
if (g.LastItemData.InFlags & ImGuiItemFlags_HasSelectionUserData)
{
IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
}
window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position)
}
}
@ -11206,7 +11219,7 @@ void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGu
ImGuiContext& g = *GImGui;
g.NavMoveScoringItems = false;
g.LastItemData.ID = tree_node_data->ID;
g.LastItemData.InFlags = tree_node_data->InFlags;
g.LastItemData.InFlags = tree_node_data->InFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
g.LastItemData.NavRect = tree_node_data->NavRect;
NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()
NavClearPreferredPosForAxis(ImGuiAxis_Y);
@ -11273,6 +11286,7 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
{
ImGuiWindow* prev_nav_window = g.NavWindow;
g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); // FIXME-NAV: Should clear ongoing nav requests?
g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
if (prev_nav_window)
IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name);
}
@ -11568,6 +11582,8 @@ void ImGui::NavInitRequestApplyResult()
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: 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);
g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result
if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
g.NavLastValidSelectionUserData = result->SelectionUserData;
if (g.NavInitRequestFromMove)
NavRestoreHighlightAfterMove();
}
@ -11799,6 +11815,7 @@ void ImGui::NavMoveRequestApplyResult()
{
IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name);
g.NavWindow = result->Window;
g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
}
if (g.ActiveId != result->ID)
ClearActiveID();
@ -11816,6 +11833,8 @@ 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);
ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];
SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
g.NavLastValidSelectionUserData = result->SelectionUserData;
// Restore last preferred position for current axis
// (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)
@ -14113,6 +14132,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
DebugLocateItemOnHover(g.NavId);
Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource));
Text("NavLastValidSelectionUserData = %" IM_PRId64 " (0x%" IM_PRIX64 ")", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData);
Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);
Text("NavActivateFlags: %04X", g.NavActivateFlags);

View File

@ -309,6 +309,18 @@ namespace ImStb
#endif
#endif // #ifndef IM_DEBUG_BREAK
// Format specifiers, printing 64-bit hasn't been decently standardized...
// In a real application you should be using PRId64 and PRIu64 from <inttypes.h> (non-windows) and on Windows define them yourself.
#if defined(_MSC_VER) && !defined(__clang__)
#define IM_PRId64 "I64d"
#define IM_PRIu64 "I64u"
#define IM_PRIX64 "I64X"
#else
#define IM_PRId64 "lld"
#define IM_PRIu64 "llu"
#define IM_PRIX64 "llX"
#endif
//-----------------------------------------------------------------------------
// [SECTION] Generic helpers
// Note that the ImXXX helpers functions are lower-level than ImGui functions.
@ -810,6 +822,7 @@ enum ImGuiItemFlags_
// Controlled by widget code
ImGuiItemFlags_Inputable = 1 << 10, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
ImGuiItemFlags_HasSelectionUserData = 1 << 11, // false // Set by SetNextItemSelectionUserData()
};
// Status flags for an already submitted item
@ -1174,6 +1187,10 @@ struct ImGuiNextWindowData
inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; }
};
// Multi-Selection item index or identifier when using SetNextItemSelectionUserData()/BeginMultiSelect()
// (Most users are likely to use this store an item INDEX but this may be used to store a POINTER as well.)
typedef ImS64 ImGuiSelectionUserData;
enum ImGuiNextItemDataFlags_
{
ImGuiNextItemDataFlags_None = 0,
@ -1184,13 +1201,14 @@ enum ImGuiNextItemDataFlags_
struct ImGuiNextItemData
{
ImGuiNextItemDataFlags Flags;
ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap.
float Width; // Set by SetNextItemWidth()
ImGuiID FocusScopeId; // Set by SetNextItemMultiSelectData() (!= 0 signify value has been set, so it's an alternate version of HasSelectionData, we don't use Flags for this because they are cleared too early. This is mostly used for debugging)
ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap.
// Non-flags members are NOT cleared by ItemAdd() meaning they are still valid during NavProcessItem()
float Width; // Set by SetNextItemWidth()
ImGuiSelectionUserData SelectionUserData; // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values)
ImGuiCond OpenCond;
bool OpenVal; // Set by SetNextItemOpen()
bool OpenVal; // Set by SetNextItemOpen()
ImGuiNextItemData() { memset(this, 0, sizeof(*this)); }
ImGuiNextItemData() { memset(this, 0, sizeof(*this)); SelectionUserData = -1; }
inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()!
};
@ -1528,12 +1546,13 @@ struct ImGuiNavItemData
ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID
ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space
ImGuiItemFlags InFlags; // ????,Move // Best candidate item flags
ImGuiSelectionUserData SelectionUserData;//I+Mov // Best candidate SetNextItemSelectionData() value.
float DistBox; // Move // Best candidate box distance to current NavId
float DistCenter; // Move // Best candidate center distance to current NavId
float DistAxial; // Move // Best candidate axial distance to current NavId
ImGuiNavItemData() { Clear(); }
void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; DistBox = DistCenter = DistAxial = FLT_MAX; }
void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; }
};
//-----------------------------------------------------------------------------
@ -1631,6 +1650,9 @@ struct ImGuiOldColumns
// [SECTION] Multi-select support
//-----------------------------------------------------------------------------
// We always assume that -1 is an invalid value (which works for indices and pointers)
#define ImGuiSelectionUserData_Invalid ((ImGuiSelectionUserData)-1)
#ifdef IMGUI_HAS_MULTI_SELECT
// <this is filled in 'range_select' branch>
#endif // #ifdef IMGUI_HAS_MULTI_SELECT
@ -1939,6 +1961,7 @@ struct ImGuiContext
ImGuiActivateFlags NavNextActivateFlags;
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse
ImGuiNavLayer 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.
ImGuiSelectionUserData NavLastValidSelectionUserData; // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRectRel is valid
bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default)
bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover)
@ -2186,6 +2209,7 @@ struct ImGuiContext
NavJustMovedToKeyMods = ImGuiMod_None;
NavInputSource = ImGuiInputSource_Keyboard;
NavLayer = ImGuiNavLayer_Main;
NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
NavIdIsAlive = false;
NavMousePosDirty = false;
NavDisableHighlight = true;
@ -3241,6 +3265,7 @@ namespace ImGui
IMGUI_API void TreePushOverrideID(ImGuiID id);
IMGUI_API void TreeNodeSetOpen(ImGuiID id, bool open);
IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging.
IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data);
// Template functions are instantiated in imgui_widgets.cpp for a finite number of types.
// To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036).

View File

@ -19,6 +19,7 @@ Index of this file:
// [SECTION] Widgets: TreeNode, CollapsingHeader, etc.
// [SECTION] Widgets: Selectable
// [SECTION] Widgets: Typing-Select support
// [SECTION] Widgets: Multi-Select support
// [SECTION] Widgets: ListBox
// [SECTION] Widgets: PlotLines, PlotHistogram
// [SECTION] Widgets: Value helpers
@ -6759,7 +6760,15 @@ int ImGui::TypingSelectFindResult(ImGuiTypingSelectRequest* req, int items_count
// [SECTION] Widgets: Multi-Select support
//-------------------------------------------------------------------------
//
void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data)
{
// Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code!
// This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api.
ImGuiContext& g = *GImGui;
g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData;
g.NextItemData.SelectionUserData = selection_user_data;
}
//-------------------------------------------------------------------------
// [SECTION] Widgets: ListBox