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.
14 // 08.03.2010 - QKnob = SCKnob adjusted for GUI.qt scheme (by Jakob Leben)
17 classvar <>defaultMode, <>isSquare=false, <>compactRatio=0.87;
18 // The keyboard modifier used to engage vertical mode.
19 // We need to compute it according to platform.
21 var size, widthDiv2, center, aw8, aw12, aw14, hit;
22 var <>color, <value, prevValue, <>step, <>keystep, <>mode, <centered = false;
24 var <>shift_scale = 100.0, <>ctrl_scale = 10.0, <>alt_scale = 0.1;
31 // To engage vertical mode, use a modifier that's not used for drag & drop
33 \osx, { vertMod = \ctrl },
39 GUI.skins.default.put('knob', (
41 scale: Color.black.alpha_(0.3),
42 center: Color.blue(0.7, 0.5),
43 level: Color.green(0.8, 0.8),
44 dial: Color.black.alpha_(0.7),
52 *new { arg parent, bounds;
53 ^super.new( parent, bounds ).initQKnob( parent );
56 initQKnob { arg argParent;
58 var argBounds = this.calcConsts( this.bounds );
60 this.bounds_( argBounds );
67 skin = GUI.skins.default.knob.default;
69 this.oldMethodsCompat(skin);
71 this.receiveDragHandler = { this.valueAction_(QView.currentDrag); };
72 this.beginDragAction = { value.asFloat; };
73 this.canReceiveDragHandler = { QView.currentDrag.isNumber };
75 this.drawFunc_( { this.drawKnob } );
78 calcConsts { arg rect;
80 rect = rect.asRect.height_( rect.asRect.width );
82 rect = rect.asRect.height_( (rect.asRect.width * compactRatio).ceil );
85 widthDiv2 = size * 0.5;
86 aw8 = widthDiv2 - (0.08 * size);
87 aw12 = widthDiv2 - (0.12 * size);
88 aw14 = widthDiv2 - (0.14 * size);
89 center = Point(widthDiv2, widthDiv2);
99 rect = this.calcConsts(rect);
104 var startAngle, arcAngle;
106 QPen.color = color[2];
107 QPen.addAnnularWedge(
118 arcAngle = 1.5pi * value;
121 arcAngle = 1.5pi * (value - 0.5);
124 QPen.color = color[1];
125 QPen.addAnnularWedge(
134 QPen.color = color[0];
135 QPen.addWedge(center, aw14, 0, 2pi);
138 QPen.color = color[3];
139 QPen.width = (0.08 * size);
141 QPen.lineTo(Polar.new(aw14, 0.75pi + (1.5pi * value)).asPoint + center);
145 mouseDown { arg x, y, modifiers, buttonNumber, clickCount;
149 mouseDownAction.value(this, x, y, modifiers, buttonNumber, clickCount);
151 this.mouseMove(x, y, modifiers);
155 mouseMove { arg x, y, modifiers;
156 var mp, pt, angle, inc = 0;
159 if( modifiers != 0 ) {
161 {modifiers.isAlt}, {m = \horiz},
162 {vertMod === \ctrl && modifiers.isCtrl}, {m = \vert},
163 {vertMod === \shift && modifiers.isShift}, {m = \vert},
170 //if (modifiers & 1048576 != 1048576) { // we are not dragging out - apple key
180 value = (value + inc).clip(0.0, 1.0);
182 if (prevValue != value) {
183 action.value(this, x, y, modifiers);
196 value = (value + inc).clip(0.0, 1.0);
198 if (prevValue != value) {
199 action.value(this, x, y, modifiers);
205 pt = center - Point(x,y);
206 angle = Point(pt.y, pt.x.neg).theta;
207 if ((angle >= -0.80pi) and: { angle <= 0.80pi} , {
208 value = [-0.75pi, 0.75pi].asSpec.unmap(angle);
209 if (prevValue != value) {
210 action.value(this, x, y, modifiers);
219 mouseMoveAction.value(this, x, y, modifiers);
222 getScale { |modifiers|
224 { modifiers.isShift } { shift_scale }
225 { modifiers.isCtrl } { ctrl_scale }
226 { modifiers.isAlt } { alt_scale }
230 defaultKeyDownAction { arg char, modifiers, unicode, keycode;
231 var zoom = this.getScale(modifiers);
235 $r, { this.valueAction = 1.0.rand },
236 $n, { this.valueAction = 0.0 },
237 $x, { this.valueAction = 1.0 },
238 $c, { this.valueAction = 0.5 },
242 16r5b, { this.decrement(zoom) },
243 16r5d, { this.increment(zoom) },
244 16r1000013, { this.increment(zoom) },
245 16r1000014, { this.increment(zoom) },
246 16r1000015, { this.decrement(zoom) },
247 16r1000012, { this.decrement(zoom) },
248 {^this} // bubble if it's an invalid key
255 increment { |zoom=1| ^this.valueAction = (this.value + (keystep * zoom)).min(1) }
257 decrement { |zoom=1| ^this.valueAction = (this.value - (keystep * zoom)).max(0) }
260 value = val.clip(0.0, 1.0);
264 valueAction_ { arg val;
265 value = val.clip(0.0, 1.0);
270 centered_ { arg bool;
276 if ( newskin.notNil ) {
278 newskin.proto_( GUI.skins.default.knob.default );
279 this.oldMethodsCompat;
282 format("%: skin not found.", this.class).inform;
292 defaultMode = skin.defaultMode;
295 *paletteExample{arg parent, bounds;
296 ^this.new(parent, bounds.asRect.height@bounds.asRect.height);