3 summary:: 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.
23 An instance of link::Classes/Rect::, or a link::Classes/Point:: indicating code::width@height::.
28 w = Window.new("The Four Noble Truths");
30 b = Button(w, Rect(20, 20, 340, 30))
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]
47 An array of labels and colors defining the states of the button.
49 An link::Classes/Array:: of arrays of the form code:: [ [String, strColor, bgColor] , .... ] ::
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::).
54 The index of an item in the states array.
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.
59 The index of an item in the states array.
62 Sets the Font of the button. Default value is the default font: Font.default .
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.
72 The method called by the primitive upon releasing the mouse.
74 A key modifier number, which is passed to the strong::action:: as its second argument upon mouse-releasing the button.
76 method:: defaultKeyDownAction
78 The default keydown actions are:
80 ## key || action || comment
81 ## " " || value + 1 || space
84 ## 3.asAscii || value + 1 || enter key or cmd-C on Mac OSX
86 To change these use code::defaultKeyDownAction_::, see link::Classes/View::.
89 A list of properties to which this view responds. See link::Classes/View::.
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::.
105 w = Window.new("Example");
107 b = Button(w, Rect(90, 20, 200, 30))
109 ["sine", Color.black, Color.rand],
110 ["saw", Color.black, Color.rand],
111 ["noise", Color.black, Color.rand],
112 ["pulse", Color.black, Color.rand]
120 // does not do action
123 // does action if it results in a change of value
126 // clips to size of states
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).
138 b = Button(w, Rect(20, 20, 80, 26))
139 .states_([["play", Color.black, Color.rand]])
141 a = {EnvGen.kr(Env.adsr, doneAction:2) * SinOsc.ar(440, 0, 0.4)}.play;
143 .action_({ arg butt, mod;
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.
160 b = Button(w, Rect(20, 20, 80, 26))
161 .states_([["start a sound", Color.black, Color.green], ["stop", Color.black, Color.red]])
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))
170 {|b| (b.value == 1).if{ snd = { SinOsc.ar(440,0,0.6) }.play} { snd.free }; } //a button action function
172 .string_("a play sine function").align_(\center).background_(Color.rand);
174 DragSource(v, Rect(20, 20, 80, 26))
176 {|b| (b.value == 1).if{ snd = { Saw.ar(440,0.4) }.play} { snd.free }; } //a button action function
178 .string_("a play saw function").align_(\center).background_(Color.rand);
180 DragSource(v, Rect(20, 20, 80, 26))
182 {|b| (b.value == 1).if{ snd = { WhiteNoise.ar(0.4) }.play } { snd.free }; } //a button action function
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
193 Using Routine to set button states on the fly.
197 w = Window.new("State Window", Rect(150,Window.screenBounds.height - 140, 380, 60));
199 // a convenient way to set the button label
201 |but, string| but.states = [[string.asString, Color.black, Color.red]];
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
225 Using Routine to set button states on the fly 2.
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|
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 ..
259 synth = { |freq = 1000, rate = 5|
261 Impulse.ar(rate.lag(4) * [1,1.01]), freq, rrand(0.01, 0.1), 0.3
266 guessVal = exprand(200.0, 18000).round;
267 synth.set(\freq, guessVal); // set the synth
268 update.value(butt, "?");
271 update.value(butt, guessVal.asString + "Hz"); // display frequency
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);
278 CmdPeriod.doOnce({w.close});
284 Complex drag and drop example try dragging the buttons to white slot, and then between white slots, or simply out of the view.
290 slots = Dictionary.new;
292 remove = {arg slot, id;
296 insert = {arg slot, fx;
298 slots["slot"++slot].value_(0).states_([[fx, Color.white, Color.blue]]);
301 slots["slot"++slot].value_(0).states_([["", Color.white, Color.white]]);
302 remove.value(slot, fx);
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");
312 var fxwin, winOpen = false, empty = ["", Color.white, Color.white];
314 slots["slot" ++ i] = Button.new(w, 70@70)
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});
323 if(winOpen == true) {
327 .canReceiveDragHandler_({ View.currentDrag.isString })
328 .receiveDragHandler_({ insert.value(i, View.currentDrag) })
331 drag = slots["slot" ++ i].states[0][0];
332 slots["slot" ++ i].value_(0).states_([empty]);
333 remove.value(i, View.currentDrag);
335 .keyDownAction_({ arg view,char,modifiers,unicode,keycode;
338 slots["slot"++i].value_(0).states_([empty]);
339 slots["slot"++i].refresh;
340 remove.value(i, View.currentDrag);
346 ["a", "b", "c", "d", "e", "f"].do{ arg item, i;
348 .states_([ [ item ] ])
350 .beginDragAction_({item})