6462e0ce39b728d717b703e75fba28a47a307229
[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   }
215
216   return true;
217 }
218
219 void Slider::OnPan( Actor actor, PanGesture gesture )
220 {
221   // gesture.position is in local actor coordinates
222   if( mState != DISABLED )
223   {
224     switch( gesture.state )
225     {
226       case Gesture::Continuing:
227       {
228         if( mState == PRESSED )
229         {
230           float value = MapBounds( MarkFilter ( MapPercentage( gesture.position ) ), GetLowerBound(), GetUpperBound() );
231           SetValue( value );
232           DisplayPopup( value );
233         }
234         break;
235       }
236       case Gesture::Finished:
237       {
238         if( mState == PRESSED && GetSnapToMarks() )
239         {
240           float value = MapBounds( SnapToMark( MapPercentage( gesture.position ) ), GetLowerBound(), GetUpperBound() );
241           SetValue( value );
242           DisplayPopup( value );
243         }
244
245         mState = NORMAL;
246         break;
247       }
248       default:
249       {
250         break;
251       }
252     }
253   }
254 }
255
256 float Slider::HitSpaceToDomain( float x )
257 {
258   float halfRegionWidth = GetHitRegion().x * 0.5f;
259   float halfDomainWidth = ( mDomain.to.x - mDomain.from.x ) * 0.5f;
260   float endDiff = halfRegionWidth - halfDomainWidth;
261
262   return x - endDiff;
263 }
264
265 float Slider::MapPercentage( const Vector2& point )
266 {
267   return Clamp( ( HitSpaceToDomain( point.x ) - mDomain.from.x ) / ( mDomain.to.x - mDomain.from.x ), 0.0f, 1.0f );
268 }
269
270 float Slider::MapValuePercentage( float value )
271 {
272   return ( value - GetLowerBound() ) / ( GetUpperBound() - GetLowerBound() );
273 }
274
275 float Slider::MapBounds( float percent, float lowerBound, float upperBound )
276 {
277   return lowerBound + percent * ( upperBound - lowerBound );
278 }
279
280 Slider::Domain Slider::CalcDomain( const Vector2& currentSize )
281 {
282    return Domain( Vector2( 0.0f, 0.0f ), currentSize );
283 }
284
285 void Slider::DisplayValue( float value, bool raiseSignals )
286 {
287   float clampledValue = Clamp( value, GetLowerBound(), GetUpperBound() );
288
289   float percent = MapValuePercentage( clampledValue );
290
291   float x = mDomain.from.x + percent * ( mDomain.to.x - mDomain.from.x );
292
293   mHandle.SetPosition( x, 0.0f, HANDLE_Z );
294
295   // Progress bar
296   if( mProgress )
297   {
298     if( clampledValue > 0.0f )
299     {
300       mProgress.SetVisible( true ); // Deliberately set this in case multiple SetValues are fired at once
301       mProgress.SetSize( x, GetBackingRegion().y );
302     }
303     else
304     {
305       mProgress.SetVisible( false );
306     }
307   }
308
309   // Signals
310   if( raiseSignals )
311   {
312     Toolkit::Slider self = Toolkit::Slider::DownCast( Self() );
313     mValueChangedSignal.Emit( self, clampledValue );
314
315     int markIndex;
316     if( MarkReached( percent, markIndex ) )
317     {
318       mMarkSignal.Emit( self, markIndex );
319     }
320   }
321
322   if( mHandleValueTextView )
323   {
324     std::stringstream ss;
325     ss.precision( GetValuePrecision() );
326     ss << fixed << clampledValue;
327     mHandleValueTextView.SetText( ss.str() );
328   }
329 }
330
331 void Slider::SetMarks( const MarkList& marks )
332 {
333   float value;
334   for( MarkList::const_iterator it = marks.begin(), itEnd = marks.end(); it != itEnd; ++it )
335   {
336     const Property::Value& propertyValue = *it;
337     propertyValue.Get( value );
338
339     mMarks.push_back( value );
340   }
341 }
342
343 const Slider::MarkList& Slider::GetMarks() const
344 {
345   return mMarks;
346 }
347
348 bool Slider::GetSnapToMarks() const
349 {
350   return Self().GetProperty<bool>( mPropertySnapToMarks );
351 }
352
353 Actor Slider::CreateHitRegion()
354 {
355   Actor hitRegion = Actor::New();
356   hitRegion.SetParentOrigin( ParentOrigin::CENTER );
357   hitRegion.SetAnchorPoint( AnchorPoint::CENTER );
358   hitRegion.TouchedSignal().Connect( this, &Slider::OnTouchEvent );
359
360   return hitRegion;
361 }
362
363 ImageActor Slider::CreateBacking()
364 {
365   ImageActor backing = ImageActor::New();
366   backing.SetParentOrigin( ParentOrigin::CENTER );
367   backing.SetAnchorPoint( AnchorPoint::CENTER );
368   backing.SetZ( BACKING_Z );
369
370   return backing;
371 }
372
373 void Slider::CreateBackingImage( const std::string& imageName )
374 {
375   if( mBacking && imageName != String::EMPTY )
376   {
377     Image image = Image::New( imageName );
378     mBacking.SetImage( image );
379   }
380 }
381
382 void Slider::SetBackingScale9( const Vector4& border )
383 {
384   if( mBacking )
385   {
386     mBacking.SetStyle( ImageActor::STYLE_NINE_PATCH );
387     mBacking.SetNinePatchBorder( border );
388   }
389 }
390
391 void Slider::SetBackingRegionSize( const Vector2& region )
392 {
393   if( mBacking )
394   {
395     mBacking.SetSize( region );
396   }
397 }
398
399 ImageActor Slider::CreateProgress()
400 {
401   ImageActor progress = ImageActor::New();
402   progress.SetParentOrigin( ParentOrigin::CENTER_LEFT );
403   progress.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
404   progress.SetZ( PROGRESS_Z );
405
406   return progress;
407 }
408
409 void Slider::CreateProgressImage( const std::string& imageName )
410 {
411   if( mProgress && imageName != String::EMPTY )
412   {
413     Image image = Image::New( imageName );
414     mProgress.SetImage( image );
415   }
416 }
417
418 void Slider::CreatePopupImage( const std::string& imageName )
419 {
420   if( mPopup && imageName != String::EMPTY )
421   {
422     Image image = Image::New( imageName );
423     mPopup.SetImage( image );
424   }
425 }
426
427 void Slider::CreatePopupArrowImage( const std::string& imageName )
428 {
429   if( mPopupArrow && imageName != String::EMPTY )
430   {
431     Image image = Image::New( imageName );
432     mPopupArrow.SetImage( image );
433   }
434 }
435
436 void Slider::SetProgressScale9( const Vector4& border )
437 {
438   if( mProgress )
439   {
440     mProgress.SetStyle( ImageActor::STYLE_NINE_PATCH );
441     mProgress.SetNinePatchBorder( border );
442   }
443 }
444
445 void Slider::SetPopupScale9( const Vector4& border )
446 {
447   if( mPopup )
448   {
449     mPopup.SetStyle( ImageActor::STYLE_NINE_PATCH );
450     mPopup.SetNinePatchBorder( border );
451   }
452 }
453
454 void Slider::ResizeProgressRegion( const Vector2& region )
455 {
456   if( mProgress )
457   {
458     mProgress.SetSize( region );
459   }
460 }
461
462 ImageActor Slider::CreateHandle()
463 {
464   ImageActor handle = ImageActor::New();
465   handle.SetParentOrigin( ParentOrigin::CENTER_LEFT );
466   handle.SetAnchorPoint( AnchorPoint::CENTER );
467   handle.SetZ( HANDLE_Z );
468
469   return handle;
470 }
471
472 ImageActor Slider::CreatePopupArrow()
473 {
474   ImageActor arrow = ImageActor::New();
475   arrow.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
476   arrow.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
477   arrow.SetZ( HANDLE_Z );
478
479   return arrow;
480 }
481
482 Toolkit::TextView Slider::CreatePopupText()
483 {
484   Toolkit::TextView textView = Toolkit::TextView::New();
485   textView.SetParentOrigin( ParentOrigin::CENTER );
486   textView.SetAnchorPoint( AnchorPoint::CENTER );
487   textView.SetSizePolicy( Control::Flexible, Control::Flexible );
488   textView.SetZ( VALUE_DISPLAY_TEXT_Z );
489   return textView;
490 }
491
492 ImageActor Slider::CreatePopup()
493 {
494   ImageActor popup = ImageActor::New();
495   popup.SetParentOrigin( ParentOrigin::TOP_CENTER );
496   popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
497
498   mValueTextView = CreatePopupText();
499   popup.Add( mValueTextView );
500
501   return popup;
502 }
503
504 void Slider::CreateHandleImage( const std::string& imageName )
505 {
506   if( mHandle && imageName != String::EMPTY )
507   {
508     Image image = Image::New( imageName );
509     mHandle.SetImage( image );
510   }
511 }
512
513 void Slider::ResizeHandleRegion( const Vector2& region )
514 {
515   if( mHandle )
516   {
517     mHandle.SetSize( region );
518   }
519 }
520
521 void Slider::CreateHandleValueDisplay()
522 {
523   if( mHandle && !mHandleValueTextView )
524   {
525     mHandleValueTextView = Toolkit::TextView::New();
526     mHandleValueTextView.SetParentOrigin( ParentOrigin::CENTER );
527     mHandleValueTextView.SetAnchorPoint( AnchorPoint::CENTER );
528     mHandleValueTextView.SetSize( GetHandleRegion() );
529     mHandleValueTextView.SetZ( HANDLE_VALUE_DISPLAY_TEXT_Z );
530     mHandle.Add( mHandleValueTextView );
531   }
532 }
533
534 void Slider::DestroyHandleValueDisplay()
535 {
536   mHandleValueTextView.Unparent();
537   mHandleValueTextView.Reset();
538 }
539
540 void Slider::UpdatePopupTextColor( const Vector4& color )
541 {
542   if( mValueTextView )
543   {
544     mValueTextView.SetColor( color );
545   }
546 }
547
548 Actor Slider::CreateValueDisplay()
549 {
550   Actor popup = Actor::New();
551   popup.SetParentOrigin( ParentOrigin::TOP_CENTER );
552   popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
553
554   mPopupArrow = CreatePopupArrow();
555   popup.Add( mPopupArrow );
556
557   mPopup = CreatePopup();
558   mPopup.SetSize( 0.0f, VALUE_POPUP_HEIGHT );
559   mPopupArrow.Add( mPopup );
560
561   return popup;
562 }
563
564 Toolkit::Slider::ValueChangedSignalType& Slider::ValueChangedSignal()
565 {
566   return mValueChangedSignal;
567 }
568
569 Toolkit::Slider::MarkSignalType& Slider::MarkSignal()
570 {
571   return mMarkSignal;
572 }
573
574 void Slider::UpdateSkin()
575 {
576   switch( mState )
577   {
578     case NORMAL:
579     {
580       mBacking.SetColor( Color::WHITE );
581       mHandle.SetColor( Color::WHITE );
582       mProgress.SetColor( Color::WHITE );
583       break;
584     }
585     case DISABLED:
586     {
587       Vector4 disableColor = GetDisableColor();
588       mBacking.SetColor( disableColor );
589       mHandle.SetColor( disableColor );
590       mProgress.SetColor( disableColor );
591       break;
592     }
593     case PRESSED:
594     {
595       break;
596     }
597     case FOCUSED:
598     {
599       break;
600     }
601   }
602 }
603
604 void Slider::CreateChildren()
605 {
606   Actor self = Self();
607
608   // Hit region
609   mHitArea = CreateHitRegion();
610   mPanDetector = PanGestureDetector::New();
611   mPanDetector.Attach( mHitArea );
612   mPanDetector.DetectedSignal().Connect( this, &Slider::OnPan );
613   self.Add( mHitArea );
614
615   // Background
616   mBacking = CreateBacking();
617   self.Add( mBacking );
618
619   // Progress bar
620   mProgress = CreateProgress();
621   mBacking.Add( mProgress );
622
623   // Handle
624   mHandle = CreateHandle();
625   mBacking.Add( mHandle );
626 }
627
628 void Slider::ResizeHitRegion( const Vector2& size )
629 {
630   if( mHitArea )
631   {
632     mHitArea.SetSize( size );
633   }
634 }
635
636 void Slider::AddPopup()
637 {
638   if( !mValueDisplay )
639   {
640     mValueDisplay = CreateValueDisplay();
641     mValueDisplay.SetVisible( false );
642     mHandle.Add( mValueDisplay );
643
644     Actor self = Self();
645     CreatePopupImage( self.GetProperty<std::string>( mPropertyPopupImageName ) );
646     SetPopupScale9( GetPopupScale9Border() );
647     CreatePopupArrowImage( self.GetProperty<std::string>( mPropertyPopupArrowImageName )  );
648
649     mValueTimer = Timer::New( VALUE_VIEW_SHOW_DURATION );
650     mValueTimer.TickSignal().Connect( this, &Slider::HideValueView );
651   }
652 }
653
654 void Slider::RemovePopup()
655 {
656   if( mValueDisplay )
657   {
658     mPopup.Unparent();
659     mPopup.Reset();
660
661     mPopupArrow.Unparent();
662     mPopupArrow.Reset();
663
664     mValueDisplay.Unparent();
665     mValueDisplay.Reset();
666
667     mValueTimer.TickSignal().Disconnect( this, &Slider::HideValueView );
668     mValueTimer.Reset();
669   }
670 }
671
672
673 float Slider::MarkFilter( float value )
674 {
675   const float MARK_TOLERANCE = GetMarkTolerance();
676
677   float mark;
678   for( MarkList::iterator it = mMarks.begin(), itEnd = mMarks.end(); it != itEnd; ++it )
679   {
680     const Property::Value& propertyValue = *it;
681     propertyValue.Get( mark );
682     mark = MapValuePercentage( mark );
683
684     // If close to a mark, return the mark
685     if( fabsf( mark - value ) < MARK_TOLERANCE )
686     {
687       return mark;
688     }
689   }
690
691   return value;
692 }
693
694 float Slider::SnapToMark( float value )
695 {
696   float closestMark = value;
697   float closestDist = std::numeric_limits<float>::max();
698
699   float mark;
700   for( MarkList::iterator it = mMarks.begin(), itEnd = mMarks.end(); it != itEnd; ++it )
701   {
702     const Property::Value& propertyValue = *it;
703     propertyValue.Get( mark );
704     mark = MapValuePercentage( mark );
705
706     float dist = fabsf( mark - value );
707     if( dist < closestDist )
708     {
709       closestDist = dist;
710       closestMark = mark;
711     }
712   }
713
714   return closestMark;
715 }
716
717 bool Slider::MarkReached( float value, int& outIndex )
718 {
719   const float MARK_TOLERANCE = GetMarkTolerance();
720
721   // Binary search
722   int head = 0,
723       tail = mMarks.size() - 1;
724   int current;
725   float mark;
726
727   while( head <= tail )
728   {
729     current = head + ( tail - head ) / 2;
730
731     const Property::Value& propertyValue = mMarks[ current ];
732     propertyValue.Get( mark );
733     mark = MapValuePercentage( mark );
734
735     if( fabsf( mark - value ) < MARK_TOLERANCE )
736     {
737       outIndex = current;
738       return true;
739     }
740     else
741     {
742       if( value < mark )
743       {
744         tail = current - 1;
745       }
746       else
747       {
748         head = current + 1;
749       }
750     }
751   }
752
753   return false;
754 }
755
756 bool Slider::HideValueView()
757 {
758   if( mValueDisplay )
759   {
760     mValueDisplay.SetVisible( false );
761   }
762
763   return false;
764 }
765
766 void Slider::OnPropertySet( Property::Index index, Property::Value propertyValue )
767 {
768   if( index == mPropertyLowerBound )
769   {
770     UpdateLowerBound( propertyValue.Get<float>() );
771   }
772   else if( index == mPropertyUpperBound )
773   {
774     UpdateUpperBound( propertyValue.Get<float>() );
775   }
776   else if( index == mPropertyValue )
777   {
778     DisplayValue( propertyValue.Get<float>(), true );
779   }
780   else if( index == mPropertyHitRegion )
781   {
782     ResizeHitRegion( propertyValue.Get<Vector2>() );
783   }
784   else if( index == mPropertyBackingRegion )
785   {
786     ResizeBackingRegion( propertyValue.Get<Vector2>() );
787   }
788   else if( index == mPropertyHandleRegion )
789   {
790     UpdateHandleRegion( propertyValue.Get<Vector2>() );
791   }
792   else if( index == mPropertyBackingImageName )
793   {
794     CreateBackingImage( propertyValue.Get<std::string>() );
795   }
796   else if( index == mPropertyHandleImageName )
797   {
798     CreateHandleImage( propertyValue.Get<std::string>() );
799   }
800   else if( index == mPropertyProgressImageName )
801   {
802     CreateProgressImage( propertyValue.Get<std::string>() );
803   }
804   else if( index == mPropertyPopupImageName )
805   {
806     CreatePopupImage( propertyValue.Get<std::string>() );
807   }
808   else if( index == mPropertyPopupArrowImageName )
809   {
810     CreatePopupArrowImage( propertyValue.Get<std::string>() );
811   }
812   else if( index == mPropertyBackingScale9Border )
813   {
814     SetBackingScale9( propertyValue.Get<Vector4>() );
815   }
816   else if( index == mPropertyProgressScale9Border )
817   {
818     SetProgressScale9( propertyValue.Get<Vector4>() );
819   }
820   else if( index == mPropertyPopupScale9Border )
821   {
822     SetPopupScale9( propertyValue.Get<Vector4>() );
823   }
824   else if( index == mPropertyDisableColor )
825   {
826     UpdateSkin();
827   }
828   else if( index == mPropertyPopupTextColor )
829   {
830     UpdatePopupTextColor( propertyValue.Get<Vector4>() );
831   }
832   else if( index == mPropertyValuePrecision )
833   {
834     DisplayValue( GetValue(), false );
835   }
836   else if( index == mPropertyShowPopup )
837   {
838     ShowPopup( propertyValue.Get<bool>() );
839   }
840   else if( index == mPropertyShowValue )
841   {
842     ShowValue( propertyValue.Get<bool>() );
843   }
844   else if( index == mPropertyEnabled )
845   {
846     SetEnabled( propertyValue.Get<bool>() );
847   }
848   else if( index == mPropertyMarks )
849   {
850     SetMarks( propertyValue.Get<Property::Array>() );
851   }
852   else if( index == mPropertySnapToMarks )
853   {
854     // Nothing
855   }
856   else if( index == mPropertyMarkTolerance )
857   {
858     // Nothing
859   }
860 }
861
862 void Slider::UpdateLowerBound( float bound )
863 {
864   DisplayValue( GetValue(), false );
865 }
866
867 float Slider::GetLowerBound() const
868 {
869   return Self().GetProperty<float>( mPropertyLowerBound );
870 }
871
872 void Slider::UpdateUpperBound( float bound )
873 {
874   DisplayValue( GetValue(), false );
875 }
876
877 float Slider::GetUpperBound() const
878 {
879   return Self().GetProperty<float>( mPropertyUpperBound );
880 }
881
882 void Slider::SetValue( float value )
883 {
884   Self().SetProperty( mPropertyValue, value );
885 }
886
887 float Slider::GetValue() const
888 {
889   return Self().GetProperty<float>( mPropertyValue );
890 }
891
892 void Slider::SetHitRegion( const Vector2& region )
893 {
894   Self().SetProperty( mPropertyHitRegion, region );
895 }
896
897 Vector2 Slider::GetHitRegion() const
898 {
899   return Self().GetProperty<Vector2>( mPropertyHitRegion );
900 }
901
902 void Slider::SetBackingRegion( const Vector2& region )
903 {
904   Self().SetProperty( mPropertyBackingRegion, region );
905 }
906
907 void Slider::ResizeBackingRegion( const Vector2& region )
908 {
909   SetBackingRegionSize( region );
910   ResizeProgressRegion( Vector2( 0.0f, region.y ) );
911
912   mDomain = CalcDomain( region );
913
914   DisplayValue( GetValue(), false );  // Set the progress bar to correct width
915 }
916
917 Vector2 Slider::GetBackingRegion() const
918 {
919   return Self().GetProperty<Vector2>( mPropertyBackingRegion );
920 }
921
922 void Slider::UpdateHandleRegion( const Vector2& region )
923 {
924   ResizeHandleRegion( region );
925
926   Vector2 hitRegion = GetHitRegion();
927   hitRegion.x += region.x;
928   SetHitRegion( hitRegion );
929 }
930
931 Vector2 Slider::GetHandleRegion() const
932 {
933   return Self().GetProperty<Vector2>( mPropertyHandleRegion );
934 }
935
936 Vector4 Slider::GetBackingScale9Border() const
937 {
938   return Self().GetProperty<Vector4>( mPropertyBackingScale9Border );
939 }
940
941 Vector4 Slider::GetPopupScale9Border() const
942 {
943   return Self().GetProperty<Vector4>( mPropertyPopupScale9Border );
944 }
945
946 Vector4 Slider::GetDisableColor() const
947 {
948   return Self().GetProperty<Vector4>( mPropertyDisableColor );
949 }
950
951 Vector4 Slider::GetPopupTextColor() const
952 {
953   return Self().GetProperty<Vector4>( mPropertyPopupTextColor );
954 }
955
956 int Slider::GetValuePrecision() const
957 {
958   return Self().GetProperty<int>( mPropertyValuePrecision );
959 }
960
961 void Slider::ShowPopup( bool showPopup )
962 {
963   // Value display
964   if( showPopup )
965   {
966     AddPopup();
967   }
968   else
969   {
970     RemovePopup();
971   }
972 }
973
974 bool Slider::GetShowPopup() const
975 {
976   return Self().GetProperty<bool>( mPropertyShowPopup );
977 }
978
979 void Slider::ShowValue( bool showValue )
980 {
981   if( showValue )
982   {
983     CreateHandleValueDisplay();
984   }
985   else
986   {
987     DestroyHandleValueDisplay();
988   }
989 }
990
991 bool Slider::GetShowValue() const
992 {
993   return Self().GetProperty<bool>( mPropertyShowValue );
994 }
995
996 void Slider::SetEnabled( bool enabled )
997 {
998   if( enabled )
999   {
1000     mState = NORMAL;
1001   }
1002   else
1003   {
1004     mState = DISABLED;
1005   }
1006
1007   UpdateSkin();
1008 }
1009
1010 bool Slider::IsEnabled() const
1011 {
1012   return mState != DISABLED;
1013 }
1014
1015 float Slider::GetMarkTolerance() const
1016 {
1017   return Self().GetProperty<float>( mPropertyMarkTolerance );
1018 }
1019
1020 // static class method to support script connecting signals
1021
1022 bool Slider::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1023 {
1024   Dali::BaseHandle handle( object );
1025
1026   bool connected = true;
1027   Toolkit::Slider slider = Toolkit::Slider::DownCast( handle );
1028
1029   if( signalName == Dali::Toolkit::Slider::SIGNAL_VALUE_CHANGED )
1030   {
1031     slider.ValueChangedSignal().Connect( tracker, functor );
1032   }
1033   else if( signalName == Dali::Toolkit::Slider::SIGNAL_MARK )
1034   {
1035     slider.MarkSignal().Connect( tracker, functor );
1036   }
1037  else
1038   {
1039     // signalName does not match any signal
1040     connected = false;
1041   }
1042
1043   return connected;
1044 }
1045
1046 void Slider::DisplayPopup( float value )
1047 {
1048   // Value display
1049   if( mValueTextView )
1050   {
1051     std::stringstream ss;
1052     ss.precision( GetValuePrecision() );
1053     ss << fixed << value;
1054     mValueTextView.SetText( ss.str() );
1055     TextStyle style;
1056     style.SetTextColor( GetPopupTextColor() );
1057     mValueTextView.SetStyleToCurrentText( style, TextStyle::COLOR);
1058
1059     if( mValueDisplay )
1060     {
1061       Font font = Font::New();
1062       float popupWidth = font.MeasureText( ss.str() ).x + VALUE_POPUP_MARGIN * 2.0f;
1063       if( popupWidth < VALUE_POPUP_MIN_WIDTH )
1064       {
1065         popupWidth = VALUE_POPUP_MIN_WIDTH;
1066       }
1067
1068       mPopup.SetSize( popupWidth, VALUE_POPUP_HEIGHT );
1069       mValueDisplay.SetVisible( true );
1070
1071       mValueTimer.SetInterval( VALUE_VIEW_SHOW_DURATION );
1072     }
1073   }
1074 }
1075
1076
1077 } // namespace Internal
1078
1079 } // namespace Toolkit
1080
1081 } // namespace Dali