class:: EZNumber
summary:: Wrapper class for label and number box
categories:: GUI>EZ-GUI
related:: Classes/NumberBox

description::
EZNumber is wrapper class which creates an (optional) link::Classes/StaticText::, and a link::Classes/NumberBox::.

subsection:: Some Important Issues Regarding NumberBox
If the parent is code::nil::, then EZNumber will create its own window. See link::Classes/EZGui:: more options.

subsection:: Scrolling and Arrow Keys
EZNumber scrolls by default, using the step size of the link::Classes/ControlSpec::. If the controlSpec's step is set to 0, or is not set, then the stepping and scrolling will be guessed according to the code::minval:: and code::maxval:: values of the spec on creation of the view.  Unlike the step variable of a regular link::Classes/NumberBox::, code::controlSpec.step:: is also the smallest possible increment for the EZNumber. By default, the shift-key modifier will allow you to step by 100 x code::controlSpec.step::, while the ctrl-key will give you 10x code::controlSpec.step::. Since the alt-key would give you 0.1 of the minimum step, it is disabled by default, but you can change that by setting code::numberView.alt_step:: to any value you like. Accordingly you can customize the other modifiers to fit your needs. See link::Classes/NumberBox::.

classmethods::
method:: new

argument:: parent
The parent view or window. If the parent is nil, then EZNumber will create its own link::Classes/Window::, and place it conveniently on the screen if the bounds are a link::Classes/Point::. If the bounds are a link::Classes/Rect::, then the link::Classes/Rect:: determines the window bounds.

argument:: bounds
An instance of link::Classes/Rect:: or link::Classes/Point::. Default value is code::160@20::.

argument:: label
The label. Default value is code::nil::. If code::nil::, then no link::Classes/StaticText:: is created.

argument:: controlSpec
The link::Classes/ControlSpec:: for scaling the value.

argument:: action
A link::Classes/Function:: called when the value changes. The function is passed the EZNumber instance as its argument.

argument:: initVal
The value to initialize the slider and number box with. If code::nil::, then it uses the link::Classes/ControlSpec::'s default value.

argument:: initAction
A link::Classes/Boolean:: indicating whether the action function should be called when setting the initial value. The default is code::false::.

argument:: labelWidth
Number of pixels width for the label. The  default is 60. In the code::\horz:: layout, if you specify the code::numberWidth::, then the code::labelWidth:: is ignored and is set to the code::bounds.width - unitWidth - numberWidth::.

argument:: numberWidth
Number of pixels width for the number box. The  default is 45. In \line2 layout, numberWidth defaults to the bounds.width minus the unitWidth.

argument:: unitWidth
Number of pixels width for the unit label. The default is 0.  If code::unitWidth:: is 0, then no code::unitLabel:: is created. In the code::\line2:: layout, if you specify the code::numberWidth::, then the code::unitWidth:: is ignored and is set to the code::bounds.width - unitWidth - numberWidth::.

argument:: labelHeight
Default is 20;

argument:: layout
code::\line2::, or code::\horz::. The default is code::\horz::; code::\line2:: is a two line version.

argument:: gap
A link::Classes/Point::. By default, the view tries to get its parent's gap, otherwise it defaults to code::2@2::. Setting it overrides these.

argument:: margin
A link::Classes/Point::. This will inset the bounds occupied  by the subviews of view.

discussion::
code::
(
w = Window.new.front;
g = EZNumber(w,        // parent
             150@20,   // bounds
             " test ", // label
             \freq,    // controlSpec
             { |ez| (ez.value.asString ++" is the value of " ++ ez).postln }, // action
             330,      // initValue
             true      // initAction
);
g.setColors(Color.grey,Color.white);
);


// Simplest version, no parent view, so a window is created
(
	g = EZNumber(label:" test ", controlSpec: \amp.asSpec.step_(0.01) );
	g.action_({ |ez| (ez.value.asString ++" is the value of " ++ ez).postln });
);
::

The contained views can be accessed via the EZNumber instance variables: code::labelView::, code::numberView::.

instancemethods::

method:: numberView
Returns the numberView

method:: action
A function to be evaluated when the value changes. Te first argument will be the EZNumber.
argument:: arg1
An instance of link::Classes/Function:: or link::Classes/FunctionList::. Default value is nil.

method:: value
The value of the slider

method:: round
Rounds the values in the number box.

method:: controlSpec
An instance of link::Classes/ControlSpec:: for scaling the values.

method:: value
Gets/sets the list/menu to the index at value. Does not perform the action.
argument:: val
An link::Classes/Integer::.

valueAction
Sets the value and performs the action at the index value and the global action.
argument:: val
An link::Classes/Integer::.

method:: doAction
Performs the action at the current index and the global action.

method:: set
Set the args after creation.

method:: enabled
Sets/gets if the list is enabled.
argument:: bool
An instance of link::Classes/Boolean::. Default is code::true::.


subsection:: Changing Appearance

method:: setColors
argument:: stringBackground
An instance of link::Classes/Color::. The code::background:: of the label and unit views.
argument:: stringColor
An instance of link::Classes/Color::. The code::stringColor:: of the label and unit views.
argument:: numBackground
An instance of link::Classes/Color::. The code::background:: of the number view.
argument:: numStringColor
An instance of link::Classes/Color::. The code::stringColor:: of the number view.
argument:: numNormalColor
An instance of link::Classes/Color::. The code::normalColor:: of the number view.
argument:: numTypingColor
An instance of link::Classes/Color::. The code::typingColor:: of the number view.
argument:: background
An instance of link::Classes/Color::. The code::background:: of the enclosing view.

method:: font
Set the link::Classes/Font:: used by all the views.
argument:: font
An instance of link::Classes/Font::.


examples::
code::
// Simplest version
(		// basic use
		w=Window.new.front;
		g=EZNumber(w, 170@16," test  ", \freq,unitWidth:30, numberWidth:60,layout:\horz);
		g.setColors(Color.grey,Color.white);
);


// lots of numberviews on on view
(
w=Window.new.front;
w.view.decorator=FlowLayout(w.view.bounds);
w.view.decorator.gap=2@2;

40.do{
		g=EZNumber(w, 170@16," test  ", \freq,unitWidth:30, numberWidth:60,layout:\horz);
		g.setColors(Color.grey,Color.white, Color.grey(0.8));
};
);


// click these parentheses to see all features and layouts
(

m=nil;
m=2@2;		//comment for no margin

/////////////////
/// Layout \horz

(		// all features
		g=EZNumber(nil, 170@20," freq  ", \freq,unitWidth:30, numberWidth:60,layout:\horz,margin:m);
		g.setColors(Color.grey,Color.white,background: Color.grey(0.7));
		g.window.bounds = g.window.bounds.moveBy(-180,50);
);

(		// no unitView
		g=EZNumber(nil, 170@20," freq  ", \freq,unitWidth:0, numberWidth:60,layout:\horz,margin:m);
		g.setColors(Color.grey,Color.white,background: Color.grey(0.7));
		g.window.bounds = g.window.bounds.moveBy(-180, -20);
);

(		// no label, with unit. use window name as label
		g=EZNumber(nil, 120@20,nil, \freq,unitWidth:30, numberWidth:60,layout:\horz,margin:m);
		g.setColors(Color.grey,Color.white,background: Color.grey(0.7));
		g.window.bounds = g.window.bounds.moveBy(-180, -90);
		g.window.name="Freq";
);


(		// no units, no label; use window name as label;
		g=EZNumber(nil, 120@20, nil, \freq,unitWidth:0, numberWidth:60,layout:\horz,margin:m);
		g.setColors(Color.grey,Color.white,background: Color.grey(0.7));
		g.window.bounds = g.window.bounds.moveBy(-180, -160);
		g.window.name="Freq";
);

/////////////////
/// Layout \line2

(		// all features
		g=EZNumber(nil, 120@42," freq  ", \freq,unitWidth:30, numberWidth:60,layout:\line2,margin:m);
		g.setColors(Color.grey,Color.white,background: Color.grey(0.7));
		g.window.bounds = g.window.bounds.moveBy(100,50);
);

(		// no unitView, with label
		g=EZNumber(nil, 170@42," freq  ", \freq,unitWidth:0, numberWidth:60,layout:\line2,margin:m);
		g.setColors(Color.grey,Color.white,background: Color.grey(0.7));
		g.window.bounds = g.window.bounds.moveBy(100, -50);
);
(		// no unitView, no label; use window name as label
		g=EZNumber(nil, 170@20,nil, \freq,unitWidth:0, numberWidth:60,layout:\line2,margin:m);
		g.setColors(Color.grey,Color.white,background: Color.grey(0.7));
		g.window.bounds = g.window.bounds.moveBy(100,-150);
		g.window.name="Freq";
);


)



// Sound example
(
// start server
s.waitForBoot({

	var w, startButton, noteControl, cutoffControl, resonControl;
	var balanceControl, ampControl;
	var node, cmdPeriodFunc;

	// define a synth
	SynthDef("window-test", { |out, note = 36, fc = 1000, rq = 0.25, bal=0, amp=0.4, gate = 1|
			var x;
			x = Mix.fill(4, {
				LFSaw.ar((note + {0.1.rand2}.dup).midicps, 0, 0.02)
			});
			x = RLPF.ar(x, fc, rq).softclip;
			x = RLPF.ar(x, fc, rq, amp).softclip;
			x = Balance2.ar(x[0], x[1], bal);
			x = x * EnvGen.kr(Env.cutoff, gate, doneAction: Done.freeSelf);
			Out.ar(out, x);
		}, [0.1, 0.1, 0.1, 0.1, 0.1, 0]
	).add;



	// make the window
	w = Window("another control panel", Rect(200, 400, 300, 180));
	w.front; // make window visible and front window.
	w.view.decorator = FlowLayout(w.view.bounds);

	w.view.background = Color.rand;

	// add a button to start and stop the sound.
	startButton = Button(w, 75 @ 20);
	startButton.states = [
		["Start", Color.black, Color.green],
		["Stop", Color.white, Color.red]
	];
	startButton.action = {|view|
			if (view.value == 1) {
				// start sound
				node = Synth( "window-test", [
					"note", noteControl.value,
					"fc", cutoffControl.value,
					"rq", resonControl.value,
					"bal", balanceControl.value,
					"amp", ampControl.value.dbamp ]);
			} {
				// set gate to zero to cause envelope to release
				node.release; node = nil;
			};
	};

	// create controls for all parameters
	w.view.decorator.nextLine;
	noteControl = EZNumber(w, 160 @ 20, "Note ", ControlSpec(24, 60, \lin, 1),
		{|ez| node.set( "note", ez.value )}, 36);

	w.view.decorator.nextLine;
	cutoffControl = EZNumber(w, 160 @ 20, "Cutoff ", ControlSpec(200, 5000, \exp),
		{|ez| node.set( "fc", ez.value )}, 1000);

	w.view.decorator.nextLine;
	resonControl = EZNumber(w, 160 @ 20, "Reson", ControlSpec(0.1, 0.7),
		{|ez| node.set( "rq", ez.value )}, 0.2);

	w.view.decorator.nextLine;
	balanceControl = EZNumber(w, 160 @ 20, "Balance ", \bipolar,
		{|ez| node.set( "bal", ez.value )}, 0);

	w.view.decorator.nextLine;
	ampControl = EZNumber(w, 160 @ 20, "Amp ", \db,
		{|ez| node.set( "amp", ez.value.dbamp )}, -6);


	// set start button to zero upon a cmd-period
	cmdPeriodFunc = { startButton.value = 0; };
	CmdPeriod.add(cmdPeriodFunc);

	// stop the sound when window closes and remove cmdPeriodFunc.
	w.onClose = {
		node.free; node = nil;
		CmdPeriod.remove(cmdPeriodFunc);
	};
});
)
::
