2 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/decorator/text-decorator.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/actors/layer.h>
24 #include <dali/public-api/adaptor-framework/timer.h>
25 #include <dali/public-api/common/stage.h>
26 #include <dali/public-api/events/touch-data.h>
27 #include <dali/public-api/events/pan-gesture.h>
28 #include <dali/public-api/images/resource-image.h>
29 #include <dali/public-api/object/property-notification.h>
30 #include <dali/public-api/rendering/geometry.h>
31 #include <dali/public-api/rendering/renderer.h>
34 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
35 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
36 #include <dali-toolkit/internal/controls/image-view/image-view-impl.h>
39 #define DECORATOR_DEBUG
43 #define MAKE_SHADER(A)#A
47 const char* VERTEX_SHADER = MAKE_SHADER(
48 attribute mediump vec2 aPosition;
49 uniform 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.SetPosition( cursor.position.x,
301 mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
303 mPrimaryCursor.SetVisible( 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.SetPosition( cursor.position.x,
316 mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
318 mSecondaryCursor.SetVisible( 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.SetVisible( 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.SetVisible( primaryVisible );
410 if( secondary.actor )
412 secondary.actor.SetVisible( 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.GetCurrentWorldPosition() - mActiveLayer.GetCurrentSize() * 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.GetCurrentWorldPosition() - mActiveLayer.GetCurrentSize() * 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.SetPosition( mCopyPastePopup.position );
655 mPopupSetNewPosition = false;
658 void CreateCursor( Control& cursor, const Vector4& color )
660 cursor = Control::New();
661 cursor.SetBackgroundColor( color );
662 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT );
663 cursor.SetAnchorPoint( 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.SetName( "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.SetName( "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.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
734 if ( mSecondaryCursor )
736 mSecondaryCursor.SetVisible( 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 = Layer::New();
771 #ifdef DECORATOR_DEBUG
772 mActiveLayer.SetName ( "ActiveLayerActor" );
775 mActiveLayer.SetParentOrigin( 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.SetSize( 0, handle.lineHeight );
793 void CreateGrabHandle()
795 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
796 if( !grabHandle.actor )
798 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
800 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
801 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
802 grabHandle.actor.SetAnchorPoint( 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.SetName( "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.SetName( "GrabArea" );
816 grabHandle.grabArea = Actor::New();
817 grabHandle.grabArea.SetName( "GrabArea" );
820 grabHandle.grabArea = Actor::New();
823 grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
824 grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
825 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
826 grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
827 grabHandle.actor.Add( grabHandle.grabArea );
828 grabHandle.actor.SetColor( mHandleColor );
830 grabHandle.grabArea.TouchSignal().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, Image& image, HandleType handleType )
855 handle.markerActor = ImageView::New( image );
856 handle.markerActor.SetColor( mHandleColor );
857 handle.actor.Add( handle.markerActor );
859 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
861 if( LEFT_SELECTION_HANDLE == handleType )
863 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
864 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
866 else if( RIGHT_SELECTION_HANDLE == handleType )
868 handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
869 handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
874 void CreateSelectionHandles()
876 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
879 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
881 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
882 #ifdef DECORATOR_DEBUG
883 primary.actor.SetName("SelectionHandleOne");
885 primary.actor.SetAnchorPoint( 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.SetColor( 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.SetName("SelectionHandleOneGrabArea");
893 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
894 primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
895 primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
896 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
898 primary.grabArea.TouchSignal().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] )
925 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
926 #ifdef DECORATOR_DEBUG
927 secondary.actor.SetName("SelectionHandleTwo");
929 secondary.actor.SetAnchorPoint( 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.SetColor( 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.SetName("SelectionHandleTwoGrabArea");
937 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
938 secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
939 secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
940 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
942 secondary.grabArea.TouchSignal().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.GetCurrentWorldPosition();
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.GetCurrentWorldPosition()
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 GetCurrentWorldPosition()
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.SetPosition( 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.GetCurrentWorldPosition()
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 GetCurrentWorldPosition()
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.SetAnchorPoint( 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.SetAnchorPoint( 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.SetPosition( 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] ? 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] ? 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.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
1145 void CreateHighlight()
1147 if( !mHighlightActor )
1149 mHighlightActor = Actor::New();
1151 mHighlightActor.SetName( "HighlightActor" );
1152 mHighlightActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
1153 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
1154 mHighlightActor.SetColor( mHighlightColor );
1155 mHighlightActor.SetColorMode( 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.SetPosition( 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.SetSize( 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 = PropertyBuffer::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 if( Gesture::Started == gesture.state )
1264 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1266 handle.globalPosition.x = handle.position.x;
1267 handle.globalPosition.y = handle.position.y;
1270 handle.grabDisplacementX += gesture.displacement.x;
1271 handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.displacement.y );
1273 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1274 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1275 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f );
1277 if( ( Gesture::Started == gesture.state ) ||
1278 ( Gesture::Continuing == gesture.state ) )
1281 mController.GetTargetSize( targetSize );
1283 if( mHorizontalScrollingEnabled &&
1284 ( x < mScrollThreshold ) )
1286 mScrollDirection = SCROLL_RIGHT;
1287 mHandleScrolling = type;
1290 else if( mHorizontalScrollingEnabled &&
1291 ( x > targetSize.width - mScrollThreshold ) )
1293 mScrollDirection = SCROLL_LEFT;
1294 mHandleScrolling = type;
1297 else if( mVerticalScrollingEnabled &&
1298 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1300 mScrollDirection = SCROLL_TOP;
1301 mHandleScrolling = type;
1304 else if( mVerticalScrollingEnabled &&
1305 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1307 mScrollDirection = SCROLL_BOTTOM;
1308 mHandleScrolling = type;
1313 mHandleScrolling = HANDLE_TYPE_COUNT;
1315 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1318 mIsHandlePanning = true;
1320 else if( ( Gesture::Finished == gesture.state ) ||
1321 ( Gesture::Cancelled == gesture.state ) )
1324 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1326 mNotifyEndOfScroll = false;
1327 mHandleScrolling = HANDLE_TYPE_COUNT;
1329 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1333 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1338 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1340 handle.pressed = false;
1342 mIsHandlePanning = false;
1346 void OnPan( Actor actor, const PanGesture& gesture )
1348 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1349 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1350 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1352 if( actor == grabHandle.grabArea )
1354 DoPan( grabHandle, GRAB_HANDLE, gesture );
1356 else if( actor == primarySelectionHandle.grabArea )
1358 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1360 else if( actor == secondarySelectionHandle.grabArea )
1362 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1366 bool OnGrabHandleTouched( Actor actor, const TouchData& touch )
1368 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1370 // Switch between pressed/release grab-handle images
1371 if( touch.GetPointCount() > 0 &&
1374 const PointState::Type state = touch.GetState( 0 );
1376 if( PointState::DOWN == state )
1378 grabHandle.pressed = true;
1380 else if( ( PointState::UP == state ) ||
1381 ( PointState::INTERRUPTED == state ) )
1383 grabHandle.pressed = false;
1386 SetHandleImage( GRAB_HANDLE );
1389 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1393 bool OnHandleOneTouched( Actor actor, const TouchData& touch )
1395 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1397 // Switch between pressed/release selection handle images
1398 if( touch.GetPointCount() > 0 &&
1399 primarySelectionHandle.actor )
1401 const PointState::Type state = touch.GetState( 0 );
1403 if( PointState::DOWN == state )
1405 primarySelectionHandle.pressed = true;
1406 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1408 else if( ( PointState::UP == state ) ||
1409 ( PointState::INTERRUPTED == state ) )
1411 primarySelectionHandle.pressed = false;
1412 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1413 mIsHandlePanning = false;
1414 mHandleReleased = LEFT_SELECTION_HANDLE;
1417 SetHandleImage( LEFT_SELECTION_HANDLE );
1420 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1424 bool OnHandleTwoTouched( Actor actor, const TouchData& touch )
1426 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1428 // Switch between pressed/release selection handle images
1429 if( touch.GetPointCount() > 0 &&
1430 secondarySelectionHandle.actor )
1432 const PointState::Type state = touch.GetState( 0 );
1434 if( PointState::DOWN == state )
1436 secondarySelectionHandle.pressed = true;
1437 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1439 else if( ( PointState::UP == state ) ||
1440 ( PointState::INTERRUPTED == state ) )
1442 secondarySelectionHandle.pressed = false;
1443 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1444 mIsHandlePanning = false;
1445 mHandleReleased = RIGHT_SELECTION_HANDLE;
1448 SetHandleImage( RIGHT_SELECTION_HANDLE );
1451 // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1455 void HandleResetPosition( PropertyNotification& source )
1457 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1459 if( grabHandle.active )
1461 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1462 SetGrabHandlePosition();
1464 // Sets the grab handle image according if it's pressed, flipped, etc.
1465 SetHandleImage( GRAB_HANDLE );
1469 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1470 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1472 // Sets the primary handle image according if it's pressed, flipped, etc.
1473 SetHandleImage( LEFT_SELECTION_HANDLE );
1475 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1476 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1478 // Sets the secondary handle image according if it's pressed, flipped, etc.
1479 SetHandleImage( RIGHT_SELECTION_HANDLE );
1483 void SetupActiveLayerPropertyNotifications()
1490 // Vertical notifications.
1492 // Disconnect any previous connected callback.
1493 if( mHandleVerticalLessThanNotification )
1495 mHandleVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1496 mActiveLayer.RemovePropertyNotification( mHandleVerticalLessThanNotification );
1499 if( mHandleVerticalGreaterThanNotification )
1501 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1502 mActiveLayer.RemovePropertyNotification( mHandleVerticalGreaterThanNotification );
1505 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1506 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1507 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1509 if( grabHandle.active )
1511 if( grabHandle.verticallyFlipped )
1513 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1514 mHandleVerticalGreaterThanNotification.Reset();
1516 // The vertical distance from the center of the active layer to the top edje of the display.
1517 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1519 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1520 LessThanCondition( mBoundingBox.y + topHeight ) );
1522 // Notifies the change from false to true and from true to false.
1523 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1525 // Connects the signals with the callbacks.
1526 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1530 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1531 mHandleVerticalLessThanNotification.Reset();
1533 // The vertical distance from the center of the active layer to the bottom edje of the display.
1534 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1536 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1537 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1539 // Notifies the change from false to true and from true to false.
1540 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1542 // Connects the signals with the callbacks.
1543 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1546 else // The selection handles are active
1548 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1550 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1551 mHandleVerticalGreaterThanNotification.Reset();
1553 // The vertical distance from the center of the active layer to the top edje of the display.
1554 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1556 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1557 LessThanCondition( mBoundingBox.y + topHeight ) );
1559 // Notifies the change from false to true and from true to false.
1560 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1562 // Connects the signals with the callbacks.
1563 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1565 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1567 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1568 mHandleVerticalLessThanNotification.Reset();
1570 // The vertical distance from the center of the active layer to the bottom edje of the display.
1571 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1572 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1574 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1575 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1577 // Notifies the change from false to true and from true to false.
1578 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1580 // Connects the signals with the callbacks.
1581 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1585 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1587 // The vertical distance from the center of the active layer to the top edje of the display.
1588 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1589 -primaryHandle.position.y + primaryHandle.size.height :
1590 -secondaryHandle.position.y + secondaryHandle.size.height );
1592 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1593 LessThanCondition( mBoundingBox.y + topHeight ) );
1595 // Notifies the change from false to true and from true to false.
1596 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1598 // Connects the signals with the callbacks.
1599 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1601 // The vertical distance from the center of the active layer to the bottom edje of the display.
1602 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1603 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1604 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1606 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1607 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1609 // Notifies the change from false to true and from true to false.
1610 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1612 // Connects the signals with the callbacks.
1613 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1617 // Horizontal notifications.
1619 // Disconnect any previous connected callback.
1620 if( mHandleHorizontalLessThanNotification )
1622 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1623 mActiveLayer.RemovePropertyNotification( mHandleHorizontalLessThanNotification );
1626 if( mHandleHorizontalGreaterThanNotification )
1628 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1629 mActiveLayer.RemovePropertyNotification( mHandleHorizontalGreaterThanNotification );
1632 if( primaryHandle.active || secondaryHandle.active )
1634 // The horizontal distance from the center of the active layer to the left edje of the display.
1635 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1636 -secondaryHandle.position.x + secondaryHandle.size.width );
1638 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1639 LessThanCondition( mBoundingBox.x + leftWidth ) );
1641 // Notifies the change from false to true and from true to false.
1642 mHandleHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1644 // Connects the signals with the callbacks.
1645 mHandleHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1647 // The horizontal distance from the center of the active layer to the right edje of the display.
1648 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1649 secondaryHandle.position.x + secondaryHandle.size.width );
1651 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1652 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1654 // Notifies the change from false to true and from true to false.
1655 mHandleHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1657 // Connects the signals with the callbacks.
1658 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1664 float AlternatePopUpPositionRelativeToCursor( bool topBottom )
1666 float alternativePosition = 0.0f;
1668 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1670 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1671 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1672 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1673 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1675 if( primaryHandle.active || secondaryHandle.active )
1677 float handleY = 0.f;
1678 float maxHandleHeight = 0.f;
1680 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1681 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1683 if( primaryVisible && secondaryVisible )
1685 handleY = std::max( primaryHandle.position.y, secondaryHandle.position.y );
1686 maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1688 else if( primaryVisible && !secondaryVisible )
1690 handleY = primaryHandle.position.y;
1691 maxHandleHeight = primaryHandle.size.height;
1693 else if( !primaryVisible && secondaryVisible )
1695 handleY = secondaryHandle.position.y;
1696 maxHandleHeight = secondaryHandle.size.height;
1699 alternativePosition = handleY + ( topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight );
1703 alternativePosition = cursor.position.y + ( topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height );
1706 return alternativePosition;
1709 void PopUpLeavesTopBoundary( PropertyNotification& source )
1711 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1713 // Sets the position of the popup below.
1714 mCopyPastePopup.actor.SetY( floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, true ) ) );
1717 void PopUpLeavesBottomBoundary( PropertyNotification& source )
1719 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1721 // Sets the position of the popup above.
1722 mCopyPastePopup.actor.SetY( floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, false ) ) );
1725 void SetUpPopupPositionNotifications( const Vector3& popupHalfSize )
1727 // Disconnect any previous connected callback.
1728 if( mPopupTopExceedNotification )
1730 mPopupTopExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1731 mCopyPastePopup.actor.RemovePropertyNotification( mPopupTopExceedNotification );
1734 if( mPopupBottomExceedNotification )
1736 mPopupBottomExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1737 mCopyPastePopup.actor.RemovePropertyNotification( mPopupBottomExceedNotification );
1740 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1742 // Exceeding vertical boundary
1744 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1745 LessThanCondition( mBoundingBox.y + popupHalfSize.height ) );
1747 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1748 GreaterThanCondition( mBoundingBox.w - popupHalfSize.height ) );
1750 // Notifies the change from false to true and from true to false.
1751 mPopupTopExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1752 mPopupBottomExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1754 mPopupTopExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1755 mPopupBottomExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1758 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1760 HandleImpl& handle = mHandle[handleType];
1761 handle.size = Size( image.GetWidth(), image.GetHeight() );
1763 mHandleImages[handleType][handleImageType] = image;
1766 void SetScrollThreshold( float threshold )
1768 mScrollThreshold = threshold;
1771 float GetScrollThreshold() const
1773 return mScrollThreshold;
1776 void SetScrollSpeed( float speed )
1778 mScrollSpeed = speed;
1779 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1782 float GetScrollSpeed() const
1784 return mScrollSpeed;
1787 void NotifyEndOfScroll()
1793 mNotifyEndOfScroll = true;
1798 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1800 * It only starts the timer if it's already created.
1802 void StartScrollTimer()
1806 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1807 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1810 if( !mScrollTimer.IsRunning() )
1812 mScrollTimer.Start();
1817 * Stops the timer used to scroll the text.
1819 void StopScrollTimer()
1823 mScrollTimer.Stop();
1828 * Callback called by the timer used to scroll the text.
1830 * It calculates and sets a new scroll position.
1832 bool OnScrollTimerTick()
1834 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1839 switch( mScrollDirection )
1843 x = mScrollDistance;
1848 x = -mScrollDistance;
1853 y = mScrollDistance;
1858 y = -mScrollDistance;
1865 mController.DecorationEvent( mHandleScrolling,
1874 ControllerInterface& mController;
1876 TapGestureDetector mTapDetector;
1877 PanGestureDetector mPanDetector;
1878 LongPressGestureDetector mLongPressDetector;
1880 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1881 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1883 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1884 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1885 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1886 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1887 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1888 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1889 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1890 Control mPrimaryCursor;
1891 Control mSecondaryCursor;
1893 Actor mHighlightActor; ///< Actor to display highlight
1894 Renderer mHighlightRenderer;
1895 Shader mHighlightShader; ///< Shader used for highlight
1896 Property::Map mQuadVertexFormat;
1897 PopupImpl mCopyPastePopup;
1898 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1899 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1901 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1902 Vector4 mHandleColor;
1904 CursorImpl mCursor[CURSOR_COUNT];
1905 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1907 PropertyBuffer mQuadVertices;
1908 Geometry mQuadGeometry;
1909 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1911 Vector4 mBoundingBox; ///< The bounding box in world coords.
1912 Vector4 mHighlightColor; ///< Color of the highlight
1913 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1914 Size mHighlightSize; ///< The size of the highlighted text.
1915 Size mControlSize; ///< The control's size. Set by the Relayout.
1916 float mHighlightOutlineOffset; ///< The outline's offset.
1918 unsigned int mActiveCursor;
1919 unsigned int mCursorBlinkInterval;
1920 float mCursorBlinkDuration;
1921 float mCursorWidth; ///< The width of the cursors in pixels.
1922 HandleType mHandleScrolling; ///< The handle which is scrolling.
1923 HandleType mHandleReleased; ///< The last handle released.
1924 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1925 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1926 float mScrollSpeed; ///< The scroll speed in pixels per second.
1927 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1928 int mTextDepth; ///< The depth used to render the text.
1930 bool mActiveCopyPastePopup : 1;
1931 bool mPopupSetNewPosition : 1;
1932 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1933 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1934 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1935 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1936 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1937 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1938 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1939 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1940 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1941 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1942 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1943 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1944 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1945 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1946 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1949 DecoratorPtr Decorator::New( ControllerInterface& controller,
1950 TextSelectionPopupCallbackInterface& callbackInterface )
1952 return DecoratorPtr( new Decorator( controller,
1953 callbackInterface ) );
1956 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1958 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1961 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1963 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1966 void Decorator::Relayout( const Vector2& size )
1968 mImpl->Relayout( size );
1971 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1973 mImpl->UpdatePositions( scrollOffset );
1978 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1980 mImpl->mActiveCursor = activeCursor;
1983 unsigned int Decorator::GetActiveCursor() const
1985 return mImpl->mActiveCursor;
1988 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1990 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1992 cursorImpl.position.x = x;
1993 cursorImpl.position.y = y;
1994 cursorImpl.cursorHeight = cursorHeight;
1995 cursorImpl.lineHeight = lineHeight;
1998 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
2000 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2002 x = cursorImpl.position.x;
2003 y = cursorImpl.position.y;
2004 cursorHeight = cursorImpl.cursorHeight;
2005 lineHeight = cursorImpl.lineHeight;
2008 const Vector2& Decorator::GetPosition( Cursor cursor ) const
2010 return mImpl->mCursor[cursor].position;
2013 void Decorator::SetGlyphOffset( Cursor cursor, float glyphOffset )
2015 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2017 cursorImpl.glyphOffset = glyphOffset;
2020 const float Decorator::GetGlyphOffset( Cursor cursor) const
2022 return mImpl->mCursor[cursor].glyphOffset;
2025 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
2027 mImpl->mCursor[cursor].color = color;
2030 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
2032 return mImpl->mCursor[cursor].color;
2035 void Decorator::StartCursorBlink()
2037 if ( !mImpl->mCursorBlinkTimer )
2039 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
2040 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
2043 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
2045 mImpl->mCursorBlinkTimer.Start();
2049 void Decorator::StopCursorBlink()
2051 if ( mImpl->mCursorBlinkTimer )
2053 mImpl->mCursorBlinkTimer.Stop();
2056 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2059 void Decorator::DelayCursorBlink()
2061 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2062 mImpl->mDelayCursorBlink = true;
2065 void Decorator::SetCursorBlinkInterval( float seconds )
2067 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
2070 float Decorator::GetCursorBlinkInterval() const
2072 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
2075 void Decorator::SetCursorBlinkDuration( float seconds )
2077 mImpl->mCursorBlinkDuration = seconds;
2080 float Decorator::GetCursorBlinkDuration() const
2082 return mImpl->mCursorBlinkDuration;
2085 void Decorator::SetCursorWidth( int width )
2087 mImpl->mCursorWidth = static_cast<float>( width );
2090 int Decorator::GetCursorWidth() const
2092 return static_cast<int>( mImpl->mCursorWidth );
2097 void Decorator::SetHandleActive( HandleType handleType, bool active )
2099 mImpl->mHandle[handleType].active = active;
2103 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
2105 mImpl->mIsHandlePreviouslyCrossed = false;
2108 // TODO: this is a work-around.
2109 // The problem is the handle actor does not receive the touch event with the Interrupt
2110 // state when the power button is pressed and the application goes to background.
2111 mImpl->mHandle[handleType].pressed = false;
2112 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
2113 ImageView imageView = mImpl->mHandle[handleType].actor;
2114 if( imageReleased && imageView )
2116 imageView.SetImage( imageReleased );
2122 bool Decorator::IsHandleActive( HandleType handleType ) const
2124 return mImpl->mHandle[handleType].active ;
2127 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
2129 mImpl->SetHandleImage( handleType, handleImageType, image );
2132 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
2134 return mImpl->mHandleImages[handleType][handleImageType];
2137 void Decorator::SetHandleColor( const Vector4& color )
2139 mImpl->mHandleColor = color;
2142 const Vector4& Decorator::GetHandleColor() const
2144 return mImpl->mHandleColor;
2147 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
2149 // Adjust handle's displacement
2150 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2152 handle.position.x = x;
2153 handle.position.y = y;
2154 handle.lineHeight = height;
2156 if( mImpl->mSmoothHandlePanEnabled )
2158 handle.grabDisplacementX = 0.f;
2159 handle.grabDisplacementY = 0.f;
2163 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2165 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2167 x = handle.position.x;
2168 y = handle.position.y;
2169 height = handle.lineHeight;
2172 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2174 return mImpl->mHandle[handleType].position;
2177 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2179 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2182 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2184 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2187 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2189 mImpl->mFlipSelectionHandlesOnCross = enable;
2192 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2194 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2195 mImpl->mFlipLeftSelectionHandleDirection = left;
2196 mImpl->mFlipRightSelectionHandleDirection = right;
2199 void Decorator::AddHighlight( unsigned int index, const Vector4& quad )
2201 *( mImpl->mHighlightQuadList.Begin() + index ) = quad;
2204 void Decorator::SetHighLightBox( const Vector2& position, const Size& size, float outlineOffset )
2206 mImpl->mHighlightPosition = position;
2207 mImpl->mHighlightSize = size;
2208 mImpl->mHighlightOutlineOffset = outlineOffset;
2211 void Decorator::ClearHighlights()
2213 mImpl->mHighlightQuadList.Clear();
2214 mImpl->mHighlightPosition = Vector2::ZERO;
2215 mImpl->mHighlightOutlineOffset = 0.f;
2218 void Decorator::ResizeHighlightQuads( unsigned int numberOfQuads )
2220 mImpl->mHighlightQuadList.Resize( numberOfQuads );
2223 void Decorator::SetHighlightColor( const Vector4& color )
2225 mImpl->mHighlightColor = color;
2228 const Vector4& Decorator::GetHighlightColor() const
2230 return mImpl->mHighlightColor;
2233 void Decorator::SetHighlightActive( bool active )
2235 mImpl->mIsHighlightBoxActive = active;
2238 bool Decorator::IsHighlightActive() const
2240 return mImpl->mIsHighlightBoxActive;
2243 bool Decorator::IsHighlightVisible() const
2245 return ( mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent() );
2248 void Decorator::SetTextDepth( int textDepth )
2250 mImpl->mTextDepth = textDepth;
2253 void Decorator::SetPopupActive( bool active )
2255 mImpl->mActiveCopyPastePopup = active;
2258 bool Decorator::IsPopupActive() const
2260 return mImpl->mActiveCopyPastePopup;
2263 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2265 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2267 if ( !mImpl->mCopyPastePopup.actor )
2269 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2270 #ifdef DECORATOR_DEBUG
2271 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
2273 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
2274 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::SetPopupPosition ); // Position popup after size negotiation
2277 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2280 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2282 return mImpl->mEnabledPopupButtons;
2287 void Decorator::SetScrollThreshold( float threshold )
2289 mImpl->SetScrollThreshold( threshold );
2292 float Decorator::GetScrollThreshold() const
2294 return mImpl->GetScrollThreshold();
2297 void Decorator::SetScrollSpeed( float speed )
2299 mImpl->SetScrollSpeed( speed );
2302 float Decorator::GetScrollSpeed() const
2304 return mImpl->GetScrollSpeed();
2307 void Decorator::NotifyEndOfScroll()
2309 mImpl->NotifyEndOfScroll();
2312 void Decorator::SetHorizontalScrollEnabled( bool enable )
2314 mImpl->mHorizontalScrollingEnabled = enable;
2317 bool Decorator::IsHorizontalScrollEnabled() const
2319 return mImpl->mHorizontalScrollingEnabled;
2322 void Decorator::SetVerticalScrollEnabled( bool enable )
2324 mImpl->mVerticalScrollingEnabled = enable;
2327 bool Decorator::IsVerticalScrollEnabled() const
2329 return mImpl->mVerticalScrollingEnabled;
2332 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2334 mImpl->mSmoothHandlePanEnabled = enable;
2337 bool Decorator::IsSmoothHandlePanEnabled() const
2339 return mImpl->mSmoothHandlePanEnabled;
2342 Decorator::~Decorator()
2347 Decorator::Decorator( ControllerInterface& controller,
2348 TextSelectionPopupCallbackInterface& callbackInterface )
2351 mImpl = new Decorator::Impl( controller, callbackInterface );
2356 } // namespace Toolkit