1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2014 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version. The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * OpenSceneGraph Public License for more details.
12 */
13
14
15 #include <osg/Geode>
16 #include <osg/ScriptEngine>
17 #include <osg/Geometry>
18 #include <osg/MatrixTransform>
19 #include <osg/ValueObject>
20 #include <osg/io_utils>
21
22 #include <osgUI/Widget>
23 #include <osgGA/EventVisitor>
24 #include <osgGA/GUIActionAdapter>
25 #include <osgViewer/View>
26
27 #include <algorithm>
28
29 using namespace osgUI;
30
Widget()31 Widget::Widget():
32 _focusBehaviour(FOCUS_FOLLOWS_POINTER),
33 _hasEventFocus(false),
34 _graphicsInitialized(false),
35 _autoFillBackground(false),
36 _visible(true),
37 _enabled(true)
38 {
39 setNumChildrenRequiringEventTraversal(1);
40 }
41
Widget(const Widget & widget,const osg::CopyOp & copyop)42 Widget::Widget(const Widget& widget, const osg::CopyOp& copyop):
43 osg::Group(),
44 _focusBehaviour(widget._focusBehaviour),
45 _hasEventFocus(false),
46 _graphicsInitialized(false),
47 _alignmentSettings(osg::clone(widget._alignmentSettings.get(), copyop)),
48 _frameSettings(osg::clone(widget._frameSettings.get(), copyop)),
49 _textSettings(osg::clone(widget._textSettings.get(), copyop)),
50 _autoFillBackground(widget._autoFillBackground),
51 _visible(widget._visible),
52 _enabled(widget._enabled)
53 {
54 setNumChildrenRequiringEventTraversal(1);
55 }
56
setExtents(const osg::BoundingBoxf & bb)57 void Widget::setExtents(const osg::BoundingBoxf& bb)
58 {
59 _extents = bb;
60 }
61
updateFocus(osg::NodeVisitor & nv)62 void Widget::updateFocus(osg::NodeVisitor& nv)
63 {
64 osgGA::EventVisitor* ev = dynamic_cast<osgGA::EventVisitor*>(&nv);
65 osgGA::GUIActionAdapter* aa = ev ? ev->getActionAdapter() : 0;
66 if (ev && aa)
67 {
68 // OSG_NOTICE<<"updateFocus"<<std::endl;
69
70 osgGA::EventQueue::Events& events = ev->getEvents();
71 for(osgGA::EventQueue::Events::iterator itr = events.begin();
72 itr != events.end();
73 ++itr)
74 {
75 osgGA::GUIEventAdapter* ea = (*itr)->asGUIEventAdapter();
76 if (ea)
77 {
78 int numButtonsPressed = 0;
79 if (ea->getEventType()==osgGA::GUIEventAdapter::PUSH)
80 {
81 if (ea->getButtonMask()&osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON) ++numButtonsPressed;
82 if (ea->getButtonMask()&osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON) ++numButtonsPressed;
83 if (ea->getButtonMask()&osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON) ++numButtonsPressed;
84 }
85
86 bool previousFocus = _hasEventFocus;
87 if (_focusBehaviour==CLICK_TO_FOCUS)
88 {
89 if (ea->getEventType()==osgGA::GUIEventAdapter::PUSH)
90 {
91 if (numButtonsPressed==1)
92 {
93 osg::Vec3d intersection;
94 bool withinWidget = computeExtentsPositionInLocalCoordinates(ev, ea, intersection);
95
96 if (withinWidget) _hasEventFocus = true;
97 else _hasEventFocus = false;
98 }
99 }
100 }
101 else if (_focusBehaviour==FOCUS_FOLLOWS_POINTER)
102 {
103 bool checkWithinWidget = false;
104 if (!_hasEventFocus)
105 {
106 checkWithinWidget = (ea->getEventType()!=osgGA::GUIEventAdapter::FRAME) && ea->getButtonMask()==0;
107 }
108 else
109 {
110 // if mouse move or mouse release check to see if mouse still within widget to retain focus
111 if (ea->getEventType()==osgGA::GUIEventAdapter::MOVE)
112 {
113 checkWithinWidget = true;
114 }
115 else if (ea->getEventType()==osgGA::GUIEventAdapter::RELEASE)
116 {
117 // if no buttons pressed then check
118 if (ea->getButtonMask()==0) checkWithinWidget = true;
119 }
120 }
121
122 if (checkWithinWidget)
123 {
124 osg::Vec3d intersection;
125 bool withinWidget = computeExtentsPositionInLocalCoordinates(ev, ea, intersection);
126
127 _hasEventFocus = withinWidget;
128 }
129 }
130
131 if (_hasEventFocus && (ea->getEventType()==osgGA::GUIEventAdapter::PUSH || ea->getEventType()==osgGA::GUIEventAdapter::SCROLL) )
132 {
133 osgViewer::View* view = dynamic_cast<osgViewer::View*>(aa);
134 if (view && view->getCameraManipulator())
135 {
136 view->getCameraManipulator()->finishAnimation();
137 view->requestContinuousUpdate( false );
138 }
139 }
140
141
142 if (previousFocus != _hasEventFocus)
143 {
144 if (_hasEventFocus)
145 {
146 enter();
147
148 }
149 else
150 {
151 leave();
152 }
153 }
154
155 }
156 }
157 }
158 }
159
setHasEventFocus(bool focus)160 void Widget::setHasEventFocus(bool focus)
161 {
162 if (_hasEventFocus == focus) return;
163
164 _hasEventFocus = focus;
165
166 if (_hasEventFocus) enter();
167 else leave();
168 }
169
getHasEventFocus() const170 bool Widget::getHasEventFocus() const
171 {
172 return _hasEventFocus;
173 }
174
enter()175 void Widget::enter()
176 {
177 if (!runCallbacks("enter")) enterImplementation();
178 }
179
enterImplementation()180 void Widget::enterImplementation()
181 {
182 OSG_NOTICE<<"Widget::enter()"<<std::endl;
183 }
184
leave()185 void Widget::leave()
186 {
187 if (!runCallbacks("leave")) leaveImplementation();
188 }
189
leaveImplementation()190 void Widget::leaveImplementation()
191 {
192 OSG_NOTICE<<"Widget::leave()"<<std::endl;
193 }
194
traverse(osg::NodeVisitor & nv)195 void Widget::traverse(osg::NodeVisitor& nv)
196 {
197 if (nv.referenceCount()!=0)
198 {
199 osg::Parameters inputParameters, outputParameters;
200 inputParameters.push_back(&nv);
201 if (runCallbacks("traverse",inputParameters, outputParameters)) return;
202 }
203
204 traverseImplementation(nv);
205
206 }
207
traverseImplementation(osg::NodeVisitor & nv)208 void Widget::traverseImplementation(osg::NodeVisitor& nv)
209 {
210 if (!_graphicsInitialized && nv.getVisitorType()!=osg::NodeVisitor::CULL_VISITOR) createGraphics();
211
212 osgGA::EventVisitor* ev = dynamic_cast<osgGA::EventVisitor*>(&nv);
213 if (ev)
214 {
215 if (_visible && _enabled)
216 {
217
218 updateFocus(nv);
219
220 // OSG_NOTICE<<"EventTraversal getHasEventFocus()="<<getHasEventFocus()<<std::endl;
221
222 // signify that event has been taken by widget with focus
223
224 bool widgetsWithFocusSetHandled = getHasEventFocus();
225
226 osgGA::EventQueue::Events& events = ev->getEvents();
227 for(osgGA::EventQueue::Events::iterator itr = events.begin();
228 itr != events.end();
229 ++itr)
230 {
231 if (handle(ev, itr->get()) || widgetsWithFocusSetHandled)
232 {
233 (*itr)->setHandled(true);
234 ev->setEventHandled(true);
235 }
236 }
237
238 GraphicsSubgraphMap::iterator itr = _graphicsSubgraphMap.begin();
239 while(itr!= _graphicsSubgraphMap.end() && itr->first<=0)
240 {
241 itr->second->accept(nv);
242 ++itr;
243 }
244
245 osg::Group::traverse(nv);
246
247 while(itr!= _graphicsSubgraphMap.end())
248 {
249 itr->second->accept(nv);
250 ++itr;
251 }
252
253 }
254 }
255 else if (_visible ||
256 (nv.getVisitorType()!=osg::NodeVisitor::UPDATE_VISITOR && nv.getVisitorType()!=osg::NodeVisitor::CULL_VISITOR && nv.getVisitorType()!=osg::NodeVisitor::INTERSECTION_VISITOR) )
257 {
258 osgUtil::CullVisitor* cv = (nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR) ? dynamic_cast<osgUtil::CullVisitor*>(&nv) : 0;
259 if (cv && _widgetStateSet.valid()) cv->pushStateSet(_widgetStateSet.get());
260
261 GraphicsSubgraphMap::iterator itr = _graphicsSubgraphMap.begin();
262 while(itr!= _graphicsSubgraphMap.end() && itr->first<=0)
263 {
264 itr->second->accept(nv);
265 ++itr;
266 }
267
268 Group::traverse(nv);
269
270 while(itr!= _graphicsSubgraphMap.end())
271 {
272 itr->second->accept(nv);
273 ++itr;
274 }
275
276 if (cv && _widgetStateSet.valid()) cv->popStateSet();
277 }
278 }
279
handle(osgGA::EventVisitor * ev,osgGA::Event * event)280 bool Widget::handle(osgGA::EventVisitor* ev, osgGA::Event* event)
281 {
282 // currently lua scripting takes a ref count so messes up handling of NodeVisitor's created on stack,
283 // so don't attempt to call the sctipt.
284 if (ev->referenceCount()!=0)
285 {
286 osg::Parameters inputParameters, outputParameters;
287 inputParameters.push_back(ev);
288 inputParameters.push_back(event);
289 if (runCallbacks("handle",inputParameters, outputParameters))
290 {
291 if (outputParameters.size()>=1)
292 {
293 osg::BoolValueObject* bvo = dynamic_cast<osg::BoolValueObject*>(outputParameters[0].get());
294 return bvo ? bvo->getValue() : false;
295 }
296 }
297 }
298
299 return handleImplementation(ev, event);
300 }
301
handleImplementation(osgGA::EventVisitor * ev,osgGA::Event * event)302 bool Widget::handleImplementation(osgGA::EventVisitor* ev, osgGA::Event* event)
303 {
304 return false;
305 }
306
dirty()307 void Widget::dirty()
308 {
309 _graphicsInitialized = false;
310 }
311
createGraphics()312 void Widget::createGraphics()
313 {
314 if (!runCallbacks("createGraphics")) createGraphicsImplementation();
315 }
316
createGraphicsImplementation()317 void Widget::createGraphicsImplementation()
318 {
319 _graphicsInitialized = true;
320 }
321
computeBound() const322 osg::BoundingSphere Widget::computeBound() const
323 {
324 osg::BoundingSphere bs;
325 if (_extents.valid()) bs.expandBy(_extents);
326 bs.expandBy(Group::computeBound());
327 return bs;
328 }
329
resizeGLObjectBuffers(unsigned int maxSize)330 void Widget::resizeGLObjectBuffers(unsigned int maxSize)
331 {
332 for(GraphicsSubgraphMap::iterator itr = _graphicsSubgraphMap.begin();
333 itr != _graphicsSubgraphMap.end();
334 ++itr)
335 {
336 itr->second->resizeGLObjectBuffers(maxSize);
337 }
338
339 Group::resizeGLObjectBuffers(maxSize);
340 }
341
342
releaseGLObjects(osg::State * state) const343 void Widget::releaseGLObjects(osg::State* state) const
344 {
345 for(GraphicsSubgraphMap::const_iterator itr = _graphicsSubgraphMap.begin();
346 itr != _graphicsSubgraphMap.end();
347 ++itr)
348 {
349 itr->second->releaseGLObjects(state);
350 }
351
352 Group::releaseGLObjects(state);
353
354
355 }
356
computePositionInLocalCoordinates(osgGA::EventVisitor * ev,osgGA::GUIEventAdapter * event,osg::Vec3d & localPosition) const357 bool Widget::computePositionInLocalCoordinates(osgGA::EventVisitor* ev, osgGA::GUIEventAdapter* event, osg::Vec3d& localPosition) const
358 {
359 osgGA::GUIActionAdapter* aa = ev ? ev->getActionAdapter() : 0;
360 osgUtil::LineSegmentIntersector::Intersections intersections;
361 if (aa && aa->computeIntersections(*event, ev->getNodePath(), intersections))
362 {
363 localPosition = intersections.begin()->getLocalIntersectPoint();
364
365 return (_extents.contains(localPosition, 1e-6));
366 }
367 else
368 {
369 return false;
370 }
371 }
372
373 struct SortTraversalOrder
374 {
operator ()SortTraversalOrder375 bool operator() (const osgUtil::LineSegmentIntersector::Intersection* lhs, const osgUtil::LineSegmentIntersector::Intersection* rhs) const
376 {
377 double epsilon = 1e-6;
378 if (lhs->ratio > (rhs->ratio+epsilon)) return true;
379 if (lhs->ratio < (rhs->ratio-epsilon)) return false;
380
381 const osg::NodePath& np_lhs = lhs->nodePath;
382 const osg::NodePath& np_rhs = rhs->nodePath;
383
384 osg::NodePath::const_iterator itr_lhs = np_lhs.begin();
385 osg::NodePath::const_iterator end_lhs = np_lhs.end();
386 osg::NodePath::const_iterator itr_rhs = np_rhs.begin();
387 osg::NodePath::const_iterator end_rhs = np_rhs.end();
388 const osg::Group* parent = 0;
389
390 while(itr_lhs!=end_lhs && itr_rhs!=end_rhs)
391 {
392 if (*itr_lhs == *itr_rhs)
393 {
394 parent = (*itr_lhs)->asGroup();
395 ++itr_lhs;
396 ++itr_rhs;
397 }
398 else if (parent==0)
399 {
400 OSG_NOTICE<<"SortTraversalOrder::operator() NodePath has no parent, just have to use default less than operator for Intersection"<<std::endl;
401 return (*lhs)<(*rhs);
402 }
403 else
404 {
405 const osgUI::Widget* widget = dynamic_cast<const osgUI::Widget*>(parent);
406
407 unsigned int lhs_index = parent->getChildIndex(*itr_lhs);
408 double lhs_sort_value = static_cast<double>(lhs_index)/static_cast<double>(parent->getNumChildren());
409
410 unsigned int rhs_index = parent->getChildIndex(*itr_rhs);
411 double rhs_sort_value = (static_cast<double>(rhs_index)+epsilon)/static_cast<double>(parent->getNumChildren());
412
413 if (widget)
414 {
415 const osgUI::Widget::GraphicsSubgraphMap& gsm = widget->getGraphicsSubgraphMap();
416 for(osgUI::Widget::GraphicsSubgraphMap::const_iterator itr=gsm.begin();
417 itr!=gsm.end();
418 ++itr)
419 {
420 if (itr->second==(*itr_lhs)) lhs_sort_value = itr->first;
421 if (itr->second==(*itr_rhs)) rhs_sort_value = itr->first;
422 }
423 }
424
425 if (lhs_sort_value>rhs_sort_value) return true;
426 if (lhs_sort_value<rhs_sort_value) return false;
427
428 }
429 }
430
431 return false;
432 }
433 };
434
computeIntersections(osgGA::EventVisitor * ev,osgGA::GUIEventAdapter * event,Intersections & intersections,osg::Node::NodeMask traversalMask) const435 bool Widget::computeIntersections(osgGA::EventVisitor* ev, osgGA::GUIEventAdapter* event, Intersections& intersections, osg::Node::NodeMask traversalMask) const
436 {
437 osgGA::GUIActionAdapter* aa = ev ? ev->getActionAdapter() : 0;
438 osgUtil::LineSegmentIntersector::Intersections source_intersections;
439 if (aa && aa->computeIntersections(*event, ev->getNodePath(), source_intersections, traversalMask))
440 {
441 typedef std::vector<const osgUtil::LineSegmentIntersector::Intersection*> IntersectionPointerList;
442 IntersectionPointerList intersectionsToSort;
443
444 // populate the temporay vector of poiners to the original intersection pointers.
445 for(osgUtil::LineSegmentIntersector::Intersections::iterator itr = source_intersections.begin();
446 itr != source_intersections.end();
447 ++itr)
448 {
449 if (itr->drawable->getName()!="DepthSetPanel")
450 {
451 intersectionsToSort.push_back(&(*itr));
452 }
453 }
454
455 // sort the pointer list into order based on child traversal order, to be consistent with osgUI rendering order.
456 std::sort(intersectionsToSort.begin(), intersectionsToSort.end(), SortTraversalOrder());
457
458 // copy the pointers to final Intersection container
459 for(IntersectionPointerList::iterator itr = intersectionsToSort.begin();
460 itr != intersectionsToSort.end();
461 ++itr)
462 {
463 intersections.push_back(*(*itr));
464 }
465 return true;
466 }
467 return false;
468 }
469
470
computeExtentsPositionInLocalCoordinates(osgGA::EventVisitor * ev,osgGA::GUIEventAdapter * event,osg::Vec3d & localPosition,bool withinExtents) const471 bool Widget::computeExtentsPositionInLocalCoordinates(osgGA::EventVisitor* ev, osgGA::GUIEventAdapter* event, osg::Vec3d& localPosition, bool withinExtents) const
472 {
473 //OSG_NOTICE<<"Widget::computeExtentsPositionInLocalCoordinates(()"<<std::endl;
474 const osg::Camera* camera = 0;
475 double x=0.0, y=0.0;
476 if (event->getNumPointerData()>=1)
477 {
478 const osgGA::PointerData* pd = event->getPointerData(event->getNumPointerData()-1);
479 camera = dynamic_cast<const osg::Camera*>(pd->object.get());
480 if (camera)
481 {
482 x = pd->getXnormalized();
483 y = pd->getYnormalized();
484 }
485 }
486 //OSG_NOTICE<<" camera = "<<camera<<", x = "<<x<<", y="<<y<<std::endl;
487 if (!camera) return false;
488
489 const osg::NodePath& nodePath = ev->getNodePath();
490
491 osg::Matrixd matrix;
492 if (nodePath.size()>1)
493 {
494 osg::NodePath prunedNodePath(nodePath.begin(),nodePath.end()-1);
495 matrix = osg::computeLocalToWorld(prunedNodePath);
496 }
497
498 matrix.postMult(camera->getViewMatrix());
499 matrix.postMult(camera->getProjectionMatrix());
500
501 double zNear = -1.0;
502 double zFar = 1.0;
503
504 osg::Matrixd inverse;
505 inverse.invert(matrix);
506
507 osg::Vec3d startVertex = osg::Vec3d(x,y,zNear) * inverse;
508 osg::Vec3d endVertex = osg::Vec3d(x,y,zFar) * inverse;
509
510 //OSG_NOTICE<<" startVertex("<<startVertex<<"(, endVertex("<<endVertex<<")"<<std::endl;
511
512
513 osg::Plane plane(0.0, 0.0, 1.0, _extents.zMax());
514
515 //OSG_NOTICE<<" plane("<<plane<<")"<<std::endl;
516 double ds = plane.distance(startVertex);
517 double de = plane.distance(endVertex);
518 if (ds*de>0.0) return false;
519
520 double r = ds/(ds-de);
521 //OSG_NOTICE<<" r = "<<r<<std::endl;
522
523 osg::Vec3d intersection = startVertex + (endVertex-startVertex)*r;
524 //OSG_NOTICE<<" intersection = "<<intersection<<std::endl;
525 localPosition = intersection;
526
527 return withinExtents ? _extents.contains(localPosition, 1e-6) : true;
528 }
529