Files
simgear/simgear/canvas/elements/CanvasElement.hxx
T

646 lines
18 KiB
C++

///@file
/// Interface for 2D Canvas elements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// 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 GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_ELEMENT_HXX_
#define CANVAS_ELEMENT_HXX_
#include <simgear/canvas/canvas_fwd.hxx>
#include <simgear/canvas/CanvasEvent.hxx>
#include <simgear/props/PropertyBasedElement.hxx>
#include <simgear/misc/stdint.hxx> // for uint32_t
#include <simgear/std/type_traits.hxx>
#include <osg/BoundingBox>
#include <osg/MatrixTransform>
namespace osg
{
class Drawable;
}
namespace simgear
{
namespace canvas
{
/**
* Base class for Elements displayed inside a Canvas.
*/
class Element:
public PropertyBasedElement
{
public:
using SceneGroupWeakPtr = osg::observer_ptr<osg::MatrixTransform>;
/**
* Store pointer to window as user data
*/
class OSGUserData:
public osg::Referenced
{
public:
ElementPtr element;
OSGUserData(ElementPtr element);
};
typedef std::function<bool(Element&, const SGPropertyNode*)>
StyleSetterFunc;
typedef std::function<void(Element&, const SGPropertyNode*)>
StyleSetterFuncUnchecked;
struct StyleSetter:
public SGReferenced
{
StyleSetterFunc func;
SGSharedPtr<StyleSetter> next;
};
struct StyleInfo
{
StyleSetter setter; ///< Function(s) for setting this style
std::string type; ///< Interpolation type
bool inheritable; ///< Whether children can inherit this style from
/// their parents
};
/**
* Coordinate reference frame (eg. "clip" property)
*/
enum ReferenceFrame
{
GLOBAL, ///< Global coordinates
PARENT, ///< Coordinates relative to parent coordinate frame
LOCAL ///< Coordinates relative to local coordinates (parent
/// coordinates with local transformations applied)
};
/**
*
*/
virtual ~Element() = 0;
void onDestroy() override;
ElementPtr getParent() const;
CanvasWeakPtr getCanvas() const;
/**
* Called every frame to update internal state
*
* @param dt Frame time in seconds
*/
void update(double dt) override;
bool addEventListener(const std::string& type, const EventListener& cb);
virtual void clearEventListener();
/// Get (keyboard) input focus.
void setFocus();
virtual bool accept(EventVisitor& visitor);
virtual bool ascend(EventVisitor& visitor);
virtual bool traverse(EventVisitor& visitor);
/// Get the number of event handlers for the given type
size_t numEventHandler(int type) const;
virtual bool handleEvent(const EventPtr& event);
bool dispatchEvent(const EventPtr& event);
/**
*
* @param global_pos Position in global (canvas) coordinate frame
* @param parent_pos Position in parent coordinate frame
* @param local_pos Position in local (element) coordinate frame
*/
virtual bool hitBound( const osg::Vec2f& global_pos,
const osg::Vec2f& parent_pos,
const osg::Vec2f& local_pos ) const;
/**
* Set visibility of the element.
*/
virtual void setVisible(bool visible);
/**
* Get whether the element is visible or hidden.
*/
virtual bool isVisible() const;
/**
* Get the according group in the OSG scene graph
*/
// TODO ref_ptr
osg::MatrixTransform* getSceneGroup() const;
/**
* Transform position to local coordinages.
*/
osg::Vec2f posToLocal(const osg::Vec2f& pos) const;
void childAdded(SGPropertyNode* parent, SGPropertyNode* child) override;
void childRemoved(SGPropertyNode* parent, SGPropertyNode* child) override;
void valueChanged(SGPropertyNode* child) override;
virtual bool setStyle( const SGPropertyNode* child,
const StyleInfo* style_info = 0 );
/**
* Set clipping shape
*
* @note Only "rect(<top>, <right>, <bottom>, <left>)" is supported
* @see http://www.w3.org/TR/CSS21/visufx.html#propdef-clip
*/
void setClip(const std::string& clip);
/**
* Clipping coordinates reference frame
*/
void setClipFrame(ReferenceFrame rf);
/**
*
*/
void setRotation(unsigned int index, double r);
/**
*
*/
void setTranslation(unsigned int index, double x, double y);
/**
*
*
*/
void setTransformEnabled(unsigned int index, bool enabled);
/**
* Get bounding box (may not be as tight as bounding box returned by
* #getTightBoundingBox)
*/
osg::BoundingBox getBoundingBox() const;
/**
* Get tight bounding box (child points are transformed to elements
* coordinate space before calculating the bounding box).
*/
osg::BoundingBox getTightBoundingBox() const;
/**
* Get bounding box with children/drawables transformed by passed matrix
*/
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
/**
* Get the transformation matrix (product of all transforms)
*/
osg::Matrix getMatrix() const;
/**
* Create an canvas Element
*
* @tparam Derived Type of element (needs to derive from Element)
*/
template<typename Derived>
static
std::enable_if_t<
std::is_base_of<Element, Derived>::value,
ElementPtr
>
create( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& style = Style(),
Element* parent = NULL )
{
return ElementPtr( new Derived(canvas, node, style, parent) );
}
protected:
enum Attributes
{
TRANSFORM = 1,
BLEND_FUNC = TRANSFORM << 1,
LAST_ATTRIBUTE = BLEND_FUNC << 1
};
enum TransformType
{
TT_NONE,
TT_MATRIX,
TT_TRANSLATE,
TT_ROTATE,
TT_SCALE
};
class RelativeScissor;
CanvasWeakPtr _canvas;
ElementWeakPtr _parent;
mutable uint32_t _attributes_dirty = 0;
SceneGroupWeakPtr _scene_group;
std::vector<TransformType> _transform_types;
Style _style;
RelativeScissor *_scissor = nullptr;
typedef std::vector<EventListener> Listener;
typedef std::map<int, Listener> ListenerMap;
ListenerMap _listener;
typedef std::map<std::string, StyleInfo> StyleSetters;
static StyleSetters _style_setters;
static void staticInit();
Element( const CanvasWeakPtr& canvas,
const SGPropertyNode_ptr& node,
const Style& parent_style,
ElementWeakPtr parent );
/**
* Returns false on first call and true on any successive call. Use to
* perform initialization tasks which are only required once per element
* type.
*
* @tparam Derived (Derived) class type
*/
template<class Derived>
static bool isInit()
{
static bool is_init = false;
if( is_init )
return true;
is_init = true;
return false;
}
/**
* Register a function for setting a style specified by the given property
*
* @param name Property name
* @param type Interpolation type
* @param setter Setter function
* @param inheritable If this style propagates to child elements
*
* @tparam T1 Type of value used to retrieve value from property
* node
* @tparam T2 Type of value the setter function expects
* @tparam Derived Type of class the setter can be applied to
*
* @note T1 needs to be convertible to T2
*/
template<
typename T1,
typename T2,
class Derived
>
static
StyleSetter
addStyle( const std::string& name,
const std::string& type,
const std::function<void (Derived&, T2)>& setter,
bool inheritable = true )
{
StyleInfo& style_info = _style_setters[ name ];
if( !type.empty() )
{
if( !style_info.type.empty() && type != style_info.type )
SG_LOG
(
SG_GENERAL,
SG_WARN,
"changing animation type for '" << name << "': "
<< style_info.type << " -> " << type
);
style_info.type = type;
}
// TODO check if changed?
style_info.inheritable = inheritable;
StyleSetter* style = &style_info.setter;
while( style->next )
style = style->next;
if( style->func )
style = style->next = new StyleSetter;
style->func = std::bind(&type_match<Derived>::call,
std::placeholders::_1,
std::placeholders::_2,
bindStyleSetter<T1>(name, setter));
return *style;
}
template<
typename T,
class Derived
>
static
StyleSetter
addStyle( const std::string& name,
const std::string& type,
const std::function<void (Derived&, T)>& setter,
bool inheritable = true )
{
return addStyle<T, T>(name, type, setter, inheritable);
}
template<
typename T,
class Derived
>
static
StyleSetter
addStyle( const std::string& name,
const std::string& type,
void (Derived::*setter)(T),
bool inheritable = true )
{
return addStyle<T, T>
(
name,
type,
std::function<void (Derived&, T)>(setter),
inheritable
);
}
template<
typename T1,
typename T2,
class Derived
>
static
StyleSetterFunc
addStyle( const std::string& name,
const std::string& type,
void (Derived::*setter)(T2),
bool inheritable = true )
{
return addStyle<T1>
(
name,
type,
std::function<void (Derived&, T2)>(setter),
inheritable
);
}
template<
class Derived
>
static
StyleSetter
addStyle( const std::string& name,
const std::string& type,
void (Derived::*setter)(const std::string&),
bool inheritable = true )
{
return addStyle<const char*, const std::string&>
(
name,
type,
std::function<void (Derived&, const std::string&)>(setter),
inheritable
);
}
template<
typename T,
class Derived,
class Other,
class OtherRef
>
static
StyleSetter
addStyle( const std::string& name,
const std::string& type,
void (Other::*setter)(T),
OtherRef Derived::*instance_ref,
bool inheritable = true )
{
return addStyle<T, T>
(
name,
type,
bindOther(setter, instance_ref),
inheritable
);
}
template<
typename T1,
typename T2,
class Derived,
class Other,
class OtherRef
>
static
StyleSetter
addStyle( const std::string& name,
const std::string& type,
void (Other::*setter)(T2),
OtherRef Derived::*instance_ref,
bool inheritable = true )
{
return addStyle<T1>
(
name,
type,
bindOther(setter, instance_ref),
inheritable
);
}
template<
typename T1,
typename T2,
class Derived,
class Other,
class OtherRef
>
static
StyleSetter
addStyle( const std::string& name,
const std::string& type,
const std::function<void (Other&, T2)>& setter,
OtherRef Derived::*instance_ref,
bool inheritable = true )
{
return addStyle<T1>
(
name,
type,
bindOther(setter, instance_ref),
inheritable
);
}
template<
class Derived,
class Other,
class OtherRef
>
static
StyleSetter
addStyle( const std::string& name,
const std::string& type,
void (Other::*setter)(const std::string&),
OtherRef Derived::*instance_ref,
bool inheritable = true )
{
return addStyle<const char*, const std::string&>
(
name,
type,
std::function<void (Other&, const std::string&)>(setter),
instance_ref,
inheritable
);
}
template<typename T, class Derived, class Other, class OtherRef>
static
std::function<void (Derived&, T)>
bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref )
{
return std::bind(setter,
std::bind(instance_ref, std::placeholders::_1),
std::placeholders::_2);
}
template<typename T, class Derived, class Other, class OtherRef>
static
std::function<void (Derived&, T)>
bindOther( const std::function<void (Other&, T)>& setter,
OtherRef Derived::*instance_ref )
{
return std::bind(setter,
std::bind(&reference_from_pointer<Other, OtherRef>,
std::bind(instance_ref, std::placeholders::_1)),
std::placeholders::_2);
}
template<typename T1, typename T2, class Derived>
static
StyleSetterFuncUnchecked
bindStyleSetter( const std::string& name,
const std::function<void (Derived&, T2)>& setter )
{
return std::bind(setter,
// We will only call setters with Derived instances, so we can safely
// cast here.
std::bind(&derived_cast<Derived>, std::placeholders::_1),
std::bind(&getValue<T1>, std::placeholders::_2));
}
bool isStyleEmpty(const SGPropertyNode* child) const;
bool canApplyStyle(const SGPropertyNode* child) const;
bool setStyleImpl( const SGPropertyNode* child,
const StyleInfo* style_info = 0 );
const StyleInfo* getStyleInfo(const std::string& name) const;
const StyleSetter* getStyleSetter(const std::string& name) const;
const SGPropertyNode* getParentStyle(const SGPropertyNode* child) const;
virtual void childAdded(SGPropertyNode * child) {}
virtual void childRemoved(SGPropertyNode * child){}
virtual void childChanged(SGPropertyNode * child){}
void setDrawable(osg::Drawable* drawable);
/**
* Get stateset of drawable if available or use transform otherwise
*/
virtual osg::StateSet* getOrCreateStateSet();
void setupStyle();
void updateMatrix() const;
virtual void updateImpl(double dt);
private:
osg::ref_ptr<osg::Drawable> _drawable;
Element(const Element&) = delete;
template<class Derived>
static Derived& derived_cast(Element& el)
{
return static_cast<Derived&>(el);
}
template<class T, class SharedPtr>
static T& reference_from_pointer(const SharedPtr& p)
{
return *get_pointer(p);
}
/**
* Helper to call a function only of the element type can be converted to
* the required type.
*
* @return Whether the function has been called
*/
template<class Derived>
struct type_match
{
static bool call( Element& el,
const SGPropertyNode* prop,
const StyleSetterFuncUnchecked& func )
{
Derived* d = dynamic_cast<Derived*>(&el);
if( !d )
return false;
func(*d, prop);
return true;
}
};
};
} // namespace canvas
template<>
struct enum_traits<canvas::Element::ReferenceFrame>
{
static const char* name()
{
return "canvas::Element::ReferenceFrame";
}
static canvas::Element::ReferenceFrame defVal()
{
return canvas::Element::GLOBAL;
}
static bool validate(int frame)
{
return frame >= canvas::Element::GLOBAL
&& frame <= canvas::Element::LOCAL;
}
};
} // namespace simgear
#endif /* CANVAS_ELEMENT_HXX_ */