Browse Source

Don't close menu when Ctrl-clicking items. Add argument to MenuItem helpers to disable this behavior.

tags/v2.0.0
Andrew Belt 3 years ago
parent
commit
b605c2c258
7 changed files with 91 additions and 61 deletions
  1. +66
    -21
      include/helpers.hpp
  2. +5
    -2
      include/ui/MenuItem.hpp
  3. +0
    -16
      src/app/MenuBar.cpp
  4. +2
    -3
      src/app/ModuleBrowser.cpp
  5. +1
    -1
      src/app/ModuleWidget.cpp
  6. +8
    -6
      src/app/RackWidget.cpp
  7. +9
    -12
      src/ui/MenuItem.cpp

+ 66
- 21
include/helpers.hpp View File

@@ -204,17 +204,22 @@ Example:
)); ));
*/ */
template <class TMenuItem = ui::MenuItem> template <class TMenuItem = ui::MenuItem>
TMenuItem* createMenuItem(std::string text, std::string rightText, std::function<void()> action, bool disabled = false) {
TMenuItem* createMenuItem(std::string text, std::string rightText, std::function<void()> action, bool disabled = false, bool alwaysConsume = false) {
struct Item : TMenuItem { struct Item : TMenuItem {
std::function<void()> action; std::function<void()> action;
bool alwaysConsume;

void onAction(const event::Action& e) override { void onAction(const event::Action& e) override {
action(); action();
if (alwaysConsume)
e.consume(this);
} }
}; };


Item* item = createMenuItem<Item>(text, rightText); Item* item = createMenuItem<Item>(text, rightText);
item->action = action; item->action = action;
item->disabled = disabled; item->disabled = disabled;
item->alwaysConsume = alwaysConsume;
return item; return item;
} }


@@ -231,17 +236,29 @@ Example:
} }
)); ));
*/ */
inline ui::MenuItem* createCheckMenuItem(std::string text, std::function<bool()> checked, std::function<void()> action) {
struct Item : ui::MenuItem {
template <class TMenuItem = ui::MenuItem>
ui::MenuItem* createCheckMenuItem(std::string text, std::function<bool()> checked, std::function<void()> action, bool disabled = false, bool alwaysConsume = false) {
struct Item : TMenuItem {
std::function<bool()> checked;
std::function<void()> action; std::function<void()> action;
bool alwaysConsume;


void step() override {
this->rightText = CHECKMARK(checked());
TMenuItem::step();
}
void onAction(const event::Action& e) override { void onAction(const event::Action& e) override {
action(); action();
if (alwaysConsume)
e.consume(this);
} }
}; };


Item* item = createMenuItem<Item>(text, CHECKMARK(checked()));
Item* item = createMenuItem<Item>(text);
item->checked = checked;
item->action = action; item->action = action;
item->disabled = disabled;
item->alwaysConsume = alwaysConsume;
return item; return item;
} }


@@ -258,20 +275,29 @@ Example:
} }
)); ));
*/ */
inline ui::MenuItem* createBoolMenuItem(std::string text, std::function<bool()> getter, std::function<void(bool state)> setter) {
struct Item : ui::MenuItem {
template <class TMenuItem = ui::MenuItem>
ui::MenuItem* createBoolMenuItem(std::string text, std::function<bool()> getter, std::function<void(bool state)> setter, bool disabled = false, bool alwaysConsume = false) {
struct Item : TMenuItem {
std::function<bool()> getter;
std::function<void(size_t)> setter; std::function<void(size_t)> setter;
bool val;
bool alwaysConsume;


void step() override {
this->rightText = CHECKMARK(getter());
TMenuItem::step();
}
void onAction(const event::Action& e) override { void onAction(const event::Action& e) override {
setter(val);
setter(!getter());
if (alwaysConsume)
e.consume(this);
} }
}; };


bool currVal = getter();
Item* item = createMenuItem<Item>(text, CHECKMARK(currVal));
Item* item = createMenuItem<Item>(text);
item->getter = getter;
item->setter = setter; item->setter = setter;
item->val = !currVal;
item->disabled = disabled;
item->alwaysConsume = alwaysConsume;
return item; return item;
} }


@@ -300,8 +326,9 @@ Example:
} }
)); ));
*/ */
inline ui::MenuItem* createSubmenuItem(std::string text, std::string rightText, std::function<void(ui::Menu* menu)> createMenu, bool disabled = false) {
struct Item : ui::MenuItem {
template <class TMenuItem = ui::MenuItem>
ui::MenuItem* createSubmenuItem(std::string text, std::string rightText, std::function<void(ui::Menu* menu)> createMenu, bool disabled = false) {
struct Item : TMenuItem {
std::function<void(ui::Menu* menu)> createMenu; std::function<void(ui::Menu* menu)> createMenu;


ui::Menu* createChildMenu() override { ui::Menu* createChildMenu() override {
@@ -311,7 +338,7 @@ inline ui::MenuItem* createSubmenuItem(std::string text, std::string rightText,
} }
}; };


Item* item = createMenuItem<Item>(text, rightText + (rightText.empty() ? "" : " ") + RIGHT_ARROW);
Item* item = createMenuItem<Item>(text, rightText + (rightText.empty() ? "" : " ") + RIGHT_ARROW);
item->createMenu = createMenu; item->createMenu = createMenu;
item->disabled = disabled; item->disabled = disabled;
return item; return item;
@@ -331,40 +358,58 @@ Example:
} }
)); ));
*/ */
inline ui::MenuItem* createIndexSubmenuItem(std::string text, std::vector<std::string> labels, std::function<size_t()> getter, std::function<void(size_t val)> setter) {
template <class TMenuItem = ui::MenuItem>
ui::MenuItem* createIndexSubmenuItem(std::string text, std::vector<std::string> labels, std::function<size_t()> getter, std::function<void(size_t val)> setter, bool disabled = false, bool alwaysConsume = false) {
struct IndexItem : ui::MenuItem { struct IndexItem : ui::MenuItem {
std::function<size_t()> getter;
std::function<void(size_t)> setter; std::function<void(size_t)> setter;
size_t index; size_t index;
bool alwaysConsume;


void step() override {
size_t currIndex = getter();
this->rightText = CHECKMARK(currIndex == index);
MenuItem::step();
}
void onAction(const event::Action& e) override { void onAction(const event::Action& e) override {
setter(index); setter(index);
if (alwaysConsume)
e.consume(this);
} }
}; };


struct Item : ui::MenuItem {
struct Item : TMenuItem {
std::function<size_t()> getter; std::function<size_t()> getter;
std::function<void(size_t)> setter; std::function<void(size_t)> setter;
std::vector<std::string> labels; std::vector<std::string> labels;
bool alwaysConsume;


void step() override {
size_t currIndex = getter();
std::string label = (currIndex < labels.size()) ? labels[currIndex] : "";
this->rightText = label + " " + RIGHT_ARROW;
TMenuItem::step();
}
ui::Menu* createChildMenu() override { ui::Menu* createChildMenu() override {
ui::Menu* menu = new ui::Menu; ui::Menu* menu = new ui::Menu;
size_t currIndex = getter();
for (size_t i = 0; i < labels.size(); i++) { for (size_t i = 0; i < labels.size(); i++) {
IndexItem* item = createMenuItem<IndexItem>(labels[i], CHECKMARK(currIndex == i));
IndexItem* item = createMenuItem<IndexItem>(labels[i]);
item->getter = getter;
item->setter = setter; item->setter = setter;
item->index = i; item->index = i;
item->alwaysConsume = alwaysConsume;
menu->addChild(item); menu->addChild(item);
} }
return menu; return menu;
} }
}; };


size_t currIndex = getter();
std::string label = (currIndex < labels.size()) ? labels[currIndex] : "";
Item* item = createMenuItem<Item>(text, label + " " + RIGHT_ARROW);
Item* item = createMenuItem<Item>(text);
item->getter = getter; item->getter = getter;
item->setter = setter; item->setter = setter;
item->labels = labels; item->labels = labels;
item->disabled = disabled;
item->alwaysConsume = alwaysConsume;
return item; return item;
} }




+ 5
- 2
include/ui/MenuItem.hpp View File

@@ -13,16 +13,19 @@ struct MenuItem : MenuEntry {
std::string text; std::string text;
std::string rightText; std::string rightText;
bool disabled = false; bool disabled = false;
bool active = false;


void draw(const DrawArgs& args) override; void draw(const DrawArgs& args) override;
void step() override; void step() override;
void onEnter(const EnterEvent& e) override; void onEnter(const EnterEvent& e) override;
void onDragDrop(const DragDropEvent& e) override; void onDragDrop(const DragDropEvent& e) override;
void doAction();
void doAction(bool consume = true);
virtual Menu* createChildMenu() { virtual Menu* createChildMenu() {
return NULL; return NULL;
} }
/** Override to handle behavior when user clicks the menu item.
Event is consumed by default. Unconsume to prevent the menu from being closed.
If Ctrl (Cmd on Mac) is held, the event is *not* pre-consumed, so if your menu must be closed, always consume the event.
*/
void onAction(const ActionEvent& e) override; void onAction(const ActionEvent& e) override;
}; };




+ 0
- 16
src/app/MenuBar.cpp View File

@@ -344,22 +344,6 @@ struct HaloBrightnessSlider : ui::Slider {
} }
}; };


struct FrameRateValueItem : ui::MenuItem {
int frameSwapInterval;
void onAction(const ActionEvent& e) override {
settings::frameSwapInterval = frameSwapInterval;
}
};

struct FrameRateItem : ui::MenuItem {
ui::Menu* createChildMenu() override {
ui::Menu* menu = new ui::Menu;

return menu;
}
};


struct ViewButton : MenuButton { struct ViewButton : MenuButton {
void onAction(const ActionEvent& e) override { void onAction(const ActionEvent& e) override {
ui::Menu* menu = createMenu(); ui::Menu* menu = createMenu();


+ 2
- 3
src/app/ModuleBrowser.cpp View File

@@ -843,9 +843,8 @@ inline void TagItem::onAction(const ActionEvent& e) {
bool isSelected = (it != browser->tagIds.end()); bool isSelected = (it != browser->tagIds.end());


if (tagId >= 0) { if (tagId >= 0) {
// Actual tag
int mods = APP->window->getMods();
if ((mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
// Specific tag
if (!e.isConsumed()) {
// Multi select // Multi select
if (isSelected) if (isSelected)
browser->tagIds.erase(tagId); browser->tagIds.erase(tagId);


+ 1
- 1
src/app/ModuleWidget.cpp View File

@@ -1091,7 +1091,7 @@ void ModuleWidget::createContextMenu() {
if (!weakThis) if (!weakThis)
return; return;
weakThis->removeAction(); weakThis->removeAction();
}));
}, false, true));


appendContextMenu(menu); appendContextMenu(menu);
} }


+ 8
- 6
src/app/RackWidget.cpp View File

@@ -1122,15 +1122,17 @@ void RackWidget::appendSelectionContextMenu(ui::Menu* menu) {
int n = getNumSelected(); int n = getNumSelected();
menu->addChild(createMenuLabel(string::f("%d selected %s", n, n == 1 ? "module" : "modules"))); menu->addChild(createMenuLabel(string::f("%d selected %s", n, n == 1 ? "module" : "modules")));


// Enable alwaysConsume of menu items if the number of selected modules changes

// Select all // Select all
menu->addChild(createMenuItem("Select all", RACK_MOD_CTRL_NAME "+A", [=]() { menu->addChild(createMenuItem("Select all", RACK_MOD_CTRL_NAME "+A", [=]() {
selectAll(); selectAll();
}));
}, false, true));


// Deselect // Deselect
menu->addChild(createMenuItem("Deselect", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+A", [=]() { menu->addChild(createMenuItem("Deselect", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+A", [=]() {
deselect(); deselect();
}, n == 0));
}, n == 0, true));


// Copy // Copy
menu->addChild(createMenuItem("Copy", RACK_MOD_CTRL_NAME "+C", [=]() { menu->addChild(createMenuItem("Copy", RACK_MOD_CTRL_NAME "+C", [=]() {
@@ -1140,12 +1142,12 @@ void RackWidget::appendSelectionContextMenu(ui::Menu* menu) {
// Paste // Paste
menu->addChild(createMenuItem("Paste", RACK_MOD_CTRL_NAME "+V", [=]() { menu->addChild(createMenuItem("Paste", RACK_MOD_CTRL_NAME "+V", [=]() {
pasteClipboardAction(); pasteClipboardAction();
}));
}, false, true));


// Load // Load
menu->addChild(createMenuItem("Import selection", "", [=]() { menu->addChild(createMenuItem("Import selection", "", [=]() {
loadSelectionDialog(); loadSelectionDialog();
}));
}, false, true));


// Save // Save
menu->addChild(createMenuItem("Save selection as", "", [=]() { menu->addChild(createMenuItem("Save selection as", "", [=]() {
@@ -1174,7 +1176,7 @@ void RackWidget::appendSelectionContextMenu(ui::Menu* menu) {
bypassText += " " CHECKMARK_STRING; bypassText += " " CHECKMARK_STRING;
menu->addChild(createMenuItem("Bypass", bypassText, [=]() { menu->addChild(createMenuItem("Bypass", bypassText, [=]() {
bypassSelectionAction(!bypassed); bypassSelectionAction(!bypassed);
}, n == 0));
}, n == 0, true));


// Duplicate // Duplicate
menu->addChild(createMenuItem("Duplicate", RACK_MOD_CTRL_NAME "+D", [=]() { menu->addChild(createMenuItem("Duplicate", RACK_MOD_CTRL_NAME "+D", [=]() {
@@ -1184,7 +1186,7 @@ void RackWidget::appendSelectionContextMenu(ui::Menu* menu) {
// Delete // Delete
menu->addChild(createMenuItem("Delete", "Backspace/Delete", [=]() { menu->addChild(createMenuItem("Delete", "Backspace/Delete", [=]() {
deleteSelectionAction(); deleteSelectionAction();
}, n == 0));
}, n == 0, true));
} }


void RackWidget::clearCables() { void RackWidget::clearCables() {


+ 9
- 12
src/ui/MenuItem.cpp View File

@@ -17,9 +17,6 @@ void MenuItem::draw(const DrawArgs& args) {
if (parentMenu && parentMenu->activeEntry == this) if (parentMenu && parentMenu->activeEntry == this)
state = BND_ACTIVE; state = BND_ACTIVE;


if (active)
state = BND_ACTIVE;

// Main text and background // Main text and background
if (!disabled) if (!disabled)
bndMenuItem(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str()); bndMenuItem(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str());
@@ -58,24 +55,24 @@ void MenuItem::onEnter(const EnterEvent& e) {
} }


void MenuItem::onDragDrop(const DragDropEvent& e) { void MenuItem::onDragDrop(const DragDropEvent& e) {
if (e.origin != this)
return;
doAction();
if (e.origin == this && !disabled) {
int mods = APP->window->getMods();
doAction((mods & RACK_MOD_MASK) != RACK_MOD_CTRL);
}
} }


void MenuItem::doAction() {
if (disabled)
return;

void MenuItem::doAction(bool consume) {
widget::EventContext cAction; widget::EventContext cAction;
ActionEvent eAction; ActionEvent eAction;
eAction.context = &cAction; eAction.context = &cAction;
// Consume event by default, but allow action to un-consume it to prevent the menu from being removed.
eAction.consume(this);
if (consume) {
eAction.consume(this);
}
onAction(eAction); onAction(eAction);
if (!cAction.consumed) if (!cAction.consumed)
return; return;


// Close menu
MenuOverlay* overlay = getAncestorOfType<MenuOverlay>(); MenuOverlay* overlay = getAncestorOfType<MenuOverlay>();
if (overlay) { if (overlay) {
overlay->requestDelete(); overlay->requestDelete();


Loading…
Cancel
Save