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