157 lines
4.5 KiB
C++
157 lines
4.5 KiB
C++
#include <XCEngine/UI/Text/UITextInputController.h>
|
|
|
|
#include <XCEngine/Input/InputTypes.h>
|
|
#include <XCEngine/UI/Text/UITextEditing.h>
|
|
|
|
#include <algorithm>
|
|
|
|
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<std::int32_t>(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<std::int32_t>(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<std::int32_t>(KeyCode::Left)) {
|
|
result.handled = true;
|
|
state.caret = RetreatUtf8Offset(state.value, state.caret);
|
|
return result;
|
|
}
|
|
|
|
if (keyCode == static_cast<std::int32_t>(KeyCode::Right)) {
|
|
result.handled = true;
|
|
state.caret = AdvanceUtf8Offset(state.value, state.caret);
|
|
return result;
|
|
}
|
|
|
|
if (keyCode == static_cast<std::int32_t>(KeyCode::Up) && options.multiline) {
|
|
result.handled = true;
|
|
state.caret = MoveCaretVertically(state.value, state.caret, -1);
|
|
return result;
|
|
}
|
|
|
|
if (keyCode == static_cast<std::int32_t>(KeyCode::Down) && options.multiline) {
|
|
result.handled = true;
|
|
state.caret = MoveCaretVertically(state.value, state.caret, 1);
|
|
return result;
|
|
}
|
|
|
|
if (keyCode == static_cast<std::int32_t>(KeyCode::Home)) {
|
|
result.handled = true;
|
|
state.caret = options.multiline
|
|
? FindLineStartOffset(state.value, state.caret)
|
|
: 0u;
|
|
return result;
|
|
}
|
|
|
|
if (keyCode == static_cast<std::int32_t>(KeyCode::End)) {
|
|
result.handled = true;
|
|
state.caret = options.multiline
|
|
? FindLineEndOffset(state.value, state.caret)
|
|
: state.value.size();
|
|
return result;
|
|
}
|
|
|
|
if (keyCode == static_cast<std::int32_t>(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<std::int32_t>(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
|