Skip to content

Commit

Permalink
Text input: Add Ctrl/Alt word navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
glebm committed Oct 30, 2023
1 parent 55b0fc5 commit 2b0876e
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 25 deletions.
19 changes: 11 additions & 8 deletions Source/DiabloUI/text_input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,24 @@ bool HandleInputEvent(const SDL_Event &event, TextInputState &state,
tl::function_ref<bool(std::string_view)> typeFn,
[[maybe_unused]] tl::function_ref<bool(std::string_view)> assignFn)
{
const bool isShift = (SDL_GetModState() & KMOD_SHIFT) != 0;
const auto modState = SDL_GetModState();
const bool isCtrl = (modState & KMOD_CTRL) != 0;
const bool isAlt = (modState & KMOD_ALT) != 0;
const bool isShift = (modState & KMOD_SHIFT) != 0;
switch (event.type) {
case SDL_KEYDOWN: {
switch (event.key.keysym.sym) {
#ifndef USE_SDL1
case SDLK_c:
if ((SDL_GetModState() & KMOD_CTRL) != 0) {
if (isCtrl) {
const std::string selectedText { state.selectedText() };
if (SDL_SetClipboardText(selectedText.c_str()) < 0) {
Log("{}", SDL_GetError());
}
}
return true;
case SDLK_x:
if ((SDL_GetModState() & KMOD_CTRL) != 0) {
if (isCtrl) {
const std::string selectedText { state.selectedText() };
if (SDL_SetClipboardText(selectedText.c_str()) < 0) {
Log("{}", SDL_GetError());
Expand All @@ -49,7 +52,7 @@ bool HandleInputEvent(const SDL_Event &event, TextInputState &state,
}
return true;
case SDLK_v:
if ((SDL_GetModState() & KMOD_CTRL) != 0) {
if (isCtrl) {
if (SDL_HasClipboardText() == SDL_TRUE) {
std::unique_ptr<char, SDLFreeDeleter<char>> clipboard { SDL_GetClipboardText() };
if (clipboard == nullptr || *clipboard == '\0') {
Expand All @@ -62,16 +65,16 @@ bool HandleInputEvent(const SDL_Event &event, TextInputState &state,
return true;
#endif
case SDLK_BACKSPACE:
state.backspace();
state.backspace(/*word=*/isCtrl || isAlt);
return true;
case SDLK_DELETE:
state.del();
state.del(/*word=*/isCtrl || isAlt);
return true;
case SDLK_LEFT:
isShift ? state.moveSelectCursorLeft() : state.moveCursorLeft();
isShift ? state.moveSelectCursorLeft(/*word=*/isCtrl || isAlt) : state.moveCursorLeft(/*word=*/isCtrl || isAlt);
return true;
case SDLK_RIGHT:
isShift ? state.moveSelectCursorRight() : state.moveCursorRight();
isShift ? state.moveSelectCursorRight(/*word=*/isCtrl || isAlt) : state.moveCursorRight(/*word=*/isCtrl || isAlt);
return true;
case SDLK_HOME:
isShift ? state.setSelectCursorToStart() : state.setCursorToStart();
Expand Down
71 changes: 54 additions & 17 deletions Source/DiabloUI/text_input.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,33 +206,28 @@ class TextInputState {
cursor_->position += value_.size() - prevSize;
}

void backspace()
void backspace(bool word)
{
if (cursor_->selection.empty()) {
if (cursor_->position == 0)
return;
cursor_->selection.begin = FindLastUtf8Symbols(beforeCursor());
cursor_->selection.begin = prevPosition(word);
cursor_->selection.end = cursor_->position;
}
eraseSelection();
}

void del()
void del(bool word)
{
if (cursor_->selection.empty()) {
if (cursor_->position == value_.size())
return;
cursor_->selection.begin = cursor_->position;
cursor_->selection.end = cursor_->position + Utf8CodePointLen(afterCursor().data());
cursor_->selection.end = nextPosition(word);
}
eraseSelection();
}

void delSelection()
{
value_.erase(cursor_->selection.begin, cursor_->selection.size());
}

void setCursorToStart()
{
cursor_->position = 0;
Expand Down Expand Up @@ -265,20 +260,20 @@ class TextInputState {
cursor_->selection.end = cursor_->position = value_.size();
}

void moveCursorLeft()
void moveCursorLeft(bool word)
{
cursor_->selection.clear();
if (cursor_->position == 0)
return;
const size_t newPosition = FindLastUtf8Symbols(beforeCursor());
const size_t newPosition = prevPosition(word);
cursor_->position = newPosition;
}

void moveSelectCursorLeft()
void moveSelectCursorLeft(bool word)
{
if (cursor_->position == 0)
return;
const size_t newPosition = FindLastUtf8Symbols(beforeCursor());
const size_t newPosition = prevPosition(word);
if (cursor_->selection.empty()) {
cursor_->selection.begin = newPosition;
cursor_->selection.end = cursor_->position;
Expand All @@ -290,20 +285,20 @@ class TextInputState {
cursor_->position = newPosition;
}

void moveCursorRight()
void moveCursorRight(bool word)
{
cursor_->selection.clear();
if (cursor_->position == value_.size())
return;
const size_t newPosition = cursor_->position + Utf8CodePointLen(afterCursor().data());
const size_t newPosition = nextPosition(word);
cursor_->position = newPosition;
}

void moveSelectCursorRight()
void moveSelectCursorRight(bool word)
{
if (cursor_->position == value_.size())
return;
const size_t newPosition = cursor_->position + Utf8CodePointLen(afterCursor().data());
const size_t newPosition = nextPosition(word);
if (cursor_->selection.empty()) {
cursor_->selection.begin = cursor_->position;
cursor_->selection.end = newPosition;
Expand All @@ -316,6 +311,48 @@ class TextInputState {
}

private:
[[nodiscard]] static bool isWordSeparator(unsigned char c)
{
const bool isAsciiWordChar = (c >= '0' && c <= '9')
|| (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_';
return c <= '\x7E' && !isAsciiWordChar;
}

[[nodiscard]] size_t prevPosition(bool word) const
{
const std::string_view str = beforeCursor();
size_t pos = FindLastUtf8Symbols(str);
if (!word)
return pos;
while (pos > 0 && isWordSeparator(str[pos])) {
pos = FindLastUtf8Symbols({ str.data(), pos });
}
while (pos > 0) {
const size_t prevPos = FindLastUtf8Symbols({ str.data(), pos });
if (isWordSeparator(str[prevPos]))
break;
pos = prevPos;
}
return pos;
}

[[nodiscard]] size_t nextPosition(bool word) const
{
const std::string_view str = afterCursor();
size_t pos = Utf8CodePointLen(str.data());
if (!word)
return cursor_->position + pos;
while (pos < str.size() && isWordSeparator(str[pos])) {
pos += Utf8CodePointLen(str.data() + pos);
}
while (pos < str.size()) {
pos += Utf8CodePointLen(str.data() + pos);
if (isWordSeparator(str[pos]))
break;
}
return cursor_->position + pos;
}

[[nodiscard]] std::string_view beforeCursor() const
{
return value().substr(0, cursor_->position);
Expand Down

0 comments on commit 2b0876e

Please sign in to comment.