Reduce allocations in actions and CSS style updates

- allocate Action instances from reusable size-class pools
  - use inline storage for Runnable callbacks, action sequences, spawned actions,
    callback registrations, and main-thread action queues
  - preserve action factory and ownership semantics while recycling completed actions
  - cache computed transitions in ElementDefinition instead of reparsing them on
    every state change
  - parse transition and timing-function values through allocation-light string views
    and inline parameter storage
  - add a non-owning FunctionString view parser with callback-based parameter iteration
  - preserve nested functions, quoted parameters, escaped quotes, and early termination
  - fix transition timing-function lists to cycle independently from delay lists
  - avoid copying variable caches during CSS variable substitution
  - parse drawable sizes and positions without allocating split string vectors
  - reuse animation state and reduce temporary property parsing allocations
  - avoid memory-manager bookkeeping allocations for static source filenames
  - consolidate whitespace trimming through the existing String utilities
  - add regression coverage for action reuse, transition parsing, computed transition
    lookup, timing-list cycling, and non-owning FunctionString parsing
This commit is contained in:
Martín Lucas Golini
2026-08-06 01:16:52 -03:00
parent 74b0454721
commit d90ffe6fea
38 changed files with 1229 additions and 387 deletions
+5 -3
View File
@@ -12,10 +12,12 @@ namespace EE {
class EE_API AllocatedPointer {
public:
AllocatedPointer( void* data, const std::string& File, int Line, size_t memory,
bool track = false, bool globalAllocation = false );
/** file must have static storage duration. All memory-manager entry points pass __FILE__ or a
* string literal, so retaining the pointer avoids allocating bookkeeping strings. */
AllocatedPointer( void* data, const char* file, int line, size_t memory, bool track = false,
bool globalAllocation = false );
std::string mFile;
const char* mFile;
int mLine;
size_t mMemory;
void* mData;
+174
View File
@@ -0,0 +1,174 @@
#ifndef EE_CORE_SMALL_FUNCTION_HPP
#define EE_CORE_SMALL_FUNCTION_HPP
#include <cstddef>
#include <functional>
#include <new>
#include <type_traits>
#include <utility>
namespace EE {
/** Copyable type-erased void callable with inline storage and a heap fallback.
*
* This intentionally implements only the signature used by the scene scheduler. Ordinary lambdas
* stay inside the object; unusually large or over-aligned callables retain std::function-like
* fallback behavior.
*/
template <std::size_t InlineSize = 64> class SmallFunction {
public:
SmallFunction() noexcept = default;
SmallFunction( std::nullptr_t ) noexcept {}
SmallFunction( const SmallFunction& other ) {
if ( other.mVTable )
other.mVTable->copy( *this, other );
}
SmallFunction( SmallFunction&& other ) {
if ( other.mVTable )
other.mVTable->move( *this, other );
}
template <typename Callable, typename Decayed = std::decay_t<Callable>,
std::enable_if_t<!std::is_same_v<Decayed, SmallFunction> &&
std::is_invocable_r_v<void, Decayed&>,
int> = 0>
SmallFunction( Callable&& callable ) {
emplace<Decayed>( std::forward<Callable>( callable ) );
}
~SmallFunction() { reset(); }
SmallFunction& operator=( const SmallFunction& other ) {
if ( this != &other ) {
reset();
if ( other.mVTable )
other.mVTable->copy( *this, other );
}
return *this;
}
SmallFunction& operator=( SmallFunction&& other ) {
if ( this != &other ) {
reset();
if ( other.mVTable )
other.mVTable->move( *this, other );
}
return *this;
}
template <typename Callable, typename Decayed = std::decay_t<Callable>,
std::enable_if_t<!std::is_same_v<Decayed, SmallFunction> &&
std::is_invocable_r_v<void, Decayed&>,
int> = 0>
SmallFunction& operator=( Callable&& callable ) {
reset();
emplace<Decayed>( std::forward<Callable>( callable ) );
return *this;
}
SmallFunction& operator=( std::nullptr_t ) noexcept {
reset();
return *this;
}
explicit operator bool() const noexcept { return nullptr != mVTable; }
void operator()() const {
if ( !mVTable )
throw std::bad_function_call();
mVTable->invoke( const_cast<SmallFunction&>( *this ) );
}
void reset() noexcept {
if ( mVTable ) {
mVTable->destroy( *this );
mVTable = nullptr;
}
}
static constexpr std::size_t inlineSize() noexcept { return InlineSize; }
private:
static constexpr std::size_t StorageSize =
InlineSize < sizeof( void* ) ? sizeof( void* ) : InlineSize;
using Storage = std::aligned_storage_t<StorageSize, alignof( std::max_align_t )>;
struct VTable {
void ( *invoke )( SmallFunction& );
void ( *copy )( SmallFunction&, const SmallFunction& );
void ( *move )( SmallFunction&, SmallFunction& );
void ( *destroy )( SmallFunction& ) noexcept;
};
template <typename Callable>
static constexpr bool FitsInline =
sizeof( Callable ) <= StorageSize && alignof( Callable ) <= alignof( Storage );
template <typename Callable> static Callable*& heapPointer( SmallFunction& function ) {
return *reinterpret_cast<Callable**>( &function.mStorage );
}
template <typename Callable> static Callable* inlinePointer( SmallFunction& function ) {
return reinterpret_cast<Callable*>( &function.mStorage );
}
template <typename Callable, typename Value> void emplace( Value&& value ) {
static_assert( std::is_copy_constructible_v<Callable>,
"SmallFunction requires a copy-constructible callable" );
if constexpr ( FitsInline<Callable> ) {
new ( &mStorage ) Callable( std::forward<Value>( value ) );
mVTable = &inlineVTable<Callable>();
} else {
heapPointer<Callable>( *this ) = new Callable( std::forward<Value>( value ) );
mVTable = &heapVTable<Callable>();
}
}
template <typename Callable> static const VTable& inlineVTable() {
static const VTable table{
[]( SmallFunction& function ) { ( *inlinePointer<Callable>( function ) )(); },
[]( SmallFunction& destination, const SmallFunction& source ) {
new ( &destination.mStorage )
Callable( *inlinePointer<Callable>( const_cast<SmallFunction&>( source ) ) );
destination.mVTable = &inlineVTable<Callable>();
},
[]( SmallFunction& destination, SmallFunction& source ) {
new ( &destination.mStorage )
Callable( std::move( *inlinePointer<Callable>( source ) ) );
inlinePointer<Callable>( source )->~Callable();
destination.mVTable = &inlineVTable<Callable>();
source.mVTable = nullptr;
},
[]( SmallFunction& function ) noexcept {
inlinePointer<Callable>( function )->~Callable();
} };
return table;
}
template <typename Callable> static const VTable& heapVTable() {
static const VTable table{
[]( SmallFunction& function ) { ( *heapPointer<Callable>( function ) )(); },
[]( SmallFunction& destination, const SmallFunction& source ) {
heapPointer<Callable>( destination ) =
new Callable( *heapPointer<Callable>( const_cast<SmallFunction&>( source ) ) );
destination.mVTable = &heapVTable<Callable>();
},
[]( SmallFunction& destination, SmallFunction& source ) {
heapPointer<Callable>( destination ) = heapPointer<Callable>( source );
heapPointer<Callable>( source ) = nullptr;
destination.mVTable = &heapVTable<Callable>();
source.mVTable = nullptr;
},
[]( SmallFunction& function ) noexcept { delete heapPointer<Callable>( function ); } };
return table;
}
Storage mStorage;
const VTable* mVTable{ nullptr };
};
} // namespace EE
#endif
+16 -7
View File
@@ -1,8 +1,8 @@
#ifndef EE_MATH_EASE
#define EE_MATH_EASE
#include <eepp/system/functionstring.hpp>
#include <string>
#include <string_view>
namespace EE { namespace Math {
@@ -118,7 +118,7 @@ class Ease {
return "";
}
static Interpolation fromName( const std::string& name,
static Interpolation fromName( std::string_view name,
const Interpolation& defaultInterpolation = Ease::Linear ) {
if ( "linear" == name )
return Ease::Linear;
@@ -184,13 +184,22 @@ class Ease {
return Ease::ElasticInOut;
if ( "cubicbezier" == name || "cubic-bezier" == name )
return Ease::CubizBezier;
// Handle the case that the name is a function
if ( name.find_first_of( '(' ) != std::string::npos ) {
System::FunctionString func( System::FunctionString::parse( name ) );
return fromName( func.getName(), defaultInterpolation );
}
// Function parameters do not participate in interpolation-name lookup.
const auto functionStart = name.find_first_of( '(' );
if ( functionStart != std::string_view::npos )
return fromName( name.substr( 0, functionStart ), defaultInterpolation );
return defaultInterpolation;
}
static Interpolation fromName( const std::string& name,
const Interpolation& defaultInterpolation = Ease::Linear ) {
return fromName( std::string_view{ name }, defaultInterpolation );
}
static Interpolation fromName( const char* name,
const Interpolation& defaultInterpolation = Ease::Linear ) {
return fromName( std::string_view{ name }, defaultInterpolation );
}
};
}} // namespace EE::Math
+19 -1
View File
@@ -1,6 +1,7 @@
#ifndef EE_SCENEACTION_HPP
#define EE_SCENEACTION_HPP
#include <cstddef>
#include <eepp/core.hpp>
#include <eepp/system/time.hpp>
using namespace EE::System;
@@ -21,6 +22,18 @@ class EE_API Action {
virtual ~Action();
/** Allocates actions from a shared size-class pool. Existing Action factories and ownership
* semantics are unchanged; completed slots are recycled by operator delete. */
static void* operator new( std::size_t size );
static void operator delete( void* ptr ) noexcept;
static void operator delete( void* ptr, std::size_t size ) noexcept;
static void* operator new( std::size_t, void* place ) noexcept { return place; }
static void operator delete( void*, void* ) noexcept {}
/** Starts the action. */
virtual void start() = 0;
@@ -91,7 +104,12 @@ class EE_API Action {
protected:
friend class Node;
typedef UnorderedMap<ActionType, UnorderedMap<Uint32, ActionCallback>> ActionCallbackMap;
struct ActionCallbackEntry {
ActionType type;
Uint32 id;
ActionCallback callback;
};
using ActionCallbackMap = SmallVector<ActionCallbackEntry, 2>;
Node* mNode{ nullptr };
Action::UniqueID mId{ 0 };
+3
View File
@@ -43,6 +43,9 @@ class EE_API ActionManager {
std::vector<Action*> getActionsByTagFromTarget( Node* target, const Action::UniqueID& tag );
void getActionsByTagFromTarget( Node* target, const Action::UniqueID& tag,
SmallVector<Action*, 4>& actions );
void update( const Time& time );
std::size_t count() const;
+2 -2
View File
@@ -1,15 +1,15 @@
#ifndef EE_SCENE_ACTION_CALLBACK_HPP
#define EE_SCENE_ACTION_CALLBACK_HPP
#include <eepp/core/small_function.hpp>
#include <eepp/scene/actions/delay.hpp>
#include <eepp/system/mutex.hpp>
#include <functional>
namespace EE { namespace Scene { namespace Actions {
class EE_API Runnable : public Delay {
public:
typedef std::function<void()> RunnableFunc;
using RunnableFunc = SmallFunction<48>;
static Runnable* New( RunnableFunc callback, const Time& time = Seconds( 0 ),
bool loop = false );
+4 -1
View File
@@ -42,11 +42,14 @@ class EE_API Sequence : public Action {
virtual ~Sequence();
protected:
std::vector<Action*> mSequence;
using ActionContainer = SmallVector<Action*, 4>;
ActionContainer mSequence;
Time mDuration;
Uint32 mCurPos;
Sequence( const std::vector<Action*> sequence );
Sequence( ActionContainer sequence );
};
}}} // namespace EE::Scene::Actions
+4 -1
View File
@@ -42,10 +42,13 @@ class EE_API Spawn : public Action {
virtual ~Spawn();
protected:
std::vector<Action*> mSpawn;
using ActionContainer = SmallVector<Action*, 4>;
ActionContainer mSpawn;
bool mAllDone;
Spawn( const std::vector<Action*> spawn );
Spawn( ActionContainer spawn );
};
}}} // namespace EE::Scene::Actions
+3
View File
@@ -1346,6 +1346,9 @@ class EE_API Node : public Transformable {
*/
std::vector<Action*> getActionsByTag( const Action::UniqueID& tag );
/** Appends matching actions to inline-capable storage without allocating in the common case. */
void getActionsByTag( const Action::UniqueID& tag, SmallVector<Action*, 4>& actions );
/**
* @brief Removes all actions from this node.
*
+78 -1
View File
@@ -6,6 +6,7 @@
#include <eepp/core/string.hpp>
#include <string>
#include <utility>
namespace EE { namespace System {
@@ -14,6 +15,82 @@ class EE_API FunctionString {
using Parameters = SmallVector<std::string, 4>;
using TypeStringVector = SmallVector<bool, 4>;
class View {
public:
View() = default;
std::string_view getName() const { return mName; }
bool isEmpty() const { return mName.empty(); }
template <typename Callback> bool forEachParameter( Callback&& callback ) const {
return FunctionString::forEachParameter( mParameters,
std::forward<Callback>( callback ) );
}
private:
friend class FunctionString;
View( std::string_view name, std::string_view parameters ) :
mName( name ), mParameters( parameters ) {}
std::string_view mName;
std::string_view mParameters;
};
static View parseView( std::string_view function );
/** Iterates a comma-separated parameter list without allocating. Nested parentheses and
* quoted strings protect commas. Quoted parameters are reported without their outer quotes;
* escaped quotes remain escaped because the returned views reference the original input.
* The callback receives (parameter, parameterWasString) and returns whether to continue. */
template <typename Callback>
static bool forEachParameter( std::string_view parameters, Callback&& callback ) {
std::size_t start = 0;
int parenthesisDepth = 0;
char quote = 0;
bool escaped = false;
auto emit = [&]( std::size_t end ) {
std::string_view parameter =
String::trim( parameters.substr( start, end - start ), " \t\n\r\f\v" );
bool wasString = parameter.size() >= 2 &&
( parameter.front() == '\'' || parameter.front() == '"' ) &&
parameter.back() == parameter.front();
if ( wasString )
parameter = parameter.substr( 1, parameter.size() - 2 );
if ( parameter.empty() && !wasString )
return true;
return callback( parameter, wasString );
};
for ( std::size_t i = 0; i < parameters.size(); ++i ) {
const char character = parameters[i];
if ( quote ) {
if ( escaped ) {
escaped = false;
} else if ( character == '\\' ) {
escaped = true;
} else if ( character == quote ) {
quote = 0;
}
continue;
}
if ( character == '\'' || character == '"' ) {
quote = character;
} else if ( character == '(' ) {
++parenthesisDepth;
} else if ( character == ')' && parenthesisDepth > 0 ) {
--parenthesisDepth;
} else if ( character == ',' && parenthesisDepth == 0 ) {
if ( !emit( i ) )
return false;
start = i + 1;
}
}
return emit( parameters.size() );
}
static FunctionString parse( const std::string& function );
static FunctionString parse( std::string_view function );
@@ -41,7 +118,7 @@ class EE_API FunctionString {
Parameters parameters;
TypeStringVector typeStringData;
template <typename StringType> static FunctionString parse( StringType function );
static FunctionString parseOwned( std::string_view function );
};
}} // namespace EE::System
+11 -2
View File
@@ -4,6 +4,7 @@
#include <eepp/math/ease.hpp>
#include <eepp/system/time.hpp>
#include <eepp/ui/css/stylesheetproperty.hpp>
#include <eepp/ui/css/timingfunction.hpp>
#include <vector>
using namespace EE::Math;
@@ -56,8 +57,12 @@ class EE_API AnimationDefinition {
const Ease::Interpolation& getTimingFunction() const;
/** Kept for source compatibility. Internal animation code uses the inline view below so this
* legacy vector is only materialized when an external caller requests it. */
const std::vector<double>& getTimingFunctionParameters() const;
const TimingFunction::Parameters& getTimingFunctionParametersInline() const;
const AnimationFillMode& getFillMode() const;
void setName( const std::string& value );
@@ -72,6 +77,8 @@ class EE_API AnimationDefinition {
void setTimingFunctionParameters( const std::vector<double>& timingFunctionParameters );
void setTimingFunctionParameters( const TimingFunction::Parameters& timingFunctionParameters );
void setDirection( const AnimationDirection& value );
void setFillMode( const AnimationFillMode& value );
@@ -87,7 +94,9 @@ class EE_API AnimationDefinition {
Time mDuration = Time::Zero;
Int32 mIterations = 1; /* -1 == "infinite" */
Ease::Interpolation mTimingFunction = Ease::Interpolation::Linear;
std::vector<double> mTimingFunctionParameters{};
TimingFunction::Parameters mTimingFunctionParameters{};
mutable std::vector<double> mTimingFunctionParametersVector;
mutable bool mTimingFunctionParametersVectorValid{ false };
AnimationDirection mDirection = Normal;
AnimationFillMode mFillMode = None;
bool mPaused = false;
@@ -98,7 +107,7 @@ inline bool operator==( const AnimationDefinition& a, const AnimationDefinition&
a.getDelay() == b.getDelay() && a.getDirection() == b.getDirection() &&
a.isPaused() == b.isPaused() && a.getIterations() == b.getIterations() &&
a.getName() == b.getName() &&
a.getTimingFunctionParameters() == b.getTimingFunctionParameters();
a.getTimingFunctionParametersInline() == b.getTimingFunctionParametersInline();
}
inline bool operator!=( const AnimationDefinition& a, const AnimationDefinition& b ) {
@@ -5,6 +5,7 @@
#include <eepp/ui/css/propertyidset.hpp>
#include <eepp/ui/css/stylesheetproperty.hpp>
#include <eepp/ui/css/stylesheetstyle.hpp>
#include <eepp/ui/css/transitiondefinition.hpp>
namespace EE { namespace UI { namespace CSS {
@@ -22,6 +23,8 @@ class EE_API ElementDefinition : NonCopyable {
const std::vector<const CSS::StyleSheetProperty*>& getTransitionProperties() const;
const ComputedTransitions& getTransitions() const;
const std::vector<const CSS::StyleSheetProperty*>& getAnimationProperties() const;
const StyleSheetVariables& getVariables() const;
@@ -38,6 +41,7 @@ class EE_API ElementDefinition : NonCopyable {
StyleSheetVariables mVariables;
PropertyIdSet mPropertyIds;
std::vector<const CSS::StyleSheetProperty*> mTransitionProperties;
ComputedTransitions mTransitions;
std::vector<const CSS::StyleSheetProperty*> mAnimationProperties;
bool mStructurallyVolatile;
+10 -1
View File
@@ -58,12 +58,21 @@ class EE_API StyleSheetLength {
static StyleSheetLength fromString( const std::string& str, const Float& defaultValue = 0,
bool pxAsDp = false );
static StyleSheetLength fromString( std::string_view str, const Float& defaultValue = 0,
bool pxAsDp = false );
static StyleSheetLength fromString( const char* str, const Float& defaultValue = 0,
bool pxAsDp = false ) {
return fromString( std::string_view{ str }, defaultValue, pxAsDp );
}
StyleSheetLength();
StyleSheetLength( const Float& val, const Unit& unit );
StyleSheetLength( const std::string& val, const Float& defaultValue = 0 );
StyleSheetLength( std::string_view val, const Float& defaultValue = 0 );
StyleSheetLength( const char* val, const Float& defaultValue = 0 ) :
StyleSheetLength( std::string_view{ val }, defaultValue ) {}
StyleSheetLength( const StyleSheetLength& val );
@@ -98,7 +107,7 @@ class EE_API StyleSheetLength {
protected:
static bool isFunctionString( std::string_view str );
static bool parseFunction( const std::string& str, Unit& outUnit, Arguments& outArgs );
static bool parseFunction( std::string_view str, Unit& outUnit, Arguments& outArgs );
Float resolveFunction( const Float& parentSize, const Sizef& viewSize, const Float& displayDpi,
const Float& elFontSize, const Float& globalFontSize,
@@ -26,7 +26,7 @@ class EE_API StyleSheetPropertyAnimation : public Action {
const PropertyDefinition* property, const std::string& startValue,
const std::string& endValue,
const Ease::Interpolation& timingFunction,
const std::vector<double> timingFunctionParameters,
const TimingFunction::Parameters& timingFunctionParameters,
const Uint32& propertyIndex, const bool& isDone );
static StyleSheetPropertyAnimation* fromAnimationKeyframes(
@@ -48,6 +48,13 @@ class EE_API StyleSheetPropertyAnimation : public Action {
const std::vector<double>& timingFunctionParameters,
const AnimationOrigin& animationOrigin );
static StyleSheetPropertyAnimation*
New( const PropertyDefinition* property, const std::string& startValue,
const std::string& endValue, const Uint32& propertyIndex, const Time& duration,
const Time& delay, const Ease::Interpolation& timingFunction,
const TimingFunction::Parameters& timingFunctionParameters,
const AnimationOrigin& animationOrigin );
void start() override;
void stop() override;
@@ -87,8 +94,8 @@ class EE_API StyleSheetPropertyAnimation : public Action {
protected:
AnimationDefinition mAnimation;
const PropertyDefinition* mPropertyDef;
std::vector<std::string> mStates;
std::vector<Float> mAnimationStepsTime;
SmallVector<std::string, 4> mStates;
SmallVector<Float, 4> mAnimationStepsTime;
Time mRealElapsed;
Time mElapsed;
Int32 mPendingIterations;
@@ -103,6 +110,13 @@ class EE_API StyleSheetPropertyAnimation : public Action {
std::vector<Float> animationStepsTime, const Uint32& propertyIndex,
const AnimationOrigin& animationOrigin );
StyleSheetPropertyAnimation( const AnimationDefinition& animation,
const PropertyDefinition* propertyDef,
SmallVector<std::string, 4> states,
SmallVector<Float, 4> animationStepsTime,
const Uint32& propertyIndex,
const AnimationOrigin& animationOrigin );
void onStart() override;
void onUpdate( const Time& time ) override;
+10 -2
View File
@@ -1,20 +1,28 @@
#ifndef EE_UI_CSS_TIMINGFUNCTION_HPP
#define EE_UI_CSS_TIMINGFUNCTION_HPP
#include <eepp/core/small_vector.hpp>
#include <eepp/core/string.hpp>
#include <eepp/math/ease.hpp>
using namespace EE;
using namespace EE::Math;
namespace EE { namespace UI { namespace CSS {
class EE_API TimingFunction {
public:
using Parameters = SmallVector<double, 4>;
static TimingFunction parse( std::string timingFunction );
static TimingFunction parse( std::string_view timingFunction );
static TimingFunction parse( const char* timingFunction ) {
return parse( std::string_view{ timingFunction } );
}
Ease::Interpolation interpolation{ Ease::None };
std::vector<double> parameters;
Parameters parameters;
};
}}} // namespace EE::UI::CSS
@@ -1,9 +1,11 @@
#ifndef EE_UI_CSS_TRANSITIONDEFINITION_HPP
#define EE_UI_CSS_TRANSITIONDEFINITION_HPP
#include <eepp/core/small_vector.hpp>
#include <eepp/math/ease.hpp>
#include <eepp/system/time.hpp>
#include <eepp/ui/css/stylesheetproperty.hpp>
#include <eepp/ui/css/timingfunction.hpp>
#include <vector>
using namespace EE::Math;
@@ -37,6 +39,33 @@ class EE_API TransitionDefinition {
typedef UnorderedMap<std::string, TransitionDefinition> TransitionsMap;
/** Compact transition data used by computed element definitions. It owns no strings and keeps
* the complete CSS timing-function payload inline. */
struct ComputedTransitionDefinition {
String::HashType propertyNameHash{ 0 };
Ease::Interpolation timingFunction{ Ease::Linear };
TimingFunction::Parameters timingFunctionParameters;
Time delay{ Time::Zero };
Time duration{ Time::Zero };
};
class EE_API ComputedTransitions {
public:
static ComputedTransitions
parse( const std::vector<const StyleSheetProperty*>& styleSheetProperties );
const ComputedTransitionDefinition* get( String::HashType propertyNameHash ) const;
bool empty() const { return mTransitions.empty(); }
std::size_t size() const { return mTransitions.size(); }
private:
SmallVector<ComputedTransitionDefinition, 4> mTransitions;
void set( const ComputedTransitionDefinition& transition );
};
}}} // namespace EE::UI::CSS
#endif
+1 -1
View File
@@ -96,7 +96,7 @@ class EE_API UINodeDrawable : public Drawable {
Sizef calcDrawableSize( const std::string& drawableSizeEq );
Vector2f calcPosition( std::string positionXEq, std::string positionYEq );
Vector2f calcPosition( const std::string& positionXEq, const std::string& positionYEq );
const std::string& getPositionX() const;
-1
View File
@@ -100,7 +100,6 @@ class EE_API UIStyle : public UIState {
std::shared_ptr<CSS::StyleSheetStyle> mElementStyle;
std::shared_ptr<CSS::ElementDefinition> mGlobalDefinition;
std::shared_ptr<CSS::ElementDefinition> mDefinition;
CSS::TransitionsMap mTransitions;
CSS::AnimationsMap mAnimations;
UnorderedSet<UIWidget*> mRelatedWidgets;
UnorderedSet<UIWidget*> mSubscribedWidgets;