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;
53 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
54 gl_Position = uMvpMatrix * position;
58 const char* FRAGMENT_SHADER = MAKE_SHADER(
59 uniform lowp vec4 uColor;
63 gl_FragColor = uColor;
74 #ifdef DECORATOR_DEBUG
75 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
85 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
86 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
88 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.
90 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
92 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
93 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
94 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
96 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
97 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.
98 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
100 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
102 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
105 * structure to hold coordinates of each quad, which will make up the mesh.
107 struct QuadCoordinates
110 * Default constructor
118 * @param[in] x1 left co-ordinate
119 * @param[in] y1 top co-ordinate
120 * @param[in] x2 right co-ordinate
121 * @param[in] y2 bottom co-ordinate
123 QuadCoordinates(float x1, float y1, float x2, float y2)
129 Dali::Vector2 min; ///< top-left (minimum) position of quad
130 Dali::Vector2 max; ///< bottom-right (maximum) position of quad
133 typedef std::vector<QuadCoordinates> QuadContainer;
136 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
137 * @param[in] boundingRectangle local bounding
138 * @param[out] Vector4 World coordinate bounding Box.
140 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
142 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
143 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
145 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
146 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
148 boundingBox = Dali::Vector4( originX,
150 originX + boundingRectangle.width,
151 originY + boundingRectangle.height );
154 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
156 // Convert to local coordinates and store as a Dali::Rect.
157 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
159 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
160 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
161 boundingRectangle.width = boundingBox.z - boundingBox.x;
162 boundingRectangle.height = boundingBox.w - boundingBox.y;
165 } // end of namespace
176 struct Decorator::Impl : public ConnectionTracker
190 : color( Dali::Color::BLACK ),
192 cursorHeight( 0.0f ),
210 grabDisplacementX( 0.f ),
211 grabDisplacementY( 0.f ),
215 verticallyFlippedPreferred( false ),
216 horizontallyFlipped( false ),
217 verticallyFlipped( false )
223 ImageView markerActor;
226 Vector2 globalPosition;
228 float lineHeight; ///< Not the handle height
229 float grabDisplacementX;
230 float grabDisplacementY;
234 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
235 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
236 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
246 TextSelectionPopup actor;
250 Impl( ControllerInterface& controller,
251 TextSelectionPopupCallbackInterface& callbackInterface )
252 : mController( controller ),
253 mEnabledPopupButtons( TextSelectionPopup::NONE ),
254 mTextSelectionPopupCallbackInterface( callbackInterface ),
255 mHandleColor( HANDLE_COLOR ),
257 mHighlightColor( LIGHT_BLUE ),
258 mHighlightPosition( Vector2::ZERO ),
259 mActiveCursor( ACTIVE_CURSOR_NONE ),
260 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
261 mCursorBlinkDuration( 0.0f ),
262 mCursorWidth( CURSOR_WIDTH ),
263 mHandleScrolling( HANDLE_TYPE_COUNT ),
264 mScrollDirection( SCROLL_NONE ),
265 mScrollThreshold( SCROLL_THRESHOLD ),
266 mScrollSpeed( SCROLL_SPEED ),
267 mScrollDistance( SCROLL_DISTANCE ),
269 mActiveCopyPastePopup( false ),
270 mPopupSetNewPosition( true ),
271 mCursorBlinkStatus( true ),
272 mDelayCursorBlink( false ),
273 mPrimaryCursorVisible( false ),
274 mSecondaryCursorVisible( false ),
275 mFlipSelectionHandlesOnCross( false ),
276 mFlipLeftSelectionHandleDirection( false ),
277 mFlipRightSelectionHandleDirection( false ),
278 mIsHandlePanning( false ),
279 mIsHandleCurrentlyCrossed( false ),
280 mIsHandlePreviouslyCrossed( false ),
281 mNotifyEndOfScroll( false ),
282 mHorizontalScrollingEnabled( false ),
283 mVerticalScrollingEnabled( false )
285 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
286 mHighlightShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
291 * Relayout of the decorations owned by the decorator.
292 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
294 void Relayout( const Vector2& size )
298 // TODO - Remove this if nothing is active
301 // Show or hide the cursors
306 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
307 mPrimaryCursorVisible = ( ( cursor.position.x + mCursorWidth <= mControlSize.width ) &&
308 ( cursor.position.x >= 0.f ) &&
309 ( cursor.position.y + cursor.cursorHeight <= mControlSize.height ) &&
310 ( cursor.position.y >= 0.f ) );
311 if( mPrimaryCursorVisible )
313 mPrimaryCursor.SetPosition( cursor.position.x,
315 mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
317 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
319 if( mSecondaryCursor )
321 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
322 mSecondaryCursorVisible = ( ( cursor.position.x + mCursorWidth <= mControlSize.width ) &&
323 ( cursor.position.x >= 0.f ) &&
324 ( cursor.position.y + cursor.cursorHeight <= mControlSize.height ) &&
325 ( cursor.position.y >= 0.f ) );
326 if( mSecondaryCursorVisible )
328 mSecondaryCursor.SetPosition( cursor.position.x,
330 mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
332 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
335 // Show or hide the grab handle
336 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
337 bool newGrabHandlePosition = false;
338 if( grabHandle.active )
340 const bool isVisible = ( ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) <= mControlSize.width ) &&
341 ( grabHandle.position.x >= 0.f ) &&
342 ( grabHandle.position.y <= mControlSize.height - grabHandle.lineHeight ) &&
343 ( grabHandle.position.y >= 0.f ) );
349 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
350 SetGrabHandlePosition();
352 // Sets the grab handle image according if it's pressed, flipped, etc.
353 SetHandleImage( GRAB_HANDLE );
355 newGrabHandlePosition = true;
358 if( grabHandle.actor )
360 grabHandle.actor.SetVisible( isVisible );
363 else if( grabHandle.actor )
365 grabHandle.actor.Unparent();
368 // Show or hide the selection handles/highlight
369 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
370 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
371 bool newPrimaryHandlePosition = false;
372 bool newSecondaryHandlePosition = false;
373 if( primary.active || secondary.active )
375 const bool isPrimaryVisible = ( ( primary.position.x <= mControlSize.width ) &&
376 ( primary.position.x >= 0.f ) &&
377 ( primary.position.y <= mControlSize.height - primary.lineHeight ) &&
378 ( primary.position.y >= 0.f ) );
379 const bool isSecondaryVisible = ( ( secondary.position.x <= mControlSize.width ) &&
380 ( secondary.position.x >= 0.f ) &&
381 ( secondary.position.y <= mControlSize.height - secondary.lineHeight ) &&
382 ( secondary.position.y >= 0.f ) );
384 if( isPrimaryVisible || isSecondaryVisible )
386 CreateSelectionHandles();
388 if( isPrimaryVisible )
390 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
392 // Sets the primary handle image according if it's pressed, flipped, etc.
393 SetHandleImage( LEFT_SELECTION_HANDLE );
395 SetSelectionHandleMarkerSize( primary );
397 newPrimaryHandlePosition = true;
400 if( isSecondaryVisible )
402 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
404 // Sets the secondary handle image according if it's pressed, flipped, etc.
405 SetHandleImage( RIGHT_SELECTION_HANDLE );
407 SetSelectionHandleMarkerSize( secondary );
409 newSecondaryHandlePosition = true;
415 primary.actor.SetVisible( isPrimaryVisible );
417 if( secondary.actor )
419 secondary.actor.SetVisible( isSecondaryVisible );
429 primary.actor.Unparent();
431 if( secondary.actor )
433 secondary.actor.Unparent();
435 if( mHighlightActor )
437 mHighlightActor.Unparent();
441 if( newGrabHandlePosition ||
442 newPrimaryHandlePosition ||
443 newSecondaryHandlePosition )
445 // Setup property notifications to find whether the handles leave the boundaries of the current display.
446 SetupActiveLayerPropertyNotifications();
449 if( mActiveCopyPastePopup )
452 mPopupSetNewPosition = true;
456 if( mCopyPastePopup.actor )
458 mCopyPastePopup.actor.HidePopup();
459 mPopupSetNewPosition = true;
464 void UpdatePositions( const Vector2& scrollOffset )
466 mCursor[PRIMARY_CURSOR].position += scrollOffset;
467 mCursor[SECONDARY_CURSOR].position += scrollOffset;
468 mHandle[ GRAB_HANDLE ].position += scrollOffset;
469 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
470 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
471 mHighlightPosition += scrollOffset;
476 if ( !mCopyPastePopup.actor )
481 if( !mCopyPastePopup.actor.GetParent() )
483 mActiveLayer.Add( mCopyPastePopup.actor );
486 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
487 mCopyPastePopup.actor.ShowPopup();
490 void DeterminePositionPopup()
492 if( !mActiveCopyPastePopup )
497 // Retrieves the popup's size after relayout.
498 const Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
500 if( mPopupSetNewPosition )
502 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
503 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
504 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
506 if( primaryHandle.active || secondaryHandle.active )
508 // Calculates the popup's position if selection handles are active.
509 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
510 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
511 const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
513 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
514 mCopyPastePopup.position.y = -0.5f * popupSize.height - maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
518 // Calculates the popup's position if the grab handle is active.
519 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
520 if( grabHandle.verticallyFlipped )
522 mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height - grabHandle.size.height + cursor.position.y, 0.0f );
526 mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height + cursor.position.y, 0.0f );
531 // Checks if there is enough space above the text control. If not it places the popup under it.
532 GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize * AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
534 SetUpPopupPositionNotifications();
536 mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
537 mPopupSetNewPosition = false;
540 void PopupRelayoutComplete( Actor actor )
542 // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
544 DeterminePositionPopup();
547 void CreateCursor( Control& cursor, const Vector4& color )
549 cursor = Control::New();
550 cursor.SetBackgroundColor( color );
551 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT );
552 cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
555 // Add or Remove cursor(s) from parent
558 if( mActiveCursor == ACTIVE_CURSOR_NONE )
562 mPrimaryCursor.Unparent();
564 if( mSecondaryCursor )
566 mSecondaryCursor.Unparent();
571 // Create Primary and or Secondary Cursor(s) if active and add to parent
572 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
573 mActiveCursor == ACTIVE_CURSOR_BOTH )
575 if ( !mPrimaryCursor )
577 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
578 #ifdef DECORATOR_DEBUG
579 mPrimaryCursor.SetName( "PrimaryCursorActor" );
583 if( !mPrimaryCursor.GetParent() )
585 mActiveLayer.Add( mPrimaryCursor );
589 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
591 if ( !mSecondaryCursor )
593 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
594 #ifdef DECORATOR_DEBUG
595 mSecondaryCursor.SetName( "SecondaryCursorActor" );
599 if( !mSecondaryCursor.GetParent() )
601 mActiveLayer.Add( mSecondaryCursor );
606 if( mSecondaryCursor )
608 mSecondaryCursor.Unparent();
614 bool OnCursorBlinkTimerTick()
616 if( !mDelayCursorBlink )
619 if ( mPrimaryCursor )
621 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
623 if ( mSecondaryCursor )
625 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
628 mCursorBlinkStatus = !mCursorBlinkStatus;
633 mDelayCursorBlink = false;
641 // Will consume tap gestures on handles.
642 mTapDetector = TapGestureDetector::New();
644 // Will consume double tap gestures on handles.
645 mTapDetector.SetMaximumTapsRequired( 2u );
647 // Will consume long press gestures on handles.
648 mLongPressDetector = LongPressGestureDetector::New();
650 // Detects pan gestures on handles.
651 mPanDetector = PanGestureDetector::New();
652 mPanDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
655 void CreateActiveLayer()
659 mActiveLayer = Layer::New();
660 #ifdef DECORATOR_DEBUG
661 mActiveLayer.SetName ( "ActiveLayerActor" );
664 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
665 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
667 // Add the active layer telling the controller it doesn't need clipping.
668 mController.AddDecoration( mActiveLayer, false );
671 mActiveLayer.RaiseToTop();
674 void SetSelectionHandleMarkerSize( HandleImpl& handle )
676 if( handle.markerActor )
678 handle.markerActor.SetSize( 0, handle.lineHeight );
682 void CreateGrabHandle()
684 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
685 if( !grabHandle.actor )
687 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
689 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
690 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
691 grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
693 // Area that Grab handle responds to, larger than actual handle so easier to move
694 #ifdef DECORATOR_DEBUG
695 grabHandle.actor.SetName( "GrabHandleActor" );
696 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
698 grabHandle.grabArea = Control::New();
699 Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
700 control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
701 grabHandle.grabArea.SetName( "GrabArea" );
705 grabHandle.grabArea = Actor::New();
706 grabHandle.grabArea.SetName( "GrabArea" );
709 grabHandle.grabArea = Actor::New();
712 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
713 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
714 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
715 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
716 grabHandle.actor.Add( grabHandle.grabArea );
717 grabHandle.actor.SetColor( mHandleColor );
719 grabHandle.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
721 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
722 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
723 mTapDetector.Attach( grabHandle.actor );
724 mLongPressDetector.Attach( grabHandle.actor );
726 // The grab handle's area is attached to the pan detector.
727 // The OnPan() method is connected to the signals emitted by the pan detector.
728 mPanDetector.Attach( grabHandle.grabArea );
730 mActiveLayer.Add( grabHandle.actor );
734 if( grabHandle.actor && !grabHandle.actor.GetParent() )
736 mActiveLayer.Add( grabHandle.actor );
740 void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
744 handle.markerActor = ImageView::New( image );
745 handle.markerActor.SetColor( mHandleColor );
746 handle.actor.Add( handle.markerActor );
748 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
750 if( LEFT_SELECTION_HANDLE == handleType )
752 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
753 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
755 else if( RIGHT_SELECTION_HANDLE == handleType )
757 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
758 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
763 void CreateSelectionHandles()
765 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
768 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
770 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
771 #ifdef DECORATOR_DEBUG
772 primary.actor.SetName("SelectionHandleOne");
774 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
775 GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
776 primary.actor.SetColor( mHandleColor );
778 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
779 #ifdef DECORATOR_DEBUG
780 primary.grabArea.SetName("SelectionHandleOneGrabArea");
782 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
783 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
784 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
785 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
787 primary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
789 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
790 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
791 mTapDetector.Attach( primary.actor );
792 mLongPressDetector.Attach( primary.actor );
794 // The handle's area is attached to the pan detector.
795 // The OnPan() method is connected to the signals emitted by the pan detector.
796 mPanDetector.Attach( primary.grabArea );
798 primary.actor.Add( primary.grabArea );
800 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
804 if( primary.actor && !primary.actor.GetParent() )
806 mActiveLayer.Add( primary.actor );
809 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
810 if( !secondary.actor )
812 if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
814 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
815 #ifdef DECORATOR_DEBUG
816 secondary.actor.SetName("SelectionHandleTwo");
818 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
819 GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
820 secondary.actor.SetColor( mHandleColor );
822 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
823 #ifdef DECORATOR_DEBUG
824 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
826 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
827 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
828 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
829 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
831 secondary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
833 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
834 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
835 mTapDetector.Attach( secondary.actor );
836 mLongPressDetector.Attach( secondary.actor );
838 // The handle's area is attached to the pan detector.
839 // The OnPan() method is connected to the signals emitted by the pan detector.
840 mPanDetector.Attach( secondary.grabArea );
842 secondary.actor.Add( secondary.grabArea );
844 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
848 if( secondary.actor && !secondary.actor.GetParent() )
850 mActiveLayer.Add( secondary.actor );
854 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
856 // Gets the world position of the active layer. The active layer is where the handles are added.
857 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
859 // The grab handle position in world coords.
860 // The active layer's world position is the center of the active layer. The origin of the
861 // coord system of the handles is the top left of the active layer.
862 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f );
863 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f );
866 void SetGrabHandlePosition()
868 // Reference to the grab handle.
869 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
871 // Transforms the handle position into world coordinates.
872 // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
873 // as it's transforming the handle's position set by the text-controller and not
874 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
875 // retrieves the position of the center of the actor but the handle's position set
876 // by the text controller is not the center of the actor.
877 Vector2 grabHandleWorldPosition;
878 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
880 // Check if the grab handle exceeds the boundaries of the decoration box.
881 // At the moment only the height is checked for the grab handle.
883 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
884 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
885 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
887 // The grab handle 'y' position in local coords.
888 // If the grab handle exceeds the bottom of the decoration box,
889 // set the 'y' position to the top of the line.
890 // The SetGrabHandleImage() method will change the orientation.
891 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
893 if( grabHandle.actor )
895 grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ) + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f ),
896 yLocalPosition + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f ) );
900 void SetSelectionHandlePosition( HandleType type )
902 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
904 // Reference to the selection handle.
905 HandleImpl& handle = mHandle[type];
907 // Transforms the handle position into world coordinates.
908 // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
909 // as it's transforming the handle's position set by the text-controller and not
910 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
911 // retrieves the position of the center of the actor but the handle's position set
912 // by the text controller is not the center of the actor.
913 Vector2 handleWorldPosition;
914 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
916 // Whether to flip the handle (horizontally).
917 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
919 // Whether to flip the handles if they are crossed.
920 bool crossFlip = false;
921 if( mFlipSelectionHandlesOnCross || !mIsHandlePanning )
923 crossFlip = mIsHandleCurrentlyCrossed;
926 // Whether the handle was crossed before start the panning.
927 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
929 // Does not flip if both conditions are true (double flip)
930 flipHandle = flipHandle != ( crossFlip || isHandlePreviouslyCrossed );
932 // Will flip the handles vertically if the user prefers it.
933 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
935 if( crossFlip || isHandlePreviouslyCrossed )
937 if( isPrimaryHandle )
939 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
943 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
947 // Check if the selection handle exceeds the boundaries of the decoration box.
948 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
949 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
951 // Does not flip if both conditions are true (double flip)
952 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
956 if( handle.actor && !handle.horizontallyFlipped )
958 // Change the anchor point to flip the image.
959 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
961 handle.horizontallyFlipped = true;
966 if( handle.actor && handle.horizontallyFlipped )
968 // Reset the anchor point.
969 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
971 handle.horizontallyFlipped = false;
975 // Whether to flip the handle vertically.
976 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
977 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
978 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
980 // The primary selection handle 'y' position in local coords.
981 // If the handle exceeds the bottom of the decoration box,
982 // set the 'y' position to the top of the line.
983 // The SetHandleImage() method will change the orientation.
984 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
988 handle.actor.SetPosition( handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f ),
989 yLocalPosition + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f ) );
993 void SetHandleImage( HandleType type )
995 HandleImpl& handle = mHandle[type];
997 HandleType markerType = HANDLE_TYPE_COUNT;
998 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
999 if( LEFT_SELECTION_HANDLE == type )
1001 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1002 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1004 else if( RIGHT_SELECTION_HANDLE == type )
1006 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1007 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1010 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1013 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1015 handle.actor.SetImage( mHandleImages[type][imageType] );
1018 if( HANDLE_TYPE_COUNT != markerType )
1020 if( handle.markerActor )
1022 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1023 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
1027 // Whether to flip the handle vertically.
1030 handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
1034 void CreateHighlight()
1036 if( !mHighlightActor )
1038 mHighlightActor = Actor::New();
1040 #ifdef DECORATOR_DEBUG
1041 mHighlightActor.SetName( "HighlightActor" );
1043 mHighlightActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
1044 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
1045 mHighlightActor.SetColor( mHighlightColor );
1046 mHighlightActor.SetColorMode( USE_OWN_COLOR );
1049 // Add the highlight box telling the controller it needs clipping.
1050 mController.AddDecoration( mHighlightActor, true );
1053 void UpdateHighlight()
1055 if ( mHighlightActor )
1057 // Sets the position of the highlight actor inside the decorator.
1058 mHighlightActor.SetPosition( mHighlightPosition.x,
1059 mHighlightPosition.y );
1061 const unsigned int numberOfQuads = mHighlightQuadList.size();
1062 if( 0u != numberOfQuads )
1064 // Set the size of the highlighted text to the actor.
1065 mHighlightActor.SetSize( mHighlightSize );
1067 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1068 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1069 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1071 Vector<Vector2> vertices;
1072 Vector<unsigned short> indices;
1074 vertices.Reserve( 4u * numberOfQuads );
1075 indices.Reserve( 6u * numberOfQuads );
1077 // Index to the vertex.
1078 unsigned int v = 0u;
1080 // Traverse all quads.
1081 for( std::vector<QuadCoordinates>::iterator it = mHighlightQuadList.begin(),
1082 endIt = mHighlightQuadList.end();
1086 QuadCoordinates& quad = *it;
1091 vertex.x = quad.min.x - offsetX;
1092 vertex.y = quad.min.y - offsetY;
1093 vertices.PushBack( vertex );
1096 vertex.x = quad.max.x - offsetX;
1097 vertex.y = quad.min.y - offsetY;
1098 vertices.PushBack( vertex );
1100 // bottom-left (v+2)
1101 vertex.x = quad.min.x - offsetX;
1102 vertex.y = quad.max.y - offsetY;
1103 vertices.PushBack( vertex );
1105 // bottom-right (v+3)
1106 vertex.x = quad.max.x - offsetX;
1107 vertex.y = quad.max.y - offsetY;
1108 vertices.PushBack( vertex );
1110 // triangle A (3, 1, 0)
1111 indices.PushBack( v + 3 );
1112 indices.PushBack( v + 1 );
1113 indices.PushBack( v );
1115 // triangle B (0, 2, 3)
1116 indices.PushBack( v );
1117 indices.PushBack( v + 2 );
1118 indices.PushBack( v + 3 );
1121 if( ! mQuadVertices )
1123 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
1126 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1128 if( !mQuadGeometry )
1130 mQuadGeometry = Geometry::New();
1131 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1133 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1135 if( !mHighlightRenderer )
1137 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1138 mHighlightActor.AddRenderer( mHighlightRenderer );
1142 mHighlightQuadList.clear();
1144 if( mHighlightRenderer )
1146 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1151 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1153 if( Gesture::Started == gesture.state )
1155 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1157 handle.globalPosition.x = handle.position.x;
1158 handle.globalPosition.y = handle.position.y;
1161 handle.grabDisplacementX += gesture.displacement.x;
1162 handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.displacement.y );
1164 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1165 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1166 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlipped ? handle.lineHeight : 0.f );
1168 if( ( Gesture::Started == gesture.state ) ||
1169 ( Gesture::Continuing == gesture.state ) )
1172 mController.GetTargetSize( targetSize );
1174 if( mHorizontalScrollingEnabled &&
1175 ( x < mScrollThreshold ) )
1177 mScrollDirection = SCROLL_RIGHT;
1178 mHandleScrolling = type;
1181 else if( mHorizontalScrollingEnabled &&
1182 ( x > targetSize.width - mScrollThreshold ) )
1184 mScrollDirection = SCROLL_LEFT;
1185 mHandleScrolling = type;
1188 else if( mVerticalScrollingEnabled &&
1189 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1191 mScrollDirection = SCROLL_TOP;
1192 mHandleScrolling = type;
1195 else if( mVerticalScrollingEnabled &&
1196 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1198 mScrollDirection = SCROLL_BOTTOM;
1199 mHandleScrolling = type;
1204 mHandleScrolling = HANDLE_TYPE_COUNT;
1206 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1209 mIsHandlePanning = true;
1211 else if( ( Gesture::Finished == gesture.state ) ||
1212 ( Gesture::Cancelled == gesture.state ) )
1215 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1217 mNotifyEndOfScroll = false;
1218 mHandleScrolling = HANDLE_TYPE_COUNT;
1220 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1224 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1229 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1231 handle.pressed = false;
1233 mIsHandlePanning = false;
1237 void OnPan( Actor actor, const PanGesture& gesture )
1239 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1240 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1241 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1243 if( actor == grabHandle.grabArea )
1245 DoPan( grabHandle, GRAB_HANDLE, gesture );
1247 else if( actor == primarySelectionHandle.grabArea )
1249 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1251 else if( actor == secondarySelectionHandle.grabArea )
1253 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1257 bool OnGrabHandleTouched( Actor actor, const TouchData& touch )
1259 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1261 // Switch between pressed/release grab-handle images
1262 if( touch.GetPointCount() > 0 &&
1265 const PointState::Type state = touch.GetState( 0 );
1267 if( PointState::DOWN == state )
1269 grabHandle.pressed = true;
1271 else if( ( PointState::UP == state ) ||
1272 ( PointState::INTERRUPTED == state ) )
1274 grabHandle.pressed = false;
1277 SetHandleImage( GRAB_HANDLE );
1280 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1284 bool OnHandleOneTouched( Actor actor, const TouchData& touch )
1286 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1288 // Switch between pressed/release selection handle images
1289 if( touch.GetPointCount() > 0 &&
1290 primarySelectionHandle.actor )
1292 const PointState::Type state = touch.GetState( 0 );
1294 if( PointState::DOWN == state )
1296 primarySelectionHandle.pressed = true;
1298 else if( ( PointState::UP == state ) ||
1299 ( PointState::INTERRUPTED == state ) )
1301 primarySelectionHandle.pressed = false;
1302 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1303 mIsHandlePanning = false;
1306 SetHandleImage( LEFT_SELECTION_HANDLE );
1309 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1313 bool OnHandleTwoTouched( Actor actor, const TouchData& touch )
1315 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1317 // Switch between pressed/release selection handle images
1318 if( touch.GetPointCount() > 0 &&
1319 secondarySelectionHandle.actor )
1321 const PointState::Type state = touch.GetState( 0 );
1323 if( PointState::DOWN == state )
1325 secondarySelectionHandle.pressed = true;
1327 else if( ( PointState::UP == state ) ||
1328 ( PointState::INTERRUPTED == state ) )
1330 secondarySelectionHandle.pressed = false;
1331 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1332 mIsHandlePanning = false;
1335 SetHandleImage( RIGHT_SELECTION_HANDLE );
1338 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1342 void HandleResetPosition( PropertyNotification& source )
1344 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1346 if( grabHandle.active )
1348 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1349 SetGrabHandlePosition();
1351 // Sets the grab handle image according if it's pressed, flipped, etc.
1352 SetHandleImage( GRAB_HANDLE );
1356 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1357 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1359 // Sets the primary handle image according if it's pressed, flipped, etc.
1360 SetHandleImage( LEFT_SELECTION_HANDLE );
1362 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1363 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1365 // Sets the secondary handle image according if it's pressed, flipped, etc.
1366 SetHandleImage( RIGHT_SELECTION_HANDLE );
1370 void SetupActiveLayerPropertyNotifications()
1377 // Vertical notifications.
1379 // Disconnect any previous connected callback.
1380 if( mVerticalLessThanNotification )
1382 mVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1383 mActiveLayer.RemovePropertyNotification( mVerticalLessThanNotification );
1386 if( mVerticalGreaterThanNotification )
1388 mVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1389 mActiveLayer.RemovePropertyNotification( mVerticalGreaterThanNotification );
1392 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1393 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1394 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1396 if( grabHandle.active )
1398 if( grabHandle.verticallyFlipped )
1400 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1401 mVerticalGreaterThanNotification.Reset();
1403 // The vertical distance from the center of the active layer to the top edje of the display.
1404 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1406 mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1407 LessThanCondition( mBoundingBox.y + topHeight ) );
1409 // Notifies the change from false to true and from true to false.
1410 mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1412 // Connects the signals with the callbacks.
1413 mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1417 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1418 mVerticalLessThanNotification.Reset();
1420 // The vertical distance from the center of the active layer to the bottom edje of the display.
1421 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1423 mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1424 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1426 // Notifies the change from false to true and from true to false.
1427 mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1429 // Connects the signals with the callbacks.
1430 mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1433 else // The selection handles are active
1435 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1437 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1438 mVerticalGreaterThanNotification.Reset();
1440 // The vertical distance from the center of the active layer to the top edje of the display.
1441 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1443 mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1444 LessThanCondition( mBoundingBox.y + topHeight ) );
1446 // Notifies the change from false to true and from true to false.
1447 mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1449 // Connects the signals with the callbacks.
1450 mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1452 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1454 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1455 mVerticalLessThanNotification.Reset();
1457 // The vertical distance from the center of the active layer to the bottom edje of the display.
1458 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1459 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1461 mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1462 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1464 // Notifies the change from false to true and from true to false.
1465 mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1467 // Connects the signals with the callbacks.
1468 mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1472 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1474 // The vertical distance from the center of the active layer to the top edje of the display.
1475 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1476 -primaryHandle.position.y + primaryHandle.size.height :
1477 -secondaryHandle.position.y + secondaryHandle.size.height );
1479 mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1480 LessThanCondition( mBoundingBox.y + topHeight ) );
1482 // Notifies the change from false to true and from true to false.
1483 mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1485 // Connects the signals with the callbacks.
1486 mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1488 // The vertical distance from the center of the active layer to the bottom edje of the display.
1489 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1490 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1491 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1493 mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1494 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1496 // Notifies the change from false to true and from true to false.
1497 mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1499 // Connects the signals with the callbacks.
1500 mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1504 // Horizontal notifications.
1506 // Disconnect any previous connected callback.
1507 if( mHorizontalLessThanNotification )
1509 mHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1510 mActiveLayer.RemovePropertyNotification( mHorizontalLessThanNotification );
1513 if( mHorizontalGreaterThanNotification )
1515 mHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1516 mActiveLayer.RemovePropertyNotification( mHorizontalGreaterThanNotification );
1519 if( primaryHandle.active || secondaryHandle.active )
1521 // The horizontal distance from the center of the active layer to the left edje of the display.
1522 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1523 -secondaryHandle.position.x + secondaryHandle.size.width );
1525 mHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1526 LessThanCondition( mBoundingBox.x + leftWidth ) );
1528 // Notifies the change from false to true and from true to false.
1529 mHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1531 // Connects the signals with the callbacks.
1532 mHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1534 // The horizontal distance from the center of the active layer to the right edje of the display.
1535 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1536 secondaryHandle.position.x + secondaryHandle.size.width );
1538 mHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1539 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1541 // Notifies the change from false to true and from true to false.
1542 mHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1544 // Connects the signals with the callbacks.
1545 mHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1551 float AlternatePopUpPositionRelativeToCursor()
1553 float alternativePosition = 0.0f;
1555 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1557 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1558 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1559 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1560 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1562 if( primaryHandle.active || secondaryHandle.active )
1564 const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1565 alternativePosition = 0.5f * popupHeight + cursor.lineHeight + maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
1569 alternativePosition = 0.5f * popupHeight + cursor.lineHeight + grabHandle.size.height + cursor.position.y;
1572 return alternativePosition;
1575 void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1577 float alternativeYPosition = 0.0f;
1578 // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1579 // if can't be positioned above, then position below row.
1580 alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1582 mCopyPastePopup.actor.SetY( alternativeYPosition );
1585 void SetUpPopupPositionNotifications()
1587 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1589 // Exceeding vertical boundary
1591 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1593 PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1594 OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
1595 mBoundingBox.w - popupHeight * 0.5f ) );
1597 verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1600 void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, const Vector3& popupDistanceFromAnchorPoint, Actor parent, const Vector4& boundingRectangleWorld )
1602 DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1604 // Parent must already by added to Stage for these Get calls to work
1605 const Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize() * parent.GetCurrentAnchorPoint();
1606 const Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position
1608 // Calculate distance to move popup (in local space) so fits within the boundary
1609 float xOffSetToKeepWithinBounds = 0.0f;
1610 if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1612 xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1614 else if( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1616 xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x );
1619 // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1620 if( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1622 requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1625 requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1627 // Prevent pixel mis-alignment by rounding down.
1628 requiredPopupPosition.x = floor( requiredPopupPosition.x );
1629 requiredPopupPosition.y = floor( requiredPopupPosition.y );
1632 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1634 HandleImpl& handle = mHandle[handleType];
1635 handle.size = Size( image.GetWidth(), image.GetHeight() );
1637 mHandleImages[handleType][handleImageType] = image;
1640 void SetScrollThreshold( float threshold )
1642 mScrollThreshold = threshold;
1645 float GetScrollThreshold() const
1647 return mScrollThreshold;
1650 void SetScrollSpeed( float speed )
1652 mScrollSpeed = speed;
1653 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1656 float GetScrollSpeed() const
1658 return mScrollSpeed;
1661 void NotifyEndOfScroll()
1667 mNotifyEndOfScroll = true;
1672 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1674 * It only starts the timer if it's already created.
1676 void StartScrollTimer()
1680 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1681 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1684 if( !mScrollTimer.IsRunning() )
1686 mScrollTimer.Start();
1691 * Stops the timer used to scroll the text.
1693 void StopScrollTimer()
1697 mScrollTimer.Stop();
1702 * Callback called by the timer used to scroll the text.
1704 * It calculates and sets a new scroll position.
1706 bool OnScrollTimerTick()
1708 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1713 switch( mScrollDirection )
1717 x = mScrollDistance;
1722 x = -mScrollDistance;
1727 y = mScrollDistance;
1732 y = -mScrollDistance;
1739 mController.DecorationEvent( mHandleScrolling,
1748 ControllerInterface& mController;
1750 TapGestureDetector mTapDetector;
1751 PanGestureDetector mPanDetector;
1752 LongPressGestureDetector mLongPressDetector;
1754 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1755 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1757 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1758 PropertyNotification mVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1759 PropertyNotification mVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1760 PropertyNotification mHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1761 PropertyNotification mHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1762 Control mPrimaryCursor;
1763 Control mSecondaryCursor;
1765 Actor mHighlightActor; ///< Actor to display highlight
1766 Renderer mHighlightRenderer;
1767 Shader mHighlightShader; ///< Shader used for highlight
1768 Property::Map mQuadVertexFormat;
1769 PopupImpl mCopyPastePopup;
1770 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1771 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1773 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1774 Vector4 mHandleColor;
1776 CursorImpl mCursor[CURSOR_COUNT];
1777 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1779 PropertyBuffer mQuadVertices;
1780 Geometry mQuadGeometry;
1781 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight
1783 Vector4 mBoundingBox; ///< The bounding box in world coords.
1784 Vector4 mHighlightColor; ///< Color of the highlight
1785 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1786 Size mHighlightSize; ///< The size of the highlighted text.
1787 Size mControlSize; ///< The control's size. Set by the Relayout.
1789 unsigned int mActiveCursor;
1790 unsigned int mCursorBlinkInterval;
1791 float mCursorBlinkDuration;
1792 float mCursorWidth; ///< The width of the cursors in pixels.
1793 HandleType mHandleScrolling; ///< The handle which is scrolling.
1794 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1795 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1796 float mScrollSpeed; ///< The scroll speed in pixels per second.
1797 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1798 int mTextDepth; ///< The depth used to render the text.
1800 bool mActiveCopyPastePopup : 1;
1801 bool mPopupSetNewPosition : 1;
1802 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1803 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1804 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1805 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1806 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1807 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1808 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1809 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1810 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1811 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1812 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1813 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1814 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1815 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1818 DecoratorPtr Decorator::New( ControllerInterface& controller,
1819 TextSelectionPopupCallbackInterface& callbackInterface )
1821 return DecoratorPtr( new Decorator( controller,
1822 callbackInterface ) );
1825 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1827 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1830 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1832 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1835 void Decorator::Relayout( const Vector2& size )
1837 mImpl->Relayout( size );
1840 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1842 mImpl->UpdatePositions( scrollOffset );
1847 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1849 mImpl->mActiveCursor = activeCursor;
1852 unsigned int Decorator::GetActiveCursor() const
1854 return mImpl->mActiveCursor;
1857 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1859 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1861 cursorImpl.position.x = x;
1862 cursorImpl.position.y = y;
1863 cursorImpl.cursorHeight = cursorHeight;
1864 cursorImpl.lineHeight = lineHeight;
1867 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1869 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1871 x = cursorImpl.position.x;
1872 y = cursorImpl.position.y;
1873 cursorHeight = cursorImpl.cursorHeight;
1874 lineHeight = cursorImpl.lineHeight;
1877 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1879 return mImpl->mCursor[cursor].position;
1882 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1884 mImpl->mCursor[cursor].color = color;
1887 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1889 return mImpl->mCursor[cursor].color;
1892 void Decorator::StartCursorBlink()
1894 if ( !mImpl->mCursorBlinkTimer )
1896 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1897 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1900 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1902 mImpl->mCursorBlinkTimer.Start();
1906 void Decorator::StopCursorBlink()
1908 if ( mImpl->mCursorBlinkTimer )
1910 mImpl->mCursorBlinkTimer.Stop();
1913 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
1916 void Decorator::DelayCursorBlink()
1918 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
1919 mImpl->mDelayCursorBlink = true;
1922 void Decorator::SetCursorBlinkInterval( float seconds )
1924 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1927 float Decorator::GetCursorBlinkInterval() const
1929 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1932 void Decorator::SetCursorBlinkDuration( float seconds )
1934 mImpl->mCursorBlinkDuration = seconds;
1937 float Decorator::GetCursorBlinkDuration() const
1939 return mImpl->mCursorBlinkDuration;
1942 void Decorator::SetCursorWidth( int width )
1944 mImpl->mCursorWidth = static_cast<float>( width );
1947 int Decorator::GetCursorWidth() const
1949 return static_cast<int>( mImpl->mCursorWidth );
1954 void Decorator::SetHandleActive( HandleType handleType, bool active )
1956 mImpl->mHandle[handleType].active = active;
1960 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
1962 mImpl->mIsHandlePreviouslyCrossed = false;
1965 // TODO: this is a work-around.
1966 // The problem is the handle actor does not receive the touch event with the Interrupt
1967 // state when the power button is pressed and the application goes to background.
1968 mImpl->mHandle[handleType].pressed = false;
1969 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1970 ImageView imageView = mImpl->mHandle[handleType].actor;
1971 if( imageReleased && imageView )
1973 imageView.SetImage( imageReleased );
1979 bool Decorator::IsHandleActive( HandleType handleType ) const
1981 return mImpl->mHandle[handleType].active ;
1984 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1986 mImpl->SetHandleImage( handleType, handleImageType, image );
1989 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1991 return mImpl->mHandleImages[handleType][handleImageType];
1994 void Decorator::SetHandleColor( const Vector4& color )
1996 mImpl->mHandleColor = color;
1999 const Vector4& Decorator::GetHandleColor() const
2001 return mImpl->mHandleColor;
2004 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
2006 // Adjust handle's displacement
2007 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2009 handle.position.x = x;
2010 handle.position.y = y;
2011 handle.lineHeight = height;
2013 if( mImpl->mSmoothHandlePanEnabled )
2015 handle.grabDisplacementX = 0.f;
2016 handle.grabDisplacementY = 0.f;
2020 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2022 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2024 x = handle.position.x;
2025 y = handle.position.y;
2026 height = handle.lineHeight;
2029 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2031 return mImpl->mHandle[handleType].position;
2034 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2036 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2039 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2041 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2044 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2046 mImpl->mFlipSelectionHandlesOnCross = enable;
2049 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2051 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2052 mImpl->mFlipLeftSelectionHandleDirection = left;
2053 mImpl->mFlipRightSelectionHandleDirection = right;
2056 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
2058 mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
2061 void Decorator::SetHighLightBox( const Vector2& position, const Size& size )
2063 mImpl->mHighlightPosition = position;
2064 mImpl->mHighlightSize = size;
2067 void Decorator::ClearHighlights()
2069 mImpl->mHighlightQuadList.clear();
2070 mImpl->mHighlightPosition = Vector2::ZERO;
2073 void Decorator::SetHighlightColor( const Vector4& color )
2075 mImpl->mHighlightColor = color;
2078 const Vector4& Decorator::GetHighlightColor() const
2080 return mImpl->mHighlightColor;
2083 void Decorator::SetTextDepth( int textDepth )
2085 mImpl->mTextDepth = textDepth;
2088 void Decorator::SetPopupActive( bool active )
2090 mImpl->mActiveCopyPastePopup = active;
2093 bool Decorator::IsPopupActive() const
2095 return mImpl->mActiveCopyPastePopup ;
2098 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2100 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2102 if ( !mImpl->mCopyPastePopup.actor )
2104 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2105 #ifdef DECORATOR_DEBUG
2106 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
2108 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
2109 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::PopupRelayoutComplete ); // Position popup after size negotiation
2112 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2115 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2117 return mImpl->mEnabledPopupButtons;
2122 void Decorator::SetScrollThreshold( float threshold )
2124 mImpl->SetScrollThreshold( threshold );
2127 float Decorator::GetScrollThreshold() const
2129 return mImpl->GetScrollThreshold();
2132 void Decorator::SetScrollSpeed( float speed )
2134 mImpl->SetScrollSpeed( speed );
2137 float Decorator::GetScrollSpeed() const
2139 return mImpl->GetScrollSpeed();
2142 void Decorator::NotifyEndOfScroll()
2144 mImpl->NotifyEndOfScroll();
2147 void Decorator::SetHorizontalScrollEnabled( bool enable )
2149 mImpl->mHorizontalScrollingEnabled = enable;
2152 bool Decorator::IsHorizontalScrollEnabled() const
2154 return mImpl->mHorizontalScrollingEnabled;
2157 void Decorator::SetVerticalScrollEnabled( bool enable )
2159 mImpl->mVerticalScrollingEnabled = enable;
2162 bool Decorator::IsVerticalScrollEnabled() const
2164 return mImpl->mVerticalScrollingEnabled;
2167 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2169 mImpl->mSmoothHandlePanEnabled = enable;
2172 bool Decorator::IsSmoothHandlePanEnabled() const
2174 return mImpl->mSmoothHandlePanEnabled;
2177 Decorator::~Decorator()
2182 Decorator::Decorator( ControllerInterface& controller,
2183 TextSelectionPopupCallbackInterface& callbackInterface )
2186 mImpl = new Decorator::Impl( controller, callbackInterface );
2191 } // namespace Toolkit