HelpBrowser: path box becomes a more conventional search box
[supercollider.git] / SCClassLibrary / JITLib / GUI / TaskProxyGui.sc
blob299cd96725e98b128b6f5c78013ae3634db592d4
1 TaskProxyGui : JITGui {
3         var <nameBut, <playBut, <pauseBut, <srcBut, <envBut;
4         var <envirGui, <usedKeys;
6         setDefaults { |options|
7                 defPos = 10@260;
8                 minSize = 250 @ (skin.buttonHeight * (numItems + 1) + (numItems.sign * 4));
9                 if (parent.notNil) { skin = skin.copy.put(\margin, 0@0) };
10         //      "% - minSize: %\n".postf(this.class, minSize);
11         }
13         makeViews { |options|
14                 var height = skin.buttonHeight;
15                 var lineWidth = zone.bounds.width - (skin.margin.y * 2);
16                 var width = lineWidth * 0.62 / 4;
17                 var nameWidth = lineWidth * 0.38;
18                 var zoneMargin = if ( (numItems > 0) or: { parent.isKindOf(Window.implClass) }) { skin.margin } { 0@0 };
20                 zone.decorator = FlowLayout(zone.bounds, zoneMargin, skin.gap);
22                 nameBut = Button(zone, Rect(0,0, nameWidth, height))
23                         .font_(font)
24                         .resize_(2)
25                         .states_([
26                                 [" ", skin.fontColor, skin.onColor]
27                         ])
28                         .keyDownAction_({ |btn, char|
29                                 char.postcs;
30                                 if (char.ascii == 127) {
31                                         object.clear;
32                                         object.class.all.removeAt(btn.states.first.first.asSymbol);
33                                          object = nil;
34                                 };
35                         });
37                 playBut = Button(zone, Rect(0,0, width, height))
38                         .font_(font)
39                         .resize_(3)
40                         .states_([
41                                 [" >", skin.fontColor, skin.offColor],
42                                 [" _", skin.fontColor, skin.onColor ],
43                                 [" |", skin.fontColor, skin.offColor ]
44                         ])
45                         .action_({ |but| var string;
46                                 if (object.notNil) {
47                                         if (History.started) {
48                                                                 // historical action, sets cmdLine and gets recorded.
49                                                 string = object.asCompileString
50                                                         ++ [".play;", ".play;", ".stop;" ][but.value];
51                                                 thisProcess.interpreter.cmdLine_(string)
52                                                         .interpretPrintCmdLine;
53                                         } {
54                                                                 // a-historical, but faster
55                                                 [ { object.play }, { object.play }, { object.stop } ][but.value].value
56                                         };
57                                         this.checkUpdate;
58                                 };
59                         });
61                 pauseBut = Button(zone, Rect(0,0, width, height))
62                         .font_(font)
63                         .resize_(3)
64                         .states_([
65                                 ["paus", skin.fontColor, skin.onColor],
66                                 ["rsum", skin.fontColor, skin.offColor]
67                         ])
68                         .action_({ |but| var string;
69                                 if (object.notNil) {
70                                         if (History.started) {          //      "// historical".postln;
71                                                 string = object.asCompileString
72                                                         ++ [".resume;", ".pause;" ][but.value];
73                                                 thisProcess.interpreter.cmdLine_(string)
74                                                         .interpretPrintCmdLine;
75                                         } {                                             //      "// faster".postln;
76                                                 [ { object.resume },{ object.pause } ][but.value].value
77                                         };
78                                                 this.checkUpdate;
79                                 };
80                         });
82                 srcBut = Button(zone, Rect(0,0, width, height))
83                         .font_(font)
84                         .resize_(3)
85                         .states_([
86                                 ["src", skin.fontColor, skin.offColor],
87                                 ["src", skin.fontColor, skin.onColor]
88                         ])
89                         .action_({ |but|
90                                 this.openDoc(this.srcString);
91                                 but.value_(object.hasSource.binaryValue)
92                         });
94                 envBut = Button(zone, Rect(0,0, width, height))
95                         .font_(font)
96                         .resize_(3)
97                         .states_([
98                                 ["env", skin.fontColor, skin.offColor],
99                                 ["env", skin.fontColor, skin.onColor]
100                         ])
101                         .action_({ |but, mod|
102                                 if (mod.isAlt) {
103                                         this.class.new(object, max(object.envir.size, 8), nil, 400@20);
104                                 } {
105                                         if (object.envir.isNil) {
106                                                 this.openDoc(this.editString)
107                                         } {
108                                                 this.openDoc(this.editStrings)
109                                         }
110                                 };
111                                 but.value_(object.hasEnvir.binaryValue)
112                         });
114                 if (numItems > 0) { this.makeEnvirGui(lineWidth, height) };
115                 this.checkUpdate;
116         }
118         makeEnvirGui { |lineWidth, height|
119                 zone.decorator.nextLine.shift(0, 2);
121                 envirGui = EnvirGui(
122                         try { this.object.envir },
123                         numItems,
124                         zone,
125                         Rect(0, 20, lineWidth, numItems * height),
126                         false
127                 );
128         }
130         accepts { |obj| ^obj.isNil or: { obj.isKindOf(this.class.observedClass) } }
134         getState {
135                 if (object.isNil) {
136                         ^(playState: 0, hasSource: 0, hasEnvir: 0, canPause: 0, isPaused: 0)
137                 };
139                 ^(
140                         isPlaying: object.isPlaying,    // == proxy is playing now or will play
141                         isActive: object.isActive,              // == really does something right now
142                         hasSource: object.source.notNil,        // has a source
143                         hasEnvir: object.envir.notNil,  // has an envir
144                         canPause: object.canPause,
145                         isPaused: object.isPaused
146                 ).collect(_.binaryValue)
147                         .put(\name, object.key)
148                         .put(\object, object);
149         }
151         checkUpdate {
152                 var newState = this.getState;
153                 var playState;
155                 // compare newState and prevState, update gui items as needed
156                 if (newState == prevState) { ^this };
158                 if (newState[\object].isNil) {
159                         prevState = newState;
160                         zone.visible_(false);
161                         ^this;
162                 };
164                 if (newState[\name] != prevState[\name]) {  // name
165                         zone.visible_(true);
166                         nameBut.states_(nameBut.states.collect(_.put(0, object.key.asString))).refresh;
167                 };
169                 playState = newState[\isPlaying] * 2 - newState[\isActive];
170                 newState.put(\playState, playState);
172                 if (playState != prevState[\playState]) {
173                                 // stopped/playing/ended
174                                 // 0 is stopped, 1 is active, 2 is playing but waiting:
175                         playBut.value_(playState).refresh;
176                 };
177                 if (newState[\hasSource] != prevState[\hasSource]) {
178                         srcBut.value_(newState[\hasSource]).refresh;
179                 };
180                 if (newState[\hasEnvir] != prevState[\hasEnvir]) {  // has envir
181                         envBut.value_(newState[\hasEnvir]).refresh;
182                 };
183                 if (newState[\canPause] != prevState[\canPause]) {
184                         pauseBut.visible_(newState[\canPause] > 0).refresh;
185                 };
187                 if (newState[\isPaused] != prevState[\isPaused]) {
188                         pauseBut.value_(newState[\isPaused]).refresh;
189                 };
191                                 // object_ does checkUpdate!
192                 if (envirGui.notNil) {
193                         envirGui.object_(if (object.isNil) { nil } { object.envir });
194                 };
195                 prevState = newState
196         }
198         clear { object = nil; this.checkUpdate }
200         srcString {
201                 ^this.class.observedClass.asString
202                         ++ "(" + object.key.asCompileString + ","
203                         + object.source.asCompileString + ");\n"
204         }
206         editString { |edKey|
207                 var keyText = if (edKey.isNil) {
208                         "\\anyKey, nil"
209                 } {
210                         edKey.asCompileString + ","
211                         + object.envir[edKey].asCompileString
212                 };
214                 ^(this.class.observedClass.asString
215                         ++ "(" + object.key.asCompileString
216                         + ").set(" + keyText + ");\n"
217                 )
218         }
220         editStrings { |edKeys|
221                 edKeys = edKeys ? this.getUsedKeys;
222                 ^edKeys.collect (this.editString(_))
223         }
225         getUsedKeys {
226                 if (object.envir.isNil) { ^[] };
227                 ^usedKeys = object.envir.keys.rejectAs(_ == \self, Array).sort;
228         }
230         openDoc { |strings, bounds|
231                 var doc = strings.join.newTextWindow("edit me");
232                 try { doc.bounds_(bounds ? Rect(0, 400, 400, 200)) };
233         }
237 TdefGui : TaskProxyGui {
238         *observedClass { ^Tdef }
241 PdefGui : TaskProxyGui {
242         *observedClass { ^Pdef }
245 TaskProxyAllGui :JITGui {
246         var <filtBut, <filTextV, <edits, <tpGui;
247         var <>prefix = "", <>filtering = false;
248         var <names, <keysRotation=0;
249         var <editZone;
251         *new { |numItems = 16, parent, bounds, makeSkip = true, options = #[]|
252                         ^super.new(this.observedClass.all, numItems, parent, bounds, makeSkip, options );
253         }
255                         // options could include a TdefGui with EnvirGui ...
256         makeViews { |options|
258                 if (parent.isKindOf(Window.implClass)) {
259                         parent.name = this.class.observedClass.name ++ ".all";
260                 };
262                 filtBut = Button(zone, Rect(0,0, 80, skin.headHeight))
263                         .canFocus_(false)
264                         .states_([["all"], ["filt"]])
265                         .action_({ |btn|
266                                 this.filtering_(btn.value > 0);
267                         });
269                 filTextV = TextView(zone, Rect(60,0, 80, skin.headHeight))
270                         .string_("")
271                         .enterInterpretsSelection_(false)
272                         .resize_(2)
273                         .keyDownAction_({ |txvw, char, mod, uni, keycode|
274                                 var str = txvw.string;
275                                 if (str == "") { str = nil };
276                                 this.prefix_(txvw.string);
277                         });
279                 edits = Array.fill(numItems, {
280                                 this.class.tpGuiClass.new(
281                                         numItems: 0,
282                                         parent: zone,
283                                         bounds: Rect(0,0, zone.bounds.width - 16, skin.buttonHeight),
284                                         makeSkip: false
285                                 )
286                 });
288                 parent.view.decorator.left_(zone.bounds.right - 12)
289                         .top_(zone.bounds.top + skin.headHeight);
291                 scroller = EZScroller(parent,
292                         Rect(0, 0, 12, numItems * skin.buttonHeight),
293                         numItems, numItems,
294                         { |sc| keysRotation = sc.value.asInteger.max(0) }
295                 ).visible_(false);
297                 scroller.slider.resize_(3);
299 //              if (options.includes(\edit)) {
300 //                      editZone = CompositeView.new(parent,)
301 //                      zone.resize_(1);
302 //              };
303         }
305         checkUpdate {
306                         var overflow, tooMany;
308                         names = object.keys.as(Array);
310                         try { names.sort };
311                         if (filtering) {
312                                 if (prefix == "") {
313                                         names = names.reject { |name| name.asString.includes($_) };
314                                 } {
315                                         names = names.select { |name| name.asString.contains(prefix) };
316                                 };
317                         };
318                         overflow = (names.size - numItems).max(0);
319                         if (overflow > 0) {
320                                 scroller.visible_(true);
321                                 scroller.numItems_(names.size);
322                                 scroller.value_(keysRotation ? overflow);
323                                 names = names.drop(keysRotation).keep(numItems);
324                         } {
325                                 scroller.visible_(false);
326                         };
327                         edits.do { |edit, i| edit.object_(object[names[i]]) };
328                         if (tpGui.notNil) { tpGui.checkUpdate };
329         }
332 PdefnGui : JITGui {
333         
334         *observedClass { ^Pdefn }
335         
336         accepts { |obj| ^obj.isNil or: { obj.isKindOf(this.class.observedClass) } }
338         getState { 
339                 // get all the state I need to know of the object I am watching
340                 ^(object: object, source: try { object.source }) 
341         }
342         
343         checkUpdate { 
344                 var newState = this.getState; 
345                 zone.visible_(newState[\object].notNil);
346                 
347                 if (newState[\object] != prevState[\object]) { 
348                 //      zone.visible_(newState[\object].notNil);
349                         this.name_(this.getName); 
350                 };
351                 if (newState[\source] != prevState[\source]) { 
352                         if (csView.textField.hasFocus.not) { 
353                                 csView.value_(object);
354                                         // ugly
355                                 try { csView.textField.string_(object.asCode) };
356                         };
357                 };
358         } 
361 TdefAllGui : TaskProxyAllGui {
362         *observedClass { ^Tdef }
363         *tpGuiClass { ^TdefGui }
365         setDefaults {
366                 defPos = 10@660;
367                 minSize = 260 @ (numItems + 1 * 20);
368         }
371 PdefAllGui : TaskProxyAllGui {
372         *observedClass { ^Pdef }
373         *tpGuiClass { ^PdefGui }
375         setDefaults {
376                 defPos = 270@660;
377                 minSize = 260 @ (numItems + 1 * 20);
378         }
381 PdefnAllGui : TaskProxyAllGui {
382         *observedClass { ^Pdefn }
383         *tpGuiClass { ^PdefnGui }
385         setDefaults { 
386                 defPos = 540@660;
387                 minSize = 260 @ (numItems + 1 * 20);            
388         }