2 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/text/decorator/text-decorator.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/actors/layer.h>
24 #include <dali/public-api/adaptor-framework/timer.h>
25 #include <dali/public-api/common/stage.h>
26 #include <dali/public-api/events/touch-data.h>
27 #include <dali/public-api/events/pan-gesture.h>
28 #include <dali/public-api/images/resource-image.h>
29 #include <dali/public-api/object/property-notification.h>
30 #include <dali/public-api/rendering/geometry.h>
31 #include <dali/public-api/rendering/renderer.h>
34 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
35 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
36 #include <dali-toolkit/internal/controls/image-view/image-view-impl.h>
39 #define DECORATOR_DEBUG
43 #define MAKE_SHADER(A)#A
47 const char* VERTEX_SHADER = MAKE_SHADER(
48 attribute mediump vec2 aPosition;
49 uniform mediump mat4 uMvpMatrix;
50 uniform mediump vec3 uSize;
54 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
55 position.xyz *= uSize;
56 gl_Position = uMvpMatrix * position;
60 const char* FRAGMENT_SHADER = MAKE_SHADER(
61 uniform lowp vec4 uColor;
65 gl_FragColor = uColor;
76 #ifdef DECORATOR_DEBUG
77 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
87 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
88 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
90 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f ); // The text highlight color. TODO: due some problems, maybe with the blending function in the text clipping, the color is fully opaque.
92 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
94 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
95 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
96 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
98 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
99 const float SCROLL_THRESHOLD = 10.f; ///< Threshold in pixels close to the edges of the decorator boundaries from where the scroll timer starts to emit signals.
100 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
102 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
104 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
107 * structure to hold coordinates of each quad, which will make up the mesh.
109 struct QuadCoordinates
112 * Default constructor
120 * @param[in] x1 left co-ordinate
121 * @param[in] y1 top co-ordinate
122 * @param[in] x2 right co-ordinate
123 * @param[in] y2 bottom co-ordinate
125 QuadCoordinates(float x1, float y1, float x2, float y2)
131 Dali::Vector2 min; ///< top-left (minimum) position of quad
132 Dali::Vector2 max; ///< bottom-right (maximum) position of quad
135 typedef std::vector<QuadCoordinates> QuadContainer;
138 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
139 * @param[in] boundingRectangle local bounding
140 * @param[out] Vector4 World coordinate bounding Box.
142 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
144 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
145 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
147 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
148 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
150 boundingBox = Dali::Vector4( originX,
152 originX + boundingRectangle.width,
153 originY + boundingRectangle.height );
156 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
158 // Convert to local coordinates and store as a Dali::Rect.
159 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
161 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
162 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
163 boundingRectangle.width = boundingBox.z - boundingBox.x;
164 boundingRectangle.height = boundingBox.w - boundingBox.y;
167 } // end of namespace
178 struct Decorator::Impl : public ConnectionTracker
192 : color( Dali::Color::BLACK ),
194 cursorHeight( 0.0f ),
212 grabDisplacementX( 0.f ),
213 grabDisplacementY( 0.f ),
217 verticallyFlippedPreferred( false ),
218 horizontallyFlipped( false ),
219 verticallyFlipped( false )
225 ImageView markerActor;
228 Vector2 globalPosition;
230 float lineHeight; ///< Not the handle height
231 float grabDisplacementX;
232 float grabDisplacementY;
236 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
237 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
238 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
248 TextSelectionPopup actor;
252 Impl( ControllerInterface& controller,
253 TextSelectionPopupCallbackInterface& callbackInterface )
254 : mController( controller ),
255 mEnabledPopupButtons( TextSelectionPopup::NONE ),
256 mTextSelectionPopupCallbackInterface( callbackInterface ),
257 mHandleColor( HANDLE_COLOR ),
259 mHighlightColor( LIGHT_BLUE ),
260 mHighlightPosition( Vector2::ZERO ),
261 mActiveCursor( ACTIVE_CURSOR_NONE ),
262 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
263 mCursorBlinkDuration( 0.0f ),
264 mCursorWidth( CURSOR_WIDTH ),
265 mHandleScrolling( HANDLE_TYPE_COUNT ),
266 mScrollDirection( SCROLL_NONE ),
267 mScrollThreshold( SCROLL_THRESHOLD ),
268 mScrollSpeed( SCROLL_SPEED ),
269 mScrollDistance( SCROLL_DISTANCE ),
271 mActiveCopyPastePopup( false ),
272 mPopupSetNewPosition( true ),
273 mCursorBlinkStatus( true ),
274 mDelayCursorBlink( false ),
275 mPrimaryCursorVisible( false ),
276 mSecondaryCursorVisible( false ),
277 mFlipSelectionHandlesOnCross( false ),
278 mFlipLeftSelectionHandleDirection( false ),
279 mFlipRightSelectionHandleDirection( false ),
280 mIsHandlePanning( false ),
281 mIsHandleCurrentlyCrossed( false ),
282 mIsHandlePreviouslyCrossed( false ),
283 mNotifyEndOfScroll( false ),
284 mHorizontalScrollingEnabled( false ),
285 mVerticalScrollingEnabled( false )
287 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
288 mHighlightShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
293 * Relayout of the decorations owned by the decorator.
294 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
296 void Relayout( const Vector2& size )
300 // TODO - Remove this if nothing is active
303 // Show or hide the cursors
308 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
309 mPrimaryCursorVisible = ( ( cursor.position.x + mCursorWidth <= mControlSize.width ) &&
310 ( cursor.position.x >= 0.f ) &&
311 ( cursor.position.y + cursor.cursorHeight <= mControlSize.height ) &&
312 ( cursor.position.y >= 0.f ) );
313 if( mPrimaryCursorVisible )
315 mPrimaryCursor.SetPosition( cursor.position.x,
317 mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
319 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
321 if( mSecondaryCursor )
323 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
324 mSecondaryCursorVisible = ( ( cursor.position.x + mCursorWidth <= mControlSize.width ) &&
325 ( cursor.position.x >= 0.f ) &&
326 ( cursor.position.y + cursor.cursorHeight <= mControlSize.height ) &&
327 ( cursor.position.y >= 0.f ) );
328 if( mSecondaryCursorVisible )
330 mSecondaryCursor.SetPosition( cursor.position.x,
332 mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
334 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
337 // Show or hide the grab handle
338 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
339 bool newGrabHandlePosition = false;
340 if( grabHandle.active )
342 const bool isVisible = ( ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) <= mControlSize.width ) &&
343 ( grabHandle.position.x >= 0.f ) &&
344 ( grabHandle.position.y <= mControlSize.height - grabHandle.lineHeight ) &&
345 ( grabHandle.position.y >= 0.f ) );
351 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
352 SetGrabHandlePosition();
354 // Sets the grab handle image according if it's pressed, flipped, etc.
355 SetHandleImage( GRAB_HANDLE );
357 newGrabHandlePosition = true;
360 if( grabHandle.actor )
362 grabHandle.actor.SetVisible( isVisible );
365 else if( grabHandle.actor )
367 grabHandle.actor.Unparent();
370 // Show or hide the selection handles/highlight
371 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
372 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
373 bool newPrimaryHandlePosition = false;
374 bool newSecondaryHandlePosition = false;
375 if( primary.active || secondary.active )
377 const bool isPrimaryVisible = ( ( primary.position.x <= mControlSize.width ) &&
378 ( primary.position.x >= 0.f ) &&
379 ( primary.position.y <= mControlSize.height - primary.lineHeight ) &&
380 ( primary.position.y >= 0.f ) );
381 const bool isSecondaryVisible = ( ( secondary.position.x <= mControlSize.width ) &&
382 ( secondary.position.x >= 0.f ) &&
383 ( secondary.position.y <= mControlSize.height - secondary.lineHeight ) &&
384 ( secondary.position.y >= 0.f ) );
386 if( isPrimaryVisible || isSecondaryVisible )
388 CreateSelectionHandles();
390 if( isPrimaryVisible )
392 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
394 // Sets the primary handle image according if it's pressed, flipped, etc.
395 SetHandleImage( LEFT_SELECTION_HANDLE );
397 SetSelectionHandleMarkerSize( primary );
399 newPrimaryHandlePosition = true;
402 if( isSecondaryVisible )
404 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
406 // Sets the secondary handle image according if it's pressed, flipped, etc.
407 SetHandleImage( RIGHT_SELECTION_HANDLE );
409 SetSelectionHandleMarkerSize( secondary );
411 newSecondaryHandlePosition = true;
417 primary.actor.SetVisible( isPrimaryVisible );
419 if( secondary.actor )
421 secondary.actor.SetVisible( isSecondaryVisible );
431 primary.actor.Unparent();
433 if( secondary.actor )
435 secondary.actor.Unparent();
437 if( mHighlightActor )
439 mHighlightActor.Unparent();
443 if( newGrabHandlePosition ||
444 newPrimaryHandlePosition ||
445 newSecondaryHandlePosition )
447 // Setup property notifications to find whether the handles leave the boundaries of the current display.
448 SetupActiveLayerPropertyNotifications();
451 if( mActiveCopyPastePopup )
454 mPopupSetNewPosition = true;
458 if( mCopyPastePopup.actor )
460 mCopyPastePopup.actor.HidePopup();
461 mPopupSetNewPosition = true;
466 void UpdatePositions( const Vector2& scrollOffset )
468 mCursor[PRIMARY_CURSOR].position += scrollOffset;
469 mCursor[SECONDARY_CURSOR].position += scrollOffset;
470 mHandle[ GRAB_HANDLE ].position += scrollOffset;
471 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
472 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
473 mHighlightPosition += scrollOffset;
478 if ( !mCopyPastePopup.actor )
483 if( !mCopyPastePopup.actor.GetParent() )
485 mActiveLayer.Add( mCopyPastePopup.actor );
488 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
489 mCopyPastePopup.actor.ShowPopup();
492 void DeterminePositionPopup()
494 if( !mActiveCopyPastePopup )
499 // Retrieves the popup's size after relayout.
500 const Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
502 if( mPopupSetNewPosition )
504 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
505 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
506 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
508 if( primaryHandle.active || secondaryHandle.active )
510 // Calculates the popup's position if selection handles are active.
511 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
512 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
513 const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
515 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
516 mCopyPastePopup.position.y = -0.5f * popupSize.height - maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
520 // Calculates the popup's position if the grab handle is active.
521 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
522 if( grabHandle.verticallyFlipped )
524 mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height - grabHandle.size.height + cursor.position.y, 0.0f );
528 mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height + cursor.position.y, 0.0f );
533 // Checks if there is enough space above the text control. If not it places the popup under it.
534 GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize * AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
536 SetUpPopupPositionNotifications();
538 mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
539 mPopupSetNewPosition = false;
542 void PopupRelayoutComplete( Actor actor )
544 // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
546 DeterminePositionPopup();
549 void CreateCursor( Control& cursor, const Vector4& color )
551 cursor = Control::New();
552 cursor.SetBackgroundColor( color );
553 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT );
554 cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
557 // Add or Remove cursor(s) from parent
560 if( mActiveCursor == ACTIVE_CURSOR_NONE )
564 mPrimaryCursor.Unparent();
566 if( mSecondaryCursor )
568 mSecondaryCursor.Unparent();
573 // Create Primary and or Secondary Cursor(s) if active and add to parent
574 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
575 mActiveCursor == ACTIVE_CURSOR_BOTH )
577 if ( !mPrimaryCursor )
579 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
580 #ifdef DECORATOR_DEBUG
581 mPrimaryCursor.SetName( "PrimaryCursorActor" );
585 if( !mPrimaryCursor.GetParent() )
587 mActiveLayer.Add( mPrimaryCursor );
591 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
593 if ( !mSecondaryCursor )
595 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
596 #ifdef DECORATOR_DEBUG
597 mSecondaryCursor.SetName( "SecondaryCursorActor" );
601 if( !mSecondaryCursor.GetParent() )
603 mActiveLayer.Add( mSecondaryCursor );
608 if( mSecondaryCursor )
610 mSecondaryCursor.Unparent();
616 bool OnCursorBlinkTimerTick()
618 if( !mDelayCursorBlink )
621 if ( mPrimaryCursor )
623 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
625 if ( mSecondaryCursor )
627 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
630 mCursorBlinkStatus = !mCursorBlinkStatus;
635 mDelayCursorBlink = false;
643 // Will consume tap gestures on handles.
644 mTapDetector = TapGestureDetector::New();
646 // Will consume double tap gestures on handles.
647 mTapDetector.SetMaximumTapsRequired( 2u );
649 // Will consume long press gestures on handles.
650 mLongPressDetector = LongPressGestureDetector::New();
652 // Detects pan gestures on handles.
653 mPanDetector = PanGestureDetector::New();
654 mPanDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
657 void CreateActiveLayer()
661 mActiveLayer = Layer::New();
662 #ifdef DECORATOR_DEBUG
663 mActiveLayer.SetName ( "ActiveLayerActor" );
666 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
667 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
669 // Add the active layer telling the controller it doesn't need clipping.
670 mController.AddDecoration( mActiveLayer, false );
673 mActiveLayer.RaiseToTop();
676 void SetSelectionHandleMarkerSize( HandleImpl& handle )
678 if( handle.markerActor )
680 handle.markerActor.SetSize( 0, handle.lineHeight );
684 void CreateGrabHandle()
686 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
687 if( !grabHandle.actor )
689 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
691 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
692 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
693 grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
695 // Area that Grab handle responds to, larger than actual handle so easier to move
696 #ifdef DECORATOR_DEBUG
697 grabHandle.actor.SetName( "GrabHandleActor" );
698 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
700 grabHandle.grabArea = Control::New();
701 Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
702 control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
703 grabHandle.grabArea.SetName( "GrabArea" );
707 grabHandle.grabArea = Actor::New();
708 grabHandle.grabArea.SetName( "GrabArea" );
711 grabHandle.grabArea = Actor::New();
714 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
715 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
716 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
717 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
718 grabHandle.actor.Add( grabHandle.grabArea );
719 grabHandle.actor.SetColor( mHandleColor );
721 grabHandle.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
723 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
724 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
725 mTapDetector.Attach( grabHandle.actor );
726 mLongPressDetector.Attach( grabHandle.actor );
728 // The grab handle's area is attached to the pan detector.
729 // The OnPan() method is connected to the signals emitted by the pan detector.
730 mPanDetector.Attach( grabHandle.grabArea );
732 mActiveLayer.Add( grabHandle.actor );
736 if( grabHandle.actor && !grabHandle.actor.GetParent() )
738 mActiveLayer.Add( grabHandle.actor );
742 void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
746 handle.markerActor = ImageView::New( image );
747 handle.markerActor.SetColor( mHandleColor );
748 handle.actor.Add( handle.markerActor );
750 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
752 if( LEFT_SELECTION_HANDLE == handleType )
754 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
755 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
757 else if( RIGHT_SELECTION_HANDLE == handleType )
759 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
760 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
765 void CreateSelectionHandles()
767 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
770 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
772 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
773 #ifdef DECORATOR_DEBUG
774 primary.actor.SetName("SelectionHandleOne");
776 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
777 GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
778 primary.actor.SetColor( mHandleColor );
780 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
781 #ifdef DECORATOR_DEBUG
782 primary.grabArea.SetName("SelectionHandleOneGrabArea");
784 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
785 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
786 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
787 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
789 primary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
791 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
792 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
793 mTapDetector.Attach( primary.actor );
794 mLongPressDetector.Attach( primary.actor );
796 // The handle's area is attached to the pan detector.
797 // The OnPan() method is connected to the signals emitted by the pan detector.
798 mPanDetector.Attach( primary.grabArea );
800 primary.actor.Add( primary.grabArea );
802 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
806 if( primary.actor && !primary.actor.GetParent() )
808 mActiveLayer.Add( primary.actor );
811 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
812 if( !secondary.actor )
814 if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
816 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
817 #ifdef DECORATOR_DEBUG
818 secondary.actor.SetName("SelectionHandleTwo");
820 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
821 GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
822 secondary.actor.SetColor( mHandleColor );
824 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
825 #ifdef DECORATOR_DEBUG
826 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
828 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
829 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
830 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
831 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
833 secondary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
835 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
836 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
837 mTapDetector.Attach( secondary.actor );
838 mLongPressDetector.Attach( secondary.actor );
840 // The handle's area is attached to the pan detector.
841 // The OnPan() method is connected to the signals emitted by the pan detector.
842 mPanDetector.Attach( secondary.grabArea );
844 secondary.actor.Add( secondary.grabArea );
846 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
850 if( secondary.actor && !secondary.actor.GetParent() )
852 mActiveLayer.Add( secondary.actor );
856 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
858 // Gets the world position of the active layer. The active layer is where the handles are added.
859 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
861 // The grab handle position in world coords.
862 // The active layer's world position is the center of the active layer. The origin of the
863 // coord system of the handles is the top left of the active layer.
864 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f );
865 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f );
868 void SetGrabHandlePosition()
870 // Reference to the grab handle.
871 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
873 // Transforms the handle position into world coordinates.
874 // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
875 // as it's transforming the handle's position set by the text-controller and not
876 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
877 // retrieves the position of the center of the actor but the handle's position set
878 // by the text controller is not the center of the actor.
879 Vector2 grabHandleWorldPosition;
880 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
882 // Check if the grab handle exceeds the boundaries of the decoration box.
883 // At the moment only the height is checked for the grab handle.
885 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
886 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
887 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
889 // The grab handle 'y' position in local coords.
890 // If the grab handle exceeds the bottom of the decoration box,
891 // set the 'y' position to the top of the line.
892 // The SetGrabHandleImage() method will change the orientation.
893 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
895 if( grabHandle.actor )
897 grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ) + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f ),
898 yLocalPosition + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f ) );
902 void SetSelectionHandlePosition( HandleType type )
904 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
906 // Reference to the selection handle.
907 HandleImpl& handle = mHandle[type];
909 // Transforms the handle position into world coordinates.
910 // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
911 // as it's transforming the handle's position set by the text-controller and not
912 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
913 // retrieves the position of the center of the actor but the handle's position set
914 // by the text controller is not the center of the actor.
915 Vector2 handleWorldPosition;
916 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
918 // Whether to flip the handle (horizontally).
919 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
921 // Whether to flip the handles if they are crossed.
922 bool crossFlip = false;
923 if( mFlipSelectionHandlesOnCross || !mIsHandlePanning )
925 crossFlip = mIsHandleCurrentlyCrossed;
928 // Whether the handle was crossed before start the panning.
929 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
931 // Does not flip if both conditions are true (double flip)
932 flipHandle = flipHandle != ( crossFlip || isHandlePreviouslyCrossed );
934 // Will flip the handles vertically if the user prefers it.
935 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
937 if( crossFlip || isHandlePreviouslyCrossed )
939 if( isPrimaryHandle )
941 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
945 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
949 // Check if the selection handle exceeds the boundaries of the decoration box.
950 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
951 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
953 // Does not flip if both conditions are true (double flip)
954 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
958 if( handle.actor && !handle.horizontallyFlipped )
960 // Change the anchor point to flip the image.
961 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
963 handle.horizontallyFlipped = true;
968 if( handle.actor && handle.horizontallyFlipped )
970 // Reset the anchor point.
971 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
973 handle.horizontallyFlipped = false;
977 // Whether to flip the handle vertically.
978 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
979 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
980 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
982 // The primary selection handle 'y' position in local coords.
983 // If the handle exceeds the bottom of the decoration box,
984 // set the 'y' position to the top of the line.
985 // The SetHandleImage() method will change the orientation.
986 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
990 handle.actor.SetPosition( handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f ),
991 yLocalPosition + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f ) );
995 void SetHandleImage( HandleType type )
997 HandleImpl& handle = mHandle[type];
999 HandleType markerType = HANDLE_TYPE_COUNT;
1000 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1001 if( LEFT_SELECTION_HANDLE == type )
1003 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1004 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1006 else if( RIGHT_SELECTION_HANDLE == type )
1008 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1009 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1012 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1015 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1017 handle.actor.SetImage( mHandleImages[type][imageType] );
1020 if( HANDLE_TYPE_COUNT != markerType )
1022 if( handle.markerActor )
1024 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1025 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
1029 // Whether to flip the handle vertically.
1032 handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
1036 void CreateHighlight()
1038 if( !mHighlightActor )
1040 mHighlightActor = Actor::New();
1042 #ifdef DECORATOR_DEBUG
1043 mHighlightActor.SetName( "HighlightActor" );
1045 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
1046 mHighlightActor.SetSize( 1.0f, 1.0f );
1047 mHighlightActor.SetColor( mHighlightColor );
1048 mHighlightActor.SetColorMode( USE_OWN_COLOR );
1051 // Add the highlight box telling the controller it needs clipping.
1052 mController.AddDecoration( mHighlightActor, true );
1055 void UpdateHighlight()
1057 if ( mHighlightActor )
1059 if( !mHighlightQuadList.empty() )
1061 Vector< Vector2 > vertices;
1062 Vector< unsigned short> indices;
1065 std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
1066 std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
1068 for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
1070 QuadCoordinates& quad = *iter;
1073 vertex.x = quad.min.x;
1074 vertex.y = quad.min.y;
1075 vertices.PushBack( vertex );
1078 vertex.x = quad.max.x;
1079 vertex.y = quad.min.y;
1080 vertices.PushBack( vertex );
1082 // bottom-left (v+2)
1083 vertex.x = quad.min.x;
1084 vertex.y = quad.max.y;
1085 vertices.PushBack( vertex );
1087 // bottom-right (v+3)
1088 vertex.x = quad.max.x;
1089 vertex.y = quad.max.y;
1090 vertices.PushBack( vertex );
1092 // triangle A (3, 1, 0)
1093 indices.PushBack( v + 3 );
1094 indices.PushBack( v + 1 );
1095 indices.PushBack( v );
1097 // triangle B (0, 2, 3)
1098 indices.PushBack( v );
1099 indices.PushBack( v + 2 );
1100 indices.PushBack( v + 3 );
1103 if( ! mQuadVertices )
1105 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
1108 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1110 if( !mQuadGeometry )
1112 mQuadGeometry = Geometry::New();
1113 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1115 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1117 if( !mHighlightRenderer )
1119 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1120 mHighlightActor.AddRenderer( mHighlightRenderer );
1124 mHighlightActor.SetPosition( mHighlightPosition.x,
1125 mHighlightPosition.y );
1127 mHighlightQuadList.clear();
1129 if( mHighlightRenderer )
1131 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1136 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1138 if( Gesture::Started == gesture.state )
1140 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1142 handle.globalPosition.x = handle.position.x;
1143 handle.globalPosition.y = handle.position.y;
1146 handle.grabDisplacementX += gesture.displacement.x;
1147 handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.displacement.y );
1149 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1150 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1151 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlipped ? handle.lineHeight : 0.f );
1153 if( ( Gesture::Started == gesture.state ) ||
1154 ( Gesture::Continuing == gesture.state ) )
1157 mController.GetTargetSize( targetSize );
1159 if( mHorizontalScrollingEnabled &&
1160 ( x < mScrollThreshold ) )
1162 mScrollDirection = SCROLL_RIGHT;
1163 mHandleScrolling = type;
1166 else if( mHorizontalScrollingEnabled &&
1167 ( x > targetSize.width - mScrollThreshold ) )
1169 mScrollDirection = SCROLL_LEFT;
1170 mHandleScrolling = type;
1173 else if( mVerticalScrollingEnabled &&
1174 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1176 mScrollDirection = SCROLL_TOP;
1177 mHandleScrolling = type;
1180 else if( mVerticalScrollingEnabled &&
1181 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1183 mScrollDirection = SCROLL_BOTTOM;
1184 mHandleScrolling = type;
1189 mHandleScrolling = HANDLE_TYPE_COUNT;
1191 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1194 mIsHandlePanning = true;
1196 else if( ( Gesture::Finished == gesture.state ) ||
1197 ( Gesture::Cancelled == gesture.state ) )
1200 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1202 mNotifyEndOfScroll = false;
1203 mHandleScrolling = HANDLE_TYPE_COUNT;
1205 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1209 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1214 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1216 handle.pressed = false;
1218 mIsHandlePanning = false;
1222 void OnPan( Actor actor, const PanGesture& gesture )
1224 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1225 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1226 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1228 if( actor == grabHandle.grabArea )
1230 DoPan( grabHandle, GRAB_HANDLE, gesture );
1232 else if( actor == primarySelectionHandle.grabArea )
1234 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1236 else if( actor == secondarySelectionHandle.grabArea )
1238 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1242 bool OnGrabHandleTouched( Actor actor, const TouchData& touch )
1244 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1246 // Switch between pressed/release grab-handle images
1247 if( touch.GetPointCount() > 0 &&
1250 const PointState::Type state = touch.GetState( 0 );
1252 if( PointState::DOWN == state )
1254 grabHandle.pressed = true;
1256 else if( ( PointState::UP == state ) ||
1257 ( PointState::INTERRUPTED == state ) )
1259 grabHandle.pressed = false;
1262 SetHandleImage( GRAB_HANDLE );
1265 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1269 bool OnHandleOneTouched( Actor actor, const TouchData& touch )
1271 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1273 // Switch between pressed/release selection handle images
1274 if( touch.GetPointCount() > 0 &&
1275 primarySelectionHandle.actor )
1277 const PointState::Type state = touch.GetState( 0 );
1279 if( PointState::DOWN == state )
1281 primarySelectionHandle.pressed = true;
1283 else if( ( PointState::UP == state ) ||
1284 ( PointState::INTERRUPTED == state ) )
1286 primarySelectionHandle.pressed = false;
1287 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1288 mIsHandlePanning = false;
1291 SetHandleImage( LEFT_SELECTION_HANDLE );
1294 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1298 bool OnHandleTwoTouched( Actor actor, const TouchData& touch )
1300 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1302 // Switch between pressed/release selection handle images
1303 if( touch.GetPointCount() > 0 &&
1304 secondarySelectionHandle.actor )
1306 const PointState::Type state = touch.GetState( 0 );
1308 if( PointState::DOWN == state )
1310 secondarySelectionHandle.pressed = true;
1312 else if( ( PointState::UP == state ) ||
1313 ( PointState::INTERRUPTED == state ) )
1315 secondarySelectionHandle.pressed = false;
1316 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1317 mIsHandlePanning = false;
1320 SetHandleImage( RIGHT_SELECTION_HANDLE );
1323 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1327 void HandleResetPosition( PropertyNotification& source )
1329 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1331 if( grabHandle.active )
1333 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1334 SetGrabHandlePosition();
1336 // Sets the grab handle image according if it's pressed, flipped, etc.
1337 SetHandleImage( GRAB_HANDLE );
1341 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1342 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1344 // Sets the primary handle image according if it's pressed, flipped, etc.
1345 SetHandleImage( LEFT_SELECTION_HANDLE );
1347 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1348 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1350 // Sets the secondary handle image according if it's pressed, flipped, etc.
1351 SetHandleImage( RIGHT_SELECTION_HANDLE );
1355 void SetupActiveLayerPropertyNotifications()
1362 // Vertical notifications.
1364 // Disconnect any previous connected callback.
1365 if( mVerticalLessThanNotification )
1367 mVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1368 mActiveLayer.RemovePropertyNotification( mVerticalLessThanNotification );
1371 if( mVerticalGreaterThanNotification )
1373 mVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1374 mActiveLayer.RemovePropertyNotification( mVerticalGreaterThanNotification );
1377 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1378 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1379 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1381 if( grabHandle.active )
1383 if( grabHandle.verticallyFlipped )
1385 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1386 mVerticalGreaterThanNotification.Reset();
1388 // The vertical distance from the center of the active layer to the top edje of the display.
1389 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1391 mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1392 LessThanCondition( mBoundingBox.y + topHeight ) );
1394 // Notifies the change from false to true and from true to false.
1395 mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1397 // Connects the signals with the callbacks.
1398 mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1402 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1403 mVerticalLessThanNotification.Reset();
1405 // The vertical distance from the center of the active layer to the bottom edje of the display.
1406 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1408 mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1409 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1411 // Notifies the change from false to true and from true to false.
1412 mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1414 // Connects the signals with the callbacks.
1415 mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1418 else // The selection handles are active
1420 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1422 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1423 mVerticalGreaterThanNotification.Reset();
1425 // The vertical distance from the center of the active layer to the top edje of the display.
1426 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1428 mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1429 LessThanCondition( mBoundingBox.y + topHeight ) );
1431 // Notifies the change from false to true and from true to false.
1432 mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1434 // Connects the signals with the callbacks.
1435 mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1437 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1439 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1440 mVerticalLessThanNotification.Reset();
1442 // The vertical distance from the center of the active layer to the bottom edje of the display.
1443 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1444 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1446 mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1447 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1449 // Notifies the change from false to true and from true to false.
1450 mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1452 // Connects the signals with the callbacks.
1453 mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1457 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1459 // The vertical distance from the center of the active layer to the top edje of the display.
1460 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1461 -primaryHandle.position.y + primaryHandle.size.height :
1462 -secondaryHandle.position.y + secondaryHandle.size.height );
1464 mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1465 LessThanCondition( mBoundingBox.y + topHeight ) );
1467 // Notifies the change from false to true and from true to false.
1468 mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1470 // Connects the signals with the callbacks.
1471 mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1473 // The vertical distance from the center of the active layer to the bottom edje of the display.
1474 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1475 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1476 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1478 mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1479 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1481 // Notifies the change from false to true and from true to false.
1482 mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1484 // Connects the signals with the callbacks.
1485 mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1489 // Horizontal notifications.
1491 // Disconnect any previous connected callback.
1492 if( mHorizontalLessThanNotification )
1494 mHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1495 mActiveLayer.RemovePropertyNotification( mHorizontalLessThanNotification );
1498 if( mHorizontalGreaterThanNotification )
1500 mHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1501 mActiveLayer.RemovePropertyNotification( mHorizontalGreaterThanNotification );
1504 if( primaryHandle.active || secondaryHandle.active )
1506 // The horizontal distance from the center of the active layer to the left edje of the display.
1507 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1508 -secondaryHandle.position.x + secondaryHandle.size.width );
1510 mHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1511 LessThanCondition( mBoundingBox.x + leftWidth ) );
1513 // Notifies the change from false to true and from true to false.
1514 mHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1516 // Connects the signals with the callbacks.
1517 mHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1519 // The horizontal distance from the center of the active layer to the right edje of the display.
1520 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1521 secondaryHandle.position.x + secondaryHandle.size.width );
1523 mHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1524 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1526 // Notifies the change from false to true and from true to false.
1527 mHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1529 // Connects the signals with the callbacks.
1530 mHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1536 float AlternatePopUpPositionRelativeToCursor()
1538 float alternativePosition = 0.0f;
1540 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1542 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1543 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1544 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1545 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1547 if( primaryHandle.active || secondaryHandle.active )
1549 const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1550 alternativePosition = 0.5f * popupHeight + cursor.lineHeight + maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
1554 alternativePosition = 0.5f * popupHeight + cursor.lineHeight + grabHandle.size.height + cursor.position.y;
1557 return alternativePosition;
1560 void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1562 float alternativeYPosition = 0.0f;
1563 // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1564 // if can't be positioned above, then position below row.
1565 alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1567 mCopyPastePopup.actor.SetY( alternativeYPosition );
1570 void SetUpPopupPositionNotifications()
1572 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1574 // Exceeding vertical boundary
1576 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1578 PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1579 OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
1580 mBoundingBox.w - popupHeight * 0.5f ) );
1582 verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1585 void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, const Vector3& popupDistanceFromAnchorPoint, Actor parent, const Vector4& boundingRectangleWorld )
1587 DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1589 // Parent must already by added to Stage for these Get calls to work
1590 const Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize() * parent.GetCurrentAnchorPoint();
1591 const Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position
1593 // Calculate distance to move popup (in local space) so fits within the boundary
1594 float xOffSetToKeepWithinBounds = 0.0f;
1595 if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1597 xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1599 else if( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1601 xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x );
1604 // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1605 if( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1607 requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1610 requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1612 // Prevent pixel mis-alignment by rounding down.
1613 requiredPopupPosition.x = floor( requiredPopupPosition.x );
1614 requiredPopupPosition.y = floor( requiredPopupPosition.y );
1617 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1619 HandleImpl& handle = mHandle[handleType];
1620 handle.size = Size( image.GetWidth(), image.GetHeight() );
1622 mHandleImages[handleType][handleImageType] = image;
1625 void SetScrollThreshold( float threshold )
1627 mScrollThreshold = threshold;
1630 float GetScrollThreshold() const
1632 return mScrollThreshold;
1635 void SetScrollSpeed( float speed )
1637 mScrollSpeed = speed;
1638 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1641 float GetScrollSpeed() const
1643 return mScrollSpeed;
1646 void NotifyEndOfScroll()
1652 mNotifyEndOfScroll = true;
1657 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1659 * It only starts the timer if it's already created.
1661 void StartScrollTimer()
1665 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1666 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1669 if( !mScrollTimer.IsRunning() )
1671 mScrollTimer.Start();
1676 * Stops the timer used to scroll the text.
1678 void StopScrollTimer()
1682 mScrollTimer.Stop();
1687 * Callback called by the timer used to scroll the text.
1689 * It calculates and sets a new scroll position.
1691 bool OnScrollTimerTick()
1693 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1698 switch( mScrollDirection )
1702 x = mScrollDistance;
1707 x = -mScrollDistance;
1712 y = mScrollDistance;
1717 y = -mScrollDistance;
1724 mController.DecorationEvent( mHandleScrolling,
1733 ControllerInterface& mController;
1735 TapGestureDetector mTapDetector;
1736 PanGestureDetector mPanDetector;
1737 LongPressGestureDetector mLongPressDetector;
1739 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1740 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1742 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1743 PropertyNotification mVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1744 PropertyNotification mVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1745 PropertyNotification mHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1746 PropertyNotification mHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1747 Control mPrimaryCursor;
1748 Control mSecondaryCursor;
1750 Actor mHighlightActor; ///< Actor to display highlight
1751 Renderer mHighlightRenderer;
1752 Shader mHighlightShader; ///< Shader used for highlight
1753 Property::Map mQuadVertexFormat;
1754 PopupImpl mCopyPastePopup;
1755 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1756 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1758 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1759 Vector4 mHandleColor;
1761 CursorImpl mCursor[CURSOR_COUNT];
1762 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1764 PropertyBuffer mQuadVertices;
1765 Geometry mQuadGeometry;
1766 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight
1768 Vector4 mBoundingBox; ///< The bounding box in world coords.
1769 Vector4 mHighlightColor; ///< Color of the highlight
1770 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1771 Vector2 mControlSize; ///< The control's size. Set by the Relayout.
1773 unsigned int mActiveCursor;
1774 unsigned int mCursorBlinkInterval;
1775 float mCursorBlinkDuration;
1776 float mCursorWidth; ///< The width of the cursors in pixels.
1777 HandleType mHandleScrolling; ///< The handle which is scrolling.
1778 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1779 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1780 float mScrollSpeed; ///< The scroll speed in pixels per second.
1781 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1782 int mTextDepth; ///< The depth used to render the text.
1784 bool mActiveCopyPastePopup : 1;
1785 bool mPopupSetNewPosition : 1;
1786 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1787 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1788 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1789 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1790 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1791 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1792 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1793 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1794 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1795 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1796 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1797 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1798 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1799 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1802 DecoratorPtr Decorator::New( ControllerInterface& controller,
1803 TextSelectionPopupCallbackInterface& callbackInterface )
1805 return DecoratorPtr( new Decorator( controller,
1806 callbackInterface ) );
1809 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1811 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1814 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1816 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1819 void Decorator::Relayout( const Vector2& size )
1821 mImpl->Relayout( size );
1824 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1826 mImpl->UpdatePositions( scrollOffset );
1831 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1833 mImpl->mActiveCursor = activeCursor;
1836 unsigned int Decorator::GetActiveCursor() const
1838 return mImpl->mActiveCursor;
1841 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1843 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1845 cursorImpl.position.x = x;
1846 cursorImpl.position.y = y;
1847 cursorImpl.cursorHeight = cursorHeight;
1848 cursorImpl.lineHeight = lineHeight;
1851 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1853 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1855 x = cursorImpl.position.x;
1856 y = cursorImpl.position.y;
1857 cursorHeight = cursorImpl.cursorHeight;
1858 lineHeight = cursorImpl.lineHeight;
1861 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1863 return mImpl->mCursor[cursor].position;
1866 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1868 mImpl->mCursor[cursor].color = color;
1871 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1873 return mImpl->mCursor[cursor].color;
1876 void Decorator::StartCursorBlink()
1878 if ( !mImpl->mCursorBlinkTimer )
1880 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1881 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1884 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1886 mImpl->mCursorBlinkTimer.Start();
1890 void Decorator::StopCursorBlink()
1892 if ( mImpl->mCursorBlinkTimer )
1894 mImpl->mCursorBlinkTimer.Stop();
1897 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
1900 void Decorator::DelayCursorBlink()
1902 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
1903 mImpl->mDelayCursorBlink = true;
1906 void Decorator::SetCursorBlinkInterval( float seconds )
1908 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1911 float Decorator::GetCursorBlinkInterval() const
1913 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1916 void Decorator::SetCursorBlinkDuration( float seconds )
1918 mImpl->mCursorBlinkDuration = seconds;
1921 float Decorator::GetCursorBlinkDuration() const
1923 return mImpl->mCursorBlinkDuration;
1926 void Decorator::SetCursorWidth( int width )
1928 mImpl->mCursorWidth = static_cast<float>( width );
1931 int Decorator::GetCursorWidth() const
1933 return static_cast<int>( mImpl->mCursorWidth );
1938 void Decorator::SetHandleActive( HandleType handleType, bool active )
1940 mImpl->mHandle[handleType].active = active;
1944 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
1946 mImpl->mIsHandlePreviouslyCrossed = false;
1949 // TODO: this is a work-around.
1950 // The problem is the handle actor does not receive the touch event with the Interrupt
1951 // state when the power button is pressed and the application goes to background.
1952 mImpl->mHandle[handleType].pressed = false;
1953 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1954 ImageView imageView = mImpl->mHandle[handleType].actor;
1955 if( imageReleased && imageView )
1957 imageView.SetImage( imageReleased );
1963 bool Decorator::IsHandleActive( HandleType handleType ) const
1965 return mImpl->mHandle[handleType].active ;
1968 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1970 mImpl->SetHandleImage( handleType, handleImageType, image );
1973 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1975 return mImpl->mHandleImages[handleType][handleImageType];
1978 void Decorator::SetHandleColor( const Vector4& color )
1980 mImpl->mHandleColor = color;
1983 const Vector4& Decorator::GetHandleColor() const
1985 return mImpl->mHandleColor;
1988 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1990 // Adjust handle's displacement
1991 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1993 handle.position.x = x;
1994 handle.position.y = y;
1995 handle.lineHeight = height;
1997 if( mImpl->mSmoothHandlePanEnabled )
1999 handle.grabDisplacementX = 0.f;
2000 handle.grabDisplacementY = 0.f;
2004 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2006 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2008 x = handle.position.x;
2009 y = handle.position.y;
2010 height = handle.lineHeight;
2013 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2015 return mImpl->mHandle[handleType].position;
2018 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2020 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2023 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2025 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2028 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2030 mImpl->mFlipSelectionHandlesOnCross = enable;
2033 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2035 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2036 mImpl->mFlipLeftSelectionHandleDirection = left;
2037 mImpl->mFlipRightSelectionHandleDirection = right;
2040 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
2042 mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
2045 void Decorator::ClearHighlights()
2047 mImpl->mHighlightQuadList.clear();
2048 mImpl->mHighlightPosition = Vector2::ZERO;
2051 void Decorator::SetHighlightColor( const Vector4& color )
2053 mImpl->mHighlightColor = color;
2056 const Vector4& Decorator::GetHighlightColor() const
2058 return mImpl->mHighlightColor;
2061 void Decorator::SetTextDepth( int textDepth )
2063 mImpl->mTextDepth = textDepth;
2066 void Decorator::SetPopupActive( bool active )
2068 mImpl->mActiveCopyPastePopup = active;
2071 bool Decorator::IsPopupActive() const
2073 return mImpl->mActiveCopyPastePopup ;
2076 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2078 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2080 if ( !mImpl->mCopyPastePopup.actor )
2082 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2083 #ifdef DECORATOR_DEBUG
2084 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
2086 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
2087 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::PopupRelayoutComplete ); // Position popup after size negotiation
2090 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2093 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2095 return mImpl->mEnabledPopupButtons;
2100 void Decorator::SetScrollThreshold( float threshold )
2102 mImpl->SetScrollThreshold( threshold );
2105 float Decorator::GetScrollThreshold() const
2107 return mImpl->GetScrollThreshold();
2110 void Decorator::SetScrollSpeed( float speed )
2112 mImpl->SetScrollSpeed( speed );
2115 float Decorator::GetScrollSpeed() const
2117 return mImpl->GetScrollSpeed();
2120 void Decorator::NotifyEndOfScroll()
2122 mImpl->NotifyEndOfScroll();
2125 void Decorator::SetHorizontalScrollEnabled( bool enable )
2127 mImpl->mHorizontalScrollingEnabled = enable;
2130 bool Decorator::IsHorizontalScrollEnabled() const
2132 return mImpl->mHorizontalScrollingEnabled;
2135 void Decorator::SetVerticalScrollEnabled( bool enable )
2137 mImpl->mVerticalScrollingEnabled = enable;
2140 bool Decorator::IsVerticalScrollEnabled() const
2142 return mImpl->mVerticalScrollingEnabled;
2145 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2147 mImpl->mSmoothHandlePanEnabled = enable;
2150 bool Decorator::IsSmoothHandlePanEnabled() const
2152 return mImpl->mSmoothHandlePanEnabled;
2155 Decorator::~Decorator()
2160 Decorator::Decorator( ControllerInterface& controller,
2161 TextSelectionPopupCallbackInterface& callbackInterface )
2164 mImpl = new Decorator::Impl( controller, callbackInterface );
2169 } // namespace Toolkit