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