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 mPrimaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
289 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
290 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
291 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
292 if( mPrimaryCursorVisible )
294 mPrimaryCursor.SetPosition( cursor.position.x,
296 mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
298 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
300 if( mSecondaryCursor )
302 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
303 mSecondaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
304 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
305 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
306 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
307 if( mSecondaryCursorVisible )
309 mSecondaryCursor.SetPosition( cursor.position.x,
311 mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
313 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
316 // Show or hide the grab handle
317 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
318 bool newGrabHandlePosition = false;
319 grabHandle.horizontallyVisible = false;
320 grabHandle.verticallyVisible = false;
321 if( grabHandle.active )
323 grabHandle.horizontallyVisible = ( ( mControlSize.width - ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) ) > -Math::MACHINE_EPSILON_1000 ) &&
324 ( grabHandle.position.x > -Math::MACHINE_EPSILON_1000 ) );
325 grabHandle.verticallyVisible = ( ( ( mControlSize.height - grabHandle.lineHeight ) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) &&
326 ( grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) );
328 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible;
333 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
334 SetGrabHandlePosition();
336 // Sets the grab handle image according if it's pressed, flipped, etc.
337 SetHandleImage( GRAB_HANDLE );
339 newGrabHandlePosition = true;
342 if( grabHandle.actor )
344 grabHandle.actor.SetVisible( isVisible );
347 else if( grabHandle.actor )
349 grabHandle.actor.Unparent();
352 // Show or hide the selection handles/highlight
353 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
354 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
355 bool newPrimaryHandlePosition = false;
356 bool newSecondaryHandlePosition = false;
358 primary.horizontallyVisible = ( ( mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
359 ( primary.position.x > -Math::MACHINE_EPSILON_1000 ) );
360 primary.verticallyVisible = ( ( ( mControlSize.height - primary.lineHeight ) - primary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
361 ( primary.position.y + ( primary.verticallyFlipped ? 0.f : primary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
362 secondary.horizontallyVisible = ( ( mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
363 ( secondary.position.x > -Math::MACHINE_EPSILON_1000 ) );
364 secondary.verticallyVisible = ( ( ( mControlSize.height - secondary.lineHeight ) - secondary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
365 ( secondary.position.y + ( secondary.verticallyFlipped ? 0.f : secondary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
367 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
368 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
370 if( primary.active || secondary.active )
372 if( primaryVisible || secondaryVisible )
374 CreateSelectionHandles();
378 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
380 // Sets the primary handle image according if it's pressed, flipped, etc.
381 SetHandleImage( LEFT_SELECTION_HANDLE );
383 SetSelectionHandleMarkerSize( primary );
385 newPrimaryHandlePosition = true;
388 if( secondaryVisible )
390 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
392 // Sets the secondary handle image according if it's pressed, flipped, etc.
393 SetHandleImage( RIGHT_SELECTION_HANDLE );
395 SetSelectionHandleMarkerSize( secondary );
397 newSecondaryHandlePosition = true;
403 primary.actor.SetVisible( primaryVisible );
405 if( secondary.actor )
407 secondary.actor.SetVisible( secondaryVisible );
415 primary.actor.Unparent();
417 if( secondary.actor )
419 secondary.actor.Unparent();
423 if( mIsHighlightBoxActive )
430 if( mHighlightActor )
432 mHighlightActor.Unparent();
436 if( newGrabHandlePosition ||
437 newPrimaryHandlePosition ||
438 newSecondaryHandlePosition )
440 // Setup property notifications to find whether the handles leave the boundaries of the current display.
441 SetupActiveLayerPropertyNotifications();
444 if( mActiveCopyPastePopup &&
445 ( primaryVisible || secondaryVisible ) )
448 mPopupSetNewPosition = true;
452 if( mCopyPastePopup.actor )
454 mCopyPastePopup.actor.HidePopup();
455 mPopupSetNewPosition = true;
460 void UpdatePositions( const Vector2& scrollOffset )
462 mCursor[PRIMARY_CURSOR].position += scrollOffset;
463 mCursor[SECONDARY_CURSOR].position += scrollOffset;
464 mHandle[ GRAB_HANDLE ].position += scrollOffset;
465 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
466 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
467 mHighlightPosition += scrollOffset;
472 if( !mCopyPastePopup.actor )
477 if( !mCopyPastePopup.actor.GetParent() )
479 mActiveLayer.Add( mCopyPastePopup.actor );
482 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
483 mCopyPastePopup.actor.ShowPopup();
486 float CalculateVerticalPopUpPosition( float halfHeight, bool preferBelow )
488 float yPosition = 0.f;
490 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
491 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
492 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
494 if( primaryHandle.active || secondaryHandle.active )
496 // The origin of the decorator's coordinate system in world coords.
497 const Vector3 originWorldCoords = mActiveLayer.GetCurrentWorldPosition() - mActiveLayer.GetCurrentSize() * ACTIVE_LAYER_ANCHOR_POINT;
501 // Find out if there is enough space for the popup at the bottom.
502 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
503 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
505 float maxY = std::max( primaryBelowY, secondaryBelowY );
507 yPosition = halfHeight + maxY;
509 if( originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w )
511 // Does not fit below.
513 // Try to fit first below the non active handle. Otherwise above the active handle.
514 if( RIGHT_SELECTION_HANDLE == mHandleReleased )
516 if( primaryBelowY < secondaryBelowY )
518 yPosition = halfHeight + primaryBelowY;
522 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
525 else if( LEFT_SELECTION_HANDLE == mHandleReleased )
527 if( secondaryBelowY < primaryBelowY )
529 yPosition = halfHeight + secondaryBelowY;
533 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
537 // Check the handle is whithin the decoration box.
538 if( originWorldCoords.y + yPosition < mBoundingBox.y )
540 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
543 if( originWorldCoords.y + yPosition > mBoundingBox.w )
545 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
551 // Find out if there is enough space for the popup at the top.
552 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
553 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
555 float minY = std::min( primaryTopY, secondaryTopY );
557 yPosition = -halfHeight + minY;
559 } // ( primaryHandle.active || secondaryHandle.active )
560 else if( grabHandle.active )
564 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
568 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
575 void ConstrainPopupPosition( const Vector3& popupHalfSize )
577 // Check if the popup is within the boundaries of the decoration box.
579 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
581 // The origin of the decorator's coordinate system in world coords.
582 const Vector3 originWorldCoords = mActiveLayer.GetCurrentWorldPosition() - mActiveLayer.GetCurrentSize() * ACTIVE_LAYER_ANCHOR_POINT;
584 // The popup's position in world coords.
585 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
587 if( popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x )
589 mCopyPastePopup.position.x += mBoundingBox.x - ( popupPositionWorldCoords.x - popupHalfSize.width );
591 else if( popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z )
593 mCopyPastePopup.position.x += mBoundingBox.z - ( popupPositionWorldCoords.x + popupHalfSize.width );
596 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
597 if( popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y )
599 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition( popupHalfSize.height, true ); // true -> prefer to set the popup's position below.
603 void SetPopupPosition( Actor actor )
605 if( !mActiveCopyPastePopup )
610 // Retrieves the popup's size after relayout.
611 const Vector3 popupSize( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
612 const Vector3 popupHalfSize = popupSize * 0.5f;
614 if( mPopupSetNewPosition )
616 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
617 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
618 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
620 if( primaryHandle.active || secondaryHandle.active )
622 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
623 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
625 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
627 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - ( primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING );
628 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - ( secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING );
630 mCopyPastePopup.position.y = std::min( primaryY, secondaryY );
632 else if( grabHandle.active )
634 mCopyPastePopup.position.x = grabHandle.position.x;
636 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - ( grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING );
638 } // mPopupSetNewPosition
640 // It may change the popup's position to fit within the decoration box.
641 ConstrainPopupPosition( popupHalfSize );
643 SetUpPopupPositionNotifications( popupHalfSize );
645 // Prevent pixel mis-alignment by rounding down.
646 mCopyPastePopup.position.x = floorf( mCopyPastePopup.position.x );
647 mCopyPastePopup.position.y = floorf( mCopyPastePopup.position.y );
649 mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
650 mPopupSetNewPosition = false;
653 void CreateCursor( Control& cursor, const Vector4& color )
655 cursor = Control::New();
656 cursor.SetBackgroundColor( color );
657 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT );
658 cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
661 // Add or Remove cursor(s) from parent
664 if( mActiveCursor == ACTIVE_CURSOR_NONE )
668 mPrimaryCursor.Unparent();
670 if( mSecondaryCursor )
672 mSecondaryCursor.Unparent();
677 // Create Primary and or Secondary Cursor(s) if active and add to parent
678 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
679 mActiveCursor == ACTIVE_CURSOR_BOTH )
681 if ( !mPrimaryCursor )
683 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
684 #ifdef DECORATOR_DEBUG
685 mPrimaryCursor.SetName( "PrimaryCursorActor" );
689 if( !mPrimaryCursor.GetParent() )
691 mActiveLayer.Add( mPrimaryCursor );
695 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
697 if ( !mSecondaryCursor )
699 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
700 #ifdef DECORATOR_DEBUG
701 mSecondaryCursor.SetName( "SecondaryCursorActor" );
705 if( !mSecondaryCursor.GetParent() )
707 mActiveLayer.Add( mSecondaryCursor );
712 if( mSecondaryCursor )
714 mSecondaryCursor.Unparent();
720 bool OnCursorBlinkTimerTick()
722 if( !mDelayCursorBlink )
725 if ( mPrimaryCursor )
727 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
729 if ( mSecondaryCursor )
731 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
734 mCursorBlinkStatus = !mCursorBlinkStatus;
739 mDelayCursorBlink = false;
747 // Will consume tap gestures on handles.
748 mTapDetector = TapGestureDetector::New();
750 // Will consume double tap gestures on handles.
751 mTapDetector.SetMaximumTapsRequired( 2u );
753 // Will consume long press gestures on handles.
754 mLongPressDetector = LongPressGestureDetector::New();
756 // Detects pan gestures on handles.
757 mPanDetector = PanGestureDetector::New();
758 mPanDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
761 void CreateActiveLayer()
765 mActiveLayer = Layer::New();
766 #ifdef DECORATOR_DEBUG
767 mActiveLayer.SetName ( "ActiveLayerActor" );
770 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
771 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
773 // Add the active layer telling the controller it doesn't need clipping.
774 mController.AddDecoration( mActiveLayer, false );
777 mActiveLayer.RaiseToTop();
780 void SetSelectionHandleMarkerSize( HandleImpl& handle )
782 if( handle.markerActor )
784 handle.markerActor.SetSize( 0, handle.lineHeight );
788 void CreateGrabHandle()
790 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
791 if( !grabHandle.actor )
793 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
795 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
796 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
797 grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
799 // Area that Grab handle responds to, larger than actual handle so easier to move
800 #ifdef DECORATOR_DEBUG
801 grabHandle.actor.SetName( "GrabHandleActor" );
802 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
804 grabHandle.grabArea = Control::New();
805 Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
806 control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
807 grabHandle.grabArea.SetName( "GrabArea" );
811 grabHandle.grabArea = Actor::New();
812 grabHandle.grabArea.SetName( "GrabArea" );
815 grabHandle.grabArea = Actor::New();
818 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
819 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
820 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
821 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
822 grabHandle.actor.Add( grabHandle.grabArea );
823 grabHandle.actor.SetColor( mHandleColor );
825 grabHandle.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
827 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
828 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
829 mTapDetector.Attach( grabHandle.actor );
830 mLongPressDetector.Attach( grabHandle.actor );
832 // The grab handle's area is attached to the pan detector.
833 // The OnPan() method is connected to the signals emitted by the pan detector.
834 mPanDetector.Attach( grabHandle.grabArea );
836 mActiveLayer.Add( grabHandle.actor );
840 if( grabHandle.actor && !grabHandle.actor.GetParent() )
842 mActiveLayer.Add( grabHandle.actor );
846 void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
850 handle.markerActor = ImageView::New( image );
851 handle.markerActor.SetColor( mHandleColor );
852 handle.actor.Add( handle.markerActor );
854 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
856 if( LEFT_SELECTION_HANDLE == handleType )
858 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
859 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
861 else if( RIGHT_SELECTION_HANDLE == handleType )
863 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
864 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
869 void CreateSelectionHandles()
871 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
874 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
876 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
877 #ifdef DECORATOR_DEBUG
878 primary.actor.SetName("SelectionHandleOne");
880 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
881 GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
882 primary.actor.SetColor( mHandleColor );
884 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
885 #ifdef DECORATOR_DEBUG
886 primary.grabArea.SetName("SelectionHandleOneGrabArea");
888 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
889 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
890 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
891 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
893 primary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
895 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
896 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
897 mTapDetector.Attach( primary.actor );
898 mLongPressDetector.Attach( primary.actor );
900 // The handle's area is attached to the pan detector.
901 // The OnPan() method is connected to the signals emitted by the pan detector.
902 mPanDetector.Attach( primary.grabArea );
904 primary.actor.Add( primary.grabArea );
906 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
910 if( primary.actor && !primary.actor.GetParent() )
912 mActiveLayer.Add( primary.actor );
915 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
916 if( !secondary.actor )
918 if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
920 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
921 #ifdef DECORATOR_DEBUG
922 secondary.actor.SetName("SelectionHandleTwo");
924 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
925 GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
926 secondary.actor.SetColor( mHandleColor );
928 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
929 #ifdef DECORATOR_DEBUG
930 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
932 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
933 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
934 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
935 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
937 secondary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
939 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
940 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
941 mTapDetector.Attach( secondary.actor );
942 mLongPressDetector.Attach( secondary.actor );
944 // The handle's area is attached to the pan detector.
945 // The OnPan() method is connected to the signals emitted by the pan detector.
946 mPanDetector.Attach( secondary.grabArea );
948 secondary.actor.Add( secondary.grabArea );
950 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
954 if( secondary.actor && !secondary.actor.GetParent() )
956 mActiveLayer.Add( secondary.actor );
960 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
962 // Gets the world position of the active layer. The active layer is where the handles are added.
963 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
965 // The grab handle position in world coords.
966 // The active layer's world position is the center of the active layer. The origin of the
967 // coord system of the handles is the top left of the active layer.
968 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f );
969 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f );
972 void SetGrabHandlePosition()
974 // Reference to the grab handle.
975 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
977 // Transforms the handle position into world coordinates.
978 // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
979 // as it's transforming the handle's position set by the text-controller and not
980 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
981 // retrieves the position of the center of the actor but the handle's position set
982 // by the text controller is not the center of the actor.
983 Vector2 grabHandleWorldPosition;
984 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
986 // Check if the grab handle exceeds the boundaries of the decoration box.
987 // At the moment only the height is checked for the grab handle.
989 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
990 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
991 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
993 // The grab handle 'y' position in local coords.
994 // If the grab handle exceeds the bottom of the decoration box,
995 // set the 'y' position to the top of the line.
996 // The SetGrabHandleImage() method will change the orientation.
997 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
999 if( grabHandle.actor )
1001 grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ) + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f ),
1002 yLocalPosition + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f ) );
1006 void SetSelectionHandlePosition( HandleType type )
1008 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
1010 // Reference to the selection handle.
1011 HandleImpl& handle = mHandle[type];
1013 // Transforms the handle position into world coordinates.
1014 // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
1015 // as it's transforming the handle's position set by the text-controller and not
1016 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
1017 // retrieves the position of the center of the actor but the handle's position set
1018 // by the text controller is not the center of the actor.
1019 Vector2 handleWorldPosition;
1020 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
1022 // Whether to flip the handle (horizontally).
1023 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1025 // Whether to flip the handles if they are crossed.
1026 bool crossFlip = false;
1027 if( mFlipSelectionHandlesOnCross || !mIsHandlePanning )
1029 crossFlip = mIsHandleCurrentlyCrossed;
1032 // Whether the handle was crossed before start the panning.
1033 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1035 // Does not flip if both conditions are true (double flip)
1036 flipHandle = flipHandle != ( crossFlip || isHandlePreviouslyCrossed );
1038 // Will flip the handles vertically if the user prefers it.
1039 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1041 if( crossFlip || isHandlePreviouslyCrossed )
1043 if( isPrimaryHandle )
1045 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1049 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1053 // Check if the selection handle exceeds the boundaries of the decoration box.
1054 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
1055 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
1057 // Does not flip if both conditions are true (double flip)
1058 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
1062 if( handle.actor && !handle.horizontallyFlipped )
1064 // Change the anchor point to flip the image.
1065 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
1067 handle.horizontallyFlipped = true;
1072 if( handle.actor && handle.horizontallyFlipped )
1074 // Reset the anchor point.
1075 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
1077 handle.horizontallyFlipped = false;
1081 // Whether to flip the handle vertically.
1082 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
1083 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
1084 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
1086 // The primary selection handle 'y' position in local coords.
1087 // If the handle exceeds the bottom of the decoration box,
1088 // set the 'y' position to the top of the line.
1089 // The SetHandleImage() method will change the orientation.
1090 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1094 handle.actor.SetPosition( handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f ),
1095 yLocalPosition + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f ) );
1099 void SetHandleImage( HandleType type )
1101 HandleImpl& handle = mHandle[type];
1103 HandleType markerType = HANDLE_TYPE_COUNT;
1104 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1105 if( LEFT_SELECTION_HANDLE == type )
1107 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1108 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1110 else if( RIGHT_SELECTION_HANDLE == type )
1112 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1113 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1116 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1119 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1121 handle.actor.SetImage( mHandleImages[type][imageType] );
1124 if( HANDLE_TYPE_COUNT != markerType )
1126 if( handle.markerActor )
1128 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1129 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
1133 // Whether to flip the handle vertically.
1136 handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
1140 void CreateHighlight()
1142 if( !mHighlightActor )
1144 mHighlightActor = Actor::New();
1146 #ifdef DECORATOR_DEBUG
1147 mHighlightActor.SetName( "HighlightActor" );
1149 mHighlightActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
1150 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
1151 mHighlightActor.SetColor( mHighlightColor );
1152 mHighlightActor.SetColorMode( USE_OWN_COLOR );
1155 // Add the highlight box telling the controller it needs clipping.
1156 mController.AddDecoration( mHighlightActor, true );
1159 void UpdateHighlight()
1161 if ( mHighlightActor )
1163 // Sets the position of the highlight actor inside the decorator.
1164 mHighlightActor.SetPosition( mHighlightPosition.x,
1165 mHighlightPosition.y );
1167 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1168 if( 0u != numberOfQuads )
1170 // Set the size of the highlighted text to the actor.
1171 mHighlightActor.SetSize( mHighlightSize );
1173 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1174 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1175 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1177 Vector<Vector2> vertices;
1178 Vector<unsigned short> indices;
1180 vertices.Reserve( 4u * numberOfQuads );
1181 indices.Reserve( 6u * numberOfQuads );
1183 // Index to the vertex.
1184 unsigned int v = 0u;
1186 // Traverse all quads.
1187 for( Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1188 endIt = mHighlightQuadList.End();
1192 const Vector4& quad = *it;
1197 vertex.x = quad.x - offsetX;
1198 vertex.y = quad.y - offsetY;
1199 vertices.PushBack( vertex );
1202 vertex.x = quad.z - offsetX;
1203 vertex.y = quad.y - offsetY;
1204 vertices.PushBack( vertex );
1206 // bottom-left (v+2)
1207 vertex.x = quad.x - offsetX;
1208 vertex.y = quad.w - offsetY;
1209 vertices.PushBack( vertex );
1211 // bottom-right (v+3)
1212 vertex.x = quad.z - offsetX;
1213 vertex.y = quad.w - offsetY;
1214 vertices.PushBack( vertex );
1216 // triangle A (3, 1, 0)
1217 indices.PushBack( v + 3 );
1218 indices.PushBack( v + 1 );
1219 indices.PushBack( v );
1221 // triangle B (0, 2, 3)
1222 indices.PushBack( v );
1223 indices.PushBack( v + 2 );
1224 indices.PushBack( v + 3 );
1227 if( ! mQuadVertices )
1229 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
1232 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1234 if( !mQuadGeometry )
1236 mQuadGeometry = Geometry::New();
1237 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1239 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1241 if( !mHighlightRenderer )
1243 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1244 mHighlightActor.AddRenderer( mHighlightRenderer );
1248 mHighlightQuadList.Clear();
1250 if( mHighlightRenderer )
1252 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1257 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1259 if( Gesture::Started == gesture.state )
1261 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1263 handle.globalPosition.x = handle.position.x;
1264 handle.globalPosition.y = handle.position.y;
1267 handle.grabDisplacementX += gesture.displacement.x;
1268 handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.displacement.y );
1270 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1271 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1272 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f );
1274 if( ( Gesture::Started == gesture.state ) ||
1275 ( Gesture::Continuing == gesture.state ) )
1278 mController.GetTargetSize( targetSize );
1280 if( mHorizontalScrollingEnabled &&
1281 ( x < mScrollThreshold ) )
1283 mScrollDirection = SCROLL_RIGHT;
1284 mHandleScrolling = type;
1287 else if( mHorizontalScrollingEnabled &&
1288 ( x > targetSize.width - mScrollThreshold ) )
1290 mScrollDirection = SCROLL_LEFT;
1291 mHandleScrolling = type;
1294 else if( mVerticalScrollingEnabled &&
1295 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1297 mScrollDirection = SCROLL_TOP;
1298 mHandleScrolling = type;
1301 else if( mVerticalScrollingEnabled &&
1302 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1304 mScrollDirection = SCROLL_BOTTOM;
1305 mHandleScrolling = type;
1310 mHandleScrolling = HANDLE_TYPE_COUNT;
1312 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1315 mIsHandlePanning = true;
1317 else if( ( Gesture::Finished == gesture.state ) ||
1318 ( Gesture::Cancelled == gesture.state ) )
1321 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1323 mNotifyEndOfScroll = false;
1324 mHandleScrolling = HANDLE_TYPE_COUNT;
1326 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1330 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1335 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1337 handle.pressed = false;
1339 mIsHandlePanning = false;
1343 void OnPan( Actor actor, const PanGesture& gesture )
1345 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1346 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1347 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1349 if( actor == grabHandle.grabArea )
1351 DoPan( grabHandle, GRAB_HANDLE, gesture );
1353 else if( actor == primarySelectionHandle.grabArea )
1355 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1357 else if( actor == secondarySelectionHandle.grabArea )
1359 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1363 bool OnGrabHandleTouched( Actor actor, const TouchData& touch )
1365 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1367 // Switch between pressed/release grab-handle images
1368 if( touch.GetPointCount() > 0 &&
1371 const PointState::Type state = touch.GetState( 0 );
1373 if( PointState::DOWN == state )
1375 grabHandle.pressed = true;
1377 else if( ( PointState::UP == state ) ||
1378 ( PointState::INTERRUPTED == state ) )
1380 grabHandle.pressed = false;
1383 SetHandleImage( GRAB_HANDLE );
1386 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1390 bool OnHandleOneTouched( Actor actor, const TouchData& touch )
1392 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1394 // Switch between pressed/release selection handle images
1395 if( touch.GetPointCount() > 0 &&
1396 primarySelectionHandle.actor )
1398 const PointState::Type state = touch.GetState( 0 );
1400 if( PointState::DOWN == state )
1402 primarySelectionHandle.pressed = true;
1403 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1405 else if( ( PointState::UP == state ) ||
1406 ( PointState::INTERRUPTED == state ) )
1408 primarySelectionHandle.pressed = false;
1409 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1410 mIsHandlePanning = false;
1411 mHandleReleased = LEFT_SELECTION_HANDLE;
1414 SetHandleImage( LEFT_SELECTION_HANDLE );
1417 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1421 bool OnHandleTwoTouched( Actor actor, const TouchData& touch )
1423 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1425 // Switch between pressed/release selection handle images
1426 if( touch.GetPointCount() > 0 &&
1427 secondarySelectionHandle.actor )
1429 const PointState::Type state = touch.GetState( 0 );
1431 if( PointState::DOWN == state )
1433 secondarySelectionHandle.pressed = true;
1434 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1436 else if( ( PointState::UP == state ) ||
1437 ( PointState::INTERRUPTED == state ) )
1439 secondarySelectionHandle.pressed = false;
1440 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1441 mIsHandlePanning = false;
1442 mHandleReleased = RIGHT_SELECTION_HANDLE;
1445 SetHandleImage( RIGHT_SELECTION_HANDLE );
1448 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1452 void HandleResetPosition( PropertyNotification& source )
1454 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1456 if( grabHandle.active )
1458 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1459 SetGrabHandlePosition();
1461 // Sets the grab handle image according if it's pressed, flipped, etc.
1462 SetHandleImage( GRAB_HANDLE );
1466 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1467 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1469 // Sets the primary handle image according if it's pressed, flipped, etc.
1470 SetHandleImage( LEFT_SELECTION_HANDLE );
1472 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1473 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1475 // Sets the secondary handle image according if it's pressed, flipped, etc.
1476 SetHandleImage( RIGHT_SELECTION_HANDLE );
1480 void SetupActiveLayerPropertyNotifications()
1487 // Vertical notifications.
1489 // Disconnect any previous connected callback.
1490 if( mHandleVerticalLessThanNotification )
1492 mHandleVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1493 mActiveLayer.RemovePropertyNotification( mHandleVerticalLessThanNotification );
1496 if( mHandleVerticalGreaterThanNotification )
1498 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1499 mActiveLayer.RemovePropertyNotification( mHandleVerticalGreaterThanNotification );
1502 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1503 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1504 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1506 if( grabHandle.active )
1508 if( grabHandle.verticallyFlipped )
1510 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1511 mHandleVerticalGreaterThanNotification.Reset();
1513 // The vertical distance from the center of the active layer to the top edje of the display.
1514 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1516 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1517 LessThanCondition( mBoundingBox.y + topHeight ) );
1519 // Notifies the change from false to true and from true to false.
1520 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1522 // Connects the signals with the callbacks.
1523 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1527 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1528 mHandleVerticalLessThanNotification.Reset();
1530 // The vertical distance from the center of the active layer to the bottom edje of the display.
1531 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1533 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1534 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1536 // Notifies the change from false to true and from true to false.
1537 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1539 // Connects the signals with the callbacks.
1540 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1543 else // The selection handles are active
1545 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1547 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1548 mHandleVerticalGreaterThanNotification.Reset();
1550 // The vertical distance from the center of the active layer to the top edje of the display.
1551 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1553 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1554 LessThanCondition( mBoundingBox.y + topHeight ) );
1556 // Notifies the change from false to true and from true to false.
1557 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1559 // Connects the signals with the callbacks.
1560 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1562 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1564 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1565 mHandleVerticalLessThanNotification.Reset();
1567 // The vertical distance from the center of the active layer to the bottom edje of the display.
1568 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1569 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1571 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1572 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1574 // Notifies the change from false to true and from true to false.
1575 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1577 // Connects the signals with the callbacks.
1578 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1582 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1584 // The vertical distance from the center of the active layer to the top edje of the display.
1585 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1586 -primaryHandle.position.y + primaryHandle.size.height :
1587 -secondaryHandle.position.y + secondaryHandle.size.height );
1589 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1590 LessThanCondition( mBoundingBox.y + topHeight ) );
1592 // Notifies the change from false to true and from true to false.
1593 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1595 // Connects the signals with the callbacks.
1596 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1598 // The vertical distance from the center of the active layer to the bottom edje of the display.
1599 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1600 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1601 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1603 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1604 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1606 // Notifies the change from false to true and from true to false.
1607 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1609 // Connects the signals with the callbacks.
1610 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1614 // Horizontal notifications.
1616 // Disconnect any previous connected callback.
1617 if( mHandleHorizontalLessThanNotification )
1619 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1620 mActiveLayer.RemovePropertyNotification( mHandleHorizontalLessThanNotification );
1623 if( mHandleHorizontalGreaterThanNotification )
1625 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1626 mActiveLayer.RemovePropertyNotification( mHandleHorizontalGreaterThanNotification );
1629 if( primaryHandle.active || secondaryHandle.active )
1631 // The horizontal distance from the center of the active layer to the left edje of the display.
1632 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1633 -secondaryHandle.position.x + secondaryHandle.size.width );
1635 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1636 LessThanCondition( mBoundingBox.x + leftWidth ) );
1638 // Notifies the change from false to true and from true to false.
1639 mHandleHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1641 // Connects the signals with the callbacks.
1642 mHandleHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1644 // The horizontal distance from the center of the active layer to the right edje of the display.
1645 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1646 secondaryHandle.position.x + secondaryHandle.size.width );
1648 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1649 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1651 // Notifies the change from false to true and from true to false.
1652 mHandleHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1654 // Connects the signals with the callbacks.
1655 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1661 float AlternatePopUpPositionRelativeToCursor( bool topBottom )
1663 float alternativePosition = 0.0f;
1665 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1667 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1668 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1669 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1670 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1672 if( primaryHandle.active || secondaryHandle.active )
1674 float handleY = 0.f;
1675 float maxHandleHeight = 0.f;
1677 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1678 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1680 if( primaryVisible && secondaryVisible )
1682 handleY = std::max( primaryHandle.position.y, secondaryHandle.position.y );
1683 maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1685 else if( primaryVisible && !secondaryVisible )
1687 handleY = primaryHandle.position.y;
1688 maxHandleHeight = primaryHandle.size.height;
1690 else if( !primaryVisible && secondaryVisible )
1692 handleY = secondaryHandle.position.y;
1693 maxHandleHeight = secondaryHandle.size.height;
1696 alternativePosition = handleY + ( topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight );
1700 alternativePosition = cursor.position.y + ( topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height );
1703 return alternativePosition;
1706 void PopUpLeavesTopBoundary( PropertyNotification& source )
1708 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1710 // Sets the position of the popup below.
1711 mCopyPastePopup.actor.SetY( floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, true ) ) );
1714 void PopUpLeavesBottomBoundary( PropertyNotification& source )
1716 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1718 // Sets the position of the popup above.
1719 mCopyPastePopup.actor.SetY( floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, false ) ) );
1722 void SetUpPopupPositionNotifications( const Vector3& popupHalfSize )
1724 // Disconnect any previous connected callback.
1725 if( mPopupTopExceedNotification )
1727 mPopupTopExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1728 mCopyPastePopup.actor.RemovePropertyNotification( mPopupTopExceedNotification );
1731 if( mPopupBottomExceedNotification )
1733 mPopupBottomExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1734 mCopyPastePopup.actor.RemovePropertyNotification( mPopupBottomExceedNotification );
1737 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1739 // Exceeding vertical boundary
1741 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1742 LessThanCondition( mBoundingBox.y + popupHalfSize.height ) );
1744 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1745 GreaterThanCondition( mBoundingBox.w - popupHalfSize.height ) );
1747 // Notifies the change from false to true and from true to false.
1748 mPopupTopExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1749 mPopupBottomExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1751 mPopupTopExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1752 mPopupBottomExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1755 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1757 HandleImpl& handle = mHandle[handleType];
1758 handle.size = Size( image.GetWidth(), image.GetHeight() );
1760 mHandleImages[handleType][handleImageType] = image;
1763 void SetScrollThreshold( float threshold )
1765 mScrollThreshold = threshold;
1768 float GetScrollThreshold() const
1770 return mScrollThreshold;
1773 void SetScrollSpeed( float speed )
1775 mScrollSpeed = speed;
1776 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1779 float GetScrollSpeed() const
1781 return mScrollSpeed;
1784 void NotifyEndOfScroll()
1790 mNotifyEndOfScroll = true;
1795 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1797 * It only starts the timer if it's already created.
1799 void StartScrollTimer()
1803 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1804 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1807 if( !mScrollTimer.IsRunning() )
1809 mScrollTimer.Start();
1814 * Stops the timer used to scroll the text.
1816 void StopScrollTimer()
1820 mScrollTimer.Stop();
1825 * Callback called by the timer used to scroll the text.
1827 * It calculates and sets a new scroll position.
1829 bool OnScrollTimerTick()
1831 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1836 switch( mScrollDirection )
1840 x = mScrollDistance;
1845 x = -mScrollDistance;
1850 y = mScrollDistance;
1855 y = -mScrollDistance;
1862 mController.DecorationEvent( mHandleScrolling,
1871 ControllerInterface& mController;
1873 TapGestureDetector mTapDetector;
1874 PanGestureDetector mPanDetector;
1875 LongPressGestureDetector mLongPressDetector;
1877 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1878 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1880 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1881 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1882 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1883 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1884 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1885 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1886 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1887 Control mPrimaryCursor;
1888 Control mSecondaryCursor;
1890 Actor mHighlightActor; ///< Actor to display highlight
1891 Renderer mHighlightRenderer;
1892 Shader mHighlightShader; ///< Shader used for highlight
1893 Property::Map mQuadVertexFormat;
1894 PopupImpl mCopyPastePopup;
1895 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1896 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1898 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1899 Vector4 mHandleColor;
1901 CursorImpl mCursor[CURSOR_COUNT];
1902 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1904 PropertyBuffer mQuadVertices;
1905 Geometry mQuadGeometry;
1906 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1908 Vector4 mBoundingBox; ///< The bounding box in world coords.
1909 Vector4 mHighlightColor; ///< Color of the highlight
1910 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1911 Size mHighlightSize; ///< The size of the highlighted text.
1912 Size mControlSize; ///< The control's size. Set by the Relayout.
1914 unsigned int mActiveCursor;
1915 unsigned int mCursorBlinkInterval;
1916 float mCursorBlinkDuration;
1917 float mCursorWidth; ///< The width of the cursors in pixels.
1918 HandleType mHandleScrolling; ///< The handle which is scrolling.
1919 HandleType mHandleReleased; ///< The last handle released.
1920 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1921 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1922 float mScrollSpeed; ///< The scroll speed in pixels per second.
1923 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1924 int mTextDepth; ///< The depth used to render the text.
1926 bool mActiveCopyPastePopup : 1;
1927 bool mPopupSetNewPosition : 1;
1928 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1929 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1930 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1931 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1932 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1933 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1934 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1935 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1936 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1937 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1938 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1939 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1940 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1941 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1942 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1945 DecoratorPtr Decorator::New( ControllerInterface& controller,
1946 TextSelectionPopupCallbackInterface& callbackInterface )
1948 return DecoratorPtr( new Decorator( controller,
1949 callbackInterface ) );
1952 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1954 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1957 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1959 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1962 void Decorator::Relayout( const Vector2& size )
1964 mImpl->Relayout( size );
1967 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1969 mImpl->UpdatePositions( scrollOffset );
1974 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1976 mImpl->mActiveCursor = activeCursor;
1979 unsigned int Decorator::GetActiveCursor() const
1981 return mImpl->mActiveCursor;
1984 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1986 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1988 cursorImpl.position.x = x;
1989 cursorImpl.position.y = y;
1990 cursorImpl.cursorHeight = cursorHeight;
1991 cursorImpl.lineHeight = lineHeight;
1994 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1996 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1998 x = cursorImpl.position.x;
1999 y = cursorImpl.position.y;
2000 cursorHeight = cursorImpl.cursorHeight;
2001 lineHeight = cursorImpl.lineHeight;
2004 const Vector2& Decorator::GetPosition( Cursor cursor ) const
2006 return mImpl->mCursor[cursor].position;
2009 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
2011 mImpl->mCursor[cursor].color = color;
2014 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
2016 return mImpl->mCursor[cursor].color;
2019 void Decorator::StartCursorBlink()
2021 if ( !mImpl->mCursorBlinkTimer )
2023 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
2024 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
2027 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
2029 mImpl->mCursorBlinkTimer.Start();
2033 void Decorator::StopCursorBlink()
2035 if ( mImpl->mCursorBlinkTimer )
2037 mImpl->mCursorBlinkTimer.Stop();
2040 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2043 void Decorator::DelayCursorBlink()
2045 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2046 mImpl->mDelayCursorBlink = true;
2049 void Decorator::SetCursorBlinkInterval( float seconds )
2051 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
2054 float Decorator::GetCursorBlinkInterval() const
2056 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
2059 void Decorator::SetCursorBlinkDuration( float seconds )
2061 mImpl->mCursorBlinkDuration = seconds;
2064 float Decorator::GetCursorBlinkDuration() const
2066 return mImpl->mCursorBlinkDuration;
2069 void Decorator::SetCursorWidth( int width )
2071 mImpl->mCursorWidth = static_cast<float>( width );
2074 int Decorator::GetCursorWidth() const
2076 return static_cast<int>( mImpl->mCursorWidth );
2081 void Decorator::SetHandleActive( HandleType handleType, bool active )
2083 mImpl->mHandle[handleType].active = active;
2087 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
2089 mImpl->mIsHandlePreviouslyCrossed = false;
2092 // TODO: this is a work-around.
2093 // The problem is the handle actor does not receive the touch event with the Interrupt
2094 // state when the power button is pressed and the application goes to background.
2095 mImpl->mHandle[handleType].pressed = false;
2096 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
2097 ImageView imageView = mImpl->mHandle[handleType].actor;
2098 if( imageReleased && imageView )
2100 imageView.SetImage( imageReleased );
2106 bool Decorator::IsHandleActive( HandleType handleType ) const
2108 return mImpl->mHandle[handleType].active ;
2111 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
2113 mImpl->SetHandleImage( handleType, handleImageType, image );
2116 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
2118 return mImpl->mHandleImages[handleType][handleImageType];
2121 void Decorator::SetHandleColor( const Vector4& color )
2123 mImpl->mHandleColor = color;
2126 const Vector4& Decorator::GetHandleColor() const
2128 return mImpl->mHandleColor;
2131 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
2133 // Adjust handle's displacement
2134 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2136 handle.position.x = x;
2137 handle.position.y = y;
2138 handle.lineHeight = height;
2140 if( mImpl->mSmoothHandlePanEnabled )
2142 handle.grabDisplacementX = 0.f;
2143 handle.grabDisplacementY = 0.f;
2147 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2149 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2151 x = handle.position.x;
2152 y = handle.position.y;
2153 height = handle.lineHeight;
2156 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2158 return mImpl->mHandle[handleType].position;
2161 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2163 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2166 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2168 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2171 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2173 mImpl->mFlipSelectionHandlesOnCross = enable;
2176 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2178 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2179 mImpl->mFlipLeftSelectionHandleDirection = left;
2180 mImpl->mFlipRightSelectionHandleDirection = right;
2183 void Decorator::AddHighlight( unsigned int index, const Vector4& quad )
2185 *( mImpl->mHighlightQuadList.Begin() + index ) = quad;
2188 void Decorator::SetHighLightBox( const Vector2& position, const Size& size )
2190 mImpl->mHighlightPosition = position;
2191 mImpl->mHighlightSize = size;
2194 void Decorator::ClearHighlights()
2196 mImpl->mHighlightQuadList.Clear();
2197 mImpl->mHighlightPosition = Vector2::ZERO;
2200 void Decorator::ResizeHighlightQuads( unsigned int numberOfQuads )
2202 mImpl->mHighlightQuadList.Resize( numberOfQuads );
2205 void Decorator::SetHighlightColor( const Vector4& color )
2207 mImpl->mHighlightColor = color;
2210 const Vector4& Decorator::GetHighlightColor() const
2212 return mImpl->mHighlightColor;
2215 void Decorator::SetHighlightActive( bool active )
2217 mImpl->mIsHighlightBoxActive = active;
2220 bool Decorator::IsHighlightActive() const
2222 return mImpl->mIsHighlightBoxActive;
2225 void Decorator::SetTextDepth( int textDepth )
2227 mImpl->mTextDepth = textDepth;
2230 void Decorator::SetPopupActive( bool active )
2232 mImpl->mActiveCopyPastePopup = active;
2235 bool Decorator::IsPopupActive() const
2237 return mImpl->mActiveCopyPastePopup;
2240 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2242 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2244 if ( !mImpl->mCopyPastePopup.actor )
2246 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2247 #ifdef DECORATOR_DEBUG
2248 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
2250 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
2251 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::SetPopupPosition ); // Position popup after size negotiation
2254 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2257 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2259 return mImpl->mEnabledPopupButtons;
2264 void Decorator::SetScrollThreshold( float threshold )
2266 mImpl->SetScrollThreshold( threshold );
2269 float Decorator::GetScrollThreshold() const
2271 return mImpl->GetScrollThreshold();
2274 void Decorator::SetScrollSpeed( float speed )
2276 mImpl->SetScrollSpeed( speed );
2279 float Decorator::GetScrollSpeed() const
2281 return mImpl->GetScrollSpeed();
2284 void Decorator::NotifyEndOfScroll()
2286 mImpl->NotifyEndOfScroll();
2289 void Decorator::SetHorizontalScrollEnabled( bool enable )
2291 mImpl->mHorizontalScrollingEnabled = enable;
2294 bool Decorator::IsHorizontalScrollEnabled() const
2296 return mImpl->mHorizontalScrollingEnabled;
2299 void Decorator::SetVerticalScrollEnabled( bool enable )
2301 mImpl->mVerticalScrollingEnabled = enable;
2304 bool Decorator::IsVerticalScrollEnabled() const
2306 return mImpl->mVerticalScrollingEnabled;
2309 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2311 mImpl->mSmoothHandlePanEnabled = enable;
2314 bool Decorator::IsSmoothHandlePanEnabled() const
2316 return mImpl->mSmoothHandlePanEnabled;
2319 Decorator::~Decorator()
2324 Decorator::Decorator( ControllerInterface& controller,
2325 TextSelectionPopupCallbackInterface& callbackInterface )
2328 mImpl = new Decorator::Impl( controller, callbackInterface );
2333 } // namespace Toolkit