best way for a module to capture clicks anywhere on other modules?

Hi devs!

After quite some time where all my Rack hours were making music, I’m coding again (uh-oh) on a proof of concept.

For now I’m aiming for a workflow where:

  • I click my own module (or a widget thereof);
  • When I immediately click another module, my module gets a pointer to that module (or, equivalently, its moduleId).

I’ve got an approximation of this working fine based on the mapping technique used in Core MIDI-Map:

  1. override onDeselect();
  2. inside it, if (APP->scene->rack->touchedParam) then touchedParam->module gets me the pointer, at which point I can do as I please.

But this only works if a ParamWidget gets touched, not any part of module (and that only works in turn because ParamWidgets have a special onButton event that updates touchedParam in APP->scene->rack). I’d like to be able to click anywhere on a module, including on its panel, and get the desired result.

I could extend the touchedParam approach in a custom build to add a similar touchedModule routine in ModuleWidget, but I’m trying to make everything work on vanilla Rack so that I can eventually consider distributing this. I feel as though this must be a solved problem but I’m drawing a blank on existing modules with this workflow…

Thanks in advance for any good ideas or prior art!

PS. I think if I understood the event system better I would be able to move past my lingering concern that my onDeselect() method might sometimes get called before the scene’s onButton() event and therefore not get the new param. I know this has to work reliably because it’s what @Vortico did in MIDI-Map but I haven’t done the necessary work to figure out why it’s reliable. If anyone knows why this works off-hand and feels like tossing out a sentence or two on it, it would put me at ease :slight_smile:

OK, I think I’ve got a working solution (after much too long). If anyone’s curious, the basic idea is:

  void onDeselect(const DeselectEvent& e) override {
    if (!module)
      return;

    // NOT APP->scene->getMousePos(); that returns window coordinates
    math::Vec mousePosOnDeselect = APP->scene->rack->getMousePos(); 

    // NOT APP->scene->rack->children (which are intermediate container widgets)
    for (Widget* child : APP->scene->rack->getModuleContainer()->children) {
      ModuleWidget* mw = dynamic_cast<ModuleWidget*>(child);

      if (mw && mw->getBox().contains(mousePosOnDeselect)) {
        INFO("Selected %s.%s (#%lli)", 
          mw->module->getModel()->plugin->slug.c_str(),
          mw->module->getModel()->slug.c_str(),
          mw->module->getId());

        break; // there can be only one
      }
    }

The two gotchas (of which #1 took up most of my debugging time) are noted in comments.

After I test this I’ll mark it as a solution, but if anyone has a better solution, please chime in! I still wonder if there’s a bottom-up approach that uses getAncestorOfType<ModuleWidget>()

3 Likes