Text Decorator - Set positions in Control coords.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / 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/internal/text/decorator/text-decorator.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/actors/actor.h>
24 #include <dali/public-api/adaptor-framework/timer.h>
25 #include <dali/public-api/actors/image-actor.h>
26 #include <dali/public-api/actors/layer.h>
27 #include <dali/public-api/actors/mesh-actor.h>
28 #include <dali/public-api/animation/constraint.h>
29 #include <dali/public-api/common/constants.h>
30 #include <dali/public-api/common/stage.h>
31 #include <dali/public-api/events/tap-gesture.h>
32 #include <dali/public-api/events/tap-gesture-detector.h>
33 #include <dali/public-api/events/pan-gesture.h>
34 #include <dali/public-api/events/pan-gesture-detector.h>
35 #include <dali/public-api/geometry/mesh.h>
36 #include <dali/public-api/geometry/mesh-data.h>
37 #include <dali/public-api/images/resource-image.h>
38 #include <dali/public-api/math/rect.h>
39 #include <dali/public-api/math/vector2.h>
40 #include <dali/public-api/math/vector4.h>
41 #include <dali/public-api/object/property-notification.h>
42 #include <dali/public-api/signals/connection-tracker.h>
43
44 // INTERNAL INCLUDES
45 #include <dali-toolkit/public-api/controls/control.h>
46 #include <dali-toolkit/public-api/controls/control-impl.h>
47 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
48 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
49 #include <dali-toolkit/public-api/controls/text-controls/text-label.h>
50 #include <dali-toolkit/public-api/controls/text-controls/text-selection-popup.h>
51
52 #ifdef DEBUG_ENABLED
53 #define DECORATOR_DEBUG
54
55 #endif
56
57 namespace Dali
58 {
59 namespace Internal
60 {
61 namespace
62 {
63 #ifdef DECORATOR_DEBUG
64 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
65 #endif
66 }
67 }
68 }
69
70
71 // Local Data
72 namespace
73 {
74
75 const char* DEFAULT_GRAB_HANDLE_IMAGE( DALI_IMAGE_DIR "insertpoint-icon.png" );
76 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
77 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
78 //const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
79 //const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
80
81 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
82 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
83
84 const unsigned int CURSOR_BLINK_INTERVAL = 500u; // Cursor blink interval
85 const float TO_MILLISECONDS = 1000.f;
86 const float TO_SECONDS = 1.f / 1000.f;
87
88 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( -0.05f );
89
90 /**
91  * structure to hold coordinates of each quad, which will make up the mesh.
92  */
93 struct QuadCoordinates
94 {
95   /**
96    * Default constructor
97    */
98   QuadCoordinates()
99   {
100   }
101
102   /**
103    * Constructor
104    * @param[in] x1 left co-ordinate
105    * @param[in] y1 top co-ordinate
106    * @param[in] x2 right co-ordinate
107    * @param[in] y2 bottom co-ordinate
108    */
109   QuadCoordinates(float x1, float y1, float x2, float y2)
110   : min(x1, y1),
111     max(x2, y2)
112   {
113   }
114
115   Dali::Vector2 min;                          ///< top-left (minimum) position of quad
116   Dali::Vector2 max;                          ///< bottom-right (maximum) position of quad
117 };
118
119 typedef std::vector<QuadCoordinates> QuadContainer;
120
121 /**
122  * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
123  * @param[in] boundingRectangle local bounding
124  * @param[out] Vector4 World coordinate bounding Box.
125  */
126 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
127 {
128   // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
129   Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
130
131   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
132   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
133
134   boundingBox = Dali::Vector4( originX,
135                                originY,
136                                originX + boundingRectangle.width,
137                                originY + boundingRectangle.height );
138 }
139
140
141 } // end of namespace
142
143 namespace Dali
144 {
145
146 namespace Toolkit
147 {
148
149 namespace Text
150 {
151
152 struct Decorator::Impl : public ConnectionTracker
153 {
154   struct CursorImpl
155   {
156     CursorImpl()
157     : color( Dali::Color::WHITE ),
158       position(),
159       cursorHeight( 0.0f ),
160       lineHeight( 0.0f )
161     {
162     }
163
164     Vector4 color;
165     Vector2 position;
166     float cursorHeight;
167     float lineHeight;
168   };
169
170   struct SelectionHandleImpl
171   {
172     SelectionHandleImpl()
173     : position(),
174       lineHeight( 0.0f ),
175       flipped( false )
176     {
177     }
178
179     ImageActor actor;
180     Actor grabArea;
181
182     Image pressedImage;
183     Image releasedImage;
184
185     Vector2 position;
186     float lineHeight; ///< Not the handle height
187     bool flipped;
188   };
189
190   Impl( Dali::Toolkit::Internal::Control& parent, Observer& observer )
191   : mTextControlParent( parent ),
192     mObserver( observer ),
193     mBoundingBox( Rect<int>() ),
194     mHighlightColor( 0.07f, 0.41f, 0.59f, 1.0f ), // light blue
195     mActiveCursor( ACTIVE_CURSOR_NONE ),
196     mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
197     mCursorBlinkDuration( 0.0f ),
198     mGrabDisplacementX( 0.0f ),
199     mGrabDisplacementY( 0.0f ),
200     mActiveGrabHandle( false ),
201     mActiveSelection( false ),
202     mActiveCopyPastePopup( false ),
203     mCursorBlinkStatus( true )
204   {
205   }
206
207   /**
208    * Relayout of the decorations owned by the decorator.
209    * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
210    */
211   void Relayout( const Vector2& size )
212   {
213     // TODO - Remove this if nothing is active
214     CreateActiveLayer();
215
216     // Show or hide the cursors
217     CreateCursors();
218     if( mPrimaryCursor )
219     {
220       mPrimaryCursor.SetPosition( mCursor[PRIMARY_CURSOR].position.x,
221                                   mCursor[PRIMARY_CURSOR].position.y );
222       mPrimaryCursor.SetSize( Size( 1.0f, mCursor[PRIMARY_CURSOR].cursorHeight ) );
223     }
224     if( mSecondaryCursor )
225     {
226       mSecondaryCursor.SetPosition( mCursor[SECONDARY_CURSOR].position.x,
227                                     mCursor[SECONDARY_CURSOR].position.y );
228       mSecondaryCursor.SetSize( Size( 1.0f, mCursor[SECONDARY_CURSOR].cursorHeight ) );
229     }
230
231     // Show or hide the grab handle
232     if( mActiveGrabHandle )
233     {
234       SetupTouchEvents();
235
236       CreateGrabHandle();
237
238       mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].position.x,
239                                mCursor[PRIMARY_CURSOR].position.y + mCursor[PRIMARY_CURSOR].lineHeight );
240     }
241     else if( mGrabHandle )
242     {
243       UnparentAndReset( mGrabHandle );
244     }
245
246     // Show or hide the selection handles/highlight
247     if( mActiveSelection )
248     {
249       SetupTouchEvents();
250
251       CreateSelectionHandles();
252
253       SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
254       primary.actor.SetPosition( primary.position.x,
255                                  primary.position.y + primary.lineHeight );
256
257       SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
258       secondary.actor.SetPosition( secondary.position.x,
259                                    secondary.position.y + secondary.lineHeight );
260
261       CreateHighlight();
262       UpdateHighlight();
263     }
264     else
265     {
266       UnparentAndReset( mSelectionHandle[ PRIMARY_SELECTION_HANDLE ].actor );
267       UnparentAndReset( mSelectionHandle[ SECONDARY_SELECTION_HANDLE ].actor );
268       UnparentAndReset( mHighlightMeshActor );
269     }
270
271     if ( mActiveCopyPastePopup )
272     {
273       if ( !mCopyPastePopup )
274       {
275         mCopyPastePopup = TextSelectionPopup::New();
276 #ifdef DECORATOR_DEBUG
277         mCopyPastePopup.SetName("mCopyPastePopup");
278 #endif
279         mCopyPastePopup.SetAnchorPoint( AnchorPoint::CENTER );
280         mCopyPastePopup.OnRelayoutSignal().Connect( this,  &Decorator::Impl::PopUpRelayoutComplete  ); // Position popup after size negotiation
281         mActiveLayer.Add ( mCopyPastePopup );
282       }
283     }
284     else
285     {
286      if ( mCopyPastePopup )
287      {
288        UnparentAndReset( mCopyPastePopup );
289      }
290     }
291   }
292
293   void UpdatePositions( const Vector2& scrollOffset )
294   {
295     mCursor[PRIMARY_CURSOR].position += scrollOffset;
296     mCursor[SECONDARY_CURSOR].position += scrollOffset;
297     mSelectionHandle[ PRIMARY_SELECTION_HANDLE ].position += scrollOffset;
298     mSelectionHandle[ SECONDARY_SELECTION_HANDLE ].position += scrollOffset;
299
300     // TODO Highlight box??
301   }
302
303   void PopUpRelayoutComplete( Actor actor )
304   {
305     // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
306
307     mCopyPastePopup.OnRelayoutSignal().Disconnect( this, &Decorator::Impl::PopUpRelayoutComplete  );
308
309     Vector3 popupPosition( mCursor[PRIMARY_CURSOR].position.x, mCursor[PRIMARY_CURSOR].position.y -100.0f , 0.0f); //todo 100 to be an offset Property
310
311     Vector3 popupSize = Vector3( mCopyPastePopup.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
312
313     GetConstrainedPopupPosition( popupPosition, popupSize, AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
314
315     SetUpPopUpPositionNotifications();
316
317     mCopyPastePopup.SetPosition( popupPosition ); //todo grabhandle(cursor) or selection handle positions to be used
318   }
319
320   void CreateCursor( ImageActor& cursor )
321   {
322     cursor = CreateSolidColorActor( Color::WHITE );
323     cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one.
324     cursor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
325   }
326
327   // Add or Remove cursor(s) from parent
328   void CreateCursors()
329   {
330     if( mActiveCursor == ACTIVE_CURSOR_NONE )
331     {
332       UnparentAndReset( mPrimaryCursor );
333       UnparentAndReset( mSecondaryCursor );
334     }
335     else
336     {
337       /* Create Primary and or Secondary Cursor(s) if active and add to parent */
338       if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
339            mActiveCursor == ACTIVE_CURSOR_BOTH )
340       {
341         if ( !mPrimaryCursor )
342         {
343           CreateCursor( mPrimaryCursor );
344 #ifdef DECORATOR_DEBUG
345           mPrimaryCursor.SetName( "PrimaryCursorActor" );
346 #endif
347           mActiveLayer.Add( mPrimaryCursor );
348         }
349       }
350
351       if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
352       {
353         if ( !mSecondaryCursor )
354         {
355           CreateCursor( mSecondaryCursor );
356 #ifdef DECORATOR_DEBUG
357           mSecondaryCursor.SetName( "SecondaryCursorActor" );
358 #endif
359           mActiveLayer.Add( mSecondaryCursor );
360         }
361       }
362       else
363       {
364         UnparentAndReset( mSecondaryCursor );
365       }
366     }
367   }
368
369   bool OnCursorBlinkTimerTick()
370   {
371     // Cursor blinking
372     if ( mPrimaryCursor )
373     {
374       mPrimaryCursor.SetVisible( mCursorBlinkStatus );
375     }
376     if ( mSecondaryCursor )
377     {
378       mSecondaryCursor.SetVisible( mCursorBlinkStatus );
379     }
380
381     mCursorBlinkStatus = !mCursorBlinkStatus;
382
383     return true;
384   }
385
386   void SetupTouchEvents()
387   {
388     if ( !mTapDetector )
389     {
390       mTapDetector = TapGestureDetector::New();
391       mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
392     }
393
394     if ( !mPanGestureDetector )
395     {
396       mPanGestureDetector = PanGestureDetector::New();
397       mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
398     }
399   }
400
401   void CreateActiveLayer()
402   {
403     if( !mActiveLayer )
404     {
405       Actor parent = mTextControlParent.Self();
406
407       mActiveLayer = Layer::New();
408 #ifdef DECORATOR_DEBUG
409       mActiveLayer.SetName ( "ActiveLayerActor" );
410 #endif
411
412       mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
413       mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
414       mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
415
416       parent.Add( mActiveLayer );
417     }
418
419     mActiveLayer.RaiseToTop();
420   }
421
422   void CreateGrabHandle()
423   {
424     if( !mGrabHandle )
425     {
426       if ( !mGrabHandleImage )
427       {
428         mGrabHandleImage = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE );
429       }
430
431       mGrabHandle = ImageActor::New( mGrabHandleImage );
432       mGrabHandle.SetAnchorPoint( AnchorPoint::TOP_CENTER );
433       mGrabHandle.SetDrawMode( DrawMode::OVERLAY );
434       // Area that Grab handle responds to, larger than actual handle so easier to move
435 #ifdef DECORATOR_DEBUG
436       mGrabHandle.SetName( "GrabHandleActor" );
437       if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
438       {
439         mGrabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 );
440         mGrabArea.SetName( "GrabArea" );
441       }
442       else
443       {
444         mGrabArea = Actor::New();
445         mGrabArea.SetName( "GrabArea" );
446       }
447 #else
448       mGrabArea = Actor::New();
449 #endif
450
451       mGrabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
452       mGrabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
453       mGrabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
454       mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
455       mGrabHandle.Add( mGrabArea );
456
457       mTapDetector.Attach( mGrabArea );
458       mPanGestureDetector.Attach( mGrabArea );
459
460       mActiveLayer.Add( mGrabHandle );
461     }
462   }
463
464   void CreateSelectionHandles()
465   {
466     SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
467     if ( !primary.actor )
468     {
469       if ( !primary.releasedImage )
470       {
471         primary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
472       }
473
474       primary.actor = ImageActor::New( primary.releasedImage );
475 #ifdef DECORATOR_DEBUG
476       primary.actor.SetName("SelectionHandleOne");
477 #endif
478       primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
479       primary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
480       primary.flipped = false;
481
482       primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
483 #ifdef DECORATOR_DEBUG
484       primary.grabArea.SetName("SelectionHandleOneGrabArea");
485 #endif
486       primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
487       primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
488       primary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
489
490       mTapDetector.Attach( primary.grabArea );
491       mPanGestureDetector.Attach( primary.grabArea );
492       primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
493
494       primary.actor.Add( primary.grabArea );
495       mActiveLayer.Add( primary.actor );
496     }
497
498     SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
499     if ( !secondary.actor )
500     {
501       if ( !secondary.releasedImage )
502       {
503         secondary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
504       }
505
506       secondary.actor = ImageActor::New( secondary.releasedImage );
507 #ifdef DECORATOR_DEBUG
508       secondary.actor.SetName("SelectionHandleTwo");
509 #endif
510       secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
511       secondary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
512       secondary.flipped = false;
513
514       secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
515 #ifdef DECORATOR_DEBUG
516       secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
517 #endif
518       secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
519       secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
520       secondary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
521
522       mTapDetector.Attach( secondary.grabArea );
523       mPanGestureDetector.Attach( secondary.grabArea );
524       secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
525
526       secondary.actor.Add( secondary.grabArea );
527       mActiveLayer.Add( secondary.actor );
528     }
529
530     //SetUpHandlePropertyNotifications(); TODO
531   }
532
533   void CreateHighlight()
534   {
535     if ( !mHighlightMeshActor )
536     {
537       mHighlightMaterial = Material::New( "HighlightMaterial" );
538       mHighlightMaterial.SetDiffuseColor( mHighlightColor );
539
540       mHighlightMeshData.SetMaterial( mHighlightMaterial );
541       mHighlightMeshData.SetHasNormals( true );
542
543       mHighlightMesh = Mesh::New( mHighlightMeshData );
544
545       mHighlightMeshActor = MeshActor::New( mHighlightMesh );
546 #ifdef DECORATOR_DEBUG
547       mHighlightMeshActor.SetName( "HighlightMeshActor" );
548 #endif
549       mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
550       mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
551
552       Actor parent = mTextControlParent.Self();
553       parent.Add( mHighlightMeshActor );
554     }
555   }
556
557   void UpdateHighlight()
558   {
559     //  Construct a Mesh with a texture to be used as the highlight 'box' for selected text
560     //
561     //  Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
562     //
563     //   [ TOP   ]  [ TOP ]      [TOP ]  [ TOP    ]      [ TOP  ]      [ TOP  ]
564     //  [ MIDDLE ]             [BOTTOM]  [BOTTOM]      [ MIDDLE ]   [ MIDDLE  ]
565     //  [ BOTTOM]                                      [ MIDDLE ]   [ MIDDLE  ]
566     //                                                 [BOTTOM]     [ MIDDLE  ]
567     //                                                              [BOTTOM]
568     //
569     //  Each quad is created as 2 triangles.
570     //  Middle is just 1 quad regardless of its size.
571     //
572     //  (0,0)         (0,0)
573     //     0*    *2     0*       *2
574     //     TOP          TOP
575     //     3*    *1     3*       *1
576     //  4*       *1     4*     *6
577     //     MIDDLE         BOTTOM
578     //  6*       *5     7*     *5
579     //  6*    *8
580     //   BOTTOM
581     //  9*    *7
582     //
583
584     if ( mHighlightMesh && mHighlightMaterial && !mHighlightQuadList.empty() )
585     {
586       MeshData::VertexContainer vertices;
587       Dali::MeshData::FaceIndices faceIndices;
588
589       std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
590       std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
591
592       // vertex position defaults to (0 0 0)
593       MeshData::Vertex vertex;
594       // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
595       vertex.nZ = 1.0f;
596
597       for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
598       {
599         // Add each quad geometry (a sub-selection) to the mesh data.
600
601         // 0-----1
602         // |\    |
603         // | \ A |
604         // |  \  |
605         // | B \ |
606         // |    \|
607         // 2-----3
608
609         QuadCoordinates& quad = *iter;
610         // top-left (v+0)
611         vertex.x = quad.min.x;
612         vertex.y = quad.min.y;
613         vertices.push_back( vertex );
614
615         // top-right (v+1)
616         vertex.x = quad.max.x;
617         vertex.y = quad.min.y;
618         vertices.push_back( vertex );
619
620         // bottom-left (v+2)
621         vertex.x = quad.min.x;
622         vertex.y = quad.max.y;
623         vertices.push_back( vertex );
624
625         // bottom-right (v+3)
626         vertex.x = quad.max.x;
627         vertex.y = quad.max.y;
628         vertices.push_back( vertex );
629
630         // triangle A (3, 1, 0)
631         faceIndices.push_back( v + 3 );
632         faceIndices.push_back( v + 1 );
633         faceIndices.push_back( v );
634
635         // triangle B (0, 2, 3)
636         faceIndices.push_back( v );
637         faceIndices.push_back( v + 2 );
638         faceIndices.push_back( v + 3 );
639
640         mHighlightMeshData.SetFaceIndices( faceIndices );
641       }
642
643       BoneContainer bones(0); // passed empty as bones not required
644       mHighlightMeshData.SetData( vertices, faceIndices, bones, mHighlightMaterial );
645       mHighlightMesh.UpdateMeshData( mHighlightMeshData );
646     }
647   }
648
649   void OnTap( Actor actor, const TapGesture& tap )
650   {
651     if( actor == mGrabHandle )
652     {
653       // TODO
654     }
655   }
656
657   void OnPan( Actor actor, const PanGesture& gesture )
658   {
659     if( actor == mGrabArea )
660     {
661       if( Gesture::Started == gesture.state )
662       {
663         mGrabDisplacementX = mGrabDisplacementY = 0;
664       }
665
666       mGrabDisplacementX += gesture.displacement.x;
667       mGrabDisplacementY += gesture.displacement.y;
668
669       const float x = mCursor[PRIMARY_CURSOR].position.x + mGrabDisplacementX;
670       const float y = mCursor[PRIMARY_CURSOR].position.y + mCursor[PRIMARY_CURSOR].lineHeight*0.5f + mGrabDisplacementY;
671
672       if( Gesture::Started    == gesture.state ||
673           Gesture::Continuing == gesture.state )
674       {
675         mObserver.GrabHandleEvent( GRAB_HANDLE_PRESSED, x, y );
676       }
677       else if( Gesture::Finished  == gesture.state ||
678                Gesture::Cancelled == gesture.state )
679       {
680         mObserver.GrabHandleEvent( GRAB_HANDLE_RELEASED, x, y );
681       }
682     }
683   }
684
685   bool OnHandleOneTouched( Actor actor, const TouchEvent& touch )
686   {
687     // TODO
688     return false;
689   }
690
691   bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch )
692   {
693     // TODO
694     return false;
695   }
696
697   // Popup
698
699   float AlternatePopUpPositionRelativeToCursor()
700   {
701     float alternativePosition=0.0f;;
702
703     if ( mPrimaryCursor ) // Secondary cursor not used for paste
704     {
705       Cursor cursor = PRIMARY_CURSOR;
706       alternativePosition = mCursor[cursor].position.y;
707     }
708
709     const float popupHeight = 120.0f; // todo Set as a MaxSize Property in Control or retrieve from CopyPastePopup class.
710
711     if( mActiveGrabHandle )
712     {
713       // If grab handle enabled then position pop-up below the grab handle.
714       const Vector2 grabHandleSize( 59.0f, 56.0f ); // todo
715       const float BOTTOM_HANDLE_BOTTOM_OFFSET = 1.5; //todo Should be a property
716       alternativePosition +=  grabHandleSize.height  + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET ;
717     }
718     else
719     {
720       alternativePosition += popupHeight;
721     }
722
723     return alternativePosition;
724   }
725
726   void PopUpLeavesVerticalBoundary( PropertyNotification& source )
727   {
728     float alternativeYPosition=0.0f;
729   // todo
730   //  if( mHighlightMeshActor ) // Text Selection mode
731   //  {
732   //    alternativePosition = AlternatePopUpPositionRelativeToSelectionHandles();
733   //  }
734   //  else // Not in Text Selection mode
735   //  {
736     // if can't be positioned above, then position below row.
737     alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
738    // }
739     mCopyPastePopup.SetY( alternativeYPosition );
740   }
741
742
743   void SetUpPopUpPositionNotifications( )
744   {
745     // Note Property notifications ignore any set anchor point so conditions must allow for this.  Default is Top Left.
746
747     // Exceeding vertical boundary
748
749     Vector4 worldCoordinatesBoundingBox;
750     LocalToWorldCoordinatesBoundingBox( mBoundingBox, worldCoordinatesBoundingBox );
751
752     float popupHeight = mCopyPastePopup.GetRelayoutSize( Dimension::HEIGHT);
753
754     PropertyNotification verticalExceedNotification = mCopyPastePopup.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
755                                                       OutsideCondition( worldCoordinatesBoundingBox.y + popupHeight/2,
756                                                                         worldCoordinatesBoundingBox.w - popupHeight/2 ) );
757
758     verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
759   }
760
761   void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, Vector3& popupSize, Vector3 anchorPoint, Actor& parent, Rect<int>& boundingBox )
762   {
763     DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
764
765     // Parent must already by added to Stage for these Get calls to work
766     Vector3 parentAnchorPoint = parent.GetCurrentAnchorPoint();
767     Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize()*parentAnchorPoint;
768     Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition;  // Parent World position plus popup local position gives World Position
769     Vector3 popupDistanceFromAnchorPoint = popupSize*anchorPoint;
770
771     // Bounding rectangle is supplied as screen coordinates, bounding will be done in world coordinates.
772     Vector4 boundingRectangleWorld;
773     LocalToWorldCoordinatesBoundingBox( boundingBox, boundingRectangleWorld );
774
775     // Calculate distance to move popup (in local space) so fits within the boundary
776     float xOffSetToKeepWithinBounds = 0.0f;
777     if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
778     {
779       xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
780     }
781     else if ( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
782     {
783       xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x );
784     }
785
786     // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
787     if ( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
788     {
789       requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
790     }
791
792     requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
793   }
794
795   Internal::Control&  mTextControlParent;
796   Observer&           mObserver;
797
798   TapGestureDetector  mTapDetector;
799   PanGestureDetector  mPanGestureDetector;
800   Timer               mCursorBlinkTimer;          ///< Timer to signal cursor to blink
801
802   Layer               mActiveLayer;               ///< Layer for active handles and alike that ensures they are above all else.
803   ImageActor          mPrimaryCursor;
804   ImageActor          mSecondaryCursor;
805   ImageActor          mGrabHandle;
806   Actor               mGrabArea;
807   MeshActor           mHighlightMeshActor;        ///< Mesh Actor to display highlight
808   TextSelectionPopup  mCopyPastePopup;
809
810   Image               mCursorImage;
811   Image               mGrabHandleImage;
812   Mesh                mHighlightMesh;             ///< Mesh for highlight
813   MeshData            mHighlightMeshData;         ///< Mesh Data for highlight
814   Material            mHighlightMaterial;         ///< Material used for highlight
815
816   CursorImpl          mCursor[CURSOR_COUNT];
817   SelectionHandleImpl mSelectionHandle[SELECTION_HANDLE_COUNT];
818   QuadContainer       mHighlightQuadList;         ///< Sub-selections that combine to create the complete selection highlight
819
820   Rect<int>           mBoundingBox;
821   Vector4             mHighlightColor;            ///< Color of the highlight
822
823   unsigned int        mActiveCursor;
824   unsigned int        mCursorBlinkInterval;
825   float               mCursorBlinkDuration;
826   float               mGrabDisplacementX;
827   float               mGrabDisplacementY;
828
829   bool                mActiveGrabHandle:1;
830   bool                mActiveSelection:1;
831   bool                mActiveCopyPastePopup:1;
832   bool                mCursorBlinkStatus:1;       ///< Flag to switch between blink on and blink off
833 };
834
835 DecoratorPtr Decorator::New( Internal::Control& parent, Observer& observer )
836 {
837   return DecoratorPtr( new Decorator(parent, observer) );
838 }
839
840 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
841 {
842   mImpl->mBoundingBox = boundingBox;
843 }
844
845 const Rect<int>& Decorator::GetBoundingBox() const
846 {
847   return mImpl->mBoundingBox;
848 }
849
850 void Decorator::Relayout( const Vector2& size )
851 {
852   mImpl->Relayout( size );
853 }
854
855 void Decorator::UpdatePositions( const Vector2& scrollOffset )
856 {
857   mImpl->UpdatePositions( scrollOffset );
858 }
859
860 /** Cursor **/
861
862 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
863 {
864   mImpl->mActiveCursor = activeCursor;
865 }
866
867 unsigned int Decorator::GetActiveCursor() const
868 {
869   return mImpl->mActiveCursor;
870 }
871
872 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
873 {
874   // Adjust grab handle displacement
875   mImpl->mGrabDisplacementX -= x - mImpl->mCursor[cursor].position.x;
876   mImpl->mGrabDisplacementY -= y - mImpl->mCursor[cursor].position.y;
877
878   mImpl->mCursor[cursor].position.x = x;
879   mImpl->mCursor[cursor].position.y = y;
880   mImpl->mCursor[cursor].cursorHeight = cursorHeight;
881   mImpl->mCursor[cursor].lineHeight = lineHeight;
882 }
883
884 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
885 {
886   x = mImpl->mCursor[cursor].position.x;
887   y = mImpl->mCursor[cursor].position.y;
888   cursorHeight = mImpl->mCursor[cursor].cursorHeight;
889   lineHeight = mImpl->mCursor[cursor].lineHeight;
890 }
891
892 void Decorator::SetColor( Cursor cursor, const Dali::Vector4& color )
893 {
894   mImpl->mCursor[cursor].color = color;
895 }
896
897 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
898 {
899   return mImpl->mCursor[cursor].color;
900 }
901
902 void Decorator::StartCursorBlink()
903 {
904   if ( !mImpl->mCursorBlinkTimer )
905   {
906     mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
907     mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
908   }
909
910   if ( !mImpl->mCursorBlinkTimer.IsRunning() )
911   {
912     mImpl->mCursorBlinkTimer.Start();
913   }
914 }
915
916 void Decorator::StopCursorBlink()
917 {
918   if ( mImpl->mCursorBlinkTimer )
919   {
920     mImpl->mCursorBlinkTimer.Stop();
921   }
922 }
923
924 void Decorator::SetCursorBlinkInterval( float seconds )
925 {
926   mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
927 }
928
929 float Decorator::GetCursorBlinkInterval() const
930 {
931   return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
932 }
933
934 void Decorator::SetCursorBlinkDuration( float seconds )
935 {
936   mImpl->mCursorBlinkDuration = seconds;
937 }
938
939 float Decorator::GetCursorBlinkDuration() const
940 {
941   return mImpl->mCursorBlinkDuration;
942 }
943
944 /** GrabHandle **/
945
946 void Decorator::SetGrabHandleActive( bool active )
947 {
948   mImpl->mActiveGrabHandle = active;
949 }
950
951 bool Decorator::IsGrabHandleActive() const
952 {
953   return mImpl->mActiveGrabHandle;
954 }
955
956 void Decorator::SetGrabHandleImage( Dali::Image image )
957 {
958   mImpl->mGrabHandleImage = image;
959 }
960
961 Dali::Image Decorator::GetGrabHandleImage() const
962 {
963   return mImpl->mGrabHandleImage;
964 }
965
966 /** Selection **/
967
968 void Decorator::SetSelectionActive( bool active )
969 {
970   mImpl->mActiveSelection = active;
971 }
972
973 bool Decorator::IsSelectionActive() const
974 {
975   return mImpl->mActiveSelection;
976 }
977
978 void Decorator::SetPosition( SelectionHandle handle, float x, float y, float height )
979 {
980   mImpl->mSelectionHandle[handle].position.x = x;
981   mImpl->mSelectionHandle[handle].position.y = y;
982   mImpl->mSelectionHandle[handle].lineHeight = height;
983 }
984
985 void Decorator::GetPosition( SelectionHandle handle, float& x, float& y, float& height ) const
986 {
987   x = mImpl->mSelectionHandle[handle].position.x;
988   y = mImpl->mSelectionHandle[handle].position.y;
989   height = mImpl->mSelectionHandle[handle].lineHeight;
990 }
991
992 void Decorator::SetImage( SelectionHandle handle, SelectionHandleState state, Dali::Image image )
993 {
994   if( SELECTION_HANDLE_PRESSED == state )
995   {
996     mImpl->mSelectionHandle[handle].pressedImage = image;
997   }
998   else
999   {
1000     mImpl->mSelectionHandle[handle].releasedImage = image;
1001   }
1002 }
1003
1004 Dali::Image Decorator::GetImage( SelectionHandle handle, SelectionHandleState state ) const
1005 {
1006   if( SELECTION_HANDLE_PRESSED == state )
1007   {
1008     return mImpl->mSelectionHandle[handle].pressedImage;
1009   }
1010
1011   return mImpl->mSelectionHandle[handle].releasedImage;
1012 }
1013
1014 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1015 {
1016   mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1017 }
1018
1019 void Decorator::ClearHighlights()
1020 {
1021   mImpl->mHighlightQuadList.clear();
1022 }
1023
1024 void Decorator::SetPopupActive( bool active )
1025 {
1026   mImpl->mActiveCopyPastePopup = active;
1027 }
1028
1029 bool Decorator::IsPopupActive() const
1030 {
1031   return mImpl->mActiveCopyPastePopup ;
1032 }
1033
1034 Decorator::~Decorator()
1035 {
1036   delete mImpl;
1037 }
1038
1039 Decorator::Decorator( Dali::Toolkit::Internal::Control& parent, Observer& observer )
1040 : mImpl( NULL )
1041 {
1042   mImpl = new Decorator::Impl( parent, observer );
1043 }
1044
1045 } // namespace Text
1046
1047 } // namespace Toolkit
1048
1049 } // namespace Dali