|
|
@@ -56,93 +56,107 @@ void TextField::onSelectText(const event::SelectText& e) { |
|
|
|
} |
|
|
|
|
|
|
|
void TextField::onSelectKey(const event::SelectKey& e) { |
|
|
|
auto cursorToPrevWord = [&]() { |
|
|
|
size_t pos = text.rfind(' ', std::max(cursor - 2, 0)); |
|
|
|
if (pos == std::string::npos) |
|
|
|
cursor = 0; |
|
|
|
else |
|
|
|
cursor = pos; |
|
|
|
}; |
|
|
|
auto cursorToNextWord = [&]() { |
|
|
|
size_t pos = text.find(' ', std::min(cursor + 1, (int) text.size())); |
|
|
|
if (pos == std::string::npos) |
|
|
|
pos = text.size(); |
|
|
|
cursor = pos; |
|
|
|
}; |
|
|
|
|
|
|
|
if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { |
|
|
|
// Backspace |
|
|
|
if (e.key == GLFW_KEY_BACKSPACE && (e.mods & RACK_MOD_MASK) == 0) { |
|
|
|
if (cursor == selection) { |
|
|
|
cursor--; |
|
|
|
if (cursor >= 0) { |
|
|
|
text.erase(cursor, 1); |
|
|
|
event::Change eChange; |
|
|
|
onChange(eChange); |
|
|
|
} |
|
|
|
selection = cursor; |
|
|
|
cursor = std::max(cursor - 1, 0); |
|
|
|
} |
|
|
|
else { |
|
|
|
int begin = std::min(cursor, selection); |
|
|
|
text.erase(begin, std::abs(selection - cursor)); |
|
|
|
event::Change eChange; |
|
|
|
onChange(eChange); |
|
|
|
cursor = selection = begin; |
|
|
|
insertText(""); |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Ctrl+Backspace |
|
|
|
if (e.key == GLFW_KEY_BACKSPACE && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { |
|
|
|
if (cursor == selection) { |
|
|
|
cursorToPrevWord(); |
|
|
|
} |
|
|
|
insertText(""); |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Delete |
|
|
|
if (e.key == GLFW_KEY_DELETE && (e.mods & RACK_MOD_MASK) == 0) { |
|
|
|
if (cursor == selection) { |
|
|
|
text.erase(cursor, 1); |
|
|
|
event::Change eChange; |
|
|
|
onChange(eChange); |
|
|
|
cursor = std::min(cursor + 1, (int) text.size()); |
|
|
|
} |
|
|
|
else { |
|
|
|
int begin = std::min(cursor, selection); |
|
|
|
text.erase(begin, std::abs(selection - cursor)); |
|
|
|
event::Change eChange; |
|
|
|
onChange(eChange); |
|
|
|
cursor = selection = begin; |
|
|
|
insertText(""); |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Ctrl+Delete |
|
|
|
if (e.key == GLFW_KEY_DELETE && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { |
|
|
|
if (cursor == selection) { |
|
|
|
cursorToNextWord(); |
|
|
|
} |
|
|
|
insertText(""); |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Left |
|
|
|
if (e.key == GLFW_KEY_LEFT) { |
|
|
|
if (e.mods & RACK_MOD_CTRL) { |
|
|
|
while (--cursor > 0) { |
|
|
|
if (text[cursor - 1] == ' ') |
|
|
|
break; |
|
|
|
} |
|
|
|
if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { |
|
|
|
cursorToPrevWord(); |
|
|
|
} |
|
|
|
else { |
|
|
|
cursor--; |
|
|
|
cursor = std::max(cursor - 1, 0); |
|
|
|
} |
|
|
|
if (!(e.mods & GLFW_MOD_SHIFT)) { |
|
|
|
selection = cursor; |
|
|
|
} |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Right |
|
|
|
if (e.key == GLFW_KEY_RIGHT) { |
|
|
|
if (e.mods & RACK_MOD_CTRL) { |
|
|
|
while (++cursor < (int) text.size()) { |
|
|
|
if (text[cursor] == ' ') |
|
|
|
break; |
|
|
|
} |
|
|
|
if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { |
|
|
|
cursorToNextWord(); |
|
|
|
} |
|
|
|
else { |
|
|
|
cursor++; |
|
|
|
cursor = std::min(cursor + 1, (int) text.size()); |
|
|
|
} |
|
|
|
if (!(e.mods & GLFW_MOD_SHIFT)) { |
|
|
|
selection = cursor; |
|
|
|
} |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Home |
|
|
|
if (e.key == GLFW_KEY_HOME && (e.mods & RACK_MOD_MASK) == 0) { |
|
|
|
selection = cursor = 0; |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Shift+Home |
|
|
|
if (e.key == GLFW_KEY_HOME && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) { |
|
|
|
cursor = 0; |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// End |
|
|
|
if (e.key == GLFW_KEY_END && (e.mods & RACK_MOD_MASK) == 0) { |
|
|
|
selection = cursor = text.size(); |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Shift+End |
|
|
|
if (e.key == GLFW_KEY_END && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) { |
|
|
|
cursor = text.size(); |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Ctrl+V |
|
|
|
if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { |
|
|
|
const char* newText = glfwGetClipboardString(APP->window->win); |
|
|
|
if (newText) |
|
|
|
insertText(newText); |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Ctrl+C |
|
|
|
if (e.keyName == "x" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { |
|
|
|
if (cursor != selection) { |
|
|
|
int begin = std::min(cursor, selection); |
|
|
@@ -152,6 +166,7 @@ void TextField::onSelectKey(const event::SelectKey& e) { |
|
|
|
} |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Ctrl+C |
|
|
|
if (e.keyName == "c" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { |
|
|
|
if (cursor != selection) { |
|
|
|
int begin = std::min(cursor, selection); |
|
|
@@ -160,10 +175,12 @@ void TextField::onSelectKey(const event::SelectKey& e) { |
|
|
|
} |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Ctrl+A |
|
|
|
if (e.keyName == "a" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { |
|
|
|
selectAll(); |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Enter |
|
|
|
if ((e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) && (e.mods & RACK_MOD_MASK) == 0) { |
|
|
|
if (multiline) { |
|
|
|
insertText("\n"); |
|
|
@@ -178,30 +195,41 @@ void TextField::onSelectKey(const event::SelectKey& e) { |
|
|
|
if (e.keyName != "") { |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
// Esc |
|
|
|
if (e.key == GLFW_KEY_ESCAPE && (e.mods & RACK_MOD_MASK) == 0) { |
|
|
|
APP->event->setSelected(NULL); |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
|
|
|
|
cursor = math::clamp(cursor, 0, (int) text.size()); |
|
|
|
selection = math::clamp(selection, 0, (int) text.size()); |
|
|
|
assert(0 <= cursor); |
|
|
|
assert(cursor <= (int) text.size()); |
|
|
|
assert(0 <= selection); |
|
|
|
assert(selection <= (int) text.size()); |
|
|
|
} |
|
|
|
|
|
|
|
// Consume ALL keys with ALL actions. Is this bad? I don't know, need more testing. |
|
|
|
// Consume ALL keys with ALL actions |
|
|
|
e.consume(this); |
|
|
|
} |
|
|
|
|
|
|
|
void TextField::insertText(std::string text) { |
|
|
|
bool changed = false; |
|
|
|
if (cursor != selection) { |
|
|
|
// Delete selected text |
|
|
|
int begin = std::min(cursor, selection); |
|
|
|
this->text.erase(begin, std::abs(selection - cursor)); |
|
|
|
cursor = selection = begin; |
|
|
|
changed = true; |
|
|
|
} |
|
|
|
if (!text.empty()) { |
|
|
|
this->text.insert(cursor, text); |
|
|
|
cursor += text.size(); |
|
|
|
selection = cursor; |
|
|
|
changed = true; |
|
|
|
} |
|
|
|
if (changed) { |
|
|
|
event::Change eChange; |
|
|
|
onChange(eChange); |
|
|
|
} |
|
|
|
this->text.insert(cursor, text); |
|
|
|
cursor += text.size(); |
|
|
|
selection = cursor; |
|
|
|
event::Change eChange; |
|
|
|
onChange(eChange); |
|
|
|
} |
|
|
|
|
|
|
|
void TextField::setText(std::string text) { |
|
|
|