[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / slider / slider-impl.cpp
1 /*
2  * Copyright (c) 2024 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   // Connect to the touch signal
206   self.TouchedSignal().Connect(this, &Slider::OnTouch);
207
208   // Accessibility
209   self.SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::SLIDER);
210   self.SetProperty(DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE, true);
211 }
212
213 DevelControl::ControlAccessible* Slider::CreateAccessibleObject()
214 {
215   return new SliderAccessible(Self());
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 float Slider::MarkFilter(float value)
846 {
847   const float MARK_TOLERANCE = GetMarkTolerance();
848
849   float mark = SnapToMark(value);
850   if(fabsf(mark - value) < MARK_TOLERANCE)
851     return mark;
852
853   return value;
854 }
855
856 float Slider::SnapToMark(float value)
857 {
858   float closestMark = value;
859   float closestDist = std::numeric_limits<float>::max();
860
861   float mark;
862   for(MarkList::SizeType i = 0; i < mMarks.Count(); ++i)
863   {
864     const Property::Value& propertyValue = mMarks[i];
865     if(propertyValue.Get(mark))
866     {
867       mark = MapValuePercentage(mark);
868
869       float dist = fabsf(mark - value);
870       if(dist < closestDist)
871       {
872         closestDist = dist;
873         closestMark = mark;
874       }
875     }
876   }
877
878   return closestMark;
879 }
880
881 bool Slider::MarkReached(float value, int& outIndex)
882 {
883   const float MARK_TOLERANCE = GetMarkTolerance();
884
885   // Binary search
886   int head = 0,
887       tail = mMarks.Size() - 1;
888   int   current;
889   float mark;
890
891   while(head <= tail)
892   {
893     current = head + (tail - head) / 2;
894
895     const Property::Value& propertyValue = mMarks[current];
896     if(propertyValue.Get(mark))
897     {
898       mark = MapValuePercentage(mark);
899
900       if(fabsf(mark - value) < MARK_TOLERANCE)
901       {
902         outIndex = current;
903         return true;
904       }
905
906       if(value < mark)
907       {
908         tail = current - 1;
909       }
910       else
911       {
912         head = current + 1;
913       }
914     }
915   }
916
917   return false;
918 }
919
920 bool Slider::HideValueView()
921 {
922   if(mValueDisplay)
923   {
924     mValueDisplay.SetProperty(Actor::Property::VISIBLE, false);
925   }
926
927   return false;
928 }
929
930 void Slider::SetLowerBound(float bound)
931 {
932   mLowerBound = bound;
933   DisplayValue(GetValue(), false);
934 }
935
936 float Slider::GetLowerBound() const
937 {
938   return mLowerBound;
939 }
940
941 void Slider::SetUpperBound(float bound)
942 {
943   mUpperBound = bound;
944   DisplayValue(GetValue(), false);
945 }
946
947 float Slider::GetUpperBound() const
948 {
949   return mUpperBound;
950 }
951
952 void Slider::SetValue(float value)
953 {
954   mValue = value;
955   DisplayValue(mValue, true);
956   if(Self() == Dali::Accessibility::Accessible::GetCurrentlyHighlightedActor())
957   {
958     auto accessible = GetAccessibleObject();
959     if(DALI_LIKELY(accessible))
960     {
961       accessible->Emit(Dali::Accessibility::ObjectPropertyChangeEvent::VALUE);
962     }
963   }
964 }
965
966 float Slider::GetValue() const
967 {
968   return mValue;
969 }
970
971 void Slider::SetTrackRegion(const Vector2& region)
972 {
973   mTrackRegion = region;
974
975   if(mTrack)
976   {
977     mTrack.SetProperty(Actor::Property::SIZE, mTrackRegion);
978   }
979
980   ResizeProgressRegion(Vector2(0.0f, mTrackRegion.y));
981
982   mDomain = CalcDomain(mTrackRegion);
983
984   DisplayValue(GetValue(), false); // Set the progress bar to correct width
985 }
986
987 const Vector2& Slider::GetTrackRegion() const
988 {
989   return mTrackRegion;
990 }
991
992 void Slider::SetHandleSize(const Vector2& size)
993 {
994   mHandleSize = size;
995
996   ResizeHandleSize(mHandleSize);
997
998   Vector2 hitRegion = GetHitRegion();
999   hitRegion.x += mHandleSize.x;
1000   SetHitRegion(hitRegion);
1001 }
1002
1003 const Vector2& Slider::GetHandleSize() const
1004 {
1005   return mHandleSize;
1006 }
1007
1008 void Slider::SetDisabledColor(const Vector4& color)
1009 {
1010   mDisabledColor = color;
1011
1012   UpdateSkin();
1013 }
1014
1015 Vector4 Slider::GetDisabledColor() const
1016 {
1017   return mDisabledColor;
1018 }
1019
1020 void Slider::SetValuePrecision(int precision)
1021 {
1022   mValuePrecision = precision;
1023 }
1024
1025 int Slider::GetValuePrecision() const
1026 {
1027   return mValuePrecision;
1028 }
1029
1030 void Slider::SetShowPopup(bool showPopup)
1031 {
1032   mShowPopup = showPopup;
1033
1034   // Value display
1035   if(mShowPopup)
1036   {
1037     AddPopup();
1038   }
1039   else
1040   {
1041     RemovePopup();
1042   }
1043 }
1044
1045 bool Slider::GetShowPopup() const
1046 {
1047   return mShowPopup;
1048 }
1049
1050 void Slider::SetShowValue(bool showValue)
1051 {
1052   mShowValue = showValue;
1053
1054   if(mShowValue)
1055   {
1056     CreateHandleValueDisplay();
1057   }
1058   else
1059   {
1060     DestroyHandleValueDisplay();
1061   }
1062 }
1063
1064 bool Slider::GetShowValue() const
1065 {
1066   return mShowValue;
1067 }
1068
1069 void Slider::SetEnabled(bool enabled)
1070 {
1071   if(enabled)
1072   {
1073     mState = NORMAL;
1074   }
1075   else
1076   {
1077     mState = DISABLED;
1078   }
1079
1080   UpdateSkin();
1081 }
1082
1083 bool Slider::IsEnabled() const
1084 {
1085   return mState != DISABLED;
1086 }
1087
1088 void Slider::SetMarkTolerance(float tolerance)
1089 {
1090   mMarkTolerance = tolerance;
1091 }
1092
1093 float Slider::GetMarkTolerance() const
1094 {
1095   return mMarkTolerance;
1096 }
1097
1098 // Static class method to support script connecting signals
1099 bool Slider::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
1100 {
1101   Dali::BaseHandle handle(object);
1102
1103   bool            connected = true;
1104   Toolkit::Slider slider    = Toolkit::Slider::DownCast(handle);
1105
1106   if(0 == strcmp(signalName.c_str(), SIGNAL_VALUE_CHANGED))
1107   {
1108     slider.ValueChangedSignal().Connect(tracker, functor);
1109   }
1110   else if(0 == strcmp(signalName.c_str(), SIGNAL_MARK))
1111   {
1112     slider.MarkReachedSignal().Connect(tracker, functor);
1113   }
1114   else
1115   {
1116     // signalName does not match any signal
1117     connected = false;
1118   }
1119
1120   return connected;
1121 }
1122
1123 void Slider::DisplayPopup(float value)
1124 {
1125   // Value displayDoConnectSignal
1126   if(mValueTextLabel)
1127   {
1128     std::stringstream ss;
1129     ss.precision(GetValuePrecision());
1130     ss << std::fixed << value;
1131     mValueTextLabel.SetProperty(Toolkit::TextLabel::Property::TEXT, ss.str());
1132
1133     if(mValueDisplay)
1134     {
1135       mValueDisplay.SetProperty(Actor::Property::VISIBLE, true);
1136
1137       mValueTimer.SetInterval(VALUE_VIEW_SHOW_DURATION);
1138     }
1139   }
1140 }
1141
1142 void Slider::SetProperty(BaseObject* object, Property::Index propertyIndex, const Property::Value& value)
1143 {
1144   Toolkit::Slider slider = Toolkit::Slider::DownCast(Dali::BaseHandle(object));
1145
1146   if(slider)
1147   {
1148     Slider& sliderImpl(GetImpl(slider));
1149
1150     switch(propertyIndex)
1151     {
1152       case Toolkit::Slider::Property::LOWER_BOUND:
1153       {
1154         sliderImpl.SetLowerBound(value.Get<float>());
1155         break;
1156       }
1157
1158       case Toolkit::Slider::Property::UPPER_BOUND:
1159       {
1160         sliderImpl.SetUpperBound(value.Get<float>());
1161         break;
1162       }
1163
1164       case Toolkit::Slider::Property::VALUE:
1165       {
1166         sliderImpl.SetValue(value.Get<float>());
1167         break;
1168       }
1169
1170       case Toolkit::Slider::Property::TRACK_VISUAL:
1171       {
1172         Property::Map map;
1173         if(value.Get(map))
1174         {
1175           sliderImpl.SetTrackVisual(map);
1176         }
1177         break;
1178       }
1179
1180       case Toolkit::Slider::Property::HANDLE_VISUAL:
1181       {
1182         Property::Map map;
1183         if(value.Get(map))
1184         {
1185           sliderImpl.SetHandleVisual(map);
1186         }
1187         break;
1188       }
1189
1190       case Toolkit::Slider::Property::PROGRESS_VISUAL:
1191       {
1192         Property::Map map;
1193         if(value.Get(map))
1194         {
1195           sliderImpl.SetProgressVisual(map);
1196         }
1197         break;
1198       }
1199
1200       case Toolkit::Slider::Property::POPUP_VISUAL:
1201       {
1202         std::string imageUrl;
1203         if(value.Get(imageUrl))
1204         {
1205           sliderImpl.SetPopupVisual(imageUrl);
1206         }
1207
1208         // If it is not a string, then get a Property::Map from the property if possible.
1209         Property::Map map;
1210         if(value.Get(map))
1211         {
1212           sliderImpl.SetPopupVisual(map);
1213         }
1214
1215         break;
1216       }
1217
1218       case Toolkit::Slider::Property::POPUP_ARROW_VISUAL:
1219       {
1220         Property::Map map;
1221         if(value.Get(map))
1222         {
1223           sliderImpl.SetPopupArrowVisual(map);
1224         }
1225         break;
1226       }
1227
1228       case Toolkit::Slider::Property::DISABLED_COLOR:
1229       {
1230         sliderImpl.SetDisabledColor(value.Get<Vector4>());
1231         break;
1232       }
1233
1234       case Toolkit::Slider::Property::VALUE_PRECISION:
1235       {
1236         sliderImpl.SetValuePrecision(value.Get<int>());
1237         break;
1238       }
1239
1240       case Toolkit::Slider::Property::SHOW_POPUP:
1241       {
1242         sliderImpl.SetShowPopup(value.Get<bool>());
1243         break;
1244       }
1245
1246       case Toolkit::Slider::Property::SHOW_VALUE:
1247       {
1248         sliderImpl.SetShowValue(value.Get<bool>());
1249         break;
1250       }
1251
1252       case Toolkit::Slider::Property::MARKS:
1253       {
1254         sliderImpl.SetMarks(value.Get<Property::Array>());
1255         break;
1256       }
1257
1258       case Toolkit::Slider::Property::SNAP_TO_MARKS:
1259       {
1260         sliderImpl.SetSnapToMarks(value.Get<bool>());
1261         break;
1262       }
1263
1264       case Toolkit::Slider::Property::MARK_TOLERANCE:
1265       {
1266         sliderImpl.SetMarkTolerance(value.Get<float>());
1267         break;
1268       }
1269     }
1270   }
1271 }
1272
1273 Property::Value Slider::GetProperty(BaseObject* object, Property::Index propertyIndex)
1274 {
1275   Property::Value value;
1276
1277   Toolkit::Slider slider = Toolkit::Slider::DownCast(Dali::BaseHandle(object));
1278
1279   if(slider)
1280   {
1281     Slider& sliderImpl(GetImpl(slider));
1282
1283     switch(propertyIndex)
1284     {
1285       case Toolkit::Slider::Property::LOWER_BOUND:
1286       {
1287         value = sliderImpl.GetLowerBound();
1288         break;
1289       }
1290
1291       case Toolkit::Slider::Property::UPPER_BOUND:
1292       {
1293         value = sliderImpl.GetUpperBound();
1294         break;
1295       }
1296
1297       case Toolkit::Slider::Property::VALUE:
1298       {
1299         value = sliderImpl.GetValue();
1300         break;
1301       }
1302
1303       case Toolkit::Slider::Property::TRACK_VISUAL:
1304       {
1305         if(!sliderImpl.mTrackVisual.empty())
1306         {
1307           value = sliderImpl.GetTrackVisual();
1308         }
1309         else if(!sliderImpl.mTrackMap.Empty())
1310         {
1311           value = sliderImpl.mTrackMap;
1312         }
1313         break;
1314       }
1315
1316       case Toolkit::Slider::Property::HANDLE_VISUAL:
1317       {
1318         if(!sliderImpl.mHandleVisual.empty())
1319         {
1320           value = sliderImpl.GetHandleVisual();
1321         }
1322         else if(!sliderImpl.mHandleMap.Empty())
1323         {
1324           value = sliderImpl.mHandleMap;
1325         }
1326         break;
1327       }
1328
1329       case Toolkit::Slider::Property::PROGRESS_VISUAL:
1330       {
1331         if(!sliderImpl.mProgressVisual.empty())
1332         {
1333           value = sliderImpl.GetProgressVisual();
1334         }
1335         else if(!sliderImpl.mProgressMap.Empty())
1336         {
1337           value = sliderImpl.mProgressMap;
1338         }
1339         break;
1340       }
1341
1342       case Toolkit::Slider::Property::POPUP_VISUAL:
1343       {
1344         if(!sliderImpl.mPopupVisual.empty())
1345         {
1346           value = sliderImpl.GetPopupVisual();
1347         }
1348         else if(!sliderImpl.mPopupMap.Empty())
1349         {
1350           value = sliderImpl.mPopupMap;
1351         }
1352         break;
1353       }
1354
1355       case Toolkit::Slider::Property::POPUP_ARROW_VISUAL:
1356       {
1357         if(!sliderImpl.mPopupArrowVisual.empty())
1358         {
1359           value = sliderImpl.GetPopupArrowVisual();
1360         }
1361         else if(!sliderImpl.mPopupArrowMap.Empty())
1362         {
1363           value = sliderImpl.mPopupArrowMap;
1364         }
1365         break;
1366       }
1367
1368       case Toolkit::Slider::Property::DISABLED_COLOR:
1369       {
1370         value = sliderImpl.GetDisabledColor();
1371         break;
1372       }
1373
1374       case Toolkit::Slider::Property::VALUE_PRECISION:
1375       {
1376         value = sliderImpl.GetValuePrecision();
1377         break;
1378       }
1379
1380       case Toolkit::Slider::Property::SHOW_POPUP:
1381       {
1382         value = sliderImpl.GetShowPopup();
1383         break;
1384       }
1385
1386       case Toolkit::Slider::Property::SHOW_VALUE:
1387       {
1388         value = sliderImpl.GetShowValue();
1389         break;
1390       }
1391
1392       case Toolkit::Slider::Property::MARKS:
1393       {
1394         Property::Value  value1(Property::ARRAY);
1395         Property::Array* markArray = value1.GetArray();
1396
1397         if(markArray)
1398         {
1399           *markArray = sliderImpl.GetMarks();
1400         }
1401
1402         value = value1;
1403         break;
1404       }
1405
1406       case Toolkit::Slider::Property::SNAP_TO_MARKS:
1407       {
1408         value = sliderImpl.GetSnapToMarks();
1409         break;
1410       }
1411
1412       case Toolkit::Slider::Property::MARK_TOLERANCE:
1413       {
1414         value = sliderImpl.GetMarkTolerance();
1415         break;
1416       }
1417     }
1418   }
1419
1420   return value;
1421 }
1422
1423 double Slider::SliderAccessible::GetMinimum() const
1424 {
1425   auto self = Toolkit::Slider::DownCast(Self());
1426   return self.GetProperty(Toolkit::Slider::Property::LOWER_BOUND).Get<float>();
1427 }
1428
1429 double Slider::SliderAccessible::GetCurrent() const
1430 {
1431   auto self = Toolkit::Slider::DownCast(Self());
1432   return self.GetProperty(Toolkit::Slider::Property::VALUE).Get<float>();
1433 }
1434
1435 std::string Slider::SliderAccessible::GetValueText() const
1436 {
1437   return {}; // Text mode is not used at the moment
1438 }
1439
1440 double Slider::SliderAccessible::GetMaximum() const
1441 {
1442   auto self = Toolkit::Slider::DownCast(Self());
1443   return self.GetProperty(Toolkit::Slider::Property::UPPER_BOUND).Get<float>();
1444 }
1445
1446 bool Slider::SliderAccessible::SetCurrent(double current)
1447 {
1448   if(current < GetMinimum() || current > GetMaximum())
1449     return false;
1450
1451   auto  self = Toolkit::Slider::DownCast(Self());
1452   auto& impl = Toolkit::GetImpl(self);
1453
1454   const float prev = self.GetProperty<float>(Toolkit::Slider::Property::VALUE);
1455   float       next = static_cast<float>(current);
1456
1457   if(fabsf(next - prev) < Math::MACHINE_EPSILON_0)
1458   {
1459     // do nothing
1460   }
1461   else if(self.GetProperty<bool>(Toolkit::Slider::Property::SNAP_TO_MARKS))
1462   {
1463     auto marks = self.GetProperty<Property::Array>(Toolkit::Slider::Property::MARKS);
1464
1465     int prevIdx;
1466     if(impl.MarkReached(impl.MapValuePercentage(prev), prevIdx))
1467     {
1468       int nextIdx = prevIdx;
1469       nextIdx += (next > prev) ? 1 : -1;
1470
1471       if(nextIdx < 0 || nextIdx >= static_cast<int>(marks.Count()))
1472       {
1473         return false;
1474       }
1475
1476       next = marks[nextIdx].Get<float>();
1477     }
1478     else
1479     {
1480       next = impl.MapBounds(impl.SnapToMark(impl.MapValuePercentage(next)), impl.GetLowerBound(), impl.GetUpperBound());
1481     }
1482   }
1483   else
1484   {
1485     next = impl.MapBounds(impl.MarkFilter(impl.MapValuePercentage(next)), impl.GetLowerBound(), impl.GetUpperBound());
1486   }
1487
1488   impl.SetValue(next);
1489   impl.DisplayPopup(next);
1490
1491   return true;
1492 }
1493
1494 double Slider::SliderAccessible::GetMinimumIncrement() const
1495 {
1496   auto self = Toolkit::Slider::DownCast(Self());
1497
1498   bool  hasMarks  = !self.GetProperty<Property::Array>(Toolkit::Slider::Property::MARKS).Empty();
1499   float tolerance = self.GetProperty<float>(Toolkit::Slider::Property::MARK_TOLERANCE);
1500
1501   if(!hasMarks || fabsf(tolerance) < 0.01)
1502   {
1503     return 0.0; // let screen-reader choose the increment
1504   }
1505
1506   return Math::MACHINE_EPSILON_10000 + tolerance * (GetMaximum() - GetMinimum());
1507 }
1508
1509 } // namespace Internal
1510
1511 } // namespace Toolkit
1512
1513 } // namespace Dali