2 * Copyright (c) 2015 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>
24 #include <dali/public-api/adaptor-framework/timer.h>
25 #include <dali/public-api/actors/layer.h>
26 #include <dali/public-api/common/stage.h>
27 #include <dali/public-api/events/touch-data.h>
28 #include <dali/public-api/events/pan-gesture.h>
29 #include <dali/public-api/images/resource-image.h>
30 #include <dali/public-api/object/property-notification.h>
32 #include <dali/devel-api/rendering/geometry.h>
33 #include <dali/devel-api/rendering/renderer.h>
36 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
37 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
38 #include <dali-toolkit/internal/controls/image-view/image-view-impl.h>
41 #define DECORATOR_DEBUG
45 #define MAKE_SHADER(A)#A
49 const char* VERTEX_SHADER = MAKE_SHADER(
50 attribute mediump vec2 aPosition;
51 uniform mediump mat4 uMvpMatrix;
52 uniform mediump vec3 uSize;
56 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
57 position.xyz *= uSize;
58 gl_Position = uMvpMatrix * position;
62 const char* FRAGMENT_SHADER = MAKE_SHADER(
63 uniform lowp vec4 uColor;
67 gl_FragColor = uColor;
78 #ifdef DECORATOR_DEBUG
79 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
89 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
90 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
92 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.
94 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
96 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
97 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
98 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
100 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
101 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.
102 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
104 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
106 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
109 * structure to hold coordinates of each quad, which will make up the mesh.
111 struct QuadCoordinates
114 * Default constructor
122 * @param[in] x1 left co-ordinate
123 * @param[in] y1 top co-ordinate
124 * @param[in] x2 right co-ordinate
125 * @param[in] y2 bottom co-ordinate
127 QuadCoordinates(float x1, float y1, float x2, float y2)
133 Dali::Vector2 min; ///< top-left (minimum) position of quad
134 Dali::Vector2 max; ///< bottom-right (maximum) position of quad
137 typedef std::vector<QuadCoordinates> QuadContainer;
140 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
141 * @param[in] boundingRectangle local bounding
142 * @param[out] Vector4 World coordinate bounding Box.
144 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
146 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
147 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
149 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
150 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
152 boundingBox = Dali::Vector4( originX,
154 originX + boundingRectangle.width,
155 originY + boundingRectangle.height );
158 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
160 // Convert to local coordinates and store as a Dali::Rect.
161 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
163 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
164 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
165 boundingRectangle.width = boundingBox.z - boundingBox.x;
166 boundingRectangle.height = boundingBox.w - boundingBox.y;
169 } // end of namespace
180 struct Decorator::Impl : public ConnectionTracker
194 : color( Dali::Color::BLACK ),
196 cursorHeight( 0.0f ),
214 grabDisplacementX( 0.f ),
215 grabDisplacementY( 0.f ),
219 verticallyFlippedPreferred( false ),
220 horizontallyFlipped( false ),
221 verticallyFlipped( false )
227 ImageView markerActor;
230 Vector2 globalPosition;
232 float lineHeight; ///< Not the handle height
233 float grabDisplacementX;
234 float grabDisplacementY;
238 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
239 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
240 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
250 TextSelectionPopup actor;
254 Impl( ControllerInterface& controller,
255 TextSelectionPopupCallbackInterface& callbackInterface )
256 : mController( controller ),
257 mEnabledPopupButtons( TextSelectionPopup::NONE ),
258 mTextSelectionPopupCallbackInterface( callbackInterface ),
259 mHandleColor( HANDLE_COLOR ),
261 mHighlightColor( LIGHT_BLUE ),
262 mHighlightPosition( Vector2::ZERO ),
263 mActiveCursor( ACTIVE_CURSOR_NONE ),
264 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
265 mCursorBlinkDuration( 0.0f ),
266 mCursorWidth( CURSOR_WIDTH ),
267 mHandleScrolling( HANDLE_TYPE_COUNT ),
268 mScrollDirection( SCROLL_NONE ),
269 mScrollThreshold( SCROLL_THRESHOLD ),
270 mScrollSpeed( SCROLL_SPEED ),
271 mScrollDistance( SCROLL_DISTANCE ),
273 mActiveCopyPastePopup( false ),
274 mPopupSetNewPosition( true ),
275 mCursorBlinkStatus( true ),
276 mDelayCursorBlink( false ),
277 mPrimaryCursorVisible( false ),
278 mSecondaryCursorVisible( false ),
279 mFlipSelectionHandlesOnCross( false ),
280 mFlipLeftSelectionHandleDirection( false ),
281 mFlipRightSelectionHandleDirection( false ),
282 mIsHandlePanning( false ),
283 mIsHandleCurrentlyCrossed( false ),
284 mIsHandlePreviouslyCrossed( false ),
285 mNotifyEndOfScroll( false ),
286 mHorizontalScrollingEnabled( false ),
287 mVerticalScrollingEnabled( false )
289 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
290 mHighlightShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
295 * Relayout of the decorations owned by the decorator.
296 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
298 void Relayout( const Vector2& size )
302 // TODO - Remove this if nothing is active
305 // Show or hide the cursors
310 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
311 mPrimaryCursorVisible = ( ( cursor.position.x + mCursorWidth <= mControlSize.width ) &&
312 ( cursor.position.x >= 0.f ) &&
313 ( cursor.position.y + cursor.cursorHeight <= mControlSize.height ) &&
314 ( cursor.position.y >= 0.f ) );
315 if( mPrimaryCursorVisible )
317 mPrimaryCursor.SetPosition( cursor.position.x,
319 mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
321 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
323 if( mSecondaryCursor )
325 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
326 mSecondaryCursorVisible = ( ( cursor.position.x + mCursorWidth <= mControlSize.width ) &&
327 ( cursor.position.x >= 0.f ) &&
328 ( cursor.position.y + cursor.cursorHeight <= mControlSize.height ) &&
329 ( cursor.position.y >= 0.f ) );
330 if( mSecondaryCursorVisible )
332 mSecondaryCursor.SetPosition( cursor.position.x,
334 mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
336 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
339 // Show or hide the grab handle
340 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
341 bool newGrabHandlePosition = false;
342 if( grabHandle.active )
344 const bool isVisible = ( ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) <= mControlSize.width ) &&
345 ( grabHandle.position.x >= 0.f ) &&
346 ( grabHandle.position.y <= mControlSize.height - grabHandle.lineHeight ) &&
347 ( grabHandle.position.y >= 0.f ) );
353 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
354 SetGrabHandlePosition();
356 // Sets the grab handle image according if it's pressed, flipped, etc.
357 SetHandleImage( GRAB_HANDLE );
359 newGrabHandlePosition = true;
362 if( grabHandle.actor )
364 grabHandle.actor.SetVisible( isVisible );
367 else if( grabHandle.actor )
369 grabHandle.actor.Unparent();
372 // Show or hide the selection handles/highlight
373 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
374 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
375 bool newPrimaryHandlePosition = false;
376 bool newSecondaryHandlePosition = false;
377 if( primary.active || secondary.active )
379 const bool isPrimaryVisible = ( ( primary.position.x <= mControlSize.width ) &&
380 ( primary.position.x >= 0.f ) &&
381 ( primary.position.y <= mControlSize.height - primary.lineHeight ) &&
382 ( primary.position.y >= 0.f ) );
383 const bool isSecondaryVisible = ( ( secondary.position.x <= mControlSize.width ) &&
384 ( secondary.position.x >= 0.f ) &&
385 ( secondary.position.y <= mControlSize.height - secondary.lineHeight ) &&
386 ( secondary.position.y >= 0.f ) );
388 if( isPrimaryVisible || isSecondaryVisible )
390 CreateSelectionHandles();
392 if( isPrimaryVisible )
394 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
396 // Sets the primary handle image according if it's pressed, flipped, etc.
397 SetHandleImage( LEFT_SELECTION_HANDLE );
399 SetSelectionHandleMarkerSize( primary );
401 newPrimaryHandlePosition = true;
404 if( isSecondaryVisible )
406 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
408 // Sets the secondary handle image according if it's pressed, flipped, etc.
409 SetHandleImage( RIGHT_SELECTION_HANDLE );
411 SetSelectionHandleMarkerSize( secondary );
413 newSecondaryHandlePosition = true;
419 primary.actor.SetVisible( isPrimaryVisible );
421 if( secondary.actor )
423 secondary.actor.SetVisible( isSecondaryVisible );
433 primary.actor.Unparent();
435 if( secondary.actor )
437 secondary.actor.Unparent();
439 if( mHighlightActor )
441 mHighlightActor.Unparent();
445 if( newGrabHandlePosition ||
446 newPrimaryHandlePosition ||
447 newSecondaryHandlePosition )
449 // Setup property notifications to find whether the handles leave the boundaries of the current display.
450 SetupActiveLayerPropertyNotifications();
453 if( mActiveCopyPastePopup )
456 mPopupSetNewPosition = true;
460 if( mCopyPastePopup.actor )
462 mCopyPastePopup.actor.HidePopup();
463 mPopupSetNewPosition = true;
468 void UpdatePositions( const Vector2& scrollOffset )
470 mCursor[PRIMARY_CURSOR].position += scrollOffset;
471 mCursor[SECONDARY_CURSOR].position += scrollOffset;
472 mHandle[ GRAB_HANDLE ].position += scrollOffset;
473 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
474 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
475 mHighlightPosition += scrollOffset;
480 if ( !mCopyPastePopup.actor )
485 if( !mCopyPastePopup.actor.GetParent() )
487 mActiveLayer.Add( mCopyPastePopup.actor );
490 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
491 mCopyPastePopup.actor.ShowPopup();
494 void DeterminePositionPopup()
496 if( !mActiveCopyPastePopup )
501 // Retrieves the popup's size after relayout.
502 const Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
504 if( mPopupSetNewPosition )
506 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
507 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
508 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
510 if( primaryHandle.active || secondaryHandle.active )
512 // Calculates the popup's position if selection handles are active.
513 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
514 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
515 const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
517 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
518 mCopyPastePopup.position.y = -0.5f * popupSize.height - maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
522 // Calculates the popup's position if the grab handle is active.
523 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
524 if( grabHandle.verticallyFlipped )
526 mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height - grabHandle.size.height + cursor.position.y, 0.0f );
530 mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height + cursor.position.y, 0.0f );
535 // Checks if there is enough space above the text control. If not it places the popup under it.
536 GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize * AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
538 SetUpPopupPositionNotifications();
540 mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
541 mPopupSetNewPosition = false;
544 void PopupRelayoutComplete( Actor actor )
546 // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
548 DeterminePositionPopup();
551 void CreateCursor( Control& cursor, const Vector4& color )
553 cursor = Control::New();
554 cursor.SetBackgroundColor( color );
555 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT );
556 cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
559 // Add or Remove cursor(s) from parent
562 if( mActiveCursor == ACTIVE_CURSOR_NONE )
566 mPrimaryCursor.Unparent();
568 if( mSecondaryCursor )
570 mSecondaryCursor.Unparent();
575 // Create Primary and or Secondary Cursor(s) if active and add to parent
576 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
577 mActiveCursor == ACTIVE_CURSOR_BOTH )
579 if ( !mPrimaryCursor )
581 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
582 #ifdef DECORATOR_DEBUG
583 mPrimaryCursor.SetName( "PrimaryCursorActor" );
587 if( !mPrimaryCursor.GetParent() )
589 mActiveLayer.Add( mPrimaryCursor );
593 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
595 if ( !mSecondaryCursor )
597 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
598 #ifdef DECORATOR_DEBUG
599 mSecondaryCursor.SetName( "SecondaryCursorActor" );
603 if( !mSecondaryCursor.GetParent() )
605 mActiveLayer.Add( mSecondaryCursor );
610 if( mSecondaryCursor )
612 mSecondaryCursor.Unparent();
618 bool OnCursorBlinkTimerTick()
620 if( !mDelayCursorBlink )
623 if ( mPrimaryCursor )
625 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
627 if ( mSecondaryCursor )
629 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
632 mCursorBlinkStatus = !mCursorBlinkStatus;
637 mDelayCursorBlink = false;
645 // Will consume tap gestures on handles.
646 mTapDetector = TapGestureDetector::New();
648 // Will consume double tap gestures on handles.
649 mTapDetector.SetMaximumTapsRequired( 2u );
651 // Will consume long press gestures on handles.
652 mLongPressDetector = LongPressGestureDetector::New();
654 // Detects pan gestures on handles.
655 mPanDetector = PanGestureDetector::New();
656 mPanDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
659 void CreateActiveLayer()
663 mActiveLayer = Layer::New();
664 #ifdef DECORATOR_DEBUG
665 mActiveLayer.SetName ( "ActiveLayerActor" );
668 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
669 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
671 // Add the active layer telling the controller it doesn't need clipping.
672 mController.AddDecoration( mActiveLayer, false );
675 mActiveLayer.RaiseToTop();
678 void SetSelectionHandleMarkerSize( HandleImpl& handle )
680 if( handle.markerActor )
682 handle.markerActor.SetSize( 0, handle.lineHeight );
686 void CreateGrabHandle()
688 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
689 if( !grabHandle.actor )
691 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
693 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
694 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
695 grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
697 // Area that Grab handle responds to, larger than actual handle so easier to move
698 #ifdef DECORATOR_DEBUG
699 grabHandle.actor.SetName( "GrabHandleActor" );
700 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
702 grabHandle.grabArea = Control::New();
703 Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
704 control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
705 grabHandle.grabArea.SetName( "GrabArea" );
709 grabHandle.grabArea = Actor::New();
710 grabHandle.grabArea.SetName( "GrabArea" );
713 grabHandle.grabArea = Actor::New();
716 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
717 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
718 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
719 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
720 grabHandle.actor.Add( grabHandle.grabArea );
721 grabHandle.actor.SetColor( mHandleColor );
723 grabHandle.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
725 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
726 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
727 mTapDetector.Attach( grabHandle.actor );
728 mLongPressDetector.Attach( grabHandle.actor );
730 // The grab handle's area is attached to the pan detector.
731 // The OnPan() method is connected to the signals emitted by the pan detector.
732 mPanDetector.Attach( grabHandle.grabArea );
734 mActiveLayer.Add( grabHandle.actor );
738 if( grabHandle.actor && !grabHandle.actor.GetParent() )
740 mActiveLayer.Add( grabHandle.actor );
744 void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
748 handle.markerActor = ImageView::New( image );
749 handle.markerActor.SetColor( mHandleColor );
750 handle.actor.Add( handle.markerActor );
752 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
754 if( LEFT_SELECTION_HANDLE == handleType )
756 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
757 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
759 else if( RIGHT_SELECTION_HANDLE == handleType )
761 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
762 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
767 void CreateSelectionHandles()
769 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
772 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
774 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
775 #ifdef DECORATOR_DEBUG
776 primary.actor.SetName("SelectionHandleOne");
778 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
779 GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
780 primary.actor.SetColor( mHandleColor );
782 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
783 #ifdef DECORATOR_DEBUG
784 primary.grabArea.SetName("SelectionHandleOneGrabArea");
786 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
787 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
788 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
789 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
791 primary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
793 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
794 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
795 mTapDetector.Attach( primary.actor );
796 mLongPressDetector.Attach( primary.actor );
798 // The handle's area is attached to the pan detector.
799 // The OnPan() method is connected to the signals emitted by the pan detector.
800 mPanDetector.Attach( primary.grabArea );
802 primary.actor.Add( primary.grabArea );
804 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
808 if( primary.actor && !primary.actor.GetParent() )
810 mActiveLayer.Add( primary.actor );
813 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
814 if( !secondary.actor )
816 if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
818 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
819 #ifdef DECORATOR_DEBUG
820 secondary.actor.SetName("SelectionHandleTwo");
822 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
823 GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
824 secondary.actor.SetColor( mHandleColor );
826 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
827 #ifdef DECORATOR_DEBUG
828 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
830 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
831 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
832 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
833 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
835 secondary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
837 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
838 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
839 mTapDetector.Attach( secondary.actor );
840 mLongPressDetector.Attach( secondary.actor );
842 // The handle's area is attached to the pan detector.
843 // The OnPan() method is connected to the signals emitted by the pan detector.
844 mPanDetector.Attach( secondary.grabArea );
846 secondary.actor.Add( secondary.grabArea );
848 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
852 if( secondary.actor && !secondary.actor.GetParent() )
854 mActiveLayer.Add( secondary.actor );
858 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
860 // Gets the world position of the active layer. The active layer is where the handles are added.
861 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
863 // The grab handle position in world coords.
864 // The active layer's world position is the center of the active layer. The origin of the
865 // coord system of the handles is the top left of the active layer.
866 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f );
867 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f );
870 void SetGrabHandlePosition()
872 // Reference to the grab handle.
873 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
875 // Transforms the handle position into world coordinates.
876 // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
877 // as it's transforming the handle's position set by the text-controller and not
878 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
879 // retrieves the position of the center of the actor but the handle's position set
880 // by the text controller is not the center of the actor.
881 Vector2 grabHandleWorldPosition;
882 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
884 // Check if the grab handle exceeds the boundaries of the decoration box.
885 // At the moment only the height is checked for the grab handle.
887 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
888 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
889 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
891 // The grab handle 'y' position in local coords.
892 // If the grab handle exceeds the bottom of the decoration box,
893 // set the 'y' position to the top of the line.
894 // The SetGrabHandleImage() method will change the orientation.
895 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
897 if( grabHandle.actor )
899 grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ) + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f ),
900 yLocalPosition + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f ) );
904 void SetSelectionHandlePosition( HandleType type )
906 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
908 // Reference to the selection handle.
909 HandleImpl& handle = mHandle[type];
911 // Transforms the handle position into world coordinates.
912 // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
913 // as it's transforming the handle's position set by the text-controller and not
914 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
915 // retrieves the position of the center of the actor but the handle's position set
916 // by the text controller is not the center of the actor.
917 Vector2 handleWorldPosition;
918 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
920 // Whether to flip the handle (horizontally).
921 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
923 // Whether to flip the handles if they are crossed.
924 bool crossFlip = false;
925 if( mFlipSelectionHandlesOnCross || !mIsHandlePanning )
927 crossFlip = mIsHandleCurrentlyCrossed;
930 // Whether the handle was crossed before start the panning.
931 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
933 // Does not flip if both conditions are true (double flip)
934 flipHandle = flipHandle != ( crossFlip || isHandlePreviouslyCrossed );
936 // Will flip the handles vertically if the user prefers it.
937 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
939 if( crossFlip || isHandlePreviouslyCrossed )
941 if( isPrimaryHandle )
943 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
947 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
951 // Check if the selection handle exceeds the boundaries of the decoration box.
952 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
953 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
955 // Does not flip if both conditions are true (double flip)
956 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
960 if( handle.actor && !handle.horizontallyFlipped )
962 // Change the anchor point to flip the image.
963 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
965 handle.horizontallyFlipped = true;
970 if( handle.actor && handle.horizontallyFlipped )
972 // Reset the anchor point.
973 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
975 handle.horizontallyFlipped = false;
979 // Whether to flip the handle vertically.
980 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
981 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
982 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
984 // The primary selection handle 'y' position in local coords.
985 // If the handle exceeds the bottom of the decoration box,
986 // set the 'y' position to the top of the line.
987 // The SetHandleImage() method will change the orientation.
988 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
992 handle.actor.SetPosition( handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f ),
993 yLocalPosition + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f ) );
997 void SetHandleImage( HandleType type )
999 HandleImpl& handle = mHandle[type];
1001 HandleType markerType = HANDLE_TYPE_COUNT;
1002 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1003 if( LEFT_SELECTION_HANDLE == type )
1005 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1006 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1008 else if( RIGHT_SELECTION_HANDLE == type )
1010 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1011 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1014 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1017 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1019 handle.actor.SetImage( mHandleImages[type][imageType] );
1022 if( HANDLE_TYPE_COUNT != markerType )
1024 if( handle.markerActor )
1026 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1027 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
1031 // Whether to flip the handle vertically.
1034 handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
1038 void CreateHighlight()
1040 if( !mHighlightActor )
1042 mHighlightActor = Actor::New();
1044 #ifdef DECORATOR_DEBUG
1045 mHighlightActor.SetName( "HighlightActor" );
1047 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
1048 mHighlightActor.SetSize( 1.0f, 1.0f );
1049 mHighlightActor.SetColor( mHighlightColor );
1050 mHighlightActor.SetColorMode( USE_OWN_COLOR );
1053 // Add the highlight box telling the controller it needs clipping.
1054 mController.AddDecoration( mHighlightActor, true );
1057 void UpdateHighlight()
1059 if ( mHighlightActor )
1061 if( !mHighlightQuadList.empty() )
1063 Vector< Vector2 > vertices;
1064 Vector< unsigned short> indices;
1067 std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
1068 std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
1070 for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
1072 QuadCoordinates& quad = *iter;
1075 vertex.x = quad.min.x;
1076 vertex.y = quad.min.y;
1077 vertices.PushBack( vertex );
1080 vertex.x = quad.max.x;
1081 vertex.y = quad.min.y;
1082 vertices.PushBack( vertex );
1084 // bottom-left (v+2)
1085 vertex.x = quad.min.x;
1086 vertex.y = quad.max.y;
1087 vertices.PushBack( vertex );
1089 // bottom-right (v+3)
1090 vertex.x = quad.max.x;
1091 vertex.y = quad.max.y;
1092 vertices.PushBack( vertex );
1094 // triangle A (3, 1, 0)
1095 indices.PushBack( v + 3 );
1096 indices.PushBack( v + 1 );
1097 indices.PushBack( v );
1099 // triangle B (0, 2, 3)
1100 indices.PushBack( v );
1101 indices.PushBack( v + 2 );
1102 indices.PushBack( v + 3 );
1105 if( ! mQuadVertices )
1107 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
1110 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1112 if( !mQuadGeometry )
1114 mQuadGeometry = Geometry::New();
1115 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1117 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1119 if( !mHighlightRenderer )
1121 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1122 mHighlightActor.AddRenderer( mHighlightRenderer );
1126 mHighlightActor.SetPosition( mHighlightPosition.x,
1127 mHighlightPosition.y );
1129 mHighlightQuadList.clear();
1131 if( mHighlightRenderer )
1133 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1138 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1140 if( Gesture::Started == gesture.state )
1142 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1144 handle.globalPosition.x = handle.position.x;
1145 handle.globalPosition.y = handle.position.y;
1148 handle.grabDisplacementX += gesture.displacement.x;
1149 handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.displacement.y );
1151 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1152 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1153 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlipped ? handle.lineHeight : 0.f );
1155 if( ( Gesture::Started == gesture.state ) ||
1156 ( Gesture::Continuing == gesture.state ) )
1159 mController.GetTargetSize( targetSize );
1161 if( mHorizontalScrollingEnabled &&
1162 ( x < mScrollThreshold ) )
1164 mScrollDirection = SCROLL_RIGHT;
1165 mHandleScrolling = type;
1168 else if( mHorizontalScrollingEnabled &&
1169 ( x > targetSize.width - mScrollThreshold ) )
1171 mScrollDirection = SCROLL_LEFT;
1172 mHandleScrolling = type;
1175 else if( mVerticalScrollingEnabled &&
1176 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1178 mScrollDirection = SCROLL_TOP;
1179 mHandleScrolling = type;
1182 else if( mVerticalScrollingEnabled &&
1183 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1185 mScrollDirection = SCROLL_BOTTOM;
1186 mHandleScrolling = type;
1191 mHandleScrolling = HANDLE_TYPE_COUNT;
1193 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1196 mIsHandlePanning = true;
1198 else if( ( Gesture::Finished == gesture.state ) ||
1199 ( Gesture::Cancelled == gesture.state ) )
1202 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1204 mNotifyEndOfScroll = false;
1205 mHandleScrolling = HANDLE_TYPE_COUNT;
1207 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1211 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1216 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1218 handle.pressed = false;
1220 mIsHandlePanning = false;
1224 void OnPan( Actor actor, const PanGesture& gesture )
1226 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1227 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1228 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1230 if( actor == grabHandle.grabArea )
1232 DoPan( grabHandle, GRAB_HANDLE, gesture );
1234 else if( actor == primarySelectionHandle.grabArea )
1236 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1238 else if( actor == secondarySelectionHandle.grabArea )
1240 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1244 bool OnGrabHandleTouched( Actor actor, const TouchData& touch )
1246 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1248 // Switch between pressed/release grab-handle images
1249 if( touch.GetPointCount() > 0 &&
1252 const PointState::Type state = touch.GetState( 0 );
1254 if( PointState::DOWN == state )
1256 grabHandle.pressed = true;
1258 else if( ( PointState::UP == state ) ||
1259 ( PointState::INTERRUPTED == state ) )
1261 grabHandle.pressed = false;
1264 SetHandleImage( GRAB_HANDLE );
1267 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1271 bool OnHandleOneTouched( Actor actor, const TouchData& touch )
1273 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1275 // Switch between pressed/release selection handle images
1276 if( touch.GetPointCount() > 0 &&
1277 primarySelectionHandle.actor )
1279 const PointState::Type state = touch.GetState( 0 );
1281 if( PointState::DOWN == state )
1283 primarySelectionHandle.pressed = true;
1285 else if( ( PointState::UP == state ) ||
1286 ( PointState::INTERRUPTED == state ) )
1288 primarySelectionHandle.pressed = false;
1289 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1290 mIsHandlePanning = false;
1293 SetHandleImage( LEFT_SELECTION_HANDLE );
1296 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1300 bool OnHandleTwoTouched( Actor actor, const TouchData& touch )
1302 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1304 // Switch between pressed/release selection handle images
1305 if( touch.GetPointCount() > 0 &&
1306 secondarySelectionHandle.actor )
1308 const PointState::Type state = touch.GetState( 0 );
1310 if( PointState::DOWN == state )
1312 secondarySelectionHandle.pressed = true;
1314 else if( ( PointState::UP == state ) ||
1315 ( PointState::INTERRUPTED == state ) )
1317 secondarySelectionHandle.pressed = false;
1318 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1319 mIsHandlePanning = false;
1322 SetHandleImage( RIGHT_SELECTION_HANDLE );
1325 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1329 void HandleResetPosition( PropertyNotification& source )
1331 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1333 if( grabHandle.active )
1335 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1336 SetGrabHandlePosition();
1338 // Sets the grab handle image according if it's pressed, flipped, etc.
1339 SetHandleImage( GRAB_HANDLE );
1343 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1344 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1346 // Sets the primary handle image according if it's pressed, flipped, etc.
1347 SetHandleImage( LEFT_SELECTION_HANDLE );
1349 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1350 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1352 // Sets the secondary handle image according if it's pressed, flipped, etc.
1353 SetHandleImage( RIGHT_SELECTION_HANDLE );
1357 void SetupActiveLayerPropertyNotifications()
1364 // Vertical notifications.
1366 // Disconnect any previous connected callback.
1367 if( mVerticalLessThanNotification )
1369 mVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1370 mActiveLayer.RemovePropertyNotification( mVerticalLessThanNotification );
1373 if( mVerticalGreaterThanNotification )
1375 mVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1376 mActiveLayer.RemovePropertyNotification( mVerticalGreaterThanNotification );
1379 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1380 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1381 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1383 if( grabHandle.active )
1385 if( grabHandle.verticallyFlipped )
1387 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1388 mVerticalGreaterThanNotification.Reset();
1390 // The vertical distance from the center of the active layer to the top edje of the display.
1391 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1393 mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1394 LessThanCondition( mBoundingBox.y + topHeight ) );
1396 // Notifies the change from false to true and from true to false.
1397 mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1399 // Connects the signals with the callbacks.
1400 mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1404 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1405 mVerticalLessThanNotification.Reset();
1407 // The vertical distance from the center of the active layer to the bottom edje of the display.
1408 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1410 mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1411 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1413 // Notifies the change from false to true and from true to false.
1414 mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1416 // Connects the signals with the callbacks.
1417 mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1420 else // The selection handles are active
1422 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1424 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1425 mVerticalGreaterThanNotification.Reset();
1427 // The vertical distance from the center of the active layer to the top edje of the display.
1428 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1430 mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1431 LessThanCondition( mBoundingBox.y + topHeight ) );
1433 // Notifies the change from false to true and from true to false.
1434 mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1436 // Connects the signals with the callbacks.
1437 mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1439 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1441 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1442 mVerticalLessThanNotification.Reset();
1444 // The vertical distance from the center of the active layer to the bottom edje of the display.
1445 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1446 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1448 mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1449 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1451 // Notifies the change from false to true and from true to false.
1452 mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1454 // Connects the signals with the callbacks.
1455 mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1459 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1461 // The vertical distance from the center of the active layer to the top edje of the display.
1462 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1463 -primaryHandle.position.y + primaryHandle.size.height :
1464 -secondaryHandle.position.y + secondaryHandle.size.height );
1466 mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1467 LessThanCondition( mBoundingBox.y + topHeight ) );
1469 // Notifies the change from false to true and from true to false.
1470 mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1472 // Connects the signals with the callbacks.
1473 mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1475 // The vertical distance from the center of the active layer to the bottom edje of the display.
1476 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1477 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1478 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1480 mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1481 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1483 // Notifies the change from false to true and from true to false.
1484 mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1486 // Connects the signals with the callbacks.
1487 mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1491 // Horizontal notifications.
1493 // Disconnect any previous connected callback.
1494 if( mHorizontalLessThanNotification )
1496 mHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1497 mActiveLayer.RemovePropertyNotification( mHorizontalLessThanNotification );
1500 if( mHorizontalGreaterThanNotification )
1502 mHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1503 mActiveLayer.RemovePropertyNotification( mHorizontalGreaterThanNotification );
1506 if( primaryHandle.active || secondaryHandle.active )
1508 // The horizontal distance from the center of the active layer to the left edje of the display.
1509 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1510 -secondaryHandle.position.x + secondaryHandle.size.width );
1512 mHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1513 LessThanCondition( mBoundingBox.x + leftWidth ) );
1515 // Notifies the change from false to true and from true to false.
1516 mHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1518 // Connects the signals with the callbacks.
1519 mHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1521 // The horizontal distance from the center of the active layer to the right edje of the display.
1522 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1523 secondaryHandle.position.x + secondaryHandle.size.width );
1525 mHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1526 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1528 // Notifies the change from false to true and from true to false.
1529 mHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1531 // Connects the signals with the callbacks.
1532 mHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1538 float AlternatePopUpPositionRelativeToCursor()
1540 float alternativePosition = 0.0f;
1542 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1544 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1545 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1546 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1547 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1549 if( primaryHandle.active || secondaryHandle.active )
1551 const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1552 alternativePosition = 0.5f * popupHeight + cursor.lineHeight + maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
1556 alternativePosition = 0.5f * popupHeight + cursor.lineHeight + grabHandle.size.height + cursor.position.y;
1559 return alternativePosition;
1562 void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1564 float alternativeYPosition = 0.0f;
1565 // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1566 // if can't be positioned above, then position below row.
1567 alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1569 mCopyPastePopup.actor.SetY( alternativeYPosition );
1572 void SetUpPopupPositionNotifications()
1574 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1576 // Exceeding vertical boundary
1578 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1580 PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1581 OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
1582 mBoundingBox.w - popupHeight * 0.5f ) );
1584 verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1587 void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, const Vector3& popupDistanceFromAnchorPoint, Actor parent, const Vector4& boundingRectangleWorld )
1589 DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1591 // Parent must already by added to Stage for these Get calls to work
1592 const Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize() * parent.GetCurrentAnchorPoint();
1593 const Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position
1595 // Calculate distance to move popup (in local space) so fits within the boundary
1596 float xOffSetToKeepWithinBounds = 0.0f;
1597 if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1599 xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1601 else if( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1603 xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x );
1606 // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1607 if( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1609 requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1612 requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1614 // Prevent pixel mis-alignment by rounding down.
1615 requiredPopupPosition.x = floor( requiredPopupPosition.x );
1616 requiredPopupPosition.y = floor( requiredPopupPosition.y );
1619 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1621 HandleImpl& handle = mHandle[handleType];
1622 handle.size = Size( image.GetWidth(), image.GetHeight() );
1624 mHandleImages[handleType][handleImageType] = image;
1627 void SetScrollThreshold( float threshold )
1629 mScrollThreshold = threshold;
1632 float GetScrollThreshold() const
1634 return mScrollThreshold;
1637 void SetScrollSpeed( float speed )
1639 mScrollSpeed = speed;
1640 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1643 float GetScrollSpeed() const
1645 return mScrollSpeed;
1648 void NotifyEndOfScroll()
1654 mNotifyEndOfScroll = true;
1659 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1661 * It only starts the timer if it's already created.
1663 void StartScrollTimer()
1667 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1668 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1671 if( !mScrollTimer.IsRunning() )
1673 mScrollTimer.Start();
1678 * Stops the timer used to scroll the text.
1680 void StopScrollTimer()
1684 mScrollTimer.Stop();
1689 * Callback called by the timer used to scroll the text.
1691 * It calculates and sets a new scroll position.
1693 bool OnScrollTimerTick()
1695 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1700 switch( mScrollDirection )
1704 x = mScrollDistance;
1709 x = -mScrollDistance;
1714 y = mScrollDistance;
1719 y = -mScrollDistance;
1726 mController.DecorationEvent( mHandleScrolling,
1735 ControllerInterface& mController;
1737 TapGestureDetector mTapDetector;
1738 PanGestureDetector mPanDetector;
1739 LongPressGestureDetector mLongPressDetector;
1741 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1742 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1744 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1745 PropertyNotification mVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1746 PropertyNotification mVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1747 PropertyNotification mHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1748 PropertyNotification mHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1749 Control mPrimaryCursor;
1750 Control mSecondaryCursor;
1752 Actor mHighlightActor; ///< Actor to display highlight
1753 Renderer mHighlightRenderer;
1754 Shader mHighlightShader; ///< Shader used for highlight
1755 Property::Map mQuadVertexFormat;
1756 PopupImpl mCopyPastePopup;
1757 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1758 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1760 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1761 Vector4 mHandleColor;
1763 CursorImpl mCursor[CURSOR_COUNT];
1764 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1766 PropertyBuffer mQuadVertices;
1767 Geometry mQuadGeometry;
1768 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight
1770 Vector4 mBoundingBox; ///< The bounding box in world coords.
1771 Vector4 mHighlightColor; ///< Color of the highlight
1772 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1773 Vector2 mControlSize; ///< The control's size. Set by the Relayout.
1775 unsigned int mActiveCursor;
1776 unsigned int mCursorBlinkInterval;
1777 float mCursorBlinkDuration;
1778 float mCursorWidth; ///< The width of the cursors in pixels.
1779 HandleType mHandleScrolling; ///< The handle which is scrolling.
1780 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1781 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1782 float mScrollSpeed; ///< The scroll speed in pixels per second.
1783 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1784 int mTextDepth; ///< The depth used to render the text.
1786 bool mActiveCopyPastePopup : 1;
1787 bool mPopupSetNewPosition : 1;
1788 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1789 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1790 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1791 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1792 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1793 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1794 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1795 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1796 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1797 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1798 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1799 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1800 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1801 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1804 DecoratorPtr Decorator::New( ControllerInterface& controller,
1805 TextSelectionPopupCallbackInterface& callbackInterface )
1807 return DecoratorPtr( new Decorator( controller,
1808 callbackInterface ) );
1811 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1813 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1816 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1818 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1821 void Decorator::Relayout( const Vector2& size )
1823 mImpl->Relayout( size );
1826 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1828 mImpl->UpdatePositions( scrollOffset );
1833 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1835 mImpl->mActiveCursor = activeCursor;
1838 unsigned int Decorator::GetActiveCursor() const
1840 return mImpl->mActiveCursor;
1843 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1845 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1847 cursorImpl.position.x = x;
1848 cursorImpl.position.y = y;
1849 cursorImpl.cursorHeight = cursorHeight;
1850 cursorImpl.lineHeight = lineHeight;
1853 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1855 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1857 x = cursorImpl.position.x;
1858 y = cursorImpl.position.y;
1859 cursorHeight = cursorImpl.cursorHeight;
1860 lineHeight = cursorImpl.lineHeight;
1863 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1865 return mImpl->mCursor[cursor].position;
1868 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1870 mImpl->mCursor[cursor].color = color;
1873 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1875 return mImpl->mCursor[cursor].color;
1878 void Decorator::StartCursorBlink()
1880 if ( !mImpl->mCursorBlinkTimer )
1882 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1883 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1886 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1888 mImpl->mCursorBlinkTimer.Start();
1892 void Decorator::StopCursorBlink()
1894 if ( mImpl->mCursorBlinkTimer )
1896 mImpl->mCursorBlinkTimer.Stop();
1899 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
1902 void Decorator::DelayCursorBlink()
1904 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
1905 mImpl->mDelayCursorBlink = true;
1908 void Decorator::SetCursorBlinkInterval( float seconds )
1910 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1913 float Decorator::GetCursorBlinkInterval() const
1915 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1918 void Decorator::SetCursorBlinkDuration( float seconds )
1920 mImpl->mCursorBlinkDuration = seconds;
1923 float Decorator::GetCursorBlinkDuration() const
1925 return mImpl->mCursorBlinkDuration;
1928 void Decorator::SetCursorWidth( int width )
1930 mImpl->mCursorWidth = static_cast<float>( width );
1933 int Decorator::GetCursorWidth() const
1935 return static_cast<int>( mImpl->mCursorWidth );
1940 void Decorator::SetHandleActive( HandleType handleType, bool active )
1942 mImpl->mHandle[handleType].active = active;
1946 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
1948 mImpl->mIsHandlePreviouslyCrossed = false;
1951 // TODO: this is a work-around.
1952 // The problem is the handle actor does not receive the touch event with the Interrupt
1953 // state when the power button is pressed and the application goes to background.
1954 mImpl->mHandle[handleType].pressed = false;
1955 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1956 ImageView imageView = mImpl->mHandle[handleType].actor;
1957 if( imageReleased && imageView )
1959 imageView.SetImage( imageReleased );
1965 bool Decorator::IsHandleActive( HandleType handleType ) const
1967 return mImpl->mHandle[handleType].active ;
1970 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1972 mImpl->SetHandleImage( handleType, handleImageType, image );
1975 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1977 return mImpl->mHandleImages[handleType][handleImageType];
1980 void Decorator::SetHandleColor( const Vector4& color )
1982 mImpl->mHandleColor = color;
1985 const Vector4& Decorator::GetHandleColor() const
1987 return mImpl->mHandleColor;
1990 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1992 // Adjust handle's displacement
1993 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1995 handle.position.x = x;
1996 handle.position.y = y;
1997 handle.lineHeight = height;
1999 if( mImpl->mSmoothHandlePanEnabled )
2001 handle.grabDisplacementX = 0.f;
2002 handle.grabDisplacementY = 0.f;
2006 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2008 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2010 x = handle.position.x;
2011 y = handle.position.y;
2012 height = handle.lineHeight;
2015 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2017 return mImpl->mHandle[handleType].position;
2020 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2022 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2025 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2027 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2030 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2032 mImpl->mFlipSelectionHandlesOnCross = enable;
2035 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2037 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2038 mImpl->mFlipLeftSelectionHandleDirection = left;
2039 mImpl->mFlipRightSelectionHandleDirection = right;
2042 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
2044 mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
2047 void Decorator::ClearHighlights()
2049 mImpl->mHighlightQuadList.clear();
2050 mImpl->mHighlightPosition = Vector2::ZERO;
2053 void Decorator::SetHighlightColor( const Vector4& color )
2055 mImpl->mHighlightColor = color;
2058 const Vector4& Decorator::GetHighlightColor() const
2060 return mImpl->mHighlightColor;
2063 void Decorator::SetTextDepth( int textDepth )
2065 mImpl->mTextDepth = textDepth;
2068 void Decorator::SetPopupActive( bool active )
2070 mImpl->mActiveCopyPastePopup = active;
2073 bool Decorator::IsPopupActive() const
2075 return mImpl->mActiveCopyPastePopup ;
2078 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2080 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2082 if ( !mImpl->mCopyPastePopup.actor )
2084 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2085 #ifdef DECORATOR_DEBUG
2086 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
2088 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
2089 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::PopupRelayoutComplete ); // Position popup after size negotiation
2092 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2095 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2097 return mImpl->mEnabledPopupButtons;
2102 void Decorator::SetScrollThreshold( float threshold )
2104 mImpl->SetScrollThreshold( threshold );
2107 float Decorator::GetScrollThreshold() const
2109 return mImpl->GetScrollThreshold();
2112 void Decorator::SetScrollSpeed( float speed )
2114 mImpl->SetScrollSpeed( speed );
2117 float Decorator::GetScrollSpeed() const
2119 return mImpl->GetScrollSpeed();
2122 void Decorator::NotifyEndOfScroll()
2124 mImpl->NotifyEndOfScroll();
2127 void Decorator::SetHorizontalScrollEnabled( bool enable )
2129 mImpl->mHorizontalScrollingEnabled = enable;
2132 bool Decorator::IsHorizontalScrollEnabled() const
2134 return mImpl->mHorizontalScrollingEnabled;
2137 void Decorator::SetVerticalScrollEnabled( bool enable )
2139 mImpl->mVerticalScrollingEnabled = enable;
2142 bool Decorator::IsVerticalScrollEnabled() const
2144 return mImpl->mVerticalScrollingEnabled;
2147 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2149 mImpl->mSmoothHandlePanEnabled = enable;
2152 bool Decorator::IsSmoothHandlePanEnabled() const
2154 return mImpl->mSmoothHandlePanEnabled;
2157 Decorator::~Decorator()
2162 Decorator::Decorator( ControllerInterface& controller,
2163 TextSelectionPopupCallbackInterface& callbackInterface )
2166 mImpl = new Decorator::Impl( controller, callbackInterface );
2171 } // namespace Toolkit