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;
976 grabHandle.actor.SetProperty(Actor::Property::POSITION, Vector2(grabHandle.position.x + floor(0.5f * mCursorWidth) + (mSmoothHandlePanEnabled ? grabHandle.grabDisplacementX : 0.f), yLocalPosition + (mSmoothHandlePanEnabled ? grabHandle.grabDisplacementY : 0.f)));
980 void SetSelectionHandlePosition(HandleType type)
982 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
984 // Reference to the selection handle.
985 HandleImpl& handle = mHandle[type];
987 // Transforms the handle position into world coordinates.
988 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
989 // as it's transforming the handle's position set by the text-controller and not
990 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
991 // retrieves the position of the center of the actor but the handle's position set
992 // by the text controller is not the center of the actor.
993 Vector2 handleWorldPosition;
994 CalculateHandleWorldCoordinates(handle, handleWorldPosition);
996 // Whether to flip the handle (horizontally).
997 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
999 // Whether to flip the handles if they are crossed.
1000 bool crossFlip = false;
1001 if(mFlipSelectionHandlesOnCross || !mIsHandlePanning)
1003 crossFlip = mIsHandleCurrentlyCrossed;
1006 // Whether the handle was crossed before start the panning.
1007 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1009 // Does not flip if both conditions are true (double flip)
1010 flipHandle = flipHandle != (crossFlip || isHandlePreviouslyCrossed);
1012 // Will flip the handles vertically if the user prefers it.
1013 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1015 if(crossFlip || isHandlePreviouslyCrossed)
1019 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1023 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1027 // Check if the selection handle exceeds the boundaries of the decoration box.
1028 const bool exceedsLeftEdge = (isPrimaryHandle ? !flipHandle : flipHandle) && (handleWorldPosition.x - handle.size.width < mBoundingBox.x);
1029 const bool exceedsRightEdge = (isPrimaryHandle ? flipHandle : !flipHandle) && (handleWorldPosition.x + handle.size.width > mBoundingBox.z);
1031 // Does not flip if both conditions are true (double flip)
1032 flipHandle = flipHandle != (exceedsLeftEdge || exceedsRightEdge);
1036 if(handle.actor && !handle.horizontallyFlipped)
1038 // Change the anchor point to flip the image.
1039 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT);
1041 handle.horizontallyFlipped = true;
1046 if(handle.actor && handle.horizontallyFlipped)
1048 // Reset the anchor point.
1049 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT);
1051 handle.horizontallyFlipped = false;
1055 // Whether to flip the handle vertically.
1056 handle.verticallyFlipped = (verticallyFlippedPreferred &&
1057 ((handleWorldPosition.y - handle.size.height) > mBoundingBox.y)) ||
1058 (handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w);
1060 // The primary selection handle 'y' position in local coords.
1061 // If the handle exceeds the bottom of the decoration box,
1062 // set the 'y' position to the top of the line.
1063 // The SetHandleImage() method will change the orientation.
1064 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1068 handle.actor.SetProperty(Actor::Property::POSITION, Vector2(handle.position.x + (mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f), yLocalPosition + (mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f)));
1072 void SetHandleImage(HandleType type)
1074 HandleImpl& handle = mHandle[type];
1076 HandleType markerType = HANDLE_TYPE_COUNT;
1077 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1078 if(LEFT_SELECTION_HANDLE == type)
1080 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1081 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1083 else if(RIGHT_SELECTION_HANDLE == type)
1085 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1086 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1089 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1092 const HandleImageType imageType = (handle.pressed ? (mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1094 handle.actor.SetImage(mHandleImages[type][imageType]);
1097 if(HANDLE_TYPE_COUNT != markerType)
1099 if(handle.markerActor)
1101 const HandleImageType markerImageType = (handle.pressed ? (mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1102 handle.markerActor.SetImage(mHandleImages[markerType][markerImageType]);
1106 // Whether to flip the handle vertically.
1109 handle.actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS));
1113 void CreateHighlight()
1115 if(!mHighlightActor)
1117 mHighlightActor = Actor::New();
1119 mHighlightActor.SetProperty(Dali::Actor::Property::NAME, "HighlightActor");
1120 mHighlightActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1121 mHighlightActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1122 mHighlightActor.SetProperty(Actor::Property::COLOR, mHighlightColor);
1123 mHighlightActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
1126 // Add the highlight box telling the controller it needs clipping.
1127 mController.AddDecoration(mHighlightActor, true);
1130 void UpdateHighlight()
1134 // Sets the position of the highlight actor inside the decorator.
1135 mHighlightActor.SetProperty(Actor::Property::POSITION, Vector2(mHighlightPosition.x + mHighlightOutlineOffset, mHighlightPosition.y + mHighlightOutlineOffset));
1137 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1138 if(0u != numberOfQuads)
1140 // Set the size of the highlighted text to the actor.
1141 mHighlightActor.SetProperty(Actor::Property::SIZE, mHighlightSize);
1143 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1144 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1145 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1147 Vector<Vector2> vertices;
1148 Vector<unsigned short> indices;
1150 vertices.Reserve(4u * numberOfQuads);
1151 indices.Reserve(6u * numberOfQuads);
1153 // Index to the vertex.
1154 unsigned int v = 0u;
1156 // Traverse all quads.
1157 for(Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1158 endIt = mHighlightQuadList.End();
1162 const Vector4& quad = *it;
1167 vertex.x = quad.x - offsetX;
1168 vertex.y = quad.y - offsetY;
1169 vertices.PushBack(vertex);
1172 vertex.x = quad.z - offsetX;
1173 vertex.y = quad.y - offsetY;
1174 vertices.PushBack(vertex);
1176 // bottom-left (v+2)
1177 vertex.x = quad.x - offsetX;
1178 vertex.y = quad.w - offsetY;
1179 vertices.PushBack(vertex);
1181 // bottom-right (v+3)
1182 vertex.x = quad.z - offsetX;
1183 vertex.y = quad.w - offsetY;
1184 vertices.PushBack(vertex);
1186 // triangle A (3, 1, 0)
1187 indices.PushBack(v + 3);
1188 indices.PushBack(v + 1);
1189 indices.PushBack(v);
1191 // triangle B (0, 2, 3)
1192 indices.PushBack(v);
1193 indices.PushBack(v + 2);
1194 indices.PushBack(v + 3);
1199 mQuadVertices = VertexBuffer::New(mQuadVertexFormat);
1202 mQuadVertices.SetData(&vertices[0], vertices.Size());
1206 mQuadGeometry = Geometry::New();
1207 mQuadGeometry.AddVertexBuffer(mQuadVertices);
1209 mQuadGeometry.SetIndexBuffer(&indices[0], indices.Size());
1211 if(!mHighlightRenderer)
1213 mHighlightRenderer = Dali::Renderer::New(mQuadGeometry, mHighlightShader);
1214 mHighlightActor.AddRenderer(mHighlightRenderer);
1218 mHighlightQuadList.Clear();
1220 if(mHighlightRenderer)
1222 mHighlightRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mTextDepth - 2); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1227 void DoPan(HandleImpl& handle, HandleType type, const PanGesture& gesture)
1229 GestureState state = gesture.GetState();
1230 if(GestureState::STARTED == state)
1232 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1234 handle.globalPosition.x = handle.position.x;
1235 handle.globalPosition.y = handle.position.y;
1238 const Vector2& displacement = gesture.GetDisplacement();
1239 handle.grabDisplacementX += displacement.x;
1240 handle.grabDisplacementY += (handle.verticallyFlipped ? -displacement.y : displacement.y);
1242 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1243 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1244 const float yVerticallyFlippedCorrected = y - (handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f);
1246 if((GestureState::STARTED == state) ||
1247 (GestureState::CONTINUING == state))
1250 mController.GetTargetSize(targetSize);
1252 if(mHorizontalScrollingEnabled &&
1253 (x < mScrollThreshold))
1255 mScrollDirection = SCROLL_RIGHT;
1256 mHandleScrolling = type;
1259 else if(mHorizontalScrollingEnabled &&
1260 (x > targetSize.width - mScrollThreshold))
1262 mScrollDirection = SCROLL_LEFT;
1263 mHandleScrolling = type;
1266 else if(mVerticalScrollingEnabled &&
1267 (yVerticallyFlippedCorrected < mScrollThreshold))
1269 mScrollDirection = SCROLL_TOP;
1270 mHandleScrolling = type;
1273 else if(mVerticalScrollingEnabled &&
1274 (yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold))
1276 mScrollDirection = SCROLL_BOTTOM;
1277 mHandleScrolling = type;
1282 mHandleScrolling = HANDLE_TYPE_COUNT;
1284 mController.DecorationEvent(type, HANDLE_PRESSED, x, y);
1287 mIsHandlePanning = true;
1289 else if((GestureState::FINISHED == state) ||
1290 (GestureState::CANCELLED == state))
1293 (mScrollTimer.IsRunning() || mNotifyEndOfScroll))
1295 mNotifyEndOfScroll = false;
1296 mHandleScrolling = HANDLE_TYPE_COUNT;
1298 mController.DecorationEvent(type, HANDLE_STOP_SCROLLING, x, y);
1302 mController.DecorationEvent(type, HANDLE_RELEASED, x, y);
1307 handle.actor.SetImage(mHandleImages[type][HANDLE_IMAGE_RELEASED]);
1309 handle.pressed = false;
1311 mIsHandlePanning = false;
1315 void OnPan(Actor actor, const PanGesture& gesture)
1317 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1318 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1319 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1321 if(actor == grabHandle.grabArea)
1323 DoPan(grabHandle, GRAB_HANDLE, gesture);
1325 else if(actor == primarySelectionHandle.grabArea)
1327 DoPan(primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture);
1329 else if(actor == secondarySelectionHandle.grabArea)
1331 DoPan(secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture);
1335 bool OnGrabHandleTouched(Actor actor, const TouchEvent& touch)
1337 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1339 // Switch between pressed/release grab-handle images
1340 if(touch.GetPointCount() > 0 &&
1343 const PointState::Type state = touch.GetState(0);
1345 if(PointState::DOWN == state)
1347 grabHandle.pressed = true;
1349 else if((PointState::UP == state) ||
1350 (PointState::INTERRUPTED == state))
1352 grabHandle.pressed = false;
1355 SetHandleImage(GRAB_HANDLE);
1361 bool OnHandleOneTouched(Actor actor, const TouchEvent& touch)
1363 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1365 // Switch between pressed/release selection handle images
1366 if(touch.GetPointCount() > 0 &&
1367 primarySelectionHandle.actor)
1369 const PointState::Type state = touch.GetState(0);
1371 if(PointState::DOWN == state)
1373 primarySelectionHandle.pressed = true;
1374 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1376 else if((PointState::UP == state) ||
1377 (PointState::INTERRUPTED == state))
1379 primarySelectionHandle.pressed = false;
1380 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1381 mIsHandlePanning = false;
1382 mHandleReleased = LEFT_SELECTION_HANDLE;
1385 SetHandleImage(LEFT_SELECTION_HANDLE);
1391 bool OnHandleTwoTouched(Actor actor, const TouchEvent& touch)
1393 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1395 // Switch between pressed/release selection handle images
1396 if(touch.GetPointCount() > 0 &&
1397 secondarySelectionHandle.actor)
1399 const PointState::Type state = touch.GetState(0);
1401 if(PointState::DOWN == state)
1403 secondarySelectionHandle.pressed = true;
1404 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1406 else if((PointState::UP == state) ||
1407 (PointState::INTERRUPTED == state))
1409 secondarySelectionHandle.pressed = false;
1410 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1411 mIsHandlePanning = false;
1412 mHandleReleased = RIGHT_SELECTION_HANDLE;
1415 SetHandleImage(RIGHT_SELECTION_HANDLE);
1421 void HandleResetPosition(PropertyNotification& source)
1423 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1425 if(grabHandle.active)
1427 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1428 SetGrabHandlePosition();
1430 // Sets the grab handle image according if it's pressed, flipped, etc.
1431 SetHandleImage(GRAB_HANDLE);
1435 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1436 SetSelectionHandlePosition(LEFT_SELECTION_HANDLE);
1438 // Sets the primary handle image according if it's pressed, flipped, etc.
1439 SetHandleImage(LEFT_SELECTION_HANDLE);
1441 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1442 SetSelectionHandlePosition(RIGHT_SELECTION_HANDLE);
1444 // Sets the secondary handle image according if it's pressed, flipped, etc.
1445 SetHandleImage(RIGHT_SELECTION_HANDLE);
1449 void SetupActiveLayerPropertyNotifications()
1456 // Vertical notifications.
1458 // Disconnect any previous connected callback.
1459 if(mHandleVerticalLessThanNotification)
1461 mHandleVerticalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1462 mActiveLayer.RemovePropertyNotification(mHandleVerticalLessThanNotification);
1465 if(mHandleVerticalGreaterThanNotification)
1467 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1468 mActiveLayer.RemovePropertyNotification(mHandleVerticalGreaterThanNotification);
1471 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1472 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1473 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1475 if(grabHandle.active)
1477 if(grabHandle.verticallyFlipped)
1479 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1480 mHandleVerticalGreaterThanNotification.Reset();
1482 // The vertical distance from the center of the active layer to the top edje of the display.
1483 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1485 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1486 LessThanCondition(mBoundingBox.y + topHeight));
1488 // Notifies the change from false to true and from true to false.
1489 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1491 // Connects the signals with the callbacks.
1492 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1496 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1497 mHandleVerticalLessThanNotification.Reset();
1499 // The vertical distance from the center of the active layer to the bottom edje of the display.
1500 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1502 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1503 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1505 // Notifies the change from false to true and from true to false.
1506 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1508 // Connects the signals with the callbacks.
1509 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1512 else // The selection handles are active
1514 if(primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped)
1516 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1517 mHandleVerticalGreaterThanNotification.Reset();
1519 // The vertical distance from the center of the active layer to the top edje of the display.
1520 const float topHeight = 0.5f * mControlSize.height + std::max(-primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height);
1522 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1523 LessThanCondition(mBoundingBox.y + topHeight));
1525 // Notifies the change from false to true and from true to false.
1526 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1528 // Connects the signals with the callbacks.
1529 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1531 else if(!primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped)
1533 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1534 mHandleVerticalLessThanNotification.Reset();
1536 // The vertical distance from the center of the active layer to the bottom edje of the display.
1537 const float bottomHeight = -0.5f * mControlSize.height + std::max(primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1538 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height);
1540 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1541 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1543 // Notifies the change from false to true and from true to false.
1544 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1546 // Connects the signals with the callbacks.
1547 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1551 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
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 + (primaryHandle.verticallyFlipped ? -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 // The vertical distance from the center of the active layer to the bottom edje of the display.
1566 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);
1568 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1569 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1571 // Notifies the change from false to true and from true to false.
1572 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1574 // Connects the signals with the callbacks.
1575 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1579 // Horizontal notifications.
1581 // Disconnect any previous connected callback.
1582 if(mHandleHorizontalLessThanNotification)
1584 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1585 mActiveLayer.RemovePropertyNotification(mHandleHorizontalLessThanNotification);
1588 if(mHandleHorizontalGreaterThanNotification)
1590 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1591 mActiveLayer.RemovePropertyNotification(mHandleHorizontalGreaterThanNotification);
1594 if(primaryHandle.active || secondaryHandle.active)
1596 // The horizontal distance from the center of the active layer to the left edje of the display.
1597 const float leftWidth = 0.5f * mControlSize.width + std::max(-primaryHandle.position.x + primaryHandle.size.width,
1598 -secondaryHandle.position.x + secondaryHandle.size.width);
1600 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1601 LessThanCondition(mBoundingBox.x + leftWidth));
1603 // Notifies the change from false to true and from true to false.
1604 mHandleHorizontalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1606 // Connects the signals with the callbacks.
1607 mHandleHorizontalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1609 // The horizontal distance from the center of the active layer to the right edje of the display.
1610 const float rightWidth = -0.5f * mControlSize.width + std::max(primaryHandle.position.x + primaryHandle.size.width,
1611 secondaryHandle.position.x + secondaryHandle.size.width);
1613 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1614 GreaterThanCondition(mBoundingBox.z - rightWidth));
1616 // Notifies the change from false to true and from true to false.
1617 mHandleHorizontalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1619 // Connects the signals with the callbacks.
1620 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1626 float AlternatePopUpPositionRelativeToCursor(bool topBottom)
1628 float alternativePosition = 0.0f;
1630 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1632 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1633 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1634 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1635 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1637 if(primaryHandle.active || secondaryHandle.active)
1639 float handleY = 0.f;
1640 float maxHandleHeight = 0.f;
1642 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1643 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1645 if(primaryVisible && secondaryVisible)
1647 handleY = std::max(primaryHandle.position.y, secondaryHandle.position.y);
1648 maxHandleHeight = std::max(primaryHandle.size.height, secondaryHandle.size.height);
1650 else if(primaryVisible && !secondaryVisible)
1652 handleY = primaryHandle.position.y;
1653 maxHandleHeight = primaryHandle.size.height;
1655 else if(!primaryVisible && secondaryVisible)
1657 handleY = secondaryHandle.position.y;
1658 maxHandleHeight = secondaryHandle.size.height;
1661 alternativePosition = handleY + (topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight);
1665 alternativePosition = cursor.position.y + (topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height);
1668 return alternativePosition;
1671 void PopUpLeavesTopBoundary(PropertyNotification& source)
1673 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1675 // Sets the position of the popup below.
1676 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, true)));
1679 void PopUpLeavesBottomBoundary(PropertyNotification& source)
1681 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1683 // Sets the position of the popup above.
1684 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, false)));
1687 void SetUpPopupPositionNotifications(const Vector3& popupHalfSize)
1689 // Disconnect any previous connected callback.
1690 if(mPopupTopExceedNotification)
1692 mPopupTopExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1693 mCopyPastePopup.actor.RemovePropertyNotification(mPopupTopExceedNotification);
1696 if(mPopupBottomExceedNotification)
1698 mPopupBottomExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1699 mCopyPastePopup.actor.RemovePropertyNotification(mPopupBottomExceedNotification);
1702 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1704 // Exceeding vertical boundary
1706 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1707 LessThanCondition(mBoundingBox.y + popupHalfSize.height));
1709 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1710 GreaterThanCondition(mBoundingBox.w - popupHalfSize.height));
1712 // Notifies the change from false to true and from true to false.
1713 mPopupTopExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1714 mPopupBottomExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1716 mPopupTopExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1717 mPopupBottomExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1720 void SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
1722 ImageDimensions dimensions = Dali::GetOriginalImageSize(imageFileName);
1724 HandleImpl& handle = mHandle[handleType];
1725 handle.size = Size(dimensions.GetWidth(), dimensions.GetHeight());
1727 mHandleImages[handleType][handleImageType] = imageFileName;
1730 void SetScrollThreshold(float threshold)
1732 mScrollThreshold = threshold;
1735 float GetScrollThreshold() const
1737 return mScrollThreshold;
1740 void SetScrollSpeed(float speed)
1742 mScrollSpeed = speed;
1743 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1746 float GetScrollSpeed() const
1748 return mScrollSpeed;
1751 void NotifyEndOfScroll()
1757 mNotifyEndOfScroll = true;
1762 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1764 * It only starts the timer if it's already created.
1766 void StartScrollTimer()
1770 mScrollTimer = Timer::New(SCROLL_TICK_INTERVAL);
1771 mScrollTimer.TickSignal().Connect(this, &Decorator::Impl::OnScrollTimerTick);
1774 if(!mScrollTimer.IsRunning())
1776 mScrollTimer.Start();
1781 * Stops the timer used to scroll the text.
1783 void StopScrollTimer()
1787 mScrollTimer.Stop();
1792 * Callback called by the timer used to scroll the text.
1794 * It calculates and sets a new scroll position.
1796 bool OnScrollTimerTick()
1798 if(HANDLE_TYPE_COUNT != mHandleScrolling)
1803 switch(mScrollDirection)
1807 x = mScrollDistance;
1812 x = -mScrollDistance;
1817 y = mScrollDistance;
1822 y = -mScrollDistance;
1829 mController.DecorationEvent(mHandleScrolling,
1838 ControllerInterface& mController;
1840 TapGestureDetector mTapDetector;
1841 PanGestureDetector mPanDetector;
1842 LongPressGestureDetector mLongPressDetector;
1844 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1845 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1847 Actor mActiveLayer; ///< Actor for active handles and alike that ensures they are above all else.
1848 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1849 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1850 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1851 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1852 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1853 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1854 Control mPrimaryCursor;
1855 Control mSecondaryCursor;
1857 Actor mHighlightActor; ///< Actor to display highlight
1858 Renderer mHighlightRenderer;
1859 Shader mHighlightShader; ///< Shader used for highlight
1860 Property::Map mQuadVertexFormat;
1861 PopupImpl mCopyPastePopup;
1862 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1863 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1865 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1866 Vector4 mHandleColor;
1868 CursorImpl mCursor[CURSOR_COUNT];
1869 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1871 VertexBuffer mQuadVertices;
1872 Geometry mQuadGeometry;
1873 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1875 Vector4 mBoundingBox; ///< The bounding box in world coords.
1876 Vector4 mHighlightColor; ///< Color of the highlight
1877 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1878 Size mHighlightSize; ///< The size of the highlighted text.
1879 Size mControlSize; ///< The control's size. Set by the Relayout.
1880 float mHighlightOutlineOffset; ///< The outline's offset.
1882 unsigned int mActiveCursor;
1883 unsigned int mCursorBlinkInterval;
1884 float mCursorBlinkDuration;
1885 float mCursorWidth; ///< The width of the cursors in pixels.
1886 HandleType mHandleScrolling; ///< The handle which is scrolling.
1887 HandleType mHandleReleased; ///< The last handle released.
1888 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1889 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1890 float mScrollSpeed; ///< The scroll speed in pixels per second.
1891 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1892 int mTextDepth; ///< The depth used to render the text.
1894 bool mActiveCopyPastePopup : 1;
1895 bool mPopupSetNewPosition : 1;
1896 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1897 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1898 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1899 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1900 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1901 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1902 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1903 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1904 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1905 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1906 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1907 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1908 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1909 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1910 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1911 bool mHidePrimaryCursorAndGrabHandle : 1; ///< Whether the primary cursor and grab are hidden always.
1914 DecoratorPtr Decorator::New(ControllerInterface& controller,
1915 TextSelectionPopupCallbackInterface& callbackInterface)
1917 return DecoratorPtr(new Decorator(controller,
1918 callbackInterface));
1921 void Decorator::SetBoundingBox(const Rect<int>& boundingBox)
1923 LocalToWorldCoordinatesBoundingBox(boundingBox, mImpl->mBoundingBox);
1926 void Decorator::GetBoundingBox(Rect<int>& boundingBox) const
1928 WorldToLocalCoordinatesBoundingBox(mImpl->mBoundingBox, boundingBox);
1931 void Decorator::Relayout(const Vector2& size)
1933 mImpl->Relayout(size);
1936 void Decorator::UpdatePositions(const Vector2& scrollOffset)
1938 mImpl->UpdatePositions(scrollOffset);
1943 void Decorator::SetActiveCursor(ActiveCursor activeCursor)
1945 mImpl->mActiveCursor = activeCursor;
1948 unsigned int Decorator::GetActiveCursor() const
1950 return mImpl->mActiveCursor;
1953 void Decorator::SetPosition(Cursor cursor, float x, float y, float cursorHeight, float lineHeight)
1955 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1957 cursorImpl.position.x = x;
1958 cursorImpl.position.y = y;
1959 cursorImpl.cursorHeight = cursorHeight;
1960 cursorImpl.lineHeight = lineHeight;
1963 void Decorator::GetPosition(Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight) const
1965 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1967 x = cursorImpl.position.x;
1968 y = cursorImpl.position.y;
1969 cursorHeight = cursorImpl.cursorHeight;
1970 lineHeight = cursorImpl.lineHeight;
1973 const Vector2& Decorator::GetPosition(Cursor cursor) const
1975 return mImpl->mCursor[cursor].position;
1978 void Decorator::SetGlyphOffset(Cursor cursor, float glyphOffset)
1980 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1982 cursorImpl.glyphOffset = glyphOffset;
1985 const float Decorator::GetGlyphOffset(Cursor cursor) const
1987 return mImpl->mCursor[cursor].glyphOffset;
1990 void Decorator::SetCursorColor(Cursor cursor, const Dali::Vector4& color)
1992 mImpl->mCursor[cursor].color = color;
1995 const Dali::Vector4& Decorator::GetColor(Cursor cursor) const
1997 return mImpl->mCursor[cursor].color;
2000 void Decorator::StartCursorBlink()
2002 if(!mImpl->mCursorBlinkTimer)
2004 mImpl->mCursorBlinkTimer = Timer::New(mImpl->mCursorBlinkInterval);
2005 mImpl->mCursorBlinkTimer.TickSignal().Connect(mImpl, &Decorator::Impl::OnCursorBlinkTimerTick);
2008 if(!mImpl->mCursorBlinkTimer.IsRunning())
2010 mImpl->mCursorBlinkTimer.Start();
2014 void Decorator::StopCursorBlink()
2016 if(mImpl->mCursorBlinkTimer)
2018 mImpl->mCursorBlinkTimer.Stop();
2021 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2024 void Decorator::DelayCursorBlink()
2026 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2027 mImpl->mDelayCursorBlink = true;
2030 void Decorator::SetCursorBlinkInterval(float seconds)
2032 mImpl->mCursorBlinkInterval = static_cast<unsigned int>(seconds * TO_MILLISECONDS); // Convert to milliseconds
2035 float Decorator::GetCursorBlinkInterval() const
2037 return static_cast<float>(mImpl->mCursorBlinkInterval) * TO_SECONDS;
2040 void Decorator::SetCursorBlinkDuration(float seconds)
2042 mImpl->mCursorBlinkDuration = seconds;
2045 float Decorator::GetCursorBlinkDuration() const
2047 return mImpl->mCursorBlinkDuration;
2050 void Decorator::SetCursorWidth(int width)
2052 mImpl->mCursorWidth = static_cast<float>(width);
2055 int Decorator::GetCursorWidth() const
2057 return static_cast<int>(mImpl->mCursorWidth);
2060 void Decorator::SetEditable(bool editable)
2062 mImpl->mHidePrimaryCursorAndGrabHandle = !editable;
2063 mImpl->Relayout(mImpl->mControlSize);
2067 void Decorator::SetHandleActive(HandleType handleType, bool active)
2069 mImpl->mHandle[handleType].active = active;
2073 if((LEFT_SELECTION_HANDLE == handleType) || (RIGHT_SELECTION_HANDLE == handleType))
2075 mImpl->mIsHandlePreviouslyCrossed = false;
2078 // TODO: this is a work-around.
2079 // The problem is the handle actor does not receive the touch event with the Interrupt
2080 // state when the power button is pressed and the application goes to background.
2081 mImpl->mHandle[handleType].pressed = false;
2082 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2083 ImageView imageView = mImpl->mHandle[handleType].actor;
2084 if(imageReleased && imageView)
2086 imageView.SetImage(mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED]);
2091 bool Decorator::IsHandleActive(HandleType handleType) const
2093 return mImpl->mHandle[handleType].active;
2096 void Decorator::SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
2098 mImpl->SetHandleImage(handleType, handleImageType, imageFileName);
2101 const std::string& Decorator::GetHandleImage(HandleType handleType, HandleImageType handleImageType) const
2103 return mImpl->mHandleImages[handleType][handleImageType];
2106 void Decorator::SetHandleColor(const Vector4& color)
2108 mImpl->mHandleColor = color;
2111 const Vector4& Decorator::GetHandleColor() const
2113 return mImpl->mHandleColor;
2116 void Decorator::SetPosition(HandleType handleType, float x, float y, float height)
2118 // Adjust handle's displacement
2119 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2121 handle.position.x = x;
2122 handle.position.y = y;
2123 handle.lineHeight = height;
2125 if(mImpl->mSmoothHandlePanEnabled)
2127 handle.grabDisplacementX = 0.f;
2128 handle.grabDisplacementY = 0.f;
2132 void Decorator::GetPosition(HandleType handleType, float& x, float& y, float& height) const
2134 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2136 x = handle.position.x;
2137 y = handle.position.y;
2138 height = handle.lineHeight;
2141 const Vector2& Decorator::GetPosition(HandleType handleType) const
2143 return mImpl->mHandle[handleType].position;
2146 void Decorator::FlipHandleVertically(HandleType handleType, bool flip)
2148 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2151 bool Decorator::IsHandleVerticallyFlipped(HandleType handleType) const
2153 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2156 void Decorator::FlipSelectionHandlesOnCrossEnabled(bool enable)
2158 mImpl->mFlipSelectionHandlesOnCross = enable;
2161 void Decorator::SetSelectionHandleFlipState(bool indicesSwapped, bool left, bool right)
2163 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2164 mImpl->mFlipLeftSelectionHandleDirection = left;
2165 mImpl->mFlipRightSelectionHandleDirection = right;
2168 void Decorator::AddHighlight(unsigned int index, const Vector4& quad)
2170 *(mImpl->mHighlightQuadList.Begin() + index) = quad;
2173 void Decorator::SetHighLightBox(const Vector2& position, const Size& size, float outlineOffset)
2175 mImpl->mHighlightPosition = position;
2176 mImpl->mHighlightSize = size;
2177 mImpl->mHighlightOutlineOffset = outlineOffset;
2180 void Decorator::ClearHighlights()
2182 mImpl->mHighlightQuadList.Clear();
2183 mImpl->mHighlightPosition = Vector2::ZERO;
2184 mImpl->mHighlightOutlineOffset = 0.f;
2187 void Decorator::ResizeHighlightQuads(unsigned int numberOfQuads)
2189 mImpl->mHighlightQuadList.Resize(numberOfQuads);
2192 void Decorator::SetHighlightColor(const Vector4& color)
2194 mImpl->mHighlightColor = color;
2197 const Vector4& Decorator::GetHighlightColor() const
2199 return mImpl->mHighlightColor;
2202 void Decorator::SetHighlightActive(bool active)
2204 mImpl->mIsHighlightBoxActive = active;
2207 bool Decorator::IsHighlightActive() const
2209 return mImpl->mIsHighlightBoxActive;
2212 bool Decorator::IsHighlightVisible() const
2214 return (mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent());
2217 void Decorator::SetTextDepth(int textDepth)
2219 mImpl->mTextDepth = textDepth;
2222 void Decorator::SetPopupActive(bool active)
2224 mImpl->mActiveCopyPastePopup = active;
2227 bool Decorator::IsPopupActive() const
2229 return mImpl->mActiveCopyPastePopup;
2232 void Decorator::SetEnabledPopupButtons(TextSelectionPopup::Buttons& enabledButtonsBitMask)
2234 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2236 if(!mImpl->mCopyPastePopup.actor)
2238 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New(&mImpl->mTextSelectionPopupCallbackInterface);
2239 #ifdef DECORATOR_DEBUG
2240 mImpl->mCopyPastePopup.actor.SetProperty(Dali::Actor::Property::NAME, "mCopyPastePopup");
2242 mImpl->mCopyPastePopup.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
2243 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect(mImpl, &Decorator::Impl::SetPopupPosition); // Position popup after size negotiation
2246 mImpl->mCopyPastePopup.actor.EnableButtons(mImpl->mEnabledPopupButtons);
2249 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2251 return mImpl->mEnabledPopupButtons;
2256 void Decorator::SetScrollThreshold(float threshold)
2258 mImpl->SetScrollThreshold(threshold);
2261 float Decorator::GetScrollThreshold() const
2263 return mImpl->GetScrollThreshold();
2266 void Decorator::SetScrollSpeed(float speed)
2268 mImpl->SetScrollSpeed(speed);
2271 float Decorator::GetScrollSpeed() const
2273 return mImpl->GetScrollSpeed();
2276 void Decorator::NotifyEndOfScroll()
2278 mImpl->NotifyEndOfScroll();
2281 void Decorator::SetHorizontalScrollEnabled(bool enable)
2283 mImpl->mHorizontalScrollingEnabled = enable;
2286 bool Decorator::IsHorizontalScrollEnabled() const
2288 return mImpl->mHorizontalScrollingEnabled;
2291 void Decorator::SetVerticalScrollEnabled(bool enable)
2293 mImpl->mVerticalScrollingEnabled = enable;
2296 bool Decorator::IsVerticalScrollEnabled() const
2298 return mImpl->mVerticalScrollingEnabled;
2301 void Decorator::SetSmoothHandlePanEnabled(bool enable)
2303 mImpl->mSmoothHandlePanEnabled = enable;
2306 bool Decorator::IsSmoothHandlePanEnabled() const
2308 return mImpl->mSmoothHandlePanEnabled;
2311 Decorator::~Decorator()
2316 Decorator::Decorator(ControllerInterface& controller,
2317 TextSelectionPopupCallbackInterface& callbackInterface)
2320 mImpl = new Decorator::Impl(controller, callbackInterface);
2325 } // namespace Toolkit