old quark gui: openOS is not osx only
[supercollider.git] / SCClassLibrary / QtCollider / QKnob.sc
blob94a493b5cc0c5fca5799d3dbb405b0a2f5087355
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
5 //
6 //  03.10.2008 - new implementation:
7 //    - Knob now is a subclass of SCViewHolder
8 //    - Relative origin
9 //
10 //  01.20.2009 - SCKnob
11 //    - a subclass of SCUserView again.
12 //    - isSquare option
14 //  08.03.2010 - QKnob = SCKnob adjusted for GUI.qt scheme (by Jakob Leben)
16 QKnob : QUserView {
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.
20   classvar vertMod;
21   var size, widthDiv2, center, aw8, aw12, aw14, hit;
22   var <>color, <value, prevValue, <>step, <>keystep, <>mode, <centered = false;
23   var <skin;
24   var <>shift_scale = 100.0, <>ctrl_scale = 10.0, <>alt_scale = 0.1;
26   *initClass {
27     var version;
29     defaultMode='round';
31     // To engage vertical mode, use a modifier that's not used for drag & drop
32     Platform.case (
33       \osx, { vertMod = \ctrl },
34       { vertMod = \shift }
35     );
37     StartUp.add({
39       GUI.skins.default.put('knob', (
40         default: (
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),
45           defaultMode: 'round'
46         )
47       ));
49     });
50   }
52   *new { arg parent, bounds;
53     ^super.new( parent, bounds ).initQKnob( parent );
54   }
56   initQKnob { arg argParent;
58     var argBounds = this.calcConsts( this.bounds );
60     this.bounds_( argBounds );
62     value = 0.0;
63     keystep = 0.01;
64     step = 0.01;
65     mode = defaultMode;
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 } );
76   }
78   calcConsts { arg rect;
79     if ( isSquare ) {
80       rect = rect.asRect.height_( rect.asRect.width );
81     }{
82       rect = rect.asRect.height_( (rect.asRect.width * compactRatio).ceil );
83     };
84     size = rect.width;
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);
91     ^rect
92   }
94   *sizeHint {
95     ^Point(60,60);
96   }
98   bounds_ { arg rect;
99     rect = this.calcConsts(rect);
100     super.bounds_(rect);
101   }
103   drawKnob {
104     var startAngle, arcAngle;
106     QPen.color = color[2];
107     QPen.addAnnularWedge(
108       center,
109       aw8,
110       widthDiv2,
111       0.25pi,
112       -1.5pi
113     );
114     QPen.perform(\fill);
116     if (centered.not, {
117       startAngle = 0.75pi;
118       arcAngle = 1.5pi * value;
119     }, {
120       startAngle = -0.5pi;
121       arcAngle = 1.5pi * (value - 0.5);
122     });
124     QPen.color = color[1];
125     QPen.addAnnularWedge(
126       center,
127       aw12,
128       widthDiv2,
129       startAngle,
130       arcAngle
131     );
132     QPen.perform(\fill);
134     QPen.color = color[0];
135     QPen.addWedge(center, aw14, 0, 2pi);
136     QPen.perform(\fill);
138     QPen.color = color[3];
139     QPen.width = (0.08 * size);
140     QPen.moveTo(center);
141     QPen.lineTo(Polar.new(aw14, 0.75pi + (1.5pi * value)).asPoint + center);
142     QPen.stroke;
143   }
145   mouseDown { arg x, y, modifiers, buttonNumber, clickCount;
147     hit =  x @ y;
149     mouseDownAction.value(this, x, y, modifiers, buttonNumber, clickCount);
151     this.mouseMove(x, y, modifiers);
153   }
155   mouseMove { arg x, y, modifiers;
156     var mp, pt, angle, inc = 0;
157     var m; // mode used
159     if( modifiers != 0 ) {
160         case (
161           {modifiers.isAlt}, {m = \horiz},
162           {vertMod === \ctrl && modifiers.isCtrl}, {m = \vert},
163           {vertMod === \shift && modifiers.isShift}, {m = \vert},
164           {m = mode}
165         );
166     }{
167       m = mode;
168     };
170     //if (modifiers & 1048576 != 1048576) { // we are not dragging out - apple key
171       case
172         { (m === \vert) } {
173           if ( hit.y > y, {
174             inc = step;
175           }, {
176             if ( hit.y < y, {
177               inc = step.neg;
178             });
179           });
180           value = (value + inc).clip(0.0, 1.0);
181           hit = Point(x,y);
182           if (prevValue != value) {
183             action.value(this, x, y, modifiers);
184             prevValue = value;
185             this.refresh;
186           }
187         }
188         { m === \horiz } {
189           if ( hit.x > x, {
190             inc = step.neg;
191           }, {
192             if ( hit.x < x, {
193               inc = step;
194             });
195           });
196           value = (value + inc).clip(0.0, 1.0);
197           hit = Point(x,y);
198           if (prevValue != value) {
199             action.value(this, x, y, modifiers);
200             prevValue = value;
201             this.refresh;
202           }
203         }
204         { m === \round } {
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);
211               prevValue = value;
212               this.refresh;
213             }
214           });
216         };
217     //};
219     mouseMoveAction.value(this, x, y, modifiers);
220   }
222   getScale { |modifiers|
223     ^case
224       { modifiers.isShift } { shift_scale }
225       { modifiers.isCtrl } { ctrl_scale }
226       { modifiers.isAlt } { alt_scale }
227       { 1 };
228   }
230   defaultKeyDownAction { arg char, modifiers, unicode, keycode;
231     var zoom = this.getScale(modifiers);
233     // standard keydown
234     switch( char,
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 },
240       {
241         switch( keycode,
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
249         )
250       }
251     );
252     ^true;
253   }
255   increment { |zoom=1| ^this.valueAction = (this.value + (keystep * zoom)).min(1) }
257   decrement { |zoom=1| ^this.valueAction = (this.value - (keystep * zoom)).max(0) }
259   value_ { arg val;
260     value = val.clip(0.0, 1.0);
261     this.refresh;
262   }
264   valueAction_ { arg val;
265     value = val.clip(0.0, 1.0);
266     action.value(this);
267     this.refresh;
268   }
270   centered_ { arg bool;
271     centered = bool;
272     this.refresh;
273   }
275   skin_ { arg newskin;
276     if ( newskin.notNil ) {
277       skin = newskin;
278       newskin.proto_( GUI.skins.default.knob.default );
279       this.oldMethodsCompat;
280       this.refresh;
281     }{
282       format("%: skin not found.", this.class).inform;
283     };
284   }
285   oldMethodsCompat {
286     color = [
287       skin.center,
288       skin.level,
289       skin.scale,
290       skin.dial
291     ];
292     defaultMode = skin.defaultMode;
293   }
295   *paletteExample{arg parent, bounds;
296     ^this.new(parent, bounds.asRect.height@bounds.asRect.height);
297   }