| @@ -38,7 +38,7 @@ LDFLAGS += \ | |||
| -L$(HOME)/pkg/portaudio-r1891-build/lib/x64/ReleaseMinDependency -lportaudio_x64 \ | |||
| -Wl,--export-all-symbols,--out-implib,libRack.a -mwindows | |||
| TARGET = Rack.exe | |||
| # OBJECTS = Rack.res | |||
| OBJECTS = Rack.res | |||
| %.res: %.rc | |||
| windres $^ -O coff -o $@ | |||
| @@ -59,6 +59,8 @@ struct ModuleWidget : OpaqueWidget { | |||
| struct WireWidget : OpaqueWidget { | |||
| OutputPort *outputPort = NULL; | |||
| InputPort *inputPort = NULL; | |||
| OutputPort *hoveredOutputPort = NULL; | |||
| InputPort *hoveredInputPort = NULL; | |||
| Wire *wire = NULL; | |||
| NVGcolor color; | |||
| @@ -183,6 +185,8 @@ struct InputPort : Port { | |||
| void onDragStart(); | |||
| void onDragDrop(Widget *origin); | |||
| void onDragEnter(Widget *origin); | |||
| void onDragLeave(Widget *origin); | |||
| }; | |||
| struct OutputPort : Port { | |||
| @@ -190,6 +194,8 @@ struct OutputPort : Port { | |||
| void onDragStart(); | |||
| void onDragDrop(Widget *origin); | |||
| void onDragEnter(Widget *origin); | |||
| void onDragLeave(Widget *origin); | |||
| }; | |||
| //////////////////// | |||
| @@ -104,6 +104,8 @@ struct Widget { | |||
| /** Called when a widget responds to `onMouseMove` and is being dragged */ | |||
| virtual void onDragMove(Vec mouseRel) {} | |||
| /** Called when a widget responds to `onMouseUp` for a left button release and a widget is being dragged */ | |||
| virtual void onDragEnter(Widget *origin) {} | |||
| virtual void onDragLeave(Widget *origin) {} | |||
| virtual void onDragDrop(Widget *origin) {} | |||
| virtual void onAction() {} | |||
| @@ -318,6 +320,7 @@ struct Scene : OpaqueWidget { | |||
| extern Vec gMousePos; | |||
| extern Widget *gHoveredWidget; | |||
| extern Widget *gDraggedWidget; | |||
| extern Widget *gDragHoveredWidget; | |||
| extern Widget *gSelectedWidget; | |||
| extern int gGuiFrame; | |||
| @@ -60,6 +60,7 @@ void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { | |||
| gDraggedWidget->onDragEnd(); | |||
| } | |||
| gDraggedWidget = NULL; | |||
| gDragHoveredWidget = NULL; | |||
| } | |||
| } | |||
| } | |||
| @@ -69,27 +70,33 @@ void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | |||
| Vec mouseRel = mousePos.minus(gMousePos); | |||
| gMousePos = mousePos; | |||
| if (glfwGetInputMode(window, GLFW_CURSOR) != GLFW_CURSOR_DISABLED) { | |||
| // TODO Lock gMousePos | |||
| } | |||
| bool locked = glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED; | |||
| // onScroll | |||
| // int middleButton = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE); | |||
| // if (middleButton == GLFW_PRESS) { | |||
| // gScene->scrollWidget->onScroll(mouseRel.neg()); | |||
| // } | |||
| // onMouseMove | |||
| Widget *hovered = gScene->onMouseMove(gMousePos, mouseRel); | |||
| if (gDraggedWidget) { | |||
| // onDragMove | |||
| // Drag slower if Ctrl is held | |||
| bool fine = glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS; | |||
| float factor = fine ? 1.0/8.0 : 1.0; | |||
| gDraggedWidget->onDragMove(mouseRel.mult(factor)); | |||
| if (locked) { | |||
| bool ctrl = glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS; | |||
| if (ctrl) | |||
| mouseRel = mouseRel.mult(0.1); | |||
| } | |||
| gDraggedWidget->onDragMove(mouseRel); | |||
| if (hovered != gDragHoveredWidget) { | |||
| if (gDragHoveredWidget) { | |||
| gDragHoveredWidget->onDragLeave(gDraggedWidget); | |||
| } | |||
| if (hovered) { | |||
| hovered->onDragEnter(gDraggedWidget); | |||
| } | |||
| gDragHoveredWidget = hovered; | |||
| } | |||
| } | |||
| else { | |||
| // onMouseMove | |||
| Widget *hovered = gScene->onMouseMove(gMousePos, mouseRel); | |||
| if (hovered != gHoveredWidget) { | |||
| if (gHoveredWidget) { | |||
| // onMouseLeave | |||
| @@ -99,8 +106,8 @@ void cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { | |||
| // onMouseEnter | |||
| hovered->onMouseEnter(); | |||
| } | |||
| gHoveredWidget = hovered; | |||
| } | |||
| gHoveredWidget = hovered; | |||
| } | |||
| } | |||
| @@ -5,6 +5,7 @@ namespace rack { | |||
| Vec gMousePos; | |||
| Widget *gHoveredWidget = NULL; | |||
| Widget *gDraggedWidget = NULL; | |||
| Widget *gDragHoveredWidget = NULL; | |||
| Widget *gSelectedWidget = NULL; | |||
| int gGuiFrame; | |||
| @@ -22,11 +22,24 @@ void InputPort::onDragStart() { | |||
| void InputPort::onDragDrop(Widget *origin) { | |||
| if (connectedWire) return; | |||
| if (gRackWidget->activeWire) { | |||
| gRackWidget->activeWire->hoveredInputPort = NULL; | |||
| if (gRackWidget->activeWire->inputPort) return; | |||
| gRackWidget->activeWire->inputPort = this; | |||
| connectedWire = gRackWidget->activeWire; | |||
| } | |||
| } | |||
| void InputPort::onDragEnter(Widget *origin) { | |||
| if (connectedWire) return; | |||
| if (gRackWidget->activeWire) { | |||
| gRackWidget->activeWire->hoveredInputPort = this; | |||
| } | |||
| } | |||
| void InputPort::onDragLeave(Widget *origin) { | |||
| if (gRackWidget->activeWire) { | |||
| gRackWidget->activeWire->hoveredInputPort = NULL; | |||
| } | |||
| } | |||
| } // namespace rack | |||
| @@ -9,16 +9,15 @@ void Light::draw(NVGcontext *vg) { | |||
| Vec c = box.getCenter(); | |||
| Vec r = box.size.div(2.0); | |||
| nvgBeginPath(vg); | |||
| nvgEllipse(vg, c.x, c.y, r.x, r.y); | |||
| nvgFillColor(vg, colorOutline); | |||
| nvgFill(vg); | |||
| nvgBeginPath(vg); | |||
| nvgEllipse(vg, c.x, c.y, r.x - 1.0, r.y - 1.0); | |||
| nvgFillColor(vg, color); | |||
| nvgFill(vg); | |||
| nvgStrokeWidth(vg, 1.0); | |||
| nvgStrokeColor(vg, colorOutline); | |||
| nvgStroke(vg); | |||
| // float radius = box.size.x / 2.0; | |||
| // NVGcolor icol, ocol; | |||
| // NVGpaint paint; | |||
| @@ -22,11 +22,24 @@ void OutputPort::onDragStart() { | |||
| void OutputPort::onDragDrop(Widget *origin) { | |||
| if (connectedWire) return; | |||
| if (gRackWidget->activeWire) { | |||
| gRackWidget->activeWire->hoveredOutputPort = NULL; | |||
| if (gRackWidget->activeWire->outputPort) return; | |||
| gRackWidget->activeWire->outputPort = this; | |||
| connectedWire = gRackWidget->activeWire; | |||
| } | |||
| } | |||
| void OutputPort::onDragEnter(Widget *origin) { | |||
| if (connectedWire) return; | |||
| if (gRackWidget->activeWire) { | |||
| gRackWidget->activeWire->hoveredOutputPort = this; | |||
| } | |||
| } | |||
| void OutputPort::onDragLeave(Widget *origin) { | |||
| if (gRackWidget->activeWire) { | |||
| gRackWidget->activeWire->hoveredOutputPort = NULL; | |||
| } | |||
| } | |||
| } // namespace rack | |||
| @@ -36,5 +36,4 @@ void Port::onDragEnd() { | |||
| gRackWidget->activeWire = NULL; | |||
| } | |||
| } // namespace rack | |||
| @@ -21,6 +21,8 @@ void RadioButton::onDragDrop(Widget *origin) { | |||
| value = 1.0; | |||
| else | |||
| value = 0.0; | |||
| onAction(); | |||
| } | |||
| } | |||
| @@ -8,12 +8,10 @@ Widget::~Widget() { | |||
| // You should only delete orphaned widgets | |||
| assert(!parent); | |||
| // Stop dragging and hovering this widget | |||
| if (gHoveredWidget == this) | |||
| gHoveredWidget = NULL; | |||
| if (gDraggedWidget == this) | |||
| gDraggedWidget = NULL; | |||
| if (gSelectedWidget == this) | |||
| gSelectedWidget = NULL; | |||
| if (gHoveredWidget == this) gHoveredWidget = NULL; | |||
| if (gDraggedWidget == this) gDraggedWidget = NULL; | |||
| if (gDragHoveredWidget == this) gDragHoveredWidget = NULL; | |||
| if (gSelectedWidget == this) gSelectedWidget = NULL; | |||
| clearChildren(); | |||
| } | |||
| @@ -71,9 +69,9 @@ void Widget::step() { | |||
| } | |||
| void Widget::draw(NVGcontext *vg) { | |||
| nvgSave(vg); | |||
| nvgTranslate(vg, box.pos.x, box.pos.y); | |||
| for (Widget *child : children) { | |||
| nvgSave(vg); | |||
| child->draw(vg); | |||
| } | |||
| nvgRestore(vg); | |||
| @@ -7,16 +7,18 @@ namespace rack { | |||
| static void drawPlug(NVGcontext *vg, Vec pos, NVGcolor color) { | |||
| NVGcolor colorOutline = nvgLerpRGBA(color, nvgRGBf(0.0, 0.0, 0.0), 0.5); | |||
| nvgBeginPath(vg); | |||
| nvgCircle(vg, pos.x, pos.y, 10.5); | |||
| nvgFillColor(vg, colorOutline); | |||
| nvgFill(vg); | |||
| // Plug solid | |||
| nvgBeginPath(vg); | |||
| nvgCircle(vg, pos.x, pos.y, 9.5); | |||
| nvgFillColor(vg, color); | |||
| nvgFill(vg); | |||
| // Border | |||
| nvgStrokeWidth(vg, 1.0); | |||
| nvgStrokeColor(vg, colorOutline); | |||
| nvgStroke(vg); | |||
| // Hole | |||
| nvgBeginPath(vg); | |||
| nvgCircle(vg, pos.x, pos.y, 5.5); | |||
| nvgFillColor(vg, nvgRGBf(0.0, 0.0, 0.0)); | |||
| @@ -118,25 +120,35 @@ void WireWidget::updateWire() { | |||
| } | |||
| void WireWidget::draw(NVGcontext *vg) { | |||
| Vec outputPos, inputPos; | |||
| Vec absolutePos = getAbsolutePos(); | |||
| float opacity = dynamic_cast<RackScene*>(gScene)->toolbar->wireOpacitySlider->value / 100.0; | |||
| float tension = dynamic_cast<RackScene*>(gScene)->toolbar->wireTensionSlider->value; | |||
| // Compute location of pos1 and pos2 | |||
| // Display the actively dragged wire as opaque | |||
| if (gRackWidget->activeWire == this) | |||
| opacity = 1.0; | |||
| // Compute location of outputPos and inputPos | |||
| Vec outputPos; | |||
| if (outputPort) { | |||
| outputPos = Rect(outputPort->getAbsolutePos(), outputPort->box.size).getCenter(); | |||
| } | |||
| else if (hoveredOutputPort) { | |||
| outputPos = Rect(hoveredOutputPort->getAbsolutePos(), hoveredOutputPort->box.size).getCenter(); | |||
| } | |||
| else { | |||
| outputPos = gMousePos; | |||
| opacity = 1.0; | |||
| } | |||
| Vec inputPos; | |||
| if (inputPort) { | |||
| inputPos = Rect(inputPort->getAbsolutePos(), inputPort->box.size).getCenter(); | |||
| } | |||
| else if (hoveredInputPort) { | |||
| inputPos = Rect(hoveredInputPort->getAbsolutePos(), hoveredInputPort->box.size).getCenter(); | |||
| } | |||
| else { | |||
| inputPos = gMousePos; | |||
| opacity = 1.0; | |||
| } | |||
| outputPos = outputPos.minus(absolutePos); | |||