9f83898d7b07c1497730f52f38ff179ffc55468e
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-input / text-input-decorator-impl.cpp
1 /*
2  * Copyright (c) 2014 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/controls/text-input/text-input-decorator-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/adaptor-framework/clipboard.h>
23 #include <dali/public-api/common/stage.h>
24 #include <dali/public-api/events/pan-gesture.h>
25 #include <dali/public-api/object/property-notification.h>
26 #include <dali/integration-api/debug.h>
27
28 // INTERNAL INCLUDES
29 #include <dali-toolkit/internal/controls/text-input/text-input-handles-impl.h>
30
31 using namespace Dali;
32
33 namespace
34 {
35 #if defined(DEBUG_ENABLED)
36   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT_DECORATOR");
37 #endif
38
39 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f );
40 const float TOP_HANDLE_TOP_OFFSET(-1.5f);                                   // Offset between top handle and cutCopyPaste pop-up
41 const float BOTTOM_HANDLE_BOTTOM_OFFSET(1.5f);                              // Offset between bottom handle and cutCopyPaste pop-up
42 const float UI_Z_OFFSET( 0.2f );                                            // Text Selection Handles/Cursor z-offset.
43 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET);                           // Text Selection Handles/Cursor offset.
44 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
45 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
46 const std::size_t CURSOR_BLINK_INTERVAL = 500;                              // Cursor blink interval
47 const float CURSOR_THICKNESS(6.0f);
48 const Degree CURSOR_ANGLE_OFFSET(2.0f);                                     // Offset from the angle
49
50 const unsigned int SCROLL_TICK_INTERVAL = 50u;
51 const float SCROLL_THRESHOLD = 10.f;
52 const float SCROLL_SPEED = 15.f;
53
54 /**
55  * Whether the given position plus the cursor size offset is inside the given boundary.
56  *
57  * @param[in] position The given position.
58  * @param[in] cursorSize The cursor size.
59  * @param[in] controlSize The given boundary.
60  * @param[in] threshold imaginary indent around boundary that will trigger the position to be outside of control.
61  *
62  * @return whether the given position is inside the given boundary.
63  */
64 bool IsPositionWithinControl( const Vector3& position, const Size& cursorSize, const Vector3& controlSize, const Vector2 threshold = Vector2::ZERO )
65 {
66   return ( position.x >= -Math::MACHINE_EPSILON_1000 + threshold.x ) &&
67          ( position.x <= controlSize.width - threshold.x + Math::MACHINE_EPSILON_1000 ) &&
68          ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 + threshold.y ) &&
69          ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 - threshold.y);
70 }
71
72 }
73
74 namespace Dali
75 {
76
77 namespace Toolkit
78 {
79
80 namespace Internal
81 {
82
83 Decorator::Decorator( TextViewCharacterPositioning& textViewManager, TextInputTextStyle& textStyle ):
84   mTextViewCharacterPositioning( textViewManager ),
85   mTextStyle( textStyle ),
86   mSelectionHandleOnePosition(0),
87   mSelectionHandleTwoPosition(0),
88   mGrabHandlePosition(0),
89   mCursorPosition( 0 ),
90   mTextHighlight( textViewManager ),
91   mCursorBlinkStatus( true ),
92   mCursorVisibility( true ),
93   mCursorRTLEnabled( false ),
94   mIsGrabHandleInScrollArea( false ),
95   mIsCursorInScrollArea( false ),
96   mGrabHandleVisibility( false ),
97   mGrabHandleEnabled( true )
98 {
99 }
100
101 Decorator::~Decorator()
102 {
103 }
104
105 /**
106  * Bounding Box
107  */
108 void Decorator::SetBoundingBox( const Rect<float>& boundingRectangle )
109 {
110   // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
111   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
112
113   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
114   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
115
116   const Vector4 boundary( originX,
117                           originY,
118                           originX + boundingRectangle.width,
119                           originY + boundingRectangle.height );
120
121   mBoundingRectangleWorldCoordinates = boundary;
122 }
123
124 Vector4 Decorator::GetBoundingBox() const
125 {
126   return mBoundingRectangleWorldCoordinates;
127 }
128
129 /**
130  * Selection Handles
131  */
132 void Decorator::OnHandlePan(Actor actor, const PanGesture& gesture)
133 {
134   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
135   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
136
137   switch (gesture.state)
138   {
139     case Gesture::Started:
140     // fall through so code not duplicated
141     case Gesture::Continuing:
142     {
143       if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
144       {
145         MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
146         HidePopUp();
147       }
148       else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
149       {
150         MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
151         HidePopUp();
152       }
153       else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
154       {
155         SetCursorVisibility( true );
156         ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
157         MoveGrabHandle( gesture.displacement );
158         HidePopUp(); // Do not show popup while handle is moving
159       }
160     }
161     break;
162
163     case Gesture::Finished:
164     {
165       // Revert back to non-pressed selection handle images
166       if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
167       {
168          mSelectionHandleOneActualPosition = MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
169         ShowPopupCutCopyPaste();
170       }
171       else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
172       {
173         mSelectionHandleTwoActualPosition = MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
174         ShowPopupCutCopyPaste();
175       }
176       else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
177       {
178         MoveGrabHandle( gesture.displacement );
179         SetCursorVisibility( true );
180         ShowPopupCutCopyPaste();
181       }
182     }
183     break;
184     default:
185       break;
186   }
187 }
188
189 void Decorator::CreateSelectionHandles( Actor targetParent )
190 {
191   if ( !mPanGestureDetector )
192   {
193     mPanGestureDetector = PanGestureDetector::New();
194     mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
195   }
196
197   if ( !mTextInputHandles.GetSelectionHandleOne() )
198   {
199     mTextInputHandles.CreateSelectionHandles();
200
201     mTextInputHandles.AttachSelectionHandlesToGivenPanGesture( mPanGestureDetector );
202
203     targetParent.Add( mTextInputHandles.GetSelectionHandleOne() );
204     targetParent.Add( mTextInputHandles.GetSelectionHandleTwo() );
205
206     SetUpHandlePropertyNotifications();
207   }
208 }
209
210 void Decorator::RemoveSelectionHandles()
211 {
212   mTextInputHandles.DestorySelectionHandles();
213 }
214
215 Vector3 Decorator::GetSelectionHandleSize()
216 {
217   return DEFAULT_SELECTION_HANDLE_SIZE;
218 }
219
220 std::size_t Decorator::GetHandleOnePosition() const
221 {
222   return mSelectionHandleOnePosition;
223 }
224
225 std::size_t Decorator::GetHandleTwoPosition() const
226 {
227   return mSelectionHandleTwoPosition;
228 }
229
230 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, std::size_t position )
231 {
232   bool direction(false);
233   Vector3 alternatePosition;
234   bool alternatePositionValid(false);
235
236   Vector3 actualPositionOfSelectionHandle = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( position, direction, alternatePosition,alternatePositionValid );
237
238   return PositionSelectionHandle( selectionHandle, actualPositionOfSelectionHandle, position );
239
240 }
241
242 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, Vector3& actualPosition, std::size_t position )
243 {
244   const Vector3 DEFAULT_HANDLE_OFFSET(0.0f, -5.0f, 0.0f);
245
246   selectionHandle.SetPosition( actualPosition += DEFAULT_HANDLE_OFFSET );
247
248   return actualPosition;
249 }
250
251 void Decorator::SetSelectionHandlesVisibility(bool visible )
252 {
253   mTextInputHandles.SetSelectionHandleOneVisibility( visible );
254   mTextInputHandles.SetSelectionHandleTwoVisibility( visible );
255 }
256
257 void Decorator::PositionSelectionHandles( std::size_t start, std::size_t end )
258 {
259   mSelectionHandleOnePosition = start;
260   mSelectionHandleTwoPosition = end;
261
262   mTextViewCharacterPositioning.UpdateTextLayoutInfo();
263
264   mSelectionHandleOneActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleOne(), mSelectionHandleOnePosition );
265   mSelectionHandleTwoActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleTwo(), mSelectionHandleTwoPosition );
266 }
267
268 Vector3 Decorator::MoveSelectionHandle( Actor selectionHandle,
269                                                           Vector3& actualSelectionHandlePosition,
270                                                           std::size_t& currentSelectionHandlePosition,
271                                                           const Vector2& displacement )
272 {
273   Vector3 actualHandlePosition;
274   actualSelectionHandlePosition.x += displacement.x * selectionHandle.GetCurrentScale().x;
275   actualSelectionHandlePosition.y += displacement.y * selectionHandle.GetCurrentScale().y;
276
277   // Selection handles should jump to the nearest character
278   std::size_t newHandlePosition = 0;
279   newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY() );
280
281   bool direction(false);
282   Vector3 alternatePosition;
283   bool alternatePositionValid(false);
284   actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition,direction, alternatePosition, alternatePositionValid  );
285
286   bool handleVisible = true;
287
288   if ( handleVisible &&                                          // Ensure the handle is visible.
289      ( newHandlePosition != currentSelectionHandlePosition ) &&  // Ensure the handle has moved.
290      ( newHandlePosition != mSelectionHandleTwoPosition ) &&     // Ensure new handle position not the same position as an existing handle.
291      ( newHandlePosition != mSelectionHandleOnePosition ) )
292   {
293     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle visible and moved]\n");
294
295     currentSelectionHandlePosition = newHandlePosition;
296
297     PositionSelectionHandle( selectionHandle, actualHandlePosition, newHandlePosition );
298
299     ShowUpdatedHighlight();
300
301     // Set Active Style to that of first character in selection
302     std::size_t firstHandleInSelection = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
303
304     const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( firstHandleInSelection );
305     mTextStyle.SetInputStyle( inputStyle );
306   }
307   return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
308 }
309
310 /**
311  * GrabHandle
312  */
313 void Decorator::PositionGrabHandle( std::size_t positionInText )
314 {
315   bool direction(false);
316   Vector3 alternatePosition;
317   bool alternatePositionValid(false);
318
319   mGrabHandlePosition = positionInText;
320
321   mTextViewCharacterPositioning.UpdateTextLayoutInfo();
322   mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( positionInText, direction, alternatePosition,alternatePositionValid );
323
324   mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
325 }
326
327 void Decorator::MoveGrabHandle( const Vector2& displacement /*, std::size_t currentHandlePosition */)
328 {
329   mActualGrabHandlePosition.x += displacement.x;
330   mActualGrabHandlePosition.y += displacement.y;
331
332   // Grab handle should jump to the nearest character and take cursor with it
333   std::size_t newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
334
335   Vector3 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition );
336
337   bool handleVisible = true;
338
339   if( ( newHandlePosition != mGrabHandlePosition ) &&                           // Only redraw cursor and do updates if position changed
340         ( handleVisible ) )// and the new position is visible (if scroll is not enabled, it's always true).
341     {
342       mActualGrabHandlePosition = actualHandlePosition;
343       mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
344
345       //PositionGrabHandle( newHandlePosition );
346       mGrabHandlePosition = newHandlePosition;
347       SetCurrentCursorPosition( mGrabHandlePosition );
348       DrawCursor( mGrabHandlePosition );
349
350       const std::size_t cursorPosition = GetCurrentCursorPosition();
351
352       // Let keyboard know the new cursor position so can 're-capture' for prediction.
353       mCursorRePositionedSignal.Emit();
354
355       // Set Input Style to that of cursor position
356       if ( !mTextViewCharacterPositioning.IsStyledTextEmpty()  && ( cursorPosition > 0 ) )
357       {
358         DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
359       }
360     }
361 }
362
363 void Decorator::ShowGrabHandle( bool visible )
364 {
365   mGrabHandleVisibility = visible;
366   mTextInputHandles.SetGrabHandleVisibility( visible );
367 }
368
369 void Decorator::CreateGrabHandle( Actor targetParent )
370 {
371   if ( !mPanGestureDetector )
372   {
373     mPanGestureDetector = PanGestureDetector::New();
374     mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
375   }
376
377   if ( !mTextInputHandles.GetGrabHandle() )
378   {
379     mTextInputHandles.CreateGrabHandle();
380     mTextInputHandles.AttachGrabHandleToGivenPanGesture( mPanGestureDetector );
381     targetParent.Add( mTextInputHandles.GetGrabHandle() );
382   }
383 }
384
385 void Decorator::SetGrabHandleImage( Image image )
386 {
387   mTextInputHandles.SetGrabHandleImage( image );
388 }
389
390 void Decorator::EnableGrabHandle( bool toggle)
391 {
392   // enables grab handle with will in turn de-activate magnifier
393   mGrabHandleEnabled = toggle;
394 }
395
396 bool Decorator::IsGrabHandleEnabled()
397 {
398   // if false then magnifier will be shown instead.
399   return mGrabHandleEnabled;
400 }
401
402 /**
403  * Cursor
404  */
405 std::size_t Decorator::GetCurrentCursorPosition() const
406 {
407   return mCursorPosition;
408 }
409
410 void Decorator::SetCurrentCursorPosition( std::size_t newCursorPosition )
411 {
412   mCursorPosition = newCursorPosition;
413 }
414
415 void Decorator::SetCursorVisibility( bool visible )
416 {
417   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::SetCursorVisibility[%s]\n", ( visible )?"true":"false");
418
419   mCursorVisibility = visible;
420   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
421   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
422 }
423
424 void Decorator::DrawCursor(const std::size_t nthChar)
425 {
426   std::size_t cursorPosition = GetCurrentCursorPosition();
427
428   // Get height of cursor and set its size
429   Size size( CURSOR_THICKNESS, 0.0f );
430
431   Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
432   size.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max  ).height;
433
434   mCursor.SetSize(size);
435
436   // If the character is italic then the cursor also tilts.
437   if ( !mTextViewCharacterPositioning.IsStyledTextEmpty()  && ( cursorPosition > 0 ) )
438   {
439     DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
440     const TextStyle styleAtCursor = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
441     mCursor.SetRotation( styleAtCursor.IsItalicsEnabled() ? Degree( styleAtCursor.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
442   }
443
444   DALI_ASSERT_DEBUG( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() );
445   if ( ( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() ) )
446   {
447     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
448     bool altPositionValid( false );  // Alternate cursor validity flag.
449     bool directionRTL( false );      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
450     Vector3 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition, directionRTL, altPosition, altPositionValid );
451
452     SetAltCursorEnabled( altPositionValid );
453
454     mCursor.SetPosition( position + UI_OFFSET );
455   }
456 }
457
458 void Decorator::SetAltCursorEnabled( bool enabled )
459 {
460   mCursorRTLEnabled = enabled;
461   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
462 }
463
464 void Decorator::SetCursorImage(Dali::Image image, const Vector4& border )
465 {
466   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
467
468   if ( image )
469   {
470     mCursor.SetImage( image );
471     mCursor.SetNinePatchBorder( border );
472   }
473 }
474
475 void Decorator::SetRTLCursorImage( Image image, const Vector4& border )
476 {
477   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
478
479   if ( image )
480   {
481     mCursorRTL.SetImage( image );
482     mCursorRTL.SetNinePatchBorder(  border );
483   }
484 }
485
486 ImageActor Decorator::CreateCursor( Image cursorImage, const Vector4& border, const std::string& cursorName )
487 {
488   ImageActor cursor;
489
490   if ( cursorImage )
491   {
492     cursor = ImageActor::New( cursorImage );
493   }
494   else
495   {
496     cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
497   }
498
499   cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
500   cursor.SetNinePatchBorder( border );
501   cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
502   cursor.SetVisible(false);
503   cursor.SetName( cursorName );
504   return cursor;
505 }
506
507 void Decorator::CreateCursors( Actor targetParent )
508 {
509   Image mCursorImage = Image::New( DEFAULT_CURSOR );
510   mCursor = CreateCursor (mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER , "mainCursor");
511   mCursorRTL = CreateCursor ( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER, "rtlCursor");
512   targetParent.Add( mCursor );
513   targetParent.Add( mCursorRTL );
514 }
515
516 Size Decorator::GetCursorSizeAt( std::size_t positionWithinTextToGetCursorSize )
517 {
518   std::size_t visualPosition = mTextViewCharacterPositioning.GetVisualPosition( positionWithinTextToGetCursorSize );
519
520   Vector2 min, max;
521
522   const Size cursorSize( CURSOR_THICKNESS,
523       mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( visualPosition, min, max ).height );
524
525   return cursorSize;
526 }
527
528 void Decorator::StartCursorBlinkTimer()
529 {
530   if ( !mCursorBlinkTimer )
531   {
532     mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
533     mCursorBlinkTimer.TickSignal().Connect( this, &Decorator::OnCursorBlinkTimerTick );
534   }
535
536   if ( !mCursorBlinkTimer.IsRunning() )
537   {
538     mCursorBlinkTimer.Start();
539   }
540 }
541
542 void Decorator::StopCursorBlinkTimer()
543 {
544   if ( mCursorBlinkTimer )
545   {
546     mCursorBlinkTimer.Stop();
547   }
548 }
549
550 bool Decorator::OnCursorBlinkTimerTick()
551 {
552   // Cursor blinking
553   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
554   if ( mCursorRTLEnabled )
555   {
556     mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
557   }
558   mCursorBlinkStatus = !mCursorBlinkStatus;
559
560   return true;
561 }
562
563 /**
564  * Highlight
565  */
566 void Decorator::ShowUpdatedHighlight()
567 {
568   Toolkit::TextView::TextLayoutInfo textLayoutInfo = mTextViewCharacterPositioning.GetLayoutInfo();
569   TextHighlight::HighlightInfo highlightInfo = mTextHighlight.CalculateHighlightInfo( mSelectionHandleOnePosition, mSelectionHandleTwoPosition, textLayoutInfo );
570
571   // Clamp highlightInfo so they don't exceed the boundary of the control.
572   const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
573   highlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
574
575   mTextHighlight.UpdateHighlight( highlightInfo );
576 }
577
578 void Decorator::CreateHighlight( Actor parent )
579 {
580   DALI_ASSERT_DEBUG( parent && "Highlight target parent does not exist" );
581
582   if ( !mHighlightMeshActor )
583   {
584     mHighlightMeshActor = MeshActor::New( mTextHighlight.CreateHighLightMesh() );
585     mHighlightMeshActor.SetName( "HighlightMeshActor" );
586     mHighlightMeshActor.SetAffectedByLighting(false);
587     parent.Add( mHighlightMeshActor );
588   }
589 }
590
591 void Decorator::RemoveHighlight()
592 {
593   if ( mHighlightMeshActor )
594   {
595     mHighlightMeshActor.Unparent();
596     mHighlightMeshActor.Reset();
597     // NOTE: We cannot dereference mHighlightMesh, due to a how the scene-graph MeshRenderer uses the Mesh data.
598   }
599 }
600
601 void Decorator::HighlightVisibility( bool visiblility )
602 {
603   if ( mHighlightMeshActor )
604   {
605     mHighlightMeshActor.SetVisible( visiblility );
606   }
607 }
608
609 /**
610  *  Callbacks connected to be Property notifications for Boundary checking.
611  */
612 // Note If PropertyNotification signal definition included Actor we would not need to duplicate functions.
613 void Decorator::OnHandleOneLeavesBoundary( PropertyNotification& source)
614 {
615   mTextInputHandles.GetSelectionHandleOne().SetOpacity(0.0f);
616 }
617
618 void Decorator::OnHandleOneWithinBoundary(PropertyNotification& source)
619 {
620   mTextInputHandles.GetSelectionHandleOne().SetOpacity(1.0f);
621 }
622
623 void Decorator::OnHandleTwoLeavesBoundary( PropertyNotification& source)
624 {
625   mTextInputHandles.GetSelectionHandleTwo().SetOpacity(0.0f);
626 }
627
628 void Decorator::OnHandleTwoWithinBoundary(PropertyNotification& source)
629 {
630   mTextInputHandles.GetSelectionHandleTwo().SetOpacity(1.0f);
631 }
632
633 void Decorator::OnLeftBoundaryExceeded(PropertyNotification& source)
634 {
635   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnLeftBoundaryExceeded\n");
636   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
637   selectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
638   selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
639 }
640
641 void Decorator::OnReturnToLeftBoundary(PropertyNotification& source)
642 {
643   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnReturnToLeftBoundary\n");
644   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
645   selectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
646   selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
647 }
648
649 void Decorator::OnRightBoundaryExceeded(PropertyNotification& source)
650 {
651   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
652   selectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
653   selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
654 }
655
656 void Decorator::OnReturnToRightBoundary(PropertyNotification& source)
657 {
658   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
659   selectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
660   selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
661 }
662
663 void Decorator::SetUpHandlePropertyNotifications()
664 {
665   /* Property notifications for handles exceeding the boundary and returning back within boundary */
666
667   Vector3 handlesize = GetSelectionHandleSize();
668
669   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
670   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
671
672   // Exceeding horizontal boundary
673   PropertyNotification leftNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
674   leftNotification.NotifySignal().Connect( this, &Decorator::OnLeftBoundaryExceeded );
675
676   PropertyNotification rightNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
677   rightNotification.NotifySignal().Connect( this, &Decorator::OnRightBoundaryExceeded );
678
679   // Within horizontal boundary
680   PropertyNotification leftLeaveNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
681   leftLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToLeftBoundary );
682
683   PropertyNotification rightLeaveNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
684   rightLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToRightBoundary );
685
686   // Exceeding vertical boundary
687   PropertyNotification verticalExceedNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
688                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
689                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
690   verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneLeavesBoundary );
691
692   PropertyNotification verticalExceedNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
693                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
694                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
695   verticalExceedNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoLeavesBoundary );
696
697   // Within vertical boundary
698   PropertyNotification verticalWithinNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
699                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
700                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
701   verticalWithinNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneWithinBoundary );
702
703   PropertyNotification verticalWithinNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
704                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
705                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
706   verticalWithinNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoWithinBoundary );
707 }
708
709 /**
710  * PopUp
711  */
712 Vector3 Decorator::PositionOfPopUpRelativeToSelectionHandles()
713 {
714   Vector3 position;
715   Vector2 min, max;
716   Vector3 topHandle;
717   Size rowSize;
718
719   // When text is selected, show popup above top handle (and text), or below bottom handle.
720
721   // topHandle: referring to the top most point of the handle or the top line of selection.
722   if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y ) // Handle may switch positions so calculate which is top.
723   {
724     topHandle = mSelectionHandleOneActualPosition;
725     rowSize= mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleOnePosition, min, max );
726   }
727   else
728   {
729     topHandle = mSelectionHandleTwoActualPosition;
730     rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition, min, max );
731   }
732   topHandle.y += TOP_HANDLE_TOP_OFFSET - rowSize.height;
733   position = Vector3(topHandle.x, topHandle.y, 0.0f);
734
735   return position;
736 }
737
738 Vector3 Decorator::AlternatePopUpPositionRelativeToSelectionHandles()
739 {
740   // alternativePosition: referring to the bottom most point of the handle or the bottom line of selection.
741   Vector3 alternativePosition;
742   alternativePosition.y = std::max ( mSelectionHandleTwoActualPosition.y , mSelectionHandleOneActualPosition.y );
743   alternativePosition.y += GetSelectionHandleSize().y + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET;
744
745   return alternativePosition;
746 }
747
748 Vector3 Decorator::PositionOfPopUpRelativeToCursor()
749 {
750   // When no text is selected, show PopUp at position of cursor
751   Vector3 position;
752   Vector2 min, max;
753   std::size_t cursorPosition = GetCurrentCursorPosition();
754   position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
755   const Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorPosition, min, max );
756   position.y -= rowSize.height;
757
758   return position;
759 }
760
761 Vector3 Decorator::AlternatePopUpPositionRelativeToCursor()
762 {
763   std::size_t cursorPosition = GetCurrentCursorPosition();
764   Vector3 alternativePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
765
766   if (  mTextInputHandles.GetGrabHandle() )
767   {
768     // If grab handle enabled then position pop-up below the grab handle.
769     alternativePosition.y +=  mTextInputHandles.GetGrabHandle().GetCurrentSize().height  + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET ;
770   }
771   else
772   {
773     alternativePosition.y += mPopUpPanel.GetSize().y;
774   }
775
776   return alternativePosition;
777
778 }
779
780 Vector3 Decorator::PositionOfPopUpRelativeToGrabHandle()
781 {
782    return Vector3::ZERO;
783 }
784
785 void Decorator::ShowPopUp()
786 {
787   Vector3 position;
788   Vector3 alternativePosition;
789   Size rowSize;
790
791   DALI_ASSERT_DEBUG( mPopUpTarget && "PopUp Target Actor does not exist" );
792
793   if( mHighlightMeshActor ) // Text Selection mode
794   {
795     position = PositionOfPopUpRelativeToSelectionHandles();
796   }
797   else // Not in Text Selection mode so position relative to cursor.
798   {
799     position = PositionOfPopUpRelativeToCursor();
800   }
801
802   // reposition popup above the desired cursor position.
803   mPopUpPanel.Show( mPopUpTarget, true );
804   mPopUpPanel.Self().SetPosition( position );
805   mPopUpPanel.PressedSignal().Connect( this, &Decorator::OnPopupButtonPressed );
806
807   SetUpPopUpPositionNotifications();
808   mPopUpPanel.ApplyConfinementConstraint( mBoundingRectangleWorldCoordinates );
809 }
810
811 void Decorator::ShowPopUp( Actor target )
812 {
813   mPopUpTarget = target;
814   ShowPopupCutCopyPaste();
815 }
816
817 void Decorator::ShowPopupCutCopyPaste()
818 {
819   bool isAllTextSelectedAlready = ( mTextViewCharacterPositioning.StyledTextSize() == GetSelectedText().size() );
820   bool isTextEmpty = mTextViewCharacterPositioning.IsStyledTextEmpty() ;
821   bool isSubsetOfTextAlreadySelected = ( !isAllTextSelectedAlready ) && mHighlightMeshActor;
822
823   Clipboard clipboard = Clipboard::Get();
824   bool hasClipboardGotContent = clipboard.NumberOfItems();
825
826   mPopUpPanel.CreateCutCopyPastePopUp( isAllTextSelectedAlready, isTextEmpty, hasClipboardGotContent, isSubsetOfTextAlreadySelected );
827   ShowPopUp();
828 }
829
830 void Decorator::HidePopUp( bool animate, bool signalFinished )
831 {
832 }
833
834 void Decorator::AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption)
835 {
836   mPopUpPanel.AddButton(name, caption, icon, finalOption);
837 }
838
839 void Decorator::ClearPopup()
840 {
841   mPopUpPanel.Clear();
842 }
843
844 void Decorator::PopUpLeavesVerticalBoundary( PropertyNotification& source)
845 {
846   Vector3 position, alternativePosition;
847
848   if( mHighlightMeshActor ) // Text Selection mode
849   {
850     alternativePosition = AlternatePopUpPositionRelativeToSelectionHandles();
851   }
852   else // Not in Text Selection mode
853   {
854     alternativePosition = AlternatePopUpPositionRelativeToCursor();
855     // if can't be positioned above, then position below row.
856   }
857   // reposition popup above the desired cursor position.
858   mPopUpPanel.Self().SetPosition( alternativePosition );
859 }
860
861 void Decorator::SetUpPopUpPositionNotifications( )
862 {
863   // Note Property notifications ignore any set anchor point so conditions must allow for this.  Default is Top Left.
864
865   // Exceeding vertical boundary
866   PropertyNotification verticalExceedNotificationOne = mPopUpPanel.Self().AddPropertyNotification( Actor::WORLD_POSITION_Y,
867                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + mPopUpPanel.GetSize().y/2,
868                                                                          mBoundingRectangleWorldCoordinates.w - mPopUpPanel.GetSize().y/2 ) );
869   verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::PopUpLeavesVerticalBoundary );
870 }
871
872 bool Decorator::OnPopupButtonPressed( Toolkit::Button button )
873 {
874   mPopUpButtonPressedSignal.Emit( button );
875   return false;
876 }
877
878 Decorator::PressedSignal& Decorator::PopUpButtonPressedSignal()
879 {
880   return mPopUpButtonPressedSignal;
881 }
882
883 Decorator::CursorPositionedSignal& Decorator::CursorRePositionedSignal()
884 {
885   return mCursorRePositionedSignal;
886 }
887
888 /**
889  *  Decoration Positioning during Scrolling
890  */
891 void Decorator::TextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
892 {
893   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::TextViewScrolled\n");
894
895   const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize(); // todo Could store size and only update in Control Size change.
896   Size cursorSize( CURSOR_THICKNESS, 0.f );
897
898   // Updates the cursor and grab handle position and visibility.
899   if( mTextInputHandles.GetGrabHandle() || mCursor )
900   {
901     Vector2 min, max;
902     size_t cursorTextPosition = GetCurrentCursorPosition();
903     cursorSize.height  = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorTextPosition, min, max ).height;
904
905     const Vector3 cursorPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorTextPosition );
906
907     bool mIsCursorInScrollArea = IsPositionWithinControl( cursorPosition, cursorSize, controlSize );
908     bool mIsGrabHandleInScrollArea = mIsCursorInScrollArea;
909
910     Vector2 actualGrabHandlePosition = cursorPosition.GetVectorXY();
911
912     if( mTextInputHandles.GetGrabHandle() )
913     {
914       ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
915       PositionGrabHandle( cursorTextPosition );
916     }
917
918     if( mCursor )
919     {
920       mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
921       DrawCursor( cursorTextPosition );
922       mCursor.SetPosition( Vector3(actualGrabHandlePosition) + UI_OFFSET );
923     }
924   }
925
926   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
927   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
928
929   // Updates the selection handles and highlighted text position and visibility.
930   if( mTextInputHandles.GetSelectionHandleOne() && mTextInputHandles.GetSelectionHandleTwo() )
931   {
932     const Vector3 cursorPositionOne = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
933     const Vector3 cursorPositionTwo = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
934
935     Size cursorSize( GetCursorSizeAt( mSelectionHandleOnePosition ) );
936     const bool isSelectionHandleOneVisible = IsPositionWithinControl( cursorPositionOne, cursorSize, controlSize );
937
938     cursorSize =  GetCursorSizeAt( mSelectionHandleTwoPosition );
939     const bool isSelectionHandleTwoVisible = IsPositionWithinControl( cursorPositionTwo, cursorSize, controlSize );
940
941     mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
942     mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
943
944     selectionHandleOne.SetVisible( isSelectionHandleOneVisible );
945     selectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
946
947     PositionSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition );
948     PositionSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition );
949
950     if( mHighlightMeshActor )
951     {
952       mHighlightMeshActor.SetVisible( true );
953       ShowUpdatedHighlight();
954     }
955   }
956 }
957
958 void Decorator::StartScrollTimer()
959 {
960   if( !mScrollTimer )
961   {
962     mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
963     mScrollTimer.TickSignal().Connect( this, &Decorator::OnScrollTimerTick );
964   }
965
966   if( !mScrollTimer.IsRunning() )
967   {
968     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StartScrollTimer\n");
969     mScrollTimer.Start();
970   }
971 }
972
973 void Decorator::StopScrollTimer()
974 {
975   if( mScrollTimer )
976   {
977     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StopScrollTimer\n");
978
979     mScrollTimer.Stop();
980     mScrollTimer.Reset();
981   }
982 }
983
984 bool Decorator::OnScrollTimerTick()
985 {
986   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick\n");
987
988   if ( mGrabHandleVisibility && mTextInputHandles.GetGrabHandle() )
989   {
990     std::size_t newGrabHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
991     if ( mGrabHandlePosition != newGrabHandlePosition )
992     {
993       Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
994       Vector2 scrollDelta = ( mActualGrabHandlePosition - mCurrentHandlePosition ).GetVectorXY();
995       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick scrollPosition(%f) scrollDelta(%f)\n", scrollPosition.x, scrollDelta.x);
996       scrollPosition += scrollDelta;
997       mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
998
999       mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newGrabHandlePosition ).GetVectorXY();
1000     }
1001   }
1002
1003   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
1004   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
1005
1006   if ( selectionHandleOne && selectionHandleTwo )
1007   {
1008     std::size_t newHandleOnePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleOneActualPosition.GetVectorXY() );
1009
1010     // todo duplicated code should be a function
1011
1012     if ( mSelectionHandleOnePosition != newHandleOnePosition )
1013     {
1014       const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleOnePosition );
1015
1016       Vector2 scrollDelta = ( actualPosition - mSelectionHandleOneActualPosition ).GetVectorXY();
1017
1018       Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1019       scrollPosition += scrollDelta;
1020       mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1021
1022       mSelectionHandleOnePosition = newHandleOnePosition;
1023       mSelectionHandleOneActualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition ).GetVectorXY();
1024     }
1025     else
1026     {
1027       mSelectionHandleOneActualPosition.x += mScrollDisplacement.x;
1028       mSelectionHandleOneActualPosition.y += mScrollDisplacement.y;
1029     }
1030
1031     std::size_t newHandleTwoPosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleTwoActualPosition.GetVectorXY() );
1032
1033     if ( mSelectionHandleTwoPosition != newHandleTwoPosition )
1034     {
1035       const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleTwoPosition );
1036
1037       Vector2 scrollDelta = ( actualPosition - mSelectionHandleTwoActualPosition ).GetVectorXY();
1038
1039       Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1040       scrollPosition += scrollDelta;
1041       mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1042
1043       mSelectionHandleTwoPosition = newHandleTwoPosition;
1044       mCurrentHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition ).GetVectorXY();
1045
1046     }
1047     else
1048     {
1049       mSelectionHandleTwoActualPosition.x += mScrollDisplacement.x;
1050       mSelectionHandleTwoActualPosition.y += mScrollDisplacement.y;
1051     }
1052   }
1053
1054   return true;
1055 }
1056
1057 /**
1058  *  Text Selection
1059  */
1060 MarkupProcessor::StyledTextArray Decorator::GetSelectedText()
1061 {
1062   MarkupProcessor::StyledTextArray currentSelectedText;
1063
1064   if ( mHighlightMeshActor ) // Text Selected
1065   {
1066     MarkupProcessor::StyledTextArray::iterator it = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1067     MarkupProcessor::StyledTextArray::iterator end = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1068
1069     for(; it != end; ++it)
1070     {
1071       MarkupProcessor::StyledText& styledText( *it );
1072       currentSelectedText.push_back( styledText );
1073     }
1074   }
1075   return currentSelectedText;
1076 }
1077
1078 } // Internal
1079
1080 } // namespace Toolkit
1081
1082 } // namespace Dali
1083