@@ -26,6 +26,7 @@ struct LedDisplayChoice : widget::OpaqueWidget { | |||||
NVGcolor bgColor; | NVGcolor bgColor; | ||||
LedDisplayChoice(); | LedDisplayChoice(); | ||||
void draw(const DrawArgs& args) override; | void draw(const DrawArgs& args) override; | ||||
void drawLayer(const DrawArgs& args, int layer) override; | |||||
void onButton(const ButtonEvent& e) override; | void onButton(const ButtonEvent& e) override; | ||||
}; | }; | ||||
@@ -36,6 +37,7 @@ struct LedDisplayTextField : ui::TextField { | |||||
NVGcolor bgColor; | NVGcolor bgColor; | ||||
LedDisplayTextField(); | LedDisplayTextField(); | ||||
void draw(const DrawArgs& args) override; | void draw(const DrawArgs& args) override; | ||||
void drawLayer(const DrawArgs& args, int layer) override; | |||||
int getTextPosition(math::Vec mousePos) override; | int getTextPosition(math::Vec mousePos) override; | ||||
}; | }; | ||||
@@ -276,79 +276,83 @@ void CableWidget::step() { | |||||
void CableWidget::draw(const DrawArgs& args) { | void CableWidget::draw(const DrawArgs& args) { | ||||
// Draw plugs | |||||
Widget::draw(args); | Widget::draw(args); | ||||
} | } | ||||
void CableWidget::drawLayer(const DrawArgs& args, int layer) { | void CableWidget::drawLayer(const DrawArgs& args, int layer) { | ||||
float opacity = settings::cableOpacity; | |||||
bool thick = false; | |||||
if (isComplete()) { | |||||
engine::Output* output = &cable->outputModule->outputs[cable->outputId]; | |||||
// Increase thickness if output port is polyphonic | |||||
if (output->isPolyphonic()) { | |||||
thick = true; | |||||
} | |||||
// Cable shadow and cable | |||||
if (layer == 2 || layer == 3) { | |||||
float opacity = settings::cableOpacity; | |||||
bool thick = false; | |||||
if (isComplete()) { | |||||
engine::Output* output = &cable->outputModule->outputs[cable->outputId]; | |||||
// Increase thickness if output port is polyphonic | |||||
if (output->isPolyphonic()) { | |||||
thick = true; | |||||
} | |||||
// Draw opaque if mouse is hovering over a connected port | |||||
Widget* hoveredWidget = APP->event->hoveredWidget; | |||||
if (outputPort == hoveredWidget || inputPort == hoveredWidget) { | |||||
opacity = 1.0; | |||||
// Draw opaque if mouse is hovering over a connected port | |||||
Widget* hoveredWidget = APP->event->hoveredWidget; | |||||
if (outputPort == hoveredWidget || inputPort == hoveredWidget) { | |||||
opacity = 1.0; | |||||
} | |||||
// Draw translucent cable if not active (i.e. 0 channels) | |||||
else if (output->getChannels() == 0) { | |||||
opacity *= 0.5; | |||||
} | |||||
} | } | ||||
// Draw translucent cable if not active (i.e. 0 channels) | |||||
else if (output->getChannels() == 0) { | |||||
opacity *= 0.5; | |||||
else { | |||||
// Draw opaque if the cable is incomplete | |||||
opacity = 1.0; | |||||
} | } | ||||
} | |||||
else { | |||||
// Draw opaque if the cable is incomplete | |||||
opacity = 1.0; | |||||
} | |||||
if (opacity <= 0.0) | |||||
return; | |||||
nvgAlpha(args.vg, std::pow(opacity, 1.5)); | |||||
math::Vec outputPos = getOutputPos(); | |||||
math::Vec inputPos = getInputPos(); | |||||
float thickness = thick ? 9.0 : 6.0; | |||||
// The endpoints are off-center | |||||
math::Vec slump = getSlumpPos(outputPos, inputPos); | |||||
outputPos = outputPos.plus(slump.minus(outputPos).normalize().mult(13.0)); | |||||
inputPos = inputPos.plus(slump.minus(inputPos).normalize().mult(13.0)); | |||||
nvgLineCap(args.vg, NVG_ROUND); | |||||
// Avoids glitches when cable is bent | |||||
nvgLineJoin(args.vg, NVG_ROUND); | |||||
if (layer == 1) { | |||||
// Draw cable shadow | |||||
math::Vec shadowSlump = slump.plus(math::Vec(0, 30)); | |||||
nvgBeginPath(args.vg); | |||||
nvgMoveTo(args.vg, VEC_ARGS(outputPos)); | |||||
nvgQuadTo(args.vg, VEC_ARGS(shadowSlump), VEC_ARGS(inputPos)); | |||||
NVGcolor shadowColor = nvgRGBAf(0, 0, 0, 0.10); | |||||
nvgStrokeColor(args.vg, shadowColor); | |||||
nvgStrokeWidth(args.vg, thickness - 1.0); | |||||
nvgStroke(args.vg); | |||||
} | |||||
else if (layer == 2) { | |||||
// Draw cable outline | |||||
nvgBeginPath(args.vg); | |||||
nvgMoveTo(args.vg, VEC_ARGS(outputPos)); | |||||
nvgQuadTo(args.vg, VEC_ARGS(slump), VEC_ARGS(inputPos)); | |||||
// nvgStrokePaint(args.vg, nvgLinearGradient(args.vg, VEC_ARGS(outputPos), VEC_ARGS(inputPos), color::mult(color, 0.5), color)); | |||||
nvgStrokeColor(args.vg, color::mult(color, 0.8)); | |||||
nvgStrokeWidth(args.vg, thickness); | |||||
nvgStroke(args.vg); | |||||
// Draw cable | |||||
nvgStrokeColor(args.vg, color::mult(color, 0.95)); | |||||
nvgStrokeWidth(args.vg, thickness - 1.0); | |||||
nvgStroke(args.vg); | |||||
if (opacity <= 0.0) | |||||
return; | |||||
nvgAlpha(args.vg, std::pow(opacity, 1.5)); | |||||
math::Vec outputPos = getOutputPos(); | |||||
math::Vec inputPos = getInputPos(); | |||||
float thickness = thick ? 9.0 : 6.0; | |||||
// The endpoints are off-center | |||||
math::Vec slump = getSlumpPos(outputPos, inputPos); | |||||
outputPos = outputPos.plus(slump.minus(outputPos).normalize().mult(13.0)); | |||||
inputPos = inputPos.plus(slump.minus(inputPos).normalize().mult(13.0)); | |||||
nvgLineCap(args.vg, NVG_ROUND); | |||||
// Avoids glitches when cable is bent | |||||
nvgLineJoin(args.vg, NVG_ROUND); | |||||
if (layer == 2) { | |||||
// Draw cable shadow | |||||
math::Vec shadowSlump = slump.plus(math::Vec(0, 30)); | |||||
nvgBeginPath(args.vg); | |||||
nvgMoveTo(args.vg, VEC_ARGS(outputPos)); | |||||
nvgQuadTo(args.vg, VEC_ARGS(shadowSlump), VEC_ARGS(inputPos)); | |||||
NVGcolor shadowColor = nvgRGBAf(0, 0, 0, 0.10); | |||||
nvgStrokeColor(args.vg, shadowColor); | |||||
nvgStrokeWidth(args.vg, thickness - 1.0); | |||||
nvgStroke(args.vg); | |||||
} | |||||
else if (layer == 3) { | |||||
// Draw cable outline | |||||
nvgBeginPath(args.vg); | |||||
nvgMoveTo(args.vg, VEC_ARGS(outputPos)); | |||||
nvgQuadTo(args.vg, VEC_ARGS(slump), VEC_ARGS(inputPos)); | |||||
// nvgStrokePaint(args.vg, nvgLinearGradient(args.vg, VEC_ARGS(outputPos), VEC_ARGS(inputPos), color::mult(color, 0.5), color)); | |||||
nvgStrokeColor(args.vg, color::mult(color, 0.8)); | |||||
nvgStrokeWidth(args.vg, thickness); | |||||
nvgStroke(args.vg); | |||||
// Draw cable | |||||
nvgStrokeColor(args.vg, color::mult(color, 0.95)); | |||||
nvgStrokeWidth(args.vg, thickness - 1.0); | |||||
nvgStroke(args.vg); | |||||
} | |||||
} | } | ||||
Widget::drawLayer(args, layer); | Widget::drawLayer(args, layer); | ||||
@@ -45,7 +45,6 @@ LedDisplayChoice::LedDisplayChoice() { | |||||
void LedDisplayChoice::draw(const DrawArgs& args) { | void LedDisplayChoice::draw(const DrawArgs& args) { | ||||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | |||||
if (bgColor.a > 0.0) { | if (bgColor.a > 0.0) { | ||||
nvgBeginPath(args.vg); | nvgBeginPath(args.vg); | ||||
nvgRect(args.vg, 0, 0, box.size.x, box.size.y); | nvgRect(args.vg, 0, 0, box.size.x, box.size.y); | ||||
@@ -53,16 +52,26 @@ void LedDisplayChoice::draw(const DrawArgs& args) { | |||||
nvgFill(args.vg); | nvgFill(args.vg); | ||||
} | } | ||||
std::shared_ptr<window::Font> font = APP->window->loadFont(fontPath); | |||||
nvgGlobalTint(args.vg, color::WHITE); | |||||
if (font && font->handle >= 0) { | |||||
nvgFillColor(args.vg, color); | |||||
nvgFontFaceId(args.vg, font->handle); | |||||
nvgTextLetterSpacing(args.vg, 0.0); | |||||
nvgFontSize(args.vg, 12); | |||||
nvgText(args.vg, textOffset.x, textOffset.y, text.c_str(), NULL); | |||||
Widget::draw(args); | |||||
} | |||||
void LedDisplayChoice::drawLayer(const DrawArgs& args, int layer) { | |||||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | |||||
if (layer == 1) { | |||||
std::shared_ptr<window::Font> font = APP->window->loadFont(fontPath); | |||||
if (font && font->handle >= 0) { | |||||
nvgFillColor(args.vg, color); | |||||
nvgFontFaceId(args.vg, font->handle); | |||||
nvgTextLetterSpacing(args.vg, 0.0); | |||||
nvgFontSize(args.vg, 12); | |||||
nvgText(args.vg, textOffset.x, textOffset.y, text.c_str(), NULL); | |||||
} | |||||
} | } | ||||
Widget::drawLayer(args, layer); | |||||
nvgResetScissor(args.vg); | nvgResetScissor(args.vg); | ||||
} | } | ||||
@@ -87,8 +96,6 @@ LedDisplayTextField::LedDisplayTextField() { | |||||
void LedDisplayTextField::draw(const DrawArgs& args) { | void LedDisplayTextField::draw(const DrawArgs& args) { | ||||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | |||||
// Background | // Background | ||||
if (bgColor.a > 0.0) { | if (bgColor.a > 0.0) { | ||||
nvgBeginPath(args.vg); | nvgBeginPath(args.vg); | ||||
@@ -97,24 +104,33 @@ void LedDisplayTextField::draw(const DrawArgs& args) { | |||||
nvgFill(args.vg); | nvgFill(args.vg); | ||||
} | } | ||||
// Text | |||||
std::shared_ptr<window::Font> font = APP->window->loadFont(fontPath); | |||||
nvgGlobalTint(args.vg, color::WHITE); | |||||
if (font && font->handle >= 0) { | |||||
bndSetFont(font->handle); | |||||
NVGcolor highlightColor = color; | |||||
highlightColor.a = 0.5; | |||||
int begin = std::min(cursor, selection); | |||||
int end = (this == APP->event->selectedWidget) ? std::max(cursor, selection) : -1; | |||||
bndIconLabelCaret(args.vg, | |||||
textOffset.x, textOffset.y, | |||||
box.size.x - 2 * textOffset.x, box.size.y - 2 * textOffset.y, | |||||
-1, color, 12, text.c_str(), highlightColor, begin, end); | |||||
bndSetFont(APP->window->uiFont->handle); | |||||
Widget::draw(args); | |||||
} | |||||
void LedDisplayTextField::drawLayer(const DrawArgs& args, int layer) { | |||||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | |||||
if (layer == 1) { | |||||
// Text | |||||
std::shared_ptr<window::Font> font = APP->window->loadFont(fontPath); | |||||
if (font && font->handle >= 0) { | |||||
bndSetFont(font->handle); | |||||
NVGcolor highlightColor = color; | |||||
highlightColor.a = 0.5; | |||||
int begin = std::min(cursor, selection); | |||||
int end = (this == APP->event->selectedWidget) ? std::max(cursor, selection) : -1; | |||||
bndIconLabelCaret(args.vg, | |||||
textOffset.x, textOffset.y, | |||||
box.size.x - 2 * textOffset.x, box.size.y - 2 * textOffset.y, | |||||
-1, color, 12, text.c_str(), highlightColor, begin, end); | |||||
bndSetFont(APP->window->uiFont->handle); | |||||
} | |||||
} | } | ||||
Widget::drawLayer(args, layer); | |||||
nvgResetScissor(args.vg); | nvgResetScissor(args.vg); | ||||
} | } | ||||
@@ -44,24 +44,39 @@ struct ModuleContainer : widget::Widget { | |||||
Widget::drawLayer(args, -1); | Widget::drawLayer(args, -1); | ||||
Widget::draw(args); | Widget::draw(args); | ||||
} | |||||
// Draw lights and light halos | |||||
nvgGlobalTint(args.vg, color::WHITE); | |||||
Widget::drawLayer(args, 1); | |||||
void drawLayer(const DrawArgs& args, int layer) override { | |||||
// Draw lights after translucent rectangle | |||||
if (layer == 1) { | |||||
Widget::drawLayer(args, 1); | |||||
} | |||||
} | } | ||||
}; | }; | ||||
struct CableContainer : widget::TransparentWidget { | struct CableContainer : widget::TransparentWidget { | ||||
void draw(const DrawArgs& args) override { | void draw(const DrawArgs& args) override { | ||||
// Draw Plugs | |||||
Widget::draw(args); | |||||
// Don't draw on layer 0 | |||||
} | |||||
// Draw cable shadows | |||||
Widget::drawLayer(args, 1); | |||||
void drawLayer(const DrawArgs& args, int layer) override { | |||||
if (layer == 2) { | |||||
// Draw Plugs | |||||
Widget::draw(args); | |||||
// Draw cables | |||||
Widget::drawLayer(args, 2); | |||||
// Draw cable lights | |||||
nvgSave(args.vg); | |||||
nvgGlobalTint(args.vg, color::WHITE); | |||||
Widget::drawLayer(args, 1); | |||||
nvgRestore(args.vg); | |||||
// Draw cable shadows | |||||
Widget::drawLayer(args, 2); | |||||
// Draw cables | |||||
Widget::drawLayer(args, 3); | |||||
} | |||||
} | } | ||||
}; | }; | ||||
@@ -103,12 +118,28 @@ void RackWidget::step() { | |||||
} | } | ||||
void RackWidget::draw(const DrawArgs& args) { | void RackWidget::draw(const DrawArgs& args) { | ||||
// Darken all children by user setting | |||||
float b = settings::rackBrightness; | float b = settings::rackBrightness; | ||||
nvgGlobalTint(args.vg, nvgRGBAf(b, b, b, 1)); | |||||
// Draw rack rails and modules | |||||
Widget::draw(args); | Widget::draw(args); | ||||
// Draw translucent dark rectangle | |||||
if (b < 1.f) { | |||||
nvgBeginPath(args.vg); | |||||
nvgRect(args.vg, 0.0, 0.0, VEC_ARGS(box.size)); | |||||
nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 1.f - b)); | |||||
nvgFill(args.vg); | |||||
} | |||||
// Draw lights and halos | |||||
Widget::drawLayer(args, 1); | |||||
// Tint all draws after this point | |||||
nvgGlobalTint(args.vg, nvgRGBAf(b, b, b, 1)); | |||||
// Draw cables | |||||
Widget::drawLayer(args, 2); | |||||
// Draw selection rectangle | // Draw selection rectangle | ||||
if (internal->selecting) { | if (internal->selecting) { | ||||
nvgBeginPath(args.vg); | nvgBeginPath(args.vg); | ||||