Browse Source

Add TextField context menu.

tags/v2.0.0
Andrew Belt 3 years ago
parent
commit
9a21827b1e
3 changed files with 156 additions and 50 deletions
  1. +3
    -3
      include/helpers.hpp
  2. +11
    -4
      include/ui/TextField.hpp
  3. +142
    -43
      src/ui/TextField.cpp

+ 3
- 3
include/helpers.hpp View File

@@ -146,14 +146,14 @@ TParamWidget* createLightParamCentered(math::Vec pos, engine::Module* module, in
} }


template <class TMenuLabel = ui::MenuLabel> template <class TMenuLabel = ui::MenuLabel>
TMenuLabel * createMenuLabel(std::string text) {
TMenuLabel* createMenuLabel(std::string text) {
TMenuLabel* o = new TMenuLabel; TMenuLabel* o = new TMenuLabel;
o->text = text; o->text = text;
return o; return o;
} }


template <class TMenuItem = ui::MenuItem> template <class TMenuItem = ui::MenuItem>
TMenuItem * createMenuItem(std::string text, std::string rightText = "") {
TMenuItem* createMenuItem(std::string text, std::string rightText = "") {
TMenuItem* o = new TMenuItem; TMenuItem* o = new TMenuItem;
o->text = text; o->text = text;
o->rightText = rightText; o->rightText = rightText;
@@ -161,7 +161,7 @@ TMenuItem * createMenuItem(std::string text, std::string rightText = "") {
} }


template <class TMenu = ui::Menu> template <class TMenu = ui::Menu>
TMenu * createMenu() {
TMenu* createMenu() {
TMenu* o = new TMenu; TMenu* o = new TMenu;
o->box.pos = APP->scene->mousePos; o->box.pos = APP->scene->mousePos;




+ 11
- 4
include/ui/TextField.hpp View File

@@ -25,14 +25,21 @@ struct TextField : widget::OpaqueWidget {
void onButton(const event::Button& e) override; void onButton(const event::Button& e) override;
void onSelectText(const event::SelectText& e) override; void onSelectText(const event::SelectText& e) override;
void onSelectKey(const event::SelectKey& e) override; void onSelectKey(const event::SelectKey& e) override;
virtual int getTextPosition(math::Vec mousePos);


/** Inserts text at the cursor, replacing the selection if necessary */
void insertText(std::string text);

std::string getText();
/** Replaces the entire text */ /** Replaces the entire text */
void setText(std::string text); void setText(std::string text);
void selectAll(); void selectAll();
virtual int getTextPosition(math::Vec mousePos);
std::string getSelectedText();
/** Inserts text at the cursor, replacing the selection if necessary */
void insertText(std::string text);
void copyClipboard();
void cutClipboard();
void pasteClipboard();
void cursorToPrevWord();
void cursorToNextWord();
void createContextMenu();
}; };






+ 142
- 43
src/ui/TextField.cpp View File

@@ -1,9 +1,56 @@
#include <ui/TextField.hpp> #include <ui/TextField.hpp>
#include <ui/MenuItem.hpp>
#include <helpers.hpp>
#include <context.hpp>


namespace rack { namespace rack {
namespace ui { namespace ui {




struct TextFieldCopyItem : ui::MenuItem {
WeakPtr<TextField> textField;
void onAction(const event::Action& e) override {
if (!textField)
return;
textField->copyClipboard();
APP->event->setSelected(textField);
}
};


struct TextFieldCutItem : ui::MenuItem {
WeakPtr<TextField> textField;
void onAction(const event::Action& e) override {
if (!textField)
return;
textField->cutClipboard();
APP->event->setSelected(textField);
}
};


struct TextFieldPasteItem : ui::MenuItem {
WeakPtr<TextField> textField;
void onAction(const event::Action& e) override {
if (!textField)
return;
textField->pasteClipboard();
APP->event->setSelected(textField);
}
};


struct TextFieldSelectAllItem : ui::MenuItem {
WeakPtr<TextField> textField;
void onAction(const event::Action& e) override {
if (!textField)
return;
textField->selectAll();
APP->event->setSelected(textField);
}
};


TextField::TextField() { TextField::TextField() {
box.size.y = BND_WIDGET_HEIGHT; box.size.y = BND_WIDGET_HEIGHT;
} }
@@ -45,6 +92,11 @@ void TextField::onButton(const event::Button& e) {
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) { if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) {
cursor = selection = getTextPosition(e.pos); cursor = selection = getTextPosition(e.pos);
} }

if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
createContextMenu();
e.consume(this);
}
} }


void TextField::onSelectText(const event::SelectText& e) { void TextField::onSelectText(const event::SelectText& e) {
@@ -56,20 +108,6 @@ void TextField::onSelectText(const event::SelectText& e) {
} }


void TextField::onSelectKey(const event::SelectKey& 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 = std::min((int) pos + 1, (int) text.size());
};
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) { if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
// Backspace // Backspace
if (e.key == GLFW_KEY_BACKSPACE && (e.mods & RACK_MOD_MASK) == 0) { if (e.key == GLFW_KEY_BACKSPACE && (e.mods & RACK_MOD_MASK) == 0) {
@@ -151,28 +189,17 @@ void TextField::onSelectKey(const event::SelectKey& e) {
} }
// Ctrl+V // Ctrl+V
if (e.keyName == "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);
pasteClipboard();
e.consume(this); e.consume(this);
} }
// Ctrl+C
// Ctrl+X
if (e.keyName == "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));
glfwSetClipboardString(APP->window->win, selectedText.c_str());
insertText("");
}
cutClipboard();
e.consume(this); e.consume(this);
} }
// Ctrl+C // Ctrl+C
if (e.keyName == "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));
glfwSetClipboardString(APP->window->win, selectedText.c_str());
}
copyClipboard();
e.consume(this); e.consume(this);
} }
// Ctrl+A // Ctrl+A
@@ -211,12 +238,42 @@ void TextField::onSelectKey(const event::SelectKey& e) {
e.consume(this); e.consume(this);
} }


int TextField::getTextPosition(math::Vec mousePos) {
return bndTextFieldTextPosition(APP->window->vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str(), mousePos.x, mousePos.y);
}

std::string TextField::getText() {
return text;
}

void TextField::setText(std::string text) {
if (this->text != text) {
this->text = text;
// event::Change
event::Change eChange;
onChange(eChange);
}
selection = cursor = text.size();
}

void TextField::selectAll() {
cursor = text.size();
selection = 0;
}

std::string TextField::getSelectedText() {
int begin = std::min(cursor, selection);
int len = std::abs(selection - cursor);
return text.substr(begin, len);
}

void TextField::insertText(std::string text) { void TextField::insertText(std::string text) {
bool changed = false; bool changed = false;
if (cursor != selection) { if (cursor != selection) {
// Delete selected text // Delete selected text
int begin = std::min(cursor, selection); int begin = std::min(cursor, selection);
this->text.erase(begin, std::abs(selection - cursor));
int len = std::abs(selection - cursor);
this->text.erase(begin, len);
cursor = selection = begin; cursor = selection = begin;
changed = true; changed = true;
} }
@@ -232,23 +289,65 @@ void TextField::insertText(std::string text) {
} }
} }


void TextField::setText(std::string text) {
if (this->text != text) {
this->text = text;
// event::Change
event::Change eChange;
onChange(eChange);
}
selection = cursor = text.size();
void TextField::copyClipboard() {
if (cursor == selection)
return;
glfwSetClipboardString(APP->window->win, getSelectedText().c_str());
} }


void TextField::selectAll() {
cursor = text.size();
selection = 0;
void TextField::cutClipboard() {
copyClipboard();
insertText("");
} }


int TextField::getTextPosition(math::Vec mousePos) {
return bndTextFieldTextPosition(APP->window->vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str(), mousePos.x, mousePos.y);
void TextField::pasteClipboard() {
const char* newText = glfwGetClipboardString(APP->window->win);
if (!newText)
return;
insertText(newText);
}

void TextField::cursorToPrevWord() {
size_t pos = text.rfind(' ', std::max(cursor - 2, 0));
if (pos == std::string::npos)
cursor = 0;
else
cursor = std::min((int) pos + 1, (int) text.size());
}

void TextField::cursorToNextWord() {
size_t pos = text.find(' ', std::min(cursor + 1, (int) text.size()));
if (pos == std::string::npos)
pos = text.size();
cursor = pos;
}

void TextField::createContextMenu() {
ui::Menu* menu = createMenu();

TextFieldCutItem* cutItem = new TextFieldCutItem;
cutItem->text = "Cut";
cutItem->rightText = RACK_MOD_CTRL_NAME "+X";
cutItem->textField = this;
menu->addChild(cutItem);

TextFieldCopyItem* copyItem = new TextFieldCopyItem;
copyItem->text = "Copy";
copyItem->rightText = RACK_MOD_CTRL_NAME "+C";
copyItem->textField = this;
menu->addChild(copyItem);

TextFieldPasteItem* pasteItem = new TextFieldPasteItem;
pasteItem->text = "Paste";
pasteItem->rightText = RACK_MOD_CTRL_NAME "+V";
pasteItem->textField = this;
menu->addChild(pasteItem);

TextFieldSelectAllItem* selectAllItem = new TextFieldSelectAllItem;
selectAllItem->text = "Select all";
selectAllItem->rightText = RACK_MOD_CTRL_NAME "+A";
selectAllItem->textField = this;
menu->addChild(selectAllItem);
} }






Loading…
Cancel
Save