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