So, prompted by your concerns I have begun to refactor some core classes in Duplex…
First thing that struck was how much of a hack the “xypad” integration was. It is ugly, with code scattered all over the place
I’m following some “guidelines” for how to improve this situation, and begun implementing those changes:
1. Refactor UIComponent-specific code into the actual UIComponent class
Not really a fundamental difference from how the Display class currently works, but it makes it much easier to see how the various elements fit together. So, essentially a nice thing for developers.
I imagine it would works by having the various UIComponents expose some static methods that the Display can then choose to use. For example, the ability to validate that required attributes are present in the control-map, and the actual code for building a virtual representation in the Duplex browser UI.
Also, I believe that such a change could make it simpler to write a class specifically for holding the sysex value (potentially, the “invisible labels” could then be defined completely inside a custom UI class). Your input on this would be very much appreciated (I’m planning to share these modifications as soon as there’s something roughly working…)
2. Introducing , a new control-map node
This will allow us to get rid of the ugly XYPad hack, and make it possible to use the native XYPad widget in Renoise for MIDI controllers, too. I suspect this approach could be used for other things as well.
Consider the following XML snippets:
-- Example OSC mapping
<param value="/tilt %f %f" type="xypad" minimum="0" maximum="127">
-- Example MIDI mapping
<param name="XYPad_X" type="xypad">
<subparam value="CC#56" orientation="vertical" minimum="0" maximum="127"></subparam>
<subparam value="CC#57" orientation="horizontal" minimum="0" maximum="127"></subparam>
3. Reduce boilerplate code
An application like mlrx contains nearly 10.000 lines of lua code. So, having written all of that I have started looking into how much it could be reduced, while still being readable.
Here’s a couple of examples:
This:
local map = self.mappings.label_x
local c = UILabel(self)
c.group_name = map.group_name
c.tooltip = map.description
c:set_pos(map.index)
self._controls.label_x = c
self:_add_component(c)
…becomes
self._controls.label_x = UILabel(self,self.mappings.label_x)
Wait, what just happened here? Simple enough, all the basic properties (like group_name, position and tooltip) was assigned by supplying the “mapping” table as an extra argument. Also, the call to Application.add_component() was automatically done when creating the UIComponent
With more complex examples, you can’t expect to save as much typing though:
local map = self.mappings.toggle_shuffle_cut
local c = UIButton(self)
c.group_name = map.group_name
c.tooltip = map.description
c:set_pos(map.index)
c.on_press = function()
-- do something
end
self._controls.toggle_shuffle_cut = c
…becomes
local c = UIButton(self,self.mappings.toggle_shuffle_cut)
c.on_press = function()
-- do something
end
self._controls.toggle_shuffle_cut = c