2 Copyright 2009 by Hans Baier, Krzysztof Foltman
10 static const uint16 GDK_Home
= 0xff50;
11 static const uint16 GDK_End
= 0xff57;
12 static const uint16 GDK_Up
= 0xff52;
13 static const uint16 GDK_Down
= 0xff54;
14 static const uint16 GDK_Shift_L
= 0xffe1;
15 static const uint16 GDK_Shift_R
= 0xffe2;
17 public enum KnobMode
{
25 public class Knob
: Range
{
26 private Cairo
.Surface? cache_surface
= null;
28 private IKnobImageSource _image_source
;
29 public IKnobImageSource image_source
{
31 if (_image_source
== null) {
32 image_source
= new
SimpleKnobImageSource ();
38 if (value
!= null && value
!= _image_source
) {
39 _image_source
= value
;
46 private KnobMode _knob_mode
;
47 public KnobMode knob_mode
{
52 if (value
!= _knob_mode
) {
66 Adjustment adj
= new
Adjustment(0, 0, 1, 0.01, 0.5, 0);
70 private void init (Adjustment an_adjustment
) {
71 requisition
.width
= (int)image_source
.get_knob_width ();
72 requisition
.height
= (int)image_source
.get_knob_height ();
73 set_flags (WidgetFlags
.CAN_FOCUS
);
74 set_adjustment (an_adjustment
);
75 this
.value_changed
+= () => {
78 add_events (Gdk
.EventMask
.KEY_PRESS_MASK
| Gdk
.EventMask
.KEY_RELEASE_MASK
);
81 public Knob
.with_adjustment (Adjustment an_adjustment
) {
85 public override bool expose_event (Gdk
.EventExpose event
) {
86 // Create a Cairo context
87 var cr
= Gdk
.cairo_create (this
.window
);
89 // Set clipping area in order to avoid unnecessary drawing
90 cr
.rectangle (event
.area
.x
, event
.area
.y
,
91 event
.area
.width
, event
.area
.height
);
94 Adjustment adj
= get_adjustment ();
96 //debug ("adjustment = %p value = %f\n", adj, adj.value);
97 int ox
= allocation
.x
, oy
= allocation
.y
;
99 ox
+= (int)(allocation
.width
- image_source
.get_knob_width ()) / 2;
100 oy
+= (int)(allocation
.height
- image_source
.get_knob_height ()) / 2;
102 int phase
= (int)((adj
.value
- adj
.lower
) * 64 / (adj
.upper
- adj
.lower
));
103 // skip middle phase except for true middle value
104 if (knob_mode
== KnobMode
.BIPOLAR
&& phase
== 32) {
105 double pt
= (adj
.value
- adj
.lower
) * 2.0 / (adj
.upper
- adj
.lower
) - 1.0;
112 // endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
113 if (knob_mode
== KnobMode
.ENDLESS
&& (phase
% 16) == 0) {
116 double nom
= adj
.lower
+ phase
* (adj
.upper
- adj
.lower
) / 64.0;
117 double diff
= (adj
.value
- nom
) / (adj
.upper
- adj
.lower
);
119 phase
= (phase
+ 1) % 64;
121 phase
= (phase
+ 63) % 64;
124 if (cache_surface
== null) {
125 // looks like its either first call or the widget has been resized.
126 // create the cache_surface.
127 var cache_width
= image_source
.get_knob_width () * image_source
.phases
;
128 var cache_height
= image_source
.get_knob_height ();
129 cache_surface
= new Cairo
.Surface
.similar (cr
.get_target (),
130 Cairo
.Content
.COLOR_ALPHA
,
134 Cairo
.Context cache_cr
= new Cairo
.Context ( cache_surface
);
135 cache_cr
.set_source_rgba (0, 0, 0, 0);
136 cache_cr
.rectangle (0, 0, cache_width
, cache_height
);
139 image_source
.paint_knobs (cache_cr
,
141 image_source
.get_knob_width () / 2,
142 image_source
.get_knob_height () / 2);
145 assert (cache_surface
!= null);
146 cr
.set_source_surface (cache_surface
, ox
- phase
* image_source
.get_knob_width (), oy
);
147 cr
.rectangle (ox
, oy
, image_source
.get_knob_width (), image_source
.get_knob_height ());
150 // debug ("exposed %p %d+%d", window, allocation.x, allocation.y);
152 Gtk
.paint_focus (style
, window
, StateType
.NORMAL
, null, this
, null, ox
, oy
,
153 (int)image_source
.get_knob_width (), (int)image_source
.get_knob_height ());
159 public override void size_request (out Gtk
.Requisition requisition
) {
160 requisition
.width
= (int)image_source
.get_knob_width ();
161 requisition
.height
= (int)image_source
.get_knob_height ();
164 void calf_knob_incr (int dir_down
) {
165 Adjustment adj
= get_adjustment ();
167 int oldstep
= (int)(0.5f
+ (adj
.value
- adj
.lower
) / adj
.step_increment
);
169 int nsteps
= (int)(0.5f
+ (adj
.upper
- adj
.lower
) / adj
.step_increment
); // less 1 actually
174 if (knob_mode
== KnobMode
.ENDLESS
&& step
>= nsteps
)
176 if (knob_mode
== KnobMode
.ENDLESS
&& step
< 0)
177 step
= nsteps
- (nsteps
- step
) % nsteps
;
179 // trying to reduce error cumulation here, by counting from lowest or from highest
180 float value
= (float) (adj
.lower
+ step
* (double) (adj
.upper
- adj
.lower
) / nsteps
);
182 // debug ("step %d:%d nsteps %d value %f:%f\n", oldstep, step, nsteps, oldvalue, value);
186 public override bool key_press_event (Gdk
.EventKey event
) {
187 Adjustment adj
= get_adjustment ();
189 switch (event
.keyval
)
192 set_value (adj
.lower
);
196 set_value (adj
.upper
);
209 start_value
= (int)get_value ();
217 public override bool key_release_event (Gdk
.EventKey event
) {
218 if (event
.keyval
== GDK_Shift_L
|| event
.keyval
== GDK_Shift_R
) {
219 start_value
= (int)get_value ();
227 public override bool button_press_event (Gdk
.EventButton event
)
231 start_x
= (int)event
.x
;
232 start_y
= (int)event
.y
;
233 start_value
= get_value ();
234 //debug ("PRESS: start_value: %f, event.y: %d, start_y: %d", start_value, (int)event.y, (int)start_y);
239 public override bool button_release_event (Gdk
.EventButton event
)
247 inline
float endless (float value
)
250 return Math
.fmodf (value
, 1.0f
);
252 return Math
.fmodf (1.0f
- Math
.fmodf (1.0f
- value
, 1.0f
), 1.0f
);
255 inline
float deadzone (float value
, float incr
, float scale
)
257 float dzw
= 10.0f
/ scale
;
265 if (value
>= (0.5f
- dzw
) && value
<= (0.5f
+ dzw
))
272 public override bool motion_notify_event (Gdk
.EventMotion event
)
274 float scale
= (event
.state
== Gdk
.ModifierType
.SHIFT_MASK
) ?
1000.0f
: 100.0f
;
279 //debug ("start_value: %d, event.y: %d, start_y: %d", (int)start_value, (int)event.y, (int)start_y);
280 if (knob_mode
== KnobMode
.ENDLESS
) {
281 set_value (endless ((float)(start_value
- (event
.y
- start_y
) / scale
)));
282 } else if (knob_mode
== KnobMode
.BIPOLAR
) {
283 set_value (deadzone ((float)start_value
, (float)(-(event
.y
- start_y
) / scale
), (float)scale
));
285 set_value (start_value
- (event
.y
- start_y
) / scale
);
289 last_y
= (int)event
.y
;
293 public override bool scroll_event (Gdk
.EventScroll event
) {
294 calf_knob_incr (event
.direction
);
300 } // namespace Prolooks