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