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