(ProgressBar) Ensure full progress image is shown when 100%
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / progress-bar / progress-bar-impl.cpp
1 /*
2  * Copyright (c) 2016 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/controls/progress-bar/progress-bar-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23 #include <sstream>
24 #include <algorithm>
25 #include <dali/public-api/object/type-registry-helper.h>
26 #include <dali/public-api/size-negotiation/relayout-container.h>
27 #include <dali/public-api/math/math-utils.h>
28
29 namespace Dali
30 {
31
32 namespace Toolkit
33 {
34
35 namespace Internal
36 {
37
38 namespace // Unnamed namespace
39 {
40
41 BaseHandle Create()
42 {
43   return Dali::Toolkit::ProgressBar::New();
44 }
45
46 // Setup properties, signals and actions using the type-registry.
47 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ProgressBar, Toolkit::Control, Create )
48
49 DALI_PROPERTY_REGISTRATION( Toolkit, ProgressBar, "progressValue",          FLOAT,    PROGRESS_VALUE         )
50 DALI_PROPERTY_REGISTRATION( Toolkit, ProgressBar, "trackVisual",            MAP,      TRACK_VISUAL           )
51 DALI_PROPERTY_REGISTRATION( Toolkit, ProgressBar, "progressVisual",         MAP,      PROGRESS_VISUAL        )
52 DALI_SIGNAL_REGISTRATION(   Toolkit, ProgressBar, "valueChanged",                     SIGNAL_VALUE_CHANGED   )
53
54 DALI_TYPE_REGISTRATION_END()
55
56 const char* SKINNED_TRACK_VISUAL = DALI_IMAGE_DIR "slider-skin.9.png";
57 const char* SKINNED_PROGRESS_VISUAL = DALI_IMAGE_DIR "slider-skin-progress.9.png";
58
59 float DEFAULT_VALUE = 0.0f;
60 float DEFAULT_LOWER_BOUND = 0.0f;
61 float DEFAULT_UPPER_BOUND = 1.0f;
62
63 } // Unnamed namespace
64
65 ///////////////////////////////////////////////////////////////////////////////////////////////////
66 // ProgressBar
67 ///////////////////////////////////////////////////////////////////////////////////////////////////
68
69 Dali::Toolkit::ProgressBar ProgressBar::New()
70 {
71   // Create the implementation
72   ProgressBarPtr progressBar( new ProgressBar() );
73
74   // Pass ownership to CustomActor via derived handle
75   Dali::Toolkit::ProgressBar handle( *progressBar );
76
77   // Second-phase init of the implementation
78   // This can only be done after the CustomActor connection has been made...
79   progressBar->Initialize();
80
81   return handle;
82 }
83
84 ProgressBar::ProgressBar()
85 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
86   mTrackVisual(""),
87   mProgressVisual(""),
88   mTrackMap(),
89   mTrackVisualSize(),
90   mProgressVisualSize(),
91   mValue( DEFAULT_VALUE )
92 {
93 }
94
95 ProgressBar::~ProgressBar()
96 {
97 }
98
99 void ProgressBar::OnInitialize()
100 {
101   // Setup
102   CreateChildren();
103
104   // Properties
105   SetTrackVisual( SKINNED_TRACK_VISUAL );
106   SetProgressVisual( SKINNED_PROGRESS_VISUAL );
107
108   DisplayValue( mValue, false );       // Run this last to display the correct value
109 }
110
111 void ProgressBar::OnRelayout( const Vector2& size, RelayoutContainer& container )
112 {
113   // Track
114   if( mTrack )
115   {
116     container.Add( mTrack, size );
117
118     // mValueTextLabel will have its relayout method called automatically as it's a child of mTrack,
119     // which is added to the container
120   }
121
122   // Progress bar
123   if( mProgress )
124   {
125     mDomain = CalcDomain( size );
126
127     Vector2 progressSize( size );
128
129     // If no progress, then we do not want a n-patch image shown incorrectly
130     progressSize.width = std::max( mProgressVisualSize.width, mDomain.from.x + mValue * ( mDomain.to.x - mDomain.from.x ) );
131     progressSize.width = std::min( progressSize.width, size.width ); // We should not exceed given size
132
133     container.Add( mProgress, progressSize );
134   }
135 }
136
137 Vector3 ProgressBar::GetNaturalSize()
138 {
139   // Return the maximum width/height combinations of our visuals
140
141   Vector3 naturalSize;
142   naturalSize.width = std::max( mTrackVisualSize.width, mProgressVisualSize.width );
143   naturalSize.height = std::max( mTrackVisualSize.height, mProgressVisualSize.height );
144   return naturalSize;
145 }
146
147 ProgressBar::Domain ProgressBar::CalcDomain( const Vector2& currentSize )
148 {
149    return Domain( Vector2( 0.0f, 0.0f ), currentSize );
150 }
151
152 void ProgressBar::DisplayValue( float value, bool raiseSignals )
153 {
154   // Signals
155   if( raiseSignals )
156   {
157     Toolkit::ProgressBar self = Toolkit::ProgressBar::DownCast( Self() );
158     mValueChangedSignal.Emit( self, value );
159   }
160
161   // Change the value of the text label
162   if( mValueTextLabel )
163   {
164     std::stringstream ss;
165     ss.precision( 0 );
166     ss << std::fixed << ( value * 100 ) << "%";
167
168     std::string label = mValueTextLabel.GetProperty<std::string>( Toolkit::TextLabel::Property::TEXT );
169     if( label.compare(ss.str()) )
170     {
171       mValueTextLabel.SetProperty( Toolkit::TextLabel::Property::TEXT, ss.str() );
172     }
173   }
174 }
175
176 Toolkit::ImageView ProgressBar::CreateTrack()
177 {
178   Toolkit::ImageView track = Toolkit::ImageView::New();
179   track.SetParentOrigin( ParentOrigin::CENTER );
180   track.SetAnchorPoint( AnchorPoint::CENTER );
181   track.SetResizePolicy(ResizePolicy::USE_ASSIGNED_SIZE, Dimension::ALL_DIMENSIONS );
182
183   return track;
184 }
185
186 void ProgressBar::SetTrackVisual( const std::string& filename )
187 {
188   if( mTrack && filename.size() > 0 )
189   {
190     mTrack.SetImage( filename );
191     mTrackVisual = filename;
192     mTrackVisualSize = Vector2::ZERO;
193     RelayoutRequest();
194   }
195 }
196
197 void ProgressBar::SetTrackVisual( Property::Map map )
198 {
199   bool relayoutRequired = false;
200
201   Property::Value* imageValue = map.Find( "url" );
202   if( imageValue )
203   {
204     mTrackVisual.clear();
205     std::string filename;
206     if( imageValue->Get( filename ) )
207     {
208       if( mTrack && ( filename.size() > 0 ) )
209       {
210         mTrack.SetImage( filename );
211         mTrackMap = map;
212         relayoutRequired = true;
213       }
214     }
215   }
216
217   Property::Value* sizeValue = map.Find( "size" );
218   if( sizeValue )
219   {
220     Vector2 size;
221     if( sizeValue->Get( size ) )
222     {
223       mTrackVisualSize = size;
224       relayoutRequired = true;
225     }
226   }
227
228   // Visual and/or visual size changed so we need to relayout
229   if( relayoutRequired )
230   {
231     RelayoutRequest();
232   }
233 }
234
235 std::string ProgressBar::GetTrackVisual()
236 {
237   return mTrackVisual;
238 }
239
240 Toolkit::ImageView ProgressBar::CreateProgress()
241 {
242   Toolkit::ImageView progress = Toolkit::ImageView::New();
243   progress.SetParentOrigin( ParentOrigin::CENTER_LEFT );
244   progress.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
245   progress.SetResizePolicy(ResizePolicy::USE_ASSIGNED_SIZE, Dimension::ALL_DIMENSIONS );
246
247   return progress;
248 }
249
250 void ProgressBar::SetProgressVisual( const std::string& filename )
251 {
252   if( mProgress && ( filename.size() > 0 ) )
253   {
254     mProgress.SetImage( filename );
255     mProgressVisual = filename;
256     mProgressVisualSize = Vector2::ZERO;
257     RelayoutRequest();
258   }
259 }
260
261 void ProgressBar::SetProgressVisual( Property::Map map )
262 {
263   bool relayoutRequired = false;
264
265   Property::Value* imageValue = map.Find( "url" );
266   if( imageValue )
267   {
268     mProgressVisual.clear();
269     std::string filename;
270     if( imageValue->Get( filename ) )
271     {
272       if( mProgress && ( filename.size() > 0 ) )
273       {
274         mProgress.SetImage( filename );
275         mProgressMap = map;
276         relayoutRequired = true;
277       }
278     }
279   }
280
281   Property::Value* sizeValue = map.Find( "size" );
282   if( sizeValue )
283   {
284     Vector2 size;
285     if( sizeValue->Get( size ) )
286     {
287       mProgressVisualSize = size;
288       relayoutRequired = true;
289     }
290   }
291
292   // Visual and/or visual size changed so we need to relayout
293   if( relayoutRequired )
294   {
295     RelayoutRequest();
296   }
297 }
298
299 std::string ProgressBar::GetProgressVisual()
300 {
301   return mProgressVisual;
302 }
303
304 Toolkit::ProgressBar::ValueChangedSignalType& ProgressBar::ValueChangedSignal()
305 {
306   return mValueChangedSignal;
307 }
308
309 void ProgressBar::CreateChildren()
310 {
311   Actor self = Self();
312
313   // Track
314   mTrack = CreateTrack();
315   self.Add( mTrack ); // Needs to be a direct child as we want to manipulate its size
316
317   // Progress bar
318   mProgress = CreateProgress();
319   self.Add( mProgress ); // Needs to be a direct child as we want to manipulate its size
320
321   // Value Text
322   mValueTextLabel = Toolkit::TextLabel::New();
323   mValueTextLabel.SetName( "ProgressBarValueTextLabel" );
324   mValueTextLabel.SetStyleName( "ProgressBarValueTextLabel" );
325   mValueTextLabel.SetParentOrigin( ParentOrigin::CENTER );
326   mValueTextLabel.SetAnchorPoint( AnchorPoint::CENTER );
327   mValueTextLabel.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
328   mValueTextLabel.SetProperty( Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
329   mTrack.Add( mValueTextLabel ); // Add to mTrack and let it automatically set its size
330 }
331
332 void ProgressBar::SetProgressValue( float value )
333 {
334   // update the progress bar value (taking float precision errors into account)
335   if( ( mValue != value ) &&
336       ( ( value >= DEFAULT_LOWER_BOUND ) || ( Equals( value, DEFAULT_LOWER_BOUND ) ) ) &&
337       ( ( value <= DEFAULT_UPPER_BOUND ) || ( Equals( value, DEFAULT_UPPER_BOUND ) ) ) )
338   {
339     mValue = Clamp( value, DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND );
340     DisplayValue( mValue, true );
341     RelayoutRequest();
342   }
343 }
344
345 float ProgressBar::GetProgressValue() const
346 {
347   return mValue;
348 }
349
350 // Static class method to support script connecting signals
351 bool ProgressBar::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
352 {
353   Dali::BaseHandle handle( object );
354
355   bool connected = true;
356   Toolkit::ProgressBar ProgressBar = Toolkit::ProgressBar::DownCast( handle );
357
358   if( 0 == strcmp( signalName.c_str(), SIGNAL_VALUE_CHANGED ) )
359   {
360     ProgressBar.ValueChangedSignal().Connect( tracker, functor );
361   }
362   else
363   {
364     // signalName does not match any signal
365     connected = false;
366   }
367
368   return connected;
369 }
370
371 void ProgressBar::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
372 {
373   Toolkit::ProgressBar progressBar = Toolkit::ProgressBar::DownCast( Dali::BaseHandle( object ) );
374
375   if ( progressBar )
376   {
377     ProgressBar& progressBarImpl( GetImpl( progressBar ) );
378
379     switch ( propertyIndex )
380     {
381       case Toolkit::ProgressBar::Property::PROGRESS_VALUE:
382       {
383         progressBarImpl.SetProgressValue( value.Get< float >() );
384         break;
385       }
386
387       case Toolkit::ProgressBar::Property::TRACK_VISUAL:
388       {
389         Property::Map map;
390         if( value.Get( map ) )
391         {
392           progressBarImpl.SetTrackVisual( map );
393         }
394         break;
395       }
396
397       case Toolkit::ProgressBar::Property::PROGRESS_VISUAL:
398       {
399         Property::Map map;
400         if( value.Get( map ) )
401         {
402           progressBarImpl.SetProgressVisual( map );
403         }
404         break;
405       }
406     }
407   }
408 }
409
410 Property::Value ProgressBar::GetProperty( BaseObject* object, Property::Index propertyIndex )
411 {
412   Property::Value value;
413
414   Toolkit::ProgressBar progressBar = Toolkit::ProgressBar::DownCast( Dali::BaseHandle( object ) );
415
416   if ( progressBar )
417   {
418     ProgressBar& progressBarImpl( GetImpl( progressBar ) );
419
420     switch ( propertyIndex )
421     {
422       case Toolkit::ProgressBar::Property::PROGRESS_VALUE:
423       {
424         value = progressBarImpl.GetProgressValue();
425         break;
426       }
427
428       case Toolkit::ProgressBar::Property::TRACK_VISUAL:
429       {
430         if( !progressBarImpl.mTrackVisual.empty() )
431         {
432           value = progressBarImpl.GetTrackVisual();
433         }
434         else if( !progressBarImpl.mTrackMap.Empty() )
435         {
436           value = progressBarImpl.mTrackMap;
437         }
438         break;
439       }
440
441       case Toolkit::ProgressBar::Property::PROGRESS_VISUAL:
442       {
443         if( !progressBarImpl.mProgressVisual.empty() )
444         {
445           value = progressBarImpl.GetProgressVisual();
446         }
447         else if( !progressBarImpl.mProgressMap.Empty() )
448         {
449           value = progressBarImpl.mProgressMap;
450         }
451         break;
452       }
453     }
454   }
455
456   return value;
457 }
458
459 } // namespace Internal
460
461 } // namespace Toolkit
462
463 } // namespace Dali