Yes, it is! However, be aware that the dependency notification system is separate from object prototypes. changed
and update
are defined on Object, so this protocol is available to every object. (I used a prototype in that example because I didn’t want to define a class only for this example, and then have to explain how to install the class.)
If you mean a tutorial chapter on basic GUI usage, sure. If you mean MVC, I think that’s a bit advanced for the “getting started” tutorial. It would be good to have a Guide-type document about MVC, and link to it from a “basic GUI” tutorial page. (We are pretty good about writing help for classes. The idea of writing “topic” documentation hasn’t really taken root though.)
I think a larger number of examples just set argument values directly…?
In any case, one thing that ended up making things easier for me (though at the time, I didn’t fully predict it) was that early on, I made a class that 1/ represents a value (with a ControlSpec) and 2/ syncs this value over to a control bus (which supports automation “for free”). This was originally for my Voicer
class, where I wanted e.g. filter frequency to act like a typical hardware or software synth (all notes follow the control), but I ended up changing the hierarchy to put a freestanding GenericGlobalControl at the top.
At that time I didn’t know MVC, but then when I started looking at that, GenericGlobalControl / VoicerGlobalControl turned out to be a good Model for a single control. The way I’m using changed
in these classes is a bit weird… I thought it was a good idea at the time; now I’m not so sure. But the following for instance is a legit MVC.
s.boot;
// example uses the ddwCommon quark (I think only this one)
~pan = GenericGlobalControl(\pan, nil, 0, \bipolar);
(
var slider = Slider(nil, Rect(800, 200, 200, 50)).front;
var updater = { |obj, msg|
switch(msg[\what])
{ \value } {
defer { slider.value = ~pan.spec.unmap(~pan.value) };
}
{ \modelWasFreed } {
~pan.removeDependant(updater);
}
};
~pan.addDependant(updater);
slider.action = { |view|
~pan.set(~pan.spec.map(view.value))
};
slider.onClose = { ~pan.removeDependant(updater) };
)
(
p = Pbind(
\instrument, \default,
\freq, Pexprand(200, 800, inf),
\dur, Pexprand(0.1, 0.4, inf),
\legato, Pexprand(0.3, 4, inf),
\pan, ~pan.asMap
).play;
)
// `watch`: sync LFO values back to the language side
// ... which fires off a dependant notification
// so the GUI picks it up "for free"
~pan.watch.automate { LFDNoise3.kr(LFDNoise3.kr(0.25).exprange(0.7, 14)).range(-1, 1) };
p.stop;
~pan.stopAuto.stopWatching;
~pan.free;
I’m rambling about that just to point out that an early object-modeling decision made some problems go away in the long term.
hjh