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
261 // Show or hide the cursors
266 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
267 mPrimaryCursorVisible = (!mHidePrimaryCursorAndGrabHandle) && ((mControlSize.width - (cursor.position.x + mCursorWidth) > -Math::MACHINE_EPSILON_1000) &&
268 (cursor.position.x > -Math::MACHINE_EPSILON_1000) &&
269 (mControlSize.height - (cursor.position.y + cursor.cursorHeight) > -Math::MACHINE_EPSILON_1000) &&
270 (cursor.position.y > -Math::MACHINE_EPSILON_1000));
271 if(mPrimaryCursorVisible)
273 mPrimaryCursor.SetProperty(Actor::Property::POSITION, Vector2(cursor.position.x, cursor.position.y));
274 mPrimaryCursor.SetProperty(Actor::Property::SIZE, Size(mCursorWidth, cursor.cursorHeight));
276 mPrimaryCursor.SetProperty(Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus);
280 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
281 mSecondaryCursorVisible = ((mControlSize.width - (cursor.position.x + mCursorWidth) > -Math::MACHINE_EPSILON_1000) &&
282 (cursor.position.x > -Math::MACHINE_EPSILON_1000) &&
283 (mControlSize.height - (cursor.position.y + cursor.cursorHeight) > -Math::MACHINE_EPSILON_1000) &&
284 (cursor.position.y > -Math::MACHINE_EPSILON_1000));
285 if(mSecondaryCursorVisible)
287 mSecondaryCursor.SetProperty(Actor::Property::POSITION, Vector2(cursor.position.x, cursor.position.y));
288 mSecondaryCursor.SetProperty(Actor::Property::SIZE, Size(mCursorWidth, cursor.cursorHeight));
290 mSecondaryCursor.SetProperty(Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus);
293 // Show or hide the grab handle
294 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
295 bool newGrabHandlePosition = false;
296 grabHandle.horizontallyVisible = false;
297 grabHandle.verticallyVisible = false;
298 if(grabHandle.active)
300 grabHandle.horizontallyVisible = ((mControlSize.width - (grabHandle.position.x + floor(0.5f * mCursorWidth)) > -Math::MACHINE_EPSILON_1000) &&
301 (grabHandle.position.x > -Math::MACHINE_EPSILON_1000));
302 grabHandle.verticallyVisible = (((mControlSize.height - grabHandle.lineHeight) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000) &&
303 (grabHandle.position.y > -Math::MACHINE_EPSILON_1000));
305 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible && (!mHidePrimaryCursorAndGrabHandle);
310 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
311 SetGrabHandlePosition();
313 // Sets the grab handle image according if it's pressed, flipped, etc.
314 SetHandleImage(GRAB_HANDLE);
316 newGrabHandlePosition = true;
321 grabHandle.actor.SetProperty(Actor::Property::VISIBLE, isVisible);
324 else if(grabHandle.actor)
326 grabHandle.actor.Unparent();
329 // Show or hide the selection handles/highlight
330 HandleImpl& primary = mHandle[LEFT_SELECTION_HANDLE];
331 HandleImpl& secondary = mHandle[RIGHT_SELECTION_HANDLE];
332 bool newPrimaryHandlePosition = false;
333 bool newSecondaryHandlePosition = false;
335 primary.horizontallyVisible = ((mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000) &&
336 (primary.position.x > -Math::MACHINE_EPSILON_1000));
337 primary.verticallyVisible = (((mControlSize.height - primary.lineHeight) - primary.position.y > -Math::MACHINE_EPSILON_1000) &&
338 (primary.position.y + (primary.verticallyFlipped ? 0.f : primary.lineHeight) > -Math::MACHINE_EPSILON_1000));
339 secondary.horizontallyVisible = ((mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000) &&
340 (secondary.position.x > -Math::MACHINE_EPSILON_1000));
341 secondary.verticallyVisible = (((mControlSize.height - secondary.lineHeight) - secondary.position.y > -Math::MACHINE_EPSILON_1000) &&
342 (secondary.position.y + (secondary.verticallyFlipped ? 0.f : secondary.lineHeight) > -Math::MACHINE_EPSILON_1000));
344 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
345 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
347 if(primary.active || secondary.active)
349 if(primaryVisible || secondaryVisible)
351 CreateSelectionHandles();
355 SetSelectionHandlePosition(LEFT_SELECTION_HANDLE);
357 // Sets the primary handle image according if it's pressed, flipped, etc.
358 SetHandleImage(LEFT_SELECTION_HANDLE);
360 SetSelectionHandleMarkerSize(primary);
362 newPrimaryHandlePosition = true;
367 SetSelectionHandlePosition(RIGHT_SELECTION_HANDLE);
369 // Sets the secondary handle image according if it's pressed, flipped, etc.
370 SetHandleImage(RIGHT_SELECTION_HANDLE);
372 SetSelectionHandleMarkerSize(secondary);
374 newSecondaryHandlePosition = true;
380 primary.actor.SetProperty(Actor::Property::VISIBLE, primaryVisible);
384 secondary.actor.SetProperty(Actor::Property::VISIBLE, secondaryVisible);
391 primary.actor.Unparent();
395 secondary.actor.Unparent();
399 if(mIsHighlightBoxActive)
408 mHighlightActor.Unparent();
412 if(newGrabHandlePosition ||
413 newPrimaryHandlePosition ||
414 newSecondaryHandlePosition)
416 // Setup property notifications to find whether the handles leave the boundaries of the current display.
417 SetupActiveLayerPropertyNotifications();
420 if(mActiveCopyPastePopup &&
421 (primaryVisible || secondaryVisible))
424 mPopupSetNewPosition = true;
428 if(mCopyPastePopup.actor)
430 mCopyPastePopup.actor.HidePopup();
431 mPopupSetNewPosition = true;
436 void UpdatePositions(const Vector2& scrollOffset)
438 mCursor[PRIMARY_CURSOR].position += scrollOffset;
439 mCursor[SECONDARY_CURSOR].position += scrollOffset;
440 mHandle[GRAB_HANDLE].position += scrollOffset;
441 mHandle[LEFT_SELECTION_HANDLE].position += scrollOffset;
442 mHandle[RIGHT_SELECTION_HANDLE].position += scrollOffset;
443 mHighlightPosition += scrollOffset;
448 if(!mCopyPastePopup.actor)
453 if(!mCopyPastePopup.actor.GetParent())
455 mActiveLayer.Add(mCopyPastePopup.actor);
458 mCopyPastePopup.actor.RaiseAbove(mActiveLayer);
459 mCopyPastePopup.actor.ShowPopup();
462 float CalculateVerticalPopUpPosition(float halfHeight, bool preferBelow)
464 float yPosition = 0.f;
466 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
467 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
468 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
470 if(primaryHandle.active || secondaryHandle.active)
472 // The origin of the decorator's coordinate system in world coords.
473 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION) - mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * ACTIVE_LAYER_ANCHOR_POINT;
477 // Find out if there is enough space for the popup at the bottom.
478 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
479 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
481 float maxY = std::max(primaryBelowY, secondaryBelowY);
483 yPosition = halfHeight + maxY;
485 if(originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w)
487 // Does not fit below.
489 // Try to fit first below the non active handle. Otherwise above the active handle.
490 if(RIGHT_SELECTION_HANDLE == mHandleReleased)
492 if(primaryBelowY < secondaryBelowY)
494 yPosition = halfHeight + primaryBelowY;
498 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
501 else if(LEFT_SELECTION_HANDLE == mHandleReleased)
503 if(secondaryBelowY < primaryBelowY)
505 yPosition = halfHeight + secondaryBelowY;
509 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
513 // Check the handle is whithin the decoration box.
514 if(originWorldCoords.y + yPosition < mBoundingBox.y)
516 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
519 if(originWorldCoords.y + yPosition > mBoundingBox.w)
521 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
527 // Find out if there is enough space for the popup at the top.
528 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
529 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
531 float minY = std::min(primaryTopY, secondaryTopY);
533 yPosition = -halfHeight + minY;
535 } // ( primaryHandle.active || secondaryHandle.active )
536 else if(grabHandle.active)
540 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
544 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
551 void ConstrainPopupPosition(const Vector3& popupHalfSize)
553 // Check if the popup is within the boundaries of the decoration box.
555 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
557 // The origin of the decorator's coordinate system in world coords.
558 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION) - mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * ACTIVE_LAYER_ANCHOR_POINT;
560 // The popup's position in world coords.
561 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
563 if(popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x)
565 mCopyPastePopup.position.x += mBoundingBox.x - (popupPositionWorldCoords.x - popupHalfSize.width);
567 else if(popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z)
569 mCopyPastePopup.position.x += mBoundingBox.z - (popupPositionWorldCoords.x + popupHalfSize.width);
572 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
573 if(popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y)
575 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition(popupHalfSize.height, true); // true -> prefer to set the popup's position below.
579 void SetPopupPosition(Actor actor)
581 if(!mActiveCopyPastePopup)
586 // Retrieves the popup's size after relayout.
587 const Vector3 popupSize(mCopyPastePopup.actor.GetRelayoutSize(Dimension::WIDTH), mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT), 0.0f);
588 const Vector3 popupHalfSize = popupSize * 0.5f;
590 if(mPopupSetNewPosition)
592 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
593 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
594 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
596 if(primaryHandle.active || secondaryHandle.active)
598 const float minHandleXPosition = std::min(primaryHandle.position.x, secondaryHandle.position.x);
599 const float maxHandleXPosition = std::max(primaryHandle.position.x, secondaryHandle.position.x);
601 mCopyPastePopup.position.x = minHandleXPosition + ((maxHandleXPosition - minHandleXPosition) * 0.5f);
603 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - (primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING);
604 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - (secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING);
606 mCopyPastePopup.position.y = std::min(primaryY, secondaryY);
608 else if(grabHandle.active)
610 mCopyPastePopup.position.x = grabHandle.position.x;
612 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - (grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING);
614 } // mPopupSetNewPosition
616 // It may change the popup's position to fit within the decoration box.
617 ConstrainPopupPosition(popupHalfSize);
619 SetUpPopupPositionNotifications(popupHalfSize);
621 // Prevent pixel mis-alignment by rounding down.
622 mCopyPastePopup.position.x = floorf(mCopyPastePopup.position.x);
623 mCopyPastePopup.position.y = floorf(mCopyPastePopup.position.y);
625 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION, mCopyPastePopup.position);
626 mPopupSetNewPosition = false;
629 void CreateCursor(Control& cursor, const Vector4& color)
631 cursor = Control::New();
632 cursor.SetBackgroundColor(color);
633 cursor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
634 cursor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
635 cursor.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIDDEN, true);
638 // Add or Remove cursor(s) from parent
641 if(mActiveCursor == ACTIVE_CURSOR_NONE)
645 mPrimaryCursor.Unparent();
649 mSecondaryCursor.Unparent();
654 // Create Primary and or Secondary Cursor(s) if active and add to parent
655 if(mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
656 mActiveCursor == ACTIVE_CURSOR_BOTH)
660 CreateCursor(mPrimaryCursor, mCursor[PRIMARY_CURSOR].color);
661 #ifdef DECORATOR_DEBUG
662 mPrimaryCursor.SetProperty(Dali::Actor::Property::NAME, "PrimaryCursorActor");
666 if(!mPrimaryCursor.GetParent())
668 mActiveLayer.Add(mPrimaryCursor);
672 if(mActiveCursor == ACTIVE_CURSOR_BOTH)
674 if(!mSecondaryCursor)
676 CreateCursor(mSecondaryCursor, mCursor[SECONDARY_CURSOR].color);
677 #ifdef DECORATOR_DEBUG
678 mSecondaryCursor.SetProperty(Dali::Actor::Property::NAME, "SecondaryCursorActor");
682 if(!mSecondaryCursor.GetParent())
684 mActiveLayer.Add(mSecondaryCursor);
691 mSecondaryCursor.Unparent();
697 bool OnCursorBlinkTimerTick()
699 if(!mDelayCursorBlink)
704 mPrimaryCursor.SetProperty(Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus);
708 mSecondaryCursor.SetProperty(Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus);
711 mCursorBlinkStatus = !mCursorBlinkStatus;
716 mDelayCursorBlink = false;
724 // Will consume tap gestures on handles.
725 mTapDetector = TapGestureDetector::New();
727 // Will consume double tap gestures on handles.
728 mTapDetector.SetMaximumTapsRequired(2u);
730 // Will consume long press gestures on handles.
731 mLongPressDetector = LongPressGestureDetector::New();
733 // Detects pan gestures on handles.
734 mPanDetector = PanGestureDetector::New();
735 mPanDetector.DetectedSignal().Connect(this, &Decorator::Impl::OnPan);
738 void CreateActiveLayer()
742 mActiveLayer = Actor::New();
743 #ifdef DECORATOR_DEBUG
744 mActiveLayer.SetProperty(Actor::Property::NAME, "ActiveLayerActor");
747 mActiveLayer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
748 mActiveLayer.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
750 // Add the active layer telling the controller it doesn't need clipping.
751 mController.AddDecoration(mActiveLayer, false);
754 mActiveLayer.RaiseToTop();
757 void SetSelectionHandleMarkerSize(HandleImpl& handle)
759 if(handle.markerActor)
761 handle.markerActor.SetProperty(Actor::Property::SIZE, Vector2(0, handle.lineHeight));
765 void CreateGrabHandle()
767 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
768 if(!grabHandle.actor)
770 if(mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED].size())
772 grabHandle.actor = ImageView::New(mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED]);
773 GetImpl(grabHandle.actor).SetDepthIndex(DepthIndex::DECORATION);
774 grabHandle.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
776 // Area that Grab handle responds to, larger than actual handle so easier to move
777 #ifdef DECORATOR_DEBUG
778 grabHandle.actor.SetProperty(Dali::Actor::Property::NAME, "GrabHandleActor");
779 if(Dali::Internal::gLogFilter->IsEnabledFor(Debug::Verbose))
781 grabHandle.grabArea = Control::New();
782 Toolkit::Control control = Toolkit::Control::DownCast(grabHandle.grabArea);
783 control.SetBackgroundColor(Vector4(1.0f, 1.0f, 1.0f, 0.5f));
784 grabHandle.grabArea.SetProperty(Dali::Actor::Property::NAME, "GrabArea");
788 grabHandle.grabArea = Actor::New();
789 grabHandle.grabArea.SetProperty(Dali::Actor::Property::NAME, "GrabArea");
792 grabHandle.grabArea = Actor::New();
795 grabHandle.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
796 grabHandle.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
797 grabHandle.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
798 grabHandle.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_GRAB_HANDLE_RELATIVE_SIZE);
799 grabHandle.actor.Add(grabHandle.grabArea);
800 grabHandle.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
802 grabHandle.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnGrabHandleTouched);
804 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
805 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
806 mTapDetector.Attach(grabHandle.actor);
807 mLongPressDetector.Attach(grabHandle.actor);
809 // The grab handle's area is attached to the pan detector.
810 // The OnPan() method is connected to the signals emitted by the pan detector.
811 mPanDetector.Attach(grabHandle.grabArea);
813 mActiveLayer.Add(grabHandle.actor);
817 if(grabHandle.actor && !grabHandle.actor.GetParent())
819 mActiveLayer.Add(grabHandle.actor);
823 void CreateHandleMarker(HandleImpl& handle, const std::string& image, HandleType handleType)
827 handle.markerActor = ImageView::New(image);
828 handle.markerActor.SetProperty(Actor::Property::COLOR, mHandleColor);
829 handle.actor.Add(handle.markerActor);
831 handle.markerActor.SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
833 if(LEFT_SELECTION_HANDLE == handleType)
835 handle.markerActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT);
836 handle.markerActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_RIGHT);
838 else if(RIGHT_SELECTION_HANDLE == handleType)
840 handle.markerActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT);
841 handle.markerActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
846 void CreateSelectionHandles()
848 HandleImpl& primary = mHandle[LEFT_SELECTION_HANDLE];
851 if(mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size())
853 primary.actor = ImageView::New(mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]);
854 #ifdef DECORATOR_DEBUG
855 primary.actor.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleOne");
857 primary.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
858 GetImpl(primary.actor).SetDepthIndex(DepthIndex::DECORATION);
859 primary.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
861 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
862 #ifdef DECORATOR_DEBUG
863 primary.grabArea.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleOneGrabArea");
865 primary.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
866 primary.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
867 primary.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
868 primary.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE);
870 primary.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnHandleOneTouched);
872 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
873 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
874 mTapDetector.Attach(primary.actor);
875 mLongPressDetector.Attach(primary.actor);
877 // The handle's area is attached to the pan detector.
878 // The OnPan() method is connected to the signals emitted by the pan detector.
879 mPanDetector.Attach(primary.grabArea);
881 primary.actor.Add(primary.grabArea);
883 CreateHandleMarker(primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE);
887 if(primary.actor && !primary.actor.GetParent())
889 mActiveLayer.Add(primary.actor);
892 HandleImpl& secondary = mHandle[RIGHT_SELECTION_HANDLE];
895 if(mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size())
897 secondary.actor = ImageView::New(mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]);
898 #ifdef DECORATOR_DEBUG
899 secondary.actor.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleTwo");
901 secondary.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
902 GetImpl(secondary.actor).SetDepthIndex(DepthIndex::DECORATION);
903 secondary.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
905 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
906 #ifdef DECORATOR_DEBUG
907 secondary.grabArea.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleTwoGrabArea");
909 secondary.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
910 secondary.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
911 secondary.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
912 secondary.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE);
914 secondary.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnHandleTwoTouched);
916 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
917 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
918 mTapDetector.Attach(secondary.actor);
919 mLongPressDetector.Attach(secondary.actor);
921 // The handle's area is attached to the pan detector.
922 // The OnPan() method is connected to the signals emitted by the pan detector.
923 mPanDetector.Attach(secondary.grabArea);
925 secondary.actor.Add(secondary.grabArea);
927 CreateHandleMarker(secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE);
931 if(secondary.actor && !secondary.actor.GetParent())
933 mActiveLayer.Add(secondary.actor);
937 void CalculateHandleWorldCoordinates(HandleImpl& handle, Vector2& position)
939 // Gets the world position of the active layer. The active layer is where the handles are added.
940 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION);
942 // The grab handle position in world coords.
943 // The active layer's world position is the center of the active layer. The origin of the
944 // coord system of the handles is the top left of the active layer.
945 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + (mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f);
946 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + (mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f);
949 void SetGrabHandlePosition()
951 // Reference to the grab handle.
952 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
954 // Transforms the handle position into world coordinates.
955 // @note This is not the same value as grabHandle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
956 // as it's transforming the handle's position set by the text-controller and not
957 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
958 // retrieves the position of the center of the actor but the handle's position set
959 // by the text controller is not the center of the actor.
960 Vector2 grabHandleWorldPosition;
961 CalculateHandleWorldCoordinates(grabHandle, grabHandleWorldPosition);
963 // Check if the grab handle exceeds the boundaries of the decoration box.
964 // At the moment only the height is checked for the grab handle.
966 grabHandle.verticallyFlipped = (grabHandle.verticallyFlippedPreferred &&
967 ((grabHandleWorldPosition.y - grabHandle.size.height) > mBoundingBox.y)) ||
968 (grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w);
970 // The grab handle 'y' position in local coords.
971 // If the grab handle exceeds the bottom of the decoration box,
972 // set the 'y' position to the top of the line.
973 // The SetGrabHandleImage() method will change the orientation.
974 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
976 ApplyDisplacement(grabHandle, yLocalPosition);
979 void SetSelectionHandlePosition(HandleType type)
981 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
983 // Reference to the selection handle.
984 HandleImpl& handle = mHandle[type];
986 // Transforms the handle position into world coordinates.
987 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
988 // as it's transforming the handle's position set by the text-controller and not
989 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
990 // retrieves the position of the center of the actor but the handle's position set
991 // by the text controller is not the center of the actor.
992 Vector2 handleWorldPosition;
993 CalculateHandleWorldCoordinates(handle, handleWorldPosition);
995 // Whether to flip the handle (horizontally).
996 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
998 // Whether to flip the handles if they are crossed.
999 bool crossFlip = false;
1000 if(mFlipSelectionHandlesOnCross || !mIsHandlePanning)
1002 crossFlip = mIsHandleCurrentlyCrossed;
1005 // Whether the handle was crossed before start the panning.
1006 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1008 // Does not flip if both conditions are true (double flip)
1009 flipHandle = flipHandle != (crossFlip || isHandlePreviouslyCrossed);
1011 // Will flip the handles vertically if the user prefers it.
1012 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1014 if(crossFlip || isHandlePreviouslyCrossed)
1018 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1022 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1026 // Check if the selection handle exceeds the boundaries of the decoration box.
1027 const bool exceedsLeftEdge = (isPrimaryHandle ? !flipHandle : flipHandle) && (handleWorldPosition.x - handle.size.width < mBoundingBox.x);
1028 const bool exceedsRightEdge = (isPrimaryHandle ? flipHandle : !flipHandle) && (handleWorldPosition.x + handle.size.width > mBoundingBox.z);
1030 // Does not flip if both conditions are true (double flip)
1031 flipHandle = flipHandle != (exceedsLeftEdge || exceedsRightEdge);
1035 if(handle.actor && !handle.horizontallyFlipped)
1037 // Change the anchor point to flip the image.
1038 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT);
1040 handle.horizontallyFlipped = true;
1045 if(handle.actor && handle.horizontallyFlipped)
1047 // Reset the anchor point.
1048 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT);
1050 handle.horizontallyFlipped = false;
1054 // Whether to flip the handle vertically.
1055 handle.verticallyFlipped = (verticallyFlippedPreferred &&
1056 ((handleWorldPosition.y - handle.size.height) > mBoundingBox.y)) ||
1057 (handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w);
1059 // The primary selection handle 'y' position in local coords.
1060 // If the handle exceeds the bottom of the decoration box,
1061 // set the 'y' position to the top of the line.
1062 // The SetHandleImage() method will change the orientation.
1063 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1065 ApplyDisplacement(handle, yLocalPosition);
1068 void ApplyDisplacement(HandleImpl& handle, float yLocalPosition)
1072 float adjustedDisplacementX = 0.0f;
1073 float adjustedDisplacementY = 0.0f;
1074 if (mSmoothHandlePanEnabled)
1076 adjustedDisplacementX = CalculateAdjustedDisplacement(handle.position.x, handle.grabDisplacementX, mControlSize.x);
1077 adjustedDisplacementY = CalculateAdjustedDisplacement(handle.position.y, handle.grabDisplacementY, (mControlSize.y - handle.lineHeight));
1079 handle.actor.SetProperty(Actor::Property::POSITION,
1080 Vector2(handle.position.x + floor(0.5f * mCursorWidth) + adjustedDisplacementX,
1081 yLocalPosition + adjustedDisplacementY));
1085 float CalculateAdjustedDisplacement(float position, float displacement, float edge)
1087 //Apply the displacement (on the X-axis & the Y-axis)
1088 //as long as it does not exceed the control's edge.
1089 float adjustedDisplacement = 0.0f;
1090 if(position + displacement < 0.0f)
1092 // -position to cancel it out and relocate to 0.
1093 adjustedDisplacement = -position;
1095 else if(position + displacement > edge)
1097 // move in a displacement which is sufficient to reach the edge.
1098 adjustedDisplacement = edge - position;
1102 // move normally in the displacement.
1103 adjustedDisplacement = displacement;
1105 return adjustedDisplacement;
1108 void SetHandleImage(HandleType type)
1110 HandleImpl& handle = mHandle[type];
1112 HandleType markerType = HANDLE_TYPE_COUNT;
1113 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1114 if(LEFT_SELECTION_HANDLE == type)
1116 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1117 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1119 else if(RIGHT_SELECTION_HANDLE == type)
1121 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1122 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1125 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1128 const HandleImageType imageType = (handle.pressed ? (mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1130 handle.actor.SetImage(mHandleImages[type][imageType]);
1133 if(HANDLE_TYPE_COUNT != markerType)
1135 if(handle.markerActor)
1137 const HandleImageType markerImageType = (handle.pressed ? (mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1138 handle.markerActor.SetImage(mHandleImages[markerType][markerImageType]);
1142 // Whether to flip the handle vertically.
1145 handle.actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS));
1149 void CreateHighlight()
1151 if(!mHighlightActor)
1153 mHighlightActor = Actor::New();
1155 mHighlightActor.SetProperty(Dali::Actor::Property::NAME, "HighlightActor");
1156 mHighlightActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1157 mHighlightActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1158 mHighlightActor.SetProperty(Actor::Property::COLOR, mHighlightColor);
1159 mHighlightActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
1162 // Add the highlight box telling the controller it needs clipping.
1163 mController.AddDecoration(mHighlightActor, true);
1166 void UpdateHighlight()
1170 // Sets the position of the highlight actor inside the decorator.
1171 mHighlightActor.SetProperty(Actor::Property::POSITION, Vector2(mHighlightPosition.x + mHighlightOutlineOffset, mHighlightPosition.y + mHighlightOutlineOffset));
1173 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1174 if(0u != numberOfQuads)
1176 // Set the size of the highlighted text to the actor.
1177 mHighlightActor.SetProperty(Actor::Property::SIZE, mHighlightSize);
1179 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1180 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1181 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1183 Vector<Vector2> vertices;
1184 Vector<unsigned short> indices;
1186 vertices.Reserve(4u * numberOfQuads);
1187 indices.Reserve(6u * numberOfQuads);
1189 // Index to the vertex.
1190 unsigned int v = 0u;
1192 // Traverse all quads.
1193 for(Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1194 endIt = mHighlightQuadList.End();
1198 const Vector4& quad = *it;
1203 vertex.x = quad.x - offsetX;
1204 vertex.y = quad.y - offsetY;
1205 vertices.PushBack(vertex);
1208 vertex.x = quad.z - offsetX;
1209 vertex.y = quad.y - offsetY;
1210 vertices.PushBack(vertex);
1212 // bottom-left (v+2)
1213 vertex.x = quad.x - offsetX;
1214 vertex.y = quad.w - offsetY;
1215 vertices.PushBack(vertex);
1217 // bottom-right (v+3)
1218 vertex.x = quad.z - offsetX;
1219 vertex.y = quad.w - offsetY;
1220 vertices.PushBack(vertex);
1222 // triangle A (3, 1, 0)
1223 indices.PushBack(v + 3);
1224 indices.PushBack(v + 1);
1225 indices.PushBack(v);
1227 // triangle B (0, 2, 3)
1228 indices.PushBack(v);
1229 indices.PushBack(v + 2);
1230 indices.PushBack(v + 3);
1235 mQuadVertices = VertexBuffer::New(mQuadVertexFormat);
1238 mQuadVertices.SetData(&vertices[0], vertices.Size());
1242 mQuadGeometry = Geometry::New();
1243 mQuadGeometry.AddVertexBuffer(mQuadVertices);
1245 mQuadGeometry.SetIndexBuffer(&indices[0], indices.Size());
1247 if(!mHighlightRenderer)
1249 mHighlightRenderer = Dali::Renderer::New(mQuadGeometry, mHighlightShader);
1250 mHighlightActor.AddRenderer(mHighlightRenderer);
1254 mHighlightQuadList.Clear();
1256 if(mHighlightRenderer)
1258 mHighlightRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mTextDepth - 2); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1263 void DoPan(HandleImpl& handle, HandleType type, const PanGesture& gesture)
1265 GestureState state = gesture.GetState();
1266 if(GestureState::STARTED == state)
1268 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1270 handle.globalPosition.x = handle.position.x;
1271 handle.globalPosition.y = handle.position.y;
1274 const Vector2& displacement = gesture.GetDisplacement();
1275 handle.grabDisplacementX += displacement.x;
1276 handle.grabDisplacementY += (handle.verticallyFlipped ? -displacement.y : displacement.y);
1278 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1279 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1280 const float yVerticallyFlippedCorrected = y - (handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f);
1282 if((GestureState::STARTED == state) ||
1283 (GestureState::CONTINUING == state))
1286 mController.GetTargetSize(targetSize);
1288 if(mHorizontalScrollingEnabled &&
1289 (x < mScrollThreshold))
1291 mScrollDirection = SCROLL_RIGHT;
1292 mHandleScrolling = type;
1295 else if(mHorizontalScrollingEnabled &&
1296 (x > targetSize.width - mScrollThreshold))
1298 mScrollDirection = SCROLL_LEFT;
1299 mHandleScrolling = type;
1302 else if(mVerticalScrollingEnabled &&
1303 (yVerticallyFlippedCorrected < mScrollThreshold))
1305 mScrollDirection = SCROLL_TOP;
1306 mHandleScrolling = type;
1309 else if(mVerticalScrollingEnabled &&
1310 (yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold))
1312 mScrollDirection = SCROLL_BOTTOM;
1313 mHandleScrolling = type;
1318 mHandleScrolling = HANDLE_TYPE_COUNT;
1320 mController.DecorationEvent(type, HANDLE_PRESSED, x, y);
1323 mIsHandlePanning = true;
1325 else if((GestureState::FINISHED == state) ||
1326 (GestureState::CANCELLED == state))
1329 (mScrollTimer.IsRunning() || mNotifyEndOfScroll))
1331 mNotifyEndOfScroll = false;
1332 mHandleScrolling = HANDLE_TYPE_COUNT;
1334 mController.DecorationEvent(type, HANDLE_STOP_SCROLLING, x, y);
1338 mController.DecorationEvent(type, HANDLE_RELEASED, x, y);
1343 handle.actor.SetImage(mHandleImages[type][HANDLE_IMAGE_RELEASED]);
1345 handle.pressed = false;
1347 mIsHandlePanning = false;
1351 void OnPan(Actor actor, const PanGesture& gesture)
1353 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1354 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1355 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1357 if(actor == grabHandle.grabArea)
1359 DoPan(grabHandle, GRAB_HANDLE, gesture);
1361 else if(actor == primarySelectionHandle.grabArea)
1363 DoPan(primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture);
1365 else if(actor == secondarySelectionHandle.grabArea)
1367 DoPan(secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture);
1371 bool OnGrabHandleTouched(Actor actor, const TouchEvent& touch)
1373 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1375 // Switch between pressed/release grab-handle images
1376 if(touch.GetPointCount() > 0 &&
1379 const PointState::Type state = touch.GetState(0);
1381 if(PointState::DOWN == state)
1383 grabHandle.pressed = true;
1385 else if((PointState::UP == state) ||
1386 (PointState::INTERRUPTED == state))
1388 grabHandle.pressed = false;
1391 SetHandleImage(GRAB_HANDLE);
1397 bool OnHandleOneTouched(Actor actor, const TouchEvent& touch)
1399 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1401 // Switch between pressed/release selection handle images
1402 if(touch.GetPointCount() > 0 &&
1403 primarySelectionHandle.actor)
1405 const PointState::Type state = touch.GetState(0);
1407 if(PointState::DOWN == state)
1409 primarySelectionHandle.pressed = true;
1410 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1412 else if((PointState::UP == state) ||
1413 (PointState::INTERRUPTED == state))
1415 primarySelectionHandle.pressed = false;
1416 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1417 mIsHandlePanning = false;
1418 mHandleReleased = LEFT_SELECTION_HANDLE;
1421 SetHandleImage(LEFT_SELECTION_HANDLE);
1427 bool OnHandleTwoTouched(Actor actor, const TouchEvent& touch)
1429 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1431 // Switch between pressed/release selection handle images
1432 if(touch.GetPointCount() > 0 &&
1433 secondarySelectionHandle.actor)
1435 const PointState::Type state = touch.GetState(0);
1437 if(PointState::DOWN == state)
1439 secondarySelectionHandle.pressed = true;
1440 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1442 else if((PointState::UP == state) ||
1443 (PointState::INTERRUPTED == state))
1445 secondarySelectionHandle.pressed = false;
1446 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1447 mIsHandlePanning = false;
1448 mHandleReleased = RIGHT_SELECTION_HANDLE;
1451 SetHandleImage(RIGHT_SELECTION_HANDLE);
1457 void HandleResetPosition(PropertyNotification& source)
1459 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1461 if(grabHandle.active)
1463 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1464 SetGrabHandlePosition();
1466 // Sets the grab handle image according if it's pressed, flipped, etc.
1467 SetHandleImage(GRAB_HANDLE);
1471 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1472 SetSelectionHandlePosition(LEFT_SELECTION_HANDLE);
1474 // Sets the primary handle image according if it's pressed, flipped, etc.
1475 SetHandleImage(LEFT_SELECTION_HANDLE);
1477 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1478 SetSelectionHandlePosition(RIGHT_SELECTION_HANDLE);
1480 // Sets the secondary handle image according if it's pressed, flipped, etc.
1481 SetHandleImage(RIGHT_SELECTION_HANDLE);
1485 void SetupActiveLayerPropertyNotifications()
1492 // Vertical notifications.
1494 // Disconnect any previous connected callback.
1495 if(mHandleVerticalLessThanNotification)
1497 mHandleVerticalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1498 mActiveLayer.RemovePropertyNotification(mHandleVerticalLessThanNotification);
1501 if(mHandleVerticalGreaterThanNotification)
1503 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1504 mActiveLayer.RemovePropertyNotification(mHandleVerticalGreaterThanNotification);
1507 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1508 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1509 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1511 if(grabHandle.active)
1513 if(grabHandle.verticallyFlipped)
1515 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1516 mHandleVerticalGreaterThanNotification.Reset();
1518 // The vertical distance from the center of the active layer to the top edje of the display.
1519 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1521 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1522 LessThanCondition(mBoundingBox.y + topHeight));
1524 // Notifies the change from false to true and from true to false.
1525 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1527 // Connects the signals with the callbacks.
1528 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1532 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1533 mHandleVerticalLessThanNotification.Reset();
1535 // The vertical distance from the center of the active layer to the bottom edje of the display.
1536 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1538 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1539 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1541 // Notifies the change from false to true and from true to false.
1542 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1544 // Connects the signals with the callbacks.
1545 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1548 else // The selection handles are active
1550 if(primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped)
1552 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1553 mHandleVerticalGreaterThanNotification.Reset();
1555 // The vertical distance from the center of the active layer to the top edje of the display.
1556 const float topHeight = 0.5f * mControlSize.height + std::max(-primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height);
1558 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1559 LessThanCondition(mBoundingBox.y + topHeight));
1561 // Notifies the change from false to true and from true to false.
1562 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1564 // Connects the signals with the callbacks.
1565 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1567 else if(!primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped)
1569 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1570 mHandleVerticalLessThanNotification.Reset();
1572 // The vertical distance from the center of the active layer to the bottom edje of the display.
1573 const float bottomHeight = -0.5f * mControlSize.height + std::max(primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1574 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height);
1576 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1577 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1579 // Notifies the change from false to true and from true to false.
1580 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1582 // Connects the signals with the callbacks.
1583 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1587 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1589 // The vertical distance from the center of the active layer to the top edje of the display.
1590 const float topHeight = 0.5f * mControlSize.height + (primaryHandle.verticallyFlipped ? -primaryHandle.position.y + primaryHandle.size.height : -secondaryHandle.position.y + secondaryHandle.size.height);
1592 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1593 LessThanCondition(mBoundingBox.y + topHeight));
1595 // Notifies the change from false to true and from true to false.
1596 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1598 // Connects the signals with the callbacks.
1599 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1601 // The vertical distance from the center of the active layer to the bottom edje of the display.
1602 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);
1604 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1605 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1607 // Notifies the change from false to true and from true to false.
1608 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1610 // Connects the signals with the callbacks.
1611 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1615 // Horizontal notifications.
1617 // Disconnect any previous connected callback.
1618 if(mHandleHorizontalLessThanNotification)
1620 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1621 mActiveLayer.RemovePropertyNotification(mHandleHorizontalLessThanNotification);
1624 if(mHandleHorizontalGreaterThanNotification)
1626 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1627 mActiveLayer.RemovePropertyNotification(mHandleHorizontalGreaterThanNotification);
1630 if(primaryHandle.active || secondaryHandle.active)
1632 // The horizontal distance from the center of the active layer to the left edje of the display.
1633 const float leftWidth = 0.5f * mControlSize.width + std::max(-primaryHandle.position.x + primaryHandle.size.width,
1634 -secondaryHandle.position.x + secondaryHandle.size.width);
1636 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1637 LessThanCondition(mBoundingBox.x + leftWidth));
1639 // Notifies the change from false to true and from true to false.
1640 mHandleHorizontalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1642 // Connects the signals with the callbacks.
1643 mHandleHorizontalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1645 // The horizontal distance from the center of the active layer to the right edje of the display.
1646 const float rightWidth = -0.5f * mControlSize.width + std::max(primaryHandle.position.x + primaryHandle.size.width,
1647 secondaryHandle.position.x + secondaryHandle.size.width);
1649 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1650 GreaterThanCondition(mBoundingBox.z - rightWidth));
1652 // Notifies the change from false to true and from true to false.
1653 mHandleHorizontalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1655 // Connects the signals with the callbacks.
1656 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1662 float AlternatePopUpPositionRelativeToCursor(bool topBottom)
1664 float alternativePosition = 0.0f;
1666 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1668 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1669 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1670 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1671 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1673 if(primaryHandle.active || secondaryHandle.active)
1675 float handleY = 0.f;
1676 float maxHandleHeight = 0.f;
1678 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1679 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1681 if(primaryVisible && secondaryVisible)
1683 handleY = std::max(primaryHandle.position.y, secondaryHandle.position.y);
1684 maxHandleHeight = std::max(primaryHandle.size.height, secondaryHandle.size.height);
1686 else if(primaryVisible && !secondaryVisible)
1688 handleY = primaryHandle.position.y;
1689 maxHandleHeight = primaryHandle.size.height;
1691 else if(!primaryVisible && secondaryVisible)
1693 handleY = secondaryHandle.position.y;
1694 maxHandleHeight = secondaryHandle.size.height;
1697 alternativePosition = handleY + (topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight);
1701 alternativePosition = cursor.position.y + (topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height);
1704 return alternativePosition;
1707 void PopUpLeavesTopBoundary(PropertyNotification& source)
1709 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1711 // Sets the position of the popup below.
1712 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, true)));
1715 void PopUpLeavesBottomBoundary(PropertyNotification& source)
1717 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1719 // Sets the position of the popup above.
1720 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, false)));
1723 void SetUpPopupPositionNotifications(const Vector3& popupHalfSize)
1725 // Disconnect any previous connected callback.
1726 if(mPopupTopExceedNotification)
1728 mPopupTopExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1729 mCopyPastePopup.actor.RemovePropertyNotification(mPopupTopExceedNotification);
1732 if(mPopupBottomExceedNotification)
1734 mPopupBottomExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1735 mCopyPastePopup.actor.RemovePropertyNotification(mPopupBottomExceedNotification);
1738 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1740 // Exceeding vertical boundary
1742 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1743 LessThanCondition(mBoundingBox.y + popupHalfSize.height));
1745 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1746 GreaterThanCondition(mBoundingBox.w - popupHalfSize.height));
1748 // Notifies the change from false to true and from true to false.
1749 mPopupTopExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1750 mPopupBottomExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1752 mPopupTopExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1753 mPopupBottomExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1756 void SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
1758 ImageDimensions dimensions = Dali::GetOriginalImageSize(imageFileName);
1760 HandleImpl& handle = mHandle[handleType];
1761 handle.size = Size(dimensions.GetWidth(), dimensions.GetHeight());
1763 mHandleImages[handleType][handleImageType] = imageFileName;
1766 void SetScrollThreshold(float threshold)
1768 mScrollThreshold = threshold;
1771 float GetScrollThreshold() const
1773 return mScrollThreshold;
1776 void SetScrollSpeed(float speed)
1778 mScrollSpeed = speed;
1779 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1782 float GetScrollSpeed() const
1784 return mScrollSpeed;
1787 void NotifyEndOfScroll()
1793 mNotifyEndOfScroll = true;
1798 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1800 * It only starts the timer if it's already created.
1802 void StartScrollTimer()
1806 mScrollTimer = Timer::New(SCROLL_TICK_INTERVAL);
1807 mScrollTimer.TickSignal().Connect(this, &Decorator::Impl::OnScrollTimerTick);
1810 if(!mScrollTimer.IsRunning())
1812 mScrollTimer.Start();
1817 * Stops the timer used to scroll the text.
1819 void StopScrollTimer()
1823 mScrollTimer.Stop();
1828 * Callback called by the timer used to scroll the text.
1830 * It calculates and sets a new scroll position.
1832 bool OnScrollTimerTick()
1834 if(HANDLE_TYPE_COUNT != mHandleScrolling)
1839 switch(mScrollDirection)
1843 x = mScrollDistance;
1848 x = -mScrollDistance;
1853 y = mScrollDistance;
1858 y = -mScrollDistance;
1865 mController.DecorationEvent(mHandleScrolling,
1874 ControllerInterface& mController;
1876 TapGestureDetector mTapDetector;
1877 PanGestureDetector mPanDetector;
1878 LongPressGestureDetector mLongPressDetector;
1880 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1881 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1883 Actor mActiveLayer; ///< Actor for active handles and alike that ensures they are above all else.
1884 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1885 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1886 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1887 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1888 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1889 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1890 Control mPrimaryCursor;
1891 Control mSecondaryCursor;
1893 Actor mHighlightActor; ///< Actor to display highlight
1894 Renderer mHighlightRenderer;
1895 Shader mHighlightShader; ///< Shader used for highlight
1896 Property::Map mQuadVertexFormat;
1897 PopupImpl mCopyPastePopup;
1898 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1899 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1901 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1902 Vector4 mHandleColor;
1904 CursorImpl mCursor[CURSOR_COUNT];
1905 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1907 VertexBuffer mQuadVertices;
1908 Geometry mQuadGeometry;
1909 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1911 Vector4 mBoundingBox; ///< The bounding box in world coords.
1912 Vector4 mHighlightColor; ///< Color of the highlight
1913 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1914 Size mHighlightSize; ///< The size of the highlighted text.
1915 Size mControlSize; ///< The control's size. Set by the Relayout.
1916 float mHighlightOutlineOffset; ///< The outline's offset.
1918 unsigned int mActiveCursor;
1919 unsigned int mCursorBlinkInterval;
1920 float mCursorBlinkDuration;
1921 float mCursorWidth; ///< The width of the cursors in pixels.
1922 HandleType mHandleScrolling; ///< The handle which is scrolling.
1923 HandleType mHandleReleased; ///< The last handle released.
1924 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1925 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1926 float mScrollSpeed; ///< The scroll speed in pixels per second.
1927 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1928 int mTextDepth; ///< The depth used to render the text.
1930 bool mActiveCopyPastePopup : 1;
1931 bool mPopupSetNewPosition : 1;
1932 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1933 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1934 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1935 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1936 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1937 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1938 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1939 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1940 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1941 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1942 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1943 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1944 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1945 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1946 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1947 bool mHidePrimaryCursorAndGrabHandle : 1; ///< Whether the primary cursor and grab are hidden always.
1950 DecoratorPtr Decorator::New(ControllerInterface& controller,
1951 TextSelectionPopupCallbackInterface& callbackInterface)
1953 return DecoratorPtr(new Decorator(controller,
1954 callbackInterface));
1957 void Decorator::SetBoundingBox(const Rect<int>& boundingBox)
1959 LocalToWorldCoordinatesBoundingBox(boundingBox, mImpl->mBoundingBox);
1962 void Decorator::GetBoundingBox(Rect<int>& boundingBox) const
1964 WorldToLocalCoordinatesBoundingBox(mImpl->mBoundingBox, boundingBox);
1967 void Decorator::Relayout(const Vector2& size)
1969 mImpl->Relayout(size);
1972 void Decorator::UpdatePositions(const Vector2& scrollOffset)
1974 mImpl->UpdatePositions(scrollOffset);
1979 void Decorator::SetActiveCursor(ActiveCursor activeCursor)
1981 mImpl->mActiveCursor = activeCursor;
1984 unsigned int Decorator::GetActiveCursor() const
1986 return mImpl->mActiveCursor;
1989 void Decorator::SetPosition(Cursor cursor, float x, float y, float cursorHeight, float lineHeight)
1991 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
1993 cursorImpl.position.x = x;
1994 cursorImpl.position.y = y;
1995 cursorImpl.cursorHeight = cursorHeight;
1996 cursorImpl.lineHeight = lineHeight;
1999 void Decorator::GetPosition(Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight) const
2001 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2003 x = cursorImpl.position.x;
2004 y = cursorImpl.position.y;
2005 cursorHeight = cursorImpl.cursorHeight;
2006 lineHeight = cursorImpl.lineHeight;
2009 const Vector2& Decorator::GetPosition(Cursor cursor) const
2011 return mImpl->mCursor[cursor].position;
2014 void Decorator::SetGlyphOffset(Cursor cursor, float glyphOffset)
2016 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2018 cursorImpl.glyphOffset = glyphOffset;
2021 const float Decorator::GetGlyphOffset(Cursor cursor) const
2023 return mImpl->mCursor[cursor].glyphOffset;
2026 void Decorator::SetCursorColor(Cursor cursor, const Dali::Vector4& color)
2028 mImpl->mCursor[cursor].color = color;
2031 const Dali::Vector4& Decorator::GetColor(Cursor cursor) const
2033 return mImpl->mCursor[cursor].color;
2036 void Decorator::StartCursorBlink()
2038 if(!mImpl->mCursorBlinkTimer)
2040 mImpl->mCursorBlinkTimer = Timer::New(mImpl->mCursorBlinkInterval);
2041 mImpl->mCursorBlinkTimer.TickSignal().Connect(mImpl, &Decorator::Impl::OnCursorBlinkTimerTick);
2044 if(!mImpl->mCursorBlinkTimer.IsRunning())
2046 mImpl->mCursorBlinkTimer.Start();
2050 void Decorator::StopCursorBlink()
2052 if(mImpl->mCursorBlinkTimer)
2054 mImpl->mCursorBlinkTimer.Stop();
2057 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2060 void Decorator::DelayCursorBlink()
2062 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2063 mImpl->mDelayCursorBlink = true;
2066 void Decorator::SetCursorBlinkInterval(float seconds)
2068 mImpl->mCursorBlinkInterval = static_cast<unsigned int>(seconds * TO_MILLISECONDS); // Convert to milliseconds
2071 float Decorator::GetCursorBlinkInterval() const
2073 return static_cast<float>(mImpl->mCursorBlinkInterval) * TO_SECONDS;
2076 void Decorator::SetCursorBlinkDuration(float seconds)
2078 mImpl->mCursorBlinkDuration = seconds;
2081 float Decorator::GetCursorBlinkDuration() const
2083 return mImpl->mCursorBlinkDuration;
2086 void Decorator::SetCursorWidth(int width)
2088 mImpl->mCursorWidth = static_cast<float>(width);
2091 int Decorator::GetCursorWidth() const
2093 return static_cast<int>(mImpl->mCursorWidth);
2096 void Decorator::SetEditable(bool editable)
2098 mImpl->mHidePrimaryCursorAndGrabHandle = !editable;
2099 mImpl->Relayout(mImpl->mControlSize);
2103 void Decorator::SetHandleActive(HandleType handleType, bool active)
2105 mImpl->mHandle[handleType].active = active;
2109 if((LEFT_SELECTION_HANDLE == handleType) || (RIGHT_SELECTION_HANDLE == handleType))
2111 mImpl->mIsHandlePreviouslyCrossed = false;
2114 // TODO: this is a work-around.
2115 // The problem is the handle actor does not receive the touch event with the Interrupt
2116 // state when the power button is pressed and the application goes to background.
2117 mImpl->mHandle[handleType].pressed = false;
2118 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2119 ImageView imageView = mImpl->mHandle[handleType].actor;
2120 if(imageReleased && imageView)
2122 imageView.SetImage(mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED]);
2127 bool Decorator::IsHandleActive(HandleType handleType) const
2129 return mImpl->mHandle[handleType].active;
2132 void Decorator::SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
2134 mImpl->SetHandleImage(handleType, handleImageType, imageFileName);
2137 const std::string& Decorator::GetHandleImage(HandleType handleType, HandleImageType handleImageType) const
2139 return mImpl->mHandleImages[handleType][handleImageType];
2142 void Decorator::SetHandleColor(const Vector4& color)
2144 mImpl->mHandleColor = color;
2147 const Vector4& Decorator::GetHandleColor() const
2149 return mImpl->mHandleColor;
2152 void Decorator::SetPosition(HandleType handleType, float x, float y, float height)
2154 // Adjust handle's displacement
2155 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2157 handle.position.x = x;
2158 handle.position.y = y;
2159 handle.lineHeight = height;
2161 if(mImpl->mSmoothHandlePanEnabled)
2163 handle.grabDisplacementX = 0.f;
2164 handle.grabDisplacementY = 0.f;
2168 void Decorator::GetPosition(HandleType handleType, float& x, float& y, float& height) const
2170 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2172 x = handle.position.x;
2173 y = handle.position.y;
2174 height = handle.lineHeight;
2177 const Vector2& Decorator::GetPosition(HandleType handleType) const
2179 return mImpl->mHandle[handleType].position;
2182 void Decorator::FlipHandleVertically(HandleType handleType, bool flip)
2184 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2187 bool Decorator::IsHandleVerticallyFlipped(HandleType handleType) const
2189 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2192 void Decorator::FlipSelectionHandlesOnCrossEnabled(bool enable)
2194 mImpl->mFlipSelectionHandlesOnCross = enable;
2197 void Decorator::SetSelectionHandleFlipState(bool indicesSwapped, bool left, bool right)
2199 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2200 mImpl->mFlipLeftSelectionHandleDirection = left;
2201 mImpl->mFlipRightSelectionHandleDirection = right;
2204 void Decorator::AddHighlight(unsigned int index, const Vector4& quad)
2206 *(mImpl->mHighlightQuadList.Begin() + index) = quad;
2209 void Decorator::SetHighLightBox(const Vector2& position, const Size& size, float outlineOffset)
2211 mImpl->mHighlightPosition = position;
2212 mImpl->mHighlightSize = size;
2213 mImpl->mHighlightOutlineOffset = outlineOffset;
2216 void Decorator::ClearHighlights()
2218 mImpl->mHighlightQuadList.Clear();
2219 mImpl->mHighlightPosition = Vector2::ZERO;
2220 mImpl->mHighlightOutlineOffset = 0.f;
2223 void Decorator::ResizeHighlightQuads(unsigned int numberOfQuads)
2225 mImpl->mHighlightQuadList.Resize(numberOfQuads);
2228 void Decorator::SetHighlightColor(const Vector4& color)
2230 mImpl->mHighlightColor = color;
2233 const Vector4& Decorator::GetHighlightColor() const
2235 return mImpl->mHighlightColor;
2238 void Decorator::SetHighlightActive(bool active)
2240 mImpl->mIsHighlightBoxActive = active;
2243 bool Decorator::IsHighlightActive() const
2245 return mImpl->mIsHighlightBoxActive;
2248 bool Decorator::IsHighlightVisible() const
2250 return (mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent());
2253 void Decorator::SetTextDepth(int textDepth)
2255 mImpl->mTextDepth = textDepth;
2258 void Decorator::SetPopupActive(bool active)
2260 mImpl->mActiveCopyPastePopup = active;
2263 bool Decorator::IsPopupActive() const
2265 return mImpl->mActiveCopyPastePopup;
2268 void Decorator::SetEnabledPopupButtons(TextSelectionPopup::Buttons& enabledButtonsBitMask)
2270 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2272 if(!mImpl->mCopyPastePopup.actor)
2274 mImpl->mCopyPastePopup.actor = TextSelectionPopup::New(&mImpl->mTextSelectionPopupCallbackInterface);
2275 #ifdef DECORATOR_DEBUG
2276 mImpl->mCopyPastePopup.actor.SetProperty(Dali::Actor::Property::NAME, "mCopyPastePopup");
2278 mImpl->mCopyPastePopup.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
2279 mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect(mImpl, &Decorator::Impl::SetPopupPosition); // Position popup after size negotiation
2282 mImpl->mCopyPastePopup.actor.EnableButtons(mImpl->mEnabledPopupButtons);
2285 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2287 return mImpl->mEnabledPopupButtons;
2292 void Decorator::SetScrollThreshold(float threshold)
2294 mImpl->SetScrollThreshold(threshold);
2297 float Decorator::GetScrollThreshold() const
2299 return mImpl->GetScrollThreshold();
2302 void Decorator::SetScrollSpeed(float speed)
2304 mImpl->SetScrollSpeed(speed);
2307 float Decorator::GetScrollSpeed() const
2309 return mImpl->GetScrollSpeed();
2312 void Decorator::NotifyEndOfScroll()
2314 mImpl->NotifyEndOfScroll();
2317 void Decorator::SetHorizontalScrollEnabled(bool enable)
2319 mImpl->mHorizontalScrollingEnabled = enable;
2322 bool Decorator::IsHorizontalScrollEnabled() const
2324 return mImpl->mHorizontalScrollingEnabled;
2327 void Decorator::SetVerticalScrollEnabled(bool enable)
2329 mImpl->mVerticalScrollingEnabled = enable;
2332 bool Decorator::IsVerticalScrollEnabled() const
2334 return mImpl->mVerticalScrollingEnabled;
2337 void Decorator::SetSmoothHandlePanEnabled(bool enable)
2339 mImpl->mSmoothHandlePanEnabled = enable;
2342 bool Decorator::IsSmoothHandlePanEnabled() const
2344 return mImpl->mSmoothHandlePanEnabled;
2347 Decorator::~Decorator()
2352 Decorator::Decorator(ControllerInterface& controller,
2353 TextSelectionPopupCallbackInterface& callbackInterface)
2356 mImpl = new Decorator::Impl(controller, callbackInterface);
2361 } // namespace Toolkit