This class is meant as an emulation of SCUserView. last mod: 17-Jul-09 sciss
Also refer to JSCView for different behaviour affecting all widgets
different behaviour | |
refresh | see the introductory paragraph for SwingOSC's refresh policy |
view bounds | here is no way to paint outside the logical bounds of a swing view, all draw operations are automatically clipped to the view's bounds. |
extended functionality | |
focusVisible_ | allows you to switch off the default focus border painting |
refreshOnFocus_ | allows you to switch off refreshing as result of focus change |
Note: please use the abstraction layer UserView if possible!
JSCUserView
is a user-definable view intended mainly for use with JPen and drawHooks.
See also: JSCWindow, JPen, JSCTabletView.
Sets the function which should be evaluated if the view is refreshed. Note: A refresh happens if either of the following conditions meet:
refresh
on the view.refresh
on the window that contains the view.drawFunc_
on the viewrefreshOnFocus
is true
and the view gains or looses focus.
Example:
( var func; func = { arg me; JPen.use { // clipping into the boundingbox JPen.addRect( me.bounds.moveTo( 0, 0 )); JPen.clip; // draw background // (alternatively use addRect) JPen.fillColor = Color.gray( 0.5 ); JPen.moveTo( 0 @ 0 ); JPen.lineTo( me.bounds.width @ 0 ); JPen.lineTo( me.bounds.width @ me.bounds.height ); JPen.lineTo( 0 @ me.bounds.height ); JPen.lineTo( 0 @ 0 ); JPen.fill; 10.do { JPen.color = Color.red( rrand( 0.0, 1 ), rrand( 0.0, 0.5 )); JPen.addArc( 400.exprand( 2 ) @ 100.rand, rrand( 10, 100 ), 2pi.rand, pi ); JPen.perform([ \stroke, \fill ].choose ); } } }; w = JSCWindow.new( "DrawFunc Examples" ).front; w.view.background_( Color.white ); 3.do { arg i; v = JSCUserView( w, Rect( 20 + (i * 120), 100, 100, 100 )); v.drawFunc = func; }; ) v.refresh; // updates the last created (third) view w.refresh; // updates all user views in the window
// these only work in SwingOSC: v.refreshOnFocus = false; // do not re-evaluate drawFunc when focus is gained or lost (can be more efficient) v.focusVisible = false; // don't paint focus border. you can do yourself in the drawFunc, checking for me.hasFocus!
Sets the coordinate origin of JPen in the view's drawFunc
to be relative to the view's top left corner (when true
, which is the default) or to be relative to the window's top left corner (when false
).
( w = JSCWindow.new( "relativeOrigin = true" ); w.view.background_( Color.white ); 3.do { arg i; v = JSCUserView(w, Rect( 20 + (i * 120), 100, 100, 100 )) // this code not needed since it's the default // .relativeOrigin_( true ) .drawFunc_({ JPen.strokeRect( Rect( 0, 0, 100, 100 )); JPen.line( 0 @ 0, 100 @ 100 ); JPen.line( 100 @ 0, 0 @ 100 ); JPen.stroke; }); }; w.front; )
You can achieve a similar result with relativeOrigin
being false
, by translating the coordinate system at the beginning of the drawFunc
:
( w = JSCWindow.new( "relativeOrigin = false" ); w.view.background_( Color.white ); 3.do { arg i; v = JSCUserView(w, Rect( 20 + (i * 120), 100, 100, 100 )) .relativeOrigin_( false ) .drawFunc_({ arg view; var bounds; bounds = view.bounds; JPen.translate( bounds.left, bounds.top ); JPen.strokeRect( Rect( 0, 0, 100, 100 )); JPen.line( 0 @ 0, 100 @ 100 ); JPen.line( 100 @ 0, 0 @ 100 ); JPen.stroke; }) }; w.front; )
The effect of relativeOrigin
setting regarding the reported mouse coordinates is shown in the section mouseUpAction_.
Sets the function which should be evaluated if the view is in focus and a key is pressed. This function will be passed four arguments: the view itself, the key pressed as a Char, the modifier keys Integer (shift, alt, etc.), and the unicode value Integer. See JSCView for more details.
( // select the window, type something and watch the post window w = JSCWindow.new( "select this window and type something" ); c = JSCUserView( w, w.view.bounds ); c.keyDownAction = { arg view, char, modifiers, unicode, keycode; [char, modifiers, unicode, keycode].postln; c.drawFunc = { JPen.font = JSCFont( "Helvetica", 70 ); JPen.fillColor = Color.blue( 0.3, 0.5 ); JPen.stringAtPoint( char.asString, 180 @ 150 ); }; }; w.front; c.focus; )
Sets the function which should be evaluated if the mouse is at the beginning of tracking (mouse-down). This function will be passed four arguments: theView, x coordinate, y coordinate, and keyboard modifiers.
Sets the function which should be evaluated if the mouse is tracked. This function will be passed four arguments: theView, x coordinate, y coordinate, and keyboard modifiers.
Sets 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.
Example:
( var drawFunc, beginTrackFunc, endTrackFunc, trackFunc, sat = 0, absX; drawFunc = { arg me; JPen.use { // clipping into the boundingbox JPen.addRect( me.bounds.moveTo( 0, 0 )); JPen.clip; // draw background JPen.fillColor = Color.gray( sat ); JPen.addRect( me.bounds.moveTo( 0, 0 )); JPen.fill; 10.do { JPen.color = Color.red( rrand( 0.0, 1 ), rrand( 0.0, 0.5 )); JPen.addArc( 400.exprand( 2 ) @ 100.rand, rrand( 10, 100 ), 2pi.rand, pi ); JPen.perform([ \stroke, \fill ].choose ); } } }; beginTrackFunc = { arg me, x, y, mod; absX = x; postf( "begin path: x=%\n", absX ); }; endTrackFunc = { arg me, x, y, mod; postf( "end path: (absX-x)=%\n", (absX - x)); }; trackFunc = { arg me, x, y, mod; sat = (absX - x) / 100; me.refresh; }; w = JSCWindow.new.front; w.view.background_(Color.white); 3.do { arg i; v = JSCUserView( w, Rect( 20 + (i * 120), 100, 100, 100 )); v.drawFunc = drawFunc; v.mouseDownAction = beginTrackFunc; v.mouseUpAction = endTrackFunc; v.mouseMoveAction = trackFunc; }; )
Important: the mouse coordinates reported depend on the setting of relativeOrigin
: for the default of true
, they are relative to the left-top corner of the view, for false
, they are relative to the view's parent container top-left. That means that the mouse coordinates always refer to the same origin as the pen drawing coordinates. As a result, you can always plug the mouse coordinates directly into drawing commands without the need of translation, regardless of the relativeOrigin
setting:
( w = JSCWindow.new; 2.do({ arg i; var v, f; v = JSCUserView( w, Rect( 20 + (i * 180), 30, 150, 300 )) .relativeOrigin_( i == 0 ) .background_( Color.rand ) .focusVisible_( false ); f = { arg view, x, y; v.drawFunc = { arg view; JPen.stringAtPoint( "%, %".format( x, y ), (x + 12) @ (y - 4 ))}}; v.mouseDownAction = f; v.mouseMoveAction = f; x = v; }); w.front; )
A final example for mouse control shows how it can be used to draw live on the view:
// NOTE: in swing, do not rely on correct painting // when components overlap. components should never overlap. // therefore, in this example, we use the drawFunc of the user view // and not the window to paint (see SCUserView help for comparison) // NOTE: after some drawing, the number of OSC packets sent // during each refresh increases which results in more sluggish // updates ( var w, txt, tmppoints, all; tmppoints = []; w = JSCWindow( "draw on me", Rect( 128, 64, 340, 360 )); v = JSCUserView( w, Rect( 0, 0, 340, 360 )) .canFocus_( false ) .mouseMoveAction_({ arg view,x,y; tmppoints = tmppoints.add( x @ y ); view.refresh; }) .mouseUpAction_({ arg view, x, y; all = all.add( tmppoints.copy ); tmppoints = []; view.refresh; }) .drawFunc_({ JPen.use { JPen.width = 1; JPen.beginPath; tmppoints.do { arg p, i; if( i == 0, { JPen.moveTo( p ); }, { JPen.lineTo( p ); }); }; all.do{ arg points; points.do { arg p, i; if( i == 0, { JPen.moveTo( p ); }, { JPen.lineTo( p ); }); }; }; JPen.stroke; }; }); w.front; )
clearOnRefresh_
sets 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 on top of the previous graphics.
Example:
( var theta = 0; w = JSCWindow( "clearOnRefresh = false", resizable: false ); x = JSCUserView( w, w.view.bounds ) .focusVisible_( false ) .clearOnRefresh_( false ) .drawFunc_({ arg view; var x = 20 * sin( theta ), y = 42 * cos( theta ), b = view.bounds; theta = theta + 0.01; JPen.fillColor = Color.red( 0.2, 0.05 ); JPen.fillRect( Rect( 0, 0, b.width, b.height )); JPen.strokeColor = Color.white; JPen.translate( b.width * 0.5, b.height * 0.5 ); 6.do({ arg i; JPen.rotate( theta * (1 - (i / 6)) ); JPen.scale( 0.7 + (i * 0.4), 0.4 + (i * 0.5) ); JPen.strokeOval( Rect.aboutPoint( x @ y, 60, 40 )); }); }); w.front; fork { while({ x.isClosed.not }, { x.refresh; (1/5).wait })}; )
You can explicitly trigger a request to clear the drawing at the next view's refresh, by calling the clearDrawing
method:
( w.name = "Press mouse to clear drawing"; x.mouseDownAction_({ arg view, x, y; view.clearDrawing }); )
To "freeze" a drawing, that is, to prevent it to be re-drawn, use either .canFocus_( false )
or .refreshOnFocus_( false )
:
( var func, views; func = { 10.do { JPen.color = Color.red( rrand(0.0, 1), rrand( 0.0, 0.5 )); JPen.addArc( 400.exprand( 2 ) @ 100.rand, rrand( 10, 100 ), 2pi.rand, pi ); JPen.perform([ \stroke, \fill ].choose ); }; }; w = JSCWindow.new( "Frozen drawing" ); w.view.background_( Color.white ); views = { arg i; v = JSCUserView( w, Rect( 20 + (i * 120), 100, 100, 100 )) .refreshOnFocus_( false ) .drawFunc_( func ); } ! 3; w.front; )