1 // blackrain at realizedsound dot net - 05/2006
2 // fix key modidiers bug by Stephan Wittwer 08/2006 - thanks!
3 // Knob updates only on value changes - 10/2006
4 // GUI.cocoa changes - 04/2007
6 // 03.10.2008 - new implementation:
7 // - Knob now is a subclass of SCViewHolder
10 // 01.20.2009 - SCKnob
11 // - a subclass of SCUserView again.
16 classvar <>defaultMode, <>isSquare=false, <>compactRatio=0.87;
17 var size, widthDiv2, center, aw8, aw12, aw14, hit;
18 var <>color, <value, prevValue, <>step, <>keystep, <>mode, <centered = false;
20 var <>shift_scale = 100.0, <>ctrl_scale = 10.0, <>alt_scale = 0.1;
22 *viewClass { ^SCUserView }
31 GUI.skins.default.put('knob', (
33 scale: Color.black.alpha_(0.3),
34 center: Color.blue(0.7, 0.5),
35 level: Color.green(0.8, 0.8),
36 dial: Color.black.alpha_(0.7),
44 init { arg argParent, argBounds;
46 argBounds = this.calcConsts(argBounds);
48 super.init(argParent, argBounds);
55 skin = GUI.skins.default.knob.default;
57 this.oldMethodsCompat(skin);
59 this.receiveDragHandler = { this.valueAction_(SCView.currentDrag); };
60 this.beginDragAction = { value.asFloat; };
61 this.canReceiveDragHandler = { SCView.currentDrag.isNumber };
64 calcConsts { arg rect;
66 rect = rect.asRect.height_( rect.asRect.width );
68 rect = rect.asRect.height_( (rect.asRect.width * compactRatio).ceil );
71 widthDiv2 = size * 0.5;
72 aw8 = widthDiv2 - (0.08 * size);
73 aw12 = widthDiv2 - (0.12 * size);
74 aw14 = widthDiv2 - (0.14 * size);
75 center = Point(widthDiv2, widthDiv2);
81 rect = this.calcConsts(rect);
86 var startAngle, arcAngle;
89 SCPen.addAnnularWedge(
100 arcAngle = 1.5pi * value;
103 arcAngle = 1.5pi * (value - 0.5);
107 SCPen.addAnnularWedge(
114 SCPen.perform(\fill);
117 SCPen.addWedge(center, aw14, 0, 2pi);
118 SCPen.perform(\fill);
121 SCPen.width = (0.08 * size);
122 SCPen.moveTo(center);
123 SCPen.lineTo(Polar.new(aw14, 0.75pi + (1.5pi * value)).asPoint + center);
127 mouseDown { arg x, y, modifiers, buttonNumber, clickCount;
131 mouseDownAction.value(this, x, y, modifiers, buttonNumber, clickCount);
133 this.mouseMove(x, y, modifiers);
137 mouseMove { arg x, y, modifiers;
138 var mp, pt, angle, inc = 0;
141 if (modifiers & 1048576 != 1048576) { // we are not dragging out - apple key
143 { (mode == \vert) || (modifiers & 262144 == 262144) } { // Control
151 value = (value + inc).clip(0.0, 1.0);
153 if (prevValue != value) {
154 action.value(this, x, y, modifiers);
159 { (mode == \horiz) || (modifiers & 524288 == 524288) } { // Option
167 value = (value + inc).clip(0.0, 1.0);
169 if (prevValue != value) {
170 action.value(this, x, y, modifiers);
176 pt = center - Point(x,y);
177 angle = Point(pt.y, pt.x.neg).theta;
178 if ((angle >= -0.80pi) and: { angle <= 0.80pi} , {
179 value = [-0.75pi, 0.75pi].asSpec.unmap(angle);
180 if (prevValue != value) {
181 action.value(this, x, y, modifiers);
190 mouseMoveAction.value(this, x, y, modifiers);
193 getScale { |modifiers|
195 { modifiers & 131072 == 131072 } { shift_scale }
196 { modifiers & 262144 == 262144 } { ctrl_scale }
197 { modifiers & 524288 == 524288 } { alt_scale }
201 defaultKeyDownAction { arg char, modifiers, unicode,keycode;
202 var zoom = this.getScale(modifiers);
205 if (char == $r, { this.valueAction = 1.0.rand; ^this });
206 if (char == $n, { this.valueAction = 0.0; ^this });
207 if (char == $x, { this.valueAction = 1.0; ^this });
208 if (char == $c, { this.valueAction = 0.5; ^this });
210 if (char == $[, { this.decrement(zoom); ^this });
211 if (char == $], { this.increment(zoom); ^this });
212 if (unicode == 16rF700, { this.increment(zoom); ^this });
213 if (unicode == 16rF703, { this.increment(zoom); ^this });
214 if (unicode == 16rF701, { this.decrement(zoom); ^this });
215 if (unicode == 16rF702, { this.decrement(zoom); ^this });
216 ^nil // bubble if it's an invalid key
219 increment { |zoom=1| ^this.valueAction = (this.value + (keystep * zoom)).min(1) }
221 decrement { |zoom=1| ^this.valueAction = (this.value - (keystep * zoom)).max(0) }
224 value = val.clip(0.0, 1.0);
228 valueAction_ { arg val;
229 value = val.clip(0.0, 1.0);
234 centered_ { arg bool;
240 if ( newskin.notNil ) {
242 newskin.proto_( GUI.skins.default.knob.default );
243 this.oldMethodsCompat;
246 format("%: skin not found.", this.class).inform;
256 defaultMode = skin.defaultMode;
259 *paletteExample{arg parent, bounds;
260 ^this.new(parent, bounds.asRect.height@bounds.asRect.height);