Extract XCUI text input controller to common layer

This commit is contained in:
2026-04-05 06:33:06 +08:00
parent b4c95e4085
commit 6159eef3af
7 changed files with 354 additions and 123 deletions

View File

@@ -7,6 +7,7 @@ set(UI_TEST_SOURCES
test_layout_engine.cpp
test_ui_runtime.cpp
test_ui_text_editing.cpp
test_ui_text_input_controller.cpp
)
add_executable(core_ui_tests ${UI_TEST_SOURCES})

View File

@@ -0,0 +1,115 @@
#include <gtest/gtest.h>
#include <XCEngine/Input/InputTypes.h>
#include <XCEngine/UI/Text/UITextInputController.h>
namespace {
namespace UIText = XCEngine::UI::Text;
using XCEngine::Input::KeyCode;
TEST(UITextInputControllerTest, InsertCharacterTracksUtf8CaretMovement) {
UIText::UITextInputState state = {};
EXPECT_TRUE(UIText::InsertCharacter(state, 'A'));
EXPECT_TRUE(UIText::InsertCharacter(state, 0x4F60u));
EXPECT_EQ(state.value, std::string("A") + "\xE4\xBD\xA0");
EXPECT_EQ(state.caret, state.value.size());
}
TEST(UITextInputControllerTest, BackspaceAndArrowKeysUseUtf8Boundaries) {
UIText::UITextInputState state = {};
state.value = std::string("A") + "\xE4\xBD\xA0" + "B";
state.caret = state.value.size();
const auto moveLeft = UIText::HandleKeyDown(
state,
static_cast<std::int32_t>(KeyCode::Left),
{},
{});
EXPECT_TRUE(moveLeft.handled);
EXPECT_EQ(state.caret, 4u);
const auto backspace = UIText::HandleKeyDown(
state,
static_cast<std::int32_t>(KeyCode::Backspace),
{},
{});
EXPECT_TRUE(backspace.handled);
EXPECT_TRUE(backspace.valueChanged);
EXPECT_EQ(state.value, "AB");
EXPECT_EQ(state.caret, 1u);
}
TEST(UITextInputControllerTest, SingleLineEnterRequestsSubmitWithoutMutatingValue) {
UIText::UITextInputState state = {};
state.value = "prompt";
state.caret = state.value.size();
const auto result = UIText::HandleKeyDown(
state,
static_cast<std::int32_t>(KeyCode::Enter),
{},
{});
EXPECT_TRUE(result.handled);
EXPECT_FALSE(result.valueChanged);
EXPECT_TRUE(result.submitRequested);
EXPECT_EQ(state.value, "prompt");
}
TEST(UITextInputControllerTest, MultilineEnterAndVerticalMovementStayInController) {
UIText::UITextInputState state = {};
state.value = std::string("A") + "\xE4\xBD\xA0" + "Z\nBC";
state.caret = 4u;
const UIText::UITextInputOptions options = { true, 4u };
const auto moveDown = UIText::HandleKeyDown(
state,
static_cast<std::int32_t>(KeyCode::Down),
{},
options);
EXPECT_TRUE(moveDown.handled);
EXPECT_EQ(state.caret, 8u);
const auto enter = UIText::HandleKeyDown(
state,
static_cast<std::int32_t>(KeyCode::Enter),
{},
options);
EXPECT_TRUE(enter.handled);
EXPECT_TRUE(enter.valueChanged);
EXPECT_EQ(state.value, std::string("A") + "\xE4\xBD\xA0" + "Z\nBC\n");
}
TEST(UITextInputControllerTest, MultilineTabAndShiftTabIndentAndOutdentCurrentLine) {
UIText::UITextInputState state = {};
state.value = "root\nnode";
state.caret = 5u;
const UIText::UITextInputOptions options = { true, 4u };
const auto indent = UIText::HandleKeyDown(
state,
static_cast<std::int32_t>(KeyCode::Tab),
{},
options);
EXPECT_TRUE(indent.handled);
EXPECT_TRUE(indent.valueChanged);
EXPECT_EQ(state.value, "root\n node");
EXPECT_EQ(state.caret, 9u);
XCEngine::UI::UIInputModifiers modifiers = {};
modifiers.shift = true;
const auto outdent = UIText::HandleKeyDown(
state,
static_cast<std::int32_t>(KeyCode::Tab),
modifiers,
options);
EXPECT_TRUE(outdent.handled);
EXPECT_TRUE(outdent.valueChanged);
EXPECT_EQ(state.value, "root\nnode");
EXPECT_EQ(state.caret, 5u);
}
} // namespace