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 );
87 const Dali::Vector3 ACTIVE_LAYER_ANCHOR_POINT( 0.5f, 0.5f, 0.5f );
89 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.
91 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
93 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
94 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
95 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
97 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
98 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.
99 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
101 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
103 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
105 const float POPUP_PADDING = 2.f; ///< Padding space between the highlight box and the text's popup.
107 typedef Dali::Vector<Dali::Vector4> QuadContainer;
110 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
111 * @param[in] boundingRectangle local bounding
112 * @param[out] Vector4 World coordinate bounding Box.
114 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
116 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
117 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
119 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
120 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
122 boundingBox = Dali::Vector4( originX,
124 originX + boundingRectangle.width,
125 originY + boundingRectangle.height );
128 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
130 // Convert to local coordinates and store as a Dali::Rect.
131 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
133 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
134 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
135 boundingRectangle.width = boundingBox.z - boundingBox.x;
136 boundingRectangle.height = boundingBox.w - boundingBox.y;
139 } // end of namespace
150 struct Decorator::Impl : public ConnectionTracker
164 : color( Dali::Color::BLACK ),
166 cursorHeight( 0.0f ),
184 grabDisplacementX( 0.f ),
185 grabDisplacementY( 0.f ),
187 horizontallyVisible( false ),
188 verticallyVisible( false ),
190 verticallyFlippedPreferred( false ),
191 horizontallyFlipped( false ),
192 verticallyFlipped( false ),
193 verticallyFlippedOnTouch( false )
199 ImageView markerActor;
202 Vector2 globalPosition;
204 float lineHeight; ///< Not the handle height
205 float grabDisplacementX;
206 float grabDisplacementY;
208 bool horizontallyVisible : 1;
209 bool verticallyVisible : 1;
211 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
212 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
213 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
214 bool verticallyFlippedOnTouch : 1; ///< Whether the handle is vertically flipped on touch.
224 TextSelectionPopup actor;
228 Impl( ControllerInterface& controller,
229 TextSelectionPopupCallbackInterface& callbackInterface )
230 : mController( controller ),
231 mEnabledPopupButtons( TextSelectionPopup::NONE ),
232 mTextSelectionPopupCallbackInterface( callbackInterface ),
233 mHandleColor( HANDLE_COLOR ),
235 mHighlightColor( LIGHT_BLUE ),
236 mHighlightPosition( Vector2::ZERO ),
237 mActiveCursor( ACTIVE_CURSOR_NONE ),
238 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
239 mCursorBlinkDuration( 0.0f ),
240 mCursorWidth( CURSOR_WIDTH ),
241 mHandleScrolling( HANDLE_TYPE_COUNT ),
242 mHandleReleased( HANDLE_TYPE_COUNT ),
243 mScrollDirection( SCROLL_NONE ),
244 mScrollThreshold( SCROLL_THRESHOLD ),
245 mScrollSpeed( SCROLL_SPEED ),
246 mScrollDistance( SCROLL_DISTANCE ),
248 mActiveCopyPastePopup( false ),
249 mPopupSetNewPosition( true ),
250 mCursorBlinkStatus( true ),
251 mDelayCursorBlink( false ),
252 mPrimaryCursorVisible( false ),
253 mSecondaryCursorVisible( false ),
254 mFlipSelectionHandlesOnCross( false ),
255 mFlipLeftSelectionHandleDirection( false ),
256 mFlipRightSelectionHandleDirection( false ),
257 mIsHandlePanning( false ),
258 mIsHandleCurrentlyCrossed( false ),
259 mIsHandlePreviouslyCrossed( false ),
260 mNotifyEndOfScroll( false ),
261 mHorizontalScrollingEnabled( false ),
262 mVerticalScrollingEnabled( false ),
263 mSmoothHandlePanEnabled( false ),
264 mIsHighlightBoxActive( false )
266 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
267 mHighlightShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
272 * Relayout of the decorations owned by the decorator.
273 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
275 void Relayout( const Vector2& size )
279 // TODO - Remove this if nothing is active
282 // Show or hide the cursors
287 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
288 const float cursorBottom = cursor.position.y + cursor.cursorHeight;
290 mPrimaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
291 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
292 ( cursor.position.y < mControlSize.height ) &&
293 ( cursorBottom > -Math::MACHINE_EPSILON_1000 ) );
295 if( mPrimaryCursorVisible )
297 Size size( mCursorWidth, cursor.cursorHeight );
298 Vector2 position( cursor.position.x, cursor.position.y );
299 if( cursor.position.y < Math::MACHINE_EPSILON_1000 )
301 size.height += cursor.position.y;
304 if( cursorBottom > mControlSize.height )
306 size.height -= ( cursorBottom - mControlSize.height );
309 mPrimaryCursor.SetPosition( position.x, position.y );
311 mPrimaryCursor.SetSize( size );
313 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
316 if( mSecondaryCursor )
318 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
319 const float cursorBottom = cursor.position.y + cursor.cursorHeight;
321 mSecondaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
322 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
323 ( cursor.position.y < mControlSize.height ) &&
324 ( cursorBottom > -Math::MACHINE_EPSILON_1000 ) );
326 if( mSecondaryCursorVisible )
328 Size size( mCursorWidth, cursor.cursorHeight );
329 Vector2 position( cursor.position.x, cursor.position.y );
330 if( cursor.position.y < Math::MACHINE_EPSILON_1000 )
332 size.height += cursor.position.y;
335 if( cursorBottom > mControlSize.height )
337 size.height -= ( cursorBottom - mControlSize.height );
340 mSecondaryCursor.SetPosition( position.x, position.y );
341 mSecondaryCursor.SetSize( size );
343 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
346 // Show or hide the grab handle
347 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
348 bool newGrabHandlePosition = false;
349 grabHandle.horizontallyVisible = false;
350 grabHandle.verticallyVisible = false;
351 if( grabHandle.active )
353 grabHandle.horizontallyVisible = ( ( mControlSize.width - ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) ) > -Math::MACHINE_EPSILON_1000 ) &&
354 ( grabHandle.position.x > -Math::MACHINE_EPSILON_1000 ) );
355 grabHandle.verticallyVisible = ( ( ( mControlSize.height - grabHandle.lineHeight ) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) &&
356 ( grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) );
358 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible;
363 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
364 SetGrabHandlePosition();
366 // Sets the grab handle image according if it's pressed, flipped, etc.
367 SetHandleImage( GRAB_HANDLE );
369 newGrabHandlePosition = true;
372 if( grabHandle.actor )
374 grabHandle.actor.SetVisible( isVisible );
377 else if( grabHandle.actor )
379 grabHandle.actor.Unparent();
382 // Show or hide the selection handles/highlight
383 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
384 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
385 bool newPrimaryHandlePosition = false;
386 bool newSecondaryHandlePosition = false;
388 primary.horizontallyVisible = ( ( mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
389 ( primary.position.x > -Math::MACHINE_EPSILON_1000 ) );
390 primary.verticallyVisible = ( ( ( mControlSize.height - primary.lineHeight ) - primary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
391 ( primary.position.y + ( primary.verticallyFlipped ? 0.f : primary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
392 secondary.horizontallyVisible = ( ( mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
393 ( secondary.position.x > -Math::MACHINE_EPSILON_1000 ) );
394 secondary.verticallyVisible = ( ( ( mControlSize.height - secondary.lineHeight ) - secondary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
395 ( secondary.position.y + ( secondary.verticallyFlipped ? 0.f : secondary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
397 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
398 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
400 if( primary.active || secondary.active )
402 if( primaryVisible || secondaryVisible )
404 CreateSelectionHandles();
408 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
410 // Sets the primary handle image according if it's pressed, flipped, etc.
411 SetHandleImage( LEFT_SELECTION_HANDLE );
413 SetSelectionHandleMarkerSize( primary );
415 newPrimaryHandlePosition = true;
418 if( secondaryVisible )
420 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
422 // Sets the secondary handle image according if it's pressed, flipped, etc.
423 SetHandleImage( RIGHT_SELECTION_HANDLE );
425 SetSelectionHandleMarkerSize( secondary );
427 newSecondaryHandlePosition = true;
433 primary.actor.SetVisible( primaryVisible );
435 if( secondary.actor )
437 secondary.actor.SetVisible( secondaryVisible );
445 primary.actor.Unparent();
447 if( secondary.actor )
449 secondary.actor.Unparent();
453 if( mIsHighlightBoxActive )
460 if( mHighlightActor )
462 mHighlightActor.Unparent();
466 if( newGrabHandlePosition ||
467 newPrimaryHandlePosition ||
468 newSecondaryHandlePosition )
470 // Setup property notifications to find whether the handles leave the boundaries of the current display.
471 SetupActiveLayerPropertyNotifications();
474 if( mActiveCopyPastePopup &&
475 ( primaryVisible || secondaryVisible ) )
478 mPopupSetNewPosition = true;
482 if( mCopyPastePopup.actor )
484 mCopyPastePopup.actor.HidePopup();
485 mPopupSetNewPosition = true;
490 void UpdatePositions( const Vector2& scrollOffset )
492 mCursor[PRIMARY_CURSOR].position += scrollOffset;
493 mCursor[SECONDARY_CURSOR].position += scrollOffset;
494 mHandle[ GRAB_HANDLE ].position += scrollOffset;
495 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
496 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
497 mHighlightPosition += scrollOffset;
502 if( !mCopyPastePopup.actor )
507 if( !mCopyPastePopup.actor.GetParent() )
509 mActiveLayer.Add( mCopyPastePopup.actor );
512 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
513 mCopyPastePopup.actor.ShowPopup();
516 float CalculateVerticalPopUpPosition( float halfHeight, bool preferBelow )
518 float yPosition = 0.f;
520 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
521 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
522 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
524 if( primaryHandle.active || secondaryHandle.active )
526 // The origin of the decorator's coordinate system in world coords.
527 const Vector3 originWorldCoords = mActiveLayer.GetCurrentWorldPosition() - mActiveLayer.GetCurrentSize() * ACTIVE_LAYER_ANCHOR_POINT;
531 // Find out if there is enough space for the popup at the bottom.
532 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
533 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
535 float maxY = std::max( primaryBelowY, secondaryBelowY );
537 yPosition = halfHeight + maxY;
539 if( originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w )
541 // Does not fit below.
543 // Try to fit first below the non active handle. Otherwise above the active handle.
544 if( RIGHT_SELECTION_HANDLE == mHandleReleased )
546 if( primaryBelowY < secondaryBelowY )
548 yPosition = halfHeight + primaryBelowY;
552 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
555 else if( LEFT_SELECTION_HANDLE == mHandleReleased )
557 if( secondaryBelowY < primaryBelowY )
559 yPosition = halfHeight + secondaryBelowY;
563 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
567 // Check the handle is whithin the decoration box.
568 if( originWorldCoords.y + yPosition < mBoundingBox.y )
570 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
573 if( originWorldCoords.y + yPosition > mBoundingBox.w )
575 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
581 // Find out if there is enough space for the popup at the top.
582 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
583 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
585 float minY = std::min( primaryTopY, secondaryTopY );
587 yPosition = -halfHeight + minY;
589 } // ( primaryHandle.active || secondaryHandle.active )
590 else if( grabHandle.active )
594 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
598 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
605 void ConstrainPopupPosition( const Vector3& popupHalfSize )
607 // Check if the popup is within the boundaries of the decoration box.
609 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
611 // The origin of the decorator's coordinate system in world coords.
612 const Vector3 originWorldCoords = mActiveLayer.GetCurrentWorldPosition() - mActiveLayer.GetCurrentSize() * ACTIVE_LAYER_ANCHOR_POINT;
614 // The popup's position in world coords.
615 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
617 if( popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x )
619 mCopyPastePopup.position.x += mBoundingBox.x - ( popupPositionWorldCoords.x - popupHalfSize.width );
621 else if( popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z )
623 mCopyPastePopup.position.x += mBoundingBox.z - ( popupPositionWorldCoords.x + popupHalfSize.width );
626 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
627 if( popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y )
629 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition( popupHalfSize.height, true ); // true -> prefer to set the popup's position below.
633 void SetPopupPosition( Actor actor )
635 if( !mActiveCopyPastePopup )
640 // Retrieves the popup's size after relayout.
641 const Vector3 popupSize( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
642 const Vector3 popupHalfSize = popupSize * 0.5f;
644 if( mPopupSetNewPosition )
646 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
647 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
648 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
650 if( primaryHandle.active || secondaryHandle.active )
652 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
653 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
655 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
657 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - ( primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING );
658 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - ( secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING );
660 mCopyPastePopup.position.y = std::min( primaryY, secondaryY );
662 else if( grabHandle.active )
664 mCopyPastePopup.position.x = grabHandle.position.x;
666 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - ( grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING );
668 } // mPopupSetNewPosition
670 // It may change the popup's position to fit within the decoration box.
671 ConstrainPopupPosition( popupHalfSize );
673 SetUpPopupPositionNotifications( popupHalfSize );
675 // Prevent pixel mis-alignment by rounding down.
676 mCopyPastePopup.position.x = floorf( mCopyPastePopup.position.x );
677 mCopyPastePopup.position.y = floorf( mCopyPastePopup.position.y );
679 mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
680 mPopupSetNewPosition = false;
683 void CreateCursor( Control& cursor, const Vector4& color )
685 cursor = Control::New();
686 cursor.SetBackgroundColor( color );
687 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT );
688 cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
691 // Add or Remove cursor(s) from parent
694 if( mActiveCursor == ACTIVE_CURSOR_NONE )
698 mPrimaryCursor.Unparent();
700 if( mSecondaryCursor )
702 mSecondaryCursor.Unparent();
707 // Create Primary and or Secondary Cursor(s) if active and add to parent
708 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
709 mActiveCursor == ACTIVE_CURSOR_BOTH )
711 if ( !mPrimaryCursor )
713 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
714 #ifdef DECORATOR_DEBUG
715 mPrimaryCursor.SetName( "PrimaryCursorActor" );
719 if( !mPrimaryCursor.GetParent() )
721 mActiveLayer.Add( mPrimaryCursor );
725 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
727 if ( !mSecondaryCursor )
729 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
730 #ifdef DECORATOR_DEBUG
731 mSecondaryCursor.SetName( "SecondaryCursorActor" );
735 if( !mSecondaryCursor.GetParent() )
737 mActiveLayer.Add( mSecondaryCursor );
742 if( mSecondaryCursor )
744 mSecondaryCursor.Unparent();
750 bool OnCursorBlinkTimerTick()
752 if( !mDelayCursorBlink )
755 if ( mPrimaryCursor )
757 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
759 if ( mSecondaryCursor )
761 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
764 mCursorBlinkStatus = !mCursorBlinkStatus;
769 mDelayCursorBlink = false;
777 // Will consume tap gestures on handles.
778 mTapDetector = TapGestureDetector::New();
780 // Will consume double tap gestures on handles.
781 mTapDetector.SetMaximumTapsRequired( 2u );
783 // Will consume long press gestures on handles.
784 mLongPressDetector = LongPressGestureDetector::New();
786 // Detects pan gestures on handles.
787 mPanDetector = PanGestureDetector::New();
788 mPanDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
791 void CreateActiveLayer()
795 mActiveLayer = Layer::New();
796 #ifdef DECORATOR_DEBUG
797 mActiveLayer.SetName ( "ActiveLayerActor" );
800 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
801 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
803 // Add the active layer telling the controller it doesn't need clipping.
804 mController.AddDecoration( mActiveLayer, false );
807 mActiveLayer.RaiseToTop();
810 void SetSelectionHandleMarkerSize( HandleImpl& handle )
812 if( handle.markerActor )
814 handle.markerActor.SetSize( 0, handle.lineHeight );
818 void CreateGrabHandle()
820 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
821 if( !grabHandle.actor )
823 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
825 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
826 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
827 grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
829 // Area that Grab handle responds to, larger than actual handle so easier to move
830 #ifdef DECORATOR_DEBUG
831 grabHandle.actor.SetName( "GrabHandleActor" );
832 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
834 grabHandle.grabArea = Control::New();
835 Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
836 control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
837 grabHandle.grabArea.SetName( "GrabArea" );
841 grabHandle.grabArea = Actor::New();
842 grabHandle.grabArea.SetName( "GrabArea" );
845 grabHandle.grabArea = Actor::New();
848 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
849 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
850 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
851 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
852 grabHandle.actor.Add( grabHandle.grabArea );
853 grabHandle.actor.SetColor( mHandleColor );
855 grabHandle.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
857 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
858 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
859 mTapDetector.Attach( grabHandle.actor );
860 mLongPressDetector.Attach( grabHandle.actor );
862 // The grab handle's area is attached to the pan detector.
863 // The OnPan() method is connected to the signals emitted by the pan detector.
864 mPanDetector.Attach( grabHandle.grabArea );
866 mActiveLayer.Add( grabHandle.actor );
870 if( grabHandle.actor && !grabHandle.actor.GetParent() )
872 mActiveLayer.Add( grabHandle.actor );
876 void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
880 handle.markerActor = ImageView::New( image );
881 handle.markerActor.SetColor( mHandleColor );
882 handle.actor.Add( handle.markerActor );
884 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
886 if( LEFT_SELECTION_HANDLE == handleType )
888 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
889 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
891 else if( RIGHT_SELECTION_HANDLE == handleType )
893 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
894 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
899 void CreateSelectionHandles()
901 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
904 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
906 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
907 #ifdef DECORATOR_DEBUG
908 primary.actor.SetName("SelectionHandleOne");
910 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
911 GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
912 primary.actor.SetColor( mHandleColor );
914 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
915 #ifdef DECORATOR_DEBUG
916 primary.grabArea.SetName("SelectionHandleOneGrabArea");
918 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
919 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
920 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
921 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
923 primary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
925 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
926 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
927 mTapDetector.Attach( primary.actor );
928 mLongPressDetector.Attach( primary.actor );
930 // The handle's area is attached to the pan detector.
931 // The OnPan() method is connected to the signals emitted by the pan detector.
932 mPanDetector.Attach( primary.grabArea );
934 primary.actor.Add( primary.grabArea );
936 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
940 if( primary.actor && !primary.actor.GetParent() )
942 mActiveLayer.Add( primary.actor );
945 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
946 if( !secondary.actor )
948 if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
950 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
951 #ifdef DECORATOR_DEBUG
952 secondary.actor.SetName("SelectionHandleTwo");
954 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
955 GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
956 secondary.actor.SetColor( mHandleColor );
958 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
959 #ifdef DECORATOR_DEBUG
960 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
962 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
963 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
964 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
965 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
967 secondary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
969 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
970 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
971 mTapDetector.Attach( secondary.actor );
972 mLongPressDetector.Attach( secondary.actor );
974 // The handle's area is attached to the pan detector.
975 // The OnPan() method is connected to the signals emitted by the pan detector.
976 mPanDetector.Attach( secondary.grabArea );
978 secondary.actor.Add( secondary.grabArea );
980 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
984 if( secondary.actor && !secondary.actor.GetParent() )
986 mActiveLayer.Add( secondary.actor );
990 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
992 // Gets the world position of the active layer. The active layer is where the handles are added.
993 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
995 // The grab handle position in world coords.
996 // The active layer's world position is the center of the active layer. The origin of the
997 // coord system of the handles is the top left of the active layer.
998 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f );
999 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f );
1002 void SetGrabHandlePosition()
1004 // Reference to the grab handle.
1005 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1007 // Transforms the handle position into world coordinates.
1008 // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
1009 // as it's transforming the handle's position set by the text-controller and not
1010 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
1011 // retrieves the position of the center of the actor but the handle's position set
1012 // by the text controller is not the center of the actor.
1013 Vector2 grabHandleWorldPosition;
1014 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
1016 // Check if the grab handle exceeds the boundaries of the decoration box.
1017 // At the moment only the height is checked for the grab handle.
1019 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
1020 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
1021 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
1023 // The grab handle 'y' position in local coords.
1024 // If the grab handle exceeds the bottom of the decoration box,
1025 // set the 'y' position to the top of the line.
1026 // The SetGrabHandleImage() method will change the orientation.
1027 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
1029 if( grabHandle.actor )
1031 grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ) + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f ),
1032 yLocalPosition + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f ) );
1036 void SetSelectionHandlePosition( HandleType type )
1038 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
1040 // Reference to the selection handle.
1041 HandleImpl& handle = mHandle[type];
1043 // Transforms the handle position into world coordinates.
1044 // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
1045 // as it's transforming the handle's position set by the text-controller and not
1046 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
1047 // retrieves the position of the center of the actor but the handle's position set
1048 // by the text controller is not the center of the actor.
1049 Vector2 handleWorldPosition;
1050 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
1052 // Whether to flip the handle (horizontally).
1053 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1055 // Whether to flip the handles if they are crossed.
1056 bool crossFlip = false;
1057 if( mFlipSelectionHandlesOnCross || !mIsHandlePanning )
1059 crossFlip = mIsHandleCurrentlyCrossed;
1062 // Whether the handle was crossed before start the panning.
1063 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1065 // Does not flip if both conditions are true (double flip)
1066 flipHandle = flipHandle != ( crossFlip || isHandlePreviouslyCrossed );
1068 // Will flip the handles vertically if the user prefers it.
1069 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1071 if( crossFlip || isHandlePreviouslyCrossed )
1073 if( isPrimaryHandle )
1075 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1079 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1083 // Check if the selection handle exceeds the boundaries of the decoration box.
1084 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
1085 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
1087 // Does not flip if both conditions are true (double flip)
1088 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
1092 if( handle.actor && !handle.horizontallyFlipped )
1094 // Change the anchor point to flip the image.
1095 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
1097 handle.horizontallyFlipped = true;
1102 if( handle.actor && handle.horizontallyFlipped )
1104 // Reset the anchor point.
1105 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
1107 handle.horizontallyFlipped = false;
1111 // Whether to flip the handle vertically.
1112 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
1113 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
1114 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
1116 // The primary selection handle 'y' position in local coords.
1117 // If the handle exceeds the bottom of the decoration box,
1118 // set the 'y' position to the top of the line.
1119 // The SetHandleImage() method will change the orientation.
1120 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1124 handle.actor.SetPosition( handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f ),
1125 yLocalPosition + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f ) );
1129 void SetHandleImage( HandleType type )
1131 HandleImpl& handle = mHandle[type];
1133 HandleType markerType = HANDLE_TYPE_COUNT;
1134 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1135 if( LEFT_SELECTION_HANDLE == type )
1137 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1138 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1140 else if( RIGHT_SELECTION_HANDLE == type )
1142 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1143 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1146 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1149 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1151 handle.actor.SetImage( mHandleImages[type][imageType] );
1154 if( HANDLE_TYPE_COUNT != markerType )
1156 if( handle.markerActor )
1158 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1159 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
1163 // Whether to flip the handle vertically.
1166 handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
1170 void CreateHighlight()
1172 if( !mHighlightActor )
1174 mHighlightActor = Actor::New();
1176 #ifdef DECORATOR_DEBUG
1177 mHighlightActor.SetName( "HighlightActor" );
1179 mHighlightActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
1180 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
1181 mHighlightActor.SetColor( mHighlightColor );
1182 mHighlightActor.SetColorMode( USE_OWN_COLOR );
1185 // Add the highlight box telling the controller it needs clipping.
1186 mController.AddDecoration( mHighlightActor, true );
1189 void UpdateHighlight()
1191 if ( mHighlightActor )
1193 // Sets the position of the highlight actor inside the decorator.
1194 mHighlightActor.SetPosition( mHighlightPosition.x,
1195 mHighlightPosition.y, -0.0001f );
1197 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1198 if( 0u != numberOfQuads )
1200 // Set the size of the highlighted text to the actor.
1201 mHighlightActor.SetSize( mHighlightSize );
1203 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1204 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1205 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1207 Vector<Vector2> vertices;
1208 Vector<unsigned short> indices;
1210 vertices.Reserve( 4u * numberOfQuads );
1211 indices.Reserve( 6u * numberOfQuads );
1213 // Index to the vertex.
1214 unsigned int v = 0u;
1216 // Traverse all quads.
1217 for( Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1218 endIt = mHighlightQuadList.End();
1222 const Vector4& quad = *it;
1227 vertex.x = quad.x - offsetX;
1228 vertex.y = quad.y - offsetY;
1229 vertices.PushBack( vertex );
1232 vertex.x = quad.z - offsetX;
1233 vertex.y = quad.y - offsetY;
1234 vertices.PushBack( vertex );
1236 // bottom-left (v+2)
1237 vertex.x = quad.x - offsetX;
1238 vertex.y = quad.w - offsetY;
1239 vertices.PushBack( vertex );
1241 // bottom-right (v+3)
1242 vertex.x = quad.z - offsetX;
1243 vertex.y = quad.w - offsetY;
1244 vertices.PushBack( vertex );
1246 // triangle A (3, 1, 0)
1247 indices.PushBack( v + 3 );
1248 indices.PushBack( v + 1 );
1249 indices.PushBack( v );
1251 // triangle B (0, 2, 3)
1252 indices.PushBack( v );
1253 indices.PushBack( v + 2 );
1254 indices.PushBack( v + 3 );
1257 if( ! mQuadVertices )
1259 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
1262 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1264 if( !mQuadGeometry )
1266 mQuadGeometry = Geometry::New();
1267 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1269 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1271 if( !mHighlightRenderer )
1273 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1274 mHighlightActor.AddRenderer( mHighlightRenderer );
1278 mHighlightQuadList.Clear();
1280 if( mHighlightRenderer )
1282 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1287 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1289 if( Gesture::Started == gesture.state )
1291 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1293 handle.globalPosition.x = handle.position.x;
1294 handle.globalPosition.y = handle.position.y;
1297 handle.grabDisplacementX += gesture.displacement.x;
1298 handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.displacement.y );
1300 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1301 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1302 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f );
1304 if( ( Gesture::Started == gesture.state ) ||
1305 ( Gesture::Continuing == gesture.state ) )
1308 mController.GetTargetSize( targetSize );
1310 if( mHorizontalScrollingEnabled &&
1311 ( x < mScrollThreshold ) )
1313 mScrollDirection = SCROLL_RIGHT;
1314 mHandleScrolling = type;
1317 else if( mHorizontalScrollingEnabled &&
1318 ( x > targetSize.width - mScrollThreshold ) )
1320 mScrollDirection = SCROLL_LEFT;
1321 mHandleScrolling = type;
1324 else if( mVerticalScrollingEnabled &&
1325 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1327 mScrollDirection = SCROLL_TOP;
1328 mHandleScrolling = type;
1331 else if( mVerticalScrollingEnabled &&
1332 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1334 mScrollDirection = SCROLL_BOTTOM;
1335 mHandleScrolling = type;
1340 mHandleScrolling = HANDLE_TYPE_COUNT;
1342 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1345 mIsHandlePanning = true;
1347 else if( ( Gesture::Finished == gesture.state ) ||
1348 ( Gesture::Cancelled == gesture.state ) )
1351 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1353 mNotifyEndOfScroll = false;
1354 mHandleScrolling = HANDLE_TYPE_COUNT;
1356 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1360 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1365 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1367 handle.pressed = false;
1369 mIsHandlePanning = false;
1373 void OnPan( Actor actor, const PanGesture& gesture )
1375 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1376 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1377 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1379 if( actor == grabHandle.grabArea )
1381 DoPan( grabHandle, GRAB_HANDLE, gesture );
1383 else if( actor == primarySelectionHandle.grabArea )
1385 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1387 else if( actor == secondarySelectionHandle.grabArea )
1389 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1393 bool OnGrabHandleTouched( Actor actor, const TouchData& touch )
1395 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1397 // Switch between pressed/release grab-handle images
1398 if( touch.GetPointCount() > 0 &&
1401 const PointState::Type state = touch.GetState( 0 );
1403 if( PointState::DOWN == state )
1405 grabHandle.pressed = true;
1407 else if( ( PointState::UP == state ) ||
1408 ( PointState::INTERRUPTED == state ) )
1410 grabHandle.pressed = false;
1413 SetHandleImage( GRAB_HANDLE );
1416 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1420 bool OnHandleOneTouched( Actor actor, const TouchData& touch )
1422 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1424 // Switch between pressed/release selection handle images
1425 if( touch.GetPointCount() > 0 &&
1426 primarySelectionHandle.actor )
1428 const PointState::Type state = touch.GetState( 0 );
1430 if( PointState::DOWN == state )
1432 primarySelectionHandle.pressed = true;
1433 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1435 else if( ( PointState::UP == state ) ||
1436 ( PointState::INTERRUPTED == state ) )
1438 primarySelectionHandle.pressed = false;
1439 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1440 mIsHandlePanning = false;
1441 mHandleReleased = LEFT_SELECTION_HANDLE;
1444 SetHandleImage( LEFT_SELECTION_HANDLE );
1447 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1451 bool OnHandleTwoTouched( Actor actor, const TouchData& touch )
1453 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1455 // Switch between pressed/release selection handle images
1456 if( touch.GetPointCount() > 0 &&
1457 secondarySelectionHandle.actor )
1459 const PointState::Type state = touch.GetState( 0 );
1461 if( PointState::DOWN == state )
1463 secondarySelectionHandle.pressed = true;
1464 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1466 else if( ( PointState::UP == state ) ||
1467 ( PointState::INTERRUPTED == state ) )
1469 secondarySelectionHandle.pressed = false;
1470 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1471 mIsHandlePanning = false;
1472 mHandleReleased = RIGHT_SELECTION_HANDLE;
1475 SetHandleImage( RIGHT_SELECTION_HANDLE );
1478 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1482 void HandleResetPosition( PropertyNotification& source )
1484 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1486 if( grabHandle.active )
1488 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1489 SetGrabHandlePosition();
1491 // Sets the grab handle image according if it's pressed, flipped, etc.
1492 SetHandleImage( GRAB_HANDLE );
1496 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1497 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1499 // Sets the primary handle image according if it's pressed, flipped, etc.
1500 SetHandleImage( LEFT_SELECTION_HANDLE );
1502 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1503 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1505 // Sets the secondary handle image according if it's pressed, flipped, etc.
1506 SetHandleImage( RIGHT_SELECTION_HANDLE );
1510 void SetupActiveLayerPropertyNotifications()
1517 // Vertical notifications.
1519 // Disconnect any previous connected callback.
1520 if( mHandleVerticalLessThanNotification )
1522 mHandleVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1523 mActiveLayer.RemovePropertyNotification( mHandleVerticalLessThanNotification );
1526 if( mHandleVerticalGreaterThanNotification )
1528 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1529 mActiveLayer.RemovePropertyNotification( mHandleVerticalGreaterThanNotification );
1532 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1533 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1534 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1536 if( grabHandle.active )
1538 if( grabHandle.verticallyFlipped )
1540 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1541 mHandleVerticalGreaterThanNotification.Reset();
1543 // The vertical distance from the center of the active layer to the top edje of the display.
1544 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1546 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1547 LessThanCondition( mBoundingBox.y + topHeight ) );
1549 // Notifies the change from false to true and from true to false.
1550 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1552 // Connects the signals with the callbacks.
1553 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1557 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1558 mHandleVerticalLessThanNotification.Reset();
1560 // The vertical distance from the center of the active layer to the bottom edje of the display.
1561 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1563 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1564 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1566 // Notifies the change from false to true and from true to false.
1567 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1569 // Connects the signals with the callbacks.
1570 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1573 else // The selection handles are active
1575 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1577 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1578 mHandleVerticalGreaterThanNotification.Reset();
1580 // The vertical distance from the center of the active layer to the top edje of the display.
1581 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1583 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1584 LessThanCondition( mBoundingBox.y + topHeight ) );
1586 // Notifies the change from false to true and from true to false.
1587 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1589 // Connects the signals with the callbacks.
1590 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1592 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1594 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1595 mHandleVerticalLessThanNotification.Reset();
1597 // The vertical distance from the center of the active layer to the bottom edje of the display.
1598 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1599 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1601 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1602 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1604 // Notifies the change from false to true and from true to false.
1605 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1607 // Connects the signals with the callbacks.
1608 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1612 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1614 // The vertical distance from the center of the active layer to the top edje of the display.
1615 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1616 -primaryHandle.position.y + primaryHandle.size.height :
1617 -secondaryHandle.position.y + secondaryHandle.size.height );
1619 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1620 LessThanCondition( mBoundingBox.y + topHeight ) );
1622 // Notifies the change from false to true and from true to false.
1623 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1625 // Connects the signals with the callbacks.
1626 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1628 // The vertical distance from the center of the active layer to the bottom edje of the display.
1629 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1630 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1631 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1633 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1634 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1636 // Notifies the change from false to true and from true to false.
1637 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1639 // Connects the signals with the callbacks.
1640 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1644 // Horizontal notifications.
1646 // Disconnect any previous connected callback.
1647 if( mHandleHorizontalLessThanNotification )
1649 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1650 mActiveLayer.RemovePropertyNotification( mHandleHorizontalLessThanNotification );
1653 if( mHandleHorizontalGreaterThanNotification )
1655 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1656 mActiveLayer.RemovePropertyNotification( mHandleHorizontalGreaterThanNotification );
1659 if( primaryHandle.active || secondaryHandle.active )
1661 // The horizontal distance from the center of the active layer to the left edje of the display.
1662 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1663 -secondaryHandle.position.x + secondaryHandle.size.width );
1665 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1666 LessThanCondition( mBoundingBox.x + leftWidth ) );
1668 // Notifies the change from false to true and from true to false.
1669 mHandleHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1671 // Connects the signals with the callbacks.
1672 mHandleHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1674 // The horizontal distance from the center of the active layer to the right edje of the display.
1675 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1676 secondaryHandle.position.x + secondaryHandle.size.width );
1678 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1679 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1681 // Notifies the change from false to true and from true to false.
1682 mHandleHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1684 // Connects the signals with the callbacks.
1685 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1691 float AlternatePopUpPositionRelativeToCursor( bool topBottom )
1693 float alternativePosition = 0.0f;
1695 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1697 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1698 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1699 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1700 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1702 if( primaryHandle.active || secondaryHandle.active )
1704 float handleY = 0.f;
1705 float maxHandleHeight = 0.f;
1707 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1708 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1710 if( primaryVisible && secondaryVisible )
1712 handleY = std::max( primaryHandle.position.y, secondaryHandle.position.y );
1713 maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1715 else if( primaryVisible && !secondaryVisible )
1717 handleY = primaryHandle.position.y;
1718 maxHandleHeight = primaryHandle.size.height;
1720 else if( !primaryVisible && secondaryVisible )
1722 handleY = secondaryHandle.position.y;
1723 maxHandleHeight = secondaryHandle.size.height;
1726 alternativePosition = handleY + ( topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight );
1730 alternativePosition = cursor.position.y + ( topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height );
1733 return alternativePosition;
1736 void PopUpLeavesTopBoundary( PropertyNotification& source )
1738 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1740 // Sets the position of the popup below.
1741 mCopyPastePopup.actor.SetY( floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, true ) ) );
1744 void PopUpLeavesBottomBoundary( PropertyNotification& source )
1746 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1748 // Sets the position of the popup above.
1749 mCopyPastePopup.actor.SetY( floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, false ) ) );
1752 void SetUpPopupPositionNotifications( const Vector3& popupHalfSize )
1754 // Disconnect any previous connected callback.
1755 if( mPopupTopExceedNotification )
1757 mPopupTopExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1758 mCopyPastePopup.actor.RemovePropertyNotification( mPopupTopExceedNotification );
1761 if( mPopupBottomExceedNotification )
1763 mPopupBottomExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1764 mCopyPastePopup.actor.RemovePropertyNotification( mPopupBottomExceedNotification );
1767 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1769 // Exceeding vertical boundary
1771 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1772 LessThanCondition( mBoundingBox.y + popupHalfSize.height ) );
1774 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1775 GreaterThanCondition( mBoundingBox.w - popupHalfSize.height ) );
1777 // Notifies the change from false to true and from true to false.
1778 mPopupTopExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1779 mPopupBottomExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1781 mPopupTopExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1782 mPopupBottomExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1785 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1787 HandleImpl& handle = mHandle[handleType];
1788 handle.size = Size( image.GetWidth(), image.GetHeight() );
1790 mHandleImages[handleType][handleImageType] = image;
1793 void SetScrollThreshold( float threshold )
1795 mScrollThreshold = threshold;
1798 float GetScrollThreshold() const
1800 return mScrollThreshold;
1803 void SetScrollSpeed( float speed )
1805 mScrollSpeed = speed;
1806 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1809 float GetScrollSpeed() const
1811 return mScrollSpeed;
1814 void NotifyEndOfScroll()
1820 mNotifyEndOfScroll = true;
1825 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1827 * It only starts the timer if it's already created.
1829 void StartScrollTimer()
1833 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1834 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1837 if( !mScrollTimer.IsRunning() )
1839 mScrollTimer.Start();
1844 * Stops the timer used to scroll the text.
1846 void StopScrollTimer()
1850 mScrollTimer.Stop();
1855 * Callback called by the timer used to scroll the text.
1857 * It calculates and sets a new scroll position.
1859 bool OnScrollTimerTick()
1861 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1866 switch( mScrollDirection )
1870 x = mScrollDistance;
1875 x = -mScrollDistance;
1880 y = mScrollDistance;
1885 y = -mScrollDistance;
1892 mController.DecorationEvent( mHandleScrolling,
1901 ControllerInterface& mController;
1903 TapGestureDetector mTapDetector;
1904 PanGestureDetector mPanDetector;
1905 LongPressGestureDetector mLongPressDetector;
1907 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1908 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1910 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1911 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1912 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1913 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1914 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1915 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1916 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1917 Control mPrimaryCursor;
1918 Control mSecondaryCursor;
1920 Actor mHighlightActor; ///< Actor to display highlight
1921 Renderer mHighlightRenderer;
1922 Shader mHighlightShader; ///< Shader used for highlight
1923 Property::Map mQuadVertexFormat;
1924 PopupImpl mCopyPastePopup;
1925 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1926 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1928 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1929 Vector4 mHandleColor;
1931 CursorImpl mCursor[CURSOR_COUNT];
1932 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1934 PropertyBuffer mQuadVertices;
1935 Geometry mQuadGeometry;
1936 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1938 Vector4 mBoundingBox; ///< The bounding box in world coords.
1939 Vector4 mHighlightColor; ///< Color of the highlight
1940 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1941 Size mHighlightSize; ///< The size of the highlighted text.
1942 Size mControlSize; ///< The control's size. Set by the Relayout.
1944 unsigned int mActiveCursor;
1945 unsigned int mCursorBlinkInterval;
1946 float mCursorBlinkDuration;
1947 float mCursorWidth; ///< The width of the cursors in pixels.
1948 HandleType mHandleScrolling; ///< The handle which is scrolling.
1949 HandleType mHandleReleased; ///< The last handle released.
1950 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1951 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1952 float mScrollSpeed; ///< The scroll speed in pixels per second.
1953 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1954 int mTextDepth; ///< The depth used to render the text.
1956 bool mActiveCopyPastePopup : 1;
1957 bool mPopupSetNewPosition : 1;
1958 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1959 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1960 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1961 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1962 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1963 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1964 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1965 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1966 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1967 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1968 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1969 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1970 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1971 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1972 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1975 DecoratorPtr Decorator::New( ControllerInterface& controller,
1976 TextSelectionPopupCallbackInterface& callbackInterface )
1978 return DecoratorPtr( new Decorator( controller,
1979 callbackInterface ) );
1982 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1984 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1987 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1989 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1992 void Decorator::Relayout( const Vector2& size )
1994 mImpl->Relayout( size );
1997 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1999 mImpl->UpdatePositions( scrollOffset );
2004 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
2006 mImpl->mActiveCursor = activeCursor;
2009 unsigned int Decorator::GetActiveCursor() const
2011 return mImpl->mActiveCursor;
2014 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
2016 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2018 cursorImpl.position.x = x;
2019 cursorImpl.position.y = y;
2020 cursorImpl.cursorHeight = cursorHeight;
2021 cursorImpl.lineHeight = lineHeight;
2024 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
2026 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2028 x = cursorImpl.position.x;
2029 y = cursorImpl.position.y;
2030 cursorHeight = cursorImpl.cursorHeight;
2031 lineHeight = cursorImpl.lineHeight;
2034 const Vector2& Decorator::GetPosition( Cursor cursor ) const
2036 return mImpl->mCursor[cursor].position;
2039 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
2041 mImpl->mCursor[cursor].color = color;
2044 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
2046 return mImpl->mCursor[cursor].color;
2049 void Decorator::StartCursorBlink()
2051 if ( !mImpl->mCursorBlinkTimer )
2053 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
2054 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
2057 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
2059 mImpl->mCursorBlinkTimer.Start();
2063 void Decorator::StopCursorBlink()
2065 if ( mImpl->mCursorBlinkTimer )
2067 mImpl->mCursorBlinkTimer.Stop();
2070 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2073 void Decorator::DelayCursorBlink()
2075 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2076 mImpl->mDelayCursorBlink = true;
2079 void Decorator::SetCursorBlinkInterval( float seconds )
2081 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
2084 float Decorator::GetCursorBlinkInterval() const
2086 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
2089 void Decorator::SetCursorBlinkDuration( float seconds )
2091 mImpl->mCursorBlinkDuration = seconds;
2094 float Decorator::GetCursorBlinkDuration() const
2096 return mImpl->mCursorBlinkDuration;
2099 void Decorator::SetCursorWidth( int width )
2101 mImpl->mCursorWidth = static_cast<float>( width );
2104 int Decorator::GetCursorWidth() const
2106 return static_cast<int>( mImpl->mCursorWidth );
2111 void Decorator::SetHandleActive( HandleType handleType, bool active )
2113 mImpl->mHandle[handleType].active = active;
2117 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
2119 mImpl->mIsHandlePreviouslyCrossed = false;
2122 // TODO: this is a work-around.
2123 // The problem is the handle actor does not receive the touch event with the Interrupt
2124 // state when the power button is pressed and the application goes to background.
2125 mImpl->mHandle[handleType].pressed = false;
2126 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
2127 ImageView imageView = mImpl->mHandle[handleType].actor;
2128 if( imageReleased && imageView )
2130 imageView.SetImage( imageReleased );
2136 bool Decorator::IsHandleActive( HandleType handleType ) const
2138 return mImpl->mHandle[handleType].active ;
2141 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
2143 mImpl->SetHandleImage( handleType, handleImageType, image );
2146 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
2148 return mImpl->mHandleImages[handleType][handleImageType];
2151 void Decorator::SetHandleColor( const Vector4& color )
2153 mImpl->mHandleColor = color;
2156 const Vector4& Decorator::GetHandleColor() const
2158 return mImpl->mHandleColor;
2161 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
2163 // Adjust handle's displacement
2164 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2166 handle.position.x = x;
2167 handle.position.y = y;
2168 handle.lineHeight = height;
2170 if( mImpl->mSmoothHandlePanEnabled )
2172 handle.grabDisplacementX = 0.f;
2173 handle.grabDisplacementY = 0.f;
2177 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2179 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2181 x = handle.position.x;
2182 y = handle.position.y;
2183 height = handle.lineHeight;
2186 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2188 return mImpl->mHandle[handleType].position;
2191 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2193 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2196 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2198 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2201 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2203 mImpl->mFlipSelectionHandlesOnCross = enable;
2206 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2208 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2209 mImpl->mFlipLeftSelectionHandleDirection = left;
2210 mImpl->mFlipRightSelectionHandleDirection = right;
2213 void Decorator::AddHighlight( unsigned int index, const Vector4& quad )
2215 *( mImpl->mHighlightQuadList.Begin() + index ) = quad;
2218 void Decorator::SetHighLightBox( const Vector2& position, const Size& size )
2220 mImpl->mHighlightPosition = position;
2221 mImpl->mHighlightSize = size;
2224 void Decorator::ClearHighlights()
2226 mImpl->mHighlightQuadList.Clear();
2227 mImpl->mHighlightPosition = Vector2::ZERO;
2230 void Decorator::ResizeHighlightQuads( unsigned int numberOfQuads )
2232 mImpl->mHighlightQuadList.Resize( numberOfQuads );
2235 void Decorator::SetHighlightColor( const Vector4& color )
2237 mImpl->mHighlightColor = color;
2240 const Vector4& Decorator::GetHighlightColor() const
2242 return mImpl->mHighlightColor;
2245 void Decorator::SetHighlightActive( bool active )
2247 mImpl->mIsHighlightBoxActive = active;
2250 bool Decorator::IsHighlightActive() const
2252 return mImpl->mIsHighlightBoxActive;
2255 void Decorator::SetTextDepth( int textDepth )
2257 mImpl->mTextDepth = textDepth;
2260 void Decorator::SetPopupActive( bool active )
2262 mImpl->mActiveCopyPastePopup = active;
2265 bool Decorator::IsPopupActive() const
2267 return mImpl->mActiveCopyPastePopup;
2270 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2272 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2274 if ( !mImpl->mCopyPastePopup.actor )
2276 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2277 #ifdef DECORATOR_DEBUG
2278 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
2280 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
2281 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::SetPopupPosition ); // Position popup after size negotiation
2284 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2287 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2289 return mImpl->mEnabledPopupButtons;
2294 void Decorator::SetScrollThreshold( float threshold )
2296 mImpl->SetScrollThreshold( threshold );
2299 float Decorator::GetScrollThreshold() const
2301 return mImpl->GetScrollThreshold();
2304 void Decorator::SetScrollSpeed( float speed )
2306 mImpl->SetScrollSpeed( speed );
2309 float Decorator::GetScrollSpeed() const
2311 return mImpl->GetScrollSpeed();
2314 void Decorator::NotifyEndOfScroll()
2316 mImpl->NotifyEndOfScroll();
2319 void Decorator::SetHorizontalScrollEnabled( bool enable )
2321 mImpl->mHorizontalScrollingEnabled = enable;
2324 bool Decorator::IsHorizontalScrollEnabled() const
2326 return mImpl->mHorizontalScrollingEnabled;
2329 void Decorator::SetVerticalScrollEnabled( bool enable )
2331 mImpl->mVerticalScrollingEnabled = enable;
2334 bool Decorator::IsVerticalScrollEnabled() const
2336 return mImpl->mVerticalScrollingEnabled;
2339 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2341 mImpl->mSmoothHandlePanEnabled = enable;
2344 bool Decorator::IsSmoothHandlePanEnabled() const
2346 return mImpl->mSmoothHandlePanEnabled;
2349 Decorator::~Decorator()
2354 Decorator::Decorator( ControllerInterface& controller,
2355 TextSelectionPopupCallbackInterface& callbackInterface )
2358 mImpl = new Decorator::Impl( controller, callbackInterface );
2363 } // namespace Toolkit