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>
39 #define DECORATOR_DEBUG
43 #define MAKE_SHADER(A)#A
47 const char* VERTEX_SHADER = MAKE_SHADER(
48 attribute mediump vec2 aPosition;
49 uniform highp mat4 uMvpMatrix;
53 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
54 gl_Position = uMvpMatrix * position;
58 const char* FRAGMENT_SHADER = MAKE_SHADER(
59 uniform lowp vec4 uColor;
63 gl_FragColor = uColor;
74 #ifdef DECORATOR_DEBUG
75 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
85 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
86 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
87 const Dali::Vector3 ACTIVE_LAYER_ANCHOR_POINT( 0.5f, 0.5f, 0.5f );
89 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f ); // The text highlight color. TODO: due some problems, maybe with the blending function in the text clipping, the color is fully opaque.
91 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
93 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
94 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
95 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
97 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
98 const float SCROLL_THRESHOLD = 10.f; ///< Threshold in pixels close to the edges of the decorator boundaries from where the scroll timer starts to emit signals.
99 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
101 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
103 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
105 const float POPUP_PADDING = 2.f; ///< Padding space between the highlight box and the text's popup.
107 typedef Dali::Vector<Dali::Vector4> QuadContainer;
110 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
111 * @param[in] boundingRectangle local bounding
112 * @param[out] Vector4 World coordinate bounding Box.
114 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
116 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
117 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
119 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
120 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
122 boundingBox = Dali::Vector4( originX,
124 originX + boundingRectangle.width,
125 originY + boundingRectangle.height );
128 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
130 // Convert to local coordinates and store as a Dali::Rect.
131 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
133 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
134 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
135 boundingRectangle.width = boundingBox.z - boundingBox.x;
136 boundingRectangle.height = boundingBox.w - boundingBox.y;
139 } // end of namespace
150 struct Decorator::Impl : public ConnectionTracker
164 : color( Dali::Color::BLACK ),
166 cursorHeight( 0.0f ),
186 grabDisplacementX( 0.f ),
187 grabDisplacementY( 0.f ),
189 horizontallyVisible( false ),
190 verticallyVisible( false ),
192 verticallyFlippedPreferred( false ),
193 horizontallyFlipped( false ),
194 verticallyFlipped( false ),
195 verticallyFlippedOnTouch( false )
201 ImageView markerActor;
204 Vector2 globalPosition;
206 float lineHeight; ///< Not the handle height
207 float grabDisplacementX;
208 float grabDisplacementY;
210 bool horizontallyVisible : 1;
211 bool verticallyVisible : 1;
213 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
214 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
215 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
216 bool verticallyFlippedOnTouch : 1; ///< Whether the handle is vertically flipped on touch.
226 TextSelectionPopup actor;
230 Impl( ControllerInterface& controller,
231 TextSelectionPopupCallbackInterface& callbackInterface )
232 : mController( controller ),
233 mEnabledPopupButtons( TextSelectionPopup::NONE ),
234 mTextSelectionPopupCallbackInterface( callbackInterface ),
235 mHandleColor( HANDLE_COLOR ),
237 mHighlightColor( LIGHT_BLUE ),
238 mHighlightPosition( Vector2::ZERO ),
239 mHighlightSize( Vector2::ZERO ),
240 mControlSize( Vector2::ZERO ),
241 mHighlightOutlineOffset( 0.f ),
242 mActiveCursor( ACTIVE_CURSOR_NONE ),
243 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
244 mCursorBlinkDuration( 0.0f ),
245 mCursorWidth( CURSOR_WIDTH ),
246 mHandleScrolling( HANDLE_TYPE_COUNT ),
247 mHandleReleased( HANDLE_TYPE_COUNT ),
248 mScrollDirection( SCROLL_NONE ),
249 mScrollThreshold( SCROLL_THRESHOLD ),
250 mScrollSpeed( SCROLL_SPEED ),
251 mScrollDistance( SCROLL_DISTANCE ),
253 mActiveCopyPastePopup( false ),
254 mPopupSetNewPosition( true ),
255 mCursorBlinkStatus( true ),
256 mDelayCursorBlink( false ),
257 mPrimaryCursorVisible( false ),
258 mSecondaryCursorVisible( false ),
259 mFlipSelectionHandlesOnCross( false ),
260 mFlipLeftSelectionHandleDirection( false ),
261 mFlipRightSelectionHandleDirection( false ),
262 mIsHandlePanning( false ),
263 mIsHandleCurrentlyCrossed( false ),
264 mIsHandlePreviouslyCrossed( false ),
265 mNotifyEndOfScroll( false ),
266 mHorizontalScrollingEnabled( false ),
267 mVerticalScrollingEnabled( false ),
268 mSmoothHandlePanEnabled( false ),
269 mIsHighlightBoxActive( false )
271 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
272 mHighlightShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
277 * Relayout of the decorations owned by the decorator.
278 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
280 void Relayout( const Vector2& size )
284 // TODO - Remove this if nothing is active
287 // Show or hide the cursors
292 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
293 mPrimaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
294 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
295 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
296 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
297 if( mPrimaryCursorVisible )
299 mPrimaryCursor.SetProperty( Actor::Property::POSITION, Vector2( cursor.position.x,
300 cursor.position.y ) );
301 mPrimaryCursor.SetProperty( Actor::Property::SIZE, Size( mCursorWidth, cursor.cursorHeight ) );
303 mPrimaryCursor.SetProperty( Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus );
305 if( mSecondaryCursor )
307 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
308 mSecondaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
309 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
310 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
311 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
312 if( mSecondaryCursorVisible )
314 mSecondaryCursor.SetProperty( Actor::Property::POSITION, Vector2( cursor.position.x,
315 cursor.position.y ) );
316 mSecondaryCursor.SetProperty( Actor::Property::SIZE, Size( mCursorWidth, cursor.cursorHeight ) );
318 mSecondaryCursor.SetProperty( Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus );
321 // Show or hide the grab handle
322 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
323 bool newGrabHandlePosition = false;
324 grabHandle.horizontallyVisible = false;
325 grabHandle.verticallyVisible = false;
326 if( grabHandle.active )
328 grabHandle.horizontallyVisible = ( ( mControlSize.width - ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) ) > -Math::MACHINE_EPSILON_1000 ) &&
329 ( grabHandle.position.x > -Math::MACHINE_EPSILON_1000 ) );
330 grabHandle.verticallyVisible = ( ( ( mControlSize.height - grabHandle.lineHeight ) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) &&
331 ( grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) );
333 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible;
338 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
339 SetGrabHandlePosition();
341 // Sets the grab handle image according if it's pressed, flipped, etc.
342 SetHandleImage( GRAB_HANDLE );
344 newGrabHandlePosition = true;
347 if( grabHandle.actor )
349 grabHandle.actor.SetProperty( Actor::Property::VISIBLE, isVisible );
352 else if( grabHandle.actor )
354 grabHandle.actor.Unparent();
357 // Show or hide the selection handles/highlight
358 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
359 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
360 bool newPrimaryHandlePosition = false;
361 bool newSecondaryHandlePosition = false;
363 primary.horizontallyVisible = ( ( mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
364 ( primary.position.x > -Math::MACHINE_EPSILON_1000 ) );
365 primary.verticallyVisible = ( ( ( mControlSize.height - primary.lineHeight ) - primary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
366 ( primary.position.y + ( primary.verticallyFlipped ? 0.f : primary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
367 secondary.horizontallyVisible = ( ( mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
368 ( secondary.position.x > -Math::MACHINE_EPSILON_1000 ) );
369 secondary.verticallyVisible = ( ( ( mControlSize.height - secondary.lineHeight ) - secondary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
370 ( secondary.position.y + ( secondary.verticallyFlipped ? 0.f : secondary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
372 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
373 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
375 if( primary.active || secondary.active )
377 if( primaryVisible || secondaryVisible )
379 CreateSelectionHandles();
383 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
385 // Sets the primary handle image according if it's pressed, flipped, etc.
386 SetHandleImage( LEFT_SELECTION_HANDLE );
388 SetSelectionHandleMarkerSize( primary );
390 newPrimaryHandlePosition = true;
393 if( secondaryVisible )
395 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
397 // Sets the secondary handle image according if it's pressed, flipped, etc.
398 SetHandleImage( RIGHT_SELECTION_HANDLE );
400 SetSelectionHandleMarkerSize( secondary );
402 newSecondaryHandlePosition = true;
408 primary.actor.SetProperty( Actor::Property::VISIBLE, primaryVisible );
410 if( secondary.actor )
412 secondary.actor.SetProperty( Actor::Property::VISIBLE, secondaryVisible );
420 primary.actor.Unparent();
422 if( secondary.actor )
424 secondary.actor.Unparent();
428 if( mIsHighlightBoxActive )
435 if( mHighlightActor )
437 mHighlightActor.Unparent();
441 if( newGrabHandlePosition ||
442 newPrimaryHandlePosition ||
443 newSecondaryHandlePosition )
445 // Setup property notifications to find whether the handles leave the boundaries of the current display.
446 SetupActiveLayerPropertyNotifications();
449 if( mActiveCopyPastePopup &&
450 ( primaryVisible || secondaryVisible ) )
453 mPopupSetNewPosition = true;
457 if( mCopyPastePopup.actor )
459 mCopyPastePopup.actor.HidePopup();
460 mPopupSetNewPosition = true;
465 void UpdatePositions( const Vector2& scrollOffset )
467 mCursor[PRIMARY_CURSOR].position += scrollOffset;
468 mCursor[SECONDARY_CURSOR].position += scrollOffset;
469 mHandle[ GRAB_HANDLE ].position += scrollOffset;
470 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
471 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
472 mHighlightPosition += scrollOffset;
477 if( !mCopyPastePopup.actor )
482 if( !mCopyPastePopup.actor.GetParent() )
484 mActiveLayer.Add( mCopyPastePopup.actor );
487 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
488 mCopyPastePopup.actor.ShowPopup();
491 float CalculateVerticalPopUpPosition( float halfHeight, bool preferBelow )
493 float yPosition = 0.f;
495 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
496 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
497 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
499 if( primaryHandle.active || secondaryHandle.active )
501 // The origin of the decorator's coordinate system in world coords.
502 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION ) - mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ) * ACTIVE_LAYER_ANCHOR_POINT;
506 // Find out if there is enough space for the popup at the bottom.
507 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
508 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
510 float maxY = std::max( primaryBelowY, secondaryBelowY );
512 yPosition = halfHeight + maxY;
514 if( originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w )
516 // Does not fit below.
518 // Try to fit first below the non active handle. Otherwise above the active handle.
519 if( RIGHT_SELECTION_HANDLE == mHandleReleased )
521 if( primaryBelowY < secondaryBelowY )
523 yPosition = halfHeight + primaryBelowY;
527 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
530 else if( LEFT_SELECTION_HANDLE == mHandleReleased )
532 if( secondaryBelowY < primaryBelowY )
534 yPosition = halfHeight + secondaryBelowY;
538 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
542 // Check the handle is whithin the decoration box.
543 if( originWorldCoords.y + yPosition < mBoundingBox.y )
545 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
548 if( originWorldCoords.y + yPosition > mBoundingBox.w )
550 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
556 // Find out if there is enough space for the popup at the top.
557 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
558 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
560 float minY = std::min( primaryTopY, secondaryTopY );
562 yPosition = -halfHeight + minY;
564 } // ( primaryHandle.active || secondaryHandle.active )
565 else if( grabHandle.active )
569 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
573 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
580 void ConstrainPopupPosition( const Vector3& popupHalfSize )
582 // Check if the popup is within the boundaries of the decoration box.
584 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
586 // The origin of the decorator's coordinate system in world coords.
587 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION ) - mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ) * ACTIVE_LAYER_ANCHOR_POINT;
589 // The popup's position in world coords.
590 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
592 if( popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x )
594 mCopyPastePopup.position.x += mBoundingBox.x - ( popupPositionWorldCoords.x - popupHalfSize.width );
596 else if( popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z )
598 mCopyPastePopup.position.x += mBoundingBox.z - ( popupPositionWorldCoords.x + popupHalfSize.width );
601 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
602 if( popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y )
604 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition( popupHalfSize.height, true ); // true -> prefer to set the popup's position below.
608 void SetPopupPosition( Actor actor )
610 if( !mActiveCopyPastePopup )
615 // Retrieves the popup's size after relayout.
616 const Vector3 popupSize( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
617 const Vector3 popupHalfSize = popupSize * 0.5f;
619 if( mPopupSetNewPosition )
621 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
622 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
623 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
625 if( primaryHandle.active || secondaryHandle.active )
627 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
628 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
630 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
632 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - ( primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING );
633 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - ( secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING );
635 mCopyPastePopup.position.y = std::min( primaryY, secondaryY );
637 else if( grabHandle.active )
639 mCopyPastePopup.position.x = grabHandle.position.x;
641 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - ( grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING );
643 } // mPopupSetNewPosition
645 // It may change the popup's position to fit within the decoration box.
646 ConstrainPopupPosition( popupHalfSize );
648 SetUpPopupPositionNotifications( popupHalfSize );
650 // Prevent pixel mis-alignment by rounding down.
651 mCopyPastePopup.position.x = floorf( mCopyPastePopup.position.x );
652 mCopyPastePopup.position.y = floorf( mCopyPastePopup.position.y );
654 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION, mCopyPastePopup.position );
655 mPopupSetNewPosition = false;
658 void CreateCursor( Control& cursor, const Vector4& color )
660 cursor = Control::New();
661 cursor.SetBackgroundColor( color );
662 cursor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
663 cursor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
666 // Add or Remove cursor(s) from parent
669 if( mActiveCursor == ACTIVE_CURSOR_NONE )
673 mPrimaryCursor.Unparent();
675 if( mSecondaryCursor )
677 mSecondaryCursor.Unparent();
682 // Create Primary and or Secondary Cursor(s) if active and add to parent
683 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
684 mActiveCursor == ACTIVE_CURSOR_BOTH )
686 if ( !mPrimaryCursor )
688 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
689 #ifdef DECORATOR_DEBUG
690 mPrimaryCursor.SetProperty( Dali::Actor::Property::NAME, "PrimaryCursorActor" );
694 if( !mPrimaryCursor.GetParent() )
696 mActiveLayer.Add( mPrimaryCursor );
700 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
702 if ( !mSecondaryCursor )
704 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
705 #ifdef DECORATOR_DEBUG
706 mSecondaryCursor.SetProperty( Dali::Actor::Property::NAME, "SecondaryCursorActor" );
710 if( !mSecondaryCursor.GetParent() )
712 mActiveLayer.Add( mSecondaryCursor );
717 if( mSecondaryCursor )
719 mSecondaryCursor.Unparent();
725 bool OnCursorBlinkTimerTick()
727 if( !mDelayCursorBlink )
730 if ( mPrimaryCursor )
732 mPrimaryCursor.SetProperty( Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus );
734 if ( mSecondaryCursor )
736 mSecondaryCursor.SetProperty( Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus );
739 mCursorBlinkStatus = !mCursorBlinkStatus;
744 mDelayCursorBlink = false;
752 // Will consume tap gestures on handles.
753 mTapDetector = TapGestureDetector::New();
755 // Will consume double tap gestures on handles.
756 mTapDetector.SetMaximumTapsRequired( 2u );
758 // Will consume long press gestures on handles.
759 mLongPressDetector = LongPressGestureDetector::New();
761 // Detects pan gestures on handles.
762 mPanDetector = PanGestureDetector::New();
763 mPanDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
766 void CreateActiveLayer()
770 mActiveLayer = Actor::New();
771 #ifdef DECORATOR_DEBUG
772 mActiveLayer.SetProperty( Actor::Property::NAME, "ActiveLayerActor" );
775 mActiveLayer.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
776 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
778 // Add the active layer telling the controller it doesn't need clipping.
779 mController.AddDecoration( mActiveLayer, false );
782 mActiveLayer.RaiseToTop();
785 void SetSelectionHandleMarkerSize( HandleImpl& handle )
787 if( handle.markerActor )
789 handle.markerActor.SetProperty( Actor::Property::SIZE, Vector2( 0, handle.lineHeight ) );
793 void CreateGrabHandle()
795 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
796 if( !grabHandle.actor )
798 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED].size() )
800 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
801 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
802 grabHandle.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
804 // Area that Grab handle responds to, larger than actual handle so easier to move
805 #ifdef DECORATOR_DEBUG
806 grabHandle.actor.SetProperty( Dali::Actor::Property::NAME, "GrabHandleActor" );
807 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
809 grabHandle.grabArea = Control::New();
810 Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
811 control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
812 grabHandle.grabArea.SetProperty( Dali::Actor::Property::NAME, "GrabArea" );
816 grabHandle.grabArea = Actor::New();
817 grabHandle.grabArea.SetProperty( Dali::Actor::Property::NAME, "GrabArea" );
820 grabHandle.grabArea = Actor::New();
823 grabHandle.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
824 grabHandle.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
825 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
826 grabHandle.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
827 grabHandle.actor.Add( grabHandle.grabArea );
828 grabHandle.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
830 grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
832 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
833 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
834 mTapDetector.Attach( grabHandle.actor );
835 mLongPressDetector.Attach( grabHandle.actor );
837 // The grab handle's area is attached to the pan detector.
838 // The OnPan() method is connected to the signals emitted by the pan detector.
839 mPanDetector.Attach( grabHandle.grabArea );
841 mActiveLayer.Add( grabHandle.actor );
845 if( grabHandle.actor && !grabHandle.actor.GetParent() )
847 mActiveLayer.Add( grabHandle.actor );
851 void CreateHandleMarker( HandleImpl& handle, const std::string& image, HandleType handleType )
855 handle.markerActor = ImageView::New( image );
856 handle.markerActor.SetProperty( Actor::Property::COLOR, mHandleColor );
857 handle.actor.Add( handle.markerActor );
859 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
861 if( LEFT_SELECTION_HANDLE == handleType )
863 handle.markerActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT );
864 handle.markerActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_RIGHT );
866 else if( RIGHT_SELECTION_HANDLE == handleType )
868 handle.markerActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT );
869 handle.markerActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
874 void CreateSelectionHandles()
876 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
879 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size() )
881 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
882 #ifdef DECORATOR_DEBUG
883 primary.actor.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleOne");
885 primary.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
886 GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
887 primary.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
889 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
890 #ifdef DECORATOR_DEBUG
891 primary.grabArea.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleOneGrabArea");
893 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
894 primary.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
895 primary.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
896 primary.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
898 primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
900 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
901 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
902 mTapDetector.Attach( primary.actor );
903 mLongPressDetector.Attach( primary.actor );
905 // The handle's area is attached to the pan detector.
906 // The OnPan() method is connected to the signals emitted by the pan detector.
907 mPanDetector.Attach( primary.grabArea );
909 primary.actor.Add( primary.grabArea );
911 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
915 if( primary.actor && !primary.actor.GetParent() )
917 mActiveLayer.Add( primary.actor );
920 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
921 if( !secondary.actor )
923 if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size() )
925 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
926 #ifdef DECORATOR_DEBUG
927 secondary.actor.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleTwo");
929 secondary.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
930 GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
931 secondary.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
933 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
934 #ifdef DECORATOR_DEBUG
935 secondary.grabArea.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleTwoGrabArea");
937 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
938 secondary.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
939 secondary.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
940 secondary.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
942 secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
944 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
945 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
946 mTapDetector.Attach( secondary.actor );
947 mLongPressDetector.Attach( secondary.actor );
949 // The handle's area is attached to the pan detector.
950 // The OnPan() method is connected to the signals emitted by the pan detector.
951 mPanDetector.Attach( secondary.grabArea );
953 secondary.actor.Add( secondary.grabArea );
955 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
959 if( secondary.actor && !secondary.actor.GetParent() )
961 mActiveLayer.Add( secondary.actor );
965 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
967 // Gets the world position of the active layer. The active layer is where the handles are added.
968 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION );
970 // The grab handle position in world coords.
971 // The active layer's world position is the center of the active layer. The origin of the
972 // coord system of the handles is the top left of the active layer.
973 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f );
974 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f );
977 void SetGrabHandlePosition()
979 // Reference to the grab handle.
980 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
982 // Transforms the handle position into world coordinates.
983 // @note This is not the same value as grabHandle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
984 // as it's transforming the handle's position set by the text-controller and not
985 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
986 // retrieves the position of the center of the actor but the handle's position set
987 // by the text controller is not the center of the actor.
988 Vector2 grabHandleWorldPosition;
989 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
991 // Check if the grab handle exceeds the boundaries of the decoration box.
992 // At the moment only the height is checked for the grab handle.
994 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
995 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
996 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
998 // The grab handle 'y' position in local coords.
999 // If the grab handle exceeds the bottom of the decoration box,
1000 // set the 'y' position to the top of the line.
1001 // The SetGrabHandleImage() method will change the orientation.
1002 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
1004 if( grabHandle.actor )
1006 grabHandle.actor.SetProperty( Actor::Property::POSITION, Vector2( grabHandle.position.x + floor( 0.5f * mCursorWidth ) + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f ),
1007 yLocalPosition + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f ) ) );
1011 void SetSelectionHandlePosition( HandleType type )
1013 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
1015 // Reference to the selection handle.
1016 HandleImpl& handle = mHandle[type];
1018 // Transforms the handle position into world coordinates.
1019 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1020 // as it's transforming the handle's position set by the text-controller and not
1021 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1022 // retrieves the position of the center of the actor but the handle's position set
1023 // by the text controller is not the center of the actor.
1024 Vector2 handleWorldPosition;
1025 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
1027 // Whether to flip the handle (horizontally).
1028 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1030 // Whether to flip the handles if they are crossed.
1031 bool crossFlip = false;
1032 if( mFlipSelectionHandlesOnCross || !mIsHandlePanning )
1034 crossFlip = mIsHandleCurrentlyCrossed;
1037 // Whether the handle was crossed before start the panning.
1038 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1040 // Does not flip if both conditions are true (double flip)
1041 flipHandle = flipHandle != ( crossFlip || isHandlePreviouslyCrossed );
1043 // Will flip the handles vertically if the user prefers it.
1044 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1046 if( crossFlip || isHandlePreviouslyCrossed )
1048 if( isPrimaryHandle )
1050 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1054 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1058 // Check if the selection handle exceeds the boundaries of the decoration box.
1059 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
1060 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
1062 // Does not flip if both conditions are true (double flip)
1063 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
1067 if( handle.actor && !handle.horizontallyFlipped )
1069 // Change the anchor point to flip the image.
1070 handle.actor.SetProperty( Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
1072 handle.horizontallyFlipped = true;
1077 if( handle.actor && handle.horizontallyFlipped )
1079 // Reset the anchor point.
1080 handle.actor.SetProperty( Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
1082 handle.horizontallyFlipped = false;
1086 // Whether to flip the handle vertically.
1087 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
1088 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
1089 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
1091 // The primary selection handle 'y' position in local coords.
1092 // If the handle exceeds the bottom of the decoration box,
1093 // set the 'y' position to the top of the line.
1094 // The SetHandleImage() method will change the orientation.
1095 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1099 handle.actor.SetProperty( Actor::Property::POSITION, Vector2( handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f ),
1100 yLocalPosition + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f ) ) );
1104 void SetHandleImage( HandleType type )
1106 HandleImpl& handle = mHandle[type];
1108 HandleType markerType = HANDLE_TYPE_COUNT;
1109 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1110 if( LEFT_SELECTION_HANDLE == type )
1112 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1113 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1115 else if( RIGHT_SELECTION_HANDLE == type )
1117 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1118 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1121 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1124 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1126 handle.actor.SetImage( mHandleImages[type][imageType] );
1129 if( HANDLE_TYPE_COUNT != markerType )
1131 if( handle.markerActor )
1133 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1134 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
1138 // Whether to flip the handle vertically.
1141 handle.actor.SetProperty( Actor::Property::ORIENTATION, Quaternion( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS ) );
1145 void CreateHighlight()
1147 if( !mHighlightActor )
1149 mHighlightActor = Actor::New();
1151 mHighlightActor.SetProperty( Dali::Actor::Property::NAME, "HighlightActor" );
1152 mHighlightActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
1153 mHighlightActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
1154 mHighlightActor.SetProperty( Actor::Property::COLOR, mHighlightColor );
1155 mHighlightActor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_COLOR );
1158 // Add the highlight box telling the controller it needs clipping.
1159 mController.AddDecoration( mHighlightActor, true );
1162 void UpdateHighlight()
1164 if ( mHighlightActor )
1166 // Sets the position of the highlight actor inside the decorator.
1167 mHighlightActor.SetProperty( Actor::Property::POSITION, Vector2( mHighlightPosition.x + mHighlightOutlineOffset,
1168 mHighlightPosition.y + mHighlightOutlineOffset ) );
1170 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1171 if( 0u != numberOfQuads )
1173 // Set the size of the highlighted text to the actor.
1174 mHighlightActor.SetProperty( Actor::Property::SIZE, mHighlightSize );
1176 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1177 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1178 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1180 Vector<Vector2> vertices;
1181 Vector<unsigned short> indices;
1183 vertices.Reserve( 4u * numberOfQuads );
1184 indices.Reserve( 6u * numberOfQuads );
1186 // Index to the vertex.
1187 unsigned int v = 0u;
1189 // Traverse all quads.
1190 for( Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1191 endIt = mHighlightQuadList.End();
1195 const Vector4& quad = *it;
1200 vertex.x = quad.x - offsetX;
1201 vertex.y = quad.y - offsetY;
1202 vertices.PushBack( vertex );
1205 vertex.x = quad.z - offsetX;
1206 vertex.y = quad.y - offsetY;
1207 vertices.PushBack( vertex );
1209 // bottom-left (v+2)
1210 vertex.x = quad.x - offsetX;
1211 vertex.y = quad.w - offsetY;
1212 vertices.PushBack( vertex );
1214 // bottom-right (v+3)
1215 vertex.x = quad.z - offsetX;
1216 vertex.y = quad.w - offsetY;
1217 vertices.PushBack( vertex );
1219 // triangle A (3, 1, 0)
1220 indices.PushBack( v + 3 );
1221 indices.PushBack( v + 1 );
1222 indices.PushBack( v );
1224 // triangle B (0, 2, 3)
1225 indices.PushBack( v );
1226 indices.PushBack( v + 2 );
1227 indices.PushBack( v + 3 );
1230 if( ! mQuadVertices )
1232 mQuadVertices = VertexBuffer::New( mQuadVertexFormat );
1235 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1237 if( !mQuadGeometry )
1239 mQuadGeometry = Geometry::New();
1240 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1242 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1244 if( !mHighlightRenderer )
1246 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1247 mHighlightActor.AddRenderer( mHighlightRenderer );
1251 mHighlightQuadList.Clear();
1253 if( mHighlightRenderer )
1255 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1260 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1262 GestureState state = gesture.GetState();
1263 if( GestureState::STARTED == state )
1265 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1267 handle.globalPosition.x = handle.position.x;
1268 handle.globalPosition.y = handle.position.y;
1271 const Vector2& displacement = gesture.GetDisplacement();
1272 handle.grabDisplacementX += displacement.x;
1273 handle.grabDisplacementY += ( handle.verticallyFlipped ? -displacement.y : displacement.y );
1275 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1276 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1277 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f );
1279 if( ( GestureState::STARTED == state ) ||
1280 ( GestureState::CONTINUING == state ) )
1283 mController.GetTargetSize( targetSize );
1285 if( mHorizontalScrollingEnabled &&
1286 ( x < mScrollThreshold ) )
1288 mScrollDirection = SCROLL_RIGHT;
1289 mHandleScrolling = type;
1292 else if( mHorizontalScrollingEnabled &&
1293 ( x > targetSize.width - mScrollThreshold ) )
1295 mScrollDirection = SCROLL_LEFT;
1296 mHandleScrolling = type;
1299 else if( mVerticalScrollingEnabled &&
1300 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1302 mScrollDirection = SCROLL_TOP;
1303 mHandleScrolling = type;
1306 else if( mVerticalScrollingEnabled &&
1307 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1309 mScrollDirection = SCROLL_BOTTOM;
1310 mHandleScrolling = type;
1315 mHandleScrolling = HANDLE_TYPE_COUNT;
1317 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1320 mIsHandlePanning = true;
1322 else if( ( GestureState::FINISHED == state ) ||
1323 ( GestureState::CANCELLED == state ) )
1326 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1328 mNotifyEndOfScroll = false;
1329 mHandleScrolling = HANDLE_TYPE_COUNT;
1331 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1335 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1340 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1342 handle.pressed = false;
1344 mIsHandlePanning = false;
1348 void OnPan( Actor actor, const PanGesture& gesture )
1350 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1351 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1352 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1354 if( actor == grabHandle.grabArea )
1356 DoPan( grabHandle, GRAB_HANDLE, gesture );
1358 else if( actor == primarySelectionHandle.grabArea )
1360 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1362 else if( actor == secondarySelectionHandle.grabArea )
1364 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1368 bool OnGrabHandleTouched( Actor actor, const TouchEvent& touch )
1370 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1372 // Switch between pressed/release grab-handle images
1373 if( touch.GetPointCount() > 0 &&
1376 const PointState::Type state = touch.GetState( 0 );
1378 if( PointState::DOWN == state )
1380 grabHandle.pressed = true;
1382 else if( ( PointState::UP == state ) ||
1383 ( PointState::INTERRUPTED == state ) )
1385 grabHandle.pressed = false;
1388 SetHandleImage( GRAB_HANDLE );
1391 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1395 bool OnHandleOneTouched( Actor actor, const TouchEvent& touch )
1397 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1399 // Switch between pressed/release selection handle images
1400 if( touch.GetPointCount() > 0 &&
1401 primarySelectionHandle.actor )
1403 const PointState::Type state = touch.GetState( 0 );
1405 if( PointState::DOWN == state )
1407 primarySelectionHandle.pressed = true;
1408 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1410 else if( ( PointState::UP == state ) ||
1411 ( PointState::INTERRUPTED == state ) )
1413 primarySelectionHandle.pressed = false;
1414 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1415 mIsHandlePanning = false;
1416 mHandleReleased = LEFT_SELECTION_HANDLE;
1419 SetHandleImage( LEFT_SELECTION_HANDLE );
1422 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1426 bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch )
1428 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1430 // Switch between pressed/release selection handle images
1431 if( touch.GetPointCount() > 0 &&
1432 secondarySelectionHandle.actor )
1434 const PointState::Type state = touch.GetState( 0 );
1436 if( PointState::DOWN == state )
1438 secondarySelectionHandle.pressed = true;
1439 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1441 else if( ( PointState::UP == state ) ||
1442 ( PointState::INTERRUPTED == state ) )
1444 secondarySelectionHandle.pressed = false;
1445 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1446 mIsHandlePanning = false;
1447 mHandleReleased = RIGHT_SELECTION_HANDLE;
1450 SetHandleImage( RIGHT_SELECTION_HANDLE );
1453 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1457 void HandleResetPosition( PropertyNotification& source )
1459 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1461 if( grabHandle.active )
1463 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1464 SetGrabHandlePosition();
1466 // Sets the grab handle image according if it's pressed, flipped, etc.
1467 SetHandleImage( GRAB_HANDLE );
1471 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1472 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1474 // Sets the primary handle image according if it's pressed, flipped, etc.
1475 SetHandleImage( LEFT_SELECTION_HANDLE );
1477 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1478 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1480 // Sets the secondary handle image according if it's pressed, flipped, etc.
1481 SetHandleImage( RIGHT_SELECTION_HANDLE );
1485 void SetupActiveLayerPropertyNotifications()
1492 // Vertical notifications.
1494 // Disconnect any previous connected callback.
1495 if( mHandleVerticalLessThanNotification )
1497 mHandleVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1498 mActiveLayer.RemovePropertyNotification( mHandleVerticalLessThanNotification );
1501 if( mHandleVerticalGreaterThanNotification )
1503 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1504 mActiveLayer.RemovePropertyNotification( mHandleVerticalGreaterThanNotification );
1507 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1508 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1509 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1511 if( grabHandle.active )
1513 if( grabHandle.verticallyFlipped )
1515 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1516 mHandleVerticalGreaterThanNotification.Reset();
1518 // The vertical distance from the center of the active layer to the top edje of the display.
1519 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1521 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1522 LessThanCondition( mBoundingBox.y + topHeight ) );
1524 // Notifies the change from false to true and from true to false.
1525 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1527 // Connects the signals with the callbacks.
1528 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1532 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1533 mHandleVerticalLessThanNotification.Reset();
1535 // The vertical distance from the center of the active layer to the bottom edje of the display.
1536 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1538 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1539 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1541 // Notifies the change from false to true and from true to false.
1542 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1544 // Connects the signals with the callbacks.
1545 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1548 else // The selection handles are active
1550 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1552 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1553 mHandleVerticalGreaterThanNotification.Reset();
1555 // The vertical distance from the center of the active layer to the top edje of the display.
1556 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1558 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1559 LessThanCondition( mBoundingBox.y + topHeight ) );
1561 // Notifies the change from false to true and from true to false.
1562 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1564 // Connects the signals with the callbacks.
1565 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1567 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1569 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1570 mHandleVerticalLessThanNotification.Reset();
1572 // The vertical distance from the center of the active layer to the bottom edje of the display.
1573 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1574 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1576 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1577 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1579 // Notifies the change from false to true and from true to false.
1580 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1582 // Connects the signals with the callbacks.
1583 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1587 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1589 // The vertical distance from the center of the active layer to the top edje of the display.
1590 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1591 -primaryHandle.position.y + primaryHandle.size.height :
1592 -secondaryHandle.position.y + secondaryHandle.size.height );
1594 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1595 LessThanCondition( mBoundingBox.y + topHeight ) );
1597 // Notifies the change from false to true and from true to false.
1598 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1600 // Connects the signals with the callbacks.
1601 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1603 // The vertical distance from the center of the active layer to the bottom edje of the display.
1604 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1605 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1606 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1608 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1609 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1611 // Notifies the change from false to true and from true to false.
1612 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1614 // Connects the signals with the callbacks.
1615 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1619 // Horizontal notifications.
1621 // Disconnect any previous connected callback.
1622 if( mHandleHorizontalLessThanNotification )
1624 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1625 mActiveLayer.RemovePropertyNotification( mHandleHorizontalLessThanNotification );
1628 if( mHandleHorizontalGreaterThanNotification )
1630 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1631 mActiveLayer.RemovePropertyNotification( mHandleHorizontalGreaterThanNotification );
1634 if( primaryHandle.active || secondaryHandle.active )
1636 // The horizontal distance from the center of the active layer to the left edje of the display.
1637 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1638 -secondaryHandle.position.x + secondaryHandle.size.width );
1640 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1641 LessThanCondition( mBoundingBox.x + leftWidth ) );
1643 // Notifies the change from false to true and from true to false.
1644 mHandleHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1646 // Connects the signals with the callbacks.
1647 mHandleHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1649 // The horizontal distance from the center of the active layer to the right edje of the display.
1650 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1651 secondaryHandle.position.x + secondaryHandle.size.width );
1653 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1654 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1656 // Notifies the change from false to true and from true to false.
1657 mHandleHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1659 // Connects the signals with the callbacks.
1660 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1666 float AlternatePopUpPositionRelativeToCursor( bool topBottom )
1668 float alternativePosition = 0.0f;
1670 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1672 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1673 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1674 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1675 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1677 if( primaryHandle.active || secondaryHandle.active )
1679 float handleY = 0.f;
1680 float maxHandleHeight = 0.f;
1682 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1683 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1685 if( primaryVisible && secondaryVisible )
1687 handleY = std::max( primaryHandle.position.y, secondaryHandle.position.y );
1688 maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1690 else if( primaryVisible && !secondaryVisible )
1692 handleY = primaryHandle.position.y;
1693 maxHandleHeight = primaryHandle.size.height;
1695 else if( !primaryVisible && secondaryVisible )
1697 handleY = secondaryHandle.position.y;
1698 maxHandleHeight = secondaryHandle.size.height;
1701 alternativePosition = handleY + ( topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight );
1705 alternativePosition = cursor.position.y + ( topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height );
1708 return alternativePosition;
1711 void PopUpLeavesTopBoundary( PropertyNotification& source )
1713 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1715 // Sets the position of the popup below.
1716 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION_Y, floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, true ) ) );
1719 void PopUpLeavesBottomBoundary( PropertyNotification& source )
1721 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1723 // Sets the position of the popup above.
1724 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION_Y, floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, false ) ) );
1727 void SetUpPopupPositionNotifications( const Vector3& popupHalfSize )
1729 // Disconnect any previous connected callback.
1730 if( mPopupTopExceedNotification )
1732 mPopupTopExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1733 mCopyPastePopup.actor.RemovePropertyNotification( mPopupTopExceedNotification );
1736 if( mPopupBottomExceedNotification )
1738 mPopupBottomExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1739 mCopyPastePopup.actor.RemovePropertyNotification( mPopupBottomExceedNotification );
1742 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1744 // Exceeding vertical boundary
1746 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1747 LessThanCondition( mBoundingBox.y + popupHalfSize.height ) );
1749 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1750 GreaterThanCondition( mBoundingBox.w - popupHalfSize.height ) );
1752 // Notifies the change from false to true and from true to false.
1753 mPopupTopExceedNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1754 mPopupBottomExceedNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1756 mPopupTopExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1757 mPopupBottomExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1760 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName )
1762 ImageDimensions dimensions = Dali::GetOriginalImageSize( imageFileName );
1764 HandleImpl& handle = mHandle[handleType];
1765 handle.size = Size( dimensions.GetWidth(), dimensions.GetHeight() );
1767 mHandleImages[handleType][handleImageType] = imageFileName;
1770 void SetScrollThreshold( float threshold )
1772 mScrollThreshold = threshold;
1775 float GetScrollThreshold() const
1777 return mScrollThreshold;
1780 void SetScrollSpeed( float speed )
1782 mScrollSpeed = speed;
1783 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1786 float GetScrollSpeed() const
1788 return mScrollSpeed;
1791 void NotifyEndOfScroll()
1797 mNotifyEndOfScroll = true;
1802 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1804 * It only starts the timer if it's already created.
1806 void StartScrollTimer()
1810 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1811 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1814 if( !mScrollTimer.IsRunning() )
1816 mScrollTimer.Start();
1821 * Stops the timer used to scroll the text.
1823 void StopScrollTimer()
1827 mScrollTimer.Stop();
1832 * Callback called by the timer used to scroll the text.
1834 * It calculates and sets a new scroll position.
1836 bool OnScrollTimerTick()
1838 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1843 switch( mScrollDirection )
1847 x = mScrollDistance;
1852 x = -mScrollDistance;
1857 y = mScrollDistance;
1862 y = -mScrollDistance;
1869 mController.DecorationEvent( mHandleScrolling,
1878 ControllerInterface& mController;
1880 TapGestureDetector mTapDetector;
1881 PanGestureDetector mPanDetector;
1882 LongPressGestureDetector mLongPressDetector;
1884 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1885 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1887 Actor mActiveLayer; ///< Actor for active handles and alike that ensures they are above all else.
1888 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1889 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1890 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1891 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1892 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1893 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1894 Control mPrimaryCursor;
1895 Control mSecondaryCursor;
1897 Actor mHighlightActor; ///< Actor to display highlight
1898 Renderer mHighlightRenderer;
1899 Shader mHighlightShader; ///< Shader used for highlight
1900 Property::Map mQuadVertexFormat;
1901 PopupImpl mCopyPastePopup;
1902 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1903 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1905 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1906 Vector4 mHandleColor;
1908 CursorImpl mCursor[CURSOR_COUNT];
1909 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1911 VertexBuffer mQuadVertices;
1912 Geometry mQuadGeometry;
1913 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1915 Vector4 mBoundingBox; ///< The bounding box in world coords.
1916 Vector4 mHighlightColor; ///< Color of the highlight
1917 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1918 Size mHighlightSize; ///< The size of the highlighted text.
1919 Size mControlSize; ///< The control's size. Set by the Relayout.
1920 float mHighlightOutlineOffset; ///< The outline's offset.
1922 unsigned int mActiveCursor;
1923 unsigned int mCursorBlinkInterval;
1924 float mCursorBlinkDuration;
1925 float mCursorWidth; ///< The width of the cursors in pixels.
1926 HandleType mHandleScrolling; ///< The handle which is scrolling.
1927 HandleType mHandleReleased; ///< The last handle released.
1928 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1929 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1930 float mScrollSpeed; ///< The scroll speed in pixels per second.
1931 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1932 int mTextDepth; ///< The depth used to render the text.
1934 bool mActiveCopyPastePopup : 1;
1935 bool mPopupSetNewPosition : 1;
1936 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1937 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1938 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1939 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1940 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1941 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1942 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1943 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1944 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1945 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1946 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1947 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1948 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1949 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1950 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1953 DecoratorPtr Decorator::New( ControllerInterface& controller,
1954 TextSelectionPopupCallbackInterface& callbackInterface )
1956 return DecoratorPtr( new Decorator( controller,
1957 callbackInterface ) );
1960 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1962 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1965 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1967 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1970 void Decorator::Relayout( const Vector2& size )
1972 mImpl->Relayout( size );
1975 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1977 mImpl->UpdatePositions( scrollOffset );
1982 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1984 mImpl->mActiveCursor = activeCursor;
1987 unsigned int Decorator::GetActiveCursor() const
1989 return mImpl->mActiveCursor;
1992 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1994 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1996 cursorImpl.position.x = x;
1997 cursorImpl.position.y = y;
1998 cursorImpl.cursorHeight = cursorHeight;
1999 cursorImpl.lineHeight = lineHeight;
2002 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
2004 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2006 x = cursorImpl.position.x;
2007 y = cursorImpl.position.y;
2008 cursorHeight = cursorImpl.cursorHeight;
2009 lineHeight = cursorImpl.lineHeight;
2012 const Vector2& Decorator::GetPosition( Cursor cursor ) const
2014 return mImpl->mCursor[cursor].position;
2017 void Decorator::SetGlyphOffset( Cursor cursor, float glyphOffset )
2019 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2021 cursorImpl.glyphOffset = glyphOffset;
2024 const float Decorator::GetGlyphOffset( Cursor cursor) const
2026 return mImpl->mCursor[cursor].glyphOffset;
2029 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
2031 mImpl->mCursor[cursor].color = color;
2034 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
2036 return mImpl->mCursor[cursor].color;
2039 void Decorator::StartCursorBlink()
2041 if ( !mImpl->mCursorBlinkTimer )
2043 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
2044 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
2047 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
2049 mImpl->mCursorBlinkTimer.Start();
2053 void Decorator::StopCursorBlink()
2055 if ( mImpl->mCursorBlinkTimer )
2057 mImpl->mCursorBlinkTimer.Stop();
2060 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2063 void Decorator::DelayCursorBlink()
2065 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2066 mImpl->mDelayCursorBlink = true;
2069 void Decorator::SetCursorBlinkInterval( float seconds )
2071 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
2074 float Decorator::GetCursorBlinkInterval() const
2076 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
2079 void Decorator::SetCursorBlinkDuration( float seconds )
2081 mImpl->mCursorBlinkDuration = seconds;
2084 float Decorator::GetCursorBlinkDuration() const
2086 return mImpl->mCursorBlinkDuration;
2089 void Decorator::SetCursorWidth( int width )
2091 mImpl->mCursorWidth = static_cast<float>( width );
2094 int Decorator::GetCursorWidth() const
2096 return static_cast<int>( mImpl->mCursorWidth );
2101 void Decorator::SetHandleActive( HandleType handleType, bool active )
2103 mImpl->mHandle[handleType].active = active;
2107 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
2109 mImpl->mIsHandlePreviouslyCrossed = false;
2112 // TODO: this is a work-around.
2113 // The problem is the handle actor does not receive the touch event with the Interrupt
2114 // state when the power button is pressed and the application goes to background.
2115 mImpl->mHandle[handleType].pressed = false;
2116 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2117 ImageView imageView = mImpl->mHandle[handleType].actor;
2118 if( imageReleased && imageView )
2120 imageView.SetImage( mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED] );
2126 bool Decorator::IsHandleActive( HandleType handleType ) const
2128 return mImpl->mHandle[handleType].active ;
2131 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName )
2133 mImpl->SetHandleImage( handleType, handleImageType, imageFileName );
2136 const std::string& Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
2138 return mImpl->mHandleImages[handleType][handleImageType];
2141 void Decorator::SetHandleColor( const Vector4& color )
2143 mImpl->mHandleColor = color;
2146 const Vector4& Decorator::GetHandleColor() const
2148 return mImpl->mHandleColor;
2151 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
2153 // Adjust handle's displacement
2154 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2156 handle.position.x = x;
2157 handle.position.y = y;
2158 handle.lineHeight = height;
2160 if( mImpl->mSmoothHandlePanEnabled )
2162 handle.grabDisplacementX = 0.f;
2163 handle.grabDisplacementY = 0.f;
2167 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2169 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2171 x = handle.position.x;
2172 y = handle.position.y;
2173 height = handle.lineHeight;
2176 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2178 return mImpl->mHandle[handleType].position;
2181 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2183 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2186 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2188 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2191 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2193 mImpl->mFlipSelectionHandlesOnCross = enable;
2196 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2198 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2199 mImpl->mFlipLeftSelectionHandleDirection = left;
2200 mImpl->mFlipRightSelectionHandleDirection = right;
2203 void Decorator::AddHighlight( unsigned int index, const Vector4& quad )
2205 *( mImpl->mHighlightQuadList.Begin() + index ) = quad;
2208 void Decorator::SetHighLightBox( const Vector2& position, const Size& size, float outlineOffset )
2210 mImpl->mHighlightPosition = position;
2211 mImpl->mHighlightSize = size;
2212 mImpl->mHighlightOutlineOffset = outlineOffset;
2215 void Decorator::ClearHighlights()
2217 mImpl->mHighlightQuadList.Clear();
2218 mImpl->mHighlightPosition = Vector2::ZERO;
2219 mImpl->mHighlightOutlineOffset = 0.f;
2222 void Decorator::ResizeHighlightQuads( unsigned int numberOfQuads )
2224 mImpl->mHighlightQuadList.Resize( numberOfQuads );
2227 void Decorator::SetHighlightColor( const Vector4& color )
2229 mImpl->mHighlightColor = color;
2232 const Vector4& Decorator::GetHighlightColor() const
2234 return mImpl->mHighlightColor;
2237 void Decorator::SetHighlightActive( bool active )
2239 mImpl->mIsHighlightBoxActive = active;
2242 bool Decorator::IsHighlightActive() const
2244 return mImpl->mIsHighlightBoxActive;
2247 bool Decorator::IsHighlightVisible() const
2249 return ( mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent() );
2252 void Decorator::SetTextDepth( int textDepth )
2254 mImpl->mTextDepth = textDepth;
2257 void Decorator::SetPopupActive( bool active )
2259 mImpl->mActiveCopyPastePopup = active;
2262 bool Decorator::IsPopupActive() const
2264 return mImpl->mActiveCopyPastePopup;
2267 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2269 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2271 if ( !mImpl->mCopyPastePopup.actor )
2273 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2274 #ifdef DECORATOR_DEBUG
2275 mImpl->mCopyPastePopup.actor.SetProperty( Dali::Actor::Property::NAME,"mCopyPastePopup");
2277 mImpl->mCopyPastePopup.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
2278 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::SetPopupPosition ); // Position popup after size negotiation
2281 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2284 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2286 return mImpl->mEnabledPopupButtons;
2291 void Decorator::SetScrollThreshold( float threshold )
2293 mImpl->SetScrollThreshold( threshold );
2296 float Decorator::GetScrollThreshold() const
2298 return mImpl->GetScrollThreshold();
2301 void Decorator::SetScrollSpeed( float speed )
2303 mImpl->SetScrollSpeed( speed );
2306 float Decorator::GetScrollSpeed() const
2308 return mImpl->GetScrollSpeed();
2311 void Decorator::NotifyEndOfScroll()
2313 mImpl->NotifyEndOfScroll();
2316 void Decorator::SetHorizontalScrollEnabled( bool enable )
2318 mImpl->mHorizontalScrollingEnabled = enable;
2321 bool Decorator::IsHorizontalScrollEnabled() const
2323 return mImpl->mHorizontalScrollingEnabled;
2326 void Decorator::SetVerticalScrollEnabled( bool enable )
2328 mImpl->mVerticalScrollingEnabled = enable;
2331 bool Decorator::IsVerticalScrollEnabled() const
2333 return mImpl->mVerticalScrollingEnabled;
2336 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2338 mImpl->mSmoothHandlePanEnabled = enable;
2341 bool Decorator::IsSmoothHandlePanEnabled() const
2343 return mImpl->mSmoothHandlePanEnabled;
2346 Decorator::~Decorator()
2351 Decorator::Decorator( ControllerInterface& controller,
2352 TextSelectionPopupCallbackInterface& callbackInterface )
2355 mImpl = new Decorator::Impl( controller, callbackInterface );
2360 } // namespace Toolkit