/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2008 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ // Code by: Jeremy Moles (cubicool) 2007-2008 #ifndef OSGWIDGET_WINDOW #define OSGWIDGET_WINDOW #include #include #include #include #include #include #include namespace osgWidget { // These are helper callbacks you can attach to Windows that will make them moveable, // rotatable, and scalable respectively. bool OSGWIDGET_EXPORT callbackWindowMove (Event&); bool OSGWIDGET_EXPORT callbackWindowRotate (Event&); bool OSGWIDGET_EXPORT callbackWindowScale (Event&); // These are helper callbacks you can attach to Windows to that will make various // keyboard events behave as you might imagine. bool OSGWIDGET_EXPORT callbackWindowTabFocus(Event&); class OSGWIDGET_EXPORT Window: public osg::MatrixTransform, public UIObjectParent, public EventInterface, public StyleInterface { public: typedef std::list > WindowList; struct Sizes { point_type current; point_type minimum; Sizes(point_type c = -1.0f, point_type m = -1.0f): current(c), minimum(m) {} }; // TODO: This is a class that puts (embeds) the Window inside of a Widget // interface, to help assemble Windows with Windows, and what have you. class OSGWIDGET_EXPORT EmbeddedWindow: public Widget { osg::ref_ptr _window; public: META_Object (osgWidget::Window, EmbeddedWindow); EmbeddedWindow (const std::string& = "", point_type = 0.0f, point_type = 0.0f); EmbeddedWindow (const EmbeddedWindow&, const osg::CopyOp&); virtual void parented (Window*); virtual void unparented (Window*); virtual void managed (WindowManager*); virtual void unmanaged (WindowManager*); virtual void positioned (); bool setWindow (Window*); void updateSizeFromWindow (); Window* getWindow() { return _window.get(); } const Window* getWindow() const { return _window.get(); } }; // These correspond to special regions honored by the WindowManager. Most Windows // will want to be NONE, unless they need to exist in the foreground or background // for some reason. enum Strata { STRATA_NONE, STRATA_BACKGROUND, STRATA_FOREGROUND }; // If you only want to display a portion of a Window (such as when it is embedded), // you will need to set the VisibilityMode to WM_PARTIAL. Otherwise, the entire // Window is visible by default. The final enum, VM_ENTIRE, says that no Scissoring // should take place at all, and is useful in cases where you want to properly // scale or rotate Windows. enum VisibilityMode { VM_FULL, VM_PARTIAL, VM_ENTIRE }; // Anchors are very similar in that they allow us to pre-apply transformations in the // call to Window::update() that allow us to position a Window somewhere relative // to the WindowManger's viewable area. However, unlike the ALIGNMENT enums, these // are totally optional (whereas a Widget must always have some ALIGNMENT value set. enum VerticalAnchor { VA_NONE, VA_CENTER, VA_TOP, VA_BOTTOM }; enum HorizontalAnchor { HA_NONE, HA_CENTER, HA_LEFT, HA_RIGHT }; Window (const std::string& = ""); Window (const Window&, const osg::CopyOp&); bool resize (point_type = 0.0f, point_type = 0.0f); bool resizeAdd (point_type = 0.0f, point_type = 0.0f); bool resizePercent (point_type = 0.0f, point_type = 0.0f); virtual void update (); virtual void managed (WindowManager*); virtual void unmanaged (WindowManager*); virtual bool addWidget (Widget*); virtual bool insertWidget (Widget*, unsigned int); virtual bool removeWidget (Widget*); virtual bool replaceWidget (Widget*, Widget*); // This method wraps our Geode's addDrawable() method and returns the index of // the newly-added Drawable. unsigned int addDrawableAndGetIndex (osg::Drawable*); unsigned int addChildAndGetIndex (osg::Node*); bool isVisible () const; bool isXYWithinVisible (float, float) const; void setVisibleArea (int = 0, int = 0, int = 0, int = 0); void addVisibleArea (int = 0, int = 0, int = 0, int = 0); bool setFocused (const Widget*); bool setFocused (const std::string&); bool grabFocus (); bool setFirstFocusable (); bool setNextFocusable (); bool getFocusList (WidgetList&) const; bool getEmbeddedList (WindowList&) const; void getParentList (WindowList&) const; XYCoord localXY (double, double) const; XYCoord getAbsoluteOrigin () const; // This method wraps the current Window in a EmbeddedWindow object and returns it. EmbeddedWindow* embed( const std::string& = "", Widget::Layer = Widget::LAYER_MIDDLE, unsigned int = 0 ); Widget* getFocused() { return _focused.get(); } const Widget* getFocused() const { return _focused.get(); } bool show() { return _setVisible(true); } bool hide() { return _setVisible(false); } bool isPointerXYWithinVisible(float x, float y) const { XYCoord xy = localXY(x, y); return isXYWithinVisible(xy.x(), xy.y()); } osg::Geode* getGeode() { return _geode(); } const osg::Geode* getGeode() const { return _geode(); } Widget* getBackground() { return _bg(); } const Widget* getBackground() const { return _bg(); } WindowManager* getWindowManager() { return _wm; } const WindowManager* getWindowManager() const { return _wm; } Window* getParent() { return _parent; } const Window* getParent() const { return _parent; } Window* getTopmostParent() { return _getTopmostParent(); } const Window* getTopmostParent() const { return _getTopmostParent(); } unsigned int getIndex() const { return _index; } matrix_type getX() const { return _x; } matrix_type getY() const { return _y; } matrix_type getZ() const { return _z; } point_type getWidth() const { return _width.current; } point_type getHeight() const { return _height.current; } point_type getMinWidth() const { return _width.minimum; } point_type getMinHeight() const { return _height.minimum; } VerticalAnchor getAnchorVertical() const { return _vAnchor; } HorizontalAnchor getAnchorHorizontal() const { return _hAnchor; } XYCoord getOrigin() const { return XYCoord(_x, _y); } XYCoord getSize() const { return XYCoord(_width.current, _height.current); } XYCoord getMinSize() const { return XYCoord(_width.minimum, _height.minimum); } matrix_type getZRange() const { return _zRange; } Strata getStrata() const { return _strata; } const Quad& getVisibleArea() const { return _visibleArea; } VisibilityMode getVisibilityMode() const { return _vis; } Point getPosition() const { return Point(_x, _y, _z); } matrix_type getRotate() const { return _r; } matrix_type getScale() const { return _s; } matrix_type getScaleDenominator() const { return _scaleDenom; } void setX(matrix_type x) { _x = x; } void setY(matrix_type y) { _y = y; } void setZ(matrix_type z) { _z = z; } void setZRange(matrix_type zRange) { _zRange = zRange; } void setPosition(matrix_type x, matrix_type y, matrix_type z) { _x = x; _y = y; _z = z; } void setPosition(const Point& p) { setPosition(p.x(), p.y(), p.z()); } void setOrigin(matrix_type x, matrix_type y) { _x = x; _y = y; } void setOrigin(const XYCoord& xy) { setOrigin(xy.x(), xy.y()); } void setRotate(matrix_type r) { _r = r; } void setScale(matrix_type s) { _s = s; } void setScaleDenominator(matrix_type sd) { _scaleDenom = sd; } void setAnchorVertical(VerticalAnchor va) { _vAnchor = va; } void setAnchorHorizontal(HorizontalAnchor ha) { _hAnchor = ha; } void setStrata(Strata s) { _strata = s; } void setVisibilityMode(VisibilityMode v) { _vis = v; } void addX(matrix_type x) { _x += x; } void addY(matrix_type y) { _y += y; } void addZ(matrix_type z) { _z += z; } void addRotate(matrix_type r) { _r += r; } void addScale(matrix_type s) { _s += s / (_scaleDenom != 0.0f ? _scaleDenom : 1.0f); } void addOrigin(matrix_type x, matrix_type y) { _x += x; _y += y; } void attachMoveCallback() { addCallback(new Callback(&callbackWindowMove, EVENT_MOUSE_DRAG)); } void attachRotateCallback() { addCallback(new Callback(&callbackWindowRotate, EVENT_MOUSE_DRAG)); } void attachScaleCallback() { addCallback(new Callback(&callbackWindowScale, EVENT_MOUSE_DRAG)); } void attachTabFocusCallback() { addCallback(new Callback(&callbackWindowTabFocus, EVENT_KEY_DOWN)); } typedef point_type (Widget::*Getter)() const; protected: typedef std::less Less; typedef std::greater Greater; typedef std::plus Plus; friend class WindowManager; // The (optional) Window that this Window is parented inside. Window* _parent; // The WindowManger to which this window is attached. WindowManager* _wm; // The positional index this Node holds within it's parent WindowManager. unsigned int _index; // The X and Y values of the Window (origin). matrix_type _x; matrix_type _y; // A pair of values representing the currently calculated Z value and the // depth range for all children to be used during the call to update(). matrix_type _z; matrix_type _zRange; // This is a special value that can be used to "force" a Window not to be // focusable and instead always exist in the foreground or background. Strata _strata; // A flag determining whether our visible area is the full Window or rather // a portion of the Window. VisibilityMode _vis; // A rotation value in degrees. matrix_type _r; // This will also need to adjust geom internally so that picking is correct. matrix_type _s; matrix_type _scaleDenom; Sizes _width; Sizes _height; VerticalAnchor _vAnchor; HorizontalAnchor _hAnchor; // Not all windows have widgets that can focus, but if they do this should // be a pointer to it. osg::observer_ptr _focused; // The "visible" area that will define both the glScissor bounds AND will // be used to make sure our pick is valid. The values herein correspond to // X, Y, W, and H--in that order. Quad _visibleArea; // This helper method is used by _compare<>() and _accumulate<>(), so ignore this // function and go see the docs for those instead. This thing is huge and unwieldy // and not to be triffled with! :) template point_type _forEachAssignOrApply( Getter get, int begin, int end, int add, bool assign ) const { point_type val = 0.0f; unsigned int c = begin; ConstIterator e = end > 0 ? _objects.begin() + end : _objects.end() + end; // I WARNED YOU! If you try and understand what this does your head will // explode! But let me say a few things: in MSVC you can't add to an iterator // such that the add will cause it to increment past the end of the container. // This appears to be safe in GCC, where it will just return the last // item therein, but causes an assertion error in other compilers. I'm not // sure if there is a cleaner remedy for this, so what we do for now is keep a // count variable called "c" that makes sure our iterator's opterator+() // method is safe to call. for( ConstIterator i = _objects.begin() + begin; i < e; c += add ) { point_type v = 0.0f; if(i->valid()) v = (i->get()->*get)(); // If you want to assign to val, and NOT add a sequence of them... if(assign) { if(T()(v, val)) val = v; } // If you want to accumulate a value INTO val... else val = T()(v, val); // Make sure our iterator is safe to increment. Otherwise, set it to // whatever end is. // TODO: This isn't 100% accurate, as it does not YET take into account // our requested end in any way other than implicitly. It should, however, // work okay for now. if((c + add) < _objects.size()) i += add; else i = e; } return val; } void _setWidthAndHeightUnknownSizeError (const std::string&, point_type); void _setWidthAndHeightNotPAError (const std::string&, point_type); void _setWidthAndHeight (); void _removeFromGeode (Widget*); Widget* _getBackground() const; Window* _getTopmostParent() const; // This method will return the T'th value returned by applying the Getter member function // pointer to each iterator in the range of iterators defined by offset and add. In // plain language, this helper method will apply some standard Widget::get* function // to a range of objects in the _objects Vector, and will return the T'th of that. // The template T can be any functor accepting two point_type values that return // a bool. For example, this is commonly used with std::less to find the smallest // width in a range of Widgets. template point_type _compare( Getter get, int begin = 0, int end = 0, int add = 1 ) const { return _forEachAssignOrApply(get, begin, end, add, true); } // This method will return the T'th value accumulated by applying the Getter member // function to each iterator in the range of iterators defined by offset and add (similar // to above). For example, this method can be used to apply std::plus to every // width in a range of Widgets, and return the total. template point_type _accumulate( Getter get, int begin = 0, int end = 0, int add = 1 ) const { return _forEachAssignOrApply(get, begin, end, add, false); } osg::Geode* _geode() { return dynamic_cast(getChild(0)); } const osg::Geode* _geode() const { return dynamic_cast(getChild(0)); } Widget* _bg() { return _getBackground(); } const Widget* _bg() const { return _getBackground(); } osg::Scissor* _scissor() { return dynamic_cast( getStateSet()->getAttribute(osg::StateAttribute::SCISSOR) ); } bool _setWidget (Widget*, int = -1); bool _setVisible (bool); void _setFocused (Widget*); void _setStyled (Widget*); void _setParented (Widget*, bool=false); void _setManaged (Widget*, bool=false); void _positionWidget(Widget*, point_type, point_type); // These return the smallest and largest width and height values for the given // range of Widgets. point_type _getMinWidgetWidth (int = 0, int = 0, int = 1) const; point_type _getMinWidgetHeight (int = 0, int = 0, int = 1) const; point_type _getMaxWidgetWidth (int = 0, int = 0, int = 1) const; point_type _getMaxWidgetHeight (int = 0, int = 0, int = 1) const; // These return the smallest and largest minWidth and minHeight values for // the given range of Widgets. point_type _getMinWidgetMinWidth (int = 0, int = 0, int = 1) const; point_type _getMinWidgetMinHeight (int = 0, int = 0, int = 1) const; point_type _getMaxWidgetMinWidth (int = 0, int = 0, int = 1) const; point_type _getMaxWidgetMinHeight (int = 0, int = 0, int = 1) const; // These return the smallest and largest width and height total (width + pad) // values for the given range of Widgets. point_type _getMinWidgetWidthTotal (int = 0, int = 0, int = 1) const; point_type _getMinWidgetHeightTotal (int = 0, int = 0, int = 1) const; point_type _getMaxWidgetWidthTotal (int = 0, int = 0, int = 1) const; point_type _getMaxWidgetHeightTotal (int = 0, int = 0, int = 1) const; // These return the smallest and largest minWidth and minHeight total // (width + pad) values for the given range of Widgets. point_type _getMinWidgetMinWidthTotal (int = 0, int = 0, int = 1) const; point_type _getMinWidgetMinHeightTotal (int = 0, int = 0, int = 1) const; point_type _getMaxWidgetMinWidthTotal (int = 0, int = 0, int = 1) const; point_type _getMaxWidgetMinHeightTotal (int = 0, int = 0, int = 1) const; // These return the smallest and largest horizontal and vertical padding // values for the given range of Widgets. point_type _getMinWidgetPadHorizontal (int = 0, int = 0, int = 1) const; point_type _getMinWidgetPadVertical (int = 0, int = 0, int = 1) const; point_type _getMaxWidgetPadHorizontal (int = 0, int = 0, int = 1) const; point_type _getMaxWidgetPadVertical (int = 0, int = 0, int = 1) const; point_type _getNumFill(int = 0, int = 0, int = 1) const; // This method is passed the additional values by which width and height should be // modified as calculed by the parent method, Window::resize. Keep in mind that these // values can be negative (indicating a potential "shrink" request) or positive (which // would indicate a "grow" reqeust). virtual void _resizeImplementation(point_type, point_type) = 0; // These are made into implementation functions since getting the width or height // of a window can potentially be an expensive operation, and we'll want to cache // results if possible (which is handled transparently by the actualy Window::resize // method). They return a Sizes struct which contains two members: cur (for current) // and min (minimum). It's important that the Window know it's minimum possible // size so that it can ignore invaled requests to resize. // // Default versions using BoundingBox calculations are provided, but some Windows // override this (Table, Box). virtual Sizes _getWidthImplementation () const; virtual Sizes _getHeightImplementation () const; }; typedef Window::WindowList WindowList; } #endif