(Slider)Added SlidingFinishedSignal
[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   mHandleValueTextView.Unparent();
549   mHandleValueTextView.Reset();
550 }
551
552 void Slider::UpdatePopupTextColor( const Vector4& color )
553 {
554   if( mValueTextView )
555   {
556     mValueTextView.SetColor( color );
557   }
558 }
559
560 Actor Slider::CreateValueDisplay()
561 {
562   Actor popup = Actor::New();
563   popup.SetParentOrigin( ParentOrigin::TOP_CENTER );
564   popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
565
566   mPopupArrow = CreatePopupArrow();
567   popup.Add( mPopupArrow );
568
569   mPopup = CreatePopup();
570   mPopup.SetSize( 0.0f, VALUE_POPUP_HEIGHT );
571   mPopupArrow.Add( mPopup );
572
573   return popup;
574 }
575
576 Toolkit::Slider::ValueChangedSignalType& Slider::ValueChangedSignal()
577 {
578   return mValueChangedSignal;
579 }
580
581 Toolkit::Slider::ValueChangedSignalType& Slider::SlidingFinishedSignal()
582 {
583   return mSlidingFinishedSignal;
584 }
585
586 Toolkit::Slider::MarkSignalType& Slider::MarkSignal()
587 {
588   return mMarkSignal;
589 }
590
591 void Slider::UpdateSkin()
592 {
593   switch( mState )
594   {
595     case NORMAL:
596     {
597       mBacking.SetColor( Color::WHITE );
598       mHandle.SetColor( Color::WHITE );
599       mProgress.SetColor( Color::WHITE );
600       break;
601     }
602     case DISABLED:
603     {
604       Vector4 disableColor = GetDisableColor();
605       mBacking.SetColor( disableColor );
606       mHandle.SetColor( disableColor );
607       mProgress.SetColor( disableColor );
608       break;
609     }
610     case PRESSED:
611     {
612       break;
613     }
614     case FOCUSED:
615     {
616       break;
617     }
618   }
619 }
620
621 void Slider::CreateChildren()
622 {
623   Actor self = Self();
624
625   // Hit region
626   mHitArea = CreateHitRegion();
627   mPanDetector = PanGestureDetector::New();
628   mPanDetector.Attach( mHitArea );
629   mPanDetector.DetectedSignal().Connect( this, &Slider::OnPan );
630   self.Add( mHitArea );
631
632   // Background
633   mBacking = CreateBacking();
634   self.Add( mBacking );
635
636   // Progress bar
637   mProgress = CreateProgress();
638   mBacking.Add( mProgress );
639
640   // Handle
641   mHandle = CreateHandle();
642   mBacking.Add( mHandle );
643 }
644
645 void Slider::ResizeHitRegion( const Vector2& size )
646 {
647   if( mHitArea )
648   {
649     mHitArea.SetSize( size );
650   }
651 }
652
653 void Slider::AddPopup()
654 {
655   if( !mValueDisplay )
656   {
657     mValueDisplay = CreateValueDisplay();
658     mValueDisplay.SetVisible( false );
659     mHandle.Add( mValueDisplay );
660
661     Actor self = Self();
662     CreatePopupImage( self.GetProperty<std::string>( mPropertyPopupImageName ) );
663     SetPopupScale9( GetPopupScale9Border() );
664     CreatePopupArrowImage( self.GetProperty<std::string>( mPropertyPopupArrowImageName )  );
665
666     mValueTimer = Timer::New( VALUE_VIEW_SHOW_DURATION );
667     mValueTimer.TickSignal().Connect( this, &Slider::HideValueView );
668   }
669 }
670
671 void Slider::RemovePopup()
672 {
673   if( mValueDisplay )
674   {
675     mPopup.Unparent();
676     mPopup.Reset();
677
678     mPopupArrow.Unparent();
679     mPopupArrow.Reset();
680
681     mValueDisplay.Unparent();
682     mValueDisplay.Reset();
683
684     mValueTimer.TickSignal().Disconnect( this, &Slider::HideValueView );
685     mValueTimer.Reset();
686   }
687 }
688
689
690 float Slider::MarkFilter( float value )
691 {
692   const float MARK_TOLERANCE = GetMarkTolerance();
693
694   float mark;
695   for( MarkList::iterator it = mMarks.begin(), itEnd = mMarks.end(); it != itEnd; ++it )
696   {
697     const Property::Value& propertyValue = *it;
698     propertyValue.Get( mark );
699     mark = MapValuePercentage( mark );
700
701     // If close to a mark, return the mark
702     if( fabsf( mark - value ) < MARK_TOLERANCE )
703     {
704       return mark;
705     }
706   }
707
708   return value;
709 }
710
711 float Slider::SnapToMark( float value )
712 {
713   float closestMark = value;
714   float closestDist = std::numeric_limits<float>::max();
715
716   float mark;
717   for( MarkList::iterator it = mMarks.begin(), itEnd = mMarks.end(); it != itEnd; ++it )
718   {
719     const Property::Value& propertyValue = *it;
720     propertyValue.Get( mark );
721     mark = MapValuePercentage( mark );
722
723     float dist = fabsf( mark - value );
724     if( dist < closestDist )
725     {
726       closestDist = dist;
727       closestMark = mark;
728     }
729   }
730
731   return closestMark;
732 }
733
734 bool Slider::MarkReached( float value, int& outIndex )
735 {
736   const float MARK_TOLERANCE = GetMarkTolerance();
737
738   // Binary search
739   int head = 0,
740       tail = mMarks.size() - 1;
741   int current;
742   float mark;
743
744   while( head <= tail )
745   {
746     current = head + ( tail - head ) / 2;
747
748     const Property::Value& propertyValue = mMarks[ current ];
749     propertyValue.Get( mark );
750     mark = MapValuePercentage( mark );
751
752     if( fabsf( mark - value ) < MARK_TOLERANCE )
753     {
754       outIndex = current;
755       return true;
756     }
757     else
758     {
759       if( value < mark )
760       {
761         tail = current - 1;
762       }
763       else
764       {
765         head = current + 1;
766       }
767     }
768   }
769
770   return false;
771 }
772
773 bool Slider::HideValueView()
774 {
775   if( mValueDisplay )
776   {
777     mValueDisplay.SetVisible( false );
778   }
779
780   return false;
781 }
782
783 void Slider::OnPropertySet( Property::Index index, Property::Value propertyValue )
784 {
785   if( index == mPropertyLowerBound )
786   {
787     UpdateLowerBound( propertyValue.Get<float>() );
788   }
789   else if( index == mPropertyUpperBound )
790   {
791     UpdateUpperBound( propertyValue.Get<float>() );
792   }
793   else if( index == mPropertyValue )
794   {
795     DisplayValue( propertyValue.Get<float>(), true );
796   }
797   else if( index == mPropertyHitRegion )
798   {
799     ResizeHitRegion( propertyValue.Get<Vector2>() );
800   }
801   else if( index == mPropertyBackingRegion )
802   {
803     ResizeBackingRegion( propertyValue.Get<Vector2>() );
804   }
805   else if( index == mPropertyHandleRegion )
806   {
807     UpdateHandleRegion( propertyValue.Get<Vector2>() );
808   }
809   else if( index == mPropertyBackingImageName )
810   {
811     CreateBackingImage( propertyValue.Get<std::string>() );
812   }
813   else if( index == mPropertyHandleImageName )
814   {
815     CreateHandleImage( propertyValue.Get<std::string>() );
816   }
817   else if( index == mPropertyProgressImageName )
818   {
819     CreateProgressImage( propertyValue.Get<std::string>() );
820   }
821   else if( index == mPropertyPopupImageName )
822   {
823     CreatePopupImage( propertyValue.Get<std::string>() );
824   }
825   else if( index == mPropertyPopupArrowImageName )
826   {
827     CreatePopupArrowImage( propertyValue.Get<std::string>() );
828   }
829   else if( index == mPropertyBackingScale9Border )
830   {
831     SetBackingScale9( propertyValue.Get<Vector4>() );
832   }
833   else if( index == mPropertyProgressScale9Border )
834   {
835     SetProgressScale9( propertyValue.Get<Vector4>() );
836   }
837   else if( index == mPropertyPopupScale9Border )
838   {
839     SetPopupScale9( propertyValue.Get<Vector4>() );
840   }
841   else if( index == mPropertyDisableColor )
842   {
843     UpdateSkin();
844   }
845   else if( index == mPropertyPopupTextColor )
846   {
847     UpdatePopupTextColor( propertyValue.Get<Vector4>() );
848   }
849   else if( index == mPropertyValuePrecision )
850   {
851     DisplayValue( GetValue(), false );
852   }
853   else if( index == mPropertyShowPopup )
854   {
855     ShowPopup( propertyValue.Get<bool>() );
856   }
857   else if( index == mPropertyShowValue )
858   {
859     ShowValue( propertyValue.Get<bool>() );
860   }
861   else if( index == mPropertyEnabled )
862   {
863     SetEnabled( propertyValue.Get<bool>() );
864   }
865   else if( index == mPropertyMarks )
866   {
867     SetMarks( propertyValue.Get<Property::Array>() );
868   }
869   else if( index == mPropertySnapToMarks )
870   {
871     // Nothing
872   }
873   else if( index == mPropertyMarkTolerance )
874   {
875     // Nothing
876   }
877 }
878
879 void Slider::UpdateLowerBound( float bound )
880 {
881   DisplayValue( GetValue(), false );
882 }
883
884 float Slider::GetLowerBound() const
885 {
886   return Self().GetProperty<float>( mPropertyLowerBound );
887 }
888
889 void Slider::UpdateUpperBound( float bound )
890 {
891   DisplayValue( GetValue(), false );
892 }
893
894 float Slider::GetUpperBound() const
895 {
896   return Self().GetProperty<float>( mPropertyUpperBound );
897 }
898
899 void Slider::SetValue( float value )
900 {
901   Self().SetProperty( mPropertyValue, value );
902 }
903
904 float Slider::GetValue() const
905 {
906   return Self().GetProperty<float>( mPropertyValue );
907 }
908
909 void Slider::SetHitRegion( const Vector2& region )
910 {
911   Self().SetProperty( mPropertyHitRegion, region );
912 }
913
914 Vector2 Slider::GetHitRegion() const
915 {
916   return Self().GetProperty<Vector2>( mPropertyHitRegion );
917 }
918
919 void Slider::SetBackingRegion( const Vector2& region )
920 {
921   Self().SetProperty( mPropertyBackingRegion, region );
922 }
923
924 void Slider::ResizeBackingRegion( const Vector2& region )
925 {
926   SetBackingRegionSize( region );
927   ResizeProgressRegion( Vector2( 0.0f, region.y ) );
928
929   mDomain = CalcDomain( region );
930
931   DisplayValue( GetValue(), false );  // Set the progress bar to correct width
932 }
933
934 Vector2 Slider::GetBackingRegion() const
935 {
936   return Self().GetProperty<Vector2>( mPropertyBackingRegion );
937 }
938
939 void Slider::UpdateHandleRegion( const Vector2& region )
940 {
941   ResizeHandleRegion( region );
942
943   Vector2 hitRegion = GetHitRegion();
944   hitRegion.x += region.x;
945   SetHitRegion( hitRegion );
946 }
947
948 Vector2 Slider::GetHandleRegion() const
949 {
950   return Self().GetProperty<Vector2>( mPropertyHandleRegion );
951 }
952
953 Vector4 Slider::GetBackingScale9Border() const
954 {
955   return Self().GetProperty<Vector4>( mPropertyBackingScale9Border );
956 }
957
958 Vector4 Slider::GetPopupScale9Border() const
959 {
960   return Self().GetProperty<Vector4>( mPropertyPopupScale9Border );
961 }
962
963 Vector4 Slider::GetDisableColor() const
964 {
965   return Self().GetProperty<Vector4>( mPropertyDisableColor );
966 }
967
968 Vector4 Slider::GetPopupTextColor() const
969 {
970   return Self().GetProperty<Vector4>( mPropertyPopupTextColor );
971 }
972
973 int Slider::GetValuePrecision() const
974 {
975   return Self().GetProperty<int>( mPropertyValuePrecision );
976 }
977
978 void Slider::ShowPopup( bool showPopup )
979 {
980   // Value display
981   if( showPopup )
982   {
983     AddPopup();
984   }
985   else
986   {
987     RemovePopup();
988   }
989 }
990
991 bool Slider::GetShowPopup() const
992 {
993   return Self().GetProperty<bool>( mPropertyShowPopup );
994 }
995
996 void Slider::ShowValue( bool showValue )
997 {
998   if( showValue )
999   {
1000     CreateHandleValueDisplay();
1001   }
1002   else
1003   {
1004     DestroyHandleValueDisplay();
1005   }
1006 }
1007
1008 bool Slider::GetShowValue() const
1009 {
1010   return Self().GetProperty<bool>( mPropertyShowValue );
1011 }
1012
1013 void Slider::SetEnabled( bool enabled )
1014 {
1015   if( enabled )
1016   {
1017     mState = NORMAL;
1018   }
1019   else
1020   {
1021     mState = DISABLED;
1022   }
1023
1024   UpdateSkin();
1025 }
1026
1027 bool Slider::IsEnabled() const
1028 {
1029   return mState != DISABLED;
1030 }
1031
1032 float Slider::GetMarkTolerance() const
1033 {
1034   return Self().GetProperty<float>( mPropertyMarkTolerance );
1035 }
1036
1037 // static class method to support script connecting signals
1038
1039 bool Slider::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1040 {
1041   Dali::BaseHandle handle( object );
1042
1043   bool connected = true;
1044   Toolkit::Slider slider = Toolkit::Slider::DownCast( handle );
1045
1046   if( signalName == Dali::Toolkit::Slider::SIGNAL_VALUE_CHANGED )
1047   {
1048     slider.ValueChangedSignal().Connect( tracker, functor );
1049   }
1050   else if( signalName == Dali::Toolkit::Slider::SIGNAL_MARK )
1051   {
1052     slider.MarkSignal().Connect( tracker, functor );
1053   }
1054  else
1055   {
1056     // signalName does not match any signal
1057     connected = false;
1058   }
1059
1060   return connected;
1061 }
1062
1063 void Slider::DisplayPopup( float value )
1064 {
1065   // Value display
1066   if( mValueTextView )
1067   {
1068     std::stringstream ss;
1069     ss.precision( GetValuePrecision() );
1070     ss << fixed << value;
1071     mValueTextView.SetText( ss.str() );
1072     TextStyle style;
1073     style.SetTextColor( GetPopupTextColor() );
1074     mValueTextView.SetStyleToCurrentText( style, TextStyle::COLOR);
1075
1076     if( mValueDisplay )
1077     {
1078       Font font = Font::New();
1079       float popupWidth = font.MeasureText( ss.str() ).x + VALUE_POPUP_MARGIN * 2.0f;
1080       if( popupWidth < VALUE_POPUP_MIN_WIDTH )
1081       {
1082         popupWidth = VALUE_POPUP_MIN_WIDTH;
1083       }
1084
1085       mPopup.SetSize( popupWidth, VALUE_POPUP_HEIGHT );
1086       mValueDisplay.SetVisible( true );
1087
1088       mValueTimer.SetInterval( VALUE_VIEW_SHOW_DURATION );
1089     }
1090   }
1091 }
1092
1093
1094 } // namespace Internal
1095
1096 } // namespace Toolkit
1097
1098 } // namespace Dali