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