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 ),
186 grabDisplacementX( 0.f ),
187 grabDisplacementY( 0.f ),
189 horizontallyVisible( false ),
190 verticallyVisible( false ),
192 verticallyFlippedPreferred( false ),
193 horizontallyFlipped( false ),
194 verticallyFlipped( false ),
195 verticallyFlippedOnTouch( false )
201 ImageView markerActor;
204 Vector2 globalPosition;
206 float lineHeight; ///< Not the handle height
207 float grabDisplacementX;
208 float grabDisplacementY;
210 bool horizontallyVisible : 1;
211 bool verticallyVisible : 1;
213 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
214 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
215 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
216 bool verticallyFlippedOnTouch : 1; ///< Whether the handle is vertically flipped on touch.
226 TextSelectionPopup actor;
230 Impl( ControllerInterface& controller,
231 TextSelectionPopupCallbackInterface& callbackInterface )
232 : mController( controller ),
233 mEnabledPopupButtons( TextSelectionPopup::NONE ),
234 mTextSelectionPopupCallbackInterface( callbackInterface ),
235 mHandleColor( HANDLE_COLOR ),
237 mHighlightColor( LIGHT_BLUE ),
238 mHighlightPosition( Vector2::ZERO ),
239 mActiveCursor( ACTIVE_CURSOR_NONE ),
240 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
241 mCursorBlinkDuration( 0.0f ),
242 mCursorWidth( CURSOR_WIDTH ),
243 mHandleScrolling( HANDLE_TYPE_COUNT ),
244 mHandleReleased( HANDLE_TYPE_COUNT ),
245 mScrollDirection( SCROLL_NONE ),
246 mScrollThreshold( SCROLL_THRESHOLD ),
247 mScrollSpeed( SCROLL_SPEED ),
248 mScrollDistance( SCROLL_DISTANCE ),
250 mActiveCopyPastePopup( false ),
251 mPopupSetNewPosition( true ),
252 mCursorBlinkStatus( true ),
253 mDelayCursorBlink( false ),
254 mPrimaryCursorVisible( false ),
255 mSecondaryCursorVisible( false ),
256 mFlipSelectionHandlesOnCross( false ),
257 mFlipLeftSelectionHandleDirection( false ),
258 mFlipRightSelectionHandleDirection( false ),
259 mIsHandlePanning( false ),
260 mIsHandleCurrentlyCrossed( false ),
261 mIsHandlePreviouslyCrossed( false ),
262 mNotifyEndOfScroll( false ),
263 mHorizontalScrollingEnabled( false ),
264 mVerticalScrollingEnabled( false ),
265 mSmoothHandlePanEnabled( false ),
266 mIsHighlightBoxActive( false )
268 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
269 mHighlightShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
274 * Relayout of the decorations owned by the decorator.
275 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
277 void Relayout( const Vector2& size )
281 // TODO - Remove this if nothing is active
284 // Show or hide the cursors
289 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
290 mPrimaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
291 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
292 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
293 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
294 if( mPrimaryCursorVisible )
296 mPrimaryCursor.SetPosition( cursor.position.x,
298 mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
300 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
302 if( mSecondaryCursor )
304 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
305 mSecondaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
306 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
307 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
308 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
309 if( mSecondaryCursorVisible )
311 mSecondaryCursor.SetPosition( cursor.position.x,
313 mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
315 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
318 // Show or hide the grab handle
319 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
320 bool newGrabHandlePosition = false;
321 grabHandle.horizontallyVisible = false;
322 grabHandle.verticallyVisible = false;
323 if( grabHandle.active )
325 grabHandle.horizontallyVisible = ( ( mControlSize.width - ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) ) > -Math::MACHINE_EPSILON_1000 ) &&
326 ( grabHandle.position.x > -Math::MACHINE_EPSILON_1000 ) );
327 grabHandle.verticallyVisible = ( ( ( mControlSize.height - grabHandle.lineHeight ) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) &&
328 ( grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) );
330 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible;
335 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
336 SetGrabHandlePosition();
338 // Sets the grab handle image according if it's pressed, flipped, etc.
339 SetHandleImage( GRAB_HANDLE );
341 newGrabHandlePosition = true;
344 if( grabHandle.actor )
346 grabHandle.actor.SetVisible( isVisible );
349 else if( grabHandle.actor )
351 grabHandle.actor.Unparent();
354 // Show or hide the selection handles/highlight
355 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
356 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
357 bool newPrimaryHandlePosition = false;
358 bool newSecondaryHandlePosition = false;
360 primary.horizontallyVisible = ( ( mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
361 ( primary.position.x > -Math::MACHINE_EPSILON_1000 ) );
362 primary.verticallyVisible = ( ( ( mControlSize.height - primary.lineHeight ) - primary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
363 ( primary.position.y + ( primary.verticallyFlipped ? 0.f : primary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
364 secondary.horizontallyVisible = ( ( mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
365 ( secondary.position.x > -Math::MACHINE_EPSILON_1000 ) );
366 secondary.verticallyVisible = ( ( ( mControlSize.height - secondary.lineHeight ) - secondary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
367 ( secondary.position.y + ( secondary.verticallyFlipped ? 0.f : secondary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
369 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
370 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
372 if( primary.active || secondary.active )
374 if( primaryVisible || secondaryVisible )
376 CreateSelectionHandles();
380 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
382 // Sets the primary handle image according if it's pressed, flipped, etc.
383 SetHandleImage( LEFT_SELECTION_HANDLE );
385 SetSelectionHandleMarkerSize( primary );
387 newPrimaryHandlePosition = true;
390 if( secondaryVisible )
392 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
394 // Sets the secondary handle image according if it's pressed, flipped, etc.
395 SetHandleImage( RIGHT_SELECTION_HANDLE );
397 SetSelectionHandleMarkerSize( secondary );
399 newSecondaryHandlePosition = true;
405 primary.actor.SetVisible( primaryVisible );
407 if( secondary.actor )
409 secondary.actor.SetVisible( secondaryVisible );
417 primary.actor.Unparent();
419 if( secondary.actor )
421 secondary.actor.Unparent();
425 if( mIsHighlightBoxActive )
432 if( mHighlightActor )
434 mHighlightActor.Unparent();
438 if( newGrabHandlePosition ||
439 newPrimaryHandlePosition ||
440 newSecondaryHandlePosition )
442 // Setup property notifications to find whether the handles leave the boundaries of the current display.
443 SetupActiveLayerPropertyNotifications();
446 if( mActiveCopyPastePopup &&
447 ( primaryVisible || secondaryVisible ) )
450 mPopupSetNewPosition = true;
454 if( mCopyPastePopup.actor )
456 mCopyPastePopup.actor.HidePopup();
457 mPopupSetNewPosition = true;
462 void UpdatePositions( const Vector2& scrollOffset )
464 mCursor[PRIMARY_CURSOR].position += scrollOffset;
465 mCursor[SECONDARY_CURSOR].position += scrollOffset;
466 mHandle[ GRAB_HANDLE ].position += scrollOffset;
467 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
468 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
469 mHighlightPosition += scrollOffset;
474 if( !mCopyPastePopup.actor )
479 if( !mCopyPastePopup.actor.GetParent() )
481 mActiveLayer.Add( mCopyPastePopup.actor );
484 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
485 mCopyPastePopup.actor.ShowPopup();
488 float CalculateVerticalPopUpPosition( float halfHeight, bool preferBelow )
490 float yPosition = 0.f;
492 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
493 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
494 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
496 if( primaryHandle.active || secondaryHandle.active )
498 // The origin of the decorator's coordinate system in world coords.
499 const Vector3 originWorldCoords = mActiveLayer.GetCurrentWorldPosition() - mActiveLayer.GetCurrentSize() * ACTIVE_LAYER_ANCHOR_POINT;
503 // Find out if there is enough space for the popup at the bottom.
504 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
505 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
507 float maxY = std::max( primaryBelowY, secondaryBelowY );
509 yPosition = halfHeight + maxY;
511 if( originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w )
513 // Does not fit below.
515 // Try to fit first below the non active handle. Otherwise above the active handle.
516 if( RIGHT_SELECTION_HANDLE == mHandleReleased )
518 if( primaryBelowY < secondaryBelowY )
520 yPosition = halfHeight + primaryBelowY;
524 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
527 else if( LEFT_SELECTION_HANDLE == mHandleReleased )
529 if( secondaryBelowY < primaryBelowY )
531 yPosition = halfHeight + secondaryBelowY;
535 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
539 // Check the handle is whithin the decoration box.
540 if( originWorldCoords.y + yPosition < mBoundingBox.y )
542 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
545 if( originWorldCoords.y + yPosition > mBoundingBox.w )
547 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
553 // Find out if there is enough space for the popup at the top.
554 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
555 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
557 float minY = std::min( primaryTopY, secondaryTopY );
559 yPosition = -halfHeight + minY;
561 } // ( primaryHandle.active || secondaryHandle.active )
562 else if( grabHandle.active )
566 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
570 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
577 void ConstrainPopupPosition( const Vector3& popupHalfSize )
579 // Check if the popup is within the boundaries of the decoration box.
581 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
583 // The origin of the decorator's coordinate system in world coords.
584 const Vector3 originWorldCoords = mActiveLayer.GetCurrentWorldPosition() - mActiveLayer.GetCurrentSize() * ACTIVE_LAYER_ANCHOR_POINT;
586 // The popup's position in world coords.
587 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
589 if( popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x )
591 mCopyPastePopup.position.x += mBoundingBox.x - ( popupPositionWorldCoords.x - popupHalfSize.width );
593 else if( popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z )
595 mCopyPastePopup.position.x += mBoundingBox.z - ( popupPositionWorldCoords.x + popupHalfSize.width );
598 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
599 if( popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y )
601 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition( popupHalfSize.height, true ); // true -> prefer to set the popup's position below.
605 void SetPopupPosition( Actor actor )
607 if( !mActiveCopyPastePopup )
612 // Retrieves the popup's size after relayout.
613 const Vector3 popupSize( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
614 const Vector3 popupHalfSize = popupSize * 0.5f;
616 if( mPopupSetNewPosition )
618 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
619 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
620 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
622 if( primaryHandle.active || secondaryHandle.active )
624 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
625 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
627 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
629 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - ( primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING );
630 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - ( secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING );
632 mCopyPastePopup.position.y = std::min( primaryY, secondaryY );
634 else if( grabHandle.active )
636 mCopyPastePopup.position.x = grabHandle.position.x;
638 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - ( grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING );
640 } // mPopupSetNewPosition
642 // It may change the popup's position to fit within the decoration box.
643 ConstrainPopupPosition( popupHalfSize );
645 SetUpPopupPositionNotifications( popupHalfSize );
647 // Prevent pixel mis-alignment by rounding down.
648 mCopyPastePopup.position.x = floorf( mCopyPastePopup.position.x );
649 mCopyPastePopup.position.y = floorf( mCopyPastePopup.position.y );
651 mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
652 mPopupSetNewPosition = false;
655 void CreateCursor( Control& cursor, const Vector4& color )
657 cursor = Control::New();
658 cursor.SetBackgroundColor( color );
659 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT );
660 cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
663 // Add or Remove cursor(s) from parent
666 if( mActiveCursor == ACTIVE_CURSOR_NONE )
670 mPrimaryCursor.Unparent();
672 if( mSecondaryCursor )
674 mSecondaryCursor.Unparent();
679 // Create Primary and or Secondary Cursor(s) if active and add to parent
680 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
681 mActiveCursor == ACTIVE_CURSOR_BOTH )
683 if ( !mPrimaryCursor )
685 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
686 #ifdef DECORATOR_DEBUG
687 mPrimaryCursor.SetName( "PrimaryCursorActor" );
691 if( !mPrimaryCursor.GetParent() )
693 mActiveLayer.Add( mPrimaryCursor );
697 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
699 if ( !mSecondaryCursor )
701 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
702 #ifdef DECORATOR_DEBUG
703 mSecondaryCursor.SetName( "SecondaryCursorActor" );
707 if( !mSecondaryCursor.GetParent() )
709 mActiveLayer.Add( mSecondaryCursor );
714 if( mSecondaryCursor )
716 mSecondaryCursor.Unparent();
722 bool OnCursorBlinkTimerTick()
724 if( !mDelayCursorBlink )
727 if ( mPrimaryCursor )
729 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
731 if ( mSecondaryCursor )
733 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
736 mCursorBlinkStatus = !mCursorBlinkStatus;
741 mDelayCursorBlink = false;
749 // Will consume tap gestures on handles.
750 mTapDetector = TapGestureDetector::New();
752 // Will consume double tap gestures on handles.
753 mTapDetector.SetMaximumTapsRequired( 2u );
755 // Will consume long press gestures on handles.
756 mLongPressDetector = LongPressGestureDetector::New();
758 // Detects pan gestures on handles.
759 mPanDetector = PanGestureDetector::New();
760 mPanDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
763 void CreateActiveLayer()
767 mActiveLayer = Layer::New();
768 #ifdef DECORATOR_DEBUG
769 mActiveLayer.SetName ( "ActiveLayerActor" );
772 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
773 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
775 // Add the active layer telling the controller it doesn't need clipping.
776 mController.AddDecoration( mActiveLayer, false );
779 mActiveLayer.RaiseToTop();
782 void SetSelectionHandleMarkerSize( HandleImpl& handle )
784 if( handle.markerActor )
786 handle.markerActor.SetSize( 0, handle.lineHeight );
790 void CreateGrabHandle()
792 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
793 if( !grabHandle.actor )
795 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
797 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
798 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
799 grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
801 // Area that Grab handle responds to, larger than actual handle so easier to move
802 #ifdef DECORATOR_DEBUG
803 grabHandle.actor.SetName( "GrabHandleActor" );
804 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
806 grabHandle.grabArea = Control::New();
807 Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
808 control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
809 grabHandle.grabArea.SetName( "GrabArea" );
813 grabHandle.grabArea = Actor::New();
814 grabHandle.grabArea.SetName( "GrabArea" );
817 grabHandle.grabArea = Actor::New();
820 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
821 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
822 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
823 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
824 grabHandle.actor.Add( grabHandle.grabArea );
825 grabHandle.actor.SetColor( mHandleColor );
827 grabHandle.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
829 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
830 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
831 mTapDetector.Attach( grabHandle.actor );
832 mLongPressDetector.Attach( grabHandle.actor );
834 // The grab handle's area is attached to the pan detector.
835 // The OnPan() method is connected to the signals emitted by the pan detector.
836 mPanDetector.Attach( grabHandle.grabArea );
838 mActiveLayer.Add( grabHandle.actor );
842 if( grabHandle.actor && !grabHandle.actor.GetParent() )
844 mActiveLayer.Add( grabHandle.actor );
848 void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
852 handle.markerActor = ImageView::New( image );
853 handle.markerActor.SetColor( mHandleColor );
854 handle.actor.Add( handle.markerActor );
856 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
858 if( LEFT_SELECTION_HANDLE == handleType )
860 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
861 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
863 else if( RIGHT_SELECTION_HANDLE == handleType )
865 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
866 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
871 void CreateSelectionHandles()
873 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
876 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
878 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
879 #ifdef DECORATOR_DEBUG
880 primary.actor.SetName("SelectionHandleOne");
882 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
883 GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
884 primary.actor.SetColor( mHandleColor );
886 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
887 #ifdef DECORATOR_DEBUG
888 primary.grabArea.SetName("SelectionHandleOneGrabArea");
890 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
891 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
892 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
893 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
895 primary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
897 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
898 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
899 mTapDetector.Attach( primary.actor );
900 mLongPressDetector.Attach( primary.actor );
902 // The handle's area is attached to the pan detector.
903 // The OnPan() method is connected to the signals emitted by the pan detector.
904 mPanDetector.Attach( primary.grabArea );
906 primary.actor.Add( primary.grabArea );
908 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
912 if( primary.actor && !primary.actor.GetParent() )
914 mActiveLayer.Add( primary.actor );
917 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
918 if( !secondary.actor )
920 if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
922 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
923 #ifdef DECORATOR_DEBUG
924 secondary.actor.SetName("SelectionHandleTwo");
926 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
927 GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
928 secondary.actor.SetColor( mHandleColor );
930 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
931 #ifdef DECORATOR_DEBUG
932 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
934 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
935 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
936 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
937 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
939 secondary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
941 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
942 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
943 mTapDetector.Attach( secondary.actor );
944 mLongPressDetector.Attach( secondary.actor );
946 // The handle's area is attached to the pan detector.
947 // The OnPan() method is connected to the signals emitted by the pan detector.
948 mPanDetector.Attach( secondary.grabArea );
950 secondary.actor.Add( secondary.grabArea );
952 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
956 if( secondary.actor && !secondary.actor.GetParent() )
958 mActiveLayer.Add( secondary.actor );
962 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
964 // Gets the world position of the active layer. The active layer is where the handles are added.
965 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
967 // The grab handle position in world coords.
968 // The active layer's world position is the center of the active layer. The origin of the
969 // coord system of the handles is the top left of the active layer.
970 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f );
971 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f );
974 void SetGrabHandlePosition()
976 // Reference to the grab handle.
977 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
979 // Transforms the handle position into world coordinates.
980 // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
981 // as it's transforming the handle's position set by the text-controller and not
982 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
983 // retrieves the position of the center of the actor but the handle's position set
984 // by the text controller is not the center of the actor.
985 Vector2 grabHandleWorldPosition;
986 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
988 // Check if the grab handle exceeds the boundaries of the decoration box.
989 // At the moment only the height is checked for the grab handle.
991 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
992 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
993 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
995 // The grab handle 'y' position in local coords.
996 // If the grab handle exceeds the bottom of the decoration box,
997 // set the 'y' position to the top of the line.
998 // The SetGrabHandleImage() method will change the orientation.
999 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
1001 if( grabHandle.actor )
1003 grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ) + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f ),
1004 yLocalPosition + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f ) );
1008 void SetSelectionHandlePosition( HandleType type )
1010 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
1012 // Reference to the selection handle.
1013 HandleImpl& handle = mHandle[type];
1015 // Transforms the handle position into world coordinates.
1016 // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
1017 // as it's transforming the handle's position set by the text-controller and not
1018 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
1019 // retrieves the position of the center of the actor but the handle's position set
1020 // by the text controller is not the center of the actor.
1021 Vector2 handleWorldPosition;
1022 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
1024 // Whether to flip the handle (horizontally).
1025 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1027 // Whether to flip the handles if they are crossed.
1028 bool crossFlip = false;
1029 if( mFlipSelectionHandlesOnCross || !mIsHandlePanning )
1031 crossFlip = mIsHandleCurrentlyCrossed;
1034 // Whether the handle was crossed before start the panning.
1035 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1037 // Does not flip if both conditions are true (double flip)
1038 flipHandle = flipHandle != ( crossFlip || isHandlePreviouslyCrossed );
1040 // Will flip the handles vertically if the user prefers it.
1041 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1043 if( crossFlip || isHandlePreviouslyCrossed )
1045 if( isPrimaryHandle )
1047 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1051 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1055 // Check if the selection handle exceeds the boundaries of the decoration box.
1056 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
1057 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
1059 // Does not flip if both conditions are true (double flip)
1060 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
1064 if( handle.actor && !handle.horizontallyFlipped )
1066 // Change the anchor point to flip the image.
1067 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
1069 handle.horizontallyFlipped = true;
1074 if( handle.actor && handle.horizontallyFlipped )
1076 // Reset the anchor point.
1077 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
1079 handle.horizontallyFlipped = false;
1083 // Whether to flip the handle vertically.
1084 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
1085 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
1086 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
1088 // The primary selection handle 'y' position in local coords.
1089 // If the handle exceeds the bottom of the decoration box,
1090 // set the 'y' position to the top of the line.
1091 // The SetHandleImage() method will change the orientation.
1092 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1096 handle.actor.SetPosition( handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f ),
1097 yLocalPosition + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f ) );
1101 void SetHandleImage( HandleType type )
1103 HandleImpl& handle = mHandle[type];
1105 HandleType markerType = HANDLE_TYPE_COUNT;
1106 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1107 if( LEFT_SELECTION_HANDLE == type )
1109 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1110 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1112 else if( RIGHT_SELECTION_HANDLE == type )
1114 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1115 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1118 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1121 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1123 handle.actor.SetImage( mHandleImages[type][imageType] );
1126 if( HANDLE_TYPE_COUNT != markerType )
1128 if( handle.markerActor )
1130 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1131 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
1135 // Whether to flip the handle vertically.
1138 handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
1142 void CreateHighlight()
1144 if( !mHighlightActor )
1146 mHighlightActor = Actor::New();
1148 #ifdef DECORATOR_DEBUG
1149 mHighlightActor.SetName( "HighlightActor" );
1151 mHighlightActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
1152 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
1153 mHighlightActor.SetColor( mHighlightColor );
1154 mHighlightActor.SetColorMode( USE_OWN_COLOR );
1157 // Add the highlight box telling the controller it needs clipping.
1158 mController.AddDecoration( mHighlightActor, true );
1161 void UpdateHighlight()
1163 if ( mHighlightActor )
1165 // Sets the position of the highlight actor inside the decorator.
1166 mHighlightActor.SetPosition( mHighlightPosition.x,
1167 mHighlightPosition.y );
1169 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1170 if( 0u != numberOfQuads )
1172 // Set the size of the highlighted text to the actor.
1173 mHighlightActor.SetSize( mHighlightSize );
1175 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1176 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1177 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1179 Vector<Vector2> vertices;
1180 Vector<unsigned short> indices;
1182 vertices.Reserve( 4u * numberOfQuads );
1183 indices.Reserve( 6u * numberOfQuads );
1185 // Index to the vertex.
1186 unsigned int v = 0u;
1188 // Traverse all quads.
1189 for( Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1190 endIt = mHighlightQuadList.End();
1194 const Vector4& quad = *it;
1199 vertex.x = quad.x - offsetX;
1200 vertex.y = quad.y - offsetY;
1201 vertices.PushBack( vertex );
1204 vertex.x = quad.z - offsetX;
1205 vertex.y = quad.y - offsetY;
1206 vertices.PushBack( vertex );
1208 // bottom-left (v+2)
1209 vertex.x = quad.x - offsetX;
1210 vertex.y = quad.w - offsetY;
1211 vertices.PushBack( vertex );
1213 // bottom-right (v+3)
1214 vertex.x = quad.z - offsetX;
1215 vertex.y = quad.w - offsetY;
1216 vertices.PushBack( vertex );
1218 // triangle A (3, 1, 0)
1219 indices.PushBack( v + 3 );
1220 indices.PushBack( v + 1 );
1221 indices.PushBack( v );
1223 // triangle B (0, 2, 3)
1224 indices.PushBack( v );
1225 indices.PushBack( v + 2 );
1226 indices.PushBack( v + 3 );
1229 if( ! mQuadVertices )
1231 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
1234 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1236 if( !mQuadGeometry )
1238 mQuadGeometry = Geometry::New();
1239 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1241 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1243 if( !mHighlightRenderer )
1245 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1246 mHighlightActor.AddRenderer( mHighlightRenderer );
1250 mHighlightQuadList.Clear();
1252 if( mHighlightRenderer )
1254 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1259 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1261 if( Gesture::Started == gesture.state )
1263 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1265 handle.globalPosition.x = handle.position.x;
1266 handle.globalPosition.y = handle.position.y;
1269 handle.grabDisplacementX += gesture.displacement.x;
1270 handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.displacement.y );
1272 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1273 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1274 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f );
1276 if( ( Gesture::Started == gesture.state ) ||
1277 ( Gesture::Continuing == gesture.state ) )
1280 mController.GetTargetSize( targetSize );
1282 if( mHorizontalScrollingEnabled &&
1283 ( x < mScrollThreshold ) )
1285 mScrollDirection = SCROLL_RIGHT;
1286 mHandleScrolling = type;
1289 else if( mHorizontalScrollingEnabled &&
1290 ( x > targetSize.width - mScrollThreshold ) )
1292 mScrollDirection = SCROLL_LEFT;
1293 mHandleScrolling = type;
1296 else if( mVerticalScrollingEnabled &&
1297 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1299 mScrollDirection = SCROLL_TOP;
1300 mHandleScrolling = type;
1303 else if( mVerticalScrollingEnabled &&
1304 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1306 mScrollDirection = SCROLL_BOTTOM;
1307 mHandleScrolling = type;
1312 mHandleScrolling = HANDLE_TYPE_COUNT;
1314 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1317 mIsHandlePanning = true;
1319 else if( ( Gesture::Finished == gesture.state ) ||
1320 ( Gesture::Cancelled == gesture.state ) )
1323 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1325 mNotifyEndOfScroll = false;
1326 mHandleScrolling = HANDLE_TYPE_COUNT;
1328 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1332 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1337 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1339 handle.pressed = false;
1341 mIsHandlePanning = false;
1345 void OnPan( Actor actor, const PanGesture& gesture )
1347 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1348 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1349 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1351 if( actor == grabHandle.grabArea )
1353 DoPan( grabHandle, GRAB_HANDLE, gesture );
1355 else if( actor == primarySelectionHandle.grabArea )
1357 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1359 else if( actor == secondarySelectionHandle.grabArea )
1361 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1365 bool OnGrabHandleTouched( Actor actor, const TouchData& touch )
1367 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1369 // Switch between pressed/release grab-handle images
1370 if( touch.GetPointCount() > 0 &&
1373 const PointState::Type state = touch.GetState( 0 );
1375 if( PointState::DOWN == state )
1377 grabHandle.pressed = true;
1379 else if( ( PointState::UP == state ) ||
1380 ( PointState::INTERRUPTED == state ) )
1382 grabHandle.pressed = false;
1385 SetHandleImage( GRAB_HANDLE );
1388 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1392 bool OnHandleOneTouched( Actor actor, const TouchData& touch )
1394 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1396 // Switch between pressed/release selection handle images
1397 if( touch.GetPointCount() > 0 &&
1398 primarySelectionHandle.actor )
1400 const PointState::Type state = touch.GetState( 0 );
1402 if( PointState::DOWN == state )
1404 primarySelectionHandle.pressed = true;
1405 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1407 else if( ( PointState::UP == state ) ||
1408 ( PointState::INTERRUPTED == state ) )
1410 primarySelectionHandle.pressed = false;
1411 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1412 mIsHandlePanning = false;
1413 mHandleReleased = LEFT_SELECTION_HANDLE;
1416 SetHandleImage( LEFT_SELECTION_HANDLE );
1419 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1423 bool OnHandleTwoTouched( Actor actor, const TouchData& touch )
1425 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1427 // Switch between pressed/release selection handle images
1428 if( touch.GetPointCount() > 0 &&
1429 secondarySelectionHandle.actor )
1431 const PointState::Type state = touch.GetState( 0 );
1433 if( PointState::DOWN == state )
1435 secondarySelectionHandle.pressed = true;
1436 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1438 else if( ( PointState::UP == state ) ||
1439 ( PointState::INTERRUPTED == state ) )
1441 secondarySelectionHandle.pressed = false;
1442 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1443 mIsHandlePanning = false;
1444 mHandleReleased = RIGHT_SELECTION_HANDLE;
1447 SetHandleImage( RIGHT_SELECTION_HANDLE );
1450 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1454 void HandleResetPosition( PropertyNotification& source )
1456 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1458 if( grabHandle.active )
1460 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1461 SetGrabHandlePosition();
1463 // Sets the grab handle image according if it's pressed, flipped, etc.
1464 SetHandleImage( GRAB_HANDLE );
1468 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1469 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1471 // Sets the primary handle image according if it's pressed, flipped, etc.
1472 SetHandleImage( LEFT_SELECTION_HANDLE );
1474 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1475 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1477 // Sets the secondary handle image according if it's pressed, flipped, etc.
1478 SetHandleImage( RIGHT_SELECTION_HANDLE );
1482 void SetupActiveLayerPropertyNotifications()
1489 // Vertical notifications.
1491 // Disconnect any previous connected callback.
1492 if( mHandleVerticalLessThanNotification )
1494 mHandleVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1495 mActiveLayer.RemovePropertyNotification( mHandleVerticalLessThanNotification );
1498 if( mHandleVerticalGreaterThanNotification )
1500 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1501 mActiveLayer.RemovePropertyNotification( mHandleVerticalGreaterThanNotification );
1504 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1505 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1506 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1508 if( grabHandle.active )
1510 if( grabHandle.verticallyFlipped )
1512 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1513 mHandleVerticalGreaterThanNotification.Reset();
1515 // The vertical distance from the center of the active layer to the top edje of the display.
1516 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1518 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1519 LessThanCondition( mBoundingBox.y + topHeight ) );
1521 // Notifies the change from false to true and from true to false.
1522 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1524 // Connects the signals with the callbacks.
1525 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1529 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1530 mHandleVerticalLessThanNotification.Reset();
1532 // The vertical distance from the center of the active layer to the bottom edje of the display.
1533 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1535 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1536 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1538 // Notifies the change from false to true and from true to false.
1539 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1541 // Connects the signals with the callbacks.
1542 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1545 else // The selection handles are active
1547 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1549 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1550 mHandleVerticalGreaterThanNotification.Reset();
1552 // The vertical distance from the center of the active layer to the top edje of the display.
1553 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1555 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1556 LessThanCondition( mBoundingBox.y + topHeight ) );
1558 // Notifies the change from false to true and from true to false.
1559 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1561 // Connects the signals with the callbacks.
1562 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1564 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1566 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1567 mHandleVerticalLessThanNotification.Reset();
1569 // The vertical distance from the center of the active layer to the bottom edje of the display.
1570 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1571 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1573 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1574 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1576 // Notifies the change from false to true and from true to false.
1577 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1579 // Connects the signals with the callbacks.
1580 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1584 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1586 // The vertical distance from the center of the active layer to the top edje of the display.
1587 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1588 -primaryHandle.position.y + primaryHandle.size.height :
1589 -secondaryHandle.position.y + secondaryHandle.size.height );
1591 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1592 LessThanCondition( mBoundingBox.y + topHeight ) );
1594 // Notifies the change from false to true and from true to false.
1595 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1597 // Connects the signals with the callbacks.
1598 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1600 // The vertical distance from the center of the active layer to the bottom edje of the display.
1601 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1602 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1603 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1605 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1606 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1608 // Notifies the change from false to true and from true to false.
1609 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1611 // Connects the signals with the callbacks.
1612 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1616 // Horizontal notifications.
1618 // Disconnect any previous connected callback.
1619 if( mHandleHorizontalLessThanNotification )
1621 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1622 mActiveLayer.RemovePropertyNotification( mHandleHorizontalLessThanNotification );
1625 if( mHandleHorizontalGreaterThanNotification )
1627 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1628 mActiveLayer.RemovePropertyNotification( mHandleHorizontalGreaterThanNotification );
1631 if( primaryHandle.active || secondaryHandle.active )
1633 // The horizontal distance from the center of the active layer to the left edje of the display.
1634 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1635 -secondaryHandle.position.x + secondaryHandle.size.width );
1637 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1638 LessThanCondition( mBoundingBox.x + leftWidth ) );
1640 // Notifies the change from false to true and from true to false.
1641 mHandleHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1643 // Connects the signals with the callbacks.
1644 mHandleHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1646 // The horizontal distance from the center of the active layer to the right edje of the display.
1647 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1648 secondaryHandle.position.x + secondaryHandle.size.width );
1650 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1651 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1653 // Notifies the change from false to true and from true to false.
1654 mHandleHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1656 // Connects the signals with the callbacks.
1657 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1663 float AlternatePopUpPositionRelativeToCursor( bool topBottom )
1665 float alternativePosition = 0.0f;
1667 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1669 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1670 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1671 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1672 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1674 if( primaryHandle.active || secondaryHandle.active )
1676 float handleY = 0.f;
1677 float maxHandleHeight = 0.f;
1679 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1680 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1682 if( primaryVisible && secondaryVisible )
1684 handleY = std::max( primaryHandle.position.y, secondaryHandle.position.y );
1685 maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1687 else if( primaryVisible && !secondaryVisible )
1689 handleY = primaryHandle.position.y;
1690 maxHandleHeight = primaryHandle.size.height;
1692 else if( !primaryVisible && secondaryVisible )
1694 handleY = secondaryHandle.position.y;
1695 maxHandleHeight = secondaryHandle.size.height;
1698 alternativePosition = handleY + ( topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight );
1702 alternativePosition = cursor.position.y + ( topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height );
1705 return alternativePosition;
1708 void PopUpLeavesTopBoundary( PropertyNotification& source )
1710 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1712 // Sets the position of the popup below.
1713 mCopyPastePopup.actor.SetY( floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, true ) ) );
1716 void PopUpLeavesBottomBoundary( PropertyNotification& source )
1718 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1720 // Sets the position of the popup above.
1721 mCopyPastePopup.actor.SetY( floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, false ) ) );
1724 void SetUpPopupPositionNotifications( const Vector3& popupHalfSize )
1726 // Disconnect any previous connected callback.
1727 if( mPopupTopExceedNotification )
1729 mPopupTopExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1730 mCopyPastePopup.actor.RemovePropertyNotification( mPopupTopExceedNotification );
1733 if( mPopupBottomExceedNotification )
1735 mPopupBottomExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1736 mCopyPastePopup.actor.RemovePropertyNotification( mPopupBottomExceedNotification );
1739 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1741 // Exceeding vertical boundary
1743 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1744 LessThanCondition( mBoundingBox.y + popupHalfSize.height ) );
1746 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1747 GreaterThanCondition( mBoundingBox.w - popupHalfSize.height ) );
1749 // Notifies the change from false to true and from true to false.
1750 mPopupTopExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1751 mPopupBottomExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1753 mPopupTopExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1754 mPopupBottomExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1757 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1759 HandleImpl& handle = mHandle[handleType];
1760 handle.size = Size( image.GetWidth(), image.GetHeight() );
1762 mHandleImages[handleType][handleImageType] = image;
1765 void SetScrollThreshold( float threshold )
1767 mScrollThreshold = threshold;
1770 float GetScrollThreshold() const
1772 return mScrollThreshold;
1775 void SetScrollSpeed( float speed )
1777 mScrollSpeed = speed;
1778 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1781 float GetScrollSpeed() const
1783 return mScrollSpeed;
1786 void NotifyEndOfScroll()
1792 mNotifyEndOfScroll = true;
1797 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1799 * It only starts the timer if it's already created.
1801 void StartScrollTimer()
1805 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1806 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1809 if( !mScrollTimer.IsRunning() )
1811 mScrollTimer.Start();
1816 * Stops the timer used to scroll the text.
1818 void StopScrollTimer()
1822 mScrollTimer.Stop();
1827 * Callback called by the timer used to scroll the text.
1829 * It calculates and sets a new scroll position.
1831 bool OnScrollTimerTick()
1833 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1838 switch( mScrollDirection )
1842 x = mScrollDistance;
1847 x = -mScrollDistance;
1852 y = mScrollDistance;
1857 y = -mScrollDistance;
1864 mController.DecorationEvent( mHandleScrolling,
1873 ControllerInterface& mController;
1875 TapGestureDetector mTapDetector;
1876 PanGestureDetector mPanDetector;
1877 LongPressGestureDetector mLongPressDetector;
1879 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1880 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1882 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1883 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1884 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1885 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1886 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1887 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1888 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1889 Control mPrimaryCursor;
1890 Control mSecondaryCursor;
1892 Actor mHighlightActor; ///< Actor to display highlight
1893 Renderer mHighlightRenderer;
1894 Shader mHighlightShader; ///< Shader used for highlight
1895 Property::Map mQuadVertexFormat;
1896 PopupImpl mCopyPastePopup;
1897 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1898 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1900 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1901 Vector4 mHandleColor;
1903 CursorImpl mCursor[CURSOR_COUNT];
1904 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1906 PropertyBuffer mQuadVertices;
1907 Geometry mQuadGeometry;
1908 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1910 Vector4 mBoundingBox; ///< The bounding box in world coords.
1911 Vector4 mHighlightColor; ///< Color of the highlight
1912 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1913 Size mHighlightSize; ///< The size of the highlighted text.
1914 Size mControlSize; ///< The control's size. Set by the Relayout.
1916 unsigned int mActiveCursor;
1917 unsigned int mCursorBlinkInterval;
1918 float mCursorBlinkDuration;
1919 float mCursorWidth; ///< The width of the cursors in pixels.
1920 HandleType mHandleScrolling; ///< The handle which is scrolling.
1921 HandleType mHandleReleased; ///< The last handle released.
1922 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1923 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1924 float mScrollSpeed; ///< The scroll speed in pixels per second.
1925 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1926 int mTextDepth; ///< The depth used to render the text.
1928 bool mActiveCopyPastePopup : 1;
1929 bool mPopupSetNewPosition : 1;
1930 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1931 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1932 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1933 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1934 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1935 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1936 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1937 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1938 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1939 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1940 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1941 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1942 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1943 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1944 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1947 DecoratorPtr Decorator::New( ControllerInterface& controller,
1948 TextSelectionPopupCallbackInterface& callbackInterface )
1950 return DecoratorPtr( new Decorator( controller,
1951 callbackInterface ) );
1954 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1956 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1959 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1961 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1964 void Decorator::Relayout( const Vector2& size )
1966 mImpl->Relayout( size );
1969 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1971 mImpl->UpdatePositions( scrollOffset );
1976 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1978 mImpl->mActiveCursor = activeCursor;
1981 unsigned int Decorator::GetActiveCursor() const
1983 return mImpl->mActiveCursor;
1986 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1988 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1990 cursorImpl.position.x = x;
1991 cursorImpl.position.y = y;
1992 cursorImpl.cursorHeight = cursorHeight;
1993 cursorImpl.lineHeight = lineHeight;
1996 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1998 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2000 x = cursorImpl.position.x;
2001 y = cursorImpl.position.y;
2002 cursorHeight = cursorImpl.cursorHeight;
2003 lineHeight = cursorImpl.lineHeight;
2006 const Vector2& Decorator::GetPosition( Cursor cursor ) const
2008 return mImpl->mCursor[cursor].position;
2011 void Decorator::SetGlyphOffset( Cursor cursor, float glyphOffset )
2013 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2015 cursorImpl.glyphOffset = glyphOffset;
2018 const float Decorator::GetGlyphOffset( Cursor cursor) const
2020 return mImpl->mCursor[cursor].glyphOffset;
2023 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
2025 mImpl->mCursor[cursor].color = color;
2028 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
2030 return mImpl->mCursor[cursor].color;
2033 void Decorator::StartCursorBlink()
2035 if ( !mImpl->mCursorBlinkTimer )
2037 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
2038 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
2041 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
2043 mImpl->mCursorBlinkTimer.Start();
2047 void Decorator::StopCursorBlink()
2049 if ( mImpl->mCursorBlinkTimer )
2051 mImpl->mCursorBlinkTimer.Stop();
2054 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2057 void Decorator::DelayCursorBlink()
2059 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2060 mImpl->mDelayCursorBlink = true;
2063 void Decorator::SetCursorBlinkInterval( float seconds )
2065 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
2068 float Decorator::GetCursorBlinkInterval() const
2070 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
2073 void Decorator::SetCursorBlinkDuration( float seconds )
2075 mImpl->mCursorBlinkDuration = seconds;
2078 float Decorator::GetCursorBlinkDuration() const
2080 return mImpl->mCursorBlinkDuration;
2083 void Decorator::SetCursorWidth( int width )
2085 mImpl->mCursorWidth = static_cast<float>( width );
2088 int Decorator::GetCursorWidth() const
2090 return static_cast<int>( mImpl->mCursorWidth );
2095 void Decorator::SetHandleActive( HandleType handleType, bool active )
2097 mImpl->mHandle[handleType].active = active;
2101 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
2103 mImpl->mIsHandlePreviouslyCrossed = false;
2106 // TODO: this is a work-around.
2107 // The problem is the handle actor does not receive the touch event with the Interrupt
2108 // state when the power button is pressed and the application goes to background.
2109 mImpl->mHandle[handleType].pressed = false;
2110 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
2111 ImageView imageView = mImpl->mHandle[handleType].actor;
2112 if( imageReleased && imageView )
2114 imageView.SetImage( imageReleased );
2120 bool Decorator::IsHandleActive( HandleType handleType ) const
2122 return mImpl->mHandle[handleType].active ;
2125 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
2127 mImpl->SetHandleImage( handleType, handleImageType, image );
2130 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
2132 return mImpl->mHandleImages[handleType][handleImageType];
2135 void Decorator::SetHandleColor( const Vector4& color )
2137 mImpl->mHandleColor = color;
2140 const Vector4& Decorator::GetHandleColor() const
2142 return mImpl->mHandleColor;
2145 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
2147 // Adjust handle's displacement
2148 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2150 handle.position.x = x;
2151 handle.position.y = y;
2152 handle.lineHeight = height;
2154 if( mImpl->mSmoothHandlePanEnabled )
2156 handle.grabDisplacementX = 0.f;
2157 handle.grabDisplacementY = 0.f;
2161 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2163 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2165 x = handle.position.x;
2166 y = handle.position.y;
2167 height = handle.lineHeight;
2170 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2172 return mImpl->mHandle[handleType].position;
2175 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2177 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2180 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2182 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2185 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2187 mImpl->mFlipSelectionHandlesOnCross = enable;
2190 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2192 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2193 mImpl->mFlipLeftSelectionHandleDirection = left;
2194 mImpl->mFlipRightSelectionHandleDirection = right;
2197 void Decorator::AddHighlight( unsigned int index, const Vector4& quad )
2199 *( mImpl->mHighlightQuadList.Begin() + index ) = quad;
2202 void Decorator::SetHighLightBox( const Vector2& position, const Size& size )
2204 mImpl->mHighlightPosition = position;
2205 mImpl->mHighlightSize = size;
2208 void Decorator::ClearHighlights()
2210 mImpl->mHighlightQuadList.Clear();
2211 mImpl->mHighlightPosition = Vector2::ZERO;
2214 void Decorator::ResizeHighlightQuads( unsigned int numberOfQuads )
2216 mImpl->mHighlightQuadList.Resize( numberOfQuads );
2219 void Decorator::SetHighlightColor( const Vector4& color )
2221 mImpl->mHighlightColor = color;
2224 const Vector4& Decorator::GetHighlightColor() const
2226 return mImpl->mHighlightColor;
2229 void Decorator::SetHighlightActive( bool active )
2231 mImpl->mIsHighlightBoxActive = active;
2234 bool Decorator::IsHighlightActive() const
2236 return mImpl->mIsHighlightBoxActive;
2239 void Decorator::SetTextDepth( int textDepth )
2241 mImpl->mTextDepth = textDepth;
2244 void Decorator::SetPopupActive( bool active )
2246 mImpl->mActiveCopyPastePopup = active;
2249 bool Decorator::IsPopupActive() const
2251 return mImpl->mActiveCopyPastePopup;
2254 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2256 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2258 if ( !mImpl->mCopyPastePopup.actor )
2260 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2261 #ifdef DECORATOR_DEBUG
2262 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
2264 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
2265 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::SetPopupPosition ); // Position popup after size negotiation
2268 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2271 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2273 return mImpl->mEnabledPopupButtons;
2278 void Decorator::SetScrollThreshold( float threshold )
2280 mImpl->SetScrollThreshold( threshold );
2283 float Decorator::GetScrollThreshold() const
2285 return mImpl->GetScrollThreshold();
2288 void Decorator::SetScrollSpeed( float speed )
2290 mImpl->SetScrollSpeed( speed );
2293 float Decorator::GetScrollSpeed() const
2295 return mImpl->GetScrollSpeed();
2298 void Decorator::NotifyEndOfScroll()
2300 mImpl->NotifyEndOfScroll();
2303 void Decorator::SetHorizontalScrollEnabled( bool enable )
2305 mImpl->mHorizontalScrollingEnabled = enable;
2308 bool Decorator::IsHorizontalScrollEnabled() const
2310 return mImpl->mHorizontalScrollingEnabled;
2313 void Decorator::SetVerticalScrollEnabled( bool enable )
2315 mImpl->mVerticalScrollingEnabled = enable;
2318 bool Decorator::IsVerticalScrollEnabled() const
2320 return mImpl->mVerticalScrollingEnabled;
2323 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2325 mImpl->mSmoothHandlePanEnabled = enable;
2328 bool Decorator::IsSmoothHandlePanEnabled() const
2330 return mImpl->mSmoothHandlePanEnabled;
2333 Decorator::~Decorator()
2338 Decorator::Decorator( ControllerInterface& controller,
2339 TextSelectionPopupCallbackInterface& callbackInterface )
2342 mImpl = new Decorator::Impl( controller, callbackInterface );
2347 } // namespace Toolkit