From 50550b5dd051c5baebd26365991b50f155031dc8 Mon Sep 17 00:00:00 2001 From: David Steele Date: Wed, 8 Feb 2017 21:25:58 +0000 Subject: [PATCH] Updating Builder and Control to handle styled States A) Added state and subState properties to control B) Updated builder to parse for states/visuals/transitions C) Added new Dictionary and Style classes to store style info D) Stored off new style data into builder E) Added top level state change Change-Id: I75f2d9746bdfcdbd9fef96d845396ff8c5d51ca1 Signed-off-by: David Steele --- .../dali-toolkit-styling/utc-Dali-StyleManager.cpp | 305 ++++++++++++++++- .../dali-toolkit-test-utils/dummy-control.cpp | 26 ++ .../dali-toolkit-test-utils/dummy-control.h | 6 +- .../src/dali-toolkit/utc-Dali-Builder.cpp | 4 +- dali-toolkit/devel-api/builder/tree-node.cpp | 9 +- dali-toolkit/devel-api/controls/control-devel.h | 23 ++ dali-toolkit/internal/builder/builder-impl.cpp | 379 ++++++++++++++++----- dali-toolkit/internal/builder/builder-impl.h | 43 ++- dali-toolkit/internal/builder/dictionary.h | 235 +++++++++++++ dali-toolkit/internal/builder/style.cpp | 114 +++++++ dali-toolkit/internal/builder/style.h | 112 ++++++ dali-toolkit/internal/file.list | 1 + .../internal/styling/style-manager-impl.cpp | 70 +++- dali-toolkit/internal/styling/style-manager-impl.h | 28 +- dali-toolkit/public-api/controls/control-impl.cpp | 78 +++++ 15 files changed, 1315 insertions(+), 118 deletions(-) create mode 100644 dali-toolkit/internal/builder/dictionary.h create mode 100644 dali-toolkit/internal/builder/style.cpp create mode 100644 dali-toolkit/internal/builder/style.h diff --git a/automated-tests/src/dali-toolkit-styling/utc-Dali-StyleManager.cpp b/automated-tests/src/dali-toolkit-styling/utc-Dali-StyleManager.cpp index ff6e7df..6fe0fde 100644 --- a/automated-tests/src/dali-toolkit-styling/utc-Dali-StyleManager.cpp +++ b/automated-tests/src/dali-toolkit-styling/utc-Dali-StyleManager.cpp @@ -1,4 +1,4 @@ -/* + /* * Copyright (c) 2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include using namespace Dali; using namespace Dali::Toolkit; @@ -150,6 +153,223 @@ const char* defaultTheme = " \"grabHandleImage\" : \"{DALI_STYLE_IMAGE_DIR}cursor_handler_drop_center.png\",\n" " \"selectionHandleImageLeft\" : {\"filename\":\"{DALI_STYLE_IMAGE_DIR}selection_handle_drop_left.png\" },\n" " \"selectionHandleImageRight\": {\"filename\":\"{DALI_STYLE_IMAGE_DIR}selection_handle_drop_right.png\" }\n" +" },\n" +" \"ComplexControl\":\n" +" {\n" +" \"states\":\n" +" {\n" +" \"NORMAL\":\n" +" {\n" +" \"states\":\n" +" {\n" +" \"SELECTED\":\n" +" {\n" +" \"visuals\":\n" +" {\n" +" \"foregroundVisual\":\n" +" {\n" +" \"visualType\":\"GRADIENT\",\n" +" \"startPosition\": [-1, -1],\n" +" \"endPosition\": [1, 1],\n" +" \"spreadMethod\": \"REPEAT\",\n" +" \"stopOffset\": [0.2, 0.8],\n" +" \"stopColor\": [ [ 1,0,0,1], [0,1,0,1] ]\n" +" }\n" +" }\n" +" },\n" +" \"UNSELECTED\":\n" +" {\n" +" \"visuals\":\n" +" {\n" +" \"foregroundVisual\":\n" +" {\n" +" \"visualType\":\"COLOR\",\n" +" \"mixColor\": [ 1,0,0,1]\n" +" }\n" +" }\n" +" }\n" +" },\n" +" \"transitions\":\n" +" {\n" +" \"visualName\":\"*\",\n" +" \"effect\":\"CROSSFADE\",\n" +" \"animator\":\n" +" {\n" +" \"alphaFunction\":\"EASE_IN_OUT\",\n" +" \"duration\":0.3\n" +" }\n" +" }\n" +" },\n" +" \"FOCUSED\":\n" +" {\n" +" \"visuals\":\n" +" {\n" +" \"foregroundVisual\":\n" +" {\n" +" \"visualType\":\"GRADIENT\",\n" +" \"startPosition\": [-1, -1],\n" +" \"endPosition\": [1, 1],\n" +" \"spreadMethod\": \"REPEAT\",\n" +" \"stopOffset\": [0.3, 0.9],\n" +" \"stopColor\": [ [ 0,0,1,1], [0,1,1,1] ]\n" +" },\n" +" \"focusVisual\":\n" +" {\n" +" \"visualType\":\"IMAGE\",\n" +" \"url\": \"focus.png\"\n" +" }\n" +" },\n" +" \"entryTransition\":\n" +" {\n" +" \"target\":\"focusVisual\",\n" +" \"property\":\"mixColor\",\n" +" \"initialValue\":[0,0,0,0],\n" +" \"targetValue\":[1,1,1,1],\n" +" \"animator\":\n" +" {\n" +" \"alphaFunction\":\"EASE_IN_OUT_SINE\",\n" +" \"timePeriod\": \n" +" {\n" +" \"duration\":0.5,\n" +" \"delay\":0\n" +" }\n" +" }\n" +" },\n" +" \"exitTransition\":\n" +" {\n" +" \"target\":\"focusVisual\",\n" +" \"property\":\"mixColor\",\n" +" \"initialValue\":[1,1,1,1],\n" +" \"targetValue\":[0,0,0,0],\n" +" \"animator\":\n" +" {\n" +" \"alphaFunction\":\"EASE_IN_OUT_SINE\",\n" +" \"timePeriod\": \n" +" {\n" +" \"duration\":0.5,\n" +" \"delay\":0\n" +" }\n" +" }\n" +" }\n" +" },\n" +" \"DISABLED\":\n" +" {\n" +" \"visuals\":\n" +" {\n" +" \"foregroundVisual\":\n" +" {\n" +" \"visualType\":\"COLOR\",\n" +" \"mixColor\": [1,0,0,1]\n" +" }\n" +" }\n" +" }\n" +" },\n" +" \"transitions\":\n" +" [\n" +" {\n" +" \"effect\":\"CROSSFADE\",\n" +" \"animator\":\n" +" {\n" +" \"alphaFunction\":\"EASE_IN_OUT\",\n" +" \"duration\":0.3\n" +" }\n" +" }\n" +" ]\n" +" },\n" +" \"BasicControl\":\n" +" {\n" +" \"states\":\n" +" {\n" +" \"NORMAL\":\n" +" {\n" +" \"visuals\":\n" +" {\n" +" \"foregroundVisual\":\n" +" {\n" +" \"visualType\":\"GRADIENT\",\n" +" \"startPosition\": [-1, -1],\n" +" \"endPosition\": [1, 1],\n" +" \"spreadMethod\": \"REPEAT\",\n" +" \"stopOffset\": [0.2, 0.8],\n" +" \"stopColor\": [ [ 1,0,0,1], [0,1,0,1] ]\n" +" }\n" +" }\n" +" },\n" +" \"FOCUSED\":\n" +" {\n" +" \"visuals\":\n" +" {\n" +" \"foregroundVisual\":\n" +" {\n" +" \"visualType\":\"GRADIENT\",\n" +" \"startPosition\": [-1, -1],\n" +" \"endPosition\": [1, 1],\n" +" \"spreadMethod\": \"REPEAT\",\n" +" \"stopOffset\": [0.3, 0.9],\n" +" \"stopColor\": [ [ 0,0,1,1], [0,1,1,1] ]\n" +" },\n" +" \"focusVisual\":\n" +" {\n" +" \"visualType\":\"IMAGE\",\n" +" \"url\": \"focus.png\"\n" +" }\n" +" },\n" +" \"entryTransition\":\n" +" {\n" +" \"target\":\"focusVisual\",\n" +" \"property\":\"mixColor\",\n" +" \"initialValue\":[0,0,0,0],\n" +" \"targetValue\":[1,1,1,1],\n" +" \"animator\":\n" +" {\n" +" \"alphaFunction\":\"EASE_IN_OUT_SINE\",\n" +" \"timePeriod\": \n" +" {\n" +" \"duration\":0.5,\n" +" \"delay\":0\n" +" }\n" +" }\n" +" },\n" +" \"exitTransition\":\n" +" {\n" +" \"target\":\"focusVisual\",\n" +" \"property\":\"mixColor\",\n" +" \"initialValue\":[1,1,1,1],\n" +" \"targetValue\":[0,0,0,0],\n" +" \"animator\":\n" +" {\n" +" \"alphaFunction\":\"EASE_IN_OUT_SINE\",\n" +" \"timePeriod\": \n" +" {\n" +" \"duration\":0.5,\n" +" \"delay\":0\n" +" }\n" +" }\n" +" }\n" +" },\n" +" \"DISABLED\":\n" +" {\n" +" \"visuals\":\n" +" {\n" +" \"foregroundVisual\":\n" +" {\n" +" \"visualType\":\"COLOR\",\n" +" \"mixColor\": [1,0,0,1]\n" +" }\n" +" }\n" +" }\n" +" },\n" +" \"transitions\":\n" +" [\n" +" {\n" +" \"effect\":\"CROSSFADE\",\n" +" \"animator\":\n" +" {\n" +" \"alphaFunction\":\"EASE_IN_OUT\",\n" +" \"duration\":0.3\n" +" }\n" +" }\n" +" ]\n" " }\n" " }\n" "}\n"; @@ -158,6 +378,7 @@ const char* defaultTheme = + void dali_style_manager_startup(void) { test_return_value = TET_UNDEF; @@ -977,3 +1198,85 @@ int UtcDaliStyleManagerStyleChangedSignalFontSizeTextEditor(void) END_TEST; } + + +int UtcDaliStyleManagerVisualTransitionParsing(void) +{ + tet_infoline("Instantiate dummy control and test state/visual/transition capture" ); + Test::StyleMonitor::SetThemeFileOutput( DALI_STYLE_DIR "dali-toolkit-default-theme.json", + defaultTheme ); + + ToolkitTestApplication application; + + StyleChangedSignalChecker styleChangedSignalHandler; + Dali::StyleMonitor styleMonitor = Dali::StyleMonitor::Get(); + StyleManager styleManager = StyleManager::Get(); + + DummyControl actor = DummyControl::New(true); + actor.SetStyleName("BasicControl"); + Stage::GetCurrent().Add(actor); + + Impl::DummyControl& dummyImpl = static_cast(actor.GetImplementation()); + + DALI_TEST_EQUALS(dummyImpl.IsVisualEnabled(DummyControl::Property::FOREGROUND_VISUAL), true, TEST_LOCATION); + Visual::Base visual1 = dummyImpl.GetVisual(DummyControl::Property::FOREGROUND_VISUAL); + + actor.SetProperty( DevelControl::Property::STATE, DevelControl::FOCUSED ); + + DALI_TEST_EQUALS(dummyImpl.IsVisualEnabled(DummyControl::Property::FOREGROUND_VISUAL), true, TEST_LOCATION); + DALI_TEST_EQUALS(dummyImpl.IsVisualEnabled(DummyControl::Property::FOCUS_VISUAL), true, TEST_LOCATION); + + Visual::Base visual2 = dummyImpl.GetVisual(DummyControl::Property::FOREGROUND_VISUAL); + DALI_TEST_CHECK( visual1 != visual2 ); + + actor.SetProperty( DevelControl::Property::STATE, DevelControl::DISABLED ); + + DALI_TEST_EQUALS(dummyImpl.IsVisualEnabled(DummyControl::Property::FOREGROUND_VISUAL), true, TEST_LOCATION); + + Visual::Base visual3 = dummyImpl.GetVisual(DummyControl::Property::FOREGROUND_VISUAL); + Visual::Base focusVisual = dummyImpl.GetVisual(DummyControl::Property::FOCUS_VISUAL); + DALI_TEST_CHECK( !focusVisual ); + DALI_TEST_EQUALS(dummyImpl.IsVisualEnabled(DummyControl::Property::FOCUS_VISUAL), false, TEST_LOCATION); + + DALI_TEST_CHECK( visual1 != visual3 ); + DALI_TEST_CHECK( visual2 != visual3 ); + + END_TEST; +} + + +int UtcDaliStyleManagerVisualTransitionParsing02(void) +{ + tet_infoline("Instantiate dummy control and test state/visual/transition capture" ); + Test::StyleMonitor::SetThemeFileOutput( DALI_STYLE_DIR "dali-toolkit-default-theme.json", + defaultTheme ); + + ToolkitTestApplication application; + + StyleChangedSignalChecker styleChangedSignalHandler; + Dali::StyleMonitor styleMonitor = Dali::StyleMonitor::Get(); + StyleManager styleManager = StyleManager::Get(); + + DummyControl actor = DummyControl::New(true); + actor.SetProperty(DevelControl::Property::STATE, "FOCUSED"); + + actor.SetStyleName("ComplexControl"); + Stage::GetCurrent().Add(actor); + + Impl::DummyControl& dummyImpl = static_cast(actor.GetImplementation()); + + DALI_TEST_EQUALS(dummyImpl.IsVisualEnabled(DummyControl::Property::FOREGROUND_VISUAL), true, TEST_LOCATION); + Visual::Base visual1 = dummyImpl.GetVisual(DummyControl::Property::FOREGROUND_VISUAL); + DALI_TEST_CHECK( visual1 ); + Property::Map map; + visual1.CreatePropertyMap( map ); + Property::Value* value = map.Find( Visual::Property::TYPE ); + DALI_TEST_CHECK( value ); + + int visualType; + value->Get( visualType ); + + DALI_TEST_EQUALS( visualType, (int)Toolkit::Visual::GRADIENT, TEST_LOCATION ); + + END_TEST; +} diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/dummy-control.cpp b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/dummy-control.cpp index f6fb4bf..979e56a 100644 --- a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/dummy-control.cpp +++ b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/dummy-control.cpp @@ -74,6 +74,12 @@ Dali::PropertyRegistration dummyControlVisualProperty01( Dali::PropertyRegistration dummyControlVisualProperty02( typeRegistration, "testVisual", Dali::Toolkit::DummyControl::Property::TEST_VISUAL2, Dali::Property::MAP, &Dali::Toolkit::DummyControlImpl::SetProperty, &Dali::Toolkit::DummyControlImpl::GetProperty ); +Dali::PropertyRegistration dummyControlVisualProperty03( + typeRegistration, "foregroundVisual", Dali::Toolkit::DummyControl::Property::FOREGROUND_VISUAL, Dali::Property::MAP, &Dali::Toolkit::DummyControlImpl::SetProperty, &Dali::Toolkit::DummyControlImpl::GetProperty ); + +Dali::PropertyRegistration dummyControlVisualProperty04( + typeRegistration, "focusVisual", Dali::Toolkit::DummyControl::Property::FOCUS_VISUAL, Dali::Property::MAP, &Dali::Toolkit::DummyControlImpl::SetProperty, &Dali::Toolkit::DummyControlImpl::GetProperty ); + } DummyControl DummyControlImpl::New() @@ -144,6 +150,26 @@ Animation DummyControlImpl::CreateTransition( const Toolkit::TransitionData& tra void DummyControlImpl::SetProperty( BaseObject* object, Dali::Property::Index index, const Dali::Property::Value& value ) { + Toolkit::DummyControl control = Toolkit::DummyControl::DownCast( Dali::BaseHandle( object ) ); + DummyControlImpl& dummyImpl = static_cast(control.GetImplementation()); + + switch(index) + { + case Toolkit::DummyControl::Property::TEST_VISUAL: + case Toolkit::DummyControl::Property::TEST_VISUAL2: + case Toolkit::DummyControl::Property::FOREGROUND_VISUAL: + case Toolkit::DummyControl::Property::FOCUS_VISUAL: + { + Property::Map* map = value.GetMap(); + if( map != NULL ) + { + VisualFactory visualFactory = VisualFactory::Get(); + Visual::Base visual = visualFactory.CreateVisual(*map); + dummyImpl.RegisterVisual(index, visual); + } + break; + } + } } Property::Value DummyControlImpl::GetProperty( BaseObject* object, Dali::Property::Index propertyIndex ) diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/dummy-control.h b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/dummy-control.h index f27f296..75d3efe 100644 --- a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/dummy-control.h +++ b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/dummy-control.h @@ -46,7 +46,9 @@ public: enum Type { TEST_VISUAL = PROPERTY_START_INDEX, - TEST_VISUAL2 + TEST_VISUAL2, + FOREGROUND_VISUAL, + FOCUS_VISUAL }; }; @@ -89,7 +91,7 @@ public: void RegisterVisual( Property::Index index, Toolkit::Visual::Base visual, bool enabled ); void UnregisterVisual( Property::Index index ); void EnableVisual( Property::Index index, bool enabled ); - bool IsVisualEnabled( Property::Index indepx ); + bool IsVisualEnabled( Property::Index index ); Toolkit::Visual::Base GetVisual( Property::Index index ); Animation CreateTransition( const Toolkit::TransitionData& transition ); diff --git a/automated-tests/src/dali-toolkit/utc-Dali-Builder.cpp b/automated-tests/src/dali-toolkit/utc-Dali-Builder.cpp index cb84952..60f4a1b 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-Builder.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-Builder.cpp @@ -276,7 +276,7 @@ int UtcDaliBuilderAnimationP(void) " \"name\": \"greeting\"," " \"type\": \"TextLabel\"," " \"text\": \"Touch me\"," - " \"styles\": [\"basicText\"]," + " \"inherit\": [\"basicText\"]," " \"position\": [0, -120, 0]," " \"size\": [200, 200, 1]," " \"orientation\": [0, 0, 30]," @@ -400,7 +400,7 @@ int UtcDaliBuilderAnimationN(void) " \"name\": \"greeting\"," " \"type\": \"TextLabel\"," " \"text\": \"Touch me\"," - " \"styles\": [\"basicText\"]," + " \"inherit\": [\"basicText\"]," " \"position\": [0, -120, 0]," " \"size\": [200, 200, 1]," " \"orientation\": [0, 0, 30]," diff --git a/dali-toolkit/devel-api/builder/tree-node.cpp b/dali-toolkit/devel-api/builder/tree-node.cpp index 6b0f6b3..3d9eed7 100644 --- a/dali-toolkit/devel-api/builder/tree-node.cpp +++ b/dali-toolkit/devel-api/builder/tree-node.cpp @@ -23,7 +23,8 @@ #include "dali-toolkit/devel-api/builder/tree-node.h" #include "dali-toolkit/internal/builder/tree-node-manipulator.h" -namespace + +namespace Dali { bool CaseInsensitiveCharacterCompare( unsigned char a, unsigned char b ) @@ -45,12 +46,6 @@ bool CaseInsensitiveStringCompare( const std::string& a, const std::string& b ) return result; } -} // anonymous namespace - - -namespace Dali -{ - namespace Toolkit { diff --git a/dali-toolkit/devel-api/controls/control-devel.h b/dali-toolkit/devel-api/controls/control-devel.h index 71071db..306ce2d 100644 --- a/dali-toolkit/devel-api/controls/control-devel.h +++ b/dali-toolkit/devel-api/controls/control-devel.h @@ -30,6 +30,13 @@ namespace Toolkit namespace DevelControl { +enum State +{ + NORMAL, + FOCUSED, + DISABLED +}; + namespace Property { @@ -53,6 +60,22 @@ enum * @see Toolkit::Tooltip */ TOOLTIP = BACKGROUND + 1, + + /** + * @brief The current state of the control. + * @details Name "state", type DevelControl::State ( Property::INTEGER ) or Property::STRING + * + * @see DevelControl::State + */ + STATE = BACKGROUND + 2, + + /** + * @brief The current sub state of the control. + * @details Name "subState", type Property::INTEGER or Property::STRING. The enumeration used is dependent on the derived control. + * + * @see DevelControl::State + */ + SUB_STATE = BACKGROUND + 3 }; } // namespace Property diff --git a/dali-toolkit/internal/builder/builder-impl.cpp b/dali-toolkit/internal/builder/builder-impl.cpp index fdf8d21..9b6daa0 100644 --- a/dali-toolkit/internal/builder/builder-impl.cpp +++ b/dali-toolkit/internal/builder/builder-impl.cpp @@ -72,13 +72,19 @@ namespace #define TOKEN_STRING(x) #x const std::string KEYNAME_ACTORS = "actors"; +const std::string KEYNAME_ENTRY_TRANSITION = "entryTransition"; +const std::string KEYNAME_EXIT_TRANSITION = "exitTransition"; const std::string KEYNAME_INCLUDES = "includes"; +const std::string KEYNAME_INHERIT = "inherit"; const std::string KEYNAME_MAPPINGS = "mappings"; const std::string KEYNAME_NAME = "name"; const std::string KEYNAME_SIGNALS = "signals"; +const std::string KEYNAME_STATES = "states"; const std::string KEYNAME_STYLES = "styles"; const std::string KEYNAME_TEMPLATES = "templates"; +const std::string KEYNAME_TRANSITIONS = "transitions"; const std::string KEYNAME_TYPE = "type"; +const std::string KEYNAME_VISUALS = "visuals"; const std::string PROPERTIES = "properties"; const std::string ANIMATABLE_PROPERTIES = "animatableProperties"; @@ -124,7 +130,12 @@ void CollectAllStyles( const TreeNode& stylesCollection, const TreeNode& style, { styleList.push_back( &(*node) ); - if( OptionalChild subStyle = IsChild( *node, KEYNAME_STYLES ) ) + OptionalChild subStyle = IsChild( *node, KEYNAME_INHERIT ); + if( ! subStyle ) + { + subStyle = IsChild( *node, KEYNAME_STYLES ); + } + if( subStyle ) { CollectAllStyles( stylesCollection, *subStyle, styleList ); } @@ -318,6 +329,34 @@ bool Builder::ApplyStyle( const std::string& styleName, Handle& handle ) return ApplyStyle( styleName, handle, replacer ); } +bool Builder::LookupStyleName( const std::string& styleName ) +{ + DALI_ASSERT_ALWAYS(mParser.GetRoot() && "Builder script not loaded"); + + OptionalChild styles = IsChild( *mParser.GetRoot(), KEYNAME_STYLES ); + OptionalChild style = IsChildIgnoreCase( *styles, styleName ); + + if( styles && style ) + { + return true; + } + return false; +} + +const StylePtr Builder::GetStyle( const std::string& styleName ) +{ + const StylePtr* style = mStyles.FindCaseInsensitiveC( styleName ); + + if( style==NULL ) + { + return StylePtr(NULL); + } + else + { + return *style; + } +} + void Builder::AddActors( Actor toActor ) { // 'stage' is the default/by convention section to add from @@ -1023,7 +1062,6 @@ void Builder::SetupTask( RenderTask& task, const TreeNode& node, const Replaceme SetProperties( node, task, constant ); } - bool Builder::ApplyStyle( const std::string& styleName, Handle& handle, const Replacement& replacement ) { DALI_ASSERT_ALWAYS(mParser.GetRoot() && "Builder script not loaded"); @@ -1047,59 +1085,231 @@ bool Builder::ApplyStyle( const std::string& styleName, Handle& handle, const Re void Builder::ApplyAllStyleProperties( const TreeNode& root, const TreeNode& node, Dali::Handle& handle, const Replacement& constant ) { - OptionalChild styles = IsChild(root, KEYNAME_STYLES); - OptionalChild style = IsChild(node, KEYNAME_STYLES); + const char* styleName = node.GetName(); - if( styles && style ) + StylePtr style = Style::New(); + + StylePtr* matchedStyle = NULL; + if( styleName ) { - TreeNodeList additionalStyles; + matchedStyle = mStyles.FindCaseInsensitive( styleName ); + if( ! matchedStyle ) + { + OptionalChild styleNodes = IsChild(root, KEYNAME_STYLES); + OptionalChild inheritFromNode = IsChild(node, KEYNAME_INHERIT); + if( !inheritFromNode ) + { + inheritFromNode = IsChild( node, KEYNAME_STYLES ); + } + + if( styleNodes ) + { + if( inheritFromNode ) + { + TreeNodeList additionalStyleNodes; - CollectAllStyles( *styles, *style, additionalStyles ); + CollectAllStyles( *styleNodes, *inheritFromNode, additionalStyleNodes ); #if defined(DEBUG_ENABLED) - for(TreeNode::ConstIterator iter = (*style).CBegin(); iter != (*style).CEnd(); ++iter) + for(TreeNode::ConstIterator iter = (*inheritFromNode).CBegin(); iter != (*inheritFromNode).CEnd(); ++iter) + { + if( OptionalString styleName = IsString( (*iter).second ) ) + { + DALI_SCRIPT_VERBOSE("Style Applied '%s'\n", (*styleName).c_str()); + } + } +#endif + + // a style may have other styles, which has other styles etc so we apply in reverse by convention. + for(TreeNodeList::reverse_iterator iter = additionalStyleNodes.rbegin(); iter != additionalStyleNodes.rend(); ++iter) + { + RecordStyle( style, *(*iter), handle, constant ); + ApplySignals( root, *(*iter), handle ); + ApplyStylesByActor( root, *(*iter), handle, constant ); + } + } + + RecordStyle( style, node, handle, constant ); + mStyles.Add( styleName, style ); // shallow copy + matchedStyle = &style; + } + } + } + + if( matchedStyle ) + { + StylePtr style( *matchedStyle ); + style->ApplyVisualsAndPropertiesRecursively( handle ); // (recurses through states) + } + else // If there were no styles, instead set properties + { + SetProperties( node, handle, constant ); + } + ApplySignals( root, node, handle ); + ApplyStylesByActor( root, node, handle, constant ); +} + +void Builder::RecordStyle( StylePtr style, + const TreeNode& node, + Dali::Handle& handle, + const Replacement& replacements ) +{ + // With repeated calls, accumulate inherited states, visuals and properties + // but override any with same name + + for( TreeNode::ConstIterator iter = node.CBegin(); iter != node.CEnd(); ++iter ) + { + const TreeNode::KeyNodePair& keyValue = *iter; + std::string key( keyValue.first ); + if( key == KEYNAME_STATES ) { - if( OptionalString styleName = IsString( (*iter).second ) ) + const TreeNode& states = keyValue.second; + if( states.GetType() != TreeNode::OBJECT ) { - DALI_SCRIPT_VERBOSE("Style Applied '%s'\n", (*styleName).c_str()); + DALI_LOG_WARNING( "RecordStyle() Node \"%s\" is not a JSON object\n", key.c_str() ); + continue; } - } -#endif - // a style may have other styles, which has other styles etc so we apply in reverse by convention. - for(TreeNodeList::reverse_iterator iter = additionalStyles.rbegin(); iter != additionalStyles.rend(); ++iter) + for( TreeNode::ConstIterator iter = states.CBegin(); iter != states.CEnd(); ++iter ) + { + const TreeNode& stateNode = (*iter).second; + const char* stateName = stateNode.GetName(); + if( stateNode.GetType() != TreeNode::OBJECT ) + { + DALI_LOG_WARNING( "RecordStyle() Node \"%s\" is not a JSON object\n", stateName ); + continue; + } + + StylePtr* stylePtr = style->subStates.FindCaseInsensitive( stateName ); + if( stylePtr ) + { + StylePtr style(*stylePtr); + RecordStyle( style, stateNode, handle, replacements ); + } + else + { + StylePtr subState = Style::New(); + RecordStyle( subState, stateNode, handle, replacements ); + style->subStates.Add( stateName, subState ); + } + } + } + else if( key == KEYNAME_VISUALS ) { - ApplyProperties( root, *(*iter), handle, constant ); - ApplyStylesByActor( root, *(*iter), handle, constant ); + for( TreeNode::ConstIterator iter = keyValue.second.CBegin(); iter != keyValue.second.CEnd(); ++iter ) + { + // Each key in this table should be a property name matching a visual. + const TreeNode::KeyNodePair& visual = *iter; + Dali::Property::Value property(Property::MAP); + if( DeterminePropertyFromNode( visual.second, Property::MAP, property, replacements ) ) + { + Property::Map* mapPtr = style->visuals.FindCaseInsensitive( visual.first ); + if( mapPtr ) + { + // Override existing visuals + mapPtr->Clear(); + mapPtr->Merge(*property.GetMap()); + } + else + { + style->visuals.Add(visual.first, *property.GetMap()); + } + } + } + } + else if( key == KEYNAME_ENTRY_TRANSITION ) + { + Dali::Property::Value property(Property::MAP); + if( DeterminePropertyFromNode( keyValue.second, Property::MAP, property, replacements ) ) + { + style->entryTransition = Toolkit::TransitionData::New( *property.GetMap() ); + } + } + else if( key == KEYNAME_EXIT_TRANSITION ) + { + Dali::Property::Value property(Property::MAP); + if( DeterminePropertyFromNode( keyValue.second, Property::MAP, property, replacements ) ) + { + style->exitTransition = Toolkit::TransitionData::New( *property.GetMap() ); + } + } + else if( key == KEYNAME_TRANSITIONS ) + { + //@todo add new transitions to style.transitions + // override existing transitions. A transition matches on target & property name + const TreeNode& node = keyValue.second; + if( node.GetType() == TreeNode::ARRAY ) + { + Dali::Property::Value property(Property::ARRAY); + if( DeterminePropertyFromNode( node, Property::ARRAY, property, replacements ) ) + { + style->transitions = *property.GetArray(); + } + } + else if( node.GetType() == TreeNode::OBJECT ) + { + Dali::Property::Value property(Property::MAP); + if( DeterminePropertyFromNode( node, Property::MAP, property, replacements ) ) + { + Property::Array propertyArray; + propertyArray.Add( property ); + style->transitions = propertyArray; + } + } + else + { + DALI_LOG_WARNING( "RecordState() Node \"%s\" is not a JSON array or object\n", key.c_str() ); + } + } + else if( key == KEYNAME_TYPE || + key == KEYNAME_ACTORS || + key == KEYNAME_SIGNALS || + key == KEYNAME_STYLES || + key == KEYNAME_MAPPINGS || + key == KEYNAME_INHERIT ) + { + continue; + } + else // It's a property + { + Property::Index index; + Property::Value value; + if( MapToTargetProperty( handle, key, keyValue.second, replacements, index, value ) ) + { + Property::Value* existingValuePtr = style->properties.Find( index ); + if( existingValuePtr != NULL ) + { + *existingValuePtr = value; // Overwrite existing property. + } + else + { + style->properties.Add( index, value ); + } + } } } - - // applying given node last - ApplyProperties( root, node, handle, constant ); - ApplyStylesByActor( root, node, handle, constant ); } // Set properties from node on handle. void Builder::ApplyProperties( const TreeNode& root, const TreeNode& node, Dali::Handle& handle, const Replacement& constant ) { - if( Actor actor = Actor::DownCast(handle) ) - { - SetProperties( node, actor, constant ); + SetProperties( node, handle, constant ); + ApplySignals( root, node, handle ); +} - if( actor ) - { - // add signals - SetupSignalAction( mSlotDelegate.GetConnectionTracker(), root, node, actor, this ); - SetupPropertyNotification( mSlotDelegate.GetConnectionTracker(), root, node, actor, this ); - } - } - else +void Builder::ApplySignals(const TreeNode& root, const TreeNode& node, Dali::Handle& handle ) +{ + Actor actor = Actor::DownCast(handle); + if( actor ) { - SetProperties( node, handle, constant ); + // add signals + SetupSignalAction( mSlotDelegate.GetConnectionTracker(), root, node, actor, this ); + SetupPropertyNotification( mSlotDelegate.GetConnectionTracker(), root, node, actor, this ); } } + // Appling by style helper // use FindChildByName() to apply properties referenced in KEYNAME_ACTORS in the node void Builder::ApplyStylesByActor( const TreeNode& root, const TreeNode& node, @@ -1121,16 +1331,11 @@ void Builder::ApplyStylesByActor( const TreeNode& root, const TreeNode& node, if( !foundActor ) { - // debug log cannot find searched for actor -#if defined(DEBUG_ENABLED) DALI_SCRIPT_VERBOSE("Cannot find actor in style application '%s'\n", (*iter).first); -#endif } else { -#if defined(DEBUG_ENABLED) DALI_SCRIPT_VERBOSE("Styles applied to actor '%s'\n", (*iter).first); -#endif ApplyProperties( root, (*iter).second, foundActor, constant ); } } @@ -1152,51 +1357,30 @@ void Builder::SetProperties( const TreeNode& node, Handle& handle, const Replace std::string key( keyChild.first ); // ignore special fields; - if( key == KEYNAME_TYPE || key == KEYNAME_ACTORS || key == KEYNAME_SIGNALS || key == KEYNAME_STYLES || key == KEYNAME_MAPPINGS ) + if( key == KEYNAME_TYPE || + key == KEYNAME_ACTORS || + key == KEYNAME_SIGNALS || + key == KEYNAME_STYLES || + key == KEYNAME_MAPPINGS || + key == KEYNAME_INHERIT || + key == KEYNAME_STATES || + key == KEYNAME_VISUALS || + key == KEYNAME_ENTRY_TRANSITION || + key == KEYNAME_EXIT_TRANSITION || + key == KEYNAME_TRANSITIONS ) { continue; } - Handle propertyObject( handle ); - - Dali::Property::Index index = propertyObject.GetPropertyIndex( key ); + Property::Index index; + Property::Value value; - if( Property::INVALID_INDEX != index ) + bool mapped = MapToTargetProperty( handle, key, keyChild.second, constant, index, value ); + if( mapped ) { - Property::Type type = propertyObject.GetPropertyType(index); - Property::Value value; - bool mapped = false; + DALI_SCRIPT_VERBOSE("SetProperty '%s' Index=:%d Value Type=%d Value '%s'\n", key.c_str(), index, value.GetType(), PropertyValueToString(value).c_str() ); - // if node.value is a mapping, get the property value from the "mappings" table - if( keyChild.second.GetType() == TreeNode::STRING ) - { - std::string mappingKey; - if( GetMappingKey(keyChild.second.GetString(), mappingKey) ) - { - OptionalChild mappingRoot = IsChild( mParser.GetRoot(), KEYNAME_MAPPINGS ); - mapped = GetPropertyMap( *mappingRoot, mappingKey.c_str(), type, value ); - } - } - if( ! mapped ) - { - mapped = DeterminePropertyFromNode( keyChild.second, type, value, constant ); - if( ! mapped ) - { - // Just determine the property from the node and if it's valid, let the property object handle it - DeterminePropertyFromNode( keyChild.second, value, constant ); - mapped = ( value.GetType() != Property::NONE ); - } - } - if( mapped ) - { - DALI_SCRIPT_VERBOSE("SetProperty '%s' Index=:%d Value Type=%d Value '%s'\n", key.c_str(), index, value.GetType(), PropertyValueToString(value).c_str() ); - - propertyObject.SetProperty( index, value ); - } - } - else - { - DALI_LOG_ERROR("Key '%s' not found.\n", key.c_str()); + handle.SetProperty( index, value ); } // Add custom properties @@ -1211,6 +1395,49 @@ void Builder::SetProperties( const TreeNode& node, Handle& handle, const Replace } } +bool Builder::MapToTargetProperty( + Handle& propertyObject, + const std::string& key, + const TreeNode& node, + const Replacement& constant, + Property::Index& index, + Property::Value& value ) +{ + bool mapped = false; + + index = propertyObject.GetPropertyIndex( key ); + if( Property::INVALID_INDEX != index ) + { + Property::Type type = propertyObject.GetPropertyType(index); + + // if node.value is a mapping, get the property value from the "mappings" table + if( node.GetType() == TreeNode::STRING ) + { + std::string mappingKey; + if( GetMappingKey( node.GetString(), mappingKey) ) + { + OptionalChild mappingRoot = IsChild( mParser.GetRoot(), KEYNAME_MAPPINGS ); + mapped = GetPropertyMap( *mappingRoot, mappingKey.c_str(), type, value ); + } + } + if( ! mapped ) + { + mapped = DeterminePropertyFromNode( node, type, value, constant ); + if( ! mapped ) + { + // Just determine the property from the node and if it's valid, let the property object handle it + DeterminePropertyFromNode( node, value, constant ); + mapped = ( value.GetType() != Property::NONE ); + } + } + } + else + { + DALI_LOG_ERROR("Key '%s' not found.\n", key.c_str()); + } + return mapped; +} + bool Builder::GetPropertyMap( const TreeNode& mappingRoot, const char* theKey, Property::Type propertyType, Property::Value& value ) { KeyStack keyStack; @@ -1339,7 +1566,6 @@ void Builder::SetCustomProperties( const TreeNode& node, Handle& handle, const R { const TreeNode::KeyNodePair& keyChild = *iter; std::string key( keyChild.first ); - Property::Value value; DeterminePropertyFromNode( keyChild.second, value, constant ); @@ -1349,6 +1575,7 @@ void Builder::SetCustomProperties( const TreeNode& node, Handle& handle, const R } } + } // namespace Internal } // namespace Toolkit diff --git a/dali-toolkit/internal/builder/builder-impl.h b/dali-toolkit/internal/builder/builder-impl.h index 3051917..b6db52d 100644 --- a/dali-toolkit/internal/builder/builder-impl.h +++ b/dali-toolkit/internal/builder/builder-impl.h @@ -28,13 +28,13 @@ #include #include #include -#include #include // INTERNAL INCLUDES #include #include #include +#include // Warning messages usually displayed #define DALI_SCRIPT_WARNING(format, args...) \ @@ -150,6 +150,24 @@ public: bool ApplyStyle( const std::string& styleName, Handle& handle ); /** + * Lookup the stylename in builder. If it's found in the parse tree, + * then return true. + * @param[in] styleName The style name to search for + * @return true if the stylename exists + */ + bool LookupStyleName( const std::string& styleName ); + + /** + * Lookup the stylename in the recorded Styles - if it exists, + * performs a shallow copy to the passed in style and returns true. + * Otherwise it returns false. + + * @param[in] styleName The stylename to search for + * @return A const pointer to the style object + */ + const StylePtr GetStyle( const std::string& styleName ); + + /** * @copydoc Toolkit::Builder::AddActors */ void AddActors( Actor toActor ); @@ -215,6 +233,7 @@ public: */ void EmitQuitSignal(); + protected: virtual ~Builder(); @@ -263,11 +282,25 @@ private: Dali::Handle& handle, const Replacement& constant ); + void RecordStyles( const char* styleName, + const TreeNode& node, + Dali::Handle& handle, + const Replacement& replacements ); + + void RecordStyle( StylePtr style, + const TreeNode& node, + Dali::Handle& handle, + const Replacement& replacements ); + void ApplyProperties( const TreeNode& root, const TreeNode& node, Dali::Handle& handle, const Replacement& constant ); + void ApplySignals( const TreeNode& root, + const TreeNode& node, + Dali::Handle& handle ); + void ApplyStylesByActor( const TreeNode& root, const TreeNode& node, Dali::Handle& handle, @@ -277,6 +310,13 @@ private: Handle& handle, const Replacement& constant ); + bool MapToTargetProperty( Handle& propertyObject, + const std::string& key, + const TreeNode& node, + const Replacement& constant, + Property::Index& index, + Property::Value& value ); + /** * Find the key in the mapping table, if it's present, then generate * a property value for it (of the given type if available), @@ -337,6 +377,7 @@ private: SlotDelegate mSlotDelegate; Property::Map mReplacementMap; MappingsLut mCompleteMappings; + Dictionary mStyles; // State based styles Toolkit::Builder::BuilderSignalType mQuitSignal; }; diff --git a/dali-toolkit/internal/builder/dictionary.h b/dali-toolkit/internal/builder/dictionary.h new file mode 100644 index 0000000..22d2afe --- /dev/null +++ b/dali-toolkit/internal/builder/dictionary.h @@ -0,0 +1,235 @@ +#ifndef DALI_TOOLKIT_INTERNAL_BUILDER_DICTIONARY_H +#define DALI_TOOLKIT_INTERNAL_BUILDER_DICTIONARY_H + +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace Dali +{ +extern bool CaseInsensitiveStringCompare( const std::string& a, const std::string& b ); + +namespace Toolkit +{ +namespace Internal +{ + +/** + * The Dictionary template class enables a means of storing key-value + * pairs where the keys are strings and the value can be a complex + * type. + * + * It enables lookup of keys via case-insensitive match. + */ +template +class Dictionary +{ +private: + /** + * Element is a key-value pair + */ + struct Element + { + std::string key; + EntryType entry; + Element( const std::string&name, EntryType entry ) + : key( name ), + entry( entry ) + { + } + }; + typedef std::vector Elements; + Elements container; + +public: + /** + * Only allow const iteration over the dictionary + */ + typedef typename Elements::const_iterator iterator; + + + /** + * Constructor + */ + Dictionary() + { + } + + /** + * Add a key value pair to the dictionary. + * If the entry does not already exist, add it to the dictionary + * using a shallow copy + */ + bool Add( const std::string& name, const EntryType& entry ) + { + for( typename Elements::iterator iter = container.begin(); iter != container.end(); ++iter ) + { + if( iter->key == name ) + { + return false; + } + } + container.push_back( Element(name, entry) ); + return true; + } + + /** + * Add a key-value pair to the dictionary + * If the entry does not already exist, add it to the dictionary + * (shallow copy) + */ + bool Add( const char* name, const EntryType& entry ) + { + bool result=false; + if( name != NULL ) + { + std::string theName(name); + result=Add(theName, entry); + } + return result; + } + + /** + * Find the element in the dictionary pointed at by key, and + * return a pointer to it, or NULL. + */ + EntryType* Find( const std::string& key ) const + { + EntryType* result=NULL; + + if( ! key.empty() ) + { + for( typename Elements::iterator iter = container.begin(); iter != container.end(); ++iter ) + { + if( iter->key == key ) + { + result = &(iter->entry); + break; + } + } + } + return result; + } + + /** + * Find the element in the dictionary pointed at by key, and + * return a pointer to it, or NULL + */ + EntryType* Find( const char* key ) const + { + if( key != NULL ) + { + std::string theKey(key); + return Find(theKey); + } + return NULL; + } + + /** + * Find the element in the dictionary pointed at by key using a case + * insensitive search, and return a const pointer to it, or NULL + */ + const EntryType* FindCaseInsensitiveC( const std::string& key ) const + { + if( ! key.empty() ) + { + for( typename Elements::const_iterator iter = container.begin(); iter != container.end(); ++iter ) + { + if( Dali::CaseInsensitiveStringCompare(iter->key, key )) + { + const EntryType* result = &(iter->entry); + return result; + } + } + } + return NULL; + } + + /** + * Find the element in the dictionary pointed at by key using a case + * insensitive search, and return a non-const pointer to it, or NULL + */ + EntryType* FindCaseInsensitive( const std::string& key ) const + { + EntryType* result = NULL; + if( ! key.empty() ) + { + for( typename Elements::const_iterator iter = container.begin(); iter != container.end(); ++iter ) + { + if( Dali::CaseInsensitiveStringCompare(iter->key, key )) + { + // Const cast because of const_iterator. const_iterator because, STL. + result = const_cast(&(iter->entry)); + } + } + } + return result; + } + + /** + * Find the element in the dictionary pointed at by key using a case + * insensitive search, and return a const pointer to it, or NULL + */ + const EntryType* FindCaseInsensitiveC( const char* key ) const + { + if( key != NULL ) + { + std::string theKey(key); + return FindCaseInsensitiveC( theKey ); + } + return NULL; + } + + /** + * Find the element in the dictionary pointed at by key using a case + * insensitive search, and return a non-const pointer to it, or NULL + */ + EntryType* FindCaseInsensitive( const char* key ) const + { + if( key != NULL ) + { + std::string theKey(key); + return FindCaseInsensitive( theKey ); + } + return NULL; + } + + /** + * Return an iterator pointing at the first entry in the dictionary + */ + typename Elements::const_iterator Begin() const + { + return container.begin(); + } + + /** + * Return an iterator pointing past the last entry in the dictionary + */ + typename Elements::const_iterator End() const + { + return container.end(); + } +}; + + + +}//Internal +}//Toolkit +}//Dali + +#endif // DALI_TOOLKIT_INTERNAL_BUILDER_DICTIONARY_H diff --git a/dali-toolkit/internal/builder/style.cpp b/dali-toolkit/internal/builder/style.cpp new file mode 100644 index 0000000..eaf59dc --- /dev/null +++ b/dali-toolkit/internal/builder/style.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ + +StylePtr Style::New() +{ + StylePtr stylePtr( new Style() ); + return stylePtr; +} + +void Style::ApplyVisualsAndPropertiesRecursively( Handle handle ) const +{ + ApplyVisuals( handle ); + ApplyProperties( handle ); + + Toolkit::Control control = Toolkit::Control::DownCast(handle); + if( control ) + { + Property::Value value = control.GetProperty(DevelControl::Property::STATE); + std::string stateName; + if( value.Get( stateName ) ) + { + // Look up state in states table: + const StylePtr* stylePtr = subStates.FindCaseInsensitiveC( stateName ); + if( stylePtr ) + { + const StylePtr statePtr(*stylePtr); + + // We have a state match. + statePtr->ApplyVisuals( handle ); + statePtr->ApplyProperties( handle ); + + // Apply substate visuals + Property::Value value = control.GetProperty(DevelControl::Property::SUB_STATE); + std::string subStateName; + if( value.Get( subStateName ) && ! subStateName.empty() ) + { + const StylePtr* stylePtr = statePtr->subStates.FindCaseInsensitiveC( subStateName ); + if( stylePtr ) + { + const StylePtr subStatePtr(*stylePtr); + // We have a sub-state match. + subStatePtr->ApplyVisuals( handle ); + subStatePtr->ApplyProperties( handle ); + } + } + } + } + } +} + +void Style::ApplyVisuals( Handle handle ) const +{ + for( Dictionary::iterator iter = visuals.Begin(); iter != visuals.End() ; ++iter ) + { + const std::string& visualName = (*iter).key; + const Property::Map& map = (*iter).entry; + Dali::Property::Index index = handle.GetPropertyIndex( visualName ); + if( index != Property::INVALID_INDEX ) + { + const Property::Value value(const_cast(map)); + handle.SetProperty( index, value ); + } + } +} + +void Style::ApplyProperties( Handle handle ) const +{ + for( Property::Map::SizeType i=0; i +#include +#include + +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ + +class Style; +typedef IntrusivePtr