diff --git a/imgui.cpp b/imgui.cpp index a4e6ed615..0cb5d7d8d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3106,7 +3106,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) if (id) { g.ActiveIdIsAlive = id; - g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; } // Clear declaration of inputs claimed by the widget @@ -7063,6 +7063,7 @@ void ImGui::ActivateItem(ImGuiID id) { ImGuiContext& g = *GImGui; g.NavNextActivateId = id; + g.NavNextActivateFlags = ImGuiActivateFlags_None; } void ImGui::PushFocusScope(ImGuiID id) @@ -9232,19 +9233,28 @@ static void ImGui::NavUpdate() NavUpdateCancelRequest(); // Process manual activation request - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavActivateInputId = 0; + g.NavActivateFlags = ImGuiActivateFlags_None; if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); + bool input_down = IsNavInputDown(ImGuiNavInput_Input); bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); + bool input_pressed = input_down && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); if (g.ActiveId == 0 && activate_pressed) + { g.NavActivateId = g.NavId; + g.NavActivateFlags = ImGuiActivateFlags_PreferTweak; + } + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed) + { + g.NavActivateInputId = g.NavId; + g.NavActivateFlags = ImGuiActivateFlags_PreferInput; + } if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) g.NavActivateDownId = g.NavId; if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) g.NavActivatePressedId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) - g.NavInputId = g.NavId; } if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) g.NavDisableHighlight = true; @@ -9254,7 +9264,13 @@ static void ImGui::NavUpdate() // Process programmatic activation request // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others) if (g.NavNextActivateId != 0) - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; + { + if (g.NavNextActivateFlags & ImGuiActivateFlags_PreferInput) + g.NavActivateInputId = g.NavNextActivateId; + else + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId; + g.NavActivateFlags = g.NavNextActivateFlags; + } g.NavNextActivateId = 0; // Process move requests @@ -11393,7 +11409,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); Text("NavInputSource: %s", input_source_names[g.NavInputSource]); Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); - Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); + Text("NavActivateId/DownId/PressedId/InputId: %08X/%08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId, g.NavActivateInputId); + Text("NavActivateFlags: %04X", g.NavActivateFlags); Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId); Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); diff --git a/imgui_internal.h b/imgui_internal.h index c010a9805..c5d7d7471 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -143,6 +143,7 @@ struct ImGuiWindowSettings; // Storage for a window .ini settings (we ke // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical +typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() @@ -1148,6 +1149,14 @@ struct ImGuiPtrOrIndex // [SECTION] Navigation support //----------------------------------------------------------------------------- +enum ImGuiActivateFlags_ +{ + ImGuiActivateFlags_None = 0, + ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default if keyboard is available. + ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default if keyboard is not available. + ImGuiActivateFlags_TryToPreserveState = 1 << 2 // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) +}; + enum ImGuiNavHighlightFlags_ { ImGuiNavHighlightFlags_None = 0, @@ -1498,12 +1507,14 @@ struct ImGuiContext ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem() ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_Activate) ? NavId : 0 ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0 - ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0 + ImGuiID NavActivateInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0; ImGuiActivateFlags_PreferInput will be set and NavActivateId will be 0. + ImGuiActivateFlags NavActivateFlags; ImGuiID NavJustTabbedId; // Just tabbed to this id. 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). ImGuiKeyModFlags NavJustMovedToKeyMods; ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. + ImGuiActivateFlags NavNextActivateFlags; ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. 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. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing @@ -1708,8 +1719,9 @@ struct ImGuiContext CurrentItemFlags = ImGuiItemFlags_None; NavWindow = NULL; - NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; + NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0; NavJustTabbedId = NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; + NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; NavJustMovedToKeyMods = ImGuiKeyModFlags_None; NavInputSource = ImGuiInputSource_None; NavLayer = ImGuiNavLayer_Main; diff --git a/imgui_tables.cpp b/imgui_tables.cpp index e70a4dea0..b60676a69 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1479,6 +1479,7 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows) table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b // Ensure frozen columns are ordered in their section. We still allow multiple frozen columns to be reordered. + // FIXME-TABLE: This work for preserving 2143 into 21|43. How about 4321 turning into 21|43? (preserve relative order in each section) for (int column_n = 0; column_n < table->FreezeColumnsRequest; column_n++) { int order_n = table->DisplayOrderToIndex[column_n]; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 234ba585e..6e6d679de 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -273,6 +273,7 @@ void ImGui::TextV(const char* fmt, va_list args) if (window->SkipItems) return; + // FIXME-OPT: Handle the %s shortcut? ImGuiContext& g = *GImGui; const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); TextEx(g.TempBuffer, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); @@ -2405,20 +2406,23 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, const bool focus_requested = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]); - if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavInputId == id) + if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavInputId == id)) - temp_input_is_active = true; + if (temp_input_allowed) + if (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) + temp_input_is_active = true; } + // Experimental: simple click (without moving) turns Drag into an InputText if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active) if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) { - g.NavInputId = id; + g.NavActivateId = g.NavActivateInputId = id; + g.NavActivateFlags = ImGuiActivateFlags_PreferInput; temp_input_is_active = true; } } @@ -3019,13 +3023,13 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat { const bool focus_requested = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id) + if (focus_requested || clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id)) + if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)) temp_input_is_active = true; } } @@ -3177,7 +3181,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d format = PatchFormatStringFloatToInt(format); const bool hovered = ItemHoverable(frame_bb, id); - if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) + if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); @@ -4019,7 +4023,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool focus_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); + const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); 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);