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-event.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/control-depth-index-ranges.h>
37 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
40 #define DECORATOR_DEBUG
44 #define MAKE_SHADER(A)#A
48 const char* VERTEX_SHADER = MAKE_SHADER(
49 attribute mediump vec2 aPosition;
50 uniform mediump mat4 uMvpMatrix;
51 uniform mediump vec3 uSize;
55 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
56 position.xyz *= uSize;
57 gl_Position = uMvpMatrix * position;
61 const char* FRAGMENT_SHADER = MAKE_SHADER(
62 uniform lowp vec4 uColor;
66 gl_FragColor = uColor;
77 #ifdef DECORATOR_DEBUG
78 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
88 const int DEFAULT_POPUP_OFFSET( -100.0f ); // Vertical offset of Popup from cursor or handles position.
90 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
91 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
93 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.
95 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
97 const unsigned int CURSOR_BLINK_INTERVAL = 500u; // Cursor blink interval
98 const float TO_MILLISECONDS = 1000.f;
99 const float TO_SECONDS = 1.f / TO_MILLISECONDS;
101 const unsigned int SCROLL_TICK_INTERVAL = 50u;
103 const float SCROLL_THRESHOLD = 10.f;
104 const float SCROLL_SPEED = 300.f;
105 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS;
107 const float CURSOR_WIDTH = 1.f;
110 * structure to hold coordinates of each quad, which will make up the mesh.
112 struct QuadCoordinates
115 * Default constructor
123 * @param[in] x1 left co-ordinate
124 * @param[in] y1 top co-ordinate
125 * @param[in] x2 right co-ordinate
126 * @param[in] y2 bottom co-ordinate
128 QuadCoordinates(float x1, float y1, float x2, float y2)
134 Dali::Vector2 min; ///< top-left (minimum) position of quad
135 Dali::Vector2 max; ///< bottom-right (maximum) position of quad
138 typedef std::vector<QuadCoordinates> QuadContainer;
141 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
142 * @param[in] boundingRectangle local bounding
143 * @param[out] Vector4 World coordinate bounding Box.
145 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
147 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
148 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
150 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
151 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
153 boundingBox = Dali::Vector4( originX,
155 originX + boundingRectangle.width,
156 originY + boundingRectangle.height );
159 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
161 // Convert to local coordinates and store as a Dali::Rect.
162 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
164 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
165 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
166 boundingRectangle.width = boundingBox.z - boundingBox.x;
167 boundingRectangle.height = boundingBox.w - boundingBox.y;
170 } // end of namespace
181 struct Decorator::Impl : public ConnectionTracker
195 : color( Dali::Color::BLACK ),
197 cursorHeight( 0.0f ),
214 grabDisplacementX( 0.f ),
215 grabDisplacementY( 0.f ),
219 verticallyFlippedPreferred( false ),
220 horizontallyFlipped( false ),
221 verticallyFlipped( false )
227 ImageActor markerActor;
231 float lineHeight; ///< Not the handle height
232 float grabDisplacementX;
233 float grabDisplacementY;
237 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
238 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
239 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
246 offset( DEFAULT_POPUP_OFFSET )
250 TextSelectionPopup actor;
255 Impl( ControllerInterface& controller,
256 TextSelectionPopupCallbackInterface& callbackInterface )
257 : mController( controller ),
258 mEnabledPopupButtons( TextSelectionPopup::NONE ),
259 mTextSelectionPopupCallbackInterface( callbackInterface ),
260 mHandleColor( HANDLE_COLOR ),
262 mHighlightColor( LIGHT_BLUE ),
263 mHighlightPosition( Vector2::ZERO ),
264 mActiveCursor( ACTIVE_CURSOR_NONE ),
265 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
266 mCursorBlinkDuration( 0.0f ),
267 mCursorWidth( CURSOR_WIDTH ),
268 mHandleScrolling( HANDLE_TYPE_COUNT ),
269 mScrollDirection( SCROLL_NONE ),
270 mScrollThreshold( SCROLL_THRESHOLD ),
271 mScrollSpeed( SCROLL_SPEED ),
272 mScrollDistance( SCROLL_DISTANCE ),
274 mActiveCopyPastePopup( false ),
275 mPopupSetNewPosition( true ),
276 mCursorBlinkStatus( true ),
277 mDelayCursorBlink( false ),
278 mPrimaryCursorVisible( false ),
279 mSecondaryCursorVisible( false ),
280 mFlipSelectionHandlesOnCross( false ),
281 mFlipLeftSelectionHandleDirection( false ),
282 mFlipRightSelectionHandleDirection( false ),
283 mHandlePanning( false ),
284 mHandleCurrentCrossed( false ),
285 mHandlePreviousCrossed( false ),
286 mNotifyEndOfScroll( false )
288 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
289 mQuadIndexFormat[ "indices" ] = Property::INTEGER;
290 mHighlightMaterial = Material::New( Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ) );
296 * Relayout of the decorations owned by the decorator.
297 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
299 void Relayout( const Vector2& size )
301 // TODO - Remove this if nothing is active
304 // Show or hide the cursors
309 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
310 mPrimaryCursorVisible = ( cursor.position.x + mCursorWidth <= size.width ) && ( cursor.position.x >= 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 <= size.width ) && ( cursor.position.x >= 0.f );
323 if( mSecondaryCursorVisible )
325 mSecondaryCursor.SetPosition( cursor.position.x,
327 mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
329 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
332 // Show or hide the grab handle
333 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
334 if( grabHandle.active )
336 const bool isVisible = ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) <= size.width ) && ( grabHandle.position.x >= 0.f );
342 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
343 SetGrabHandlePosition();
345 // Sets the grab handle image according if it's pressed, flipped, etc.
346 SetHandleImage( GRAB_HANDLE );
349 if( grabHandle.actor )
351 grabHandle.actor.SetVisible( isVisible );
354 else if( grabHandle.actor )
356 grabHandle.actor.Unparent();
359 // Show or hide the selection handles/highlight
360 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
361 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
362 if( primary.active || secondary.active )
364 const bool isPrimaryVisible = ( primary.position.x <= size.width ) && ( primary.position.x >= 0.f );
365 const bool isSecondaryVisible = ( secondary.position.x <= size.width ) && ( secondary.position.x >= 0.f );
367 if( isPrimaryVisible || isSecondaryVisible )
369 CreateSelectionHandles();
371 if( isPrimaryVisible )
373 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
375 // Sets the primary handle image according if it's pressed, flipped, etc.
376 SetHandleImage( LEFT_SELECTION_HANDLE );
378 SetSelectionHandleMarkerSize( primary );
381 if( isSecondaryVisible )
383 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
385 // Sets the secondary handle image according if it's pressed, flipped, etc.
386 SetHandleImage( RIGHT_SELECTION_HANDLE );
388 SetSelectionHandleMarkerSize( secondary );
394 primary.actor.SetVisible( isPrimaryVisible );
396 if( secondary.actor )
398 secondary.actor.SetVisible( isSecondaryVisible );
408 primary.actor.Unparent();
410 if( secondary.actor )
412 secondary.actor.Unparent();
414 if( mHighlightActor )
416 mHighlightActor.Unparent();
420 if( mActiveCopyPastePopup )
426 if( mCopyPastePopup.actor )
428 mCopyPastePopup.actor.HidePopup();
429 mPopupSetNewPosition = true;
434 void UpdatePositions( const Vector2& scrollOffset )
436 mCursor[PRIMARY_CURSOR].position += scrollOffset;
437 mCursor[SECONDARY_CURSOR].position += scrollOffset;
438 mHandle[ GRAB_HANDLE ].position += scrollOffset;
439 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
440 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
441 mHighlightPosition += scrollOffset;
446 if ( !mCopyPastePopup.actor )
451 if( !mCopyPastePopup.actor.GetParent() )
453 mActiveLayer.Add( mCopyPastePopup.actor );
456 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
457 mCopyPastePopup.actor.ShowPopup();
460 void DeterminePositionPopup()
462 if ( !mActiveCopyPastePopup )
467 if( mPopupSetNewPosition )
469 if ( mHandle[LEFT_SELECTION_HANDLE].active || mHandle[RIGHT_SELECTION_HANDLE].active )
471 float minHandleXPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
472 float maxHandleXPosition = std::max ( mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
474 float minHandleYPosition = std::min ( mHandle[LEFT_SELECTION_HANDLE].position.y, mHandle[RIGHT_SELECTION_HANDLE].position.y );
476 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) *0.5f );
477 mCopyPastePopup.position.y = minHandleYPosition + mCopyPastePopup.offset;
481 mCopyPastePopup.position = Vector3( mCursor[PRIMARY_CURSOR].position.x, mCursor[PRIMARY_CURSOR].position.y -100.0f , 0.0f ); //todo 100 to be an offset Property
485 Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
487 GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize, AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
489 SetUpPopupPositionNotifications();
491 mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
492 mPopupSetNewPosition = false;
495 void PopupRelayoutComplete( Actor actor )
497 // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
499 DeterminePositionPopup();
502 void CreateCursor( ImageActor& cursor, const Vector4& color )
504 cursor = CreateSolidColorActor( color );
505 cursor.SetSortModifier( DECORATION_DEPTH_INDEX );
506 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one.
507 cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
510 // Add or Remove cursor(s) from parent
513 if( mActiveCursor == ACTIVE_CURSOR_NONE )
517 mPrimaryCursor.Unparent();
519 if( mSecondaryCursor )
521 mSecondaryCursor.Unparent();
526 // Create Primary and or Secondary Cursor(s) if active and add to parent
527 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
528 mActiveCursor == ACTIVE_CURSOR_BOTH )
530 if ( !mPrimaryCursor )
532 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
533 #ifdef DECORATOR_DEBUG
534 mPrimaryCursor.SetName( "PrimaryCursorActor" );
538 if( !mPrimaryCursor.GetParent() )
540 mActiveLayer.Add( mPrimaryCursor );
544 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
546 if ( !mSecondaryCursor )
548 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
549 #ifdef DECORATOR_DEBUG
550 mSecondaryCursor.SetName( "SecondaryCursorActor" );
554 if( !mSecondaryCursor.GetParent() )
556 mActiveLayer.Add( mSecondaryCursor );
561 if( mSecondaryCursor )
563 mSecondaryCursor.Unparent();
569 bool OnCursorBlinkTimerTick()
571 if( !mDelayCursorBlink )
574 if ( mPrimaryCursor )
576 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
578 if ( mSecondaryCursor )
580 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
583 mCursorBlinkStatus = !mCursorBlinkStatus;
588 mDelayCursorBlink = false;
594 void SetupTouchEvents()
596 mTapDetector = TapGestureDetector::New();
597 mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
599 mPanGestureDetector = PanGestureDetector::New();
600 mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
603 void CreateActiveLayer()
607 mActiveLayer = Layer::New();
608 #ifdef DECORATOR_DEBUG
609 mActiveLayer.SetName ( "ActiveLayerActor" );
612 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
613 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
614 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
616 // Add the active layer telling the controller it doesn't need clipping.
617 mController.AddDecoration( mActiveLayer, false );
620 mActiveLayer.RaiseToTop();
623 void SetSelectionHandleMarkerSize( HandleImpl& handle )
625 if( handle.markerActor )
627 handle.markerActor.SetSize( 0, handle.lineHeight );
631 void CreateGrabHandle()
633 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
634 if( !grabHandle.actor )
636 grabHandle.actor = ImageActor::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
637 grabHandle.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
638 grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
639 // Area that Grab handle responds to, larger than actual handle so easier to move
640 #ifdef DECORATOR_DEBUG
641 grabHandle.actor.SetName( "GrabHandleActor" );
642 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
644 grabHandle.grabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 );
645 grabHandle.grabArea.SetName( "GrabArea" );
649 grabHandle.grabArea = Actor::New();
650 grabHandle.grabArea.SetName( "GrabArea" );
653 grabHandle.grabArea = Actor::New();
656 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
657 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
658 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
659 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
660 grabHandle.actor.Add( grabHandle.grabArea );
661 grabHandle.actor.SetColor( mHandleColor );
663 grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
664 mTapDetector.Attach( grabHandle.grabArea );
665 mPanGestureDetector.Attach( grabHandle.grabArea );
667 mActiveLayer.Add( grabHandle.actor );
670 if( !grabHandle.actor.GetParent() )
672 mActiveLayer.Add( grabHandle.actor );
676 void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
680 handle.markerActor = ImageActor::New( image );
681 handle.markerActor.SetColor( mHandleColor );
682 handle.actor.Add( handle.markerActor );
684 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
686 if( LEFT_SELECTION_HANDLE == handleType )
688 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
689 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
691 else if( RIGHT_SELECTION_HANDLE == handleType )
693 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
694 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
699 void CreateSelectionHandles()
701 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
704 primary.actor = ImageActor::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
705 #ifdef DECORATOR_DEBUG
706 primary.actor.SetName("SelectionHandleOne");
708 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
709 primary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
710 primary.actor.SetColor( mHandleColor );
712 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
713 #ifdef DECORATOR_DEBUG
714 primary.grabArea.SetName("SelectionHandleOneGrabArea");
716 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
717 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
718 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
719 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
721 mTapDetector.Attach( primary.grabArea );
722 mPanGestureDetector.Attach( primary.grabArea );
723 primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
725 primary.actor.Add( primary.grabArea );
727 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
730 if( !primary.actor.GetParent() )
732 mActiveLayer.Add( primary.actor );
735 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
736 if( !secondary.actor )
738 secondary.actor = ImageActor::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
739 #ifdef DECORATOR_DEBUG
740 secondary.actor.SetName("SelectionHandleTwo");
742 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
743 secondary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
744 secondary.actor.SetColor( mHandleColor );
746 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
747 #ifdef DECORATOR_DEBUG
748 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
750 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
751 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
752 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
753 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
755 mTapDetector.Attach( secondary.grabArea );
756 mPanGestureDetector.Attach( secondary.grabArea );
757 secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
759 secondary.actor.Add( secondary.grabArea );
761 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
764 if( !secondary.actor.GetParent() )
766 mActiveLayer.Add( secondary.actor );
770 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
772 // Get the world position of the active layer
773 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
775 // Get the size of the UI control.
777 mController.GetTargetSize( targetSize );
779 // The grab handle position in world coords.
780 position.x = parentWorldPosition.x - 0.5f * targetSize.width + handle.position.x;
781 position.y = parentWorldPosition.y - 0.5f * targetSize.height + handle.position.y + handle.lineHeight;
784 void SetGrabHandlePosition()
786 // Reference to the grab handle.
787 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
789 // The grab handle position in world coords.
790 Vector2 grabHandleWorldPosition;
791 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
793 // Check if the grab handle exceeds the boundaries of the decoration box.
794 // At the moment only the height is checked for the grab handle.
796 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
797 ( ( grabHandleWorldPosition.y - grabHandle.lineHeight - grabHandle.size.height ) > mBoundingBox.y ) ) ||
798 ( grabHandleWorldPosition.y + grabHandle.size.height > mBoundingBox.w );
800 // The grab handle 'y' position in local coords.
801 // If the grab handle exceeds the bottom of the decoration box,
802 // set the 'y' position to the top of the line.
803 // The SetGrabHandleImage() method will change the orientation.
804 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
806 grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ),
807 yLocalPosition ); // TODO : Fix for multiline.
810 void SetSelectionHandlePosition( HandleType type )
812 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
814 // Reference to the selection handle.
815 HandleImpl& handle = mHandle[type];
817 // Get the world coordinates of the handle position.
818 Vector2 handleWorldPosition;
819 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
821 // Whether to flip the handle (horizontally).
822 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
824 // Whether to flip the handles if they are crossed.
825 bool crossFlip = false;
826 if( mFlipSelectionHandlesOnCross || !mHandlePanning )
828 crossFlip = mHandleCurrentCrossed;
831 // Does not flip if both conditions are true (double flip)
832 flipHandle = flipHandle != ( crossFlip || mHandlePreviousCrossed );
834 // Will flip the handles vertically if the user prefers it.
835 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
837 if( crossFlip || mHandlePreviousCrossed )
839 if( isPrimaryHandle )
841 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
845 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
849 // Check if the selection handle exceeds the boundaries of the decoration box.
850 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
852 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
854 // Does not flip if both conditions are true (double flip)
855 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
859 if( !handle.horizontallyFlipped )
861 // Change the anchor point to flip the image.
862 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
864 handle.horizontallyFlipped = true;
869 if( handle.horizontallyFlipped )
871 // Reset the anchor point.
872 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
874 handle.horizontallyFlipped = false;
878 // Whether to flip the handle vertically.
879 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
880 ( ( handleWorldPosition.y - handle.lineHeight - handle.size.height ) > mBoundingBox.y ) ) ||
881 ( handleWorldPosition.y + handle.size.height > mBoundingBox.w );
883 // The primary selection handle 'y' position in local coords.
884 // If the handle exceeds the bottom of the decoration box,
885 // set the 'y' position to the top of the line.
886 // The SetHandleImage() method will change the orientation.
887 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
889 handle.actor.SetPosition( handle.position.x,
890 yLocalPosition ); // TODO : Fix for multiline.
893 void SetHandleImage( HandleType type )
895 HandleImpl& handle = mHandle[type];
897 HandleType markerType = HANDLE_TYPE_COUNT;
898 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
899 if( LEFT_SELECTION_HANDLE == type )
901 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
902 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
904 else if( RIGHT_SELECTION_HANDLE == type )
906 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
907 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
910 // Chooses between the released or pressed image. It checks whether the pressed image exists.
913 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
915 handle.actor.SetImage( mHandleImages[type][imageType] );
918 if( HANDLE_TYPE_COUNT != markerType )
920 if( handle.markerActor )
922 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
923 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
927 // Whether to flip the handle vertically.
930 handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
934 void CreateHighlight()
936 if( !mHighlightActor )
938 mHighlightActor = Actor::New();
940 #ifdef DECORATOR_DEBUG
941 mHighlightActor.SetName( "HighlightActor" );
943 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
944 mHighlightActor.SetSize( 1.0f, 1.0f );
945 mHighlightActor.SetColor( mHighlightColor );
946 mHighlightActor.SetColorMode( USE_OWN_COLOR );
949 // Add the highlight box telling the controller it needs clipping.
950 mController.AddDecoration( mHighlightActor, true );
953 void UpdateHighlight()
955 if ( mHighlightActor )
957 if( !mHighlightQuadList.empty() )
959 Vector< Vector2 > vertices;
960 Vector< unsigned int> indices;
963 std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
964 std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
966 for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
968 QuadCoordinates& quad = *iter;
971 vertex.x = quad.min.x;
972 vertex.y = quad.min.y;
973 vertices.PushBack( vertex );
976 vertex.x = quad.max.x;
977 vertex.y = quad.min.y;
978 vertices.PushBack( vertex );
981 vertex.x = quad.min.x;
982 vertex.y = quad.max.y;
983 vertices.PushBack( vertex );
985 // bottom-right (v+3)
986 vertex.x = quad.max.x;
987 vertex.y = quad.max.y;
988 vertices.PushBack( vertex );
990 // triangle A (3, 1, 0)
991 indices.PushBack( v + 3 );
992 indices.PushBack( v + 1 );
993 indices.PushBack( v );
995 // triangle B (0, 2, 3)
996 indices.PushBack( v );
997 indices.PushBack( v + 2 );
998 indices.PushBack( v + 3 );
1003 mQuadVertices.SetSize( vertices.Size() );
1007 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() );
1012 mQuadIndices.SetSize( indices.Size() );
1016 mQuadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() );
1019 mQuadVertices.SetData( &vertices[ 0 ] );
1020 mQuadIndices.SetData( &indices[ 0 ] );
1022 if( !mQuadGeometry )
1024 mQuadGeometry = Geometry::New();
1025 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1027 mQuadGeometry.SetIndexBuffer( mQuadIndices );
1029 if( !mHighlightRenderer )
1031 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightMaterial );
1032 mHighlightActor.AddRenderer( mHighlightRenderer );
1036 mHighlightActor.SetPosition( mHighlightPosition.x,
1037 mHighlightPosition.y );
1039 mHighlightQuadList.clear();
1041 if( mHighlightRenderer )
1043 mHighlightRenderer.SetDepthIndex( mTextDepth - 2u ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1048 void OnTap( Actor actor, const TapGesture& tap )
1050 if( actor == mHandle[GRAB_HANDLE].actor )
1056 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1058 if( Gesture::Started == gesture.state )
1060 handle.grabDisplacementX = handle.grabDisplacementY = 0;
1063 handle.grabDisplacementX += gesture.displacement.x;
1064 handle.grabDisplacementY += gesture.displacement.y;
1066 const float x = handle.position.x + handle.grabDisplacementX;
1067 const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
1069 if( Gesture::Started == gesture.state ||
1070 Gesture::Continuing == gesture.state )
1073 mController.GetTargetSize( targetSize );
1075 if( x < mScrollThreshold )
1077 mScrollDirection = SCROLL_RIGHT;
1078 mHandleScrolling = type;
1081 else if( x > targetSize.width - mScrollThreshold )
1083 mScrollDirection = SCROLL_LEFT;
1084 mHandleScrolling = type;
1089 mHandleScrolling = HANDLE_TYPE_COUNT;
1091 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1094 mHandlePanning = true;
1096 else if( Gesture::Finished == gesture.state ||
1097 Gesture::Cancelled == gesture.state )
1100 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1102 mNotifyEndOfScroll = false;
1103 mHandleScrolling = HANDLE_TYPE_COUNT;
1105 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1109 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1112 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1113 handle.pressed = false;
1115 mHandlePanning = false;
1119 void OnPan( Actor actor, const PanGesture& gesture )
1121 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1122 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1123 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1125 if( actor == grabHandle.grabArea )
1127 DoPan( grabHandle, GRAB_HANDLE, gesture );
1129 else if( actor == primarySelectionHandle.grabArea )
1131 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1133 else if( actor == secondarySelectionHandle.grabArea )
1135 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1139 bool OnGrabHandleTouched( Actor actor, const TouchEvent& event )
1141 // Switch between pressed/release grab-handle images
1142 if( event.GetPointCount() > 0 &&
1143 mHandle[GRAB_HANDLE].actor )
1145 const TouchPoint& point = event.GetPoint(0);
1147 if( TouchPoint::Down == point.state )
1149 mHandle[GRAB_HANDLE].pressed = true;
1151 else if( ( TouchPoint::Up == point.state ) ||
1152 ( TouchPoint::Interrupted == point.state ) )
1154 mHandle[GRAB_HANDLE].pressed = false;
1157 SetHandleImage( GRAB_HANDLE );
1160 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1164 bool OnHandleOneTouched( Actor actor, const TouchEvent& event )
1166 // Switch between pressed/release selection handle images
1167 if( event.GetPointCount() > 0 &&
1168 mHandle[LEFT_SELECTION_HANDLE].actor )
1170 const TouchPoint& point = event.GetPoint(0);
1172 if( TouchPoint::Down == point.state )
1174 mHandle[LEFT_SELECTION_HANDLE].pressed = true;
1176 else if( ( TouchPoint::Up == point.state ) ||
1177 ( TouchPoint::Interrupted == point.state ) )
1179 mHandle[LEFT_SELECTION_HANDLE].pressed = false;
1180 mHandlePreviousCrossed = mHandleCurrentCrossed;
1181 mHandlePanning = false;
1184 SetHandleImage( LEFT_SELECTION_HANDLE );
1187 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1191 bool OnHandleTwoTouched( Actor actor, const TouchEvent& event )
1193 // Switch between pressed/release selection handle images
1194 if( event.GetPointCount() > 0 &&
1195 mHandle[RIGHT_SELECTION_HANDLE].actor )
1197 const TouchPoint& point = event.GetPoint(0);
1199 if( TouchPoint::Down == point.state )
1201 mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
1203 else if( ( TouchPoint::Up == point.state ) ||
1204 ( TouchPoint::Interrupted == point.state ) )
1206 mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
1207 mHandlePreviousCrossed = mHandleCurrentCrossed;
1208 mHandlePanning = false;
1211 SetHandleImage( RIGHT_SELECTION_HANDLE );
1214 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1220 float AlternatePopUpPositionRelativeToCursor()
1222 const float popupHeight = 120.0f; // todo Set as a MaxSize Property in Control or retrieve from CopyPastePopup class.
1223 const float BOTTOM_HANDLE_BOTTOM_OFFSET = 1.5; //todo Should be a property
1225 float alternativePosition=0.0f;;
1227 if( mPrimaryCursor ) // Secondary cursor not used for paste
1229 alternativePosition = mCursor[PRIMARY_CURSOR].position.y + popupHeight;
1232 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1233 const HandleImpl& selectionPrimaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1234 const HandleImpl& selectionSecondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1236 if( grabHandle.active )
1238 // If grab handle enabled then position pop-up below the grab handle.
1239 alternativePosition = grabHandle.position.y + grabHandle.size.height + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET;
1242 else if( selectionPrimaryHandle.active || selectionSecondaryHandle.active )
1244 const float maxHeight = std::max( selectionPrimaryHandle.size.height,
1245 selectionSecondaryHandle.size.height );
1246 const float maxY = std::max( selectionPrimaryHandle.position.y,
1247 selectionSecondaryHandle.position.y );
1249 alternativePosition = maxY + maxHeight + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET;
1252 return alternativePosition;
1255 void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1257 float alternativeYPosition=0.0f;
1258 // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1259 // if can't be positioned above, then position below row.
1260 alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1262 mCopyPastePopup.actor.SetY( alternativeYPosition );
1265 void SetUpPopupPositionNotifications( )
1267 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1269 // Exceeding vertical boundary
1271 float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT);
1273 PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1274 OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
1275 mBoundingBox.w - popupHeight * 0.5f ) );
1277 verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1280 void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, Vector3& popupSize, Vector3 anchorPoint, Actor& parent, const Vector4& boundingRectangleWorld )
1282 DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1284 // Parent must already by added to Stage for these Get calls to work
1285 Vector3 parentAnchorPoint = parent.GetCurrentAnchorPoint();
1286 Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize()*parentAnchorPoint;
1287 Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position
1288 Vector3 popupDistanceFromAnchorPoint = popupSize*anchorPoint;
1290 // Calculate distance to move popup (in local space) so fits within the boundary
1291 float xOffSetToKeepWithinBounds = 0.0f;
1292 if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1294 xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1296 else if ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1298 xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x );
1301 // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1302 if ( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1304 requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1307 requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1309 // Prevent pixel mis-alignment by rounding down.
1310 requiredPopupPosition.x = static_cast<int>( requiredPopupPosition.x );
1311 requiredPopupPosition.y = static_cast<int>( requiredPopupPosition.y );
1314 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1316 HandleImpl& handle = mHandle[handleType];
1317 handle.size = Size( image.GetWidth(), image.GetHeight() );
1319 mHandleImages[handleType][handleImageType] = image;
1322 void SetScrollThreshold( float threshold )
1324 mScrollThreshold = threshold;
1327 float GetScrollThreshold() const
1329 return mScrollThreshold;
1332 void SetScrollSpeed( float speed )
1334 mScrollSpeed = speed;
1335 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1338 float GetScrollSpeed() const
1340 return mScrollSpeed;
1343 void NotifyEndOfScroll()
1349 mNotifyEndOfScroll = true;
1354 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1356 * It only starts the timer if it's already created.
1358 void StartScrollTimer()
1362 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1363 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1366 if( !mScrollTimer.IsRunning() )
1368 mScrollTimer.Start();
1373 * Stops the timer used to scroll the text.
1375 void StopScrollTimer()
1379 mScrollTimer.Stop();
1384 * Callback called by the timer used to scroll the text.
1386 * It calculates and sets a new scroll position.
1388 bool OnScrollTimerTick()
1390 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1392 mController.DecorationEvent( mHandleScrolling,
1394 mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
1401 ControllerInterface& mController;
1403 TapGestureDetector mTapDetector;
1404 PanGestureDetector mPanGestureDetector;
1405 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1406 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1408 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1409 ImageActor mPrimaryCursor;
1410 ImageActor mSecondaryCursor;
1412 Actor mHighlightActor; ///< Actor to display highlight
1413 Renderer mHighlightRenderer;
1414 Material mHighlightMaterial; ///< Material used for highlight
1415 Property::Map mQuadVertexFormat;
1416 Property::Map mQuadIndexFormat;
1417 PopupImpl mCopyPastePopup;
1418 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1419 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1421 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1422 Vector4 mHandleColor;
1424 CursorImpl mCursor[CURSOR_COUNT];
1425 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1427 PropertyBuffer mQuadVertices;
1428 PropertyBuffer mQuadIndices;
1429 Geometry mQuadGeometry;
1430 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight
1432 Vector4 mBoundingBox; ///< The bounding box in world coords.
1433 Vector4 mHighlightColor; ///< Color of the highlight
1434 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1436 unsigned int mActiveCursor;
1437 unsigned int mCursorBlinkInterval;
1438 float mCursorBlinkDuration;
1439 float mCursorWidth; ///< The width of the cursors in pixels.
1440 HandleType mHandleScrolling; ///< The handle which is scrolling.
1441 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1442 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1443 float mScrollSpeed; ///< The scroll speed in pixels per second.
1444 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1445 int mTextDepth; ///< The depth used to render the text.
1447 bool mActiveCopyPastePopup : 1;
1448 bool mPopupSetNewPosition : 1;
1449 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1450 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1451 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1452 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1453 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1454 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1455 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1456 bool mHandlePanning : 1; ///< Whether any of the handles is moving.
1457 bool mHandleCurrentCrossed : 1; ///< Whether the handles are crossed.
1458 bool mHandlePreviousCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1459 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1462 DecoratorPtr Decorator::New( ControllerInterface& controller,
1463 TextSelectionPopupCallbackInterface& callbackInterface )
1465 return DecoratorPtr( new Decorator( controller,
1466 callbackInterface ) );
1469 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1471 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1474 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1476 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1479 void Decorator::Relayout( const Vector2& size )
1481 mImpl->Relayout( size );
1484 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1486 mImpl->UpdatePositions( scrollOffset );
1491 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1493 mImpl->mActiveCursor = activeCursor;
1496 unsigned int Decorator::GetActiveCursor() const
1498 return mImpl->mActiveCursor;
1501 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1503 mImpl->mCursor[cursor].position.x = x;
1504 mImpl->mCursor[cursor].position.y = y;
1505 mImpl->mCursor[cursor].cursorHeight = cursorHeight;
1506 mImpl->mCursor[cursor].lineHeight = lineHeight;
1509 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1511 x = mImpl->mCursor[cursor].position.x;
1512 y = mImpl->mCursor[cursor].position.y;
1513 cursorHeight = mImpl->mCursor[cursor].cursorHeight;
1514 lineHeight = mImpl->mCursor[cursor].lineHeight;
1517 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1519 return mImpl->mCursor[cursor].position;
1522 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1524 mImpl->mCursor[cursor].color = color;
1527 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1529 return mImpl->mCursor[cursor].color;
1532 void Decorator::StartCursorBlink()
1534 if ( !mImpl->mCursorBlinkTimer )
1536 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1537 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1540 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1542 mImpl->mCursorBlinkTimer.Start();
1546 void Decorator::StopCursorBlink()
1548 if ( mImpl->mCursorBlinkTimer )
1550 mImpl->mCursorBlinkTimer.Stop();
1553 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
1556 void Decorator::DelayCursorBlink()
1558 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
1559 mImpl->mDelayCursorBlink = true;
1562 void Decorator::SetCursorBlinkInterval( float seconds )
1564 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1567 float Decorator::GetCursorBlinkInterval() const
1569 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1572 void Decorator::SetCursorBlinkDuration( float seconds )
1574 mImpl->mCursorBlinkDuration = seconds;
1577 float Decorator::GetCursorBlinkDuration() const
1579 return mImpl->mCursorBlinkDuration;
1582 void Decorator::SetCursorWidth( int width )
1584 mImpl->mCursorWidth = static_cast<float>( width );
1587 int Decorator::GetCursorWidth() const
1589 return static_cast<int>( mImpl->mCursorWidth );
1594 void Decorator::SetHandleActive( HandleType handleType, bool active )
1596 mImpl->mHandle[handleType].active = active;
1600 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
1602 mImpl->mHandlePreviousCrossed = false;
1605 // TODO: this is a work-around.
1606 // The problem is the handle actor does not receive the touch event with the Interrupt
1607 // state when the power button is pressed and the application goes to background.
1608 mImpl->mHandle[handleType].pressed = false;
1609 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1610 ImageActor imageActor = mImpl->mHandle[handleType].actor;
1611 if( imageReleased && imageActor )
1613 imageActor.SetImage( imageReleased );
1619 bool Decorator::IsHandleActive( HandleType handleType ) const
1621 return mImpl->mHandle[handleType].active ;
1624 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1626 mImpl->SetHandleImage( handleType, handleImageType, image );
1629 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1631 return mImpl->mHandleImages[handleType][handleImageType];
1634 void Decorator::SetHandleColor( const Vector4& color )
1636 mImpl->mHandleColor = color;
1639 const Vector4& Decorator::GetHandleColor() const
1641 return mImpl->mHandleColor;
1644 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1646 // Adjust grab handle displacement
1647 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1649 handle.grabDisplacementX -= x - handle.position.x;
1650 handle.grabDisplacementY -= y - handle.position.y;
1652 handle.position.x = x;
1653 handle.position.y = y;
1654 handle.lineHeight = height;
1657 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
1659 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1661 x = handle.position.x;
1662 y = handle.position.y;
1663 height = handle.lineHeight;
1666 const Vector2& Decorator::GetPosition( HandleType handleType ) const
1668 return mImpl->mHandle[handleType].position;
1671 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
1673 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
1676 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
1678 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
1681 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
1683 mImpl->mFlipSelectionHandlesOnCross = enable;
1686 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
1688 mImpl->mHandleCurrentCrossed = indicesSwapped;
1689 mImpl->mFlipLeftSelectionHandleDirection = left;
1690 mImpl->mFlipRightSelectionHandleDirection = right;
1693 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1695 mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1698 void Decorator::ClearHighlights()
1700 mImpl->mHighlightQuadList.clear();
1701 mImpl->mHighlightPosition = Vector2::ZERO;
1704 void Decorator::SetHighlightColor( const Vector4& color )
1706 mImpl->mHighlightColor = color;
1709 const Vector4& Decorator::GetHighlightColor() const
1711 return mImpl->mHighlightColor;
1714 void Decorator::SetTextDepth( int textDepth )
1716 mImpl->mTextDepth = textDepth;
1719 void Decorator::SetPopupActive( bool active )
1721 mImpl->mActiveCopyPastePopup = active;
1724 bool Decorator::IsPopupActive() const
1726 return mImpl->mActiveCopyPastePopup ;
1729 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
1731 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
1733 if ( !mImpl->mCopyPastePopup.actor )
1735 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
1736 #ifdef DECORATOR_DEBUG
1737 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
1739 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
1740 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::PopupRelayoutComplete ); // Position popup after size negotiation
1743 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
1746 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
1748 return mImpl->mEnabledPopupButtons;
1753 void Decorator::SetScrollThreshold( float threshold )
1755 mImpl->SetScrollThreshold( threshold );
1758 float Decorator::GetScrollThreshold() const
1760 return mImpl->GetScrollThreshold();
1763 void Decorator::SetScrollSpeed( float speed )
1765 mImpl->SetScrollSpeed( speed );
1768 float Decorator::GetScrollSpeed() const
1770 return mImpl->GetScrollSpeed();
1773 void Decorator::NotifyEndOfScroll()
1775 mImpl->NotifyEndOfScroll();
1778 Decorator::~Decorator()
1783 Decorator::Decorator( ControllerInterface& controller,
1784 TextSelectionPopupCallbackInterface& callbackInterface )
1787 mImpl = new Decorator::Impl( controller, callbackInterface );
1792 } // namespace Toolkit