2 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/decorator/text-decorator.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/actors/layer.h>
24 #include <dali/public-api/adaptor-framework/timer.h>
25 #include <dali/public-api/common/stage.h>
26 #include <dali/public-api/events/touch-data.h>
27 #include <dali/public-api/events/pan-gesture.h>
28 #include <dali/public-api/images/resource-image.h>
29 #include <dali/public-api/object/property-notification.h>
30 #include <dali/public-api/rendering/geometry.h>
31 #include <dali/public-api/rendering/renderer.h>
34 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
35 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
36 #include <dali-toolkit/internal/controls/image-view/image-view-impl.h>
39 #define DECORATOR_DEBUG
43 #define MAKE_SHADER(A)#A
47 const char* VERTEX_SHADER = MAKE_SHADER(
48 attribute mediump vec2 aPosition;
49 uniform mediump mat4 uMvpMatrix;
53 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
54 gl_Position = uMvpMatrix * position;
58 const char* FRAGMENT_SHADER = MAKE_SHADER(
59 uniform lowp vec4 uColor;
63 gl_FragColor = uColor;
74 #ifdef DECORATOR_DEBUG
75 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
85 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
86 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
87 const Dali::Vector3 ACTIVE_LAYER_ANCHOR_POINT( 0.5f, 0.5f, 0.5f );
89 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f ); // The text highlight color. TODO: due some problems, maybe with the blending function in the text clipping, the color is fully opaque.
91 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
93 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
94 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
95 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
97 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
98 const float SCROLL_THRESHOLD = 10.f; ///< Threshold in pixels close to the edges of the decorator boundaries from where the scroll timer starts to emit signals.
99 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
101 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
103 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
105 const float POPUP_PADDING = 2.f; ///< Padding space between the highlight box and the text's popup.
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 #ifdef DECORATOR_DEBUG
1152 mHighlightActor.SetName( "HighlightActor" );
1154 mHighlightActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
1155 mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
1156 mHighlightActor.SetColor( mHighlightColor );
1157 mHighlightActor.SetColorMode( USE_OWN_COLOR );
1160 // Add the highlight box telling the controller it needs clipping.
1161 mController.AddDecoration( mHighlightActor, true );
1164 void UpdateHighlight()
1166 if ( mHighlightActor )
1168 // Sets the position of the highlight actor inside the decorator.
1169 mHighlightActor.SetPosition( mHighlightPosition.x + mHighlightOutlineOffset,
1170 mHighlightPosition.y + mHighlightOutlineOffset );
1172 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1173 if( 0u != numberOfQuads )
1175 // Set the size of the highlighted text to the actor.
1176 mHighlightActor.SetSize( mHighlightSize );
1178 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1179 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1180 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1182 Vector<Vector2> vertices;
1183 Vector<unsigned short> indices;
1185 vertices.Reserve( 4u * numberOfQuads );
1186 indices.Reserve( 6u * numberOfQuads );
1188 // Index to the vertex.
1189 unsigned int v = 0u;
1191 // Traverse all quads.
1192 for( Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1193 endIt = mHighlightQuadList.End();
1197 const Vector4& quad = *it;
1202 vertex.x = quad.x - offsetX;
1203 vertex.y = quad.y - offsetY;
1204 vertices.PushBack( vertex );
1207 vertex.x = quad.z - offsetX;
1208 vertex.y = quad.y - offsetY;
1209 vertices.PushBack( vertex );
1211 // bottom-left (v+2)
1212 vertex.x = quad.x - offsetX;
1213 vertex.y = quad.w - offsetY;
1214 vertices.PushBack( vertex );
1216 // bottom-right (v+3)
1217 vertex.x = quad.z - offsetX;
1218 vertex.y = quad.w - offsetY;
1219 vertices.PushBack( vertex );
1221 // triangle A (3, 1, 0)
1222 indices.PushBack( v + 3 );
1223 indices.PushBack( v + 1 );
1224 indices.PushBack( v );
1226 // triangle B (0, 2, 3)
1227 indices.PushBack( v );
1228 indices.PushBack( v + 2 );
1229 indices.PushBack( v + 3 );
1232 if( ! mQuadVertices )
1234 mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
1237 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1239 if( !mQuadGeometry )
1241 mQuadGeometry = Geometry::New();
1242 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1244 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1246 if( !mHighlightRenderer )
1248 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1249 mHighlightActor.AddRenderer( mHighlightRenderer );
1253 mHighlightQuadList.Clear();
1255 if( mHighlightRenderer )
1257 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1262 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1264 if( Gesture::Started == gesture.state )
1266 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1268 handle.globalPosition.x = handle.position.x;
1269 handle.globalPosition.y = handle.position.y;
1272 handle.grabDisplacementX += gesture.displacement.x;
1273 handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.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( ( Gesture::Started == gesture.state ) ||
1280 ( Gesture::Continuing == gesture.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( ( Gesture::Finished == gesture.state ) ||
1323 ( Gesture::Cancelled == gesture.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 TouchData& 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 TouchData& 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 TouchData& 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::NotifyOnChanged );
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::NotifyOnChanged );
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::NotifyOnChanged );
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::NotifyOnChanged );
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::NotifyOnChanged );
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::NotifyOnChanged );
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::NotifyOnChanged );
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::NotifyOnChanged );
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.SetY( 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.SetY( 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::NotifyOnChanged );
1754 mPopupBottomExceedNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1756 mPopupTopExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1757 mPopupBottomExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1760 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1762 HandleImpl& handle = mHandle[handleType];
1763 handle.size = Size( image.GetWidth(), image.GetHeight() );
1765 mHandleImages[handleType][handleImageType] = image;
1768 void SetScrollThreshold( float threshold )
1770 mScrollThreshold = threshold;
1773 float GetScrollThreshold() const
1775 return mScrollThreshold;
1778 void SetScrollSpeed( float speed )
1780 mScrollSpeed = speed;
1781 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1784 float GetScrollSpeed() const
1786 return mScrollSpeed;
1789 void NotifyEndOfScroll()
1795 mNotifyEndOfScroll = true;
1800 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1802 * It only starts the timer if it's already created.
1804 void StartScrollTimer()
1808 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1809 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1812 if( !mScrollTimer.IsRunning() )
1814 mScrollTimer.Start();
1819 * Stops the timer used to scroll the text.
1821 void StopScrollTimer()
1825 mScrollTimer.Stop();
1830 * Callback called by the timer used to scroll the text.
1832 * It calculates and sets a new scroll position.
1834 bool OnScrollTimerTick()
1836 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1841 switch( mScrollDirection )
1845 x = mScrollDistance;
1850 x = -mScrollDistance;
1855 y = mScrollDistance;
1860 y = -mScrollDistance;
1867 mController.DecorationEvent( mHandleScrolling,
1876 ControllerInterface& mController;
1878 TapGestureDetector mTapDetector;
1879 PanGestureDetector mPanDetector;
1880 LongPressGestureDetector mLongPressDetector;
1882 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1883 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1885 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1886 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1887 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1888 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1889 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1890 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1891 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1892 Control mPrimaryCursor;
1893 Control mSecondaryCursor;
1895 Actor mHighlightActor; ///< Actor to display highlight
1896 Renderer mHighlightRenderer;
1897 Shader mHighlightShader; ///< Shader used for highlight
1898 Property::Map mQuadVertexFormat;
1899 PopupImpl mCopyPastePopup;
1900 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1901 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1903 Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1904 Vector4 mHandleColor;
1906 CursorImpl mCursor[CURSOR_COUNT];
1907 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1909 PropertyBuffer mQuadVertices;
1910 Geometry mQuadGeometry;
1911 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1913 Vector4 mBoundingBox; ///< The bounding box in world coords.
1914 Vector4 mHighlightColor; ///< Color of the highlight
1915 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1916 Size mHighlightSize; ///< The size of the highlighted text.
1917 Size mControlSize; ///< The control's size. Set by the Relayout.
1918 float mHighlightOutlineOffset; ///< The outline's offset.
1920 unsigned int mActiveCursor;
1921 unsigned int mCursorBlinkInterval;
1922 float mCursorBlinkDuration;
1923 float mCursorWidth; ///< The width of the cursors in pixels.
1924 HandleType mHandleScrolling; ///< The handle which is scrolling.
1925 HandleType mHandleReleased; ///< The last handle released.
1926 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1927 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1928 float mScrollSpeed; ///< The scroll speed in pixels per second.
1929 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1930 int mTextDepth; ///< The depth used to render the text.
1932 bool mActiveCopyPastePopup : 1;
1933 bool mPopupSetNewPosition : 1;
1934 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1935 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1936 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1937 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1938 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1939 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1940 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1941 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1942 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1943 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1944 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1945 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1946 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1947 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1948 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1951 DecoratorPtr Decorator::New( ControllerInterface& controller,
1952 TextSelectionPopupCallbackInterface& callbackInterface )
1954 return DecoratorPtr( new Decorator( controller,
1955 callbackInterface ) );
1958 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1960 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1963 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1965 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1968 void Decorator::Relayout( const Vector2& size )
1970 mImpl->Relayout( size );
1973 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1975 mImpl->UpdatePositions( scrollOffset );
1980 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1982 mImpl->mActiveCursor = activeCursor;
1985 unsigned int Decorator::GetActiveCursor() const
1987 return mImpl->mActiveCursor;
1990 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1992 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1994 cursorImpl.position.x = x;
1995 cursorImpl.position.y = y;
1996 cursorImpl.cursorHeight = cursorHeight;
1997 cursorImpl.lineHeight = lineHeight;
2000 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
2002 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2004 x = cursorImpl.position.x;
2005 y = cursorImpl.position.y;
2006 cursorHeight = cursorImpl.cursorHeight;
2007 lineHeight = cursorImpl.lineHeight;
2010 const Vector2& Decorator::GetPosition( Cursor cursor ) const
2012 return mImpl->mCursor[cursor].position;
2015 void Decorator::SetGlyphOffset( Cursor cursor, float glyphOffset )
2017 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2019 cursorImpl.glyphOffset = glyphOffset;
2022 const float Decorator::GetGlyphOffset( Cursor cursor) const
2024 return mImpl->mCursor[cursor].glyphOffset;
2027 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
2029 mImpl->mCursor[cursor].color = color;
2032 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
2034 return mImpl->mCursor[cursor].color;
2037 void Decorator::StartCursorBlink()
2039 if ( !mImpl->mCursorBlinkTimer )
2041 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
2042 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
2045 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
2047 mImpl->mCursorBlinkTimer.Start();
2051 void Decorator::StopCursorBlink()
2053 if ( mImpl->mCursorBlinkTimer )
2055 mImpl->mCursorBlinkTimer.Stop();
2058 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2061 void Decorator::DelayCursorBlink()
2063 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2064 mImpl->mDelayCursorBlink = true;
2067 void Decorator::SetCursorBlinkInterval( float seconds )
2069 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
2072 float Decorator::GetCursorBlinkInterval() const
2074 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
2077 void Decorator::SetCursorBlinkDuration( float seconds )
2079 mImpl->mCursorBlinkDuration = seconds;
2082 float Decorator::GetCursorBlinkDuration() const
2084 return mImpl->mCursorBlinkDuration;
2087 void Decorator::SetCursorWidth( int width )
2089 mImpl->mCursorWidth = static_cast<float>( width );
2092 int Decorator::GetCursorWidth() const
2094 return static_cast<int>( mImpl->mCursorWidth );
2099 void Decorator::SetHandleActive( HandleType handleType, bool active )
2101 mImpl->mHandle[handleType].active = active;
2105 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
2107 mImpl->mIsHandlePreviouslyCrossed = false;
2110 // TODO: this is a work-around.
2111 // The problem is the handle actor does not receive the touch event with the Interrupt
2112 // state when the power button is pressed and the application goes to background.
2113 mImpl->mHandle[handleType].pressed = false;
2114 Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
2115 ImageView imageView = mImpl->mHandle[handleType].actor;
2116 if( imageReleased && imageView )
2118 imageView.SetImage( imageReleased );
2124 bool Decorator::IsHandleActive( HandleType handleType ) const
2126 return mImpl->mHandle[handleType].active ;
2129 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
2131 mImpl->SetHandleImage( handleType, handleImageType, image );
2134 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
2136 return mImpl->mHandleImages[handleType][handleImageType];
2139 void Decorator::SetHandleColor( const Vector4& color )
2141 mImpl->mHandleColor = color;
2144 const Vector4& Decorator::GetHandleColor() const
2146 return mImpl->mHandleColor;
2149 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
2151 // Adjust handle's displacement
2152 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2154 handle.position.x = x;
2155 handle.position.y = y;
2156 handle.lineHeight = height;
2158 if( mImpl->mSmoothHandlePanEnabled )
2160 handle.grabDisplacementX = 0.f;
2161 handle.grabDisplacementY = 0.f;
2165 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2167 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2169 x = handle.position.x;
2170 y = handle.position.y;
2171 height = handle.lineHeight;
2174 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2176 return mImpl->mHandle[handleType].position;
2179 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2181 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2184 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2186 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2189 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2191 mImpl->mFlipSelectionHandlesOnCross = enable;
2194 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2196 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2197 mImpl->mFlipLeftSelectionHandleDirection = left;
2198 mImpl->mFlipRightSelectionHandleDirection = right;
2201 void Decorator::AddHighlight( unsigned int index, const Vector4& quad )
2203 *( mImpl->mHighlightQuadList.Begin() + index ) = quad;
2206 void Decorator::SetHighLightBox( const Vector2& position, const Size& size, float outlineOffset )
2208 mImpl->mHighlightPosition = position;
2209 mImpl->mHighlightSize = size;
2210 mImpl->mHighlightOutlineOffset = outlineOffset;
2213 void Decorator::ClearHighlights()
2215 mImpl->mHighlightQuadList.Clear();
2216 mImpl->mHighlightPosition = Vector2::ZERO;
2217 mImpl->mHighlightOutlineOffset = 0.f;
2220 void Decorator::ResizeHighlightQuads( unsigned int numberOfQuads )
2222 mImpl->mHighlightQuadList.Resize( numberOfQuads );
2225 void Decorator::SetHighlightColor( const Vector4& color )
2227 mImpl->mHighlightColor = color;
2230 const Vector4& Decorator::GetHighlightColor() const
2232 return mImpl->mHighlightColor;
2235 void Decorator::SetHighlightActive( bool active )
2237 mImpl->mIsHighlightBoxActive = active;
2240 bool Decorator::IsHighlightActive() const
2242 return mImpl->mIsHighlightBoxActive;
2245 bool Decorator::IsHighlightVisible() const
2247 return ( mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent() );
2250 void Decorator::SetTextDepth( int textDepth )
2252 mImpl->mTextDepth = textDepth;
2255 void Decorator::SetPopupActive( bool active )
2257 mImpl->mActiveCopyPastePopup = active;
2260 bool Decorator::IsPopupActive() const
2262 return mImpl->mActiveCopyPastePopup;
2265 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2267 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2269 if ( !mImpl->mCopyPastePopup.actor )
2271 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2272 #ifdef DECORATOR_DEBUG
2273 mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
2275 mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
2276 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::SetPopupPosition ); // Position popup after size negotiation
2279 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2282 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2284 return mImpl->mEnabledPopupButtons;
2289 void Decorator::SetScrollThreshold( float threshold )
2291 mImpl->SetScrollThreshold( threshold );
2294 float Decorator::GetScrollThreshold() const
2296 return mImpl->GetScrollThreshold();
2299 void Decorator::SetScrollSpeed( float speed )
2301 mImpl->SetScrollSpeed( speed );
2304 float Decorator::GetScrollSpeed() const
2306 return mImpl->GetScrollSpeed();
2309 void Decorator::NotifyEndOfScroll()
2311 mImpl->NotifyEndOfScroll();
2314 void Decorator::SetHorizontalScrollEnabled( bool enable )
2316 mImpl->mHorizontalScrollingEnabled = enable;
2319 bool Decorator::IsHorizontalScrollEnabled() const
2321 return mImpl->mHorizontalScrollingEnabled;
2324 void Decorator::SetVerticalScrollEnabled( bool enable )
2326 mImpl->mVerticalScrollingEnabled = enable;
2329 bool Decorator::IsVerticalScrollEnabled() const
2331 return mImpl->mVerticalScrollingEnabled;
2334 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2336 mImpl->mSmoothHandlePanEnabled = enable;
2339 bool Decorator::IsSmoothHandlePanEnabled() const
2341 return mImpl->mSmoothHandlePanEnabled;
2344 Decorator::~Decorator()
2349 Decorator::Decorator( ControllerInterface& controller,
2350 TextSelectionPopupCallbackInterface& callbackInterface )
2353 mImpl = new Decorator::Impl( controller, callbackInterface );
2358 } // namespace Toolkit