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.
108 * structure to hold coordinates of each quad, which will make up the mesh.
110 struct QuadCoordinates
113 * Default constructor
121 * @param[in] x1 left co-ordinate
122 * @param[in] y1 top co-ordinate
123 * @param[in] x2 right co-ordinate
124 * @param[in] y2 bottom co-ordinate
126 QuadCoordinates(float x1, float y1, float x2, float y2)
132 Dali::Vector2 min; ///< top-left (minimum) position of quad
133 Dali::Vector2 max; ///< bottom-right (maximum) position of quad
136 typedef std::vector<QuadCoordinates> QuadContainer;
139 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
140 * @param[in] boundingRectangle local bounding
141 * @param[out] Vector4 World coordinate bounding Box.
143 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
145 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
146 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
148 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
149 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
151 boundingBox = Dali::Vector4( originX,
153 originX + boundingRectangle.width,
154 originY + boundingRectangle.height );
157 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
159 // Convert to local coordinates and store as a Dali::Rect.
160 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
162 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
163 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
164 boundingRectangle.width = boundingBox.z - boundingBox.x;
165 boundingRectangle.height = boundingBox.w - boundingBox.y;
168 } // end of namespace
179 struct Decorator::Impl : public ConnectionTracker
193 : color( Dali::Color::BLACK ),
195 cursorHeight( 0.0f ),
213 grabDisplacementX( 0.f ),
214 grabDisplacementY( 0.f ),
216 horizontallyVisible( false ),
217 verticallyVisible( false ),
219 verticallyFlippedPreferred( false ),
220 horizontallyFlipped( false ),
221 verticallyFlipped( false ),
222 verticallyFlippedOnTouch( false )
228 ImageView markerActor;
231 Vector2 globalPosition;
233 float lineHeight; ///< Not the handle height
234 float grabDisplacementX;
235 float grabDisplacementY;
237 bool horizontallyVisible : 1;
238 bool verticallyVisible : 1;
240 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
241 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
242 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
243 bool verticallyFlippedOnTouch : 1; ///< Whether the handle is vertically flipped on touch.
253 TextSelectionPopup actor;
257 Impl( ControllerInterface& controller,
258 TextSelectionPopupCallbackInterface& callbackInterface )
259 : mController( controller ),
260 mEnabledPopupButtons( TextSelectionPopup::NONE ),
261 mTextSelectionPopupCallbackInterface( callbackInterface ),
262 mHandleColor( HANDLE_COLOR ),
264 mHighlightColor( LIGHT_BLUE ),
265 mHighlightPosition( Vector2::ZERO ),
266 mActiveCursor( ACTIVE_CURSOR_NONE ),
267 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
268 mCursorBlinkDuration( 0.0f ),
269 mCursorWidth( CURSOR_WIDTH ),
270 mHandleScrolling( HANDLE_TYPE_COUNT ),
271 mHandleReleased( HANDLE_TYPE_COUNT ),
272 mScrollDirection( SCROLL_NONE ),
273 mScrollThreshold( SCROLL_THRESHOLD ),
274 mScrollSpeed( SCROLL_SPEED ),
275 mScrollDistance( SCROLL_DISTANCE ),
277 mActiveCopyPastePopup( false ),
278 mPopupSetNewPosition( true ),
279 mCursorBlinkStatus( true ),
280 mDelayCursorBlink( false ),
281 mPrimaryCursorVisible( false ),
282 mSecondaryCursorVisible( false ),
283 mFlipSelectionHandlesOnCross( false ),
284 mFlipLeftSelectionHandleDirection( false ),
285 mFlipRightSelectionHandleDirection( false ),
286 mIsHandlePanning( false ),
287 mIsHandleCurrentlyCrossed( false ),
288 mIsHandlePreviouslyCrossed( false ),
289 mNotifyEndOfScroll( false ),
290 mHorizontalScrollingEnabled( false ),
291 mVerticalScrollingEnabled( false ),
292 mSmoothHandlePanEnabled( false ),
293 mIsHighlightBoxActive( false )
295 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
296 mHighlightShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
301 * Relayout of the decorations owned by the decorator.
302 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
304 void Relayout( const Vector2& size )
308 // TODO - Remove this if nothing is active
311 // Show or hide the cursors
316 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
317 mPrimaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
318 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
319 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
320 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
321 if( mPrimaryCursorVisible )
323 mPrimaryCursor.SetPosition( cursor.position.x,
325 mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
327 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
329 if( mSecondaryCursor )
331 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
332 mSecondaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
333 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
334 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
335 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
336 if( mSecondaryCursorVisible )
338 mSecondaryCursor.SetPosition( cursor.position.x,
340 mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
342 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
345 // Show or hide the grab handle
346 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
347 bool newGrabHandlePosition = false;
348 grabHandle.horizontallyVisible = false;
349 grabHandle.verticallyVisible = false;
350 if( grabHandle.active )
352 grabHandle.horizontallyVisible = ( ( mControlSize.width - ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) ) > -Math::MACHINE_EPSILON_1000 ) &&
353 ( grabHandle.position.x > -Math::MACHINE_EPSILON_1000 ) );
354 grabHandle.verticallyVisible = ( ( ( mControlSize.height - grabHandle.lineHeight ) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) &&
355 ( grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) );
357 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible;
362 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
363 SetGrabHandlePosition();
365 // Sets the grab handle image according if it's pressed, flipped, etc.
366 SetHandleImage( GRAB_HANDLE );
368 newGrabHandlePosition = true;
371 if( grabHandle.actor )
373 grabHandle.actor.SetVisible( isVisible );
376 else if( grabHandle.actor )
378 grabHandle.actor.Unparent();
381 // Show or hide the selection handles/highlight
382 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
383 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
384 bool newPrimaryHandlePosition = false;
385 bool newSecondaryHandlePosition = false;
387 primary.horizontallyVisible = ( ( mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
388 ( primary.position.x > -Math::MACHINE_EPSILON_1000 ) );
389 primary.verticallyVisible = ( ( ( mControlSize.height - primary.lineHeight ) - primary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
390 ( primary.position.y + ( primary.verticallyFlipped ? 0.f : primary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
391 secondary.horizontallyVisible = ( ( mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
392 ( secondary.position.x > -Math::MACHINE_EPSILON_1000 ) );
393 secondary.verticallyVisible = ( ( ( mControlSize.height - secondary.lineHeight ) - secondary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
394 ( secondary.position.y + ( secondary.verticallyFlipped ? 0.f : secondary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
396 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
397 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
399 if( primary.active || secondary.active )
401 if( primaryVisible || secondaryVisible )
403 CreateSelectionHandles();
407 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
409 // Sets the primary handle image according if it's pressed, flipped, etc.
410 SetHandleImage( LEFT_SELECTION_HANDLE );
412 SetSelectionHandleMarkerSize( primary );
414 newPrimaryHandlePosition = true;
417 if( secondaryVisible )
419 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
421 // Sets the secondary handle image according if it's pressed, flipped, etc.
422 SetHandleImage( RIGHT_SELECTION_HANDLE );
424 SetSelectionHandleMarkerSize( secondary );
426 newSecondaryHandlePosition = true;
432 primary.actor.SetVisible( primaryVisible );
434 if( secondary.actor )
436 secondary.actor.SetVisible( secondaryVisible );
444 primary.actor.Unparent();
446 if( secondary.actor )
448 secondary.actor.Unparent();
452 if( mIsHighlightBoxActive )
459 if( mHighlightActor )
461 mHighlightActor.Unparent();
465 if( newGrabHandlePosition ||
466 newPrimaryHandlePosition ||
467 newSecondaryHandlePosition )
469 // Setup property notifications to find whether the handles leave the boundaries of the current display.
470 SetupActiveLayerPropertyNotifications();
473 if( mActiveCopyPastePopup &&
474 ( primaryVisible || secondaryVisible ) )
477 mPopupSetNewPosition = true;
481 if( mCopyPastePopup.actor )
483 mCopyPastePopup.actor.HidePopup();
484 mPopupSetNewPosition = true;
489 void UpdatePositions( const Vector2& scrollOffset )
491 mCursor[PRIMARY_CURSOR].position += scrollOffset;
492 mCursor[SECONDARY_CURSOR].position += scrollOffset;
493 mHandle[ GRAB_HANDLE ].position += scrollOffset;
494 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
495 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
496 mHighlightPosition += scrollOffset;
501 if( !mCopyPastePopup.actor )
506 if( !mCopyPastePopup.actor.GetParent() )
508 mActiveLayer.Add( mCopyPastePopup.actor );
511 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
512 mCopyPastePopup.actor.ShowPopup();
515 float CalculateVerticalPopUpPosition( float halfHeight, bool preferBelow )
517 float yPosition = 0.f;
519 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
520 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
521 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
523 if( primaryHandle.active || secondaryHandle.active )
525 // The origin of the decorator's coordinate system in world coords.
526 const Vector3 originWorldCoords = mActiveLayer.GetCurrentWorldPosition() - mActiveLayer.GetCurrentSize() * ACTIVE_LAYER_ANCHOR_POINT;
530 // Find out if there is enough space for the popup at the bottom.
531 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
532 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
534 float maxY = std::max( primaryBelowY, secondaryBelowY );
536 yPosition = halfHeight + maxY;
538 if( originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w )
540 // Does not fit below.
542 // Try to fit first below the non active handle. Otherwise above the active handle.
543 if( RIGHT_SELECTION_HANDLE == mHandleReleased )
545 if( primaryBelowY < secondaryBelowY )
547 yPosition = halfHeight + primaryBelowY;
551 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
554 else if( LEFT_SELECTION_HANDLE == mHandleReleased )
556 if( secondaryBelowY < primaryBelowY )
558 yPosition = halfHeight + secondaryBelowY;
562 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
566 // Check the handle is whithin the decoration box.
567 if( originWorldCoords.y + yPosition < mBoundingBox.y )
569 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
572 if( originWorldCoords.y + yPosition > mBoundingBox.w )
574 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
580 // Find out if there is enough space for the popup at the top.
581 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
582 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
584 float minY = std::min( primaryTopY, secondaryTopY );
586 yPosition = -halfHeight + minY;
588 } // ( primaryHandle.active || secondaryHandle.active )
589 else if( grabHandle.active )
593 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
597 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
604 void ConstrainPopupPosition( const Vector3& popupHalfSize )
606 // Check if the popup is within the boundaries of the decoration box.
608 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
610 // The origin of the decorator's coordinate system in world coords.
611 const Vector3 originWorldCoords = mActiveLayer.GetCurrentWorldPosition() - mActiveLayer.GetCurrentSize() * ACTIVE_LAYER_ANCHOR_POINT;
613 // The popup's position in world coords.
614 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
616 if( popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x )
618 mCopyPastePopup.position.x += mBoundingBox.x - ( popupPositionWorldCoords.x - popupHalfSize.width );
620 else if( popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z )
622 mCopyPastePopup.position.x += mBoundingBox.z - ( popupPositionWorldCoords.x + popupHalfSize.width );
625 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
626 if( popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y )
628 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition( popupHalfSize.height, true ); // true -> prefer to set the popup's position below.
632 void SetPopupPosition( Actor actor )
634 if( !mActiveCopyPastePopup )
639 // Retrieves the popup's size after relayout.
640 const Vector3 popupSize( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
641 const Vector3 popupHalfSize = popupSize * 0.5f;
643 if( mPopupSetNewPosition )
645 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
646 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
647 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
649 if( primaryHandle.active || secondaryHandle.active )
651 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
652 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
654 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
656 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - ( primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING );
657 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - ( secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING );
659 mCopyPastePopup.position.y = std::min( primaryY, secondaryY );
661 else if( grabHandle.active )
663 mCopyPastePopup.position.x = grabHandle.position.x;
665 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - ( grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING );
667 } // mPopupSetNewPosition
669 // It may change the popup's position to fit within the decoration box.
670 ConstrainPopupPosition( popupHalfSize );
672 SetUpPopupPositionNotifications( popupHalfSize );
674 // Prevent pixel mis-alignment by rounding down.
675 mCopyPastePopup.position.x = floorf( mCopyPastePopup.position.x );
676 mCopyPastePopup.position.y = floorf( mCopyPastePopup.position.y );
678 mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
679 mPopupSetNewPosition = false;
682 void CreateCursor( Control& cursor, const Vector4& color )
684 cursor = Control::New();
685 cursor.SetBackgroundColor( color );
686 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT );
687 cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
690 // Add or Remove cursor(s) from parent
693 if( mActiveCursor == ACTIVE_CURSOR_NONE )
697 mPrimaryCursor.Unparent();
699 if( mSecondaryCursor )
701 mSecondaryCursor.Unparent();
706 // Create Primary and or Secondary Cursor(s) if active and add to parent
707 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
708 mActiveCursor == ACTIVE_CURSOR_BOTH )
710 if ( !mPrimaryCursor )
712 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
713 #ifdef DECORATOR_DEBUG
714 mPrimaryCursor.SetName( "PrimaryCursorActor" );
718 if( !mPrimaryCursor.GetParent() )
720 mActiveLayer.Add( mPrimaryCursor );
724 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
726 if ( !mSecondaryCursor )
728 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
729 #ifdef DECORATOR_DEBUG
730 mSecondaryCursor.SetName( "SecondaryCursorActor" );
734 if( !mSecondaryCursor.GetParent() )
736 mActiveLayer.Add( mSecondaryCursor );
741 if( mSecondaryCursor )
743 mSecondaryCursor.Unparent();
749 bool OnCursorBlinkTimerTick()
751 if( !mDelayCursorBlink )
754 if ( mPrimaryCursor )
756 mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
758 if ( mSecondaryCursor )
760 mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
763 mCursorBlinkStatus = !mCursorBlinkStatus;
768 mDelayCursorBlink = false;
776 // Will consume tap gestures on handles.
777 mTapDetector = TapGestureDetector::New();
779 // Will consume double tap gestures on handles.
780 mTapDetector.SetMaximumTapsRequired( 2u );
782 // Will consume long press gestures on handles.
783 mLongPressDetector = LongPressGestureDetector::New();
785 // Detects pan gestures on handles.
786 mPanDetector = PanGestureDetector::New();
787 mPanDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
790 void CreateActiveLayer()
794 mActiveLayer = Layer::New();
795 #ifdef DECORATOR_DEBUG
796 mActiveLayer.SetName ( "ActiveLayerActor" );
799 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
800 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
802 // Add the active layer telling the controller it doesn't need clipping.
803 mController.AddDecoration( mActiveLayer, false );
806 mActiveLayer.RaiseToTop();
809 void SetSelectionHandleMarkerSize( HandleImpl& handle )
811 if( handle.markerActor )
813 handle.markerActor.SetSize( 0, handle.lineHeight );
817 void CreateGrabHandle()
819 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
820 if( !grabHandle.actor )
822 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
824 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
825 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
826 grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
828 // Area that Grab handle responds to, larger than actual handle so easier to move
829 #ifdef DECORATOR_DEBUG
830 grabHandle.actor.SetName( "GrabHandleActor" );
831 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
833 grabHandle.grabArea = Control::New();
834 Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
835 control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
836 grabHandle.grabArea.SetName( "GrabArea" );
840 grabHandle.grabArea = Actor::New();
841 grabHandle.grabArea.SetName( "GrabArea" );
844 grabHandle.grabArea = Actor::New();
847 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
848 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
849 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
850 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
851 grabHandle.actor.Add( grabHandle.grabArea );
852 grabHandle.actor.SetColor( mHandleColor );
854 grabHandle.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
856 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
857 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
858 mTapDetector.Attach( grabHandle.actor );
859 mLongPressDetector.Attach( grabHandle.actor );
861 // The grab handle's area is attached to the pan detector.
862 // The OnPan() method is connected to the signals emitted by the pan detector.
863 mPanDetector.Attach( grabHandle.grabArea );
865 mActiveLayer.Add( grabHandle.actor );
869 if( grabHandle.actor && !grabHandle.actor.GetParent() )
871 mActiveLayer.Add( grabHandle.actor );
875 void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
879 handle.markerActor = ImageView::New( image );
880 handle.markerActor.SetColor( mHandleColor );
881 handle.actor.Add( handle.markerActor );
883 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
885 if( LEFT_SELECTION_HANDLE == handleType )
887 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
888 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
890 else if( RIGHT_SELECTION_HANDLE == handleType )
892 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
893 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
898 void CreateSelectionHandles()
900 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
903 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
905 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
906 #ifdef DECORATOR_DEBUG
907 primary.actor.SetName("SelectionHandleOne");
909 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
910 GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
911 primary.actor.SetColor( mHandleColor );
913 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
914 #ifdef DECORATOR_DEBUG
915 primary.grabArea.SetName("SelectionHandleOneGrabArea");
917 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
918 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
919 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
920 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
922 primary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
924 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
925 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
926 mTapDetector.Attach( primary.actor );
927 mLongPressDetector.Attach( primary.actor );
929 // The handle's area is attached to the pan detector.
930 // The OnPan() method is connected to the signals emitted by the pan detector.
931 mPanDetector.Attach( primary.grabArea );
933 primary.actor.Add( primary.grabArea );
935 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
939 if( primary.actor && !primary.actor.GetParent() )
941 mActiveLayer.Add( primary.actor );
944 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
945 if( !secondary.actor )
947 if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
949 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
950 #ifdef DECORATOR_DEBUG
951 secondary.actor.SetName("SelectionHandleTwo");
953 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
954 GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
955 secondary.actor.SetColor( mHandleColor );
957 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
958 #ifdef DECORATOR_DEBUG
959 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
961 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
962 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
963 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
964 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
966 secondary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
968 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
969 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
970 mTapDetector.Attach( secondary.actor );
971 mLongPressDetector.Attach( secondary.actor );
973 // The handle's area is attached to the pan detector.
974 // The OnPan() method is connected to the signals emitted by the pan detector.
975 mPanDetector.Attach( secondary.grabArea );
977 secondary.actor.Add( secondary.grabArea );
979 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
983 if( secondary.actor && !secondary.actor.GetParent() )
985 mActiveLayer.Add( secondary.actor );
989 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
991 // Gets the world position of the active layer. The active layer is where the handles are added.
992 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
994 // The grab handle position in world coords.
995 // The active layer's world position is the center of the active layer. The origin of the
996 // coord system of the handles is the top left of the active layer.
997 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f );
998 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f );
1001 void SetGrabHandlePosition()
1003 // Reference to the grab handle.
1004 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1006 // Transforms the handle position into world coordinates.
1007 // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
1008 // as it's transforming the handle's position set by the text-controller and not
1009 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
1010 // retrieves the position of the center of the actor but the handle's position set
1011 // by the text controller is not the center of the actor.
1012 Vector2 grabHandleWorldPosition;
1013 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
1015 // Check if the grab handle exceeds the boundaries of the decoration box.
1016 // At the moment only the height is checked for the grab handle.
1018 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
1019 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
1020 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
1022 // The grab handle 'y' position in local coords.
1023 // If the grab handle exceeds the bottom of the decoration box,
1024 // set the 'y' position to the top of the line.
1025 // The SetGrabHandleImage() method will change the orientation.
1026 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
1028 if( grabHandle.actor )
1030 grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ) + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f ),
1031 yLocalPosition + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f ) );
1035 void SetSelectionHandlePosition( HandleType type )
1037 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
1039 // Reference to the selection handle.
1040 HandleImpl& handle = mHandle[type];
1042 // Transforms the handle position into world coordinates.
1043 // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
1044 // as it's transforming the handle's position set by the text-controller and not
1045 // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
1046 // retrieves the position of the center of the actor but the handle's position set
1047 // by the text controller is not the center of the actor.
1048 Vector2 handleWorldPosition;
1049 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
1051 // Whether to flip the handle (horizontally).
1052 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1054 // Whether to flip the handles if they are crossed.
1055 bool crossFlip = false;
1056 if( mFlipSelectionHandlesOnCross || !mIsHandlePanning )
1058 crossFlip = mIsHandleCurrentlyCrossed;
1061 // Whether the handle was crossed before start the panning.
1062 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1064 // Does not flip if both conditions are true (double flip)
1065 flipHandle = flipHandle != ( crossFlip || isHandlePreviouslyCrossed );
1067 // Will flip the handles vertically if the user prefers it.
1068 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1070 if( crossFlip || isHandlePreviouslyCrossed )
1072 if( isPrimaryHandle )
1074 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1078 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1082 // Check if the selection handle exceeds the boundaries of the decoration box.
1083 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
1084 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
1086 // Does not flip if both conditions are true (double flip)
1087 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
1091 if( handle.actor && !handle.horizontallyFlipped )
1093 // Change the anchor point to flip the image.
1094 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
1096 handle.horizontallyFlipped = true;
1101 if( handle.actor && handle.horizontallyFlipped )
1103 // Reset the anchor point.
1104 handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
1106 handle.horizontallyFlipped = false;
1110 // Whether to flip the handle vertically.
1111 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
1112 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
1113 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
1115 // The primary selection handle 'y' position in local coords.
1116 // If the handle exceeds the bottom of the decoration box,
1117 // set the 'y' position to the top of the line.
1118 // The SetHandleImage() method will change the orientation.
1119 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1123 handle.actor.SetPosition( handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f ),
1124 yLocalPosition + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f ) );
1128 void SetHandleImage( HandleType type )
1130 HandleImpl& handle = mHandle[type];
1132 HandleType markerType = HANDLE_TYPE_COUNT;
1133 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1134 if( LEFT_SELECTION_HANDLE == type )
1136 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1137 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1139 else if( RIGHT_SELECTION_HANDLE == type )
1141 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1142 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1145 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1148 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1150 handle.actor.SetImage( mHandleImages[type][imageType] );
1153 if( HANDLE_TYPE_COUNT != markerType )
1155 if( handle.markerActor )
1157 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1158 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
1162 // Whether to flip the handle vertically.
1165 handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
1169 void CreateHighlight()
1171 if( !mHighlightActor )
1173 mHighlightActor = Actor::New();
1175 #ifdef DECORATOR_DEBUG
1176 mHighlightActor.SetName( "HighlightActor" );
1178 mHighlightActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
1179 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
1180 mHighlightActor.SetColor( mHighlightColor );
1181 mHighlightActor.SetColorMode( USE_OWN_COLOR );
1184 // Add the highlight box telling the controller it needs clipping.
1185 mController.AddDecoration( mHighlightActor, true );
1188 void UpdateHighlight()
1190 if ( mHighlightActor )
1192 // Sets the position of the highlight actor inside the decorator.
1193 mHighlightActor.SetPosition( mHighlightPosition.x,
1194 mHighlightPosition.y );
1196 const unsigned int numberOfQuads = mHighlightQuadList.size();
1197 if( 0u != numberOfQuads )
1199 // Set the size of the highlighted text to the actor.
1200 mHighlightActor.SetSize( mHighlightSize );
1202 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1203 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1204 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1206 Vector<Vector2> vertices;
1207 Vector<unsigned short> indices;
1209 vertices.Reserve( 4u * numberOfQuads );
1210 indices.Reserve( 6u * numberOfQuads );
1212 // Index to the vertex.
1213 unsigned int v = 0u;
1215 // Traverse all quads.
1216 for( std::vector<QuadCoordinates>::iterator it = mHighlightQuadList.begin(),
1217 endIt = mHighlightQuadList.end();
1221 QuadCoordinates& quad = *it;
1226 vertex.x = quad.min.x - offsetX;
1227 vertex.y = quad.min.y - offsetY;
1228 vertices.PushBack( vertex );
1231 vertex.x = quad.max.x - offsetX;
1232 vertex.y = quad.min.y - offsetY;
1233 vertices.PushBack( vertex );
1235 // bottom-left (v+2)
1236 vertex.x = quad.min.x - offsetX;
1237 vertex.y = quad.max.y - offsetY;
1238 vertices.PushBack( vertex );
1240 // bottom-right (v+3)
1241 vertex.x = quad.max.x - offsetX;
1242 vertex.y = quad.max.y - offsetY;
1243 vertices.PushBack( vertex );
1245 // triangle A (3, 1, 0)
1246 indices.PushBack( v + 3 );
1247 indices.PushBack( v + 1 );
1248 indices.PushBack( v );
1250 // triangle B (0, 2, 3)
1251 indices.PushBack( v );
1252 indices.PushBack( v + 2 );
1253 indices.PushBack( v + 3 );
1256 if( ! mQuadVertices )
1258 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
1261 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1263 if( !mQuadGeometry )
1265 mQuadGeometry = Geometry::New();
1266 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1268 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1270 if( !mHighlightRenderer )
1272 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1273 mHighlightActor.AddRenderer( mHighlightRenderer );
1277 mHighlightQuadList.clear();
1279 if( mHighlightRenderer )
1281 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1286 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1288 if( Gesture::Started == gesture.state )
1290 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1292 handle.globalPosition.x = handle.position.x;
1293 handle.globalPosition.y = handle.position.y;
1296 handle.grabDisplacementX += gesture.displacement.x;
1297 handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.displacement.y );
1299 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1300 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1301 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f );
1303 if( ( Gesture::Started == gesture.state ) ||
1304 ( Gesture::Continuing == gesture.state ) )
1307 mController.GetTargetSize( targetSize );
1309 if( mHorizontalScrollingEnabled &&
1310 ( x < mScrollThreshold ) )
1312 mScrollDirection = SCROLL_RIGHT;
1313 mHandleScrolling = type;
1316 else if( mHorizontalScrollingEnabled &&
1317 ( x > targetSize.width - mScrollThreshold ) )
1319 mScrollDirection = SCROLL_LEFT;
1320 mHandleScrolling = type;
1323 else if( mVerticalScrollingEnabled &&
1324 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1326 mScrollDirection = SCROLL_TOP;
1327 mHandleScrolling = type;
1330 else if( mVerticalScrollingEnabled &&
1331 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1333 mScrollDirection = SCROLL_BOTTOM;
1334 mHandleScrolling = type;
1339 mHandleScrolling = HANDLE_TYPE_COUNT;
1341 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1344 mIsHandlePanning = true;
1346 else if( ( Gesture::Finished == gesture.state ) ||
1347 ( Gesture::Cancelled == gesture.state ) )
1350 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1352 mNotifyEndOfScroll = false;
1353 mHandleScrolling = HANDLE_TYPE_COUNT;
1355 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1359 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1364 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1366 handle.pressed = false;
1368 mIsHandlePanning = false;
1372 void OnPan( Actor actor, const PanGesture& gesture )
1374 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1375 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1376 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1378 if( actor == grabHandle.grabArea )
1380 DoPan( grabHandle, GRAB_HANDLE, gesture );
1382 else if( actor == primarySelectionHandle.grabArea )
1384 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1386 else if( actor == secondarySelectionHandle.grabArea )
1388 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1392 bool OnGrabHandleTouched( Actor actor, const TouchData& touch )
1394 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1396 // Switch between pressed/release grab-handle images
1397 if( touch.GetPointCount() > 0 &&
1400 const PointState::Type state = touch.GetState( 0 );
1402 if( PointState::DOWN == state )
1404 grabHandle.pressed = true;
1406 else if( ( PointState::UP == state ) ||
1407 ( PointState::INTERRUPTED == state ) )
1409 grabHandle.pressed = false;
1412 SetHandleImage( GRAB_HANDLE );
1415 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1419 bool OnHandleOneTouched( Actor actor, const TouchData& touch )
1421 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1423 // Switch between pressed/release selection handle images
1424 if( touch.GetPointCount() > 0 &&
1425 primarySelectionHandle.actor )
1427 const PointState::Type state = touch.GetState( 0 );
1429 if( PointState::DOWN == state )
1431 primarySelectionHandle.pressed = true;
1432 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1434 else if( ( PointState::UP == state ) ||
1435 ( PointState::INTERRUPTED == state ) )
1437 primarySelectionHandle.pressed = false;
1438 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1439 mIsHandlePanning = false;
1440 mHandleReleased = LEFT_SELECTION_HANDLE;
1443 SetHandleImage( LEFT_SELECTION_HANDLE );
1446 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1450 bool OnHandleTwoTouched( Actor actor, const TouchData& touch )
1452 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1454 // Switch between pressed/release selection handle images
1455 if( touch.GetPointCount() > 0 &&
1456 secondarySelectionHandle.actor )
1458 const PointState::Type state = touch.GetState( 0 );
1460 if( PointState::DOWN == state )
1462 secondarySelectionHandle.pressed = true;
1463 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1465 else if( ( PointState::UP == state ) ||
1466 ( PointState::INTERRUPTED == state ) )
1468 secondarySelectionHandle.pressed = false;
1469 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1470 mIsHandlePanning = false;
1471 mHandleReleased = RIGHT_SELECTION_HANDLE;
1474 SetHandleImage( RIGHT_SELECTION_HANDLE );
1477 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1481 void HandleResetPosition( PropertyNotification& source )
1483 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1485 if( grabHandle.active )
1487 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1488 SetGrabHandlePosition();
1490 // Sets the grab handle image according if it's pressed, flipped, etc.
1491 SetHandleImage( GRAB_HANDLE );
1495 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1496 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1498 // Sets the primary handle image according if it's pressed, flipped, etc.
1499 SetHandleImage( LEFT_SELECTION_HANDLE );
1501 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1502 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1504 // Sets the secondary handle image according if it's pressed, flipped, etc.
1505 SetHandleImage( RIGHT_SELECTION_HANDLE );
1509 void SetupActiveLayerPropertyNotifications()
1516 // Vertical notifications.
1518 // Disconnect any previous connected callback.
1519 if( mHandleVerticalLessThanNotification )
1521 mHandleVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1522 mActiveLayer.RemovePropertyNotification( mHandleVerticalLessThanNotification );
1525 if( mHandleVerticalGreaterThanNotification )
1527 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1528 mActiveLayer.RemovePropertyNotification( mHandleVerticalGreaterThanNotification );
1531 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1532 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1533 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1535 if( grabHandle.active )
1537 if( grabHandle.verticallyFlipped )
1539 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1540 mHandleVerticalGreaterThanNotification.Reset();
1542 // The vertical distance from the center of the active layer to the top edje of the display.
1543 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1545 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1546 LessThanCondition( mBoundingBox.y + topHeight ) );
1548 // Notifies the change from false to true and from true to false.
1549 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1551 // Connects the signals with the callbacks.
1552 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1556 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1557 mHandleVerticalLessThanNotification.Reset();
1559 // The vertical distance from the center of the active layer to the bottom edje of the display.
1560 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1562 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1563 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1565 // Notifies the change from false to true and from true to false.
1566 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1568 // Connects the signals with the callbacks.
1569 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1572 else // The selection handles are active
1574 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1576 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1577 mHandleVerticalGreaterThanNotification.Reset();
1579 // The vertical distance from the center of the active layer to the top edje of the display.
1580 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1582 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1583 LessThanCondition( mBoundingBox.y + topHeight ) );
1585 // Notifies the change from false to true and from true to false.
1586 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1588 // Connects the signals with the callbacks.
1589 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1591 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1593 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1594 mHandleVerticalLessThanNotification.Reset();
1596 // The vertical distance from the center of the active layer to the bottom edje of the display.
1597 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1598 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1600 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1601 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1603 // Notifies the change from false to true and from true to false.
1604 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1606 // Connects the signals with the callbacks.
1607 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1611 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1613 // The vertical distance from the center of the active layer to the top edje of the display.
1614 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1615 -primaryHandle.position.y + primaryHandle.size.height :
1616 -secondaryHandle.position.y + secondaryHandle.size.height );
1618 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1619 LessThanCondition( mBoundingBox.y + topHeight ) );
1621 // Notifies the change from false to true and from true to false.
1622 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1624 // Connects the signals with the callbacks.
1625 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1627 // The vertical distance from the center of the active layer to the bottom edje of the display.
1628 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1629 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1630 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1632 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1633 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1635 // Notifies the change from false to true and from true to false.
1636 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1638 // Connects the signals with the callbacks.
1639 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1643 // Horizontal notifications.
1645 // Disconnect any previous connected callback.
1646 if( mHandleHorizontalLessThanNotification )
1648 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1649 mActiveLayer.RemovePropertyNotification( mHandleHorizontalLessThanNotification );
1652 if( mHandleHorizontalGreaterThanNotification )
1654 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1655 mActiveLayer.RemovePropertyNotification( mHandleHorizontalGreaterThanNotification );
1658 if( primaryHandle.active || secondaryHandle.active )
1660 // The horizontal distance from the center of the active layer to the left edje of the display.
1661 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1662 -secondaryHandle.position.x + secondaryHandle.size.width );
1664 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1665 LessThanCondition( mBoundingBox.x + leftWidth ) );
1667 // Notifies the change from false to true and from true to false.
1668 mHandleHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1670 // Connects the signals with the callbacks.
1671 mHandleHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1673 // The horizontal distance from the center of the active layer to the right edje of the display.
1674 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1675 secondaryHandle.position.x + secondaryHandle.size.width );
1677 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1678 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1680 // Notifies the change from false to true and from true to false.
1681 mHandleHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1683 // Connects the signals with the callbacks.
1684 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1690 float AlternatePopUpPositionRelativeToCursor( bool topBottom )
1692 float alternativePosition = 0.0f;
1694 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1696 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1697 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1698 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1699 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1701 if( primaryHandle.active || secondaryHandle.active )
1703 float handleY = 0.f;
1704 float maxHandleHeight = 0.f;
1706 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1707 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1709 if( primaryVisible && secondaryVisible )
1711 handleY = std::max( primaryHandle.position.y, secondaryHandle.position.y );
1712 maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1714 else if( primaryVisible && !secondaryVisible )
1716 handleY = primaryHandle.position.y;
1717 maxHandleHeight = primaryHandle.size.height;
1719 else if( !primaryVisible && secondaryVisible )
1721 handleY = secondaryHandle.position.y;
1722 maxHandleHeight = secondaryHandle.size.height;
1725 alternativePosition = handleY + ( topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight );
1729 alternativePosition = cursor.position.y + ( topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height );
1732 return alternativePosition;
1735 void PopUpLeavesTopBoundary( PropertyNotification& source )
1737 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1739 // Sets the position of the popup below.
1740 mCopyPastePopup.actor.SetY( floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, true ) ) );
1743 void PopUpLeavesBottomBoundary( PropertyNotification& source )
1745 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1747 // Sets the position of the popup above.
1748 mCopyPastePopup.actor.SetY( floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, false ) ) );
1751 void SetUpPopupPositionNotifications( const Vector3& popupHalfSize )
1753 // Disconnect any previous connected callback.
1754 if( mPopupTopExceedNotification )
1756 mPopupTopExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1757 mCopyPastePopup.actor.RemovePropertyNotification( mPopupTopExceedNotification );
1760 if( mPopupBottomExceedNotification )
1762 mPopupBottomExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1763 mCopyPastePopup.actor.RemovePropertyNotification( mPopupBottomExceedNotification );
1766 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1768 // Exceeding vertical boundary
1770 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1771 LessThanCondition( mBoundingBox.y + popupHalfSize.height ) );
1773 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1774 GreaterThanCondition( mBoundingBox.w - popupHalfSize.height ) );
1776 // Notifies the change from false to true and from true to false.
1777 mPopupTopExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1778 mPopupBottomExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1780 mPopupTopExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1781 mPopupBottomExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1784 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1786 HandleImpl& handle = mHandle[handleType];
1787 handle.size = Size( image.GetWidth(), image.GetHeight() );
1789 mHandleImages[handleType][handleImageType] = image;
1792 void SetScrollThreshold( float threshold )
1794 mScrollThreshold = threshold;
1797 float GetScrollThreshold() const
1799 return mScrollThreshold;
1802 void SetScrollSpeed( float speed )
1804 mScrollSpeed = speed;
1805 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1808 float GetScrollSpeed() const
1810 return mScrollSpeed;
1813 void NotifyEndOfScroll()
1819 mNotifyEndOfScroll = true;
1824 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1826 * It only starts the timer if it's already created.
1828 void StartScrollTimer()
1832 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1833 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1836 if( !mScrollTimer.IsRunning() )
1838 mScrollTimer.Start();
1843 * Stops the timer used to scroll the text.
1845 void StopScrollTimer()
1849 mScrollTimer.Stop();
1854 * Callback called by the timer used to scroll the text.
1856 * It calculates and sets a new scroll position.
1858 bool OnScrollTimerTick()
1860 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1865 switch( mScrollDirection )
1869 x = mScrollDistance;
1874 x = -mScrollDistance;
1879 y = mScrollDistance;
1884 y = -mScrollDistance;
1891 mController.DecorationEvent( mHandleScrolling,
1900 ControllerInterface& mController;
1902 TapGestureDetector mTapDetector;
1903 PanGestureDetector mPanDetector;
1904 LongPressGestureDetector mLongPressDetector;
1906 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1907 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1909 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1910 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1911 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1912 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1913 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1914 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1915 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1916 Control mPrimaryCursor;
1917 Control mSecondaryCursor;
1919 Actor mHighlightActor; ///< Actor to display highlight
1920 Renderer mHighlightRenderer;
1921 Shader mHighlightShader; ///< Shader used for highlight
1922 Property::Map mQuadVertexFormat;
1923 PopupImpl mCopyPastePopup;
1924 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1925 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1927 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1928 Vector4 mHandleColor;
1930 CursorImpl mCursor[CURSOR_COUNT];
1931 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1933 PropertyBuffer mQuadVertices;
1934 Geometry mQuadGeometry;
1935 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight
1937 Vector4 mBoundingBox; ///< The bounding box in world coords.
1938 Vector4 mHighlightColor; ///< Color of the highlight
1939 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1940 Size mHighlightSize; ///< The size of the highlighted text.
1941 Size mControlSize; ///< The control's size. Set by the Relayout.
1943 unsigned int mActiveCursor;
1944 unsigned int mCursorBlinkInterval;
1945 float mCursorBlinkDuration;
1946 float mCursorWidth; ///< The width of the cursors in pixels.
1947 HandleType mHandleScrolling; ///< The handle which is scrolling.
1948 HandleType mHandleReleased; ///< The last handle released.
1949 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1950 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1951 float mScrollSpeed; ///< The scroll speed in pixels per second.
1952 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1953 int mTextDepth; ///< The depth used to render the text.
1955 bool mActiveCopyPastePopup : 1;
1956 bool mPopupSetNewPosition : 1;
1957 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1958 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1959 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1960 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1961 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1962 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1963 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1964 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1965 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1966 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1967 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1968 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1969 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1970 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1971 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1974 DecoratorPtr Decorator::New( ControllerInterface& controller,
1975 TextSelectionPopupCallbackInterface& callbackInterface )
1977 return DecoratorPtr( new Decorator( controller,
1978 callbackInterface ) );
1981 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1983 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1986 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1988 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1991 void Decorator::Relayout( const Vector2& size )
1993 mImpl->Relayout( size );
1996 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1998 mImpl->UpdatePositions( scrollOffset );
2003 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
2005 mImpl->mActiveCursor = activeCursor;
2008 unsigned int Decorator::GetActiveCursor() const
2010 return mImpl->mActiveCursor;
2013 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
2015 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2017 cursorImpl.position.x = x;
2018 cursorImpl.position.y = y;
2019 cursorImpl.cursorHeight = cursorHeight;
2020 cursorImpl.lineHeight = lineHeight;
2023 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
2025 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2027 x = cursorImpl.position.x;
2028 y = cursorImpl.position.y;
2029 cursorHeight = cursorImpl.cursorHeight;
2030 lineHeight = cursorImpl.lineHeight;
2033 const Vector2& Decorator::GetPosition( Cursor cursor ) const
2035 return mImpl->mCursor[cursor].position;
2038 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
2040 mImpl->mCursor[cursor].color = color;
2043 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
2045 return mImpl->mCursor[cursor].color;
2048 void Decorator::StartCursorBlink()
2050 if ( !mImpl->mCursorBlinkTimer )
2052 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
2053 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
2056 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
2058 mImpl->mCursorBlinkTimer.Start();
2062 void Decorator::StopCursorBlink()
2064 if ( mImpl->mCursorBlinkTimer )
2066 mImpl->mCursorBlinkTimer.Stop();
2069 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2072 void Decorator::DelayCursorBlink()
2074 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2075 mImpl->mDelayCursorBlink = true;
2078 void Decorator::SetCursorBlinkInterval( float seconds )
2080 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
2083 float Decorator::GetCursorBlinkInterval() const
2085 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
2088 void Decorator::SetCursorBlinkDuration( float seconds )
2090 mImpl->mCursorBlinkDuration = seconds;
2093 float Decorator::GetCursorBlinkDuration() const
2095 return mImpl->mCursorBlinkDuration;
2098 void Decorator::SetCursorWidth( int width )
2100 mImpl->mCursorWidth = static_cast<float>( width );
2103 int Decorator::GetCursorWidth() const
2105 return static_cast<int>( mImpl->mCursorWidth );
2110 void Decorator::SetHandleActive( HandleType handleType, bool active )
2112 mImpl->mHandle[handleType].active = active;
2116 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
2118 mImpl->mIsHandlePreviouslyCrossed = false;
2121 // TODO: this is a work-around.
2122 // The problem is the handle actor does not receive the touch event with the Interrupt
2123 // state when the power button is pressed and the application goes to background.
2124 mImpl->mHandle[handleType].pressed = false;
2125 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
2126 ImageView imageView = mImpl->mHandle[handleType].actor;
2127 if( imageReleased && imageView )
2129 imageView.SetImage( imageReleased );
2135 bool Decorator::IsHandleActive( HandleType handleType ) const
2137 return mImpl->mHandle[handleType].active ;
2140 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
2142 mImpl->SetHandleImage( handleType, handleImageType, image );
2145 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
2147 return mImpl->mHandleImages[handleType][handleImageType];
2150 void Decorator::SetHandleColor( const Vector4& color )
2152 mImpl->mHandleColor = color;
2155 const Vector4& Decorator::GetHandleColor() const
2157 return mImpl->mHandleColor;
2160 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
2162 // Adjust handle's displacement
2163 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2165 handle.position.x = x;
2166 handle.position.y = y;
2167 handle.lineHeight = height;
2169 if( mImpl->mSmoothHandlePanEnabled )
2171 handle.grabDisplacementX = 0.f;
2172 handle.grabDisplacementY = 0.f;
2176 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2178 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2180 x = handle.position.x;
2181 y = handle.position.y;
2182 height = handle.lineHeight;
2185 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2187 return mImpl->mHandle[handleType].position;
2190 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2192 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2195 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2197 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2200 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2202 mImpl->mFlipSelectionHandlesOnCross = enable;
2205 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2207 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2208 mImpl->mFlipLeftSelectionHandleDirection = left;
2209 mImpl->mFlipRightSelectionHandleDirection = right;
2212 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
2214 mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
2217 void Decorator::SetHighLightBox( const Vector2& position, const Size& size )
2219 mImpl->mHighlightPosition = position;
2220 mImpl->mHighlightSize = size;
2223 void Decorator::ClearHighlights()
2225 mImpl->mHighlightQuadList.clear();
2226 mImpl->mHighlightPosition = Vector2::ZERO;
2229 void Decorator::SetHighlightColor( const Vector4& color )
2231 mImpl->mHighlightColor = color;
2234 const Vector4& Decorator::GetHighlightColor() const
2236 return mImpl->mHighlightColor;
2239 void Decorator::SetHighlightActive( bool active )
2241 mImpl->mIsHighlightBoxActive = active;
2244 bool Decorator::IsHighlightActive() const
2246 return mImpl->mIsHighlightBoxActive;
2249 void Decorator::SetTextDepth( int textDepth )
2251 mImpl->mTextDepth = textDepth;
2254 void Decorator::SetPopupActive( bool active )
2256 mImpl->mActiveCopyPastePopup = active;
2259 bool Decorator::IsPopupActive() const
2261 return mImpl->mActiveCopyPastePopup;
2264 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2266 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2268 if ( !mImpl->mCopyPastePopup.actor )
2270 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2271 #ifdef DECORATOR_DEBUG
2272 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
2274 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
2275 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::SetPopupPosition ); // Position popup after size negotiation
2278 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2281 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2283 return mImpl->mEnabledPopupButtons;
2288 void Decorator::SetScrollThreshold( float threshold )
2290 mImpl->SetScrollThreshold( threshold );
2293 float Decorator::GetScrollThreshold() const
2295 return mImpl->GetScrollThreshold();
2298 void Decorator::SetScrollSpeed( float speed )
2300 mImpl->SetScrollSpeed( speed );
2303 float Decorator::GetScrollSpeed() const
2305 return mImpl->GetScrollSpeed();
2308 void Decorator::NotifyEndOfScroll()
2310 mImpl->NotifyEndOfScroll();
2313 void Decorator::SetHorizontalScrollEnabled( bool enable )
2315 mImpl->mHorizontalScrollingEnabled = enable;
2318 bool Decorator::IsHorizontalScrollEnabled() const
2320 return mImpl->mHorizontalScrollingEnabled;
2323 void Decorator::SetVerticalScrollEnabled( bool enable )
2325 mImpl->mVerticalScrollingEnabled = enable;
2328 bool Decorator::IsVerticalScrollEnabled() const
2330 return mImpl->mVerticalScrollingEnabled;
2333 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2335 mImpl->mSmoothHandlePanEnabled = enable;
2338 bool Decorator::IsSmoothHandlePanEnabled() const
2340 return mImpl->mSmoothHandlePanEnabled;
2343 Decorator::~Decorator()
2348 Decorator::Decorator( ControllerInterface& controller,
2349 TextSelectionPopupCallbackInterface& callbackInterface )
2352 mImpl = new Decorator::Impl( controller, callbackInterface );
2357 } // namespace Toolkit