Death to helper.py, Long live SvgHelper

Dunno, was just the license file that was in the template I used. I think MIT is basically as permissible as it gets, but I’m not a lawyer either heh.

I will post back here when I have something interesting to show.

neat :slight_smile:

1 Like

One thing I have discovered so far: since I need to reload the SVG panel anyway, so that VCV Rack can redraw it, I can use the same object to find all my hidden control objects. I already do a similar traversal in my Tube Unit module where I dynamically hide/show part of the SVG depending on whether any cables are connected to the input ports:

    TubeUnitWidget(TubeUnitModule* module)
        : tubeUnitModule(module)
    {
        setModule(module);
        svgPanel = createPanel(asset::plugin(pluginInstance, "res/tubeunit.svg"));
        setPanel(svgPanel);
        if (svgPanel && svgPanel->svg && svgPanel->svg->handle)
        {
            // Search for my special <path id="audio_emphasis_path" ... />
            // I toggle this path's visibility as needed, depending on whether there are audio inputs.
            // For now we capture a pointer to this path.
            for (NSVGshape* shape = svgPanel->svg->handle->shapes; shape != nullptr; shape = shape->next)
                if (!strcmp(shape->id, "audio_emphasis_path"))
                    audioEmphasisPath = shape;
        }

        // ... more code here ...
    }

I mention this because it’s more efficient to let VCV Rack load the file and parse the XML one time, and use the same in-memory data structure for both rendering and finding the locations of controls and ports.

Oh. Wow I feel dumb. Yeah, there’s no reason at all to load the SVG again, just use the loaded panel. I’ll probably look at making these helpers just take the SVG object from the panel.

smacks forehead

PRs also accepted :wink:

1 Like

It’s all cool… I’m really grateful you had this idea because that’s the important part. This has been bugging me for a long time. With your idea of loading component positions from the SVG at runtime, and my add-on idea of hot-loading without restarting VCV Rack, I think this is something that could catch on with other developers.

3 Likes

it absolutely would. at least, it definitely would for me. i have the same frustrations when iterating on panel layouts.

OK, good news: I have the first working proof of concept for hot-loading changes to an SVG file. It both updates the panel graphics and repositions components, all without having to restart VCV Rack. Here is a demo where I loaded my dummy module in VCV Rack, then saved changes to the SVG file, then clicked the special menu item for reloading the panel:

Here is the source code: beehive.cpp

The relevant portion of the SVG file is:

<circle id="beehive_output_left"  cx="40.0" cy="50.0" r="1.0" />
<circle id="beehive_output_right" cx="40.0" cy="60.0" r="1.0" />

The only tricky bit is you have to work around VCV Rack a little bit. When you call createPanel, it loads and parses the SVG file the first time, but if you call it again, it has it cached. You have to force it to actually reload the file:

if (svgPanel == nullptr)
{
    // Load and parse the SVG file for the first time.
    svgPanel = createPanel(svgFileName);
    setPanel(svgPanel);
}
else
{
    // Once loaded, VCV Rack caches the panel internally.
    // We have to force it to reload the file.
    try
    {
        svgPanel->svg->loadFile(svgFileName);
    }
    catch (Exception& e)
    {
        WARN("Cannot reload panel from %s: %s", svgFileName.c_str(), e.what());
    }
}
4 Likes

Yeah, that just isn’t a problem for me. I just don’t iterate that much in the panel code. But I can see how if you did position elements in code there would be a lot of back and forth.

I will say your panels look nice. I do feel like anyone who makes their panels in code is making life difficult, but as long as it’s worth it, that legit.

1 Like

Oh that’s very nice Don! That is a sweet piece of code. :clap:

1 Like

Here is a better demo where I added some knobs and panel art. You can watch me edit the SVG file and dynamically reload it.

5 Likes

Im liking this idea of dynamic UI from the svg file. I can see multiple use cases if my understanding is correct:

  • Decoupling of UI design and code, Designers and coders can work separately
  • 3rd party / user mods, if the module developer chooses to keep the reload option
  • Alternate UI’s could be much more than Dark / Light variants, complete remodels could be viable by simply switching svg.

Can panels be resized dynamically? My thoughts started with the idea of a simple and advanced UI, the former with only the essential controls, and then switch to the advanced ui where all the parameters become available. Would need some more thought, as the inputs and outputs would have to remain present on all designs, whereas parameters could be omitted.

Nice work :slight_smile:

2 Likes

Yes, definitely! I think the developer needs to get the SVG file started so that the designer knows what all the control identifiers should be. It can be totally ugly to start with, but everything needs to be there.

It would be possible using SvgHelper without the reload option, but with it, the process is a lot quicker, which is of course why I wanted it for myself. My first instinct was to keep the reload option until I feel like the panel design is ready for release, then remove it before submitting to VCV Rack for approval. But now you’ve got me thinking maybe it should still be there. Very few people would ever use it, but those who want it would benefit greatly.

That’s a very interesting question! I hadn’t even thought of that, but perhaps it is possible. In fact, I have a vague feeling I have already seen resizable modules. Maybe someone else can provide an example? I would also need to find out what happens when controls are outside the viewport. I doubt putting ports outside the viewport is ever a good idea, even if possible.

1 Like

Something like this?

4 Likes

in addition to the one @Steve_Russell shared above me, the Fundamental “Blank” panel is also resizable by clicking and dragging the edge of the panel.

1 Like

By the looks of it that’s what JW based his code on; then put it into a reusable header.

1 Like

It’s interesting how there’s a place where it tries to update the widget size, but if the resize function fails, it puts the size back the way it was. I suppose the failure can happen if the widget would overlap another module?

1 Like

At least on my Windows machine, if you “grab” the right handle and move normally i.e. slowly to the right, the widget resizes correctly as you would expect and goes no further if there’s a module to the right.

If one moves the handle too fast and beyond the left hand edge of a module to the right, the widget doesn’t resize correctly until you move back to the space between the two modules, wherein it will snap to the correct size i.e. it will fill the gap between the two modules.

1 Like

Hey all,

Just wanted to mention that I’ve updated SvgHelper:

  • setPanel renamed to loadPanel
  • It’s now a mixin, meant to be used on your ModuleWidget subclasses
  • Thanks to @cosinekitty you can call loadPanel multiple times to change/update the panel SVG while Rack is running.

Changes pushed up to the repo.

2 Likes

My BASICally module is resizable, at least on one side. There are some subtle issues with this, by the way; for example, you should make resizing events part of undo/redo, or else one will get “transporter malfunctions”. My code hopefully is clear enough to provide some guidance about how resizing can work.

1 Like

Just one word: impressive! :ok_hand:

Thanks for the additional information. It confirms what I was already thinking. Namely, I started playing around with resizing and quickly discovered there is a lot more complexity than I want to deal with at this time.

My main purpose was to accelerate development. As an engineering trade-off, it’s a big win to be able to reload the SVG every time I move things around in a fixed-size panel. This is the loop I find myself iterating on the most, especially because I like to generate these parts of my SVG from a Python script.

It’s really nice to change Python code, run it, and then just reload the panel from my module’s right-click menu. Others will no doubt benefit from a workflow involving making a change in Inkscape, saving the SVG, and reloading their panel.

It doesn’t feel worth the effort to be able to change the panel’s size without restarting VCV Rack. My sense is, I generally know how big my panel needs to be. If I change my mind, I will change it once or twice at most, and a couple extra restarts of VCV Rack only takes a few seconds.

3 Likes