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");
750 bool needsClipping = false;
751 if(type == DecorationType::CURSOR_LAYER)
753 needsClipping = true;
754 layer.SetProperty(Actor::Property::NAME, "CursorLayerActor");
757 layer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
758 layer.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
760 mController.AddDecoration(layer, type, needsClipping);
766 void SetSelectionHandleMarkerSize(HandleImpl& handle)
768 if(handle.markerActor)
770 handle.markerActor.SetProperty(Actor::Property::SIZE, Vector2(0, handle.lineHeight));
774 void CreateGrabHandle()
776 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
777 if(!grabHandle.actor)
779 if(mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED].size())
781 grabHandle.actor = ImageView::New(mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED]);
782 GetImpl(grabHandle.actor).SetDepthIndex(DepthIndex::DECORATION);
783 grabHandle.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
785 // Area that Grab handle responds to, larger than actual handle so easier to move
786 #ifdef DECORATOR_DEBUG
787 grabHandle.actor.SetProperty(Dali::Actor::Property::NAME, "GrabHandleActor");
788 if(Dali::Internal::gLogFilter->IsEnabledFor(Debug::Verbose))
790 grabHandle.grabArea = Control::New();
791 Toolkit::Control control = Toolkit::Control::DownCast(grabHandle.grabArea);
792 control.SetBackgroundColor(Vector4(1.0f, 1.0f, 1.0f, 0.5f));
793 grabHandle.grabArea.SetProperty(Dali::Actor::Property::NAME, "GrabArea");
797 grabHandle.grabArea = Actor::New();
798 grabHandle.grabArea.SetProperty(Dali::Actor::Property::NAME, "GrabArea");
801 grabHandle.grabArea = Actor::New();
804 grabHandle.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
805 grabHandle.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
806 grabHandle.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
807 grabHandle.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_GRAB_HANDLE_RELATIVE_SIZE);
808 grabHandle.actor.Add(grabHandle.grabArea);
809 grabHandle.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
811 grabHandle.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnGrabHandleTouched);
813 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
814 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
815 mTapDetector.Attach(grabHandle.actor);
816 mLongPressDetector.Attach(grabHandle.actor);
818 // The grab handle's area is attached to the pan detector.
819 // The OnPan() method is connected to the signals emitted by the pan detector.
820 mPanDetector.Attach(grabHandle.grabArea);
822 mActiveLayer.Add(grabHandle.actor);
826 if(grabHandle.actor && !grabHandle.actor.GetParent())
828 mActiveLayer.Add(grabHandle.actor);
832 void CreateHandleMarker(HandleImpl& handle, const std::string& image, HandleType handleType)
836 handle.markerActor = ImageView::New(image);
837 handle.markerActor.SetProperty(Actor::Property::COLOR, mHandleColor);
838 handle.actor.Add(handle.markerActor);
840 handle.markerActor.SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
842 if(LEFT_SELECTION_HANDLE == handleType)
844 handle.markerActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT);
845 handle.markerActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_RIGHT);
847 else if(RIGHT_SELECTION_HANDLE == handleType)
849 handle.markerActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT);
850 handle.markerActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
855 void CreateSelectionHandles()
857 HandleImpl& primary = mHandle[LEFT_SELECTION_HANDLE];
860 if(mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size())
862 primary.actor = ImageView::New(mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]);
863 #ifdef DECORATOR_DEBUG
864 primary.actor.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleOne");
866 primary.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
867 GetImpl(primary.actor).SetDepthIndex(DepthIndex::DECORATION);
868 primary.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
870 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
871 #ifdef DECORATOR_DEBUG
872 primary.grabArea.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleOneGrabArea");
874 primary.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
875 primary.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
876 primary.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
877 primary.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE);
879 primary.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnHandleOneTouched);
881 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
882 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
883 mTapDetector.Attach(primary.actor);
884 mLongPressDetector.Attach(primary.actor);
886 // The handle's area is attached to the pan detector.
887 // The OnPan() method is connected to the signals emitted by the pan detector.
888 mPanDetector.Attach(primary.grabArea);
890 primary.actor.Add(primary.grabArea);
892 CreateHandleMarker(primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE);
896 if(primary.actor && !primary.actor.GetParent())
898 mActiveLayer.Add(primary.actor);
901 HandleImpl& secondary = mHandle[RIGHT_SELECTION_HANDLE];
904 if(mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size())
906 secondary.actor = ImageView::New(mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]);
907 #ifdef DECORATOR_DEBUG
908 secondary.actor.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleTwo");
910 secondary.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
911 GetImpl(secondary.actor).SetDepthIndex(DepthIndex::DECORATION);
912 secondary.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
914 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
915 #ifdef DECORATOR_DEBUG
916 secondary.grabArea.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleTwoGrabArea");
918 secondary.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
919 secondary.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
920 secondary.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
921 secondary.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE);
923 secondary.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnHandleTwoTouched);
925 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
926 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
927 mTapDetector.Attach(secondary.actor);
928 mLongPressDetector.Attach(secondary.actor);
930 // The handle's area is attached to the pan detector.
931 // The OnPan() method is connected to the signals emitted by the pan detector.
932 mPanDetector.Attach(secondary.grabArea);
934 secondary.actor.Add(secondary.grabArea);
936 CreateHandleMarker(secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE);
940 if(secondary.actor && !secondary.actor.GetParent())
942 mActiveLayer.Add(secondary.actor);
946 void CalculateHandleWorldCoordinates(HandleImpl& handle, Vector2& position)
948 // Gets the world position of the active layer. The active layer is where the handles are added.
949 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION);
951 // The grab handle position in world coords.
952 // The active layer's world position is the center of the active layer. The origin of the
953 // coord system of the handles is the top left of the active layer.
954 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + (mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f);
955 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + (mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f);
958 void SetGrabHandlePosition()
960 // Reference to the grab handle.
961 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
963 // Transforms the handle position into world coordinates.
964 // @note This is not the same value as grabHandle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
965 // as it's transforming the handle's position set by the text-controller and not
966 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
967 // retrieves the position of the center of the actor but the handle's position set
968 // by the text controller is not the center of the actor.
969 Vector2 grabHandleWorldPosition;
970 CalculateHandleWorldCoordinates(grabHandle, grabHandleWorldPosition);
972 // Check if the grab handle exceeds the boundaries of the decoration box.
973 // At the moment only the height is checked for the grab handle.
975 grabHandle.verticallyFlipped = (grabHandle.verticallyFlippedPreferred &&
976 ((grabHandleWorldPosition.y - grabHandle.size.height) > mBoundingBox.y)) ||
977 (grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w);
979 // The grab handle 'y' position in local coords.
980 // If the grab handle exceeds the bottom of the decoration box,
981 // set the 'y' position to the top of the line.
982 // The SetGrabHandleImage() method will change the orientation.
983 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
985 ApplyDisplacement(grabHandle, yLocalPosition);
988 void SetSelectionHandlePosition(HandleType type)
990 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
992 // Reference to the selection handle.
993 HandleImpl& handle = mHandle[type];
995 // Transforms the handle position into world coordinates.
996 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
997 // as it's transforming the handle's position set by the text-controller and not
998 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
999 // retrieves the position of the center of the actor but the handle's position set
1000 // by the text controller is not the center of the actor.
1001 Vector2 handleWorldPosition;
1002 CalculateHandleWorldCoordinates(handle, handleWorldPosition);
1004 // Whether to flip the handle (horizontally).
1005 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1007 // Whether to flip the handles if they are crossed.
1008 bool crossFlip = false;
1009 if(mFlipSelectionHandlesOnCross || !mIsHandlePanning)
1011 crossFlip = mIsHandleCurrentlyCrossed;
1014 // Whether the handle was crossed before start the panning.
1015 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1017 // Does not flip if both conditions are true (double flip)
1018 flipHandle = flipHandle != (crossFlip || isHandlePreviouslyCrossed);
1020 // Will flip the handles vertically if the user prefers it.
1021 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1023 if(crossFlip || isHandlePreviouslyCrossed)
1027 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1031 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1035 // Check if the selection handle exceeds the boundaries of the decoration box.
1036 const bool exceedsLeftEdge = (isPrimaryHandle ? !flipHandle : flipHandle) && (handleWorldPosition.x - handle.size.width < mBoundingBox.x);
1037 const bool exceedsRightEdge = (isPrimaryHandle ? flipHandle : !flipHandle) && (handleWorldPosition.x + handle.size.width > mBoundingBox.z);
1039 // Does not flip if both conditions are true (double flip)
1040 flipHandle = flipHandle != (exceedsLeftEdge || exceedsRightEdge);
1044 if(handle.actor && !handle.horizontallyFlipped)
1046 // Change the anchor point to flip the image.
1047 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT);
1049 handle.horizontallyFlipped = true;
1054 if(handle.actor && handle.horizontallyFlipped)
1056 // Reset the anchor point.
1057 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT);
1059 handle.horizontallyFlipped = false;
1063 // Whether to flip the handle vertically.
1064 handle.verticallyFlipped = (verticallyFlippedPreferred &&
1065 ((handleWorldPosition.y - handle.size.height) > mBoundingBox.y)) ||
1066 (handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w);
1068 // The primary selection handle 'y' position in local coords.
1069 // If the handle exceeds the bottom of the decoration box,
1070 // set the 'y' position to the top of the line.
1071 // The SetHandleImage() method will change the orientation.
1072 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1074 ApplyDisplacement(handle, yLocalPosition);
1077 void ApplyDisplacement(HandleImpl& handle, float yLocalPosition)
1081 float adjustedDisplacementX = 0.0f;
1082 float adjustedDisplacementY = 0.0f;
1083 if (mSmoothHandlePanEnabled)
1085 adjustedDisplacementX = CalculateAdjustedDisplacement(handle.position.x, handle.grabDisplacementX, mControlSize.x);
1086 adjustedDisplacementY = CalculateAdjustedDisplacement(handle.position.y, handle.grabDisplacementY, (mControlSize.y - handle.lineHeight));
1088 handle.actor.SetProperty(Actor::Property::POSITION,
1089 Vector2(handle.position.x + floor(0.5f * mCursorWidth) + adjustedDisplacementX,
1090 yLocalPosition + adjustedDisplacementY));
1094 float CalculateAdjustedDisplacement(float position, float displacement, float edge)
1096 //Apply the displacement (on the X-axis & the Y-axis)
1097 //as long as it does not exceed the control's edge.
1098 float adjustedDisplacement = 0.0f;
1099 if(position + displacement < 0.0f)
1101 // -position to cancel it out and relocate to 0.
1102 adjustedDisplacement = -position;
1104 else if(position + displacement > edge)
1106 // move in a displacement which is sufficient to reach the edge.
1107 adjustedDisplacement = edge - position;
1111 // move normally in the displacement.
1112 adjustedDisplacement = displacement;
1114 return adjustedDisplacement;
1117 void SetHandleImage(HandleType type)
1119 HandleImpl& handle = mHandle[type];
1121 HandleType markerType = HANDLE_TYPE_COUNT;
1122 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1123 if(LEFT_SELECTION_HANDLE == type)
1125 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1126 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1128 else if(RIGHT_SELECTION_HANDLE == type)
1130 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1131 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1134 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1137 const HandleImageType imageType = (handle.pressed ? (mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1139 handle.actor.SetImage(mHandleImages[type][imageType]);
1142 if(HANDLE_TYPE_COUNT != markerType)
1144 if(handle.markerActor)
1146 const HandleImageType markerImageType = (handle.pressed ? (mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1147 handle.markerActor.SetImage(mHandleImages[markerType][markerImageType]);
1151 // Whether to flip the handle vertically.
1154 handle.actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS));
1158 void CreateHighlight()
1160 if(!mHighlightActor)
1162 mHighlightActor = Actor::New();
1164 mHighlightActor.SetProperty(Dali::Actor::Property::NAME, "HighlightActor");
1165 mHighlightActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1166 mHighlightActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1167 mHighlightActor.SetProperty(Actor::Property::COLOR, mHighlightColor);
1168 mHighlightActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
1171 // Add the highlight box telling the controller it needs clipping.
1172 mController.AddDecoration(mHighlightActor, DecorationType::NONE_LAYER, true);
1175 void UpdateHighlight()
1179 // Sets the position of the highlight actor inside the decorator.
1180 mHighlightActor.SetProperty(Actor::Property::POSITION, Vector2(mHighlightPosition.x + mHighlightOutlineOffset, mHighlightPosition.y + mHighlightOutlineOffset));
1182 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1183 if(0u != numberOfQuads)
1185 // Set the size of the highlighted text to the actor.
1186 mHighlightActor.SetProperty(Actor::Property::SIZE, mHighlightSize);
1188 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1189 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1190 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1192 Vector<Vector2> vertices;
1193 Vector<unsigned short> indices;
1195 vertices.Reserve(4u * numberOfQuads);
1196 indices.Reserve(6u * numberOfQuads);
1198 // Index to the vertex.
1199 unsigned int v = 0u;
1201 // Traverse all quads.
1202 for(Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1203 endIt = mHighlightQuadList.End();
1207 const Vector4& quad = *it;
1212 vertex.x = quad.x - offsetX;
1213 vertex.y = quad.y - offsetY;
1214 vertices.PushBack(vertex);
1217 vertex.x = quad.z - offsetX;
1218 vertex.y = quad.y - offsetY;
1219 vertices.PushBack(vertex);
1221 // bottom-left (v+2)
1222 vertex.x = quad.x - offsetX;
1223 vertex.y = quad.w - offsetY;
1224 vertices.PushBack(vertex);
1226 // bottom-right (v+3)
1227 vertex.x = quad.z - offsetX;
1228 vertex.y = quad.w - offsetY;
1229 vertices.PushBack(vertex);
1231 // triangle A (3, 1, 0)
1232 indices.PushBack(v + 3);
1233 indices.PushBack(v + 1);
1234 indices.PushBack(v);
1236 // triangle B (0, 2, 3)
1237 indices.PushBack(v);
1238 indices.PushBack(v + 2);
1239 indices.PushBack(v + 3);
1244 mQuadVertices = VertexBuffer::New(mQuadVertexFormat);
1247 mQuadVertices.SetData(&vertices[0], vertices.Size());
1251 mQuadGeometry = Geometry::New();
1252 mQuadGeometry.AddVertexBuffer(mQuadVertices);
1254 mQuadGeometry.SetIndexBuffer(&indices[0], indices.Size());
1256 if(!mHighlightRenderer)
1258 mHighlightRenderer = Dali::Renderer::New(mQuadGeometry, mHighlightShader);
1259 mHighlightActor.AddRenderer(mHighlightRenderer);
1263 mHighlightQuadList.Clear();
1265 if(mHighlightRenderer)
1267 mHighlightRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mTextDepth - 2); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1272 void DoPan(HandleImpl& handle, HandleType type, const PanGesture& gesture)
1274 GestureState state = gesture.GetState();
1275 if(GestureState::STARTED == state)
1277 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1279 handle.globalPosition.x = handle.position.x;
1280 handle.globalPosition.y = handle.position.y;
1283 const Vector2& displacement = gesture.GetDisplacement();
1284 handle.grabDisplacementX += displacement.x;
1285 handle.grabDisplacementY += (handle.verticallyFlipped ? -displacement.y : displacement.y);
1287 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1288 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1289 const float yVerticallyFlippedCorrected = y - (handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f);
1291 if((GestureState::STARTED == state) ||
1292 (GestureState::CONTINUING == state))
1295 mController.GetTargetSize(targetSize);
1297 if(mHorizontalScrollingEnabled &&
1298 (x < mScrollThreshold))
1300 mScrollDirection = SCROLL_RIGHT;
1301 mHandleScrolling = type;
1304 else if(mHorizontalScrollingEnabled &&
1305 (x > targetSize.width - mScrollThreshold))
1307 mScrollDirection = SCROLL_LEFT;
1308 mHandleScrolling = type;
1311 else if(mVerticalScrollingEnabled &&
1312 (yVerticallyFlippedCorrected < mScrollThreshold))
1314 mScrollDirection = SCROLL_TOP;
1315 mHandleScrolling = type;
1318 else if(mVerticalScrollingEnabled &&
1319 (yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold))
1321 mScrollDirection = SCROLL_BOTTOM;
1322 mHandleScrolling = type;
1327 mHandleScrolling = HANDLE_TYPE_COUNT;
1329 mController.DecorationEvent(type, HANDLE_PRESSED, x, y);
1332 mIsHandlePanning = true;
1334 else if((GestureState::FINISHED == state) ||
1335 (GestureState::CANCELLED == state))
1338 (mScrollTimer.IsRunning() || mNotifyEndOfScroll))
1340 mNotifyEndOfScroll = false;
1341 mHandleScrolling = HANDLE_TYPE_COUNT;
1343 mController.DecorationEvent(type, HANDLE_STOP_SCROLLING, x, y);
1347 mController.DecorationEvent(type, HANDLE_RELEASED, x, y);
1352 handle.actor.SetImage(mHandleImages[type][HANDLE_IMAGE_RELEASED]);
1354 handle.pressed = false;
1356 mIsHandlePanning = false;
1360 void OnPan(Actor actor, const PanGesture& gesture)
1362 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1363 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1364 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1366 if(actor == grabHandle.grabArea)
1368 DoPan(grabHandle, GRAB_HANDLE, gesture);
1370 else if(actor == primarySelectionHandle.grabArea)
1372 DoPan(primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture);
1374 else if(actor == secondarySelectionHandle.grabArea)
1376 DoPan(secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture);
1380 bool OnGrabHandleTouched(Actor actor, const TouchEvent& touch)
1382 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1384 // Switch between pressed/release grab-handle images
1385 if(touch.GetPointCount() > 0 &&
1388 const PointState::Type state = touch.GetState(0);
1390 if(PointState::DOWN == state)
1392 grabHandle.pressed = true;
1394 else if((PointState::UP == state) ||
1395 (PointState::INTERRUPTED == state))
1397 grabHandle.pressed = false;
1400 SetHandleImage(GRAB_HANDLE);
1406 bool OnHandleOneTouched(Actor actor, const TouchEvent& touch)
1408 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1410 // Switch between pressed/release selection handle images
1411 if(touch.GetPointCount() > 0 &&
1412 primarySelectionHandle.actor)
1414 const PointState::Type state = touch.GetState(0);
1416 if(PointState::DOWN == state)
1418 primarySelectionHandle.pressed = true;
1419 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1421 else if((PointState::UP == state) ||
1422 (PointState::INTERRUPTED == state))
1424 primarySelectionHandle.pressed = false;
1425 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1426 mIsHandlePanning = false;
1427 mHandleReleased = LEFT_SELECTION_HANDLE;
1430 SetHandleImage(LEFT_SELECTION_HANDLE);
1436 bool OnHandleTwoTouched(Actor actor, const TouchEvent& touch)
1438 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1440 // Switch between pressed/release selection handle images
1441 if(touch.GetPointCount() > 0 &&
1442 secondarySelectionHandle.actor)
1444 const PointState::Type state = touch.GetState(0);
1446 if(PointState::DOWN == state)
1448 secondarySelectionHandle.pressed = true;
1449 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1451 else if((PointState::UP == state) ||
1452 (PointState::INTERRUPTED == state))
1454 secondarySelectionHandle.pressed = false;
1455 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1456 mIsHandlePanning = false;
1457 mHandleReleased = RIGHT_SELECTION_HANDLE;
1460 SetHandleImage(RIGHT_SELECTION_HANDLE);
1466 void HandleResetPosition(PropertyNotification& source)
1468 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1470 if(grabHandle.active)
1472 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1473 SetGrabHandlePosition();
1475 // Sets the grab handle image according if it's pressed, flipped, etc.
1476 SetHandleImage(GRAB_HANDLE);
1480 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1481 SetSelectionHandlePosition(LEFT_SELECTION_HANDLE);
1483 // Sets the primary handle image according if it's pressed, flipped, etc.
1484 SetHandleImage(LEFT_SELECTION_HANDLE);
1486 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1487 SetSelectionHandlePosition(RIGHT_SELECTION_HANDLE);
1489 // Sets the secondary handle image according if it's pressed, flipped, etc.
1490 SetHandleImage(RIGHT_SELECTION_HANDLE);
1494 void SetupActiveLayerPropertyNotifications()
1501 // Vertical notifications.
1503 // Disconnect any previous connected callback.
1504 if(mHandleVerticalLessThanNotification)
1506 mHandleVerticalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1507 mActiveLayer.RemovePropertyNotification(mHandleVerticalLessThanNotification);
1510 if(mHandleVerticalGreaterThanNotification)
1512 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1513 mActiveLayer.RemovePropertyNotification(mHandleVerticalGreaterThanNotification);
1516 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1517 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1518 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1520 if(grabHandle.active)
1522 if(grabHandle.verticallyFlipped)
1524 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1525 mHandleVerticalGreaterThanNotification.Reset();
1527 // The vertical distance from the center of the active layer to the top edje of the display.
1528 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1530 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1531 LessThanCondition(mBoundingBox.y + topHeight));
1533 // Notifies the change from false to true and from true to false.
1534 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1536 // Connects the signals with the callbacks.
1537 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1541 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1542 mHandleVerticalLessThanNotification.Reset();
1544 // The vertical distance from the center of the active layer to the bottom edje of the display.
1545 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1547 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1548 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1550 // Notifies the change from false to true and from true to false.
1551 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1553 // Connects the signals with the callbacks.
1554 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1557 else // The selection handles are active
1559 if(primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped)
1561 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1562 mHandleVerticalGreaterThanNotification.Reset();
1564 // The vertical distance from the center of the active layer to the top edje of the display.
1565 const float topHeight = 0.5f * mControlSize.height + std::max(-primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height);
1567 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1568 LessThanCondition(mBoundingBox.y + topHeight));
1570 // Notifies the change from false to true and from true to false.
1571 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1573 // Connects the signals with the callbacks.
1574 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1576 else if(!primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped)
1578 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1579 mHandleVerticalLessThanNotification.Reset();
1581 // The vertical distance from the center of the active layer to the bottom edje of the display.
1582 const float bottomHeight = -0.5f * mControlSize.height + std::max(primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1583 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height);
1585 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1586 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1588 // Notifies the change from false to true and from true to false.
1589 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1591 // Connects the signals with the callbacks.
1592 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1596 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1598 // The vertical distance from the center of the active layer to the top edje of the display.
1599 const float topHeight = 0.5f * mControlSize.height + (primaryHandle.verticallyFlipped ? -primaryHandle.position.y + primaryHandle.size.height : -secondaryHandle.position.y + secondaryHandle.size.height);
1601 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1602 LessThanCondition(mBoundingBox.y + topHeight));
1604 // Notifies the change from false to true and from true to false.
1605 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1607 // Connects the signals with the callbacks.
1608 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1610 // The vertical distance from the center of the active layer to the bottom edje of the display.
1611 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);
1613 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1614 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1616 // Notifies the change from false to true and from true to false.
1617 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1619 // Connects the signals with the callbacks.
1620 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1624 // Horizontal notifications.
1626 // Disconnect any previous connected callback.
1627 if(mHandleHorizontalLessThanNotification)
1629 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1630 mActiveLayer.RemovePropertyNotification(mHandleHorizontalLessThanNotification);
1633 if(mHandleHorizontalGreaterThanNotification)
1635 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1636 mActiveLayer.RemovePropertyNotification(mHandleHorizontalGreaterThanNotification);
1639 if(primaryHandle.active || secondaryHandle.active)
1641 // The horizontal distance from the center of the active layer to the left edje of the display.
1642 const float leftWidth = 0.5f * mControlSize.width + std::max(-primaryHandle.position.x + primaryHandle.size.width,
1643 -secondaryHandle.position.x + secondaryHandle.size.width);
1645 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1646 LessThanCondition(mBoundingBox.x + leftWidth));
1648 // Notifies the change from false to true and from true to false.
1649 mHandleHorizontalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1651 // Connects the signals with the callbacks.
1652 mHandleHorizontalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1654 // The horizontal distance from the center of the active layer to the right edje of the display.
1655 const float rightWidth = -0.5f * mControlSize.width + std::max(primaryHandle.position.x + primaryHandle.size.width,
1656 secondaryHandle.position.x + secondaryHandle.size.width);
1658 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1659 GreaterThanCondition(mBoundingBox.z - rightWidth));
1661 // Notifies the change from false to true and from true to false.
1662 mHandleHorizontalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1664 // Connects the signals with the callbacks.
1665 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1671 float AlternatePopUpPositionRelativeToCursor(bool topBottom)
1673 float alternativePosition = 0.0f;
1675 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1677 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1678 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1679 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1680 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1682 if(primaryHandle.active || secondaryHandle.active)
1684 float handleY = 0.f;
1685 float maxHandleHeight = 0.f;
1687 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1688 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1690 if(primaryVisible && secondaryVisible)
1692 handleY = std::max(primaryHandle.position.y, secondaryHandle.position.y);
1693 maxHandleHeight = std::max(primaryHandle.size.height, secondaryHandle.size.height);
1695 else if(primaryVisible && !secondaryVisible)
1697 handleY = primaryHandle.position.y;
1698 maxHandleHeight = primaryHandle.size.height;
1700 else if(!primaryVisible && secondaryVisible)
1702 handleY = secondaryHandle.position.y;
1703 maxHandleHeight = secondaryHandle.size.height;
1706 alternativePosition = handleY + (topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight);
1710 alternativePosition = cursor.position.y + (topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height);
1713 return alternativePosition;
1716 void PopUpLeavesTopBoundary(PropertyNotification& source)
1718 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1720 // Sets the position of the popup below.
1721 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, true)));
1724 void PopUpLeavesBottomBoundary(PropertyNotification& source)
1726 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1728 // Sets the position of the popup above.
1729 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, false)));
1732 void SetUpPopupPositionNotifications(const Vector3& popupHalfSize)
1734 // Disconnect any previous connected callback.
1735 if(mPopupTopExceedNotification)
1737 mPopupTopExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1738 mCopyPastePopup.actor.RemovePropertyNotification(mPopupTopExceedNotification);
1741 if(mPopupBottomExceedNotification)
1743 mPopupBottomExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1744 mCopyPastePopup.actor.RemovePropertyNotification(mPopupBottomExceedNotification);
1747 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1749 // Exceeding vertical boundary
1751 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1752 LessThanCondition(mBoundingBox.y + popupHalfSize.height));
1754 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1755 GreaterThanCondition(mBoundingBox.w - popupHalfSize.height));
1757 // Notifies the change from false to true and from true to false.
1758 mPopupTopExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1759 mPopupBottomExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1761 mPopupTopExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1762 mPopupBottomExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1765 void SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
1767 ImageDimensions dimensions = Dali::GetOriginalImageSize(imageFileName);
1769 HandleImpl& handle = mHandle[handleType];
1770 handle.size = Size(dimensions.GetWidth(), dimensions.GetHeight());
1772 mHandleImages[handleType][handleImageType] = imageFileName;
1775 void SetScrollThreshold(float threshold)
1777 mScrollThreshold = threshold;
1780 float GetScrollThreshold() const
1782 return mScrollThreshold;
1785 void SetScrollSpeed(float speed)
1787 mScrollSpeed = speed;
1788 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1791 float GetScrollSpeed() const
1793 return mScrollSpeed;
1796 void NotifyEndOfScroll()
1802 mNotifyEndOfScroll = true;
1807 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1809 * It only starts the timer if it's already created.
1811 void StartScrollTimer()
1815 mScrollTimer = Timer::New(SCROLL_TICK_INTERVAL);
1816 mScrollTimer.TickSignal().Connect(this, &Decorator::Impl::OnScrollTimerTick);
1819 if(!mScrollTimer.IsRunning())
1821 mScrollTimer.Start();
1826 * Stops the timer used to scroll the text.
1828 void StopScrollTimer()
1832 mScrollTimer.Stop();
1837 * Callback called by the timer used to scroll the text.
1839 * It calculates and sets a new scroll position.
1841 bool OnScrollTimerTick()
1843 if(HANDLE_TYPE_COUNT != mHandleScrolling)
1848 switch(mScrollDirection)
1852 x = mScrollDistance;
1857 x = -mScrollDistance;
1862 y = mScrollDistance;
1867 y = -mScrollDistance;
1874 mController.DecorationEvent(mHandleScrolling,
1883 ControllerInterface& mController;
1885 TapGestureDetector mTapDetector;
1886 PanGestureDetector mPanDetector;
1887 LongPressGestureDetector mLongPressDetector;
1889 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1890 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1892 Actor mActiveLayer; ///< Actor for active handles and alike that ensures they are above all else.
1893 Actor mCursorLayer; ///< Actor for cursor layer. this is for cursor clipping.
1894 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1895 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1896 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1897 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1898 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1899 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1900 Control mPrimaryCursor;
1901 Control mSecondaryCursor;
1903 Actor mHighlightActor; ///< Actor to display highlight
1904 Renderer mHighlightRenderer;
1905 Shader mHighlightShader; ///< Shader used for highlight
1906 Property::Map mQuadVertexFormat;
1907 PopupImpl mCopyPastePopup;
1908 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1909 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1911 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1912 Vector4 mHandleColor;
1914 CursorImpl mCursor[CURSOR_COUNT];
1915 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1917 VertexBuffer mQuadVertices;
1918 Geometry mQuadGeometry;
1919 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1921 Vector4 mBoundingBox; ///< The bounding box in world coords.
1922 Vector4 mHighlightColor; ///< Color of the highlight
1923 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1924 Size mHighlightSize; ///< The size of the highlighted text.
1925 Size mControlSize; ///< The control's size. Set by the Relayout.
1926 float mHighlightOutlineOffset; ///< The outline's offset.
1928 unsigned int mActiveCursor;
1929 unsigned int mCursorBlinkInterval;
1930 float mCursorBlinkDuration;
1931 float mCursorWidth; ///< The width of the cursors in pixels.
1932 HandleType mHandleScrolling; ///< The handle which is scrolling.
1933 HandleType mHandleReleased; ///< The last handle released.
1934 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1935 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1936 float mScrollSpeed; ///< The scroll speed in pixels per second.
1937 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1938 int mTextDepth; ///< The depth used to render the text.
1940 bool mActiveCopyPastePopup : 1;
1941 bool mPopupSetNewPosition : 1;
1942 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1943 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1944 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1945 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1946 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1947 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1948 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1949 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1950 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1951 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1952 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1953 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1954 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1955 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1956 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1957 bool mHidePrimaryCursorAndGrabHandle : 1; ///< Whether the primary cursor and grab are hidden always.
1960 DecoratorPtr Decorator::New(ControllerInterface& controller,
1961 TextSelectionPopupCallbackInterface& callbackInterface)
1963 return DecoratorPtr(new Decorator(controller,
1964 callbackInterface));
1967 void Decorator::SetBoundingBox(const Rect<int>& boundingBox)
1969 LocalToWorldCoordinatesBoundingBox(boundingBox, mImpl->mBoundingBox);
1972 void Decorator::GetBoundingBox(Rect<int>& boundingBox) const
1974 WorldToLocalCoordinatesBoundingBox(mImpl->mBoundingBox, boundingBox);
1977 void Decorator::Relayout(const Vector2& size)
1979 mImpl->Relayout(size);
1982 void Decorator::UpdatePositions(const Vector2& scrollOffset)
1984 mImpl->UpdatePositions(scrollOffset);
1989 void Decorator::SetActiveCursor(ActiveCursor activeCursor)
1991 mImpl->mActiveCursor = activeCursor;
1994 unsigned int Decorator::GetActiveCursor() const
1996 return mImpl->mActiveCursor;
1999 void Decorator::SetPosition(Cursor cursor, float x, float y, float cursorHeight, float lineHeight)
2001 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2003 cursorImpl.position.x = x;
2004 cursorImpl.position.y = y;
2005 cursorImpl.cursorHeight = cursorHeight;
2006 cursorImpl.lineHeight = lineHeight;
2009 void Decorator::GetPosition(Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight) const
2011 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2013 x = cursorImpl.position.x;
2014 y = cursorImpl.position.y;
2015 cursorHeight = cursorImpl.cursorHeight;
2016 lineHeight = cursorImpl.lineHeight;
2019 const Vector2& Decorator::GetPosition(Cursor cursor) const
2021 return mImpl->mCursor[cursor].position;
2024 void Decorator::SetGlyphOffset(Cursor cursor, float glyphOffset)
2026 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2028 cursorImpl.glyphOffset = glyphOffset;
2031 const float Decorator::GetGlyphOffset(Cursor cursor) const
2033 return mImpl->mCursor[cursor].glyphOffset;
2036 void Decorator::SetCursorColor(Cursor cursor, const Dali::Vector4& color)
2038 mImpl->mCursor[cursor].color = color;
2041 const Dali::Vector4& Decorator::GetColor(Cursor cursor) const
2043 return mImpl->mCursor[cursor].color;
2046 void Decorator::StartCursorBlink()
2048 if(!mImpl->mCursorBlinkTimer)
2050 mImpl->mCursorBlinkTimer = Timer::New(mImpl->mCursorBlinkInterval);
2051 mImpl->mCursorBlinkTimer.TickSignal().Connect(mImpl, &Decorator::Impl::OnCursorBlinkTimerTick);
2054 if(!mImpl->mCursorBlinkTimer.IsRunning())
2056 mImpl->mCursorBlinkTimer.Start();
2060 void Decorator::StopCursorBlink()
2062 if(mImpl->mCursorBlinkTimer)
2064 mImpl->mCursorBlinkTimer.Stop();
2067 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2070 void Decorator::DelayCursorBlink()
2072 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2073 mImpl->mDelayCursorBlink = true;
2076 void Decorator::SetCursorBlinkInterval(float seconds)
2078 mImpl->mCursorBlinkInterval = static_cast<unsigned int>(seconds * TO_MILLISECONDS); // Convert to milliseconds
2081 float Decorator::GetCursorBlinkInterval() const
2083 return static_cast<float>(mImpl->mCursorBlinkInterval) * TO_SECONDS;
2086 void Decorator::SetCursorBlinkDuration(float seconds)
2088 mImpl->mCursorBlinkDuration = seconds;
2091 float Decorator::GetCursorBlinkDuration() const
2093 return mImpl->mCursorBlinkDuration;
2096 void Decorator::SetCursorWidth(int width)
2098 mImpl->mCursorWidth = static_cast<float>(width);
2101 int Decorator::GetCursorWidth() const
2103 return static_cast<int>(mImpl->mCursorWidth);
2106 void Decorator::SetEditable(bool editable)
2108 mImpl->mHidePrimaryCursorAndGrabHandle = !editable;
2109 mImpl->Relayout(mImpl->mControlSize);
2113 void Decorator::SetHandleActive(HandleType handleType, bool active)
2115 mImpl->mHandle[handleType].active = active;
2119 if((LEFT_SELECTION_HANDLE == handleType) || (RIGHT_SELECTION_HANDLE == handleType))
2121 mImpl->mIsHandlePreviouslyCrossed = false;
2124 // TODO: this is a work-around.
2125 // The problem is the handle actor does not receive the touch event with the Interrupt
2126 // state when the power button is pressed and the application goes to background.
2127 mImpl->mHandle[handleType].pressed = false;
2128 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2129 ImageView imageView = mImpl->mHandle[handleType].actor;
2130 if(imageReleased && imageView)
2132 imageView.SetImage(mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED]);
2137 bool Decorator::IsHandleActive(HandleType handleType) const
2139 return mImpl->mHandle[handleType].active;
2142 void Decorator::SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
2144 mImpl->SetHandleImage(handleType, handleImageType, imageFileName);
2147 const std::string& Decorator::GetHandleImage(HandleType handleType, HandleImageType handleImageType) const
2149 return mImpl->mHandleImages[handleType][handleImageType];
2152 void Decorator::SetHandleColor(const Vector4& color)
2154 mImpl->mHandleColor = color;
2157 const Vector4& Decorator::GetHandleColor() const
2159 return mImpl->mHandleColor;
2162 void Decorator::SetPosition(HandleType handleType, float x, float y, float height)
2164 // Adjust handle's displacement
2165 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2167 handle.position.x = x;
2168 handle.position.y = y;
2169 handle.lineHeight = height;
2171 if(mImpl->mSmoothHandlePanEnabled)
2173 handle.grabDisplacementX = 0.f;
2174 handle.grabDisplacementY = 0.f;
2178 void Decorator::GetPosition(HandleType handleType, float& x, float& y, float& height) const
2180 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2182 x = handle.position.x;
2183 y = handle.position.y;
2184 height = handle.lineHeight;
2187 const Vector2& Decorator::GetPosition(HandleType handleType) const
2189 return mImpl->mHandle[handleType].position;
2192 void Decorator::FlipHandleVertically(HandleType handleType, bool flip)
2194 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2197 bool Decorator::IsHandleVerticallyFlipped(HandleType handleType) const
2199 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2202 void Decorator::FlipSelectionHandlesOnCrossEnabled(bool enable)
2204 mImpl->mFlipSelectionHandlesOnCross = enable;
2207 void Decorator::SetSelectionHandleFlipState(bool indicesSwapped, bool left, bool right)
2209 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2210 mImpl->mFlipLeftSelectionHandleDirection = left;
2211 mImpl->mFlipRightSelectionHandleDirection = right;
2214 void Decorator::AddHighlight(unsigned int index, const Vector4& quad)
2216 *(mImpl->mHighlightQuadList.Begin() + index) = quad;
2219 void Decorator::SetHighLightBox(const Vector2& position, const Size& size, float outlineOffset)
2221 mImpl->mHighlightPosition = position;
2222 mImpl->mHighlightSize = size;
2223 mImpl->mHighlightOutlineOffset = outlineOffset;
2226 void Decorator::ClearHighlights()
2228 mImpl->mHighlightQuadList.Clear();
2229 mImpl->mHighlightPosition = Vector2::ZERO;
2230 mImpl->mHighlightOutlineOffset = 0.f;
2233 void Decorator::ResizeHighlightQuads(unsigned int numberOfQuads)
2235 mImpl->mHighlightQuadList.Resize(numberOfQuads);
2238 void Decorator::SetHighlightColor(const Vector4& color)
2240 mImpl->mHighlightColor = color;
2243 const Vector4& Decorator::GetHighlightColor() const
2245 return mImpl->mHighlightColor;
2248 void Decorator::SetHighlightActive(bool active)
2250 mImpl->mIsHighlightBoxActive = active;
2253 bool Decorator::IsHighlightActive() const
2255 return mImpl->mIsHighlightBoxActive;
2258 bool Decorator::IsHighlightVisible() const
2260 return (mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent());
2263 void Decorator::SetTextDepth(int textDepth)
2265 mImpl->mTextDepth = textDepth;
2268 void Decorator::SetPopupActive(bool active)
2270 mImpl->mActiveCopyPastePopup = active;
2273 bool Decorator::IsPopupActive() const
2275 return mImpl->mActiveCopyPastePopup;
2278 void Decorator::SetEnabledPopupButtons(TextSelectionPopup::Buttons& enabledButtonsBitMask)
2280 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2282 if(!mImpl->mCopyPastePopup.actor)
2284 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New(&mImpl->mTextSelectionPopupCallbackInterface);
2285 #ifdef DECORATOR_DEBUG
2286 mImpl->mCopyPastePopup.actor.SetProperty(Dali::Actor::Property::NAME, "mCopyPastePopup");
2288 mImpl->mCopyPastePopup.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
2289 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect(mImpl, &Decorator::Impl::SetPopupPosition); // Position popup after size negotiation
2292 mImpl->mCopyPastePopup.actor.EnableButtons(mImpl->mEnabledPopupButtons);
2295 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2297 return mImpl->mEnabledPopupButtons;
2302 void Decorator::SetScrollThreshold(float threshold)
2304 mImpl->SetScrollThreshold(threshold);
2307 float Decorator::GetScrollThreshold() const
2309 return mImpl->GetScrollThreshold();
2312 void Decorator::SetScrollSpeed(float speed)
2314 mImpl->SetScrollSpeed(speed);
2317 float Decorator::GetScrollSpeed() const
2319 return mImpl->GetScrollSpeed();
2322 void Decorator::NotifyEndOfScroll()
2324 mImpl->NotifyEndOfScroll();
2327 void Decorator::SetHorizontalScrollEnabled(bool enable)
2329 mImpl->mHorizontalScrollingEnabled = enable;
2332 bool Decorator::IsHorizontalScrollEnabled() const
2334 return mImpl->mHorizontalScrollingEnabled;
2337 void Decorator::SetVerticalScrollEnabled(bool enable)
2339 mImpl->mVerticalScrollingEnabled = enable;
2342 bool Decorator::IsVerticalScrollEnabled() const
2344 return mImpl->mVerticalScrollingEnabled;
2347 void Decorator::SetSmoothHandlePanEnabled(bool enable)
2349 mImpl->mSmoothHandlePanEnabled = enable;
2352 bool Decorator::IsSmoothHandlePanEnabled() const
2354 return mImpl->mSmoothHandlePanEnabled;
2357 Decorator::~Decorator()
2362 Decorator::Decorator(ControllerInterface& controller,
2363 TextSelectionPopupCallbackInterface& callbackInterface)
2366 mImpl = new Decorator::Impl(controller, callbackInterface);
2371 } // namespace Toolkit