Browse Source

Add event::KeyBase::keyName, which fixes key commands on AZERTY and Dvorak. Add lots of documentation to event::KeyBase.

tags/v2.0.0
Andrew Belt 4 years ago
parent
commit
feece06fbb
9 changed files with 131 additions and 128 deletions
  1. +28
    -4
      include/event.hpp
  2. +10
    -12
      src/app/ModuleBrowser.cpp
  3. +30
    -51
      src/app/ModuleWidget.cpp
  4. +15
    -17
      src/app/RackScrollWidget.cpp
  5. +3
    -7
      src/app/RackWidget.cpp
  6. +18
    -16
      src/app/Scene.cpp
  7. +6
    -0
      src/event.cpp
  8. +7
    -7
      src/ui/ScrollWidget.cpp
  9. +14
    -14
      src/ui/TextField.cpp

+ 28
- 4
include/event.hpp View File

@@ -111,13 +111,37 @@ struct PositionBase {

/** An event prototype with a GLFW key. */
struct KeyBase {
/** GLFW_KEY_* */
/** The key corresponding to what it would be called in its position on a QWERTY US keyboard.
For example, the WASD directional keys used for first-person shooters will always be reported as "WASD", regardless if they say "ZQSD" on an AZERTY keyboard.
You should usually not use these for printable characters such as "Ctrl+V" key commands. Instead, use `keyName`.
You *should* use these for non-printable keys, such as Escape, arrow keys, Home, F1-12, etc.
You should also use this for Enter, Tab, and Space. Although they are printable keys, they do not appear in `keyName`.
See GLFW_KEY_* for the list of possible values.
*/
int key;
/** GLFW_KEY_*. You should usually use `key` instead. */
/** Platform-dependent "software" key code.
This variable is only included for completion. There should be no reason for you to use this.
You should instead use `key` (for non-printable characters) or `keyName` (for printable characters).
Values are platform independent and can change between different keyboards or keyboard layouts on the same OS.
*/
int scancode;
/** GLFW_RELEASE, GLFW_PRESS, GLFW_REPEAT, or RACK_HELD */
/** String containing the lowercase key name, if it produces a printable character.
This is the only variable that correctly represents the label printed on any keyboard layout, whether it's QWERTY, AZERTY, QWERTZ, Dvorak, etc.
For example, if the user presses the key labeled "q" regardless of the key position, `keyName` will be "q".
For non-printable characters this is an empty string.
Enter, Tab, and Space do not give a `keyName`. Use `key` instead.
Shift has no effect on the key name. Shift+1 results in "1", Shift+q results in "q", etc.
*/
std::string keyName;
/** The type of event occurring with the key.
Possible values are GLFW_RELEASE, GLFW_PRESS, GLFW_REPEAT, or RACK_HELD.
RACK_HELD is sent every frame while the key is held.
*/
int action;
/** GLFW_MOD_* */
/** Bitwise OR of key modifiers, such as Ctrl or Shift.
Use (mods & RACK_MOD_MASK) == RACK_MOD_CTRL to check for Ctrl on Linux and Windows but Cmd on Mac.
See GLFW_MOD_* for the list of possible values.
*/
int mods;
};



+ 10
- 12
src/app/ModuleBrowser.cpp View File

@@ -623,19 +623,17 @@ inline void TagItem::step() {

inline void BrowserSearchField::onSelectKey(const event::SelectKey& e) {
if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
switch (e.key) {
case GLFW_KEY_ESCAPE: {
BrowserOverlay* overlay = getAncestorOfType<BrowserOverlay>();
overlay->hide();
if (e.key == GLFW_KEY_ESCAPE) {
BrowserOverlay* overlay = getAncestorOfType<BrowserOverlay>();
overlay->hide();
e.consume(this);
}
if (e.key == GLFW_KEY_BACKSPACE) {
if (text == "") {
ModuleBrowser* browser = getAncestorOfType<ModuleBrowser>();
browser->clear();
e.consume(this);
} break;
case GLFW_KEY_BACKSPACE: {
if (text == "") {
ModuleBrowser* browser = getAncestorOfType<ModuleBrowser>();
browser->clear();
e.consume(this);
}
} break;
}
}
}



+ 30
- 51
src/app/ModuleWidget.cpp View File

@@ -397,61 +397,40 @@ void ModuleWidget::onHoverKey(const event::HoverKey& e) {
return;

if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
switch (e.key) {
case GLFW_KEY_I: {
if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
resetAction();
e.consume(this);
}
} break;
case GLFW_KEY_R: {
if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
randomizeAction();
e.consume(this);
}
} break;
case GLFW_KEY_C: {
if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
copyClipboard();
e.consume(this);
}
} break;
case GLFW_KEY_V: {
if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
pasteClipboardAction();
e.consume(this);
}
} break;
case GLFW_KEY_D: {
if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
cloneAction();
e.consume(this);
}
} break;
case GLFW_KEY_U: {
if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
disconnectAction();
e.consume(this);
}
} break;
case GLFW_KEY_E: {
if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
bypassAction();
e.consume(this);
}
} break;
if (e.keyName == "i" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
resetAction();
e.consume(this);
}
if (e.keyName == "r" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
randomizeAction();
e.consume(this);
}
if (e.keyName == "c" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
copyClipboard();
e.consume(this);
}
if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
pasteClipboardAction();
e.consume(this);
}
if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
cloneAction();
e.consume(this);
}
if (e.keyName == "u" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
disconnectAction();
e.consume(this);
}
if (e.keyName == "e" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
bypassAction();
e.consume(this);
}
}

if (e.action == RACK_HELD) {
switch (e.key) {
case GLFW_KEY_DELETE:
case GLFW_KEY_BACKSPACE: {
if ((e.mods & RACK_MOD_MASK) == 0) {
removeAction();
e.consume(NULL);
}
} break;
if ((e.key == GLFW_KEY_DELETE || e.key == GLFW_KEY_BACKSPACE) && (e.mods & RACK_MOD_MASK) == 0) {
removeAction();
e.consume(NULL);
}
}
}


+ 15
- 17
src/app/RackScrollWidget.cpp View File

@@ -108,23 +108,21 @@ void RackScrollWidget::onHoverKey(const event::HoverKey& e) {
arrowSpeed /= 4.0;

if (e.action == RACK_HELD) {
switch (e.key) {
case GLFW_KEY_LEFT: {
offset.x -= arrowSpeed;
e.consume(this);
} break;
case GLFW_KEY_RIGHT: {
offset.x += arrowSpeed;
e.consume(this);
} break;
case GLFW_KEY_UP: {
offset.y -= arrowSpeed;
e.consume(this);
} break;
case GLFW_KEY_DOWN: {
offset.y += arrowSpeed;
e.consume(this);
} break;
if (e.key == GLFW_KEY_LEFT) {
offset.x -= arrowSpeed;
e.consume(this);
}
if (e.key == GLFW_KEY_RIGHT) {
offset.x += arrowSpeed;
e.consume(this);
}
if (e.key == GLFW_KEY_UP) {
offset.y -= arrowSpeed;
e.consume(this);
}
if (e.key == GLFW_KEY_DOWN) {
offset.y += arrowSpeed;
e.consume(this);
}
}
}


+ 3
- 7
src/app/RackWidget.cpp View File

@@ -121,13 +121,9 @@ void RackWidget::onHoverKey(const event::HoverKey& e) {
return;

if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
switch (e.key) {
case GLFW_KEY_V: {
if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
pastePresetClipboardAction();
e.consume(this);
}
} break;
if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
pastePresetClipboardAction();
e.consume(this);
}
}
}


+ 18
- 16
src/app/Scene.cpp View File

@@ -97,39 +97,40 @@ void Scene::onHoverKey(const event::HoverKey& e) {
return;

if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
if (e.key == GLFW_KEY_N && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
// DEBUG("key '%d '%c' scancode %d '%c' keyName '%s'", e.key, e.key, e.scancode, e.scancode, e.keyName.c_str());
if (e.keyName == "n" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
APP->patch->loadTemplateDialog();
e.consume(this);
}
else if (e.key == GLFW_KEY_Q && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
if (e.keyName == "q" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
APP->window->close();
e.consume(this);
}
else if (e.key == GLFW_KEY_O && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
if (e.keyName == "o" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
APP->patch->loadDialog();
e.consume(this);
}
else if (e.key == GLFW_KEY_O && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
if (e.keyName == "o" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
APP->patch->revertDialog();
e.consume(this);
}
else if (e.key == GLFW_KEY_S && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
APP->patch->saveDialog();
e.consume(this);
}
else if (e.key == GLFW_KEY_S && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
APP->patch->saveAsDialog();
e.consume(this);
}
else if (e.key == GLFW_KEY_Z && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
if (e.keyName == "z" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
APP->history->undo();
e.consume(this);
}
else if (e.key == GLFW_KEY_Z && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
if (e.keyName == "z" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
APP->history->redo();
e.consume(this);
}
else if ((e.key == GLFW_KEY_MINUS || e.key == GLFW_KEY_KP_SUBTRACT) && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
if (e.keyName == "-" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
float zoom = settings::zoom;
zoom *= 2;
zoom = std::ceil(zoom - 0.01f) - 1;
@@ -137,7 +138,8 @@ void Scene::onHoverKey(const event::HoverKey& e) {
settings::zoom = zoom;
e.consume(this);
}
else if ((e.key == GLFW_KEY_EQUAL || e.key == GLFW_KEY_KP_ADD) && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
// Numpad has a "+" key, but the main keyboard section hides it under "="
if ((e.keyName == "=" || e.keyName == "+") && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
float zoom = settings::zoom;
zoom *= 2;
zoom = std::floor(zoom + 0.01f) + 1;
@@ -145,31 +147,31 @@ void Scene::onHoverKey(const event::HoverKey& e) {
settings::zoom = zoom;
e.consume(this);
}
else if ((e.key == GLFW_KEY_0 || e.key == GLFW_KEY_KP_0) && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
if ((e.keyName == "0") && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
settings::zoom = 0.f;
e.consume(this);
}
else if ((e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) && (e.mods & RACK_MOD_MASK) == 0) {
if ((e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) && (e.mods & RACK_MOD_MASK) == 0) {
moduleBrowser->show();
e.consume(this);
}
else if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == 0) {
if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == 0) {
std::thread t([] {
system::openBrowser("https://vcvrack.com/manual/");
});
t.detach();
e.consume(this);
}
else if (e.key == GLFW_KEY_F3 && (e.mods & RACK_MOD_MASK) == 0) {
if (e.key == GLFW_KEY_F3 && (e.mods & RACK_MOD_MASK) == 0) {
settings::cpuMeter ^= true;
e.consume(this);
}
else if (e.key == GLFW_KEY_F11 && (e.mods & RACK_MOD_MASK) == 0) {
if (e.key == GLFW_KEY_F11 && (e.mods & RACK_MOD_MASK) == 0) {
APP->window->setFullScreen(!APP->window->isFullScreen());
e.consume(this);
}
// Alternate key command for exiting fullscreen, since F11 doesn't work reliably on Mac due to "Show desktop" OS binding.
else if (e.key == GLFW_KEY_ESCAPE && (e.mods & RACK_MOD_MASK) == 0) {
if (e.key == GLFW_KEY_ESCAPE && (e.mods & RACK_MOD_MASK) == 0) {
if (APP->window->isFullScreen())
APP->window->setFullScreen(false);
e.consume(this);


+ 6
- 0
src/event.cpp View File

@@ -303,6 +303,9 @@ bool State::handleKey(math::Vec pos, int key, int scancode, int action, int mods
eSelectKey.context = &cSelectKey;
eSelectKey.key = key;
eSelectKey.scancode = scancode;
const char* keyName = glfwGetKeyName(key, scancode);
if (keyName)
eSelectKey.keyName = keyName;
eSelectKey.action = action;
eSelectKey.mods = mods;
selectedWidget->onSelectKey(eSelectKey);
@@ -317,6 +320,9 @@ bool State::handleKey(math::Vec pos, int key, int scancode, int action, int mods
eHoverKey.pos = pos;
eHoverKey.key = key;
eHoverKey.scancode = scancode;
const char* keyName = glfwGetKeyName(key, scancode);
if (keyName)
eHoverKey.keyName = keyName;
eHoverKey.action = action;
eHoverKey.mods = mods;
rootWidget->onHoverKey(eHoverKey);


+ 7
- 7
src/ui/ScrollWidget.cpp View File

@@ -122,34 +122,34 @@ void ScrollWidget::onHoverKey(const event::HoverKey& e) {
offset.y -= box.size.y * 0.5;
e.consume(this);
}
else if (e.key == GLFW_KEY_PAGE_UP && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
if (e.key == GLFW_KEY_PAGE_UP && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
offset.x -= box.size.x * 0.5;
e.consume(this);
}
else if (e.key == GLFW_KEY_PAGE_DOWN && (e.mods & RACK_MOD_MASK) == 0) {
if (e.key == GLFW_KEY_PAGE_DOWN && (e.mods & RACK_MOD_MASK) == 0) {
offset.y += box.size.y * 0.5;
e.consume(this);
}
else if (e.key == GLFW_KEY_PAGE_DOWN && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
if (e.key == GLFW_KEY_PAGE_DOWN && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
offset.x += box.size.x * 0.5;
e.consume(this);
}
else if (e.key == GLFW_KEY_HOME && (e.mods & RACK_MOD_MASK) == 0) {
if (e.key == GLFW_KEY_HOME && (e.mods & RACK_MOD_MASK) == 0) {
math::Rect containerBox = container->getChildrenBoundingBox();
offset.y = containerBox.getTop();
e.consume(this);
}
else if (e.key == GLFW_KEY_HOME && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
if (e.key == GLFW_KEY_HOME && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
math::Rect containerBox = container->getChildrenBoundingBox();
offset.x = containerBox.getLeft();
e.consume(this);
}
else if (e.key == GLFW_KEY_END && (e.mods & RACK_MOD_MASK) == 0) {
if (e.key == GLFW_KEY_END && (e.mods & RACK_MOD_MASK) == 0) {
math::Rect containerBox = container->getChildrenBoundingBox();
offset.y = containerBox.getBottom();
e.consume(this);
}
else if (e.key == GLFW_KEY_END && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
if (e.key == GLFW_KEY_END && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
math::Rect containerBox = container->getChildrenBoundingBox();
offset.x = containerBox.getRight();
e.consume(this);


+ 14
- 14
src/ui/TextField.cpp View File

@@ -76,7 +76,7 @@ void TextField::onSelectKey(const event::SelectKey& e) {
}
e.consume(this);
}
else if (e.key == GLFW_KEY_DELETE && (e.mods & RACK_MOD_MASK) == 0) {
if (e.key == GLFW_KEY_DELETE && (e.mods & RACK_MOD_MASK) == 0) {
if (cursor == selection) {
text.erase(cursor, 1);
event::Change eChange;
@@ -91,7 +91,7 @@ void TextField::onSelectKey(const event::SelectKey& e) {
}
e.consume(this);
}
else if (e.key == GLFW_KEY_LEFT) {
if (e.key == GLFW_KEY_LEFT) {
if (e.mods & RACK_MOD_CTRL) {
while (--cursor > 0) {
if (text[cursor - 1] == ' ')
@@ -106,7 +106,7 @@ void TextField::onSelectKey(const event::SelectKey& e) {
}
e.consume(this);
}
else if (e.key == GLFW_KEY_RIGHT) {
if (e.key == GLFW_KEY_RIGHT) {
if (e.mods & RACK_MOD_CTRL) {
while (++cursor < (int) text.size()) {
if (text[cursor] == ' ')
@@ -121,29 +121,29 @@ void TextField::onSelectKey(const event::SelectKey& e) {
}
e.consume(this);
}
else if (e.key == GLFW_KEY_HOME && (e.mods & RACK_MOD_MASK) == 0) {
if (e.key == GLFW_KEY_HOME && (e.mods & RACK_MOD_MASK) == 0) {
selection = cursor = 0;
e.consume(this);
}
else if (e.key == GLFW_KEY_HOME && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
if (e.key == GLFW_KEY_HOME && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
cursor = 0;
e.consume(this);
}
else if (e.key == GLFW_KEY_END && (e.mods & RACK_MOD_MASK) == 0) {
if (e.key == GLFW_KEY_END && (e.mods & RACK_MOD_MASK) == 0) {
selection = cursor = text.size();
e.consume(this);
}
else if (e.key == GLFW_KEY_END && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
if (e.key == GLFW_KEY_END && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
cursor = text.size();
e.consume(this);
}
else if (e.key == GLFW_KEY_V && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
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);
}
else if (e.key == GLFW_KEY_X && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
if (e.keyName == "x" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
if (cursor != selection) {
int begin = std::min(cursor, selection);
std::string selectedText = text.substr(begin, std::abs(selection - cursor));
@@ -152,7 +152,7 @@ void TextField::onSelectKey(const event::SelectKey& e) {
}
e.consume(this);
}
else if (e.key == GLFW_KEY_C && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
if (e.keyName == "c" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
if (cursor != selection) {
int begin = std::min(cursor, selection);
std::string selectedText = text.substr(begin, std::abs(selection - cursor));
@@ -160,11 +160,11 @@ void TextField::onSelectKey(const event::SelectKey& e) {
}
e.consume(this);
}
else if (e.key == GLFW_KEY_A && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
if (e.keyName == "a" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
selectAll();
e.consume(this);
}
else if ((e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) && (e.mods & RACK_MOD_MASK) == 0) {
if ((e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) && (e.mods & RACK_MOD_MASK) == 0) {
if (multiline) {
insertText("\n");
}
@@ -175,10 +175,10 @@ void TextField::onSelectKey(const event::SelectKey& e) {
e.consume(this);
}
// Consume all printable keys
else if ((GLFW_KEY_SPACE <= e.key && e.key <= GLFW_KEY_WORLD_2) || (GLFW_KEY_KP_0 <= e.key && e.key <= GLFW_KEY_KP_EQUAL)) {
if (e.keyName != "") {
e.consume(this);
}
else if (e.key == GLFW_KEY_ESCAPE && (e.mods & RACK_MOD_MASK) == 0) {
if (e.key == GLFW_KEY_ESCAPE && (e.mods & RACK_MOD_MASK) == 0) {
APP->event->setSelected(NULL);
e.consume(this);
}


Loading…
Cancel
Save