#include #include #include #include namespace XCEngine { namespace UI { namespace Text { namespace { using XCEngine::Input::KeyCode; } // namespace void ClampCaret(UITextInputState& state) { state.caret = (std::min)(state.caret, state.value.size()); } bool InsertCharacter(UITextInputState& state, std::uint32_t character) { if (character < 32u || character == 127u) { return false; } std::string encoded = {}; AppendUtf8Codepoint(encoded, character); if (encoded.empty()) { return false; } ClampCaret(state); state.value.insert(state.caret, encoded); state.caret += encoded.size(); return true; } UITextInputEditResult HandleKeyDown( UITextInputState& state, std::int32_t keyCode, const UIInputModifiers& modifiers, const UITextInputOptions& options) { ClampCaret(state); UITextInputEditResult result = {}; if (keyCode == static_cast(KeyCode::Backspace)) { result.handled = true; if (state.caret > 0u) { const std::size_t previousCaret = RetreatUtf8Offset(state.value, state.caret); state.value.erase(previousCaret, state.caret - previousCaret); state.caret = previousCaret; result.valueChanged = true; } return result; } if (keyCode == static_cast(KeyCode::Delete)) { result.handled = true; if (state.caret < state.value.size()) { const std::size_t nextCaret = AdvanceUtf8Offset(state.value, state.caret); state.value.erase(state.caret, nextCaret - state.caret); result.valueChanged = true; } return result; } if (keyCode == static_cast(KeyCode::Left)) { result.handled = true; state.caret = RetreatUtf8Offset(state.value, state.caret); return result; } if (keyCode == static_cast(KeyCode::Right)) { result.handled = true; state.caret = AdvanceUtf8Offset(state.value, state.caret); return result; } if (keyCode == static_cast(KeyCode::Up) && options.multiline) { result.handled = true; state.caret = MoveCaretVertically(state.value, state.caret, -1); return result; } if (keyCode == static_cast(KeyCode::Down) && options.multiline) { result.handled = true; state.caret = MoveCaretVertically(state.value, state.caret, 1); return result; } if (keyCode == static_cast(KeyCode::Home)) { result.handled = true; state.caret = options.multiline ? FindLineStartOffset(state.value, state.caret) : 0u; return result; } if (keyCode == static_cast(KeyCode::End)) { result.handled = true; state.caret = options.multiline ? FindLineEndOffset(state.value, state.caret) : state.value.size(); return result; } if (keyCode == static_cast(KeyCode::Enter)) { result.handled = true; if (options.multiline) { state.value.insert(state.caret, "\n"); ++state.caret; result.valueChanged = true; } else { result.submitRequested = true; } return result; } if (keyCode == static_cast(KeyCode::Tab) && options.multiline && !modifiers.control && !modifiers.alt && !modifiers.super) { result.handled = true; if (modifiers.shift) { const std::size_t lineStart = FindLineStartOffset(state.value, state.caret); std::size_t removed = 0u; while (removed < options.tabWidth && lineStart + removed < state.value.size() && state.value[lineStart + removed] == ' ') { ++removed; } if (removed > 0u) { state.value.erase(lineStart, removed); state.caret = state.caret >= lineStart + removed ? state.caret - removed : lineStart; result.valueChanged = true; } } else { state.value.insert(state.caret, std::string(options.tabWidth, ' ')); state.caret += options.tabWidth; result.valueChanged = true; } return result; } return result; } } // namespace Text } // namespace UI } // namespace XCEngine