1 // How to subclass SCUserView to make custom GUI interfaces. Jost Muxfeldt, 2008.
2 // For many purposes you can use this as a template, and simply adjust the methods
4 MyWidget : SCUserView {
6 // (1) Setup instance vars appropriate to your widget. Make sure to define value.
7 var <>step, <value=0, <>leftColor, <>rightColor, <>thumbWidth=7;
9 // (2) Set the viewClass to SCUserView
10 *viewClass { ^SCUserView } // this ensures that SCUserView's primitive is called
13 // (3) Set up your view
14 init { |argParent, argBounds|
16 super.init(argParent, argBounds);
18 // set defaults of your instance variables
19 rightColor=Color.grey(0.8);
20 leftColor=Color.grey(0.2);
23 // set the draw function of the SCUserView
24 this.drawFunc={ this.draw};
28 // (4) define a drawing function for SCPen
31 SCPen.fillColor = Color.grey;
32 Pen.addRect(Rect(0,0, this.bounds.width*value,this.bounds.height));
35 SCPen.fillColor = Color.red;
36 Pen.moveTo(((this.bounds.width*value)-5) @ this.bounds.height);
37 Pen.lineTo(((this.bounds.width*value)+5) @ this.bounds.height);
38 Pen.lineTo(((this.bounds.width*value)) @ (this.bounds.height/2));
39 Pen.lineTo(((this.bounds.width*value)-5) @ this.bounds.height);
42 SCPen.strokeColor = Color.black;
43 Pen.addRect(Rect(0,0, this.bounds.width,this.bounds.height));
48 // (5) define typical widget methods (only those you need or adjust as needed)
49 valueAction_{ arg val; // most widgets have this
53 value_{ |val| // in many widgets, you can change the
54 // value and refresh the view , but not do the action.
58 // these are like in SCSlider
59 increment { |zoom=1| ^this.valueAction = this.value + (max(this.step, this.pixelStep) * zoom) }
60 decrement { |zoom=1| ^this.valueAction = this.value - (max(this.step, this.pixelStep) * zoom) }
62 pixelStep { // like in SCSlider
63 var bounds = this.bounds;
64 ^(bounds.width-1).reciprocal
68 // (6) override mouseActions
69 mouseDown{ arg x, y, modifiers, buttonNumber, clickCount;
71 // this allows for user defined mouseDownAction
72 mouseDownAction.value(this, x, y, modifiers, buttonNumber, clickCount);
74 // set the value and do the action
75 ([256, 0].includes(modifiers)).if{ // restrict to no modifier
77 newVal= x.linlin(0,this.bounds.width,0,1);
78 // translates the relative mouse position in pixels to a value between 0 and 1
80 if (newVal != value) {this.valueAction_(newVal)}; // only do something if the value changed
84 mouseMove{ arg x, y, modifiers, buttonNumber, clickCount;
86 // this allows for user defined mouseMoveAction
87 mouseMoveAction.value(this, x, y, modifiers, buttonNumber, clickCount);
89 // set the value and do the action
90 ([256, 0].includes(modifiers)).if{ // restrict to no modifier
92 newVal= x.linlin(0,this.bounds.width,0,1);
93 // translates the relative mouse position in pixels to a value between 0 and 1
95 if (newVal != value) {this.valueAction_(newVal)}; // only do something if the value changed
100 // (7) define default key actions
101 // make sure to return "this", if successful, and nil if not successful
102 defaultKeyDownAction { arg char, modifiers, unicode,keycode;
103 if (unicode == 16rF700, { this.increment; ^this });
104 if (unicode == 16rF703, { this.increment; ^this });
105 if (unicode == 16rF701, { this.decrement; ^this });
106 if (unicode == 16rF702, { this.decrement; ^this });
108 ^nil // bubble if it's an invalid key
111 // (8) define drag and drop
112 defaultGetDrag {^value} // what to drag
113 defaultCanReceiveDrag {^currentDrag.isNumber} // when to receive
114 defaultReceiveDrag { this.valueAction = currentDrag;} // what to do on receiving