linux: shared memory interface - link with librt
[supercollider.git] / HelpSource / Classes / Button.schelp
blob06bdd06672e2d9ed8a6ff2cf4b6b814ed9511f32
1 class:: Button
2 redirect:: implClass
3 summary:: A multi-state button
4 categories:: GUI>Views
6 description::
7 A multi-state button.
9 subsection:: Some Important Issues Regarding Button
11 Failure to set any states at all results in an invisible button.
13 The button performs its action upon releasing the mouse. In musical contexts, you might want to use code::mouseDownAction_():: to set a function to be performed on pressing the mouse (see link::Classes/View::, and examples below).
15 If the drag contains a number, then code::valueAction_():: is performed using the code::currentDrag::. If the drag contains anything else, code::action:: is set to the current drag. You could, for example, drag a function to an Button, and action would then be set to that function.
17 classmethods::
19 method:: new
20 argument:: parent
21 The parent view.
22 argument:: bounds
23 An instance of link::Classes/Rect::, or a link::Classes/Point:: indicating code::width@height::.
24 discussion::
25 Example:
26 code::
28 w = Window.new("The Four Noble Truths");
30 b = Button(w, Rect(20, 20, 340, 30))
31                 .states_([
32                         ["there is suffering", Color.black, Color.red],
33                         ["the origin of suffering", Color.white, Color.black],
34                         ["the cessation of suffering", Color.red, Color.white],
35                         ["there is a path to cessation of suffering", Color.blue, Color.clear]
36                 ])
37                 .action_({ arg butt;
38                         butt.value.postln;
39                 });
40 w.front;
45 instancemethods::
47 method:: states
48 An array of labels and colors defining the states of the button.
49 argument:: stateArray
50 An link::Classes/Array:: of arrays of the form code:: [ [String, strColor, bgColor] , .... ] ::
52 method:: value
53 Sets or returns the index of the current state. This will strong::not:: evaluate the function assigned to strong::action:: (see link::Classes/View::).
54 argument:: argVal
55 The index of an item in the states array.
57 method:: valueAction
58 Sets the button to display the item at index strong::anInt:: of the states array, and evaluates strong::action:: (see link::Classes/View::), if the value has changed.
59 argument:: anInt
60 The index of an item in the states array.
62 method:: font
63 Sets the Font of the button. Default value is the default font: Font.default .
64 argument:: f
65 An instance of link::Classes/Font::.
68 subsection:: Subclassing and Internal Methods
70 The following methods are usually not used directly or are called by a primitive. Programmers can still call or override these as needed.
72 method:: doAction
73 The method called by the primitive upon releasing the mouse.
74 argument:: modifiers
75 A key modifier number, which is passed to the strong::action:: as its second argument upon mouse-releasing the button.
77 method:: defaultKeyDownAction
78 argument:: char
79 argument:: modifiers
80 argument:: unicode
81 discussion::
82 The default keydown actions are:
83 table::
84 ## key || action || comment
85 ## " " || value + 1 || space
86 ## \r || value + 1
87 ## \n || value + 1
88 ## 3.asAscii || value + 1 || enter key or cmd-C on Mac OSX
90 To change these use code::defaultKeyDownAction_::, see link::Classes/View::.
93 method:: properties
94 A list of properties to which this view responds. See link::Classes/View::.
95 returns::
96 [ \bounds, \visible, \enabled, \canFocus, \resize, \background, \minWidth, \maxWidth, \minHeight, \maxHeight, \value, \font, \states, \focusColor ]
98 method:: defaultGetDrag
99 The method called by default when initiating a drag strong::from:: a Button. Returns the same as link::#-value::.
101 method:: defaultCanReceiveDrag
102 The method called by default when attempting to drop a drag in this object. By default, Button will respond only to drags where the drag contains a link::Classes/Number:: or link::Classes/Function::.
104 method:: defaultReceiveDrag
105 The default method called when a drag has been received. If the drag contains a number, then action is set to the current drag. Otherwise code::valueAction_():: is performed using the code::currentDrag::.
107 examples::
108 code::
110 w = Window.new("Example");
112 b = Button(w, Rect(90, 20, 200, 30))
113                 .states_([
114                         ["sine", Color.black, Color.rand],
115                         ["saw", Color.black, Color.rand],
116                         ["noise", Color.black, Color.rand],
117                         ["pulse", Color.black, Color.rand]
118                 ])
119                 .action_({ arg butt;
120                         butt.value.postln;
121                 });
122 w.front;
125 // does not do action
126 b.value = 2;
128 // does action if it results in a change of value
129 b.valueAction = 3;
131 // clips to size of states
132 b.valueAction = -1;
134 // floats no problem
135 b.valueAction = 3.3;
138 In a musical context, a button-down press is more meaningful than a button-up (release) as it's more intuitive to press a button on the beat. For that you can use link::Classes/View::'s link::Classes/View#mouseDownAction:: (a superclass of Button).
139 code::
141 s.waitForBoot({
142         w = Window.new;
143         b = Button(w, Rect(20, 20, 80, 26))
144                         .states_([["play", Color.black, Color.rand]])
145                         .mouseDownAction_({
146                                 a = {EnvGen.kr(Env.adsr, doneAction:2) * SinOsc.ar(440, 0, 0.4)}.play;
147                         })
148                         .action_({ arg butt, mod;
149                                 a.release(0.3);
150                         });
151         w.front;
157 If you drag a function to a button, the the button's action is set to that function. you can us this for swapping functions.
158 code::
160 s.waitForBoot({
161         var w, p, snd, b;
163         w = Window.new;
165         b = Button(w, Rect(20, 20, 80, 26))
166                         .states_([["start a sound", Color.black, Color.green], ["stop", Color.black, Color.red]])
167                         .action_({});
169         v = VLayoutView(w, Rect(140, 20, 200, 300)); //Group the following views
170         StaticText(v, Rect(20, 20, 180, 60))
171                 .string_("The button does nothing at first, so try dragging a function to the button");
173         DragSource(v, Rect(20, 20, 80, 26))
174                 .object_(
175                         {|b| (b.value == 1).if{ snd = { SinOsc.ar(440,0,0.6) }.play} { snd.free }; } //a button action function
176                         )
177                 .string_("a play sine function").align_(\center).background_(Color.rand);
179         DragSource(v, Rect(20, 20, 80, 26))
180                 .object_(
181                         {|b| (b.value == 1).if{ snd = { Saw.ar(440,0.4) }.play} { snd.free }; } //a button action function
182                         )
183                 .string_("a play saw function").align_(\center).background_(Color.rand);
185         DragSource(v, Rect(20, 20, 80, 26))
186                 .object_(
187                         {|b| (b.value == 1).if{ snd = { WhiteNoise.ar(0.4) }.play } { snd.free }; } //a button action function
188                         )
189                 .string_("a play noise function").align_(\center).background_(Color.rand);
191         p = CmdPeriod.add({ b.value_(0) }); // set button to 0 on hitting Cmd-period
192         w.onClose_{ snd.free; CmdPeriod.removeAll };//clean up when window is closed
193         w.front;
198 Using Routine to set button states on the fly.
199 code::
201 var update, w, b;
202         w = Window.new("State Window", Rect(150,Window.screenBounds.height - 140, 380, 60));
204         // a convenient way to set the button label
205         update = {
206                 |but, string| but.states = [[string.asString, Color.black, Color.red]];
207                 but.refresh;
208         };
210         b = Button(w, Rect(10, 10, 360, 40));
211         b.font_(Font("Impact", 24));
213         update.value(b, "there is only one state");
215         // if an action should do something different each time it is called, a routine is the
216         // right thing to use. This is better than creating variables outside and setting them
217         // from the action function to keep state from one action to the next
219         b.action_(Routine { |butt|
220                 rrand(15, 45).do { |i|
221                         update.value(butt, "%. there is still only 1 state".format(i + 2));
222                         0.yield; // stop here
223                 };
224                 w.close;
225         });
226         w.front;
230 Using Routine to set button states on the fly 2.
231 code::
233 s.waitForBoot({
234         var update, w, b;
236         w = Window.new("State Window", Rect(150, Window.screenBounds.height - 140, 380, 60));
238         // a convenient way to set the button label
239         update = { |but, string|
240         but.states = [[string.asString, Color.black, Color.red]]; but.refresh };
242         b = Button(w, Rect(10, 10, 360, 40));
243         b.font_(Font("Impact", 24));
245         update.value(b, "there is only one state");
247         // if an action should do something different each time it is called, a routine is the
248         // right thing to use. This is better than creating variables outside and setting them
249         // from the action function to keep state from one action to the next
251         b.action_(Routine { |butt|
252                 var synth, guessVal;
254                 update.value(butt, "there are only two states");
255                 0.yield; // stop here
257                 update.value(butt, "click me");
258                 0.yield; // stop here
260                 update.value(butt, "click me again");
261                 0.yield; // stop here ..
263                 // create a synth
264                 synth = { |freq = 1000, rate = 5|
265                         Ringz.ar(
266                                 Impulse.ar(rate.lag(4) * [1,1.01]), freq, rrand(0.01, 0.1), 0.3
267                         )
268                 }.play;
269                 0.yield;
271                 guessVal = exprand(200.0, 18000).round;
272                 synth.set(\freq, guessVal); // set the synth
273                 update.value(butt, "?");
274                 0.yield;
276                 update.value(butt, guessVal.asString + "Hz"); // display frequency
277                 0.yield;
279                 synth.set(\rate, rrand(10, 50)); // set trigger rate
280                 // start an independent process
281                 fork({ 5.wait; synth.release; update.value(butt, "."); 1.wait; w.close }, AppClock);
282         });
283         CmdPeriod.doOnce({w.close});
284         w.front;
289 Complex drag and drop example try dragging the buttons to white slot, and then between white slots, or simply out of the view.
290 code::
292 var w, f, slots;
293 var insert, remove;
295 slots = Dictionary.new;
297 remove = {arg slot, id;
298         [slot, id].postln;
301 insert = {arg slot, fx;
302         if(fx != ""){
303                 slots["slot"++slot].value_(0).states_([[fx, Color.white, Color.blue]]);
304                 [slot, fx].postln;
305         }{
306                 slots["slot"++slot].value_(0).states_([["", Color.white, Color.white]]);
307                 remove.value(slot, fx);
308         };
311 w = Window.new("",Rect(200, 400, 448, 180));
312 w.view.decorator = f = FlowLayout(w.view.bounds);
314 StaticText(w, 400@20).string_("Drag & Drop holding down Cmd-key");
315 f.nextLine;
316 6.do{arg i;
317         var fxwin, winOpen = false, empty = ["", Color.white, Color.white];
319         slots["slot" ++ i] = Button.new(w, 70@70)
320                 .states_([empty])
321                 .action_({|v|
322                         if((slots["slot" ++ i].states[0][0] != "") && ( winOpen == false)) {
323                                 fxwin = Window(slots["slot" ++ i].states[0][0], Rect(rrand(0, 500),rrand(0, 500),200, 200)).front;
324                                 fxwin.view.background_(Color.rand);
325                                 fxwin.onClose_({ winOpen = false});
326                                 winOpen = true
327                         } {
328                                 if(winOpen == true) {
329                                         fxwin.front
330                                 }
331                         }; })
332                 .canReceiveDragHandler_({ View.currentDrag.isString })
333                 .receiveDragHandler_({ insert.value(i, View.currentDrag) })
334                 .beginDragAction_({
335                         var drag;
336                         drag = slots["slot" ++ i].states[0][0];
337                         slots["slot" ++ i].value_(0).states_([empty]);
338                         remove.value(i, View.currentDrag);
339                         drag; })
340                 .keyDownAction_({ arg view,char,modifiers,unicode,keycode;
341                         switch(keycode)
342                                 { 51 } {
343                                         slots["slot"++i].value_(0).states_([empty]);
344                                         slots["slot"++i].refresh;
345                                         remove.value(i, View.currentDrag);
346                                 }; });
349 f.nextLine;
351 ["a", "b", "c", "d", "e", "f"].do{ arg item, i;
352         Button.new(w, 70@70)
353         .states_([ [ item ] ])
354         .action_({ |v| })
355         .beginDragAction_({item})
357 w.front;