[Tizen](ATSPI) squashed implementation
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / slider / slider-impl.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/controls/slider/slider-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23 #include <sstream>
24 #include <limits>
25 #include <dali/public-api/events/touch-data.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/public-api/object/type-registry-helper.h>
28 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
29
30 // INTERNAL INCLUDES
31 #include <dali-toolkit/public-api/controls/control-impl.h>
32 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
33
34 using namespace Dali;
35
36 namespace Dali
37 {
38
39 namespace Toolkit
40 {
41
42 namespace Internal
43 {
44
45 namespace // Unnamed namespace
46 {
47
48 BaseHandle Create()
49 {
50   return Dali::Toolkit::Slider::New();
51 }
52
53 // Setup properties, signals and actions using the type-registry.
54 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::Slider, Toolkit::Control, Create )
55
56 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "lowerBound",             FLOAT,    LOWER_BOUND            )
57 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "upperBound",             FLOAT,    UPPER_BOUND            )
58 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "value",                  FLOAT,    VALUE                  )
59 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "trackVisual",            MAP,      TRACK_VISUAL           )
60 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "handleVisual",           MAP,      HANDLE_VISUAL          )
61 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "progressVisual",         MAP,      PROGRESS_VISUAL        )
62 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "popupVisual",            MAP,      POPUP_VISUAL           )
63 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "popupArrowVisual",       MAP,      POPUP_ARROW_VISUAL     )
64 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "disabledColor",          VECTOR4,  DISABLED_COLOR         )
65 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "valuePrecision",         INTEGER,  VALUE_PRECISION        )
66 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "showPopup",              BOOLEAN,  SHOW_POPUP             )
67 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "showValue",              BOOLEAN,  SHOW_VALUE             )
68 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "marks",                  ARRAY,    MARKS                  )
69 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "snapToMarks",            BOOLEAN,  SNAP_TO_MARKS          )
70 DALI_PROPERTY_REGISTRATION( Toolkit, Slider, "markTolerance",          FLOAT,    MARK_TOLERANCE         )
71
72 DALI_SIGNAL_REGISTRATION(   Toolkit, Slider, "valueChanged",                     SIGNAL_VALUE_CHANGED   )
73 DALI_SIGNAL_REGISTRATION(   Toolkit, Slider, "mark",                             SIGNAL_MARK            )
74
75 DALI_TYPE_REGISTRATION_END()
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 = DALI_IMAGE_DIR "slider-skin.9.png";
91 const char* SKINNED_HANDLE_VISUAL = DALI_IMAGE_DIR "slider-skin-handle.png";
92 const char* SKINNED_PROGRESS_VISUAL = DALI_IMAGE_DIR "slider-skin-progress.9.png";
93 const char* SKINNED_POPUP_VISUAL = DALI_IMAGE_DIR "slider-popup.9.png";
94 const char* SKINNED_POPUP_ARROW_VISUAL = DALI_IMAGE_DIR "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   DevelControl::SetAccessibilityConstructor( Self(), []( Dali::Actor actor ) {
162     return std::unique_ptr< Dali::Accessibility::Accessible >(
163         new AccessibleImpl( actor, Dali::Accessibility::Role::SLIDER ) );
164   } );
165 }
166
167 Slider::~Slider()
168 {
169 }
170
171 void Slider::OnInitialize()
172 {
173   // Setup
174   CreateChildren();
175
176   // Properties
177   Actor self = Self();
178
179   SetHitRegion(     DEFAULT_HIT_REGION     );
180   SetTrackRegion(   DEFAULT_TRACK_REGION   );
181   SetHandleSize(    DEFAULT_HANDLE_SIZE    );
182
183   SetTrackVisual(            SKINNED_TRACK_VISUAL             );
184   SetHandleVisual(           SKINNED_HANDLE_VISUAL            );
185   SetProgressVisual(         SKINNED_PROGRESS_VISUAL          );
186   SetPopupVisual(            SKINNED_POPUP_VISUAL             );
187   SetPopupArrowVisual(       SKINNED_POPUP_ARROW_VISUAL       );
188
189   SetShowPopup( DEFAULT_SHOW_POPUP );
190   SetShowValue( DEFAULT_SHOW_VALUE );
191
192   SetEnabled( DEFAULT_ENABLED );
193   SetDisabledColor( DEFAULT_DISABLED_COLOR );
194
195   SetSnapToMarks( DEFAULT_SNAP_TO_MARKS );
196   SetMarkTolerance( MARK_SNAP_TOLERANCE );
197
198   SetLowerBound( DEFAULT_LOWER_BOUND );
199   SetUpperBound( DEFAULT_UPPER_BOUND );
200   UpdateSkin();
201   SetValuePrecision( DEFAULT_VALUE_PRECISION );
202   mValue = DEFAULT_VALUE;
203   DisplayValue( mValue, false );       // Run this last to display the correct value
204
205   // Size the Slider actor to a default
206   self.SetSize( DEFAULT_HIT_REGION.x, DEFAULT_HIT_REGION.y );
207
208   // Connect to the touch signal
209   self.TouchSignal().Connect( this, &Slider::OnTouch );
210 }
211
212 void Slider::OnRelayout( const Vector2& size, RelayoutContainer& container )
213 {
214   SetHitRegion( Vector2( size.x, GetHitRegion().y ) );
215   // Factor in handle overshoot into size of backing
216   SetTrackRegion( Vector2( size.x - GetHandleSize().x, GetTrackRegion().y ) );
217   Control::OnRelayout( size, container );
218 }
219
220 bool Slider::OnTouch(Actor actor, const TouchData& touch)
221 {
222   if( mState != DISABLED )
223   {
224     const PointState::Type touchState = touch.GetState(0);
225
226     if( touchState == PointState::DOWN )
227     {
228       mState = PRESSED;
229
230       float percentage = MapPercentage( touch.GetLocalPosition( 0 ) );
231       float value = MapBounds( ( GetSnapToMarks() ) ? SnapToMark( percentage ) : MarkFilter( percentage ), GetLowerBound(), GetUpperBound() );
232       SetValue( value );
233       DisplayPopup( value );
234     }
235     else if( touchState == PointState::UP )
236     {
237       if( mState == PRESSED )
238       {
239         mState = NORMAL;
240         mSlidingFinishedSignal.Emit( Toolkit::Slider::DownCast( Self() ), GetValue() );
241       }
242     }
243   }
244
245   return true;
246 }
247
248 void Slider::OnPan( Actor actor, const PanGesture& gesture )
249 {
250   // gesture.position is in local actor coordinates
251   if( mState != DISABLED )
252   {
253     switch( gesture.state )
254     {
255       case Gesture::Continuing:
256       {
257         if( mState == PRESSED )
258         {
259           float value = MapBounds( MarkFilter ( MapPercentage( gesture.position ) ), GetLowerBound(), GetUpperBound() );
260           SetValue( value );
261           DisplayPopup( value );
262         }
263         break;
264       }
265       case Gesture::Finished:
266       {
267         if( mState == PRESSED  )
268         {
269           if( GetSnapToMarks() )
270           {
271             float value = MapBounds( SnapToMark( MapPercentage( gesture.position ) ), GetLowerBound(), GetUpperBound() );
272             SetValue( value );
273             DisplayPopup( value );
274           }
275           mSlidingFinishedSignal.Emit( Toolkit::Slider::DownCast( Self() ), GetValue() );
276         }
277
278         mState = NORMAL;
279         break;
280       }
281       default:
282       {
283         break;
284       }
285     }
286   }
287 }
288
289 float Slider::HitSpaceToDomain( float x )
290 {
291   float halfRegionWidth = GetHitRegion().x * 0.5f;
292   float halfDomainWidth = ( mDomain.to.x - mDomain.from.x ) * 0.5f;
293   float endDiff = halfRegionWidth - halfDomainWidth;
294
295   return x - endDiff;
296 }
297
298 float Slider::MapPercentage( const Vector2& point )
299 {
300   return Clamp( ( HitSpaceToDomain( point.x ) - mDomain.from.x ) / ( mDomain.to.x - mDomain.from.x ), 0.0f, 1.0f );
301 }
302
303 float Slider::MapValuePercentage( float value )
304 {
305   return ( value - GetLowerBound() ) / ( GetUpperBound() - GetLowerBound() );
306 }
307
308 float Slider::MapBounds( float percent, float lowerBound, float upperBound )
309 {
310   return lowerBound + percent * ( upperBound - lowerBound );
311 }
312
313 Slider::Domain Slider::CalcDomain( const Vector2& currentSize )
314 {
315    return Domain( Vector2( 0.0f, 0.0f ), currentSize );
316 }
317
318 void Slider::DisplayValue( float value, bool raiseSignals )
319 {
320   float clampedValue = Clamp( value, GetLowerBound(), GetUpperBound() );
321
322   float percent = MapValuePercentage( clampedValue );
323
324   float x = mDomain.from.x + percent * ( mDomain.to.x - mDomain.from.x );
325
326   mHandle.SetX( x );
327
328   // Progress bar
329   if( mProgress )
330   {
331     mProgress.SetSize( x, GetTrackRegion().y );
332   }
333
334   // Signals
335   if( raiseSignals )
336   {
337     Toolkit::Slider self = Toolkit::Slider::DownCast( Self() );
338     mValueChangedSignal.Emit( self, clampedValue );
339
340     int markIndex;
341     if( MarkReached( percent, markIndex ) )
342     {
343       mMarkReachedSignal.Emit( self, markIndex );
344     }
345   }
346
347   if( mHandleValueTextLabel )
348   {
349     std::stringstream ss;
350     ss.precision( GetValuePrecision() );
351     ss << std::fixed << clampedValue;
352
353     std::string label = mHandleValueTextLabel.GetProperty<std::string>( Toolkit::TextLabel::Property::TEXT );
354     if( label.compare(ss.str()) )
355     {
356       mHandleValueTextLabel.SetProperty( Toolkit::TextLabel::Property::TEXT, ss.str() );
357     }
358   }
359 }
360
361 void Slider::SetMarks( const MarkList& marks )
362 {
363   mMarks = marks;
364 }
365
366 const Slider::MarkList& Slider::GetMarks() const
367 {
368   return mMarks;
369 }
370
371 void Slider::SetSnapToMarks( bool snap )
372 {
373   mSnapToMarks = snap;
374 }
375
376 bool Slider::GetSnapToMarks() const
377 {
378   return mSnapToMarks;
379 }
380
381 Actor Slider::CreateHitRegion()
382 {
383   Actor hitRegion = Actor::New();
384   hitRegion.SetParentOrigin( ParentOrigin::CENTER );
385   hitRegion.SetAnchorPoint( AnchorPoint::CENTER );
386   hitRegion.TouchSignal().Connect( this, &Slider::OnTouch );
387
388   return hitRegion;
389 }
390
391 Toolkit::ImageView Slider::CreateTrack()
392 {
393   Toolkit::ImageView track = Toolkit::ImageView::New();
394   track.SetName("SliderTrack");
395   track.SetParentOrigin( ParentOrigin::CENTER );
396   track.SetAnchorPoint( AnchorPoint::CENTER );
397   return track;
398 }
399
400 void Slider::SetTrackVisual( const std::string& filename )
401 {
402   if( mHandle && ( filename.size() > 0 ) )
403   {
404     mTrack.SetImage( filename );
405     mTrackVisual = filename;
406   }
407 }
408
409 void Slider::SetTrackVisual( Property::Map map )
410 {
411   Property::Value* imageValue = map.Find( "url" );
412   if( imageValue )
413   {
414     mTrackVisual.clear();
415     std::string filename;
416     if( imageValue->Get( filename ) )
417     {
418       if( mTrack && ( filename.size() > 0 ) )
419       {
420         mTrack.SetImage( filename );
421         mTrackMap = map;
422       }
423     }
424   }
425
426   Property::Value* sizeValue = map.Find( "size" );
427   if( sizeValue )
428   {
429     Vector2 size;
430     if( sizeValue->Get( size ) )
431     {
432       mTrackRegion = size;
433       if( mTrack )
434       {
435         mTrack.SetSize( mTrackRegion );
436       }
437
438     ResizeProgressRegion( Vector2( 0.0f, mTrackRegion.y ) );
439
440     mDomain = CalcDomain( mTrackRegion );
441
442     // Set the progress bar to correct width
443     DisplayValue( GetValue(), false );
444     }
445   }
446 }
447
448 std::string Slider::GetTrackVisual()
449 {
450   return mTrackVisual;
451 }
452
453 Toolkit::ImageView Slider::CreateProgress()
454 {
455   Toolkit::ImageView progress = Toolkit::ImageView::New();
456   progress.SetName("SliderProgress");
457   progress.SetParentOrigin( ParentOrigin::CENTER_LEFT );
458   progress.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
459
460   return progress;
461 }
462
463 void Slider::SetProgressVisual( const std::string& filename )
464 {
465   if( mProgress && ( filename.size() > 0 ) )
466   {
467     mProgress.SetImage( filename );
468     mProgressVisual = filename;
469   }
470 }
471
472 void Slider::SetProgressVisual( Property::Map map )
473 {
474   Property::Value* imageValue = map.Find( "url" );
475   if( imageValue )
476   {
477     mProgressVisual.clear();
478     std::string filename;
479     if( imageValue->Get( filename ) )
480     {
481       if( mProgress && ( filename.size() > 0 ) )
482       {
483         mProgress.SetImage( filename );
484         mProgressMap = map;
485       }
486     }
487   }
488 }
489
490 std::string Slider::GetProgressVisual()
491 {
492   return mProgressVisual;
493 }
494
495 void Slider::SetPopupVisual( const std::string& filename )
496 {
497   mPopupVisual = filename;
498 }
499
500 void Slider::SetPopupVisual( Property::Map map )
501 {
502   Property::Value* imageValue = map.Find( "url" );
503   if( imageValue )
504   {
505     mPopupVisual.clear();
506     std::string filename;
507     if( imageValue->Get( filename ) )
508     {
509       if( mPopup && ( filename.size() > 0 ) )
510       {
511         mPopup.SetImage( filename );
512         mPopupMap = map;
513       }
514     }
515   }
516 }
517
518 std::string Slider::GetPopupVisual()
519 {
520   return mPopupVisual;
521 }
522
523 void Slider::CreatePopupImage( const std::string& filename )
524 {
525   if( mPopup && ( filename.size() > 0 ) )
526   {
527     Property::Map map;
528     map[Toolkit::ImageVisual::Property::URL] = filename;
529     mPopup.SetProperty( Toolkit::ImageView::Property::IMAGE, map );
530   }
531 }
532
533 void Slider::SetPopupArrowVisual( const std::string& filename )
534 {
535   mPopupArrowVisual = filename;
536 }
537
538 void Slider::SetPopupArrowVisual( Property::Map map )
539 {
540   Property::Value* imageValue = map.Find( "url" );
541   if( imageValue )
542   {
543     mPopupArrowVisual.clear();
544     std::string filename;
545     if( imageValue->Get( filename ) )
546     {
547       if( mPopupArrow && ( filename.size() > 0 ) )
548       {
549         mPopupArrow.SetImage( filename );
550         mPopupArrowMap = map;
551       }
552     }
553   }
554 }
555
556 std::string Slider::GetPopupArrowVisual()
557 {
558   return mPopupArrowVisual;
559 }
560
561 void Slider::CreatePopupArrowImage( const std::string& filename )
562 {
563   if( mPopupArrow && ( filename.size() > 0 ) )
564   {
565     Property::Map map;
566     map[Toolkit::ImageVisual::Property::URL] = filename;
567     mPopupArrow.SetProperty( Toolkit::ImageView::Property::IMAGE, map );
568   }
569 }
570
571 void Slider::ResizeProgressRegion( const Vector2& region )
572 {
573   if( mProgress )
574   {
575     mProgress.SetSize( region );
576   }
577 }
578
579 Toolkit::ImageView Slider::CreateHandle()
580 {
581   Toolkit::ImageView handle = Toolkit::ImageView::New();
582   handle.SetName("SliderHandle");
583   handle.SetParentOrigin( ParentOrigin::CENTER_LEFT );
584   handle.SetAnchorPoint( AnchorPoint::CENTER );
585
586   return handle;
587 }
588
589 Toolkit::ImageView Slider::CreatePopupArrow()
590 {
591   Toolkit::ImageView arrow = Toolkit::ImageView::New();
592   arrow.SetStyleName("SliderPopupArrow");
593   arrow.SetName("SliderPopupArrow");
594   arrow.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
595   arrow.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
596
597   return arrow;
598 }
599
600 Toolkit::TextLabel Slider::CreatePopupText()
601 {
602   Toolkit::TextLabel textLabel = Toolkit::TextLabel::New();
603   textLabel.SetName( "SliderPopupTextLabel" );
604   textLabel.SetStyleName( "SliderPopupTextLabel" );
605   textLabel.SetParentOrigin( ParentOrigin::CENTER );
606   textLabel.SetAnchorPoint( AnchorPoint::CENTER );
607   textLabel.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
608   textLabel.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
609   textLabel.SetProperty( Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
610   textLabel.SetPadding( Padding( POPUP_TEXT_PADDING, POPUP_TEXT_PADDING, 0.0f, 0.0f ) );
611   return textLabel;
612 }
613
614 Toolkit::ImageView Slider::CreatePopup()
615 {
616   Toolkit::ImageView popup = Toolkit::ImageView::New();
617   popup.SetName( "SliderPopup" );
618   popup.SetParentOrigin( ParentOrigin::TOP_CENTER );
619   popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
620   popup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::WIDTH );
621
622   mValueTextLabel = CreatePopupText();
623   popup.Add( mValueTextLabel );
624
625   return popup;
626 }
627
628 void Slider::SetHandleVisual( const std::string& filename )
629 {
630   if( mHandle && ( filename.size() > 0 ) )
631   {
632     mHandle.SetImage( filename );
633     mHandleVisual = filename;
634   }
635 }
636
637 void Slider::SetHandleVisual( Property::Map map )
638 {
639   Property::Value* imageValue = map.Find( "url" );
640   if( imageValue )
641   {
642     mHandleVisual.clear();
643     std::string filename;
644     if( imageValue->Get( filename ) )
645     {
646       if( mHandle && ( filename.size() > 0 ) )
647       {
648         mHandle.SetImage( filename );
649         mHandleMap = map;
650       }
651     }
652   }
653
654   Property::Value* sizeValue = map.Find( "size" );
655   if( sizeValue )
656   {
657     Vector2 size;
658     if( sizeValue->Get( size ) )
659     {
660       mHandleSize = size;
661       ResizeHandleSize( mHandleSize );
662
663       Vector2 hitRegion = GetHitRegion();
664       hitRegion.x += mHandleSize.x;
665       SetHitRegion( hitRegion );
666     }
667   }
668 }
669
670 std::string Slider::GetHandleVisual()
671 {
672   return mHandleVisual;
673 }
674
675 void Slider::ResizeHandleSize( const Vector2& size )
676 {
677   if( mHandle )
678   {
679     mHandle.SetSize( size );
680   }
681 }
682
683 void Slider::CreateHandleValueDisplay()
684 {
685   if( mHandle && !mHandleValueTextLabel )
686   {
687     mHandleValueTextLabel = Toolkit::TextLabel::New();
688     mHandleValueTextLabel.SetName("SliderHandleTextLabel");
689     mHandleValueTextLabel.SetStyleName("SliderHandleTextLabel");
690     mHandleValueTextLabel.SetParentOrigin( ParentOrigin::CENTER );
691     mHandleValueTextLabel.SetAnchorPoint( AnchorPoint::CENTER );
692     mHandleValueTextLabel.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
693     mHandleValueTextLabel.SetProperty( Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
694     mHandle.Add( mHandleValueTextLabel );
695   }
696 }
697
698 void Slider::DestroyHandleValueDisplay()
699 {
700   UnparentAndReset(mHandleValueTextLabel);
701 }
702
703 Actor Slider::CreateValueDisplay()
704 {
705   Actor popup = Actor::New();
706   popup.SetParentOrigin( ParentOrigin::TOP_CENTER );
707   popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
708
709   mPopupArrow = CreatePopupArrow();
710   popup.Add( mPopupArrow );
711
712   mPopup = CreatePopup();
713   mPopup.SetSize( 0.0f, VALUE_POPUP_HEIGHT );
714   mPopupArrow.Add( mPopup );
715
716   return popup;
717 }
718
719 Toolkit::Slider::ValueChangedSignalType& Slider::ValueChangedSignal()
720 {
721   return mValueChangedSignal;
722 }
723
724 Toolkit::Slider::ValueChangedSignalType& Slider::SlidingFinishedSignal()
725 {
726   return mSlidingFinishedSignal;
727 }
728
729 Toolkit::Slider::MarkReachedSignalType& Slider::MarkReachedSignal()
730 {
731   return mMarkReachedSignal;
732 }
733
734 void Slider::UpdateSkin()
735 {
736   switch( mState )
737   {
738     case NORMAL:
739     {
740       mTrack.SetColor( Color::WHITE );
741       mHandle.SetColor( Color::WHITE );
742       mProgress.SetColor( Color::WHITE );
743       break;
744     }
745     case DISABLED:
746     {
747       Vector4 disabledColor = GetDisabledColor();
748       mTrack.SetColor( disabledColor );
749       mHandle.SetColor( disabledColor );
750       mProgress.SetColor( disabledColor );
751       break;
752     }
753     case PRESSED:
754     {
755       break;
756     }
757     case FOCUSED:
758     {
759       break;
760     }
761   }
762 }
763
764 void Slider::CreateChildren()
765 {
766   Actor self = Self();
767
768   // Hit region
769   mHitArea = CreateHitRegion();
770   mPanDetector = PanGestureDetector::New();
771   mPanDetector.Attach( mHitArea );
772   mPanDetector.DetectedSignal().Connect( this, &Slider::OnPan );
773   self.Add( mHitArea );
774
775   // Track
776   mTrack = CreateTrack();
777   self.Add( mTrack );
778
779   // Progress bar
780   mProgress = CreateProgress();
781   mTrack.Add( mProgress );
782
783   // Handle
784   mHandle = CreateHandle();
785   mProgress.Add( mHandle );
786 }
787
788 void Slider::SetHitRegion( const Vector2& size )
789 {
790   mHitRegion = size;
791
792   if( mHitArea )
793   {
794     mHitArea.SetSize( mHitRegion );
795   }
796 }
797
798 const Vector2& Slider::GetHitRegion() const
799 {
800   return mHitRegion;
801 }
802
803 void Slider::AddPopup()
804 {
805   if( !mValueDisplay )
806   {
807     mValueDisplay = CreateValueDisplay();
808     mValueDisplay.SetVisible( false );
809     mHandle.Add( mValueDisplay );
810
811     CreatePopupImage( GetPopupVisual() );
812     CreatePopupArrowImage( GetPopupArrowVisual() );
813
814     mValueTimer = Timer::New( VALUE_VIEW_SHOW_DURATION );
815     mValueTimer.TickSignal().Connect( this, &Slider::HideValueView );
816   }
817 }
818
819 void Slider::RemovePopup()
820 {
821   if( mValueDisplay )
822   {
823     mPopup.Unparent();
824     mPopup.Reset();
825
826     mPopupArrow.Unparent();
827     mPopupArrow.Reset();
828
829     mValueDisplay.Unparent();
830     mValueDisplay.Reset();
831
832     mValueTimer.TickSignal().Disconnect( this, &Slider::HideValueView );
833     mValueTimer.Reset();
834   }
835 }
836
837
838 float Slider::MarkFilter( float value )
839 {
840   const float MARK_TOLERANCE = GetMarkTolerance();
841
842   float mark;
843   for( MarkList::SizeType i = 0; i < mMarks.Count(); ++i)
844   {
845     const Property::Value& propertyValue = mMarks[i];
846     propertyValue.Get( mark );
847     mark = MapValuePercentage( mark );
848
849     // If close to a mark, return the mark
850     if( fabsf( mark - value ) < MARK_TOLERANCE )
851     {
852       return mark;
853     }
854   }
855
856   return value;
857 }
858
859 float Slider::SnapToMark( float value )
860 {
861   float closestMark = value;
862   float closestDist = std::numeric_limits<float>::max();
863
864   float mark;
865   for( MarkList::SizeType  i = 0; i < mMarks.Count(); ++i)
866   {
867     const Property::Value& propertyValue = mMarks[i];
868     propertyValue.Get( mark );
869     mark = MapValuePercentage( mark );
870
871     float dist = fabsf( mark - value );
872     if( dist < closestDist )
873     {
874       closestDist = dist;
875       closestMark = mark;
876     }
877   }
878
879   return closestMark;
880 }
881
882 bool Slider::MarkReached( float value, int& outIndex )
883 {
884   const float MARK_TOLERANCE = GetMarkTolerance();
885
886   // Binary search
887   int head = 0,
888       tail = mMarks.Size() - 1;
889   int current;
890   float mark;
891
892   while( head <= tail )
893   {
894     current = head + ( tail - head ) / 2;
895
896     const Property::Value& propertyValue = mMarks[ current ];
897     propertyValue.Get( mark );
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   return false;
917 }
918
919 bool Slider::HideValueView()
920 {
921   if( mValueDisplay )
922   {
923     mValueDisplay.SetVisible( false );
924   }
925
926   return false;
927 }
928
929 void Slider::SetLowerBound( float bound )
930 {
931   mLowerBound = bound;
932   DisplayValue( GetValue(), false );
933 }
934
935 float Slider::GetLowerBound() const
936 {
937   return mLowerBound;
938 }
939
940 void Slider::SetUpperBound( float bound )
941 {
942   mUpperBound = bound;
943   DisplayValue( GetValue(), false );
944 }
945
946 float Slider::GetUpperBound() const
947 {
948   return mUpperBound;
949 }
950
951 void Slider::SetValue( float value )
952 {
953   mValue = value;
954   DisplayValue( mValue, true );
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.SetSize( 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.SetVisible( 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()
1415 {
1416   auto p = Toolkit::Slider::DownCast( self );
1417   return p.GetProperty( Toolkit::Slider::Property::LOWER_BOUND ).Get< float >();
1418 }
1419
1420 double Slider::AccessibleImpl::GetCurrent()
1421 {
1422   auto p = Toolkit::Slider::DownCast( self );
1423   return p.GetProperty( Toolkit::Slider::Property::VALUE ).Get< float >();
1424 }
1425
1426 double Slider::AccessibleImpl::GetMaximum()
1427 {
1428   auto p = Toolkit::Slider::DownCast( self );
1429   return p.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   auto p = Toolkit::Slider::DownCast( self );
1437   p.SetProperty( Toolkit::Slider::Property::VALUE, static_cast< float >( current ) );
1438   return true;
1439 }
1440
1441 double Slider::AccessibleImpl::GetMinimumIncrement()
1442 {
1443   auto p = Toolkit::Slider::DownCast( self );
1444   return p.GetProperty( Toolkit::Slider::Property::MARK_TOLERANCE ).Get< float >();
1445 }
1446
1447 } // namespace Internal
1448
1449 } // namespace Toolkit
1450
1451 } // namespace Dali