Merge changes I2df640e0,Ia1188305,I7fae506e,I7967a7cc,Ib0fdcdf4, ... into devel/master
authorDavid Steele <david.steele@samsung.com>
Fri, 30 Oct 2020 13:19:09 +0000 (13:19 +0000)
committerGerrit Code Review <gerrit@review>
Fri, 30 Oct 2020 13:19:09 +0000 (13:19 +0000)
* changes:
  [AT-SPI] MERGING MARKER
  [AT-SPI] Correct VISIBLE and SHOWING states
  [AT-SPI] Windows fixes
  [AT-SPI] Fixed uint underflow risk
  [AT-SPI] Guard added for potential null pointer dereference
  [AT-SPI] Implemented reading popup description
  [AT-SPI] Improving utc coverage ToggleButton tests
  [AT-SPI] More coverage
  [AT-SPI] Improving utc coverage
  [AT-SPI] Set both ScreenReaderEnabled and IsEnabled in TestEnableSC
  [AT-SPI] Rewritten UtcDaliControlAccessibilityModal
  [AT-SPI] Pass correct object to dynamic_cast
  [AT-SPI] UTC correction
  [AT-SPI] More UTC fixes
  [AT-SPI] Execute tct under dbus-launch
  [AT-SPI] UTC fixes
  [AT-SPI] Add TestDBusWrapper and bypass compilation problems
  [AT-SPI] Implement accessibility for Popup
  [AT-SPI] Fix cmake cmp0004 error
  [AT-SPI] Catch exception by reference
  [AT-SPI] Migrate Accessibility tests to dali-toolkit-internal
  [AT-SPI] Add notification for a11y name/description change
  [AT-SPI] TextController: emit characters before delete
  [AT-SPI] EmitStateChanged for togglable PushButton
  [AT-SPI] Add FOCUSABLE state to TextField and TextEditor
  [AT-SPI] Add Pause and Resume signals
  [AT-SPI] Let MarkFilter find the closest mark
  [AT-SPI] Emit ObjectPropertyChangeEvent::VALUE
  [AT-SPI] Implement proper accessibility for Slider
  [AT-SPI] Try auto-scrolling in GrabHighlight
  [AT-SPI] Override IsScrollable for Scrollable
  [AT-SPI] Support reading states and tooltips of ToggleButton
  [AT-SPI] Support gaining keyboard focus in TextField, TextEditor
  [AT-SPI] Allow two finger pan gesture
  [AT-SPI] Move setting highlightability property for controls to inheriting classes
  [AT-SPI] Make Accessible::GetName() fall back to actor name
  [AT-SPI] Fix role setting
  [AT-SPI] Squashed implementation

14 files changed:
automated-tests/README.md
automated-tests/patch-coverage.pl
automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp
dali-toolkit/devel-api/controls/text-controls/text-editor-devel.cpp
dali-toolkit/devel-api/controls/text-controls/text-editor-devel.h
dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp
dali-toolkit/internal/controls/text-controls/text-editor-impl.h
dali-toolkit/internal/text/text-controller-event-handler.cpp
dali-toolkit/internal/text/text-controller.cpp
dali-toolkit/internal/text/text-controller.h
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.cpp
dali-toolkit/internal/visuals/animated-vector-image/animated-vector-image-visual.h
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.cpp
dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.h

index 590571d..f256435 100644 (file)
@@ -150,7 +150,7 @@ a refspec to the latest commit, or uses the index/working tree.
 Testing on target
 =================
 
-To build for target, first build and install dali-core, dali-adaptor and dali-toolkit, then build dali-capi without --keep-packs option.
+To build for target, first build and install dali-core, dali-adaptor and dali-toolkit.
 
 You will need to install libconfig-tiny-perl:
 
index 702dc0b..3a97dac 100755 (executable)
@@ -54,7 +54,7 @@ sub add_counts($$);
 sub info(@);
 
 our $repo = Git->repository();
-our $debug=1;
+our $debug=0;
 our $pd_debug=0;
 our $root;
 our %info_data; # Hash containing all data from .info files
@@ -1316,7 +1316,7 @@ my $cwd = getcwd(); # expect this to be automated-tests folder
 
 # execute coverage.sh, generating build/tizen/dali.info from lib, and
 # *.dir/dali.info. Don't generate html
-`coverage.sh -n`;
+print `./coverage.sh -n`;
 chdir "..";
 $root = getcwd();
 
@@ -1338,12 +1338,7 @@ foreach (@info_files)
 my @cmd=('--no-pager','diff','--no-ext-diff','-U0','--no-color');
 my $status = $repo->command("status", "-s");
 
-if(scalar(@ARGV)) # REMOVE ME
-{
-    # REMOVE ME - temp to get past modifying this script in place.
-    push @cmd, @ARGV;
-}
-elsif( $status eq "" && !scalar(@ARGV))
+if( $status eq "" && !scalar(@ARGV))
 {
     # There are no changes in the index or working tree, and
     # no diff arguments to append. Use the last patch instead.
index 7a3ce4c..49e630d 100644 (file)
@@ -2172,6 +2172,54 @@ int utcDaliTextEditorEvent07(void)
   //text is "c"
   DALI_TEST_EQUALS( "c", editor.GetProperty<std::string>( TextEditor::Property::TEXT ), TEST_LOCATION );
 
+  // select all text
+  DevelTextEditor::SelectWholeText(editor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Copy the selected text using logical keys
+  application.ProcessEvent( GenerateKey( "", "", "", Dali::DevelKey::DALI_KEY_CONTROL_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "ؤ", "c", "ؤ", KEY_C_CODE, KEY_CONTROL_MODIFIER, 0, Integration::KeyEvent::DOWN, "c", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // select none
+  DevelTextEditor::SelectNone(editor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Paste the selected using logical keys
+  application.ProcessEvent( GenerateKey( "", "", "", Dali::DevelKey::DALI_KEY_CONTROL_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "ر", "v", "ر", KEY_V_CODE, KEY_CONTROL_MODIFIER, 0, Integration::KeyEvent::DOWN, "v", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  //text is "cc"
+  DALI_TEST_EQUALS( "cc", editor.GetProperty<std::string>( TextEditor::Property::TEXT ), TEST_LOCATION );
+
+  // select all using logical keys
+  application.ProcessEvent( GenerateKey( "", "", "", Dali::DevelKey::DALI_KEY_CONTROL_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "ش", "a", "ش", KEY_A_CODE, KEY_CONTROL_MODIFIER, 0, Integration::KeyEvent::DOWN, "a", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // cut text using logical keys
+  application.ProcessEvent( GenerateKey( "", "", "", Dali::DevelKey::DALI_KEY_CONTROL_LEFT, 0, 0, Integration::KeyEvent::DOWN, "", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+  application.ProcessEvent( GenerateKey( "ء", "x", "ء", KEY_X_CODE, KEY_CONTROL_MODIFIER, 0, Integration::KeyEvent::DOWN, "x", DEFAULT_DEVICE_NAME, Device::Class::NONE, Device::Subclass::NONE ) );
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  //text is ""
+  DALI_TEST_EQUALS( "", editor.GetProperty<std::string>( TextEditor::Property::TEXT ), TEST_LOCATION );
+
   END_TEST;
 }
 
@@ -3036,3 +3084,50 @@ int UtcDaliTextEditorEnableEditing(void)
 
   END_TEST;
 }
+
+int UtcDaliTextEditorScrolling(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextEditorScrolling ");
+
+  TextEditor textEditor = TextEditor::New();
+  DALI_TEST_CHECK( textEditor );
+
+  application.GetScene().Add( textEditor );
+
+  // Avoid a crash when core load gl resources.
+  application.GetGlAbstraction().SetCheckFramebufferStatusResult( GL_FRAMEBUFFER_COMPLETE );
+
+  application.SendNotification();
+  application.Render();
+
+  textEditor.SetProperty(TextEditor::Property::TEXT, "Tex1\nTex2\nTex3\nTex4\nTex5\nTex6\nTex7\nTex8");
+  textEditor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
+  textEditor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
+  textEditor.SetProperty(Actor::Property::SIZE, Vector2(60.0f, 160.0f));
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::VERTICAL_SCROLL_POSITION ).Get<float>(), 0.0f, TEST_LOCATION );
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::HORIZONTAL_SCROLL_POSITION ).Get<float>(), 0.0f, TEST_LOCATION );
+
+
+  DevelTextEditor::ScrollBy(textEditor, Vector2(1.0f, 1.0f));
+
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::VERTICAL_SCROLL_POSITION ).Get<float>(), 1.0f, TEST_LOCATION );
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::HORIZONTAL_SCROLL_POSITION ).Get<float>(), 0.0f, TEST_LOCATION );
+
+  DevelTextEditor::ScrollBy(textEditor, Vector2(0.0f, 1000.0f));
+
+  DALI_TEST_NOT_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::VERTICAL_SCROLL_POSITION ).Get<float>(), 1.0f, 0.1f, TEST_LOCATION );
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::HORIZONTAL_SCROLL_POSITION ).Get<float>(), 0.0f, TEST_LOCATION );
+
+  textEditor.SetProperty(DevelTextEditor::Property::VERTICAL_SCROLL_POSITION , 0.0f);
+  textEditor.SetProperty(DevelTextEditor::Property::HORIZONTAL_SCROLL_POSITION , 0.0f);
+
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::VERTICAL_SCROLL_POSITION ).Get<float>(), 0.0f, TEST_LOCATION );
+  DALI_TEST_EQUALS( textEditor.GetProperty( DevelTextEditor::Property::HORIZONTAL_SCROLL_POSITION ).Get<float>(), 0.0f, TEST_LOCATION );
+
+  END_TEST;
+}
index adcff4c..abe3389 100644 (file)
@@ -45,6 +45,11 @@ void SelectNone(TextEditor textEditor)
   GetImpl(textEditor).SelectNone();
 }
 
+void ScrollBy(TextEditor textEditor, Vector2 scroll)
+{
+  GetImpl(textEditor).ScrollBy(scroll);
+}
+
 } // namespace DevelTextEditor
 
 } // namespace Toolkit
index 0db66e5..6cb473c 100644 (file)
@@ -140,6 +140,18 @@ enum Type
   SELECTED_TEXT_END,
 
   /**
+   * @brief The horizontal scroll position in pixels.
+   * @details Name "horizontalScrollPosition", type Property::FLOAT.
+   */
+  HORIZONTAL_SCROLL_POSITION,
+
+  /**
+   * @brief The vertical scroll position in pixels.
+   * @details Name "verticalScrollPosition", type Property::FLOAT.
+   */
+  VERTICAL_SCROLL_POSITION,
+
+  /**
    * @brief The Editable state of control.
    * @details Name "enableEditing", type Property::BOOLEAN.
    */
@@ -194,6 +206,14 @@ DALI_TOOLKIT_API void SelectWholeText(TextEditor textEditor);
  */
 DALI_TOOLKIT_API void SelectNone(TextEditor textEditor);
 
+/**
+ * @brief Scroll the TextEditor by specific amount.
+ *
+ * @param[in] textEditor The instance of TextEditor.
+ * @param[in] scroll amount (in pixels) of scrolling in horizontal & vectical directions.
+ */
+DALI_TOOLKIT_API void ScrollBy(TextEditor textEditor, Vector2 scroll);
+
 } // namespace DevelTextEditor
 
 } // namespace Toolkit
index 4650a6d..dc82c36 100644 (file)
@@ -143,6 +143,8 @@ DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "renderingBackend",
 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "maxLength",                      INTEGER,   MAX_LENGTH                           )
 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "selectedTextStart",              INTEGER,   SELECTED_TEXT_START                  )
 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "selectedTextEnd",                INTEGER,   SELECTED_TEXT_END                    )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "horizontalScrollPosition",       FLOAT,     HORIZONTAL_SCROLL_POSITION           )
+DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "verticalScrollPosition",         INTEGER,   VERTICAL_SCROLL_POSITION             )
 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit, TextEditor, "enableEditing",                  BOOLEAN,   ENABLE_EDITING                       )
 DALI_DEVEL_PROPERTY_REGISTRATION_READ_ONLY( Toolkit, TextEditor, "selectedText",         STRING,    SELECTED_TEXT                        )
 
@@ -722,6 +724,26 @@ void TextEditor::SetProperty( BaseObject* object, Property::Index index, const P
         impl.SetEditable( editable );
         break;
       }
+      case Toolkit::DevelTextEditor::Property::HORIZONTAL_SCROLL_POSITION:
+      {
+        float horizontalScroll = value.Get< float >();
+        DALI_LOG_INFO( gLogFilter, Debug::General, "TextEditor %p HORIZONTAL_SCROLL_POSITION %d\n", impl.mController.Get(), horizontalScroll );
+        if (horizontalScroll >= 0.0f)
+        {
+          impl.ScrollBy( Vector2(horizontalScroll - impl.GetHorizontalScrollPosition(), 0 ));
+        }
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::VERTICAL_SCROLL_POSITION:
+      {
+        float verticalScroll = value.Get< float >();
+        DALI_LOG_INFO( gLogFilter, Debug::General, "TextEditor %p VERTICAL_SCROLL_POSITION %d\n", impl.mController.Get(), verticalScroll );
+        if (verticalScroll >= 0.0f)
+        {
+          impl.ScrollBy( Vector2(0, verticalScroll - impl.GetVerticalScrollPosition() ));
+        }
+        break;
+      }
     } // switch
   } // texteditor
 }
@@ -1058,6 +1080,16 @@ Property::Value TextEditor::GetProperty( BaseObject* object, Property::Index ind
         value = impl.IsEditable();
         break;
       }
+      case Toolkit::DevelTextEditor::Property::HORIZONTAL_SCROLL_POSITION:
+      {
+        value = impl.GetHorizontalScrollPosition();
+        break;
+      }
+      case Toolkit::DevelTextEditor::Property::VERTICAL_SCROLL_POSITION:
+      {
+        value = impl.GetVerticalScrollPosition();
+        break;
+      }
     } //switch
   }
 
@@ -1081,6 +1113,32 @@ void TextEditor::SelectNone()
   }
 }
 
+void TextEditor::ScrollBy(Vector2 scroll)
+{
+  if( mController && mController->IsShowingRealText() )
+  {
+    mController->ScrollBy(scroll);
+  }
+}
+
+float TextEditor::GetHorizontalScrollPosition()
+{
+  if( mController && mController->IsShowingRealText() )
+  {
+    return mController->GetHorizontalScrollPosition();
+  }
+  return 0;
+}
+
+float TextEditor::GetVerticalScrollPosition()
+{
+  if( mController && mController->IsShowingRealText() )
+  {
+    return mController->GetVerticalScrollPosition();
+  }
+  return 0;
+}
+
 string TextEditor::GetSelectedText() const
 {
   string selectedText = "";
index 5d0df97..c2385a2 100755 (executable)
@@ -248,6 +248,25 @@ public:
   void SelectNone() override;
 
   /**
+   * @copydoc Dali::Toolkit::DevelTextEditor::ScrollBy()
+   */
+  void ScrollBy(Vector2 Scroll);
+
+  /**
+   * @brief Get Horizontal scroll position of TextEditor.
+   *
+   * @return Horizontal scroll position (in pixels) of TextEditor.
+   */
+  float GetHorizontalScrollPosition();
+
+  /**
+   * @brief Get Vertical scroll position of TextEditor.
+   *
+   * @return Vertical scroll position (in pixels) of TextEditor.
+   */
+  float GetVerticalScrollPosition();
+
+  /**
    * @copydoc Text::SelectableControlInterface::GetSelectedText()
    */
   string GetSelectedText() const override;
index b9b260b..89ff0f9 100644 (file)
@@ -109,6 +109,9 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
     int keyCode = keyEvent.GetKeyCode();
     const std::string& keyString = keyEvent.GetKeyString();
     const std::string keyName = keyEvent.GetKeyName();
+    // Key will produce same logical-key value when ctrl
+    // is down, regardless of language layout
+    const std::string logicalKey = keyEvent.GetLogicalKey();
 
     const bool isNullKey = ( 0 == keyCode ) && ( keyString.empty() );
 
@@ -182,25 +185,25 @@ bool Controller::EventHandler::KeyEvent(Controller& controller, const Dali::KeyE
     else if ( keyEvent.IsCtrlModifier() && !keyEvent.IsShiftModifier())
     {
       bool consumed = false;
-      if (keyName == KEY_C_NAME || keyName == KEY_INSERT_NAME)
+      if (keyName == KEY_C_NAME || keyName == KEY_INSERT_NAME || logicalKey == KEY_C_NAME || logicalKey == KEY_INSERT_NAME)
       {
         // Ctrl-C or Ctrl+Insert to copy the selected text
         controller.TextPopupButtonTouched( Toolkit::TextSelectionPopup::COPY );
         consumed = true;
       }
-      else if (keyName == KEY_V_NAME)
+      else if (keyName == KEY_V_NAME || logicalKey == KEY_V_NAME)
       {
         // Ctrl-V to paste the copied text
         controller.TextPopupButtonTouched( Toolkit::TextSelectionPopup::PASTE );
         consumed = true;
       }
-      else if (keyName == KEY_X_NAME)
+      else if (keyName == KEY_X_NAME || logicalKey == KEY_X_NAME)
       {
         // Ctrl-X to cut the selected text
         controller.TextPopupButtonTouched( Toolkit::TextSelectionPopup::CUT );
         consumed = true;
       }
-      else if (keyName == KEY_A_NAME)
+      else if (keyName == KEY_A_NAME || logicalKey == KEY_A_NAME)
       {
         // Ctrl-A to select All the text
         controller.TextPopupButtonTouched( Toolkit::TextSelectionPopup::SELECT_ALL );
index 3035062..aed8d32 100644 (file)
@@ -2264,6 +2264,56 @@ void Controller::SetEditable( bool editable )
   }
 }
 
+void Controller::ScrollBy( Vector2 scroll )
+{
+  if( mImpl->mEventData && (fabs(scroll.x) > Math::MACHINE_EPSILON_0 || fabs(scroll.y) > Math::MACHINE_EPSILON_0))
+  {
+      const Vector2& layoutSize = mImpl->mModel->mVisualModel->GetLayoutSize();
+      const Vector2 currentScroll = mImpl->mModel->mScrollPosition;
+
+      scroll.x = -scroll.x;
+      scroll.y = -scroll.y;
+
+      if( fabs(scroll.x) > Math::MACHINE_EPSILON_0 )
+      {
+        mImpl->mModel->mScrollPosition.x += scroll.x;
+        mImpl->ClampHorizontalScroll( layoutSize );
+      }
+
+      if( fabs(scroll.y) > Math::MACHINE_EPSILON_0 )
+      {
+        mImpl->mModel->mScrollPosition.y += scroll.y;
+        mImpl->ClampVerticalScroll( layoutSize );
+      }
+
+      if (mImpl->mModel->mScrollPosition != currentScroll)
+      {
+        mImpl->mEventData->mDecorator->UpdatePositions( mImpl->mModel->mScrollPosition - currentScroll );
+        mImpl->RequestRelayout();
+      }
+  }
+}
+
+float Controller::GetHorizontalScrollPosition()
+{
+  if( mImpl->mEventData )
+  {
+    //scroll values are negative internally so we convert them to positive numbers
+    return -mImpl->mModel->mScrollPosition.x;
+  }
+  return 0;
+}
+
+float Controller::GetVerticalScrollPosition()
+{
+  if( mImpl->mEventData )
+  {
+    //scroll values are negative internally so we convert them to positive numbers
+    return -mImpl->mModel->mScrollPosition.y;
+  }
+  return 0;
+}
+
 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
 {
   EventHandler::DecorationEvent(*this, handleType, state, x, y);
index f600f84..d627324 100644 (file)
@@ -1563,6 +1563,21 @@ public: // Text-input Event Queuing.
   virtual void SetEditable( bool editable );
 
   /**
+   * @copydoc Dali::Toolkit::Internal::TextEditor::ScrollBy()
+   */
+  virtual void ScrollBy( Vector2 scroll );
+
+  /**
+   * @copydoc Dali::Toolkit::Internal::TextEditor::GetHorizontalScrollPosition()
+   */
+  float GetHorizontalScrollPosition();
+
+  /**
+   * @copydoc Dali::Toolkit::Internal::TextEditor::GetVerticalScrollPosition()
+   */
+  float GetVerticalScrollPosition();
+
+  /**
    * @brief Event received from input method context
    *
    * @param[in] inputMethodContext The input method context.
index ea097e1..fead0e6 100644 (file)
@@ -94,25 +94,41 @@ AnimatedVectorImageVisual::AnimatedVectorImageVisual( VisualFactoryCache& factor
   mPlacementActor(),
   mPlayState( DevelImageVisual::PlayState::STOPPED ),
   mEventCallback( nullptr ),
-  mRendererAdded( false )
+  mRendererAdded( false ),
+  mCoreShutdown(false)
 {
   // the rasterized image is with pre-multiplied alpha format
   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
 
   mVectorAnimationTask->UploadCompletedSignal().Connect( this, &AnimatedVectorImageVisual::OnUploadCompleted );
   mVectorAnimationTask->SetAnimationFinishedCallback( new EventThreadCallback( MakeCallback( this, &AnimatedVectorImageVisual::OnAnimationFinished ) ) );
+
+  auto& vectorAnimationManager = mFactoryCache.GetVectorAnimationManager();
+  vectorAnimationManager.AddObserver(*this);
 }
 
 AnimatedVectorImageVisual::~AnimatedVectorImageVisual()
 {
-  if( mEventCallback )
+  if( ! mCoreShutdown )
   {
-    mFactoryCache.GetVectorAnimationManager().UnregisterEventCallback( mEventCallback );
+    auto& vectorAnimationManager = mFactoryCache.GetVectorAnimationManager();
+    vectorAnimationManager.RemoveObserver(*this);
+
+    if( mEventCallback )
+    {
+      mFactoryCache.GetVectorAnimationManager().UnregisterEventCallback( mEventCallback );
+    }
+
+    // Finalize animation task and disconnect the signal in the main thread
+    mVectorAnimationTask->UploadCompletedSignal().Disconnect( this, &AnimatedVectorImageVisual::OnUploadCompleted );
+    mVectorAnimationTask->Finalize();
   }
+}
 
-  // Finalize animation task and disconnect the signal in the main thread
-  mVectorAnimationTask->UploadCompletedSignal().Disconnect( this, &AnimatedVectorImageVisual::OnUploadCompleted );
-  mVectorAnimationTask->Finalize();
+void AnimatedVectorImageVisual::VectorAnimationManagerDestroyed()
+{
+  // Core is shutting down. Don't talk to the plugin any more.
+  mCoreShutdown = true;
 }
 
 void AnimatedVectorImageVisual::GetNaturalSize( Vector2& naturalSize )
@@ -500,10 +516,11 @@ void AnimatedVectorImageVisual::StopAnimation()
 
 void AnimatedVectorImageVisual::TriggerVectorRasterization()
 {
-  if( !mEventCallback )
+  if( !mEventCallback && !mCoreShutdown )
   {
     mEventCallback = MakeCallback( this, &AnimatedVectorImageVisual::OnProcessEvents );
-    mFactoryCache.GetVectorAnimationManager().RegisterEventCallback( mEventCallback );
+    auto& vectorAnimationManager = mFactoryCache.GetVectorAnimationManager();
+    vectorAnimationManager.RegisterEventCallback( mEventCallback );
     Stage::GetCurrent().KeepRendering( 0.0f );  // Trigger event processing
   }
 }
index e09527a..6196e39 100644 (file)
@@ -30,6 +30,7 @@
 #include <dali-toolkit/internal/visuals/visual-url.h>
 #include <dali-toolkit/devel-api/visuals/animated-vector-image-visual-actions-devel.h>
 #include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-task.h>
+#include <dali-toolkit/internal/visuals/animated-vector-image/vector-animation-manager.h>
 
 namespace Dali
 {
@@ -55,7 +56,7 @@ using AnimatedVectorImageVisualPtr = IntrusivePtr< AnimatedVectorImageVisual >;
  * | url                      | STRING           |
  *
  */
-class AnimatedVectorImageVisual: public Visual::Base, public ConnectionTracker
+class AnimatedVectorImageVisual: public Visual::Base, public ConnectionTracker, public VectorAnimationManager::LifecycleObserver
 {
 public:
 
@@ -97,6 +98,12 @@ public:  // from Visual
    */
   void DoCreateInstancePropertyMap( Property::Map& map ) const override;
 
+protected: // From VectorAnimationManager::LifecycleObserver:
+  /**
+   * @copydoc VectorAnimationManager::LifecycleObserver::VectorAnimationManagerDestroyed()
+   */
+  void VectorAnimationManagerDestroyed() override;
+
 protected:
 
   /**
@@ -221,6 +228,7 @@ private:
   DevelImageVisual::PlayState::Type            mPlayState;
   CallbackBase*                                mEventCallback;    // Not owned
   bool                                         mRendererAdded;
+  bool                                         mCoreShutdown;
 };
 
 } // namespace Internal
index 8e47d81..ad56227 100644 (file)
@@ -45,6 +45,7 @@ Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New( Debug::NoLogging,
 
 VectorAnimationManager::VectorAnimationManager()
 : mEventCallbacks(),
+  mLifecycleObservers(),
   mVectorAnimationThread( nullptr ),
   mProcessorRegistered( false )
 {
@@ -62,6 +63,26 @@ VectorAnimationManager::~VectorAnimationManager()
   {
     Adaptor::Get().UnregisterProcessor( *this );
   }
+
+  for( auto observer : mLifecycleObservers )
+  {
+    observer->VectorAnimationManagerDestroyed();
+  }
+}
+
+void VectorAnimationManager::AddObserver( VectorAnimationManager::LifecycleObserver& observer )
+{
+  DALI_ASSERT_DEBUG( mLifecycleObservers.end() == std::find( mLifecycleObservers.begin(), mLifecycleObservers.end(), &observer));
+  mLifecycleObservers.push_back( &observer );
+}
+
+void VectorAnimationManager::RemoveObserver( VectorAnimationManager::LifecycleObserver& observer)
+{
+  auto iterator=std::find(mLifecycleObservers.begin(), mLifecycleObservers.end(), &observer);
+  if( iterator != mLifecycleObservers.end() )
+  {
+    mLifecycleObservers.erase(iterator);
+  }
 }
 
 VectorAnimationThread& VectorAnimationManager::GetVectorAnimationThread()
index 510c07e..7fbc3ca 100644 (file)
@@ -43,6 +43,10 @@ class VectorAnimationThread;
 class VectorAnimationManager: public Integration::Processor
 {
 public:
+  struct LifecycleObserver
+  {
+    virtual void VectorAnimationManagerDestroyed() = 0;
+  };
 
   /**
    * @brief Constructor.
@@ -55,6 +59,18 @@ public:
   ~VectorAnimationManager() override;
 
   /**
+   * Add a lifecycle observer
+   * @param[in] observer The object watching this one
+   */
+  void AddObserver( LifecycleObserver& observer );
+
+  /**
+   * Remove a lifecycle observer
+   * @param[in] observer The object watching this one
+   */
+  void RemoveObserver( LifecycleObserver& observer );
+
+  /**
    * Get the vector animation thread.
    * @return A raw pointer pointing to the vector animation thread.
    */
@@ -93,6 +109,7 @@ private:
 private:
 
   std::vector< CallbackBase* >             mEventCallbacks;
+  std::vector<LifecycleObserver*>         mLifecycleObservers;
   std::unique_ptr< VectorAnimationThread > mVectorAnimationThread;
   bool                                     mProcessorRegistered;
 };