private:
Vector<CallbackBase*> mCallbacks;
+ Vector<CallbackBase*> mReturnCallbacks;
std::vector<Internal::Adaptor::SceneHolder*> mWindows;
Dali::Adaptor::AdaptorSignalType mResizedSignal;
Dali::Adaptor::AdaptorSignalType mLanguageChangedSignal;
bool Adaptor::AddIdle(CallbackBase* callback, bool hasReturnValue)
{
- mCallbacks.PushBack(callback);
+ if(hasReturnValue)
+ {
+ mReturnCallbacks.PushBack(callback);
+ }
+ else
+ {
+ mCallbacks.PushBack(callback);
+ }
return true;
}
void Adaptor::RemoveIdle(CallbackBase* callback)
{
mCallbacks.Erase(std::find_if(mCallbacks.Begin(), mCallbacks.End(), [&callback](CallbackBase* current) { return callback == current; }));
+ mReturnCallbacks.Erase(std::find_if(mReturnCallbacks.Begin(), mReturnCallbacks.End(), [&callback](CallbackBase* current) { return callback == current; }));
}
void Adaptor::RunIdles()
{
+ Dali::Vector<CallbackBase*> reusedCallbacks;
+ for(auto& callback : mReturnCallbacks)
+ {
+ bool retValue = CallbackBase::ExecuteReturn<bool>(*callback);
+ if(retValue)
+ {
+ reusedCallbacks.PushBack(callback);
+ }
+ }
for(auto& callback : mCallbacks)
{
CallbackBase::Execute(*callback);
}
mCallbacks.Clear();
+ mReturnCallbacks.Clear();
+ mReturnCallbacks.Swap(reusedCallbacks);
}
Dali::RenderSurfaceInterface& Adaptor::GetSurface()
gImageView2.Reset();
}
}
+constexpr int gResourceReadySignal10MaxCounter = 5;
+
+void OnResourceReadySignal10(Control control)
+{
+ gResourceReadySignalCounter++;
+
+ tet_printf("OnResourceReadySignal10 comes!\n");
+ if(gResourceReadySignalCounter < gResourceReadySignal10MaxCounter)
+ {
+ tet_printf("OnResourceReadySignal10 Set image\n");
+ gImageView1.SetProperty(Toolkit::ImageView::Property::IMAGE, gImage_34_RGBA);
+ tet_printf("OnResourceReadySignal10 Set image done\n");
+ }
+}
} // namespace
END_TEST;
}
+int UtcDaliImageViewSetImageOnResourceReadySignal10(void)
+{
+ tet_infoline("Test ResourceReady signal comes more than 2 times.");
+
+ ToolkitTestApplication application;
+
+ gResourceReadySignalCounter = 0;
+
+ // Clear image view for clear test
+
+ if(gImageView1)
+ {
+ gImageView1.Reset();
+ }
+
+ // Dummy view to cache image.
+ ImageView dummyView = ImageView::New(gImage_34_RGBA);
+ application.GetScene().Add(dummyView);
+
+ application.SendNotification();
+ application.Render();
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ application.SendNotification();
+ application.Render();
+
+ try
+ {
+ gImageView1 = ImageView::New();
+ gImageView1.SetProperty(Toolkit::ImageView::Property::IMAGE, gImage_34_RGBA);
+ gImageView1.ResourceReadySignal().Connect(&OnResourceReadySignal10);
+ application.GetScene().Add(gImageView1); // It will call resourceReady signal 1 time.
+
+ tet_printf("ResourceReady called %d times\n", gResourceReadySignalCounter);
+
+ DALI_TEST_GREATER(gResourceReadySignal10MaxCounter, gResourceReadySignalCounter, TEST_LOCATION); // Check whether resource ready call too much.
+
+ for(int i = 0; i < gResourceReadySignal10MaxCounter; ++i)
+ {
+ tet_printf("RunIdles\n");
+ // Executes the idle callbacks.
+ application.RunIdles();
+ application.SendNotification();
+ application.Render();
+ tet_printf("RunIdles done\n");
+ }
+ tet_printf("ResourceReady called %d times\n", gResourceReadySignalCounter);
+
+ DALI_TEST_EQUALS(gResourceReadySignalCounter, gResourceReadySignal10MaxCounter, TEST_LOCATION);
+
+ DALI_TEST_CHECK(true);
+ }
+ catch(...)
+ {
+ // Exception should not happened
+ DALI_TEST_CHECK(false);
+ }
+
+ // Clear cache.
+ application.SendNotification();
+ application.Render();
+
+ gResourceReadySignalCounter = 0;
+
+ gResourceReadySignalCounter = 0;
+
+ // Clear image view for clear test
+
+ if(gImageView1)
+ {
+ gImageView1.Reset();
+ }
+
+ END_TEST;
+}
+
int UtcDaliImageViewUseSameUrlWithAnimatedImageVisual(void)
{
tet_infoline("Test multiple views with same image in animated image visual");
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
static std::vector<UniformData> customUniforms =
{
+ UniformData("pixelArea", Property::Type::VECTOR4),
UniformData("cornerRadius", Property::Type::VECTOR4),
UniformData("borderlineWidth", Property::Type::FLOAT),
UniformData("borderlineColor", Property::Type::VECTOR4),
application.Render();
float targetOpacity = 0.5f;
+ Vector4 targetPixelArea(0.0f, 1.0f, 2.0f, -0.5f);
Vector4 targetCornerRadius(20.0f, 20.0f, 0.0f, 0.0f);
float targetBorderlineWidth = 10.0f;
Vector4 targetBorderlineColor(1.0f, 0.0f, 1.0f, 0.5f);
Animation animation = Animation::New(1.0f);
animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Property::OPACITY), targetOpacity);
+ animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, ImageVisual::Property::PIXEL_AREA), targetPixelArea);
animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Property::CORNER_RADIUS), targetCornerRadius);
animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Property::BORDERLINE_WIDTH), targetBorderlineWidth);
animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Property::BORDERLINE_COLOR), targetBorderlineColor);
DALI_TEST_CHECK(colorValue);
DALI_TEST_EQUALS(colorValue->Get<Vector4>(), Vector4(1.0f, 1.0f, 1.0f, targetOpacity), TEST_LOCATION);
+ Property::Value* pixelAreaValue = resultMap.Find(ImageVisual::Property::PIXEL_AREA, Property::VECTOR4);
+ DALI_TEST_CHECK(pixelAreaValue);
+ DALI_TEST_EQUALS(pixelAreaValue->Get<Vector4>(), targetPixelArea, TEST_LOCATION);
+
Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::VECTOR4);
DALI_TEST_CHECK(cornerRadiusValue);
DALI_TEST_EQUALS(cornerRadiusValue->Get<Vector4>(), targetCornerRadius, TEST_LOCATION);
DALI_TEST_EQUALS(borderlineOffsetValue->Get<float>(), targetBorderlineOffset, TEST_LOCATION);
// Test uniform value
+ DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("pixelArea", targetPixelArea), true, TEST_LOCATION);
DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("cornerRadius", targetCornerRadius), true, TEST_LOCATION);
DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<float>("borderlineWidth", targetBorderlineWidth), true, TEST_LOCATION);
DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector4>("borderlineColor", targetBorderlineColor), true, TEST_LOCATION);
DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<float>("borderlineOffset", targetBorderlineOffset), true, TEST_LOCATION);
+ // Test non-animatable index, for coverage.
+ DALI_TEST_EQUALS(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, ImageVisual::Property::URL).propertyIndex, Property::INVALID_INDEX, TEST_LOCATION);
+
END_TEST;
}
mIsKeyboardNavigationSupported(false),
mIsKeyboardFocusGroup(false),
mIsEmittingResourceReadySignal(false),
- mNeedToEmitResourceReady(false),
+ mIdleCallbackRegistered(false),
mDispatchKeyEvents(true)
{
Dali::Accessibility::Accessible::RegisterExternalAccessibleGetter(&ExternalAccessibleGetter);
// Emit signal if all enabled visuals registered by the control are ready or there are no visuals.
if(IsResourceReady())
{
- // Reset the flag
- mNeedToEmitResourceReady = false;
EmitResourceReadySignal();
}
}
mIsEmittingResourceReadySignal = true;
// If the signal handler changes visual, it may become ready during this call & therefore this method will
- // get called again recursively. If so, mNeedToEmitResourceReady is set below, and we act on it after that secondary
+ // get called again recursively. If so, mIdleCallbackRegistered is set below, and we act on it after that secondary
// invocation has completed by notifying in an Idle callback to prevent further recursion.
Dali::Toolkit::Control handle(mControlImpl.GetOwner());
mResourceReadySignal.Emit(handle);
- if(mNeedToEmitResourceReady)
+ mIsEmittingResourceReadySignal = false;
+ }
+ else
+ {
+ if(!mIdleCallbackRegistered)
{
+ mIdleCallbackRegistered = true;
+
// Add idler to emit the signal again
if(!mIdleCallback)
{
// The callback manager takes the ownership of the callback object.
mIdleCallback = MakeCallback(this, &Control::Impl::OnIdleCallback);
- Adaptor::Get().AddIdle(mIdleCallback, false);
+ Adaptor::Get().AddIdle(mIdleCallback, true);
}
}
-
- mIsEmittingResourceReadySignal = false;
- }
- else
- {
- mNeedToEmitResourceReady = true;
}
}
-void Control::Impl::OnIdleCallback()
+bool Control::Impl::OnIdleCallback()
{
- if(mNeedToEmitResourceReady)
+ // Reset the flag
+ mIdleCallbackRegistered = false;
+
+ // A visual is ready so control may need relayouting if staged
+ if(mControlImpl.Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
{
- // Reset the flag
- mNeedToEmitResourceReady = false;
+ mControlImpl.RelayoutRequest();
+ }
- // A visual is ready so control may need relayouting if staged
- if(mControlImpl.Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
- {
- mControlImpl.RelayoutRequest();
- }
+ EmitResourceReadySignal();
- EmitResourceReadySignal();
+ if(!mIdleCallbackRegistered)
+ {
+ // Set the pointer to null as the callback manager deletes the callback after execute it.
+ mIdleCallback = nullptr;
}
- // Set the pointer to null as the callback manager deletes the callback after execute it.
- mIdleCallback = nullptr;
+ // Repeat idle if mIdleCallbackRegistered become true one more time.
+ return mIdleCallbackRegistered;
}
Toolkit::DevelControl::ControlAccessible* Control::Impl::GetAccessibleObject()
/**
* @brief Callbacks called on idle.
+ *
+ * @return True if we need to call this idle callback one more time.
*/
- void OnIdleCallback();
+ bool OnIdleCallback();
/**
* @brief Checks highlighted object geometry if it is showing or not
bool mIsKeyboardNavigationSupported : 1; ///< Stores whether keyboard navigation is supported by the control.
bool mIsKeyboardFocusGroup : 1; ///< Stores whether the control is a focus group.
bool mIsEmittingResourceReadySignal : 1; ///< True during ResourceReady().
- bool mNeedToEmitResourceReady : 1; ///< True if need to emit the resource ready signal again.
+ bool mIdleCallbackRegistered : 1; ///< True if need to emit the resource ready signal again.
bool mDispatchKeyEvents : 1; ///< Whether the actor emits key event signals
RegisteredVisualContainer mRemoveVisuals; ///< List of visuals that are being replaced by another visual once ready
Dali::SamplingMode::Type samplingMode)
: Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::IMAGE),
mPixelArea(FULL_TEXTURE_RECT),
+ mPixelAreaIndex(Property::INVALID_INDEX),
mPlacementActor(),
mImageUrl(imageUrl),
mMaskingData(),
case Toolkit::ImageVisual::Property::PIXEL_AREA:
{
value.Get(mPixelArea);
+
+ if(DALI_UNLIKELY(mImpl->mRenderer))
+ {
+ // Unusual case. SetProperty called after OnInitialize().
+ // Assume that DoAction call UPDATE_PROPERTY.
+ mPixelAreaIndex = mImpl->mRenderer.RegisterProperty(mPixelAreaIndex, PIXEL_AREA_UNIFORM_NAME, mPixelArea);
+ }
break;
}
if(mPixelArea != FULL_TEXTURE_RECT)
{
- mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea);
+ mPixelAreaIndex = mImpl->mRenderer.RegisterProperty(mPixelAreaIndex, PIXEL_AREA_UNIFORM_NAME, mPixelArea);
}
if(mLoadState == TextureManager::LoadState::LOAD_FINISHED)
map.Insert(Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode);
map.Insert(Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode);
- map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea);
+ if(mImpl->mRenderer && mPixelAreaIndex != Property::INVALID_INDEX)
+ {
+ // Update values from Renderer
+ Vector4 pixelArea = mImpl->mRenderer.GetProperty<Vector4>(mPixelAreaIndex);
+ map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, pixelArea);
+ }
+ else
+ {
+ map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea);
+ }
+
map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU);
map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV);
return shader;
}
+Dali::Property ImageVisual::OnGetPropertyObject(Dali::Property::Key key)
+{
+ if((key.type == Property::Key::INDEX && key.indexKey == Toolkit::ImageVisual::Property::PIXEL_AREA) || (key.type == Property::Key::STRING && key.stringKey == PIXEL_AREA_UNIFORM_NAME))
+ {
+ if(DALI_LIKELY(mImpl->mRenderer))
+ {
+ if(mPixelAreaIndex == Property::INVALID_INDEX)
+ {
+ mPixelAreaIndex = mImpl->mRenderer.RegisterProperty(mPixelAreaIndex, PIXEL_AREA_UNIFORM_NAME, mPixelArea);
+ }
+ return Dali::Property(mImpl->mRenderer, mPixelAreaIndex);
+ }
+ }
+
+ Handle handle;
+ return Dali::Property(handle, Property::INVALID_INDEX);
+}
+
void ImageVisual::CheckMaskTexture()
{
if(mMaskingData && !mMaskingData->mPreappliedMasking)
*/
Shader GenerateShader() const override;
+ /**
+ * @copydoc Visual::Base::OnGetPropertyObject
+ */
+ Dali::Property OnGetPropertyObject(Dali::Property::Key key) override;
+
public:
/**
* @copydoc AtlasUploadObserver::UploadCompleted
private:
Vector4 mPixelArea;
+ Property::Index mPixelAreaIndex;
WeakHandle<Actor> mPlacementActor;
VisualUrl mImageUrl;
TextureManager::MaskingDataPointer mMaskingData;