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