#include "ui.hpp" // for gVg #include "window.hpp" // for key codes #include namespace rack { void TextField::draw(NVGcontext *vg) { nvgScissor(vg, 0, 0, box.size.x, box.size.y); BNDwidgetState state; if (this == gFocusedWidget) state = BND_ACTIVE; else if (this == gHoveredWidget) state = BND_HOVER; else state = BND_DEFAULT; bndTextField(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, -1, text.c_str(), begin, end); // Draw placeholder text if (text.empty() && state != BND_ACTIVE) { bndIconLabelCaret(vg, 0.0, 0.0, box.size.x, box.size.y, -1, bndGetTheme()->textFieldTheme.itemColor, 13, placeholder.c_str(), bndGetTheme()->textFieldTheme.itemColor, 0, -1); } nvgResetScissor(vg); } void TextField::onMouseDown(EventMouseDown &e) { dragPos = getTextPosition(e.pos); begin = end = dragPos; OpaqueWidget::onMouseDown(e); } void TextField::onMouseMove(EventMouseMove &e) { if (this == gDraggedWidget) { int pos = getTextPosition(e.pos); if (pos != dragPos) { begin = min(dragPos, pos); end = max(dragPos, pos); } } OpaqueWidget::onMouseMove(e); } void TextField::onFocus(EventFocus &e) { begin = 0; end = text.size(); e.consumed = true; } void TextField::onText(EventText &e) { if (e.codepoint < 128) { std::string newText(1, (char) e.codepoint); insertText(newText); } e.consumed = true; } void TextField::onKey(EventKey &e) { switch (e.key) { case GLFW_KEY_BACKSPACE: if (begin < end) { text.erase(begin, end - begin); onTextChange(); } else { begin--; if (begin >= 0) { text.erase(begin, 1); onTextChange(); } } end = begin; break; case GLFW_KEY_DELETE: if (begin < end) { text.erase(begin, end - begin); onTextChange(); } else { text.erase(begin, 1); onTextChange(); } end = begin; break; case GLFW_KEY_LEFT: if (begin < end) { } else { begin--; } end = begin; break; case GLFW_KEY_RIGHT: if (begin < end) { begin = end; } else { begin++; } end = begin; break; case GLFW_KEY_HOME: end = begin = 0; break; case GLFW_KEY_END: end = begin = text.size(); break; case GLFW_KEY_V: if (windowIsModPressed()) { const char *newText = glfwGetClipboardString(gWindow); if (newText) insertText(newText); } break; case GLFW_KEY_X: if (windowIsModPressed()) { if (begin < end) { std::string selectedText = text.substr(begin, end - begin); glfwSetClipboardString(gWindow, selectedText.c_str()); insertText(""); } } break; case GLFW_KEY_C: if (windowIsModPressed()) { if (begin < end) { std::string selectedText = text.substr(begin, end - begin); glfwSetClipboardString(gWindow, selectedText.c_str()); } } break; case GLFW_KEY_A: if (windowIsModPressed()) { begin = 0; end = text.size(); } break; case GLFW_KEY_ENTER: if (multiline) { insertText("\n"); } else { EventAction e; onAction(e); } break; } begin = clamp(begin, 0, text.size()); end = clamp(end, 0, text.size()); e.consumed = true; } void TextField::insertText(std::string text) { if (begin < end) this->text.erase(begin, end - begin); this->text.insert(begin, text); begin += text.size(); end = begin; onTextChange(); } void TextField::setText(std::string text) { this->text = text; begin = clamp(begin, 0, text.size()); end = clamp(end, 0, text.size()); onTextChange(); } int TextField::getTextPosition(Vec mousePos) { return bndTextFieldTextPosition(gVg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str(), mousePos.x, mousePos.y); } } // namespace rack