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]
48 An array of labels and colors defining the states of the button.
50 An link::Classes/Array:: of arrays of the form code:: [ [String, strColor, bgColor] , .... ] ::
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::).
55 The index of an item in the states array.
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.
60 The index of an item in the states array.
63 Sets the Font of the button. Default value is the default font: Font.default .
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.
73 The method called by the primitive upon releasing the mouse.
75 A key modifier number, which is passed to the strong::action:: as its second argument upon mouse-releasing the button.
77 method:: defaultKeyDownAction
82 The default keydown actions are:
84 ## key || action || comment
85 ## " " || value + 1 || space
88 ## 3.asAscii || value + 1 || enter key or cmd-C on Mac OSX
90 To change these use code::defaultKeyDownAction_::, see link::Classes/View::.
94 A list of properties to which this view responds. See link::Classes/View::.
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::.
110 w = Window.new("Example");
112 b = Button(w, Rect(90, 20, 200, 30))
114 ["sine", Color.black, Color.rand],
115 ["saw", Color.black, Color.rand],
116 ["noise", Color.black, Color.rand],
117 ["pulse", Color.black, Color.rand]
125 // does not do action
128 // does action if it results in a change of value
131 // clips to size of states
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).
143 b = Button(w, Rect(20, 20, 80, 26))
144 .states_([["play", Color.black, Color.rand]])
146 a = {EnvGen.kr(Env.adsr, doneAction:2) * SinOsc.ar(440, 0, 0.4)}.play;
148 .action_({ arg butt, mod;
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.
165 b = Button(w, Rect(20, 20, 80, 26))
166 .states_([["start a sound", Color.black, Color.green], ["stop", Color.black, Color.red]])
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))
175 {|b| (b.value == 1).if{ snd = { SinOsc.ar(440,0,0.6) }.play} { snd.free }; } //a button action function
177 .string_("a play sine function").align_(\center).background_(Color.rand);
179 DragSource(v, Rect(20, 20, 80, 26))
181 {|b| (b.value == 1).if{ snd = { Saw.ar(440,0.4) }.play} { snd.free }; } //a button action function
183 .string_("a play saw function").align_(\center).background_(Color.rand);
185 DragSource(v, Rect(20, 20, 80, 26))
187 {|b| (b.value == 1).if{ snd = { WhiteNoise.ar(0.4) }.play } { snd.free }; } //a button action function
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
198 Using Routine to set button states on the fly.
202 w = Window.new("State Window", Rect(150,Window.screenBounds.height - 140, 380, 60));
204 // a convenient way to set the button label
206 |but, string| but.states = [[string.asString, Color.black, Color.red]];
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
230 Using Routine to set button states on the fly 2.
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|
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 ..
264 synth = { |freq = 1000, rate = 5|
266 Impulse.ar(rate.lag(4) * [1,1.01]), freq, rrand(0.01, 0.1), 0.3
271 guessVal = exprand(200.0, 18000).round;
272 synth.set(\freq, guessVal); // set the synth
273 update.value(butt, "?");
276 update.value(butt, guessVal.asString + "Hz"); // display frequency
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);
283 CmdPeriod.doOnce({w.close});
289 Complex drag and drop example try dragging the buttons to white slot, and then between white slots, or simply out of the view.
295 slots = Dictionary.new;
297 remove = {arg slot, id;
301 insert = {arg slot, fx;
303 slots["slot"++slot].value_(0).states_([[fx, Color.white, Color.blue]]);
306 slots["slot"++slot].value_(0).states_([["", Color.white, Color.white]]);
307 remove.value(slot, fx);
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");
317 var fxwin, winOpen = false, empty = ["", Color.white, Color.white];
319 slots["slot" ++ i] = Button.new(w, 70@70)
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});
328 if(winOpen == true) {
332 .canReceiveDragHandler_({ View.currentDrag.isString })
333 .receiveDragHandler_({ insert.value(i, View.currentDrag) })
336 drag = slots["slot" ++ i].states[0][0];
337 slots["slot" ++ i].value_(0).states_([empty]);
338 remove.value(i, View.currentDrag);
340 .keyDownAction_({ arg view,char,modifiers,unicode,keycode;
343 slots["slot"++i].value_(0).states_([empty]);
344 slots["slot"++i].refresh;
345 remove.value(i, View.currentDrag);
351 ["a", "b", "c", "d", "e", "f"].do{ arg item, i;
353 .states_([ [ item ] ])
355 .beginDragAction_({item})