@@ -15,6 +15,7 @@ struct MenuItem : MenuEntry { | |||||
bool disabled = false; | bool disabled = false; | ||||
void draw(const DrawArgs& args) override; | void draw(const DrawArgs& args) override; | ||||
PRIVATE void drawOffset(NVGcontext* vg, float offset = 0); | |||||
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; | ||||
@@ -30,5 +31,13 @@ struct MenuItem : MenuEntry { | |||||
}; | }; | ||||
struct ColorDotMenuItem : MenuItem { | |||||
NVGcolor color; | |||||
void draw(const DrawArgs& args) override; | |||||
void step() override; | |||||
}; | |||||
} // namespace ui | } // namespace ui | ||||
} // namespace rack | } // namespace rack |
@@ -78,25 +78,6 @@ struct PortTooltip : ui::Tooltip { | |||||
}; | }; | ||||
struct ColorMenuItem : ui::MenuItem { | |||||
NVGcolor color; | |||||
void draw(const DrawArgs& args) override { | |||||
MenuItem::draw(args); | |||||
// Color circle | |||||
nvgBeginPath(args.vg); | |||||
float radius = 6.0; | |||||
nvgCircle(args.vg, 8.0 + radius, box.size.y / 2, radius); | |||||
nvgFillColor(args.vg, color); | |||||
nvgFill(args.vg); | |||||
nvgStrokeWidth(args.vg, 1.0); | |||||
nvgStrokeColor(args.vg, color::mult(color, 0.5)); | |||||
nvgStroke(args.vg); | |||||
} | |||||
}; | |||||
struct PortCloneCableItem : ui::MenuItem { | struct PortCloneCableItem : ui::MenuItem { | ||||
PortWidget* pw; | PortWidget* pw; | ||||
CableWidget* cw; | CableWidget* cw; | ||||
@@ -118,7 +99,7 @@ struct PortCloneCableItem : ui::MenuItem { | |||||
}; | }; | ||||
struct CableColorItem : ColorMenuItem { | |||||
struct CableColorItem : ui::ColorDotMenuItem { | |||||
CableWidget* cw; | CableWidget* cw; | ||||
void onAction(const ActionEvent& e) override { | void onAction(const ActionEvent& e) override { | ||||
@@ -134,7 +115,7 @@ struct CableColorItem : ColorMenuItem { | |||||
}; | }; | ||||
struct PortCableItem : ColorMenuItem { | |||||
struct PortCableItem : ui::ColorDotMenuItem { | |||||
PortWidget* pw; | PortWidget* pw; | ||||
CableWidget* cw; | CableWidget* cw; | ||||
@@ -158,7 +139,7 @@ struct PortCableItem : ColorMenuItem { | |||||
for (NVGcolor color : settings::cableColors) { | for (NVGcolor color : settings::cableColors) { | ||||
// Include extra leading spaces for the color circle | // Include extra leading spaces for the color circle | ||||
CableColorItem* item = createMenuItem<CableColorItem>(" Set color"); | |||||
CableColorItem* item = createMenuItem<CableColorItem>("Set color"); | |||||
item->disabled = color::isEqual(color, cw->color); | item->disabled = color::isEqual(color, cw->color); | ||||
item->cw = cw; | item->cw = cw; | ||||
item->color = color; | item->color = color; | ||||
@@ -170,7 +151,7 @@ struct PortCableItem : ColorMenuItem { | |||||
}; | }; | ||||
struct PortCreateCableItem : ColorMenuItem { | |||||
struct PortCreateCableItem : ui::ColorDotMenuItem { | |||||
PortWidget* pw; | PortWidget* pw; | ||||
void onButton(const ButtonEvent& e) override { | void onButton(const ButtonEvent& e) override { | ||||
@@ -283,7 +264,7 @@ void PortWidget::createContextMenu() { | |||||
bool createCableDisabled = (type == engine::Port::INPUT) && topCw; | bool createCableDisabled = (type == engine::Port::INPUT) && topCw; | ||||
for (NVGcolor color : settings::cableColors) { | for (NVGcolor color : settings::cableColors) { | ||||
// Include extra leading spaces for the color circle | // Include extra leading spaces for the color circle | ||||
PortCreateCableItem* item = createMenuItem<PortCreateCableItem>(" New cable", "Click+drag"); | |||||
PortCreateCableItem* item = createMenuItem<PortCreateCableItem>("New cable", "Click+drag"); | |||||
item->disabled = createCableDisabled; | item->disabled = createCableDisabled; | ||||
item->pw = this; | item->pw = this; | ||||
item->color = color; | item->color = color; | ||||
@@ -300,7 +281,7 @@ void PortWidget::createContextMenu() { | |||||
PortWidget* pw = (type == engine::Port::INPUT) ? cw->outputPort : cw->inputPort; | PortWidget* pw = (type == engine::Port::INPUT) ? cw->outputPort : cw->inputPort; | ||||
engine::PortInfo* portInfo = pw->getPortInfo(); | engine::PortInfo* portInfo = pw->getPortInfo(); | ||||
PortCableItem* item = createMenuItem<PortCableItem>(" " + portInfo->module->model->name + ": " + portInfo->getName(), RIGHT_ARROW); | |||||
PortCableItem* item = createMenuItem<PortCableItem>(portInfo->module->model->name + ": " + portInfo->getName(), RIGHT_ARROW); | |||||
item->color = cw->color; | item->color = cw->color; | ||||
item->pw = this; | item->pw = this; | ||||
item->cw = cw; | item->cw = cw; | ||||
@@ -7,36 +7,56 @@ namespace ui { | |||||
void MenuItem::draw(const DrawArgs& args) { | void MenuItem::draw(const DrawArgs& args) { | ||||
drawOffset(args.vg, 0); | |||||
} | |||||
void MenuItem::drawOffset(NVGcontext* vg, float offset) { | |||||
BNDwidgetState state = BND_DEFAULT; | BNDwidgetState state = BND_DEFAULT; | ||||
if (APP->event->hoveredWidget == this) | if (APP->event->hoveredWidget == this) | ||||
state = BND_HOVER; | state = BND_HOVER; | ||||
// Set active state if this MenuItem | |||||
// Set active state if this MenuItem is the Menu's active entry | |||||
Menu* parentMenu = dynamic_cast<Menu*>(parent); | Menu* parentMenu = dynamic_cast<Menu*>(parent); | ||||
if (parentMenu && parentMenu->activeEntry == this) | if (parentMenu && parentMenu->activeEntry == this) | ||||
state = BND_ACTIVE; | state = BND_ACTIVE; | ||||
// Main text and background | // Main text and background | ||||
if (!disabled) | |||||
bndMenuItem(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str()); | |||||
else | |||||
bndMenuLabel(args.vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str()); | |||||
const BNDtheme* theme = bndGetTheme(); | |||||
if (!disabled) { | |||||
// bndMenuItem(vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str()); | |||||
// From bndMenuItem() implementation | |||||
if (state != BND_DEFAULT) { | |||||
bndInnerBox(vg, 0.0, 0.0, box.size.x, box.size.y, 0, 0, 0, 0, | |||||
bndOffsetColor(theme->menuItemTheme.innerSelectedColor, theme->menuItemTheme.shadeTop), | |||||
bndOffsetColor(theme->menuItemTheme.innerSelectedColor, theme->menuItemTheme.shadeDown)); | |||||
state = BND_ACTIVE; | |||||
} | |||||
bndIconLabelValue(vg, offset + 0.0, 0.0, box.size.x - offset, box.size.y, -1, | |||||
bndTextColor(&theme->menuItemTheme, state), BND_LEFT, | |||||
BND_LABEL_FONT_SIZE, text.c_str(), NULL); | |||||
} | |||||
else { | |||||
// bndMenuLabel(vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str()); | |||||
// From bndMenuLabel() implementation | |||||
bndIconLabelValue(vg, offset + 0.0, 0.0, box.size.x, box.size.y, -1, theme->menuTheme.textColor, BND_LEFT, BND_LABEL_FONT_SIZE, text.c_str(), NULL); | |||||
} | |||||
// Right text | // Right text | ||||
float x = box.size.x - bndLabelWidth(args.vg, -1, rightText.c_str()); | |||||
float x = box.size.x - bndLabelWidth(vg, -1, rightText.c_str()); | |||||
NVGcolor rightColor = (state == BND_DEFAULT && !disabled) ? bndGetTheme()->menuTheme.textColor : bndGetTheme()->menuTheme.textSelectedColor; | NVGcolor rightColor = (state == BND_DEFAULT && !disabled) ? bndGetTheme()->menuTheme.textColor : bndGetTheme()->menuTheme.textSelectedColor; | ||||
bndIconLabelValue(args.vg, x, 0.0, box.size.x, box.size.y, -1, rightColor, BND_LEFT, BND_LABEL_FONT_SIZE, rightText.c_str(), NULL); | |||||
bndIconLabelValue(vg, x, 0.0, box.size.x, box.size.y, -1, rightColor, BND_LEFT, BND_LABEL_FONT_SIZE, rightText.c_str(), NULL); | |||||
} | } | ||||
void MenuItem::step() { | void MenuItem::step() { | ||||
// Add 10 more pixels because measurements on high-DPI screens are sometimes too small for some reason | |||||
const float rightPadding = 10.0; | |||||
// HACK use APP->window->vg from the window. | // HACK use APP->window->vg from the window. | ||||
// All this does is inspect the font, so it shouldn't modify APP->window->vg and should work when called from a widget::FramebufferWidget for example. | // All this does is inspect the font, so it shouldn't modify APP->window->vg and should work when called from a widget::FramebufferWidget for example. | ||||
box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + rightPadding; | |||||
box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()); | |||||
if (!rightText.empty()) | if (!rightText.empty()) | ||||
box.size.x += bndLabelWidth(APP->window->vg, -1, rightText.c_str()) - 10.0; | box.size.x += bndLabelWidth(APP->window->vg, -1, rightText.c_str()) - 10.0; | ||||
// Add 10 more pixels because measurements on high-DPI screens are sometimes too small for some reason | |||||
box.size.x += 10.0; | |||||
Widget::step(); | Widget::step(); | ||||
} | } | ||||
@@ -86,5 +106,25 @@ void MenuItem::onAction(const ActionEvent& e) { | |||||
} | } | ||||
void ColorDotMenuItem::draw(const DrawArgs& args) { | |||||
drawOffset(args.vg, 20.0); | |||||
// Color dot | |||||
nvgBeginPath(args.vg); | |||||
float radius = 6.0; | |||||
nvgCircle(args.vg, 8.0 + radius, box.size.y / 2, radius); | |||||
nvgFillColor(args.vg, color); | |||||
nvgFill(args.vg); | |||||
nvgStrokeWidth(args.vg, 1.0); | |||||
nvgStrokeColor(args.vg, color::mult(color, 0.5)); | |||||
nvgStroke(args.vg); | |||||
} | |||||
void ColorDotMenuItem::step() { | |||||
MenuItem::step(); | |||||
box.size.x += 20.0; | |||||
} | |||||
} // namespace ui | } // namespace ui | ||||
} // namespace rack | } // namespace rack |