2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/decorator/text-decorator.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/actors/layer.h>
24 #include <dali/public-api/adaptor-framework/timer.h>
25 #include <dali/devel-api/common/stage.h>
26 #include <dali/public-api/events/touch-event.h>
27 #include <dali/public-api/events/pan-gesture.h>
28 #include <dali/public-api/object/property-notification.h>
29 #include <dali/public-api/rendering/geometry.h>
30 #include <dali/public-api/rendering/renderer.h>
31 #include <dali/devel-api/adaptor-framework/image-loading.h>
34 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
35 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
36 #include <dali-toolkit/internal/controls/image-view/image-view-impl.h>
39 #define DECORATOR_DEBUG
43 #define MAKE_SHADER(A)#A
47 const char* VERTEX_SHADER = MAKE_SHADER(
48 attribute mediump vec2 aPosition;
49 uniform highp mat4 uMvpMatrix;
53 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
54 gl_Position = uMvpMatrix * position;
58 const char* FRAGMENT_SHADER = MAKE_SHADER(
59 uniform lowp vec4 uColor;
63 gl_FragColor = uColor;
74 #ifdef DECORATOR_DEBUG
75 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
85 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
86 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
87 const Dali::Vector3 ACTIVE_LAYER_ANCHOR_POINT( 0.5f, 0.5f, 0.5f );
89 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f ); // The text highlight color. TODO: due some problems, maybe with the blending function in the text clipping, the color is fully opaque.
91 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f );
93 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
94 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
95 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
97 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
98 const float SCROLL_THRESHOLD = 10.f; ///< Threshold in pixels close to the edges of the decorator boundaries from where the scroll timer starts to emit signals.
99 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
101 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
103 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
105 const float POPUP_PADDING = 2.f; ///< Padding space between the highlight box and the text's popup.
107 typedef Dali::Vector<Dali::Vector4> QuadContainer;
110 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
111 * @param[in] boundingRectangle local bounding
112 * @param[out] Vector4 World coordinate bounding Box.
114 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
116 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
117 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
119 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
120 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
122 boundingBox = Dali::Vector4( originX,
124 originX + boundingRectangle.width,
125 originY + boundingRectangle.height );
128 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
130 // Convert to local coordinates and store as a Dali::Rect.
131 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
133 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
134 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
135 boundingRectangle.width = boundingBox.z - boundingBox.x;
136 boundingRectangle.height = boundingBox.w - boundingBox.y;
139 } // end of namespace
150 struct Decorator::Impl : public ConnectionTracker
164 : color( Dali::Color::BLACK ),
166 cursorHeight( 0.0f ),
186 grabDisplacementX( 0.f ),
187 grabDisplacementY( 0.f ),
189 horizontallyVisible( false ),
190 verticallyVisible( false ),
192 verticallyFlippedPreferred( false ),
193 horizontallyFlipped( false ),
194 verticallyFlipped( false ),
195 verticallyFlippedOnTouch( false )
201 ImageView markerActor;
204 Vector2 globalPosition;
206 float lineHeight; ///< Not the handle height
207 float grabDisplacementX;
208 float grabDisplacementY;
210 bool horizontallyVisible : 1;
211 bool verticallyVisible : 1;
213 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
214 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
215 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
216 bool verticallyFlippedOnTouch : 1; ///< Whether the handle is vertically flipped on touch.
226 TextSelectionPopup actor;
230 Impl( ControllerInterface& controller,
231 TextSelectionPopupCallbackInterface& callbackInterface )
232 : mController( controller ),
233 mEnabledPopupButtons( TextSelectionPopup::NONE ),
234 mTextSelectionPopupCallbackInterface( callbackInterface ),
235 mHandleColor( HANDLE_COLOR ),
237 mHighlightColor( LIGHT_BLUE ),
238 mHighlightPosition( Vector2::ZERO ),
239 mHighlightSize( Vector2::ZERO ),
240 mControlSize( Vector2::ZERO ),
241 mHighlightOutlineOffset( 0.f ),
242 mActiveCursor( ACTIVE_CURSOR_NONE ),
243 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
244 mCursorBlinkDuration( 0.0f ),
245 mCursorWidth( CURSOR_WIDTH ),
246 mHandleScrolling( HANDLE_TYPE_COUNT ),
247 mHandleReleased( HANDLE_TYPE_COUNT ),
248 mScrollDirection( SCROLL_NONE ),
249 mScrollThreshold( SCROLL_THRESHOLD ),
250 mScrollSpeed( SCROLL_SPEED ),
251 mScrollDistance( SCROLL_DISTANCE ),
253 mActiveCopyPastePopup( false ),
254 mPopupSetNewPosition( true ),
255 mCursorBlinkStatus( true ),
256 mDelayCursorBlink( false ),
257 mPrimaryCursorVisible( false ),
258 mSecondaryCursorVisible( false ),
259 mFlipSelectionHandlesOnCross( false ),
260 mFlipLeftSelectionHandleDirection( false ),
261 mFlipRightSelectionHandleDirection( false ),
262 mIsHandlePanning( false ),
263 mIsHandleCurrentlyCrossed( false ),
264 mIsHandlePreviouslyCrossed( false ),
265 mNotifyEndOfScroll( false ),
266 mHorizontalScrollingEnabled( false ),
267 mVerticalScrollingEnabled( false ),
268 mSmoothHandlePanEnabled( false ),
269 mIsHighlightBoxActive( false )
271 mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
272 mHighlightShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
277 * Relayout of the decorations owned by the decorator.
278 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
280 void Relayout( const Vector2& size )
284 // TODO - Remove this if nothing is active
287 // Show or hide the cursors
292 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
293 mPrimaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
294 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
295 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
296 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
297 if( mPrimaryCursorVisible )
299 mPrimaryCursor.SetProperty( Actor::Property::POSITION, Vector2( cursor.position.x,
300 cursor.position.y ) );
301 mPrimaryCursor.SetProperty( Actor::Property::SIZE, Size( mCursorWidth, cursor.cursorHeight ) );
303 mPrimaryCursor.SetProperty( Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus );
305 if( mSecondaryCursor )
307 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
308 mSecondaryCursorVisible = ( ( mControlSize.width - ( cursor.position.x + mCursorWidth ) > -Math::MACHINE_EPSILON_1000 ) &&
309 ( cursor.position.x > -Math::MACHINE_EPSILON_1000 ) &&
310 ( mControlSize.height - ( cursor.position.y + cursor.cursorHeight ) > -Math::MACHINE_EPSILON_1000 ) &&
311 ( cursor.position.y > -Math::MACHINE_EPSILON_1000 ) );
312 if( mSecondaryCursorVisible )
314 mSecondaryCursor.SetProperty( Actor::Property::POSITION, Vector2( cursor.position.x,
315 cursor.position.y ) );
316 mSecondaryCursor.SetProperty( Actor::Property::SIZE, Size( mCursorWidth, cursor.cursorHeight ) );
318 mSecondaryCursor.SetProperty( Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus );
321 // Show or hide the grab handle
322 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
323 bool newGrabHandlePosition = false;
324 grabHandle.horizontallyVisible = false;
325 grabHandle.verticallyVisible = false;
326 if( grabHandle.active )
328 grabHandle.horizontallyVisible = ( ( mControlSize.width - ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) ) > -Math::MACHINE_EPSILON_1000 ) &&
329 ( grabHandle.position.x > -Math::MACHINE_EPSILON_1000 ) );
330 grabHandle.verticallyVisible = ( ( ( mControlSize.height - grabHandle.lineHeight ) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) &&
331 ( grabHandle.position.y > -Math::MACHINE_EPSILON_1000 ) );
333 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible;
338 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
339 SetGrabHandlePosition();
341 // Sets the grab handle image according if it's pressed, flipped, etc.
342 SetHandleImage( GRAB_HANDLE );
344 newGrabHandlePosition = true;
347 if( grabHandle.actor )
349 grabHandle.actor.SetProperty( Actor::Property::VISIBLE, isVisible );
352 else if( grabHandle.actor )
354 grabHandle.actor.Unparent();
357 // Show or hide the selection handles/highlight
358 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
359 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
360 bool newPrimaryHandlePosition = false;
361 bool newSecondaryHandlePosition = false;
363 primary.horizontallyVisible = ( ( mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
364 ( primary.position.x > -Math::MACHINE_EPSILON_1000 ) );
365 primary.verticallyVisible = ( ( ( mControlSize.height - primary.lineHeight ) - primary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
366 ( primary.position.y + ( primary.verticallyFlipped ? 0.f : primary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
367 secondary.horizontallyVisible = ( ( mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000 ) &&
368 ( secondary.position.x > -Math::MACHINE_EPSILON_1000 ) );
369 secondary.verticallyVisible = ( ( ( mControlSize.height - secondary.lineHeight ) - secondary.position.y > -Math::MACHINE_EPSILON_1000 ) &&
370 ( secondary.position.y + ( secondary.verticallyFlipped ? 0.f : secondary.lineHeight ) > -Math::MACHINE_EPSILON_1000 ) );
372 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
373 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
375 if( primary.active || secondary.active )
377 if( primaryVisible || secondaryVisible )
379 CreateSelectionHandles();
383 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
385 // Sets the primary handle image according if it's pressed, flipped, etc.
386 SetHandleImage( LEFT_SELECTION_HANDLE );
388 SetSelectionHandleMarkerSize( primary );
390 newPrimaryHandlePosition = true;
393 if( secondaryVisible )
395 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
397 // Sets the secondary handle image according if it's pressed, flipped, etc.
398 SetHandleImage( RIGHT_SELECTION_HANDLE );
400 SetSelectionHandleMarkerSize( secondary );
402 newSecondaryHandlePosition = true;
408 primary.actor.SetProperty( Actor::Property::VISIBLE, primaryVisible );
410 if( secondary.actor )
412 secondary.actor.SetProperty( Actor::Property::VISIBLE, secondaryVisible );
420 primary.actor.Unparent();
422 if( secondary.actor )
424 secondary.actor.Unparent();
428 if( mIsHighlightBoxActive )
435 if( mHighlightActor )
437 mHighlightActor.Unparent();
441 if( newGrabHandlePosition ||
442 newPrimaryHandlePosition ||
443 newSecondaryHandlePosition )
445 // Setup property notifications to find whether the handles leave the boundaries of the current display.
446 SetupActiveLayerPropertyNotifications();
449 if( mActiveCopyPastePopup &&
450 ( primaryVisible || secondaryVisible ) )
453 mPopupSetNewPosition = true;
457 if( mCopyPastePopup.actor )
459 mCopyPastePopup.actor.HidePopup();
460 mPopupSetNewPosition = true;
465 void UpdatePositions( const Vector2& scrollOffset )
467 mCursor[PRIMARY_CURSOR].position += scrollOffset;
468 mCursor[SECONDARY_CURSOR].position += scrollOffset;
469 mHandle[ GRAB_HANDLE ].position += scrollOffset;
470 mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
471 mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
472 mHighlightPosition += scrollOffset;
477 if( !mCopyPastePopup.actor )
482 if( !mCopyPastePopup.actor.GetParent() )
484 mActiveLayer.Add( mCopyPastePopup.actor );
487 mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
488 mCopyPastePopup.actor.ShowPopup();
491 float CalculateVerticalPopUpPosition( float halfHeight, bool preferBelow )
493 float yPosition = 0.f;
495 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
496 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
497 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
499 if( primaryHandle.active || secondaryHandle.active )
501 // The origin of the decorator's coordinate system in world coords.
502 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION ) - mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ) * ACTIVE_LAYER_ANCHOR_POINT;
506 // Find out if there is enough space for the popup at the bottom.
507 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
508 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
510 float maxY = std::max( primaryBelowY, secondaryBelowY );
512 yPosition = halfHeight + maxY;
514 if( originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w )
516 // Does not fit below.
518 // Try to fit first below the non active handle. Otherwise above the active handle.
519 if( RIGHT_SELECTION_HANDLE == mHandleReleased )
521 if( primaryBelowY < secondaryBelowY )
523 yPosition = halfHeight + primaryBelowY;
527 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
530 else if( LEFT_SELECTION_HANDLE == mHandleReleased )
532 if( secondaryBelowY < primaryBelowY )
534 yPosition = halfHeight + secondaryBelowY;
538 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
542 // Check the handle is whithin the decoration box.
543 if( originWorldCoords.y + yPosition < mBoundingBox.y )
545 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
548 if( originWorldCoords.y + yPosition > mBoundingBox.w )
550 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
556 // Find out if there is enough space for the popup at the top.
557 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
558 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
560 float minY = std::min( primaryTopY, secondaryTopY );
562 yPosition = -halfHeight + minY;
564 } // ( primaryHandle.active || secondaryHandle.active )
565 else if( grabHandle.active )
569 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
573 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
580 void ConstrainPopupPosition( const Vector3& popupHalfSize )
582 // Check if the popup is within the boundaries of the decoration box.
584 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
586 // The origin of the decorator's coordinate system in world coords.
587 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION ) - mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ) * ACTIVE_LAYER_ANCHOR_POINT;
589 // The popup's position in world coords.
590 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
592 if( popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x )
594 mCopyPastePopup.position.x += mBoundingBox.x - ( popupPositionWorldCoords.x - popupHalfSize.width );
596 else if( popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z )
598 mCopyPastePopup.position.x += mBoundingBox.z - ( popupPositionWorldCoords.x + popupHalfSize.width );
601 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
602 if( popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y )
604 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition( popupHalfSize.height, true ); // true -> prefer to set the popup's position below.
608 void SetPopupPosition( Actor actor )
610 if( !mActiveCopyPastePopup )
615 // Retrieves the popup's size after relayout.
616 const Vector3 popupSize( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
617 const Vector3 popupHalfSize = popupSize * 0.5f;
619 if( mPopupSetNewPosition )
621 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
622 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
623 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
625 if( primaryHandle.active || secondaryHandle.active )
627 const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
628 const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
630 mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
632 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - ( primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING );
633 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - ( secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING );
635 mCopyPastePopup.position.y = std::min( primaryY, secondaryY );
637 else if( grabHandle.active )
639 mCopyPastePopup.position.x = grabHandle.position.x;
641 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - ( grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING );
643 } // mPopupSetNewPosition
645 // It may change the popup's position to fit within the decoration box.
646 ConstrainPopupPosition( popupHalfSize );
648 SetUpPopupPositionNotifications( popupHalfSize );
650 // Prevent pixel mis-alignment by rounding down.
651 mCopyPastePopup.position.x = floorf( mCopyPastePopup.position.x );
652 mCopyPastePopup.position.y = floorf( mCopyPastePopup.position.y );
654 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION, mCopyPastePopup.position );
655 mPopupSetNewPosition = false;
658 void CreateCursor( Control& cursor, const Vector4& color )
660 cursor = Control::New();
661 cursor.SetBackgroundColor( color );
662 cursor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
663 cursor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
666 // Add or Remove cursor(s) from parent
669 if( mActiveCursor == ACTIVE_CURSOR_NONE )
673 mPrimaryCursor.Unparent();
675 if( mSecondaryCursor )
677 mSecondaryCursor.Unparent();
682 // Create Primary and or Secondary Cursor(s) if active and add to parent
683 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
684 mActiveCursor == ACTIVE_CURSOR_BOTH )
686 if ( !mPrimaryCursor )
688 CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
689 #ifdef DECORATOR_DEBUG
690 mPrimaryCursor.SetProperty( Dali::Actor::Property::NAME, "PrimaryCursorActor" );
694 if( !mPrimaryCursor.GetParent() )
696 mActiveLayer.Add( mPrimaryCursor );
700 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
702 if ( !mSecondaryCursor )
704 CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
705 #ifdef DECORATOR_DEBUG
706 mSecondaryCursor.SetProperty( Dali::Actor::Property::NAME, "SecondaryCursorActor" );
710 if( !mSecondaryCursor.GetParent() )
712 mActiveLayer.Add( mSecondaryCursor );
717 if( mSecondaryCursor )
719 mSecondaryCursor.Unparent();
725 bool OnCursorBlinkTimerTick()
727 if( !mDelayCursorBlink )
730 if ( mPrimaryCursor )
732 mPrimaryCursor.SetProperty( Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus );
734 if ( mSecondaryCursor )
736 mSecondaryCursor.SetProperty( Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus );
739 mCursorBlinkStatus = !mCursorBlinkStatus;
744 mDelayCursorBlink = false;
752 // Will consume tap gestures on handles.
753 mTapDetector = TapGestureDetector::New();
755 // Will consume double tap gestures on handles.
756 mTapDetector.SetMaximumTapsRequired( 2u );
758 // Will consume long press gestures on handles.
759 mLongPressDetector = LongPressGestureDetector::New();
761 // Detects pan gestures on handles.
762 mPanDetector = PanGestureDetector::New();
763 mPanDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
766 void CreateActiveLayer()
770 mActiveLayer = Layer::New();
771 #ifdef DECORATOR_DEBUG
772 mActiveLayer.SetProperty( Actor::Property::NAME, "ActiveLayerActor" );
775 mActiveLayer.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
776 mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
778 // Add the active layer telling the controller it doesn't need clipping.
779 mController.AddDecoration( mActiveLayer, false );
782 mActiveLayer.RaiseToTop();
785 void SetSelectionHandleMarkerSize( HandleImpl& handle )
787 if( handle.markerActor )
789 handle.markerActor.SetProperty( Actor::Property::SIZE, Vector2( 0, handle.lineHeight ) );
793 void CreateGrabHandle()
795 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
796 if( !grabHandle.actor )
798 if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED].size() )
800 grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
801 GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
802 grabHandle.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
804 // Area that Grab handle responds to, larger than actual handle so easier to move
805 #ifdef DECORATOR_DEBUG
806 grabHandle.actor.SetProperty( Dali::Actor::Property::NAME, "GrabHandleActor" );
807 if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
809 grabHandle.grabArea = Control::New();
810 Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
811 control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
812 grabHandle.grabArea.SetProperty( Dali::Actor::Property::NAME, "GrabArea" );
816 grabHandle.grabArea = Actor::New();
817 grabHandle.grabArea.SetProperty( Dali::Actor::Property::NAME, "GrabArea" );
820 grabHandle.grabArea = Actor::New();
823 grabHandle.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
824 grabHandle.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
825 grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
826 grabHandle.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
827 grabHandle.actor.Add( grabHandle.grabArea );
828 grabHandle.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
830 grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
832 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
833 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
834 mTapDetector.Attach( grabHandle.actor );
835 mLongPressDetector.Attach( grabHandle.actor );
837 // The grab handle's area is attached to the pan detector.
838 // The OnPan() method is connected to the signals emitted by the pan detector.
839 mPanDetector.Attach( grabHandle.grabArea );
841 mActiveLayer.Add( grabHandle.actor );
845 if( grabHandle.actor && !grabHandle.actor.GetParent() )
847 mActiveLayer.Add( grabHandle.actor );
851 void CreateHandleMarker( HandleImpl& handle, const std::string& image, HandleType handleType )
855 handle.markerActor = ImageView::New( image );
856 handle.markerActor.SetProperty( Actor::Property::COLOR, mHandleColor );
857 handle.actor.Add( handle.markerActor );
859 handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
861 if( LEFT_SELECTION_HANDLE == handleType )
863 handle.markerActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT );
864 handle.markerActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_RIGHT );
866 else if( RIGHT_SELECTION_HANDLE == handleType )
868 handle.markerActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT );
869 handle.markerActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
874 void CreateSelectionHandles()
876 HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
879 if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size() )
881 primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
882 #ifdef DECORATOR_DEBUG
883 primary.actor.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleOne");
885 primary.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
886 GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
887 primary.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
889 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
890 #ifdef DECORATOR_DEBUG
891 primary.grabArea.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleOneGrabArea");
893 primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
894 primary.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
895 primary.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
896 primary.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
898 primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
900 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
901 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
902 mTapDetector.Attach( primary.actor );
903 mLongPressDetector.Attach( primary.actor );
905 // The handle's area is attached to the pan detector.
906 // The OnPan() method is connected to the signals emitted by the pan detector.
907 mPanDetector.Attach( primary.grabArea );
909 primary.actor.Add( primary.grabArea );
911 CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
915 if( primary.actor && !primary.actor.GetParent() )
917 mActiveLayer.Add( primary.actor );
920 HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
921 if( !secondary.actor )
923 if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size() )
925 secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
926 #ifdef DECORATOR_DEBUG
927 secondary.actor.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleTwo");
929 secondary.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
930 GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
931 secondary.actor.SetProperty( Actor::Property::COLOR, mHandleColor );
933 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
934 #ifdef DECORATOR_DEBUG
935 secondary.grabArea.SetProperty( Dali::Actor::Property::NAME,"SelectionHandleTwoGrabArea");
937 secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
938 secondary.grabArea.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
939 secondary.grabArea.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
940 secondary.grabArea.SetProperty( Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
942 secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
944 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
945 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
946 mTapDetector.Attach( secondary.actor );
947 mLongPressDetector.Attach( secondary.actor );
949 // The handle's area is attached to the pan detector.
950 // The OnPan() method is connected to the signals emitted by the pan detector.
951 mPanDetector.Attach( secondary.grabArea );
953 secondary.actor.Add( secondary.grabArea );
955 CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE );
959 if( secondary.actor && !secondary.actor.GetParent() )
961 mActiveLayer.Add( secondary.actor );
965 void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
967 // Gets the world position of the active layer. The active layer is where the handles are added.
968 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION );
970 // The grab handle position in world coords.
971 // The active layer's world position is the center of the active layer. The origin of the
972 // coord system of the handles is the top left of the active layer.
973 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f );
974 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f );
977 void SetGrabHandlePosition()
979 // Reference to the grab handle.
980 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
982 // Transforms the handle position into world coordinates.
983 // @note This is not the same value as grabHandle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
984 // as it's transforming the handle's position set by the text-controller and not
985 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
986 // retrieves the position of the center of the actor but the handle's position set
987 // by the text controller is not the center of the actor.
988 Vector2 grabHandleWorldPosition;
989 CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
991 // Check if the grab handle exceeds the boundaries of the decoration box.
992 // At the moment only the height is checked for the grab handle.
994 grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
995 ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
996 ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
998 // The grab handle 'y' position in local coords.
999 // If the grab handle exceeds the bottom of the decoration box,
1000 // set the 'y' position to the top of the line.
1001 // The SetGrabHandleImage() method will change the orientation.
1002 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
1004 if( grabHandle.actor )
1006 grabHandle.actor.SetProperty( Actor::Property::POSITION, Vector2( grabHandle.position.x + floor( 0.5f * mCursorWidth ) + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f ),
1007 yLocalPosition + ( mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f ) ) );
1011 void SetSelectionHandlePosition( HandleType type )
1013 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
1015 // Reference to the selection handle.
1016 HandleImpl& handle = mHandle[type];
1018 // Transforms the handle position into world coordinates.
1019 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1020 // as it's transforming the handle's position set by the text-controller and not
1021 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1022 // retrieves the position of the center of the actor but the handle's position set
1023 // by the text controller is not the center of the actor.
1024 Vector2 handleWorldPosition;
1025 CalculateHandleWorldCoordinates( handle, handleWorldPosition );
1027 // Whether to flip the handle (horizontally).
1028 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1030 // Whether to flip the handles if they are crossed.
1031 bool crossFlip = false;
1032 if( mFlipSelectionHandlesOnCross || !mIsHandlePanning )
1034 crossFlip = mIsHandleCurrentlyCrossed;
1037 // Whether the handle was crossed before start the panning.
1038 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1040 // Does not flip if both conditions are true (double flip)
1041 flipHandle = flipHandle != ( crossFlip || isHandlePreviouslyCrossed );
1043 // Will flip the handles vertically if the user prefers it.
1044 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1046 if( crossFlip || isHandlePreviouslyCrossed )
1048 if( isPrimaryHandle )
1050 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1054 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1058 // Check if the selection handle exceeds the boundaries of the decoration box.
1059 const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
1060 const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
1062 // Does not flip if both conditions are true (double flip)
1063 flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
1067 if( handle.actor && !handle.horizontallyFlipped )
1069 // Change the anchor point to flip the image.
1070 handle.actor.SetProperty( Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
1072 handle.horizontallyFlipped = true;
1077 if( handle.actor && handle.horizontallyFlipped )
1079 // Reset the anchor point.
1080 handle.actor.SetProperty( Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
1082 handle.horizontallyFlipped = false;
1086 // Whether to flip the handle vertically.
1087 handle.verticallyFlipped = ( verticallyFlippedPreferred &&
1088 ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
1089 ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
1091 // The primary selection handle 'y' position in local coords.
1092 // If the handle exceeds the bottom of the decoration box,
1093 // set the 'y' position to the top of the line.
1094 // The SetHandleImage() method will change the orientation.
1095 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1099 handle.actor.SetProperty( Actor::Property::POSITION, Vector2( handle.position.x + ( mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f ),
1100 yLocalPosition + ( mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f ) ) );
1104 void SetHandleImage( HandleType type )
1106 HandleImpl& handle = mHandle[type];
1108 HandleType markerType = HANDLE_TYPE_COUNT;
1109 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1110 if( LEFT_SELECTION_HANDLE == type )
1112 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1113 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1115 else if( RIGHT_SELECTION_HANDLE == type )
1117 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1118 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1121 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1124 const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1126 handle.actor.SetImage( mHandleImages[type][imageType] );
1129 if( HANDLE_TYPE_COUNT != markerType )
1131 if( handle.markerActor )
1133 const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
1134 handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
1138 // Whether to flip the handle vertically.
1141 handle.actor.SetProperty( Actor::Property::ORIENTATION, Quaternion( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS ) );
1145 void CreateHighlight()
1147 if( !mHighlightActor )
1149 mHighlightActor = Actor::New();
1151 mHighlightActor.SetProperty( Dali::Actor::Property::NAME, "HighlightActor" );
1152 mHighlightActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
1153 mHighlightActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
1154 mHighlightActor.SetProperty( Actor::Property::COLOR, mHighlightColor );
1155 mHighlightActor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_COLOR );
1158 // Add the highlight box telling the controller it needs clipping.
1159 mController.AddDecoration( mHighlightActor, true );
1162 void UpdateHighlight()
1164 if ( mHighlightActor )
1166 // Sets the position of the highlight actor inside the decorator.
1167 mHighlightActor.SetProperty( Actor::Property::POSITION, Vector2( mHighlightPosition.x + mHighlightOutlineOffset,
1168 mHighlightPosition.y + mHighlightOutlineOffset ) );
1170 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1171 if( 0u != numberOfQuads )
1173 // Set the size of the highlighted text to the actor.
1174 mHighlightActor.SetProperty( Actor::Property::SIZE, mHighlightSize );
1176 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1177 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1178 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1180 Vector<Vector2> vertices;
1181 Vector<unsigned short> indices;
1183 vertices.Reserve( 4u * numberOfQuads );
1184 indices.Reserve( 6u * numberOfQuads );
1186 // Index to the vertex.
1187 unsigned int v = 0u;
1189 // Traverse all quads.
1190 for( Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1191 endIt = mHighlightQuadList.End();
1195 const Vector4& quad = *it;
1200 vertex.x = quad.x - offsetX;
1201 vertex.y = quad.y - offsetY;
1202 vertices.PushBack( vertex );
1205 vertex.x = quad.z - offsetX;
1206 vertex.y = quad.y - offsetY;
1207 vertices.PushBack( vertex );
1209 // bottom-left (v+2)
1210 vertex.x = quad.x - offsetX;
1211 vertex.y = quad.w - offsetY;
1212 vertices.PushBack( vertex );
1214 // bottom-right (v+3)
1215 vertex.x = quad.z - offsetX;
1216 vertex.y = quad.w - offsetY;
1217 vertices.PushBack( vertex );
1219 // triangle A (3, 1, 0)
1220 indices.PushBack( v + 3 );
1221 indices.PushBack( v + 1 );
1222 indices.PushBack( v );
1224 // triangle B (0, 2, 3)
1225 indices.PushBack( v );
1226 indices.PushBack( v + 2 );
1227 indices.PushBack( v + 3 );
1230 if( ! mQuadVertices )
1232 mQuadVertices = VertexBuffer::New( mQuadVertexFormat );
1235 mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1237 if( !mQuadGeometry )
1239 mQuadGeometry = Geometry::New();
1240 mQuadGeometry.AddVertexBuffer( mQuadVertices );
1242 mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1244 if( !mHighlightRenderer )
1246 mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1247 mHighlightActor.AddRenderer( mHighlightRenderer );
1251 mHighlightQuadList.Clear();
1253 if( mHighlightRenderer )
1255 mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1260 void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1262 GestureState state = gesture.GetState();
1263 if( GestureState::STARTED == state )
1265 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1267 handle.globalPosition.x = handle.position.x;
1268 handle.globalPosition.y = handle.position.y;
1271 const Vector2& displacement = gesture.GetDisplacement();
1272 handle.grabDisplacementX += displacement.x;
1273 handle.grabDisplacementY += ( handle.verticallyFlipped ? -displacement.y : displacement.y );
1275 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1276 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1277 const float yVerticallyFlippedCorrected = y - ( handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f );
1279 if( ( GestureState::STARTED == state ) ||
1280 ( GestureState::CONTINUING == state ) )
1283 mController.GetTargetSize( targetSize );
1285 if( mHorizontalScrollingEnabled &&
1286 ( x < mScrollThreshold ) )
1288 mScrollDirection = SCROLL_RIGHT;
1289 mHandleScrolling = type;
1292 else if( mHorizontalScrollingEnabled &&
1293 ( x > targetSize.width - mScrollThreshold ) )
1295 mScrollDirection = SCROLL_LEFT;
1296 mHandleScrolling = type;
1299 else if( mVerticalScrollingEnabled &&
1300 ( yVerticallyFlippedCorrected < mScrollThreshold ) )
1302 mScrollDirection = SCROLL_TOP;
1303 mHandleScrolling = type;
1306 else if( mVerticalScrollingEnabled &&
1307 ( yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold ) )
1309 mScrollDirection = SCROLL_BOTTOM;
1310 mHandleScrolling = type;
1315 mHandleScrolling = HANDLE_TYPE_COUNT;
1317 mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1320 mIsHandlePanning = true;
1322 else if( ( GestureState::FINISHED == state ) ||
1323 ( GestureState::CANCELLED == state ) )
1326 ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1328 mNotifyEndOfScroll = false;
1329 mHandleScrolling = HANDLE_TYPE_COUNT;
1331 mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1335 mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1340 handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1342 handle.pressed = false;
1344 mIsHandlePanning = false;
1348 void OnPan( Actor actor, const PanGesture& gesture )
1350 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1351 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1352 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1354 if( actor == grabHandle.grabArea )
1356 DoPan( grabHandle, GRAB_HANDLE, gesture );
1358 else if( actor == primarySelectionHandle.grabArea )
1360 DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1362 else if( actor == secondarySelectionHandle.grabArea )
1364 DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1368 bool OnGrabHandleTouched( Actor actor, const TouchEvent& touch )
1370 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1372 // Switch between pressed/release grab-handle images
1373 if( touch.GetPointCount() > 0 &&
1376 const PointState::Type state = touch.GetState( 0 );
1378 if( PointState::DOWN == state )
1380 grabHandle.pressed = true;
1382 else if( ( PointState::UP == state ) ||
1383 ( PointState::INTERRUPTED == state ) )
1385 grabHandle.pressed = false;
1388 SetHandleImage( GRAB_HANDLE );
1394 bool OnHandleOneTouched( Actor actor, const TouchEvent& touch )
1396 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1398 // Switch between pressed/release selection handle images
1399 if( touch.GetPointCount() > 0 &&
1400 primarySelectionHandle.actor )
1402 const PointState::Type state = touch.GetState( 0 );
1404 if( PointState::DOWN == state )
1406 primarySelectionHandle.pressed = true;
1407 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1409 else if( ( PointState::UP == state ) ||
1410 ( PointState::INTERRUPTED == state ) )
1412 primarySelectionHandle.pressed = false;
1413 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1414 mIsHandlePanning = false;
1415 mHandleReleased = LEFT_SELECTION_HANDLE;
1418 SetHandleImage( LEFT_SELECTION_HANDLE );
1424 bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch )
1426 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1428 // Switch between pressed/release selection handle images
1429 if( touch.GetPointCount() > 0 &&
1430 secondarySelectionHandle.actor )
1432 const PointState::Type state = touch.GetState( 0 );
1434 if( PointState::DOWN == state )
1436 secondarySelectionHandle.pressed = true;
1437 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1439 else if( ( PointState::UP == state ) ||
1440 ( PointState::INTERRUPTED == state ) )
1442 secondarySelectionHandle.pressed = false;
1443 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1444 mIsHandlePanning = false;
1445 mHandleReleased = RIGHT_SELECTION_HANDLE;
1448 SetHandleImage( RIGHT_SELECTION_HANDLE );
1454 void HandleResetPosition( PropertyNotification& source )
1456 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1458 if( grabHandle.active )
1460 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1461 SetGrabHandlePosition();
1463 // Sets the grab handle image according if it's pressed, flipped, etc.
1464 SetHandleImage( GRAB_HANDLE );
1468 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1469 SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1471 // Sets the primary handle image according if it's pressed, flipped, etc.
1472 SetHandleImage( LEFT_SELECTION_HANDLE );
1474 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1475 SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1477 // Sets the secondary handle image according if it's pressed, flipped, etc.
1478 SetHandleImage( RIGHT_SELECTION_HANDLE );
1482 void SetupActiveLayerPropertyNotifications()
1489 // Vertical notifications.
1491 // Disconnect any previous connected callback.
1492 if( mHandleVerticalLessThanNotification )
1494 mHandleVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1495 mActiveLayer.RemovePropertyNotification( mHandleVerticalLessThanNotification );
1498 if( mHandleVerticalGreaterThanNotification )
1500 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1501 mActiveLayer.RemovePropertyNotification( mHandleVerticalGreaterThanNotification );
1504 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1505 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1506 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1508 if( grabHandle.active )
1510 if( grabHandle.verticallyFlipped )
1512 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1513 mHandleVerticalGreaterThanNotification.Reset();
1515 // The vertical distance from the center of the active layer to the top edje of the display.
1516 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1518 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1519 LessThanCondition( mBoundingBox.y + topHeight ) );
1521 // Notifies the change from false to true and from true to false.
1522 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1524 // Connects the signals with the callbacks.
1525 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1529 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1530 mHandleVerticalLessThanNotification.Reset();
1532 // The vertical distance from the center of the active layer to the bottom edje of the display.
1533 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1535 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1536 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1538 // Notifies the change from false to true and from true to false.
1539 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1541 // Connects the signals with the callbacks.
1542 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1545 else // The selection handles are active
1547 if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1549 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1550 mHandleVerticalGreaterThanNotification.Reset();
1552 // The vertical distance from the center of the active layer to the top edje of the display.
1553 const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1555 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1556 LessThanCondition( mBoundingBox.y + topHeight ) );
1558 // Notifies the change from false to true and from true to false.
1559 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1561 // Connects the signals with the callbacks.
1562 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1564 else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1566 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1567 mHandleVerticalLessThanNotification.Reset();
1569 // The vertical distance from the center of the active layer to the bottom edje of the display.
1570 const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1571 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1573 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1574 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1576 // Notifies the change from false to true and from true to false.
1577 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1579 // Connects the signals with the callbacks.
1580 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1584 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1586 // The vertical distance from the center of the active layer to the top edje of the display.
1587 const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1588 -primaryHandle.position.y + primaryHandle.size.height :
1589 -secondaryHandle.position.y + secondaryHandle.size.height );
1591 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1592 LessThanCondition( mBoundingBox.y + topHeight ) );
1594 // Notifies the change from false to true and from true to false.
1595 mHandleVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1597 // Connects the signals with the callbacks.
1598 mHandleVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1600 // The vertical distance from the center of the active layer to the bottom edje of the display.
1601 const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped ?
1602 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1603 primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1605 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1606 GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1608 // Notifies the change from false to true and from true to false.
1609 mHandleVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1611 // Connects the signals with the callbacks.
1612 mHandleVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1616 // Horizontal notifications.
1618 // Disconnect any previous connected callback.
1619 if( mHandleHorizontalLessThanNotification )
1621 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1622 mActiveLayer.RemovePropertyNotification( mHandleHorizontalLessThanNotification );
1625 if( mHandleHorizontalGreaterThanNotification )
1627 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1628 mActiveLayer.RemovePropertyNotification( mHandleHorizontalGreaterThanNotification );
1631 if( primaryHandle.active || secondaryHandle.active )
1633 // The horizontal distance from the center of the active layer to the left edje of the display.
1634 const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1635 -secondaryHandle.position.x + secondaryHandle.size.width );
1637 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1638 LessThanCondition( mBoundingBox.x + leftWidth ) );
1640 // Notifies the change from false to true and from true to false.
1641 mHandleHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1643 // Connects the signals with the callbacks.
1644 mHandleHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1646 // The horizontal distance from the center of the active layer to the right edje of the display.
1647 const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1648 secondaryHandle.position.x + secondaryHandle.size.width );
1650 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1651 GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1653 // Notifies the change from false to true and from true to false.
1654 mHandleHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1656 // Connects the signals with the callbacks.
1657 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1663 float AlternatePopUpPositionRelativeToCursor( bool topBottom )
1665 float alternativePosition = 0.0f;
1667 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1669 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1670 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1671 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1672 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1674 if( primaryHandle.active || secondaryHandle.active )
1676 float handleY = 0.f;
1677 float maxHandleHeight = 0.f;
1679 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1680 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1682 if( primaryVisible && secondaryVisible )
1684 handleY = std::max( primaryHandle.position.y, secondaryHandle.position.y );
1685 maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1687 else if( primaryVisible && !secondaryVisible )
1689 handleY = primaryHandle.position.y;
1690 maxHandleHeight = primaryHandle.size.height;
1692 else if( !primaryVisible && secondaryVisible )
1694 handleY = secondaryHandle.position.y;
1695 maxHandleHeight = secondaryHandle.size.height;
1698 alternativePosition = handleY + ( topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight );
1702 alternativePosition = cursor.position.y + ( topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height );
1705 return alternativePosition;
1708 void PopUpLeavesTopBoundary( PropertyNotification& source )
1710 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1712 // Sets the position of the popup below.
1713 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION_Y, floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, true ) ) );
1716 void PopUpLeavesBottomBoundary( PropertyNotification& source )
1718 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1720 // Sets the position of the popup above.
1721 mCopyPastePopup.actor.SetProperty( Actor::Property::POSITION_Y, floorf( CalculateVerticalPopUpPosition( 0.5f * popupHeight, false ) ) );
1724 void SetUpPopupPositionNotifications( const Vector3& popupHalfSize )
1726 // Disconnect any previous connected callback.
1727 if( mPopupTopExceedNotification )
1729 mPopupTopExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1730 mCopyPastePopup.actor.RemovePropertyNotification( mPopupTopExceedNotification );
1733 if( mPopupBottomExceedNotification )
1735 mPopupBottomExceedNotification.NotifySignal().Disconnect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1736 mCopyPastePopup.actor.RemovePropertyNotification( mPopupBottomExceedNotification );
1739 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1741 // Exceeding vertical boundary
1743 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1744 LessThanCondition( mBoundingBox.y + popupHalfSize.height ) );
1746 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1747 GreaterThanCondition( mBoundingBox.w - popupHalfSize.height ) );
1749 // Notifies the change from false to true and from true to false.
1750 mPopupTopExceedNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1751 mPopupBottomExceedNotification.SetNotifyMode( PropertyNotification::NOTIFY_ON_CHANGED );
1753 mPopupTopExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesTopBoundary );
1754 mPopupBottomExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesBottomBoundary );
1757 void SetHandleImage( HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName )
1759 ImageDimensions dimensions = Dali::GetOriginalImageSize( imageFileName );
1761 HandleImpl& handle = mHandle[handleType];
1762 handle.size = Size( dimensions.GetWidth(), dimensions.GetHeight() );
1764 mHandleImages[handleType][handleImageType] = imageFileName;
1767 void SetScrollThreshold( float threshold )
1769 mScrollThreshold = threshold;
1772 float GetScrollThreshold() const
1774 return mScrollThreshold;
1777 void SetScrollSpeed( float speed )
1779 mScrollSpeed = speed;
1780 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1783 float GetScrollSpeed() const
1785 return mScrollSpeed;
1788 void NotifyEndOfScroll()
1794 mNotifyEndOfScroll = true;
1799 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1801 * It only starts the timer if it's already created.
1803 void StartScrollTimer()
1807 mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1808 mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1811 if( !mScrollTimer.IsRunning() )
1813 mScrollTimer.Start();
1818 * Stops the timer used to scroll the text.
1820 void StopScrollTimer()
1824 mScrollTimer.Stop();
1829 * Callback called by the timer used to scroll the text.
1831 * It calculates and sets a new scroll position.
1833 bool OnScrollTimerTick()
1835 if( HANDLE_TYPE_COUNT != mHandleScrolling )
1840 switch( mScrollDirection )
1844 x = mScrollDistance;
1849 x = -mScrollDistance;
1854 y = mScrollDistance;
1859 y = -mScrollDistance;
1866 mController.DecorationEvent( mHandleScrolling,
1875 ControllerInterface& mController;
1877 TapGestureDetector mTapDetector;
1878 PanGestureDetector mPanDetector;
1879 LongPressGestureDetector mLongPressDetector;
1881 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1882 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1884 Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else.
1885 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1886 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1887 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1888 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1889 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1890 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1891 Control mPrimaryCursor;
1892 Control mSecondaryCursor;
1894 Actor mHighlightActor; ///< Actor to display highlight
1895 Renderer mHighlightRenderer;
1896 Shader mHighlightShader; ///< Shader used for highlight
1897 Property::Map mQuadVertexFormat;
1898 PopupImpl mCopyPastePopup;
1899 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1900 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1902 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1903 Vector4 mHandleColor;
1905 CursorImpl mCursor[CURSOR_COUNT];
1906 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1908 VertexBuffer mQuadVertices;
1909 Geometry mQuadGeometry;
1910 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1912 Vector4 mBoundingBox; ///< The bounding box in world coords.
1913 Vector4 mHighlightColor; ///< Color of the highlight
1914 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1915 Size mHighlightSize; ///< The size of the highlighted text.
1916 Size mControlSize; ///< The control's size. Set by the Relayout.
1917 float mHighlightOutlineOffset; ///< The outline's offset.
1919 unsigned int mActiveCursor;
1920 unsigned int mCursorBlinkInterval;
1921 float mCursorBlinkDuration;
1922 float mCursorWidth; ///< The width of the cursors in pixels.
1923 HandleType mHandleScrolling; ///< The handle which is scrolling.
1924 HandleType mHandleReleased; ///< The last handle released.
1925 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1926 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1927 float mScrollSpeed; ///< The scroll speed in pixels per second.
1928 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1929 int mTextDepth; ///< The depth used to render the text.
1931 bool mActiveCopyPastePopup : 1;
1932 bool mPopupSetNewPosition : 1;
1933 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1934 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1935 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1936 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1937 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1938 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1939 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1940 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1941 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1942 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1943 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1944 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1945 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1946 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1947 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1950 DecoratorPtr Decorator::New( ControllerInterface& controller,
1951 TextSelectionPopupCallbackInterface& callbackInterface )
1953 return DecoratorPtr( new Decorator( controller,
1954 callbackInterface ) );
1957 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1959 LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1962 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1964 WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1967 void Decorator::Relayout( const Vector2& size )
1969 mImpl->Relayout( size );
1972 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1974 mImpl->UpdatePositions( scrollOffset );
1979 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1981 mImpl->mActiveCursor = activeCursor;
1984 unsigned int Decorator::GetActiveCursor() const
1986 return mImpl->mActiveCursor;
1989 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1991 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1993 cursorImpl.position.x = x;
1994 cursorImpl.position.y = y;
1995 cursorImpl.cursorHeight = cursorHeight;
1996 cursorImpl.lineHeight = lineHeight;
1999 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
2001 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2003 x = cursorImpl.position.x;
2004 y = cursorImpl.position.y;
2005 cursorHeight = cursorImpl.cursorHeight;
2006 lineHeight = cursorImpl.lineHeight;
2009 const Vector2& Decorator::GetPosition( Cursor cursor ) const
2011 return mImpl->mCursor[cursor].position;
2014 void Decorator::SetGlyphOffset( Cursor cursor, float glyphOffset )
2016 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2018 cursorImpl.glyphOffset = glyphOffset;
2021 const float Decorator::GetGlyphOffset( Cursor cursor) const
2023 return mImpl->mCursor[cursor].glyphOffset;
2026 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
2028 mImpl->mCursor[cursor].color = color;
2031 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
2033 return mImpl->mCursor[cursor].color;
2036 void Decorator::StartCursorBlink()
2038 if ( !mImpl->mCursorBlinkTimer )
2040 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
2041 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
2044 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
2046 mImpl->mCursorBlinkTimer.Start();
2050 void Decorator::StopCursorBlink()
2052 if ( mImpl->mCursorBlinkTimer )
2054 mImpl->mCursorBlinkTimer.Stop();
2057 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2060 void Decorator::DelayCursorBlink()
2062 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2063 mImpl->mDelayCursorBlink = true;
2066 void Decorator::SetCursorBlinkInterval( float seconds )
2068 mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
2071 float Decorator::GetCursorBlinkInterval() const
2073 return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
2076 void Decorator::SetCursorBlinkDuration( float seconds )
2078 mImpl->mCursorBlinkDuration = seconds;
2081 float Decorator::GetCursorBlinkDuration() const
2083 return mImpl->mCursorBlinkDuration;
2086 void Decorator::SetCursorWidth( int width )
2088 mImpl->mCursorWidth = static_cast<float>( width );
2091 int Decorator::GetCursorWidth() const
2093 return static_cast<int>( mImpl->mCursorWidth );
2098 void Decorator::SetHandleActive( HandleType handleType, bool active )
2100 mImpl->mHandle[handleType].active = active;
2104 if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
2106 mImpl->mIsHandlePreviouslyCrossed = false;
2109 // TODO: this is a work-around.
2110 // The problem is the handle actor does not receive the touch event with the Interrupt
2111 // state when the power button is pressed and the application goes to background.
2112 mImpl->mHandle[handleType].pressed = false;
2113 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2114 ImageView imageView = mImpl->mHandle[handleType].actor;
2115 if( imageReleased && imageView )
2117 imageView.SetImage( mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED] );
2123 bool Decorator::IsHandleActive( HandleType handleType ) const
2125 return mImpl->mHandle[handleType].active ;
2128 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName )
2130 mImpl->SetHandleImage( handleType, handleImageType, imageFileName );
2133 const std::string& Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
2135 return mImpl->mHandleImages[handleType][handleImageType];
2138 void Decorator::SetHandleColor( const Vector4& color )
2140 mImpl->mHandleColor = color;
2143 const Vector4& Decorator::GetHandleColor() const
2145 return mImpl->mHandleColor;
2148 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
2150 // Adjust handle's displacement
2151 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2153 handle.position.x = x;
2154 handle.position.y = y;
2155 handle.lineHeight = height;
2157 if( mImpl->mSmoothHandlePanEnabled )
2159 handle.grabDisplacementX = 0.f;
2160 handle.grabDisplacementY = 0.f;
2164 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
2166 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2168 x = handle.position.x;
2169 y = handle.position.y;
2170 height = handle.lineHeight;
2173 const Vector2& Decorator::GetPosition( HandleType handleType ) const
2175 return mImpl->mHandle[handleType].position;
2178 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
2180 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2183 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
2185 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2188 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
2190 mImpl->mFlipSelectionHandlesOnCross = enable;
2193 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
2195 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2196 mImpl->mFlipLeftSelectionHandleDirection = left;
2197 mImpl->mFlipRightSelectionHandleDirection = right;
2200 void Decorator::AddHighlight( unsigned int index, const Vector4& quad )
2202 *( mImpl->mHighlightQuadList.Begin() + index ) = quad;
2205 void Decorator::SetHighLightBox( const Vector2& position, const Size& size, float outlineOffset )
2207 mImpl->mHighlightPosition = position;
2208 mImpl->mHighlightSize = size;
2209 mImpl->mHighlightOutlineOffset = outlineOffset;
2212 void Decorator::ClearHighlights()
2214 mImpl->mHighlightQuadList.Clear();
2215 mImpl->mHighlightPosition = Vector2::ZERO;
2216 mImpl->mHighlightOutlineOffset = 0.f;
2219 void Decorator::ResizeHighlightQuads( unsigned int numberOfQuads )
2221 mImpl->mHighlightQuadList.Resize( numberOfQuads );
2224 void Decorator::SetHighlightColor( const Vector4& color )
2226 mImpl->mHighlightColor = color;
2229 const Vector4& Decorator::GetHighlightColor() const
2231 return mImpl->mHighlightColor;
2234 void Decorator::SetHighlightActive( bool active )
2236 mImpl->mIsHighlightBoxActive = active;
2239 bool Decorator::IsHighlightActive() const
2241 return mImpl->mIsHighlightBoxActive;
2244 bool Decorator::IsHighlightVisible() const
2246 return ( mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent() );
2249 void Decorator::SetTextDepth( int textDepth )
2251 mImpl->mTextDepth = textDepth;
2254 void Decorator::SetPopupActive( bool active )
2256 mImpl->mActiveCopyPastePopup = active;
2259 bool Decorator::IsPopupActive() const
2261 return mImpl->mActiveCopyPastePopup;
2264 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
2266 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2268 if ( !mImpl->mCopyPastePopup.actor )
2270 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
2271 #ifdef DECORATOR_DEBUG
2272 mImpl->mCopyPastePopup.actor.SetProperty( Dali::Actor::Property::NAME,"mCopyPastePopup");
2274 mImpl->mCopyPastePopup.actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
2275 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl, &Decorator::Impl::SetPopupPosition ); // Position popup after size negotiation
2278 mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
2281 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2283 return mImpl->mEnabledPopupButtons;
2288 void Decorator::SetScrollThreshold( float threshold )
2290 mImpl->SetScrollThreshold( threshold );
2293 float Decorator::GetScrollThreshold() const
2295 return mImpl->GetScrollThreshold();
2298 void Decorator::SetScrollSpeed( float speed )
2300 mImpl->SetScrollSpeed( speed );
2303 float Decorator::GetScrollSpeed() const
2305 return mImpl->GetScrollSpeed();
2308 void Decorator::NotifyEndOfScroll()
2310 mImpl->NotifyEndOfScroll();
2313 void Decorator::SetHorizontalScrollEnabled( bool enable )
2315 mImpl->mHorizontalScrollingEnabled = enable;
2318 bool Decorator::IsHorizontalScrollEnabled() const
2320 return mImpl->mHorizontalScrollingEnabled;
2323 void Decorator::SetVerticalScrollEnabled( bool enable )
2325 mImpl->mVerticalScrollingEnabled = enable;
2328 bool Decorator::IsVerticalScrollEnabled() const
2330 return mImpl->mVerticalScrollingEnabled;
2333 void Decorator::SetSmoothHandlePanEnabled( bool enable )
2335 mImpl->mSmoothHandlePanEnabled = enable;
2338 bool Decorator::IsSmoothHandlePanEnabled() const
2340 return mImpl->mSmoothHandlePanEnabled;
2343 Decorator::~Decorator()
2348 Decorator::Decorator( ControllerInterface& controller,
2349 TextSelectionPopupCallbackInterface& callbackInterface )
2352 mImpl = new Decorator::Impl( controller, callbackInterface );
2357 } // namespace Toolkit