SCMultiSliderView a richly configurable view for graphically displaying and editing an array of values
Inherits from: Object : SCView
SCMultiSliderView on the surface is like a group of sliders. However, various graphical options allow you to use it for displaying and editing graphic representations of array values. It is also the super class of SCEnvelopeView.
See also: SCEnvelopeView
Some Important Issues Regarding SCMultiSliderView
If no instance variables other than parent and bounds are set, then a multislider with bounds.width/12 sliders is created. However, the default thumbSize is 12 and the gap default is 1. The view width has to be 13*n+2 for all the sliders to automatically fit in the view.
So if you want a specific number of sliders, then it is best to specify the size and set elasticMode to 1. Then you will get a multislider which distrubutes size amount of sliders over bounds.width, where the slider widths are at maximum indexThumbSize (default 12) and the gap is adusted accordingly.
Creation / Class Methods
*new (parent, bounds)
parent - The parent view.
bounds - An instance of Rect, or a Point indicating width@height.
(
n=20;
w = Window.new.front;
m = MultiSliderView(w,Rect(10,10,n*13+2,100)); //default thumbWidth is 13
m.value=Array.fill(n, {|v| v*0.05}); // size is set automatically when you set the value
m.action = { arg q;
q.value.postln;
};
)
Accessing Instance and Class Variables
size
size_(arg1)
Gets/sets the size of the array returned by action, if you explicitly set the size. If you do not set the size nor value, then the size of the array returned by action will be the amount of sliders in the view, which is bounds.width/12. Changing size, after the the multislider has been drawn or after the value array has been set, will lead to unexpected results. Instead, you should change the content of value, if you need to change the multislider contents.
arg1 - An Integer.
indexIsHorizontal_ (val)
Determines the index direction (vertical/horizontal) of the slider.
val - An instance of Boolean. Default is true.
editable
editable_ (val)
Determines if the sliders are editable. If editable is false, the multislider will still execute action on mouse click.
val - An instance of Boolean.
readOnly_ (val)
val - boolean.
step
step_ (stepSize)
Quantizes the values to the nearest multiple of stepSize.
stepSize - An instance of Float.
Accessing and Setting Values
value
value_ (val)
Gets/sets the property, \value, by calling get/setProperty(\value, val). The setter also sets size to the size of val. This will not do the action of the slider.
val - An instance of Array.
valueAction_ (val)
Sets the property, \value, by calling setPropertyWithAction(\value, val). Also sets size to the size of val. Does the sliders action.
val - An instance of Array.
index
index_ (inx)
Gets/sets the first index of the selection range.
inx - an integer.
selectionSize
selectionSize_ (aval)
Gets/sets the size of the selection. Can be 0.
aval - An integer.
startIndex_ (val)
val - An integer.
currentvalue
currentvalue_ (iny)
Gets/sets the value at the current index.
iny - A float between 0 and 1.
reference
reference_ (val)
Sets a reference value, and draws it according to other drawing options: as a line, or a thumb.
val - An Array of Floats between 0 and 1;
metaAction_(arg1)
metaAction
Gets/sets a function to be evaluate on Ctrl-click
arg1 - An instance of Function. Default value is nil.
Customizing Appearance
showIndex_ (abool)
Highlights the currently selected index.
abool - An Instance of Boolean. Default is false.
elasticMode
elasticMode_ (mode)
Determines if the sliders are distributed over the width of the view. If resize is 2, 3, or 8, then the width and gap of the individual sliders will adjust automatically using a maximum slider with of thumbSize.
mode - 0 or 1
thumbSize_ (val)
Sets the indexThumbSize_ of the sliders. If elasticMode is 1, then it determines the maximum indexThumbSize of the slider as well as the valueThumbSize.
val - A Float or Integer.
indexThumbSize_ (val)
The size of the slider thumb in the index direction.
val - A Float or Integer.
valueThumbSize_ (val)
The size of the slider thumb in the value direction.
val - A Float or Integer.
drawLines (abool)
drawLines_ (abool)
Draws connecting lines between the values.
abool - An instance of Boolean. default is false.
drawRects_ (abool)
Draws the thumbs of the sliders.
abool - An instance of Boolean. default is true.
gap
gap_ (inx)
Set/get the property, \xOffset, which is gap between the sliders. This value is ignored if elasticMode is set to 1.
inx - An integer.
isFilled_ (abool)
If set to true, this extends the thumb-rectangle to the edge, like in a bar graph. Fills the area between 0 and the value, or between 0 and the reference, depending on other draw options. If drawLines_(true) and drawRects_(false) and isFilled_(true), then it will fill the value between the value and the reference only.
abool - An instance of Boolean.
(
n=80;
w = Window.new;
m = MultiSliderView(w,Rect(10,10,20*13+2,100)); //default thumbWidth is 13
m.elasticMode_(1);
m.value=Array.fill(n, {|v| 1.0.rand});
m.reference=Array.fill(n, {|v| 0.5});
m.action = { arg q;q.value.postln; };
m.isFilled_ (true);
m.drawLines_(true);
m.drawRects_(false);
w.front;
)
fillColor_ (acolor)
The fill color of the view.
acolor - An instance of Color.
strokeColor_ (acolor)
The line color of the view.
acolor - An instance of Color.
colors_ (strokec, fillc)
Sets the stroke and fill colors of the graph.
strokec - An instance of Color.
fillc - An instance of Color.
Subclassing and Internal Methods
The following methods are usually not used directly or are called by a primitive. Programmers can still call or override these as needed.
defaultKeyDownAction (char, modifiers, unicode)
The default keydown actions are:
key action comment
unicode 16rF703, increment index by 1 right arrow
unicode 16rF702, decrement index by 1 left arrow
unicode 16rF700, decrement gap by 1 up arrow
unicode 16rF701, increment gap by 1 down arrow
properties
A list of properties to which this view responds. See SCView.
returns:
[ \bounds, \visible, \enabled, \canFocus, \resize, \background, \minWidth, \maxWidth, \minHeight, \maxHeight, \value, \thumbSize, \fillColor, \strokeColor, \xOffset, \x, \y, \showIndex, \drawLines, \drawRects, \selectionSize, \startIndex, \referenceValues, \thumbWidth, \absoluteX, \isFilled, \step, \elasticResizeMode ]
defaultGetDrag
The method called by default when initiating a drag from an SCNumberBox.
Tho following describes the defaultbehavior:
a) if selectionSize is 0, returns the content of value (an Array):
b) if selectionSize > 1, returns an Array with the values at the indexes in the selection;
if references is not nil, returns an array containing (a) or (b) , and an array of the relevant reference values ; [ [values], [references] ]
defaultCanReceiveDrag
The method called by default when attempting to place a drag in this object. Will recieve any drag, but the drag should either be an array of values, ( [ values ] ), or an array containg an array of values and an array of reference values ( [ [values], [references] ] ).
defaultReceiveDrag
The default method called when a drag has been received. If the currentDrag is a String, then Performs value_() and reference_() using currentDrag as an argument. Does not perform the action.
doMetaAction
Not normally called directly (called by the primitive).
Examples
// basic
(
n=20;
w = Window.new.front;
m = MultiSliderView(w,Rect(10,10,n*13+2,100)); //default thumbWidth is 13
m.value=Array.fill(n, {|v| v*0.05}); // size is set automatically when you set the value
m.action = { arg q;
q.value.postln;
};
)
// looks like a candlestick graph
(
var size;
size = 350 / 6;
w = Window.new;
w.view.decorator = FlowLayout(w.view.bounds);
m = MultiSliderView(w, Rect(0, 0, 350, 100));
m.value_(Array.fill(size, {0.01}));
m.isFilled_(true); // width in pixels of each stick
m.indexThumbSize_(2.0); // spacing on the value axis
m.gap_(4);
w.front;
)
// rotate the above graph
(
m.bounds_(Rect(0, 0, 100, 350));
m.indexIsHorizontal_(false);
)
Interactive Example (explains all the graphic options)
(
n=40;
w = Window("MultiSlider Options", Rect(200, Window.screenBounds.height-550, 600, 450));
f={
w.view.decorator = FlowLayout( w.view.bounds, 10@10, 10@2 );
m = MultiSliderView(w,Rect(0,0,580,200)); // default thumbWidth is 13
m.value=Array.fill(n, {|v| 0.5+((0.3*v).sin*0.25)});
m.action = { arg q;q.value.postln; };
StaticText(w,380@18).string_("indexThumbSize or thumbSize");
Slider(w,580@10).action_({arg sl; m.indexThumbSize=sl.value*24}).value_(0.5);
StaticText(w,380@18).string_("valueThumbSize");
Slider(w,580@10).action_({arg sl; m.valueThumbSize=sl.value*24}).value_(0.5);
StaticText(w,580@18).string_("xOffset or gap");
Slider(w,580@10).action_({arg sl;0.5- m.xOffset=sl.value*50});
StaticText(w,580@18).string_("setProperty(\\startIndex)");
Slider(w,580@10).action_({arg sl; m.setProperty(\startIndex, sl.value *m.size )};);
CompositeView(w,580@10);//spacer
Button(w,100@20).states_([["RESET",Color.red]])
.action_({ w.view.removeAll; f.value; });
h=StaticText(w,450@18).string_("").stringColor_(Color.yellow);
Button(w,100@20).states_([["elasticMode = 0"],["elasticMode = 1",Color.white]])
.action_({|b| m.elasticMode = b.value});
Button(w,160@20).states_([["indexIsHorizontal = false"],["indexIsHorizontal = true",Color.white]])
.action_({|b| m.indexIsHorizontal = b.value.booleanValue}).value_(1);
Button(w,120@20).states_([["isFilled = false"],["isFilled = true",Color.white]])
.action_({|b| m.isFilled = b.value.booleanValue});
Button(w,120@20).states_([["drawRects = false"],["drawRects = true",Color.white]])
.action_({|b| m.drawRects = b.value.booleanValue}).valueAction_(1);
Button(w,100@20).states_([["drawLines = false"],["drawLines = true",Color.white]])
.action_({|b| m.drawLines = b.value.booleanValue});
Button(w,160@20).states_([["readOnly = false"],["readOnly = true",Color.white]])
.action_({|b| m.readOnly = b.value.booleanValue});
Button(w,120@20).states_([["showIndex = false"],["showIndex = true",Color.white]])
.action_({|b| m.showIndex = b.value.booleanValue});
Button(w,120@20).states_([["reference = nil"],["reference filled",Color.white],["reference random",Color.yellow]])
.action_({|b| b.value.booleanValue.if({
(b.value>1).if(
{m.reference=Array.fill(n, {1.0.rand})},
{m.reference=Array.fill(m.size, {0.5})});
},{ q=m.value;m.reference=[]; h.string="reference can't be returned to nil presently. please hit RESET."}
)
});
Button(w,180@20).states_([["fillColor = Color.rand"]]).action_({m.fillColor=Color.rand});
Button(w,180@20).states_([["strokeColor = Color.rand"]]).action_({m.strokeColor=Color.rand});
Button(w,180@20).states_([["background = Color.rand"]]).action_({m.background=Color.rand});
};
f.value;
w.front;
)
Load a Sound file
(
// press shift to extend the selection
// use as waveView: scrubbing over the view returns index
// if showIndex(false) the view is not refreshed (faster);
// otherwise you can make a selection with shift - drag.
var size, file, maxval, minval;
size = 640;
a = Window("test", Rect(200 , 140, 650, 150));
a.view.decorator = FlowLayout(a.view.bounds);
b = MultiSliderView(a, Rect(0, 0, size, 50));
b.readOnly_(true);
a.view.decorator.nextLine;
d = Array.new;
c = FloatArray.newClear(65493);
r = Slider( a, Rect(0, 0, size, 12));
r.action = {arg ex; b.setProperty(\xOffset, (ex.value * 4) + 1 )};
file = SoundFile.new;
file.openRead("sounds/a11wlk01.wav");
file.numFrames.postln;
file.readData(c);
// file.inspect;
file.close;
minval = 0;
maxval = 0;
f = Array.new;
d = Array.new;
c.do({arg fi, i;
if(fi < minval, {minval = fi});
if(fi > maxval, {maxval = fi});
//f.postln;
if(i % 256 == 0,{
d = d.add((1 + maxval ) * 0.5 );
f = f.add((1 + minval ) * 0.5 );
minval = 0;
maxval = 0;
});
});
b.reference_(d); // this is used to draw the upper part of the table
b.value_(f);
r = Slider( a, Rect(0, 0, size, 12));
r.action = {arg ex; b.setProperty(\startIndex, ex.value *f.size )};
// b.enabled_(false);
b.action = {arg xb; ("index: " ++ xb.index).postln};
b.drawLines_(true);
b.drawRects_(false);
b.isFilled_(true);
b.selectionSize_(10);
b.index_(10);
b.thumbSize_(1);
b.gap_(0);
b.colors_(Color.black, Color.blue(1.0,1.0));
b.showIndex_(true);
a.front;
)
Use as Sequencer
// example with sound
(
var size;
size = 12;
s.waitForBoot{
n={arg freq=330; SinOsc.ar(freq,0,0.2)}.play;
w = Window("test", Rect(200 , 450, 10 + (size * 17), 10 + (size * 17)));
w.view.decorator = FlowLayout(w.view.bounds);
b = MultiSliderView(w, Rect(0, 0, size * 17, size * 17));
b.value_( Array.fill(size,{|i| i/size}) );
b.background_(Color.rand);
b.action = {arg xb;
n.set(\freq, 330+(1100*xb.value.at(xb.index)));
("index: " ++ xb.index ++" value: " ++ xb.value.at(xb.index)).postln};
b.elasticMode_(1); // makes the squares fit evenly
b.setProperty(\showIndex, true); // cursor mode
b.readOnly=true;
w.front;
r = Routine({
0.1.wait;
30.do({ arg i;
b.index_(i%size);
b.doAction;
0.1.wait;
});
20.do({ arg i;
b.index_(b.size.rand);
b.doAction;
[0.1,0.2].choose.wait;
});
1.wait;
n.free;
{w.close}.defer;
});
AppClock.play(r);
};
)
Note: this forces the entire view to redraw at each step and will spend a lot of CPU.