RangeSelect/MultiSelect: Transition to use FocusScope bits merged in master.

Preserve ability to shift+arrow into an item that is part of FocusScope but doesn't carry a selection without breaking selection.
This commit is contained in:
omar 2020-01-13 15:05:53 +01:00 committed by ocornut
parent abfa8487eb
commit dd52a2854c
3 changed files with 49 additions and 31 deletions

View File

@ -4554,7 +4554,6 @@ void ImGui::Shutdown(ImGuiContext* context)
g.ClipboardHandlerData.clear(); g.ClipboardHandlerData.clear();
g.MenusIdSubmittedThisFrame.clear(); g.MenusIdSubmittedThisFrame.clear();
g.InputTextState.ClearFreeMemory(); g.InputTextState.ClearFreeMemory();
g.MultiSelectScopeWindow = NULL;
g.SettingsWindows.clear(); g.SettingsWindows.clear();
g.SettingsHandlers.clear(); g.SettingsHandlers.clear();
@ -9735,6 +9734,7 @@ static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
result->InFlags = g.LastItemData.InFlags; result->InFlags = g.LastItemData.InFlags;
result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect); result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);
result->HasSelectionData = (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasSelectionData) != 0; // FIXME: Bizarre but valid we are calling NavApplyItemToResult() before clering the NextItemData
} }
// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
@ -10491,6 +10491,7 @@ void ImGui::NavMoveRequestApplyResult()
g.NavJustMovedToId = result->ID; g.NavJustMovedToId = result->ID;
g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToFocusScopeId = result->FocusScopeId;
g.NavJustMovedToKeyMods = g.NavMoveKeyMods; g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
g.NavJustMovedToHasSelectionData = result->HasSelectionData;
} }
// Focus // Focus

View File

@ -1082,9 +1082,10 @@ struct ImGuiNextWindowData
enum ImGuiNextItemDataFlags_ enum ImGuiNextItemDataFlags_
{ {
ImGuiNextItemDataFlags_None = 0, ImGuiNextItemDataFlags_None = 0,
ImGuiNextItemDataFlags_HasWidth = 1 << 0, ImGuiNextItemDataFlags_HasWidth = 1 << 0,
ImGuiNextItemDataFlags_HasOpen = 1 << 1 ImGuiNextItemDataFlags_HasOpen = 1 << 1,
ImGuiNextItemDataFlags_HasSelectionData = 1 << 2
}; };
struct ImGuiNextItemData struct ImGuiNextItemData
@ -1094,7 +1095,6 @@ struct ImGuiNextItemData
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) 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)
ImGuiCond OpenCond; ImGuiCond OpenCond;
bool OpenVal; // Set by SetNextItemOpen() bool OpenVal; // Set by SetNextItemOpen()
ImGuiID MultiSelectScopeId; // Set by SetNextItemMultiSelectData()
void* SelectionData; // Set by SetNextItemSelectionData() (note that NULL/0 is a valid value) void* SelectionData; // Set by SetNextItemSelectionData() (note that NULL/0 is a valid value)
ImGuiNextItemData() { memset(this, 0, sizeof(*this)); } ImGuiNextItemData() { memset(this, 0, sizeof(*this)); }
@ -1338,9 +1338,10 @@ struct ImGuiNavItemData
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
bool HasSelectionData; // Move // Copy of (NextItemData.Flags & ImGuiNextItemDataFlags_HasSelection)
ImGuiNavItemData() { Clear(); } 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; DistBox = DistCenter = DistAxial = FLT_MAX; HasSelectionData = false; }
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -1403,18 +1404,19 @@ struct ImGuiOldColumns
// [SECTION] Multi-select support // [SECTION] Multi-select support
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
//#define IMGUI_HAS_MULTI_SELECT 1
#ifdef IMGUI_HAS_MULTI_SELECT #ifdef IMGUI_HAS_MULTI_SELECT
struct IMGUI_API ImGuiMultiSelectState struct IMGUI_API ImGuiMultiSelectState
{ {
ImGuiID FocusScopeId; // Same as CurrentWindow->DC.FocusScopeIdCurrent (unless another selection scope was pushed manually)
ImGuiID BackupFocusScopeId;
ImGuiMultiSelectData In; // The In requests are set and returned by BeginMultiSelect() ImGuiMultiSelectData In; // The In requests are set and returned by BeginMultiSelect()
ImGuiMultiSelectData Out; // The Out requests are finalized and returned by EndMultiSelect() ImGuiMultiSelectData Out; // The Out requests are finalized and returned by EndMultiSelect()
bool InRangeDstPassedBy; // (Internal) set by the the item that match NavJustMovedToId when InRequestRangeSetNav is set. bool InRangeDstPassedBy; // (Internal) set by the the item that match NavJustMovedToId when InRequestRangeSetNav is set.
bool InRequestSetRangeNav; // (Internal) set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. bool InRequestSetRangeNav; // (Internal) set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation.
ImGuiMultiSelectState() { Clear(); } ImGuiMultiSelectState() { Clear(); }
void Clear() { In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } void Clear() { FocusScopeId = BackupFocusScopeId = 0; In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; }
}; };
#endif // #ifdef IMGUI_HAS_MULTI_SELECT #endif // #ifdef IMGUI_HAS_MULTI_SELECT
@ -1672,6 +1674,7 @@ 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).
ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest). ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest).
ImGuiKeyModFlags NavJustMovedToKeyMods; ImGuiKeyModFlags NavJustMovedToKeyMods;
bool NavJustMovedToHasSelectionData; // " (FIXME-NAV: We should maybe just store ImGuiNavMoveResult)
ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame.
ImGuiActivateFlags NavNextActivateFlags; ImGuiActivateFlags NavNextActivateFlags;
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard.
@ -1715,10 +1718,9 @@ struct ImGuiContext
bool NavWindowingToggleLayer; bool NavWindowingToggleLayer;
// Range-Select/Multi-Select // Range-Select/Multi-Select
ImGuiID MultiSelectScopeId; bool MultiSelectEnabled;
ImGuiWindow* MultiSelectScopeWindow;
ImGuiMultiSelectFlags MultiSelectFlags; ImGuiMultiSelectFlags MultiSelectFlags;
ImGuiMultiSelectState MultiSelectState; ImGuiMultiSelectState MultiSelectState; // We currently don't support recursing/stacking multi-select
// Render // Render
float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list)
@ -1890,6 +1892,7 @@ struct ImGuiContext
NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0;
NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
NavJustMovedToKeyMods = ImGuiKeyModFlags_None; NavJustMovedToKeyMods = ImGuiKeyModFlags_None;
NavJustMovedToHasSelectionData = false;
NavInputSource = ImGuiInputSource_None; NavInputSource = ImGuiInputSource_None;
NavLayer = ImGuiNavLayer_Main; NavLayer = ImGuiNavLayer_Main;
NavIdIsAlive = false; NavIdIsAlive = false;
@ -1915,9 +1918,8 @@ struct ImGuiContext
NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
NavWindowingToggleLayer = false; NavWindowingToggleLayer = false;
MultiSelectScopeId = 0; MultiSelectEnabled = false;
MultiSelectScopeWindow = NULL; MultiSelectFlags = ImGuiMultiSelectFlags_None;
MultiSelectFlags = 0;
DimBgRatio = 0.0f; DimBgRatio = 0.0f;
MouseCursor = ImGuiMouseCursor_Arrow; MouseCursor = ImGuiMouseCursor_Arrow;

View File

@ -5924,7 +5924,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
const bool was_selected = selected; const bool was_selected = selected;
// Multi-selection support (header) // Multi-selection support (header)
const bool is_multi_select = (g.MultiSelectScopeWindow == window); const bool is_multi_select = g.MultiSelectEnabled;
if (is_multi_select) if (is_multi_select)
{ {
flags |= ImGuiTreeNodeFlags_OpenOnArrow; flags |= ImGuiTreeNodeFlags_OpenOnArrow;
@ -6253,7 +6253,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; } if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; }
// Multi-selection support (header) // Multi-selection support (header)
const bool is_multi_select = (g.MultiSelectScopeWindow == window); const bool is_multi_select = g.MultiSelectEnabled;
const bool was_selected = selected; const bool was_selected = selected;
if (is_multi_select) if (is_multi_select)
{ {
@ -6369,17 +6369,20 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags
ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected)
{ {
ImGuiContext& g = *ImGui::GetCurrentContext(); ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
IM_ASSERT(g.MultiSelectScopeId == 0); // No recursion allowed yet (we could allow it if we deem it useful) IM_ASSERT(g.MultiSelectEnabled == false); // No recursion allowed yet (we could allow it if we deem it useful)
IM_ASSERT(g.MultiSelectFlags == 0); IM_ASSERT(g.MultiSelectFlags == 0);
IM_ASSERT(g.MultiSelectState.FocusScopeId == 0);
// FIXME: BeginFocusScope()
ImGuiMultiSelectState* state = &g.MultiSelectState; ImGuiMultiSelectState* state = &g.MultiSelectState;
g.MultiSelectScopeId = window->IDStack.back();
g.MultiSelectScopeWindow = window;
g.MultiSelectFlags = flags;
state->Clear(); state->Clear();
state->BackupFocusScopeId = window->DC.NavFocusScopeIdCurrent;
state->FocusScopeId = window->DC.NavFocusScopeIdCurrent = window->IDStack.back();
g.MultiSelectEnabled = true;
g.MultiSelectFlags = flags;
if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0) if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0)
{ {
@ -6388,7 +6391,7 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void*
} }
// Auto clear when using Navigation to move within the selection (we compare SelectScopeId so it possible to use multiple lists inside a same window) // Auto clear when using Navigation to move within the selection (we compare SelectScopeId so it possible to use multiple lists inside a same window)
if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.MultiSelectScopeId) if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == state->FocusScopeId && g.NavJustMovedToHasSelectionData)
{ {
if (g.IO.KeyShift) if (g.IO.KeyShift)
state->InRequestSetRangeNav = true; state->InRequestSetRangeNav = true;
@ -6411,14 +6414,19 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void*
ImGuiMultiSelectData* ImGui::EndMultiSelect() ImGuiMultiSelectData* ImGui::EndMultiSelect()
{ {
ImGuiContext& g = *ImGui::GetCurrentContext(); ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiMultiSelectState* state = &g.MultiSelectState; ImGuiMultiSelectState* state = &g.MultiSelectState;
IM_ASSERT(g.MultiSelectScopeId != 0);
IM_ASSERT(g.MultiSelectState.FocusScopeId != 0);
IM_ASSERT(g.MultiSelectState.FocusScopeId == window->DC.NavFocusScopeIdCurrent);
if (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect) if (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect)
state->Out.RangeValue = true; state->Out.RangeValue = true;
g.MultiSelectScopeId = 0; g.MultiSelectState.FocusScopeId = 0;
g.MultiSelectScopeWindow = NULL; window->DC.NavFocusScopeIdCurrent = g.MultiSelectState.BackupFocusScopeId;
g.MultiSelectFlags = 0; g.MultiSelectEnabled = false;
g.MultiSelectFlags = ImGuiMultiSelectFlags_None;
#ifdef IMGUI_DEBUG_MULTISELECT #ifdef IMGUI_DEBUG_MULTISELECT
if (state->Out.RequestClear) printf("[%05d] EndMultiSelect: RequestClear\n", g.FrameCount); if (state->Out.RequestClear) printf("[%05d] EndMultiSelect: RequestClear\n", g.FrameCount);
@ -6432,17 +6440,24 @@ ImGuiMultiSelectData* ImGui::EndMultiSelect()
void ImGui::SetNextItemSelectionData(void* item_data) void ImGui::SetNextItemSelectionData(void* item_data)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
IM_ASSERT(g.MultiSelectScopeId != 0); ImGuiWindow* window = g.CurrentWindow;
IM_ASSERT(window->DC.NavFocusScopeIdCurrent != 0);
g.NextItemData.SelectionData = item_data; g.NextItemData.SelectionData = item_data;
g.NextItemData.MultiSelectScopeId = g.MultiSelectScopeId; g.NextItemData.FocusScopeId = window->DC.NavFocusScopeIdCurrent;
// Note that the flag 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.
g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasSelectionData;
} }
void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiMultiSelectState* state = &g.MultiSelectState; ImGuiMultiSelectState* state = &g.MultiSelectState;
IM_ASSERT(g.NextItemData.MultiSelectScopeId == g.MultiSelectScopeId && "Forgot to call SetNextItemSelectionData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); IM_UNUSED(window);
IM_ASSERT(g.NextItemData.FocusScopeId == window->DC.NavFocusScopeIdCurrent && "Forgot to call SetNextItemSelectionData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope");
void* item_data = g.NextItemData.SelectionData; void* item_data = g.NextItemData.SelectionData;
// Apply Clear/SelectAll requests requested by BeginMultiSelect(). // Apply Clear/SelectAll requests requested by BeginMultiSelect().
@ -6483,7 +6498,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
ImGuiMultiSelectState* state = &g.MultiSelectState; ImGuiMultiSelectState* state = &g.MultiSelectState;
void* item_data = g.NextItemData.SelectionData; void* item_data = g.NextItemData.SelectionData;
g.NextItemData.MultiSelectScopeId = 0; g.NextItemData.FocusScopeId = 0;
bool selected = *p_selected; bool selected = *p_selected;
bool pressed = *p_pressed; bool pressed = *p_pressed;