root/OpenSceneGraph/trunk/src/osgWidget/WindowManager.cpp @ 13041

Revision 13041, 18.3 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
Line 
1// -*-c++-*- osgWidget - Code by: Jeremy Moles (cubicool) 2007-2008
2// $Id: WindowManager.cpp 66 2008-07-14 21:54:09Z cubicool $
3
4#include <iostream>
5#include <algorithm>
6#include <osg/io_utils>
7#include <osgWidget/Types>
8#include <osgWidget/Util>
9#include <osgWidget/WindowManager>
10#include <osgWidget/Lua>
11#include <osgWidget/Python>
12#include <osgWidget/Box>
13#include <osgWidget/Label>
14
15namespace osgWidget {
16
17WindowManager::WindowManager(
18    osgViewer::View* view,
19    point_type       width,
20    point_type       height,
21    unsigned int     nodeMask,
22    unsigned int     flags
23):
24_width          (width),
25_height         (height),
26_windowWidth    (width),
27_windowHeight   (height),
28_flags          (flags),
29_nodeMask       (nodeMask),
30_view           (view),
31_lastX          (0.0f),
32_lastY          (0.0f),
33_lastEvent      (0),
34_lastPush       (0),
35_lastVertical   (PD_NONE),
36_lastHorizontal (PD_NONE),
37_focusMode      (PFM_FOCUS),
38_leftDown       (false),
39_middleDown     (false),
40_rightDown      (false),
41_scrolling      (osgGA::GUIEventAdapter::SCROLL_NONE),
42_styleManager   (new StyleManager()) {
43    _name = generateRandomName("WindowManager");
44
45    if(_flags & WM_USE_LUA) {
46        _lua = new LuaEngine(this);
47
48        if(!_lua->initialize()) warn() << "Error creating LuaEngine." << std::endl;
49    }
50
51    if(_flags & WM_USE_PYTHON) {
52        _python = new PythonEngine(this);
53
54        if(!_python->initialize()) warn() << "Error creating PythonEngine." << std::endl;
55    }
56
57    if(_flags & WM_USE_RENDERBINS) getOrCreateStateSet()->setMode(GL_DEPTH_TEST, false);
58
59    // Setup our picking debug (is debug the right word here?) Window...
60    if(_flags & WM_PICK_DEBUG) {
61        _pickWindow = new Box("PickWindow", Box::VERTICAL);
62
63        Label* label = new Label("PickLabel");
64
65        label->setFontSize(13);
66        label->setFontColor(1.0f, 1.0f, 1.0f, 1.0f);
67        label->setFont("fonts/VeraMono.ttf");
68        label->setPadding(5.0f);
69        label->setCanFill(true);
70
71        _pickWindow->getBackground()->setColor(0.0f, 0.0f, 0.0f, 0.85f);
72        _pickWindow->addWidget(label);
73        _pickWindow->setNodeMask(~_nodeMask);
74        _pickWindow->removeEventMask(EVENT_MASK_FOCUS);
75        _pickWindow->setStrata(Window::STRATA_FOREGROUND);
76
77        addChild(_pickWindow.get());
78
79        _updatePickWindow(0, 0, 0);
80    }
81
82    getOrCreateStateSet()->setMode(
83        GL_BLEND,
84        osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE
85    );
86    getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
87}
88
89WindowManager::WindowManager(const WindowManager& wm, const osg::CopyOp& co):
90    osg::Switch(wm, co),
91    _width          (wm._width),
92    _height         (wm._height),
93    _windowWidth    (wm._width),
94    _windowHeight   (wm._height),
95    _flags          (wm._flags),
96    _nodeMask       (wm._nodeMask),
97    _view           (wm._view),
98    _lastX          (0.0f),
99    _lastY          (0.0f),
100    _lastEvent      (0),
101    _lastPush       (0),
102    _lastVertical   (PD_NONE),
103    _lastHorizontal (PD_NONE),
104    _focusMode      (PFM_FOCUS),
105    _leftDown       (false),
106    _middleDown     (false),
107    _rightDown      (false),
108    _scrolling      (osgGA::GUIEventAdapter::SCROLL_NONE),
109    _styleManager   (new StyleManager())
110{
111}
112
113WindowManager::~WindowManager()
114{
115    if(_flags & WM_USE_LUA) _lua->close();
116
117    if(_flags & WM_USE_PYTHON) _python->close();
118}
119
120void WindowManager::setEventFromInterface(Event& ev, EventInterface* ei) {
121    Widget* widget = dynamic_cast<Widget*>(ei);
122    Window* window = dynamic_cast<Window*>(ei);
123
124    if(widget) {
125        ev._window = widget->getParent();
126        ev._widget = widget;
127    }
128
129    else if(window) ev._window = window;
130}
131
132bool WindowManager::_handleMousePushed(float x, float y, bool& down) {
133    down = true;
134
135    Event ev(this, EVENT_MOUSE_PUSH);
136
137    WidgetList widgetList;
138
139    if(!pickAtXY(x, y, widgetList)) return false;
140
141    ev.makeMouse(x, y);
142
143    _lastPush = getFirstEventInterface(widgetList, ev);
144
145    if(!_lastPush) return false;
146
147    // TODO: This is the old way; it didn't allow Event handler code to call grabFocus().
148    // bool handled = _lastPush->callMethodAndCallbacks(ev);
149
150    if(_focusMode != PFM_SLOPPY) {
151        if(ev._window) {
152            Window* topmostWindow = ev._window->getTopmostParent();
153
154            setFocused(topmostWindow);
155
156            if(ev._widget) topmostWindow->setFocused(ev._widget);
157        }
158
159        // If the user wants to be able to "unfocus" the last Window.
160        else if(_focusMode == PFM_UNFOCUS) setFocused(0);
161    }
162
163    return _lastPush->callMethodAndCallbacks(ev);
164}
165
166bool WindowManager::_handleMouseReleased(float x, float y, bool& down) {
167    down = false;
168
169    // If were were in a drag state, reset our boolean flag.
170    // if(_lastDrag) _lastDrag = 0;
171
172    if(!_lastPush) return false;
173
174    // By design, we can only release an EventInterface we previously pressed.
175    // Whether or not we're ON the EventInterface when the release occurs isn't important.
176    Event ev(this, EVENT_MOUSE_RELEASE);
177
178    setEventFromInterface(ev, _lastPush);
179
180    bool handled = _lastPush->callMethodAndCallbacks(ev);
181
182    _lastPush = 0;
183
184    return handled;
185}
186
187void WindowManager::_getPointerXYDiff(float& x, float& y) {
188    x -= _lastX;
189    y -= _lastY;
190}
191
192void WindowManager::_updatePickWindow(const WidgetList* wl, point_type x, point_type y) {
193    Label* label = dynamic_cast<Label*>(_pickWindow->getByName("PickLabel"));
194
195    if(!wl) {
196        setValue(0, false);
197
198        return;
199    }
200
201    setValue(0, true);
202
203    std::stringstream ss;
204
205    point_type xdiff = x;
206    point_type ydiff = y;
207
208    _getPointerXYDiff(xdiff, ydiff);
209
210    ss
211        << "At XY Coords: " << x << ", " << _height - y
212        << " ( diff " << xdiff << ", " << ydiff << " )"
213        << std::endl
214    ;
215
216    const Window* parent = wl->back()->getParent();
217
218    ss
219        << "Window: " << parent->getName()
220        << " ( xyz " << parent->getPosition() << " )"
221        << " { zRange " << parent->getZRange() << " }"
222        << " < size " << parent->getSize() << " >"
223        << " EventMask: " << std::hex << parent->getEventMask()
224        << std::endl
225    ;
226
227    for(WidgetList::const_iterator i = wl->begin(); i != wl->end(); i++) {
228        Widget* widget = i->get();
229
230        ss
231            << "   - " << widget->getName()
232            << " ( xyz " << widget->getPosition() << " )"
233            << " [ XYZ " << widget->getPosition() * parent->getMatrix()
234            << " ] < size " << widget->getSize() << " >"
235            << " EventMask: " << std::hex << widget->getEventMask()
236            << std::endl
237        ;
238    }
239
240    label->setLabel(ss.str());
241
242    XYCoord size = label->getTextSize();
243
244    _pickWindow->resize(size.x() + 10.0f, size.y() + 10.0f);
245    _pickWindow->setOrigin(5.0f, _height - _pickWindow->getHeight() - 5.0f);
246    _pickWindow->update();
247}
248
249void WindowManager::childInserted(unsigned int i) {
250    Window* window = dynamic_cast<Window*>(getChild(i));
251
252    if(!window) return;
253
254    // Update Window's index
255    for(Iterator w = begin(); w != end(); w++) {
256        if(w->get()->_index >= i) w->get()->_index++;
257    }
258
259    _objects.push_back(window);
260
261    window->_index = i;
262
263    setFocused(window);
264
265    window->setNodeMask(_nodeMask);
266    window->managed(this);
267
268    for(Window::Iterator w = window->begin(); w != window->end(); w++) if(w->valid()) {
269        _styleManager->applyStyles(w->get());
270    }
271
272    _styleManager->applyStyles(window);
273}
274
275void WindowManager::childRemoved(unsigned int start, unsigned int numChildren) {
276    for (unsigned int i = start; i < start+numChildren; i++)
277    {
278        Window* window = getByIndex(i);
279
280        if(!window) continue;
281
282        if(_remove(window)) {
283
284            window->_index = 0;
285            window->unmanaged(this);
286        }
287    }
288
289    // Update Window's index
290    for(Iterator w = begin(); w != end(); w++) {
291        if(w->get()->_index >= start) w->get()->_index -= numChildren;
292    }
293
294}
295
296// This method performs intersection testing at the given XY coords, and returns true if
297// any intersections were found. It will break after processing the first pickable Window
298// it finds.
299bool WindowManager::pickAtXY(float x, float y, WidgetList& wl) {
300    Intersections intr;
301
302    if(_view->computeIntersections(x, y, intr, _nodeMask)) {
303        // Get the first Window at the XY coordinates; if you want a Window to be
304        // non-pickable, set the NodeMask to something else.
305        Window* activeWin = 0;
306
307        // Iterate over every picked result and create a list of Widgets that belong
308        // to that Window.
309        for(Intersections::iterator i = intr.begin(); i != intr.end(); i++) {
310            Window* win = dynamic_cast<Window*>(i->nodePath.back()->getParent(0));
311
312            // Make sure that our window is valid, and that our pick is within the
313            // "visible area" of the Window.
314            if(
315                !win ||
316                (win->getVisibilityMode() == Window::VM_PARTIAL && !win->isPointerXYWithinVisible(x, y))
317            ) {
318                continue;
319            }
320
321            // Set our activeWin, so that we know when we've got all the Widgets
322            // that belong to it.
323            if(!activeWin) activeWin = win;
324
325            // If we've found a new Widnow, break out!
326            else if(activeWin != win) break;
327
328            Widget* widget = dynamic_cast<Widget*>(i->drawable.get());
329
330            if(!widget) continue;
331
332            // We need to return a list of every Widget that was picked, so
333            // that the handler can operate on it accordingly.
334            else wl.push_back(widget);
335        }
336
337        if(wl.size()) {
338            // Potentially VERY expensive; only to be used for debugging. :)
339            if(_flags & WM_PICK_DEBUG) _updatePickWindow(&wl, x, y);
340
341            return true;
342        }
343    }
344
345    if(_flags & WM_PICK_DEBUG) _updatePickWindow(0, x, y);
346
347    return false;
348}
349
350/*
351bool WindowManager::pickAtXY(float x, float y, WidgetList& wl) {
352    Intersections intr;
353
354    if(!_view->computeIntersections(x, y, intr, _nodeMask)) return false;
355
356    typedef std::vector<osg::observer_ptr<Window> > WindowVector;
357
358    WindowVector windows;
359
360    Window* activeWin = 0;
361
362    for(Intersections::iterator i = intr.begin(); i != intr.end(); i++) {
363        Window* win = dynamic_cast<Window*>(i->nodePath.back()->getParent(0));
364
365        if(
366            !win ||
367            (win->getVisibilityMode() == Window::VM_PARTIAL && !win->isPointerXYWithinVisible(x, y))
368        ) {
369            continue;
370        }
371
372        if(activeWin != win) {
373            activeWin = win;
374
375            windows.push_back(win);
376        }
377    }
378
379    if(!windows.size()) return false;
380
381    std::sort(windows.begin(), windows.end(), WindowBinNumberCompare());
382
383    for(WindowVector::iterator i = windows.begin(); i != windows.end(); i++) {
384        warn() << "- " << i->get()->getName() << " " << i->get()->getOrCreateStateSet()->getBinNumber() << std::endl;
385    }
386
387    warn() << std::endl;
388
389    return false;
390}
391*/
392
393bool WindowManager::setFocused(Window* window) {
394    Event ev(this);
395
396    ev._window = window;
397
398    // Inform the previously focused Window that it is going to be unfocused.
399    if(_focused.valid()) _focused->callMethodAndCallbacks(ev.makeType(EVENT_UNFOCUS));
400
401    _focused = window;
402
403    if(!window || !window->canFocus()) return false;
404
405    // Build a vector of every Window that is focusable, in the foreground, and in the
406    // background. All these Windows are handled differently.
407    Vector focusable;
408    Vector bg;
409    Vector fg;
410
411    for(ConstIterator it = begin(); it != end(); it++) if(it->valid()) {
412        Window* w = it->get();
413
414        if(w->getStrata() == Window::STRATA_FOREGROUND) fg.push_back(w);
415
416        else if(w->getStrata() == Window::STRATA_BACKGROUND) bg.push_back(w);
417
418        else focusable.push_back(w);
419    }
420
421    // After this call to sort, the internal objects will be arranged such that the
422    // previously focused window is the first, followed by all other Windows in
423    // descending order.
424    std::sort(focusable.begin(), focusable.end(), WindowZCompare());
425
426    // This is the depth range for each Window. Each Window object must be informed of
427    // the Z space allocated to it so that it can properly arrange it's children. We
428    // add 2 additional Windows here for anything that should appear in the background
429    // and foreground areas.
430    matrix_type zRange = 1.0f / (focusable.size() + 2.0f);
431
432    // Our offset for the following for() loop.
433    unsigned int i = 3;
434
435    // Handle all of our focusable Windows.
436    for(Iterator w = focusable.begin(); w != focusable.end(); w++) {
437        Window* win = w->get();
438
439        // Set our newly focused Window as the topmost element.
440        if(*w == window) win->_z = -zRange * 2.0f;
441
442        // Set the current Z of the remaining Windows and set their zRange so that
443        // they can update their own children.
444        else {
445            win->_z = -zRange * i;
446
447            i++;
448        }
449    }
450
451    // Handled our special BACKGROUND Windows.
452    for(Iterator w = bg.begin(); w != bg.end(); w++) w->get()->_z = -zRange * i;
453
454    // Handle our special FOREGOUND Windows.
455    for(Iterator w = fg.begin(); w != fg.end(); w++) w->get()->_z = -zRange;
456
457    // Update every window, regardless.
458    for(Iterator w = begin(); w != end(); w++) {
459        Window* win = w->get();
460
461        win->_zRange = zRange;
462
463        win->update();
464    }
465
466    _focused->callMethodAndCallbacks(ev.makeType(EVENT_FOCUS));
467
468    return true;
469}
470
471void WindowManager::setPointerXY(float x, float y) {
472    float xdiff = x;
473    float ydiff = y;
474
475    _getPointerXYDiff(xdiff, ydiff);
476
477    // If ydiff isn't NEAR 0 (floating point booleans aren't 100% reliable, but that
478    // doesn't matter in our case), assume we have either up or down movement.
479    if(ydiff != 0.0f) _lastVertical = ydiff > 0.0f ? PD_UP : PD_DOWN;
480
481    else _lastVertical = PD_NONE;
482
483    // If xdiff isn't 0, assume we have either left or right movement.
484    if(xdiff != 0.0f) _lastHorizontal = xdiff > 0.0f ? PD_RIGHT : PD_LEFT;
485
486    else _lastHorizontal = PD_NONE;
487
488    _lastX = x;
489    _lastY = y;
490}
491
492void WindowManager::setStyleManager(StyleManager* sm) {
493    _styleManager = sm;
494
495    for(Iterator i = begin(); i != end(); i++) if(i->valid()) {
496        Window* window = i->get();
497
498        for(Window::Iterator w = window->begin(); w != window->end(); w++) {
499            if(!w->valid()) continue;
500
501            _styleManager->applyStyles(w->get());
502        }
503
504        _styleManager->applyStyles(window);
505    }
506}
507
508void WindowManager::resizeAllWindows(bool visible) {
509    for(Iterator i = begin(); i != end(); i++) if(i->valid()) {
510        if(visible && !getValue(i->get()->_index)) continue;
511
512        i->get()->resize();
513    }
514}
515
516// Returns the application window coordinates of the WindowManager XY position.
517XYCoord WindowManager::windowXY(double x, double y) const {
518    return XYCoord((_windowWidth / _width) * x, (_windowHeight / _height) * y);
519}
520
521// Returns the WindowManager coordinates of the application window XY position.
522XYCoord WindowManager::localXY(double x, double y) const {
523    return XYCoord((_width / _windowWidth) * x, (_height / _windowHeight) * y);
524}
525
526// This is called by a ViewerEventHandler/MouseHandler (or whatever) as the pointer moves
527// around and intersects with objects. It also resets our state data (_widget, _leftDown,
528// etc.) The return value of this method is mostly useless.
529bool WindowManager::pointerMove(float x, float y) {
530    WidgetList wl;
531    Event      ev(this);
532
533    if(!pickAtXY(x, y, wl)) {
534        if(_lastEvent) {
535            setEventFromInterface(ev.makeMouse(x, y, EVENT_MOUSE_LEAVE), _lastEvent);
536
537            _lastEvent->callMethodAndCallbacks(ev);
538        }
539
540        if(_focusMode == PFM_SLOPPY) setFocused(0);
541
542        _lastEvent  = 0;
543        _leftDown   = 0;
544        _middleDown = 0;
545        _rightDown  = 0;
546
547        return false;
548    }
549
550    EventInterface* ei = getFirstEventInterface(wl, ev.makeMouse(x, y, EVENT_MOUSE_OVER));
551
552    if(!ei) return false;
553
554    if(_lastEvent != ei) {
555        if(_lastEvent) {
556            Event evLeave(this);
557
558            evLeave.makeMouse(x, y, EVENT_MOUSE_LEAVE);
559
560            setEventFromInterface(evLeave, _lastEvent);
561
562            _lastEvent->callMethodAndCallbacks(evLeave);
563        }
564
565        _lastEvent = ei;
566
567        if(_focusMode == PFM_SLOPPY && ev._window) setFocused(ev._window);
568
569        _lastEvent->callMethodAndCallbacks(ev.makeMouse(x, y, EVENT_MOUSE_ENTER));
570    }
571
572    ei->callMethodAndCallbacks(ev.makeMouse(x, y, EVENT_MOUSE_OVER));
573
574    return true;
575}
576
577bool WindowManager::pointerDrag(float x, float y) {
578    WidgetList widgetList;
579    Event      ev(this);
580
581    float xdiff = x;
582    float ydiff = y;
583
584    _getPointerXYDiff(xdiff, ydiff);
585
586    ev.makeMouse(xdiff, ydiff, EVENT_MOUSE_DRAG);
587
588    // If we're still in the drag state...
589    if(_lastPush) {
590        setEventFromInterface(ev, _lastPush);
591
592        return _lastPush->callMethodAndCallbacks(ev);
593    }
594
595    return false;
596}
597
598bool WindowManager::mouseScroll(float x, float y) {
599    WidgetList wl;
600
601    if(!pickAtXY(x, y, wl)) return false;
602
603    Event ev(this, EVENT_MOUSE_SCROLL);
604
605    EventInterface* ei = getFirstEventInterface(wl, ev);
606
607    if(!ei) return false;
608
609    return ei->callMethodAndCallbacks(ev);
610}
611
612// Keypresses only go the focused Window.
613bool WindowManager::keyDown(int key, int mask) {
614    if(_focused.valid()) {
615        Event ev(this, EVENT_KEY_DOWN);
616
617        ev.makeKey(key, mask);
618
619        Widget* focusedWidget = _focused->getFocused();
620
621        ev._window = _focused.get();
622        ev._widget = focusedWidget;
623
624        bool handled = false;
625
626        if(focusedWidget) handled = focusedWidget->callMethodAndCallbacks(ev);
627
628        if(!handled) return _focused->callMethodAndCallbacks(ev);
629
630        else return true;
631    }
632
633    return false;
634}
635
636bool WindowManager::keyUp(int key, int mask) {
637    if(_focused.valid()) {
638        Event ev(this, EVENT_KEY_UP);
639
640        ev.makeKey(key, mask);
641
642        Widget* focusedWidget = _focused->getFocused();
643
644        ev._window = _focused.get();
645        ev._widget = focusedWidget;
646
647        bool handled = false;
648
649        if(focusedWidget) handled = focusedWidget->callMethodAndCallbacks(ev);
650
651        if(!handled) return _focused->callMethodAndCallbacks(ev);
652
653        else return true;
654    }
655
656    return false;
657}
658
659// A convenience wrapper for creating a proper orthographic camera using the current
660// width and height.
661osg::Camera* WindowManager::createParentOrthoCamera() {
662    osg::Camera* camera = createOrthoCamera(_width, _height);
663
664    camera->addChild(this);
665
666    return camera;
667}
668
669}
Note: See TracBrowser for help on using the browser.