Forgot a help fix: Drag a dock's title bar, not divider, to reposition
[supercollider.git] / HelpSource / Classes / Button.schelp
blobf22ede3ace282a492cb2fe65251a6f47f7aeceb0
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;
44 instancemethods::
46 method:: states
47 An array of labels and colors defining the states of the button.
48 argument:: stateArray
49 An link::Classes/Array:: of arrays of the form code:: [ [String, strColor, bgColor] , .... ] ::
51 method:: value
52 Sets or returns the index of the current state. This will strong::not:: evaluate the function assigned to strong::action:: (see link::Classes/View::).
53 argument:: argVal
54 The index of an item in the states array.
56 method:: valueAction
57 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.
58 argument:: anInt
59 The index of an item in the states array.
61 method:: font
62 Sets the Font of the button. Default value is the default font: Font.default .
63 argument:: f
64 An instance of link::Classes/Font::.
67 subsection:: Subclassing and Internal Methods
69 The following methods are usually not used directly or are called by a primitive. Programmers can still call or override these as needed.
71 method:: doAction
72 The method called by the primitive upon releasing the mouse.
73 argument:: modifiers
74 A key modifier number, which is passed to the strong::action:: as its second argument upon mouse-releasing the button.
76 method:: defaultKeyDownAction
77 discussion::
78 The default keydown actions are:
79 table::
80 ## key || action || comment
81 ## " " || value + 1 || space
82 ## \r || value + 1
83 ## \n || value + 1
84 ## 3.asAscii || value + 1 || enter key or cmd-C on Mac OSX
86 To change these use code::defaultKeyDownAction_::, see link::Classes/View::.
88 method:: properties
89 A list of properties to which this view responds. See link::Classes/View::.
90 returns::
91 [ \bounds, \visible, \enabled, \canFocus, \resize, \background, \minWidth, \maxWidth, \minHeight, \maxHeight, \value, \font, \states, \focusColor ]
93 method:: defaultGetDrag
94 The method called by default when initiating a drag strong::from:: a Button. Returns the same as link::#-value::.
96 method:: defaultCanReceiveDrag
97 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::.
99 method:: defaultReceiveDrag
100 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::.
102 examples::
103 code::
105 w = Window.new("Example");
107 b = Button(w, Rect(90, 20, 200, 30))
108                 .states_([
109                         ["sine", Color.black, Color.rand],
110                         ["saw", Color.black, Color.rand],
111                         ["noise", Color.black, Color.rand],
112                         ["pulse", Color.black, Color.rand]
113                 ])
114                 .action_({ arg butt;
115                         butt.value.postln;
116                 });
117 w.front;
120 // does not do action
121 b.value = 2;
123 // does action if it results in a change of value
124 b.valueAction = 3;
126 // clips to size of states
127 b.valueAction = -1;
129 // floats no problem
130 b.valueAction = 3.3;
133 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).
134 code::
136 s.waitForBoot({
137         w = Window.new;
138         b = Button(w, Rect(20, 20, 80, 26))
139                         .states_([["play", Color.black, Color.rand]])
140                         .mouseDownAction_({
141                                 a = {EnvGen.kr(Env.adsr, doneAction:2) * SinOsc.ar(440, 0, 0.4)}.play;
142                         })
143                         .action_({ arg butt, mod;
144                                 a.release(0.3);
145                         });
146         w.front;
152 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.
153 code::
155 s.waitForBoot({
156         var w, p, snd, b;
158         w = Window.new;
160         b = Button(w, Rect(20, 20, 80, 26))
161                         .states_([["start a sound", Color.black, Color.green], ["stop", Color.black, Color.red]])
162                         .action_({});
164         v = VLayoutView(w, Rect(140, 20, 200, 300)); //Group the following views
165         StaticText(v, Rect(20, 20, 180, 60))
166                 .string_("The button does nothing at first, so try dragging a function to the button");
168         DragSource(v, Rect(20, 20, 80, 26))
169                 .object_(
170                         {|b| (b.value == 1).if{ snd = { SinOsc.ar(440,0,0.6) }.play} { snd.free }; } //a button action function
171                         )
172                 .string_("a play sine function").align_(\center).background_(Color.rand);
174         DragSource(v, Rect(20, 20, 80, 26))
175                 .object_(
176                         {|b| (b.value == 1).if{ snd = { Saw.ar(440,0.4) }.play} { snd.free }; } //a button action function
177                         )
178                 .string_("a play saw function").align_(\center).background_(Color.rand);
180         DragSource(v, Rect(20, 20, 80, 26))
181                 .object_(
182                         {|b| (b.value == 1).if{ snd = { WhiteNoise.ar(0.4) }.play } { snd.free }; } //a button action function
183                         )
184                 .string_("a play noise function").align_(\center).background_(Color.rand);
186         p = CmdPeriod.add({ b.value_(0) }); // set button to 0 on hitting Cmd-period
187         w.onClose_{ snd.free; CmdPeriod.removeAll };//clean up when window is closed
188         w.front;
193 Using Routine to set button states on the fly.
194 code::
196 var update, w, b;
197         w = Window.new("State Window", Rect(150,Window.screenBounds.height - 140, 380, 60));
199         // a convenient way to set the button label
200         update = {
201                 |but, string| but.states = [[string.asString, Color.black, Color.red]];
202                 but.refresh;
203         };
205         b = Button(w, Rect(10, 10, 360, 40));
206         b.font_(Font("Impact", 24));
208         update.value(b, "there is only one state");
210         // if an action should do something different each time it is called, a routine is the
211         // right thing to use. This is better than creating variables outside and setting them
212         // from the action function to keep state from one action to the next
214         b.action_(Routine { |butt|
215                 rrand(15, 45).do { |i|
216                         update.value(butt, "%. there is still only 1 state".format(i + 2));
217                         0.yield; // stop here
218                 };
219                 w.close;
220         });
221         w.front;
225 Using Routine to set button states on the fly 2.
226 code::
228 s.waitForBoot({
229         var update, w, b;
231         w = Window.new("State Window", Rect(150, Window.screenBounds.height - 140, 380, 60));
233         // a convenient way to set the button label
234         update = { |but, string|
235         but.states = [[string.asString, Color.black, Color.red]]; but.refresh };
237         b = Button(w, Rect(10, 10, 360, 40));
238         b.font_(Font("Impact", 24));
240         update.value(b, "there is only one state");
242         // if an action should do something different each time it is called, a routine is the
243         // right thing to use. This is better than creating variables outside and setting them
244         // from the action function to keep state from one action to the next
246         b.action_(Routine { |butt|
247                 var synth, guessVal;
249                 update.value(butt, "there are only two states");
250                 0.yield; // stop here
252                 update.value(butt, "click me");
253                 0.yield; // stop here
255                 update.value(butt, "click me again");
256                 0.yield; // stop here ..
258                 // create a synth
259                 synth = { |freq = 1000, rate = 5|
260                         Ringz.ar(
261                                 Impulse.ar(rate.lag(4) * [1,1.01]), freq, rrand(0.01, 0.1), 0.3
262                         )
263                 }.play;
264                 0.yield;
266                 guessVal = exprand(200.0, 18000).round;
267                 synth.set(\freq, guessVal); // set the synth
268                 update.value(butt, "?");
269                 0.yield;
271                 update.value(butt, guessVal.asString + "Hz"); // display frequency
272                 0.yield;
274                 synth.set(\rate, rrand(10, 50)); // set trigger rate
275                 // start an independent process
276                 fork({ 5.wait; synth.release; update.value(butt, "."); 1.wait; w.close }, AppClock);
277         });
278         CmdPeriod.doOnce({w.close});
279         w.front;
284 Complex drag and drop example try dragging the buttons to white slot, and then between white slots, or simply out of the view.
285 code::
287 var w, f, slots;
288 var insert, remove;
290 slots = Dictionary.new;
292 remove = {arg slot, id;
293         [slot, id].postln;
296 insert = {arg slot, fx;
297         if(fx != ""){
298                 slots["slot"++slot].value_(0).states_([[fx, Color.white, Color.blue]]);
299                 [slot, fx].postln;
300         }{
301                 slots["slot"++slot].value_(0).states_([["", Color.white, Color.white]]);
302                 remove.value(slot, fx);
303         };
306 w = Window.new("",Rect(200, 400, 448, 180));
307 w.view.decorator = f = FlowLayout(w.view.bounds);
309 StaticText(w, 400@20).string_("Drag & Drop holding down Cmd-key");
310 f.nextLine;
311 6.do{arg i;
312         var fxwin, winOpen = false, empty = ["", Color.white, Color.white];
314         slots["slot" ++ i] = Button.new(w, 70@70)
315                 .states_([empty])
316                 .action_({|v|
317                         if((slots["slot" ++ i].states[0][0] != "") && ( winOpen == false)) {
318                                 fxwin = Window(slots["slot" ++ i].states[0][0], Rect(rrand(0, 500),rrand(0, 500),200, 200)).front;
319                                 fxwin.view.background_(Color.rand);
320                                 fxwin.onClose_({ winOpen = false});
321                                 winOpen = true
322                         } {
323                                 if(winOpen == true) {
324                                         fxwin.front
325                                 }
326                         }; })
327                 .canReceiveDragHandler_({ View.currentDrag.isString })
328                 .receiveDragHandler_({ insert.value(i, View.currentDrag) })
329                 .beginDragAction_({
330                         var drag;
331                         drag = slots["slot" ++ i].states[0][0];
332                         slots["slot" ++ i].value_(0).states_([empty]);
333                         remove.value(i, View.currentDrag);
334                         drag; })
335                 .keyDownAction_({ arg view,char,modifiers,unicode,keycode;
336                         switch(keycode)
337                                 { 51 } {
338                                         slots["slot"++i].value_(0).states_([empty]);
339                                         slots["slot"++i].refresh;
340                                         remove.value(i, View.currentDrag);
341                                 }; });
344 f.nextLine;
346 ["a", "b", "c", "d", "e", "f"].do{ arg item, i;
347         Button.new(w, 70@70)
348         .states_([ [ item ] ])
349         .action_({ |v| })
350         .beginDragAction_({item})
352 w.front;