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