Files
eepp/src/eepp/ui/uiwidget.cpp
Martín Lucas Golini eea0f6dfbc CSS clean up.
--HG--
branch : dev
2019-01-21 00:51:27 -03:00

1206 lines
39 KiB
C++

#include <eepp/ui/uiwidget.hpp>
#include <eepp/ui/uithememanager.hpp>
#include <eepp/ui/uistyle.hpp>
#include <eepp/ui/uitooltip.hpp>
#include <eepp/graphics/drawablesearcher.hpp>
#include <eepp/graphics/rectangledrawable.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/scene/actions/actions.hpp>
#include <pugixml/pugixml.hpp>
#include <algorithm>
namespace EE { namespace UI {
UIWidget * UIWidget::New() {
return eeNew( UIWidget, () );
}
UIWidget * UIWidget::NewWithTag( const std::string& tag ) {
return eeNew( UIWidget, ( tag ) );
}
UIWidget::UIWidget( const std::string & tag ) :
UINode(),
mTag( tag ),
mTheme( NULL ),
mStyle( UIStyle::New( this ) ),
mTooltip( NULL ),
mMinControlSize(),
mLayoutWeight(0),
mLayoutGravity(0),
mLayoutWidthRules(WRAP_CONTENT),
mLayoutHeightRules(WRAP_CONTENT),
mLayoutPositionRule(NONE),
mLayoutPositionRuleWidget(NULL),
mAttributesTransactionCount(0)
{
mNodeFlags |= NODE_FLAG_WIDGET;
updateAnchorsDistances();
}
UIWidget::UIWidget() :
UIWidget( "widget" )
{
}
UIWidget::~UIWidget() {
eeSAFE_DELETE( mStyle );
eeSAFE_DELETE( mTooltip );
}
Uint32 UIWidget::getType() const {
return UI_TYPE_WIDGET;
}
bool UIWidget::isType( const Uint32& type ) const {
return UIWidget::getType() == type ? true : UINode::isType( type );
}
void UIWidget::updateAnchorsDistances() {
if ( NULL != mParentCtrl ) {
mDistToBorder = Rect( mPosition.x, mPosition.y, mParentCtrl->getPixelsSize().x - ( mPosition.x + mSize.x ), mParentCtrl->getPixelsSize().y - ( mPosition.y + mSize.y ) );
}
}
Rect UIWidget::getLayoutMargin() const {
return mLayoutMargin;
}
UIWidget * UIWidget::setLayoutMargin(const Rect & margin) {
if ( mLayoutMargin != margin ) {
mLayoutMargin = margin;
notifyLayoutAttrChange();
}
return this;
}
Float UIWidget::getLayoutWeight() const {
return mLayoutWeight;
}
UIWidget * UIWidget::setLayoutWeight(const Float & weight) {
if ( mLayoutWeight != weight ) {
mLayoutWeight = weight;
notifyLayoutAttrChange();
}
return this;
}
Uint32 UIWidget::getLayoutGravity() const {
return mLayoutGravity;
}
UIWidget * UIWidget::setLayoutGravity(const Uint32 & layoutGravity) {
if ( mLayoutGravity != layoutGravity ) {
mLayoutGravity = layoutGravity;
notifyLayoutAttrChange();
}
return this;
}
LayoutSizeRules UIWidget::getLayoutWidthRules() const {
return mLayoutWidthRules;
}
UIWidget * UIWidget::setLayoutWidthRules(const LayoutSizeRules & layoutWidthRules) {
if ( mLayoutWidthRules != layoutWidthRules ) {
mLayoutWidthRules = layoutWidthRules;
notifyLayoutAttrChange();
}
return this;
}
LayoutSizeRules UIWidget::getLayoutHeightRules() const {
return mLayoutHeightRules;
}
UIWidget * UIWidget::setLayoutHeightRules(const LayoutSizeRules & layoutHeightRules) {
if ( mLayoutHeightRules != layoutHeightRules ) {
mLayoutHeightRules = layoutHeightRules;
notifyLayoutAttrChange();
}
return this;
}
UIWidget * UIWidget::setLayoutSizeRules(const LayoutSizeRules & layoutWidthRules, const LayoutSizeRules & layoutHeightRules) {
if ( mLayoutWidthRules != layoutWidthRules || mLayoutHeightRules != layoutHeightRules ) {
mLayoutWidthRules = layoutWidthRules;
mLayoutHeightRules = layoutHeightRules;
notifyLayoutAttrChange();
}
return this;
}
UIWidget * UIWidget::setLayoutPositionRule(const LayoutPositionRules & layoutPositionRule, UIWidget * of) {
if ( mLayoutPositionRule != layoutPositionRule || mLayoutPositionRuleWidget != of ) {
mLayoutPositionRule = layoutPositionRule;
mLayoutPositionRuleWidget = of;
notifyLayoutAttrChange();
}
return this;
}
UIWidget * UIWidget::getLayoutPositionRuleWidget() const {
return mLayoutPositionRuleWidget;
}
LayoutPositionRules UIWidget::getLayoutPositionRule() const {
return mLayoutPositionRule;
}
void UIWidget::createTooltip() {
if ( NULL != mTooltip )
return;
mTooltip = UITooltip::New();
mTooltip->setVisible( false )->setEnabled( false );
mTooltip->setTooltipOf( this );
}
Uint32 UIWidget::onMouseMove( const Vector2i & Pos, const Uint32& Flags ) {
if ( mVisible && NULL != mTooltip && !mTooltip->getText().empty() ) {
EventDispatcher * eventDispatcher = getEventDispatcher();
if ( NULL == eventDispatcher )
return 1;
UIThemeManager * themeManager = UIThemeManager::instance();
Vector2f Pos = eventDispatcher->getMousePosf();
Pos.x += themeManager->getCursorSize().x;
Pos.y += themeManager->getCursorSize().y;
if ( Pos.x + mTooltip->getPixelsSize().getWidth() > eventDispatcher->getSceneNode()->getPixelsSize().getWidth() ) {
Pos.x = eventDispatcher->getMousePos().x - mTooltip->getPixelsSize().getWidth();
}
if ( Pos.y + mTooltip->getPixelsSize().getHeight() > eventDispatcher->getSceneNode()->getPixelsSize().getHeight() ) {
Pos.y = eventDispatcher->getMousePos().y - mTooltip->getPixelsSize().getHeight();
}
if ( Time::Zero == themeManager->getTooltipTimeToShow() ) {
if ( !mTooltip->isVisible() || themeManager->getTooltipFollowMouse() )
mTooltip->setPosition( PixelDensity::pxToDp( Pos ) );
mTooltip->show();
} else {
if ( -1.f != mTooltip->getTooltipTime().asMilliseconds() ) {
mTooltip->addTooltipTime( mSceneNode->getElapsed() );
}
if ( mTooltip->getTooltipTime() >= themeManager->getTooltipTimeToShow() ) {
if ( mTooltip->getTooltipTime().asMilliseconds() != -1.f ) {
mTooltip->setPixelsPosition( Pos );
mTooltip->show();
mTooltip->setTooltipTime( Milliseconds( -1.f ) );
}
}
}
if ( themeManager->getTooltipFollowMouse() ) {
mTooltip->setPixelsPosition( Pos );
}
}
return UINode::onMouseMove( Pos, Flags );
}
Uint32 UIWidget::onMouseLeave( const Vector2i & Pos, const Uint32& Flags ) {
if ( mVisible && NULL != mTooltip && !mTooltip->getText().empty() ) {
mTooltip->setTooltipTime( Milliseconds( 0.f ) );
if ( mTooltip->isVisible() )
mTooltip->hide();
}
return UINode::onMouseLeave( Pos, Flags );
}
UIWidget * UIWidget::setTooltipText( const String& Text ) {
if ( NULL == mTooltip ) { // If the tooltip wasn't created it will avoid to create a new one if the string is ""
if ( Text.size() ) {
createTooltip();
mTooltip->setText( Text );
}
} else { // but if it's created, i will allow it
mTooltip->setText( Text );
}
return this;
}
String UIWidget::getTooltipText() {
if ( NULL != mTooltip )
return mTooltip->getText();
return String();
}
void UIWidget::tooltipRemove() {
mTooltip = NULL;
}
Node * UIWidget::setSize( const Sizef& size ) {
Sizef s( size );
if ( s.x < mMinControlSize.x )
s.x = mMinControlSize.x;
if ( s.y < mMinControlSize.y )
s.y = mMinControlSize.y;
return UINode::setSize( s );
}
UINode * UIWidget::setFlags(const Uint32 & flags) {
if ( flags & ( UI_ANCHOR_LEFT | UI_ANCHOR_TOP | UI_ANCHOR_RIGHT | UI_ANCHOR_BOTTOM ) ) {
updateAnchorsDistances();
}
if ( !( mFlags & UI_AUTO_SIZE ) && ( flags & UI_AUTO_SIZE ) ) {
onAutoSize();
}
return UINode::setFlags( flags );
}
UINode * UIWidget::unsetFlags(const Uint32 & flags) {
if ( flags & ( UI_ANCHOR_LEFT | UI_ANCHOR_TOP | UI_ANCHOR_RIGHT | UI_ANCHOR_BOTTOM ) ) {
updateAnchorsDistances();
}
return UINode::unsetFlags( flags );
}
UIWidget * UIWidget::setAnchors(const Uint32 & flags) {
mFlags &= ~(UI_ANCHOR_LEFT | UI_ANCHOR_TOP | UI_ANCHOR_RIGHT | UI_ANCHOR_BOTTOM);
mFlags |= flags;
updateAnchorsDistances();
return this;
}
void UIWidget::setTheme( UITheme * Theme ) {
mTheme = Theme;
invalidateDraw();
}
UINode * UIWidget::setThemeSkin( const std::string& skinName ) {
return setThemeSkin( NULL != mTheme ? mTheme : UIThemeManager::instance()->getDefaultTheme(), skinName );
}
UINode * UIWidget::setThemeSkin( UITheme * Theme, const std::string& skinName ) {
return UINode::setThemeSkin( Theme, skinName );
}
Node * UIWidget::setSize( const Float& Width, const Float& Height ) {
return UINode::setSize( Width, Height );
}
Node * UIWidget::setId( const std::string& id ) {
Node::setId( id );
reloadStyle( false );
return this;
}
const Sizef& UIWidget::getSize() const {
return UINode::getSize();
}
UITooltip * UIWidget::getTooltip() {
return mTooltip;
}
void UIWidget::onParentSizeChange( const Vector2f& SizeChange ) {
updateAnchors( SizeChange );
UINode::onParentSizeChange( SizeChange );
}
void UIWidget::onPositionChange() {
updateAnchorsDistances();
UINode::onPositionChange();
}
void UIWidget::onVisibilityChange() {
updateAnchorsDistances();
notifyLayoutAttrChange();
UINode::onVisibilityChange();
}
void UIWidget::onSizeChange() {
UINode::onSizeChange();
notifyLayoutAttrChange();
}
void UIWidget::onAutoSize() {
}
void UIWidget::onWidgetCreated() {
}
void UIWidget::notifyLayoutAttrChange() {
if ( 0 == mAttributesTransactionCount ) {
NodeMessage msg( this, NodeMessage::LayoutAttributeChange );
messagePost( &msg );
}
}
void UIWidget::notifyLayoutAttrChangeParent() {
if ( 0 == mAttributesTransactionCount && NULL != mParentCtrl ) {
NodeMessage msg( this, NodeMessage::LayoutAttributeChange );
mParentCtrl->messagePost( &msg );
}
}
void UIWidget::updateAnchors( const Vector2f& SizeChange ) {
if ( !( mFlags & ( UI_ANCHOR_LEFT | UI_ANCHOR_TOP | UI_ANCHOR_RIGHT | UI_ANCHOR_BOTTOM ) ) )
return;
Sizef newSize( mDpSize );
if ( !( mFlags & UI_ANCHOR_LEFT ) ) {
setInternalPosition( Vector2f( mDpPos.x += SizeChange.x, mDpPos.y ) );
}
if ( mFlags & UI_ANCHOR_RIGHT ) {
if ( NULL != mParentCtrl ) {
newSize.x = mParentCtrl->getSize().getWidth() - mDpPos.x - PixelDensity::pxToDpI( mDistToBorder.Right );
if ( newSize.x < mMinControlSize.getWidth() )
newSize.x = mMinControlSize.getWidth();
}
}
if ( !( mFlags & UI_ANCHOR_TOP ) ) {
setInternalPosition( Vector2f( mDpPos.x, mDpPos.y += SizeChange.y ) );
}
if ( mFlags & UI_ANCHOR_BOTTOM ) {
if ( NULL != mParentCtrl ) {
newSize.y = mParentCtrl->getSize().y - mDpPos.y - PixelDensity::pxToDpI( mDistToBorder.Bottom );
if ( newSize.y < mMinControlSize.getHeight() )
newSize.y = mMinControlSize.getHeight();
}
}
if ( newSize != mDpSize )
setSize( newSize );
}
void UIWidget::alignAgainstLayout() {
Vector2f pos = mDpPos;
switch ( fontHAlignGet( mLayoutGravity ) ) {
case UI_HALIGN_CENTER:
pos.x = ( getParent()->getSize().getWidth() - mDpSize.getWidth() ) / 2;
break;
case UI_HALIGN_RIGHT:
pos.x = getParent()->getSize().getWidth() - mLayoutMargin.Right;
break;
case UI_HALIGN_LEFT:
pos.x = mLayoutMargin.Left;
break;
}
switch ( fontVAlignGet( mLayoutGravity ) ) {
case UI_VALIGN_CENTER:
pos.y = ( getParent()->getSize().getHeight() - mDpSize.getHeight() ) / 2;
break;
case UI_VALIGN_BOTTOM:
pos.y = getParent()->getSize().getHeight() - mLayoutMargin.Bottom;
break;
case UI_VALIGN_TOP:
pos.y = mLayoutMargin.Top;
break;
}
setInternalPosition( pos );
}
void UIWidget::reportStyleStateChange() {
if ( NULL != mStyle )
mStyle->onStateChange();
}
bool UIWidget::isSceneNodeLoading() const {
return getSceneNode()->isUISceneNode() ? static_cast<UISceneNode*>( getSceneNode() )->isLoading() : false;
}
const Rectf& UIWidget::getPadding() const {
return mPadding;
}
UIWidget * UIWidget::setPadding(const Rectf& padding) {
if ( padding != mPadding ) {
mPadding = padding;
mRealPadding = PixelDensity::dpToPx( mPadding );
onAutoSize();
onPaddingChange();
notifyLayoutAttrChange();
}
return this;
}
const std::string &UIWidget::getStyleSheetId() const {
return mId;
}
const std::string& UIWidget::getStyleSheetTag() const {
return mTag;
}
const std::vector<std::string> &UIWidget::getStyleSheetClasses() const {
return mClasses;
}
CSS::StyleSheetElement * UIWidget::getStyleSheetParentElement() const {
return NULL != mParentCtrl && mParentCtrl->isWidget() ? dynamic_cast<CSS::StyleSheetElement*>( mParentCtrl ) : NULL;
}
CSS::StyleSheetElement * UIWidget::getStyleSheetPreviousSiblingElement() const {
return NULL != mPrev && mPrev->isWidget() ? dynamic_cast<CSS::StyleSheetElement*>( mPrev ) : NULL;
}
CSS::StyleSheetElement * UIWidget::getStyleSheetNextSiblingElement() const {
return NULL != mNext && mNext->isWidget() ? dynamic_cast<CSS::StyleSheetElement*>( mNext ) : NULL;
}
const std::vector<std::string> &UIWidget::getStyleSheetPseudoClasses() const {
return mPseudoClasses;
}
void UIWidget::updatePseudoClasses() {
mPseudoClasses.clear();
if ( mState & UIState::StateFlagHover )
mPseudoClasses.push_back( "hover" );
if ( mState & UIState::StateFlagFocus )
mPseudoClasses.push_back( "focus" );
if ( mState & UIState::StateFlagSelected )
mPseudoClasses.push_back( "selected" );
if ( mState & UIState::StateFlagPressed )
mPseudoClasses.push_back( "pressed" );
if ( mState & UIState::StateFlagDisabled )
mPseudoClasses.push_back( "disabled" );
invalidateDraw();
}
void UIWidget::addClass( const std::string& cls ) {
if ( !cls.empty() && !containsClass( cls ) ) {
mClasses.push_back( cls );
reloadStyle( false );
}
}
void UIWidget::addClasses( const std::vector<std::string>& classes ) {
if ( !classes.empty() ) {
for ( auto cit = classes.begin(); cit != classes.end(); ++cit ) {
const std::string& cls = *cit;
if ( !cls.empty() && !containsClass( cls ) ) {
mClasses.push_back( cls );
}
}
reloadStyle( false );
}
}
void UIWidget::removeClass( const std::string& cls ) {
if ( containsClass( cls ) ) {
mClasses.erase( std::find( mClasses.begin(), mClasses.end(), cls ) );
reloadStyle( false );
}
}
bool UIWidget::containsClass( const std::string& cls ) {
return std::find( mClasses.begin(), mClasses.end(), cls ) != mClasses.end();
}
void UIWidget::setElementTag( const std::string& tag ) {
if ( mTag != tag ) {
mTag = tag;
reloadStyle( false );
}
}
const std::string& UIWidget::getElementTag() const {
return mTag;
}
void UIWidget::pushState( const Uint32& State, bool emitEvent ) {
if ( !( mState & ( 1 << State ) ) ) {
UINode::pushState( State, false );
if ( NULL != mStyle ) {
mStyle->pushState( State );
updatePseudoClasses();
}
if ( emitEvent )
onStateChange();
}
}
void UIWidget::popState( const Uint32& State, bool emitEvent ) {
if ( mState & ( 1 << State ) ) {
UINode::popState( State, false );
if ( NULL != mStyle ) {
mStyle->popState( State );
updatePseudoClasses();
}
if ( emitEvent )
onStateChange();
}
}
const UIStyle * UIWidget::getUIStyle() const {
return mStyle;
}
void UIWidget::reloadStyle( const bool& reloadChilds ) {
if ( NULL == mStyle && getSceneNode()->isUISceneNode() && static_cast<UISceneNode*>( getSceneNode() )->hasStyleSheet() ) {
mStyle = UIStyle::New( this );
mStyle->setState( mState );
}
if ( NULL != mStyle ) {
mStyle->load();
reportStyleStateChange();
if ( NULL != mChild && reloadChilds ) {
Node * ChildLoop = mChild;
while ( NULL != ChildLoop ) {
if ( ChildLoop->isWidget() )
static_cast<UIWidget*>( ChildLoop )->reloadStyle();
ChildLoop = ChildLoop->getNextNode();
}
}
}
}
void UIWidget::onPaddingChange() {
invalidateDraw();
}
void UIWidget::onThemeLoaded() {
reportStyleStateChange();
}
void UIWidget::beginAttributesTransaction() {
mAttributesTransactionCount++;
}
void UIWidget::endAttributesTransaction() {
mAttributesTransactionCount--;
if ( 0 == mAttributesTransactionCount ) {
notifyLayoutAttrChange();
}
}
const Uint32& UIWidget::getStyleState() const {
return NULL != mStyle ? mStyle->getCurrentState() : mState;
}
const Uint32& UIWidget::getStylePreviousState() const {
return NULL != mStyle ? mStyle->getPreviousState() : mState;
}
bool UIWidget::setAttribute( const std::string& name, const std::string& value, const Uint32& state ) {
return setAttribute( NodeAttribute( name, value ), state );
}
#define SAVE_NORMAL_STATE_ATTR( ATTR_FORMATED ) \
NodeAttribute oldAttribute = mStyle->getAttribute( UIState::StateFlagNormal, attribute.getName() ); \
if ( oldAttribute.isEmpty() && mStyle->getPreviousState() == UIState::StateFlagNormal ) \
mStyle->addAttribute( UIState::StateFlagNormal, NodeAttribute( attribute.getName(), ATTR_FORMATED ) ); \
bool UIWidget::setAttribute( const NodeAttribute& attribute, const Uint32& state ) {
const std::string& name = attribute.getName();
bool attributeSet = true;
if ( "id" == name ) {
setId( attribute.value() );
} else if ( "class" == name ) {
addClasses( String::split( attribute.getValue(), ' ' ) );
} else if ( "x" == name ) {
setLayoutWidthRules( FIXED );
Float newX = attribute.asDpDimensionI();
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
SAVE_NORMAL_STATE_ATTR( String::format( "%.2f", mDpSize.getWidth() ) );
Action * action = Actions::MoveCoordinate::New( getPosition().x, newX, transitionInfo.duration, transitionInfo.timingFunction, Actions::MoveCoordinate::CoordinateX );
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setInternalPosition( Vector2f( newX, mDpPos.y ) );
notifyLayoutAttrChange();
}
} else if ( "y" == name ) {
setLayoutWidthRules( FIXED );
Float newY = attribute.asDpDimensionI();
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
SAVE_NORMAL_STATE_ATTR( String::format( "%.2f", mDpSize.getWidth() ) );
Action * action = Actions::MoveCoordinate::New( getPosition().y, newY, transitionInfo.duration, transitionInfo.timingFunction, Actions::MoveCoordinate::CoordinateY );
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setInternalPosition( Vector2f( mDpPos.x, newY ) );
notifyLayoutAttrChange();
}
} else if ( "width" == name ) {
setLayoutWidthRules( FIXED );
Float newWidth = attribute.asDpDimensionI();
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
SAVE_NORMAL_STATE_ATTR( String::format( "%.2f", mDpSize.getWidth() ) );
Action * action = Actions::ResizeWidth::New( getSize().getWidth(), newWidth, transitionInfo.duration, transitionInfo.timingFunction );
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setInternalWidth( newWidth );
notifyLayoutAttrChange();
}
} else if ( "height" == name ) {
setLayoutHeightRules( FIXED );
Float newHeight = attribute.asDpDimensionI();
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
SAVE_NORMAL_STATE_ATTR( String::format( "%.2f", mDpSize.getHeight() ) );
Action * action = Actions::ResizeHeight::New( getSize().getHeight(), newHeight, transitionInfo.duration, transitionInfo.timingFunction );
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setInternalHeight( newHeight );
notifyLayoutAttrChange();
}
} else if ( "background" == name ) {
Drawable * res = NULL;
if ( String::startsWith( attribute.getValue(), "#" ) ) {
setAttribute( NodeAttribute( "backgroundcolor", attribute.getValue() ) );
} else if ( NULL != ( res = DrawableSearcher::searchByName( attribute.getValue() ) ) ) {
setBackgroundDrawable( state, res, res->getDrawableType() == Drawable::SPRITE );
}
} else if ( "backgroundcolor" == name ) {
Color color = attribute.asColor();
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
Color start( getBackgroundColor( getStylePreviousState() ) );
Action * action = Actions::Tint::New( start, color, true, transitionInfo.duration, transitionInfo.timingFunction, Actions::Tint::Background );
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setBackgroundColor( state, color );
}
} else if ( "backgroundimage" == name ) {
NodeAttribute::FunctionType functionType = NodeAttribute::FunctionType::parse( attribute.getValue() );
if ( !functionType.isEmpty() ) {
if ( functionType.getName() == "linear-gradient" && functionType.getParameters().size() >= 2 ) {
RectangleDrawable * drawable = RectangleDrawable::New();
RectColors rectColors;
if ( Color::isColorString( functionType.getParameters().at(0) ) ) {
rectColors.TopLeft = rectColors.TopRight = Color::fromString( functionType.getParameters().at(0) );
rectColors.BottomLeft = rectColors.BottomRight = Color::fromString( functionType.getParameters().at(1) );
} else if ( functionType.getParameters().size() >= 3 ) {
std::string direction = functionType.getParameters().at(0);
String::toLowerInPlace( direction );
if ( direction == "to bottom" ) {
rectColors.TopLeft = rectColors.TopRight = Color::fromString( functionType.getParameters().at(1) );
rectColors.BottomLeft = rectColors.BottomRight = Color::fromString( functionType.getParameters().at(2) );
} else if ( direction == "to left" ) {
rectColors.TopLeft = rectColors.BottomLeft = Color::fromString( functionType.getParameters().at(2) );
rectColors.TopRight = rectColors.BottomRight = Color::fromString( functionType.getParameters().at(1) );
} else if ( direction == "to right" ) {
rectColors.TopLeft = rectColors.BottomLeft = Color::fromString( functionType.getParameters().at(1) );
rectColors.TopRight = rectColors.BottomRight = Color::fromString( functionType.getParameters().at(2) );
} else if ( direction == "to top" ) {
rectColors.TopLeft = rectColors.TopRight = Color::fromString( functionType.getParameters().at(2) );
rectColors.BottomLeft = rectColors.BottomRight = Color::fromString( functionType.getParameters().at(1) );
} else {
rectColors.TopLeft = rectColors.TopRight = Color::fromString( functionType.getParameters().at(1) );
rectColors.BottomLeft = rectColors.BottomRight = Color::fromString( functionType.getParameters().at(2) );
}
} else {
return setAttribute( "backgroundcolor", functionType.getParameters().at(0) );
}
drawable->setRectColors( rectColors );
setBackgroundDrawable( state, drawable, true );
}
}
} else if ( "foreground" == name ) {
Drawable * res = NULL;
if ( String::startsWith( attribute.getValue(), "#" ) ) {
setAttribute( NodeAttribute( "foregroundcolor", attribute.getValue() ) );
} else if ( NULL != ( res = DrawableSearcher::searchByName( attribute.getValue() ) ) ) {
setForegroundDrawable( state, res, res->getDrawableType() == Drawable::SPRITE );
}
} else if ( "foregroundcolor" == name ) {
Color color = attribute.asColor();
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
Color start( getForegroundColor( getStylePreviousState() ) );
Action * action = Actions::Tint::New( start, color, true, transitionInfo.duration, transitionInfo.timingFunction, Actions::Tint::Foreground );
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setForegroundColor( state, color );
}
} else if ( "foregroundradius" == name ) {
setForegroundRadius( state, attribute.asUint() );
} else if ( "bordercolor" == name ) {
Color color = attribute.asColor();
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
Color start( getBorderColor() );
SAVE_NORMAL_STATE_ATTR( start.toHexString() )
Action * action = Actions::Tint::New( start, color, false, transitionInfo.duration, transitionInfo.timingFunction, Actions::Tint::Border );
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setBorderColor( color );
}
} else if ( "borderwidth" == name ) {
setBorderWidth( attribute.asDpDimensionI("1") );
} else if ( "borderradius" == name ) {
Uint32 borderRadius = attribute.asUint();
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
Uint32 start( getBorderRadius( getStylePreviousState() ) );
SAVE_NORMAL_STATE_ATTR( String::format( "%d", start ) );
Action * action = Actions::ResizeBorderRadius::New( start, borderRadius, transitionInfo.duration, transitionInfo.timingFunction );
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setBorderRadius( state, attribute.asUint() );
}
} else if ( "visible" == name ) {
setVisible( attribute.asBool() );
} else if ( "enabled" == name ) {
setEnabled( attribute.asBool() );
} else if ( "theme" == name ) {
setThemeByName( attribute.asString() );
if ( !mSkinName.empty() )
setThemeSkin( mSkinName );
} else if ( "skin" == name ) {
mSkinName = attribute.asString();
setThemeSkin( mSkinName );
} else if ( "skincolor" == name ) {
Color color = attribute.asColor();
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
Color start( getSkinColor( getStylePreviousState() ) );
SAVE_NORMAL_STATE_ATTR( start.toHexString() )
Action * action = Actions::Tint::New( start, color, true, transitionInfo.duration, transitionInfo.timingFunction, Actions::Tint::Skin );
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setSkinColor( state, color );
}
} else if ( "gravity" == name ) {
std::string gravity = attribute.asString();
String::toLowerInPlace( gravity );
std::vector<std::string> strings = String::split( gravity, '|' );
if ( strings.size() ) {
for ( std::size_t i = 0; i < strings.size(); i++ ) {
std::string cur = strings[i];
String::toLowerInPlace( cur );
if ( "left" == cur )
setHorizontalAlign( UI_HALIGN_LEFT );
else if ( "right" == cur )
setHorizontalAlign( UI_HALIGN_RIGHT );
else if ( "center_horizontal" == cur )
setHorizontalAlign( UI_HALIGN_CENTER );
else if ( "top" == cur )
setVerticalAlign( UI_VALIGN_TOP );
else if ( "bottom" == cur )
setVerticalAlign( UI_VALIGN_BOTTOM );
else if ( "center_vertical" == cur ) {
setVerticalAlign( UI_VALIGN_CENTER );
} else if ( "center" == cur ) {
setHorizontalAlign( UI_HALIGN_CENTER );
setVerticalAlign( UI_VALIGN_CENTER );
}
}
notifyLayoutAttrChange();
}
} else if ( "flags" == name ) {
std::string flags = attribute.asString();
String::toLowerInPlace( flags );
std::vector<std::string> strings = String::split( flags, '|' );
if ( strings.size() ) {
for ( std::size_t i = 0; i < strings.size(); i++ ) {
std::string cur = strings[i];
String::toLowerInPlace( cur );
if ( "auto_size" == cur || "autosize" == cur ) {
setFlags( UI_AUTO_SIZE );
notifyLayoutAttrChange();
} else if ( "clip" == cur ) {
clipEnable();
} else if ( "multi" == cur ) {
setFlags( UI_MULTI_SELECT );
} else if ( "auto_padding" == cur || "autopadding" == cur ) {
setFlags( UI_AUTO_PADDING );
notifyLayoutAttrChange();
} else if ( "reportsizechangetochilds" == cur || "report_size_change_to_childs" == cur ) {
enableReportSizeChangeToChilds();
}
}
}
} else if ( String::startsWith( name, "layout_margin" ) ) {
Rect margin;
Uint32 marginFlag = 0;
if ( "layout_margin" == name ) {
margin = attribute.asRect();
marginFlag = Actions::MarginMove::All;
} else if ( "layout_marginleft" == name ) {
margin = Rect( attribute.asDpDimensionI(), mLayoutMargin.Top, mLayoutMargin.Right, mLayoutMargin.Bottom );
marginFlag = Actions::MarginMove::Left;
} else if ( "layout_marginright" == name ) {
margin = Rect( mLayoutMargin.Left, mLayoutMargin.Top, attribute.asDpDimensionI(), mLayoutMargin.Bottom );
marginFlag = Actions::MarginMove::Right;
} else if ( "layout_margintop" == name ) {
margin = Rect( mLayoutMargin.Left, attribute.asDpDimensionI(), mLayoutMargin.Right, mLayoutMargin.Bottom );
marginFlag = Actions::MarginMove::Top;
} else if ( "layout_marginbottom" == name ) {
margin = Rect( mLayoutMargin.Left, mLayoutMargin.Top, mLayoutMargin.Right, attribute.asDpDimensionI() );
marginFlag = Actions::MarginMove::Bottom;
}
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
Action * action = Actions::MarginMove::New( mLayoutMargin, margin, transitionInfo.duration, transitionInfo.timingFunction, marginFlag );
NodeAttribute oldAttribute = mStyle->getAttribute( UIState::StateFlagNormal, attribute.getName() );
if ( oldAttribute.isEmpty() && mStyle->getPreviousState() == UIState::StateFlagNormal ) {
if ( "layout_marginleft" == name || "layout_margin" == name )
mStyle->addAttribute( UIState::StateFlagNormal, NodeAttribute( attribute.getName(), String::format( "%d", mLayoutMargin.Left ) ) );
if ( "layout_marginright" == name || "layout_margin" == name )
mStyle->addAttribute( UIState::StateFlagNormal, NodeAttribute( attribute.getName(), String::format( "%d", mLayoutMargin.Right ) ) );
if ( "layout_margintop" == name || "layout_margin" == name )
mStyle->addAttribute( UIState::StateFlagNormal, NodeAttribute( attribute.getName(), String::format( "%d", mLayoutMargin.Top ) ) );
if ( "layout_marginbottom" == name || "layout_margin" == name )
mStyle->addAttribute( UIState::StateFlagNormal, NodeAttribute( attribute.getName(), String::format( "%d", mLayoutMargin.Bottom ) ) );
}
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setLayoutMargin( margin );
}
} else if ( "tooltip" == name ) {
setTooltipText( attribute.asString() );
} else if ( "layout_weight" == name ) {
setLayoutWeight( attribute.asFloat() );
} else if ( "layout_gravity" == name ) {
std::string gravityStr = attribute.asString();
String::toLowerInPlace( gravityStr );
std::vector<std::string> strings = String::split( gravityStr, '|' );
Uint32 gravity = 0;
if ( strings.size() ) {
for ( std::size_t i = 0; i < strings.size(); i++ ) {
std::string cur = strings[i];
String::toLowerInPlace( cur );
if ( "left" == cur )
gravity |= UI_HALIGN_LEFT;
else if ( "right" == cur )
gravity |= UI_HALIGN_RIGHT;
else if ( "center_horizontal" == cur )
gravity |= UI_HALIGN_CENTER;
else if ( "top" == cur )
gravity |= UI_VALIGN_TOP;
else if ( "bottom" == cur )
gravity |= UI_VALIGN_BOTTOM;
else if ( "center_vertical" == cur ) {
gravity |= UI_VALIGN_CENTER;
} else if ( "center" == cur ) {
gravity |= UI_VALIGN_CENTER | UI_HALIGN_CENTER;
}
}
setLayoutGravity( gravity );
}
} else if ( "layout_width" == name ) {
std::string val = attribute.asString();
String::toLowerInPlace( val );
if ( "match_parent" == val ) {
setLayoutWidthRules( MATCH_PARENT );
} else if ( "wrap_content" == val ) {
setLayoutWidthRules( WRAP_CONTENT );
} else if ( "fixed" == val ) {
setLayoutWidthRules( FIXED );
unsetFlags( UI_AUTO_SIZE );
} else {
unsetFlags( UI_AUTO_SIZE );
setLayoutWidthRules( FIXED );
setInternalWidth( PixelDensity::toDpFromStringI( val ) );
onSizeChange();
}
} else if ( "layout_height" == name ) {
std::string val = attribute.asString();
String::toLowerInPlace( val );
if ( "match_parent" == val ) {
setLayoutHeightRules( MATCH_PARENT );
} else if ( "wrap_content" == val ) {
setLayoutHeightRules( WRAP_CONTENT );
} else if ( "fixed" == val ) {
setLayoutHeightRules( FIXED );
unsetFlags( UI_AUTO_SIZE );
} else {
unsetFlags( UI_AUTO_SIZE );
setLayoutHeightRules( FIXED );
setInternalHeight( PixelDensity::toDpFromStringI( val ) );
onSizeChange();
}
} else if ( String::startsWith( name, "layout_to_" ) || String::startsWith( name, "layoutto" ) ) {
LayoutPositionRules rule = NONE;
if ( "layout_to_left_of" == name || "layouttoleftof" == name ) rule = LEFT_OF;
else if ( "layout_to_right_of" == name || "layouttorightof" == name ) rule = RIGHT_OF;
else if ( "layout_to_top_of" == name || "layouttotopof" == name ) rule = TOP_OF;
else if ( "layout_to_bottom_of" == name || "layouttobottomof" == name ) rule = BOTTOM_OF;
std::string id = attribute.asString();
Node * control = getParent()->find( id );
if ( NULL != control && control->isWidget() ) {
UIWidget * widget = static_cast<UIWidget*>( control );
setLayoutPositionRule( rule, widget );
}
} else if ( "clip" == name ) {
if ( attribute.asBool() )
clipEnable();
else
clipDisable();
} else if ( "rotation" == name ) {
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
Float newRotation( mStyle->getAttribute( state, attribute.getName() ).asFloat() );
Action * action = Actions::Rotate::New( mRotation, newRotation, transitionInfo.duration, transitionInfo.timingFunction );
SAVE_NORMAL_STATE_ATTR( String::format( "%2.f", mRotation ) )
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setRotation( attribute.asFloat() );
}
} else if ( "scale" == name ) {
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
Vector2f newScale( mStyle->getAttribute( state, attribute.getName() ).asVector2f() );
Action * action = Actions::Scale::New( mScale, newScale, transitionInfo.duration, transitionInfo.timingFunction );
SAVE_NORMAL_STATE_ATTR( String::format( "%2.f, %2.f", mScale.x, mScale.y ) )
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setScale( attribute.asVector2f() );
}
} else if ( "rotationoriginpoint" == name ) {
setRotationOriginPoint( attribute.asOriginPoint() );
} else if ( "scaleoriginpoint" == name ) {
setScaleOriginPoint( attribute.asOriginPoint() );
} else if ( "blendmode" == name ) {
setBlendMode( attribute.asBlendMode() );
} else if ( String::startsWith( name, "padding" ) ) {
Rectf padding;
Uint32 paddingFlag = 0;
if ( "padding" == name ) {
padding = ( attribute.asRectf() );
paddingFlag = Actions::PaddingTransition::All;
} else if ( "paddingleft" == name ) {
padding = Rectf( attribute.asDpDimension(), mPadding.Top, mPadding.Right, mPadding.Bottom );
paddingFlag = Actions::PaddingTransition::Left;
} else if ( "paddingright" == name ) {
padding = Rectf( mPadding.Left, mPadding.Top, attribute.asDpDimension(), mPadding.Bottom );
paddingFlag = Actions::PaddingTransition::Right;
} else if ( "paddingtop" == name ) {
padding = Rectf( mPadding.Left, attribute.asDpDimension(), mPadding.Right, mPadding.Bottom );
paddingFlag = Actions::PaddingTransition::Top;
} else if ( "paddingbottom" == name ) {
padding = Rectf( mPadding.Left, mPadding.Top, mPadding.Right, attribute.asDpDimension() );
paddingFlag = Actions::PaddingTransition::Bottom;
}
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
Action * action = Actions::PaddingTransition::New( mPadding, padding, transitionInfo.duration, transitionInfo.timingFunction, paddingFlag );
NodeAttribute oldAttribute = mStyle->getAttribute( UIState::StateFlagNormal, attribute.getName() );
if ( oldAttribute.isEmpty() && mStyle->getPreviousState() == UIState::StateFlagNormal ) {
if ( "paddingleft" == name || "padding" == name )
mStyle->addAttribute( UIState::StateFlagNormal, NodeAttribute( attribute.getName(), String::format( "%2.f", mPadding.Left ) ) );
if ( "paddingright" == name || "padding" == name )
mStyle->addAttribute( UIState::StateFlagNormal, NodeAttribute( attribute.getName(), String::format( "%2.f", mPadding.Right ) ) );
if ( "paddingtop" == name || "padding" == name )
mStyle->addAttribute( UIState::StateFlagNormal, NodeAttribute( attribute.getName(), String::format( "%2.f", mPadding.Top ) ) );
if ( "paddingbottom" == name || "padding" == name )
mStyle->addAttribute( UIState::StateFlagNormal, NodeAttribute( attribute.getName(), String::format( "%2.f", mPadding.Bottom ) ) );
}
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setPadding( padding );
}
} else if ( "opacity" == name ) {
Float alpha = eemin( attribute.asFloat() * 255.f, 255.f );
if ( !isSceneNodeLoading() && NULL != mStyle && mStyle->hasTransition( state, attribute.getName() ) ) {
UIStyle::TransitionInfo transitionInfo( mStyle->getTransition( state, attribute.getName() ) );
Action * action = Actions::Fade::New( mAlpha, alpha, transitionInfo.duration, transitionInfo.timingFunction );
SAVE_NORMAL_STATE_ATTR( String::format( "%2.f", mAlpha ) )
if ( Time::Zero != transitionInfo.delay )
action = Actions::Sequence::New( Actions::Delay::New( transitionInfo.delay ), action );
runAction( action );
} else {
setAlpha( alpha );
setChildsAlpha( alpha );
}
} else if ( "cursor" == name ) {
SAVE_NORMAL_STATE_ATTR( "arrow" );
mSceneNode->setCursor( Cursor::fromName( attribute.getValue() ) );
} else {
attributeSet = false;
}
return attributeSet;
}
void UIWidget::loadFromXmlNode( const pugi::xml_node& node ) {
beginAttributesTransaction();
for (pugi::xml_attribute_iterator ait = node.attributes_begin(); ait != node.attributes_end(); ++ait) {
setAttribute( ait->name(), ait->value() );
}
endAttributesTransaction();
}
}}