SCUserView user-definable view


Inherits from: Object : SCView


SCUserView is a user-definable View intended mainly for use with Pen and drawHooks. It is also good for making custom buttons or other gui interfaces and displays. Thus anything you can draw with the methods of Pen, combined with mouse tracking, can be used to create a vast variety of interfaces and displays. When a view is refreshed, either maunually, or triggered by a refresh of the parent view, drawFunc is evaluated. Using refreshInRect constrains the receiver's refresh area to the rectangle passed in a Rect (see SCView).


See also: GUI-Overview, SCWindow, Pen, Color, and String SCUserView-Subclassing


Creation / Class Methods


*new (parent, bounds)

parent - The parent view.

bounds - An instance of Rect, or a Point indicating width@height.


Accessing Instance and Class Variables

relativeOrigin

relativeOrigin_ (bool)

If set to true, child view bounds will be interpreted relative to the parents upper left corner. See the section on relativeOrigin under SCCompositeView.

bool - An instance of Boolean. Default value is true.

drawFunc_(arg1)

drawFunc

Set the function which should be evaluated if the view is refreshed. This happens every time the view or the whole window is refreshed (manually by calling SCView:refresh, SCWindow:refresh or e.g. by selecting the view or resizing the window).

.

arg1 - An instance of Function. Default value is nil. Refreshing passes the instance of SCUserView itself as the first argument to the function.


clearOnRefresh

clearOnRefresh_ (bool)

Determines whether the drawn content of the view is cleared on refresh.

bool - An instance of Boolean. Default value is true.

drawingEnabled

drawingEnabled_(bool)

Determines whether to execute the draw function or not

bool - An instance of Boolean. Default value is true.

mousePosition

Returns the relative position of the mouse cklick as a Point.

draw

Evaluates the draw function, passing the instance of SCUserView itself as the first argument to the function

clearDrawing

If clearOnRefresh is set to false, then you can use this to manually clear the drawing (you must refesh in order for it to show)

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. For a Tutorial on how to subclass SCUserView to make your own custom GUI Widgets, see SCUserView-Subclassing


init (argParent, argBounds)

parent - The parent view.

bounds - An instance of Rect, or a Point indicating width@height.

keyDownFunc

keyDownFunc_ (action)

Deprecated. Use keyDownAction instead. See SCView.


mousemouseDownFunc

mousemouseDownFunc_ (action)

Deprecated. Use mouseDownAction instead. See SCView.


mousemouseMoveFunc

mousemouseMoveFunc_ (action)

Deprecated. Use mouseMoveAction instead. See SCView.

mousemouseUpFunc

mousemouseUpFunc_ (action)

Deprecated. Use mouseUpAction instead. See SCView.

Examples


GUI-Overview contains an elobarate example on how to make a gui widget with SCUserView using interface level coding. For a Tutorial on how to write a subclass of SCUserView to make your own custom GUI Widgets, see SCUserView-Subclassing


// Basic Usage. Resize the window to refresh the drawing. Or use mouse click.


(

w=SCWindow.new;

v=SCUserView(w, w.view.bounds.insetBy(50,50));

v.background_(Color.rand);

v.relativeOrigin=true;

v.drawFunc={|uview|

Pen.moveTo(0@uview.bounds.height.rand);

Pen.lineTo(uview.bounds.width@uview.bounds.height.rand);

Pen.stroke;

};

v.mouseDownAction={v.refresh};

w.front;


)




// A More complicated drawing function.

// Resize the window to refresh the drawing

(

var func;


func = {|me|

Pen.use{

// clipping into the boundingbox

Pen.moveTo((me.bounds.left)@(me.bounds.top));

Pen.lineTo(((me.bounds.left)@(me.bounds.top))

+ (me.bounds.width@0));

Pen.lineTo(((me.bounds.left)@(me.bounds.top))

+ (me.bounds.width@me.bounds.height));

Pen.lineTo(((me.bounds.left)@(me.bounds.top))

+ (0@me.bounds.height));

Pen.lineTo((me.bounds.left)@(me.bounds.top));

Pen.clip;

// draw background

Color.gray(0.5).set;

Pen.moveTo((me.bounds.left)@(me.bounds.top));

Pen.lineTo(((me.bounds.left)@(me.bounds.top))

+ (me.bounds.width@0));

Pen.lineTo(((me.bounds.left)@(me.bounds.top))

+ (me.bounds.width@me.bounds.height));

Pen.lineTo(((me.bounds.left)@(me.bounds.top))

+ (0@me.bounds.height));

Pen.lineTo((me.bounds.left)@(me.bounds.top));

Pen.fill;


Pen.translate(100, 100);

10.do{

Color.red(rrand(0.0, 1), rrand(0.0, 0.5)).set;

Pen.addArc((400.exprand(2))@(100.rand), rrand(10, 100), 2pi.rand, pi);

Pen.perform([\stroke, \fill].choose);

}

}

};


w = SCWindow.new("DrawFunc Examples").front;

w.view.background_(Color.white);

3.do{|i|

v = SCUserView(w, Rect(20+(i*120), 100, 100, 100));

v.drawFunc = func;

v.relativeOrigin=false;


};

w.refresh;

)



// Use Relative Coordinates

// Set the Origin of Pen in the view's drawFunc relative to the SCUserView.

// This is particularly useful if resize is set in such a way the the position of the user view changes.

// Try resizing the Window.


(

var func;


func = {|me|

Pen.use{

10.do{

Color.red(rrand(0.0, 1), rrand(0.0, 0.5)).set;

Pen.addArc((400.exprand(2))@(100.rand), rrand(10, 100), 2pi.rand, pi);

Pen.perform([\stroke, \fill].choose);

}

}

};


w = SCWindow.new("DrawFunc Examples").front;

w.view.background_(Color.white);


3.do{|i|

v = SCUserView(w, Rect(20+(i*120), 100, 100, 100))

.drawFunc_(func).relativeOrigin_(true);

v.resize=3; // the func coordinates ar valid even though the view move on resize

v.background_(Color.rand);

};


w.refresh;

)



// Mouse Tracking

// Set the function which should be evaluated if the mouse is at the end of tracking (mouse-up).

// This function will be passed four arguments: theView, x coordinate, y coordinate, and keyboard modifiers.

(

var drawFunc, mouseDownFunc, mouseUpFunc, mouseMoveFunc, sat = 0, absX;


drawFunc = {|me|

Pen.use{

10.do{

Color.red(rrand(0.0, 1), rrand(0.0, 0.5)).set;

Pen.addArc((400.exprand(2))@(100.rand), rrand(10, 100), 2pi.rand, pi);

Pen.perform([\stroke, \fill].choose);

}

}

};


mouseDownFunc = {|me, x, y, mod|

absX = x;

postf("begin path: x=% realtive mousePosition:%\n",absX, me.mousePosition);

};


mouseUpFunc = {|me, x, y, mod|

postf("end path: (absX-x)==% realtive mousePosition:%\n",(absX-x), me.mousePosition);

};


mouseMoveFunc = {|me, x, y, mod|

sat = ((absX-x)/100);

((me.mousePosition)/(me.bounds.width@me.bounds.height)).postln;

me.refresh;

};


w = SCWindow.new.front;

w.view.background_(Color.white);

3.do{|i|

v = SCUserView(w, Rect(20+(i*120), 100, 100, 100));

v.background_(Color.rand);

v.drawFunc = drawFunc;

v.mouseDownAction = mouseDownFunc;

v.mouseUpAction = mouseUpFunc;

v.mouseMoveAction = mouseMoveFunc;

v.relativeOrigin = true;

};

w.refresh;

)




// Use the mouse to draw on the view


(

var w, txt, tmppoints, all;

tmppoints = [];


w = SCWindow("draw on me", Rect(128, 64, 340, 360));


v = SCUserView(w,w.view.bounds)

.mouseMoveAction_({|v,x,y|

[x,y].postln;

tmppoints = tmppoints.add(v.mousePosition);

v.refresh;

})

.mouseUpAction_({|v,x,y|

all = all.add(tmppoints.copy);

tmppoints = [];

v.refresh;

})

.drawFunc_{|me|

Pen.use {

Color.white.set;

Pen.fillRect(me.bounds.moveTo(0,0));

Pen.width = 1;

Color.black.set;


Pen.beginPath;

tmppoints.do{ |p, i|

if(i == 0){

Pen.moveTo(p);

}{

Pen.lineTo(p);

}

};

all.do{|points|

points.do{|p, i|

if(i == 0){

Pen.moveTo(p);

}{

Pen.lineTo(p);

}

};

};

Pen.stroke;

};

};

v.relativeOrigin = true;

w.front;

)



// Clearing on Refresh

//

// Set the behaviour for refreshing the view.

// If this flag is true (the default) the view will be cleared before each refresh call,

// otherwise It will draw in top of it.

// On OSX this functionality is only available for the version >= 10.4 and with the flag relativeOrigin set to true.


(

var width = 640, height = 480, w, theta = 0, drawFunc, gui;

gui = GUI.get( \cocoa );

w = gui.window.new( "clearOnRefresh = true", Rect( 128, 64, width, height ), false );

drawFunc = { arg view;

var x = 20 * sin( theta ), y = 42 * cos( theta );

gui.pen.addRect( view.bounds.moveTo( 0, 0 ));

gui.pen.clip;

theta = theta + 0.01;

gui.pen.fillColor_( Color.red( 0.2, 0.1 ));

gui.pen.fillRect( Rect( 0, 0, width, height ));

gui.pen.strokeColor_( Color.white );

gui.pen.translate( width * 0.5, height * 0.5 );

6.do { arg i;

gui.pen.rotate( theta * (1 - (i / 6)) );

gui.pen.scale( 0.7 + (i * 0.4), 0.4 + (i * 0.5) );

gui.pen.strokeOval( Rect.aboutPoint( Point( x, y ), 60, 40 ));

};

};

x = gui.userView.new( w, Rect( 10, 10, width - 20, height - 20 ))

.canFocus_( false )

.drawFunc_( drawFunc )

.clearOnRefresh_( false )

.relativeOrigin_( true );


w.front;

Routine({ while({ x.isClosed.not }, { x.refresh; (1/25).wait })}).play( AppClock );

)


(

var width= 640, height= 480, w, theta= 0, drawFunc;

w= GUI.window.new("trail test3", Rect(128, 64, width, height), false);

drawFunc= {|v|

var x= 20*sin(theta), y= 42*cos(theta);

theta= theta+0.01;

GUI.pen.fillColor_(Color.red(0.2, 0.1));

GUI.pen.fillRect(Rect(0, 0, width, height));

GUI.pen.strokeColor_(Color.white);

GUI.pen.push;

GUI.pen.translate(width*0.5, height*0.5);

6.do{|i|

GUI.pen.rotate(theta*(1-(i/6)));

GUI.pen.scale(0.7+(i*0.4), 0.4+(i*0.5));

GUI.pen.strokeOval(Rect.aboutPoint(Point(x, y), 60, 40))

};

GUI.pen.pop;

};

GUI.userView.new(w, Rect(10, 10, width-20, height-20)).drawFunc_(drawFunc).relativeOrigin_(true);

w.front;

Routine({inf.do{|i| w.refresh; (1/25).wait}}).play(AppClock);

)



// Prevent redrawing:


(

var func, views;


func = {|me|

Pen.use{

10.do{

Color.red(rrand(0.0, 1), rrand(0.0, 0.5)).set;

Pen.addArc((400.exprand(2))@(100.rand), rrand(10, 100), 2pi.rand, pi);

Pen.perform([\stroke, \fill].choose);

}

}

};


w = SCWindow.new("DrawFunc Examples").front;

w.view.background_(Color.white);

views = {|i|

v = SCUserView(w, Rect(20+(i*120), 100, 100, 100)).relativeOrigin_(true);

v.drawFunc = func;

} ! 3;

w.refresh;

{views.do{|v| v.clearOnRefresh_(false);v.drawFunc = nil}}.defer(0.4);

)



// Use refreshInRect(aRect).

// Constrains the receiver's refresh area to the rectangle passed in aRect.

// you may use Quartz Debug's flash screen updates to see the refresh area of the view


(

var userView, win, blob = Rect(0, 0, 50, 50), trackblob=false, pmouse;


a = SCImage.new("/Library/Desktop Pictures/Ripples Blue.jpg");


win = SCWindow.new("refreshInRect Test", Rect(400, 400, 600, 200), scroll:true).front;

win.onClose_({ a.free; });


userView = SCUserView(win, Rect(10,10,2000,800))

.backgroundImage_(a, 5)

.relativeOrigin_(false)

.drawFunc_({|me|

Color.blue.setFill;

Pen.fillRect(blob);

})

.mouseDownAction_({|v, x, y, mod|

pmouse = x@y;

trackblob = blob.containsPoint(pmouse);

})

.mouseUpAction_({|v, x, y, mod|

trackblob = false;

})

.mouseMoveAction_({|v, x, y, mod|

var refresh, mouse, delta;

mouse = x@y;

if(trackblob, {

refresh = blob.copy;

delta = mouse-pmouse;

blob = blob.moveBy(delta.x, delta.y);

refresh = refresh.union(blob);

v.refreshInRect(refresh);

});

pmouse = mouse;

});


blob = blob.moveBy(userView.bounds.left, userView.bounds.top);

userView.bounds.postln;

)