2 * Copyright (c) 2020 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>
32 #include <dali/devel-api/adaptor-framework/image-loading.h>
35 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
36 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
37 #include <dali-toolkit/internal/controls/image-view/image-view-impl.h>
40 #define DECORATOR_DEBUG
44 #define MAKE_SHADER(A)#A
48 const char* VERTEX_SHADER = MAKE_SHADER(
49 attribute mediump vec2 aPosition;
50 uniform highp mat4 uMvpMatrix;
54 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
55 gl_Position = uMvpMatrix * position;
59 const char* FRAGMENT_SHADER = MAKE_SHADER(
60 uniform lowp vec4 uColor;
64 gl_FragColor = uColor;
75 #ifdef DECORATOR_DEBUG
76 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
86 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
87 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
88 const Dali::Vector3 ACTIVE_LAYER_ANCHOR_POINT( 0.5f, 0.5f, 0.5f );
90 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.
92 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
94 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
95 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
96 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
98 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
99 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.
100 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
102 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
104 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
106 const float POPUP_PADDING = 2.f; ///< Padding space between the highlight box and the text's popup.
108 typedef Dali::Vector<Dali::Vector4> QuadContainer;
111 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
112 * @param[in] boundingRectangle local bounding
113 * @param[out] Vector4 World coordinate bounding Box.
115 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
117 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
118 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
120 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
121 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
123 boundingBox = Dali::Vector4( originX,
125 originX + boundingRectangle.width,
126 originY + boundingRectangle.height );
129 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
131 // Convert to local coordinates and store as a Dali::Rect.
132 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
134 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
135 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
136 boundingRectangle.width = boundingBox.z - boundingBox.x;
137 boundingRectangle.height = boundingBox.w - boundingBox.y;
140 } // end of namespace
151 struct Decorator::Impl : public ConnectionTracker
165 : color( Dali::Color::BLACK ),
167 cursorHeight( 0.0f ),
187 grabDisplacementX( 0.f ),
188 grabDisplacementY( 0.f ),
190 horizontallyVisible( false ),
191 verticallyVisible( false ),
193 verticallyFlippedPreferred( false ),
194 horizontallyFlipped( false ),
195 verticallyFlipped( false ),
196 verticallyFlippedOnTouch( false )
202 ImageView markerActor;
205 Vector2 globalPosition;
207 float lineHeight; ///< Not the handle height
208 float grabDisplacementX;
209 float grabDisplacementY;
211 bool horizontallyVisible : 1;
212 bool verticallyVisible : 1;
214 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
215 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
216 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
217 bool verticallyFlippedOnTouch : 1; ///< Whether the handle is vertically flipped on touch.
227 TextSelectionPopup actor;
231 Impl( ControllerInterface& controller,
232 TextSelectionPopupCallbackInterface& callbackInterface )
233 : mController( controller ),
234 mEnabledPopupButtons( TextSelectionPopup::NONE ),
235 mTextSelectionPopupCallbackInterface( callbackInterface ),
236 mHandleColor( HANDLE_COLOR ),
238 mHighlightColor( LIGHT_BLUE ),
239 mHighlightPosition( Vector2::ZERO ),
240 mHighlightSize( Vector2::ZERO ),
241 mControlSize( Vector2::ZERO ),
242 mHighlightOutlineOffset( 0.f ),
243 mActiveCursor( ACTIVE_CURSOR_NONE ),
244 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
245 mCursorBlinkDuration( 0.0f ),
246 mCursorWidth( CURSOR_WIDTH ),
247 mHandleScrolling( HANDLE_TYPE_COUNT ),
248 mHandleReleased( HANDLE_TYPE_COUNT ),
249 mScrollDirection( SCROLL_NONE ),
250 mScrollThreshold( SCROLL_THRESHOLD ),
251 mScrollSpeed( SCROLL_SPEED ),
252 mScrollDistance( SCROLL_DISTANCE ),
254 mActiveCopyPastePopup( false ),
255 mPopupSetNewPosition( true ),
256 mCursorBlinkStatus( true ),
257 mDelayCursorBlink( false ),
258 mPrimaryCursorVisible( false ),
259 mSecondaryCursorVisible( false ),
260 mFlipSelectionHandlesOnCross( false ),
261 mFlipLeftSelectionHandleDirection( false ),
262 mFlipRightSelectionHandleDirection( false ),
263 mIsHandlePanning( false ),
264 mIsHandleCurrentlyCrossed( false ),
265 mIsHandlePreviouslyCrossed( false ),
266 mNotifyEndOfScroll( false ),
267 mHorizontalScrollingEnabled( false ),
268 mVerticalScrollingEnabled( false ),
269 mSmoothHandlePanEnabled( false ),
270 mIsHighlightBoxActive( false )
272 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
273 mHighlightShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
278 * Relayout of the decorations owned by the decorator.
279 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
281 void Relayout( const Vector2& size )
285 // TODO - Remove this if nothing is active
288 // Show or hide the cursors
293 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
294 mPrimaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
295 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
296 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
297 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
298 if( mPrimaryCursorVisible )
300 mPrimaryCursor.SetProperty( Actor::Property::POSITION, Vector2( cursor.position.x,
301 cursor.position.y ) );
302 mPrimaryCursor.SetProperty( Actor::Property::SIZE, Size( mCursorWidth, cursor.cursorHeight ) );
304 mPrimaryCursor.SetProperty( Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus );
306 if( mSecondaryCursor )
308 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
309 mSecondaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
310 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
311 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
312 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
313 if( mSecondaryCursorVisible )
315 mSecondaryCursor.SetProperty( Actor::Property::POSITION, Vector2( cursor.position.x,
316 cursor.position.y ) );
317 mSecondaryCursor.SetProperty( Actor::Property::SIZE, Size( mCursorWidth, cursor.cursorHeight ) );
319 mSecondaryCursor.SetProperty( Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus );
322 // Show or hide the grab handle
323 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
324 bool newGrabHandlePosition = false;
325 grabHandle.horizontallyVisible = false;
326 grabHandle.verticallyVisible = false;
327 if( grabHandle.active )
329 grabHandle.horizontallyVisible = ( ( mControlSize.width - ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) ) > -Math::MACHINE_EPSILON_1000 ) &&
330 ( grabHandle.position.x > -Math::MACHINE_EPSILON_1000 ) );
331 grabHandle.verticallyVisible = ( ( ( mControlSize.height - grabHandle.lineHeight ) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) &&
332 ( grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) );
334 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible;
339 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
340 SetGrabHandlePosition();
342 // Sets the grab handle image according if it's pressed, flipped, etc.
343 SetHandleImage( GRAB_HANDLE );
345 newGrabHandlePosition = true;
348 if( grabHandle.actor )
350 grabHandle.actor.SetProperty( Actor::Property::VISIBLE, isVisible );
353 else if( grabHandle.actor )
355 grabHandle.actor.Unparent();
358 // Show or hide the selection handles/highlight
359 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
360 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
361 bool newPrimaryHandlePosition = false;
362 bool newSecondaryHandlePosition = false;
364 primary.horizontallyVisible = ( ( mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
365 ( primary.position.x > -Math::MACHINE_EPSILON_1000 ) );
366 primary.verticallyVisible = ( ( ( mControlSize.height - primary.lineHeight ) - primary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
367 ( primary.position.y + ( primary.verticallyFlipped ? 0.f : primary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
368 secondary.horizontallyVisible = ( ( mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
369 ( secondary.position.x > -Math::MACHINE_EPSILON_1000 ) );
370 secondary.verticallyVisible = ( ( ( mControlSize.height - secondary.lineHeight ) - secondary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
371 ( secondary.position.y + ( secondary.verticallyFlipped ? 0.f : secondary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
373 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
374 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
376 if( primary.active || secondary.active )
378 if( primaryVisible || secondaryVisible )
380 CreateSelectionHandles();
384 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
386 // Sets the primary handle image according if it's pressed, flipped, etc.
387 SetHandleImage( LEFT_SELECTION_HANDLE );
389 SetSelectionHandleMarkerSize( primary );
391 newPrimaryHandlePosition = true;
394 if( secondaryVisible )
396 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
398 // Sets the secondary handle image according if it's pressed, flipped, etc.
399 SetHandleImage( RIGHT_SELECTION_HANDLE );
401 SetSelectionHandleMarkerSize( secondary );
403 newSecondaryHandlePosition = true;
409 primary.actor.SetProperty( Actor::Property::VISIBLE, primaryVisible );
411 if( secondary.actor )
413 secondary.actor.SetProperty( Actor::Property::VISIBLE, secondaryVisible );
421 primary.actor.Unparent();
423 if( secondary.actor )
425 secondary.actor.Unparent();
429 if( mIsHighlightBoxActive )
436 if( mHighlightActor )
438 mHighlightActor.Unparent();
442 if( newGrabHandlePosition ||
443 newPrimaryHandlePosition ||
444 newSecondaryHandlePosition )
446 // Setup property notifications to find whether the handles leave the boundaries of the current display.
447 SetupActiveLayerPropertyNotifications();
450 if( mActiveCopyPastePopup &&
451 ( primaryVisible || secondaryVisible ) )
454 mPopupSetNewPosition = true;
458 if( mCopyPastePopup.actor )
460 mCopyPastePopup.actor.HidePopup();
461 mPopupSetNewPosition = true;
466 void UpdatePositions( const Vector2& scrollOffset )
468 mCursor[PRIMARY_CURSOR].position += scrollOffset;
469 mCursor[SECONDARY_CURSOR].position += scrollOffset;
470 mHandle[ GRAB_HANDLE ].position += scrollOffset;
471 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
472 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
473 mHighlightPosition += scrollOffset;
478 if( !mCopyPastePopup.actor )
483 if( !mCopyPastePopup.actor.GetParent() )
485 mActiveLayer.Add( mCopyPastePopup.actor );
488 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
489 mCopyPastePopup.actor.ShowPopup();
492 float CalculateVerticalPopUpPosition( float halfHeight, bool preferBelow )
494 float yPosition = 0.f;
496 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
497 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
498 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
500 if( primaryHandle.active || secondaryHandle.active )
502 // The origin of the decorator's coordinate system in world coords.
503 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION ) - mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ) * ACTIVE_LAYER_ANCHOR_POINT;
507 // Find out if there is enough space for the popup at the bottom.
508 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
509 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
511 float maxY = std::max( primaryBelowY, secondaryBelowY );
513 yPosition = halfHeight + maxY;
515 if( originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w )
517 // Does not fit below.
519 // Try to fit first below the non active handle. Otherwise above the active handle.
520 if( RIGHT_SELECTION_HANDLE == mHandleReleased )
522 if( primaryBelowY < secondaryBelowY )
524 yPosition = halfHeight + primaryBelowY;
528 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
531 else if( LEFT_SELECTION_HANDLE == mHandleReleased )
533 if( secondaryBelowY < primaryBelowY )
535 yPosition = halfHeight + secondaryBelowY;
539 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
543 // Check the handle is whithin the decoration box.
544 if( originWorldCoords.y + yPosition < mBoundingBox.y )
546 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
549 if( originWorldCoords.y + yPosition > mBoundingBox.w )
551 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
557 // Find out if there is enough space for the popup at the top.
558 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
559 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
561 float minY = std::min( primaryTopY, secondaryTopY );
563 yPosition = -halfHeight + minY;
565 } // ( primaryHandle.active || secondaryHandle.active )
566 else if( grabHandle.active )
570 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
574 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
581 void ConstrainPopupPosition( const Vector3& popupHalfSize )
583 // Check if the popup is within the boundaries of the decoration box.
585 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
587 // The origin of the decorator's coordinate system in world coords.
588 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION ) - mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ) * ACTIVE_LAYER_ANCHOR_POINT;
590 // The popup's position in world coords.
591 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
593 if( popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x )
595 mCopyPastePopup.position.x += mBoundingBox.x - ( popupPositionWorldCoords.x - popupHalfSize.width );
597 else if( popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z )
599 mCopyPastePopup.position.x += mBoundingBox.z - ( popupPositionWorldCoords.x + popupHalfSize.width );
602 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
603 if( popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y )
605 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition( popupHalfSize.height, true ); // true -> prefer to set the popup's position below.
609 void SetPopupPosition( Actor actor )
611 if( !mActiveCopyPastePopup )
616 // Retrieves the popup's size after relayout.
617 const Vector3 popupSize( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
618 const Vector3 popupHalfSize = popupSize * 0.5f;
620 if( mPopupSetNewPosition )
622 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
623 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
624 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
626 if( primaryHandle.active || secondaryHandle.active )
628 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
629 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
631 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
633 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - ( primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING );
634 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - ( secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING );
636 mCopyPastePopup.position.y = std::min( primaryY, secondaryY );
638 else if( grabHandle.active )
640 mCopyPastePopup.position.x = grabHandle.position.x;
642 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - ( grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING );
644 } // mPopupSetNewPosition
646 // It may change the popup's position to fit within the decoration box.
647 ConstrainPopupPosition( popupHalfSize );
649 SetUpPopupPositionNotifications( popupHalfSize );
651 // Prevent pixel mis-alignment by rounding down.
652 mCopyPastePopup.position.x = floorf( mCopyPastePopup.position.x );
653 mCopyPastePopup.position.y = floorf( mCopyPastePopup.position.y );
655 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION, mCopyPastePopup.position );
656 mPopupSetNewPosition = false;
659 void CreateCursor( Control& cursor, const Vector4& color )
661 cursor = Control::New();
662 cursor.SetBackgroundColor( color );
663 cursor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
664 cursor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
667 // Add or Remove cursor(s) from parent
670 if( mActiveCursor == ACTIVE_CURSOR_NONE )
674 mPrimaryCursor.Unparent();
676 if( mSecondaryCursor )
678 mSecondaryCursor.Unparent();
683 // Create Primary and or Secondary Cursor(s) if active and add to parent
684 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
685 mActiveCursor == ACTIVE_CURSOR_BOTH )
687 if ( !mPrimaryCursor )
689 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
690 #ifdef DECORATOR_DEBUG
691 mPrimaryCursor.SetProperty( Dali::Actor::Property::NAME, "PrimaryCursorActor" );
695 if( !mPrimaryCursor.GetParent() )
697 mActiveLayer.Add( mPrimaryCursor );
701 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
703 if ( !mSecondaryCursor )
705 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
706 #ifdef DECORATOR_DEBUG
707 mSecondaryCursor.SetProperty( Dali::Actor::Property::NAME, "SecondaryCursorActor" );
711 if( !mSecondaryCursor.GetParent() )
713 mActiveLayer.Add( mSecondaryCursor );
718 if( mSecondaryCursor )
720 mSecondaryCursor.Unparent();
726 bool OnCursorBlinkTimerTick()
728 if( !mDelayCursorBlink )
731 if ( mPrimaryCursor )
733 mPrimaryCursor.SetProperty( Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus );
735 if ( mSecondaryCursor )
737 mSecondaryCursor.SetProperty( Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus );
740 mCursorBlinkStatus = !mCursorBlinkStatus;
745 mDelayCursorBlink = false;
753 // Will consume tap gestures on handles.
754 mTapDetector = TapGestureDetector::New();
756 // Will consume double tap gestures on handles.
757 mTapDetector.SetMaximumTapsRequired( 2u );
759 // Will consume long press gestures on handles.
760 mLongPressDetector = LongPressGestureDetector::New();
762 // Detects pan gestures on handles.
763 mPanDetector = PanGestureDetector::New();
764 mPanDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
767 void CreateActiveLayer()
771 mActiveLayer = Layer::New();
772 #ifdef DECORATOR_DEBUG
773 mActiveLayer.SetProperty( Actor::Property::NAME, "ActiveLayerActor" );
776 mActiveLayer.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
777 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
779 // Add the active layer telling the controller it doesn't need clipping.
780 mController.AddDecoration( mActiveLayer, false );
783 mActiveLayer.RaiseToTop();
786 void SetSelectionHandleMarkerSize( HandleImpl& handle )
788 if( handle.markerActor )
790 handle.markerActor.SetProperty( Actor::Property::SIZE, Vector2( 0, handle.lineHeight ) );
794 void CreateGrabHandle()
796 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
797 if( !grabHandle.actor )
799 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED].size() )
801 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
802 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
803 grabHandle.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
805 // Area that Grab handle responds to, larger than actual handle so easier to move
806 #ifdef DECORATOR_DEBUG
807 grabHandle.actor.SetProperty( Dali::Actor::Property::NAME, "GrabHandleActor" );
808 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
810 grabHandle.grabArea = Control::New();
811 Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
812 control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
813 grabHandle.grabArea.SetProperty( Dali::Actor::Property::NAME, "GrabArea" );
817 grabHandle.grabArea = Actor::New();
818 grabHandle.grabArea.SetProperty( Dali::Actor::Property::NAME, "GrabArea" );
821 grabHandle.grabArea = Actor::New();
824 grabHandle.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
825 grabHandle.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
826 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
827 grabHandle.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
828 grabHandle.actor.Add( grabHandle.grabArea );
829 grabHandle.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
831 grabHandle.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
833 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
834 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
835 mTapDetector.Attach( grabHandle.actor );
836 mLongPressDetector.Attach( grabHandle.actor );
838 // The grab handle's area is attached to the pan detector.
839 // The OnPan() method is connected to the signals emitted by the pan detector.
840 mPanDetector.Attach( grabHandle.grabArea );
842 mActiveLayer.Add( grabHandle.actor );
846 if( grabHandle.actor && !grabHandle.actor.GetParent() )
848 mActiveLayer.Add( grabHandle.actor );
852 void CreateHandleMarker( HandleImpl& handle, const std::string& image, HandleType handleType )
856 handle.markerActor = ImageView::New( image );
857 handle.markerActor.SetProperty( Actor::Property::COLOR, mHandleColor );
858 handle.actor.Add( handle.markerActor );
860 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
862 if( LEFT_SELECTION_HANDLE == handleType )
864 handle.markerActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT );
865 handle.markerActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_RIGHT );
867 else if( RIGHT_SELECTION_HANDLE == handleType )
869 handle.markerActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT );
870 handle.markerActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
875 void CreateSelectionHandles()
877 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
880 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size() )
882 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
883 #ifdef DECORATOR_DEBUG
884 primary.actor.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleOne");
886 primary.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
887 GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
888 primary.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
890 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
891 #ifdef DECORATOR_DEBUG
892 primary.grabArea.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleOneGrabArea");
894 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
895 primary.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
896 primary.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
897 primary.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
899 primary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
901 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
902 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
903 mTapDetector.Attach( primary.actor );
904 mLongPressDetector.Attach( primary.actor );
906 // The handle's area is attached to the pan detector.
907 // The OnPan() method is connected to the signals emitted by the pan detector.
908 mPanDetector.Attach( primary.grabArea );
910 primary.actor.Add( primary.grabArea );
912 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
916 if( primary.actor && !primary.actor.GetParent() )
918 mActiveLayer.Add( primary.actor );
921 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
922 if( !secondary.actor )
924 if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size() )
926 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
927 #ifdef DECORATOR_DEBUG
928 secondary.actor.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleTwo");
930 secondary.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
931 GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
932 secondary.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
934 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
935 #ifdef DECORATOR_DEBUG
936 secondary.grabArea.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleTwoGrabArea");
938 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
939 secondary.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
940 secondary.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
941 secondary.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
943 secondary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
945 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
946 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
947 mTapDetector.Attach( secondary.actor );
948 mLongPressDetector.Attach( secondary.actor );
950 // The handle's area is attached to the pan detector.
951 // The OnPan() method is connected to the signals emitted by the pan detector.
952 mPanDetector.Attach( secondary.grabArea );
954 secondary.actor.Add( secondary.grabArea );
956 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
960 if( secondary.actor && !secondary.actor.GetParent() )
962 mActiveLayer.Add( secondary.actor );
966 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
968 // Gets the world position of the active layer. The active layer is where the handles are added.
969 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION );
971 // The grab handle position in world coords.
972 // The active layer's world position is the center of the active layer. The origin of the
973 // coord system of the handles is the top left of the active layer.
974 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f );
975 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f );
978 void SetGrabHandlePosition()
980 // Reference to the grab handle.
981 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
983 // Transforms the handle position into world coordinates.
984 // @note This is not the same value as grabHandle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
985 // as it's transforming the handle's position set by the text-controller and not
986 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
987 // retrieves the position of the center of the actor but the handle's position set
988 // by the text controller is not the center of the actor.
989 Vector2 grabHandleWorldPosition;
990 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
992 // Check if the grab handle exceeds the boundaries of the decoration box.
993 // At the moment only the height is checked for the grab handle.
995 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
996 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
997 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
999 // The grab handle 'y' position in local coords.
1000 // If the grab handle exceeds the bottom of the decoration box,
1001 // set the 'y' position to the top of the line.
1002 // The SetGrabHandleImage() method will change the orientation.
1003 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
1005 if( grabHandle.actor )
1007 grabHandle.actor.SetProperty( Actor::Property::POSITION, Vector2( grabHandle.position.x + floor( 0.5f * mCursorWidth ) + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f ),
1008 yLocalPosition + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f ) ) );
1012 void SetSelectionHandlePosition( HandleType type )
1014 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
1016 // Reference to the selection handle.
1017 HandleImpl& handle = mHandle[type];
1019 // Transforms the handle position into world coordinates.
1020 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1021 // as it's transforming the handle's position set by the text-controller and not
1022 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1023 // retrieves the position of the center of the actor but the handle's position set
1024 // by the text controller is not the center of the actor.
1025 Vector2 handleWorldPosition;
1026 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
1028 // Whether to flip the handle (horizontally).
1029 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1031 // Whether to flip the handles if they are crossed.
1032 bool crossFlip = false;
1033 if( mFlipSelectionHandlesOnCross || !mIsHandlePanning )
1035 crossFlip = mIsHandleCurrentlyCrossed;
1038 // Whether the handle was crossed before start the panning.
1039 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1041 // Does not flip if both conditions are true (double flip)
1042 flipHandle = flipHandle != ( crossFlip || isHandlePreviouslyCrossed );
1044 // Will flip the handles vertically if the user prefers it.
1045 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1047 if( crossFlip || isHandlePreviouslyCrossed )
1049 if( isPrimaryHandle )
1051 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1055 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1059 // Check if the selection handle exceeds the boundaries of the decoration box.
1060 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
1061 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
1063 // Does not flip if both conditions are true (double flip)
1064 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
1068 if( handle.actor && !handle.horizontallyFlipped )
1070 // Change the anchor point to flip the image.
1071 handle.actor.SetProperty( Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
1073 handle.horizontallyFlipped = true;
1078 if( handle.actor && handle.horizontallyFlipped )
1080 // Reset the anchor point.
1081 handle.actor.SetProperty( Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
1083 handle.horizontallyFlipped = false;
1087 // Whether to flip the handle vertically.
1088 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
1089 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
1090 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
1092 // The primary selection handle 'y' position in local coords.
1093 // If the handle exceeds the bottom of the decoration box,
1094 // set the 'y' position to the top of the line.
1095 // The SetHandleImage() method will change the orientation.
1096 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1100 handle.actor.SetProperty( Actor::Property::POSITION, Vector2( handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f ),
1101 yLocalPosition + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f ) ) );
1105 void SetHandleImage( HandleType type )
1107 HandleImpl& handle = mHandle[type];
1109 HandleType markerType = HANDLE_TYPE_COUNT;
1110 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1111 if( LEFT_SELECTION_HANDLE == type )
1113 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1114 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1116 else if( RIGHT_SELECTION_HANDLE == type )
1118 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1119 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1122 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1125 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1127 handle.actor.SetImage( mHandleImages[type][imageType] );
1130 if( HANDLE_TYPE_COUNT != markerType )
1132 if( handle.markerActor )
1134 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1135 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
1139 // Whether to flip the handle vertically.
1142 handle.actor.SetProperty( Actor::Property::ORIENTATION, Quaternion( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS ) );
1146 void CreateHighlight()
1148 if( !mHighlightActor )
1150 mHighlightActor = Actor::New();
1152 mHighlightActor.SetProperty( Dali::Actor::Property::NAME, "HighlightActor" );
1153 mHighlightActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
1154 mHighlightActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
1155 mHighlightActor.SetProperty( Actor::Property::COLOR, mHighlightColor );
1156 mHighlightActor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_COLOR );
1159 // Add the highlight box telling the controller it needs clipping.
1160 mController.AddDecoration( mHighlightActor, true );
1163 void UpdateHighlight()
1165 if ( mHighlightActor )
1167 // Sets the position of the highlight actor inside the decorator.
1168 mHighlightActor.SetProperty( Actor::Property::POSITION, Vector2( mHighlightPosition.x + mHighlightOutlineOffset,
1169 mHighlightPosition.y + mHighlightOutlineOffset ) );
1171 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1172 if( 0u != numberOfQuads )
1174 // Set the size of the highlighted text to the actor.
1175 mHighlightActor.SetProperty( Actor::Property::SIZE, mHighlightSize );
1177 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1178 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1179 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1181 Vector<Vector2> vertices;
1182 Vector<unsigned short> indices;
1184 vertices.Reserve( 4u * numberOfQuads );
1185 indices.Reserve( 6u * numberOfQuads );
1187 // Index to the vertex.
1188 unsigned int v = 0u;
1190 // Traverse all quads.
1191 for( Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1192 endIt = mHighlightQuadList.End();
1196 const Vector4& quad = *it;
1201 vertex.x = quad.x - offsetX;
1202 vertex.y = quad.y - offsetY;
1203 vertices.PushBack( vertex );
1206 vertex.x = quad.z - offsetX;
1207 vertex.y = quad.y - offsetY;
1208 vertices.PushBack( vertex );
1210 // bottom-left (v+2)
1211 vertex.x = quad.x - offsetX;
1212 vertex.y = quad.w - offsetY;
1213 vertices.PushBack( vertex );
1215 // bottom-right (v+3)
1216 vertex.x = quad.z - offsetX;
1217 vertex.y = quad.w - offsetY;
1218 vertices.PushBack( vertex );
1220 // triangle A (3, 1, 0)
1221 indices.PushBack( v + 3 );
1222 indices.PushBack( v + 1 );
1223 indices.PushBack( v );
1225 // triangle B (0, 2, 3)
1226 indices.PushBack( v );
1227 indices.PushBack( v + 2 );
1228 indices.PushBack( v + 3 );
1231 if( ! mQuadVertices )
1233 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
1236 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1238 if( !mQuadGeometry )
1240 mQuadGeometry = Geometry::New();
1241 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1243 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1245 if( !mHighlightRenderer )
1247 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1248 mHighlightActor.AddRenderer( mHighlightRenderer );
1252 mHighlightQuadList.Clear();
1254 if( mHighlightRenderer )
1256 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1261 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1263 if( Gesture::Started == gesture.state )
1265 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1267 handle.globalPosition.x = handle.position.x;
1268 handle.globalPosition.y = handle.position.y;
1271 handle.grabDisplacementX += gesture.displacement.x;
1272 handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.displacement.y );
1274 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1275 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1276 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f );
1278 if( ( Gesture::Started == gesture.state ) ||
1279 ( Gesture::Continuing == gesture.state ) )
1282 mController.GetTargetSize( targetSize );
1284 if( mHorizontalScrollingEnabled &&
1285 ( x < mScrollThreshold ) )
1287 mScrollDirection = SCROLL_RIGHT;
1288 mHandleScrolling = type;
1291 else if( mHorizontalScrollingEnabled &&
1292 ( x > targetSize.width - mScrollThreshold ) )
1294 mScrollDirection = SCROLL_LEFT;
1295 mHandleScrolling = type;
1298 else if( mVerticalScrollingEnabled &&
1299 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1301 mScrollDirection = SCROLL_TOP;
1302 mHandleScrolling = type;
1305 else if( mVerticalScrollingEnabled &&
1306 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1308 mScrollDirection = SCROLL_BOTTOM;
1309 mHandleScrolling = type;
1314 mHandleScrolling = HANDLE_TYPE_COUNT;
1316 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1319 mIsHandlePanning = true;
1321 else if( ( Gesture::Finished == gesture.state ) ||
1322 ( Gesture::Cancelled == gesture.state ) )
1325 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1327 mNotifyEndOfScroll = false;
1328 mHandleScrolling = HANDLE_TYPE_COUNT;
1330 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1334 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1339 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1341 handle.pressed = false;
1343 mIsHandlePanning = false;
1347 void OnPan( Actor actor, const PanGesture& gesture )
1349 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1350 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1351 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1353 if( actor == grabHandle.grabArea )
1355 DoPan( grabHandle, GRAB_HANDLE, gesture );
1357 else if( actor == primarySelectionHandle.grabArea )
1359 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1361 else if( actor == secondarySelectionHandle.grabArea )
1363 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1367 bool OnGrabHandleTouched( Actor actor, const TouchData& touch )
1369 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1371 // Switch between pressed/release grab-handle images
1372 if( touch.GetPointCount() > 0 &&
1375 const PointState::Type state = touch.GetState( 0 );
1377 if( PointState::DOWN == state )
1379 grabHandle.pressed = true;
1381 else if( ( PointState::UP == state ) ||
1382 ( PointState::INTERRUPTED == state ) )
1384 grabHandle.pressed = false;
1387 SetHandleImage( GRAB_HANDLE );
1390 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1394 bool OnHandleOneTouched( Actor actor, const TouchData& touch )
1396 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1398 // Switch between pressed/release selection handle images
1399 if( touch.GetPointCount() > 0 &&
1400 primarySelectionHandle.actor )
1402 const PointState::Type state = touch.GetState( 0 );
1404 if( PointState::DOWN == state )
1406 primarySelectionHandle.pressed = true;
1407 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1409 else if( ( PointState::UP == state ) ||
1410 ( PointState::INTERRUPTED == state ) )
1412 primarySelectionHandle.pressed = false;
1413 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1414 mIsHandlePanning = false;
1415 mHandleReleased = LEFT_SELECTION_HANDLE;
1418 SetHandleImage( LEFT_SELECTION_HANDLE );
1421 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1425 bool OnHandleTwoTouched( Actor actor, const TouchData& touch )
1427 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1429 // Switch between pressed/release selection handle images
1430 if( touch.GetPointCount() > 0 &&
1431 secondarySelectionHandle.actor )
1433 const PointState::Type state = touch.GetState( 0 );
1435 if( PointState::DOWN == state )
1437 secondarySelectionHandle.pressed = true;
1438 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1440 else if( ( PointState::UP == state ) ||
1441 ( PointState::INTERRUPTED == state ) )
1443 secondarySelectionHandle.pressed = false;
1444 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1445 mIsHandlePanning = false;
1446 mHandleReleased = RIGHT_SELECTION_HANDLE;
1449 SetHandleImage( RIGHT_SELECTION_HANDLE );
1452 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1456 void HandleResetPosition( PropertyNotification& source )
1458 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1460 if( grabHandle.active )
1462 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1463 SetGrabHandlePosition();
1465 // Sets the grab handle image according if it's pressed, flipped, etc.
1466 SetHandleImage( GRAB_HANDLE );
1470 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1471 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1473 // Sets the primary handle image according if it's pressed, flipped, etc.
1474 SetHandleImage( LEFT_SELECTION_HANDLE );
1476 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1477 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1479 // Sets the secondary handle image according if it's pressed, flipped, etc.
1480 SetHandleImage( RIGHT_SELECTION_HANDLE );
1484 void SetupActiveLayerPropertyNotifications()
1491 // Vertical notifications.
1493 // Disconnect any previous connected callback.
1494 if( mHandleVerticalLessThanNotification )
1496 mHandleVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1497 mActiveLayer.RemovePropertyNotification( mHandleVerticalLessThanNotification );
1500 if( mHandleVerticalGreaterThanNotification )
1502 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1503 mActiveLayer.RemovePropertyNotification( mHandleVerticalGreaterThanNotification );
1506 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1507 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1508 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1510 if( grabHandle.active )
1512 if( grabHandle.verticallyFlipped )
1514 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1515 mHandleVerticalGreaterThanNotification.Reset();
1517 // The vertical distance from the center of the active layer to the top edje of the display.
1518 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1520 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1521 LessThanCondition( mBoundingBox.y + topHeight ) );
1523 // Notifies the change from false to true and from true to false.
1524 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1526 // Connects the signals with the callbacks.
1527 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1531 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1532 mHandleVerticalLessThanNotification.Reset();
1534 // The vertical distance from the center of the active layer to the bottom edje of the display.
1535 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1537 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1538 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1540 // Notifies the change from false to true and from true to false.
1541 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1543 // Connects the signals with the callbacks.
1544 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1547 else // The selection handles are active
1549 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1551 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1552 mHandleVerticalGreaterThanNotification.Reset();
1554 // The vertical distance from the center of the active layer to the top edje of the display.
1555 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1557 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1558 LessThanCondition( mBoundingBox.y + topHeight ) );
1560 // Notifies the change from false to true and from true to false.
1561 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1563 // Connects the signals with the callbacks.
1564 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1566 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1568 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1569 mHandleVerticalLessThanNotification.Reset();
1571 // The vertical distance from the center of the active layer to the bottom edje of the display.
1572 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1573 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1575 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1576 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1578 // Notifies the change from false to true and from true to false.
1579 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1581 // Connects the signals with the callbacks.
1582 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1586 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1588 // The vertical distance from the center of the active layer to the top edje of the display.
1589 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1590 -primaryHandle.position.y + primaryHandle.size.height :
1591 -secondaryHandle.position.y + secondaryHandle.size.height );
1593 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1594 LessThanCondition( mBoundingBox.y + topHeight ) );
1596 // Notifies the change from false to true and from true to false.
1597 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1599 // Connects the signals with the callbacks.
1600 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1602 // The vertical distance from the center of the active layer to the bottom edje of the display.
1603 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1604 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1605 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1607 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1608 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1610 // Notifies the change from false to true and from true to false.
1611 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1613 // Connects the signals with the callbacks.
1614 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1618 // Horizontal notifications.
1620 // Disconnect any previous connected callback.
1621 if( mHandleHorizontalLessThanNotification )
1623 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1624 mActiveLayer.RemovePropertyNotification( mHandleHorizontalLessThanNotification );
1627 if( mHandleHorizontalGreaterThanNotification )
1629 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1630 mActiveLayer.RemovePropertyNotification( mHandleHorizontalGreaterThanNotification );
1633 if( primaryHandle.active || secondaryHandle.active )
1635 // The horizontal distance from the center of the active layer to the left edje of the display.
1636 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1637 -secondaryHandle.position.x + secondaryHandle.size.width );
1639 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1640 LessThanCondition( mBoundingBox.x + leftWidth ) );
1642 // Notifies the change from false to true and from true to false.
1643 mHandleHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1645 // Connects the signals with the callbacks.
1646 mHandleHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1648 // The horizontal distance from the center of the active layer to the right edje of the display.
1649 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1650 secondaryHandle.position.x + secondaryHandle.size.width );
1652 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1653 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1655 // Notifies the change from false to true and from true to false.
1656 mHandleHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1658 // Connects the signals with the callbacks.
1659 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1665 float AlternatePopUpPositionRelativeToCursor( bool topBottom )
1667 float alternativePosition = 0.0f;
1669 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1671 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1672 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1673 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1674 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1676 if( primaryHandle.active || secondaryHandle.active )
1678 float handleY = 0.f;
1679 float maxHandleHeight = 0.f;
1681 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1682 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1684 if( primaryVisible && secondaryVisible )
1686 handleY = std::max( primaryHandle.position.y, secondaryHandle.position.y );
1687 maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1689 else if( primaryVisible && !secondaryVisible )
1691 handleY = primaryHandle.position.y;
1692 maxHandleHeight = primaryHandle.size.height;
1694 else if( !primaryVisible && secondaryVisible )
1696 handleY = secondaryHandle.position.y;
1697 maxHandleHeight = secondaryHandle.size.height;
1700 alternativePosition = handleY + ( topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight );
1704 alternativePosition = cursor.position.y + ( topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height );
1707 return alternativePosition;
1710 void PopUpLeavesTopBoundary( PropertyNotification& source )
1712 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1714 // Sets the position of the popup below.
1715 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION_Y, floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, true ) ) );
1718 void PopUpLeavesBottomBoundary( PropertyNotification& source )
1720 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1722 // Sets the position of the popup above.
1723 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION_Y, floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, false ) ) );
1726 void SetUpPopupPositionNotifications( const Vector3& popupHalfSize )
1728 // Disconnect any previous connected callback.
1729 if( mPopupTopExceedNotification )
1731 mPopupTopExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1732 mCopyPastePopup.actor.RemovePropertyNotification( mPopupTopExceedNotification );
1735 if( mPopupBottomExceedNotification )
1737 mPopupBottomExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1738 mCopyPastePopup.actor.RemovePropertyNotification( mPopupBottomExceedNotification );
1741 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1743 // Exceeding vertical boundary
1745 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1746 LessThanCondition( mBoundingBox.y + popupHalfSize.height ) );
1748 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1749 GreaterThanCondition( mBoundingBox.w - popupHalfSize.height ) );
1751 // Notifies the change from false to true and from true to false.
1752 mPopupTopExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1753 mPopupBottomExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1755 mPopupTopExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1756 mPopupBottomExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1759 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName )
1761 ImageDimensions dimensions = Dali::GetOriginalImageSize( imageFileName );
1763 HandleImpl& handle = mHandle[handleType];
1764 handle.size = Size( dimensions.GetWidth(), dimensions.GetHeight() );
1766 mHandleImages[handleType][handleImageType] = imageFileName;
1769 void SetScrollThreshold( float threshold )
1771 mScrollThreshold = threshold;
1774 float GetScrollThreshold() const
1776 return mScrollThreshold;
1779 void SetScrollSpeed( float speed )
1781 mScrollSpeed = speed;
1782 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1785 float GetScrollSpeed() const
1787 return mScrollSpeed;
1790 void NotifyEndOfScroll()
1796 mNotifyEndOfScroll = true;
1801 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1803 * It only starts the timer if it's already created.
1805 void StartScrollTimer()
1809 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1810 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1813 if( !mScrollTimer.IsRunning() )
1815 mScrollTimer.Start();
1820 * Stops the timer used to scroll the text.
1822 void StopScrollTimer()
1826 mScrollTimer.Stop();
1831 * Callback called by the timer used to scroll the text.
1833 * It calculates and sets a new scroll position.
1835 bool OnScrollTimerTick()
1837 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1842 switch( mScrollDirection )
1846 x = mScrollDistance;
1851 x = -mScrollDistance;
1856 y = mScrollDistance;
1861 y = -mScrollDistance;
1868 mController.DecorationEvent( mHandleScrolling,
1877 ControllerInterface& mController;
1879 TapGestureDetector mTapDetector;
1880 PanGestureDetector mPanDetector;
1881 LongPressGestureDetector mLongPressDetector;
1883 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1884 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1886 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1887 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1888 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1889 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1890 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1891 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1892 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1893 Control mPrimaryCursor;
1894 Control mSecondaryCursor;
1896 Actor mHighlightActor; ///< Actor to display highlight
1897 Renderer mHighlightRenderer;
1898 Shader mHighlightShader; ///< Shader used for highlight
1899 Property::Map mQuadVertexFormat;
1900 PopupImpl mCopyPastePopup;
1901 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1902 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1904 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1905 Vector4 mHandleColor;
1907 CursorImpl mCursor[CURSOR_COUNT];
1908 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1910 PropertyBuffer mQuadVertices;
1911 Geometry mQuadGeometry;
1912 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1914 Vector4 mBoundingBox; ///< The bounding box in world coords.
1915 Vector4 mHighlightColor; ///< Color of the highlight
1916 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1917 Size mHighlightSize; ///< The size of the highlighted text.
1918 Size mControlSize; ///< The control's size. Set by the Relayout.
1919 float mHighlightOutlineOffset; ///< The outline's offset.
1921 unsigned int mActiveCursor;
1922 unsigned int mCursorBlinkInterval;
1923 float mCursorBlinkDuration;
1924 float mCursorWidth; ///< The width of the cursors in pixels.
1925 HandleType mHandleScrolling; ///< The handle which is scrolling.
1926 HandleType mHandleReleased; ///< The last handle released.
1927 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1928 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1929 float mScrollSpeed; ///< The scroll speed in pixels per second.
1930 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1931 int mTextDepth; ///< The depth used to render the text.
1933 bool mActiveCopyPastePopup : 1;
1934 bool mPopupSetNewPosition : 1;
1935 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1936 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1937 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1938 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1939 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1940 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1941 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1942 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1943 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1944 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1945 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1946 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1947 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1948 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1949 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1952 DecoratorPtr Decorator::New( ControllerInterface& controller,
1953 TextSelectionPopupCallbackInterface& callbackInterface )
1955 return DecoratorPtr( new Decorator( controller,
1956 callbackInterface ) );
1959 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1961 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1964 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1966 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1969 void Decorator::Relayout( const Vector2& size )
1971 mImpl->Relayout( size );
1974 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1976 mImpl->UpdatePositions( scrollOffset );
1981 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1983 mImpl->mActiveCursor = activeCursor;
1986 unsigned int Decorator::GetActiveCursor() const
1988 return mImpl->mActiveCursor;
1991 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1993 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1995 cursorImpl.position.x = x;
1996 cursorImpl.position.y = y;
1997 cursorImpl.cursorHeight = cursorHeight;
1998 cursorImpl.lineHeight = lineHeight;
2001 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
2003 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2005 x = cursorImpl.position.x;
2006 y = cursorImpl.position.y;
2007 cursorHeight = cursorImpl.cursorHeight;
2008 lineHeight = cursorImpl.lineHeight;
2011 const Vector2& Decorator::GetPosition( Cursor cursor ) const
2013 return mImpl->mCursor[cursor].position;
2016 void Decorator::SetGlyphOffset( Cursor cursor, float glyphOffset )
2018 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2020 cursorImpl.glyphOffset = glyphOffset;
2023 const float Decorator::GetGlyphOffset( Cursor cursor) const
2025 return mImpl->mCursor[cursor].glyphOffset;
2028 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
2030 mImpl->mCursor[cursor].color = color;
2033 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
2035 return mImpl->mCursor[cursor].color;
2038 void Decorator::StartCursorBlink()
2040 if ( !mImpl->mCursorBlinkTimer )
2042 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
2043 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
2046 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
2048 mImpl->mCursorBlinkTimer.Start();
2052 void Decorator::StopCursorBlink()
2054 if ( mImpl->mCursorBlinkTimer )
2056 mImpl->mCursorBlinkTimer.Stop();
2059 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2062 void Decorator::DelayCursorBlink()
2064 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2065 mImpl->mDelayCursorBlink = true;
2068 void Decorator::SetCursorBlinkInterval( float seconds )
2070 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
2073 float Decorator::GetCursorBlinkInterval() const
2075 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
2078 void Decorator::SetCursorBlinkDuration( float seconds )
2080 mImpl->mCursorBlinkDuration = seconds;
2083 float Decorator::GetCursorBlinkDuration() const
2085 return mImpl->mCursorBlinkDuration;
2088 void Decorator::SetCursorWidth( int width )
2090 mImpl->mCursorWidth = static_cast<float>( width );
2093 int Decorator::GetCursorWidth() const
2095 return static_cast<int>( mImpl->mCursorWidth );
2100 void Decorator::SetHandleActive( HandleType handleType, bool active )
2102 mImpl->mHandle[handleType].active = active;
2106 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
2108 mImpl->mIsHandlePreviouslyCrossed = false;
2111 // TODO: this is a work-around.
2112 // The problem is the handle actor does not receive the touch event with the Interrupt
2113 // state when the power button is pressed and the application goes to background.
2114 mImpl->mHandle[handleType].pressed = false;
2115 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2116 ImageView imageView = mImpl->mHandle[handleType].actor;
2117 if( imageReleased && imageView )
2119 imageView.SetImage( mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED] );
2125 bool Decorator::IsHandleActive( HandleType handleType ) const
2127 return mImpl->mHandle[handleType].active ;
2130 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName )
2132 mImpl->SetHandleImage( handleType, handleImageType, imageFileName );
2135 const std::string& Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
2137 return mImpl->mHandleImages[handleType][handleImageType];
2140 void Decorator::SetHandleColor( const Vector4& color )
2142 mImpl->mHandleColor = color;
2145 const Vector4& Decorator::GetHandleColor() const
2147 return mImpl->mHandleColor;
2150 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
2152 // Adjust handle's displacement
2153 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2155 handle.position.x = x;
2156 handle.position.y = y;
2157 handle.lineHeight = height;
2159 if( mImpl->mSmoothHandlePanEnabled )
2161 handle.grabDisplacementX = 0.f;
2162 handle.grabDisplacementY = 0.f;
2166 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2168 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2170 x = handle.position.x;
2171 y = handle.position.y;
2172 height = handle.lineHeight;
2175 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2177 return mImpl->mHandle[handleType].position;
2180 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2182 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2185 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2187 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2190 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2192 mImpl->mFlipSelectionHandlesOnCross = enable;
2195 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2197 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2198 mImpl->mFlipLeftSelectionHandleDirection = left;
2199 mImpl->mFlipRightSelectionHandleDirection = right;
2202 void Decorator::AddHighlight( unsigned int index, const Vector4& quad )
2204 *( mImpl->mHighlightQuadList.Begin() + index ) = quad;
2207 void Decorator::SetHighLightBox( const Vector2& position, const Size& size, float outlineOffset )
2209 mImpl->mHighlightPosition = position;
2210 mImpl->mHighlightSize = size;
2211 mImpl->mHighlightOutlineOffset = outlineOffset;
2214 void Decorator::ClearHighlights()
2216 mImpl->mHighlightQuadList.Clear();
2217 mImpl->mHighlightPosition = Vector2::ZERO;
2218 mImpl->mHighlightOutlineOffset = 0.f;
2221 void Decorator::ResizeHighlightQuads( unsigned int numberOfQuads )
2223 mImpl->mHighlightQuadList.Resize( numberOfQuads );
2226 void Decorator::SetHighlightColor( const Vector4& color )
2228 mImpl->mHighlightColor = color;
2231 const Vector4& Decorator::GetHighlightColor() const
2233 return mImpl->mHighlightColor;
2236 void Decorator::SetHighlightActive( bool active )
2238 mImpl->mIsHighlightBoxActive = active;
2241 bool Decorator::IsHighlightActive() const
2243 return mImpl->mIsHighlightBoxActive;
2246 bool Decorator::IsHighlightVisible() const
2248 return ( mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent() );
2251 void Decorator::SetTextDepth( int textDepth )
2253 mImpl->mTextDepth = textDepth;
2256 void Decorator::SetPopupActive( bool active )
2258 mImpl->mActiveCopyPastePopup = active;
2261 bool Decorator::IsPopupActive() const
2263 return mImpl->mActiveCopyPastePopup;
2266 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2268 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2270 if ( !mImpl->mCopyPastePopup.actor )
2272 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2273 #ifdef DECORATOR_DEBUG
2274 mImpl->mCopyPastePopup.actor.SetProperty( Dali::Actor::Property::NAME,"mCopyPastePopup");
2276 mImpl->mCopyPastePopup.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
2277 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::SetPopupPosition ); // Position popup after size negotiation
2280 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2283 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2285 return mImpl->mEnabledPopupButtons;
2290 void Decorator::SetScrollThreshold( float threshold )
2292 mImpl->SetScrollThreshold( threshold );
2295 float Decorator::GetScrollThreshold() const
2297 return mImpl->GetScrollThreshold();
2300 void Decorator::SetScrollSpeed( float speed )
2302 mImpl->SetScrollSpeed( speed );
2305 float Decorator::GetScrollSpeed() const
2307 return mImpl->GetScrollSpeed();
2310 void Decorator::NotifyEndOfScroll()
2312 mImpl->NotifyEndOfScroll();
2315 void Decorator::SetHorizontalScrollEnabled( bool enable )
2317 mImpl->mHorizontalScrollingEnabled = enable;
2320 bool Decorator::IsHorizontalScrollEnabled() const
2322 return mImpl->mHorizontalScrollingEnabled;
2325 void Decorator::SetVerticalScrollEnabled( bool enable )
2327 mImpl->mVerticalScrollingEnabled = enable;
2330 bool Decorator::IsVerticalScrollEnabled() const
2332 return mImpl->mVerticalScrollingEnabled;
2335 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2337 mImpl->mSmoothHandlePanEnabled = enable;
2340 bool Decorator::IsSmoothHandlePanEnabled() const
2342 return mImpl->mSmoothHandlePanEnabled;
2345 Decorator::~Decorator()
2350 Decorator::Decorator( ControllerInterface& controller,
2351 TextSelectionPopupCallbackInterface& callbackInterface )
2354 mImpl = new Decorator::Impl( controller, callbackInterface );
2359 } // namespace Toolkit