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 <osgUI/TabWidget>
16 #include <osgText/String>
17 #include <osgText/Font>
18 #include <osgText/Text>
19 #include <osg/Notify>
20 #include <osg/ValueObject>
21 #include <osg/io_utils>
22 
23 using namespace osgUI;
24 
TabWidget()25 TabWidget::TabWidget():
26     _currentIndex(0)
27 {
28 }
29 
TabWidget(const osgUI::TabWidget & tabwidget,const osg::CopyOp & copyop)30 TabWidget::TabWidget(const osgUI::TabWidget& tabwidget, const osg::CopyOp& copyop):
31     Widget(tabwidget, copyop),
32     _tabs(tabwidget._tabs)
33 {
34 }
35 
handleImplementation(osgGA::EventVisitor * ev,osgGA::Event * event)36 bool TabWidget::handleImplementation(osgGA::EventVisitor* ev, osgGA::Event* event)
37 {
38     osgGA::GUIEventAdapter* ea = event->asGUIEventAdapter();
39     if (!ea) return false;
40 
41     osgGA::GUIActionAdapter* aa = ev ? ev->getActionAdapter() : 0;
42     if (!aa) return false;
43 
44     if (!getHasEventFocus()) return false;
45 
46     unsigned int tabHeaderContainsPointer = _tabs.size();
47 
48     // test if active tab header contains pointer
49     {
50         osg::NodePath nodePath = ev->getNodePath();
51         nodePath.push_back(_activeHeaderSwitch.get());
52 
53         osgUtil::LineSegmentIntersector::Intersections intersections;
54         if (aa->computeIntersections(*ea, nodePath, intersections))
55         {
56             tabHeaderContainsPointer = _currentIndex;
57         }
58     }
59 
60     // test if inactive tab header contains pointer
61     {
62         osg::NodePath nodePath = ev->getNodePath();
63         nodePath.push_back(_inactiveHeaderSwitch.get());
64 
65         osgUtil::LineSegmentIntersector::Intersections intersections;
66         if (aa->computeIntersections(*ea, nodePath, intersections))
67         {
68             const osgUtil::LineSegmentIntersector::Intersection& Intersection = *intersections.begin();
69             for(osg::NodePath::const_iterator itr = Intersection.nodePath.begin();
70                 itr != Intersection.nodePath.end();
71                 ++itr)
72             {
73                 if ((*itr)->getUserValue("index",tabHeaderContainsPointer)) break;
74             }
75         }
76     }
77 
78     if (tabHeaderContainsPointer>=_tabs.size()) return false;
79 
80     switch(ea->getEventType())
81     {
82         case(osgGA::GUIEventAdapter::SCROLL):
83             if (ea->getScrollingMotion()==osgGA::GUIEventAdapter::SCROLL_DOWN)
84             {
85                 if (getCurrentIndex()<_tabs.size()-1) setCurrentIndex(getCurrentIndex()+1);
86                 return true;
87             }
88             else if (ea->getScrollingMotion()==osgGA::GUIEventAdapter::SCROLL_UP)
89             {
90                 if (getCurrentIndex()>0) setCurrentIndex(getCurrentIndex()-1);
91                 return true;
92             }
93             break;
94 
95         case(osgGA::GUIEventAdapter::KEYDOWN):
96             if (ea->getKey()==osgGA::GUIEventAdapter::KEY_Down || ea->getKey()==osgGA::GUIEventAdapter::KEY_Right )
97             {
98                 if (getCurrentIndex()<_tabs.size()-1) setCurrentIndex(getCurrentIndex()+1);
99                 return true;
100             }
101             else if (ea->getKey()==osgGA::GUIEventAdapter::KEY_Up || ea->getKey()==osgGA::GUIEventAdapter::KEY_Left )
102             {
103                 if (getCurrentIndex()>0) setCurrentIndex(getCurrentIndex()-1);
104                 return true;
105             }
106 
107             break;
108 
109         case(osgGA::GUIEventAdapter::RELEASE):
110         {
111             setCurrentIndex(tabHeaderContainsPointer);
112             return true;
113 
114             break;
115         }
116         default:
117             break;
118     }
119 
120     return false;
121 }
122 
enterImplementation()123 void TabWidget::enterImplementation()
124 {
125     OSG_NOTICE<<"TabWidget enter"<<std::endl;
126 }
127 
128 
leaveImplementation()129 void TabWidget::leaveImplementation()
130 {
131     OSG_NOTICE<<"TabWidget leave"<<std::endl;
132 }
133 
setCurrentIndex(unsigned int i)134 void TabWidget::setCurrentIndex(unsigned int i)
135 {
136     // OSG_NOTICE << "TabWidget::setCurrentIndex("<<i<<")"<<std::endl;
137     if (_currentIndex==i) return;
138 
139     _currentIndex = i;
140     _activateWidgets();
141 
142     currrentIndexChanged(_currentIndex);
143 }
144 
currrentIndexChanged(unsigned int i)145 void TabWidget::currrentIndexChanged(unsigned int i)
146 {
147     osg::CallbackObject* co = getCallbackObject(this, "currentIndexChanged");
148     if (co)
149     {
150         osg::Parameters inputParameters, outputParameters;
151         inputParameters.push_back(new osg::UIntValueObject("index",i));
152         if (co->run(this, inputParameters, outputParameters))
153         {
154             return;
155         }
156     }
157     currentIndexChangedImplementation(i);
158 }
159 
currentIndexChangedImplementation(unsigned int i)160 void TabWidget::currentIndexChangedImplementation(unsigned int i)
161 {
162   OSG_NOTICE<<"TabWidget::currentIndexChangedImplementation("<<i<<")"<<std::endl;
163 }
164 
165 
166 
createGraphicsImplementation()167 void TabWidget::createGraphicsImplementation()
168 {
169     Style* style = (getStyle()!=0) ? getStyle() : Style::instance().get();
170 
171     // bool requiresFrame = (getFrameSettings() && getFrameSettings()->getShape()!=osgUI::FrameSettings::NO_FRAME);
172 
173     _inactiveHeaderSwitch = new osg::Switch;
174     _activeHeaderSwitch = new osg::Switch;
175     _tabWidgetSwitch = new osg::Switch;
176 
177     float active = 0.84f;
178     float inactive = 0.80f;
179     float titleHeight = 10.0f;
180     float characterWidth = titleHeight*0.7f;
181     float margin = titleHeight*0.2f;
182 
183     unsigned int tabIndex = 0;
184 
185     osg::BoundingBox centerExtents = _extents;
186     centerExtents.yMax() -= titleHeight;
187 
188     osg::ref_ptr<osgUI::AlignmentSettings> textAlignment = new osgUI::AlignmentSettings(osgUI::AlignmentSettings::LEFT_CENTER);
189 
190     osg::ref_ptr<FrameSettings> fs = getFrameSettings();
191     if (!fs)
192     {
193         fs = new osgUI::FrameSettings;
194         fs->setShadow(osgUI::FrameSettings::RAISED);
195         fs->setLineWidth(1.0f);
196     }
197 
198     osg::Vec4 dialogBackgroundColor(active,active,active,1.0);
199     osg::Vec4 inactiveColor(inactive, inactive, inactive, 1.0f);
200 
201     float xPos = _extents.xMin();
202     float yMin = _extents.yMax() - titleHeight - fs->getLineWidth();
203     float yMax = _extents.yMax();
204     float zMin = _extents.zMin();
205     float zMax = _extents.zMax();
206 
207     for(Tabs::iterator itr = _tabs.begin();
208         itr != _tabs.end();
209         ++itr, ++tabIndex)
210     {
211         Tab* tab = itr->get();
212 
213         float width = tab->getText().size() * characterWidth;
214 
215         osg::BoundingBox headerExtents( xPos, yMin, zMin, xPos+width, yMax, zMax);
216         osg::BoundingBox textExtents( xPos+margin, yMin, zMin, xPos+width-margin, yMax, zMax);
217 
218         osg::ref_ptr<osg::Node> textNode = style->createText(textExtents, textAlignment.get(), getTextSettings(), tab->getText());
219 
220         osg::ref_ptr<osgText::Text> text = dynamic_cast<osgText::Text*>(textNode.get());
221         if (text.valid()) textExtents = text->getBoundingBox();
222 
223         // adjust position of size of text.
224         float textWidth = (textExtents.xMax() - textExtents.xMin());
225 
226         headerExtents.xMax() = textExtents.xMin() + textWidth + margin;
227 
228         osg::ref_ptr<osg::Node> inactive_panel = _createTabHeader(headerExtents, fs.get(), inactiveColor);
229         osg::ref_ptr<osg::Node> selected_panel = _createTabHeader(headerExtents, fs.get(), dialogBackgroundColor);
230 
231         osg::ref_ptr<osg::Group> selected_group = new osg::Group;
232         selected_group->setUserValue("index",tabIndex);
233         selected_group->addChild(selected_panel.get());
234         selected_group->addChild(text.get());
235 
236         osg::ref_ptr<osg::Group> inactive_group = new osg::Group;
237         inactive_group->setUserValue("index",tabIndex);
238         inactive_group->addChild(inactive_panel.get());
239         inactive_group->addChild(text.get());
240 
241         _inactiveHeaderSwitch->addChild(inactive_group.get());
242         _activeHeaderSwitch->addChild(selected_group.get());
243         _tabWidgetSwitch->addChild(tab->getWidget());
244 
245         xPos += textWidth+3.0*margin;
246 
247     }
248 
249     setGraphicsSubgraph(-4, _inactiveHeaderSwitch.get());
250 
251     osg::ref_ptr<osg::Node> backgroundPanel = _createTabFrame( centerExtents, fs.get(), dialogBackgroundColor);
252     setGraphicsSubgraph(-3, backgroundPanel.get());
253 
254     setGraphicsSubgraph(-2, _activeHeaderSwitch.get());
255     setGraphicsSubgraph(-1, _tabWidgetSwitch.get());
256 
257     _activateWidgets();
258 }
259 
_activateWidgets()260 void TabWidget::_activateWidgets()
261 {
262     if (_graphicsInitialized && _currentIndex<_tabs.size())
263     {
264         OSG_NOTICE<<"Activating widget "<<_currentIndex<<std::endl;
265 
266         _inactiveHeaderSwitch->setAllChildrenOn();
267         _inactiveHeaderSwitch->setValue(_currentIndex, false);
268 
269         _activeHeaderSwitch->setAllChildrenOff();
270         _activeHeaderSwitch->setValue(_currentIndex, true);
271 
272         _tabWidgetSwitch->setAllChildrenOff();
273         _tabWidgetSwitch->setValue(_currentIndex, true);
274     }
275 }
276 
_createTabFrame(const osg::BoundingBox & extents,osgUI::FrameSettings * fs,const osg::Vec4 & color)277 osg::Node* TabWidget::_createTabFrame(const osg::BoundingBox& extents, osgUI::FrameSettings* fs, const osg::Vec4& color)
278 {
279     Style* style = (getStyle()!=0) ? getStyle() : Style::instance().get();
280     osg::ref_ptr<osg::Group> group = new osg::Group;
281 
282     group->addChild( style->createPanel(extents, color) );
283     group->addChild( style->createFrame(extents, fs, color) );
284 
285     return group.release();
286 }
287 
_createTabHeader(const osg::BoundingBox & extents,osgUI::FrameSettings * frameSettings,const osg::Vec4 & color)288 osg::Node* TabWidget::_createTabHeader(const osg::BoundingBox& extents, osgUI::FrameSettings* frameSettings, const osg::Vec4& color)
289 {
290     osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
291     geometry->setName("Frame");
292 
293     float topScale = 1.0f;
294     float bottomScale = 1.0f;
295     float leftScale = 1.0f;
296     float rightScale = 1.0f;
297 
298     if (frameSettings)
299     {
300         switch(frameSettings->getShadow())
301         {
302             case(FrameSettings::PLAIN):
303                 // default settings are appropriate for PLAIN
304                 break;
305             case(FrameSettings::SUNKEN):
306                 topScale = 0.6f;
307                 bottomScale = 1.2f;
308                 leftScale = 0.8f;
309                 rightScale = 0.8f;
310                 break;
311             case(FrameSettings::RAISED):
312                 topScale = 1.2f;
313                 bottomScale = 0.6f;
314                 leftScale = 0.8f;
315                 rightScale = 0.8f;
316                 break;
317         }
318     }
319 
320     osg::Vec4 topColor(osg::minimum(color.r()*topScale,1.0f), osg::minimum(color.g()*topScale,1.0f), osg::minimum(color.b()*topScale,1.0f), color.a());
321     osg::Vec4 bottomColor(osg::minimum(color.r()*bottomScale,1.0f), osg::minimum(color.g()*bottomScale,1.0f), osg::minimum(color.b()*bottomScale,1.0f), color.a());
322     osg::Vec4 leftColor(osg::minimum(color.r()*leftScale,1.0f), osg::minimum(color.g()*leftScale,1.0f), osg::minimum(color.b()*leftScale,1.0f), color.a());
323     osg::Vec4 rightColor(osg::minimum(color.r()*rightScale,1.0f), osg::minimum(color.g()*rightScale,1.0f), osg::minimum(color.b()*rightScale,1.0f), color.a());
324 
325     float lineWidth = frameSettings ? frameSettings->getLineWidth() : 1.0f;
326 
327     osg::Vec3 outerBottomLeft(extents.xMin(), extents.yMin()+lineWidth, extents.zMin());
328     osg::Vec3 outerBottomRight(extents.xMax(), extents.yMin()+lineWidth, extents.zMin());
329     osg::Vec3 outerTopLeft(extents.xMin(), extents.yMax(), extents.zMin());
330     osg::Vec3 outerTopRight(extents.xMax(), extents.yMax(), extents.zMin());
331 
332     osg::Vec3 innerBottomLeft(extents.xMin()+lineWidth, extents.yMin(), extents.zMin());
333     osg::Vec3 innerBottomRight(extents.xMax()-lineWidth, extents.yMin(), extents.zMin());
334     osg::Vec3 innerTopLeft(extents.xMin()+lineWidth, extents.yMax()-lineWidth, extents.zMin());
335     osg::Vec3 innerTopRight(extents.xMax()-lineWidth, extents.yMax()-lineWidth, extents.zMin());
336 
337     osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
338     geometry->setVertexArray(vertices.get());
339 
340     vertices->push_back( outerBottomLeft );   // 0
341     vertices->push_back( outerBottomRight );  // 1
342     vertices->push_back( outerTopLeft );      // 2
343     vertices->push_back( outerTopRight );     // 3
344 
345     vertices->push_back( innerBottomLeft );  // 4
346     vertices->push_back( innerBottomRight ); // 5
347     vertices->push_back( innerTopLeft );     // 6
348     vertices->push_back( innerTopRight );    // 7
349 
350     osg::ref_ptr<osg::Vec4Array> colours = new osg::Vec4Array;
351     geometry->setColorArray(colours.get(), osg::Array::BIND_PER_PRIMITIVE_SET);
352 
353     // bottom
354     {
355         colours->push_back(bottomColor);
356 
357         osg::ref_ptr<osg::DrawElementsUShort> primitives = new osg::DrawElementsUShort(GL_TRIANGLE_STRIP);
358         geometry->addPrimitiveSet(primitives.get());
359         primitives->push_back(4);
360         primitives->push_back(0);
361         primitives->push_back(5);
362         primitives->push_back(1);
363     }
364 
365     // top
366     {
367         colours->push_back(topColor);
368 
369         osg::ref_ptr<osg::DrawElementsUShort> primitives = new osg::DrawElementsUShort(GL_TRIANGLE_STRIP);
370         geometry->addPrimitiveSet(primitives.get());
371         primitives->push_back(2);
372         primitives->push_back(6);
373         primitives->push_back(3);
374         primitives->push_back(7);
375     }
376 
377     // left
378     {
379         colours->push_back(leftColor);
380 
381         osg::ref_ptr<osg::DrawElementsUShort> primitives = new osg::DrawElementsUShort(GL_TRIANGLE_STRIP);
382         geometry->addPrimitiveSet(primitives.get());
383         primitives->push_back(2);
384         primitives->push_back(0);
385         primitives->push_back(6);
386         primitives->push_back(4);
387     }
388 
389     // right
390     {
391         colours->push_back(rightColor);
392 
393         osg::ref_ptr<osg::DrawElementsUShort> primitives = new osg::DrawElementsUShort(GL_TRIANGLE_STRIP);
394         geometry->addPrimitiveSet(primitives.get());
395         primitives->push_back(7);
396         primitives->push_back(5);
397         primitives->push_back(3);
398         primitives->push_back(1);
399     }
400 
401 
402     // center
403     {
404         colours->push_back(color);
405 
406         osg::ref_ptr<osg::DrawElementsUShort> primitives = new osg::DrawElementsUShort(GL_TRIANGLE_STRIP);
407         geometry->addPrimitiveSet(primitives.get());
408         primitives->push_back(6);
409         primitives->push_back(4);
410         primitives->push_back(7);
411         primitives->push_back(5);
412     }
413 
414     return geometry.release();
415 }
416