stop text scrolling when label is disconnected from scene
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-scroller.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-scroller.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23
24 // INTERNAL INCLUDES
25 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
26 #include <dali-toolkit/internal/text/text-scroller-interface.h>
27
28 namespace Dali
29 {
30 namespace Toolkit
31 {
32 namespace
33 {
34 #if defined(DEBUG_ENABLED)
35 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_SCROLLING");
36 #endif
37
38 const int MINIMUM_SCROLL_SPEED = 1; // Speed should be set by Property system.
39
40 /**
41  * @brief How the text should be aligned horizontally when scrolling the text.
42  *
43  * -0.5f aligns the text to the left, 0.0f aligns the text to the center, 0.5f aligns the text to the right.
44  * The final alignment depends on two factors:
45  *   1) The alignment value of the text label (Use Text::HorizontalAlignment enumerations).
46  *   2) The text direction, i.e. whether it's LTR or RTL (0 = LTR, 1 = RTL).
47  */
48 const float HORIZONTAL_ALIGNMENT_TABLE[Text::HorizontalAlignment::END + 1][2] =
49   {
50     // HorizontalAlignment::BEGIN
51     {
52       -0.5f, // LTR
53       0.5f   // RTL
54     },
55
56     // HorizontalAlignment::CENTER
57     {
58       0.0f, // LTR
59       0.0f  // RTL
60     },
61
62     // HorizontalAlignment::END
63     {
64       0.5f, // LTR
65       -0.5f // RTL
66     }};
67
68 /**
69  * @brief How the text should be aligned vertically when scrolling the text.
70  *
71  * -0.5f aligns the text to the top, 0.0f aligns the text to the center, 0.5f aligns the text to the bottom.
72  * The alignment depends on the alignment value of the text label (Use Text::VerticalAlignment enumerations).
73  */
74 const float VERTICAL_ALIGNMENT_TABLE[Text::VerticalAlignment::BOTTOM + 1] =
75   {
76     -0.5f, // VerticalAlignment::TOP
77     0.0f,  // VerticalAlignment::CENTER
78     0.5f   // VerticalAlignment::BOTTOM
79 };
80
81 } // namespace
82
83 namespace Text
84 {
85 TextScrollerPtr TextScroller::New(ScrollerInterface& scrollerInterface)
86 {
87   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextScroller::New\n");
88
89   TextScrollerPtr textScroller(new TextScroller(scrollerInterface));
90   return textScroller;
91 }
92
93 void TextScroller::SetGap(int gap)
94 {
95   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextScroller::SetGap gap[%d]\n", gap);
96   mWrapGap = static_cast<float>(gap);
97 }
98
99 int TextScroller::GetGap() const
100 {
101   return static_cast<int>(mWrapGap);
102 }
103
104 void TextScroller::SetSpeed(int scrollSpeed)
105 {
106   mScrollSpeed = std::max(MINIMUM_SCROLL_SPEED, scrollSpeed);
107 }
108
109 int TextScroller::GetSpeed() const
110 {
111   return mScrollSpeed;
112 }
113
114 void TextScroller::SetLoopCount(int loopCount)
115 {
116   if(loopCount >= 0)
117   {
118     mLoopCount = loopCount;
119   }
120
121   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextScroller::SetLoopCount [%d] Status[%s]\n", mLoopCount, (loopCount) ? "looping" : "stop");
122 }
123
124 int TextScroller::GetLoopCount() const
125 {
126   return mLoopCount;
127 }
128
129 void TextScroller::SetLoopDelay(float delay)
130 {
131   mLoopDelay = delay;
132 }
133
134 float TextScroller::GetLoopDelay() const
135 {
136   return mLoopDelay;
137 }
138
139 void TextScroller::SetStopMode(TextLabel::AutoScrollStopMode::Type stopMode)
140 {
141   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextScroller::SetAutoScrollStopMode [%s]\n", (stopMode == TextLabel::AutoScrollStopMode::IMMEDIATE) ? "IMMEDIATE" : "FINISH_LOOP");
142   mStopMode = stopMode;
143 }
144
145 void TextScroller::StopScrolling(bool immediate)
146 {
147   if(mScrollAnimation && mScrollAnimation.GetState() == Animation::PLAYING)
148   {
149     if(mStopMode == TextLabel::AutoScrollStopMode::IMMEDIATE || immediate)
150     {
151       mScrollAnimation.Stop();
152       mScrollerInterface.ScrollingFinished();
153     }
154     else if(mStopMode == TextLabel::AutoScrollStopMode::FINISH_LOOP)
155     {
156       mScrollAnimation.SetLoopCount(1); // As animation already playing this allows the current animation to finish instead of trying to stop mid-way
157     }
158     else
159     {
160       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Undifined AutoScrollStopMode\n");
161     }
162   }
163   else
164   {
165     mScrollerInterface.ScrollingFinished();
166   }
167 }
168
169 TextLabel::AutoScrollStopMode::Type TextScroller::GetStopMode() const
170 {
171   return mStopMode;
172 }
173
174 TextScroller::TextScroller(ScrollerInterface& scrollerInterface)
175 : mScrollerInterface(scrollerInterface),
176   mScrollDeltaIndex(Property::INVALID_INDEX),
177   mScrollSpeed(MINIMUM_SCROLL_SPEED),
178   mLoopCount(1),
179   mLoopDelay(0.0f),
180   mWrapGap(0.0f),
181   mStopMode(TextLabel::AutoScrollStopMode::FINISH_LOOP)
182 {
183   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextScroller Default Constructor\n");
184 }
185
186 TextScroller::~TextScroller()
187 {
188 }
189
190 void TextScroller::SetParameters(Actor scrollingTextActor, Renderer renderer, TextureSet textureSet, const Size& controlSize, const Size& textureSize, const float wrapGap, CharacterDirection direction, HorizontalAlignment::Type horizontalAlignment, VerticalAlignment::Type verticalAlignment)
191 {
192   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextScroller::SetParameters controlSize[%f,%f] textureSize[%f,%f] direction[%d]\n", controlSize.x, controlSize.y, textureSize.x, textureSize.y, direction);
193
194   mRenderer = renderer;
195
196   float animationProgress = 0.0f;
197   int   remainedLoop      = mLoopCount;
198   if(mScrollAnimation)
199   {
200     if(mScrollAnimation.GetState() == Animation::PLAYING)
201     {
202       animationProgress = mScrollAnimation.GetCurrentProgress();
203
204       if(mLoopCount > 0) // If not a ininity loop, then calculate remained loop
205       {
206         remainedLoop = mLoopCount - (mScrollAnimation.GetCurrentLoop());
207         remainedLoop = (remainedLoop <= 0 ? 1 : remainedLoop);
208       }
209     }
210     mScrollAnimation.Clear();
211
212     // Reset to the original shader and texture before scrolling
213     mRenderer.SetShader(mShader);
214     if(mTextureSet)
215     {
216       mRenderer.SetTextures(mTextureSet);
217     }
218   }
219
220   mShader     = mRenderer.GetShader();
221   mTextureSet = mRenderer.GetTextures();
222
223   // Set the shader and texture for scrolling
224   Shader shader = Shader::New(SHADER_TEXT_SCROLLER_SHADER_VERT, SHADER_TEXT_SCROLLER_SHADER_FRAG, Shader::Hint::NONE);
225   mRenderer.SetShader(shader);
226   mRenderer.SetTextures(textureSet);
227
228   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextScroller::SetParameters wrapGap[%f]\n", wrapGap);
229
230   float horizontalAlign;
231
232   if(textureSize.x > controlSize.x)
233   {
234     // if Text is elided, scroll should start at the begin of text.
235     horizontalAlign = HORIZONTAL_ALIGNMENT_TABLE[HorizontalAlignment::BEGIN][direction];
236   }
237   else
238   {
239     horizontalAlign = HORIZONTAL_ALIGNMENT_TABLE[horizontalAlignment][direction];
240   }
241
242   const float verticalAlign = VERTICAL_ALIGNMENT_TABLE[verticalAlignment];
243
244   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextScroller::SetParameters horizontalAlign[%f], verticalAlign[%f]\n", horizontalAlign, verticalAlign);
245
246   shader.RegisterProperty("uTextureSize", textureSize);
247   shader.RegisterProperty("uHorizontalAlign", horizontalAlign);
248   shader.RegisterProperty("uVerticalAlign", verticalAlign);
249   shader.RegisterProperty("uGap", wrapGap);
250   mScrollDeltaIndex = shader.RegisterProperty("uDelta", 0.0f);
251
252   float scrollAmount   = std::max(textureSize.width, controlSize.width);
253   float scrollDuration = scrollAmount / mScrollSpeed;
254
255   if(direction)
256   {
257     scrollAmount = -scrollAmount; // reverse direction of scrolling
258   }
259
260   StartScrolling(scrollingTextActor, scrollAmount, scrollDuration, remainedLoop);
261   mScrollAnimation.SetCurrentProgress(animationProgress);
262 }
263
264 void TextScroller::AutoScrollAnimationFinished(Dali::Animation& animation)
265 {
266   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextScroller::AutoScrollAnimationFinished\n");
267   mScrollerInterface.ScrollingFinished();
268
269   // Revert to the original shader and texture after scrolling
270   mRenderer.SetShader(mShader);
271   if(mTextureSet)
272   {
273     mRenderer.SetTextures(mTextureSet);
274   }
275 }
276
277 void TextScroller::StartScrolling(Actor scrollingTextActor, float scrollAmount, float scrollDuration, int loopCount)
278 {
279   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextScroller::StartScrolling scrollAmount[%f] scrollDuration[%f], loop[%d] speed[%d]\n", scrollAmount, scrollDuration, loopCount, mScrollSpeed);
280
281   Shader shader    = mRenderer.GetShader();
282   mScrollAnimation = Animation::New(scrollDuration);
283   mScrollAnimation.AnimateTo(Property(shader, mScrollDeltaIndex), scrollAmount, TimePeriod(mLoopDelay, scrollDuration));
284   mScrollAnimation.SetEndAction(Animation::DISCARD);
285   mScrollAnimation.SetLoopCount(loopCount);
286   mScrollAnimation.FinishedSignal().Connect(this, &TextScroller::AutoScrollAnimationFinished);
287   mScrollAnimation.Play();
288 }
289
290 } // namespace Text
291
292 } // namespace Toolkit
293
294 } // namespace Dali