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/devel-api/controls/control-devel.h>
36 #include <dali-toolkit/internal/controls/image-view/image-view-impl.h>
37 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
38 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
41 #define DECORATOR_DEBUG
51 #ifdef DECORATOR_DEBUG
52 Integration::Log::Filter* gLogFilter(Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR"));
55 } // namespace Internal
61 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE(1.25f, 1.5f, 1.0f);
62 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE(1.25f, 1.5f, 1.0f);
63 const Dali::Vector3 ACTIVE_LAYER_ANCHOR_POINT(0.5f, 0.5f, 0.5f);
65 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.
67 const Dali::Vector4 HANDLE_COLOR(0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f);
69 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
70 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
71 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
73 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
74 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.
75 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
77 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
79 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
81 const float POPUP_PADDING = 2.f; ///< Padding space between the highlight box and the text's popup.
83 typedef Dali::Vector<Dali::Vector4> QuadContainer;
86 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
87 * @param[in] boundingRectangle local bounding
88 * @param[out] Vector4 World coordinate bounding Box.
90 void LocalToWorldCoordinatesBoundingBox(const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox)
92 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
93 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
95 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
96 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
98 boundingBox = Dali::Vector4(originX,
100 originX + boundingRectangle.width,
101 originY + boundingRectangle.height);
104 void WorldToLocalCoordinatesBoundingBox(const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle)
106 // Convert to local coordinates and store as a Dali::Rect.
107 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
109 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
110 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
111 boundingRectangle.width = boundingBox.z - boundingBox.x;
112 boundingRectangle.height = boundingBox.w - boundingBox.y;
115 } // end of namespace
123 struct Decorator::Impl : public ConnectionTracker
137 : color(Dali::Color::BLACK),
159 grabDisplacementX(0.f),
160 grabDisplacementY(0.f),
162 horizontallyVisible(false),
163 verticallyVisible(false),
165 verticallyFlippedPreferred(false),
166 horizontallyFlipped(false),
167 verticallyFlipped(false),
168 verticallyFlippedOnTouch(false)
174 ImageView markerActor;
177 Vector2 globalPosition;
179 float lineHeight; ///< Not the handle height
180 float grabDisplacementX;
181 float grabDisplacementY;
183 bool horizontallyVisible : 1;
184 bool verticallyVisible : 1;
186 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
187 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
188 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
189 bool verticallyFlippedOnTouch : 1; ///< Whether the handle is vertically flipped on touch.
199 TextSelectionPopup actor;
203 Impl(ControllerInterface& controller,
204 TextSelectionPopupCallbackInterface& callbackInterface)
205 : mController(controller),
206 mEnabledPopupButtons(TextSelectionPopup::NONE),
207 mTextSelectionPopupCallbackInterface(callbackInterface),
208 mHandleColor(HANDLE_COLOR),
210 mHighlightColor(LIGHT_BLUE),
211 mHighlightPosition(Vector2::ZERO),
212 mHighlightSize(Vector2::ZERO),
213 mControlSize(Vector2::ZERO),
214 mHighlightOutlineOffset(0.f),
215 mActiveCursor(ACTIVE_CURSOR_NONE),
216 mCursorBlinkInterval(CURSOR_BLINK_INTERVAL),
217 mCursorBlinkDuration(0.0f),
218 mCursorWidth(CURSOR_WIDTH),
219 mHandleScrolling(HANDLE_TYPE_COUNT),
220 mHandleReleased(HANDLE_TYPE_COUNT),
221 mScrollDirection(SCROLL_NONE),
222 mScrollThreshold(SCROLL_THRESHOLD),
223 mScrollSpeed(SCROLL_SPEED),
224 mScrollDistance(SCROLL_DISTANCE),
226 mActiveCopyPastePopup(false),
227 mPopupSetNewPosition(true),
228 mCursorBlinkStatus(true),
229 mDelayCursorBlink(false),
230 mPrimaryCursorVisible(false),
231 mSecondaryCursorVisible(false),
232 mFlipSelectionHandlesOnCross(false),
233 mFlipLeftSelectionHandleDirection(false),
234 mFlipRightSelectionHandleDirection(false),
235 mIsHandlePanning(false),
236 mIsHandleCurrentlyCrossed(false),
237 mIsHandlePreviouslyCrossed(false),
238 mNotifyEndOfScroll(false),
239 mHorizontalScrollingEnabled(false),
240 mVerticalScrollingEnabled(false),
241 mSmoothHandlePanEnabled(false),
242 mIsHighlightBoxActive(false),
243 mHidePrimaryCursorAndGrabHandle(false)
245 mQuadVertexFormat["aPosition"] = Property::VECTOR2;
246 mHighlightShader = Shader::New(SHADER_TEXT_DECORATOR_SHADER_VERT, SHADER_TEXT_DECORATOR_SHADER_FRAG);
251 * Relayout of the decorations owned by the decorator.
252 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
254 void Relayout(const Vector2& size)
258 // TODO - Remove this if nothing is active
259 CreateLayer(mActiveLayer, DecorationType::ACTIVE_LAYER);
260 CreateLayer(mCursorLayer, DecorationType::CURSOR_LAYER);
262 // Show or hide the cursors
267 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
268 mPrimaryCursorVisible = (!mHidePrimaryCursorAndGrabHandle) && ((mControlSize.width - (cursor.position.x + mCursorWidth) > -Math::MACHINE_EPSILON_1000) &&
269 (cursor.position.x > -Math::MACHINE_EPSILON_1000) &&
270 (mControlSize.height - cursor.position.y > -Math::MACHINE_EPSILON_1000) &&
271 (cursor.position.y + cursor.cursorHeight > -Math::MACHINE_EPSILON_1000));
272 if(mPrimaryCursorVisible)
274 mPrimaryCursor.SetProperty(Actor::Property::POSITION, Vector2(cursor.position.x, cursor.position.y));
275 mPrimaryCursor.SetProperty(Actor::Property::SIZE, Size(mCursorWidth, cursor.cursorHeight));
277 mPrimaryCursor.SetProperty(Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus);
281 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
282 mSecondaryCursorVisible = ((mControlSize.width - (cursor.position.x + mCursorWidth) > -Math::MACHINE_EPSILON_1000) &&
283 (cursor.position.x > -Math::MACHINE_EPSILON_1000) &&
284 (mControlSize.height - cursor.position.y > -Math::MACHINE_EPSILON_1000) &&
285 (cursor.position.y + cursor.cursorHeight > -Math::MACHINE_EPSILON_1000));
286 if(mSecondaryCursorVisible)
288 mSecondaryCursor.SetProperty(Actor::Property::POSITION, Vector2(cursor.position.x, cursor.position.y));
289 mSecondaryCursor.SetProperty(Actor::Property::SIZE, Size(mCursorWidth, cursor.cursorHeight));
291 mSecondaryCursor.SetProperty(Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus);
294 // Show or hide the grab handle
295 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
296 bool newGrabHandlePosition = false;
297 grabHandle.horizontallyVisible = false;
298 grabHandle.verticallyVisible = false;
299 if(grabHandle.active)
301 grabHandle.horizontallyVisible = ((mControlSize.width - (grabHandle.position.x + floor(0.5f * mCursorWidth)) > -Math::MACHINE_EPSILON_1000) &&
302 (grabHandle.position.x > -Math::MACHINE_EPSILON_1000));
303 grabHandle.verticallyVisible = ((fabsf(mControlSize.height - grabHandle.lineHeight) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000) &&
304 (grabHandle.position.y + grabHandle.lineHeight > -Math::MACHINE_EPSILON_1000));
306 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible && (!mHidePrimaryCursorAndGrabHandle);
311 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
312 SetGrabHandlePosition();
314 // Sets the grab handle image according if it's pressed, flipped, etc.
315 SetHandleImage(GRAB_HANDLE);
317 newGrabHandlePosition = true;
322 grabHandle.actor.SetProperty(Actor::Property::VISIBLE, isVisible);
325 else if(grabHandle.actor)
327 grabHandle.actor.Unparent();
330 // Show or hide the selection handles/highlight
331 HandleImpl& primary = mHandle[LEFT_SELECTION_HANDLE];
332 HandleImpl& secondary = mHandle[RIGHT_SELECTION_HANDLE];
333 bool newPrimaryHandlePosition = false;
334 bool newSecondaryHandlePosition = false;
336 primary.horizontallyVisible = ((mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000) &&
337 (primary.position.x > -Math::MACHINE_EPSILON_1000));
338 primary.verticallyVisible = ((fabsf(mControlSize.height - primary.lineHeight) - primary.position.y > -Math::MACHINE_EPSILON_1000) &&
339 (primary.position.y + (primary.verticallyFlipped ? 0.f : primary.lineHeight) > -Math::MACHINE_EPSILON_1000));
340 secondary.horizontallyVisible = ((mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000) &&
341 (secondary.position.x > -Math::MACHINE_EPSILON_1000));
342 secondary.verticallyVisible = ((fabsf(mControlSize.height - secondary.lineHeight) - secondary.position.y > -Math::MACHINE_EPSILON_1000) &&
343 (secondary.position.y + (secondary.verticallyFlipped ? 0.f : secondary.lineHeight) > -Math::MACHINE_EPSILON_1000));
345 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
346 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
348 if(primary.active || secondary.active)
350 if(primaryVisible || secondaryVisible)
352 CreateSelectionHandles();
356 SetSelectionHandlePosition(LEFT_SELECTION_HANDLE);
358 // Sets the primary handle image according if it's pressed, flipped, etc.
359 SetHandleImage(LEFT_SELECTION_HANDLE);
361 SetSelectionHandleMarkerSize(primary);
363 newPrimaryHandlePosition = true;
368 SetSelectionHandlePosition(RIGHT_SELECTION_HANDLE);
370 // Sets the secondary handle image according if it's pressed, flipped, etc.
371 SetHandleImage(RIGHT_SELECTION_HANDLE);
373 SetSelectionHandleMarkerSize(secondary);
375 newSecondaryHandlePosition = true;
381 primary.actor.SetProperty(Actor::Property::VISIBLE, primaryVisible);
385 secondary.actor.SetProperty(Actor::Property::VISIBLE, secondaryVisible);
392 primary.actor.Unparent();
396 secondary.actor.Unparent();
400 if(mIsHighlightBoxActive)
409 mHighlightActor.Unparent();
413 if(newGrabHandlePosition ||
414 newPrimaryHandlePosition ||
415 newSecondaryHandlePosition)
417 // Setup property notifications to find whether the handles leave the boundaries of the current display.
418 SetupActiveLayerPropertyNotifications();
421 if(mActiveCopyPastePopup &&
422 (primaryVisible || secondaryVisible))
425 mPopupSetNewPosition = true;
429 if(mCopyPastePopup.actor)
431 mCopyPastePopup.actor.HidePopup();
432 mPopupSetNewPosition = true;
437 void UpdatePositions(const Vector2& scrollOffset)
439 mCursor[PRIMARY_CURSOR].position += scrollOffset;
440 mCursor[SECONDARY_CURSOR].position += scrollOffset;
441 mHandle[GRAB_HANDLE].position += scrollOffset;
442 mHandle[LEFT_SELECTION_HANDLE].position += scrollOffset;
443 mHandle[RIGHT_SELECTION_HANDLE].position += scrollOffset;
444 mHighlightPosition += scrollOffset;
449 if(!mCopyPastePopup.actor)
454 if(!mCopyPastePopup.actor.GetParent())
456 mActiveLayer.Add(mCopyPastePopup.actor);
459 mCopyPastePopup.actor.RaiseAbove(mActiveLayer);
460 mCopyPastePopup.actor.ShowPopup();
463 float CalculateVerticalPopUpPosition(float halfHeight, bool preferBelow)
465 float yPosition = 0.f;
467 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
468 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
469 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
471 if(primaryHandle.active || secondaryHandle.active)
473 // The origin of the decorator's coordinate system in world coords.
474 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION) - mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * ACTIVE_LAYER_ANCHOR_POINT;
478 // Find out if there is enough space for the popup at the bottom.
479 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
480 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
482 float maxY = std::max(primaryBelowY, secondaryBelowY);
484 yPosition = halfHeight + maxY;
486 if(originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w)
488 // Does not fit below.
490 // Try to fit first below the non active handle. Otherwise above the active handle.
491 if(RIGHT_SELECTION_HANDLE == mHandleReleased)
493 if(primaryBelowY < secondaryBelowY)
495 yPosition = halfHeight + primaryBelowY;
499 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
502 else if(LEFT_SELECTION_HANDLE == mHandleReleased)
504 if(secondaryBelowY < primaryBelowY)
506 yPosition = halfHeight + secondaryBelowY;
510 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
514 // Check the handle is whithin the decoration box.
515 if(originWorldCoords.y + yPosition < mBoundingBox.y)
517 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
520 if(originWorldCoords.y + yPosition > mBoundingBox.w)
522 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
528 // Find out if there is enough space for the popup at the top.
529 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
530 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
532 float minY = std::min(primaryTopY, secondaryTopY);
534 yPosition = -halfHeight + minY;
536 } // ( primaryHandle.active || secondaryHandle.active )
537 else if(grabHandle.active)
541 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
545 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
552 void ConstrainPopupPosition(const Vector3& popupHalfSize)
554 // Check if the popup is within the boundaries of the decoration box.
556 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
558 // The origin of the decorator's coordinate system in world coords.
559 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION) - mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * ACTIVE_LAYER_ANCHOR_POINT;
561 // The popup's position in world coords.
562 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
564 if(popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x)
566 mCopyPastePopup.position.x += mBoundingBox.x - (popupPositionWorldCoords.x - popupHalfSize.width);
568 else if(popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z)
570 mCopyPastePopup.position.x += mBoundingBox.z - (popupPositionWorldCoords.x + popupHalfSize.width);
573 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
574 if(popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y)
576 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition(popupHalfSize.height, true); // true -> prefer to set the popup's position below.
580 void SetPopupPosition(Actor actor)
582 if(!mActiveCopyPastePopup)
587 // Retrieves the popup's size after relayout.
588 const Vector3 popupSize(mCopyPastePopup.actor.GetRelayoutSize(Dimension::WIDTH), mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT), 0.0f);
589 const Vector3 popupHalfSize = popupSize * 0.5f;
591 if(mPopupSetNewPosition)
593 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
594 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
595 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
597 if(primaryHandle.active || secondaryHandle.active)
599 const float minHandleXPosition = std::min(primaryHandle.position.x, secondaryHandle.position.x);
600 const float maxHandleXPosition = std::max(primaryHandle.position.x, secondaryHandle.position.x);
602 mCopyPastePopup.position.x = minHandleXPosition + ((maxHandleXPosition - minHandleXPosition) * 0.5f);
604 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - (primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING);
605 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - (secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING);
607 mCopyPastePopup.position.y = std::min(primaryY, secondaryY);
609 else if(grabHandle.active)
611 mCopyPastePopup.position.x = grabHandle.position.x;
613 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - (grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING);
615 } // mPopupSetNewPosition
617 // It may change the popup's position to fit within the decoration box.
618 ConstrainPopupPosition(popupHalfSize);
620 SetUpPopupPositionNotifications(popupHalfSize);
622 // Prevent pixel mis-alignment by rounding down.
623 mCopyPastePopup.position.x = floorf(mCopyPastePopup.position.x);
624 mCopyPastePopup.position.y = floorf(mCopyPastePopup.position.y);
626 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION, mCopyPastePopup.position);
627 mPopupSetNewPosition = false;
630 void CreateCursor(Control& cursor, const Vector4& color)
632 cursor = Control::New();
633 cursor.SetBackgroundColor(color);
634 cursor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
635 cursor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
636 cursor.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIDDEN, true);
639 // Add or Remove cursor(s) from parent
642 if(mActiveCursor == ACTIVE_CURSOR_NONE)
646 mPrimaryCursor.Unparent();
650 mSecondaryCursor.Unparent();
655 // Create Primary and or Secondary Cursor(s) if active and add to parent
656 if(mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
657 mActiveCursor == ACTIVE_CURSOR_BOTH)
661 CreateCursor(mPrimaryCursor, mCursor[PRIMARY_CURSOR].color);
662 #ifdef DECORATOR_DEBUG
663 mPrimaryCursor.SetProperty(Dali::Actor::Property::NAME, "PrimaryCursorActor");
667 if(!mPrimaryCursor.GetParent())
669 mCursorLayer.Add(mPrimaryCursor);
673 if(mActiveCursor == ACTIVE_CURSOR_BOTH)
675 if(!mSecondaryCursor)
677 CreateCursor(mSecondaryCursor, mCursor[SECONDARY_CURSOR].color);
678 #ifdef DECORATOR_DEBUG
679 mSecondaryCursor.SetProperty(Dali::Actor::Property::NAME, "SecondaryCursorActor");
683 if(!mSecondaryCursor.GetParent())
685 mCursorLayer.Add(mSecondaryCursor);
692 mSecondaryCursor.Unparent();
698 bool OnCursorBlinkTimerTick()
700 if(!mDelayCursorBlink)
705 mPrimaryCursor.SetProperty(Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus);
709 mSecondaryCursor.SetProperty(Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus);
712 mCursorBlinkStatus = !mCursorBlinkStatus;
717 mDelayCursorBlink = false;
725 // Will consume tap gestures on handles.
726 mTapDetector = TapGestureDetector::New();
728 // Will consume double tap gestures on handles.
729 mTapDetector.SetMaximumTapsRequired(2u);
731 // Will consume long press gestures on handles.
732 mLongPressDetector = LongPressGestureDetector::New();
734 // Detects pan gestures on handles.
735 mPanDetector = PanGestureDetector::New();
736 mPanDetector.DetectedSignal().Connect(this, &Decorator::Impl::OnPan);
739 void CreateLayer(Actor& layer, DecorationType type)
743 layer = Actor::New();
744 #ifdef DECORATOR_DEBUG
745 if(type == DecorationType::ACTIVE_LAYER)
747 layer.SetProperty(Actor::Property::NAME, "ActiveLayerActor");
749 else if(type == DecorationType::CURSOR_LAYER)
751 layer.SetProperty(Actor::Property::NAME, "CursorLayerActor");
754 bool needsClipping = false;
755 if(type == DecorationType::CURSOR_LAYER)
757 needsClipping = true;
760 layer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
761 layer.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
763 mController.AddDecoration(layer, type, needsClipping);
769 void SetSelectionHandleMarkerSize(HandleImpl& handle)
771 if(handle.markerActor)
773 handle.markerActor.SetProperty(Actor::Property::SIZE, Vector2(0, handle.lineHeight));
777 void CreateGrabHandle()
779 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
780 if(!grabHandle.actor)
782 if(mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED].size())
784 grabHandle.actor = ImageView::New(mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED]);
785 GetImpl(grabHandle.actor).SetDepthIndex(DepthIndex::DECORATION);
786 grabHandle.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
788 // Area that Grab handle responds to, larger than actual handle so easier to move
789 #ifdef DECORATOR_DEBUG
790 grabHandle.actor.SetProperty(Dali::Actor::Property::NAME, "GrabHandleActor");
791 if(Dali::Internal::gLogFilter->IsEnabledFor(Debug::Verbose))
793 grabHandle.grabArea = Control::New();
794 Toolkit::Control control = Toolkit::Control::DownCast(grabHandle.grabArea);
795 control.SetBackgroundColor(Vector4(1.0f, 1.0f, 1.0f, 0.5f));
796 grabHandle.grabArea.SetProperty(Dali::Actor::Property::NAME, "GrabArea");
800 grabHandle.grabArea = Actor::New();
801 grabHandle.grabArea.SetProperty(Dali::Actor::Property::NAME, "GrabArea");
804 grabHandle.grabArea = Actor::New();
807 grabHandle.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
808 grabHandle.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
809 grabHandle.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
810 grabHandle.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_GRAB_HANDLE_RELATIVE_SIZE);
811 grabHandle.actor.Add(grabHandle.grabArea);
812 grabHandle.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
814 grabHandle.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnGrabHandleTouched);
816 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
817 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
818 mTapDetector.Attach(grabHandle.actor);
819 mLongPressDetector.Attach(grabHandle.actor);
821 // The grab handle's area is attached to the pan detector.
822 // The OnPan() method is connected to the signals emitted by the pan detector.
823 mPanDetector.Attach(grabHandle.grabArea);
825 mActiveLayer.Add(grabHandle.actor);
829 if(grabHandle.actor && !grabHandle.actor.GetParent())
831 mActiveLayer.Add(grabHandle.actor);
835 void CreateHandleMarker(HandleImpl& handle, const std::string& image, HandleType handleType)
839 handle.markerActor = ImageView::New(image);
840 handle.markerActor.SetProperty(Actor::Property::COLOR, mHandleColor);
841 handle.actor.Add(handle.markerActor);
843 handle.markerActor.SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
845 if(LEFT_SELECTION_HANDLE == handleType)
847 handle.markerActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT);
848 handle.markerActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_RIGHT);
850 else if(RIGHT_SELECTION_HANDLE == handleType)
852 handle.markerActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT);
853 handle.markerActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
858 void CreateSelectionHandles()
860 HandleImpl& primary = mHandle[LEFT_SELECTION_HANDLE];
863 if(mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size())
865 primary.actor = ImageView::New(mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]);
866 #ifdef DECORATOR_DEBUG
867 primary.actor.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleOne");
869 primary.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
870 GetImpl(primary.actor).SetDepthIndex(DepthIndex::DECORATION);
871 primary.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
873 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
874 #ifdef DECORATOR_DEBUG
875 primary.grabArea.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleOneGrabArea");
877 primary.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
878 primary.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
879 primary.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
880 primary.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE);
882 primary.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnHandleOneTouched);
884 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
885 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
886 mTapDetector.Attach(primary.actor);
887 mLongPressDetector.Attach(primary.actor);
889 // The handle's area is attached to the pan detector.
890 // The OnPan() method is connected to the signals emitted by the pan detector.
891 mPanDetector.Attach(primary.grabArea);
893 primary.actor.Add(primary.grabArea);
895 CreateHandleMarker(primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE);
899 if(primary.actor && !primary.actor.GetParent())
901 mActiveLayer.Add(primary.actor);
904 HandleImpl& secondary = mHandle[RIGHT_SELECTION_HANDLE];
907 if(mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size())
909 secondary.actor = ImageView::New(mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]);
910 #ifdef DECORATOR_DEBUG
911 secondary.actor.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleTwo");
913 secondary.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
914 GetImpl(secondary.actor).SetDepthIndex(DepthIndex::DECORATION);
915 secondary.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
917 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
918 #ifdef DECORATOR_DEBUG
919 secondary.grabArea.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleTwoGrabArea");
921 secondary.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
922 secondary.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
923 secondary.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
924 secondary.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE);
926 secondary.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnHandleTwoTouched);
928 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
929 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
930 mTapDetector.Attach(secondary.actor);
931 mLongPressDetector.Attach(secondary.actor);
933 // The handle's area is attached to the pan detector.
934 // The OnPan() method is connected to the signals emitted by the pan detector.
935 mPanDetector.Attach(secondary.grabArea);
937 secondary.actor.Add(secondary.grabArea);
939 CreateHandleMarker(secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE);
943 if(secondary.actor && !secondary.actor.GetParent())
945 mActiveLayer.Add(secondary.actor);
949 void CalculateHandleWorldCoordinates(HandleImpl& handle, Vector2& position)
951 // Gets the world position of the active layer. The active layer is where the handles are added.
952 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION);
954 // The grab handle position in world coords.
955 // The active layer's world position is the center of the active layer. The origin of the
956 // coord system of the handles is the top left of the active layer.
957 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + (mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f);
958 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + (mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f);
961 void SetGrabHandlePosition()
963 // Reference to the grab handle.
964 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
966 // Transforms the handle position into world coordinates.
967 // @note This is not the same value as grabHandle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
968 // as it's transforming the handle's position set by the text-controller and not
969 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
970 // retrieves the position of the center of the actor but the handle's position set
971 // by the text controller is not the center of the actor.
972 Vector2 grabHandleWorldPosition;
973 CalculateHandleWorldCoordinates(grabHandle, grabHandleWorldPosition);
975 // Check if the grab handle exceeds the boundaries of the decoration box.
976 // At the moment only the height is checked for the grab handle.
978 grabHandle.verticallyFlipped = (grabHandle.verticallyFlippedPreferred &&
979 ((grabHandleWorldPosition.y - grabHandle.size.height) > mBoundingBox.y)) ||
980 (grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w);
982 // The grab handle 'y' position in local coords.
983 // If the grab handle exceeds the bottom of the decoration box,
984 // set the 'y' position to the top of the line.
985 // The SetGrabHandleImage() method will change the orientation.
986 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
988 ApplyDisplacement(grabHandle, yLocalPosition);
991 void SetSelectionHandlePosition(HandleType type)
993 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
995 // Reference to the selection handle.
996 HandleImpl& handle = mHandle[type];
998 // Transforms the handle position into world coordinates.
999 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1000 // as it's transforming the handle's position set by the text-controller and not
1001 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1002 // retrieves the position of the center of the actor but the handle's position set
1003 // by the text controller is not the center of the actor.
1004 Vector2 handleWorldPosition;
1005 CalculateHandleWorldCoordinates(handle, handleWorldPosition);
1007 // Whether to flip the handle (horizontally).
1008 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1010 // Whether to flip the handles if they are crossed.
1011 bool crossFlip = false;
1012 if(mFlipSelectionHandlesOnCross || !mIsHandlePanning)
1014 crossFlip = mIsHandleCurrentlyCrossed;
1017 // Whether the handle was crossed before start the panning.
1018 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1020 // Does not flip if both conditions are true (double flip)
1021 flipHandle = flipHandle != (crossFlip || isHandlePreviouslyCrossed);
1023 // Will flip the handles vertically if the user prefers it.
1024 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1026 if(crossFlip || isHandlePreviouslyCrossed)
1030 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1034 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1038 // Check if the selection handle exceeds the boundaries of the decoration box.
1039 const bool exceedsLeftEdge = (isPrimaryHandle ? !flipHandle : flipHandle) && (handleWorldPosition.x - handle.size.width < mBoundingBox.x);
1040 const bool exceedsRightEdge = (isPrimaryHandle ? flipHandle : !flipHandle) && (handleWorldPosition.x + handle.size.width > mBoundingBox.z);
1042 // Does not flip if both conditions are true (double flip)
1043 flipHandle = flipHandle != (exceedsLeftEdge || exceedsRightEdge);
1047 if(handle.actor && !handle.horizontallyFlipped)
1049 // Change the anchor point to flip the image.
1050 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT);
1052 handle.horizontallyFlipped = true;
1057 if(handle.actor && handle.horizontallyFlipped)
1059 // Reset the anchor point.
1060 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT);
1062 handle.horizontallyFlipped = false;
1066 // Whether to flip the handle vertically.
1067 handle.verticallyFlipped = (verticallyFlippedPreferred &&
1068 ((handleWorldPosition.y - handle.size.height) > mBoundingBox.y)) ||
1069 (handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w);
1071 // The primary selection handle 'y' position in local coords.
1072 // If the handle exceeds the bottom of the decoration box,
1073 // set the 'y' position to the top of the line.
1074 // The SetHandleImage() method will change the orientation.
1075 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1077 ApplyDisplacement(handle, yLocalPosition);
1080 void ApplyDisplacement(HandleImpl& handle, float yLocalPosition)
1084 float adjustedDisplacementX = 0.0f;
1085 float adjustedDisplacementY = 0.0f;
1086 if (mSmoothHandlePanEnabled)
1088 adjustedDisplacementX = CalculateAdjustedDisplacement(handle.position.x, handle.grabDisplacementX, mControlSize.x);
1089 adjustedDisplacementY = CalculateAdjustedDisplacement(handle.position.y, handle.grabDisplacementY, (mControlSize.y - handle.lineHeight));
1091 handle.actor.SetProperty(Actor::Property::POSITION,
1092 Vector2(handle.position.x + floor(0.5f * mCursorWidth) + adjustedDisplacementX,
1093 yLocalPosition + adjustedDisplacementY));
1097 float CalculateAdjustedDisplacement(float position, float displacement, float edge)
1099 //Apply the displacement (on the X-axis & the Y-axis)
1100 //as long as it does not exceed the control's edge.
1101 float adjustedDisplacement = 0.0f;
1102 if(position + displacement < 0.0f)
1104 // -position to cancel it out and relocate to 0.
1105 adjustedDisplacement = -position;
1107 else if(position + displacement > edge)
1109 // move in a displacement which is sufficient to reach the edge.
1110 adjustedDisplacement = edge - position;
1114 // move normally in the displacement.
1115 adjustedDisplacement = displacement;
1117 return adjustedDisplacement;
1120 void SetHandleImage(HandleType type)
1122 HandleImpl& handle = mHandle[type];
1124 HandleType markerType = HANDLE_TYPE_COUNT;
1125 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1126 if(LEFT_SELECTION_HANDLE == type)
1128 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1129 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1131 else if(RIGHT_SELECTION_HANDLE == type)
1133 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1134 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1137 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1140 const HandleImageType imageType = (handle.pressed ? (mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1142 handle.actor.SetImage(mHandleImages[type][imageType]);
1145 if(HANDLE_TYPE_COUNT != markerType)
1147 if(handle.markerActor)
1149 const HandleImageType markerImageType = (handle.pressed ? (mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1150 handle.markerActor.SetImage(mHandleImages[markerType][markerImageType]);
1154 // Whether to flip the handle vertically.
1157 handle.actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS));
1161 void CreateHighlight()
1163 if(!mHighlightActor)
1165 mHighlightActor = Actor::New();
1167 mHighlightActor.SetProperty(Dali::Actor::Property::NAME, "HighlightActor");
1168 mHighlightActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1169 mHighlightActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1170 mHighlightActor.SetProperty(Actor::Property::COLOR, mHighlightColor);
1171 mHighlightActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
1174 // Add the highlight box telling the controller it needs clipping.
1175 mController.AddDecoration(mHighlightActor, DecorationType::NONE_LAYER, true);
1178 void UpdateHighlight()
1182 // Sets the position of the highlight actor inside the decorator.
1183 mHighlightActor.SetProperty(Actor::Property::POSITION, Vector2(mHighlightPosition.x + mHighlightOutlineOffset, mHighlightPosition.y + mHighlightOutlineOffset));
1185 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1186 if(0u != numberOfQuads)
1188 // Set the size of the highlighted text to the actor.
1189 mHighlightActor.SetProperty(Actor::Property::SIZE, mHighlightSize);
1191 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1192 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1193 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1195 Vector<Vector2> vertices;
1196 Vector<unsigned short> indices;
1198 vertices.Reserve(4u * numberOfQuads);
1199 indices.Reserve(6u * numberOfQuads);
1201 // Index to the vertex.
1202 unsigned int v = 0u;
1204 // Traverse all quads.
1205 for(Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1206 endIt = mHighlightQuadList.End();
1210 const Vector4& quad = *it;
1215 vertex.x = quad.x - offsetX;
1216 vertex.y = quad.y - offsetY;
1217 vertices.PushBack(vertex);
1220 vertex.x = quad.z - offsetX;
1221 vertex.y = quad.y - offsetY;
1222 vertices.PushBack(vertex);
1224 // bottom-left (v+2)
1225 vertex.x = quad.x - offsetX;
1226 vertex.y = quad.w - offsetY;
1227 vertices.PushBack(vertex);
1229 // bottom-right (v+3)
1230 vertex.x = quad.z - offsetX;
1231 vertex.y = quad.w - offsetY;
1232 vertices.PushBack(vertex);
1234 // triangle A (3, 1, 0)
1235 indices.PushBack(v + 3);
1236 indices.PushBack(v + 1);
1237 indices.PushBack(v);
1239 // triangle B (0, 2, 3)
1240 indices.PushBack(v);
1241 indices.PushBack(v + 2);
1242 indices.PushBack(v + 3);
1247 mQuadVertices = VertexBuffer::New(mQuadVertexFormat);
1250 mQuadVertices.SetData(&vertices[0], vertices.Size());
1254 mQuadGeometry = Geometry::New();
1255 mQuadGeometry.AddVertexBuffer(mQuadVertices);
1257 mQuadGeometry.SetIndexBuffer(&indices[0], indices.Size());
1259 if(!mHighlightRenderer)
1261 mHighlightRenderer = Dali::Renderer::New(mQuadGeometry, mHighlightShader);
1262 mHighlightActor.AddRenderer(mHighlightRenderer);
1266 mHighlightQuadList.Clear();
1268 if(mHighlightRenderer)
1270 mHighlightRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mTextDepth - 2); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1275 void DoPan(HandleImpl& handle, HandleType type, const PanGesture& gesture)
1277 GestureState state = gesture.GetState();
1278 if(GestureState::STARTED == state)
1280 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1282 handle.globalPosition.x = handle.position.x;
1283 handle.globalPosition.y = handle.position.y;
1286 const Vector2& displacement = gesture.GetDisplacement();
1287 handle.grabDisplacementX += displacement.x;
1288 handle.grabDisplacementY += (handle.verticallyFlipped ? -displacement.y : displacement.y);
1290 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1291 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1292 const float yVerticallyFlippedCorrected = y - (handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f);
1294 if((GestureState::STARTED == state) ||
1295 (GestureState::CONTINUING == state))
1298 mController.GetTargetSize(targetSize);
1300 if(mHorizontalScrollingEnabled &&
1301 (x < mScrollThreshold))
1303 mScrollDirection = SCROLL_RIGHT;
1304 mHandleScrolling = type;
1307 else if(mHorizontalScrollingEnabled &&
1308 (x > targetSize.width - mScrollThreshold))
1310 mScrollDirection = SCROLL_LEFT;
1311 mHandleScrolling = type;
1314 else if(mVerticalScrollingEnabled &&
1315 (yVerticallyFlippedCorrected < mScrollThreshold))
1317 mScrollDirection = SCROLL_TOP;
1318 mHandleScrolling = type;
1321 else if(mVerticalScrollingEnabled &&
1322 (yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold))
1324 mScrollDirection = SCROLL_BOTTOM;
1325 mHandleScrolling = type;
1330 mHandleScrolling = HANDLE_TYPE_COUNT;
1332 mController.DecorationEvent(type, HANDLE_PRESSED, x, y);
1335 mIsHandlePanning = true;
1337 else if((GestureState::FINISHED == state) ||
1338 (GestureState::CANCELLED == state))
1341 (mScrollTimer.IsRunning() || mNotifyEndOfScroll))
1343 mNotifyEndOfScroll = false;
1344 mHandleScrolling = HANDLE_TYPE_COUNT;
1346 mController.DecorationEvent(type, HANDLE_STOP_SCROLLING, x, y);
1350 mController.DecorationEvent(type, HANDLE_RELEASED, x, y);
1355 handle.actor.SetImage(mHandleImages[type][HANDLE_IMAGE_RELEASED]);
1357 handle.pressed = false;
1359 mIsHandlePanning = false;
1363 void OnPan(Actor actor, const PanGesture& gesture)
1365 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1366 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1367 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1369 if(actor == grabHandle.grabArea)
1371 DoPan(grabHandle, GRAB_HANDLE, gesture);
1373 else if(actor == primarySelectionHandle.grabArea)
1375 DoPan(primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture);
1377 else if(actor == secondarySelectionHandle.grabArea)
1379 DoPan(secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture);
1383 bool OnGrabHandleTouched(Actor actor, const TouchEvent& touch)
1385 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1387 // Switch between pressed/release grab-handle images
1388 if(touch.GetPointCount() > 0 &&
1391 const PointState::Type state = touch.GetState(0);
1393 if(PointState::DOWN == state)
1395 grabHandle.pressed = true;
1397 else if((PointState::UP == state) ||
1398 (PointState::INTERRUPTED == state))
1400 grabHandle.pressed = false;
1403 SetHandleImage(GRAB_HANDLE);
1409 bool OnHandleOneTouched(Actor actor, const TouchEvent& touch)
1411 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1413 // Switch between pressed/release selection handle images
1414 if(touch.GetPointCount() > 0 &&
1415 primarySelectionHandle.actor)
1417 const PointState::Type state = touch.GetState(0);
1419 if(PointState::DOWN == state)
1421 primarySelectionHandle.pressed = true;
1422 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1424 else if((PointState::UP == state) ||
1425 (PointState::INTERRUPTED == state))
1427 primarySelectionHandle.pressed = false;
1428 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1429 mIsHandlePanning = false;
1430 mHandleReleased = LEFT_SELECTION_HANDLE;
1433 SetHandleImage(LEFT_SELECTION_HANDLE);
1439 bool OnHandleTwoTouched(Actor actor, const TouchEvent& touch)
1441 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1443 // Switch between pressed/release selection handle images
1444 if(touch.GetPointCount() > 0 &&
1445 secondarySelectionHandle.actor)
1447 const PointState::Type state = touch.GetState(0);
1449 if(PointState::DOWN == state)
1451 secondarySelectionHandle.pressed = true;
1452 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1454 else if((PointState::UP == state) ||
1455 (PointState::INTERRUPTED == state))
1457 secondarySelectionHandle.pressed = false;
1458 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1459 mIsHandlePanning = false;
1460 mHandleReleased = RIGHT_SELECTION_HANDLE;
1463 SetHandleImage(RIGHT_SELECTION_HANDLE);
1469 void HandleResetPosition(PropertyNotification& source)
1471 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1473 if(grabHandle.active)
1475 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1476 SetGrabHandlePosition();
1478 // Sets the grab handle image according if it's pressed, flipped, etc.
1479 SetHandleImage(GRAB_HANDLE);
1483 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1484 SetSelectionHandlePosition(LEFT_SELECTION_HANDLE);
1486 // Sets the primary handle image according if it's pressed, flipped, etc.
1487 SetHandleImage(LEFT_SELECTION_HANDLE);
1489 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1490 SetSelectionHandlePosition(RIGHT_SELECTION_HANDLE);
1492 // Sets the secondary handle image according if it's pressed, flipped, etc.
1493 SetHandleImage(RIGHT_SELECTION_HANDLE);
1497 void SetupActiveLayerPropertyNotifications()
1504 // Vertical notifications.
1506 // Disconnect any previous connected callback.
1507 if(mHandleVerticalLessThanNotification)
1509 mHandleVerticalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1510 mActiveLayer.RemovePropertyNotification(mHandleVerticalLessThanNotification);
1513 if(mHandleVerticalGreaterThanNotification)
1515 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1516 mActiveLayer.RemovePropertyNotification(mHandleVerticalGreaterThanNotification);
1519 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1520 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1521 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1523 if(grabHandle.active)
1525 if(grabHandle.verticallyFlipped)
1527 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1528 mHandleVerticalGreaterThanNotification.Reset();
1530 // The vertical distance from the center of the active layer to the top edje of the display.
1531 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1533 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1534 LessThanCondition(mBoundingBox.y + topHeight));
1536 // Notifies the change from false to true and from true to false.
1537 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1539 // Connects the signals with the callbacks.
1540 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1544 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1545 mHandleVerticalLessThanNotification.Reset();
1547 // The vertical distance from the center of the active layer to the bottom edje of the display.
1548 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1550 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1551 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1553 // Notifies the change from false to true and from true to false.
1554 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1556 // Connects the signals with the callbacks.
1557 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1560 else // The selection handles are active
1562 if(primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped)
1564 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1565 mHandleVerticalGreaterThanNotification.Reset();
1567 // The vertical distance from the center of the active layer to the top edje of the display.
1568 const float topHeight = 0.5f * mControlSize.height + std::max(-primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height);
1570 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1571 LessThanCondition(mBoundingBox.y + topHeight));
1573 // Notifies the change from false to true and from true to false.
1574 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1576 // Connects the signals with the callbacks.
1577 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1579 else if(!primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped)
1581 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1582 mHandleVerticalLessThanNotification.Reset();
1584 // The vertical distance from the center of the active layer to the bottom edje of the display.
1585 const float bottomHeight = -0.5f * mControlSize.height + std::max(primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1586 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height);
1588 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1589 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1591 // Notifies the change from false to true and from true to false.
1592 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1594 // Connects the signals with the callbacks.
1595 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1599 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1601 // The vertical distance from the center of the active layer to the top edje of the display.
1602 const float topHeight = 0.5f * mControlSize.height + (primaryHandle.verticallyFlipped ? -primaryHandle.position.y + primaryHandle.size.height : -secondaryHandle.position.y + secondaryHandle.size.height);
1604 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1605 LessThanCondition(mBoundingBox.y + topHeight));
1607 // Notifies the change from false to true and from true to false.
1608 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1610 // Connects the signals with the callbacks.
1611 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1613 // The vertical distance from the center of the active layer to the bottom edje of the display.
1614 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);
1616 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1617 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1619 // Notifies the change from false to true and from true to false.
1620 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1622 // Connects the signals with the callbacks.
1623 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1627 // Horizontal notifications.
1629 // Disconnect any previous connected callback.
1630 if(mHandleHorizontalLessThanNotification)
1632 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1633 mActiveLayer.RemovePropertyNotification(mHandleHorizontalLessThanNotification);
1636 if(mHandleHorizontalGreaterThanNotification)
1638 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1639 mActiveLayer.RemovePropertyNotification(mHandleHorizontalGreaterThanNotification);
1642 if(primaryHandle.active || secondaryHandle.active)
1644 // The horizontal distance from the center of the active layer to the left edje of the display.
1645 const float leftWidth = 0.5f * mControlSize.width + std::max(-primaryHandle.position.x + primaryHandle.size.width,
1646 -secondaryHandle.position.x + secondaryHandle.size.width);
1648 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1649 LessThanCondition(mBoundingBox.x + leftWidth));
1651 // Notifies the change from false to true and from true to false.
1652 mHandleHorizontalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1654 // Connects the signals with the callbacks.
1655 mHandleHorizontalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1657 // The horizontal distance from the center of the active layer to the right edje of the display.
1658 const float rightWidth = -0.5f * mControlSize.width + std::max(primaryHandle.position.x + primaryHandle.size.width,
1659 secondaryHandle.position.x + secondaryHandle.size.width);
1661 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1662 GreaterThanCondition(mBoundingBox.z - rightWidth));
1664 // Notifies the change from false to true and from true to false.
1665 mHandleHorizontalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1667 // Connects the signals with the callbacks.
1668 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1674 float AlternatePopUpPositionRelativeToCursor(bool topBottom)
1676 float alternativePosition = 0.0f;
1678 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1680 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1681 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1682 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1683 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1685 if(primaryHandle.active || secondaryHandle.active)
1687 float handleY = 0.f;
1688 float maxHandleHeight = 0.f;
1690 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1691 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1693 if(primaryVisible && secondaryVisible)
1695 handleY = std::max(primaryHandle.position.y, secondaryHandle.position.y);
1696 maxHandleHeight = std::max(primaryHandle.size.height, secondaryHandle.size.height);
1698 else if(primaryVisible && !secondaryVisible)
1700 handleY = primaryHandle.position.y;
1701 maxHandleHeight = primaryHandle.size.height;
1703 else if(!primaryVisible && secondaryVisible)
1705 handleY = secondaryHandle.position.y;
1706 maxHandleHeight = secondaryHandle.size.height;
1709 alternativePosition = handleY + (topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight);
1713 alternativePosition = cursor.position.y + (topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height);
1716 return alternativePosition;
1719 void PopUpLeavesTopBoundary(PropertyNotification& source)
1721 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1723 // Sets the position of the popup below.
1724 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, true)));
1727 void PopUpLeavesBottomBoundary(PropertyNotification& source)
1729 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1731 // Sets the position of the popup above.
1732 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, false)));
1735 void SetUpPopupPositionNotifications(const Vector3& popupHalfSize)
1737 // Disconnect any previous connected callback.
1738 if(mPopupTopExceedNotification)
1740 mPopupTopExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1741 mCopyPastePopup.actor.RemovePropertyNotification(mPopupTopExceedNotification);
1744 if(mPopupBottomExceedNotification)
1746 mPopupBottomExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1747 mCopyPastePopup.actor.RemovePropertyNotification(mPopupBottomExceedNotification);
1750 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1752 // Exceeding vertical boundary
1754 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1755 LessThanCondition(mBoundingBox.y + popupHalfSize.height));
1757 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1758 GreaterThanCondition(mBoundingBox.w - popupHalfSize.height));
1760 // Notifies the change from false to true and from true to false.
1761 mPopupTopExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1762 mPopupBottomExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1764 mPopupTopExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1765 mPopupBottomExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1768 void SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
1770 ImageDimensions dimensions = Dali::GetOriginalImageSize(imageFileName);
1772 HandleImpl& handle = mHandle[handleType];
1773 handle.size = Size(dimensions.GetWidth(), dimensions.GetHeight());
1775 mHandleImages[handleType][handleImageType] = imageFileName;
1778 void SetScrollThreshold(float threshold)
1780 mScrollThreshold = threshold;
1783 float GetScrollThreshold() const
1785 return mScrollThreshold;
1788 void SetScrollSpeed(float speed)
1790 mScrollSpeed = speed;
1791 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1794 float GetScrollSpeed() const
1796 return mScrollSpeed;
1799 void NotifyEndOfScroll()
1805 mNotifyEndOfScroll = true;
1810 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1812 * It only starts the timer if it's already created.
1814 void StartScrollTimer()
1818 mScrollTimer = Timer::New(SCROLL_TICK_INTERVAL);
1819 mScrollTimer.TickSignal().Connect(this, &Decorator::Impl::OnScrollTimerTick);
1822 if(!mScrollTimer.IsRunning())
1824 mScrollTimer.Start();
1829 * Stops the timer used to scroll the text.
1831 void StopScrollTimer()
1835 mScrollTimer.Stop();
1840 * Callback called by the timer used to scroll the text.
1842 * It calculates and sets a new scroll position.
1844 bool OnScrollTimerTick()
1846 if(HANDLE_TYPE_COUNT != mHandleScrolling)
1851 switch(mScrollDirection)
1855 x = mScrollDistance;
1860 x = -mScrollDistance;
1865 y = mScrollDistance;
1870 y = -mScrollDistance;
1877 mController.DecorationEvent(mHandleScrolling,
1886 ControllerInterface& mController;
1888 TapGestureDetector mTapDetector;
1889 PanGestureDetector mPanDetector;
1890 LongPressGestureDetector mLongPressDetector;
1892 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1893 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1895 Actor mActiveLayer; ///< Actor for active handles and alike that ensures they are above all else.
1896 Actor mCursorLayer; ///< Actor for cursor layer. this is for cursor clipping.
1897 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1898 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1899 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1900 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1901 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1902 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1903 Control mPrimaryCursor;
1904 Control mSecondaryCursor;
1906 Actor mHighlightActor; ///< Actor to display highlight
1907 Renderer mHighlightRenderer;
1908 Shader mHighlightShader; ///< Shader used for highlight
1909 Property::Map mQuadVertexFormat;
1910 PopupImpl mCopyPastePopup;
1911 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1912 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1914 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1915 Vector4 mHandleColor;
1917 CursorImpl mCursor[CURSOR_COUNT];
1918 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1920 VertexBuffer mQuadVertices;
1921 Geometry mQuadGeometry;
1922 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1924 Vector4 mBoundingBox; ///< The bounding box in world coords.
1925 Vector4 mHighlightColor; ///< Color of the highlight
1926 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1927 Size mHighlightSize; ///< The size of the highlighted text.
1928 Size mControlSize; ///< The control's size. Set by the Relayout.
1929 float mHighlightOutlineOffset; ///< The outline's offset.
1931 unsigned int mActiveCursor;
1932 unsigned int mCursorBlinkInterval;
1933 float mCursorBlinkDuration;
1934 float mCursorWidth; ///< The width of the cursors in pixels.
1935 HandleType mHandleScrolling; ///< The handle which is scrolling.
1936 HandleType mHandleReleased; ///< The last handle released.
1937 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1938 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1939 float mScrollSpeed; ///< The scroll speed in pixels per second.
1940 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1941 int mTextDepth; ///< The depth used to render the text.
1943 bool mActiveCopyPastePopup : 1;
1944 bool mPopupSetNewPosition : 1;
1945 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1946 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1947 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1948 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1949 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1950 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1951 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1952 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1953 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1954 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1955 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1956 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1957 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1958 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1959 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1960 bool mHidePrimaryCursorAndGrabHandle : 1; ///< Whether the primary cursor and grab are hidden always.
1963 DecoratorPtr Decorator::New(ControllerInterface& controller,
1964 TextSelectionPopupCallbackInterface& callbackInterface)
1966 return DecoratorPtr(new Decorator(controller,
1967 callbackInterface));
1970 void Decorator::SetBoundingBox(const Rect<int>& boundingBox)
1972 LocalToWorldCoordinatesBoundingBox(boundingBox, mImpl->mBoundingBox);
1975 void Decorator::GetBoundingBox(Rect<int>& boundingBox) const
1977 WorldToLocalCoordinatesBoundingBox(mImpl->mBoundingBox, boundingBox);
1980 void Decorator::Relayout(const Vector2& size)
1982 mImpl->Relayout(size);
1985 void Decorator::UpdatePositions(const Vector2& scrollOffset)
1987 mImpl->UpdatePositions(scrollOffset);
1992 void Decorator::SetActiveCursor(ActiveCursor activeCursor)
1994 mImpl->mActiveCursor = activeCursor;
1997 unsigned int Decorator::GetActiveCursor() const
1999 return mImpl->mActiveCursor;
2002 void Decorator::SetPosition(Cursor cursor, float x, float y, float cursorHeight, float lineHeight)
2004 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2006 cursorImpl.position.x = x;
2007 cursorImpl.position.y = y;
2008 cursorImpl.cursorHeight = cursorHeight;
2009 cursorImpl.lineHeight = lineHeight;
2012 void Decorator::GetPosition(Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight) const
2014 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2016 x = cursorImpl.position.x;
2017 y = cursorImpl.position.y;
2018 cursorHeight = cursorImpl.cursorHeight;
2019 lineHeight = cursorImpl.lineHeight;
2022 const Vector2& Decorator::GetPosition(Cursor cursor) const
2024 return mImpl->mCursor[cursor].position;
2027 void Decorator::SetGlyphOffset(Cursor cursor, float glyphOffset)
2029 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2031 cursorImpl.glyphOffset = glyphOffset;
2034 const float Decorator::GetGlyphOffset(Cursor cursor) const
2036 return mImpl->mCursor[cursor].glyphOffset;
2039 void Decorator::SetCursorColor(Cursor cursor, const Dali::Vector4& color)
2041 mImpl->mCursor[cursor].color = color;
2044 const Dali::Vector4& Decorator::GetColor(Cursor cursor) const
2046 return mImpl->mCursor[cursor].color;
2049 void Decorator::StartCursorBlink()
2051 if(!mImpl->mCursorBlinkTimer)
2053 mImpl->mCursorBlinkTimer = Timer::New(mImpl->mCursorBlinkInterval);
2054 mImpl->mCursorBlinkTimer.TickSignal().Connect(mImpl, &Decorator::Impl::OnCursorBlinkTimerTick);
2057 if(!mImpl->mCursorBlinkTimer.IsRunning())
2059 mImpl->mCursorBlinkTimer.Start();
2063 void Decorator::StopCursorBlink()
2065 if(mImpl->mCursorBlinkTimer)
2067 mImpl->mCursorBlinkTimer.Stop();
2070 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2073 void Decorator::DelayCursorBlink()
2075 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2076 mImpl->mDelayCursorBlink = true;
2079 void Decorator::SetCursorBlinkInterval(float seconds)
2081 mImpl->mCursorBlinkInterval = static_cast<unsigned int>(seconds * TO_MILLISECONDS); // Convert to milliseconds
2084 float Decorator::GetCursorBlinkInterval() const
2086 return static_cast<float>(mImpl->mCursorBlinkInterval) * TO_SECONDS;
2089 void Decorator::SetCursorBlinkDuration(float seconds)
2091 mImpl->mCursorBlinkDuration = seconds;
2094 float Decorator::GetCursorBlinkDuration() const
2096 return mImpl->mCursorBlinkDuration;
2099 void Decorator::SetCursorWidth(int width)
2101 mImpl->mCursorWidth = static_cast<float>(width);
2104 int Decorator::GetCursorWidth() const
2106 return static_cast<int>(mImpl->mCursorWidth);
2109 void Decorator::SetEditable(bool editable)
2111 mImpl->mHidePrimaryCursorAndGrabHandle = !editable;
2112 // If editable is false, all decorators should be disabled.
2115 if(IsHighlightActive())
2117 SetHighlightActive(false);
2119 if(IsHandleActive(LEFT_SELECTION_HANDLE))
2121 SetHandleActive(LEFT_SELECTION_HANDLE, false);
2123 if(IsHandleActive(RIGHT_SELECTION_HANDLE))
2125 SetHandleActive(RIGHT_SELECTION_HANDLE, false);
2129 SetPopupActive(false);
2133 mImpl->Relayout(mImpl->mControlSize);
2137 void Decorator::SetHandleActive(HandleType handleType, bool active)
2139 mImpl->mHandle[handleType].active = active;
2143 if((LEFT_SELECTION_HANDLE == handleType) || (RIGHT_SELECTION_HANDLE == handleType))
2145 mImpl->mIsHandlePreviouslyCrossed = false;
2148 // TODO: this is a work-around.
2149 // The problem is the handle actor does not receive the touch event with the Interrupt
2150 // state when the power button is pressed and the application goes to background.
2151 mImpl->mHandle[handleType].pressed = false;
2152 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2153 ImageView imageView = mImpl->mHandle[handleType].actor;
2154 if(imageReleased && imageView)
2156 imageView.SetImage(mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED]);
2161 bool Decorator::IsHandleActive(HandleType handleType) const
2163 return mImpl->mHandle[handleType].active;
2166 void Decorator::SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
2168 mImpl->SetHandleImage(handleType, handleImageType, imageFileName);
2171 const std::string& Decorator::GetHandleImage(HandleType handleType, HandleImageType handleImageType) const
2173 return mImpl->mHandleImages[handleType][handleImageType];
2176 void Decorator::SetHandleColor(const Vector4& color)
2178 mImpl->mHandleColor = color;
2181 const Vector4& Decorator::GetHandleColor() const
2183 return mImpl->mHandleColor;
2186 void Decorator::SetPosition(HandleType handleType, float x, float y, float height)
2188 // Adjust handle's displacement
2189 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2191 handle.position.x = x;
2192 handle.position.y = y;
2193 handle.lineHeight = height;
2195 if(mImpl->mSmoothHandlePanEnabled)
2197 handle.grabDisplacementX = 0.f;
2198 handle.grabDisplacementY = 0.f;
2202 void Decorator::GetPosition(HandleType handleType, float& x, float& y, float& height) const
2204 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2206 x = handle.position.x;
2207 y = handle.position.y;
2208 height = handle.lineHeight;
2211 const Vector2& Decorator::GetPosition(HandleType handleType) const
2213 return mImpl->mHandle[handleType].position;
2216 void Decorator::FlipHandleVertically(HandleType handleType, bool flip)
2218 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2221 bool Decorator::IsHandleVerticallyFlipped(HandleType handleType) const
2223 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2226 void Decorator::FlipSelectionHandlesOnCrossEnabled(bool enable)
2228 mImpl->mFlipSelectionHandlesOnCross = enable;
2231 void Decorator::SetSelectionHandleFlipState(bool indicesSwapped, bool left, bool right)
2233 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2234 mImpl->mFlipLeftSelectionHandleDirection = left;
2235 mImpl->mFlipRightSelectionHandleDirection = right;
2238 void Decorator::AddHighlight(unsigned int index, const Vector4& quad)
2240 *(mImpl->mHighlightQuadList.Begin() + index) = quad;
2243 void Decorator::SetHighLightBox(const Vector2& position, const Size& size, float outlineOffset)
2245 mImpl->mHighlightPosition = position;
2246 mImpl->mHighlightSize = size;
2247 mImpl->mHighlightOutlineOffset = outlineOffset;
2250 void Decorator::ClearHighlights()
2252 mImpl->mHighlightQuadList.Clear();
2253 mImpl->mHighlightPosition = Vector2::ZERO;
2254 mImpl->mHighlightOutlineOffset = 0.f;
2257 void Decorator::ResizeHighlightQuads(unsigned int numberOfQuads)
2259 mImpl->mHighlightQuadList.Resize(numberOfQuads);
2262 void Decorator::SetHighlightColor(const Vector4& color)
2264 mImpl->mHighlightColor = color;
2267 const Vector4& Decorator::GetHighlightColor() const
2269 return mImpl->mHighlightColor;
2272 void Decorator::SetHighlightActive(bool active)
2274 mImpl->mIsHighlightBoxActive = active;
2277 bool Decorator::IsHighlightActive() const
2279 return mImpl->mIsHighlightBoxActive;
2282 bool Decorator::IsHighlightVisible() const
2284 return (mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent());
2287 void Decorator::SetTextDepth(int textDepth)
2289 mImpl->mTextDepth = textDepth;
2292 void Decorator::SetPopupActive(bool active)
2294 mImpl->mActiveCopyPastePopup = active;
2297 bool Decorator::IsPopupActive() const
2299 return mImpl->mActiveCopyPastePopup;
2302 void Decorator::SetEnabledPopupButtons(TextSelectionPopup::Buttons& enabledButtonsBitMask)
2304 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2306 if(!mImpl->mCopyPastePopup.actor)
2308 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New(&mImpl->mTextSelectionPopupCallbackInterface);
2309 #ifdef DECORATOR_DEBUG
2310 mImpl->mCopyPastePopup.actor.SetProperty(Dali::Actor::Property::NAME, "mCopyPastePopup");
2312 mImpl->mCopyPastePopup.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
2313 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect(mImpl, &Decorator::Impl::SetPopupPosition); // Position popup after size negotiation
2316 mImpl->mCopyPastePopup.actor.EnableButtons(mImpl->mEnabledPopupButtons);
2319 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2321 return mImpl->mEnabledPopupButtons;
2326 void Decorator::SetScrollThreshold(float threshold)
2328 mImpl->SetScrollThreshold(threshold);
2331 float Decorator::GetScrollThreshold() const
2333 return mImpl->GetScrollThreshold();
2336 void Decorator::SetScrollSpeed(float speed)
2338 mImpl->SetScrollSpeed(speed);
2341 float Decorator::GetScrollSpeed() const
2343 return mImpl->GetScrollSpeed();
2346 void Decorator::NotifyEndOfScroll()
2348 mImpl->NotifyEndOfScroll();
2351 void Decorator::SetHorizontalScrollEnabled(bool enable)
2353 mImpl->mHorizontalScrollingEnabled = enable;
2356 bool Decorator::IsHorizontalScrollEnabled() const
2358 return mImpl->mHorizontalScrollingEnabled;
2361 void Decorator::SetVerticalScrollEnabled(bool enable)
2363 mImpl->mVerticalScrollingEnabled = enable;
2366 bool Decorator::IsVerticalScrollEnabled() const
2368 return mImpl->mVerticalScrollingEnabled;
2371 void Decorator::SetSmoothHandlePanEnabled(bool enable)
2373 mImpl->mSmoothHandlePanEnabled = enable;
2376 bool Decorator::IsSmoothHandlePanEnabled() const
2378 return mImpl->mSmoothHandlePanEnabled;
2381 Decorator::~Decorator()
2386 Decorator::Decorator(ControllerInterface& controller,
2387 TextSelectionPopupCallbackInterface& callbackInterface)
2390 mImpl = new Decorator::Impl(controller, callbackInterface);
2395 } // namespace Toolkit