Prevent slider setting handle text if no change
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / slider / slider-impl.cpp
1 /*
2  * Copyright (c) 2014 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/slider/slider-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23 #include <sstream>
24 #include <limits>
25 #include <dali/public-api/events/touch-data.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/public-api/object/type-registry-helper.h>
28 #include <dali/public-api/images/resource-image.h>
29
30 // INTERNAL INCLUDES
31 #include <dali-toolkit/public-api/controls/control-impl.h>
32 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
33
34 using namespace Dali;
35
36 namespace Dali
37 {
38
39 namespace Toolkit
40 {
41
42 namespace Internal
43 {
44
45 namespace // Unnamed namespace
46 {
47
48 BaseHandle Create()
49 {
50   return Dali::Toolkit::Slider::New();
51 }
52
53 // Setup properties, signals and actions using the type-registry.
54 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::Slider, Toolkit::Control, Create )
55
56 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "lowerBound",             FLOAT,    LOWER_BOUND            )
57 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "upperBound",             FLOAT,    UPPER_BOUND            )
58 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "value",                  FLOAT,    VALUE                  )
59 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "trackVisual",            MAP,      TRACK_VISUAL           )
60 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "handleVisual",           MAP,      HANDLE_VISUAL          )
61 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "progressVisual",         MAP,      PROGRESS_VISUAL        )
62 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "popupVisual",            MAP,      POPUP_VISUAL           )
63 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "popupArrowVisual",       MAP,      POPUP_ARROW_VISUAL     )
64 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "disabledColor",          VECTOR4,  DISABLED_COLOR         )
65 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "valuePrecision",         INTEGER,  VALUE_PRECISION        )
66 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "showPopup",              BOOLEAN,  SHOW_POPUP             )
67 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "showValue",              BOOLEAN,  SHOW_VALUE             )
68 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "marks",                  ARRAY,    MARKS                  )
69 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "snapToMarks",            BOOLEAN,  SNAP_TO_MARKS          )
70 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "markTolerance",          FLOAT,    MARK_TOLERANCE         )
71
72 DALI_SIGNAL_REGISTRATION(   Toolkit, Slider, "valueChanged",                     SIGNAL_VALUE_CHANGED   )
73 DALI_SIGNAL_REGISTRATION(   Toolkit, Slider, "mark",                             SIGNAL_MARK            )
74
75 DALI_TYPE_REGISTRATION_END()
76
77 const float MARK_SNAP_TOLERANCE = 0.05f; // 5% of slider width
78
79 const int VALUE_VIEW_SHOW_DURATION = 1000;  // millisec
80 const int VALUE_VIEW_SHOW_DURATION_LONG = 2000;  // millisec
81
82 const float VALUE_VERTICAL_OFFSET = 48.0f;
83
84 const float DEFAULT_WIDTH = 0.0f;
85 const float DEFAULT_HEIGHT = 27.0f;
86 const float DEFAULT_HIT_HEIGHT = 72.0f;
87 const float DEFAULT_HANDLE_HEIGHT = DEFAULT_HIT_HEIGHT;
88 const float POPUP_TEXT_PADDING = 10.0f;
89
90 const char* SKINNED_TRACK_VISUAL = DALI_IMAGE_DIR "slider-skin.9.png";
91 const char* SKINNED_HANDLE_VISUAL = DALI_IMAGE_DIR "slider-skin-handle.png";
92 const char* SKINNED_PROGRESS_VISUAL = DALI_IMAGE_DIR "slider-skin-progress.9.png";
93 const char* SKINNED_POPUP_VISUAL = DALI_IMAGE_DIR "slider-popup.9.png";
94 const char* SKINNED_POPUP_ARROW_VISUAL = DALI_IMAGE_DIR "slider-popup-arrow.png";
95
96 const Vector2 DEFAULT_HIT_REGION( DEFAULT_WIDTH, DEFAULT_HIT_HEIGHT );
97 const Vector2 DEFAULT_TRACK_REGION( DEFAULT_WIDTH, DEFAULT_HEIGHT );
98 const Vector2 DEFAULT_HANDLE_SIZE( DEFAULT_HANDLE_HEIGHT, DEFAULT_HANDLE_HEIGHT );
99
100 const Vector4 DEFAULT_DISABLED_COLOR( 0.5f, 0.5f, 0.5f, 1.0f );
101
102 const float VALUE_POPUP_MARGIN = 10.0f;
103 const float VALUE_POPUP_HEIGHT = 81.0f;
104 const float VALUE_POPUP_MIN_WIDTH = 54.0f;
105
106 const float DEFAULT_LOWER_BOUND = 0.0f;
107 const float DEFAULT_UPPER_BOUND = 1.0f;
108 const float DEFAULT_VALUE = 0.0f;
109 const int DEFAULT_VALUE_PRECISION = 0;
110 const bool DEFAULT_SHOW_POPUP = false;
111 const bool DEFAULT_SHOW_VALUE = true;
112 const bool DEFAULT_ENABLED = true;
113 const bool DEFAULT_SNAP_TO_MARKS = false;
114
115 } // Unnamed namespace
116
117 ///////////////////////////////////////////////////////////////////////////////////////////////////
118 // Slider
119 ///////////////////////////////////////////////////////////////////////////////////////////////////
120
121 Dali::Toolkit::Slider Slider::New()
122 {
123   // Create the implementation
124   SliderPtr slider( new Slider() );
125
126   // Pass ownership to CustomActor via derived handle
127   Dali::Toolkit::Slider handle( *slider );
128
129   // Second-phase init of the implementation
130   // This can only be done after the CustomActor connection has been made...
131   slider->Initialize();
132
133   return handle;
134 }
135
136 Slider::Slider()
137 : Control( ControlBehaviour( REQUIRES_STYLE_CHANGE_SIGNALS ) ),
138   mState( NORMAL ),
139   mPopupVisual(""),
140   mPopupArrowVisual(""),
141   mTrackVisual(""),
142   mHandleVisual(""),
143   mProgressVisual(""),
144   mPopupMap(),
145   mTrackMap(),
146   mHandleMap(),
147   mPopupArrowMap(),
148   mDisabledColor( 0.0f, 0.0f, 0.0f, 0.0f ),
149   mHitRegion( 0.0f, 0.0f ),
150   mTrackRegion( 0.0f, 0.0f ),
151   mHandleSize( 0.0f, 0.0f ),
152   mLowerBound( 0.0f ),
153   mUpperBound( 0.0f ),
154   mValue( 0.0f ),
155   mMarkTolerance( 0.0f ),
156   mValuePrecision( 0 ),
157   mShowPopup( false ),
158   mShowValue( false ),
159   mSnapToMarks( false )
160 {
161 }
162
163 Slider::~Slider()
164 {
165 }
166
167 void Slider::OnInitialize()
168 {
169   // Setup
170   CreateChildren();
171
172   // Properties
173   Actor self = Self();
174
175   SetHitRegion(     DEFAULT_HIT_REGION     );
176   SetTrackRegion(   DEFAULT_TRACK_REGION   );
177   SetHandleSize(    DEFAULT_HANDLE_SIZE    );
178
179   SetTrackVisual(            SKINNED_TRACK_VISUAL             );
180   SetHandleVisual(           SKINNED_HANDLE_VISUAL            );
181   SetProgressVisual(         SKINNED_PROGRESS_VISUAL          );
182   SetPopupVisual(            SKINNED_POPUP_VISUAL             );
183   SetPopupArrowVisual(       SKINNED_POPUP_ARROW_VISUAL       );
184
185   SetShowPopup( DEFAULT_SHOW_POPUP );
186   SetShowValue( DEFAULT_SHOW_VALUE );
187
188   SetEnabled( DEFAULT_ENABLED );
189   SetDisabledColor( DEFAULT_DISABLED_COLOR );
190
191   SetSnapToMarks( DEFAULT_SNAP_TO_MARKS );
192   SetMarkTolerance( MARK_SNAP_TOLERANCE );
193
194   SetLowerBound( DEFAULT_LOWER_BOUND );
195   SetUpperBound( DEFAULT_UPPER_BOUND );
196   UpdateSkin();
197   SetValuePrecision( DEFAULT_VALUE_PRECISION );
198   mValue = DEFAULT_VALUE;
199   DisplayValue( mValue, false );       // Run this last to display the correct value
200
201   // Size the Slider actor to a default
202   self.SetSize( DEFAULT_HIT_REGION.x, DEFAULT_HIT_REGION.y );
203
204   // Connect to the touch signal
205   self.TouchSignal().Connect( this, &Slider::OnTouch );
206 }
207
208 void Slider::OnSizeSet( const Vector3& size )
209 {
210 }
211
212 void Slider::OnRelayout( const Vector2& size, RelayoutContainer& container )
213 {
214   SetHitRegion( Vector2( size.x, GetHitRegion().y ) );
215   // Factor in handle overshoot into size of backing
216   SetTrackRegion( Vector2( size.x - GetHandleSize().x, GetTrackRegion().y ) );
217   Control::OnRelayout( size, container );
218 }
219
220 bool Slider::OnTouch(Actor actor, const TouchData& touch)
221 {
222   if( mState != DISABLED )
223   {
224     const PointState::Type touchState = touch.GetState(0);
225
226     if( touchState == PointState::DOWN )
227     {
228       mState = PRESSED;
229
230       float percentage = MapPercentage( touch.GetLocalPosition( 0 ) );
231       float value = MapBounds( ( GetSnapToMarks() ) ? SnapToMark( percentage ) : MarkFilter( percentage ), GetLowerBound(), GetUpperBound() );
232       SetValue( value );
233       DisplayPopup( value );
234     }
235     else if( touchState == PointState::UP )
236     {
237       if( mState == PRESSED )
238       {
239         mState = NORMAL;
240         mSlidingFinishedSignal.Emit( Toolkit::Slider::DownCast( Self() ), GetValue() );
241       }
242     }
243   }
244
245   return true;
246 }
247
248 void Slider::OnPan( Actor actor, const PanGesture& gesture )
249 {
250   // gesture.position is in local actor coordinates
251   if( mState != DISABLED )
252   {
253     switch( gesture.state )
254     {
255       case Gesture::Continuing:
256       {
257         if( mState == PRESSED )
258         {
259           float value = MapBounds( MarkFilter ( MapPercentage( gesture.position ) ), GetLowerBound(), GetUpperBound() );
260           SetValue( value );
261           DisplayPopup( value );
262         }
263         break;
264       }
265       case Gesture::Finished:
266       {
267         if( mState == PRESSED  )
268         {
269           if( GetSnapToMarks() )
270           {
271             float value = MapBounds( SnapToMark( MapPercentage( gesture.position ) ), GetLowerBound(), GetUpperBound() );
272             SetValue( value );
273             DisplayPopup( value );
274           }
275           mSlidingFinishedSignal.Emit( Toolkit::Slider::DownCast( Self() ), GetValue() );
276         }
277
278         mState = NORMAL;
279         break;
280       }
281       default:
282       {
283         break;
284       }
285     }
286   }
287 }
288
289 float Slider::HitSpaceToDomain( float x )
290 {
291   float halfRegionWidth = GetHitRegion().x * 0.5f;
292   float halfDomainWidth = ( mDomain.to.x - mDomain.from.x ) * 0.5f;
293   float endDiff = halfRegionWidth - halfDomainWidth;
294
295   return x - endDiff;
296 }
297
298 float Slider::MapPercentage( const Vector2& point )
299 {
300   return Clamp( ( HitSpaceToDomain( point.x ) - mDomain.from.x ) / ( mDomain.to.x - mDomain.from.x ), 0.0f, 1.0f );
301 }
302
303 float Slider::MapValuePercentage( float value )
304 {
305   return ( value - GetLowerBound() ) / ( GetUpperBound() - GetLowerBound() );
306 }
307
308 float Slider::MapBounds( float percent, float lowerBound, float upperBound )
309 {
310   return lowerBound + percent * ( upperBound - lowerBound );
311 }
312
313 Slider::Domain Slider::CalcDomain( const Vector2& currentSize )
314 {
315    return Domain( Vector2( 0.0f, 0.0f ), currentSize );
316 }
317
318 void Slider::DisplayValue( float value, bool raiseSignals )
319 {
320   float clampedValue = Clamp( value, GetLowerBound(), GetUpperBound() );
321
322   float percent = MapValuePercentage( clampedValue );
323
324   float x = mDomain.from.x + percent * ( mDomain.to.x - mDomain.from.x );
325
326   mHandle.SetX( x );
327
328   // Progress bar
329   if( mProgress )
330   {
331     mProgress.SetSize( x, GetTrackRegion().y );
332   }
333
334   // Signals
335   if( raiseSignals )
336   {
337     Toolkit::Slider self = Toolkit::Slider::DownCast( Self() );
338     mValueChangedSignal.Emit( self, clampedValue );
339
340     int markIndex;
341     if( MarkReached( percent, markIndex ) )
342     {
343       mMarkReachedSignal.Emit( self, markIndex );
344     }
345   }
346
347   if( mHandleValueTextLabel )
348   {
349     std::stringstream ss;
350     ss.precision( GetValuePrecision() );
351     ss << std::fixed << clampedValue;
352
353     std::string label = mHandleValueTextLabel.GetProperty<std::string>( Toolkit::TextLabel::Property::TEXT );
354     if( label.compare(ss.str()) )
355     {
356       mHandleValueTextLabel.SetProperty( Toolkit::TextLabel::Property::TEXT, ss.str() );
357     }
358   }
359 }
360
361 void Slider::SetMarks( const MarkList& marks )
362 {
363   mMarks = marks;
364 }
365
366 const Slider::MarkList& Slider::GetMarks() const
367 {
368   return mMarks;
369 }
370
371 void Slider::SetSnapToMarks( bool snap )
372 {
373   mSnapToMarks = snap;
374 }
375
376 bool Slider::GetSnapToMarks() const
377 {
378   return mSnapToMarks;
379 }
380
381 Actor Slider::CreateHitRegion()
382 {
383   Actor hitRegion = Actor::New();
384   hitRegion.SetParentOrigin( ParentOrigin::CENTER );
385   hitRegion.SetAnchorPoint( AnchorPoint::CENTER );
386   hitRegion.TouchSignal().Connect( this, &Slider::OnTouch );
387
388   return hitRegion;
389 }
390
391 Toolkit::ImageView Slider::CreateTrack()
392 {
393   Toolkit::ImageView track = Toolkit::ImageView::New();
394   track.SetName("SliderTrack");
395   track.SetParentOrigin( ParentOrigin::CENTER );
396   track.SetAnchorPoint( AnchorPoint::CENTER );
397   return track;
398 }
399
400 void Slider::SetTrackVisual( const std::string& filename )
401 {
402   if( mHandle && ( filename.size() > 0 ) )
403   {
404     mTrack.SetImage( filename );
405     mTrackVisual = filename;
406   }
407 }
408
409 void Slider::SetTrackVisual( Property::Map map )
410 {
411   Property::Value* imageValue = map.Find( "url" );
412   if( imageValue )
413   {
414     mTrackVisual.clear();
415     std::string filename;
416     if( imageValue->Get( filename ) )
417     {
418       if( mTrack && ( filename.size() > 0 ) )
419       {
420         mTrack.SetImage( filename );
421         mTrackMap = map;
422       }
423     }
424   }
425
426   Property::Value* sizeValue = map.Find( "size" );
427   if( sizeValue )
428   {
429     Vector2 size;
430     if( sizeValue->Get( size ) )
431     {
432       mTrackRegion = size;
433       if( mTrack )
434       {
435         mTrack.SetSize( mTrackRegion );
436       }
437
438     ResizeProgressRegion( Vector2( 0.0f, mTrackRegion.y ) );
439
440     mDomain = CalcDomain( mTrackRegion );
441
442     // Set the progress bar to correct width
443     DisplayValue( GetValue(), false );
444     }
445   }
446 }
447
448 std::string Slider::GetTrackVisual()
449 {
450   return mTrackVisual;
451 }
452
453 Toolkit::ImageView Slider::CreateProgress()
454 {
455   Toolkit::ImageView progress = Toolkit::ImageView::New();
456   progress.SetName("SliderProgress");
457   progress.SetParentOrigin( ParentOrigin::CENTER_LEFT );
458   progress.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
459
460   return progress;
461 }
462
463 void Slider::SetProgressVisual( const std::string& filename )
464 {
465   if( mProgress && ( filename.size() > 0 ) )
466   {
467     mProgress.SetImage( filename );
468     mProgressVisual = filename;
469   }
470 }
471
472 void Slider::SetProgressVisual( Property::Map map )
473 {
474   Property::Value* imageValue = map.Find( "url" );
475   if( imageValue )
476   {
477     mProgressVisual.clear();
478     std::string filename;
479     if( imageValue->Get( filename ) )
480     {
481       if( mProgress && ( filename.size() > 0 ) )
482       {
483         mProgress.SetImage( filename );
484         mProgressMap = map;
485       }
486     }
487   }
488 }
489
490 std::string Slider::GetProgressVisual()
491 {
492   return mProgressVisual;
493 }
494
495 void Slider::SetPopupVisual( const std::string& filename )
496 {
497   mPopupVisual = filename;
498 }
499
500 void Slider::SetPopupVisual( Property::Map map )
501 {
502   Property::Value* imageValue = map.Find( "url" );
503   if( imageValue )
504   {
505     mPopupVisual.clear();
506     std::string filename;
507     if( imageValue->Get( filename ) )
508     {
509       if( mPopup && ( filename.size() > 0 ) )
510       {
511         mPopup.SetImage( filename );
512         mPopupMap = map;
513       }
514     }
515   }
516 }
517
518 std::string Slider::GetPopupVisual()
519 {
520   return mPopupVisual;
521 }
522
523 void Slider::CreatePopupImage( const std::string& filename )
524 {
525   if( mPopup && ( filename.size() > 0 ) )
526   {
527     Image image = ResourceImage::New( filename );
528     mPopup.SetImage( image );
529   }
530 }
531
532 void Slider::SetPopupArrowVisual( const std::string& filename )
533 {
534   mPopupArrowVisual = filename;
535 }
536
537 void Slider::SetPopupArrowVisual( Property::Map map )
538 {
539   Property::Value* imageValue = map.Find( "url" );
540   if( imageValue )
541   {
542     mPopupArrowVisual.clear();
543     std::string filename;
544     if( imageValue->Get( filename ) )
545     {
546       if( mPopupArrow && ( filename.size() > 0 ) )
547       {
548         mPopupArrow.SetImage( filename );
549         mPopupArrowMap = map;
550       }
551     }
552   }
553 }
554
555 std::string Slider::GetPopupArrowVisual()
556 {
557   return mPopupArrowVisual;
558 }
559
560 void Slider::CreatePopupArrowImage( const std::string& filename )
561 {
562   if( mPopupArrow && ( filename.size() > 0 ) )
563   {
564     Image image = ResourceImage::New( filename );
565     mPopupArrow.SetImage( image );
566   }
567 }
568
569 void Slider::ResizeProgressRegion( const Vector2& region )
570 {
571   if( mProgress )
572   {
573     mProgress.SetSize( region );
574   }
575 }
576
577 Toolkit::ImageView Slider::CreateHandle()
578 {
579   Toolkit::ImageView handle = Toolkit::ImageView::New();
580   handle.SetName("SliderHandle");
581   handle.SetParentOrigin( ParentOrigin::CENTER_LEFT );
582   handle.SetAnchorPoint( AnchorPoint::CENTER );
583
584   return handle;
585 }
586
587 Toolkit::ImageView Slider::CreatePopupArrow()
588 {
589   Toolkit::ImageView arrow = Toolkit::ImageView::New();
590   arrow.SetStyleName("SliderPopupArrow");
591   arrow.SetName("SliderPopupArrow");
592   arrow.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
593   arrow.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
594
595   return arrow;
596 }
597
598 Toolkit::TextLabel Slider::CreatePopupText()
599 {
600   Toolkit::TextLabel textLabel = Toolkit::TextLabel::New();
601   textLabel.SetName( "SliderPopupTextLabel" );
602   textLabel.SetStyleName( "SliderPopupTextLabel" );
603   textLabel.SetParentOrigin( ParentOrigin::CENTER );
604   textLabel.SetAnchorPoint( AnchorPoint::CENTER );
605   textLabel.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
606   textLabel.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
607   textLabel.SetProperty( Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
608   textLabel.SetPadding( Padding( POPUP_TEXT_PADDING, POPUP_TEXT_PADDING, 0.0f, 0.0f ) );
609   return textLabel;
610 }
611
612 Toolkit::ImageView Slider::CreatePopup()
613 {
614   Toolkit::ImageView popup = Toolkit::ImageView::New();
615   popup.SetName( "SliderPopup" );
616   popup.SetParentOrigin( ParentOrigin::TOP_CENTER );
617   popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
618   popup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::WIDTH );
619
620   mValueTextLabel = CreatePopupText();
621   popup.Add( mValueTextLabel );
622
623   return popup;
624 }
625
626 void Slider::SetHandleVisual( const std::string& filename )
627 {
628   if( mHandle && ( filename.size() > 0 ) )
629   {
630     mHandle.SetImage( filename );
631     mHandleVisual = filename;
632   }
633 }
634
635 void Slider::SetHandleVisual( Property::Map map )
636 {
637   Property::Value* imageValue = map.Find( "url" );
638   if( imageValue )
639   {
640     mHandleVisual.clear();
641     std::string filename;
642     if( imageValue->Get( filename ) )
643     {
644       if( mHandle && ( filename.size() > 0 ) )
645       {
646         mHandle.SetImage( filename );
647         mHandleMap = map;
648       }
649     }
650   }
651
652   Property::Value* sizeValue = map.Find( "size" );
653   if( sizeValue )
654   {
655     Vector2 size;
656     if( sizeValue->Get( size ) )
657     {
658       mHandleSize = size;
659       ResizeHandleSize( mHandleSize );
660
661       Vector2 hitRegion = GetHitRegion();
662       hitRegion.x += mHandleSize.x;
663       SetHitRegion( hitRegion );
664     }
665   }
666 }
667
668 std::string Slider::GetHandleVisual()
669 {
670   return mHandleVisual;
671 }
672
673 void Slider::ResizeHandleSize( const Vector2& size )
674 {
675   if( mHandle )
676   {
677     mHandle.SetSize( size );
678   }
679 }
680
681 void Slider::CreateHandleValueDisplay()
682 {
683   if( mHandle && !mHandleValueTextLabel )
684   {
685     mHandleValueTextLabel = Toolkit::TextLabel::New();
686     mHandleValueTextLabel.SetName("SliderHandleTextLabel");
687     mHandleValueTextLabel.SetStyleName("SliderHandleTextLabel");
688     mHandleValueTextLabel.SetParentOrigin( ParentOrigin::CENTER );
689     mHandleValueTextLabel.SetAnchorPoint( AnchorPoint::CENTER );
690     mHandleValueTextLabel.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
691     mHandleValueTextLabel.SetProperty( Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
692     mHandle.Add( mHandleValueTextLabel );
693   }
694 }
695
696 void Slider::DestroyHandleValueDisplay()
697 {
698   UnparentAndReset(mHandleValueTextLabel);
699 }
700
701 Actor Slider::CreateValueDisplay()
702 {
703   Actor popup = Actor::New();
704   popup.SetParentOrigin( ParentOrigin::TOP_CENTER );
705   popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
706
707   mPopupArrow = CreatePopupArrow();
708   popup.Add( mPopupArrow );
709
710   mPopup = CreatePopup();
711   mPopup.SetSize( 0.0f, VALUE_POPUP_HEIGHT );
712   mPopupArrow.Add( mPopup );
713
714   return popup;
715 }
716
717 Toolkit::Slider::ValueChangedSignalType& Slider::ValueChangedSignal()
718 {
719   return mValueChangedSignal;
720 }
721
722 Toolkit::Slider::ValueChangedSignalType& Slider::SlidingFinishedSignal()
723 {
724   return mSlidingFinishedSignal;
725 }
726
727 Toolkit::Slider::MarkReachedSignalType& Slider::MarkReachedSignal()
728 {
729   return mMarkReachedSignal;
730 }
731
732 void Slider::UpdateSkin()
733 {
734   switch( mState )
735   {
736     case NORMAL:
737     {
738       mTrack.SetColor( Color::WHITE );
739       mHandle.SetColor( Color::WHITE );
740       mProgress.SetColor( Color::WHITE );
741       break;
742     }
743     case DISABLED:
744     {
745       Vector4 disabledColor = GetDisabledColor();
746       mTrack.SetColor( disabledColor );
747       mHandle.SetColor( disabledColor );
748       mProgress.SetColor( disabledColor );
749       break;
750     }
751     case PRESSED:
752     {
753       break;
754     }
755     case FOCUSED:
756     {
757       break;
758     }
759   }
760 }
761
762 void Slider::CreateChildren()
763 {
764   Actor self = Self();
765
766   // Hit region
767   mHitArea = CreateHitRegion();
768   mPanDetector = PanGestureDetector::New();
769   mPanDetector.Attach( mHitArea );
770   mPanDetector.DetectedSignal().Connect( this, &Slider::OnPan );
771   self.Add( mHitArea );
772
773   // Track
774   mTrack = CreateTrack();
775   self.Add( mTrack );
776
777   // Progress bar
778   mProgress = CreateProgress();
779   mTrack.Add( mProgress );
780
781   // Handle
782   mHandle = CreateHandle();
783   mProgress.Add( mHandle );
784 }
785
786 void Slider::SetHitRegion( const Vector2& size )
787 {
788   mHitRegion = size;
789
790   if( mHitArea )
791   {
792     mHitArea.SetSize( mHitRegion );
793   }
794 }
795
796 const Vector2& Slider::GetHitRegion() const
797 {
798   return mHitRegion;
799 }
800
801 void Slider::AddPopup()
802 {
803   if( !mValueDisplay )
804   {
805     mValueDisplay = CreateValueDisplay();
806     mValueDisplay.SetVisible( false );
807     mHandle.Add( mValueDisplay );
808
809     CreatePopupImage( GetPopupVisual() );
810     CreatePopupArrowImage( GetPopupArrowVisual() );
811
812     mValueTimer = Timer::New( VALUE_VIEW_SHOW_DURATION );
813     mValueTimer.TickSignal().Connect( this, &Slider::HideValueView );
814   }
815 }
816
817 void Slider::RemovePopup()
818 {
819   if( mValueDisplay )
820   {
821     mPopup.Unparent();
822     mPopup.Reset();
823
824     mPopupArrow.Unparent();
825     mPopupArrow.Reset();
826
827     mValueDisplay.Unparent();
828     mValueDisplay.Reset();
829
830     mValueTimer.TickSignal().Disconnect( this, &Slider::HideValueView );
831     mValueTimer.Reset();
832   }
833 }
834
835
836 float Slider::MarkFilter( float value )
837 {
838   const float MARK_TOLERANCE = GetMarkTolerance();
839
840   float mark;
841   for( MarkList::SizeType i = 0; i < mMarks.Count(); ++i)
842   {
843     const Property::Value& propertyValue = mMarks[i];
844     propertyValue.Get( mark );
845     mark = MapValuePercentage( mark );
846
847     // If close to a mark, return the mark
848     if( fabsf( mark - value ) < MARK_TOLERANCE )
849     {
850       return mark;
851     }
852   }
853
854   return value;
855 }
856
857 float Slider::SnapToMark( float value )
858 {
859   float closestMark = value;
860   float closestDist = std::numeric_limits<float>::max();
861
862   float mark;
863   for( MarkList::SizeType  i = 0; i < mMarks.Count(); ++i)
864   {
865     const Property::Value& propertyValue = mMarks[i];
866     propertyValue.Get( mark );
867     mark = MapValuePercentage( mark );
868
869     float dist = fabsf( mark - value );
870     if( dist < closestDist )
871     {
872       closestDist = dist;
873       closestMark = mark;
874     }
875   }
876
877   return closestMark;
878 }
879
880 bool Slider::MarkReached( float value, int& outIndex )
881 {
882   const float MARK_TOLERANCE = GetMarkTolerance();
883
884   // Binary search
885   int head = 0,
886       tail = mMarks.Size() - 1;
887   int current;
888   float mark;
889
890   while( head <= tail )
891   {
892     current = head + ( tail - head ) / 2;
893
894     const Property::Value& propertyValue = mMarks[ current ];
895     propertyValue.Get( mark );
896     mark = MapValuePercentage( mark );
897
898     if( fabsf( mark - value ) < MARK_TOLERANCE )
899     {
900       outIndex = current;
901       return true;
902     }
903
904     if( value < mark )
905     {
906       tail = current - 1;
907     }
908     else
909     {
910       head = current + 1;
911     }
912   }
913
914   return false;
915 }
916
917 bool Slider::HideValueView()
918 {
919   if( mValueDisplay )
920   {
921     mValueDisplay.SetVisible( false );
922   }
923
924   return false;
925 }
926
927 void Slider::SetLowerBound( float bound )
928 {
929   mLowerBound = bound;
930   DisplayValue( GetValue(), false );
931 }
932
933 float Slider::GetLowerBound() const
934 {
935   return mLowerBound;
936 }
937
938 void Slider::SetUpperBound( float bound )
939 {
940   mUpperBound = bound;
941   DisplayValue( GetValue(), false );
942 }
943
944 float Slider::GetUpperBound() const
945 {
946   return mUpperBound;
947 }
948
949 void Slider::SetValue( float value )
950 {
951   mValue = value;
952   DisplayValue( mValue, true );
953 }
954
955 float Slider::GetValue() const
956 {
957   return mValue;
958 }
959
960 void Slider::SetTrackRegion( const Vector2& region )
961 {
962   mTrackRegion = region;
963
964   if( mTrack )
965   {
966     mTrack.SetSize( mTrackRegion );
967   }
968
969   ResizeProgressRegion( Vector2( 0.0f, mTrackRegion.y ) );
970
971   mDomain = CalcDomain( mTrackRegion );
972
973   DisplayValue( GetValue(), false );  // Set the progress bar to correct width
974 }
975
976 const Vector2& Slider::GetTrackRegion() const
977 {
978   return mTrackRegion;
979 }
980
981 void Slider::SetHandleSize( const Vector2& size )
982 {
983   mHandleSize = size;
984
985   ResizeHandleSize( mHandleSize );
986
987   Vector2 hitRegion = GetHitRegion();
988   hitRegion.x += mHandleSize.x;
989   SetHitRegion( hitRegion );
990 }
991
992 const Vector2& Slider::GetHandleSize() const
993 {
994   return mHandleSize;
995 }
996
997 void Slider::SetDisabledColor( const Vector4& color )
998 {
999   mDisabledColor = color;
1000
1001   UpdateSkin();
1002 }
1003
1004 Vector4 Slider::GetDisabledColor() const
1005 {
1006   return mDisabledColor;
1007 }
1008
1009 void Slider::SetValuePrecision( int precision )
1010 {
1011   mValuePrecision = precision;
1012 }
1013
1014 int Slider::GetValuePrecision() const
1015 {
1016   return mValuePrecision;
1017 }
1018
1019 void Slider::SetShowPopup( bool showPopup )
1020 {
1021   mShowPopup = showPopup;
1022
1023   // Value display
1024   if( mShowPopup )
1025   {
1026     AddPopup();
1027   }
1028   else
1029   {
1030     RemovePopup();
1031   }
1032 }
1033
1034 bool Slider::GetShowPopup() const
1035 {
1036   return mShowPopup;
1037 }
1038
1039 void Slider::SetShowValue( bool showValue )
1040 {
1041   mShowValue = showValue;
1042
1043   if( mShowValue )
1044   {
1045     CreateHandleValueDisplay();
1046   }
1047   else
1048   {
1049     DestroyHandleValueDisplay();
1050   }
1051 }
1052
1053 bool Slider::GetShowValue() const
1054 {
1055   return mShowValue;
1056 }
1057
1058 void Slider::SetEnabled( bool enabled )
1059 {
1060   if( enabled )
1061   {
1062     mState = NORMAL;
1063   }
1064   else
1065   {
1066     mState = DISABLED;
1067   }
1068
1069   UpdateSkin();
1070 }
1071
1072 bool Slider::IsEnabled() const
1073 {
1074   return mState != DISABLED;
1075 }
1076
1077 void Slider::SetMarkTolerance( float tolerance )
1078 {
1079   mMarkTolerance = tolerance;
1080 }
1081
1082 float Slider::GetMarkTolerance() const
1083 {
1084   return mMarkTolerance;
1085 }
1086
1087 // Static class method to support script connecting signals
1088 bool Slider::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1089 {
1090   Dali::BaseHandle handle( object );
1091
1092   bool connected = true;
1093   Toolkit::Slider slider = Toolkit::Slider::DownCast( handle );
1094
1095   if( 0 == strcmp( signalName.c_str(), SIGNAL_VALUE_CHANGED ) )
1096   {
1097     slider.ValueChangedSignal().Connect( tracker, functor );
1098   }
1099   else if( 0 == strcmp( signalName.c_str(), SIGNAL_MARK ) )
1100   {
1101     slider.MarkReachedSignal().Connect( tracker, functor );
1102   }
1103   else
1104   {
1105     // signalName does not match any signal
1106     connected = false;
1107   }
1108
1109   return connected;
1110 }
1111
1112 void Slider::DisplayPopup( float value )
1113 {
1114   // Value displayDoConnectSignal
1115   if( mValueTextLabel )
1116   {
1117     std::stringstream ss;
1118     ss.precision( GetValuePrecision() );
1119     ss << std::fixed << value;
1120     mValueTextLabel.SetProperty( Toolkit::TextLabel::Property::TEXT, ss.str() );
1121
1122     if( mValueDisplay )
1123     {
1124       mValueDisplay.SetVisible( true );
1125
1126       mValueTimer.SetInterval( VALUE_VIEW_SHOW_DURATION );
1127     }
1128   }
1129 }
1130
1131 void Slider::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
1132 {
1133   Toolkit::Slider slider = Toolkit::Slider::DownCast( Dali::BaseHandle( object ) );
1134
1135   if ( slider )
1136   {
1137     Slider& sliderImpl( GetImpl( slider ) );
1138
1139     switch ( propertyIndex )
1140     {
1141       case Toolkit::Slider::Property::LOWER_BOUND:
1142       {
1143         sliderImpl.SetLowerBound( value.Get< float >() );
1144         break;
1145       }
1146
1147       case Toolkit::Slider::Property::UPPER_BOUND:
1148       {
1149         sliderImpl.SetUpperBound( value.Get< float >() );
1150         break;
1151       }
1152
1153       case Toolkit::Slider::Property::VALUE:
1154       {
1155         sliderImpl.SetValue( value.Get< float >() );
1156         break;
1157       }
1158
1159       case Toolkit::Slider::Property::TRACK_VISUAL:
1160       {
1161         Property::Map map;
1162         if( value.Get( map ) )
1163         {
1164           sliderImpl.SetTrackVisual( map );
1165         }
1166         break;
1167       }
1168
1169       case Toolkit::Slider::Property::HANDLE_VISUAL:
1170       {
1171         Property::Map map;
1172         if( value.Get( map ) )
1173         {
1174           sliderImpl.SetHandleVisual( map );
1175         }
1176         break;
1177       }
1178
1179       case Toolkit::Slider::Property::PROGRESS_VISUAL:
1180       {
1181         Property::Map map;
1182         if( value.Get( map ) )
1183         {
1184           sliderImpl.SetProgressVisual( map );
1185         }
1186         break;
1187       }
1188
1189       case Toolkit::Slider::Property::POPUP_VISUAL:
1190       {
1191         std::string imageUrl;
1192         if( value.Get( imageUrl ) )
1193         {
1194           sliderImpl.SetPopupVisual( imageUrl );
1195         }
1196
1197         // If it is not a string, then get a Property::Map from the property if possible.
1198         Property::Map map;
1199         if( value.Get( map ) )
1200         {
1201           sliderImpl.SetPopupVisual( map );
1202         }
1203
1204         break;
1205       }
1206
1207       case Toolkit::Slider::Property::POPUP_ARROW_VISUAL:
1208       {
1209         Property::Map map;
1210         if( value.Get( map ) )
1211         {
1212           sliderImpl.SetPopupArrowVisual( map );
1213         }
1214         break;
1215       }
1216
1217       case Toolkit::Slider::Property::DISABLED_COLOR:
1218       {
1219         sliderImpl.SetDisabledColor( value.Get< Vector4 >() );
1220         break;
1221       }
1222
1223       case Toolkit::Slider::Property::VALUE_PRECISION:
1224       {
1225         sliderImpl.SetValuePrecision( value.Get< int >() );
1226         break;
1227       }
1228
1229       case Toolkit::Slider::Property::SHOW_POPUP:
1230       {
1231         sliderImpl.SetShowPopup( value.Get< bool >() );
1232         break;
1233       }
1234
1235       case Toolkit::Slider::Property::SHOW_VALUE:
1236       {
1237         sliderImpl.SetShowValue( value.Get< bool >() );
1238         break;
1239       }
1240
1241       case Toolkit::Slider::Property::MARKS:
1242       {
1243         sliderImpl.SetMarks( value.Get< Property::Array >() );
1244         break;
1245       }
1246
1247       case Toolkit::Slider::Property::SNAP_TO_MARKS:
1248       {
1249         sliderImpl.SetSnapToMarks( value.Get< bool >() );
1250         break;
1251       }
1252
1253       case Toolkit::Slider::Property::MARK_TOLERANCE:
1254       {
1255         sliderImpl.SetMarkTolerance( value.Get< float >() );
1256         break;
1257       }
1258     }
1259   }
1260 }
1261
1262 Property::Value Slider::GetProperty( BaseObject* object, Property::Index propertyIndex )
1263 {
1264   Property::Value value;
1265
1266   Toolkit::Slider slider = Toolkit::Slider::DownCast( Dali::BaseHandle( object ) );
1267
1268   if ( slider )
1269   {
1270     Slider& sliderImpl( GetImpl( slider ) );
1271
1272     switch ( propertyIndex )
1273     {
1274       case Toolkit::Slider::Property::LOWER_BOUND:
1275       {
1276         value = sliderImpl.GetLowerBound();
1277         break;
1278       }
1279
1280       case Toolkit::Slider::Property::UPPER_BOUND:
1281       {
1282         value = sliderImpl.GetUpperBound();
1283         break;
1284       }
1285
1286       case Toolkit::Slider::Property::VALUE:
1287       {
1288         value = sliderImpl.GetValue();
1289         break;
1290       }
1291
1292       case Toolkit::Slider::Property::TRACK_VISUAL:
1293       {
1294         if( !sliderImpl.mTrackVisual.empty() )
1295         {
1296           value = sliderImpl.GetTrackVisual();
1297         }
1298         else if( !sliderImpl.mTrackMap.Empty() )
1299         {
1300           value = sliderImpl.mTrackMap;
1301         }
1302         break;
1303       }
1304
1305       case Toolkit::Slider::Property::HANDLE_VISUAL:
1306       {
1307         if( !sliderImpl.mHandleVisual.empty() )
1308         {
1309           value = sliderImpl.GetHandleVisual();
1310         }
1311         else if( !sliderImpl.mHandleMap.Empty() )
1312         {
1313           value = sliderImpl.mHandleMap;
1314         }
1315         break;
1316       }
1317
1318       case Toolkit::Slider::Property::PROGRESS_VISUAL:
1319       {
1320         if( !sliderImpl.mProgressVisual.empty() )
1321         {
1322           value = sliderImpl.GetProgressVisual();
1323         }
1324         else if( !sliderImpl.mProgressMap.Empty() )
1325         {
1326           value = sliderImpl.mProgressMap;
1327         }
1328         break;
1329       }
1330
1331       case Toolkit::Slider::Property::POPUP_VISUAL:
1332       {
1333         if( !sliderImpl.mPopupVisual.empty() )
1334         {
1335           value = sliderImpl.GetPopupVisual();
1336         }
1337         else if( !sliderImpl.mPopupMap.Empty() )
1338         {
1339           value = sliderImpl.mPopupMap;
1340         }
1341         break;
1342       }
1343
1344       case Toolkit::Slider::Property::POPUP_ARROW_VISUAL:
1345       {
1346         if( !sliderImpl.mPopupArrowVisual.empty() )
1347         {
1348           value = sliderImpl.GetPopupArrowVisual();
1349         }
1350         else if( !sliderImpl.mPopupArrowMap.Empty() )
1351         {
1352           value = sliderImpl.mPopupArrowMap;
1353         }
1354         break;
1355       }
1356
1357       case Toolkit::Slider::Property::DISABLED_COLOR:
1358       {
1359         value = sliderImpl.GetDisabledColor();
1360         break;
1361       }
1362
1363       case Toolkit::Slider::Property::VALUE_PRECISION:
1364       {
1365         value = sliderImpl.GetValuePrecision();
1366         break;
1367       }
1368
1369       case Toolkit::Slider::Property::SHOW_POPUP:
1370       {
1371         value = sliderImpl.GetShowPopup();
1372         break;
1373       }
1374
1375       case Toolkit::Slider::Property::SHOW_VALUE:
1376       {
1377         value = sliderImpl.GetShowValue();
1378         break;
1379       }
1380
1381       case Toolkit::Slider::Property::MARKS:
1382       {
1383         // TODO: Need to be able to return a PropertyArray
1384         // value = sliderImpl.GetMarks();
1385         break;
1386       }
1387
1388       case Toolkit::Slider::Property::SNAP_TO_MARKS:
1389       {
1390         value = sliderImpl.GetSnapToMarks();
1391         break;
1392       }
1393
1394       case Toolkit::Slider::Property::MARK_TOLERANCE:
1395       {
1396         value = sliderImpl.GetMarkTolerance();
1397         break;
1398       }
1399     }
1400   }
1401
1402   return value;
1403 }
1404
1405 } // namespace Internal
1406
1407 } // namespace Toolkit
1408
1409 } // namespace Dali