[dali_1.2.22] Merge branch '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/public-api/images/resource-image.h>
29
30 // INTERNAL INCLUDES
31 #include <dali-toolkit/public-api/controls/control-impl.h>
32 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
33
34 using namespace Dali;
35
36 namespace Dali
37 {
38
39 namespace Toolkit
40 {
41
42 namespace Internal
43 {
44
45 namespace // Unnamed namespace
46 {
47
48 BaseHandle Create()
49 {
50   return Dali::Toolkit::Slider::New();
51 }
52
53 // Setup properties, signals and actions using the type-registry.
54 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::Slider, Toolkit::Control, Create )
55
56 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "lowerBound",             FLOAT,    LOWER_BOUND            )
57 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "upperBound",             FLOAT,    UPPER_BOUND            )
58 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "value",                  FLOAT,    VALUE                  )
59 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "trackVisual",            MAP,      TRACK_VISUAL           )
60 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "handleVisual",           MAP,      HANDLE_VISUAL          )
61 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "progressVisual",         MAP,      PROGRESS_VISUAL        )
62 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "popupVisual",            MAP,      POPUP_VISUAL           )
63 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "popupArrowVisual",       MAP,      POPUP_ARROW_VISUAL     )
64 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "disabledColor",          VECTOR4,  DISABLED_COLOR         )
65 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "valuePrecision",         INTEGER,  VALUE_PRECISION        )
66 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "showPopup",              BOOLEAN,  SHOW_POPUP             )
67 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "showValue",              BOOLEAN,  SHOW_VALUE             )
68 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "marks",                  ARRAY,    MARKS                  )
69 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "snapToMarks",            BOOLEAN,  SNAP_TO_MARKS          )
70 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "markTolerance",          FLOAT,    MARK_TOLERANCE         )
71
72 DALI_SIGNAL_REGISTRATION(   Toolkit, Slider, "valueChanged",                     SIGNAL_VALUE_CHANGED   )
73 DALI_SIGNAL_REGISTRATION(   Toolkit, Slider, "mark",                             SIGNAL_MARK            )
74
75 DALI_TYPE_REGISTRATION_END()
76
77 const float MARK_SNAP_TOLERANCE = 0.05f; // 5% of slider width
78
79 const int VALUE_VIEW_SHOW_DURATION = 1000;  // millisec
80 const int VALUE_VIEW_SHOW_DURATION_LONG = 2000;  // millisec
81
82 const float VALUE_VERTICAL_OFFSET = 48.0f;
83
84 const float DEFAULT_WIDTH = 0.0f;
85 const float DEFAULT_HEIGHT = 27.0f;
86 const float DEFAULT_HIT_HEIGHT = 72.0f;
87 const float DEFAULT_HANDLE_HEIGHT = DEFAULT_HIT_HEIGHT;
88 const float POPUP_TEXT_PADDING = 10.0f;
89
90 const char* SKINNED_TRACK_VISUAL = DALI_IMAGE_DIR "slider-skin.9.png";
91 const char* SKINNED_HANDLE_VISUAL = DALI_IMAGE_DIR "slider-skin-handle.png";
92 const char* SKINNED_PROGRESS_VISUAL = DALI_IMAGE_DIR "slider-skin-progress.9.png";
93 const char* SKINNED_POPUP_VISUAL = DALI_IMAGE_DIR "slider-popup.9.png";
94 const char* SKINNED_POPUP_ARROW_VISUAL = DALI_IMAGE_DIR "slider-popup-arrow.png";
95
96 const Vector2 DEFAULT_HIT_REGION( DEFAULT_WIDTH, DEFAULT_HIT_HEIGHT );
97 const Vector2 DEFAULT_TRACK_REGION( DEFAULT_WIDTH, DEFAULT_HEIGHT );
98 const Vector2 DEFAULT_HANDLE_SIZE( DEFAULT_HANDLE_HEIGHT, DEFAULT_HANDLE_HEIGHT );
99
100 const Vector4 DEFAULT_DISABLED_COLOR( 0.5f, 0.5f, 0.5f, 1.0f );
101
102 const float VALUE_POPUP_MARGIN = 10.0f;
103 const float VALUE_POPUP_HEIGHT = 81.0f;
104 const float VALUE_POPUP_MIN_WIDTH = 54.0f;
105
106 const float DEFAULT_LOWER_BOUND = 0.0f;
107 const float DEFAULT_UPPER_BOUND = 1.0f;
108 const float DEFAULT_VALUE = 0.0f;
109 const int DEFAULT_VALUE_PRECISION = 0;
110 const bool DEFAULT_SHOW_POPUP = false;
111 const bool DEFAULT_SHOW_VALUE = true;
112 const bool DEFAULT_ENABLED = true;
113 const bool DEFAULT_SNAP_TO_MARKS = false;
114
115 } // Unnamed namespace
116
117 ///////////////////////////////////////////////////////////////////////////////////////////////////
118 // Slider
119 ///////////////////////////////////////////////////////////////////////////////////////////////////
120
121 Dali::Toolkit::Slider Slider::New()
122 {
123   // Create the implementation
124   SliderPtr slider( new Slider() );
125
126   // Pass ownership to CustomActor via derived handle
127   Dali::Toolkit::Slider handle( *slider );
128
129   // Second-phase init of the implementation
130   // This can only be done after the CustomActor connection has been made...
131   slider->Initialize();
132
133   return handle;
134 }
135
136 Slider::Slider()
137 : Control( ControlBehaviour( 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     Image image = ResourceImage::New( filename );
524     mPopup.SetImage( image );
525   }
526 }
527
528 void Slider::SetPopupArrowVisual( const std::string& filename )
529 {
530   mPopupArrowVisual = filename;
531 }
532
533 void Slider::SetPopupArrowVisual( Property::Map map )
534 {
535   Property::Value* imageValue = map.Find( "url" );
536   if( imageValue )
537   {
538     mPopupArrowVisual.clear();
539     std::string filename;
540     if( imageValue->Get( filename ) )
541     {
542       if( mPopupArrow && ( filename.size() > 0 ) )
543       {
544         mPopupArrow.SetImage( filename );
545         mPopupArrowMap = map;
546       }
547     }
548   }
549 }
550
551 std::string Slider::GetPopupArrowVisual()
552 {
553   return mPopupArrowVisual;
554 }
555
556 void Slider::CreatePopupArrowImage( const std::string& filename )
557 {
558   if( mPopupArrow && ( filename.size() > 0 ) )
559   {
560     Image image = ResourceImage::New( filename );
561     mPopupArrow.SetImage( image );
562   }
563 }
564
565 void Slider::ResizeProgressRegion( const Vector2& region )
566 {
567   if( mProgress )
568   {
569     mProgress.SetSize( region );
570   }
571 }
572
573 Toolkit::ImageView Slider::CreateHandle()
574 {
575   Toolkit::ImageView handle = Toolkit::ImageView::New();
576   handle.SetName("SliderHandle");
577   handle.SetParentOrigin( ParentOrigin::CENTER_LEFT );
578   handle.SetAnchorPoint( AnchorPoint::CENTER );
579
580   return handle;
581 }
582
583 Toolkit::ImageView Slider::CreatePopupArrow()
584 {
585   Toolkit::ImageView arrow = Toolkit::ImageView::New();
586   arrow.SetStyleName("SliderPopupArrow");
587   arrow.SetName("SliderPopupArrow");
588   arrow.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
589   arrow.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
590
591   return arrow;
592 }
593
594 Toolkit::TextLabel Slider::CreatePopupText()
595 {
596   Toolkit::TextLabel textLabel = Toolkit::TextLabel::New();
597   textLabel.SetName( "SliderPopupTextLabel" );
598   textLabel.SetStyleName( "SliderPopupTextLabel" );
599   textLabel.SetParentOrigin( ParentOrigin::CENTER );
600   textLabel.SetAnchorPoint( AnchorPoint::CENTER );
601   textLabel.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
602   textLabel.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
603   textLabel.SetProperty( Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
604   textLabel.SetPadding( Padding( POPUP_TEXT_PADDING, POPUP_TEXT_PADDING, 0.0f, 0.0f ) );
605   return textLabel;
606 }
607
608 Toolkit::ImageView Slider::CreatePopup()
609 {
610   Toolkit::ImageView popup = Toolkit::ImageView::New();
611   popup.SetName( "SliderPopup" );
612   popup.SetParentOrigin( ParentOrigin::TOP_CENTER );
613   popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
614   popup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::WIDTH );
615
616   mValueTextLabel = CreatePopupText();
617   popup.Add( mValueTextLabel );
618
619   return popup;
620 }
621
622 void Slider::SetHandleVisual( const std::string& filename )
623 {
624   if( mHandle && ( filename.size() > 0 ) )
625   {
626     mHandle.SetImage( filename );
627     mHandleVisual = filename;
628   }
629 }
630
631 void Slider::SetHandleVisual( Property::Map map )
632 {
633   Property::Value* imageValue = map.Find( "url" );
634   if( imageValue )
635   {
636     mHandleVisual.clear();
637     std::string filename;
638     if( imageValue->Get( filename ) )
639     {
640       if( mHandle && ( filename.size() > 0 ) )
641       {
642         mHandle.SetImage( filename );
643         mHandleMap = map;
644       }
645     }
646   }
647
648   Property::Value* sizeValue = map.Find( "size" );
649   if( sizeValue )
650   {
651     Vector2 size;
652     if( sizeValue->Get( size ) )
653     {
654       mHandleSize = size;
655       ResizeHandleSize( mHandleSize );
656
657       Vector2 hitRegion = GetHitRegion();
658       hitRegion.x += mHandleSize.x;
659       SetHitRegion( hitRegion );
660     }
661   }
662 }
663
664 std::string Slider::GetHandleVisual()
665 {
666   return mHandleVisual;
667 }
668
669 void Slider::ResizeHandleSize( const Vector2& size )
670 {
671   if( mHandle )
672   {
673     mHandle.SetSize( size );
674   }
675 }
676
677 void Slider::CreateHandleValueDisplay()
678 {
679   if( mHandle && !mHandleValueTextLabel )
680   {
681     mHandleValueTextLabel = Toolkit::TextLabel::New();
682     mHandleValueTextLabel.SetName("SliderHandleTextLabel");
683     mHandleValueTextLabel.SetStyleName("SliderHandleTextLabel");
684     mHandleValueTextLabel.SetParentOrigin( ParentOrigin::CENTER );
685     mHandleValueTextLabel.SetAnchorPoint( AnchorPoint::CENTER );
686     mHandleValueTextLabel.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
687     mHandleValueTextLabel.SetProperty( Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
688     mHandle.Add( mHandleValueTextLabel );
689   }
690 }
691
692 void Slider::DestroyHandleValueDisplay()
693 {
694   UnparentAndReset(mHandleValueTextLabel);
695 }
696
697 Actor Slider::CreateValueDisplay()
698 {
699   Actor popup = Actor::New();
700   popup.SetParentOrigin( ParentOrigin::TOP_CENTER );
701   popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
702
703   mPopupArrow = CreatePopupArrow();
704   popup.Add( mPopupArrow );
705
706   mPopup = CreatePopup();
707   mPopup.SetSize( 0.0f, VALUE_POPUP_HEIGHT );
708   mPopupArrow.Add( mPopup );
709
710   return popup;
711 }
712
713 Toolkit::Slider::ValueChangedSignalType& Slider::ValueChangedSignal()
714 {
715   return mValueChangedSignal;
716 }
717
718 Toolkit::Slider::ValueChangedSignalType& Slider::SlidingFinishedSignal()
719 {
720   return mSlidingFinishedSignal;
721 }
722
723 Toolkit::Slider::MarkReachedSignalType& Slider::MarkReachedSignal()
724 {
725   return mMarkReachedSignal;
726 }
727
728 void Slider::UpdateSkin()
729 {
730   switch( mState )
731   {
732     case NORMAL:
733     {
734       mTrack.SetColor( Color::WHITE );
735       mHandle.SetColor( Color::WHITE );
736       mProgress.SetColor( Color::WHITE );
737       break;
738     }
739     case DISABLED:
740     {
741       Vector4 disabledColor = GetDisabledColor();
742       mTrack.SetColor( disabledColor );
743       mHandle.SetColor( disabledColor );
744       mProgress.SetColor( disabledColor );
745       break;
746     }
747     case PRESSED:
748     {
749       break;
750     }
751     case FOCUSED:
752     {
753       break;
754     }
755   }
756 }
757
758 void Slider::CreateChildren()
759 {
760   Actor self = Self();
761
762   // Hit region
763   mHitArea = CreateHitRegion();
764   mPanDetector = PanGestureDetector::New();
765   mPanDetector.Attach( mHitArea );
766   mPanDetector.DetectedSignal().Connect( this, &Slider::OnPan );
767   self.Add( mHitArea );
768
769   // Track
770   mTrack = CreateTrack();
771   self.Add( mTrack );
772
773   // Progress bar
774   mProgress = CreateProgress();
775   mTrack.Add( mProgress );
776
777   // Handle
778   mHandle = CreateHandle();
779   mProgress.Add( mHandle );
780 }
781
782 void Slider::SetHitRegion( const Vector2& size )
783 {
784   mHitRegion = size;
785
786   if( mHitArea )
787   {
788     mHitArea.SetSize( mHitRegion );
789   }
790 }
791
792 const Vector2& Slider::GetHitRegion() const
793 {
794   return mHitRegion;
795 }
796
797 void Slider::AddPopup()
798 {
799   if( !mValueDisplay )
800   {
801     mValueDisplay = CreateValueDisplay();
802     mValueDisplay.SetVisible( false );
803     mHandle.Add( mValueDisplay );
804
805     CreatePopupImage( GetPopupVisual() );
806     CreatePopupArrowImage( GetPopupArrowVisual() );
807
808     mValueTimer = Timer::New( VALUE_VIEW_SHOW_DURATION );
809     mValueTimer.TickSignal().Connect( this, &Slider::HideValueView );
810   }
811 }
812
813 void Slider::RemovePopup()
814 {
815   if( mValueDisplay )
816   {
817     mPopup.Unparent();
818     mPopup.Reset();
819
820     mPopupArrow.Unparent();
821     mPopupArrow.Reset();
822
823     mValueDisplay.Unparent();
824     mValueDisplay.Reset();
825
826     mValueTimer.TickSignal().Disconnect( this, &Slider::HideValueView );
827     mValueTimer.Reset();
828   }
829 }
830
831
832 float Slider::MarkFilter( float value )
833 {
834   const float MARK_TOLERANCE = GetMarkTolerance();
835
836   float mark;
837   for( MarkList::SizeType i = 0; i < mMarks.Count(); ++i)
838   {
839     const Property::Value& propertyValue = mMarks[i];
840     propertyValue.Get( mark );
841     mark = MapValuePercentage( mark );
842
843     // If close to a mark, return the mark
844     if( fabsf( mark - value ) < MARK_TOLERANCE )
845     {
846       return mark;
847     }
848   }
849
850   return value;
851 }
852
853 float Slider::SnapToMark( float value )
854 {
855   float closestMark = value;
856   float closestDist = std::numeric_limits<float>::max();
857
858   float mark;
859   for( MarkList::SizeType  i = 0; i < mMarks.Count(); ++i)
860   {
861     const Property::Value& propertyValue = mMarks[i];
862     propertyValue.Get( mark );
863     mark = MapValuePercentage( mark );
864
865     float dist = fabsf( mark - value );
866     if( dist < closestDist )
867     {
868       closestDist = dist;
869       closestMark = mark;
870     }
871   }
872
873   return closestMark;
874 }
875
876 bool Slider::MarkReached( float value, int& outIndex )
877 {
878   const float MARK_TOLERANCE = GetMarkTolerance();
879
880   // Binary search
881   int head = 0,
882       tail = mMarks.Size() - 1;
883   int current;
884   float mark;
885
886   while( head <= tail )
887   {
888     current = head + ( tail - head ) / 2;
889
890     const Property::Value& propertyValue = mMarks[ current ];
891     propertyValue.Get( mark );
892     mark = MapValuePercentage( mark );
893
894     if( fabsf( mark - value ) < MARK_TOLERANCE )
895     {
896       outIndex = current;
897       return true;
898     }
899
900     if( value < mark )
901     {
902       tail = current - 1;
903     }
904     else
905     {
906       head = current + 1;
907     }
908   }
909
910   return false;
911 }
912
913 bool Slider::HideValueView()
914 {
915   if( mValueDisplay )
916   {
917     mValueDisplay.SetVisible( false );
918   }
919
920   return false;
921 }
922
923 void Slider::SetLowerBound( float bound )
924 {
925   mLowerBound = bound;
926   DisplayValue( GetValue(), false );
927 }
928
929 float Slider::GetLowerBound() const
930 {
931   return mLowerBound;
932 }
933
934 void Slider::SetUpperBound( float bound )
935 {
936   mUpperBound = bound;
937   DisplayValue( GetValue(), false );
938 }
939
940 float Slider::GetUpperBound() const
941 {
942   return mUpperBound;
943 }
944
945 void Slider::SetValue( float value )
946 {
947   mValue = value;
948   DisplayValue( mValue, true );
949 }
950
951 float Slider::GetValue() const
952 {
953   return mValue;
954 }
955
956 void Slider::SetTrackRegion( const Vector2& region )
957 {
958   mTrackRegion = region;
959
960   if( mTrack )
961   {
962     mTrack.SetSize( mTrackRegion );
963   }
964
965   ResizeProgressRegion( Vector2( 0.0f, mTrackRegion.y ) );
966
967   mDomain = CalcDomain( mTrackRegion );
968
969   DisplayValue( GetValue(), false );  // Set the progress bar to correct width
970 }
971
972 const Vector2& Slider::GetTrackRegion() const
973 {
974   return mTrackRegion;
975 }
976
977 void Slider::SetHandleSize( const Vector2& size )
978 {
979   mHandleSize = size;
980
981   ResizeHandleSize( mHandleSize );
982
983   Vector2 hitRegion = GetHitRegion();
984   hitRegion.x += mHandleSize.x;
985   SetHitRegion( hitRegion );
986 }
987
988 const Vector2& Slider::GetHandleSize() const
989 {
990   return mHandleSize;
991 }
992
993 void Slider::SetDisabledColor( const Vector4& color )
994 {
995   mDisabledColor = color;
996
997   UpdateSkin();
998 }
999
1000 Vector4 Slider::GetDisabledColor() const
1001 {
1002   return mDisabledColor;
1003 }
1004
1005 void Slider::SetValuePrecision( int precision )
1006 {
1007   mValuePrecision = precision;
1008 }
1009
1010 int Slider::GetValuePrecision() const
1011 {
1012   return mValuePrecision;
1013 }
1014
1015 void Slider::SetShowPopup( bool showPopup )
1016 {
1017   mShowPopup = showPopup;
1018
1019   // Value display
1020   if( mShowPopup )
1021   {
1022     AddPopup();
1023   }
1024   else
1025   {
1026     RemovePopup();
1027   }
1028 }
1029
1030 bool Slider::GetShowPopup() const
1031 {
1032   return mShowPopup;
1033 }
1034
1035 void Slider::SetShowValue( bool showValue )
1036 {
1037   mShowValue = showValue;
1038
1039   if( mShowValue )
1040   {
1041     CreateHandleValueDisplay();
1042   }
1043   else
1044   {
1045     DestroyHandleValueDisplay();
1046   }
1047 }
1048
1049 bool Slider::GetShowValue() const
1050 {
1051   return mShowValue;
1052 }
1053
1054 void Slider::SetEnabled( bool enabled )
1055 {
1056   if( enabled )
1057   {
1058     mState = NORMAL;
1059   }
1060   else
1061   {
1062     mState = DISABLED;
1063   }
1064
1065   UpdateSkin();
1066 }
1067
1068 bool Slider::IsEnabled() const
1069 {
1070   return mState != DISABLED;
1071 }
1072
1073 void Slider::SetMarkTolerance( float tolerance )
1074 {
1075   mMarkTolerance = tolerance;
1076 }
1077
1078 float Slider::GetMarkTolerance() const
1079 {
1080   return mMarkTolerance;
1081 }
1082
1083 // Static class method to support script connecting signals
1084 bool Slider::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1085 {
1086   Dali::BaseHandle handle( object );
1087
1088   bool connected = true;
1089   Toolkit::Slider slider = Toolkit::Slider::DownCast( handle );
1090
1091   if( 0 == strcmp( signalName.c_str(), SIGNAL_VALUE_CHANGED ) )
1092   {
1093     slider.ValueChangedSignal().Connect( tracker, functor );
1094   }
1095   else if( 0 == strcmp( signalName.c_str(), SIGNAL_MARK ) )
1096   {
1097     slider.MarkReachedSignal().Connect( tracker, functor );
1098   }
1099   else
1100   {
1101     // signalName does not match any signal
1102     connected = false;
1103   }
1104
1105   return connected;
1106 }
1107
1108 void Slider::DisplayPopup( float value )
1109 {
1110   // Value displayDoConnectSignal
1111   if( mValueTextLabel )
1112   {
1113     std::stringstream ss;
1114     ss.precision( GetValuePrecision() );
1115     ss << std::fixed << value;
1116     mValueTextLabel.SetProperty( Toolkit::TextLabel::Property::TEXT, ss.str() );
1117
1118     if( mValueDisplay )
1119     {
1120       mValueDisplay.SetVisible( true );
1121
1122       mValueTimer.SetInterval( VALUE_VIEW_SHOW_DURATION );
1123     }
1124   }
1125 }
1126
1127 void Slider::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
1128 {
1129   Toolkit::Slider slider = Toolkit::Slider::DownCast( Dali::BaseHandle( object ) );
1130
1131   if ( slider )
1132   {
1133     Slider& sliderImpl( GetImpl( slider ) );
1134
1135     switch ( propertyIndex )
1136     {
1137       case Toolkit::Slider::Property::LOWER_BOUND:
1138       {
1139         sliderImpl.SetLowerBound( value.Get< float >() );
1140         break;
1141       }
1142
1143       case Toolkit::Slider::Property::UPPER_BOUND:
1144       {
1145         sliderImpl.SetUpperBound( value.Get< float >() );
1146         break;
1147       }
1148
1149       case Toolkit::Slider::Property::VALUE:
1150       {
1151         sliderImpl.SetValue( value.Get< float >() );
1152         break;
1153       }
1154
1155       case Toolkit::Slider::Property::TRACK_VISUAL:
1156       {
1157         Property::Map map;
1158         if( value.Get( map ) )
1159         {
1160           sliderImpl.SetTrackVisual( map );
1161         }
1162         break;
1163       }
1164
1165       case Toolkit::Slider::Property::HANDLE_VISUAL:
1166       {
1167         Property::Map map;
1168         if( value.Get( map ) )
1169         {
1170           sliderImpl.SetHandleVisual( map );
1171         }
1172         break;
1173       }
1174
1175       case Toolkit::Slider::Property::PROGRESS_VISUAL:
1176       {
1177         Property::Map map;
1178         if( value.Get( map ) )
1179         {
1180           sliderImpl.SetProgressVisual( map );
1181         }
1182         break;
1183       }
1184
1185       case Toolkit::Slider::Property::POPUP_VISUAL:
1186       {
1187         std::string imageUrl;
1188         if( value.Get( imageUrl ) )
1189         {
1190           sliderImpl.SetPopupVisual( imageUrl );
1191         }
1192
1193         // If it is not a string, then get a Property::Map from the property if possible.
1194         Property::Map map;
1195         if( value.Get( map ) )
1196         {
1197           sliderImpl.SetPopupVisual( map );
1198         }
1199
1200         break;
1201       }
1202
1203       case Toolkit::Slider::Property::POPUP_ARROW_VISUAL:
1204       {
1205         Property::Map map;
1206         if( value.Get( map ) )
1207         {
1208           sliderImpl.SetPopupArrowVisual( map );
1209         }
1210         break;
1211       }
1212
1213       case Toolkit::Slider::Property::DISABLED_COLOR:
1214       {
1215         sliderImpl.SetDisabledColor( value.Get< Vector4 >() );
1216         break;
1217       }
1218
1219       case Toolkit::Slider::Property::VALUE_PRECISION:
1220       {
1221         sliderImpl.SetValuePrecision( value.Get< int >() );
1222         break;
1223       }
1224
1225       case Toolkit::Slider::Property::SHOW_POPUP:
1226       {
1227         sliderImpl.SetShowPopup( value.Get< bool >() );
1228         break;
1229       }
1230
1231       case Toolkit::Slider::Property::SHOW_VALUE:
1232       {
1233         sliderImpl.SetShowValue( value.Get< bool >() );
1234         break;
1235       }
1236
1237       case Toolkit::Slider::Property::MARKS:
1238       {
1239         sliderImpl.SetMarks( value.Get< Property::Array >() );
1240         break;
1241       }
1242
1243       case Toolkit::Slider::Property::SNAP_TO_MARKS:
1244       {
1245         sliderImpl.SetSnapToMarks( value.Get< bool >() );
1246         break;
1247       }
1248
1249       case Toolkit::Slider::Property::MARK_TOLERANCE:
1250       {
1251         sliderImpl.SetMarkTolerance( value.Get< float >() );
1252         break;
1253       }
1254     }
1255   }
1256 }
1257
1258 Property::Value Slider::GetProperty( BaseObject* object, Property::Index propertyIndex )
1259 {
1260   Property::Value value;
1261
1262   Toolkit::Slider slider = Toolkit::Slider::DownCast( Dali::BaseHandle( object ) );
1263
1264   if ( slider )
1265   {
1266     Slider& sliderImpl( GetImpl( slider ) );
1267
1268     switch ( propertyIndex )
1269     {
1270       case Toolkit::Slider::Property::LOWER_BOUND:
1271       {
1272         value = sliderImpl.GetLowerBound();
1273         break;
1274       }
1275
1276       case Toolkit::Slider::Property::UPPER_BOUND:
1277       {
1278         value = sliderImpl.GetUpperBound();
1279         break;
1280       }
1281
1282       case Toolkit::Slider::Property::VALUE:
1283       {
1284         value = sliderImpl.GetValue();
1285         break;
1286       }
1287
1288       case Toolkit::Slider::Property::TRACK_VISUAL:
1289       {
1290         if( !sliderImpl.mTrackVisual.empty() )
1291         {
1292           value = sliderImpl.GetTrackVisual();
1293         }
1294         else if( !sliderImpl.mTrackMap.Empty() )
1295         {
1296           value = sliderImpl.mTrackMap;
1297         }
1298         break;
1299       }
1300
1301       case Toolkit::Slider::Property::HANDLE_VISUAL:
1302       {
1303         if( !sliderImpl.mHandleVisual.empty() )
1304         {
1305           value = sliderImpl.GetHandleVisual();
1306         }
1307         else if( !sliderImpl.mHandleMap.Empty() )
1308         {
1309           value = sliderImpl.mHandleMap;
1310         }
1311         break;
1312       }
1313
1314       case Toolkit::Slider::Property::PROGRESS_VISUAL:
1315       {
1316         if( !sliderImpl.mProgressVisual.empty() )
1317         {
1318           value = sliderImpl.GetProgressVisual();
1319         }
1320         else if( !sliderImpl.mProgressMap.Empty() )
1321         {
1322           value = sliderImpl.mProgressMap;
1323         }
1324         break;
1325       }
1326
1327       case Toolkit::Slider::Property::POPUP_VISUAL:
1328       {
1329         if( !sliderImpl.mPopupVisual.empty() )
1330         {
1331           value = sliderImpl.GetPopupVisual();
1332         }
1333         else if( !sliderImpl.mPopupMap.Empty() )
1334         {
1335           value = sliderImpl.mPopupMap;
1336         }
1337         break;
1338       }
1339
1340       case Toolkit::Slider::Property::POPUP_ARROW_VISUAL:
1341       {
1342         if( !sliderImpl.mPopupArrowVisual.empty() )
1343         {
1344           value = sliderImpl.GetPopupArrowVisual();
1345         }
1346         else if( !sliderImpl.mPopupArrowMap.Empty() )
1347         {
1348           value = sliderImpl.mPopupArrowMap;
1349         }
1350         break;
1351       }
1352
1353       case Toolkit::Slider::Property::DISABLED_COLOR:
1354       {
1355         value = sliderImpl.GetDisabledColor();
1356         break;
1357       }
1358
1359       case Toolkit::Slider::Property::VALUE_PRECISION:
1360       {
1361         value = sliderImpl.GetValuePrecision();
1362         break;
1363       }
1364
1365       case Toolkit::Slider::Property::SHOW_POPUP:
1366       {
1367         value = sliderImpl.GetShowPopup();
1368         break;
1369       }
1370
1371       case Toolkit::Slider::Property::SHOW_VALUE:
1372       {
1373         value = sliderImpl.GetShowValue();
1374         break;
1375       }
1376
1377       case Toolkit::Slider::Property::MARKS:
1378       {
1379         // TODO: Need to be able to return a PropertyArray
1380         // value = sliderImpl.GetMarks();
1381         break;
1382       }
1383
1384       case Toolkit::Slider::Property::SNAP_TO_MARKS:
1385       {
1386         value = sliderImpl.GetSnapToMarks();
1387         break;
1388       }
1389
1390       case Toolkit::Slider::Property::MARK_TOLERANCE:
1391       {
1392         value = sliderImpl.GetMarkTolerance();
1393         break;
1394       }
1395     }
1396   }
1397
1398   return value;
1399 }
1400
1401 } // namespace Internal
1402
1403 } // namespace Toolkit
1404
1405 } // namespace Dali