| @@ -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); | |||