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/devel-api/common/stage.h>
26 #include <dali/public-api/events/touch-event.h>
27 #include <dali/public-api/events/pan-gesture.h>
28 #include <dali/public-api/object/property-notification.h>
29 #include <dali/public-api/rendering/geometry.h>
30 #include <dali/public-api/rendering/renderer.h>
31 #include <dali/devel-api/adaptor-framework/image-loading.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>
37 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
40 #define DECORATOR_DEBUG
50 #ifdef DECORATOR_DEBUG
51 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
61 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
62 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
63 const Dali::Vector3 ACTIVE_LAYER_ANCHOR_POINT( 0.5f, 0.5f, 0.5f );
65 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.
67 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
69 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
70 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
71 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
73 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
74 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.
75 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
77 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
79 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
81 const float POPUP_PADDING = 2.f; ///< Padding space between the highlight box and the text's popup.
83 typedef Dali::Vector<Dali::Vector4> QuadContainer;
86 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
87 * @param[in] boundingRectangle local bounding
88 * @param[out] Vector4 World coordinate bounding Box.
90 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
92 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
93 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
95 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
96 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
98 boundingBox = Dali::Vector4( originX,
100 originX + boundingRectangle.width,
101 originY + boundingRectangle.height );
104 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
106 // Convert to local coordinates and store as a Dali::Rect.
107 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
109 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
110 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
111 boundingRectangle.width = boundingBox.z - boundingBox.x;
112 boundingRectangle.height = boundingBox.w - boundingBox.y;
115 } // end of namespace
126 struct Decorator::Impl : public ConnectionTracker
140 : color( Dali::Color::BLACK ),
142 cursorHeight( 0.0f ),
162 grabDisplacementX( 0.f ),
163 grabDisplacementY( 0.f ),
165 horizontallyVisible( false ),
166 verticallyVisible( false ),
168 verticallyFlippedPreferred( false ),
169 horizontallyFlipped( false ),
170 verticallyFlipped( false ),
171 verticallyFlippedOnTouch( false )
177 ImageView markerActor;
180 Vector2 globalPosition;
182 float lineHeight; ///< Not the handle height
183 float grabDisplacementX;
184 float grabDisplacementY;
186 bool horizontallyVisible : 1;
187 bool verticallyVisible : 1;
189 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
190 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
191 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
192 bool verticallyFlippedOnTouch : 1; ///< Whether the handle is vertically flipped on touch.
202 TextSelectionPopup actor;
206 Impl( ControllerInterface& controller,
207 TextSelectionPopupCallbackInterface& callbackInterface )
208 : mController( controller ),
209 mEnabledPopupButtons( TextSelectionPopup::NONE ),
210 mTextSelectionPopupCallbackInterface( callbackInterface ),
211 mHandleColor( HANDLE_COLOR ),
213 mHighlightColor( LIGHT_BLUE ),
214 mHighlightPosition( Vector2::ZERO ),
215 mHighlightSize( Vector2::ZERO ),
216 mControlSize( Vector2::ZERO ),
217 mHighlightOutlineOffset( 0.f ),
218 mActiveCursor( ACTIVE_CURSOR_NONE ),
219 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
220 mCursorBlinkDuration( 0.0f ),
221 mCursorWidth( CURSOR_WIDTH ),
222 mHandleScrolling( HANDLE_TYPE_COUNT ),
223 mHandleReleased( HANDLE_TYPE_COUNT ),
224 mScrollDirection( SCROLL_NONE ),
225 mScrollThreshold( SCROLL_THRESHOLD ),
226 mScrollSpeed( SCROLL_SPEED ),
227 mScrollDistance( SCROLL_DISTANCE ),
229 mActiveCopyPastePopup( false ),
230 mPopupSetNewPosition( true ),
231 mCursorBlinkStatus( true ),
232 mDelayCursorBlink( false ),
233 mPrimaryCursorVisible( false ),
234 mSecondaryCursorVisible( false ),
235 mFlipSelectionHandlesOnCross( false ),
236 mFlipLeftSelectionHandleDirection( false ),
237 mFlipRightSelectionHandleDirection( false ),
238 mIsHandlePanning( false ),
239 mIsHandleCurrentlyCrossed( false ),
240 mIsHandlePreviouslyCrossed( false ),
241 mNotifyEndOfScroll( false ),
242 mHorizontalScrollingEnabled( false ),
243 mVerticalScrollingEnabled( false ),
244 mSmoothHandlePanEnabled( false ),
245 mIsHighlightBoxActive( false ),
246 mHidePrimaryCursorAndGrabHandle( false )
248 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
249 mHighlightShader = Shader::New( SHADER_TEXT_DECORATOR_SHADER_VERT, SHADER_TEXT_DECORATOR_SHADER_FRAG );
254 * Relayout of the decorations owned by the decorator.
255 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
257 void Relayout( const Vector2& size )
261 // TODO - Remove this if nothing is active
264 // Show or hide the cursors
269 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
270 mPrimaryCursorVisible = (!mHidePrimaryCursorAndGrabHandle) && ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
271 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
272 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
273 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
274 if( mPrimaryCursorVisible )
276 mPrimaryCursor.SetProperty( Actor::Property::POSITION, Vector2( cursor.position.x,
277 cursor.position.y ) );
278 mPrimaryCursor.SetProperty( Actor::Property::SIZE, Size( mCursorWidth, cursor.cursorHeight ) );
280 mPrimaryCursor.SetProperty( Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus );
282 if( mSecondaryCursor )
284 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
285 mSecondaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
286 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
287 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
288 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
289 if( mSecondaryCursorVisible )
291 mSecondaryCursor.SetProperty( Actor::Property::POSITION, Vector2( cursor.position.x,
292 cursor.position.y ) );
293 mSecondaryCursor.SetProperty( Actor::Property::SIZE, Size( mCursorWidth, cursor.cursorHeight ) );
295 mSecondaryCursor.SetProperty( Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus );
298 // Show or hide the grab handle
299 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
300 bool newGrabHandlePosition = false;
301 grabHandle.horizontallyVisible = false;
302 grabHandle.verticallyVisible = false;
303 if( grabHandle.active )
305 grabHandle.horizontallyVisible = ( ( mControlSize.width - ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) ) > -Math::MACHINE_EPSILON_1000 ) &&
306 ( grabHandle.position.x > -Math::MACHINE_EPSILON_1000 ) );
307 grabHandle.verticallyVisible = ( ( ( mControlSize.height - grabHandle.lineHeight ) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) &&
308 ( grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) );
310 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible && (!mHidePrimaryCursorAndGrabHandle);
315 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
316 SetGrabHandlePosition();
318 // Sets the grab handle image according if it's pressed, flipped, etc.
319 SetHandleImage( GRAB_HANDLE );
321 newGrabHandlePosition = true;
324 if( grabHandle.actor )
326 grabHandle.actor.SetProperty( Actor::Property::VISIBLE, isVisible );
329 else if( grabHandle.actor )
331 grabHandle.actor.Unparent();
334 // Show or hide the selection handles/highlight
335 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
336 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
337 bool newPrimaryHandlePosition = false;
338 bool newSecondaryHandlePosition = false;
340 primary.horizontallyVisible = ( ( mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
341 ( primary.position.x > -Math::MACHINE_EPSILON_1000 ) );
342 primary.verticallyVisible = ( ( ( mControlSize.height - primary.lineHeight ) - primary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
343 ( primary.position.y + ( primary.verticallyFlipped ? 0.f : primary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
344 secondary.horizontallyVisible = ( ( mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
345 ( secondary.position.x > -Math::MACHINE_EPSILON_1000 ) );
346 secondary.verticallyVisible = ( ( ( mControlSize.height - secondary.lineHeight ) - secondary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
347 ( secondary.position.y + ( secondary.verticallyFlipped ? 0.f : secondary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
349 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
350 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
352 if( primary.active || secondary.active )
354 if( primaryVisible || secondaryVisible )
356 CreateSelectionHandles();
360 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
362 // Sets the primary handle image according if it's pressed, flipped, etc.
363 SetHandleImage( LEFT_SELECTION_HANDLE );
365 SetSelectionHandleMarkerSize( primary );
367 newPrimaryHandlePosition = true;
370 if( secondaryVisible )
372 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
374 // Sets the secondary handle image according if it's pressed, flipped, etc.
375 SetHandleImage( RIGHT_SELECTION_HANDLE );
377 SetSelectionHandleMarkerSize( secondary );
379 newSecondaryHandlePosition = true;
385 primary.actor.SetProperty( Actor::Property::VISIBLE, primaryVisible );
387 if( secondary.actor )
389 secondary.actor.SetProperty( Actor::Property::VISIBLE, secondaryVisible );
397 primary.actor.Unparent();
399 if( secondary.actor )
401 secondary.actor.Unparent();
405 if( mIsHighlightBoxActive )
412 if( mHighlightActor )
414 mHighlightActor.Unparent();
418 if( newGrabHandlePosition ||
419 newPrimaryHandlePosition ||
420 newSecondaryHandlePosition )
422 // Setup property notifications to find whether the handles leave the boundaries of the current display.
423 SetupActiveLayerPropertyNotifications();
426 if( mActiveCopyPastePopup &&
427 ( primaryVisible || secondaryVisible ) )
430 mPopupSetNewPosition = true;
434 if( mCopyPastePopup.actor )
436 mCopyPastePopup.actor.HidePopup();
437 mPopupSetNewPosition = true;
442 void UpdatePositions( const Vector2& scrollOffset )
444 mCursor[PRIMARY_CURSOR].position += scrollOffset;
445 mCursor[SECONDARY_CURSOR].position += scrollOffset;
446 mHandle[ GRAB_HANDLE ].position += scrollOffset;
447 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
448 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
449 mHighlightPosition += scrollOffset;
454 if( !mCopyPastePopup.actor )
459 if( !mCopyPastePopup.actor.GetParent() )
461 mActiveLayer.Add( mCopyPastePopup.actor );
464 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
465 mCopyPastePopup.actor.ShowPopup();
468 float CalculateVerticalPopUpPosition( float halfHeight, bool preferBelow )
470 float yPosition = 0.f;
472 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
473 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
474 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
476 if( primaryHandle.active || secondaryHandle.active )
478 // The origin of the decorator's coordinate system in world coords.
479 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION ) - mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ) * ACTIVE_LAYER_ANCHOR_POINT;
483 // Find out if there is enough space for the popup at the bottom.
484 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
485 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
487 float maxY = std::max( primaryBelowY, secondaryBelowY );
489 yPosition = halfHeight + maxY;
491 if( originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w )
493 // Does not fit below.
495 // Try to fit first below the non active handle. Otherwise above the active handle.
496 if( RIGHT_SELECTION_HANDLE == mHandleReleased )
498 if( primaryBelowY < secondaryBelowY )
500 yPosition = halfHeight + primaryBelowY;
504 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
507 else if( LEFT_SELECTION_HANDLE == mHandleReleased )
509 if( secondaryBelowY < primaryBelowY )
511 yPosition = halfHeight + secondaryBelowY;
515 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
519 // Check the handle is whithin the decoration box.
520 if( originWorldCoords.y + yPosition < mBoundingBox.y )
522 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
525 if( originWorldCoords.y + yPosition > mBoundingBox.w )
527 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
533 // Find out if there is enough space for the popup at the top.
534 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
535 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
537 float minY = std::min( primaryTopY, secondaryTopY );
539 yPosition = -halfHeight + minY;
541 } // ( primaryHandle.active || secondaryHandle.active )
542 else if( grabHandle.active )
546 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
550 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
557 void ConstrainPopupPosition( const Vector3& popupHalfSize )
559 // Check if the popup is within the boundaries of the decoration box.
561 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
563 // The origin of the decorator's coordinate system in world coords.
564 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION ) - mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ) * ACTIVE_LAYER_ANCHOR_POINT;
566 // The popup's position in world coords.
567 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
569 if( popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x )
571 mCopyPastePopup.position.x += mBoundingBox.x - ( popupPositionWorldCoords.x - popupHalfSize.width );
573 else if( popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z )
575 mCopyPastePopup.position.x += mBoundingBox.z - ( popupPositionWorldCoords.x + popupHalfSize.width );
578 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
579 if( popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y )
581 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition( popupHalfSize.height, true ); // true -> prefer to set the popup's position below.
585 void SetPopupPosition( Actor actor )
587 if( !mActiveCopyPastePopup )
592 // Retrieves the popup's size after relayout.
593 const Vector3 popupSize( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
594 const Vector3 popupHalfSize = popupSize * 0.5f;
596 if( mPopupSetNewPosition )
598 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
599 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
600 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
602 if( primaryHandle.active || secondaryHandle.active )
604 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
605 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
607 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
609 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - ( primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING );
610 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - ( secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING );
612 mCopyPastePopup.position.y = std::min( primaryY, secondaryY );
614 else if( grabHandle.active )
616 mCopyPastePopup.position.x = grabHandle.position.x;
618 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - ( grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING );
620 } // mPopupSetNewPosition
622 // It may change the popup's position to fit within the decoration box.
623 ConstrainPopupPosition( popupHalfSize );
625 SetUpPopupPositionNotifications( popupHalfSize );
627 // Prevent pixel mis-alignment by rounding down.
628 mCopyPastePopup.position.x = floorf( mCopyPastePopup.position.x );
629 mCopyPastePopup.position.y = floorf( mCopyPastePopup.position.y );
631 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION, mCopyPastePopup.position );
632 mPopupSetNewPosition = false;
635 void CreateCursor( Control& cursor, const Vector4& color )
637 cursor = Control::New();
638 cursor.SetBackgroundColor( color );
639 cursor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
640 cursor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
643 // Add or Remove cursor(s) from parent
646 if( mActiveCursor == ACTIVE_CURSOR_NONE )
650 mPrimaryCursor.Unparent();
652 if( mSecondaryCursor )
654 mSecondaryCursor.Unparent();
659 // Create Primary and or Secondary Cursor(s) if active and add to parent
660 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
661 mActiveCursor == ACTIVE_CURSOR_BOTH )
663 if ( !mPrimaryCursor )
665 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
666 #ifdef DECORATOR_DEBUG
667 mPrimaryCursor.SetProperty( Dali::Actor::Property::NAME, "PrimaryCursorActor" );
671 if( !mPrimaryCursor.GetParent() )
673 mActiveLayer.Add( mPrimaryCursor );
677 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
679 if ( !mSecondaryCursor )
681 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
682 #ifdef DECORATOR_DEBUG
683 mSecondaryCursor.SetProperty( Dali::Actor::Property::NAME, "SecondaryCursorActor" );
687 if( !mSecondaryCursor.GetParent() )
689 mActiveLayer.Add( mSecondaryCursor );
694 if( mSecondaryCursor )
696 mSecondaryCursor.Unparent();
702 bool OnCursorBlinkTimerTick()
704 if( !mDelayCursorBlink )
707 if ( mPrimaryCursor )
709 mPrimaryCursor.SetProperty( Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus );
711 if ( mSecondaryCursor )
713 mSecondaryCursor.SetProperty( Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus );
716 mCursorBlinkStatus = !mCursorBlinkStatus;
721 mDelayCursorBlink = false;
729 // Will consume tap gestures on handles.
730 mTapDetector = TapGestureDetector::New();
732 // Will consume double tap gestures on handles.
733 mTapDetector.SetMaximumTapsRequired( 2u );
735 // Will consume long press gestures on handles.
736 mLongPressDetector = LongPressGestureDetector::New();
738 // Detects pan gestures on handles.
739 mPanDetector = PanGestureDetector::New();
740 mPanDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
743 void CreateActiveLayer()
747 mActiveLayer = Actor::New();
748 #ifdef DECORATOR_DEBUG
749 mActiveLayer.SetProperty( Actor::Property::NAME, "ActiveLayerActor" );
752 mActiveLayer.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
753 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
755 // Add the active layer telling the controller it doesn't need clipping.
756 mController.AddDecoration( mActiveLayer, false );
759 mActiveLayer.RaiseToTop();
762 void SetSelectionHandleMarkerSize( HandleImpl& handle )
764 if( handle.markerActor )
766 handle.markerActor.SetProperty( Actor::Property::SIZE, Vector2( 0, handle.lineHeight ) );
770 void CreateGrabHandle()
772 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
773 if( !grabHandle.actor )
775 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED].size() )
777 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
778 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
779 grabHandle.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
781 // Area that Grab handle responds to, larger than actual handle so easier to move
782 #ifdef DECORATOR_DEBUG
783 grabHandle.actor.SetProperty( Dali::Actor::Property::NAME, "GrabHandleActor" );
784 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
786 grabHandle.grabArea = Control::New();
787 Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
788 control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
789 grabHandle.grabArea.SetProperty( Dali::Actor::Property::NAME, "GrabArea" );
793 grabHandle.grabArea = Actor::New();
794 grabHandle.grabArea.SetProperty( Dali::Actor::Property::NAME, "GrabArea" );
797 grabHandle.grabArea = Actor::New();
800 grabHandle.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
801 grabHandle.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
802 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
803 grabHandle.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
804 grabHandle.actor.Add( grabHandle.grabArea );
805 grabHandle.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
807 grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
809 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
810 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
811 mTapDetector.Attach( grabHandle.actor );
812 mLongPressDetector.Attach( grabHandle.actor );
814 // The grab handle's area is attached to the pan detector.
815 // The OnPan() method is connected to the signals emitted by the pan detector.
816 mPanDetector.Attach( grabHandle.grabArea );
818 mActiveLayer.Add( grabHandle.actor );
822 if( grabHandle.actor && !grabHandle.actor.GetParent() )
824 mActiveLayer.Add( grabHandle.actor );
828 void CreateHandleMarker( HandleImpl& handle, const std::string& image, HandleType handleType )
832 handle.markerActor = ImageView::New( image );
833 handle.markerActor.SetProperty( Actor::Property::COLOR, mHandleColor );
834 handle.actor.Add( handle.markerActor );
836 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
838 if( LEFT_SELECTION_HANDLE == handleType )
840 handle.markerActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT );
841 handle.markerActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_RIGHT );
843 else if( RIGHT_SELECTION_HANDLE == handleType )
845 handle.markerActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT );
846 handle.markerActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
851 void CreateSelectionHandles()
853 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
856 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size() )
858 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
859 #ifdef DECORATOR_DEBUG
860 primary.actor.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleOne");
862 primary.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
863 GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
864 primary.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
866 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
867 #ifdef DECORATOR_DEBUG
868 primary.grabArea.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleOneGrabArea");
870 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
871 primary.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
872 primary.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
873 primary.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
875 primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
877 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
878 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
879 mTapDetector.Attach( primary.actor );
880 mLongPressDetector.Attach( primary.actor );
882 // The handle's area is attached to the pan detector.
883 // The OnPan() method is connected to the signals emitted by the pan detector.
884 mPanDetector.Attach( primary.grabArea );
886 primary.actor.Add( primary.grabArea );
888 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
892 if( primary.actor && !primary.actor.GetParent() )
894 mActiveLayer.Add( primary.actor );
897 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
898 if( !secondary.actor )
900 if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size() )
902 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
903 #ifdef DECORATOR_DEBUG
904 secondary.actor.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleTwo");
906 secondary.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
907 GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
908 secondary.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
910 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
911 #ifdef DECORATOR_DEBUG
912 secondary.grabArea.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleTwoGrabArea");
914 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
915 secondary.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
916 secondary.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
917 secondary.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
919 secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
921 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
922 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
923 mTapDetector.Attach( secondary.actor );
924 mLongPressDetector.Attach( secondary.actor );
926 // The handle's area is attached to the pan detector.
927 // The OnPan() method is connected to the signals emitted by the pan detector.
928 mPanDetector.Attach( secondary.grabArea );
930 secondary.actor.Add( secondary.grabArea );
932 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
936 if( secondary.actor && !secondary.actor.GetParent() )
938 mActiveLayer.Add( secondary.actor );
942 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
944 // Gets the world position of the active layer. The active layer is where the handles are added.
945 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION );
947 // The grab handle position in world coords.
948 // The active layer's world position is the center of the active layer. The origin of the
949 // coord system of the handles is the top left of the active layer.
950 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f );
951 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f );
954 void SetGrabHandlePosition()
956 // Reference to the grab handle.
957 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
959 // Transforms the handle position into world coordinates.
960 // @note This is not the same value as grabHandle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
961 // as it's transforming the handle's position set by the text-controller and not
962 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
963 // retrieves the position of the center of the actor but the handle's position set
964 // by the text controller is not the center of the actor.
965 Vector2 grabHandleWorldPosition;
966 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
968 // Check if the grab handle exceeds the boundaries of the decoration box.
969 // At the moment only the height is checked for the grab handle.
971 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
972 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
973 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
975 // The grab handle 'y' position in local coords.
976 // If the grab handle exceeds the bottom of the decoration box,
977 // set the 'y' position to the top of the line.
978 // The SetGrabHandleImage() method will change the orientation.
979 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
981 if( grabHandle.actor )
983 grabHandle.actor.SetProperty( Actor::Property::POSITION, Vector2( grabHandle.position.x + floor( 0.5f * mCursorWidth ) + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f ),
984 yLocalPosition + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f ) ) );
988 void SetSelectionHandlePosition( HandleType type )
990 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
992 // Reference to the selection handle.
993 HandleImpl& handle = mHandle[type];
995 // Transforms the handle position into world coordinates.
996 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
997 // as it's transforming the handle's position set by the text-controller and not
998 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
999 // retrieves the position of the center of the actor but the handle's position set
1000 // by the text controller is not the center of the actor.
1001 Vector2 handleWorldPosition;
1002 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
1004 // Whether to flip the handle (horizontally).
1005 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1007 // Whether to flip the handles if they are crossed.
1008 bool crossFlip = false;
1009 if( mFlipSelectionHandlesOnCross || !mIsHandlePanning )
1011 crossFlip = mIsHandleCurrentlyCrossed;
1014 // Whether the handle was crossed before start the panning.
1015 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1017 // Does not flip if both conditions are true (double flip)
1018 flipHandle = flipHandle != ( crossFlip || isHandlePreviouslyCrossed );
1020 // Will flip the handles vertically if the user prefers it.
1021 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1023 if( crossFlip || isHandlePreviouslyCrossed )
1025 if( isPrimaryHandle )
1027 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1031 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1035 // Check if the selection handle exceeds the boundaries of the decoration box.
1036 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
1037 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
1039 // Does not flip if both conditions are true (double flip)
1040 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
1044 if( handle.actor && !handle.horizontallyFlipped )
1046 // Change the anchor point to flip the image.
1047 handle.actor.SetProperty( Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
1049 handle.horizontallyFlipped = true;
1054 if( handle.actor && handle.horizontallyFlipped )
1056 // Reset the anchor point.
1057 handle.actor.SetProperty( Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
1059 handle.horizontallyFlipped = false;
1063 // Whether to flip the handle vertically.
1064 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
1065 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
1066 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
1068 // The primary selection handle 'y' position in local coords.
1069 // If the handle exceeds the bottom of the decoration box,
1070 // set the 'y' position to the top of the line.
1071 // The SetHandleImage() method will change the orientation.
1072 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1076 handle.actor.SetProperty( Actor::Property::POSITION, Vector2( handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f ),
1077 yLocalPosition + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f ) ) );
1081 void SetHandleImage( HandleType type )
1083 HandleImpl& handle = mHandle[type];
1085 HandleType markerType = HANDLE_TYPE_COUNT;
1086 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1087 if( LEFT_SELECTION_HANDLE == type )
1089 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1090 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1092 else if( RIGHT_SELECTION_HANDLE == type )
1094 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1095 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1098 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1101 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1103 handle.actor.SetImage( mHandleImages[type][imageType] );
1106 if( HANDLE_TYPE_COUNT != markerType )
1108 if( handle.markerActor )
1110 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1111 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
1115 // Whether to flip the handle vertically.
1118 handle.actor.SetProperty( Actor::Property::ORIENTATION, Quaternion( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS ) );
1122 void CreateHighlight()
1124 if( !mHighlightActor )
1126 mHighlightActor = Actor::New();
1128 mHighlightActor.SetProperty( Dali::Actor::Property::NAME, "HighlightActor" );
1129 mHighlightActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
1130 mHighlightActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
1131 mHighlightActor.SetProperty( Actor::Property::COLOR, mHighlightColor );
1132 mHighlightActor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_COLOR );
1135 // Add the highlight box telling the controller it needs clipping.
1136 mController.AddDecoration( mHighlightActor, true );
1139 void UpdateHighlight()
1141 if ( mHighlightActor )
1143 // Sets the position of the highlight actor inside the decorator.
1144 mHighlightActor.SetProperty( Actor::Property::POSITION, Vector2( mHighlightPosition.x + mHighlightOutlineOffset,
1145 mHighlightPosition.y + mHighlightOutlineOffset ) );
1147 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1148 if( 0u != numberOfQuads )
1150 // Set the size of the highlighted text to the actor.
1151 mHighlightActor.SetProperty( Actor::Property::SIZE, mHighlightSize );
1153 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1154 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1155 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1157 Vector<Vector2> vertices;
1158 Vector<unsigned short> indices;
1160 vertices.Reserve( 4u * numberOfQuads );
1161 indices.Reserve( 6u * numberOfQuads );
1163 // Index to the vertex.
1164 unsigned int v = 0u;
1166 // Traverse all quads.
1167 for( Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1168 endIt = mHighlightQuadList.End();
1172 const Vector4& quad = *it;
1177 vertex.x = quad.x - offsetX;
1178 vertex.y = quad.y - offsetY;
1179 vertices.PushBack( vertex );
1182 vertex.x = quad.z - offsetX;
1183 vertex.y = quad.y - offsetY;
1184 vertices.PushBack( vertex );
1186 // bottom-left (v+2)
1187 vertex.x = quad.x - offsetX;
1188 vertex.y = quad.w - offsetY;
1189 vertices.PushBack( vertex );
1191 // bottom-right (v+3)
1192 vertex.x = quad.z - offsetX;
1193 vertex.y = quad.w - offsetY;
1194 vertices.PushBack( vertex );
1196 // triangle A (3, 1, 0)
1197 indices.PushBack( v + 3 );
1198 indices.PushBack( v + 1 );
1199 indices.PushBack( v );
1201 // triangle B (0, 2, 3)
1202 indices.PushBack( v );
1203 indices.PushBack( v + 2 );
1204 indices.PushBack( v + 3 );
1207 if( ! mQuadVertices )
1209 mQuadVertices = VertexBuffer::New( mQuadVertexFormat );
1212 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1214 if( !mQuadGeometry )
1216 mQuadGeometry = Geometry::New();
1217 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1219 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1221 if( !mHighlightRenderer )
1223 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1224 mHighlightActor.AddRenderer( mHighlightRenderer );
1228 mHighlightQuadList.Clear();
1230 if( mHighlightRenderer )
1232 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1237 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1239 GestureState state = gesture.GetState();
1240 if( GestureState::STARTED == state )
1242 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1244 handle.globalPosition.x = handle.position.x;
1245 handle.globalPosition.y = handle.position.y;
1248 const Vector2& displacement = gesture.GetDisplacement();
1249 handle.grabDisplacementX += displacement.x;
1250 handle.grabDisplacementY += ( handle.verticallyFlipped ? -displacement.y : displacement.y );
1252 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1253 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1254 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f );
1256 if( ( GestureState::STARTED == state ) ||
1257 ( GestureState::CONTINUING == state ) )
1260 mController.GetTargetSize( targetSize );
1262 if( mHorizontalScrollingEnabled &&
1263 ( x < mScrollThreshold ) )
1265 mScrollDirection = SCROLL_RIGHT;
1266 mHandleScrolling = type;
1269 else if( mHorizontalScrollingEnabled &&
1270 ( x > targetSize.width - mScrollThreshold ) )
1272 mScrollDirection = SCROLL_LEFT;
1273 mHandleScrolling = type;
1276 else if( mVerticalScrollingEnabled &&
1277 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1279 mScrollDirection = SCROLL_TOP;
1280 mHandleScrolling = type;
1283 else if( mVerticalScrollingEnabled &&
1284 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1286 mScrollDirection = SCROLL_BOTTOM;
1287 mHandleScrolling = type;
1292 mHandleScrolling = HANDLE_TYPE_COUNT;
1294 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1297 mIsHandlePanning = true;
1299 else if( ( GestureState::FINISHED == state ) ||
1300 ( GestureState::CANCELLED == state ) )
1303 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1305 mNotifyEndOfScroll = false;
1306 mHandleScrolling = HANDLE_TYPE_COUNT;
1308 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1312 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1317 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1319 handle.pressed = false;
1321 mIsHandlePanning = false;
1325 void OnPan( Actor actor, const PanGesture& gesture )
1327 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1328 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1329 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1331 if( actor == grabHandle.grabArea )
1333 DoPan( grabHandle, GRAB_HANDLE, gesture );
1335 else if( actor == primarySelectionHandle.grabArea )
1337 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1339 else if( actor == secondarySelectionHandle.grabArea )
1341 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1345 bool OnGrabHandleTouched( Actor actor, const TouchEvent& touch )
1347 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1349 // Switch between pressed/release grab-handle images
1350 if( touch.GetPointCount() > 0 &&
1353 const PointState::Type state = touch.GetState( 0 );
1355 if( PointState::DOWN == state )
1357 grabHandle.pressed = true;
1359 else if( ( PointState::UP == state ) ||
1360 ( PointState::INTERRUPTED == state ) )
1362 grabHandle.pressed = false;
1365 SetHandleImage( GRAB_HANDLE );
1371 bool OnHandleOneTouched( Actor actor, const TouchEvent& touch )
1373 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1375 // Switch between pressed/release selection handle images
1376 if( touch.GetPointCount() > 0 &&
1377 primarySelectionHandle.actor )
1379 const PointState::Type state = touch.GetState( 0 );
1381 if( PointState::DOWN == state )
1383 primarySelectionHandle.pressed = true;
1384 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1386 else if( ( PointState::UP == state ) ||
1387 ( PointState::INTERRUPTED == state ) )
1389 primarySelectionHandle.pressed = false;
1390 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1391 mIsHandlePanning = false;
1392 mHandleReleased = LEFT_SELECTION_HANDLE;
1395 SetHandleImage( LEFT_SELECTION_HANDLE );
1401 bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch )
1403 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1405 // Switch between pressed/release selection handle images
1406 if( touch.GetPointCount() > 0 &&
1407 secondarySelectionHandle.actor )
1409 const PointState::Type state = touch.GetState( 0 );
1411 if( PointState::DOWN == state )
1413 secondarySelectionHandle.pressed = true;
1414 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1416 else if( ( PointState::UP == state ) ||
1417 ( PointState::INTERRUPTED == state ) )
1419 secondarySelectionHandle.pressed = false;
1420 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1421 mIsHandlePanning = false;
1422 mHandleReleased = RIGHT_SELECTION_HANDLE;
1425 SetHandleImage( RIGHT_SELECTION_HANDLE );
1431 void HandleResetPosition( PropertyNotification& source )
1433 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1435 if( grabHandle.active )
1437 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1438 SetGrabHandlePosition();
1440 // Sets the grab handle image according if it's pressed, flipped, etc.
1441 SetHandleImage( GRAB_HANDLE );
1445 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1446 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1448 // Sets the primary handle image according if it's pressed, flipped, etc.
1449 SetHandleImage( LEFT_SELECTION_HANDLE );
1451 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1452 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1454 // Sets the secondary handle image according if it's pressed, flipped, etc.
1455 SetHandleImage( RIGHT_SELECTION_HANDLE );
1459 void SetupActiveLayerPropertyNotifications()
1466 // Vertical notifications.
1468 // Disconnect any previous connected callback.
1469 if( mHandleVerticalLessThanNotification )
1471 mHandleVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1472 mActiveLayer.RemovePropertyNotification( mHandleVerticalLessThanNotification );
1475 if( mHandleVerticalGreaterThanNotification )
1477 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1478 mActiveLayer.RemovePropertyNotification( mHandleVerticalGreaterThanNotification );
1481 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1482 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1483 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1485 if( grabHandle.active )
1487 if( grabHandle.verticallyFlipped )
1489 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1490 mHandleVerticalGreaterThanNotification.Reset();
1492 // The vertical distance from the center of the active layer to the top edje of the display.
1493 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1495 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1496 LessThanCondition( mBoundingBox.y + topHeight ) );
1498 // Notifies the change from false to true and from true to false.
1499 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1501 // Connects the signals with the callbacks.
1502 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1506 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1507 mHandleVerticalLessThanNotification.Reset();
1509 // The vertical distance from the center of the active layer to the bottom edje of the display.
1510 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1512 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1513 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1515 // Notifies the change from false to true and from true to false.
1516 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1518 // Connects the signals with the callbacks.
1519 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1522 else // The selection handles are active
1524 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1526 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1527 mHandleVerticalGreaterThanNotification.Reset();
1529 // The vertical distance from the center of the active layer to the top edje of the display.
1530 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1532 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1533 LessThanCondition( mBoundingBox.y + topHeight ) );
1535 // Notifies the change from false to true and from true to false.
1536 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1538 // Connects the signals with the callbacks.
1539 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1541 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1543 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1544 mHandleVerticalLessThanNotification.Reset();
1546 // The vertical distance from the center of the active layer to the bottom edje of the display.
1547 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1548 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1550 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1551 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1553 // Notifies the change from false to true and from true to false.
1554 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1556 // Connects the signals with the callbacks.
1557 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1561 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1563 // The vertical distance from the center of the active layer to the top edje of the display.
1564 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1565 -primaryHandle.position.y + primaryHandle.size.height :
1566 -secondaryHandle.position.y + secondaryHandle.size.height );
1568 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1569 LessThanCondition( mBoundingBox.y + topHeight ) );
1571 // Notifies the change from false to true and from true to false.
1572 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1574 // Connects the signals with the callbacks.
1575 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1577 // The vertical distance from the center of the active layer to the bottom edje of the display.
1578 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1579 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1580 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1582 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1583 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1585 // Notifies the change from false to true and from true to false.
1586 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1588 // Connects the signals with the callbacks.
1589 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1593 // Horizontal notifications.
1595 // Disconnect any previous connected callback.
1596 if( mHandleHorizontalLessThanNotification )
1598 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1599 mActiveLayer.RemovePropertyNotification( mHandleHorizontalLessThanNotification );
1602 if( mHandleHorizontalGreaterThanNotification )
1604 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1605 mActiveLayer.RemovePropertyNotification( mHandleHorizontalGreaterThanNotification );
1608 if( primaryHandle.active || secondaryHandle.active )
1610 // The horizontal distance from the center of the active layer to the left edje of the display.
1611 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1612 -secondaryHandle.position.x + secondaryHandle.size.width );
1614 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1615 LessThanCondition( mBoundingBox.x + leftWidth ) );
1617 // Notifies the change from false to true and from true to false.
1618 mHandleHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1620 // Connects the signals with the callbacks.
1621 mHandleHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1623 // The horizontal distance from the center of the active layer to the right edje of the display.
1624 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1625 secondaryHandle.position.x + secondaryHandle.size.width );
1627 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1628 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1630 // Notifies the change from false to true and from true to false.
1631 mHandleHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1633 // Connects the signals with the callbacks.
1634 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1640 float AlternatePopUpPositionRelativeToCursor( bool topBottom )
1642 float alternativePosition = 0.0f;
1644 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1646 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1647 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1648 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1649 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1651 if( primaryHandle.active || secondaryHandle.active )
1653 float handleY = 0.f;
1654 float maxHandleHeight = 0.f;
1656 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1657 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1659 if( primaryVisible && secondaryVisible )
1661 handleY = std::max( primaryHandle.position.y, secondaryHandle.position.y );
1662 maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1664 else if( primaryVisible && !secondaryVisible )
1666 handleY = primaryHandle.position.y;
1667 maxHandleHeight = primaryHandle.size.height;
1669 else if( !primaryVisible && secondaryVisible )
1671 handleY = secondaryHandle.position.y;
1672 maxHandleHeight = secondaryHandle.size.height;
1675 alternativePosition = handleY + ( topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight );
1679 alternativePosition = cursor.position.y + ( topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height );
1682 return alternativePosition;
1685 void PopUpLeavesTopBoundary( PropertyNotification& source )
1687 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1689 // Sets the position of the popup below.
1690 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION_Y, floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, true ) ) );
1693 void PopUpLeavesBottomBoundary( PropertyNotification& source )
1695 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1697 // Sets the position of the popup above.
1698 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION_Y, floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, false ) ) );
1701 void SetUpPopupPositionNotifications( const Vector3& popupHalfSize )
1703 // Disconnect any previous connected callback.
1704 if( mPopupTopExceedNotification )
1706 mPopupTopExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1707 mCopyPastePopup.actor.RemovePropertyNotification( mPopupTopExceedNotification );
1710 if( mPopupBottomExceedNotification )
1712 mPopupBottomExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1713 mCopyPastePopup.actor.RemovePropertyNotification( mPopupBottomExceedNotification );
1716 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1718 // Exceeding vertical boundary
1720 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1721 LessThanCondition( mBoundingBox.y + popupHalfSize.height ) );
1723 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1724 GreaterThanCondition( mBoundingBox.w - popupHalfSize.height ) );
1726 // Notifies the change from false to true and from true to false.
1727 mPopupTopExceedNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1728 mPopupBottomExceedNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1730 mPopupTopExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1731 mPopupBottomExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1734 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName )
1736 ImageDimensions dimensions = Dali::GetOriginalImageSize( imageFileName );
1738 HandleImpl& handle = mHandle[handleType];
1739 handle.size = Size( dimensions.GetWidth(), dimensions.GetHeight() );
1741 mHandleImages[handleType][handleImageType] = imageFileName;
1744 void SetScrollThreshold( float threshold )
1746 mScrollThreshold = threshold;
1749 float GetScrollThreshold() const
1751 return mScrollThreshold;
1754 void SetScrollSpeed( float speed )
1756 mScrollSpeed = speed;
1757 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1760 float GetScrollSpeed() const
1762 return mScrollSpeed;
1765 void NotifyEndOfScroll()
1771 mNotifyEndOfScroll = true;
1776 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1778 * It only starts the timer if it's already created.
1780 void StartScrollTimer()
1784 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1785 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1788 if( !mScrollTimer.IsRunning() )
1790 mScrollTimer.Start();
1795 * Stops the timer used to scroll the text.
1797 void StopScrollTimer()
1801 mScrollTimer.Stop();
1806 * Callback called by the timer used to scroll the text.
1808 * It calculates and sets a new scroll position.
1810 bool OnScrollTimerTick()
1812 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1817 switch( mScrollDirection )
1821 x = mScrollDistance;
1826 x = -mScrollDistance;
1831 y = mScrollDistance;
1836 y = -mScrollDistance;
1843 mController.DecorationEvent( mHandleScrolling,
1852 ControllerInterface& mController;
1854 TapGestureDetector mTapDetector;
1855 PanGestureDetector mPanDetector;
1856 LongPressGestureDetector mLongPressDetector;
1858 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1859 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1861 Actor mActiveLayer; ///< Actor for active handles and alike that ensures they are above all else.
1862 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1863 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1864 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1865 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1866 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1867 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1868 Control mPrimaryCursor;
1869 Control mSecondaryCursor;
1871 Actor mHighlightActor; ///< Actor to display highlight
1872 Renderer mHighlightRenderer;
1873 Shader mHighlightShader; ///< Shader used for highlight
1874 Property::Map mQuadVertexFormat;
1875 PopupImpl mCopyPastePopup;
1876 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1877 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1879 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1880 Vector4 mHandleColor;
1882 CursorImpl mCursor[CURSOR_COUNT];
1883 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1885 VertexBuffer mQuadVertices;
1886 Geometry mQuadGeometry;
1887 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1889 Vector4 mBoundingBox; ///< The bounding box in world coords.
1890 Vector4 mHighlightColor; ///< Color of the highlight
1891 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1892 Size mHighlightSize; ///< The size of the highlighted text.
1893 Size mControlSize; ///< The control's size. Set by the Relayout.
1894 float mHighlightOutlineOffset; ///< The outline's offset.
1896 unsigned int mActiveCursor;
1897 unsigned int mCursorBlinkInterval;
1898 float mCursorBlinkDuration;
1899 float mCursorWidth; ///< The width of the cursors in pixels.
1900 HandleType mHandleScrolling; ///< The handle which is scrolling.
1901 HandleType mHandleReleased; ///< The last handle released.
1902 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1903 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1904 float mScrollSpeed; ///< The scroll speed in pixels per second.
1905 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1906 int mTextDepth; ///< The depth used to render the text.
1908 bool mActiveCopyPastePopup : 1;
1909 bool mPopupSetNewPosition : 1;
1910 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1911 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1912 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1913 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1914 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1915 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1916 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1917 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1918 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1919 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1920 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1921 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1922 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1923 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1924 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1925 bool mHidePrimaryCursorAndGrabHandle : 1; ///< Whether the primary cursor and grab are hidden always.
1928 DecoratorPtr Decorator::New( ControllerInterface& controller,
1929 TextSelectionPopupCallbackInterface& callbackInterface )
1931 return DecoratorPtr( new Decorator( controller,
1932 callbackInterface ) );
1935 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1937 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1940 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1942 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1945 void Decorator::Relayout( const Vector2& size )
1947 mImpl->Relayout( size );
1950 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1952 mImpl->UpdatePositions( scrollOffset );
1957 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1959 mImpl->mActiveCursor = activeCursor;
1962 unsigned int Decorator::GetActiveCursor() const
1964 return mImpl->mActiveCursor;
1967 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1969 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1971 cursorImpl.position.x = x;
1972 cursorImpl.position.y = y;
1973 cursorImpl.cursorHeight = cursorHeight;
1974 cursorImpl.lineHeight = lineHeight;
1977 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1979 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1981 x = cursorImpl.position.x;
1982 y = cursorImpl.position.y;
1983 cursorHeight = cursorImpl.cursorHeight;
1984 lineHeight = cursorImpl.lineHeight;
1987 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1989 return mImpl->mCursor[cursor].position;
1992 void Decorator::SetGlyphOffset( Cursor cursor, float glyphOffset )
1994 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1996 cursorImpl.glyphOffset = glyphOffset;
1999 const float Decorator::GetGlyphOffset( Cursor cursor) const
2001 return mImpl->mCursor[cursor].glyphOffset;
2004 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
2006 mImpl->mCursor[cursor].color = color;
2009 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
2011 return mImpl->mCursor[cursor].color;
2014 void Decorator::StartCursorBlink()
2016 if ( !mImpl->mCursorBlinkTimer )
2018 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
2019 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
2022 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
2024 mImpl->mCursorBlinkTimer.Start();
2028 void Decorator::StopCursorBlink()
2030 if ( mImpl->mCursorBlinkTimer )
2032 mImpl->mCursorBlinkTimer.Stop();
2035 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2038 void Decorator::DelayCursorBlink()
2040 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2041 mImpl->mDelayCursorBlink = true;
2044 void Decorator::SetCursorBlinkInterval( float seconds )
2046 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
2049 float Decorator::GetCursorBlinkInterval() const
2051 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
2054 void Decorator::SetCursorBlinkDuration( float seconds )
2056 mImpl->mCursorBlinkDuration = seconds;
2059 float Decorator::GetCursorBlinkDuration() const
2061 return mImpl->mCursorBlinkDuration;
2064 void Decorator::SetCursorWidth( int width )
2066 mImpl->mCursorWidth = static_cast<float>( width );
2069 int Decorator::GetCursorWidth() const
2071 return static_cast<int>( mImpl->mCursorWidth );
2074 void Decorator::SetEditable( bool editable )
2076 mImpl->mHidePrimaryCursorAndGrabHandle = !editable;
2077 mImpl->Relayout( mImpl->mControlSize );
2081 void Decorator::SetHandleActive( HandleType handleType, bool active )
2083 mImpl->mHandle[handleType].active = active;
2087 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
2089 mImpl->mIsHandlePreviouslyCrossed = false;
2092 // TODO: this is a work-around.
2093 // The problem is the handle actor does not receive the touch event with the Interrupt
2094 // state when the power button is pressed and the application goes to background.
2095 mImpl->mHandle[handleType].pressed = false;
2096 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2097 ImageView imageView = mImpl->mHandle[handleType].actor;
2098 if( imageReleased && imageView )
2100 imageView.SetImage( mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED] );
2106 bool Decorator::IsHandleActive( HandleType handleType ) const
2108 return mImpl->mHandle[handleType].active ;
2111 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName )
2113 mImpl->SetHandleImage( handleType, handleImageType, imageFileName );
2116 const std::string& Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
2118 return mImpl->mHandleImages[handleType][handleImageType];
2121 void Decorator::SetHandleColor( const Vector4& color )
2123 mImpl->mHandleColor = color;
2126 const Vector4& Decorator::GetHandleColor() const
2128 return mImpl->mHandleColor;
2131 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
2133 // Adjust handle's displacement
2134 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2136 handle.position.x = x;
2137 handle.position.y = y;
2138 handle.lineHeight = height;
2140 if( mImpl->mSmoothHandlePanEnabled )
2142 handle.grabDisplacementX = 0.f;
2143 handle.grabDisplacementY = 0.f;
2147 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2149 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2151 x = handle.position.x;
2152 y = handle.position.y;
2153 height = handle.lineHeight;
2156 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2158 return mImpl->mHandle[handleType].position;
2161 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2163 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2166 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2168 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2171 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2173 mImpl->mFlipSelectionHandlesOnCross = enable;
2176 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2178 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2179 mImpl->mFlipLeftSelectionHandleDirection = left;
2180 mImpl->mFlipRightSelectionHandleDirection = right;
2183 void Decorator::AddHighlight( unsigned int index, const Vector4& quad )
2185 *( mImpl->mHighlightQuadList.Begin() + index ) = quad;
2188 void Decorator::SetHighLightBox( const Vector2& position, const Size& size, float outlineOffset )
2190 mImpl->mHighlightPosition = position;
2191 mImpl->mHighlightSize = size;
2192 mImpl->mHighlightOutlineOffset = outlineOffset;
2195 void Decorator::ClearHighlights()
2197 mImpl->mHighlightQuadList.Clear();
2198 mImpl->mHighlightPosition = Vector2::ZERO;
2199 mImpl->mHighlightOutlineOffset = 0.f;
2202 void Decorator::ResizeHighlightQuads( unsigned int numberOfQuads )
2204 mImpl->mHighlightQuadList.Resize( numberOfQuads );
2207 void Decorator::SetHighlightColor( const Vector4& color )
2209 mImpl->mHighlightColor = color;
2212 const Vector4& Decorator::GetHighlightColor() const
2214 return mImpl->mHighlightColor;
2217 void Decorator::SetHighlightActive( bool active )
2219 mImpl->mIsHighlightBoxActive = active;
2222 bool Decorator::IsHighlightActive() const
2224 return mImpl->mIsHighlightBoxActive;
2227 bool Decorator::IsHighlightVisible() const
2229 return ( mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent() );
2232 void Decorator::SetTextDepth( int textDepth )
2234 mImpl->mTextDepth = textDepth;
2237 void Decorator::SetPopupActive( bool active )
2239 mImpl->mActiveCopyPastePopup = active;
2242 bool Decorator::IsPopupActive() const
2244 return mImpl->mActiveCopyPastePopup;
2247 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2249 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2251 if ( !mImpl->mCopyPastePopup.actor )
2253 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2254 #ifdef DECORATOR_DEBUG
2255 mImpl->mCopyPastePopup.actor.SetProperty( Dali::Actor::Property::NAME,"mCopyPastePopup");
2257 mImpl->mCopyPastePopup.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
2258 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::SetPopupPosition ); // Position popup after size negotiation
2261 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2264 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2266 return mImpl->mEnabledPopupButtons;
2271 void Decorator::SetScrollThreshold( float threshold )
2273 mImpl->SetScrollThreshold( threshold );
2276 float Decorator::GetScrollThreshold() const
2278 return mImpl->GetScrollThreshold();
2281 void Decorator::SetScrollSpeed( float speed )
2283 mImpl->SetScrollSpeed( speed );
2286 float Decorator::GetScrollSpeed() const
2288 return mImpl->GetScrollSpeed();
2291 void Decorator::NotifyEndOfScroll()
2293 mImpl->NotifyEndOfScroll();
2296 void Decorator::SetHorizontalScrollEnabled( bool enable )
2298 mImpl->mHorizontalScrollingEnabled = enable;
2301 bool Decorator::IsHorizontalScrollEnabled() const
2303 return mImpl->mHorizontalScrollingEnabled;
2306 void Decorator::SetVerticalScrollEnabled( bool enable )
2308 mImpl->mVerticalScrollingEnabled = enable;
2311 bool Decorator::IsVerticalScrollEnabled() const
2313 return mImpl->mVerticalScrollingEnabled;
2316 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2318 mImpl->mSmoothHandlePanEnabled = enable;
2321 bool Decorator::IsSmoothHandlePanEnabled() const
2323 return mImpl->mSmoothHandlePanEnabled;
2326 Decorator::~Decorator()
2331 Decorator::Decorator( ControllerInterface& controller,
2332 TextSelectionPopupCallbackInterface& callbackInterface )
2335 mImpl = new Decorator::Impl( controller, callbackInterface );
2340 } // namespace Toolkit