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);
100 getName { ^try { object.key } ? "_anon_" }
101 winName { |name| ^this.class.name ++ $_ ++ (name ?? { this.getName }) }
105 if(bounds.isKindOf(Rect)) {
106 bounds.setExtent(max(bounds.width, minSize.x), max(bounds.height, minSize.y));
110 defBounds = Rect.fromPoints(defPos, defPos + minSize + (skin.margin + skin.margin));
116 if (bounds.isKindOf(Point)) {
117 bounds = defBounds.setExtent(max(bounds.x, minSize.x), max(bounds.y, minSize.y));
122 parent = Window(this.winName, bounds.copy.resizeBy(10, 10)).front;
123 parent.addFlowLayout;
128 zone = CompositeView(parent, bounds).background_(Color.white);
129 zone.addFlowLayout(skin.margin, skin.gap);
131 zone.background_(skin.foreground);
135 if (hasWindow) { parent.bounds = parent.bounds.moveTo(h, v); }
139 if (hasWindow) { parent.close }
143 skipjack = SkipJack({
147 // (this.getName + "checkUpdate failed.").postln;
151 { parent.isNil or: { parent.isClosed } },
157 // these methods should be overridden in subclasses:
158 setDefaults { |options|
162 defPos = skin.margin;
164 minSize = 250 @ (numItems * skin.buttonHeight + skin.headHeight);
165 // "minSize: %\n".postf(minSize);
169 var lineheight = max(
170 skin.buttonHeight * numItems + skin.headHeight,
171 zone.bounds.height) - (skin.margin.y * 2);
173 nameView = DragBoth(zone, Rect(0,0, 60, lineheight))
176 .receiveDragHandler_({ arg obj; this.object = View.currentDrag });
178 csView = EZText(zone,
179 Rect(0,0, bounds.width - 65, lineheight),
180 nil, { |ez| object = ez.value; })
182 csView.bounds.postln;
186 // get all the state I need to know of the object I am watching
191 var newState = this.getState;
193 // compare newState and prevState, update gui items as needed
194 if (newState == prevState) { ^this };
196 if (newState[\object] != prevState[\object]) {
197 this.name_(this.getName);
198 if (csView.textField.hasFocus.not) { csView.value_(object) };
200 prevState = newState;
204 // if numItems is exceeded, shift items along by some number with an EZScroller