Having some trouble with widget drag+drop positioning

Maybe it’s just late, but I’m having a tough time with what should be some easy code. I’m trying to create a custom widget that can be dragged to a position:

The circle (grey) and bounding box (red-ish) should be in the same location. I’m not so worried about that. What I’m trying to accomplish is the ability to drag the widget to a new x-location.

But I think I’m running into some misunderstanding on my part about the relationship between a widget’s position and box.x, box.y.

Here’s the entirety of the widget’s code:

struct RangeGrabberRightWidget : TransparentWidget
{
  GrooveBox *module;
  bool is_moused_over = false;
  float diameter = 24.0;
  float radius = diameter / 2.0;
  Vec drag_position;

  RangeGrabberRightWidget()
  {
    box.size = Vec(diameter, diameter);
  }

  void draw(const DrawArgs &args) override
  {
    Vec position = this->getPosition();

    const auto vg = args.vg;
    nvgSave(vg);
    nvgBeginPath(vg);
    nvgRect(vg, box.pos.x, 0, box.size.x, box.size.y);
    nvgFillColor(vg, nvgRGBA(120, 20, 20, 100));
    nvgFill(vg);
    nvgRestore(vg);

    // Draw circle
    nvgSave(vg);
    nvgBeginPath(vg);

    if(module) {
      nvgCircle(vg, position.x, 0, radius);
    }
    else {
      nvgCircle(vg, position.x, 0, radius);
    }

    if(is_moused_over)
    {
      nvgFillColor(vg, nvgRGBA(200, 200, 200, 40));
    }
    else
    {
      nvgFillColor(vg, nvgRGBA(200, 200, 200, 90));
    }

    nvgFill(vg);

    nvgRestore(vg);

  }

  void onButton(const event::Button &e) override
  {
    if(e.button == GLFW_MOUSE_BUTTON_LEFT && e.action == GLFW_PRESS)
    {
      e.consume(this);
      drag_position = e.pos;
    }
  }

  void onEnter(const event::Enter &e) override
  {
    this->is_moused_over = true;
    TransparentWidget::onEnter(e);
  }

  void onLeave(const event::Leave &e) override
  {
    this->is_moused_over = false;
    TransparentWidget::onLeave(e);
  }

  void onHover(const event::Hover& e) override {
    e.consume(this);
    this->is_moused_over = true;
  }

  void step() override {
    TransparentWidget::step();
  }

  void onDragMove(const event::DragMove &e) override
  {
    // TransparentWidget::onDragMove(e);
    float zoom = getAbsoluteZoom();
    drag_position = drag_position.plus(e.mouseDelta.div(zoom));
    this->box.pos = Vec(drag_position.x, this->box.pos.y);
    // Vec current_position = this->getPosition();
    // this->setPosition(Vec(drag_position.x, current_position.y));
  }


  void onDragStart(const event::DragStart &e) override
  {
    TransparentWidget::onDragStart(e);
  }

  void onDragEnd(const event::DragEnd &e) override
  {
    TransparentWidget::onDragEnd(e);
  }

};

And here’s a video of the behavior:

I’m a bit lost, so any ideas would help!

Try this one:

1 Like

I’d like to learn more abstractly what’s going on. My very first attempt seemed pretty promising. Here’s a video. The bounding box is in RED.

Everything seemed great for the initial drag. However, when I click on the widget to drag it again, it “snaps” to somewhere close to the original position. This feels to me like my attempt to position the widget might be wrong. The relevant code for this is:

struct RangeGrabberRightWidget : TransparentWidget
{
  GrooveBox *module;
  bool is_moused_over = false;
  float diameter = 24.0;
  float radius = diameter / 2.0;
  Vec drag_position;

  RangeGrabberRightWidget()
  {
    box.size = Vec(diameter, diameter);
  }

  void draw(const DrawArgs &args) override
  {
    // Draw bounding box in red, for debugging
    const auto vg = args.vg;
    nvgSave(vg);
    nvgBeginPath(vg);
    nvgRect(vg, 0, 0, box.size.x, box.size.y);
    nvgFillColor(vg, nvgRGBA(120, 20, 20, 100));
    nvgFill(vg);
    nvgRestore(vg);

    // Draw circle
    nvgSave(vg);
    nvgBeginPath(vg);
    nvgCircle(vg, box.size.x - radius, box.size.y - radius, radius);
    nvgFillColor(vg, nvgRGBA(200, 200, 200, 40));
    nvgFill(vg);
    nvgRestore(vg);
  }

  void onButton(const event::Button &e) override
  {
    if(e.button == GLFW_MOUSE_BUTTON_LEFT && e.action == GLFW_PRESS)
    {
      e.consume(this);
      drag_position = e.pos;
    }
  }

  void onDragMove(const event::DragMove &e) override
  {
    // TransparentWidget::onDragMove(e);
    float zoom = getAbsoluteZoom();
    drag_position = drag_position.plus(e.mouseDelta.div(zoom));
    this->box.pos = Vec(drag_position.x, this->box.pos.y);
  }

The real weird part is that the position seems to “reset” when I click on the widget a second time. I think that if I can figure out why that’s happening, then I’ll be able to get my code working. Any thoughts?

I don’t know, it’d probably try debugging.

1 Like

Without looking at this too closely, isn’t this always the relative position within the widget? As you’re using this to accumulate the relative mouse movement and offset the box position, shouldn’t you be setting drag_position to this->box.pos (in onButton) instead, or something relative to it?

1 Like

This was it! Thanks @nickfeisst !

1 Like