0b15b4910e41d09964a9a384f10b5b17d71f46ce
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / public-api / text / decorator / text-decorator.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/public-api/text/decorator/text-decorator.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/actors/actor.h>
23 #include <dali/public-api/adaptor-framework/timer.h>
24 #include <dali/public-api/actors/image-actor.h>
25 #include <dali/public-api/actors/layer.h>
26 #include <dali/public-api/common/constants.h>
27 #include <dali/public-api/common/stage.h>
28 #include <dali/public-api/events/tap-gesture.h>
29 #include <dali/public-api/events/tap-gesture-detector.h>
30 #include <dali/public-api/events/pan-gesture.h>
31 #include <dali/public-api/events/pan-gesture-detector.h>
32 #include <dali/public-api/images/resource-image.h>
33 #include <dali/public-api/math/vector2.h>
34 #include <dali/public-api/math/vector4.h>
35 #include <dali/public-api/signals/connection-tracker.h>
36
37 // INTERNAL INCLUDES
38 #include <dali-toolkit/public-api/controls/control.h>
39 #include <dali-toolkit/public-api/controls/control-impl.h>
40
41 #ifdef DEBUG_ENABLED
42 #define DECORATOR_DEBUG
43 #endif
44
45 // Local Data
46 namespace
47 {
48
49 const char* DEFAULT_GRAB_HANDLE_IMAGE( DALI_IMAGE_DIR "insertpoint-icon.png" );
50 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
51 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
52 //const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
53 //const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
54 const char* DEFAULT_CURSOR_IMAGE( DALI_IMAGE_DIR "decorator-cursor.png");
55
56 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
57 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
58
59 const std::size_t CURSOR_BLINK_INTERVAL = 500; // Cursor blink interval
60 const std::size_t MILLISECONDS = 1000;
61
62 } // end of namespace
63
64 namespace Dali
65 {
66
67 namespace Toolkit
68 {
69
70 namespace Text
71 {
72
73 struct Decorator::Impl : public ConnectionTracker
74 {
75   struct CursorImpl
76   {
77     CursorImpl()
78     : x(0.0f),
79       y(0.0f),
80       height(0.0f),
81       color(Dali::Color::WHITE)
82     {
83     }
84
85     float x;
86     float y;
87     float height;
88
89     Vector4 color;
90   };
91
92   struct SelectionHandleImpl
93   {
94     SelectionHandleImpl()
95     : x(0.0f),
96       y(0.0f),
97       cursorHeight(0.0f),
98       flipped(false)
99     {
100     }
101
102     float x;
103     float y;
104     float cursorHeight; ///< Not the handle height
105     bool flipped;
106
107     ImageActor actor;
108     Actor grabArea;
109
110     Image pressedImage;
111     Image releasedImage;
112   };
113
114   Impl( Dali::Toolkit::Internal::Control& parent, Observer& observer )
115   : mParent(parent),
116     mObserver(observer),
117     mActiveCursor(ACTIVE_CURSOR_NONE),
118     mActiveGrabHandle(false),
119     mActiveSelection( false ),
120     mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
121     mCursorBlinkDuration( 0.0f ),
122     mCursorBlinkStatus( true ),
123     mGrabDisplacementX( 0.0f ),
124     mGrabDisplacementY( 0.0f ),
125     mBoundingBox( Rect<int>() )
126   {
127   }
128
129   /**
130    * Relayout of the decorations owned by the decorator.
131    * @param[in] size The Size of the UI control the decorater is adding it's decorations to.
132    */
133   void Relayout( const Vector2& size )
134   {
135     SetCursors();
136
137     // Show or hide the grab handle
138     if( mActiveGrabHandle )
139     {
140       SetupTouchEvents();
141
142       CreateActiveLayer();
143       CreateGrabHandle();
144
145       mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].x, mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height );
146     }
147     else if( mGrabHandle )
148     {
149       UnparentAndReset( mGrabHandle );
150     }
151
152     // Show or hide the selection handles/highlight
153     if( mActiveSelection )
154     {
155       SetupTouchEvents();
156
157       CreateActiveLayer();
158       CreateSelectionHandles();
159
160       SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
161       primary.actor.SetPosition( primary.x, primary.y + primary.cursorHeight );
162
163       SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
164       secondary.actor.SetPosition( secondary.x, secondary.y + secondary.cursorHeight );
165
166       //CreateHighlight(); TODO
167     }
168     else
169     {
170       UnparentAndReset( mSelectionHandle[ PRIMARY_SELECTION_HANDLE ].actor );
171       UnparentAndReset( mSelectionHandle[ SECONDARY_SELECTION_HANDLE ].actor );
172     }
173   }
174
175   /**
176    * Creates a cursor
177    */
178   void CreateCursor( ImageActor& cursor )
179   {
180     if ( !mCursorImage )
181     {
182       mCursorImage = ResourceImage::New( DEFAULT_CURSOR_IMAGE );
183     }
184     cursor = ImageActor::New( mCursorImage );
185     cursor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
186   }
187
188   /**
189    * Add / Remove cursor(s) from parent
190    */
191   void SetCursors()
192   {
193     Actor parent = mParent.Self();
194     /* Create Primary and or Secondary Cursor(s) if active and add to parent */
195     if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY )
196     {
197       if ( !mPrimaryCursor )
198       {
199         CreateCursor( mPrimaryCursor );
200 #ifdef DECORATOR_DEBUG
201         mPrimaryCursor.SetName( "PrimaryCursorActor" );
202 #endif
203         parent.Add( mPrimaryCursor);
204       }
205
206       mPrimaryCursor.SetPosition( mCursor[PRIMARY_CURSOR].x, mCursor[PRIMARY_CURSOR].y );
207     }
208     else if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
209     {
210       if ( !mSecondaryCursor )
211       {
212         CreateCursor( mSecondaryCursor );
213 #ifdef DECORATOR_DEBUG
214         mSecondaryCursor.SetName( "SecondaryCursorActor" );
215 #endif
216         parent.Add( mSecondaryCursor);
217       }
218     }
219     else
220     {
221       /* ACTIVE_CURSOR_NONE so unparent cursors*/
222       if ( mPrimaryCursor )
223       {
224         UnparentAndReset( mPrimaryCursor );
225       }
226
227       if ( mSecondaryCursor )
228       {
229         UnparentAndReset( mSecondaryCursor );
230       }
231     }
232   }
233
234   bool OnCursorBlinkTimerTick()
235   {
236     // Cursor blinking
237     if ( ACTIVE_CURSOR_PRIMARY )
238     {
239       mPrimaryCursor.SetVisible( mCursorBlinkStatus );
240     }
241     else if ( ACTIVE_CURSOR_BOTH )
242     {
243       mPrimaryCursor.SetVisible( mCursorBlinkStatus );
244       mSecondaryCursor.SetVisible( mCursorBlinkStatus );
245     }
246
247     mCursorBlinkStatus = !mCursorBlinkStatus;
248
249     return true;
250   }
251
252   void SetupTouchEvents()
253   {
254     if ( !mTapDetector )
255     {
256       mTapDetector = TapGestureDetector::New();
257       mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
258     }
259
260     if ( !mPanGestureDetector )
261     {
262       mPanGestureDetector = PanGestureDetector::New();
263       mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
264     }
265   }
266
267   void CreateActiveLayer()
268   {
269     if( !mActiveLayer )
270     {
271       Actor parent = mParent.Self();
272
273       mActiveLayer = Layer::New();
274 #ifdef DECORATOR_DEBUG
275       mActiveLayer.SetName ( "ActiveLayerActor" );
276 #endif
277
278       mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
279       mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
280       mActiveLayer.SetSizeMode( SIZE_EQUAL_TO_PARENT );
281       mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
282
283       parent.Add( mActiveLayer );
284     }
285
286     mActiveLayer.RaiseToTop();
287   }
288
289   void CreateGrabHandle()
290   {
291     if( !mGrabHandle )
292     {
293       if ( !mGrabHandleImage )
294       {
295         mGrabHandleImage = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE );
296       }
297
298       mGrabHandle = ImageActor::New( mGrabHandleImage );
299 #ifdef DECORATOR_DEBUG
300       mGrabHandle.SetName( "GrabHandleActor" );
301 #endif
302       mGrabHandle.SetParentOrigin( ParentOrigin::TOP_LEFT );
303       mGrabHandle.SetAnchorPoint( AnchorPoint::TOP_CENTER );
304       mGrabHandle.SetDrawMode( DrawMode::OVERLAY );
305
306       mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
307 #ifdef DECORATOR_DEBUG
308       mGrabArea.SetName( "GrabArea" );
309 #endif
310       mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
311       mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
312       mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
313       mGrabHandle.Add(mGrabArea);
314
315       mTapDetector.Attach( mGrabArea );
316       mPanGestureDetector.Attach( mGrabArea );
317
318       mActiveLayer.Add(mGrabHandle);
319     }
320   }
321
322   void CreateSelectionHandles()
323   {
324     SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
325     if ( !primary.actor )
326     {
327       if ( !primary.releasedImage )
328       {
329         primary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
330       }
331
332       primary.actor = ImageActor::New( primary.releasedImage );
333 #ifdef DECORATOR_DEBUG
334       primary.actor.SetName("SelectionHandleOne");
335 #endif
336       primary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
337       primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
338       primary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
339       primary.flipped = false;
340
341       primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
342 #ifdef DECORATOR_DEBUG
343       primary.grabArea.SetName("SelectionHandleOneGrabArea");
344 #endif
345       primary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
346       primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
347       primary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
348
349       mTapDetector.Attach( primary.grabArea );
350       mPanGestureDetector.Attach( primary.grabArea );
351       primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
352
353       primary.actor.Add( primary.grabArea );
354       mActiveLayer.Add( primary.actor );
355     }
356
357     SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
358     if ( !secondary.actor )
359     {
360       if ( !secondary.releasedImage )
361       {
362         secondary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
363       }
364
365       secondary.actor = ImageActor::New( secondary.releasedImage );
366 #ifdef DECORATOR_DEBUG
367       secondary.actor.SetName("SelectionHandleTwo");
368 #endif
369       secondary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
370       secondary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
371       secondary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
372       secondary.flipped = false;
373
374       secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
375 #ifdef DECORATOR_DEBUG
376       secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
377 #endif
378       secondary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
379       secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
380       secondary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
381
382       mTapDetector.Attach( secondary.grabArea );
383       mPanGestureDetector.Attach( secondary.grabArea );
384       secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
385
386       secondary.actor.Add( secondary.grabArea );
387       mActiveLayer.Add( secondary.actor );
388     }
389
390     //SetUpHandlePropertyNotifications(); TODO
391   }
392
393   void OnTap( Actor actor, const TapGesture& tap )
394   {
395     if( actor == mGrabHandle )
396     {
397       // TODO
398     }
399   }
400
401   void OnPan( Actor actor, const PanGesture& gesture )
402   {
403     if( actor == mGrabArea )
404     {
405       if( Gesture::Started == gesture.state )
406       {
407         mGrabDisplacementX = mGrabDisplacementY = 0;
408       }
409
410       mGrabDisplacementX += gesture.displacement.x;
411       mGrabDisplacementY += gesture.displacement.y;
412
413       float x = mCursor[PRIMARY_CURSOR].x + mGrabDisplacementX;
414       float y = mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height*0.5f + mGrabDisplacementY;
415
416       if( Gesture::Started    == gesture.state ||
417           Gesture::Continuing == gesture.state )
418       {
419         mObserver.GrabHandleEvent( GRAB_HANDLE_PRESSED, x, y );
420       }
421       else if( Gesture::Finished  == gesture.state ||
422                Gesture::Cancelled == gesture.state )
423       {
424         mObserver.GrabHandleEvent( GRAB_HANDLE_RELEASED, x, y );
425       }
426     }
427   }
428
429   bool OnHandleOneTouched( Actor actor, const TouchEvent& touch )
430   {
431     // TODO
432     return false;
433   }
434
435   bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch )
436   {
437     // TODO
438     return false;
439   }
440
441   Internal::Control& mParent;
442   Observer& mObserver;
443
444   Layer mActiveLayer; // Layer for active handles and alike that ensures they are above all else.
445
446   unsigned int mActiveCursor;
447   bool         mActiveGrabHandle;
448   bool         mActiveSelection;
449
450   CursorImpl mCursor[CURSOR_COUNT];
451
452   Timer mCursorBlinkTimer; // Timer to signal cursor to blink
453   unsigned int mCursorBlinkInterval;
454   float mCursorBlinkDuration;
455   bool mCursorBlinkStatus; // Flag to switch between blink on and blink off
456
457   ImageActor mPrimaryCursor;
458   ImageActor mSecondaryCursor;
459
460   ImageActor mGrabHandle;
461   Actor mGrabArea;
462   float mGrabDisplacementX;
463   float mGrabDisplacementY;
464
465   SelectionHandleImpl mSelectionHandle[SELECTION_HANDLE_COUNT];
466
467   Image mCursorImage;
468   Image mGrabHandleImage;
469
470   TapGestureDetector mTapDetector;
471   PanGestureDetector mPanGestureDetector;
472
473   Rect<int> mBoundingBox;
474 };
475
476 DecoratorPtr Decorator::New( Internal::Control& parent, Observer& observer )
477 {
478   return DecoratorPtr( new Decorator(parent, observer) );
479 }
480
481 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
482 {
483   mImpl->mBoundingBox = boundingBox;
484 }
485
486 const Rect<int>& Decorator::GetBoundingBox() const
487 {
488   return mImpl->mBoundingBox;
489 }
490
491 void Decorator::Relayout( const Vector2& size )
492 {
493   mImpl->Relayout( size );
494 }
495
496 /** Cursor **/
497
498 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
499 {
500   mImpl->mActiveCursor = activeCursor;
501 }
502
503 unsigned int Decorator::GetActiveCursor() const
504 {
505   return mImpl->mActiveCursor;
506 }
507
508 void Decorator::SetPosition( Cursor cursor, float x, float y, float height )
509 {
510   // Adjust grab handle displacement
511   mImpl->mGrabDisplacementX -= x - mImpl->mCursor[cursor].x;
512   mImpl->mGrabDisplacementY -= y - mImpl->mCursor[cursor].y;
513
514   mImpl->mCursor[cursor].x = x;
515   mImpl->mCursor[cursor].y = y;
516   mImpl->mCursor[cursor].height = height;
517 }
518
519 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& height ) const
520 {
521   x = mImpl->mCursor[cursor].x;
522   y = mImpl->mCursor[cursor].y;
523   height = mImpl->mCursor[cursor].height;
524 }
525
526 void Decorator::SetCursorImage( Dali::Image image )
527 {
528   mImpl->mCursorImage = image;
529 }
530
531 Dali::Image Decorator::GetCursorImage() const
532 {
533   return mImpl->mCursorImage;
534 }
535
536 void Decorator::SetColor( Cursor cursor, const Dali::Vector4& color )
537 {
538   mImpl->mCursor[cursor].color = color;
539 }
540
541 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
542 {
543   return mImpl->mCursor[cursor].color;
544 }
545
546 void Decorator::StartCursorBlink()
547 {
548   if ( !mImpl->mCursorBlinkTimer )
549   {
550     mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
551     mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
552   }
553
554   if ( !mImpl->mCursorBlinkTimer.IsRunning() )
555   {
556     mImpl->mCursorBlinkTimer.Start();
557   }
558 }
559
560 void Decorator::StopCursorBlink()
561 {
562   if ( mImpl->mCursorBlinkTimer )
563   {
564     mImpl->mCursorBlinkTimer.Stop();
565   }
566 }
567
568 void Decorator::SetCursorBlinkInterval( float seconds )
569 {
570   mImpl->mCursorBlinkInterval = seconds*MILLISECONDS; // Convert to milliseconds
571 }
572
573 float Decorator::GetCursorBlinkInterval() const
574 {
575   return mImpl->mCursorBlinkInterval;
576 }
577
578 void Decorator::SetCursorBlinkDuration( float seconds )
579 {
580   mImpl->mCursorBlinkDuration = seconds;
581 }
582
583 float Decorator::GetCursorBlinkDuration() const
584 {
585   return mImpl->mCursorBlinkDuration;
586 }
587
588 /** GrabHandle **/
589
590 void Decorator::SetGrabHandleActive( bool active )
591 {
592   mImpl->mActiveGrabHandle = active;
593 }
594
595 bool Decorator::IsGrabHandleActive() const
596 {
597   return mImpl->mActiveGrabHandle;
598 }
599
600 void Decorator::SetGrabHandleImage( Dali::Image image )
601 {
602   mImpl->mGrabHandleImage = image;
603 }
604
605 Dali::Image Decorator::GetGrabHandleImage() const
606 {
607   return mImpl->mGrabHandleImage;
608 }
609
610 /** Selection **/
611
612 void Decorator::SetSelectionActive( bool active )
613 {
614   mImpl->mActiveSelection = active;
615 }
616
617 bool Decorator::IsSelectionActive() const
618 {
619   return mImpl->mActiveSelection;
620 }
621
622 void Decorator::SetPosition( SelectionHandle handle, float x, float y, float height )
623 {
624   mImpl->mSelectionHandle[handle].x = x;
625   mImpl->mSelectionHandle[handle].y = y;
626   mImpl->mSelectionHandle[handle].cursorHeight = height;
627 }
628
629 void Decorator::GetPosition( SelectionHandle handle, float& x, float& y, float& height ) const
630 {
631   x = mImpl->mSelectionHandle[handle].x;
632   y = mImpl->mSelectionHandle[handle].y;
633   height = mImpl->mSelectionHandle[handle].cursorHeight;
634 }
635
636 void Decorator::SetImage( SelectionHandle handle, SelectionHandleState state, Dali::Image image )
637 {
638   if( SELECTION_HANDLE_PRESSED == state )
639   {
640     mImpl->mSelectionHandle[handle].pressedImage = image;
641   }
642   else
643   {
644     mImpl->mSelectionHandle[handle].releasedImage = image;
645   }
646 }
647
648 Dali::Image Decorator::GetImage( SelectionHandle handle, SelectionHandleState state ) const
649 {
650   if( SELECTION_HANDLE_PRESSED == state )
651   {
652     return mImpl->mSelectionHandle[handle].pressedImage;
653   }
654
655   return mImpl->mSelectionHandle[handle].releasedImage;
656 }
657
658 Decorator::~Decorator()
659 {
660   delete mImpl;
661 }
662
663 Decorator::Decorator( Dali::Toolkit::Internal::Control& parent, Observer& observer )
664 : mImpl( NULL )
665 {
666   mImpl = new Decorator::Impl( parent, observer );
667 }
668
669 } // namespace Text
670
671 } // namespace Toolkit
672
673 } // namespace Dali