From 628bd9b187995e8b28c96c5a4f550fed9fb209ae Mon Sep 17 00:00:00 2001 From: ronak69 Date: Fri, 4 Oct 2024 19:36:58 +0000 Subject: [PATCH 1/2] TestEngine: Add ability to hold the last click in MouseClickMulti() Add an optional bool parameter `hold` to specify whether to submit the final mouse up event. --- imgui_test_engine/imgui_te_context.cpp | 6 ++++-- imgui_test_engine/imgui_te_context.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/imgui_test_engine/imgui_te_context.cpp b/imgui_test_engine/imgui_te_context.cpp index 1f03845f..62ed642b 100644 --- a/imgui_test_engine/imgui_te_context.cpp +++ b/imgui_test_engine/imgui_te_context.cpp @@ -2153,7 +2153,7 @@ void ImGuiTestContext::MouseClick(ImGuiMouseButton button) } // TODO: click time argument (seconds and/or frames) -void ImGuiTestContext::MouseClickMulti(ImGuiMouseButton button, int count) +void ImGuiTestContext::MouseClickMulti(ImGuiMouseButton button, int count, bool hold) { if (IsError()) return; @@ -2181,7 +2181,9 @@ void ImGuiTestContext::MouseClickMulti(ImGuiMouseButton button, int count) Yield(2); // Leave enough time for non-alive IDs to expire. (#5325) else Yield(); - Inputs->MouseButtonsValue = 0; + + if (n != count - 1 || !hold) + Inputs->MouseButtonsValue = 0; if (EngineIO->ConfigRunSpeed != ImGuiTestRunSpeed_Fast) Yield(2); // Not strictly necessary but covers more variant. diff --git a/imgui_test_engine/imgui_te_context.h b/imgui_test_engine/imgui_te_context.h index 2f70afa1..2236236b 100644 --- a/imgui_test_engine/imgui_te_context.h +++ b/imgui_test_engine/imgui_te_context.h @@ -347,7 +347,7 @@ struct IMGUI_API ImGuiTestContext void MouseMoveToPos(ImVec2 pos); void MouseTeleportToPos(ImVec2 pos, ImGuiTestOpFlags flags = ImGuiTestOpFlags_None); void MouseClick(ImGuiMouseButton button = 0); - void MouseClickMulti(ImGuiMouseButton button, int count); + void MouseClickMulti(ImGuiMouseButton button, int count, bool hold = false); void MouseDoubleClick(ImGuiMouseButton button = 0); void MouseDown(ImGuiMouseButton button = 0); void MouseUp(ImGuiMouseButton button = 0); From b3971f2b6cef2145db3640c64234355f1906f28b Mon Sep 17 00:00:00 2001 From: ronak69 Date: Fri, 4 Oct 2024 19:44:38 +0000 Subject: [PATCH 2/2] TestSuite: Add tests for InputText word/line selection modes --- .../imgui_tests_widgets_inputtext.cpp | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/imgui_test_suite/imgui_tests_widgets_inputtext.cpp b/imgui_test_suite/imgui_tests_widgets_inputtext.cpp index c5647d64..76bc794d 100644 --- a/imgui_test_suite/imgui_tests_widgets_inputtext.cpp +++ b/imgui_test_suite/imgui_tests_widgets_inputtext.cpp @@ -895,6 +895,246 @@ void RegisterTests_WidgetsInputText(ImGuiTestEngine* e) } }; +#if IMGUI_VERSION_NUM >= 19130 + // ## Test mouse double/triple-click word/line selection modes. (#8032) + t = IM_REGISTER_TEST(e, "widgets", "widgets_inputtext_selection_modes"); + struct InputTextSelectionVars { Str str; ImVec2 top_left; }; + t->SetVarsDataType([](auto* ctx, auto& vars) + { + vars.str = "Hello world! What's-up? good/bad\n" + "http://example.com/sub-domain <- url\n" + "#hashtag g/^re$/p (foo@bar) 9+10 $100\n" + " ints & floats \n" + "1. Earth 2. Moon 3. Sun\n"; + }); + t->GuiFunc = [](ImGuiTestContext* ctx) + { + InputTextSelectionVars& vars = ctx->GetVars(); + ImGui::Begin("Test Window", NULL, ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize); + ImGui::InputTextMultiline("Field", &vars.str, ImVec2(300, 100)); + vars.top_left = ImGui::GetItemRectMin(); + ImGui::End(); + }; + t->TestFunc = [](ImGuiTestContext* ctx) + { + InputTextSelectionVars& vars = ctx->GetVars(); + ctx->SetRef("Test Window"); + ctx->ItemClick("Field"); + ImGuiInputTextState* state = ImGui::GetInputTextState(ctx->GetID("Field")); + ImStb::STB_TexteditState& stb = *state->Stb; + + ImVector newline_indices; + for (int i = 0; i < vars.str.length(); ++i) + { + if (vars.str[i] == '\n') + newline_indices.push_back(i); + } + + const auto& index_pos = [&](int index) -> ImVec2 + { + int base = -1; + int row = 0; + for (int newline_index : newline_indices) + { + if (index <= newline_index) + break; + + base = newline_index; + ++row; + } + + return vars.top_left + (ImGui::CalcTextSize("") * row) + ImGui::CalcTextSize(vars.str.c_str() + base + 1, vars.str.c_str() + index + 1); + }; + + // Double click on "W[|h]at" + // Quirk: "[|W]hat" will (incorrectly) do "world[!|]" + ctx->MouseMoveToPos(index_pos(14)); + ctx->MouseDoubleClick(); + // Expected selection: "[What's-up?|]" + IM_CHECK_EQ(stb.select_start, 13); + IM_CHECK_EQ(stb.select_end, 23); + IM_CHECK_EQ(stb.cursor, stb.select_end); + + ctx->MouseClick(); IM_CHECK_EQ(state->HasSelection(), false); + + // Quirk: cursor is always on the left side of first line selection + ctx->MouseClickMulti(0, 3); + IM_CHECK_EQ(stb.select_start, 33); + IM_CHECK_EQ(stb.select_end, 0); + IM_CHECK_EQ(stb.cursor, stb.select_end); + + ctx->MouseClick(); IM_CHECK_EQ(state->HasSelection(), false); + + // Double click and hold on "(f[|o]o@" + ctx->MouseMoveToPos(index_pos(90)); + ctx->MouseClickMulti(0, 2, true); + // Verify selection mode states + IM_CHECK_EQ(state->SelectionClicks, 2); + IM_CHECK_EQ(state->SelectionOrigin, 90); + // Expected selection: "([foo@bar)|]" + IM_CHECK_EQ(stb.select_start, 89); + IM_CHECK_EQ(stb.select_end, 97); + IM_CHECK_EQ(stb.cursor, stb.select_end); + + // Mouse move to "2. |Moon" + ctx->MouseMoveToPos(index_pos(138)); + // Expected selection: "... 2. Moon|]" + IM_CHECK_EQ(stb.select_start, 89); + IM_CHECK_EQ(stb.select_end, 142); + IM_CHECK_EQ(stb.cursor, stb.select_end); + + // Mouse move to "worl|d!" + ctx->MouseMoveToPos(index_pos(10)); + // Expected selection: "[|world! ..." + IM_CHECK_EQ(stb.select_start, 97); + IM_CHECK_EQ(stb.select_end, 6); + IM_CHECK_EQ(stb.cursor, stb.select_end); + + // Mouse move to "(|foo@" + ctx->MouseMoveToPos(index_pos(89)); + // Expected selection: "([|foo@bar)]" + IM_CHECK_EQ(stb.select_start, 97); + IM_CHECK_EQ(stb.select_end, 89); + IM_CHECK_EQ(stb.cursor, stb.select_end); + + ctx->MouseUp(); + IM_CHECK_EQ(state->SelectionClicks, 0); + + // Double click and hold on "ints| &" + ctx->MouseMoveToPos(index_pos(114)); + ctx->MouseClickMulti(0, 2, true); + // Verify selection mode states + IM_CHECK_EQ(state->SelectionClicks, 2); + IM_CHECK_EQ(state->SelectionOrigin, 114); + // Expected selection: "[ints|]" + IM_CHECK_EQ(stb.select_start, 110); + IM_CHECK_EQ(stb.select_end, 114); + IM_CHECK_EQ(stb.cursor, stb.select_end); + + // Mouse move to "|world" + ctx->MouseMoveToPos(index_pos(6)); + // Expected selection: "[|world ... ints]" + IM_CHECK_EQ(stb.select_start, 114); + IM_CHECK_EQ(stb.select_end, 6); + IM_CHECK_EQ(stb.cursor, stb.select_end); + + ctx->MouseUp(); + IM_CHECK_EQ(state->SelectionClicks, 0); + + // Triple click and hold on "ex|ample" + ctx->MouseMoveToPos(index_pos(42)); + ctx->MouseClickMulti(0, 3, true); + IM_CHECK_EQ(state->SelectionClicks, 3); + IM_CHECK_EQ(state->SelectionOrigin, 42); + // Expected selection: "[|http ... url\n]" + IM_CHECK_EQ(stb.select_start, 70); + IM_CHECK_EQ(stb.select_end, 33); + IM_CHECK_EQ(stb.cursor, stb.select_end); + + // Mouse move to "good|/bad" + ctx->MouseMoveToPos(index_pos(28)); + // Expected selection: "[|Hello ... url\n]" + IM_CHECK_EQ(stb.select_start, 70); + IM_CHECK_EQ(stb.select_end, 0); + IM_CHECK_EQ(stb.cursor, stb.select_end); + + // Mouse move to "f|loats" + ctx->MouseMoveToPos(index_pos(118)); + // Expected selection: "[http ... floats \n|]" + IM_CHECK_EQ(stb.select_start, 33); + IM_CHECK_EQ(stb.select_end, 126); + IM_CHECK_EQ(stb.cursor, stb.select_end); + + // Mouse move to "<|- url" + ctx->MouseMoveToPos(index_pos(64)); + // Expected selection: "[http ... url\n|]" + IM_CHECK_EQ(stb.select_start, 33); + IM_CHECK_EQ(stb.select_end, 70); + IM_CHECK_EQ(stb.cursor, stb.select_end); + + ctx->MouseUp(); + IM_CHECK_EQ(state->SelectionClicks, 0); + + // Double click and hold on "domain| " + ctx->MouseMoveToPos(index_pos(62)); + ctx->MouseClickMulti(0, 2, true); + // Verify selection mode states + IM_CHECK_EQ(state->SelectionClicks, 2); + IM_CHECK_EQ(state->SelectionOrigin, 62); + // Expected selection: "[domain|]" + IM_CHECK_EQ(stb.select_start, 52); + IM_CHECK_EQ(stb.select_end, 62); + IM_CHECK_EQ(stb.cursor, stb.select_end); + // Verify that text operations cancel ongoing selection + ctx->KeyPress(ImGuiKey_Backspace); + IM_CHECK_EQ(state->SelectionClicks, 0); + IM_CHECK_EQ(state->HasSelection(), false); + IM_CHECK_EQ(stb.cursor, 52); + ctx->MouseUp(); + + // Triple click and hold on "com/|" + ctx->MouseMoveToPos(index_pos(52)); + ctx->MouseClickMulti(0, 3, true); + IM_CHECK_EQ(state->SelectionClicks, 3); + IM_CHECK_EQ(state->SelectionOrigin, 52); + // Expected selection: "[|http ... url\n]" + IM_CHECK_EQ(stb.select_start, 60); + IM_CHECK_EQ(stb.select_end, 33); + IM_CHECK_EQ(stb.cursor, stb.select_end); + ctx->KeyChars("line replace while mouse down"); + ctx->KeyPress(ImGuiKey_Enter); + IM_CHECK_EQ(state->SelectionClicks, 0); + IM_CHECK_EQ(state->HasSelection(), false); + IM_CHECK_EQ(stb.cursor, 63); + ctx->MouseUp(); + + // Should update newline_indices when text changes. Skip for now. + + // Double click and hold on "whi|le" + ctx->MouseMoveToPos(index_pos(49)); + ctx->MouseClickMulti(0, 2, true); + IM_CHECK_EQ(state->SelectionClicks, 2); + IM_CHECK_EQ(state->SelectionOrigin, 49); + // Expected selection: "[while|]" + IM_CHECK_EQ(stb.select_start, 46); + IM_CHECK_EQ(stb.select_end, 51); + IM_CHECK_EQ(stb.cursor, stb.select_end); + // Verify keyboard selection still works + ctx->KeyPress(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_RightArrow, 3); + // Expected selection: "[while ... down|]" + IM_CHECK_EQ(stb.select_start, 46); + IM_CHECK_EQ(stb.select_end, 62); + IM_CHECK_EQ(stb.cursor, stb.select_end); + // Expected selection: "[while mouse |]" + ctx->KeyPress(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_LeftArrow); + IM_CHECK_EQ(stb.select_start, 46); + IM_CHECK_EQ(stb.select_end, 58); + IM_CHECK_EQ(stb.cursor, stb.select_end); + ctx->MouseUp(); + + // Double click and hold on "whi|le" + ctx->MouseMoveToPos(index_pos(49)); + ctx->MouseClickMulti(0, 2, true); + ctx->MouseMoveToPos(index_pos(48)); + // Expected selection: "[|while]" + IM_CHECK_EQ(stb.select_start, 51); + IM_CHECK_EQ(stb.select_end, 46); + IM_CHECK_EQ(stb.cursor, stb.select_end); + // Verify keyboard selection still works + ctx->KeyPress(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_LeftArrow, 2); + // Expected selection: "[|line ... while]" + IM_CHECK_EQ(stb.select_start, 51); + IM_CHECK_EQ(stb.select_end, 33); + IM_CHECK_EQ(stb.cursor, stb.select_end); + ctx->KeyPress(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_RightArrow); + // Expected selection: "[|replace while]" + IM_CHECK_EQ(stb.select_start, 51); + IM_CHECK_EQ(stb.select_end, 38); + IM_CHECK_EQ(stb.cursor, stb.select_end); + ctx->MouseUp(); + }; +#endif + // ## Verify that text selection does not leak spaces in password fields. (#4155) t = IM_REGISTER_TEST(e, "widgets", "widgets_inputtext_password"); t->SetVarsDataType();