@@ -26,6 +26,7 @@ struct LedDisplayChoice : widget::OpaqueWidget { | |||
NVGcolor bgColor; | |||
LedDisplayChoice(); | |||
void draw(const DrawArgs& args) override; | |||
void drawLayer(const DrawArgs& args, int layer) override; | |||
void onButton(const ButtonEvent& e) override; | |||
}; | |||
@@ -36,6 +37,7 @@ struct LedDisplayTextField : ui::TextField { | |||
NVGcolor bgColor; | |||
LedDisplayTextField(); | |||
void draw(const DrawArgs& args) override; | |||
void drawLayer(const DrawArgs& args, int layer) override; | |||
int getTextPosition(math::Vec mousePos) override; | |||
}; | |||
@@ -276,79 +276,83 @@ void CableWidget::step() { | |||
void CableWidget::draw(const DrawArgs& args) { | |||
// Draw plugs | |||
Widget::draw(args); | |||
} | |||
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); | |||
@@ -45,7 +45,6 @@ LedDisplayChoice::LedDisplayChoice() { | |||
void LedDisplayChoice::draw(const DrawArgs& args) { | |||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | |||
if (bgColor.a > 0.0) { | |||
nvgBeginPath(args.vg); | |||
nvgRect(args.vg, 0, 0, box.size.x, box.size.y); | |||
@@ -53,16 +52,26 @@ void LedDisplayChoice::draw(const DrawArgs& args) { | |||
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); | |||
} | |||
@@ -87,8 +96,6 @@ LedDisplayTextField::LedDisplayTextField() { | |||
void LedDisplayTextField::draw(const DrawArgs& args) { | |||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | |||
// Background | |||
if (bgColor.a > 0.0) { | |||
nvgBeginPath(args.vg); | |||
@@ -97,24 +104,33 @@ void LedDisplayTextField::draw(const DrawArgs& args) { | |||
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); | |||
} | |||
@@ -44,24 +44,39 @@ struct ModuleContainer : widget::Widget { | |||
Widget::drawLayer(args, -1); | |||
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 { | |||
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) { | |||
// Darken all children by user setting | |||
float b = settings::rackBrightness; | |||
nvgGlobalTint(args.vg, nvgRGBAf(b, b, b, 1)); | |||
// Draw rack rails and modules | |||
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 | |||
if (internal->selecting) { | |||
nvgBeginPath(args.vg); | |||