1
2 #include "widget.hxx"
3
4 #include <math.h>
5
6 #include "ui.hxx"
7 #include "theme.hxx"
8
9 namespace Avtk
10 {
11 #ifdef AVTK_DEBUG
12 int Widget::widgetCounter = 0;
13 #endif
14
15 // constructor for top-level windows only
Widget(Avtk::UI * ui_,int w,int h)16 Widget::Widget( Avtk::UI* ui_, int w, int h ) :
17 ui( ui_ ),
18 noHandle_( false ),
19 label_("avtk-top-level"),
20 x_( 0 ),
21 y_( 0 ),
22 w_( w ),
23 h_( h ),
24 initX( 0 ),
25 initY( 0 ),
26 initW( w ),
27 initH( h ),
28 visible_(true),
29 parent_( 0x0 ) // top levels don't have a parent
30 {
31 #ifdef AVTK_DEBUG
32 widgetCounter++;
33 #endif
34 }
35
Widget(Avtk::UI * ui_,int x,int y,int w,int h,std::string label__)36 Widget::Widget( Avtk::UI* ui_, int x, int y, int w, int h, std::string label__) :
37 ui(ui_),
38 parent_( 0 ),
39 theme_( ui->theme() ),
40
41 noHandle_( false ),
42 groupChild( false ),
43 groupItemNumber_( -1 ),
44
45 x_( x ),
46 y_( y ),
47 w_( w ),
48 h_( h ),
49 initX( x ),
50 initY( y ),
51 initW( w ),
52 initH( h ),
53
54 label_( label__ ),
55 visible_( true ),
56
57 value_( 0 ),
58 defaultValue_( 0 ),
59 auditionValue_( 0 ),
60
61 callback( Avtk::UI::staticWidgetValueCB ),
62 callbackUD( ui_ ),
63
64 mouseButtonPressed_(0),
65
66 cm( CLICK_NONE ),
67 dm( DM_NONE ),
68 vm( VALUE_FLOAT_0_1 ),
69 rcm(RCLICK_VALUE_DEFAULT),
70
71 mX(0),
72 mY(0),
73 scrollDisable( 1 ),
74 scrollInvert( 0 ),
75 // actual scroll in PX / number == delta
76 scrollDeltaAmount( 10 )
77 {
78 #ifdef AVTK_DEBUG
79 widgetCounter++;
80 #endif
81
82
83 /// add the widget to the top of the parentStack
84 /// parentStack is a stack of Group* widgets. group->end() pops from stack
85 AVTK_DEV("Widget() %s : adding this to %s\n", label(), ui->parentStackTop()->label() );
86 ui->parentStackTop()->add( this );
87 }
88
theme(Theme * t)89 void Widget::theme( Theme* t )
90 {
91 theme_ = t;
92 }
93
handle(const PuglEvent * event)94 int Widget::handle( const PuglEvent* event )
95 {
96 // eg: noHandle means this widget doesn't take any input.
97 // !visible_ implies the widget isn't shown: so a user can't interact with it
98 if( noHandle_ || !visible_ ) {
99 AVTK_DEV("widget %s noHandle (%i) or visible (%i)\n", label(), int(noHandle_), int(visible_) );
100
101 // FIXME TODO: this causes issues when !visible widgets are overlayed
102 // with widgets that use drag operations
103 // no point in calling motion() on a widget that isn't shown, or doesn't handle
104 //ui->wantsMotionUpdates( this, false );
105 return 0;
106 }
107 switch (event->type) {
108 case PUGL_BUTTON_PRESS: {
109 if ( event->button.x == 0 && event->button.y == 0 )
110 return 0;
111
112 if( touches( event->button.x, event->button.y ) ) {
113 mouseButtonPressed_ = event->button.button;
114 mousePressX = event->button.x;
115 mousePressY = event->button.y;
116 AVTK_DEV("click touches %s, clickMode %i, mouseBtn %i\n", label_.c_str(), clickMode(), mouseButton() );
117
118 if( mouseButtonPressed_ == 3 &&
119 rcm == RCLICK_VALUE_DEFAULT ) {
120 // check value() - if far from default set default, else set previous
121 if( fabsf(value_ - defaultValue_) > 0.00001 ) {
122 auditionValue_ = value_;
123 value( defaultValue_ );
124 callback( this, callbackUD );
125 } else {
126 value( auditionValue_ );
127 callback( this, callbackUD );
128 }
129
130 AVTK_DEV("rclick - reset to default - value( %f )\n", defaultValue_ );
131 }
132
133 if( cm == CLICK_TOGGLE ) {
134 value( !value() );
135 callback( this, callbackUD );
136 ui->redraw( this );
137 } else if ( cm == CLICK_MOMENTARY ) {
138 value( 1 );
139 callback( this, callbackUD );
140 ui->redraw( this );
141 } else if ( cm == CLICK_VALUE_FROM_Y ) {
142 float tmp = (event->button.y - y_) / h_/0.92;
143 value( tmp );
144 AVTK_DEV("Widget::handle() value from Y, %f\n", tmp);
145
146 callback( this, callbackUD );
147 ui->redraw( this );
148 }
149
150
151 if( dm == DM_DRAG_VERTICAL ||
152 dm == DM_DRAG_HORIZONTAL ) {
153 // sample the vertical mouse position, drag events affect += value()
154 mX = event->button.x;
155 mY = event->button.y;
156 }
157
158 // tell the UI that the current widget wants motion notify updates
159 // this also handles Drag-n-Drop, so we need motion updates even if we
160 // don't have DM_DRAG_VERTICAL || DM_DRAG_HORIZONTAL
161 ui->wantsMotionUpdates( this, true );
162 return 1;
163 }
164 }
165 break;
166
167 case PUGL_BUTTON_RELEASE: {
168 // FIXME: this is now run for each existing widget, until the touches()
169 // widget is found, then event-propogation stops. Optimizte to avoid
170 // repeated calling.
171 ui->wantsMotionUpdates( this, false );
172
173 //AVTK_DEV("click release %s, clickMode %i\n", label_.c_str(), clickMode() );
174 if( touches( event->button.x, event->button.y ) ) {
175 //AVTK_DEV("Widget PUGL button release, cm %i\n", cm);
176
177 if ( cm == CLICK_MOMENTARY ) {
178 value( 0 );
179 ui->redraw();
180 //AVTK_DEV("Widget MOMENTARY, redrawn value\n");
181 }
182 return 1;
183 }
184 }
185 return 0;
186 break;
187
188 case PUGL_SCROLL: {
189 bool scTch = touches( event->scroll.x, event->scroll.y );
190 if( scTch && !scrollDisable ) {
191 AVTK_DEV("scroll touch %i, x %lf, y %lf\n", int(scTch), event->scroll.x, event->scroll.y );
192 float delta = event->scroll.dy / float(scrollDeltaAmount);
193 if( scrollInvert )
194 delta = -delta;
195 value( value() + delta );
196 callback( this, callbackUD );
197 ui->redraw( this );
198 return 1;
199 }
200 }
201 break;
202
203 case PUGL_KEY_PRESS: {
204 PuglEventKey* e = (PuglEventKey*)event;
205 if( touches( e->x, e->y ) ) {
206 if (event->key.character == ' ') {
207 //AVTK_DEV("pugl space\n");
208 callback( this, callbackUD );
209 } else if (event->key.character == 's') {
210 /*
211 AVTK_DEV("pugl key s\n");
212 float delta = 1 / 10.f;
213 value( value_ - delta );
214 ui->redraw( this );
215 return 1;
216 */
217 }
218 break;
219 }
220 }
221
222 default:
223 return 0;
224 break;
225 }
226
227 return 0;
228 }
229
motion(int inX,int inY)230 void Widget::motion( int inX, int inY )
231 {
232 if ( dm == DM_NONE ) {
233 // if widget is pressed, and mouse moves outside the widget area
234 // inform UI of possible drag-drop action
235 if( !touches( inX, inY ) ) {
236 static const char* testData = "DragDropTestPayload";
237 AVTK_DEV("motion outside widget -> DND?\n");
238 ui->dragDropInit( this, strlen( testData ), (void*)testData );
239
240 if( cm == CLICK_MOMENTARY ) {
241 value( 0 ); // return widget state to normal
242 }
243 }
244 return;
245 }
246
247 // handle value() on the widget
248 float delta = 0;
249 float dragSpeed = float(h_);
250 if ( dm == DM_DRAG_VERTICAL ) {
251 if( dragSpeed < 100 ) {
252 dragSpeed = 100; // num of px for "full-scale" drag
253 //AVTK_DEV("dragspeed set to %f\n", dragSpeed);
254 }
255
256 delta = ( mY - inY ) / dragSpeed;
257 } else if ( dm == DM_DRAG_HORIZONTAL ) {
258 dragSpeed = float(w_);
259 if( dragSpeed < 100 ) {
260 dragSpeed = 100; // num of px for "full-scale" drag
261 //AVTK_DEV("dragspeed set to %f\n", dragSpeed);
262 }
263 delta = ( inX - mX ) / dragSpeed;
264 }
265
266 value( value_ + delta );
267 //AVTK_DEV("drag(), delta %f, new value %f\n", delta, value() );
268
269 mX = inX;
270 mY = inY;
271
272 // check types of "when()" here?
273 // immidiate
274 // on-release
275 if( true )
276 callback( this, callbackUD );
277
278 ui->redraw( this );
279 }
280
valueMode(ValueMode v,int base,int range)281 void Widget::valueMode( ValueMode v, int base, int range )
282 {
283 vm = v;
284 valueIntBase = base;
285 valueIntRange = range;
286 setScrollDeltaAmount( 1 );
287 }
288
setScrollDeltaAmount(float sda)289 void Widget::setScrollDeltaAmount( float sda )
290 {
291 scrollDeltaAmount = sda;
292 }
293
value()294 float Widget::value()
295 {
296 if( vm == VALUE_FLOAT_0_1 )
297 return value_;
298
299 return (value_ * valueIntRange) + valueIntBase;
300 }
301
value(float v)302 void Widget::value( float v )
303 {
304 if( vm == VALUE_INT ) {
305 //value_ * valueIntRange + valueIntBase;
306 float tmp = (v-valueIntBase) / float(valueIntRange);
307 //AVTK_DEV("VALUE_INT input %f, internal value %f\n", v, tmp );
308 v = tmp;
309 }
310
311 if( v > 1.0 ) v = 1.0;
312 if( v < 0.0 ) v = 0.0;
313
314 value_ = v;
315
316 //AVTK_DEV("Widget %s value() %f\n", label_.c_str(), v );
317 ui->redraw();
318 }
319
defaultValue(float dv)320 void Widget::defaultValue( float dv )
321 {
322 // store new default value as both default and audition
323 defaultValue_ = dv;
324 auditionValue_ = dv;
325 }
326
touches(int inx,int iny)327 bool Widget::touches( int inx, int iny )
328 {
329 return ( inx >= x_ && inx <= x_ + w_ && iny >= y_ && iny <= y_ + h_);
330 }
331
clickMode(ClickMode c)332 void Widget::clickMode( ClickMode c )
333 {
334 cm = c;
335 #ifdef AVTK_DEBUG
336 //AVTK_DEV("Widget %s clickMode %i, %i\n", label_.c_str(), cm, c);
337 #endif // AVTK_DEBUG
338 }
339
rClickMode(RClickMode r)340 void Widget::rClickMode( RClickMode r )
341 {
342 rcm = r;
343 }
344
visible(bool v)345 void Widget::visible( bool v )
346 {
347 visible_ = v;
348 }
349
addToGroup(Group * p,int gin)350 void Widget::addToGroup( Group* p, int gin )
351 {
352 AVTK_DEV("%s adding %s to %s\n", __PRETTY_FUNCTION__, label(), p->label() );
353
354 groupChild = true;
355 parent_ = p;
356 groupItemNumber_ = gin;
357 }
358
dragMode(DragMode d)359 void Widget::dragMode( DragMode d )
360 {
361 dm = d;
362 }
363
~Widget()364 Widget::~Widget()
365 {
366 #ifdef AVTK_DEBUG
367 //widgetCounter--;
368 //AVTK_DEV("widgetCounter = %i\n", widgetCounter );
369 #endif
370
371 #ifdef AVTK_DEBUG_DTOR
372 AVTK_DEV("%s %s\n", __PRETTY_FUNCTION__, label() );
373 #endif // AVTK_DEBUG
374 }
375
376 }; // Avtk
377
378