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