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