diff --git a/src/Canvas/CMakeLists.txt b/src/Canvas/CMakeLists.txt index c2c52e251..d8c45bde1 100644 --- a/src/Canvas/CMakeLists.txt +++ b/src/Canvas/CMakeLists.txt @@ -8,7 +8,12 @@ set(SOURCES elements/map.cxx elements/path.cxx elements/text.cxx + gui_mgr.cxx + placement.cxx + property_based_element.cxx + property_based_mgr.cxx property_helper.cxx + window.cxx ) set(HEADERS @@ -19,7 +24,12 @@ set(HEADERS elements/map.hxx elements/path.hxx elements/text.hxx + gui_mgr.hxx + placement.hxx + property_based_element.hxx + property_based_mgr.hxx property_helper.hxx + window.hxx ) flightgear_component(Canvas "${SOURCES}" "${HEADERS}") diff --git a/src/Canvas/canvas.cxx b/src/Canvas/canvas.cxx index 673e9f12c..4c6504fbc 100644 --- a/src/Canvas/canvas.cxx +++ b/src/Canvas/canvas.cxx @@ -18,6 +18,7 @@ #include "canvas.hxx" #include "elements/group.hxx" +#include "mouse_event.hxx" #include <Canvas/property_helper.hxx> #include <Main/globals.hxx> @@ -31,172 +32,95 @@ #include <osgText/Text> #include <osgViewer/Viewer> +#include <boost/algorithm/string/predicate.hpp> #include <iostream> -//------------------------------------------------------------------------------ -/** - * Callback used to disable/enable rendering to the texture if it is not - * visible - */ -class CameraCullCallback: - public osg::NodeCallback +//---------------------------------------------------------------------------- +Canvas::CameraCullCallback::CameraCullCallback(): + _render( true ), + _render_frame( 0 ) { - public: - CameraCullCallback(): - _render( true ), - _render_frame( 0 ) - {} +} - /** - * Enable rendering for the next frame - */ - void enableRendering() - { - _render = true; - } - - private: - - bool _render; - unsigned int _render_frame; - - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) - { - if( !_render && nv->getTraversalNumber() != _render_frame ) - return; - - traverse(node, nv); - - _render = false; - _render_frame = nv->getTraversalNumber(); - } -}; - -/** - * This callback is installed on every placement of the canvas in the scene to - * only render the canvas if at least one placement is visible - */ -class PlacementCullCallback: - public osg::NodeCallback +//---------------------------------------------------------------------------- +void Canvas::CameraCullCallback::enableRendering() { - public: + _render = true; +} - PlacementCullCallback(Canvas* canvas, CameraCullCallback* camera_cull): - _canvas( canvas ), - _camera_cull( camera_cull ) - {} +//---------------------------------------------------------------------------- +void Canvas::CameraCullCallback::operator()( osg::Node* node, + osg::NodeVisitor* nv ) +{ + if( !_render && nv->getTraversalNumber() != _render_frame ) + return; - private: + traverse(node, nv); - Canvas *_canvas; - CameraCullCallback *_camera_cull; + _render = false; + _render_frame = nv->getTraversalNumber(); +} - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) - { - if( nv->getTraversalMask() & simgear::MODEL_BIT ) - _camera_cull->enableRendering(); +//---------------------------------------------------------------------------- +Canvas::CullCallback::CullCallback(CameraCullCallback* camera_cull): + _camera_cull( camera_cull ) +{ - traverse(node, nv); - } -}; +} + +//---------------------------------------------------------------------------- +void Canvas::CullCallback::operator()( osg::Node* node, + osg::NodeVisitor* nv ) +{ + if( nv->getTraversalMask() & simgear::MODEL_BIT ) + _camera_cull->enableRendering(); + + traverse(node, nv); +} //------------------------------------------------------------------------------ -Canvas::Canvas(): +Canvas::Canvas(SGPropertyNode* node): + PropertyBasedElement(node), _size_x(-1), _size_y(-1), _view_width(-1), _view_height(-1), - _status(0), + _status(node, "status"), + _status_msg(node, "status-msg"), + _mouse_x(node, "mouse/x"), + _mouse_y(node, "mouse/y"), + _mouse_dx(node, "mouse/dx"), + _mouse_dy(node, "mouse/dy"), + _mouse_button(node, "mouse/button"), + _mouse_state(node, "mouse/state"), + _mouse_mod(node, "mouse/mod"), + _mouse_scroll(node, "mouse/scroll"), + _mouse_event(node, "mouse/event"), _sampling_dirty(false), _color_dirty(true), - _node(0), + _root_group( new canvas::Group(node) ), _render_always(false) { + _status = 0; setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y); _camera_callback = new CameraCullCallback; - _cull_callback = new PlacementCullCallback(this, _camera_callback); + _cull_callback = new CullCallback(_camera_callback); + + canvas::linkColorNodes + ( + "color-background", + _node, + _color_background, + osg::Vec4f(0,0,0,1) + ); } //------------------------------------------------------------------------------ Canvas::~Canvas() { - clearPlacements(); - unbind(); - _node = 0; -} - -//------------------------------------------------------------------------------ -int Canvas::getStatus() const -{ - return _status; -} - -//------------------------------------------------------------------------------ -void Canvas::reset(SGPropertyNode* node) -{ - if( node ) - SG_LOG - ( - SG_GL, - SG_INFO, - "Canvas::reset() texture[" << node->getIndex() << "]" - ); - - unbind(); - - _node = node; - setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y); - - if( _node ) - { - _root_group.reset( new canvas::Group(_node) ); - _node->tie - ( - "size[0]", - SGRawValueMethods<Canvas, int>( *this, &Canvas::getSizeX, - &Canvas::setSizeX ) - ); - _node->tie - ( - "size[1]", - SGRawValueMethods<Canvas, int>( *this, &Canvas::getSizeY, - &Canvas::setSizeY ) - ); - _node->tie - ( - "view[0]", - SGRawValueMethods<Canvas, int>( *this, &Canvas::getViewWidth, - &Canvas::setViewWidth ) - ); - _node->tie - ( - "view[1]", - SGRawValueMethods<Canvas, int>( *this, &Canvas::getViewHeight, - &Canvas::setViewHeight ) - ); - _node->tie - ( - "status", - SGRawValueMethods<Canvas, int>(*this, &Canvas::getStatus) - ); - _node->tie - ( - "status-msg", - SGRawValueMethods<Canvas, const char*>(*this, &Canvas::getStatusMsg) - ); - _node->addChangeListener(this); - - canvas::linkColorNodes - ( - "color-background", - _node, - _color_background, - osg::Vec4f(0,0,0,1) - ); - } } //------------------------------------------------------------------------------ @@ -257,15 +181,29 @@ void Canvas::update(double delta_time_sec) // New placement _placements.resize(node->getIndex() + 1); else - // Remove maybe already existing placements - clearPlacements(node->getIndex()); + // Remove possibly existing placements + _placements[ node->getIndex() ].clear(); - // add new placements - _placements[node->getIndex()] = _texture.set_texture( - node, - _texture.getTexture(), - _cull_callback - ); + // Get new placements + PlacementFactoryMap::const_iterator placement_factory = + _placement_factories.find( node->getStringValue("type", "object") ); + if( placement_factory != _placement_factories.end() ) + { + canvas::Placements& placements = + _placements[ node->getIndex() ] = + placement_factory->second + ( + node, + boost::shared_static_cast<Canvas>(_self.lock()) + ); + node->setStringValue + ( + "status-msg", + placements.empty() ? "No match" : "Ok" + ); + } + else + node->setStringValue("status-msg", "Unknown placement type"); } if( _render_always ) @@ -290,12 +228,6 @@ void Canvas::setSizeX(int sx) setStatusFlags(CREATE_FAILED, false); } -//------------------------------------------------------------------------------ -int Canvas::getSizeX() const -{ - return _size_x; -} - //------------------------------------------------------------------------------ void Canvas::setSizeY(int sy) { @@ -314,12 +246,6 @@ void Canvas::setSizeY(int sy) setStatusFlags(CREATE_FAILED, false); } -//------------------------------------------------------------------------------ -int Canvas::getSizeY() const -{ - return _size_y; -} - //------------------------------------------------------------------------------ void Canvas::setViewWidth(int w) { @@ -330,12 +256,6 @@ void Canvas::setViewWidth(int w) _texture.setViewSize(_view_width, _view_height); } -//------------------------------------------------------------------------------ -int Canvas::getViewWidth() const -{ - return _view_width; -} - //------------------------------------------------------------------------------ void Canvas::setViewHeight(int h) { @@ -347,15 +267,19 @@ void Canvas::setViewHeight(int h) } //------------------------------------------------------------------------------ -int Canvas::getViewHeight() const +bool Canvas::handleMouseEvent(const canvas::MouseEvent& event) { - return _view_height; -} - -//------------------------------------------------------------------------------ -const char* Canvas::getStatusMsg() const -{ - return _status_msg.c_str(); + _mouse_x = event.x; + _mouse_y = event.y; + _mouse_dx = event.dx; + _mouse_dy = event.dy; + _mouse_button = event.button; + _mouse_state = event.state; + _mouse_mod = event.mod; + _mouse_scroll = event.scroll; + // Always set event type last because all listeners are attached to it + _mouse_event = event.type; + return true; } //------------------------------------------------------------------------------ @@ -380,7 +304,7 @@ void Canvas::childRemoved( SGPropertyNode * parent, return; if( child->getNameString() == "placement" ) - clearPlacements(child->getIndex()); + _placements[ child->getIndex() ].clear(); else static_cast<canvas::Element*>(_root_group.get()) ->childRemoved(parent, child); @@ -389,6 +313,9 @@ void Canvas::childRemoved( SGPropertyNode * parent, //---------------------------------------------------------------------------- void Canvas::valueChanged(SGPropertyNode* node) { + if( boost::starts_with(node->getNameString(), "status") ) + return; + if( node->getParent()->getParent() == _node ) { if( !_color_background.empty() @@ -416,11 +343,31 @@ void Canvas::valueChanged(SGPropertyNode* node) _sampling_dirty = true; else if( node->getNameString() == "render-always" ) _render_always = node->getBoolValue(); + else if( node->getNameString() == "size" ) + { + if( node->getIndex() == 0 ) + setSizeX( node->getIntValue() ); + else if( node->getIndex() == 1 ) + setSizeY( node->getIntValue() ); + } + else if( node->getNameString() == "view" ) + { + if( node->getIndex() == 0 ) + setViewWidth( node->getIntValue() ); + else if( node->getIndex() == 1 ) + setViewHeight( node->getIntValue() ); + } } _root_group->valueChanged(node); } +//------------------------------------------------------------------------------ +osg::Texture2D* Canvas::getTexture() const +{ + return _texture.getTexture(); +} + //------------------------------------------------------------------------------ GLuint Canvas::getTexId() const { @@ -449,13 +396,41 @@ GLuint Canvas::getTexId() const return tobj->_id; } +//------------------------------------------------------------------------------ +Canvas::CameraCullCallbackPtr Canvas::getCameraCullCallback() const +{ + return _camera_callback; +} + +//---------------------------------------------------------------------------- +Canvas::CullCallbackPtr Canvas::getCullCallback() const +{ + return _cull_callback; +} + +//------------------------------------------------------------------------------ +void Canvas::addPlacementFactory( const std::string& type, + canvas::PlacementFactory factory ) +{ + if( _placement_factories.find(type) != _placement_factories.end() ) + SG_LOG + ( + SG_GENERAL, + SG_WARN, + "Canvas::addPlacementFactory: replace existing factor for type " << type + ); + + _placement_factories[type] = factory; +} + //------------------------------------------------------------------------------ void Canvas::setStatusFlags(unsigned int flags, bool set) { if( set ) - _status |= flags; + _status = _status | flags; else - _status &= ~flags; + _status = _status & ~flags; + // TODO maybe extend simgear::PropertyObject to allow |=, &= etc. if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) ) _status_msg = "Missing size"; @@ -472,47 +447,4 @@ void Canvas::setStatusFlags(unsigned int flags, bool set) } //------------------------------------------------------------------------------ -void Canvas::clearPlacements(int index) -{ - Placements& placements = _placements.at(index); - while( !placements.empty() ) - { - osg::ref_ptr<osg::Group> group = placements.back(); - placements.pop_back(); - - assert( group->getNumChildren() == 1 ); - osg::Node *child = group->getChild(0); - - if( group->getNumParents() ) - { - osg::Group *parent = group->getParent(0); - parent->addChild(child); - parent->removeChild(group); - } - - group->removeChild(child); - } -} - -//------------------------------------------------------------------------------ -void Canvas::clearPlacements() -{ - for(size_t i = 0; i < _placements.size(); ++i) - clearPlacements(i); - _placements.clear(); -} - -//------------------------------------------------------------------------------ -void Canvas::unbind() -{ - if( !_node ) - return; - - _node->untie("size[0]"); - _node->untie("size[1]"); - _node->untie("view[0]"); - _node->untie("view[1]"); - _node->untie("status"); - _node->untie("status-msg"); - _node->removeChangeListener(this); -} +Canvas::PlacementFactoryMap Canvas::_placement_factories; diff --git a/src/Canvas/canvas.hxx b/src/Canvas/canvas.hxx index a10713bd4..fa75f95d0 100644 --- a/src/Canvas/canvas.hxx +++ b/src/Canvas/canvas.hxx @@ -19,21 +19,20 @@ #ifndef CANVAS_HXX_ #define CANVAS_HXX_ +#include "placement.hxx" +#include "property_based_element.hxx" + +#include <Canvas/canvas_fwd.hpp> #include <Instrumentation/od_gauge.hxx> -#include <simgear/props/props.hxx> + +#include <simgear/props/propertyObject.hxx> #include <osg/NodeCallback> #include <memory> #include <string> -namespace canvas -{ - class Group; -} - -class CameraCullCallback; class Canvas: - public SGPropertyChangeListener + public PropertyBasedElement { public: @@ -45,26 +44,58 @@ class Canvas: CREATE_FAILED = 0x0004 }; - Canvas(); + /** + * Callback used to disable/enable rendering to the texture if it is not + * visible + */ + class CameraCullCallback: + public osg::NodeCallback + { + public: + CameraCullCallback(); + + /** + * Enable rendering for the next frame + */ + void enableRendering(); + + private: + bool _render; + unsigned int _render_frame; + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + }; + typedef osg::ref_ptr<CameraCullCallback> CameraCullCallbackPtr; + + /** + * This callback is installed on every placement of the canvas in the + * scene to only render the canvas if at least one placement is visible + */ + class CullCallback: + public osg::NodeCallback + { + public: + CullCallback(CameraCullCallback* camera_cull); + + private: + CameraCullCallback *_camera_cull; + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + }; + typedef osg::ref_ptr<CullCallback> CullCallbackPtr; + + Canvas(SGPropertyNode* node); virtual ~Canvas(); - void reset(SGPropertyNode* node); void update(double delta_time_sec); void setSizeX(int sx); - int getSizeX() const; - void setSizeY(int sy); - int getSizeY() const; void setViewWidth(int w); - int getViewWidth() const; - void setViewHeight(int h); - int getViewHeight() const; - int getStatus() const; - const char* getStatusMsg() const; + bool handleMouseEvent(const canvas::MouseEvent& event); virtual void childAdded( SGPropertyNode * parent, SGPropertyNode * child ); @@ -72,8 +103,15 @@ class Canvas: SGPropertyNode * child ); virtual void valueChanged (SGPropertyNode * node); + osg::Texture2D* getTexture() const; GLuint getTexId() const; + CameraCullCallbackPtr getCameraCullCallback() const; + CullCallbackPtr getCullCallback() const; + + static void addPlacementFactory( const std::string& type, + canvas::PlacementFactory factory ); + private: Canvas(const Canvas&); // = delete; @@ -84,8 +122,16 @@ class Canvas: _view_width, _view_height; - int _status; - std::string _status_msg; + simgear::PropertyObject<int> _status; + simgear::PropertyObject<std::string> _status_msg; + + simgear::PropertyObject<int> _mouse_x, _mouse_y, + _mouse_dx, _mouse_dy, + _mouse_button, + _mouse_state, + _mouse_mod, + _mouse_scroll, + _mouse_event; bool _sampling_dirty, _color_dirty; @@ -93,22 +139,19 @@ class Canvas: FGODGauge _texture; std::auto_ptr<canvas::Group> _root_group; - SGPropertyNode_ptr _node; std::vector<SGPropertyNode_ptr> _color_background; - osg::ref_ptr<CameraCullCallback> _camera_callback; - osg::ref_ptr<osg::NodeCallback> _cull_callback; - + CameraCullCallbackPtr _camera_callback; + CullCallbackPtr _cull_callback; bool _render_always; //<! Used to disable automatic lazy rendering (culling) std::vector<SGPropertyNode*> _dirty_placements; - std::vector<Placements> _placements; + std::vector<canvas::Placements> _placements; + + typedef std::map<std::string, canvas::PlacementFactory> PlacementFactoryMap; + static PlacementFactoryMap _placement_factories; void setStatusFlags(unsigned int flags, bool set = true); - void clearPlacements(int index); - void clearPlacements(); - - void unbind(); }; #endif /* CANVAS_HXX_ */ diff --git a/src/Canvas/canvas_fwd.hpp b/src/Canvas/canvas_fwd.hpp new file mode 100644 index 000000000..c56612fd9 --- /dev/null +++ b/src/Canvas/canvas_fwd.hpp @@ -0,0 +1,53 @@ +// Canvas forward declarations +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef CANVAS_FWD_HPP_ +#define CANVAS_FWD_HPP_ + +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include <vector> + +class SGPropertyNode; + +class Canvas; +typedef boost::shared_ptr<Canvas> CanvasPtr; +typedef boost::weak_ptr<Canvas> CanvasWeakPtr; + +class PropertyBasedElement; +typedef boost::shared_ptr<PropertyBasedElement> PropertyBasedElementPtr; +typedef boost::weak_ptr<PropertyBasedElement> PropertyBasedElementWeakPtr; + +namespace canvas +{ + class Group; + class MouseEvent; + + class Placement; + typedef boost::shared_ptr<Placement> PlacementPtr; + typedef std::vector<PlacementPtr> Placements; + typedef boost::function<Placements( const SGPropertyNode*, + CanvasPtr )> PlacementFactory; + + class Window; + typedef boost::shared_ptr<Window> WindowPtr; + typedef boost::weak_ptr<Window> WindowWeakPtr; +} + +#endif /* CANVAS_FWD_HPP_ */ diff --git a/src/Canvas/canvas_mgr.cxx b/src/Canvas/canvas_mgr.cxx index b8300654a..21fcccf80 100644 --- a/src/Canvas/canvas_mgr.cxx +++ b/src/Canvas/canvas_mgr.cxx @@ -19,131 +19,38 @@ #include "canvas_mgr.hxx" #include "canvas.hxx" -#include <Main/fg_props.hxx> +#include <boost/bind.hpp> -#include <osg/Camera> -#include <osg/Texture2D> +typedef boost::shared_ptr<Canvas> CanvasPtr; +CanvasPtr canvasFactory(SGPropertyNode* node) +{ + return CanvasPtr(new Canvas(node)); +} -#include <stdexcept> -#include <string> //------------------------------------------------------------------------------ CanvasMgr::CanvasMgr(): - _props( fgGetNode("/canvas", true) ) + PropertyBasedMgr("/canvas", "texture", &canvasFactory) { - -} - -//------------------------------------------------------------------------------ -CanvasMgr::~CanvasMgr() -{ - -} - -//------------------------------------------------------------------------------ -void CanvasMgr::init() -{ - _props->addChangeListener(this); - triggerChangeRecursive(_props); -} - -//------------------------------------------------------------------------------ -void CanvasMgr::reinit() -{ - -} - -//------------------------------------------------------------------------------ -void CanvasMgr::shutdown() -{ - _props->removeChangeListener(this); -} - -//------------------------------------------------------------------------------ -void CanvasMgr::bind() -{ -} - -//------------------------------------------------------------------------------ -void CanvasMgr::unbind() -{ -} - -//------------------------------------------------------------------------------ -void CanvasMgr::update(double delta_time_sec) -{ - for( size_t i = 0; i < _canvases.size(); ++i ) - if( _canvases[i] ) - _canvases[i]->update(delta_time_sec); -} - -//------------------------------------------------------------------------------ -void CanvasMgr::childAdded( SGPropertyNode * parent, - SGPropertyNode * child ) -{ - if( parent != _props ) - return; - - if( child->getNameString() == "texture" ) - textureAdded(child); -} - -//------------------------------------------------------------------------------ -void CanvasMgr::childRemoved( SGPropertyNode * parent, - SGPropertyNode * child ) -{ - if( parent != _props ) - return; - - if( child->getNameString() == "texture" ) - { - size_t index = child->getIndex(); - - if( index >= _canvases.size() ) - SG_LOG(SG_GL, SG_WARN, "can't removed unknown texture[" << index << "]!"); - else - // remove the canvas... - _canvases[index].reset(); - } + Canvas::addPlacementFactory + ( + "object", + boost::bind + ( + &FGODGauge::set_texture, + _1, + boost::bind(&Canvas::getTexture, _2), + boost::bind(&Canvas::getCullCallback, _2) + ) + ); } //------------------------------------------------------------------------------ unsigned int CanvasMgr::getCanvasTexId(size_t index) const { - if( index >= _canvases.size() - || !_canvases[index] ) + if( index >= _elements.size() + || !_elements[index] ) return 0; - return _canvases[index]->getTexId(); -} - -//------------------------------------------------------------------------------ -void CanvasMgr::textureAdded(SGPropertyNode* node) -{ - size_t index = node->getIndex(); - - if( index >= _canvases.size() ) - { - if( index > _canvases.size() ) - SG_LOG(SG_GL, SG_WARN, "Skipping unused texture slot(s)!"); - - _canvases.resize(index + 1); - } - else if( _canvases[index] ) - SG_LOG(SG_GL, SG_WARN, "texture[" << index << "] already exists!"); - - _canvases[index].reset( new Canvas() ); - _canvases[index]->reset(node); -} - -//------------------------------------------------------------------------------ -void CanvasMgr::triggerChangeRecursive(SGPropertyNode* node) -{ - node->getParent()->fireChildAdded(node); - - if( node->nChildren() == 0 && node->getType() != simgear::props::NONE ) - return node->fireValueChanged(); - - for( int i = 0; i < node->nChildren(); ++i ) - triggerChangeRecursive( node->getChild(i) ); + return static_cast<Canvas*>(_elements[index].get())->getTexId(); } diff --git a/src/Canvas/canvas_mgr.hxx b/src/Canvas/canvas_mgr.hxx index d1ae92603..10cbe9a76 100644 --- a/src/Canvas/canvas_mgr.hxx +++ b/src/Canvas/canvas_mgr.hxx @@ -19,36 +19,13 @@ #ifndef CANVAS_MGR_H_ #define CANVAS_MGR_H_ -#include <simgear/props/props.hxx> -#include <simgear/structure/subsystem_mgr.hxx> - -#include <boost/shared_ptr.hpp> -#include <vector> - -class Canvas; -typedef boost::shared_ptr<Canvas> CanvasPtr; +#include "property_based_mgr.hxx" class CanvasMgr: - public SGSubsystem, - public SGPropertyChangeListener + public PropertyBasedMgr { public: CanvasMgr(); - virtual ~CanvasMgr(); - - virtual void init(); - virtual void reinit(); - virtual void shutdown(); - - virtual void bind(); - virtual void unbind(); - - virtual void update(double delta_time_sec); - - virtual void childAdded( SGPropertyNode * parent, - SGPropertyNode * child ); - virtual void childRemoved( SGPropertyNode * parent, - SGPropertyNode * child ); /** * Get OpenGL texture name for given canvas @@ -57,23 +34,6 @@ class CanvasMgr: * @return OpenGL texture name */ unsigned int getCanvasTexId(size_t index) const; - - private: - - /** Root node for everything concerning the canvas system */ - SGPropertyNode_ptr _props; - - /** The actual canvases */ - std::vector<CanvasPtr> _canvases; - - void textureAdded(SGPropertyNode* node); - - /** - * Trigger a childAdded and valueChanged event for every child of node - * (Unlimited depth) and node itself. - */ - void triggerChangeRecursive(SGPropertyNode* node); - }; #endif /* CANVAS_MGR_H_ */ diff --git a/src/Canvas/elements/text.cxx b/src/Canvas/elements/text.cxx index 725d67e12..518e78378 100644 --- a/src/Canvas/elements/text.cxx +++ b/src/Canvas/elements/text.cxx @@ -126,54 +126,12 @@ namespace canvas _font_size = getChildDefault<float>(_node, "character-size", 32); _font_aspect = getChildDefault<float>(_node, "character-aspect-ratio", 1); - - _node->tie - ( - "alignment", - SGRawValueMethods<Text, const char*> - ( - *this, - &Text::getAlignment, - &Text::setAlignment - ) - ); - _node->tie - ( - "padding", - SGRawValueMethods<osgText::Text, float> - ( - *_text.get(), - &osgText::Text::getBoundingBoxMargin, - &osgText::Text::setBoundingBoxMargin - ) - ); - typedef SGRawValueMethods<osgText::Text, int> TextIntMethods; - _node->tie - ( - "draw-mode", - // TEXT = 1 default - // BOUNDINGBOX = 2 - // FILLEDBOUNDINGBOX = 4 - // ALIGNMENT = 8 - TextIntMethods - ( - *_text.get(), - (TextIntMethods::getter_t)&osgText::Text::getDrawMode, - (TextIntMethods::setter_t)&osgText::Text::setDrawMode - ) - ); } //---------------------------------------------------------------------------- Text::~Text() { - if( _node ) - { - _node->untie("alignment"); - _node->untie("padding"); - _node->untie("draw-mode"); - } - _node = 0; + } //---------------------------------------------------------------------------- @@ -210,10 +168,20 @@ namespace canvas #include "text-alignment.hxx" #undef ENUM_MAPPING else + { + if( !align_string.empty() ) + SG_LOG + ( + SG_GENERAL, + SG_WARN, + "canvas::Text: unknown alignment '" << align_string << "'" + ); _text->setAlignment(osgText::Text::LEFT_BASE_LINE); + } } //---------------------------------------------------------------------------- +#if 0 const char* Text::getAlignment() const { switch( _text->getAlignment() ) @@ -227,7 +195,7 @@ namespace canvas return "unknown"; } } - +#endif //---------------------------------------------------------------------------- void Text::childChanged(SGPropertyNode* child) { @@ -247,10 +215,20 @@ namespace canvas osgText::String( child->getStringValue(), osgText::String::ENCODING_UTF8 ) ); + else if( name == "padding" ) + _text->setBoundingBoxMargin( child->getFloatValue() ); + else if( name == "draw-mode" ) + // TEXT = 1 default + // BOUNDINGBOX = 2 + // FILLEDBOUNDINGBOX = 4 + // ALIGNMENT = 8 + _text->setDrawMode( child->getIntValue() ); else if( name == "max-width" ) _text->setMaximumWidth( child->getFloatValue() ); else if( name == "font" ) setFont( child->getStringValue() ); + else if( name == "alignment" ) + setAlignment( child->getStringValue() ); } //---------------------------------------------------------------------------- diff --git a/src/Canvas/elements/text.hxx b/src/Canvas/elements/text.hxx index 6147e4367..630e717cf 100644 --- a/src/Canvas/elements/text.hxx +++ b/src/Canvas/elements/text.hxx @@ -33,14 +33,12 @@ namespace canvas { public: Text(SGPropertyNode_ptr node); - virtual ~Text(); + ~Text(); virtual void update(double dt); void setFont(const char* name); - void setAlignment(const char* align); - const char* getAlignment() const; protected: diff --git a/src/Canvas/gui_mgr.cxx b/src/Canvas/gui_mgr.cxx new file mode 100644 index 000000000..208d209c9 --- /dev/null +++ b/src/Canvas/gui_mgr.cxx @@ -0,0 +1,297 @@ +// Canvas gui/dialog manager +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "gui_mgr.hxx" +#include "mouse_event.hxx" +#include <Canvas/window.hxx> +#include <Canvas/canvas.hxx> + +#include <Main/globals.hxx> +#include <Viewer/CameraGroup.hxx> +#include <Viewer/renderer.hxx> + +#include <osgViewer/Viewer> +#include <osgGA/GUIEventHandler> + +#include <boost/bind.hpp> + +/** + * Event handler + */ +class GUIEventHandler: + public osgGA::GUIEventHandler +{ + public: + GUIEventHandler(GUIMgr* gui_mgr): + _gui_mgr( gui_mgr ) + {} + + bool handle( const osgGA::GUIEventAdapter& ea, + osgGA::GUIActionAdapter& aa, + osg::Object*, + osg::NodeVisitor* ) + { + return _gui_mgr->handleEvent(ea); + } + + protected: + GUIMgr *_gui_mgr; +}; + +/** + * Track a canvas placement on a window + */ +class WindowPlacement: + public canvas::Placement +{ + public: + WindowPlacement( canvas::WindowPtr window, + CanvasPtr canvas ): + _window(window), + _canvas(canvas) + {} + + /** + * Remove placement from window + */ + virtual ~WindowPlacement() + { + canvas::WindowPtr window = _window.lock(); + CanvasPtr canvas = _canvas.lock(); + + if( window && canvas && canvas == window->getCanvas().lock() ) + window->setCanvas( CanvasPtr() ); + } + + private: + canvas::WindowWeakPtr _window; + CanvasWeakPtr _canvas; +}; + +//------------------------------------------------------------------------------ +typedef boost::shared_ptr<canvas::Window> WindowPtr; +WindowPtr windowFactory(SGPropertyNode* node) +{ + return WindowPtr(new canvas::Window(node)); +} + +//------------------------------------------------------------------------------ +GUIMgr::GUIMgr(): + PropertyBasedMgr("/sim/gui/canvas", "window", &windowFactory), + _event_handler( new GUIEventHandler(this) ), + _transform( new osg::MatrixTransform ), + _geode_windows( new osg::Geode ), + _width(_props, "size[0]"), + _height(_props, "size[1]"), + _last_push(-1) +{ + _width = _height = -1; + + osg::Camera* camera = + flightgear::getGUICamera( flightgear::CameraGroup::getDefault() ); + assert(camera); + + osg::Viewport* vp = camera->getViewport(); + handleResize(vp->x(), vp->y(), vp->width(), vp->height()); + + _transform->addChild(_geode_windows); + camera->addChild(_transform); + + Canvas::addPlacementFactory + ( + "window", + boost::bind(&GUIMgr::addPlacement, this, _1, _2) + ); +} + +//------------------------------------------------------------------------------ +void GUIMgr::init() +{ + PropertyBasedMgr::init(); + + globals->get_renderer() + ->getViewer() + ->addEventHandler( _event_handler ); +} + +//------------------------------------------------------------------------------ +void GUIMgr::shutdown() +{ + PropertyBasedMgr::shutdown(); + + globals->get_renderer() + ->getViewer() + ->removeEventHandler( _event_handler ); +} + +//------------------------------------------------------------------------------ +void GUIMgr::elementCreated(PropertyBasedElementPtr element) +{ + _geode_windows->addDrawable + ( + static_cast<canvas::Window*>(element.get())->getDrawable() + ); +} + +//------------------------------------------------------------------------------ +bool GUIMgr::handleEvent(const osgGA::GUIEventAdapter& ea) +{ + switch( ea.getEventType() ) + { + case osgGA::GUIEventAdapter::PUSH: + case osgGA::GUIEventAdapter::RELEASE: +// case osgGA::GUIEventAdapter::DOUBLECLICK: +// // DOUBLECLICK doesn't seem to be triggered... + case osgGA::GUIEventAdapter::DRAG: + case osgGA::GUIEventAdapter::MOVE: + case osgGA::GUIEventAdapter::SCROLL: + return handleMouse(ea); +// case osgGA::GUIEventAdapter::MOVE: +// std::cout << "MOVE" << std::endl; +// break; + case osgGA::GUIEventAdapter::RESIZE: + handleResize( ea.getWindowX(), + ea.getWindowY(), + ea.getWindowWidth(), + ea.getWindowHeight() ); + return true; + default: + return false; + } +} + +//------------------------------------------------------------------------------ +canvas::WindowPtr GUIMgr::getWindow(size_t i) +{ + return boost::shared_static_cast<canvas::Window>(_elements[i]); +} + +//------------------------------------------------------------------------------ +canvas::Placements GUIMgr::addPlacement( const SGPropertyNode* node, + CanvasPtr canvas ) +{ + int placement_index = node->getIntValue("index", -1); + + canvas::Placements placements; + for( size_t i = 0; i < _elements.size(); ++i ) + { + if( placement_index > 0 && static_cast<int>(i) != placement_index ) + continue; + + canvas::WindowPtr window = getWindow(i); + if( !window ) + continue; + + window->setCanvas(canvas); + placements.push_back( + canvas::PlacementPtr(new WindowPlacement(window, canvas)) + ); + } + return placements; +} + +//------------------------------------------------------------------------------ +bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea) +{ + canvas::MouseEvent event( ea.getEventType() ); + + event.x = 0.5 * (ea.getXnormalized() + 1) * _width + 0.5; + event.y = 0.5 * (ea.getYnormalized() + 1) * _height + 0.5; + if( ea.getMouseYOrientation() + != osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS ) + event.y = _height - event.y; + + event.button = ea.getButton(); + event.state = ea.getButtonMask(); + event.mod = ea.getModKeyMask(); + event.scroll = ea.getScrollingMotion(); + + int window_at_cursor = -1; + for( size_t i = 0; i < _elements.size(); ++i ) + { + if( _elements[i] + && getWindow(i)->getRegion().contains(event.x, event.y) ) + { + window_at_cursor = i; + break; + } + } + + int target_window = window_at_cursor; + switch( ea.getEventType() ) + { + case osgGA::GUIEventAdapter::PUSH: + _last_push = window_at_cursor; + break; + case osgGA::GUIEventAdapter::SCROLL: + case osgGA::GUIEventAdapter::MOVE: + break; + + case osgGA::GUIEventAdapter::RELEASE: + if( _last_push < 0 ) + return false; + + target_window = _last_push; + _last_push = -1; + break; + + case osgGA::GUIEventAdapter::DRAG: + target_window = _last_push; + break; + + default: + return false; + } + + if( target_window >= 0 ) + { + canvas::WindowPtr window = getWindow(target_window); + + event.dx = event.x - _last_x; + event.dy = event.y - _last_y; + + _last_x = event.x; + _last_y = event.y; + + // Let the event position be always relative to the top left window corner + event.x -= window->getRegion().x(); + event.y -= window->getRegion().y(); + + return window->handleMouseEvent(event); + } + else + return false; +} + +//------------------------------------------------------------------------------ +void GUIMgr::handleResize(int x, int y, int width, int height) +{ + if( _width == width && _height == height ) + return; + + _width = width; + _height = height; + + // Origin should be at top left corner, therefore we need to mirror the y-axis + _transform->setMatrix(osg::Matrix( + 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, _height, 0, 1 + )); +} diff --git a/src/Canvas/gui_mgr.hxx b/src/Canvas/gui_mgr.hxx new file mode 100644 index 000000000..6b010a020 --- /dev/null +++ b/src/Canvas/gui_mgr.hxx @@ -0,0 +1,71 @@ +// Canvas gui/dialog manager +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef CANVAS_GUI_MGR_HXX_ +#define CANVAS_GUI_MGR_HXX_ + +#include "property_based_mgr.hxx" +#include <Canvas/canvas_fwd.hpp> +#include <Canvas/placement.hxx> + +#include <simgear/props/propertyObject.hxx> + +#include <osg/ref_ptr> +#include <osg/Geode> +#include <osg/MatrixTransform> + +namespace osgGA +{ + class GUIEventAdapter; +} + +class GUIEventHandler; +class GUIMgr: + public PropertyBasedMgr +{ + public: + GUIMgr(); + + virtual void init(); + virtual void shutdown(); + + virtual void elementCreated(PropertyBasedElementPtr element); + + bool handleEvent(const osgGA::GUIEventAdapter& ea); + + protected: + osg::ref_ptr<GUIEventHandler> _event_handler; + osg::ref_ptr<osg::MatrixTransform> _transform; + osg::ref_ptr<osg::Geode> _geode_windows; + + simgear::PropertyObject<int> _width, + _height; + + int _last_push, + _last_x, + _last_y; + + canvas::WindowPtr getWindow(size_t i); + canvas::Placements addPlacement( const SGPropertyNode*, + CanvasPtr canvas ); + + bool handleMouse(const osgGA::GUIEventAdapter& ea); + void handleResize(int x, int y, int width, int height); +}; + +#endif /* CANVAS_GUI_MGR_HXX_ */ diff --git a/src/Canvas/mouse_event.hxx b/src/Canvas/mouse_event.hxx new file mode 100644 index 000000000..851f0f65d --- /dev/null +++ b/src/Canvas/mouse_event.hxx @@ -0,0 +1,52 @@ +// Mouse event +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef CANVAS_MOUSE_EVENT_HXX_ +#define CANVAS_MOUSE_EVENT_HXX_ + +#include <osgGA/GUIEventAdapter> + +namespace canvas +{ + + struct MouseEvent + { + typedef osgGA::GUIEventAdapter::EventType EventType; + typedef osgGA::GUIEventAdapter::ScrollingMotion Scroll; + + MouseEvent(EventType type): + type(type), + x(-1), y(-1), + dx(0), dy(0), + button(-1), + state(-1), + mod(-1) + {} + + EventType type; + int x, y, + dx, dy, + button, //<! Button for this event + state, //<! Current button state + mod; //<! Keyboard modifier state + Scroll scroll; + }; + +} // namespace canvas + +#endif /* CANVAS_MOUSE_EVENT_HXX_ */ diff --git a/src/Canvas/placement.cxx b/src/Canvas/placement.cxx new file mode 100644 index 000000000..c2634b62b --- /dev/null +++ b/src/Canvas/placement.cxx @@ -0,0 +1,36 @@ +// Base class for canvas placements +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "placement.hxx" + +namespace canvas +{ + + //---------------------------------------------------------------------------- + Placement::Placement() + { + + } + + //---------------------------------------------------------------------------- + Placement::~Placement() + { + + } + +} // namespace canvas diff --git a/src/Canvas/placement.hxx b/src/Canvas/placement.hxx new file mode 100644 index 000000000..e424f2129 --- /dev/null +++ b/src/Canvas/placement.hxx @@ -0,0 +1,38 @@ +// Base class for canvas placements +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef CANVAS_PLACEMENT_HXX_ +#define CANVAS_PLACEMENT_HXX_ + +namespace canvas +{ + + class Placement + { + public: + Placement(); + virtual ~Placement() = 0; + + private: + Placement(const Placement&) /* = delete */; + Placement& operator=(const Placement&) /* = delete */; + }; + +} // namespace canvas + +#endif /* CANVAS_PLACEMENT_HXX_ */ diff --git a/src/Canvas/property_based_element.cxx b/src/Canvas/property_based_element.cxx new file mode 100644 index 000000000..3b801a7c1 --- /dev/null +++ b/src/Canvas/property_based_element.cxx @@ -0,0 +1,32 @@ +// Base class for elements of property controlled subsystems +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "property_based_element.hxx" + +//------------------------------------------------------------------------------ +PropertyBasedElement::PropertyBasedElement(SGPropertyNode* node): + _node(node) +{ + _node->addChangeListener(this); +} + +//------------------------------------------------------------------------------ +PropertyBasedElement::~PropertyBasedElement() +{ + _node->removeChangeListener(this); +} diff --git a/src/Canvas/property_based_element.hxx b/src/Canvas/property_based_element.hxx new file mode 100644 index 000000000..358ae7d0c --- /dev/null +++ b/src/Canvas/property_based_element.hxx @@ -0,0 +1,46 @@ +// Base class for elements of property controlled subsystems +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef PROPERTY_BASED_ELEMENT_HXX_ +#define PROPERTY_BASED_ELEMENT_HXX_ + +#include <Canvas/canvas_fwd.hpp> +#include <simgear/props/props.hxx> + +/** + * Base class for a property controlled element + */ +class PropertyBasedElement: + public SGPropertyChangeListener +{ + public: + PropertyBasedElement(SGPropertyNode* node); + virtual ~PropertyBasedElement(); + + virtual void update(double delta_time_sec) = 0; + + protected: + + friend class PropertyBasedMgr; + + SGPropertyNode_ptr _node; + PropertyBasedElementWeakPtr _self; +}; + + +#endif /* PROPERTY_BASED_ELEMENT_HXX_ */ diff --git a/src/Canvas/property_based_mgr.cxx b/src/Canvas/property_based_mgr.cxx new file mode 100644 index 000000000..1d57228df --- /dev/null +++ b/src/Canvas/property_based_mgr.cxx @@ -0,0 +1,118 @@ +// Base class for all property controlled subsystems +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "property_based_mgr.hxx" +#include "property_helper.hxx" +#include <Main/fg_props.hxx> + +#include <stdexcept> +#include <string> + +//------------------------------------------------------------------------------ +void PropertyBasedMgr::init() +{ + _props->addChangeListener(this); + canvas::triggerChangeRecursive(_props); +} + +//------------------------------------------------------------------------------ +void PropertyBasedMgr::shutdown() +{ + _props->removeChangeListener(this); +} + +//------------------------------------------------------------------------------ +void PropertyBasedMgr::update(double delta_time_sec) +{ + for( size_t i = 0; i < _elements.size(); ++i ) + if( _elements[i] ) + _elements[i]->update(delta_time_sec); +} + +//------------------------------------------------------------------------------ +void PropertyBasedMgr::childAdded( SGPropertyNode * parent, + SGPropertyNode * child ) +{ + if( parent != _props || child->getNameString() != _name_elements ) + return; + + size_t index = child->getIndex(); + + if( index >= _elements.size() ) + { + if( index > _elements.size() ) + SG_LOG + ( + SG_GENERAL, + SG_WARN, + "Skipping unused " << _name_elements << " slot(s)!" + ); + + _elements.resize(index + 1); + } + else if( _elements[index] ) + SG_LOG + ( + SG_GENERAL, + SG_WARN, + _name_elements << "[" << index << "] already exists!" + ); + + PropertyBasedElementPtr el = _element_factory(child); + el->_self = el; + _elements[index] = el; + elementCreated( el ); +} + +//------------------------------------------------------------------------------ +void PropertyBasedMgr::childRemoved( SGPropertyNode * parent, + SGPropertyNode * child ) +{ + if( parent != _props || child->getNameString() != _name_elements ) + return; + + size_t index = child->getIndex(); + + if( index >= _elements.size() ) + SG_LOG + ( + SG_GENERAL, + SG_WARN, + "can't removed unknown " << _name_elements << "[" << index << "]!" + ); + else + // remove the element... + _elements[index].reset(); +} + +//------------------------------------------------------------------------------ +PropertyBasedMgr::PropertyBasedMgr( const std::string& path_root, + const std::string& name_elements, + ElementFactory element_factory ): + _props( fgGetNode(path_root, true) ), + _name_elements( name_elements ), + _element_factory( element_factory ) +{ + +} + +//------------------------------------------------------------------------------ +PropertyBasedMgr::~PropertyBasedMgr() +{ + +} diff --git a/src/Canvas/property_based_mgr.hxx b/src/Canvas/property_based_mgr.hxx new file mode 100644 index 000000000..904b3c619 --- /dev/null +++ b/src/Canvas/property_based_mgr.hxx @@ -0,0 +1,75 @@ +// Base class for all property controlled subsystems +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef PROPERTY_BASED_MGR_HXX_ +#define PROPERTY_BASED_MGR_HXX_ + +#include "property_based_element.hxx" +#include <simgear/structure/subsystem_mgr.hxx> + +#include <boost/shared_ptr.hpp> +#include <boost/function.hpp> +#include <vector> + +class PropertyBasedMgr: + public SGSubsystem, + public SGPropertyChangeListener +{ + public: + virtual void init(); + virtual void shutdown(); + + virtual void update (double delta_time_sec); + + virtual void childAdded( SGPropertyNode * parent, + SGPropertyNode * child ); + virtual void childRemoved( SGPropertyNode * parent, + SGPropertyNode * child ); + + virtual void elementCreated(PropertyBasedElementPtr element) {} + + protected: + + typedef boost::function<PropertyBasedElementPtr(SGPropertyNode*)> + ElementFactory; + + /** Branch in the property tree for this property managed subsystem */ + SGPropertyNode* _props; + + /** Property name of managed elements */ + const std::string _name_elements; + + /** The actually managed elements */ + std::vector<PropertyBasedElementPtr> _elements; + + /** Function object which creates a new element */ + ElementFactory _element_factory; + + /** + * @param path_root Path to property branch used for controlling this + * subsystem + * @param name_elements The name of the nodes for the managed elements + */ + PropertyBasedMgr( const std::string& path_root, + const std::string& name_elements, + ElementFactory element_factory ); + virtual ~PropertyBasedMgr() = 0; + +}; + +#endif /* PROPERTY_BASED_MGR_HXX_ */ diff --git a/src/Canvas/property_helper.cxx b/src/Canvas/property_helper.cxx index 7bf65404e..795dd72d0 100644 --- a/src/Canvas/property_helper.cxx +++ b/src/Canvas/property_helper.cxx @@ -44,4 +44,16 @@ namespace canvas for( size_t i = 0; i < num_channels; ++i ) nodes.push_back( getChildDefault(color, channels[i], def[i]) ); } + + //---------------------------------------------------------------------------- + void triggerChangeRecursive(SGPropertyNode* node) + { + node->getParent()->fireChildAdded(node); + + if( node->nChildren() == 0 && node->getType() != simgear::props::NONE ) + return node->fireValueChanged(); + + for( int i = 0; i < node->nChildren(); ++i ) + triggerChangeRecursive( node->getChild(i) ); + } } diff --git a/src/Canvas/property_helper.hxx b/src/Canvas/property_helper.hxx index 429cc636e..dbafdd77e 100644 --- a/src/Canvas/property_helper.hxx +++ b/src/Canvas/property_helper.hxx @@ -78,6 +78,12 @@ namespace canvas std::vector<SGPropertyNode_ptr>& nodes, const osg::Vec4& def = osg::Vec4(0,0,0,1) ); + /** + * Trigger a childAdded and valueChanged event for every child of node + * (Unlimited depth) and node itself. + */ + void triggerChangeRecursive(SGPropertyNode* node); + } // namespace canvas #endif /* PROPERTY_HELPER_HXX_ */ diff --git a/src/Canvas/rect.hxx b/src/Canvas/rect.hxx new file mode 100644 index 000000000..b3b42cc4e --- /dev/null +++ b/src/Canvas/rect.hxx @@ -0,0 +1,68 @@ +// Class representing a rectangular region +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef CANVAS_RECT_HXX_ +#define CANVAS_RECT_HXX_ + +#include <osg/Vec2> + +namespace canvas +{ + template<typename T> + class Rect + { + public: + Rect() {} + + Rect(T x, T y, T w, T h): + _x1(x), + _x2(x + w), + _y1(y), + _y2(y + h) + {} + + void set(T x, T y, T w, T h) + { + _x1 = x; + _x2 = x + w; + _y1 = y; + _y2 = y + h; + } + + T x() const { return _x1; } + T y() const { return _y1; } + T width() const { return _x2 - _x1; } + T height() const { return _y2 - _y1; } + + T l() const { return _x1; } + T r() const { return _x2; } + T t() const { return _y1; } + T b() const { return _y2; } + + bool contains(T x, T y) const + { + return _x1 <= x && x <= _x2 + && _y1 <= y && y <= _y2; + } + + private: + T _x1, _x2, _y1, _y2; + }; +} // namespace canvas + +#endif /* CANVAS_RECT_HXX_ */ diff --git a/src/Canvas/window.cxx b/src/Canvas/window.cxx new file mode 100644 index 000000000..40dc85500 --- /dev/null +++ b/src/Canvas/window.cxx @@ -0,0 +1,177 @@ +// Window for placing a Canvas onto it (for dialogs, menus, etc.) +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "window.hxx" +#include <Canvas/canvas.hxx> + +#include <osg/BlendFunc> +#include <osg/Geometry> +#include <osg/Texture2D> +#include <osgGA/GUIEventHandler> + +/** + * Callback to enable/disable rendering of canvas displayed inside windows + */ +class CullCallback: + public osg::Drawable::CullCallback +{ + public: + CullCallback(Canvas::CameraCullCallback* camera_cull); + + private: + Canvas::CameraCullCallback *_camera_cull; + + virtual bool cull( osg::NodeVisitor* nv, + osg::Drawable* drawable, + osg::RenderInfo* renderInfo ) const; +}; + +//------------------------------------------------------------------------------ +CullCallback::CullCallback(Canvas::CameraCullCallback* camera_cull): + _camera_cull( camera_cull ) +{ + +} + +//------------------------------------------------------------------------------ +bool CullCallback::cull( osg::NodeVisitor* nv, + osg::Drawable* drawable, + osg::RenderInfo* renderInfo ) const +{ + _camera_cull->enableRendering(); + return false; +} + +namespace canvas +{ + //---------------------------------------------------------------------------- + Window::Window(SGPropertyNode* node): + PropertyBasedElement(node), + _dirty(true), + _geometry( new osg::Geometry ), + _vertices( new osg::Vec3Array(4) ), + _tex_coords( new osg::Vec2Array(4) ), + _x(node, "x"), + _y(node, "y"), + _width(node, "size[0]"), + _height(node, "size[1]") + { + _x = 50; + _y = 100; + _width = 400; + _height = 300; + + _geometry->setVertexArray(_vertices); + _geometry->setTexCoordArray(0,_tex_coords); + + osg::Vec4Array* colors = new osg::Vec4Array(1); + (*colors)[0].set(1.0f,1.0f,1.0,1.0f); + _geometry->setColorArray(colors); + _geometry->setColorBinding(osg::Geometry::BIND_OVERALL); + + _geometry->addPrimitiveSet( + new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4) + ); + _geometry->setDataVariance(osg::Object::DYNAMIC); + + osg::StateSet* stateSet = _geometry->getOrCreateStateSet(); + stateSet->setRenderBinDetails(1000, "RenderBin"); + + // speed optimization? + stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + stateSet->setAttribute(new osg::BlendFunc( + osg::BlendFunc::SRC_ALPHA, + osg::BlendFunc::ONE_MINUS_SRC_ALPHA) + ); + stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); + stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + stateSet->setMode(GL_FOG, osg::StateAttribute::OFF); + stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON); + } + + //---------------------------------------------------------------------------- + Window::~Window() + { + + } + + //---------------------------------------------------------------------------- + void Window::update(double delta_time_sec) + { + if( !_dirty ) + return; + _dirty = false; + + _region.set(_x, _y, _width, _height); + + int z = 0; // TODO do we need to use z for depth ordering? + + (*_vertices)[0].set(_region.l(), _region.t(), z); + (*_vertices)[1].set(_region.r(), _region.t(), z); + (*_vertices)[2].set(_region.r(), _region.b(), z); + (*_vertices)[3].set(_region.l(), _region.b(), z); + + float l = 0, t = 1, b = 0, r = 1; + (*_tex_coords)[0].set(l,t); + (*_tex_coords)[1].set(r,t); + (*_tex_coords)[2].set(r,b); + (*_tex_coords)[3].set(l,b); + + _geometry->dirtyDisplayList(); + } + + //---------------------------------------------------------------------------- + void Window::valueChanged (SGPropertyNode * node) + { + if( node->getParent() != _node ) + return; + + const std::string& name = node->getNameString(); + if( name == "x" || name == "y" || name == "size" ) + _dirty = true; + } + + //---------------------------------------------------------------------------- + void Window::setCanvas(CanvasPtr canvas) + { + _canvas = canvas; + _geometry->getOrCreateStateSet() + ->setTextureAttribute(0, canvas ? canvas->getTexture() : 0); + _geometry->dirtyDisplayList(); + _geometry->setCullCallback( + canvas ? new CullCallback(canvas->getCameraCullCallback()) : 0 + ); + } + + //---------------------------------------------------------------------------- + CanvasWeakPtr Window::getCanvas() const + { + return _canvas; + } + + //---------------------------------------------------------------------------- + bool Window::handleMouseEvent(const MouseEvent& event) + { + if( !_canvas.expired() ) + return _canvas.lock()->handleMouseEvent(event); + else + return false; + } + +} // namespace canvas diff --git a/src/Canvas/window.hxx b/src/Canvas/window.hxx new file mode 100644 index 000000000..afefbe515 --- /dev/null +++ b/src/Canvas/window.hxx @@ -0,0 +1,66 @@ +// Window for placing a Canvas onto it (for dialogs, menus, etc.) +// +// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef CANVAS_WINDOW_HXX_ +#define CANVAS_WINDOW_HXX_ + +#include "mouse_event.hxx" +#include "property_based_element.hxx" +#include "rect.hxx" + +#include <simgear/props/propertyObject.hxx> + +#include <osg/Geometry> + +namespace canvas +{ + class Window: + public PropertyBasedElement + { + public: + Window(SGPropertyNode* node); + virtual ~Window(); + + virtual void update(double delta_time_sec); + virtual void valueChanged (SGPropertyNode * node); + + osg::Drawable* getDrawable() { return _geometry; } + const Rect<int>& getRegion() const { return _region; } + + void setCanvas(CanvasPtr canvas); + CanvasWeakPtr getCanvas() const; + + bool handleMouseEvent(const MouseEvent& event); + + protected: + + bool _dirty; + + osg::ref_ptr<osg::Geometry> _geometry; + osg::ref_ptr<osg::Vec3Array> _vertices; + osg::ref_ptr<osg::Vec2Array> _tex_coords; + + simgear::PropertyObject<int> _x, _y, + _width, _height; + Rect<int> _region; + + CanvasWeakPtr _canvas; + }; +} // namespace canvas + +#endif /* CANVAS_WINDOW_HXX_ */ diff --git a/src/Instrumentation/od_gauge.cxx b/src/Instrumentation/od_gauge.cxx index 6314e734a..2a003706f 100644 --- a/src/Instrumentation/od_gauge.cxx +++ b/src/Instrumentation/od_gauge.cxx @@ -311,7 +311,7 @@ class ReplaceStaticTextureVisitor: * Get a list of groups which have been inserted into the scene graph to * replace the given texture */ - Placements& getPlacements() + canvas::Placements& getPlacements() { return _placements; } @@ -386,7 +386,9 @@ class ReplaceStaticTextureVisitor: if( _cull_callback ) group->setCullCallback(_cull_callback); - _placements.push_back(group); + _placements.push_back( + canvas::PlacementPtr(new ObjectPlacement(group)) + ); osg::StateSet* stateSet = group->getOrCreateStateSet(); stateSet->setTextureAttribute( unit, _new_texture, @@ -407,10 +409,38 @@ class ReplaceStaticTextureVisitor: } } - - protected: + class ObjectPlacement: + public canvas::Placement + { + public: + ObjectPlacement(osg::ref_ptr<osg::Group> group): + _group(group) + {} + + /** + * Remove placement from the scene + */ + virtual ~ObjectPlacement() + { + assert( _group->getNumChildren() == 1 ); + osg::Node *child = _group->getChild(0); + + if( _group->getNumParents() ) + { + osg::Group *parent = _group->getParent(0); + parent->addChild(child); + parent->removeChild(_group); + } + + _group->removeChild(child); + } + + private: + osg::ref_ptr<osg::Group> _group; + }; + std::string _tex_name, ///<! Name of texture to be replaced _node_name, ///<! Only replace if node name matches _parent_name; ///<! Only replace if any parent node matches @@ -418,11 +448,12 @@ class ReplaceStaticTextureVisitor: osg::Texture2D *_new_texture; osg::NodeCallback *_cull_callback; - Placements _placements; + canvas::Placements _placements; }; //------------------------------------------------------------------------------ - Placements FGODGauge::set_texture(const char* name, osg::Texture2D* new_texture) +canvas::Placements FGODGauge::set_texture( const char* name, + osg::Texture2D* new_texture ) { osg::Group* root = globals->get_scenery()->get_aircraft_branch(); ReplaceStaticTextureVisitor visitor(name, new_texture); @@ -431,9 +462,9 @@ class ReplaceStaticTextureVisitor: } //------------------------------------------------------------------------------ -Placements FGODGauge::set_texture( const SGPropertyNode* placement, - osg::Texture2D* new_texture, - osg::NodeCallback* cull_callback ) +canvas::Placements FGODGauge::set_texture( const SGPropertyNode* placement, + osg::Texture2D* new_texture, + osg::NodeCallback* cull_callback ) { osg::Group* root = globals->get_scenery()->get_aircraft_branch(); ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback); diff --git a/src/Instrumentation/od_gauge.hxx b/src/Instrumentation/od_gauge.hxx index 7d5ebf4ee..13ae90751 100644 --- a/src/Instrumentation/od_gauge.hxx +++ b/src/Instrumentation/od_gauge.hxx @@ -29,6 +29,9 @@ #ifndef _OD_GAUGE_HXX #define _OD_GAUGE_HXX +#include <Canvas/canvas_fwd.hpp> +#include <Canvas/placement.hxx> + #include <osg/NodeCallback> #include <osg/Group> @@ -38,14 +41,13 @@ namespace osg { } class SGPropertyNode; -typedef std::vector<osg::ref_ptr<osg::Group> > Placements; /** * Owner Drawn Gauge helper class. */ class FGODGauge { -public: + public: FGODGauge(); virtual ~FGODGauge(); @@ -109,7 +111,9 @@ public: * @param new_texture dynamic texture to replace the old one * @return A list of groups which override the given texture */ - Placements set_texture(const char * name, osg::Texture2D* new_texture); + static + canvas::Placements set_texture( const char * name, + osg::Texture2D* new_texture ); /** * Replace an opengl texture name inside the aircraft scene graph. @@ -125,9 +129,10 @@ public: * object * @return A list of groups which override the given texture */ - Placements set_texture( const SGPropertyNode* placement, - osg::Texture2D* new_texture, - osg::NodeCallback* cull_callback = 0 ); + static + canvas::Placements set_texture( const SGPropertyNode* placement, + osg::Texture2D* new_texture, + osg::NodeCallback* cull_callback = 0 ); /** * Get the OSG camera for drawing this gauge. diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 55d5581a5..7b7f7fff9 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -81,6 +81,7 @@ #include <Cockpit/panel_io.hxx> #include <Canvas/canvas_mgr.hxx> +#include <Canvas/gui_mgr.hxx> #include <GUI/new_gui.hxx> #include <Input/input.hxx> #include <Instrumentation/instrument_mgr.hxx> @@ -1164,6 +1165,7 @@ bool fgInitSubsystems() { // Initialize the canvas 2d drawing subsystem. //////////////////////////////////////////////////////////////////// globals->add_subsystem("Canvas", new CanvasMgr, SGSubsystemMgr::DISPLAY); + globals->add_subsystem("CanvasGUI", new GUIMgr, SGSubsystemMgr::DISPLAY); //////////////////////////////////////////////////////////////////// // Initialise the ATIS Manager