2 Abstract superclass for
9 ProxyMixer (ProxySpaceGui),
11 and possibly others to follow.
14 an observed object (a proxy, monitor, space, envir, ...)
15 a skipjack for watching it;
16 a getState method for getting all the observed state
17 a prevState instvar for keeping the old state around;
18 a checkUpdate method for comparing old and new state,
19 and updating the gui elements that have changed;
21 parent, minimum bounds for its numItems, actual bounds;
23 if the object has settings, make an EnvirGui or ParamGui
24 ParamGui can do sliders or knobs ...
26 Simpler ones: EnvirGui, TaskProxyGui;
31 var <object, <numItems, <parent, <bounds, <zone, <minSize, <defPos, <skin, <font;
32 var <nameView, <csView;
33 var <prevState, <skipjack, <scroller;
34 var <config, <hasWindow = false;
37 Class.initClassTree(GUI);
40 fontSpecs: ["Helvetica", 12],
41 fontColor: Color.black,
42 background: Color(0.8, 0.85, 0.7, 0.5),
43 foreground: Color.grey(0.95),
44 onColor: Color(0.5, 1, 0.5),
45 offColor: Color.clear,
46 hiliteColor: Color.green(1.0, 0.5),
56 *new { |object, numItems = (0), parent, bounds, makeSkip = true, options = #[]|
57 ^super.newCopyArgs(nil, numItems, parent, bounds)
58 .init(makeSkip, options)
62 init { |makeSkip, options|
65 font = Font(*skin.fontSpecs);
68 // calc bounds - at least minbounds
69 this.setDefaults(options);
70 this.calcBounds(options);
72 if (parent.isNil) { this.makeWindow };
75 // then put all the other gui items on it
76 this.makeViews(options);
78 if (makeSkip) { this.makeSkip };
81 accepts { |obj| ^true }
84 if (this.accepts(obj)) {
88 "% : object % not accepted!".format(this.class, obj).warn;
93 if (hasWindow) { parent.name_(this.winName(name)) };
94 if (nameView.notNil) {
95 try { nameView.object_(object) };
96 nameView.string_(name);
101 ^nameView.notNil and: { nameView.string.notNil }
104 getName { ^try { object.key } ? "_anon_" }
105 winName { |name| ^this.class.name ++ $_ ++ (name ?? { this.getName }) }
109 if(bounds.isKindOf(Rect)) {
110 bounds.setExtent(max(bounds.width, minSize.x), max(bounds.height, minSize.y));
114 defBounds = Rect.fromPoints(defPos, defPos + minSize + (skin.margin + skin.margin));
120 if (bounds.isKindOf(Point)) {
121 bounds = defBounds.setExtent(max(bounds.x, minSize.x), max(bounds.y, minSize.y));
126 parent = Window(this.winName, bounds.copy.resizeBy(10, 10)).front;
127 parent.addFlowLayout;
132 zone = CompositeView(parent, bounds).background_(Color.white);
133 zone.addFlowLayout(skin.margin, skin.gap);
135 zone.background_(skin.foreground);
139 if (hasWindow) { parent.bounds = parent.bounds.moveTo(h, v); }
143 if (hasWindow) { parent.close }
147 skipjack = SkipJack({
151 // (this.getName + "checkUpdate failed.").postln;
155 { parent.isNil or: { parent.isClosed } },
161 // these methods should be overridden in subclasses:
162 setDefaults { |options|
166 defPos = skin.margin;
168 minSize = 250 @ (numItems * skin.buttonHeight + skin.headHeight);
169 // "minSize: %\n".postf(minSize);
173 var lineheight = max(
174 skin.buttonHeight * numItems + skin.headHeight,
175 zone.bounds.height) - (skin.margin.y * 2);
177 nameView = DragBoth(zone, Rect(0,0, 60, lineheight))
180 .receiveDragHandler_({ arg obj; this.object = View.currentDrag });
182 csView = EZText(zone,
183 Rect(0,0, bounds.width - 65, lineheight),
184 nil, { |ez| object = ez.value; })
186 csView.bounds.postln;
190 // get all the state I need to know of the object I am watching
195 var newState = this.getState;
197 // compare newState and prevState, update gui items as needed
198 if (newState == prevState) { ^this };
200 if (newState[\object] != prevState[\object]) {
201 this.name_(this.getName);
202 if (csView.textField.hasFocus.not) { csView.value_(object) };
204 prevState = newState;
208 // if numItems is exceeded, shift items along by some number with an EZScroller