2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/decorator/text-decorator.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/actors/layer.h>
24 #include <dali/public-api/adaptor-framework/timer.h>
25 #include <dali/devel-api/common/stage.h>
26 #include <dali/public-api/events/touch-event.h>
27 #include <dali/public-api/events/pan-gesture.h>
28 #include <dali/public-api/object/property-notification.h>
29 #include <dali/public-api/rendering/geometry.h>
30 #include <dali/public-api/rendering/renderer.h>
31 #include <dali/devel-api/adaptor-framework/image-loading.h>
34 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
35 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
36 #include <dali-toolkit/internal/controls/image-view/image-view-impl.h>
39 #define DECORATOR_DEBUG
43 #define MAKE_SHADER(A)#A
47 const char* VERTEX_SHADER = MAKE_SHADER(
48 attribute mediump vec2 aPosition;
49 uniform highp mat4 uMvpMatrix;
53 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
54 gl_Position = uMvpMatrix * position;
58 const char* FRAGMENT_SHADER = MAKE_SHADER(
59 uniform lowp vec4 uColor;
63 gl_FragColor = uColor;
74 #ifdef DECORATOR_DEBUG
75 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
85 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
86 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
87 const Dali::Vector3 ACTIVE_LAYER_ANCHOR_POINT( 0.5f, 0.5f, 0.5f );
89 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f ); // The text highlight color. TODO: due some problems, maybe with the blending function in the text clipping, the color is fully opaque.
91 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
93 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
94 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
95 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
97 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
98 const float SCROLL_THRESHOLD = 10.f; ///< Threshold in pixels close to the edges of the decorator boundaries from where the scroll timer starts to emit signals.
99 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
101 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
103 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
105 const float POPUP_PADDING = 2.f; ///< Padding space between the highlight box and the text's popup.
107 typedef Dali::Vector<Dali::Vector4> QuadContainer;
110 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
111 * @param[in] boundingRectangle local bounding
112 * @param[out] Vector4 World coordinate bounding Box.
114 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
116 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
117 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
119 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
120 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
122 boundingBox = Dali::Vector4( originX,
124 originX + boundingRectangle.width,
125 originY + boundingRectangle.height );
128 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
130 // Convert to local coordinates and store as a Dali::Rect.
131 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
133 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
134 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
135 boundingRectangle.width = boundingBox.z - boundingBox.x;
136 boundingRectangle.height = boundingBox.w - boundingBox.y;
139 } // end of namespace
150 struct Decorator::Impl : public ConnectionTracker
164 : color( Dali::Color::BLACK ),
166 cursorHeight( 0.0f ),
186 grabDisplacementX( 0.f ),
187 grabDisplacementY( 0.f ),
189 horizontallyVisible( false ),
190 verticallyVisible( false ),
192 verticallyFlippedPreferred( false ),
193 horizontallyFlipped( false ),
194 verticallyFlipped( false ),
195 verticallyFlippedOnTouch( false )
201 ImageView markerActor;
204 Vector2 globalPosition;
206 float lineHeight; ///< Not the handle height
207 float grabDisplacementX;
208 float grabDisplacementY;
210 bool horizontallyVisible : 1;
211 bool verticallyVisible : 1;
213 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
214 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
215 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
216 bool verticallyFlippedOnTouch : 1; ///< Whether the handle is vertically flipped on touch.
226 TextSelectionPopup actor;
230 Impl( ControllerInterface& controller,
231 TextSelectionPopupCallbackInterface& callbackInterface )
232 : mController( controller ),
233 mEnabledPopupButtons( TextSelectionPopup::NONE ),
234 mTextSelectionPopupCallbackInterface( callbackInterface ),
235 mHandleColor( HANDLE_COLOR ),
237 mHighlightColor( LIGHT_BLUE ),
238 mHighlightPosition( Vector2::ZERO ),
239 mHighlightSize( Vector2::ZERO ),
240 mControlSize( Vector2::ZERO ),
241 mHighlightOutlineOffset( 0.f ),
242 mActiveCursor( ACTIVE_CURSOR_NONE ),
243 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
244 mCursorBlinkDuration( 0.0f ),
245 mCursorWidth( CURSOR_WIDTH ),
246 mHandleScrolling( HANDLE_TYPE_COUNT ),
247 mHandleReleased( HANDLE_TYPE_COUNT ),
248 mScrollDirection( SCROLL_NONE ),
249 mScrollThreshold( SCROLL_THRESHOLD ),
250 mScrollSpeed( SCROLL_SPEED ),
251 mScrollDistance( SCROLL_DISTANCE ),
253 mActiveCopyPastePopup( false ),
254 mPopupSetNewPosition( true ),
255 mCursorBlinkStatus( true ),
256 mDelayCursorBlink( false ),
257 mPrimaryCursorVisible( false ),
258 mSecondaryCursorVisible( false ),
259 mFlipSelectionHandlesOnCross( false ),
260 mFlipLeftSelectionHandleDirection( false ),
261 mFlipRightSelectionHandleDirection( false ),
262 mIsHandlePanning( false ),
263 mIsHandleCurrentlyCrossed( false ),
264 mIsHandlePreviouslyCrossed( false ),
265 mNotifyEndOfScroll( false ),
266 mHorizontalScrollingEnabled( false ),
267 mVerticalScrollingEnabled( false ),
268 mSmoothHandlePanEnabled( false ),
269 mIsHighlightBoxActive( false ),
270 mHidePrimaryCursorAndGrabHandle( false )
272 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
273 mHighlightShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
278 * Relayout of the decorations owned by the decorator.
279 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
281 void Relayout( const Vector2& size )
285 // TODO - Remove this if nothing is active
288 // Show or hide the cursors
293 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
294 mPrimaryCursorVisible = (!mHidePrimaryCursorAndGrabHandle) && ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
295 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
296 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
297 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
298 if( mPrimaryCursorVisible )
300 mPrimaryCursor.SetProperty( Actor::Property::POSITION, Vector2( cursor.position.x,
301 cursor.position.y ) );
302 mPrimaryCursor.SetProperty( Actor::Property::SIZE, Size( mCursorWidth, cursor.cursorHeight ) );
304 mPrimaryCursor.SetProperty( Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus );
306 if( mSecondaryCursor )
308 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
309 mSecondaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
310 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
311 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
312 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
313 if( mSecondaryCursorVisible )
315 mSecondaryCursor.SetProperty( Actor::Property::POSITION, Vector2( cursor.position.x,
316 cursor.position.y ) );
317 mSecondaryCursor.SetProperty( Actor::Property::SIZE, Size( mCursorWidth, cursor.cursorHeight ) );
319 mSecondaryCursor.SetProperty( Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus );
322 // Show or hide the grab handle
323 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
324 bool newGrabHandlePosition = false;
325 grabHandle.horizontallyVisible = false;
326 grabHandle.verticallyVisible = false;
327 if( grabHandle.active )
329 grabHandle.horizontallyVisible = ( ( mControlSize.width - ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) ) > -Math::MACHINE_EPSILON_1000 ) &&
330 ( grabHandle.position.x > -Math::MACHINE_EPSILON_1000 ) );
331 grabHandle.verticallyVisible = ( ( ( mControlSize.height - grabHandle.lineHeight ) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) &&
332 ( grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) );
334 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible && (!mHidePrimaryCursorAndGrabHandle);
339 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
340 SetGrabHandlePosition();
342 // Sets the grab handle image according if it's pressed, flipped, etc.
343 SetHandleImage( GRAB_HANDLE );
345 newGrabHandlePosition = true;
348 if( grabHandle.actor )
350 grabHandle.actor.SetProperty( Actor::Property::VISIBLE, isVisible );
353 else if( grabHandle.actor )
355 grabHandle.actor.Unparent();
358 // Show or hide the selection handles/highlight
359 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
360 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
361 bool newPrimaryHandlePosition = false;
362 bool newSecondaryHandlePosition = false;
364 primary.horizontallyVisible = ( ( mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
365 ( primary.position.x > -Math::MACHINE_EPSILON_1000 ) );
366 primary.verticallyVisible = ( ( ( mControlSize.height - primary.lineHeight ) - primary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
367 ( primary.position.y + ( primary.verticallyFlipped ? 0.f : primary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
368 secondary.horizontallyVisible = ( ( mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
369 ( secondary.position.x > -Math::MACHINE_EPSILON_1000 ) );
370 secondary.verticallyVisible = ( ( ( mControlSize.height - secondary.lineHeight ) - secondary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
371 ( secondary.position.y + ( secondary.verticallyFlipped ? 0.f : secondary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
373 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
374 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
376 if( primary.active || secondary.active )
378 if( primaryVisible || secondaryVisible )
380 CreateSelectionHandles();
384 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
386 // Sets the primary handle image according if it's pressed, flipped, etc.
387 SetHandleImage( LEFT_SELECTION_HANDLE );
389 SetSelectionHandleMarkerSize( primary );
391 newPrimaryHandlePosition = true;
394 if( secondaryVisible )
396 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
398 // Sets the secondary handle image according if it's pressed, flipped, etc.
399 SetHandleImage( RIGHT_SELECTION_HANDLE );
401 SetSelectionHandleMarkerSize( secondary );
403 newSecondaryHandlePosition = true;
409 primary.actor.SetProperty( Actor::Property::VISIBLE, primaryVisible );
411 if( secondary.actor )
413 secondary.actor.SetProperty( Actor::Property::VISIBLE, secondaryVisible );
421 primary.actor.Unparent();
423 if( secondary.actor )
425 secondary.actor.Unparent();
429 if( mIsHighlightBoxActive )
436 if( mHighlightActor )
438 mHighlightActor.Unparent();
442 if( newGrabHandlePosition ||
443 newPrimaryHandlePosition ||
444 newSecondaryHandlePosition )
446 // Setup property notifications to find whether the handles leave the boundaries of the current display.
447 SetupActiveLayerPropertyNotifications();
450 if( mActiveCopyPastePopup &&
451 ( primaryVisible || secondaryVisible ) )
454 mPopupSetNewPosition = true;
458 if( mCopyPastePopup.actor )
460 mCopyPastePopup.actor.HidePopup();
461 mPopupSetNewPosition = true;
466 void UpdatePositions( const Vector2& scrollOffset )
468 mCursor[PRIMARY_CURSOR].position += scrollOffset;
469 mCursor[SECONDARY_CURSOR].position += scrollOffset;
470 mHandle[ GRAB_HANDLE ].position += scrollOffset;
471 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
472 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
473 mHighlightPosition += scrollOffset;
478 if( !mCopyPastePopup.actor )
483 if( !mCopyPastePopup.actor.GetParent() )
485 mActiveLayer.Add( mCopyPastePopup.actor );
488 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
489 mCopyPastePopup.actor.ShowPopup();
492 float CalculateVerticalPopUpPosition( float halfHeight, bool preferBelow )
494 float yPosition = 0.f;
496 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
497 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
498 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
500 if( primaryHandle.active || secondaryHandle.active )
502 // The origin of the decorator's coordinate system in world coords.
503 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION ) - mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ) * ACTIVE_LAYER_ANCHOR_POINT;
507 // Find out if there is enough space for the popup at the bottom.
508 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
509 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
511 float maxY = std::max( primaryBelowY, secondaryBelowY );
513 yPosition = halfHeight + maxY;
515 if( originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w )
517 // Does not fit below.
519 // Try to fit first below the non active handle. Otherwise above the active handle.
520 if( RIGHT_SELECTION_HANDLE == mHandleReleased )
522 if( primaryBelowY < secondaryBelowY )
524 yPosition = halfHeight + primaryBelowY;
528 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
531 else if( LEFT_SELECTION_HANDLE == mHandleReleased )
533 if( secondaryBelowY < primaryBelowY )
535 yPosition = halfHeight + secondaryBelowY;
539 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
543 // Check the handle is whithin the decoration box.
544 if( originWorldCoords.y + yPosition < mBoundingBox.y )
546 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
549 if( originWorldCoords.y + yPosition > mBoundingBox.w )
551 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
557 // Find out if there is enough space for the popup at the top.
558 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
559 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
561 float minY = std::min( primaryTopY, secondaryTopY );
563 yPosition = -halfHeight + minY;
565 } // ( primaryHandle.active || secondaryHandle.active )
566 else if( grabHandle.active )
570 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
574 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
581 void ConstrainPopupPosition( const Vector3& popupHalfSize )
583 // Check if the popup is within the boundaries of the decoration box.
585 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
587 // The origin of the decorator's coordinate system in world coords.
588 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION ) - mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ) * ACTIVE_LAYER_ANCHOR_POINT;
590 // The popup's position in world coords.
591 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
593 if( popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x )
595 mCopyPastePopup.position.x += mBoundingBox.x - ( popupPositionWorldCoords.x - popupHalfSize.width );
597 else if( popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z )
599 mCopyPastePopup.position.x += mBoundingBox.z - ( popupPositionWorldCoords.x + popupHalfSize.width );
602 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
603 if( popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y )
605 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition( popupHalfSize.height, true ); // true -> prefer to set the popup's position below.
609 void SetPopupPosition( Actor actor )
611 if( !mActiveCopyPastePopup )
616 // Retrieves the popup's size after relayout.
617 const Vector3 popupSize( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
618 const Vector3 popupHalfSize = popupSize * 0.5f;
620 if( mPopupSetNewPosition )
622 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
623 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
624 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
626 if( primaryHandle.active || secondaryHandle.active )
628 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
629 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
631 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
633 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - ( primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING );
634 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - ( secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING );
636 mCopyPastePopup.position.y = std::min( primaryY, secondaryY );
638 else if( grabHandle.active )
640 mCopyPastePopup.position.x = grabHandle.position.x;
642 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - ( grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING );
644 } // mPopupSetNewPosition
646 // It may change the popup's position to fit within the decoration box.
647 ConstrainPopupPosition( popupHalfSize );
649 SetUpPopupPositionNotifications( popupHalfSize );
651 // Prevent pixel mis-alignment by rounding down.
652 mCopyPastePopup.position.x = floorf( mCopyPastePopup.position.x );
653 mCopyPastePopup.position.y = floorf( mCopyPastePopup.position.y );
655 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION, mCopyPastePopup.position );
656 mPopupSetNewPosition = false;
659 void CreateCursor( Control& cursor, const Vector4& color )
661 cursor = Control::New();
662 cursor.SetBackgroundColor( color );
663 cursor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
664 cursor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
667 // Add or Remove cursor(s) from parent
670 if( mActiveCursor == ACTIVE_CURSOR_NONE )
674 mPrimaryCursor.Unparent();
676 if( mSecondaryCursor )
678 mSecondaryCursor.Unparent();
683 // Create Primary and or Secondary Cursor(s) if active and add to parent
684 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
685 mActiveCursor == ACTIVE_CURSOR_BOTH )
687 if ( !mPrimaryCursor )
689 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
690 #ifdef DECORATOR_DEBUG
691 mPrimaryCursor.SetProperty( Dali::Actor::Property::NAME, "PrimaryCursorActor" );
695 if( !mPrimaryCursor.GetParent() )
697 mActiveLayer.Add( mPrimaryCursor );
701 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
703 if ( !mSecondaryCursor )
705 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
706 #ifdef DECORATOR_DEBUG
707 mSecondaryCursor.SetProperty( Dali::Actor::Property::NAME, "SecondaryCursorActor" );
711 if( !mSecondaryCursor.GetParent() )
713 mActiveLayer.Add( mSecondaryCursor );
718 if( mSecondaryCursor )
720 mSecondaryCursor.Unparent();
726 bool OnCursorBlinkTimerTick()
728 if( !mDelayCursorBlink )
731 if ( mPrimaryCursor )
733 mPrimaryCursor.SetProperty( Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus );
735 if ( mSecondaryCursor )
737 mSecondaryCursor.SetProperty( Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus );
740 mCursorBlinkStatus = !mCursorBlinkStatus;
745 mDelayCursorBlink = false;
753 // Will consume tap gestures on handles.
754 mTapDetector = TapGestureDetector::New();
756 // Will consume double tap gestures on handles.
757 mTapDetector.SetMaximumTapsRequired( 2u );
759 // Will consume long press gestures on handles.
760 mLongPressDetector = LongPressGestureDetector::New();
762 // Detects pan gestures on handles.
763 mPanDetector = PanGestureDetector::New();
764 mPanDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
767 void CreateActiveLayer()
771 mActiveLayer = Actor::New();
772 #ifdef DECORATOR_DEBUG
773 mActiveLayer.SetProperty( Actor::Property::NAME, "ActiveLayerActor" );
776 mActiveLayer.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
777 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
779 // Add the active layer telling the controller it doesn't need clipping.
780 mController.AddDecoration( mActiveLayer, false );
783 mActiveLayer.RaiseToTop();
786 void SetSelectionHandleMarkerSize( HandleImpl& handle )
788 if( handle.markerActor )
790 handle.markerActor.SetProperty( Actor::Property::SIZE, Vector2( 0, handle.lineHeight ) );
794 void CreateGrabHandle()
796 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
797 if( !grabHandle.actor )
799 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED].size() )
801 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
802 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
803 grabHandle.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
805 // Area that Grab handle responds to, larger than actual handle so easier to move
806 #ifdef DECORATOR_DEBUG
807 grabHandle.actor.SetProperty( Dali::Actor::Property::NAME, "GrabHandleActor" );
808 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
810 grabHandle.grabArea = Control::New();
811 Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
812 control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
813 grabHandle.grabArea.SetProperty( Dali::Actor::Property::NAME, "GrabArea" );
817 grabHandle.grabArea = Actor::New();
818 grabHandle.grabArea.SetProperty( Dali::Actor::Property::NAME, "GrabArea" );
821 grabHandle.grabArea = Actor::New();
824 grabHandle.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
825 grabHandle.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
826 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
827 grabHandle.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
828 grabHandle.actor.Add( grabHandle.grabArea );
829 grabHandle.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
831 grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
833 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
834 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
835 mTapDetector.Attach( grabHandle.actor );
836 mLongPressDetector.Attach( grabHandle.actor );
838 // The grab handle's area is attached to the pan detector.
839 // The OnPan() method is connected to the signals emitted by the pan detector.
840 mPanDetector.Attach( grabHandle.grabArea );
842 mActiveLayer.Add( grabHandle.actor );
846 if( grabHandle.actor && !grabHandle.actor.GetParent() )
848 mActiveLayer.Add( grabHandle.actor );
852 void CreateHandleMarker( HandleImpl& handle, const std::string& image, HandleType handleType )
856 handle.markerActor = ImageView::New( image );
857 handle.markerActor.SetProperty( Actor::Property::COLOR, mHandleColor );
858 handle.actor.Add( handle.markerActor );
860 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
862 if( LEFT_SELECTION_HANDLE == handleType )
864 handle.markerActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT );
865 handle.markerActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_RIGHT );
867 else if( RIGHT_SELECTION_HANDLE == handleType )
869 handle.markerActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT );
870 handle.markerActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
875 void CreateSelectionHandles()
877 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
880 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size() )
882 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
883 #ifdef DECORATOR_DEBUG
884 primary.actor.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleOne");
886 primary.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
887 GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
888 primary.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
890 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
891 #ifdef DECORATOR_DEBUG
892 primary.grabArea.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleOneGrabArea");
894 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
895 primary.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
896 primary.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
897 primary.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
899 primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
901 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
902 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
903 mTapDetector.Attach( primary.actor );
904 mLongPressDetector.Attach( primary.actor );
906 // The handle's area is attached to the pan detector.
907 // The OnPan() method is connected to the signals emitted by the pan detector.
908 mPanDetector.Attach( primary.grabArea );
910 primary.actor.Add( primary.grabArea );
912 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
916 if( primary.actor && !primary.actor.GetParent() )
918 mActiveLayer.Add( primary.actor );
921 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
922 if( !secondary.actor )
924 if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size() )
926 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
927 #ifdef DECORATOR_DEBUG
928 secondary.actor.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleTwo");
930 secondary.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
931 GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
932 secondary.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
934 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
935 #ifdef DECORATOR_DEBUG
936 secondary.grabArea.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleTwoGrabArea");
938 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
939 secondary.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
940 secondary.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
941 secondary.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
943 secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
945 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
946 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
947 mTapDetector.Attach( secondary.actor );
948 mLongPressDetector.Attach( secondary.actor );
950 // The handle's area is attached to the pan detector.
951 // The OnPan() method is connected to the signals emitted by the pan detector.
952 mPanDetector.Attach( secondary.grabArea );
954 secondary.actor.Add( secondary.grabArea );
956 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
960 if( secondary.actor && !secondary.actor.GetParent() )
962 mActiveLayer.Add( secondary.actor );
966 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
968 // Gets the world position of the active layer. The active layer is where the handles are added.
969 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION );
971 // The grab handle position in world coords.
972 // The active layer's world position is the center of the active layer. The origin of the
973 // coord system of the handles is the top left of the active layer.
974 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f );
975 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f );
978 void SetGrabHandlePosition()
980 // Reference to the grab handle.
981 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
983 // Transforms the handle position into world coordinates.
984 // @note This is not the same value as grabHandle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
985 // as it's transforming the handle's position set by the text-controller and not
986 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
987 // retrieves the position of the center of the actor but the handle's position set
988 // by the text controller is not the center of the actor.
989 Vector2 grabHandleWorldPosition;
990 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
992 // Check if the grab handle exceeds the boundaries of the decoration box.
993 // At the moment only the height is checked for the grab handle.
995 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
996 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
997 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
999 // The grab handle 'y' position in local coords.
1000 // If the grab handle exceeds the bottom of the decoration box,
1001 // set the 'y' position to the top of the line.
1002 // The SetGrabHandleImage() method will change the orientation.
1003 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
1005 if( grabHandle.actor )
1007 grabHandle.actor.SetProperty( Actor::Property::POSITION, Vector2( grabHandle.position.x + floor( 0.5f * mCursorWidth ) + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f ),
1008 yLocalPosition + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f ) ) );
1012 void SetSelectionHandlePosition( HandleType type )
1014 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
1016 // Reference to the selection handle.
1017 HandleImpl& handle = mHandle[type];
1019 // Transforms the handle position into world coordinates.
1020 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1021 // as it's transforming the handle's position set by the text-controller and not
1022 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1023 // retrieves the position of the center of the actor but the handle's position set
1024 // by the text controller is not the center of the actor.
1025 Vector2 handleWorldPosition;
1026 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
1028 // Whether to flip the handle (horizontally).
1029 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1031 // Whether to flip the handles if they are crossed.
1032 bool crossFlip = false;
1033 if( mFlipSelectionHandlesOnCross || !mIsHandlePanning )
1035 crossFlip = mIsHandleCurrentlyCrossed;
1038 // Whether the handle was crossed before start the panning.
1039 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1041 // Does not flip if both conditions are true (double flip)
1042 flipHandle = flipHandle != ( crossFlip || isHandlePreviouslyCrossed );
1044 // Will flip the handles vertically if the user prefers it.
1045 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1047 if( crossFlip || isHandlePreviouslyCrossed )
1049 if( isPrimaryHandle )
1051 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1055 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1059 // Check if the selection handle exceeds the boundaries of the decoration box.
1060 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
1061 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
1063 // Does not flip if both conditions are true (double flip)
1064 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
1068 if( handle.actor && !handle.horizontallyFlipped )
1070 // Change the anchor point to flip the image.
1071 handle.actor.SetProperty( Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
1073 handle.horizontallyFlipped = true;
1078 if( handle.actor && handle.horizontallyFlipped )
1080 // Reset the anchor point.
1081 handle.actor.SetProperty( Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
1083 handle.horizontallyFlipped = false;
1087 // Whether to flip the handle vertically.
1088 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
1089 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
1090 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
1092 // The primary selection handle 'y' position in local coords.
1093 // If the handle exceeds the bottom of the decoration box,
1094 // set the 'y' position to the top of the line.
1095 // The SetHandleImage() method will change the orientation.
1096 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1100 handle.actor.SetProperty( Actor::Property::POSITION, Vector2( handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f ),
1101 yLocalPosition + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f ) ) );
1105 void SetHandleImage( HandleType type )
1107 HandleImpl& handle = mHandle[type];
1109 HandleType markerType = HANDLE_TYPE_COUNT;
1110 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1111 if( LEFT_SELECTION_HANDLE == type )
1113 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1114 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1116 else if( RIGHT_SELECTION_HANDLE == type )
1118 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1119 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1122 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1125 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1127 handle.actor.SetImage( mHandleImages[type][imageType] );
1130 if( HANDLE_TYPE_COUNT != markerType )
1132 if( handle.markerActor )
1134 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1135 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
1139 // Whether to flip the handle vertically.
1142 handle.actor.SetProperty( Actor::Property::ORIENTATION, Quaternion( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS ) );
1146 void CreateHighlight()
1148 if( !mHighlightActor )
1150 mHighlightActor = Actor::New();
1152 mHighlightActor.SetProperty( Dali::Actor::Property::NAME, "HighlightActor" );
1153 mHighlightActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
1154 mHighlightActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
1155 mHighlightActor.SetProperty( Actor::Property::COLOR, mHighlightColor );
1156 mHighlightActor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_COLOR );
1159 // Add the highlight box telling the controller it needs clipping.
1160 mController.AddDecoration( mHighlightActor, true );
1163 void UpdateHighlight()
1165 if ( mHighlightActor )
1167 // Sets the position of the highlight actor inside the decorator.
1168 mHighlightActor.SetProperty( Actor::Property::POSITION, Vector2( mHighlightPosition.x + mHighlightOutlineOffset,
1169 mHighlightPosition.y + mHighlightOutlineOffset ) );
1171 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1172 if( 0u != numberOfQuads )
1174 // Set the size of the highlighted text to the actor.
1175 mHighlightActor.SetProperty( Actor::Property::SIZE, mHighlightSize );
1177 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1178 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1179 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1181 Vector<Vector2> vertices;
1182 Vector<unsigned short> indices;
1184 vertices.Reserve( 4u * numberOfQuads );
1185 indices.Reserve( 6u * numberOfQuads );
1187 // Index to the vertex.
1188 unsigned int v = 0u;
1190 // Traverse all quads.
1191 for( Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1192 endIt = mHighlightQuadList.End();
1196 const Vector4& quad = *it;
1201 vertex.x = quad.x - offsetX;
1202 vertex.y = quad.y - offsetY;
1203 vertices.PushBack( vertex );
1206 vertex.x = quad.z - offsetX;
1207 vertex.y = quad.y - offsetY;
1208 vertices.PushBack( vertex );
1210 // bottom-left (v+2)
1211 vertex.x = quad.x - offsetX;
1212 vertex.y = quad.w - offsetY;
1213 vertices.PushBack( vertex );
1215 // bottom-right (v+3)
1216 vertex.x = quad.z - offsetX;
1217 vertex.y = quad.w - offsetY;
1218 vertices.PushBack( vertex );
1220 // triangle A (3, 1, 0)
1221 indices.PushBack( v + 3 );
1222 indices.PushBack( v + 1 );
1223 indices.PushBack( v );
1225 // triangle B (0, 2, 3)
1226 indices.PushBack( v );
1227 indices.PushBack( v + 2 );
1228 indices.PushBack( v + 3 );
1231 if( ! mQuadVertices )
1233 mQuadVertices = VertexBuffer::New( mQuadVertexFormat );
1236 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1238 if( !mQuadGeometry )
1240 mQuadGeometry = Geometry::New();
1241 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1243 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1245 if( !mHighlightRenderer )
1247 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1248 mHighlightActor.AddRenderer( mHighlightRenderer );
1252 mHighlightQuadList.Clear();
1254 if( mHighlightRenderer )
1256 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1261 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1263 GestureState state = gesture.GetState();
1264 if( GestureState::STARTED == state )
1266 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1268 handle.globalPosition.x = handle.position.x;
1269 handle.globalPosition.y = handle.position.y;
1272 const Vector2& displacement = gesture.GetDisplacement();
1273 handle.grabDisplacementX += displacement.x;
1274 handle.grabDisplacementY += ( handle.verticallyFlipped ? -displacement.y : displacement.y );
1276 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1277 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1278 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f );
1280 if( ( GestureState::STARTED == state ) ||
1281 ( GestureState::CONTINUING == state ) )
1284 mController.GetTargetSize( targetSize );
1286 if( mHorizontalScrollingEnabled &&
1287 ( x < mScrollThreshold ) )
1289 mScrollDirection = SCROLL_RIGHT;
1290 mHandleScrolling = type;
1293 else if( mHorizontalScrollingEnabled &&
1294 ( x > targetSize.width - mScrollThreshold ) )
1296 mScrollDirection = SCROLL_LEFT;
1297 mHandleScrolling = type;
1300 else if( mVerticalScrollingEnabled &&
1301 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1303 mScrollDirection = SCROLL_TOP;
1304 mHandleScrolling = type;
1307 else if( mVerticalScrollingEnabled &&
1308 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1310 mScrollDirection = SCROLL_BOTTOM;
1311 mHandleScrolling = type;
1316 mHandleScrolling = HANDLE_TYPE_COUNT;
1318 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1321 mIsHandlePanning = true;
1323 else if( ( GestureState::FINISHED == state ) ||
1324 ( GestureState::CANCELLED == state ) )
1327 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1329 mNotifyEndOfScroll = false;
1330 mHandleScrolling = HANDLE_TYPE_COUNT;
1332 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1336 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1341 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1343 handle.pressed = false;
1345 mIsHandlePanning = false;
1349 void OnPan( Actor actor, const PanGesture& gesture )
1351 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1352 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1353 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1355 if( actor == grabHandle.grabArea )
1357 DoPan( grabHandle, GRAB_HANDLE, gesture );
1359 else if( actor == primarySelectionHandle.grabArea )
1361 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1363 else if( actor == secondarySelectionHandle.grabArea )
1365 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1369 bool OnGrabHandleTouched( Actor actor, const TouchEvent& touch )
1371 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1373 // Switch between pressed/release grab-handle images
1374 if( touch.GetPointCount() > 0 &&
1377 const PointState::Type state = touch.GetState( 0 );
1379 if( PointState::DOWN == state )
1381 grabHandle.pressed = true;
1383 else if( ( PointState::UP == state ) ||
1384 ( PointState::INTERRUPTED == state ) )
1386 grabHandle.pressed = false;
1389 SetHandleImage( GRAB_HANDLE );
1395 bool OnHandleOneTouched( Actor actor, const TouchEvent& touch )
1397 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1399 // Switch between pressed/release selection handle images
1400 if( touch.GetPointCount() > 0 &&
1401 primarySelectionHandle.actor )
1403 const PointState::Type state = touch.GetState( 0 );
1405 if( PointState::DOWN == state )
1407 primarySelectionHandle.pressed = true;
1408 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1410 else if( ( PointState::UP == state ) ||
1411 ( PointState::INTERRUPTED == state ) )
1413 primarySelectionHandle.pressed = false;
1414 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1415 mIsHandlePanning = false;
1416 mHandleReleased = LEFT_SELECTION_HANDLE;
1419 SetHandleImage( LEFT_SELECTION_HANDLE );
1425 bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch )
1427 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1429 // Switch between pressed/release selection handle images
1430 if( touch.GetPointCount() > 0 &&
1431 secondarySelectionHandle.actor )
1433 const PointState::Type state = touch.GetState( 0 );
1435 if( PointState::DOWN == state )
1437 secondarySelectionHandle.pressed = true;
1438 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1440 else if( ( PointState::UP == state ) ||
1441 ( PointState::INTERRUPTED == state ) )
1443 secondarySelectionHandle.pressed = false;
1444 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1445 mIsHandlePanning = false;
1446 mHandleReleased = RIGHT_SELECTION_HANDLE;
1449 SetHandleImage( RIGHT_SELECTION_HANDLE );
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::NOTIFY_ON_CHANGED );
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::NOTIFY_ON_CHANGED );
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::NOTIFY_ON_CHANGED );
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::NOTIFY_ON_CHANGED );
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::NOTIFY_ON_CHANGED );
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::NOTIFY_ON_CHANGED );
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::NOTIFY_ON_CHANGED );
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::NOTIFY_ON_CHANGED );
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.SetProperty( Actor::Property::POSITION_Y, 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.SetProperty( Actor::Property::POSITION_Y, 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::NOTIFY_ON_CHANGED );
1752 mPopupBottomExceedNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1754 mPopupTopExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1755 mPopupBottomExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1758 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName )
1760 ImageDimensions dimensions = Dali::GetOriginalImageSize( imageFileName );
1762 HandleImpl& handle = mHandle[handleType];
1763 handle.size = Size( dimensions.GetWidth(), dimensions.GetHeight() );
1765 mHandleImages[handleType][handleImageType] = imageFileName;
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 Actor mActiveLayer; ///< Actor 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 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1904 Vector4 mHandleColor;
1906 CursorImpl mCursor[CURSOR_COUNT];
1907 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1909 VertexBuffer 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.
1949 bool mHidePrimaryCursorAndGrabHandle : 1; ///< Whether the primary cursor and grab are hidden always.
1952 DecoratorPtr Decorator::New( ControllerInterface& controller,
1953 TextSelectionPopupCallbackInterface& callbackInterface )
1955 return DecoratorPtr( new Decorator( controller,
1956 callbackInterface ) );
1959 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1961 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1964 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1966 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1969 void Decorator::Relayout( const Vector2& size )
1971 mImpl->Relayout( size );
1974 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1976 mImpl->UpdatePositions( scrollOffset );
1981 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1983 mImpl->mActiveCursor = activeCursor;
1986 unsigned int Decorator::GetActiveCursor() const
1988 return mImpl->mActiveCursor;
1991 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1993 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1995 cursorImpl.position.x = x;
1996 cursorImpl.position.y = y;
1997 cursorImpl.cursorHeight = cursorHeight;
1998 cursorImpl.lineHeight = lineHeight;
2001 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
2003 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2005 x = cursorImpl.position.x;
2006 y = cursorImpl.position.y;
2007 cursorHeight = cursorImpl.cursorHeight;
2008 lineHeight = cursorImpl.lineHeight;
2011 const Vector2& Decorator::GetPosition( Cursor cursor ) const
2013 return mImpl->mCursor[cursor].position;
2016 void Decorator::SetGlyphOffset( Cursor cursor, float glyphOffset )
2018 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2020 cursorImpl.glyphOffset = glyphOffset;
2023 const float Decorator::GetGlyphOffset( Cursor cursor) const
2025 return mImpl->mCursor[cursor].glyphOffset;
2028 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
2030 mImpl->mCursor[cursor].color = color;
2033 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
2035 return mImpl->mCursor[cursor].color;
2038 void Decorator::StartCursorBlink()
2040 if ( !mImpl->mCursorBlinkTimer )
2042 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
2043 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
2046 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
2048 mImpl->mCursorBlinkTimer.Start();
2052 void Decorator::StopCursorBlink()
2054 if ( mImpl->mCursorBlinkTimer )
2056 mImpl->mCursorBlinkTimer.Stop();
2059 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2062 void Decorator::DelayCursorBlink()
2064 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2065 mImpl->mDelayCursorBlink = true;
2068 void Decorator::SetCursorBlinkInterval( float seconds )
2070 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
2073 float Decorator::GetCursorBlinkInterval() const
2075 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
2078 void Decorator::SetCursorBlinkDuration( float seconds )
2080 mImpl->mCursorBlinkDuration = seconds;
2083 float Decorator::GetCursorBlinkDuration() const
2085 return mImpl->mCursorBlinkDuration;
2088 void Decorator::SetCursorWidth( int width )
2090 mImpl->mCursorWidth = static_cast<float>( width );
2093 int Decorator::GetCursorWidth() const
2095 return static_cast<int>( mImpl->mCursorWidth );
2098 void Decorator::SetEditable( bool editable )
2100 mImpl->mHidePrimaryCursorAndGrabHandle = !editable;
2101 mImpl->Relayout( mImpl->mControlSize );
2105 void Decorator::SetHandleActive( HandleType handleType, bool active )
2107 mImpl->mHandle[handleType].active = active;
2111 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
2113 mImpl->mIsHandlePreviouslyCrossed = false;
2116 // TODO: this is a work-around.
2117 // The problem is the handle actor does not receive the touch event with the Interrupt
2118 // state when the power button is pressed and the application goes to background.
2119 mImpl->mHandle[handleType].pressed = false;
2120 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2121 ImageView imageView = mImpl->mHandle[handleType].actor;
2122 if( imageReleased && imageView )
2124 imageView.SetImage( mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED] );
2130 bool Decorator::IsHandleActive( HandleType handleType ) const
2132 return mImpl->mHandle[handleType].active ;
2135 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName )
2137 mImpl->SetHandleImage( handleType, handleImageType, imageFileName );
2140 const std::string& Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
2142 return mImpl->mHandleImages[handleType][handleImageType];
2145 void Decorator::SetHandleColor( const Vector4& color )
2147 mImpl->mHandleColor = color;
2150 const Vector4& Decorator::GetHandleColor() const
2152 return mImpl->mHandleColor;
2155 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
2157 // Adjust handle's displacement
2158 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2160 handle.position.x = x;
2161 handle.position.y = y;
2162 handle.lineHeight = height;
2164 if( mImpl->mSmoothHandlePanEnabled )
2166 handle.grabDisplacementX = 0.f;
2167 handle.grabDisplacementY = 0.f;
2171 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2173 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2175 x = handle.position.x;
2176 y = handle.position.y;
2177 height = handle.lineHeight;
2180 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2182 return mImpl->mHandle[handleType].position;
2185 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2187 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2190 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2192 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2195 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2197 mImpl->mFlipSelectionHandlesOnCross = enable;
2200 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2202 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2203 mImpl->mFlipLeftSelectionHandleDirection = left;
2204 mImpl->mFlipRightSelectionHandleDirection = right;
2207 void Decorator::AddHighlight( unsigned int index, const Vector4& quad )
2209 *( mImpl->mHighlightQuadList.Begin() + index ) = quad;
2212 void Decorator::SetHighLightBox( const Vector2& position, const Size& size, float outlineOffset )
2214 mImpl->mHighlightPosition = position;
2215 mImpl->mHighlightSize = size;
2216 mImpl->mHighlightOutlineOffset = outlineOffset;
2219 void Decorator::ClearHighlights()
2221 mImpl->mHighlightQuadList.Clear();
2222 mImpl->mHighlightPosition = Vector2::ZERO;
2223 mImpl->mHighlightOutlineOffset = 0.f;
2226 void Decorator::ResizeHighlightQuads( unsigned int numberOfQuads )
2228 mImpl->mHighlightQuadList.Resize( numberOfQuads );
2231 void Decorator::SetHighlightColor( const Vector4& color )
2233 mImpl->mHighlightColor = color;
2236 const Vector4& Decorator::GetHighlightColor() const
2238 return mImpl->mHighlightColor;
2241 void Decorator::SetHighlightActive( bool active )
2243 mImpl->mIsHighlightBoxActive = active;
2246 bool Decorator::IsHighlightActive() const
2248 return mImpl->mIsHighlightBoxActive;
2251 bool Decorator::IsHighlightVisible() const
2253 return ( mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent() );
2256 void Decorator::SetTextDepth( int textDepth )
2258 mImpl->mTextDepth = textDepth;
2261 void Decorator::SetPopupActive( bool active )
2263 mImpl->mActiveCopyPastePopup = active;
2266 bool Decorator::IsPopupActive() const
2268 return mImpl->mActiveCopyPastePopup;
2271 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2273 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2275 if ( !mImpl->mCopyPastePopup.actor )
2277 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2278 #ifdef DECORATOR_DEBUG
2279 mImpl->mCopyPastePopup.actor.SetProperty( Dali::Actor::Property::NAME,"mCopyPastePopup");
2281 mImpl->mCopyPastePopup.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
2282 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::SetPopupPosition ); // Position popup after size negotiation
2285 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2288 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2290 return mImpl->mEnabledPopupButtons;
2295 void Decorator::SetScrollThreshold( float threshold )
2297 mImpl->SetScrollThreshold( threshold );
2300 float Decorator::GetScrollThreshold() const
2302 return mImpl->GetScrollThreshold();
2305 void Decorator::SetScrollSpeed( float speed )
2307 mImpl->SetScrollSpeed( speed );
2310 float Decorator::GetScrollSpeed() const
2312 return mImpl->GetScrollSpeed();
2315 void Decorator::NotifyEndOfScroll()
2317 mImpl->NotifyEndOfScroll();
2320 void Decorator::SetHorizontalScrollEnabled( bool enable )
2322 mImpl->mHorizontalScrollingEnabled = enable;
2325 bool Decorator::IsHorizontalScrollEnabled() const
2327 return mImpl->mHorizontalScrollingEnabled;
2330 void Decorator::SetVerticalScrollEnabled( bool enable )
2332 mImpl->mVerticalScrollingEnabled = enable;
2335 bool Decorator::IsVerticalScrollEnabled() const
2337 return mImpl->mVerticalScrollingEnabled;
2340 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2342 mImpl->mSmoothHandlePanEnabled = enable;
2345 bool Decorator::IsSmoothHandlePanEnabled() const
2347 return mImpl->mSmoothHandlePanEnabled;
2350 Decorator::~Decorator()
2355 Decorator::Decorator( ControllerInterface& controller,
2356 TextSelectionPopupCallbackInterface& callbackInterface )
2359 mImpl = new Decorator::Impl( controller, callbackInterface );
2364 } // namespace Toolkit