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