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 CreateSelectionPopup()
951 if(!mCopyPastePopup.actor)
953 mCopyPastePopup.actor = TextSelectionPopup::New(&mTextSelectionPopupCallbackInterface);
954 #ifdef DECORATOR_DEBUG
955 mCopyPastePopup.actor.SetProperty(Dali::Actor::Property::NAME, "mCopyPastePopup");
957 mCopyPastePopup.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
958 mCopyPastePopup.actor.OnRelayoutSignal().Connect(this, &Decorator::Impl::SetPopupPosition); // Position popup after size negotiation
962 void CalculateHandleWorldCoordinates(HandleImpl& handle, Vector2& position)
964 // Gets the world position of the active layer. The active layer is where the handles are added.
965 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION);
967 // The grab handle position in world coords.
968 // The active layer's world position is the center of the active layer. The origin of the
969 // coord system of the handles is the top left of the active layer.
970 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + (mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f);
971 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + (mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f);
974 void SetGrabHandlePosition()
976 // Reference to the grab handle.
977 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
979 // Transforms the handle position into world coordinates.
980 // @note This is not the same value as grabHandle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
981 // as it's transforming the handle's position set by the text-controller and not
982 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
983 // retrieves the position of the center of the actor but the handle's position set
984 // by the text controller is not the center of the actor.
985 Vector2 grabHandleWorldPosition;
986 CalculateHandleWorldCoordinates(grabHandle, grabHandleWorldPosition);
988 // Check if the grab handle exceeds the boundaries of the decoration box.
989 // At the moment only the height is checked for the grab handle.
991 grabHandle.verticallyFlipped = (grabHandle.verticallyFlippedPreferred &&
992 ((grabHandleWorldPosition.y - grabHandle.size.height) > mBoundingBox.y)) ||
993 (grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w);
995 // The grab handle 'y' position in local coords.
996 // If the grab handle exceeds the bottom of the decoration box,
997 // set the 'y' position to the top of the line.
998 // The SetGrabHandleImage() method will change the orientation.
999 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
1001 ApplyDisplacement(grabHandle, yLocalPosition);
1004 void SetSelectionHandlePosition(HandleType type)
1006 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
1008 // Reference to the selection handle.
1009 HandleImpl& handle = mHandle[type];
1011 // Transforms the handle position into world coordinates.
1012 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1013 // as it's transforming the handle's position set by the text-controller and not
1014 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1015 // retrieves the position of the center of the actor but the handle's position set
1016 // by the text controller is not the center of the actor.
1017 Vector2 handleWorldPosition;
1018 CalculateHandleWorldCoordinates(handle, handleWorldPosition);
1020 // Whether to flip the handle (horizontally).
1021 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1023 // Whether to flip the handles if they are crossed.
1024 bool crossFlip = false;
1025 if(mFlipSelectionHandlesOnCross || !mIsHandlePanning)
1027 crossFlip = mIsHandleCurrentlyCrossed;
1030 // Whether the handle was crossed before start the panning.
1031 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1033 // Does not flip if both conditions are true (double flip)
1034 flipHandle = flipHandle != (crossFlip || isHandlePreviouslyCrossed);
1036 // Will flip the handles vertically if the user prefers it.
1037 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1039 if(crossFlip || isHandlePreviouslyCrossed)
1043 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1047 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1051 // Check if the selection handle exceeds the boundaries of the decoration box.
1052 const bool exceedsLeftEdge = (isPrimaryHandle ? !flipHandle : flipHandle) && (handleWorldPosition.x - handle.size.width < mBoundingBox.x);
1053 const bool exceedsRightEdge = (isPrimaryHandle ? flipHandle : !flipHandle) && (handleWorldPosition.x + handle.size.width > mBoundingBox.z);
1055 // Does not flip if both conditions are true (double flip)
1056 flipHandle = flipHandle != (exceedsLeftEdge || exceedsRightEdge);
1060 if(handle.actor && !handle.horizontallyFlipped)
1062 // Change the anchor point to flip the image.
1063 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT);
1065 handle.horizontallyFlipped = true;
1070 if(handle.actor && handle.horizontallyFlipped)
1072 // Reset the anchor point.
1073 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT);
1075 handle.horizontallyFlipped = false;
1079 // Whether to flip the handle vertically.
1080 handle.verticallyFlipped = (verticallyFlippedPreferred &&
1081 ((handleWorldPosition.y - handle.size.height) > mBoundingBox.y)) ||
1082 (handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w);
1084 // The primary selection handle 'y' position in local coords.
1085 // If the handle exceeds the bottom of the decoration box,
1086 // set the 'y' position to the top of the line.
1087 // The SetHandleImage() method will change the orientation.
1088 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1090 ApplyDisplacement(handle, yLocalPosition);
1093 void ApplyDisplacement(HandleImpl& handle, float yLocalPosition)
1097 float adjustedDisplacementX = 0.0f;
1098 float adjustedDisplacementY = 0.0f;
1099 if (mSmoothHandlePanEnabled)
1101 adjustedDisplacementX = CalculateAdjustedDisplacement(handle.position.x, handle.grabDisplacementX, mControlSize.x);
1102 adjustedDisplacementY = CalculateAdjustedDisplacement(handle.position.y, handle.grabDisplacementY, (mControlSize.y - handle.lineHeight));
1104 handle.actor.SetProperty(Actor::Property::POSITION,
1105 Vector2(handle.position.x + floor(0.5f * mCursorWidth) + adjustedDisplacementX,
1106 yLocalPosition + adjustedDisplacementY));
1110 float CalculateAdjustedDisplacement(float position, float displacement, float edge)
1112 //Apply the displacement (on the X-axis & the Y-axis)
1113 //as long as it does not exceed the control's edge.
1114 float adjustedDisplacement = 0.0f;
1115 if(position + displacement < 0.0f)
1117 // -position to cancel it out and relocate to 0.
1118 adjustedDisplacement = -position;
1120 else if(position + displacement > edge)
1122 // move in a displacement which is sufficient to reach the edge.
1123 adjustedDisplacement = edge - position;
1127 // move normally in the displacement.
1128 adjustedDisplacement = displacement;
1130 return adjustedDisplacement;
1133 void SetHandleImage(HandleType type)
1135 HandleImpl& handle = mHandle[type];
1137 HandleType markerType = HANDLE_TYPE_COUNT;
1138 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1139 if(LEFT_SELECTION_HANDLE == type)
1141 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1142 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1144 else if(RIGHT_SELECTION_HANDLE == type)
1146 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1147 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1150 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1153 const HandleImageType imageType = (handle.pressed ? (mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1155 handle.actor.SetImage(mHandleImages[type][imageType]);
1158 if(HANDLE_TYPE_COUNT != markerType)
1160 if(handle.markerActor)
1162 const HandleImageType markerImageType = (handle.pressed ? (mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1163 handle.markerActor.SetImage(mHandleImages[markerType][markerImageType]);
1167 // Whether to flip the handle vertically.
1170 handle.actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS));
1174 void CreateHighlight()
1176 if(!mHighlightActor)
1178 mHighlightActor = Actor::New();
1180 mHighlightActor.SetProperty(Dali::Actor::Property::NAME, "HighlightActor");
1181 mHighlightActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1182 mHighlightActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1183 mHighlightActor.SetProperty(Actor::Property::COLOR, mHighlightColor);
1184 mHighlightActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
1187 // Add the highlight box telling the controller it needs clipping.
1188 mController.AddDecoration(mHighlightActor, DecorationType::NONE_LAYER, true);
1191 void UpdateHighlight()
1195 // Sets the position of the highlight actor inside the decorator.
1196 mHighlightActor.SetProperty(Actor::Property::POSITION, Vector2(mHighlightPosition.x + mHighlightOutlineOffset, mHighlightPosition.y + mHighlightOutlineOffset));
1198 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1199 if(0u != numberOfQuads)
1201 // Set the size of the highlighted text to the actor.
1202 mHighlightActor.SetProperty(Actor::Property::SIZE, mHighlightSize);
1204 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1205 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1206 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1208 Vector<Vector2> vertices;
1209 Vector<unsigned short> indices;
1211 vertices.Reserve(4u * numberOfQuads);
1212 indices.Reserve(6u * numberOfQuads);
1214 // Index to the vertex.
1215 unsigned int v = 0u;
1217 // Traverse all quads.
1218 for(Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1219 endIt = mHighlightQuadList.End();
1223 const Vector4& quad = *it;
1228 vertex.x = quad.x - offsetX;
1229 vertex.y = quad.y - offsetY;
1230 vertices.PushBack(vertex);
1233 vertex.x = quad.z - offsetX;
1234 vertex.y = quad.y - offsetY;
1235 vertices.PushBack(vertex);
1237 // bottom-left (v+2)
1238 vertex.x = quad.x - offsetX;
1239 vertex.y = quad.w - offsetY;
1240 vertices.PushBack(vertex);
1242 // bottom-right (v+3)
1243 vertex.x = quad.z - offsetX;
1244 vertex.y = quad.w - offsetY;
1245 vertices.PushBack(vertex);
1247 // triangle A (3, 1, 0)
1248 indices.PushBack(v + 3);
1249 indices.PushBack(v + 1);
1250 indices.PushBack(v);
1252 // triangle B (0, 2, 3)
1253 indices.PushBack(v);
1254 indices.PushBack(v + 2);
1255 indices.PushBack(v + 3);
1260 mQuadVertices = VertexBuffer::New(mQuadVertexFormat);
1263 mQuadVertices.SetData(&vertices[0], vertices.Size());
1267 mQuadGeometry = Geometry::New();
1268 mQuadGeometry.AddVertexBuffer(mQuadVertices);
1270 mQuadGeometry.SetIndexBuffer(&indices[0], indices.Size());
1272 if(!mHighlightRenderer)
1274 mHighlightRenderer = Dali::Renderer::New(mQuadGeometry, mHighlightShader);
1275 mHighlightActor.AddRenderer(mHighlightRenderer);
1279 mHighlightQuadList.Clear();
1281 if(mHighlightRenderer)
1283 mHighlightRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mTextDepth - 2); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1288 void DoPan(HandleImpl& handle, HandleType type, const PanGesture& gesture)
1290 GestureState state = gesture.GetState();
1291 if(GestureState::STARTED == state)
1293 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1295 handle.globalPosition.x = handle.position.x;
1296 handle.globalPosition.y = handle.position.y;
1299 const Vector2& displacement = gesture.GetDisplacement();
1300 handle.grabDisplacementX += displacement.x;
1301 handle.grabDisplacementY += (handle.verticallyFlipped ? -displacement.y : displacement.y);
1303 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1304 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1305 const float yVerticallyFlippedCorrected = y - (handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f);
1307 if((GestureState::STARTED == state) ||
1308 (GestureState::CONTINUING == state))
1311 mController.GetTargetSize(targetSize);
1313 if(mHorizontalScrollingEnabled &&
1314 (x < mScrollThreshold))
1316 mScrollDirection = SCROLL_RIGHT;
1317 mHandleScrolling = type;
1320 else if(mHorizontalScrollingEnabled &&
1321 (x > targetSize.width - mScrollThreshold))
1323 mScrollDirection = SCROLL_LEFT;
1324 mHandleScrolling = type;
1327 else if(mVerticalScrollingEnabled &&
1328 (yVerticallyFlippedCorrected < mScrollThreshold))
1330 mScrollDirection = SCROLL_TOP;
1331 mHandleScrolling = type;
1334 else if(mVerticalScrollingEnabled &&
1335 (yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold))
1337 mScrollDirection = SCROLL_BOTTOM;
1338 mHandleScrolling = type;
1343 mHandleScrolling = HANDLE_TYPE_COUNT;
1345 mController.DecorationEvent(type, HANDLE_PRESSED, x, y);
1348 mIsHandlePanning = true;
1350 else if((GestureState::FINISHED == state) ||
1351 (GestureState::CANCELLED == state))
1354 (mScrollTimer.IsRunning() || mNotifyEndOfScroll))
1356 mNotifyEndOfScroll = false;
1357 mHandleScrolling = HANDLE_TYPE_COUNT;
1359 mController.DecorationEvent(type, HANDLE_STOP_SCROLLING, x, y);
1363 mController.DecorationEvent(type, HANDLE_RELEASED, x, y);
1368 handle.actor.SetImage(mHandleImages[type][HANDLE_IMAGE_RELEASED]);
1370 handle.pressed = false;
1372 mIsHandlePanning = false;
1376 void OnPan(Actor actor, const PanGesture& gesture)
1378 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1379 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1380 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1382 if(actor == grabHandle.grabArea)
1384 DoPan(grabHandle, GRAB_HANDLE, gesture);
1386 else if(actor == primarySelectionHandle.grabArea)
1388 DoPan(primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture);
1390 else if(actor == secondarySelectionHandle.grabArea)
1392 DoPan(secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture);
1396 bool OnGrabHandleTouched(Actor actor, const TouchEvent& touch)
1398 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1400 // Switch between pressed/release grab-handle images
1401 if(touch.GetPointCount() > 0 &&
1404 const PointState::Type state = touch.GetState(0);
1406 if(PointState::DOWN == state)
1408 grabHandle.pressed = true;
1410 else if((PointState::UP == state) ||
1411 (PointState::INTERRUPTED == state))
1413 grabHandle.pressed = false;
1416 SetHandleImage(GRAB_HANDLE);
1422 bool OnHandleOneTouched(Actor actor, const TouchEvent& touch)
1424 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1426 // Switch between pressed/release selection handle images
1427 if(touch.GetPointCount() > 0 &&
1428 primarySelectionHandle.actor)
1430 const PointState::Type state = touch.GetState(0);
1432 if(PointState::DOWN == state)
1434 primarySelectionHandle.pressed = true;
1435 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1437 else if((PointState::UP == state) ||
1438 (PointState::INTERRUPTED == state))
1440 primarySelectionHandle.pressed = false;
1441 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1442 mIsHandlePanning = false;
1443 mHandleReleased = LEFT_SELECTION_HANDLE;
1446 SetHandleImage(LEFT_SELECTION_HANDLE);
1452 bool OnHandleTwoTouched(Actor actor, const TouchEvent& touch)
1454 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1456 // Switch between pressed/release selection handle images
1457 if(touch.GetPointCount() > 0 &&
1458 secondarySelectionHandle.actor)
1460 const PointState::Type state = touch.GetState(0);
1462 if(PointState::DOWN == state)
1464 secondarySelectionHandle.pressed = true;
1465 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1467 else if((PointState::UP == state) ||
1468 (PointState::INTERRUPTED == state))
1470 secondarySelectionHandle.pressed = false;
1471 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1472 mIsHandlePanning = false;
1473 mHandleReleased = RIGHT_SELECTION_HANDLE;
1476 SetHandleImage(RIGHT_SELECTION_HANDLE);
1482 void HandleResetPosition(PropertyNotification& source)
1484 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1486 if(grabHandle.active)
1488 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1489 SetGrabHandlePosition();
1491 // Sets the grab handle image according if it's pressed, flipped, etc.
1492 SetHandleImage(GRAB_HANDLE);
1496 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1497 SetSelectionHandlePosition(LEFT_SELECTION_HANDLE);
1499 // Sets the primary handle image according if it's pressed, flipped, etc.
1500 SetHandleImage(LEFT_SELECTION_HANDLE);
1502 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1503 SetSelectionHandlePosition(RIGHT_SELECTION_HANDLE);
1505 // Sets the secondary handle image according if it's pressed, flipped, etc.
1506 SetHandleImage(RIGHT_SELECTION_HANDLE);
1510 void SetupActiveLayerPropertyNotifications()
1517 // Vertical notifications.
1519 // Disconnect any previous connected callback.
1520 if(mHandleVerticalLessThanNotification)
1522 mHandleVerticalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1523 mActiveLayer.RemovePropertyNotification(mHandleVerticalLessThanNotification);
1526 if(mHandleVerticalGreaterThanNotification)
1528 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1529 mActiveLayer.RemovePropertyNotification(mHandleVerticalGreaterThanNotification);
1532 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1533 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1534 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1536 if(grabHandle.active)
1538 if(grabHandle.verticallyFlipped)
1540 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1541 mHandleVerticalGreaterThanNotification.Reset();
1543 // The vertical distance from the center of the active layer to the top edje of the display.
1544 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1546 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1547 LessThanCondition(mBoundingBox.y + topHeight));
1549 // Notifies the change from false to true and from true to false.
1550 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1552 // Connects the signals with the callbacks.
1553 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1557 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1558 mHandleVerticalLessThanNotification.Reset();
1560 // The vertical distance from the center of the active layer to the bottom edje of the display.
1561 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1563 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1564 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1566 // Notifies the change from false to true and from true to false.
1567 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1569 // Connects the signals with the callbacks.
1570 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1573 else // The selection handles are active
1575 if(primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped)
1577 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1578 mHandleVerticalGreaterThanNotification.Reset();
1580 // The vertical distance from the center of the active layer to the top edje of the display.
1581 const float topHeight = 0.5f * mControlSize.height + std::max(-primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height);
1583 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1584 LessThanCondition(mBoundingBox.y + topHeight));
1586 // Notifies the change from false to true and from true to false.
1587 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1589 // Connects the signals with the callbacks.
1590 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1592 else if(!primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped)
1594 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1595 mHandleVerticalLessThanNotification.Reset();
1597 // The vertical distance from the center of the active layer to the bottom edje of the display.
1598 const float bottomHeight = -0.5f * mControlSize.height + std::max(primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1599 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height);
1601 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1602 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1604 // Notifies the change from false to true and from true to false.
1605 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1607 // Connects the signals with the callbacks.
1608 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1612 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1614 // The vertical distance from the center of the active layer to the top edje of the display.
1615 const float topHeight = 0.5f * mControlSize.height + (primaryHandle.verticallyFlipped ? -primaryHandle.position.y + primaryHandle.size.height : -secondaryHandle.position.y + secondaryHandle.size.height);
1617 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1618 LessThanCondition(mBoundingBox.y + topHeight));
1620 // Notifies the change from false to true and from true to false.
1621 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1623 // Connects the signals with the callbacks.
1624 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1626 // The vertical distance from the center of the active layer to the bottom edje of the display.
1627 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);
1629 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1630 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1632 // Notifies the change from false to true and from true to false.
1633 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1635 // Connects the signals with the callbacks.
1636 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1640 // Horizontal notifications.
1642 // Disconnect any previous connected callback.
1643 if(mHandleHorizontalLessThanNotification)
1645 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1646 mActiveLayer.RemovePropertyNotification(mHandleHorizontalLessThanNotification);
1649 if(mHandleHorizontalGreaterThanNotification)
1651 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1652 mActiveLayer.RemovePropertyNotification(mHandleHorizontalGreaterThanNotification);
1655 if(primaryHandle.active || secondaryHandle.active)
1657 // The horizontal distance from the center of the active layer to the left edje of the display.
1658 const float leftWidth = 0.5f * mControlSize.width + std::max(-primaryHandle.position.x + primaryHandle.size.width,
1659 -secondaryHandle.position.x + secondaryHandle.size.width);
1661 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1662 LessThanCondition(mBoundingBox.x + leftWidth));
1664 // Notifies the change from false to true and from true to false.
1665 mHandleHorizontalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1667 // Connects the signals with the callbacks.
1668 mHandleHorizontalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1670 // The horizontal distance from the center of the active layer to the right edje of the display.
1671 const float rightWidth = -0.5f * mControlSize.width + std::max(primaryHandle.position.x + primaryHandle.size.width,
1672 secondaryHandle.position.x + secondaryHandle.size.width);
1674 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1675 GreaterThanCondition(mBoundingBox.z - rightWidth));
1677 // Notifies the change from false to true and from true to false.
1678 mHandleHorizontalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1680 // Connects the signals with the callbacks.
1681 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1687 float AlternatePopUpPositionRelativeToCursor(bool topBottom)
1689 float alternativePosition = 0.0f;
1691 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1693 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1694 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1695 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1696 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1698 if(primaryHandle.active || secondaryHandle.active)
1700 float handleY = 0.f;
1701 float maxHandleHeight = 0.f;
1703 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1704 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1706 if(primaryVisible && secondaryVisible)
1708 handleY = std::max(primaryHandle.position.y, secondaryHandle.position.y);
1709 maxHandleHeight = std::max(primaryHandle.size.height, secondaryHandle.size.height);
1711 else if(primaryVisible && !secondaryVisible)
1713 handleY = primaryHandle.position.y;
1714 maxHandleHeight = primaryHandle.size.height;
1716 else if(!primaryVisible && secondaryVisible)
1718 handleY = secondaryHandle.position.y;
1719 maxHandleHeight = secondaryHandle.size.height;
1722 alternativePosition = handleY + (topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight);
1726 alternativePosition = cursor.position.y + (topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height);
1729 return alternativePosition;
1732 void PopUpLeavesTopBoundary(PropertyNotification& source)
1734 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1736 // Sets the position of the popup below.
1737 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, true)));
1740 void PopUpLeavesBottomBoundary(PropertyNotification& source)
1742 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1744 // Sets the position of the popup above.
1745 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, false)));
1748 void SetUpPopupPositionNotifications(const Vector3& popupHalfSize)
1750 // Disconnect any previous connected callback.
1751 if(mPopupTopExceedNotification)
1753 mPopupTopExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1754 mCopyPastePopup.actor.RemovePropertyNotification(mPopupTopExceedNotification);
1757 if(mPopupBottomExceedNotification)
1759 mPopupBottomExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1760 mCopyPastePopup.actor.RemovePropertyNotification(mPopupBottomExceedNotification);
1763 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1765 // Exceeding vertical boundary
1767 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1768 LessThanCondition(mBoundingBox.y + popupHalfSize.height));
1770 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1771 GreaterThanCondition(mBoundingBox.w - popupHalfSize.height));
1773 // Notifies the change from false to true and from true to false.
1774 mPopupTopExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1775 mPopupBottomExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1777 mPopupTopExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1778 mPopupBottomExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1781 void SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
1783 ImageDimensions dimensions = Dali::GetOriginalImageSize(imageFileName);
1785 HandleImpl& handle = mHandle[handleType];
1786 handle.size = Size(dimensions.GetWidth(), dimensions.GetHeight());
1788 mHandleImages[handleType][handleImageType] = imageFileName;
1791 void SetScrollThreshold(float threshold)
1793 mScrollThreshold = threshold;
1796 float GetScrollThreshold() const
1798 return mScrollThreshold;
1801 void SetScrollSpeed(float speed)
1803 mScrollSpeed = speed;
1804 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1807 float GetScrollSpeed() const
1809 return mScrollSpeed;
1812 void NotifyEndOfScroll()
1818 mNotifyEndOfScroll = true;
1823 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1825 * It only starts the timer if it's already created.
1827 void StartScrollTimer()
1831 mScrollTimer = Timer::New(SCROLL_TICK_INTERVAL);
1832 mScrollTimer.TickSignal().Connect(this, &Decorator::Impl::OnScrollTimerTick);
1835 if(!mScrollTimer.IsRunning())
1837 mScrollTimer.Start();
1842 * Stops the timer used to scroll the text.
1844 void StopScrollTimer()
1848 mScrollTimer.Stop();
1853 * Callback called by the timer used to scroll the text.
1855 * It calculates and sets a new scroll position.
1857 bool OnScrollTimerTick()
1859 if(HANDLE_TYPE_COUNT != mHandleScrolling)
1864 switch(mScrollDirection)
1868 x = mScrollDistance;
1873 x = -mScrollDistance;
1878 y = mScrollDistance;
1883 y = -mScrollDistance;
1890 mController.DecorationEvent(mHandleScrolling,
1899 ControllerInterface& mController;
1901 TapGestureDetector mTapDetector;
1902 PanGestureDetector mPanDetector;
1903 LongPressGestureDetector mLongPressDetector;
1905 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1906 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1908 Actor mActiveLayer; ///< Actor for active handles and alike that ensures they are above all else.
1909 Actor mCursorLayer; ///< Actor for cursor layer. this is for cursor clipping.
1910 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1911 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1912 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1913 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1914 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1915 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1916 Control mPrimaryCursor;
1917 Control mSecondaryCursor;
1919 Actor mHighlightActor; ///< Actor to display highlight
1920 Renderer mHighlightRenderer;
1921 Shader mHighlightShader; ///< Shader used for highlight
1922 Property::Map mQuadVertexFormat;
1923 PopupImpl mCopyPastePopup;
1924 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1925 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1927 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1928 Vector4 mHandleColor;
1930 CursorImpl mCursor[CURSOR_COUNT];
1931 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1933 VertexBuffer mQuadVertices;
1934 Geometry mQuadGeometry;
1935 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1937 Vector4 mBoundingBox; ///< The bounding box in world coords.
1938 Vector4 mHighlightColor; ///< Color of the highlight
1939 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1940 Size mHighlightSize; ///< The size of the highlighted text.
1941 Size mControlSize; ///< The control's size. Set by the Relayout.
1942 float mHighlightOutlineOffset; ///< The outline's offset.
1944 unsigned int mActiveCursor;
1945 unsigned int mCursorBlinkInterval;
1946 float mCursorBlinkDuration;
1947 float mCursorWidth; ///< The width of the cursors in pixels.
1948 HandleType mHandleScrolling; ///< The handle which is scrolling.
1949 HandleType mHandleReleased; ///< The last handle released.
1950 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1951 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1952 float mScrollSpeed; ///< The scroll speed in pixels per second.
1953 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1954 int mTextDepth; ///< The depth used to render the text.
1956 bool mActiveCopyPastePopup : 1;
1957 bool mPopupSetNewPosition : 1;
1958 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1959 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1960 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1961 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1962 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1963 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1964 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1965 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1966 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1967 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1968 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1969 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1970 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1971 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1972 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1973 bool mHidePrimaryCursorAndGrabHandle : 1; ///< Whether the primary cursor and grab are hidden always.
1976 DecoratorPtr Decorator::New(ControllerInterface& controller,
1977 TextSelectionPopupCallbackInterface& callbackInterface)
1979 return DecoratorPtr(new Decorator(controller,
1980 callbackInterface));
1983 void Decorator::SetBoundingBox(const Rect<int>& boundingBox)
1985 LocalToWorldCoordinatesBoundingBox(boundingBox, mImpl->mBoundingBox);
1988 void Decorator::GetBoundingBox(Rect<int>& boundingBox) const
1990 WorldToLocalCoordinatesBoundingBox(mImpl->mBoundingBox, boundingBox);
1993 void Decorator::Relayout(const Vector2& size)
1995 mImpl->Relayout(size);
1998 void Decorator::UpdatePositions(const Vector2& scrollOffset)
2000 mImpl->UpdatePositions(scrollOffset);
2005 void Decorator::SetActiveCursor(ActiveCursor activeCursor)
2007 mImpl->mActiveCursor = activeCursor;
2010 unsigned int Decorator::GetActiveCursor() const
2012 return mImpl->mActiveCursor;
2015 void Decorator::SetPosition(Cursor cursor, float x, float y, float cursorHeight, float lineHeight)
2017 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2019 cursorImpl.position.x = x;
2020 cursorImpl.position.y = y;
2021 cursorImpl.cursorHeight = cursorHeight;
2022 cursorImpl.lineHeight = lineHeight;
2025 void Decorator::GetPosition(Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight) const
2027 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2029 x = cursorImpl.position.x;
2030 y = cursorImpl.position.y;
2031 cursorHeight = cursorImpl.cursorHeight;
2032 lineHeight = cursorImpl.lineHeight;
2035 const Vector2& Decorator::GetPosition(Cursor cursor) const
2037 return mImpl->mCursor[cursor].position;
2040 void Decorator::SetGlyphOffset(Cursor cursor, float glyphOffset)
2042 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2044 cursorImpl.glyphOffset = glyphOffset;
2047 const float Decorator::GetGlyphOffset(Cursor cursor) const
2049 return mImpl->mCursor[cursor].glyphOffset;
2052 void Decorator::SetCursorColor(Cursor cursor, const Dali::Vector4& color)
2054 mImpl->mCursor[cursor].color = color;
2057 const Dali::Vector4& Decorator::GetColor(Cursor cursor) const
2059 return mImpl->mCursor[cursor].color;
2062 void Decorator::StartCursorBlink()
2064 if(!mImpl->mCursorBlinkTimer)
2066 mImpl->mCursorBlinkTimer = Timer::New(mImpl->mCursorBlinkInterval);
2067 mImpl->mCursorBlinkTimer.TickSignal().Connect(mImpl, &Decorator::Impl::OnCursorBlinkTimerTick);
2070 if(!mImpl->mCursorBlinkTimer.IsRunning())
2072 mImpl->mCursorBlinkTimer.Start();
2076 void Decorator::StopCursorBlink()
2078 if(mImpl->mCursorBlinkTimer)
2080 mImpl->mCursorBlinkTimer.Stop();
2083 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2086 void Decorator::DelayCursorBlink()
2088 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2089 mImpl->mDelayCursorBlink = true;
2092 void Decorator::SetCursorBlinkInterval(float seconds)
2094 mImpl->mCursorBlinkInterval = static_cast<unsigned int>(seconds * TO_MILLISECONDS); // Convert to milliseconds
2097 float Decorator::GetCursorBlinkInterval() const
2099 return static_cast<float>(mImpl->mCursorBlinkInterval) * TO_SECONDS;
2102 void Decorator::SetCursorBlinkDuration(float seconds)
2104 mImpl->mCursorBlinkDuration = seconds;
2107 float Decorator::GetCursorBlinkDuration() const
2109 return mImpl->mCursorBlinkDuration;
2112 void Decorator::SetCursorWidth(int width)
2114 mImpl->mCursorWidth = static_cast<float>(width);
2117 int Decorator::GetCursorWidth() const
2119 return static_cast<int>(mImpl->mCursorWidth);
2122 void Decorator::SetEditable(bool editable)
2124 mImpl->mHidePrimaryCursorAndGrabHandle = !editable;
2125 // If editable is false, all decorators should be disabled.
2128 if(IsHighlightActive())
2130 SetHighlightActive(false);
2132 if(IsHandleActive(LEFT_SELECTION_HANDLE))
2134 SetHandleActive(LEFT_SELECTION_HANDLE, false);
2136 if(IsHandleActive(RIGHT_SELECTION_HANDLE))
2138 SetHandleActive(RIGHT_SELECTION_HANDLE, false);
2142 SetPopupActive(false);
2146 mImpl->Relayout(mImpl->mControlSize);
2150 void Decorator::SetHandleActive(HandleType handleType, bool active)
2152 mImpl->mHandle[handleType].active = active;
2156 if((LEFT_SELECTION_HANDLE == handleType) || (RIGHT_SELECTION_HANDLE == handleType))
2158 mImpl->mIsHandlePreviouslyCrossed = false;
2161 // TODO: this is a work-around.
2162 // The problem is the handle actor does not receive the touch event with the Interrupt
2163 // state when the power button is pressed and the application goes to background.
2164 mImpl->mHandle[handleType].pressed = false;
2165 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2166 ImageView imageView = mImpl->mHandle[handleType].actor;
2167 if(imageReleased && imageView)
2169 imageView.SetImage(mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED]);
2174 bool Decorator::IsHandleActive(HandleType handleType) const
2176 return mImpl->mHandle[handleType].active;
2179 void Decorator::SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
2181 mImpl->SetHandleImage(handleType, handleImageType, imageFileName);
2184 const std::string& Decorator::GetHandleImage(HandleType handleType, HandleImageType handleImageType) const
2186 return mImpl->mHandleImages[handleType][handleImageType];
2189 void Decorator::SetHandleColor(const Vector4& color)
2191 mImpl->mHandleColor = color;
2194 const Vector4& Decorator::GetHandleColor() const
2196 return mImpl->mHandleColor;
2199 void Decorator::SetPosition(HandleType handleType, float x, float y, float height)
2201 // Adjust handle's displacement
2202 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2204 handle.position.x = x;
2205 handle.position.y = y;
2206 handle.lineHeight = height;
2208 if(mImpl->mSmoothHandlePanEnabled)
2210 handle.grabDisplacementX = 0.f;
2211 handle.grabDisplacementY = 0.f;
2215 void Decorator::GetPosition(HandleType handleType, float& x, float& y, float& height) const
2217 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2219 x = handle.position.x;
2220 y = handle.position.y;
2221 height = handle.lineHeight;
2224 const Vector2& Decorator::GetPosition(HandleType handleType) const
2226 return mImpl->mHandle[handleType].position;
2229 void Decorator::FlipHandleVertically(HandleType handleType, bool flip)
2231 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2234 bool Decorator::IsHandleVerticallyFlipped(HandleType handleType) const
2236 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2239 void Decorator::FlipSelectionHandlesOnCrossEnabled(bool enable)
2241 mImpl->mFlipSelectionHandlesOnCross = enable;
2244 void Decorator::SetSelectionHandleFlipState(bool indicesSwapped, bool left, bool right)
2246 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2247 mImpl->mFlipLeftSelectionHandleDirection = left;
2248 mImpl->mFlipRightSelectionHandleDirection = right;
2251 void Decorator::AddHighlight(unsigned int index, const Vector4& quad)
2253 *(mImpl->mHighlightQuadList.Begin() + index) = quad;
2256 void Decorator::SetHighLightBox(const Vector2& position, const Size& size, float outlineOffset)
2258 mImpl->mHighlightPosition = position;
2259 mImpl->mHighlightSize = size;
2260 mImpl->mHighlightOutlineOffset = outlineOffset;
2263 void Decorator::ClearHighlights()
2265 mImpl->mHighlightQuadList.Clear();
2266 mImpl->mHighlightPosition = Vector2::ZERO;
2267 mImpl->mHighlightOutlineOffset = 0.f;
2270 void Decorator::ResizeHighlightQuads(unsigned int numberOfQuads)
2272 mImpl->mHighlightQuadList.Resize(numberOfQuads);
2275 void Decorator::SetHighlightColor(const Vector4& color)
2277 mImpl->mHighlightColor = color;
2280 const Vector4& Decorator::GetHighlightColor() const
2282 return mImpl->mHighlightColor;
2285 void Decorator::SetHighlightActive(bool active)
2287 mImpl->mIsHighlightBoxActive = active;
2290 bool Decorator::IsHighlightActive() const
2292 return mImpl->mIsHighlightBoxActive;
2295 bool Decorator::IsHighlightVisible() const
2297 return (mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent());
2300 void Decorator::SetTextDepth(int textDepth)
2302 mImpl->mTextDepth = textDepth;
2305 void Decorator::SetPopupActive(bool active)
2307 mImpl->mActiveCopyPastePopup = active;
2310 bool Decorator::IsPopupActive() const
2312 return mImpl->mActiveCopyPastePopup;
2315 void Decorator::SetEnabledPopupButtons(TextSelectionPopup::Buttons& enabledButtonsBitMask)
2317 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2318 mImpl->CreateSelectionPopup();
2319 mImpl->mCopyPastePopup.actor.EnableButtons(mImpl->mEnabledPopupButtons);
2322 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2324 return mImpl->mEnabledPopupButtons;
2327 void Decorator::SetSelectionPopupStyle(const Property::Map& options)
2329 mImpl->CreateSelectionPopup();
2330 mImpl->mCopyPastePopup.actor.SetProperties(options);
2333 void Decorator::GetSelectionPopupStyle(Property::Map& options)
2335 if(mImpl->mCopyPastePopup.actor)
2337 mImpl->mCopyPastePopup.actor.GetProperties(options);
2343 void Decorator::SetScrollThreshold(float threshold)
2345 mImpl->SetScrollThreshold(threshold);
2348 float Decorator::GetScrollThreshold() const
2350 return mImpl->GetScrollThreshold();
2353 void Decorator::SetScrollSpeed(float speed)
2355 mImpl->SetScrollSpeed(speed);
2358 float Decorator::GetScrollSpeed() const
2360 return mImpl->GetScrollSpeed();
2363 void Decorator::NotifyEndOfScroll()
2365 mImpl->NotifyEndOfScroll();
2368 void Decorator::SetHorizontalScrollEnabled(bool enable)
2370 mImpl->mHorizontalScrollingEnabled = enable;
2373 bool Decorator::IsHorizontalScrollEnabled() const
2375 return mImpl->mHorizontalScrollingEnabled;
2378 void Decorator::SetVerticalScrollEnabled(bool enable)
2380 mImpl->mVerticalScrollingEnabled = enable;
2383 bool Decorator::IsVerticalScrollEnabled() const
2385 return mImpl->mVerticalScrollingEnabled;
2388 void Decorator::SetSmoothHandlePanEnabled(bool enable)
2390 mImpl->mSmoothHandlePanEnabled = enable;
2393 bool Decorator::IsSmoothHandlePanEnabled() const
2395 return mImpl->mSmoothHandlePanEnabled;
2398 Decorator::~Decorator()
2403 Decorator::Decorator(ControllerInterface& controller,
2404 TextSelectionPopupCallbackInterface& callbackInterface)
2407 mImpl = new Decorator::Impl(controller, callbackInterface);
2412 } // namespace Toolkit