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