2 * Copyright (c) 2021 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/devel-api/adaptor-framework/image-loading.h>
23 #include <dali/devel-api/common/stage.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/actors/layer.h>
26 #include <dali/public-api/adaptor-framework/timer.h>
27 #include <dali/public-api/events/pan-gesture.h>
28 #include <dali/public-api/events/touch-event.h>
29 #include <dali/public-api/object/property-notification.h>
30 #include <dali/public-api/rendering/geometry.h>
31 #include <dali/public-api/rendering/renderer.h>
34 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
35 #include <dali-toolkit/internal/controls/image-view/image-view-impl.h>
36 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
37 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
40 #define DECORATOR_DEBUG
50 #ifdef DECORATOR_DEBUG
51 Integration::Log::Filter* gLogFilter(Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR"));
54 } // namespace Internal
60 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE(1.25f, 1.5f, 1.0f);
61 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE(1.25f, 1.5f, 1.0f);
62 const Dali::Vector3 ACTIVE_LAYER_ANCHOR_POINT(0.5f, 0.5f, 0.5f);
64 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.
66 const Dali::Vector4 HANDLE_COLOR(0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f);
68 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
69 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
70 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
72 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
73 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.
74 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
76 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
78 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
80 const float POPUP_PADDING = 2.f; ///< Padding space between the highlight box and the text's popup.
82 typedef Dali::Vector<Dali::Vector4> QuadContainer;
85 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
86 * @param[in] boundingRectangle local bounding
87 * @param[out] Vector4 World coordinate bounding Box.
89 void LocalToWorldCoordinatesBoundingBox(const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox)
91 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
92 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
94 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
95 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
97 boundingBox = Dali::Vector4(originX,
99 originX + boundingRectangle.width,
100 originY + boundingRectangle.height);
103 void WorldToLocalCoordinatesBoundingBox(const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle)
105 // Convert to local coordinates and store as a Dali::Rect.
106 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
108 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
109 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
110 boundingRectangle.width = boundingBox.z - boundingBox.x;
111 boundingRectangle.height = boundingBox.w - boundingBox.y;
114 } // end of namespace
122 struct Decorator::Impl : public ConnectionTracker
136 : color(Dali::Color::BLACK),
158 grabDisplacementX(0.f),
159 grabDisplacementY(0.f),
161 horizontallyVisible(false),
162 verticallyVisible(false),
164 verticallyFlippedPreferred(false),
165 horizontallyFlipped(false),
166 verticallyFlipped(false),
167 verticallyFlippedOnTouch(false)
173 ImageView markerActor;
176 Vector2 globalPosition;
178 float lineHeight; ///< Not the handle height
179 float grabDisplacementX;
180 float grabDisplacementY;
182 bool horizontallyVisible : 1;
183 bool verticallyVisible : 1;
185 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
186 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
187 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
188 bool verticallyFlippedOnTouch : 1; ///< Whether the handle is vertically flipped on touch.
198 TextSelectionPopup actor;
202 Impl(ControllerInterface& controller,
203 TextSelectionPopupCallbackInterface& callbackInterface)
204 : mController(controller),
205 mEnabledPopupButtons(TextSelectionPopup::NONE),
206 mTextSelectionPopupCallbackInterface(callbackInterface),
207 mHandleColor(HANDLE_COLOR),
209 mHighlightColor(LIGHT_BLUE),
210 mHighlightPosition(Vector2::ZERO),
211 mHighlightSize(Vector2::ZERO),
212 mControlSize(Vector2::ZERO),
213 mHighlightOutlineOffset(0.f),
214 mActiveCursor(ACTIVE_CURSOR_NONE),
215 mCursorBlinkInterval(CURSOR_BLINK_INTERVAL),
216 mCursorBlinkDuration(0.0f),
217 mCursorWidth(CURSOR_WIDTH),
218 mHandleScrolling(HANDLE_TYPE_COUNT),
219 mHandleReleased(HANDLE_TYPE_COUNT),
220 mScrollDirection(SCROLL_NONE),
221 mScrollThreshold(SCROLL_THRESHOLD),
222 mScrollSpeed(SCROLL_SPEED),
223 mScrollDistance(SCROLL_DISTANCE),
225 mActiveCopyPastePopup(false),
226 mPopupSetNewPosition(true),
227 mCursorBlinkStatus(true),
228 mDelayCursorBlink(false),
229 mPrimaryCursorVisible(false),
230 mSecondaryCursorVisible(false),
231 mFlipSelectionHandlesOnCross(false),
232 mFlipLeftSelectionHandleDirection(false),
233 mFlipRightSelectionHandleDirection(false),
234 mIsHandlePanning(false),
235 mIsHandleCurrentlyCrossed(false),
236 mIsHandlePreviouslyCrossed(false),
237 mNotifyEndOfScroll(false),
238 mHorizontalScrollingEnabled(false),
239 mVerticalScrollingEnabled(false),
240 mSmoothHandlePanEnabled(false),
241 mIsHighlightBoxActive(false),
242 mHidePrimaryCursorAndGrabHandle(false)
244 mQuadVertexFormat["aPosition"] = Property::VECTOR2;
245 mHighlightShader = Shader::New(SHADER_TEXT_DECORATOR_SHADER_VERT, SHADER_TEXT_DECORATOR_SHADER_FRAG);
250 * Relayout of the decorations owned by the decorator.
251 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
253 void Relayout(const Vector2& size)
257 // TODO - Remove this if nothing is active
260 // Show or hide the cursors
265 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
266 mPrimaryCursorVisible = (!mHidePrimaryCursorAndGrabHandle) && ((mControlSize.width - (cursor.position.x + mCursorWidth) > -Math::MACHINE_EPSILON_1000) &&
267 (cursor.position.x > -Math::MACHINE_EPSILON_1000) &&
268 (mControlSize.height - (cursor.position.y + cursor.cursorHeight) > -Math::MACHINE_EPSILON_1000) &&
269 (cursor.position.y > -Math::MACHINE_EPSILON_1000));
270 if(mPrimaryCursorVisible)
272 mPrimaryCursor.SetProperty(Actor::Property::POSITION, Vector2(cursor.position.x, cursor.position.y));
273 mPrimaryCursor.SetProperty(Actor::Property::SIZE, Size(mCursorWidth, cursor.cursorHeight));
275 mPrimaryCursor.SetProperty(Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus);
279 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
280 mSecondaryCursorVisible = ((mControlSize.width - (cursor.position.x + mCursorWidth) > -Math::MACHINE_EPSILON_1000) &&
281 (cursor.position.x > -Math::MACHINE_EPSILON_1000) &&
282 (mControlSize.height - (cursor.position.y + cursor.cursorHeight) > -Math::MACHINE_EPSILON_1000) &&
283 (cursor.position.y > -Math::MACHINE_EPSILON_1000));
284 if(mSecondaryCursorVisible)
286 mSecondaryCursor.SetProperty(Actor::Property::POSITION, Vector2(cursor.position.x, cursor.position.y));
287 mSecondaryCursor.SetProperty(Actor::Property::SIZE, Size(mCursorWidth, cursor.cursorHeight));
289 mSecondaryCursor.SetProperty(Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus);
292 // Show or hide the grab handle
293 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
294 bool newGrabHandlePosition = false;
295 grabHandle.horizontallyVisible = false;
296 grabHandle.verticallyVisible = false;
297 if(grabHandle.active)
299 grabHandle.horizontallyVisible = ((mControlSize.width - (grabHandle.position.x + floor(0.5f * mCursorWidth)) > -Math::MACHINE_EPSILON_1000) &&
300 (grabHandle.position.x > -Math::MACHINE_EPSILON_1000));
301 grabHandle.verticallyVisible = (((mControlSize.height - grabHandle.lineHeight) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000) &&
302 (grabHandle.position.y > -Math::MACHINE_EPSILON_1000));
304 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible && (!mHidePrimaryCursorAndGrabHandle);
309 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
310 SetGrabHandlePosition();
312 // Sets the grab handle image according if it's pressed, flipped, etc.
313 SetHandleImage(GRAB_HANDLE);
315 newGrabHandlePosition = true;
320 grabHandle.actor.SetProperty(Actor::Property::VISIBLE, isVisible);
323 else if(grabHandle.actor)
325 grabHandle.actor.Unparent();
328 // Show or hide the selection handles/highlight
329 HandleImpl& primary = mHandle[LEFT_SELECTION_HANDLE];
330 HandleImpl& secondary = mHandle[RIGHT_SELECTION_HANDLE];
331 bool newPrimaryHandlePosition = false;
332 bool newSecondaryHandlePosition = false;
334 primary.horizontallyVisible = ((mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000) &&
335 (primary.position.x > -Math::MACHINE_EPSILON_1000));
336 primary.verticallyVisible = (((mControlSize.height - primary.lineHeight) - primary.position.y > -Math::MACHINE_EPSILON_1000) &&
337 (primary.position.y + (primary.verticallyFlipped ? 0.f : primary.lineHeight) > -Math::MACHINE_EPSILON_1000));
338 secondary.horizontallyVisible = ((mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000) &&
339 (secondary.position.x > -Math::MACHINE_EPSILON_1000));
340 secondary.verticallyVisible = (((mControlSize.height - secondary.lineHeight) - secondary.position.y > -Math::MACHINE_EPSILON_1000) &&
341 (secondary.position.y + (secondary.verticallyFlipped ? 0.f : secondary.lineHeight) > -Math::MACHINE_EPSILON_1000));
343 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
344 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
346 if(primary.active || secondary.active)
348 if(primaryVisible || secondaryVisible)
350 CreateSelectionHandles();
354 SetSelectionHandlePosition(LEFT_SELECTION_HANDLE);
356 // Sets the primary handle image according if it's pressed, flipped, etc.
357 SetHandleImage(LEFT_SELECTION_HANDLE);
359 SetSelectionHandleMarkerSize(primary);
361 newPrimaryHandlePosition = true;
366 SetSelectionHandlePosition(RIGHT_SELECTION_HANDLE);
368 // Sets the secondary handle image according if it's pressed, flipped, etc.
369 SetHandleImage(RIGHT_SELECTION_HANDLE);
371 SetSelectionHandleMarkerSize(secondary);
373 newSecondaryHandlePosition = true;
379 primary.actor.SetProperty(Actor::Property::VISIBLE, primaryVisible);
383 secondary.actor.SetProperty(Actor::Property::VISIBLE, secondaryVisible);
390 primary.actor.Unparent();
394 secondary.actor.Unparent();
398 if(mIsHighlightBoxActive)
407 mHighlightActor.Unparent();
411 if(newGrabHandlePosition ||
412 newPrimaryHandlePosition ||
413 newSecondaryHandlePosition)
415 // Setup property notifications to find whether the handles leave the boundaries of the current display.
416 SetupActiveLayerPropertyNotifications();
419 if(mActiveCopyPastePopup &&
420 (primaryVisible || secondaryVisible))
423 mPopupSetNewPosition = true;
427 if(mCopyPastePopup.actor)
429 mCopyPastePopup.actor.HidePopup();
430 mPopupSetNewPosition = true;
435 void UpdatePositions(const Vector2& scrollOffset)
437 mCursor[PRIMARY_CURSOR].position += scrollOffset;
438 mCursor[SECONDARY_CURSOR].position += scrollOffset;
439 mHandle[GRAB_HANDLE].position += scrollOffset;
440 mHandle[LEFT_SELECTION_HANDLE].position += scrollOffset;
441 mHandle[RIGHT_SELECTION_HANDLE].position += scrollOffset;
442 mHighlightPosition += scrollOffset;
447 if(!mCopyPastePopup.actor)
452 if(!mCopyPastePopup.actor.GetParent())
454 mActiveLayer.Add(mCopyPastePopup.actor);
457 mCopyPastePopup.actor.RaiseAbove(mActiveLayer);
458 mCopyPastePopup.actor.ShowPopup();
461 float CalculateVerticalPopUpPosition(float halfHeight, bool preferBelow)
463 float yPosition = 0.f;
465 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
466 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
467 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
469 if(primaryHandle.active || secondaryHandle.active)
471 // The origin of the decorator's coordinate system in world coords.
472 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION) - mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * ACTIVE_LAYER_ANCHOR_POINT;
476 // Find out if there is enough space for the popup at the bottom.
477 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
478 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
480 float maxY = std::max(primaryBelowY, secondaryBelowY);
482 yPosition = halfHeight + maxY;
484 if(originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w)
486 // Does not fit below.
488 // Try to fit first below the non active handle. Otherwise above the active handle.
489 if(RIGHT_SELECTION_HANDLE == mHandleReleased)
491 if(primaryBelowY < secondaryBelowY)
493 yPosition = halfHeight + primaryBelowY;
497 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
500 else if(LEFT_SELECTION_HANDLE == mHandleReleased)
502 if(secondaryBelowY < primaryBelowY)
504 yPosition = halfHeight + secondaryBelowY;
508 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
512 // Check the handle is whithin the decoration box.
513 if(originWorldCoords.y + yPosition < mBoundingBox.y)
515 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
518 if(originWorldCoords.y + yPosition > mBoundingBox.w)
520 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
526 // Find out if there is enough space for the popup at the top.
527 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
528 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
530 float minY = std::min(primaryTopY, secondaryTopY);
532 yPosition = -halfHeight + minY;
534 } // ( primaryHandle.active || secondaryHandle.active )
535 else if(grabHandle.active)
539 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
543 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
550 void ConstrainPopupPosition(const Vector3& popupHalfSize)
552 // Check if the popup is within the boundaries of the decoration box.
554 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
556 // The origin of the decorator's coordinate system in world coords.
557 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION) - mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * ACTIVE_LAYER_ANCHOR_POINT;
559 // The popup's position in world coords.
560 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
562 if(popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x)
564 mCopyPastePopup.position.x += mBoundingBox.x - (popupPositionWorldCoords.x - popupHalfSize.width);
566 else if(popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z)
568 mCopyPastePopup.position.x += mBoundingBox.z - (popupPositionWorldCoords.x + popupHalfSize.width);
571 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
572 if(popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y)
574 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition(popupHalfSize.height, true); // true -> prefer to set the popup's position below.
578 void SetPopupPosition(Actor actor)
580 if(!mActiveCopyPastePopup)
585 // Retrieves the popup's size after relayout.
586 const Vector3 popupSize(mCopyPastePopup.actor.GetRelayoutSize(Dimension::WIDTH), mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT), 0.0f);
587 const Vector3 popupHalfSize = popupSize * 0.5f;
589 if(mPopupSetNewPosition)
591 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
592 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
593 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
595 if(primaryHandle.active || secondaryHandle.active)
597 const float minHandleXPosition = std::min(primaryHandle.position.x, secondaryHandle.position.x);
598 const float maxHandleXPosition = std::max(primaryHandle.position.x, secondaryHandle.position.x);
600 mCopyPastePopup.position.x = minHandleXPosition + ((maxHandleXPosition - minHandleXPosition) * 0.5f);
602 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - (primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING);
603 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - (secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING);
605 mCopyPastePopup.position.y = std::min(primaryY, secondaryY);
607 else if(grabHandle.active)
609 mCopyPastePopup.position.x = grabHandle.position.x;
611 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - (grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING);
613 } // mPopupSetNewPosition
615 // It may change the popup's position to fit within the decoration box.
616 ConstrainPopupPosition(popupHalfSize);
618 SetUpPopupPositionNotifications(popupHalfSize);
620 // Prevent pixel mis-alignment by rounding down.
621 mCopyPastePopup.position.x = floorf(mCopyPastePopup.position.x);
622 mCopyPastePopup.position.y = floorf(mCopyPastePopup.position.y);
624 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION, mCopyPastePopup.position);
625 mPopupSetNewPosition = false;
628 void CreateCursor(Control& cursor, const Vector4& color)
630 cursor = Control::New();
631 cursor.SetBackgroundColor(color);
632 cursor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
633 cursor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
636 // Add or Remove cursor(s) from parent
639 if(mActiveCursor == ACTIVE_CURSOR_NONE)
643 mPrimaryCursor.Unparent();
647 mSecondaryCursor.Unparent();
652 // Create Primary and or Secondary Cursor(s) if active and add to parent
653 if(mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
654 mActiveCursor == ACTIVE_CURSOR_BOTH)
658 CreateCursor(mPrimaryCursor, mCursor[PRIMARY_CURSOR].color);
659 #ifdef DECORATOR_DEBUG
660 mPrimaryCursor.SetProperty(Dali::Actor::Property::NAME, "PrimaryCursorActor");
664 if(!mPrimaryCursor.GetParent())
666 mActiveLayer.Add(mPrimaryCursor);
670 if(mActiveCursor == ACTIVE_CURSOR_BOTH)
672 if(!mSecondaryCursor)
674 CreateCursor(mSecondaryCursor, mCursor[SECONDARY_CURSOR].color);
675 #ifdef DECORATOR_DEBUG
676 mSecondaryCursor.SetProperty(Dali::Actor::Property::NAME, "SecondaryCursorActor");
680 if(!mSecondaryCursor.GetParent())
682 mActiveLayer.Add(mSecondaryCursor);
689 mSecondaryCursor.Unparent();
695 bool OnCursorBlinkTimerTick()
697 if(!mDelayCursorBlink)
702 mPrimaryCursor.SetProperty(Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus);
706 mSecondaryCursor.SetProperty(Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus);
709 mCursorBlinkStatus = !mCursorBlinkStatus;
714 mDelayCursorBlink = false;
722 // Will consume tap gestures on handles.
723 mTapDetector = TapGestureDetector::New();
725 // Will consume double tap gestures on handles.
726 mTapDetector.SetMaximumTapsRequired(2u);
728 // Will consume long press gestures on handles.
729 mLongPressDetector = LongPressGestureDetector::New();
731 // Detects pan gestures on handles.
732 mPanDetector = PanGestureDetector::New();
733 mPanDetector.DetectedSignal().Connect(this, &Decorator::Impl::OnPan);
736 void CreateActiveLayer()
740 mActiveLayer = Actor::New();
741 #ifdef DECORATOR_DEBUG
742 mActiveLayer.SetProperty(Actor::Property::NAME, "ActiveLayerActor");
745 mActiveLayer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
746 mActiveLayer.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
748 // Add the active layer telling the controller it doesn't need clipping.
749 mController.AddDecoration(mActiveLayer, false);
752 mActiveLayer.RaiseToTop();
755 void SetSelectionHandleMarkerSize(HandleImpl& handle)
757 if(handle.markerActor)
759 handle.markerActor.SetProperty(Actor::Property::SIZE, Vector2(0, handle.lineHeight));
763 void CreateGrabHandle()
765 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
766 if(!grabHandle.actor)
768 if(mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED].size())
770 grabHandle.actor = ImageView::New(mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED]);
771 GetImpl(grabHandle.actor).SetDepthIndex(DepthIndex::DECORATION);
772 grabHandle.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
774 // Area that Grab handle responds to, larger than actual handle so easier to move
775 #ifdef DECORATOR_DEBUG
776 grabHandle.actor.SetProperty(Dali::Actor::Property::NAME, "GrabHandleActor");
777 if(Dali::Internal::gLogFilter->IsEnabledFor(Debug::Verbose))
779 grabHandle.grabArea = Control::New();
780 Toolkit::Control control = Toolkit::Control::DownCast(grabHandle.grabArea);
781 control.SetBackgroundColor(Vector4(1.0f, 1.0f, 1.0f, 0.5f));
782 grabHandle.grabArea.SetProperty(Dali::Actor::Property::NAME, "GrabArea");
786 grabHandle.grabArea = Actor::New();
787 grabHandle.grabArea.SetProperty(Dali::Actor::Property::NAME, "GrabArea");
790 grabHandle.grabArea = Actor::New();
793 grabHandle.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
794 grabHandle.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
795 grabHandle.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
796 grabHandle.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_GRAB_HANDLE_RELATIVE_SIZE);
797 grabHandle.actor.Add(grabHandle.grabArea);
798 grabHandle.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
800 grabHandle.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnGrabHandleTouched);
802 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
803 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
804 mTapDetector.Attach(grabHandle.actor);
805 mLongPressDetector.Attach(grabHandle.actor);
807 // The grab handle's area is attached to the pan detector.
808 // The OnPan() method is connected to the signals emitted by the pan detector.
809 mPanDetector.Attach(grabHandle.grabArea);
811 mActiveLayer.Add(grabHandle.actor);
815 if(grabHandle.actor && !grabHandle.actor.GetParent())
817 mActiveLayer.Add(grabHandle.actor);
821 void CreateHandleMarker(HandleImpl& handle, const std::string& image, HandleType handleType)
825 handle.markerActor = ImageView::New(image);
826 handle.markerActor.SetProperty(Actor::Property::COLOR, mHandleColor);
827 handle.actor.Add(handle.markerActor);
829 handle.markerActor.SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
831 if(LEFT_SELECTION_HANDLE == handleType)
833 handle.markerActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT);
834 handle.markerActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_RIGHT);
836 else if(RIGHT_SELECTION_HANDLE == handleType)
838 handle.markerActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT);
839 handle.markerActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
844 void CreateSelectionHandles()
846 HandleImpl& primary = mHandle[LEFT_SELECTION_HANDLE];
849 if(mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size())
851 primary.actor = ImageView::New(mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]);
852 #ifdef DECORATOR_DEBUG
853 primary.actor.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleOne");
855 primary.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
856 GetImpl(primary.actor).SetDepthIndex(DepthIndex::DECORATION);
857 primary.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
859 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
860 #ifdef DECORATOR_DEBUG
861 primary.grabArea.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleOneGrabArea");
863 primary.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
864 primary.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
865 primary.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
866 primary.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE);
868 primary.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnHandleOneTouched);
870 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
871 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
872 mTapDetector.Attach(primary.actor);
873 mLongPressDetector.Attach(primary.actor);
875 // The handle's area is attached to the pan detector.
876 // The OnPan() method is connected to the signals emitted by the pan detector.
877 mPanDetector.Attach(primary.grabArea);
879 primary.actor.Add(primary.grabArea);
881 CreateHandleMarker(primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE);
885 if(primary.actor && !primary.actor.GetParent())
887 mActiveLayer.Add(primary.actor);
890 HandleImpl& secondary = mHandle[RIGHT_SELECTION_HANDLE];
893 if(mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size())
895 secondary.actor = ImageView::New(mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]);
896 #ifdef DECORATOR_DEBUG
897 secondary.actor.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleTwo");
899 secondary.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
900 GetImpl(secondary.actor).SetDepthIndex(DepthIndex::DECORATION);
901 secondary.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
903 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
904 #ifdef DECORATOR_DEBUG
905 secondary.grabArea.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleTwoGrabArea");
907 secondary.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
908 secondary.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
909 secondary.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
910 secondary.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE);
912 secondary.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnHandleTwoTouched);
914 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
915 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
916 mTapDetector.Attach(secondary.actor);
917 mLongPressDetector.Attach(secondary.actor);
919 // The handle's area is attached to the pan detector.
920 // The OnPan() method is connected to the signals emitted by the pan detector.
921 mPanDetector.Attach(secondary.grabArea);
923 secondary.actor.Add(secondary.grabArea);
925 CreateHandleMarker(secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE);
929 if(secondary.actor && !secondary.actor.GetParent())
931 mActiveLayer.Add(secondary.actor);
935 void CalculateHandleWorldCoordinates(HandleImpl& handle, Vector2& position)
937 // Gets the world position of the active layer. The active layer is where the handles are added.
938 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION);
940 // The grab handle position in world coords.
941 // The active layer's world position is the center of the active layer. The origin of the
942 // coord system of the handles is the top left of the active layer.
943 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + (mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f);
944 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + (mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f);
947 void SetGrabHandlePosition()
949 // Reference to the grab handle.
950 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
952 // Transforms the handle position into world coordinates.
953 // @note This is not the same value as grabHandle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
954 // as it's transforming the handle's position set by the text-controller and not
955 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
956 // retrieves the position of the center of the actor but the handle's position set
957 // by the text controller is not the center of the actor.
958 Vector2 grabHandleWorldPosition;
959 CalculateHandleWorldCoordinates(grabHandle, grabHandleWorldPosition);
961 // Check if the grab handle exceeds the boundaries of the decoration box.
962 // At the moment only the height is checked for the grab handle.
964 grabHandle.verticallyFlipped = (grabHandle.verticallyFlippedPreferred &&
965 ((grabHandleWorldPosition.y - grabHandle.size.height) > mBoundingBox.y)) ||
966 (grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w);
968 // The grab handle 'y' position in local coords.
969 // If the grab handle exceeds the bottom of the decoration box,
970 // set the 'y' position to the top of the line.
971 // The SetGrabHandleImage() method will change the orientation.
972 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
974 ApplyDisplacement(grabHandle, yLocalPosition);
977 void SetSelectionHandlePosition(HandleType type)
979 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
981 // Reference to the selection handle.
982 HandleImpl& handle = mHandle[type];
984 // Transforms the handle position into world coordinates.
985 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
986 // as it's transforming the handle's position set by the text-controller and not
987 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
988 // retrieves the position of the center of the actor but the handle's position set
989 // by the text controller is not the center of the actor.
990 Vector2 handleWorldPosition;
991 CalculateHandleWorldCoordinates(handle, handleWorldPosition);
993 // Whether to flip the handle (horizontally).
994 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
996 // Whether to flip the handles if they are crossed.
997 bool crossFlip = false;
998 if(mFlipSelectionHandlesOnCross || !mIsHandlePanning)
1000 crossFlip = mIsHandleCurrentlyCrossed;
1003 // Whether the handle was crossed before start the panning.
1004 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1006 // Does not flip if both conditions are true (double flip)
1007 flipHandle = flipHandle != (crossFlip || isHandlePreviouslyCrossed);
1009 // Will flip the handles vertically if the user prefers it.
1010 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1012 if(crossFlip || isHandlePreviouslyCrossed)
1016 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1020 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1024 // Check if the selection handle exceeds the boundaries of the decoration box.
1025 const bool exceedsLeftEdge = (isPrimaryHandle ? !flipHandle : flipHandle) && (handleWorldPosition.x - handle.size.width < mBoundingBox.x);
1026 const bool exceedsRightEdge = (isPrimaryHandle ? flipHandle : !flipHandle) && (handleWorldPosition.x + handle.size.width > mBoundingBox.z);
1028 // Does not flip if both conditions are true (double flip)
1029 flipHandle = flipHandle != (exceedsLeftEdge || exceedsRightEdge);
1033 if(handle.actor && !handle.horizontallyFlipped)
1035 // Change the anchor point to flip the image.
1036 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT);
1038 handle.horizontallyFlipped = true;
1043 if(handle.actor && handle.horizontallyFlipped)
1045 // Reset the anchor point.
1046 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT);
1048 handle.horizontallyFlipped = false;
1052 // Whether to flip the handle vertically.
1053 handle.verticallyFlipped = (verticallyFlippedPreferred &&
1054 ((handleWorldPosition.y - handle.size.height) > mBoundingBox.y)) ||
1055 (handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w);
1057 // The primary selection handle 'y' position in local coords.
1058 // If the handle exceeds the bottom of the decoration box,
1059 // set the 'y' position to the top of the line.
1060 // The SetHandleImage() method will change the orientation.
1061 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1063 ApplyDisplacement(handle, yLocalPosition);
1066 void ApplyDisplacement(HandleImpl& handle, float yLocalPosition)
1070 float adjustedDisplacementX = 0.0f;
1071 float adjustedDisplacementY = 0.0f;
1072 if (mSmoothHandlePanEnabled)
1074 adjustedDisplacementX = CalculateAdjustedDisplacement(handle.position.x, handle.grabDisplacementX, mControlSize.x);
1075 adjustedDisplacementY = CalculateAdjustedDisplacement(handle.position.y, handle.grabDisplacementY, (mControlSize.y - handle.lineHeight));
1077 handle.actor.SetProperty(Actor::Property::POSITION,
1078 Vector2(handle.position.x + floor(0.5f * mCursorWidth) + adjustedDisplacementX,
1079 yLocalPosition + adjustedDisplacementY));
1083 float CalculateAdjustedDisplacement(float position, float displacement, float edge)
1085 //Apply the displacement (on the X-axis & the Y-axis)
1086 //as long as it does not exceed the control's edge.
1087 float adjustedDisplacement = 0.0f;
1088 if(position + displacement < 0.0f)
1090 // -position to cancel it out and relocate to 0.
1091 adjustedDisplacement = -position;
1093 else if(position + displacement > edge)
1095 // move in a displacement which is sufficient to reach the edge.
1096 adjustedDisplacement = edge - position;
1100 // move normally in the displacement.
1101 adjustedDisplacement = displacement;
1103 return adjustedDisplacement;
1106 void SetHandleImage(HandleType type)
1108 HandleImpl& handle = mHandle[type];
1110 HandleType markerType = HANDLE_TYPE_COUNT;
1111 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1112 if(LEFT_SELECTION_HANDLE == type)
1114 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1115 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1117 else if(RIGHT_SELECTION_HANDLE == type)
1119 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1120 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1123 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1126 const HandleImageType imageType = (handle.pressed ? (mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1128 handle.actor.SetImage(mHandleImages[type][imageType]);
1131 if(HANDLE_TYPE_COUNT != markerType)
1133 if(handle.markerActor)
1135 const HandleImageType markerImageType = (handle.pressed ? (mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1136 handle.markerActor.SetImage(mHandleImages[markerType][markerImageType]);
1140 // Whether to flip the handle vertically.
1143 handle.actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS));
1147 void CreateHighlight()
1149 if(!mHighlightActor)
1151 mHighlightActor = Actor::New();
1153 mHighlightActor.SetProperty(Dali::Actor::Property::NAME, "HighlightActor");
1154 mHighlightActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1155 mHighlightActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1156 mHighlightActor.SetProperty(Actor::Property::COLOR, mHighlightColor);
1157 mHighlightActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
1160 // Add the highlight box telling the controller it needs clipping.
1161 mController.AddDecoration(mHighlightActor, true);
1164 void UpdateHighlight()
1168 // Sets the position of the highlight actor inside the decorator.
1169 mHighlightActor.SetProperty(Actor::Property::POSITION, Vector2(mHighlightPosition.x + mHighlightOutlineOffset, 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);
1233 mQuadVertices = VertexBuffer::New(mQuadVertexFormat);
1236 mQuadVertices.SetData(&vertices[0], vertices.Size());
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 ? -primaryHandle.position.y + primaryHandle.size.height : -secondaryHandle.position.y + secondaryHandle.size.height);
1590 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1591 LessThanCondition(mBoundingBox.y + topHeight));
1593 // Notifies the change from false to true and from true to false.
1594 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1596 // Connects the signals with the callbacks.
1597 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1599 // The vertical distance from the center of the active layer to the bottom edje of the display.
1600 const float bottomHeight = -0.5f * mControlSize.height + (primaryHandle.verticallyFlipped ? secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height : primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height);
1602 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1603 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1605 // Notifies the change from false to true and from true to false.
1606 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1608 // Connects the signals with the callbacks.
1609 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1613 // Horizontal notifications.
1615 // Disconnect any previous connected callback.
1616 if(mHandleHorizontalLessThanNotification)
1618 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1619 mActiveLayer.RemovePropertyNotification(mHandleHorizontalLessThanNotification);
1622 if(mHandleHorizontalGreaterThanNotification)
1624 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1625 mActiveLayer.RemovePropertyNotification(mHandleHorizontalGreaterThanNotification);
1628 if(primaryHandle.active || secondaryHandle.active)
1630 // The horizontal distance from the center of the active layer to the left edje of the display.
1631 const float leftWidth = 0.5f * mControlSize.width + std::max(-primaryHandle.position.x + primaryHandle.size.width,
1632 -secondaryHandle.position.x + secondaryHandle.size.width);
1634 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1635 LessThanCondition(mBoundingBox.x + leftWidth));
1637 // Notifies the change from false to true and from true to false.
1638 mHandleHorizontalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1640 // Connects the signals with the callbacks.
1641 mHandleHorizontalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1643 // The horizontal distance from the center of the active layer to the right edje of the display.
1644 const float rightWidth = -0.5f * mControlSize.width + std::max(primaryHandle.position.x + primaryHandle.size.width,
1645 secondaryHandle.position.x + secondaryHandle.size.width);
1647 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1648 GreaterThanCondition(mBoundingBox.z - rightWidth));
1650 // Notifies the change from false to true and from true to false.
1651 mHandleHorizontalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1653 // Connects the signals with the callbacks.
1654 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1660 float AlternatePopUpPositionRelativeToCursor(bool topBottom)
1662 float alternativePosition = 0.0f;
1664 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1666 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1667 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1668 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1669 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1671 if(primaryHandle.active || secondaryHandle.active)
1673 float handleY = 0.f;
1674 float maxHandleHeight = 0.f;
1676 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1677 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1679 if(primaryVisible && secondaryVisible)
1681 handleY = std::max(primaryHandle.position.y, secondaryHandle.position.y);
1682 maxHandleHeight = std::max(primaryHandle.size.height, secondaryHandle.size.height);
1684 else if(primaryVisible && !secondaryVisible)
1686 handleY = primaryHandle.position.y;
1687 maxHandleHeight = primaryHandle.size.height;
1689 else if(!primaryVisible && secondaryVisible)
1691 handleY = secondaryHandle.position.y;
1692 maxHandleHeight = secondaryHandle.size.height;
1695 alternativePosition = handleY + (topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight);
1699 alternativePosition = cursor.position.y + (topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height);
1702 return alternativePosition;
1705 void PopUpLeavesTopBoundary(PropertyNotification& source)
1707 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1709 // Sets the position of the popup below.
1710 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, true)));
1713 void PopUpLeavesBottomBoundary(PropertyNotification& source)
1715 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1717 // Sets the position of the popup above.
1718 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, false)));
1721 void SetUpPopupPositionNotifications(const Vector3& popupHalfSize)
1723 // Disconnect any previous connected callback.
1724 if(mPopupTopExceedNotification)
1726 mPopupTopExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1727 mCopyPastePopup.actor.RemovePropertyNotification(mPopupTopExceedNotification);
1730 if(mPopupBottomExceedNotification)
1732 mPopupBottomExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1733 mCopyPastePopup.actor.RemovePropertyNotification(mPopupBottomExceedNotification);
1736 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1738 // Exceeding vertical boundary
1740 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1741 LessThanCondition(mBoundingBox.y + popupHalfSize.height));
1743 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1744 GreaterThanCondition(mBoundingBox.w - popupHalfSize.height));
1746 // Notifies the change from false to true and from true to false.
1747 mPopupTopExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1748 mPopupBottomExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1750 mPopupTopExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1751 mPopupBottomExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1754 void SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
1756 ImageDimensions dimensions = Dali::GetOriginalImageSize(imageFileName);
1758 HandleImpl& handle = mHandle[handleType];
1759 handle.size = Size(dimensions.GetWidth(), dimensions.GetHeight());
1761 mHandleImages[handleType][handleImageType] = imageFileName;
1764 void SetScrollThreshold(float threshold)
1766 mScrollThreshold = threshold;
1769 float GetScrollThreshold() const
1771 return mScrollThreshold;
1774 void SetScrollSpeed(float speed)
1776 mScrollSpeed = speed;
1777 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1780 float GetScrollSpeed() const
1782 return mScrollSpeed;
1785 void NotifyEndOfScroll()
1791 mNotifyEndOfScroll = true;
1796 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1798 * It only starts the timer if it's already created.
1800 void StartScrollTimer()
1804 mScrollTimer = Timer::New(SCROLL_TICK_INTERVAL);
1805 mScrollTimer.TickSignal().Connect(this, &Decorator::Impl::OnScrollTimerTick);
1808 if(!mScrollTimer.IsRunning())
1810 mScrollTimer.Start();
1815 * Stops the timer used to scroll the text.
1817 void StopScrollTimer()
1821 mScrollTimer.Stop();
1826 * Callback called by the timer used to scroll the text.
1828 * It calculates and sets a new scroll position.
1830 bool OnScrollTimerTick()
1832 if(HANDLE_TYPE_COUNT != mHandleScrolling)
1837 switch(mScrollDirection)
1841 x = mScrollDistance;
1846 x = -mScrollDistance;
1851 y = mScrollDistance;
1856 y = -mScrollDistance;
1863 mController.DecorationEvent(mHandleScrolling,
1872 ControllerInterface& mController;
1874 TapGestureDetector mTapDetector;
1875 PanGestureDetector mPanDetector;
1876 LongPressGestureDetector mLongPressDetector;
1878 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1879 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1881 Actor mActiveLayer; ///< Actor for active handles and alike that ensures they are above all else.
1882 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1883 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1884 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1885 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1886 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1887 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1888 Control mPrimaryCursor;
1889 Control mSecondaryCursor;
1891 Actor mHighlightActor; ///< Actor to display highlight
1892 Renderer mHighlightRenderer;
1893 Shader mHighlightShader; ///< Shader used for highlight
1894 Property::Map mQuadVertexFormat;
1895 PopupImpl mCopyPastePopup;
1896 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1897 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1899 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1900 Vector4 mHandleColor;
1902 CursorImpl mCursor[CURSOR_COUNT];
1903 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1905 VertexBuffer mQuadVertices;
1906 Geometry mQuadGeometry;
1907 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1909 Vector4 mBoundingBox; ///< The bounding box in world coords.
1910 Vector4 mHighlightColor; ///< Color of the highlight
1911 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1912 Size mHighlightSize; ///< The size of the highlighted text.
1913 Size mControlSize; ///< The control's size. Set by the Relayout.
1914 float mHighlightOutlineOffset; ///< The outline's offset.
1916 unsigned int mActiveCursor;
1917 unsigned int mCursorBlinkInterval;
1918 float mCursorBlinkDuration;
1919 float mCursorWidth; ///< The width of the cursors in pixels.
1920 HandleType mHandleScrolling; ///< The handle which is scrolling.
1921 HandleType mHandleReleased; ///< The last handle released.
1922 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1923 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1924 float mScrollSpeed; ///< The scroll speed in pixels per second.
1925 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1926 int mTextDepth; ///< The depth used to render the text.
1928 bool mActiveCopyPastePopup : 1;
1929 bool mPopupSetNewPosition : 1;
1930 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1931 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1932 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1933 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1934 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1935 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1936 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1937 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1938 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1939 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1940 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1941 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1942 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1943 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1944 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1945 bool mHidePrimaryCursorAndGrabHandle : 1; ///< Whether the primary cursor and grab are hidden always.
1948 DecoratorPtr Decorator::New(ControllerInterface& controller,
1949 TextSelectionPopupCallbackInterface& callbackInterface)
1951 return DecoratorPtr(new Decorator(controller,
1952 callbackInterface));
1955 void Decorator::SetBoundingBox(const Rect<int>& boundingBox)
1957 LocalToWorldCoordinatesBoundingBox(boundingBox, mImpl->mBoundingBox);
1960 void Decorator::GetBoundingBox(Rect<int>& boundingBox) const
1962 WorldToLocalCoordinatesBoundingBox(mImpl->mBoundingBox, boundingBox);
1965 void Decorator::Relayout(const Vector2& size)
1967 mImpl->Relayout(size);
1970 void Decorator::UpdatePositions(const Vector2& scrollOffset)
1972 mImpl->UpdatePositions(scrollOffset);
1977 void Decorator::SetActiveCursor(ActiveCursor activeCursor)
1979 mImpl->mActiveCursor = activeCursor;
1982 unsigned int Decorator::GetActiveCursor() const
1984 return mImpl->mActiveCursor;
1987 void Decorator::SetPosition(Cursor cursor, float x, float y, float cursorHeight, float lineHeight)
1989 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1991 cursorImpl.position.x = x;
1992 cursorImpl.position.y = y;
1993 cursorImpl.cursorHeight = cursorHeight;
1994 cursorImpl.lineHeight = lineHeight;
1997 void Decorator::GetPosition(Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight) const
1999 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2001 x = cursorImpl.position.x;
2002 y = cursorImpl.position.y;
2003 cursorHeight = cursorImpl.cursorHeight;
2004 lineHeight = cursorImpl.lineHeight;
2007 const Vector2& Decorator::GetPosition(Cursor cursor) const
2009 return mImpl->mCursor[cursor].position;
2012 void Decorator::SetGlyphOffset(Cursor cursor, float glyphOffset)
2014 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2016 cursorImpl.glyphOffset = glyphOffset;
2019 const float Decorator::GetGlyphOffset(Cursor cursor) const
2021 return mImpl->mCursor[cursor].glyphOffset;
2024 void Decorator::SetCursorColor(Cursor cursor, const Dali::Vector4& color)
2026 mImpl->mCursor[cursor].color = color;
2029 const Dali::Vector4& Decorator::GetColor(Cursor cursor) const
2031 return mImpl->mCursor[cursor].color;
2034 void Decorator::StartCursorBlink()
2036 if(!mImpl->mCursorBlinkTimer)
2038 mImpl->mCursorBlinkTimer = Timer::New(mImpl->mCursorBlinkInterval);
2039 mImpl->mCursorBlinkTimer.TickSignal().Connect(mImpl, &Decorator::Impl::OnCursorBlinkTimerTick);
2042 if(!mImpl->mCursorBlinkTimer.IsRunning())
2044 mImpl->mCursorBlinkTimer.Start();
2048 void Decorator::StopCursorBlink()
2050 if(mImpl->mCursorBlinkTimer)
2052 mImpl->mCursorBlinkTimer.Stop();
2055 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2058 void Decorator::DelayCursorBlink()
2060 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2061 mImpl->mDelayCursorBlink = true;
2064 void Decorator::SetCursorBlinkInterval(float seconds)
2066 mImpl->mCursorBlinkInterval = static_cast<unsigned int>(seconds * TO_MILLISECONDS); // Convert to milliseconds
2069 float Decorator::GetCursorBlinkInterval() const
2071 return static_cast<float>(mImpl->mCursorBlinkInterval) * TO_SECONDS;
2074 void Decorator::SetCursorBlinkDuration(float seconds)
2076 mImpl->mCursorBlinkDuration = seconds;
2079 float Decorator::GetCursorBlinkDuration() const
2081 return mImpl->mCursorBlinkDuration;
2084 void Decorator::SetCursorWidth(int width)
2086 mImpl->mCursorWidth = static_cast<float>(width);
2089 int Decorator::GetCursorWidth() const
2091 return static_cast<int>(mImpl->mCursorWidth);
2094 void Decorator::SetEditable(bool editable)
2096 mImpl->mHidePrimaryCursorAndGrabHandle = !editable;
2097 mImpl->Relayout(mImpl->mControlSize);
2101 void Decorator::SetHandleActive(HandleType handleType, bool active)
2103 mImpl->mHandle[handleType].active = active;
2107 if((LEFT_SELECTION_HANDLE == handleType) || (RIGHT_SELECTION_HANDLE == handleType))
2109 mImpl->mIsHandlePreviouslyCrossed = false;
2112 // TODO: this is a work-around.
2113 // The problem is the handle actor does not receive the touch event with the Interrupt
2114 // state when the power button is pressed and the application goes to background.
2115 mImpl->mHandle[handleType].pressed = false;
2116 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2117 ImageView imageView = mImpl->mHandle[handleType].actor;
2118 if(imageReleased && imageView)
2120 imageView.SetImage(mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED]);
2125 bool Decorator::IsHandleActive(HandleType handleType) const
2127 return mImpl->mHandle[handleType].active;
2130 void Decorator::SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
2132 mImpl->SetHandleImage(handleType, handleImageType, imageFileName);
2135 const std::string& Decorator::GetHandleImage(HandleType handleType, HandleImageType handleImageType) const
2137 return mImpl->mHandleImages[handleType][handleImageType];
2140 void Decorator::SetHandleColor(const Vector4& color)
2142 mImpl->mHandleColor = color;
2145 const Vector4& Decorator::GetHandleColor() const
2147 return mImpl->mHandleColor;
2150 void Decorator::SetPosition(HandleType handleType, float x, float y, float height)
2152 // Adjust handle's displacement
2153 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2155 handle.position.x = x;
2156 handle.position.y = y;
2157 handle.lineHeight = height;
2159 if(mImpl->mSmoothHandlePanEnabled)
2161 handle.grabDisplacementX = 0.f;
2162 handle.grabDisplacementY = 0.f;
2166 void Decorator::GetPosition(HandleType handleType, float& x, float& y, float& height) const
2168 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2170 x = handle.position.x;
2171 y = handle.position.y;
2172 height = handle.lineHeight;
2175 const Vector2& Decorator::GetPosition(HandleType handleType) const
2177 return mImpl->mHandle[handleType].position;
2180 void Decorator::FlipHandleVertically(HandleType handleType, bool flip)
2182 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2185 bool Decorator::IsHandleVerticallyFlipped(HandleType handleType) const
2187 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2190 void Decorator::FlipSelectionHandlesOnCrossEnabled(bool enable)
2192 mImpl->mFlipSelectionHandlesOnCross = enable;
2195 void Decorator::SetSelectionHandleFlipState(bool indicesSwapped, bool left, bool right)
2197 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2198 mImpl->mFlipLeftSelectionHandleDirection = left;
2199 mImpl->mFlipRightSelectionHandleDirection = right;
2202 void Decorator::AddHighlight(unsigned int index, const Vector4& quad)
2204 *(mImpl->mHighlightQuadList.Begin() + index) = quad;
2207 void Decorator::SetHighLightBox(const Vector2& position, const Size& size, float outlineOffset)
2209 mImpl->mHighlightPosition = position;
2210 mImpl->mHighlightSize = size;
2211 mImpl->mHighlightOutlineOffset = outlineOffset;
2214 void Decorator::ClearHighlights()
2216 mImpl->mHighlightQuadList.Clear();
2217 mImpl->mHighlightPosition = Vector2::ZERO;
2218 mImpl->mHighlightOutlineOffset = 0.f;
2221 void Decorator::ResizeHighlightQuads(unsigned int numberOfQuads)
2223 mImpl->mHighlightQuadList.Resize(numberOfQuads);
2226 void Decorator::SetHighlightColor(const Vector4& color)
2228 mImpl->mHighlightColor = color;
2231 const Vector4& Decorator::GetHighlightColor() const
2233 return mImpl->mHighlightColor;
2236 void Decorator::SetHighlightActive(bool active)
2238 mImpl->mIsHighlightBoxActive = active;
2241 bool Decorator::IsHighlightActive() const
2243 return mImpl->mIsHighlightBoxActive;
2246 bool Decorator::IsHighlightVisible() const
2248 return (mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent());
2251 void Decorator::SetTextDepth(int textDepth)
2253 mImpl->mTextDepth = textDepth;
2256 void Decorator::SetPopupActive(bool active)
2258 mImpl->mActiveCopyPastePopup = active;
2261 bool Decorator::IsPopupActive() const
2263 return mImpl->mActiveCopyPastePopup;
2266 void Decorator::SetEnabledPopupButtons(TextSelectionPopup::Buttons& enabledButtonsBitMask)
2268 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2270 if(!mImpl->mCopyPastePopup.actor)
2272 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New(&mImpl->mTextSelectionPopupCallbackInterface);
2273 #ifdef DECORATOR_DEBUG
2274 mImpl->mCopyPastePopup.actor.SetProperty(Dali::Actor::Property::NAME, "mCopyPastePopup");
2276 mImpl->mCopyPastePopup.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
2277 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect(mImpl, &Decorator::Impl::SetPopupPosition); // Position popup after size negotiation
2280 mImpl->mCopyPastePopup.actor.EnableButtons(mImpl->mEnabledPopupButtons);
2283 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2285 return mImpl->mEnabledPopupButtons;
2290 void Decorator::SetScrollThreshold(float threshold)
2292 mImpl->SetScrollThreshold(threshold);
2295 float Decorator::GetScrollThreshold() const
2297 return mImpl->GetScrollThreshold();
2300 void Decorator::SetScrollSpeed(float speed)
2302 mImpl->SetScrollSpeed(speed);
2305 float Decorator::GetScrollSpeed() const
2307 return mImpl->GetScrollSpeed();
2310 void Decorator::NotifyEndOfScroll()
2312 mImpl->NotifyEndOfScroll();
2315 void Decorator::SetHorizontalScrollEnabled(bool enable)
2317 mImpl->mHorizontalScrollingEnabled = enable;
2320 bool Decorator::IsHorizontalScrollEnabled() const
2322 return mImpl->mHorizontalScrollingEnabled;
2325 void Decorator::SetVerticalScrollEnabled(bool enable)
2327 mImpl->mVerticalScrollingEnabled = enable;
2330 bool Decorator::IsVerticalScrollEnabled() const
2332 return mImpl->mVerticalScrollingEnabled;
2335 void Decorator::SetSmoothHandlePanEnabled(bool enable)
2337 mImpl->mSmoothHandlePanEnabled = enable;
2340 bool Decorator::IsSmoothHandlePanEnabled() const
2342 return mImpl->mSmoothHandlePanEnabled;
2345 Decorator::~Decorator()
2350 Decorator::Decorator(ControllerInterface& controller,
2351 TextSelectionPopupCallbackInterface& callbackInterface)
2354 mImpl = new Decorator::Impl(controller, callbackInterface);
2359 } // namespace Toolkit