37650e44994a3fe2e94db687c7131c945df795a9
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / slider / slider-impl.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 #include <dali-toolkit/internal/controls/slider/slider-impl.h>
18 #include <dali-toolkit/public-api/controls/control-impl.h>
19
20 #include <sstream>
21
22 using namespace Dali;
23 using namespace std;
24
25 namespace Dali
26 {
27
28 namespace Toolkit
29 {
30
31 namespace Internal
32 {
33
34 namespace
35 {
36 const float BACKING_Z = -0.1f;
37 const float PROGRESS_Z = 0.1f;
38 const float HANDLE_Z = 1.0f;
39 const float VALUE_TEXT_INCREMENT = 0.01f;
40 const float HANDLE_VALUE_DISPLAY_TEXT_Z = HANDLE_Z + VALUE_TEXT_INCREMENT;
41 const float VALUE_DISPLAY_TEXT_Z = VALUE_TEXT_INCREMENT + VALUE_TEXT_INCREMENT;  // Put above HANDLE_VALUE_DISPLAY_TEXT_Z (parented to handle)
42
43 const float MARK_SNAP_TOLERANCE = 0.05f; // 5% of slider width
44
45 const int VALUE_VIEW_SHOW_DURATION = 1000;  // millisec
46 const int VALUE_VIEW_SHOW_DURATION_LONG = 2000;  // millisec
47
48 const float VALUE_VERTICAL_OFFSET = 48.0f;
49
50 const float DEFAULT_WIDTH = 0.0f;
51 const float DEFAULT_HEIGHT = 27.0f;
52 const float DEFAULT_HIT_HEIGHT = 72.0f;
53 const float DEFAULT_HANDLE_HEIGHT = DEFAULT_HIT_HEIGHT;
54
55 const char* SKINNED_BACKING_IMAGE_NAME = DALI_IMAGE_DIR "slider-skin.png";
56 const char* SKINNED_HANDLE_IMAGE_NAME = DALI_IMAGE_DIR "slider-skin-handle.png";;
57 const char* SKINNED_PROGRESS_IMAGE_NAME = DALI_IMAGE_DIR "slider-skin-progress.png";
58 const char* SKINNED_POPUP_IMAGE_NAME = DALI_IMAGE_DIR "slider-popup.png";
59 const char* SKINNED_POPUP_ARROW_IMAGE_NAME = DALI_IMAGE_DIR "slider-popup-arrow.png";
60
61 const Vector4 SKINNED_BACKING_SCALE9_BORDER( 12.0f, 0.0f, 12.0f, 0.0f );
62 const Vector4 SKINNED_PROGRESS_SCALE9_BORDER( 14.0f, 0.0f, 0.0f, 0.0f );
63 const Vector4 SKINNED_POPUP_SCALE9_BORDER( 10.0f, 10.0f, 10.0f, 10.0f );
64
65 const Vector2 DEFAULT_HIT_REGION( DEFAULT_WIDTH, DEFAULT_HIT_HEIGHT );
66 const Vector2 DEFAULT_BACKING_REGION( DEFAULT_WIDTH, DEFAULT_HEIGHT );
67 const Vector2 DEFAULT_HANDLE_REGION( DEFAULT_HANDLE_HEIGHT, DEFAULT_HANDLE_HEIGHT );
68
69 const Vector4 DEFAULT_DISABLE_COLOR( 0.5f, 0.5f, 0.5f, 1.0f );
70 const Vector4 DEFAULT_POPUP_TEXT_COLOR( 0.5f, 0.5f, 0.5f, 1.0f );
71
72 const float VALUE_POPUP_MARGIN = 10.0f;
73 const float VALUE_POPUP_HEIGHT = 81.0f;
74 const float VALUE_POPUP_MIN_WIDTH = 54.0f;
75 const Vector2 VALUE_POPUP_ARROW_SIZE( 18.0f, 18.0f );
76
77 const float DEFAULT_LOWER_BOUND = 0.0f;
78 const float DEFAULT_UPPER_BOUND = 1.0f;
79 const float DEFAULT_VALUE = 0.0f;
80 const int DEFAULT_VALUE_PRECISION = 0;
81 const bool DEFAULT_SHOW_POPUP = false;
82 const bool DEFAULT_SHOW_VALUE = true;
83 const bool DEFAULT_ENABLED = true;
84
85
86 BaseHandle Create()
87 {
88   return Dali::Toolkit::Slider::New();
89 }
90
91 TypeRegistration typeRegistration( typeid(Dali::Toolkit::Slider), typeid(Dali::Toolkit::Control), Create );
92
93 SignalConnectorType signalConnector1( typeRegistration, Toolkit::Slider::SIGNAL_VALUE_CHANGED, &Toolkit::Internal::Slider::DoConnectSignal );
94 SignalConnectorType signalConnector2( typeRegistration, Toolkit::Slider::SIGNAL_MARK, &Toolkit::Internal::Slider::DoConnectSignal );
95
96 } // namespace
97
98 ///////////////////////////////////////////////////////////////////////////////////////////////////
99 // Slider
100 ///////////////////////////////////////////////////////////////////////////////////////////////////
101
102 Dali::Toolkit::Slider Slider::New()
103 {
104   // Create the implementation
105   SliderPtr slider( new Slider() );
106
107   // Pass ownership to CustomActor via derived handle
108   Dali::Toolkit::Slider handle( *slider );
109
110   // Second-phase init of the implementation
111   // This can only be done after the CustomActor connection has been made...
112   slider->Initialize();
113
114   return handle;
115 }
116
117 Slider::Slider()
118 : ControlImpl( true ),
119   mState( NORMAL )
120 {
121 }
122
123 Slider::~Slider()
124 {
125 }
126
127 void Slider::OnInitialize()
128 {
129   // Setup
130   CreateChildren();
131
132   // Properties
133   Actor self = Self();
134
135   // Register properties in a block so the properties are ready for the update functions
136   mPropertyHitRegion = self.RegisterProperty( Dali::Toolkit::Slider::HIT_REGION_PROPERTY_NAME, DEFAULT_HIT_REGION, Property::READ_WRITE );
137   mPropertyBackingRegion = self.RegisterProperty( Dali::Toolkit::Slider::BACKING_REGION_PROPERTY_NAME, DEFAULT_BACKING_REGION, Property::READ_WRITE );
138   mPropertyHandleRegion = self.RegisterProperty( Dali::Toolkit::Slider::HANDLE_REGION_PROPERTY_NAME, DEFAULT_HANDLE_REGION, Property::READ_WRITE );
139
140   mPropertyBackingImageName = self.RegisterProperty( Dali::Toolkit::Slider::BACKING_IMAGE_NAME_PROPERTY_NAME, SKINNED_BACKING_IMAGE_NAME, Property::READ_WRITE );
141   mPropertyHandleImageName = self.RegisterProperty( Dali::Toolkit::Slider::HANDLE_IMAGE_NAME_PROPERTY_NAME, SKINNED_HANDLE_IMAGE_NAME, Property::READ_WRITE );
142
143   mPropertyProgressImageName = self.RegisterProperty( Dali::Toolkit::Slider::PROGRESS_IMAGE_NAME_PROPERTY_NAME, SKINNED_PROGRESS_IMAGE_NAME, Property::READ_WRITE );
144   mPropertyPopupImageName = self.RegisterProperty( Dali::Toolkit::Slider::POPUP_IMAGE_NAME_PROPERTY_NAME, SKINNED_POPUP_IMAGE_NAME, Property::READ_WRITE );
145   mPropertyPopupArrowImageName = self.RegisterProperty( Dali::Toolkit::Slider::POPUP_ARROW_IMAGE_NAME_PROPERTY_NAME, SKINNED_POPUP_ARROW_IMAGE_NAME, Property::READ_WRITE );
146
147   mPropertyBackingScale9Border = self.RegisterProperty( Dali::Toolkit::Slider::BACKING_SCALE9_BORDER_PROPERTY_NAME, SKINNED_BACKING_SCALE9_BORDER, Property::READ_WRITE );
148   mPropertyProgressScale9Border = self.RegisterProperty( Dali::Toolkit::Slider::PROGRESS_SCALE9_BORDER_PROPERTY_NAME, SKINNED_PROGRESS_SCALE9_BORDER, Property::READ_WRITE );
149   mPropertyPopupScale9Border = self.RegisterProperty( Dali::Toolkit::Slider::POPUP_SCALE9_BORDER_PROPERTY_NAME, SKINNED_POPUP_SCALE9_BORDER, Property::READ_WRITE );
150
151   mPropertyDisableColor = self.RegisterProperty( Dali::Toolkit::Slider::DISABLE_COLOR_PROPERTY_NAME, DEFAULT_DISABLE_COLOR, Property::READ_WRITE );
152   mPropertyPopupTextColor = self.RegisterProperty( Dali::Toolkit::Slider::POPUP_TEXT_COLOR_PROPERTY_NAME, DEFAULT_POPUP_TEXT_COLOR, Property::READ_WRITE );
153
154   mPropertyValuePrecision = self.RegisterProperty( Dali::Toolkit::Slider::VALUE_PRECISION_PROPERTY_NAME, DEFAULT_VALUE_PRECISION, Property::READ_WRITE );
155   mPropertyShowPopup = self.RegisterProperty( Dali::Toolkit::Slider::SHOW_POPUP_PROPERTY_NAME, DEFAULT_SHOW_POPUP, Property::READ_WRITE );
156   mPropertyShowValue = self.RegisterProperty( Dali::Toolkit::Slider::SHOW_VALUE_PROPERTY_NAME, DEFAULT_SHOW_VALUE, Property::READ_WRITE );
157
158   mPropertyEnabled = self.RegisterProperty( Dali::Toolkit::Slider::ENABLED_PROPERTY_NAME, DEFAULT_ENABLED, Property::READ_WRITE );
159
160   mPropertyMarks = self.RegisterProperty( Dali::Toolkit::Slider::MARKS_PROPERTY_NAME, mMarks, Property::READ_WRITE );
161   mPropertySnapToMarks = self.RegisterProperty( Dali::Toolkit::Slider::SNAP_TO_MARKS_PROPERTY_NAME, false, Property::READ_WRITE );
162   mPropertyMarkTolerance = self.RegisterProperty( Dali::Toolkit::Slider::MARK_TOLERANCE_PROPERTY_NAME, MARK_SNAP_TOLERANCE, Property::READ_WRITE );
163
164   mPropertyLowerBound = self.RegisterProperty( Dali::Toolkit::Slider::LOWER_BOUND_PROPERTY_NAME, DEFAULT_LOWER_BOUND, Property::READ_WRITE );
165   mPropertyUpperBound = self.RegisterProperty( Dali::Toolkit::Slider::UPPER_BOUND_PROPERTY_NAME, DEFAULT_UPPER_BOUND, Property::READ_WRITE );
166   mPropertyValue = self.RegisterProperty( Dali::Toolkit::Slider::VALUE_PROPERTY_NAME, DEFAULT_VALUE, Property::READ_WRITE );
167
168   ResizeHitRegion( DEFAULT_HIT_REGION );
169   SetBackingRegion( DEFAULT_BACKING_REGION );
170   UpdateHandleRegion( DEFAULT_HANDLE_REGION );
171   CreateBackingImage( SKINNED_BACKING_IMAGE_NAME );
172   CreateHandleImage( SKINNED_HANDLE_IMAGE_NAME );
173   CreateProgressImage( SKINNED_PROGRESS_IMAGE_NAME );
174   CreatePopupImage( SKINNED_POPUP_IMAGE_NAME );
175   CreatePopupArrowImage( SKINNED_POPUP_ARROW_IMAGE_NAME );
176   SetBackingScale9( SKINNED_BACKING_SCALE9_BORDER );
177   SetProgressScale9( SKINNED_PROGRESS_SCALE9_BORDER );
178   SetPopupScale9( SKINNED_POPUP_SCALE9_BORDER );
179   UpdatePopupTextColor( DEFAULT_POPUP_TEXT_COLOR );
180   ShowPopup( DEFAULT_SHOW_POPUP );
181   ShowValue( DEFAULT_SHOW_VALUE );
182   SetEnabled( DEFAULT_ENABLED );
183   UpdateLowerBound( DEFAULT_LOWER_BOUND );
184   UpdateUpperBound( DEFAULT_UPPER_BOUND );
185   UpdateSkin();
186   DisplayValue( DEFAULT_VALUE, false );       // Run this last to display the correct value
187
188   // Size the Slider actor to a default
189   self.SetSize( DEFAULT_HIT_REGION.x, DEFAULT_HIT_REGION.y );
190 }
191
192 void Slider::OnControlSizeSet( const Vector3& size )
193 {
194   // Factor in handle overshoot into size of backing
195   SetHitRegion( Vector2( size.x, GetHitRegion().y ) );
196   SetBackingRegion( Vector2( size.x - GetHandleRegion().x, GetBackingRegion().y ) );
197 }
198
199 bool Slider::OnTouchEvent(Actor actor, const TouchEvent& event)
200 {
201   if( mState != DISABLED )
202   {
203     TouchPoint::State touchState = event.GetPoint(0).state;
204
205     if( touchState == TouchPoint::Down )
206     {
207       mState = PRESSED;
208
209       float percentage = MapPercentage( event.GetPoint(0).local );
210       float value = MapBounds( ( GetSnapToMarks() ) ? SnapToMark( percentage ) : MarkFilter( percentage ), GetLowerBound(), GetUpperBound() );
211       SetValue( value );
212       DisplayPopup( value );
213     }
214     else if( touchState == TouchPoint::Up)
215     {
216       if( mState == PRESSED )
217       {
218         mState = NORMAL;
219         mSlidingFinishedSignal.Emit( Toolkit::Slider::DownCast( Self() ), Self().GetProperty( mPropertyValue ).Get<float>());
220       }
221     }
222   }
223
224   return true;
225 }
226
227 void Slider::OnPan( Actor actor, PanGesture gesture )
228 {
229   // gesture.position is in local actor coordinates
230   if( mState != DISABLED )
231   {
232     switch( gesture.state )
233     {
234       case Gesture::Continuing:
235       {
236         if( mState == PRESSED )
237         {
238           float value = MapBounds( MarkFilter ( MapPercentage( gesture.position ) ), GetLowerBound(), GetUpperBound() );
239           SetValue( value );
240           DisplayPopup( value );
241         }
242         break;
243       }
244       case Gesture::Finished:
245       {
246         if( mState == PRESSED  )
247         {
248           if( GetSnapToMarks() )
249           {
250             float value = MapBounds( SnapToMark( MapPercentage( gesture.position ) ), GetLowerBound(), GetUpperBound() );
251             SetValue( value );
252             DisplayPopup( value );
253           }
254           mSlidingFinishedSignal.Emit( Toolkit::Slider::DownCast( Self() ), Self().GetProperty( mPropertyValue ).Get<float>());
255         }
256
257         mState = NORMAL;
258         break;
259       }
260       default:
261       {
262         break;
263       }
264     }
265   }
266 }
267
268 float Slider::HitSpaceToDomain( float x )
269 {
270   float halfRegionWidth = GetHitRegion().x * 0.5f;
271   float halfDomainWidth = ( mDomain.to.x - mDomain.from.x ) * 0.5f;
272   float endDiff = halfRegionWidth - halfDomainWidth;
273
274   return x - endDiff;
275 }
276
277 float Slider::MapPercentage( const Vector2& point )
278 {
279   return Clamp( ( HitSpaceToDomain( point.x ) - mDomain.from.x ) / ( mDomain.to.x - mDomain.from.x ), 0.0f, 1.0f );
280 }
281
282 float Slider::MapValuePercentage( float value )
283 {
284   return ( value - GetLowerBound() ) / ( GetUpperBound() - GetLowerBound() );
285 }
286
287 float Slider::MapBounds( float percent, float lowerBound, float upperBound )
288 {
289   return lowerBound + percent * ( upperBound - lowerBound );
290 }
291
292 Slider::Domain Slider::CalcDomain( const Vector2& currentSize )
293 {
294    return Domain( Vector2( 0.0f, 0.0f ), currentSize );
295 }
296
297 void Slider::DisplayValue( float value, bool raiseSignals )
298 {
299   float clampledValue = Clamp( value, GetLowerBound(), GetUpperBound() );
300
301   float percent = MapValuePercentage( clampledValue );
302
303   float x = mDomain.from.x + percent * ( mDomain.to.x - mDomain.from.x );
304
305   mHandle.SetPosition( x, 0.0f, HANDLE_Z );
306
307   // Progress bar
308   if( mProgress )
309   {
310     if( clampledValue > 0.0f )
311     {
312       mProgress.SetVisible( true ); // Deliberately set this in case multiple SetValues are fired at once
313       mProgress.SetSize( x, GetBackingRegion().y );
314     }
315     else
316     {
317       mProgress.SetVisible( false );
318     }
319   }
320
321   // Signals
322   if( raiseSignals )
323   {
324     Toolkit::Slider self = Toolkit::Slider::DownCast( Self() );
325     mValueChangedSignal.Emit( self, clampledValue );
326
327     int markIndex;
328     if( MarkReached( percent, markIndex ) )
329     {
330       mMarkSignal.Emit( self, markIndex );
331     }
332   }
333
334   if( mHandleValueTextView )
335   {
336     std::stringstream ss;
337     ss.precision( GetValuePrecision() );
338     ss << fixed << clampledValue;
339     mHandleValueTextView.SetText( ss.str() );
340   }
341 }
342
343 void Slider::SetMarks( const MarkList& marks )
344 {
345   float value;
346   for( MarkList::const_iterator it = marks.begin(), itEnd = marks.end(); it != itEnd; ++it )
347   {
348     const Property::Value& propertyValue = *it;
349     propertyValue.Get( value );
350
351     mMarks.push_back( value );
352   }
353 }
354
355 const Slider::MarkList& Slider::GetMarks() const
356 {
357   return mMarks;
358 }
359
360 bool Slider::GetSnapToMarks() const
361 {
362   return Self().GetProperty<bool>( mPropertySnapToMarks );
363 }
364
365 Actor Slider::CreateHitRegion()
366 {
367   Actor hitRegion = Actor::New();
368   hitRegion.SetParentOrigin( ParentOrigin::CENTER );
369   hitRegion.SetAnchorPoint( AnchorPoint::CENTER );
370   hitRegion.TouchedSignal().Connect( this, &Slider::OnTouchEvent );
371
372   return hitRegion;
373 }
374
375 ImageActor Slider::CreateBacking()
376 {
377   ImageActor backing = ImageActor::New();
378   backing.SetParentOrigin( ParentOrigin::CENTER );
379   backing.SetAnchorPoint( AnchorPoint::CENTER );
380   backing.SetZ( BACKING_Z );
381
382   return backing;
383 }
384
385 void Slider::CreateBackingImage( const std::string& imageName )
386 {
387   if( mBacking && imageName != String::EMPTY )
388   {
389     Image image = Image::New( imageName );
390     mBacking.SetImage( image );
391   }
392 }
393
394 void Slider::SetBackingScale9( const Vector4& border )
395 {
396   if( mBacking )
397   {
398     mBacking.SetStyle( ImageActor::STYLE_NINE_PATCH );
399     mBacking.SetNinePatchBorder( border );
400   }
401 }
402
403 void Slider::SetBackingRegionSize( const Vector2& region )
404 {
405   if( mBacking )
406   {
407     mBacking.SetSize( region );
408   }
409 }
410
411 ImageActor Slider::CreateProgress()
412 {
413   ImageActor progress = ImageActor::New();
414   progress.SetParentOrigin( ParentOrigin::CENTER_LEFT );
415   progress.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
416   progress.SetZ( PROGRESS_Z );
417
418   return progress;
419 }
420
421 void Slider::CreateProgressImage( const std::string& imageName )
422 {
423   if( mProgress && imageName != String::EMPTY )
424   {
425     Image image = Image::New( imageName );
426     mProgress.SetImage( image );
427   }
428 }
429
430 void Slider::CreatePopupImage( const std::string& imageName )
431 {
432   if( mPopup && imageName != String::EMPTY )
433   {
434     Image image = Image::New( imageName );
435     mPopup.SetImage( image );
436   }
437 }
438
439 void Slider::CreatePopupArrowImage( const std::string& imageName )
440 {
441   if( mPopupArrow && imageName != String::EMPTY )
442   {
443     Image image = Image::New( imageName );
444     mPopupArrow.SetImage( image );
445   }
446 }
447
448 void Slider::SetProgressScale9( const Vector4& border )
449 {
450   if( mProgress )
451   {
452     mProgress.SetStyle( ImageActor::STYLE_NINE_PATCH );
453     mProgress.SetNinePatchBorder( border );
454   }
455 }
456
457 void Slider::SetPopupScale9( const Vector4& border )
458 {
459   if( mPopup )
460   {
461     mPopup.SetStyle( ImageActor::STYLE_NINE_PATCH );
462     mPopup.SetNinePatchBorder( border );
463   }
464 }
465
466 void Slider::ResizeProgressRegion( const Vector2& region )
467 {
468   if( mProgress )
469   {
470     mProgress.SetSize( region );
471   }
472 }
473
474 ImageActor Slider::CreateHandle()
475 {
476   ImageActor handle = ImageActor::New();
477   handle.SetParentOrigin( ParentOrigin::CENTER_LEFT );
478   handle.SetAnchorPoint( AnchorPoint::CENTER );
479   handle.SetZ( HANDLE_Z );
480
481   return handle;
482 }
483
484 ImageActor Slider::CreatePopupArrow()
485 {
486   ImageActor arrow = ImageActor::New();
487   arrow.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
488   arrow.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
489   arrow.SetZ( HANDLE_Z );
490
491   return arrow;
492 }
493
494 Toolkit::TextView Slider::CreatePopupText()
495 {
496   Toolkit::TextView textView = Toolkit::TextView::New();
497   textView.SetParentOrigin( ParentOrigin::CENTER );
498   textView.SetAnchorPoint( AnchorPoint::CENTER );
499   textView.SetSizePolicy( Control::Flexible, Control::Flexible );
500   textView.SetZ( VALUE_DISPLAY_TEXT_Z );
501   return textView;
502 }
503
504 ImageActor Slider::CreatePopup()
505 {
506   ImageActor popup = ImageActor::New();
507   popup.SetParentOrigin( ParentOrigin::TOP_CENTER );
508   popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
509
510   mValueTextView = CreatePopupText();
511   popup.Add( mValueTextView );
512
513   return popup;
514 }
515
516 void Slider::CreateHandleImage( const std::string& imageName )
517 {
518   if( mHandle && imageName != String::EMPTY )
519   {
520     Image image = Image::New( imageName );
521     mHandle.SetImage( image );
522   }
523 }
524
525 void Slider::ResizeHandleRegion( const Vector2& region )
526 {
527   if( mHandle )
528   {
529     mHandle.SetSize( region );
530   }
531 }
532
533 void Slider::CreateHandleValueDisplay()
534 {
535   if( mHandle && !mHandleValueTextView )
536   {
537     mHandleValueTextView = Toolkit::TextView::New();
538     mHandleValueTextView.SetParentOrigin( ParentOrigin::CENTER );
539     mHandleValueTextView.SetAnchorPoint( AnchorPoint::CENTER );
540     mHandleValueTextView.SetSize( GetHandleRegion() );
541     mHandleValueTextView.SetZ( HANDLE_VALUE_DISPLAY_TEXT_Z );
542     mHandle.Add( mHandleValueTextView );
543   }
544 }
545
546 void Slider::DestroyHandleValueDisplay()
547 {
548   if(mHandleValueTextView)
549   {
550     mHandleValueTextView.Unparent();
551     mHandleValueTextView.Reset();
552   }
553 }
554
555 void Slider::UpdatePopupTextColor( const Vector4& color )
556 {
557   if( mValueTextView )
558   {
559     mValueTextView.SetColor( color );
560   }
561 }
562
563 Actor Slider::CreateValueDisplay()
564 {
565   Actor popup = Actor::New();
566   popup.SetParentOrigin( ParentOrigin::TOP_CENTER );
567   popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
568
569   mPopupArrow = CreatePopupArrow();
570   popup.Add( mPopupArrow );
571
572   mPopup = CreatePopup();
573   mPopup.SetSize( 0.0f, VALUE_POPUP_HEIGHT );
574   mPopupArrow.Add( mPopup );
575
576   return popup;
577 }
578
579 Toolkit::Slider::ValueChangedSignalType& Slider::ValueChangedSignal()
580 {
581   return mValueChangedSignal;
582 }
583
584 Toolkit::Slider::ValueChangedSignalType& Slider::SlidingFinishedSignal()
585 {
586   return mSlidingFinishedSignal;
587 }
588
589 Toolkit::Slider::MarkSignalType& Slider::MarkSignal()
590 {
591   return mMarkSignal;
592 }
593
594 void Slider::UpdateSkin()
595 {
596   switch( mState )
597   {
598     case NORMAL:
599     {
600       mBacking.SetColor( Color::WHITE );
601       mHandle.SetColor( Color::WHITE );
602       mProgress.SetColor( Color::WHITE );
603       break;
604     }
605     case DISABLED:
606     {
607       Vector4 disableColor = GetDisableColor();
608       mBacking.SetColor( disableColor );
609       mHandle.SetColor( disableColor );
610       mProgress.SetColor( disableColor );
611       break;
612     }
613     case PRESSED:
614     {
615       break;
616     }
617     case FOCUSED:
618     {
619       break;
620     }
621   }
622 }
623
624 void Slider::CreateChildren()
625 {
626   Actor self = Self();
627
628   // Hit region
629   mHitArea = CreateHitRegion();
630   mPanDetector = PanGestureDetector::New();
631   mPanDetector.Attach( mHitArea );
632   mPanDetector.DetectedSignal().Connect( this, &Slider::OnPan );
633   self.Add( mHitArea );
634
635   // Background
636   mBacking = CreateBacking();
637   self.Add( mBacking );
638
639   // Progress bar
640   mProgress = CreateProgress();
641   mBacking.Add( mProgress );
642
643   // Handle
644   mHandle = CreateHandle();
645   mBacking.Add( mHandle );
646 }
647
648 void Slider::ResizeHitRegion( const Vector2& size )
649 {
650   if( mHitArea )
651   {
652     mHitArea.SetSize( size );
653   }
654 }
655
656 void Slider::AddPopup()
657 {
658   if( !mValueDisplay )
659   {
660     mValueDisplay = CreateValueDisplay();
661     mValueDisplay.SetVisible( false );
662     mHandle.Add( mValueDisplay );
663
664     Actor self = Self();
665     CreatePopupImage( self.GetProperty<std::string>( mPropertyPopupImageName ) );
666     SetPopupScale9( GetPopupScale9Border() );
667     CreatePopupArrowImage( self.GetProperty<std::string>( mPropertyPopupArrowImageName )  );
668
669     mValueTimer = Timer::New( VALUE_VIEW_SHOW_DURATION );
670     mValueTimer.TickSignal().Connect( this, &Slider::HideValueView );
671   }
672 }
673
674 void Slider::RemovePopup()
675 {
676   if( mValueDisplay )
677   {
678     mPopup.Unparent();
679     mPopup.Reset();
680
681     mPopupArrow.Unparent();
682     mPopupArrow.Reset();
683
684     mValueDisplay.Unparent();
685     mValueDisplay.Reset();
686
687     mValueTimer.TickSignal().Disconnect( this, &Slider::HideValueView );
688     mValueTimer.Reset();
689   }
690 }
691
692
693 float Slider::MarkFilter( float value )
694 {
695   const float MARK_TOLERANCE = GetMarkTolerance();
696
697   float mark;
698   for( MarkList::iterator it = mMarks.begin(), itEnd = mMarks.end(); it != itEnd; ++it )
699   {
700     const Property::Value& propertyValue = *it;
701     propertyValue.Get( mark );
702     mark = MapValuePercentage( mark );
703
704     // If close to a mark, return the mark
705     if( fabsf( mark - value ) < MARK_TOLERANCE )
706     {
707       return mark;
708     }
709   }
710
711   return value;
712 }
713
714 float Slider::SnapToMark( float value )
715 {
716   float closestMark = value;
717   float closestDist = std::numeric_limits<float>::max();
718
719   float mark;
720   for( MarkList::iterator it = mMarks.begin(), itEnd = mMarks.end(); it != itEnd; ++it )
721   {
722     const Property::Value& propertyValue = *it;
723     propertyValue.Get( mark );
724     mark = MapValuePercentage( mark );
725
726     float dist = fabsf( mark - value );
727     if( dist < closestDist )
728     {
729       closestDist = dist;
730       closestMark = mark;
731     }
732   }
733
734   return closestMark;
735 }
736
737 bool Slider::MarkReached( float value, int& outIndex )
738 {
739   const float MARK_TOLERANCE = GetMarkTolerance();
740
741   // Binary search
742   int head = 0,
743       tail = mMarks.size() - 1;
744   int current;
745   float mark;
746
747   while( head <= tail )
748   {
749     current = head + ( tail - head ) / 2;
750
751     const Property::Value& propertyValue = mMarks[ current ];
752     propertyValue.Get( mark );
753     mark = MapValuePercentage( mark );
754
755     if( fabsf( mark - value ) < MARK_TOLERANCE )
756     {
757       outIndex = current;
758       return true;
759     }
760     else
761     {
762       if( value < mark )
763       {
764         tail = current - 1;
765       }
766       else
767       {
768         head = current + 1;
769       }
770     }
771   }
772
773   return false;
774 }
775
776 bool Slider::HideValueView()
777 {
778   if( mValueDisplay )
779   {
780     mValueDisplay.SetVisible( false );
781   }
782
783   return false;
784 }
785
786 void Slider::OnPropertySet( Property::Index index, Property::Value propertyValue )
787 {
788   if( index == mPropertyLowerBound )
789   {
790     UpdateLowerBound( propertyValue.Get<float>() );
791   }
792   else if( index == mPropertyUpperBound )
793   {
794     UpdateUpperBound( propertyValue.Get<float>() );
795   }
796   else if( index == mPropertyValue )
797   {
798     DisplayValue( propertyValue.Get<float>(), true );
799   }
800   else if( index == mPropertyHitRegion )
801   {
802     ResizeHitRegion( propertyValue.Get<Vector2>() );
803   }
804   else if( index == mPropertyBackingRegion )
805   {
806     ResizeBackingRegion( propertyValue.Get<Vector2>() );
807   }
808   else if( index == mPropertyHandleRegion )
809   {
810     UpdateHandleRegion( propertyValue.Get<Vector2>() );
811   }
812   else if( index == mPropertyBackingImageName )
813   {
814     CreateBackingImage( propertyValue.Get<std::string>() );
815   }
816   else if( index == mPropertyHandleImageName )
817   {
818     CreateHandleImage( propertyValue.Get<std::string>() );
819   }
820   else if( index == mPropertyProgressImageName )
821   {
822     CreateProgressImage( propertyValue.Get<std::string>() );
823   }
824   else if( index == mPropertyPopupImageName )
825   {
826     CreatePopupImage( propertyValue.Get<std::string>() );
827   }
828   else if( index == mPropertyPopupArrowImageName )
829   {
830     CreatePopupArrowImage( propertyValue.Get<std::string>() );
831   }
832   else if( index == mPropertyBackingScale9Border )
833   {
834     SetBackingScale9( propertyValue.Get<Vector4>() );
835   }
836   else if( index == mPropertyProgressScale9Border )
837   {
838     SetProgressScale9( propertyValue.Get<Vector4>() );
839   }
840   else if( index == mPropertyPopupScale9Border )
841   {
842     SetPopupScale9( propertyValue.Get<Vector4>() );
843   }
844   else if( index == mPropertyDisableColor )
845   {
846     UpdateSkin();
847   }
848   else if( index == mPropertyPopupTextColor )
849   {
850     UpdatePopupTextColor( propertyValue.Get<Vector4>() );
851   }
852   else if( index == mPropertyValuePrecision )
853   {
854     DisplayValue( GetValue(), false );
855   }
856   else if( index == mPropertyShowPopup )
857   {
858     ShowPopup( propertyValue.Get<bool>() );
859   }
860   else if( index == mPropertyShowValue )
861   {
862     ShowValue( propertyValue.Get<bool>() );
863   }
864   else if( index == mPropertyEnabled )
865   {
866     SetEnabled( propertyValue.Get<bool>() );
867   }
868   else if( index == mPropertyMarks )
869   {
870     SetMarks( propertyValue.Get<Property::Array>() );
871   }
872   else if( index == mPropertySnapToMarks )
873   {
874     // Nothing
875   }
876   else if( index == mPropertyMarkTolerance )
877   {
878     // Nothing
879   }
880 }
881
882 void Slider::UpdateLowerBound( float bound )
883 {
884   DisplayValue( GetValue(), false );
885 }
886
887 float Slider::GetLowerBound() const
888 {
889   return Self().GetProperty<float>( mPropertyLowerBound );
890 }
891
892 void Slider::UpdateUpperBound( float bound )
893 {
894   DisplayValue( GetValue(), false );
895 }
896
897 float Slider::GetUpperBound() const
898 {
899   return Self().GetProperty<float>( mPropertyUpperBound );
900 }
901
902 void Slider::SetValue( float value )
903 {
904   Self().SetProperty( mPropertyValue, value );
905 }
906
907 float Slider::GetValue() const
908 {
909   return Self().GetProperty<float>( mPropertyValue );
910 }
911
912 void Slider::SetHitRegion( const Vector2& region )
913 {
914   Self().SetProperty( mPropertyHitRegion, region );
915 }
916
917 Vector2 Slider::GetHitRegion() const
918 {
919   return Self().GetProperty<Vector2>( mPropertyHitRegion );
920 }
921
922 void Slider::SetBackingRegion( const Vector2& region )
923 {
924   Self().SetProperty( mPropertyBackingRegion, region );
925 }
926
927 void Slider::ResizeBackingRegion( const Vector2& region )
928 {
929   SetBackingRegionSize( region );
930   ResizeProgressRegion( Vector2( 0.0f, region.y ) );
931
932   mDomain = CalcDomain( region );
933
934   DisplayValue( GetValue(), false );  // Set the progress bar to correct width
935 }
936
937 Vector2 Slider::GetBackingRegion() const
938 {
939   return Self().GetProperty<Vector2>( mPropertyBackingRegion );
940 }
941
942 void Slider::UpdateHandleRegion( const Vector2& region )
943 {
944   ResizeHandleRegion( region );
945
946   Vector2 hitRegion = GetHitRegion();
947   hitRegion.x += region.x;
948   SetHitRegion( hitRegion );
949 }
950
951 Vector2 Slider::GetHandleRegion() const
952 {
953   return Self().GetProperty<Vector2>( mPropertyHandleRegion );
954 }
955
956 Vector4 Slider::GetBackingScale9Border() const
957 {
958   return Self().GetProperty<Vector4>( mPropertyBackingScale9Border );
959 }
960
961 Vector4 Slider::GetPopupScale9Border() const
962 {
963   return Self().GetProperty<Vector4>( mPropertyPopupScale9Border );
964 }
965
966 Vector4 Slider::GetDisableColor() const
967 {
968   return Self().GetProperty<Vector4>( mPropertyDisableColor );
969 }
970
971 Vector4 Slider::GetPopupTextColor() const
972 {
973   return Self().GetProperty<Vector4>( mPropertyPopupTextColor );
974 }
975
976 int Slider::GetValuePrecision() const
977 {
978   return Self().GetProperty<int>( mPropertyValuePrecision );
979 }
980
981 void Slider::ShowPopup( bool showPopup )
982 {
983   // Value display
984   if( showPopup )
985   {
986     AddPopup();
987   }
988   else
989   {
990     RemovePopup();
991   }
992 }
993
994 bool Slider::GetShowPopup() const
995 {
996   return Self().GetProperty<bool>( mPropertyShowPopup );
997 }
998
999 void Slider::ShowValue( bool showValue )
1000 {
1001   if( showValue )
1002   {
1003     CreateHandleValueDisplay();
1004   }
1005   else
1006   {
1007     DestroyHandleValueDisplay();
1008   }
1009 }
1010
1011 bool Slider::GetShowValue() const
1012 {
1013   return Self().GetProperty<bool>( mPropertyShowValue );
1014 }
1015
1016 void Slider::SetEnabled( bool enabled )
1017 {
1018   if( enabled )
1019   {
1020     mState = NORMAL;
1021   }
1022   else
1023   {
1024     mState = DISABLED;
1025   }
1026
1027   UpdateSkin();
1028 }
1029
1030 bool Slider::IsEnabled() const
1031 {
1032   return mState != DISABLED;
1033 }
1034
1035 float Slider::GetMarkTolerance() const
1036 {
1037   return Self().GetProperty<float>( mPropertyMarkTolerance );
1038 }
1039
1040 // static class method to support script connecting signals
1041
1042 bool Slider::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1043 {
1044   Dali::BaseHandle handle( object );
1045
1046   bool connected = true;
1047   Toolkit::Slider slider = Toolkit::Slider::DownCast( handle );
1048
1049   if( signalName == Dali::Toolkit::Slider::SIGNAL_VALUE_CHANGED )
1050   {
1051     slider.ValueChangedSignal().Connect( tracker, functor );
1052   }
1053   else if( signalName == Dali::Toolkit::Slider::SIGNAL_MARK )
1054   {
1055     slider.MarkSignal().Connect( tracker, functor );
1056   }
1057  else
1058   {
1059     // signalName does not match any signal
1060     connected = false;
1061   }
1062
1063   return connected;
1064 }
1065
1066 void Slider::DisplayPopup( float value )
1067 {
1068   // Value display
1069   if( mValueTextView )
1070   {
1071     std::stringstream ss;
1072     ss.precision( GetValuePrecision() );
1073     ss << fixed << value;
1074     mValueTextView.SetText( ss.str() );
1075     TextStyle style;
1076     style.SetTextColor( GetPopupTextColor() );
1077     mValueTextView.SetStyleToCurrentText( style, TextStyle::COLOR);
1078
1079     if( mValueDisplay )
1080     {
1081       Font font = Font::New();
1082       float popupWidth = font.MeasureText( ss.str() ).x + VALUE_POPUP_MARGIN * 2.0f;
1083       if( popupWidth < VALUE_POPUP_MIN_WIDTH )
1084       {
1085         popupWidth = VALUE_POPUP_MIN_WIDTH;
1086       }
1087
1088       mPopup.SetSize( popupWidth, VALUE_POPUP_HEIGHT );
1089       mValueDisplay.SetVisible( true );
1090
1091       mValueTimer.SetInterval( VALUE_VIEW_SHOW_DURATION );
1092     }
1093   }
1094 }
1095
1096
1097 } // namespace Internal
1098
1099 } // namespace Toolkit
1100
1101 } // namespace Dali