From 04c407077f62dbeff93c7d3b1083117234ed73b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Tue, 25 Feb 2020 01:15:27 -0300 Subject: [PATCH] Several improvements in the CSS Animations. UITabWidget now acts as a draw invalidator, skipping the draw invalidation if the element invalidating is not visible in the current tab. premake5 file now supports different architectures. Updated README.md and docs. Minor fixes in UIColorPicker. Updated SOIL2 and efsw. Added LICENSE file. --- LICENSE | 19 +++ README.md | 7 +- bin/assets/layouts/test.css | 12 +- docs/articles/uiintroduction.md | 74 +++++---- include/eepp/scene/actionmanager.hpp | 1 - include/eepp/scene/node.hpp | 2 +- include/eepp/ui/css/animationdefinition.hpp | 55 +++++-- .../ui/css/stylesheetpropertyanimation.hpp | 7 + include/eepp/ui/uistyle.hpp | 18 ++- include/eepp/ui/uitabwidget.hpp | 4 + include/eepp/ui/uiwindow.hpp | 2 +- premake4.lua | 9 +- premake5.lua | 18 ++- projects/linux/ee.creator.user | 36 ++--- src/eepp/maps/mapeditor/uimap.cpp | 2 +- src/eepp/scene/actionmanager.cpp | 59 +++----- src/eepp/scene/eventdispatcher.cpp | 2 +- src/eepp/scene/node.cpp | 4 +- src/eepp/ui/css/animationdefinition.cpp | 98 ++++++++---- src/eepp/ui/css/stylesheet.cpp | 8 +- .../ui/css/stylesheetpropertyanimation.cpp | 109 ++++++++++---- src/eepp/ui/css/stylesheetspecification.cpp | 9 +- src/eepp/ui/tools/uicolorpicker.cpp | 10 +- src/eepp/ui/uistyle.cpp | 140 +++++++++++++++--- src/eepp/ui/uitabwidget.cpp | 27 ++++ src/eepp/ui/uiwindow.cpp | 6 +- src/thirdparty/SOIL2 | 2 +- src/thirdparty/efsw | 2 +- 28 files changed, 512 insertions(+), 230 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..b39d117b6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Martín Lucas Golini + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 31bb15558..8cc6d1a77 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,8 @@ UITextView::New()->setText( "Text on test 1" ) Element styling can be done with a custom implementation of Cascading Style Sheets, most common CSS2 rules are available, plus several CSS3 rules (some -examples: [transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions), +examples: [animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/), +[transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions), [custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties), [media queries](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries), [@font-face at rule](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face), @@ -522,8 +523,6 @@ Keep improving the UI system, adding new widgets and layouts and improving the C Simplify and improve the UI widgets skinning/theming support. -Add [CSS animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations) support. - Improve/create documentation for the UI module. Add more examples and some tools. @@ -532,7 +531,7 @@ Add Scripting support ( first I would like to stabilize the library, but I'm get Add 2D skeletal animations support ( probably Spine2D, shouldn't be much work to implement ). -Probably deprecate the Maps module, since i will focus my efforts on the UI system. +Probably deprecate the Maps module, since I will focus my efforts on the UI system. ## Acknowledgements diff --git a/bin/assets/layouts/test.css b/bin/assets/layouts/test.css index 84c645438..0c9c65c8a 100644 --- a/bin/assets/layouts/test.css +++ b/bin/assets/layouts/test.css @@ -184,7 +184,7 @@ CheckBox:checked { background-color: #221122; } to { - background-color: #332233; + background-color: #110011; } } @@ -456,6 +456,16 @@ TabWidget { animation: 0.5s infinite alternate pulse; } +#rttv { + animation: 0.5s infinite alternate paused ease-in pulse; +} + +#rttv:hover { + /** This rule should be ignored. */ + foreground-color: red; + animation-play-state: running; +} + @media screen and (max-width: 1024px) { #lvbox { diff --git a/docs/articles/uiintroduction.md b/docs/articles/uiintroduction.md index 3762f70a3..99537c638 100644 --- a/docs/articles/uiintroduction.md +++ b/docs/articles/uiintroduction.md @@ -1,30 +1,27 @@ -UI Introduction -=============== +# UI Introduction +## Introduction -Introduction ------------- eepp UI is designed to be flexible and efficient. Inspired in [Android Layouts structure](https://developer.android.com/guide/topics/ui/declaring-layout) and [CSS standards](https://developer.mozilla.org/en-US/docs/Web/CSS). It's still a work in progress so several features are still not stable, but already provides a very solid foundation to create rich and interactive UIs. +## Layout -Layout ------- A layout defines the structure for a user interface in your app. For constructing layouts in eepp we have two options: instantiate layout elements at runtime or declare UI elements in XML. - * **Declare UI elements in XML.** eepp provides a straightforward XML - vocabulary that corresponds to the UI Widget classes and subclasses. - There are two kinds of widgets: normal widgets and layouts. The difference - between this two is that layouts are the ones in charge of arranging their - child in a particular way. +* **Declare UI elements in XML.** eepp provides a straightforward XML +vocabulary that corresponds to the UI Widget classes and subclasses. +There are two kinds of widgets: normal widgets and layouts. The difference +between this two is that layouts are the ones in charge of arranging their +child in a particular way. - * **Instantiate layout elements at runtime.** Your app can create Widget and - Layout objects (and manipulate their properties) programmatically. +* **Instantiate layout elements at runtime.** Your app can create Widget and +Layout objects (and manipulate their properties) programmatically. Declaring your UI in XML allows you to separate the presentation of your app from the code that controls its behavior. Using XML files in conjunction with @@ -35,9 +32,8 @@ The framework gives you the flexibility to use either or both of these methods to build your app's UI. For example, you can declare your app's default layouts in XML, and then modify the layout at runtime. +## Write the XML -Write the XML -------------- Using eepp XML vocabulary, you can quickly design UI layouts and the screen elements they contain, in the same way you create web pages in HTML — with a series of nested elements. @@ -67,9 +63,8 @@ and a EE::UI::UIPushButton: After you've declared your layout in XML, save the file with the `.xml` extension into a project accessible path and you're done. +## Initializing the UI -Initializing the UI -------------------- The library does not assume that the user is going to use the UI at all, so you must initialize it manually. This usually comes right after the main EE::Window::Window initialization. @@ -79,6 +74,7 @@ EE::UI::UISceneNode. Setting the font as the default scene node. And finally, adding this scene node to the EE::Scene::SceneManager. For example: + ```cpp // ... after window creation // Load a font to use as the default font in our UI. @@ -98,13 +94,13 @@ SceneManager::instance()->add( uiSceneNode ); And that's all we need for a basic initialization. +## Updating the UI -Updating the UI ---------------- Updating the UI consist in two simple steps: updating and drawing the UI scene node contained by the EE::Scene::SceneManager. So we just need to do in our main loop: + ```cpp // Update the UI scene SceneManager::instance()->update(); @@ -130,9 +126,8 @@ the scene on every update or only when is needed. There are several options regarding this topic, since each scene node can be drawn into a separated frame buffer, in order to be able to control when to redraw a scene. +## Load the XML Layout Resource -Load the XML Layout Resource ----------------------------- XML layout resources can be loaded from any file system path, EE::UI::Pack accessible path or a hard-coded string. @@ -142,6 +137,7 @@ root scene node will be used. Following the [Write the XML](#write-the-xml) layout and the [Initializing the UI](#initializing-the-ui) code: + ```cpp uiSceneNode->loadLayoutFromString( R"xml( loadLayoutFromString( R"xml( This will load and create the widgets into the root element of the UI scene node. +## Cascading Style Sheets -Cascading Style Sheets ----------------------- [CSS (Cascading Style Sheets)](https://developer.mozilla.org/en-US/docs/Web/CSS) is the code you use to style the UI. It's very heavily based on the [CSS 2.1](https://www.w3.org/TR/CSS21/) standards with a good amount of CSS 3 features. Some of the main current differences are: eepp CSS properties don't -support inheritance (except for -[Custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties), +support inheritance (except for the case +[custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties), and `*` is supported), eepp also adds new properties oriented to decoration related and layout control stuffs, and CSS layout properties differ from the -standard since we use a different layout model. But you can learn how style the +standard since we use a different layout model. But you can learn how to style the UI following the CSS standards and then reading about the specific eepp UI features. Important CSS3 features that are currently supported: - * [Transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions) +* [Animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/) - * [Custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) +* [Transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions) - * [Media queries](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries) +* [Custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) - * [@font-face at rule](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face) +* [Media queries](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries) - * [:root element](https://developer.mozilla.org/en-US/docs/Web/CSS/:root) +* [@font-face at rule](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face) - * [Most of the background properties](https://developer.mozilla.org/en-US/docs/Web/CSS/background) +* [:root element](https://developer.mozilla.org/en-US/docs/Web/CSS/:root) +* [Most of the background properties](https://developer.mozilla.org/en-US/docs/Web/CSS/background) + +## Write the CSS -Write the CSS -------------- Following [Load the XML Layout Resource](#load-the-xml-layout-resource) we are going to write a very simple style to our UI. In order to that we can create a new CSS file (with `.css` extension) in our file system and save it in an @@ -200,6 +196,7 @@ accesible path to our project, or we can simply write the CSS in a string in our code. This is how our CSS could look like: + ```css * { font-size: 22dp; @@ -223,8 +220,8 @@ pixel](https://en.wikipedia.org/wiki/Device-independent_pixel), that allow us to keep our layout consistent between different screen densities. This concept was taken from the [Android pixel density implementation](https://developer.android.com/guide/practices/screens_support.html#density). -Loading the CSS Resource ------------------------- +## Loading the CSS Resource + Following [Load the XML Layout Resource](#load-the-xml-layout-resource) and [Write the CSS](#write-the-css) we are going to load our CSS from a string: @@ -252,13 +249,14 @@ To load a CSS from a file we need to first parse it with the EE::UI::CSS::StyleSheetParser. It will look like: + ```cpp CSS::StyleSheetParser parser; parser.loadFromFile( "path/to/our/stylesheet.css" ); uiSceneNode->setStyleSheet( parser.getStyleSheet() ); ``` -Example -------- +## Example + For a complete example of this introduction you can look into: [src/examples/ui_hello_world/ui_hello_world.cpp](https://github.com/SpartanJ/eepp/blob/develop/src/examples/ui_hello_world/ui_hello_world.cpp). diff --git a/include/eepp/scene/actionmanager.hpp b/include/eepp/scene/actionmanager.hpp index a30cea45f..488f88763 100644 --- a/include/eepp/scene/actionmanager.hpp +++ b/include/eepp/scene/actionmanager.hpp @@ -49,7 +49,6 @@ class EE_API ActionManager { protected: std::list mActions; std::list mActionsRemoveList; - Mutex mMutex; bool mUpdating; }; diff --git a/include/eepp/scene/node.hpp b/include/eepp/scene/node.hpp index 0b48eb35d..f9e1c58e5 100644 --- a/include/eepp/scene/node.hpp +++ b/include/eepp/scene/node.hpp @@ -329,7 +329,7 @@ class EE_API Node : public Transformable { bool invalidated() const; - void invalidate(); + virtual void invalidate( Node* invalidator ); Uint32 getChildCount() const; diff --git a/include/eepp/ui/css/animationdefinition.hpp b/include/eepp/ui/css/animationdefinition.hpp index 315ce595c..0033eab60 100644 --- a/include/eepp/ui/css/animationdefinition.hpp +++ b/include/eepp/ui/css/animationdefinition.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include using namespace EE::Math; @@ -14,7 +14,7 @@ namespace EE { namespace UI { namespace CSS { class EE_API AnimationDefinition { public: - static std::map + static std::unordered_map parseAnimationProperties( const std::vector& stylesheetProperties ); /* https://developer.mozilla.org/en-US/docs/Web/CSS/animation-direction */ @@ -57,17 +57,50 @@ class EE_API AnimationDefinition { const Ease::Interpolation& getTimingFunction() const; - std::string name; - Time delay = Time::Zero; - Time duration = Time::Zero; - Int32 iterations = 1; /* -1 == "infinite" */ - Ease::Interpolation timingFunction = Ease::Interpolation::Linear; - AnimationDirection direction = Normal; - AnimationFillMode fillMode = None; - bool paused = false; + const AnimationFillMode& getFillMode() const; + + void setName( const std::string& value ); + + void setDelay( const Time& value ); + + void setDuration( const Time& value ); + + void setIterations( const Int32& value ); + + void setTimingFunction( const Ease::Interpolation& value ); + + void setDirection( const AnimationDirection& value ); + + void setFillMode( const AnimationFillMode& value ); + + void setPaused( bool value ); + + const Uint32& getId() const; + + protected: + Uint32 mId; + std::string mName; + Time mDelay = Time::Zero; + Time mDuration = Time::Zero; + Int32 mIterations = 1; /* -1 == "infinite" */ + Ease::Interpolation mTimingFunction = Ease::Interpolation::Linear; + AnimationDirection mDirection = Normal; + AnimationFillMode mFillMode = None; + bool mPaused = false; }; -typedef std::map AnimationsMap; +inline bool operator==( const AnimationDefinition& a, const AnimationDefinition& b ) { + return a.getDuration() == b.getDuration() && a.getTimingFunction() == b.getTimingFunction() && + a.getDelay() == b.getDelay() && a.getDirection() == b.getDirection() && + a.isPaused() == b.isPaused() && a.getIterations() == b.getIterations() && + a.getName() == b.getName(); +} + +inline bool operator!=( const AnimationDefinition& a, const AnimationDefinition& b ) { + return !( a == b ); +} + +typedef std::unordered_map AnimationsMap; }}} // namespace EE::UI::CSS diff --git a/include/eepp/ui/css/stylesheetpropertyanimation.hpp b/include/eepp/ui/css/stylesheetpropertyanimation.hpp index 85cad78e3..148ca245c 100644 --- a/include/eepp/ui/css/stylesheetpropertyanimation.hpp +++ b/include/eepp/ui/css/stylesheetpropertyanimation.hpp @@ -74,8 +74,14 @@ class EE_API StyleSheetPropertyAnimation : public Action { const AnimationOrigin& getAnimationOrigin() const; + void setRunning( const bool& running ); + + void setPaused( const bool& paused ); + void notifyClose(); + const AnimationDefinition& getAnimation() const; + protected: AnimationDefinition mAnimation; const PropertyDefinition* mPropertyDef; @@ -87,6 +93,7 @@ class EE_API StyleSheetPropertyAnimation : public Action { Uint32 mPropertyIndex; std::string mFillModeValue; AnimationOrigin mAnimationOrigin; + bool mPaused; StyleSheetPropertyAnimation( const AnimationDefinition& animation, const PropertyDefinition* propertyDef, diff --git a/include/eepp/ui/uistyle.hpp b/include/eepp/ui/uistyle.hpp index 14f503d45..ca288a9f2 100644 --- a/include/eepp/ui/uistyle.hpp +++ b/include/eepp/ui/uistyle.hpp @@ -15,6 +15,10 @@ namespace EE { namespace Graphics { class Font; }} // namespace EE::Graphics +namespace EE { namespace UI { namespace CSS { +class StyleSheetPropertyAnimation; +}}} // namespace EE::UI::CSS + namespace EE { namespace UI { class UIWidget; @@ -41,6 +45,10 @@ class EE_API UIStyle : public UIState { bool hasTransition( const std::string& propertyName ); + CSS::StyleSheetPropertyAnimation* getAnimation( const CSS::PropertyDefinition* propertyDef ); + + bool hasAnimation( const CSS::PropertyDefinition* propertyDef ); + CSS::TransitionDefinition getTransition( const std::string& propertyName ); const bool& isChangingState() const; @@ -54,8 +62,8 @@ class EE_API UIStyle : public UIState { CSS::StyleSheetStyle mElementStyle; CSS::StyleSheetProperties mProperties; CSS::StyleSheetVariables mVariables; - std::vector mTransitionAttributes; - std::vector mAnimationAttributes; + std::vector mTransitionProperties; + std::vector mAnimationProperties; CSS::TransitionsMap mTransitions; CSS::AnimationsMap mAnimations; std::set mRelatedWidgets; @@ -87,7 +95,11 @@ class EE_API UIStyle : public UIState { void applyStyleSheetProperty( const CSS::StyleSheetProperty& property, CSS::StyleSheetProperties& prevProperties ); - void startAnimations(); + void updateAnimationsPlayState(); + + void updateAnimations(); + + void startAnimations( const CSS::AnimationsMap& animations ); void removeAllAnimations(); diff --git a/include/eepp/ui/uitabwidget.hpp b/include/eepp/ui/uitabwidget.hpp index 10b4f6a0c..a7c009af6 100644 --- a/include/eepp/ui/uitabwidget.hpp +++ b/include/eepp/ui/uitabwidget.hpp @@ -116,6 +116,10 @@ class EE_API UITabWidget : public UIWidget { virtual std::string getPropertyString( const PropertyDefinition* propertyDef, const Uint32& propertyIndex = 0 ); + virtual bool isDrawInvalidator() const; + + void invalidate( Node* invalidator ); + protected: friend class UITab; diff --git a/include/eepp/ui/uiwindow.hpp b/include/eepp/ui/uiwindow.hpp index 21c2c7813..f9171f961 100644 --- a/include/eepp/ui/uiwindow.hpp +++ b/include/eepp/ui/uiwindow.hpp @@ -146,7 +146,7 @@ class EE_API UIWindow : public UIWidget { virtual void internalDraw(); - void invalidate(); + void invalidate( Node* invalidator ); bool invalidated(); diff --git a/premake4.lua b/premake4.lua index 33799ec0e..062f7b040 100644 --- a/premake4.lua +++ b/premake4.lua @@ -802,14 +802,7 @@ solution "eepp" project "SOIL2-static" kind "StaticLib" - - if is_vs() then - language "C++" - buildoptions { "/TP" } - else - language "C" - end - + language "C" set_targetdir("libs/" .. os.get_real() .. "/thirdparty/") files { "src/thirdparty/SOIL2/src/SOIL2/*.c" } includedirs { "src/thirdparty/SOIL2" } diff --git a/premake5.lua b/premake5.lua index b736efadc..4f54da639 100644 --- a/premake5.lua +++ b/premake5.lua @@ -216,9 +216,12 @@ function build_link_configuration( package_name, use_ee_icon ) links { "SDL2", "SDL2main" } end - filter { "options:windows-vc-build", "system:windows" } + filter { "options:windows-vc-build", "system:windows", "platforms:x86" } syslibdirs { "src/thirdparty/" .. remote_sdl2_version .."/lib/x86" } + filter { "options:windows-vc-build", "system:windows", "platforms:x86_64" } + syslibdirs { "src/thirdparty/" .. remote_sdl2_version .."/lib/x64" } + filter "system:emscripten" linkoptions{ "-O2 -s TOTAL_MEMORY=67108864 -s ASM_JS=1 -s VERBOSE=1 -s DISABLE_EXCEPTION_CATCHING=0 -s USE_SDL=2 -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s ERROR_ON_MISSING_LIBRARIES=0 -s FULL_ES3=1 -s \"BINARYEN_TRAP_MODE='clamp'\"" } buildoptions { "-fno-strict-aliasing -O2 -s USE_SDL=2 -s PRECISE_F32=1 -s ENVIRONMENT=web" } @@ -534,6 +537,7 @@ end workspace "eepp" targetdir("./bin/") configurations { "debug", "release" } + platforms { "x86_64", "x86" } rtti "On" download_and_extract_dependencies() select_backend() @@ -542,6 +546,12 @@ workspace "eepp" location("./make/" .. os.target() .. "/") objdir("obj/" .. os.target() .. "/") + filter "platforms:x86" + architecture "x86" + + filter "platforms:x86_64" + architecture "x86_64" + filter "system:macosx" defines { "GL_SILENCE_DEPRECATION" } @@ -558,15 +568,11 @@ workspace "eepp" project "SOIL2-static" kind "StaticLib" + language "C" targetdir("libs/" .. os.target() .. "/thirdparty/") files { "src/thirdparty/SOIL2/src/SOIL2/*.c" } incdirs { "src/thirdparty/SOIL2" } build_base_configuration( "SOIL2" ) - filter "action:vs*" - language "C++" - buildoptions { "/TP" } - filter "action:not vs*" - language "C" project "glew-static" kind "StaticLib" diff --git a/projects/linux/ee.creator.user b/projects/linux/ee.creator.user index 41fdda298..ec02bbd82 100644 --- a/projects/linux/ee.creator.user +++ b/projects/linux/ee.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -79,7 +79,7 @@ {6d057187-158a-4883-8d5b-d470a6b6b025} 10 0 - 15 + 0 ../../make/linux @@ -130,7 +130,7 @@ false - -e config=release eepp-test + -e config=release_x86_64 eepp-test false true @@ -144,7 +144,7 @@ true - -e config=release clean + -e config=release_x86_64 clean /usr/bin/make %{buildDir} clean @@ -217,8 +217,8 @@ false - -e config=release - make + -e config=release_x86_64 + false true GenericProjectManager.GenericMakeStep @@ -231,7 +231,7 @@ true - -e config=release clean + -e config=release_x86_64 clean make %{buildDir} ProjectExplorer.ProcessStep @@ -253,7 +253,7 @@ false - + -e config=debug_x86 make.sh false true @@ -289,7 +289,7 @@ false - -e config=release + -e config=release_x86 make.sh false true @@ -303,7 +303,7 @@ true - -e config=release clean + -e config=release_x86 clean make.sh %{buildDir} ProjectExplorer.ProcessStep @@ -657,7 +657,7 @@ false - -e config=release eepp-static + -e config=release_x86_64 eepp-static false true @@ -671,7 +671,7 @@ true - -e config=release clean + -e config=release_x86_64 clean make %{buildDir} clean @@ -731,7 +731,7 @@ false - -e config=release eepp-shared + -e config=release_x86_64 eepp-shared false true @@ -745,7 +745,7 @@ true - -e config=release clean + -e config=release_x86_64 clean make %{buildDir} clean @@ -813,7 +813,7 @@ false - -e config=release eepp-ew + -e config=release_x86_64 eepp-ew false true @@ -828,7 +828,7 @@ true - -e config=release clean + -e config=release_x86_64 clean /usr/bin/make %{buildDir} Clone of clean @@ -889,7 +889,7 @@ false - -e config=release eepp-es + -e config=release_x86_64 eepp-es false true @@ -904,7 +904,7 @@ true - -e config=release clean + -e config=release_x86_64 clean /usr/bin/make %{buildDir} Clone of clean diff --git a/src/eepp/maps/mapeditor/uimap.cpp b/src/eepp/maps/mapeditor/uimap.cpp index 2c7357f4d..b51d59688 100644 --- a/src/eepp/maps/mapeditor/uimap.cpp +++ b/src/eepp/maps/mapeditor/uimap.cpp @@ -104,7 +104,7 @@ void UIMap::scheduledUpdate( const Time& time ) { UIWindow::scheduledUpdate( time ); if ( NULL != mMap ) { - invalidate(); + invalidate( this ); invalidateDraw(); mMap->update(); diff --git a/src/eepp/scene/actionmanager.cpp b/src/eepp/scene/actionmanager.cpp index e1751ccc4..c93b99fe5 100644 --- a/src/eepp/scene/actionmanager.cpp +++ b/src/eepp/scene/actionmanager.cpp @@ -17,8 +17,6 @@ ActionManager::~ActionManager() { } void ActionManager::addAction( Action* action ) { - Lock lock( mMutex ); - bool found = ( std::find( mActions.begin(), mActions.end(), action ) != mActions.end() ); if ( !found ) { @@ -27,8 +25,6 @@ void ActionManager::addAction( Action* action ) { } Action* ActionManager::getActionByTag( const Uint32& tag ) { - Lock lock( mMutex ); - for ( auto it = mActions.begin(); it != mActions.end(); ++it ) { Action* action = ( *it ); @@ -40,7 +36,6 @@ Action* ActionManager::getActionByTag( const Uint32& tag ) { } std::vector ActionManager::getActionsFromTarget( Node* target ) { - Lock lock( mMutex ); std::vector actions; for ( auto it = mActions.begin(); it != mActions.end(); ++it ) { @@ -54,7 +49,6 @@ std::vector ActionManager::getActionsFromTarget( Node* target ) { } std::vector ActionManager::getActionsByTagFromTarget( Node* target, const Uint32& tag ) { - Lock lock( mMutex ); std::vector actions; for ( auto it = mActions.begin(); it != mActions.end(); ++it ) { @@ -74,15 +68,11 @@ void ActionManager::removeActionByTag( const Uint32& tag ) { void ActionManager::removeActionsByTagFromTarget( Node* target, const Uint32& tag ) { std::vector removeList; - { - Lock lock( mMutex ); + for ( auto it = mActions.begin(); it != mActions.end(); ++it ) { + Action* action = ( *it ); - for ( auto it = mActions.begin(); it != mActions.end(); ++it ) { - Action* action = ( *it ); - - if ( action->getTarget() == target && action->getTag() == tag ) { - removeList.push_back( *it ); - } + if ( action->getTarget() == target && action->getTag() == tag ) { + removeList.push_back( *it ); } } @@ -96,25 +86,22 @@ void ActionManager::update( const Time& time ) { std::vector removeList; - { - Lock lock( mMutex ); - mUpdating = true; + mUpdating = true; - for ( auto it = mActions.begin(); it != mActions.end(); ++it ) { - Action* action = ( *it ); + for ( auto it = mActions.begin(); it != mActions.end(); ++it ) { + Action* action = ( *it ); - action->update( time ); + action->update( time ); - if ( action->isDone() ) { - action->sendEvent( Action::ActionType::OnDone ); + if ( action->isDone() ) { + action->sendEvent( Action::ActionType::OnDone ); - removeList.push_back( action ); - } + removeList.push_back( action ); } - - mUpdating = false; } + mUpdating = false; + for ( auto it = mActionsRemoveList.begin(); it != mActionsRemoveList.end(); ++it ) removeAction( ( *it ) ); @@ -125,20 +112,14 @@ void ActionManager::update( const Time& time ) { } std::size_t ActionManager::count() const { - Lock lock( const_cast( mMutex ) ); - return mActions.size(); } bool ActionManager::isEmpty() const { - Lock lock( const_cast( mMutex ) ); - return mActions.empty(); } void ActionManager::clear() { - Lock lock( mMutex ); - for ( auto it = mActions.begin(); it != mActions.end(); ++it ) { Action* action = ( *it ); @@ -149,8 +130,6 @@ void ActionManager::clear() { } void ActionManager::removeAction( Action* action ) { - Lock lock( mMutex ); - if ( NULL != action ) { if ( !mUpdating ) { bool found = std::find( mActions.begin(), mActions.end(), action ) != mActions.end(); @@ -175,15 +154,11 @@ void ActionManager::removeActions( const std::vector& actions ) { void ActionManager::removeAllActionsFromTarget( Node* target ) { std::vector removeList; - { - Lock lock( mMutex ); + for ( auto it = mActions.begin(); it != mActions.end(); ++it ) { + Action* action = ( *it ); - for ( auto it = mActions.begin(); it != mActions.end(); ++it ) { - Action* action = ( *it ); - - if ( action->getTarget() == target ) { - removeList.push_back( *it ); - } + if ( action->getTarget() == target ) { + removeList.push_back( *it ); } } diff --git a/src/eepp/scene/eventdispatcher.cpp b/src/eepp/scene/eventdispatcher.cpp index e0bc84f74..6e25a1110 100644 --- a/src/eepp/scene/eventdispatcher.cpp +++ b/src/eepp/scene/eventdispatcher.cpp @@ -52,7 +52,7 @@ void EventDispatcher::inputCallback( InputEvent* Event ) { case InputEvent::VideoResize: case InputEvent::VideoExpose: { if ( NULL != mSceneNode ) - mSceneNode->invalidate(); + mSceneNode->invalidate( NULL ); } } } diff --git a/src/eepp/scene/node.cpp b/src/eepp/scene/node.cpp index d4aa857f2..3215afcc3 100644 --- a/src/eepp/scene/node.cpp +++ b/src/eepp/scene/node.cpp @@ -1101,7 +1101,7 @@ void Node::setReverseDraw( bool reverseDraw ) { void Node::invalidateDraw() { if ( NULL != mNodeDrawInvalidator ) { - mNodeDrawInvalidator->invalidate(); + mNodeDrawInvalidator->invalidate( this ); } } @@ -1572,7 +1572,7 @@ bool Node::isDrawInvalidator() const { return false; } -void Node::invalidate() { +void Node::invalidate( Node* invalidator ) { if ( mVisible && mAlpha != 0.f ) { writeNodeFlag( NODE_FLAG_VIEW_DIRTY, 1 ); } diff --git a/src/eepp/ui/css/animationdefinition.cpp b/src/eepp/ui/css/animationdefinition.cpp index c877aaddf..d462e1291 100644 --- a/src/eepp/ui/css/animationdefinition.cpp +++ b/src/eepp/ui/css/animationdefinition.cpp @@ -7,7 +7,7 @@ bool isTimingFunction( const std::string& str ) { return Ease::Interpolation::None != Ease::fromName( str, Ease::Interpolation::None ); } -std::map AnimationDefinition::parseAnimationProperties( +std::unordered_map AnimationDefinition::parseAnimationProperties( const std::vector& stylesheetProperties ) { AnimationsMap animations; std::vector names; @@ -41,40 +41,39 @@ std::map AnimationDefinition::parseAnimationPr std::string val( String::trim( String::toLower( part ) ) ); if ( isDirectionString( val ) ) { - animationDef.direction = directionFromString( val ); + animationDef.setDirection( directionFromString( val ) ); } else if ( isAnimationFillModeString( val ) ) { - animationDef.fillMode = fillModeFromString( val ); + animationDef.setFillMode( fillModeFromString( val ) ); } else if ( "infinite" == val ) { - animationDef.iterations = -1; + animationDef.setIterations( -1 ); } else if ( "paused" == val ) { - animationDef.paused = true; + animationDef.setPaused( true ); } else if ( "running" == val ) { - animationDef.paused = false; + animationDef.setPaused( false ); } else if ( isTimingFunction( val ) ) { - animationDef.timingFunction = Ease::fromName( val ); + animationDef.setTimingFunction( Ease::fromName( val ) ); } else if ( Time::isValid( val ) ) { if ( durationSet ) { - animationDef.delay = - StyleSheetProperty( "animation-delay", val ).asTime(); + animationDef.setDelay( Time::fromString( val ) ); } else { - animationDef.duration = - StyleSheetProperty( "animation-duration", val ).asTime(); + animationDef.setDuration( Time::fromString( val ) ); durationSet = true; } } else if ( String::isNumber( val, true ) ) { int iterations = 1; if ( String::fromString( iterations, val ) ) { - animationDef.iterations = iterations; + animationDef.setIterations( iterations ); } } else { - animationDef.name = part; + animationDef.setName( part ); } } - animations[animationDef.name] = std::move( animationDef ); + animations[animationDef.getName()] = std::move( animationDef ); } } + return animations; break; } case PropertyId::AnimationName: @@ -133,27 +132,27 @@ std::map AnimationDefinition::parseAnimationPr for ( size_t i = 0; i < names.size(); i++ ) { AnimationDefinition animationDef; - animationDef.name = names[i]; + animationDef.setName( names[i] ); if ( !delays.empty() ) - animationDef.delay = delays[i % delays.size()]; + animationDef.setDelay( delays[i % delays.size()] ); if ( !durations.empty() ) - animationDef.duration = delays[i % durations.size()]; + animationDef.setDuration( delays[i % durations.size()] ); if ( !fillModes.empty() ) - animationDef.fillMode = fillModes[i % fillModes.size()]; + animationDef.setFillMode( fillModes[i % fillModes.size()] ); if ( !pausedStates.empty() ) - animationDef.paused = pausedStates[i % pausedStates.size()]; + animationDef.setPaused( pausedStates[i % pausedStates.size()] ); if ( !iterations.empty() ) - animationDef.iterations = iterations[i % iterations.size()]; + animationDef.setIterations( iterations[i % iterations.size()] ); if ( !timingFunctions.empty() ) - animationDef.timingFunction = timingFunctions[i % timingFunctions.size()]; + animationDef.setTimingFunction( timingFunctions[i % timingFunctions.size()] ); - animations[animationDef.name] = std::move( animationDef ); + animations[animationDef.getName()] = std::move( animationDef ); } return animations; @@ -205,31 +204,72 @@ AnimationDefinition::AnimationFillMode AnimationDefinition::fillModeFromString( AnimationDefinition::AnimationDefinition() {} const AnimationDefinition::AnimationDirection& AnimationDefinition::getDirection() const { - return direction; + return mDirection; } const bool& AnimationDefinition::isPaused() const { - return paused; + return mPaused; } const Int32& AnimationDefinition::getIterations() const { - return iterations; + return mIterations; } const std::string& AnimationDefinition::getName() const { - return name; + return mName; } const Time& AnimationDefinition::getDelay() const { - return delay; + return mDelay; } const Time& AnimationDefinition::getDuration() const { - return duration; + return mDuration; } const Ease::Interpolation& AnimationDefinition::getTimingFunction() const { - return timingFunction; + return mTimingFunction; +} + +void AnimationDefinition::setName( const std::string& value ) { + mName = value; + mId = String::hash( mName ); +} + +void AnimationDefinition::setFillMode( const AnimationFillMode& value ) { + mFillMode = value; +} + +void AnimationDefinition::setPaused( bool value ) { + mPaused = value; +} + +const Uint32& AnimationDefinition::getId() const { + return mId; +} + +const AnimationDefinition::AnimationFillMode& AnimationDefinition::getFillMode() const { + return mFillMode; +} + +void AnimationDefinition::setDirection( const AnimationDirection& value ) { + mDirection = value; +} + +void AnimationDefinition::setTimingFunction( const Ease::Interpolation& value ) { + mTimingFunction = value; +} + +void AnimationDefinition::setIterations( const Int32& value ) { + mIterations = value; +} + +void AnimationDefinition::setDuration( const Time& value ) { + mDuration = value; +} + +void AnimationDefinition::setDelay( const Time& value ) { + mDelay = value; } }}} // namespace EE::UI::CSS diff --git a/src/eepp/ui/css/stylesheet.cpp b/src/eepp/ui/css/stylesheet.cpp index 67c9210c7..e458567b5 100644 --- a/src/eepp/ui/css/stylesheet.cpp +++ b/src/eepp/ui/css/stylesheet.cpp @@ -106,12 +106,14 @@ const KeyframesDefinition& StyleSheet::getKeyframesDefinition( const std::string } void StyleSheet::addKeyframes( const KeyframesDefinition& keyframes ) { - mKeyframesMap[keyframes.getName()] = keyframes; + // "none" is a reserved keyword + if ( keyframes.getName() != "none" ) + mKeyframesMap[keyframes.getName()] = keyframes; } void StyleSheet::addKeyframes( const KeyframesDefinitionMap& keyframesMap ) { - for ( auto& keyframe : keyframesMap ) { - mKeyframesMap[keyframe.first] = keyframe.second; + for ( auto& keyframes : keyframesMap ) { + addKeyframes( keyframes.second ); } } diff --git a/src/eepp/ui/css/stylesheetpropertyanimation.cpp b/src/eepp/ui/css/stylesheetpropertyanimation.cpp index ccfd3fc3f..14e110d52 100644 --- a/src/eepp/ui/css/stylesheetpropertyanimation.cpp +++ b/src/eepp/ui/css/stylesheetpropertyanimation.cpp @@ -172,9 +172,9 @@ StyleSheetPropertyAnimation* StyleSheetPropertyAnimation::New( const Uint32& propertyIndex, const Time& duration, const Time& delay, const Ease::Interpolation& timingFunction, const AnimationOrigin& animationOrigin ) { AnimationDefinition animation; - animation.delay = delay; - animation.duration = duration; - animation.timingFunction = timingFunction; + animation.setDelay( delay ); + animation.setDuration( duration ); + animation.setTimingFunction( timingFunction ); return New( animation, property, {startValue, endValue}, {0, 1}, propertyIndex, animationOrigin ); } @@ -189,9 +189,10 @@ StyleSheetPropertyAnimation::StyleSheetPropertyAnimation( const AnimationDefinit mPropertyDef( propertyDef ), mStates( states ), mAnimationStepsTime( animationStepsTime ), - mPendingIterations( animation.iterations ), + mPendingIterations( animation.getIterations() ), mPropertyIndex( propertyIndex ), - mAnimationOrigin( animationOrigin ) { + mAnimationOrigin( animationOrigin ), + mPaused( mAnimation.isPaused() ) { mId = ID; } @@ -208,38 +209,41 @@ void StyleSheetPropertyAnimation::stop() { } void StyleSheetPropertyAnimation::update( const Time& time ) { + if ( mPaused ) + return; + mRealElapsed += time; bool wasDone = false; - if ( mRealElapsed >= mAnimation.delay ) { + if ( mRealElapsed >= mAnimation.getDelay() ) { mElapsed += time; if ( mPendingIterations > 0 ) { - while ( mElapsed > mAnimation.duration ) { + while ( mElapsed > mAnimation.getDuration() ) { if ( mPendingIterations > 0 ) { mPendingIterations--; if ( mPendingIterations > 0 ) { wasDone = true; - mElapsed -= mAnimation.duration; + mElapsed -= mAnimation.getDuration(); } else { - mElapsed = mAnimation.duration; + mElapsed = mAnimation.getDuration(); } } else { break; } } } else if ( mPendingIterations == -1 ) { - while ( mElapsed > mAnimation.duration ) { - mElapsed -= mAnimation.duration; + while ( mElapsed > mAnimation.getDuration() ) { + mElapsed -= mAnimation.getDuration(); wasDone = true; } } if ( wasDone && ( mPendingIterations > 0 || mPendingIterations == -1 ) ) { - if ( mAnimation.direction == AnimationDefinition::AnimationDirection::Alternate || - mAnimation.direction == + if ( mAnimation.getDirection() == AnimationDefinition::AnimationDirection::Alternate || + mAnimation.getDirection() == AnimationDefinition::AnimationDirection::AlternateReverse ) { reverseAnimation(); } @@ -254,16 +258,16 @@ void StyleSheetPropertyAnimation::update( const Time& time ) { } bool StyleSheetPropertyAnimation::isDone() { - return mElapsed.asMicroseconds() >= mAnimation.duration.asMicroseconds() && + return mElapsed.asMicroseconds() >= mAnimation.getDuration().asMicroseconds() && ( mPendingIterations == 0 || mPendingIterations != -1 ); } Float StyleSheetPropertyAnimation::getCurrentProgress() { - return eemin( mElapsed.asMilliseconds() / mAnimation.duration.asMilliseconds(), 1. ); + return eemin( mElapsed.asMilliseconds() / mAnimation.getDuration().asMilliseconds(), 1. ); } Time StyleSheetPropertyAnimation::getTotalTime() { - return mAnimation.duration; + return mAnimation.getDuration(); } Action* StyleSheetPropertyAnimation::clone() const { @@ -291,7 +295,7 @@ const std::string& StyleSheetPropertyAnimation::getEndValue() const { } void StyleSheetPropertyAnimation::onStart() { - if ( mRealElapsed >= mAnimation.delay ) { + if ( mRealElapsed >= mAnimation.getDelay() ) { onUpdate( Time::Zero ); } } @@ -313,15 +317,58 @@ void StyleSheetPropertyAnimation::onUpdate( const Time& time ) { if ( curPos - 1 >= 0 && curPos < static_cast( mStates.size() ) ) { tweenProperty( widget, normalizedProgress, mPropertyDef, mStates[curPos - 1], - mStates[curPos], mAnimation.timingFunction, mPropertyIndex, isDone() ); + mStates[curPos], mAnimation.getTimingFunction(), mPropertyIndex, + isDone() ); } } } void StyleSheetPropertyAnimation::onTargetChange() { if ( NULL != mNode && mNode->isWidget() && mAnimationOrigin == AnimationOrigin::Animation ) { - UIWidget* widget = mNode->asType(); - mFillModeValue = widget->getPropertyString( mPropertyDef, mPropertyIndex ); + if ( mAnimation.getFillMode() == AnimationDefinition::AnimationFillMode::None ) { + UIWidget* widget = mNode->asType(); + mFillModeValue = widget->getPropertyString( mPropertyDef, mPropertyIndex ); + } else if ( mAnimation.getFillMode() == AnimationDefinition::AnimationFillMode::Forwards ) { + if ( mStates.empty() ) + return; + switch ( mAnimation.getDirection() ) { + case AnimationDefinition::AnimationDirection::Normal: { + mFillModeValue = mStates[mStates.size() - 1]; + break; + } + case AnimationDefinition::AnimationDirection::Reverse: { + mFillModeValue = mStates[0]; + break; + } + case AnimationDefinition::AnimationDirection::Alternate: { + if ( mAnimation.getIterations() % 2 == 0 ) { + mFillModeValue = mStates[0]; + } else { + mFillModeValue = mStates[mStates.size() - 1]; + } + break; + } + case AnimationDefinition::AnimationDirection::AlternateReverse: { + if ( mAnimation.getIterations() % 2 == 0 ) { + mFillModeValue = mStates[mStates.size() - 1]; + } else { + + mFillModeValue = mStates[0]; + } + break; + } + } + } else if ( mAnimation.getFillMode() == + AnimationDefinition::AnimationFillMode::Backwards ) { + if ( mStates.empty() ) + return; + if ( mAnimation.getDirection() == AnimationDefinition::AnimationDirection::Normal || + mAnimation.getDirection() == AnimationDefinition::AnimationDirection::Alternate ) { + mFillModeValue = mStates[0]; + } else { + mFillModeValue = mStates[mStates.size() - 1]; + } + } } } @@ -329,10 +376,10 @@ void StyleSheetPropertyAnimation::setElapsed( const Time& elapsed ) { mElapsed = elapsed; if ( mPendingIterations > 0 ) { - while ( mElapsed > mAnimation.duration ) { + while ( mElapsed > mAnimation.getDuration() ) { if ( mPendingIterations > 0 ) { mPendingIterations--; - mElapsed = mAnimation.duration - mElapsed; + mElapsed = mAnimation.getDuration() - mElapsed; } else { break; } @@ -344,9 +391,17 @@ const AnimationOrigin& StyleSheetPropertyAnimation::getAnimationOrigin() const { return mAnimationOrigin; } +void StyleSheetPropertyAnimation::setRunning( const bool& running ) { + mPaused = !running; +} + +void StyleSheetPropertyAnimation::setPaused( const bool& paused ) { + mPaused = paused; +} + void StyleSheetPropertyAnimation::notifyClose() { if ( mAnimationOrigin == AnimationOrigin::Animation && NULL != mNode && mNode->isWidget() ) { - if ( mAnimation.fillMode == AnimationDefinition::AnimationFillMode::None ) { + if ( mAnimation.getFillMode() != AnimationDefinition::AnimationFillMode::Both ) { UIWidget* widget = mNode->asType(); widget->applyProperty( StyleSheetProperty( mPropertyDef, mFillModeValue, mPropertyIndex ) ); @@ -354,13 +409,17 @@ void StyleSheetPropertyAnimation::notifyClose() { } } +const AnimationDefinition& StyleSheetPropertyAnimation::getAnimation() const { + return mAnimation; +} + const Time& StyleSheetPropertyAnimation::getElapsed() const { return mElapsed; } void StyleSheetPropertyAnimation::prepareDirection() { - if ( mAnimation.direction == AnimationDefinition::AnimationDirection::Reverse || - mAnimation.direction == AnimationDefinition::AnimationDirection::AlternateReverse ) { + if ( mAnimation.getDirection() == AnimationDefinition::AnimationDirection::Reverse || + mAnimation.getDirection() == AnimationDefinition::AnimationDirection::AlternateReverse ) { reverseAnimation(); } } diff --git a/src/eepp/ui/css/stylesheetspecification.cpp b/src/eepp/ui/css/stylesheetspecification.cpp index 8a3a59119..a11082e51 100644 --- a/src/eepp/ui/css/stylesheetspecification.cpp +++ b/src/eepp/ui/css/stylesheetspecification.cpp @@ -6,11 +6,14 @@ namespace EE { namespace UI { namespace CSS { SINGLETON_DECLARE_IMPLEMENTATION( StyleSheetSpecification ) StyleSheetSpecification::StyleSheetSpecification() { - // TODO: Add support to animations (@keyframes). + // TODO: Add correct "background" and "foreground" shorthand. // TODO: Support border-color top right bottom left. // TODO: Support border-radius top right bottom left. - // TODO: Support box-sizing or something similar. - // TODO: Add correct "background" and "foreground" shorthand. + // TODO: Create a rule to set the border position against its box. + // Something like: "border-box", with the following options: + // inside: The border is drawn inside the box. + // outside: The border is drawn outside the box. + // over: The border is drawn in the middle point of inside and outside. registerDefaultProperties(); registerDefaultNodeSelectors(); } diff --git a/src/eepp/ui/tools/uicolorpicker.cpp b/src/eepp/ui/tools/uicolorpicker.cpp index 3246a5c85..f14ef60a6 100644 --- a/src/eepp/ui/tools/uicolorpicker.cpp +++ b/src/eepp/ui/tools/uicolorpicker.cpp @@ -182,27 +182,27 @@ UIColorPicker::UIColorPicker( UIWindow* attachTo, const UIColorPicker::ColorPick - + - + - + - + - + diff --git a/src/eepp/ui/uistyle.cpp b/src/eepp/ui/uistyle.cpp index fcac76633..3738d146f 100644 --- a/src/eepp/ui/uistyle.cpp +++ b/src/eepp/ui/uistyle.cpp @@ -97,6 +97,26 @@ bool UIStyle::hasTransition( const std::string& propertyName ) { mTransitions.find( "all" ) != mTransitions.end(); } +StyleSheetPropertyAnimation* UIStyle::getAnimation( const PropertyDefinition* propertyDef ) { + std::vector actions = mWidget->getActionsByTag( propertyDef->getId() ); + if ( !actions.empty() ) { + for ( auto& action : actions ) { + if ( action->getId() == StyleSheetPropertyAnimation::ID ) { + StyleSheetPropertyAnimation* animation = + static_cast( action ); + if ( animation->getAnimationOrigin() == AnimationOrigin::Animation ) { + return animation; + } + } + } + } + return NULL; +} + +bool UIStyle::hasAnimation( const PropertyDefinition* propertyDef ) { + return NULL != getAnimation( propertyDef ); +} + TransitionDefinition UIStyle::getTransition( const std::string& propertyName ) { auto propertyTransitionIt = mTransitions.find( propertyName ); @@ -152,9 +172,9 @@ void UIStyle::tryApplyStyle( const StyleSheetStyle& style ) { mProperties[property.getId()] = property; if ( String::startsWith( property.getName(), "transition" ) ) - mTransitionAttributes.push_back( property ); + mTransitionProperties.push_back( property ); else if ( String::startsWith( property.getName(), "animation" ) ) - mAnimationAttributes.push_back( property ); + mAnimationProperties.push_back( property ); } } } @@ -223,12 +243,10 @@ void UIStyle::onStateChange() { if ( NULL != mWidget ) { mChangingState = true; - bool wasAnAnimation = !mAnimations.empty(); CSS::StyleSheetProperties prevProperties( mProperties ); mProperties.clear(); - mTransitionAttributes.clear(); - mAnimationAttributes.clear(); - mAnimations.clear(); + mTransitionProperties.clear(); + mAnimationProperties.clear(); tryApplyStyle( mElementStyle ); @@ -241,21 +259,14 @@ void UIStyle::onStateChange() { } if ( !mapEquals( mProperties, prevProperties ) ) { - if ( wasAnAnimation ) - removeAllAnimations(); - - if ( !mAnimationAttributes.empty() ) { - mAnimations = AnimationDefinition::parseAnimationProperties( mAnimationAttributes ); - } - - if ( !mTransitionAttributes.empty() ) { - mTransitions = - TransitionDefinition::parseTransitionProperties( mTransitionAttributes ); - } - mWidget->beginAttributesTransaction(); - startAnimations(); + updateAnimations(); + + if ( !mTransitionProperties.empty() ) { + mTransitions = + TransitionDefinition::parseTransitionProperties( mTransitionProperties ); + } for ( auto& prop : mProperties ) { StyleSheetProperty& property = prop.second; @@ -382,7 +393,8 @@ void UIStyle::applyStyleSheetProperty( const StyleSheetProperty& property, if ( !mWidget->isSceneNodeLoading() && NULL != propertyDefinition && StyleSheetPropertyAnimation::animationSupported( propertyDefinition->getType() ) && - hasTransition( property.getName() ) ) { + hasTransition( property.getName() ) && + !hasAnimation( property.getPropertyDefinition() ) ) { std::string currentValue = mWidget->getPropertyString( propertyDefinition, property.getIndex() ); std::string startValue( currentValue ); @@ -467,14 +479,95 @@ void UIStyle::applyStyleSheetProperty( const StyleSheetProperty& property, } } -void UIStyle::startAnimations() { +void UIStyle::updateAnimations() { + bool isDifferent = false; + CSS::AnimationsMap animations; + + if ( !mAnimationProperties.empty() ) { + animations = AnimationDefinition::parseAnimationProperties( mAnimationProperties ); + if ( animations.size() == mAnimations.size() ) { + for ( auto& animation : animations ) { + auto animIt = mAnimations.find( animation.second.getName() ); + if ( animIt == mAnimations.end() || animIt->second != animation.second ) { + isDifferent = true; + break; + } + } + } else { + isDifferent = true; + } + } else if ( !mAnimations.empty() && mAnimationProperties.empty() ) { + isDifferent = true; + } + + if ( isDifferent ) { + mAnimations.clear(); + + removeAllAnimations(); + + startAnimations( animations ); + } else if ( !mAnimationProperties.empty() ) { + updateAnimationsPlayState(); + } +} + +void UIStyle::updateAnimationsPlayState() { + if ( mAnimations.empty() ) + return; + std::vector actions = mWidget->getActions(); + for ( auto& action : actions ) { + if ( action->getId() == StyleSheetPropertyAnimation::ID ) { + StyleSheetPropertyAnimation* animation = + static_cast( action ); + if ( animation->getAnimationOrigin() == AnimationOrigin::Animation ) { + // Check all the active animations. + size_t animPos = 0; + for ( auto anim = mAnimations.begin(); anim != mAnimations.end(); anim++ ) { + // Find the animation index by iterating over them... + if ( anim->first == animation->getAnimation().getName() ) { + // Once found the iteration index of the corresponding keyframe animation + // First check if in the current animation properties is there any + // "animation-play-state" definition. + bool isSet = false; + for ( auto& animProp : mAnimationProperties ) { + if ( NULL != animProp.getPropertyDefinition() && + animProp.getPropertyDefinition()->getPropertyId() == + PropertyId::AnimationPlayState ) { + // If found, get the pause/running state of the property, using the + // index of the current animation, and set the animation.play-state. + size_t animPropCount = animProp.getPropertyIndexCount(); + bool paused = animProp.getPropertyIndex( animPos % animPropCount ) + .getValue() == "paused" + ? true + : false; + animation->setPaused( paused ); + isSet = true; + break; + } + } + // If animation-play-state if set, continue with the next action. + if ( isSet ) + break; + // Otherwise set the animation-play-state defined on the animation. + animation->setPaused( animation->getAnimation().isPaused() ); + } + animPos++; + } + } + } + } +} + +void UIStyle::startAnimations( const CSS::AnimationsMap& animations ) { UISceneNode* uiSceneNode = mWidget->getUISceneNode(); if ( NULL == uiSceneNode ) return; + mAnimations = animations; + CSS::StyleSheet& styleSheet = uiSceneNode->getStyleSheet(); - for ( auto& anim : mAnimations ) { + for ( auto& anim : animations ) { if ( styleSheet.isKeyframesDefined( anim.first ) ) { const AnimationDefinition& animation = anim.second; const KeyframesDefinition& keyframes = styleSheet.getKeyframesDefinition( anim.first ); @@ -493,6 +586,7 @@ void UIStyle::startAnimations() { StyleSheetPropertyAnimation* newAnimation = StyleSheetPropertyAnimation::fromAnimationKeyframes( animation, keyframes, propDef, mWidget, i ); + newAnimation->setFlags( animation.getId() ); newAnimation->setTag( propDef->getId() ); mWidget->runAction( newAnimation ); } @@ -501,6 +595,7 @@ void UIStyle::startAnimations() { StyleSheetPropertyAnimation* newAnimation = StyleSheetPropertyAnimation::fromAnimationKeyframes( animation, keyframes, propDef, mWidget, 0 ); + newAnimation->setFlags( animation.getId() ); newAnimation->setTag( propDef->getId() ); mWidget->runAction( newAnimation ); } @@ -509,6 +604,7 @@ void UIStyle::startAnimations() { StyleSheetPropertyAnimation* newAnimation = StyleSheetPropertyAnimation::fromAnimationKeyframes( animation, keyframes, propDef, mWidget, 0 ); + newAnimation->setFlags( animation.getId() ); newAnimation->setTag( propDef->getId() ); mWidget->runAction( newAnimation ); } diff --git a/src/eepp/ui/uitabwidget.cpp b/src/eepp/ui/uitabwidget.cpp index fdb3a618e..3962639b5 100644 --- a/src/eepp/ui/uitabwidget.cpp +++ b/src/eepp/ui/uitabwidget.cpp @@ -160,6 +160,33 @@ std::string UITabWidget::getPropertyString( const PropertyDefinition* propertyDe } } +bool UITabWidget::isDrawInvalidator() const { + return true; +} + +void UITabWidget::invalidate( Node* invalidator ) { + // Only invalidate if the invalidator is actually visible in the current active tab. + if ( NULL != invalidator ) { + if ( invalidator == mCtrlContainer ) { + mSceneNode->invalidate( mCtrlContainer ); + } else if ( invalidator->getParent() == mCtrlContainer ) { + if ( invalidator->isVisible() ) { + mSceneNode->invalidate( mCtrlContainer ); + } + } else { + Node* container = invalidator->getParent(); + while ( container->getParent() != NULL && container->getParent() != mCtrlContainer ) { + container = container->getParent(); + } + if ( container->getParent() == mCtrlContainer && container->isVisible() ) { + mSceneNode->invalidate( mCtrlContainer ); + } + } + } else if ( NULL != mSceneNode ) { + mSceneNode->invalidate( this ); + } +} + bool UITabWidget::applyProperty( const StyleSheetProperty& attribute ) { if ( !checkPropertyDefinition( attribute ) ) return false; diff --git a/src/eepp/ui/uiwindow.cpp b/src/eepp/ui/uiwindow.cpp index 4ba464e86..a612684ef 100644 --- a/src/eepp/ui/uiwindow.cpp +++ b/src/eepp/ui/uiwindow.cpp @@ -1112,7 +1112,7 @@ void UIWindow::onPositionChange() { // Invalidate the buffer since a position change can get childs into a drawable position // (on screen), when the drawable could have been outside the viewport and not drawn in the // previous position. - invalidate(); + invalidate( this ); UIWidget::onPositionChange(); } @@ -1241,12 +1241,12 @@ void UIWindow::internalDraw() { } } -void UIWindow::invalidate() { +void UIWindow::invalidate( Node* invalidator ) { if ( mVisible && mAlpha != 0.f ) { writeNodeFlag( NODE_FLAG_VIEW_DIRTY, 1 ); if ( NULL != mSceneNode ) - mSceneNode->invalidate(); + mSceneNode->invalidate( invalidator ); } } diff --git a/src/thirdparty/SOIL2 b/src/thirdparty/SOIL2 index d6886e006..5adc6cd56 160000 --- a/src/thirdparty/SOIL2 +++ b/src/thirdparty/SOIL2 @@ -1 +1 @@ -Subproject commit d6886e006e9596de1340fb59a76c4579e228864e +Subproject commit 5adc6cd56b6b1deda7c7273e0c9ce232950893d1 diff --git a/src/thirdparty/efsw b/src/thirdparty/efsw index e9990449f..36f27d7cc 160000 --- a/src/thirdparty/efsw +++ b/src/thirdparty/efsw @@ -1 +1 @@ -Subproject commit e9990449f4073b37000a50a834ecf60243315e3c +Subproject commit 36f27d7ccf0cfc1b0aa5c43476affc432760b59c