2 * Copyright (c) 2024 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>
32 #include <dali/public-api/size-negotiation/relayout-container.h>
35 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
36 #include <dali-toolkit/devel-api/controls/control-devel.h>
37 #include <dali-toolkit/internal/controls/image-view/image-view-impl.h>
38 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
39 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
42 #define DECORATOR_DEBUG
52 #ifdef DECORATOR_DEBUG
53 Integration::Log::Filter* gLogFilter(Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR"));
56 } // namespace Internal
62 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE(1.25f, 1.5f, 1.0f);
63 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE(1.25f, 1.5f, 1.0f);
64 const Dali::Vector3 ACTIVE_LAYER_ANCHOR_POINT(0.5f, 0.5f, 0.5f);
66 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.
68 const Dali::Vector4 HANDLE_COLOR(0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f);
70 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
71 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
72 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
74 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
75 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.
76 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
78 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
80 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
82 const float POPUP_PADDING = 2.f; ///< Padding space between the highlight box and the text's popup.
84 typedef Dali::Vector<Dali::Vector4> QuadContainer;
87 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
88 * @param[in] boundingRectangle local bounding
89 * @param[out] Vector4 World coordinate bounding Box.
91 void LocalToWorldCoordinatesBoundingBox(const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox)
93 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
94 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
96 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
97 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
99 boundingBox = Dali::Vector4(originX,
101 originX + boundingRectangle.width,
102 originY + boundingRectangle.height);
105 void WorldToLocalCoordinatesBoundingBox(const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle)
107 // Convert to local coordinates and store as a Dali::Rect.
108 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
110 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
111 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
112 boundingRectangle.width = boundingBox.z - boundingBox.x;
113 boundingRectangle.height = boundingBox.w - boundingBox.y;
116 } // end of namespace
124 struct Decorator::Impl : public ConnectionTracker
138 : color(Dali::Color::BLACK),
160 grabDisplacementX(0.f),
161 grabDisplacementY(0.f),
163 horizontallyVisible(false),
164 verticallyVisible(false),
166 verticallyFlippedPreferred(false),
167 horizontallyFlipped(false),
168 verticallyFlipped(false),
169 verticallyFlippedOnTouch(false)
175 ImageView markerActor;
178 Vector2 globalPosition;
180 float lineHeight; ///< Not the handle height
181 float grabDisplacementX;
182 float grabDisplacementY;
184 bool horizontallyVisible : 1;
185 bool verticallyVisible : 1;
187 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
188 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
189 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
190 bool verticallyFlippedOnTouch : 1; ///< Whether the handle is vertically flipped on touch.
200 TextSelectionPopup actor;
204 Impl(ControllerInterface& controller,
205 TextSelectionPopupCallbackInterface& callbackInterface)
206 : mController(controller),
207 mEnabledPopupButtons(TextSelectionPopup::NONE),
208 mTextSelectionPopupCallbackInterface(callbackInterface),
209 mHandleColor(HANDLE_COLOR),
211 mHighlightColor(LIGHT_BLUE),
212 mHighlightPosition(Vector2::ZERO),
213 mHighlightSize(Vector2::ZERO),
214 mControlSize(Vector2::ZERO),
215 mHighlightOutlineOffset(0.f),
216 mActiveCursor(ACTIVE_CURSOR_NONE),
217 mCursorBlinkInterval(CURSOR_BLINK_INTERVAL),
218 mCursorBlinkDuration(0.0f),
219 mCursorWidth(CURSOR_WIDTH),
220 mHandleScrolling(HANDLE_TYPE_COUNT),
221 mHandleReleased(HANDLE_TYPE_COUNT),
222 mScrollDirection(SCROLL_NONE),
223 mScrollThreshold(SCROLL_THRESHOLD),
224 mScrollSpeed(SCROLL_SPEED),
225 mScrollDistance(SCROLL_DISTANCE),
227 mActiveCopyPastePopup(false),
228 mPopupSetNewPosition(true),
229 mCursorBlinkStatus(true),
230 mDelayCursorBlink(false),
231 mPrimaryCursorVisible(false),
232 mSecondaryCursorVisible(false),
233 mFlipSelectionHandlesOnCross(false),
234 mFlipLeftSelectionHandleDirection(false),
235 mFlipRightSelectionHandleDirection(false),
236 mIsHandlePanning(false),
237 mIsHandleCurrentlyCrossed(false),
238 mIsHandlePreviouslyCrossed(false),
239 mNotifyEndOfScroll(false),
240 mHorizontalScrollingEnabled(false),
241 mVerticalScrollingEnabled(false),
242 mSmoothHandlePanEnabled(false),
243 mIsHighlightBoxActive(false),
244 mHidePrimaryCursorAndGrabHandle(false)
246 mQuadVertexFormat["aPosition"] = Property::VECTOR2;
247 mHighlightShader = Shader::New(SHADER_TEXT_DECORATOR_SHADER_VERT, SHADER_TEXT_DECORATOR_SHADER_FRAG, Shader::Hint::NONE, "TEXT_DECORATOR");
252 * Relayout of the decorations owned by the decorator.
253 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
255 void Relayout(const Vector2& size, RelayoutContainer& container)
259 // TODO - Remove this if nothing is active
260 CreateLayer(mActiveLayer, DecorationType::ACTIVE_LAYER);
261 CreateLayer(mCursorLayer, DecorationType::CURSOR_LAYER);
263 // Show or hide the cursors
268 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
269 mPrimaryCursorVisible = (!mHidePrimaryCursorAndGrabHandle) && ((mControlSize.width - (cursor.position.x + mCursorWidth) > -Math::MACHINE_EPSILON_1000) &&
270 (cursor.position.x > -Math::MACHINE_EPSILON_1000) &&
271 (mControlSize.height - cursor.position.y > -Math::MACHINE_EPSILON_1000) &&
272 (cursor.position.y + cursor.cursorHeight > -Math::MACHINE_EPSILON_1000));
273 if(mPrimaryCursorVisible)
275 mPrimaryCursor.SetProperty(Actor::Property::POSITION, Vector2(cursor.position.x, cursor.position.y));
276 mPrimaryCursor.SetProperty(Actor::Property::SIZE, Size(mCursorWidth, cursor.cursorHeight));
278 container.Add(mPrimaryCursor, Size(mCursorWidth, cursor.cursorHeight));
280 mPrimaryCursor.SetProperty(Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus);
284 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
285 mSecondaryCursorVisible = ((mControlSize.width - (cursor.position.x + mCursorWidth) > -Math::MACHINE_EPSILON_1000) &&
286 (cursor.position.x > -Math::MACHINE_EPSILON_1000) &&
287 (mControlSize.height - cursor.position.y > -Math::MACHINE_EPSILON_1000) &&
288 (cursor.position.y + cursor.cursorHeight > -Math::MACHINE_EPSILON_1000));
289 if(mSecondaryCursorVisible)
291 mSecondaryCursor.SetProperty(Actor::Property::POSITION, Vector2(cursor.position.x, cursor.position.y));
292 mSecondaryCursor.SetProperty(Actor::Property::SIZE, Size(mCursorWidth, cursor.cursorHeight));
294 container.Add(mSecondaryCursor, Size(mCursorWidth, cursor.cursorHeight));
296 mSecondaryCursor.SetProperty(Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus);
299 // Show or hide the grab handle
300 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
301 bool newGrabHandlePosition = false;
302 grabHandle.horizontallyVisible = false;
303 grabHandle.verticallyVisible = false;
304 if(grabHandle.active)
306 grabHandle.horizontallyVisible = ((mControlSize.width - (grabHandle.position.x + floor(0.5f * mCursorWidth)) > -Math::MACHINE_EPSILON_1000) &&
307 (grabHandle.position.x > -Math::MACHINE_EPSILON_1000));
308 grabHandle.verticallyVisible = ((fabsf(mControlSize.height - grabHandle.lineHeight) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000) &&
309 (grabHandle.position.y + grabHandle.lineHeight > -Math::MACHINE_EPSILON_1000));
311 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible && (!mHidePrimaryCursorAndGrabHandle);
316 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
317 SetGrabHandlePosition();
319 // Sets the grab handle image according if it's pressed, flipped, etc.
320 SetHandleImage(GRAB_HANDLE);
322 newGrabHandlePosition = true;
327 grabHandle.actor.SetProperty(Actor::Property::VISIBLE, isVisible);
330 else if(grabHandle.actor)
332 grabHandle.actor.Unparent();
335 // Show or hide the selection handles/highlight
336 HandleImpl& primary = mHandle[LEFT_SELECTION_HANDLE];
337 HandleImpl& secondary = mHandle[RIGHT_SELECTION_HANDLE];
338 bool newPrimaryHandlePosition = false;
339 bool newSecondaryHandlePosition = false;
341 primary.horizontallyVisible = ((mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000) &&
342 (primary.position.x > -Math::MACHINE_EPSILON_1000));
343 primary.verticallyVisible = ((fabsf(mControlSize.height - primary.lineHeight) - primary.position.y > -Math::MACHINE_EPSILON_1000) &&
344 (primary.position.y + (primary.verticallyFlipped ? 0.f : primary.lineHeight) > -Math::MACHINE_EPSILON_1000));
345 secondary.horizontallyVisible = ((mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000) &&
346 (secondary.position.x > -Math::MACHINE_EPSILON_1000));
347 secondary.verticallyVisible = ((fabsf(mControlSize.height - secondary.lineHeight) - secondary.position.y > -Math::MACHINE_EPSILON_1000) &&
348 (secondary.position.y + (secondary.verticallyFlipped ? 0.f : secondary.lineHeight) > -Math::MACHINE_EPSILON_1000));
350 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
351 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
353 if(primary.active || secondary.active)
355 if(primaryVisible || secondaryVisible)
357 CreateSelectionHandles();
361 SetSelectionHandlePosition(LEFT_SELECTION_HANDLE);
363 // Sets the primary handle image according if it's pressed, flipped, etc.
364 SetHandleImage(LEFT_SELECTION_HANDLE);
366 SetSelectionHandleMarkerSize(primary);
368 newPrimaryHandlePosition = true;
373 SetSelectionHandlePosition(RIGHT_SELECTION_HANDLE);
375 // Sets the secondary handle image according if it's pressed, flipped, etc.
376 SetHandleImage(RIGHT_SELECTION_HANDLE);
378 SetSelectionHandleMarkerSize(secondary);
380 newSecondaryHandlePosition = true;
386 primary.actor.SetProperty(Actor::Property::VISIBLE, primaryVisible);
390 secondary.actor.SetProperty(Actor::Property::VISIBLE, secondaryVisible);
397 primary.actor.Unparent();
401 secondary.actor.Unparent();
405 if(mIsHighlightBoxActive)
414 mHighlightActor.Unparent();
418 if(newGrabHandlePosition ||
419 newPrimaryHandlePosition ||
420 newSecondaryHandlePosition)
422 // Setup property notifications to find whether the handles leave the boundaries of the current display.
423 SetupActiveLayerPropertyNotifications();
426 if(mActiveCopyPastePopup &&
427 (primaryVisible || secondaryVisible))
430 mPopupSetNewPosition = true;
434 if(mCopyPastePopup.actor)
436 mCopyPastePopup.actor.HidePopup();
437 mPopupSetNewPosition = true;
442 void UpdatePositions(const Vector2& scrollOffset)
444 mCursor[PRIMARY_CURSOR].position += scrollOffset;
445 mCursor[SECONDARY_CURSOR].position += scrollOffset;
446 mHandle[GRAB_HANDLE].position += scrollOffset;
447 mHandle[LEFT_SELECTION_HANDLE].position += scrollOffset;
448 mHandle[RIGHT_SELECTION_HANDLE].position += scrollOffset;
449 mHighlightPosition += scrollOffset;
454 if(!mCopyPastePopup.actor)
459 if(!mCopyPastePopup.actor.GetParent())
461 mActiveLayer.Add(mCopyPastePopup.actor);
464 mCopyPastePopup.actor.RaiseAbove(mActiveLayer);
465 mCopyPastePopup.actor.ShowPopup();
468 float CalculateVerticalPopUpPosition(float halfHeight, bool preferBelow)
470 float yPosition = 0.f;
472 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
473 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
474 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
476 if(primaryHandle.active || secondaryHandle.active)
478 // The origin of the decorator's coordinate system in world coords.
479 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION) - mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * ACTIVE_LAYER_ANCHOR_POINT;
483 // Find out if there is enough space for the popup at the bottom.
484 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
485 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
487 float maxY = std::max(primaryBelowY, secondaryBelowY);
489 yPosition = halfHeight + maxY;
491 if(originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w)
493 // Does not fit below.
495 // Try to fit first below the non active handle. Otherwise above the active handle.
496 if(RIGHT_SELECTION_HANDLE == mHandleReleased)
498 if(primaryBelowY < secondaryBelowY)
500 yPosition = halfHeight + primaryBelowY;
504 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
507 else if(LEFT_SELECTION_HANDLE == mHandleReleased)
509 if(secondaryBelowY < primaryBelowY)
511 yPosition = halfHeight + secondaryBelowY;
515 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
519 // Check the handle is whithin the decoration box.
520 if(originWorldCoords.y + yPosition < mBoundingBox.y)
522 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
525 if(originWorldCoords.y + yPosition > mBoundingBox.w)
527 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
533 // Find out if there is enough space for the popup at the top.
534 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
535 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
537 float minY = std::min(primaryTopY, secondaryTopY);
539 yPosition = -halfHeight + minY;
541 } // ( primaryHandle.active || secondaryHandle.active )
542 else if(grabHandle.active)
546 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
550 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
557 void ConstrainPopupPosition(const Vector3& popupHalfSize)
559 // Check if the popup is within the boundaries of the decoration box.
561 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
563 // The origin of the decorator's coordinate system in world coords.
564 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION) - mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * ACTIVE_LAYER_ANCHOR_POINT;
566 // The popup's position in world coords.
567 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
569 if(popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x)
571 mCopyPastePopup.position.x += mBoundingBox.x - (popupPositionWorldCoords.x - popupHalfSize.width);
573 else if(popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z)
575 mCopyPastePopup.position.x += mBoundingBox.z - (popupPositionWorldCoords.x + popupHalfSize.width);
578 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
579 if(popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y)
581 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition(popupHalfSize.height, true); // true -> prefer to set the popup's position below.
585 void SetPopupPosition(Actor actor)
587 if(!mActiveCopyPastePopup)
592 // Retrieves the popup's size after relayout.
593 const Vector3 popupSize(mCopyPastePopup.actor.GetRelayoutSize(Dimension::WIDTH), mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT), 0.0f);
594 const Vector3 popupHalfSize = popupSize * 0.5f;
596 if(mPopupSetNewPosition)
598 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
599 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
600 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
602 if(primaryHandle.active || secondaryHandle.active)
604 const float minHandleXPosition = std::min(primaryHandle.position.x, secondaryHandle.position.x);
605 const float maxHandleXPosition = std::max(primaryHandle.position.x, secondaryHandle.position.x);
607 mCopyPastePopup.position.x = minHandleXPosition + ((maxHandleXPosition - minHandleXPosition) * 0.5f);
609 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - (primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING);
610 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - (secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING);
612 mCopyPastePopup.position.y = std::min(primaryY, secondaryY);
614 else if(grabHandle.active)
616 mCopyPastePopup.position.x = grabHandle.position.x;
618 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - (grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING);
620 } // mPopupSetNewPosition
622 // It may change the popup's position to fit within the decoration box.
623 ConstrainPopupPosition(popupHalfSize);
625 SetUpPopupPositionNotifications(popupHalfSize);
627 // Prevent pixel mis-alignment by rounding down.
628 mCopyPastePopup.position.x = floorf(mCopyPastePopup.position.x);
629 mCopyPastePopup.position.y = floorf(mCopyPastePopup.position.y);
631 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION, mCopyPastePopup.position);
632 mPopupSetNewPosition = false;
635 void CreateCursor(Control& cursor, const Vector4& color)
637 cursor = Control::New();
638 cursor.SetBackgroundColor(color);
639 cursor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
640 cursor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
641 cursor.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIDDEN, true);
644 // Add or Remove cursor(s) from parent
647 if(mActiveCursor == ACTIVE_CURSOR_NONE)
651 mPrimaryCursor.Unparent();
655 mSecondaryCursor.Unparent();
660 // Create Primary and or Secondary Cursor(s) if active and add to parent
661 if(mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
662 mActiveCursor == ACTIVE_CURSOR_BOTH)
666 CreateCursor(mPrimaryCursor, mCursor[PRIMARY_CURSOR].color);
667 #ifdef DECORATOR_DEBUG
668 mPrimaryCursor.SetProperty(Dali::Actor::Property::NAME, "PrimaryCursorActor");
672 if(!mPrimaryCursor.GetParent())
674 mCursorLayer.Add(mPrimaryCursor);
678 if(mActiveCursor == ACTIVE_CURSOR_BOTH)
680 if(!mSecondaryCursor)
682 CreateCursor(mSecondaryCursor, mCursor[SECONDARY_CURSOR].color);
683 #ifdef DECORATOR_DEBUG
684 mSecondaryCursor.SetProperty(Dali::Actor::Property::NAME, "SecondaryCursorActor");
688 if(!mSecondaryCursor.GetParent())
690 mCursorLayer.Add(mSecondaryCursor);
697 mSecondaryCursor.Unparent();
703 bool OnCursorBlinkTimerTick()
705 if(!mDelayCursorBlink)
710 mPrimaryCursor.SetProperty(Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus);
714 mSecondaryCursor.SetProperty(Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus);
717 mCursorBlinkStatus = !mCursorBlinkStatus;
722 mDelayCursorBlink = false;
730 // Will consume tap gestures on handles.
731 mTapDetector = TapGestureDetector::New();
733 // Will consume double tap gestures on handles.
734 mTapDetector.SetMaximumTapsRequired(2u);
736 // Will consume long press gestures on handles.
737 mLongPressDetector = LongPressGestureDetector::New();
739 // Detects pan gestures on handles.
740 mPanDetector = PanGestureDetector::New();
741 mPanDetector.DetectedSignal().Connect(this, &Decorator::Impl::OnPan);
744 void CreateLayer(Actor& layer, DecorationType type)
748 layer = Actor::New();
749 #ifdef DECORATOR_DEBUG
750 if(type == DecorationType::ACTIVE_LAYER)
752 layer.SetProperty(Actor::Property::NAME, "ActiveLayerActor");
754 else if(type == DecorationType::CURSOR_LAYER)
756 layer.SetProperty(Actor::Property::NAME, "CursorLayerActor");
759 bool needsClipping = false;
760 if(type == DecorationType::CURSOR_LAYER)
762 needsClipping = true;
765 layer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
766 layer.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
768 mController.AddDecoration(layer, type, needsClipping);
774 void SetSelectionHandleMarkerSize(HandleImpl& handle)
776 if(handle.markerActor)
778 handle.markerActor.SetProperty(Actor::Property::SIZE, Vector2(0, handle.lineHeight));
782 void CreateGrabHandle()
784 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
785 if(!grabHandle.actor)
787 if(mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED].size())
789 grabHandle.actor = ImageView::New(mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED]);
790 GetImpl(grabHandle.actor).SetDepthIndex(DepthIndex::DECORATION);
791 grabHandle.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
792 grabHandle.actor.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
794 // Area that Grab handle responds to, larger than actual handle so easier to move
795 #ifdef DECORATOR_DEBUG
796 grabHandle.actor.SetProperty(Dali::Actor::Property::NAME, "GrabHandleActor");
797 if(Dali::Internal::gLogFilter->IsEnabledFor(Debug::Verbose))
799 grabHandle.grabArea = Control::New();
800 Toolkit::Control control = Toolkit::Control::DownCast(grabHandle.grabArea);
801 control.SetBackgroundColor(Vector4(1.0f, 1.0f, 1.0f, 0.5f));
802 grabHandle.grabArea.SetProperty(Dali::Actor::Property::NAME, "GrabArea");
806 grabHandle.grabArea = Actor::New();
807 grabHandle.grabArea.SetProperty(Dali::Actor::Property::NAME, "GrabArea");
810 grabHandle.grabArea = Actor::New();
813 grabHandle.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
814 grabHandle.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
815 grabHandle.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
816 grabHandle.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_GRAB_HANDLE_RELATIVE_SIZE);
817 grabHandle.actor.Add(grabHandle.grabArea);
818 grabHandle.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
820 grabHandle.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnGrabHandleTouched);
822 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
823 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
824 mTapDetector.Attach(grabHandle.actor);
825 mLongPressDetector.Attach(grabHandle.actor);
827 // The grab handle's area is attached to the pan detector.
828 // The OnPan() method is connected to the signals emitted by the pan detector.
829 mPanDetector.Attach(grabHandle.grabArea);
831 mActiveLayer.Add(grabHandle.actor);
835 if(grabHandle.actor && !grabHandle.actor.GetParent())
837 mActiveLayer.Add(grabHandle.actor);
841 void CreateHandleMarker(HandleImpl& handle, const std::string& image, HandleType handleType)
845 handle.markerActor = ImageView::New(image);
846 handle.markerActor.SetProperty(Actor::Property::COLOR, mHandleColor);
847 handle.actor.Add(handle.markerActor);
849 handle.markerActor.SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
851 if(LEFT_SELECTION_HANDLE == handleType)
853 handle.markerActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT);
854 handle.markerActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_RIGHT);
856 else if(RIGHT_SELECTION_HANDLE == handleType)
858 handle.markerActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT);
859 handle.markerActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
864 void CreateSelectionHandles()
866 HandleImpl& primary = mHandle[LEFT_SELECTION_HANDLE];
869 if(mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size())
871 primary.actor = ImageView::New(mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]);
872 #ifdef DECORATOR_DEBUG
873 primary.actor.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleOne");
875 primary.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
876 primary.actor.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
877 GetImpl(primary.actor).SetDepthIndex(DepthIndex::DECORATION);
878 primary.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
880 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
881 #ifdef DECORATOR_DEBUG
882 primary.grabArea.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleOneGrabArea");
884 primary.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
885 primary.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
886 primary.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
887 primary.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE);
889 primary.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnHandleOneTouched);
891 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
892 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
893 mTapDetector.Attach(primary.actor);
894 mLongPressDetector.Attach(primary.actor);
896 // The handle's area is attached to the pan detector.
897 // The OnPan() method is connected to the signals emitted by the pan detector.
898 mPanDetector.Attach(primary.grabArea);
900 primary.actor.Add(primary.grabArea);
902 CreateHandleMarker(primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE);
906 if(primary.actor && !primary.actor.GetParent())
908 mActiveLayer.Add(primary.actor);
911 HandleImpl& secondary = mHandle[RIGHT_SELECTION_HANDLE];
914 if(mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size())
916 secondary.actor = ImageView::New(mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]);
917 #ifdef DECORATOR_DEBUG
918 secondary.actor.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleTwo");
920 secondary.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
921 secondary.actor.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
922 GetImpl(secondary.actor).SetDepthIndex(DepthIndex::DECORATION);
923 secondary.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
925 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
926 #ifdef DECORATOR_DEBUG
927 secondary.grabArea.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleTwoGrabArea");
929 secondary.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
930 secondary.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
931 secondary.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
932 secondary.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE);
934 secondary.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnHandleTwoTouched);
936 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
937 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
938 mTapDetector.Attach(secondary.actor);
939 mLongPressDetector.Attach(secondary.actor);
941 // The handle's area is attached to the pan detector.
942 // The OnPan() method is connected to the signals emitted by the pan detector.
943 mPanDetector.Attach(secondary.grabArea);
945 secondary.actor.Add(secondary.grabArea);
947 CreateHandleMarker(secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE);
951 if(secondary.actor && !secondary.actor.GetParent())
953 mActiveLayer.Add(secondary.actor);
957 void CreateSelectionPopup()
959 if(!mCopyPastePopup.actor)
961 mCopyPastePopup.actor = TextSelectionPopup::New(&mTextSelectionPopupCallbackInterface);
962 #ifdef DECORATOR_DEBUG
963 mCopyPastePopup.actor.SetProperty(Dali::Actor::Property::NAME, "mCopyPastePopup");
965 mCopyPastePopup.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
966 mCopyPastePopup.actor.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
967 mCopyPastePopup.actor.OnRelayoutSignal().Connect(this, &Decorator::Impl::SetPopupPosition); // Position popup after size negotiation
971 void CalculateHandleWorldCoordinates(HandleImpl& handle, Vector2& position)
973 // Gets the world position of the active layer. The active layer is where the handles are added.
974 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION);
976 // The grab handle position in world coords.
977 // The active layer's world position is the center of the active layer. The origin of the
978 // coord system of the handles is the top left of the active layer.
979 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + (mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f);
980 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + (mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f);
983 void SetGrabHandlePosition()
985 // Reference to the grab handle.
986 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
988 // Transforms the handle position into world coordinates.
989 // @note This is not the same value as grabHandle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
990 // as it's transforming the handle's position set by the text-controller and not
991 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
992 // retrieves the position of the center of the actor but the handle's position set
993 // by the text controller is not the center of the actor.
994 Vector2 grabHandleWorldPosition;
995 CalculateHandleWorldCoordinates(grabHandle, grabHandleWorldPosition);
997 // Check if the grab handle exceeds the boundaries of the decoration box.
998 // At the moment only the height is checked for the grab handle.
1000 grabHandle.verticallyFlipped = (grabHandle.verticallyFlippedPreferred &&
1001 ((grabHandleWorldPosition.y - grabHandle.size.height) > mBoundingBox.y)) ||
1002 (grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w);
1004 // The grab handle 'y' position in local coords.
1005 // If the grab handle exceeds the bottom of the decoration box,
1006 // set the 'y' position to the top of the line.
1007 // The SetGrabHandleImage() method will change the orientation.
1008 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
1010 ApplyDisplacement(grabHandle, yLocalPosition);
1013 void SetSelectionHandlePosition(HandleType type)
1015 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
1017 // Reference to the selection handle.
1018 HandleImpl& handle = mHandle[type];
1020 // Transforms the handle position into world coordinates.
1021 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1022 // as it's transforming the handle's position set by the text-controller and not
1023 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1024 // retrieves the position of the center of the actor but the handle's position set
1025 // by the text controller is not the center of the actor.
1026 Vector2 handleWorldPosition;
1027 CalculateHandleWorldCoordinates(handle, handleWorldPosition);
1029 // Whether to flip the handle (horizontally).
1030 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1032 // Whether to flip the handles if they are crossed.
1033 bool crossFlip = false;
1034 if(mFlipSelectionHandlesOnCross || !mIsHandlePanning)
1036 crossFlip = mIsHandleCurrentlyCrossed;
1039 // Whether the handle was crossed before start the panning.
1040 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1042 // Does not flip if both conditions are true (double flip)
1043 flipHandle = flipHandle != (crossFlip || isHandlePreviouslyCrossed);
1045 // Will flip the handles vertically if the user prefers it.
1046 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1048 if(crossFlip || isHandlePreviouslyCrossed)
1052 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1056 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1060 // Check if the selection handle exceeds the boundaries of the decoration box.
1061 const bool exceedsLeftEdge = (isPrimaryHandle ? !flipHandle : flipHandle) && (handleWorldPosition.x - handle.size.width < mBoundingBox.x);
1062 const bool exceedsRightEdge = (isPrimaryHandle ? flipHandle : !flipHandle) && (handleWorldPosition.x + handle.size.width > mBoundingBox.z);
1064 // Does not flip if both conditions are true (double flip)
1065 flipHandle = flipHandle != (exceedsLeftEdge || exceedsRightEdge);
1069 if(handle.actor && !handle.horizontallyFlipped)
1071 // Change the anchor point to flip the image.
1072 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT);
1074 handle.horizontallyFlipped = true;
1079 if(handle.actor && handle.horizontallyFlipped)
1081 // Reset the anchor point.
1082 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT);
1084 handle.horizontallyFlipped = false;
1088 // Whether to flip the handle vertically.
1089 handle.verticallyFlipped = (verticallyFlippedPreferred &&
1090 ((handleWorldPosition.y - handle.size.height) > mBoundingBox.y)) ||
1091 (handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w);
1093 // The primary selection handle 'y' position in local coords.
1094 // If the handle exceeds the bottom of the decoration box,
1095 // set the 'y' position to the top of the line.
1096 // The SetHandleImage() method will change the orientation.
1097 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1099 ApplyDisplacement(handle, yLocalPosition);
1102 void ApplyDisplacement(HandleImpl& handle, float yLocalPosition)
1106 float adjustedDisplacementX = 0.0f;
1107 float adjustedDisplacementY = 0.0f;
1108 if(mSmoothHandlePanEnabled)
1110 adjustedDisplacementX = CalculateAdjustedDisplacement(handle.position.x, handle.grabDisplacementX, mControlSize.x);
1111 adjustedDisplacementY = CalculateAdjustedDisplacement(handle.position.y, handle.grabDisplacementY, (mControlSize.y - handle.lineHeight));
1113 handle.actor.SetProperty(Actor::Property::POSITION,
1114 Vector2(handle.position.x + floor(0.5f * mCursorWidth) + adjustedDisplacementX,
1115 yLocalPosition + adjustedDisplacementY));
1119 float CalculateAdjustedDisplacement(float position, float displacement, float edge)
1121 //Apply the displacement (on the X-axis & the Y-axis)
1122 //as long as it does not exceed the control's edge.
1123 float adjustedDisplacement = 0.0f;
1124 if(position + displacement < 0.0f)
1126 // -position to cancel it out and relocate to 0.
1127 adjustedDisplacement = -position;
1129 else if(position + displacement > edge)
1131 // move in a displacement which is sufficient to reach the edge.
1132 adjustedDisplacement = edge - position;
1136 // move normally in the displacement.
1137 adjustedDisplacement = displacement;
1139 return adjustedDisplacement;
1142 void SetHandleImage(HandleType type)
1144 HandleImpl& handle = mHandle[type];
1146 HandleType markerType = HANDLE_TYPE_COUNT;
1147 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1148 if(LEFT_SELECTION_HANDLE == type)
1150 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1151 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1153 else if(RIGHT_SELECTION_HANDLE == type)
1155 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1156 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1159 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1162 const HandleImageType imageType = (handle.pressed ? (mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1164 handle.actor.SetImage(mHandleImages[type][imageType]);
1167 if(HANDLE_TYPE_COUNT != markerType)
1169 if(handle.markerActor)
1171 const HandleImageType markerImageType = (handle.pressed ? (mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1172 handle.markerActor.SetImage(mHandleImages[markerType][markerImageType]);
1176 // Whether to flip the handle vertically.
1179 handle.actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS));
1183 void CreateHighlight()
1185 if(!mHighlightActor)
1187 mHighlightActor = Actor::New();
1189 mHighlightActor.SetProperty(Dali::Actor::Property::NAME, "HighlightActor");
1190 mHighlightActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1191 mHighlightActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1192 mHighlightActor.SetProperty(Actor::Property::COLOR, mHighlightColor);
1193 mHighlightActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
1196 // Add the highlight box telling the controller it needs clipping.
1197 mController.AddDecoration(mHighlightActor, DecorationType::NONE_LAYER, true);
1200 void UpdateHighlight()
1204 // Sets the position of the highlight actor inside the decorator.
1205 mHighlightActor.SetProperty(Actor::Property::POSITION, Vector2(mHighlightPosition.x + mHighlightOutlineOffset, mHighlightPosition.y + mHighlightOutlineOffset));
1207 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1208 if(0u != numberOfQuads)
1210 // Set the size of the highlighted text to the actor.
1211 mHighlightActor.SetProperty(Actor::Property::SIZE, mHighlightSize);
1213 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1214 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1215 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1217 Vector<Vector2> vertices;
1218 Vector<unsigned short> indices;
1220 vertices.Reserve(4u * numberOfQuads);
1221 indices.Reserve(6u * numberOfQuads);
1223 // Index to the vertex.
1224 unsigned int v = 0u;
1226 // Traverse all quads.
1227 for(Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1228 endIt = mHighlightQuadList.End();
1232 const Vector4& quad = *it;
1237 vertex.x = quad.x - offsetX;
1238 vertex.y = quad.y - offsetY;
1239 vertices.PushBack(vertex);
1242 vertex.x = quad.z - offsetX;
1243 vertex.y = quad.y - offsetY;
1244 vertices.PushBack(vertex);
1246 // bottom-left (v+2)
1247 vertex.x = quad.x - offsetX;
1248 vertex.y = quad.w - offsetY;
1249 vertices.PushBack(vertex);
1251 // bottom-right (v+3)
1252 vertex.x = quad.z - offsetX;
1253 vertex.y = quad.w - offsetY;
1254 vertices.PushBack(vertex);
1256 // triangle A (3, 1, 0)
1257 indices.PushBack(v + 3);
1258 indices.PushBack(v + 1);
1259 indices.PushBack(v);
1261 // triangle B (0, 2, 3)
1262 indices.PushBack(v);
1263 indices.PushBack(v + 2);
1264 indices.PushBack(v + 3);
1269 mQuadVertices = VertexBuffer::New(mQuadVertexFormat);
1272 mQuadVertices.SetData(&vertices[0], vertices.Size());
1276 mQuadGeometry = Geometry::New();
1277 mQuadGeometry.AddVertexBuffer(mQuadVertices);
1279 mQuadGeometry.SetIndexBuffer(&indices[0], indices.Size());
1281 if(!mHighlightRenderer)
1283 mHighlightRenderer = Dali::Renderer::New(mQuadGeometry, mHighlightShader);
1284 mHighlightActor.AddRenderer(mHighlightRenderer);
1288 mHighlightQuadList.Clear();
1290 if(mHighlightRenderer)
1292 mHighlightRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mTextDepth - 2); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1297 void DoPan(HandleImpl& handle, HandleType type, const PanGesture& gesture)
1299 GestureState state = gesture.GetState();
1300 if(GestureState::STARTED == state)
1302 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1304 handle.globalPosition.x = handle.position.x;
1305 handle.globalPosition.y = handle.position.y;
1308 const Vector2& displacement = gesture.GetDisplacement();
1309 handle.grabDisplacementX += displacement.x;
1310 handle.grabDisplacementY += (handle.verticallyFlipped ? -displacement.y : displacement.y);
1312 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1313 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1314 const float yVerticallyFlippedCorrected = y - (handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f);
1316 if((GestureState::STARTED == state) ||
1317 (GestureState::CONTINUING == state))
1320 mController.GetTargetSize(targetSize);
1322 if(mHorizontalScrollingEnabled &&
1323 (x < mScrollThreshold))
1325 mScrollDirection = SCROLL_RIGHT;
1326 mHandleScrolling = type;
1329 else if(mHorizontalScrollingEnabled &&
1330 (x > targetSize.width - mScrollThreshold))
1332 mScrollDirection = SCROLL_LEFT;
1333 mHandleScrolling = type;
1336 else if(mVerticalScrollingEnabled &&
1337 (yVerticallyFlippedCorrected < mScrollThreshold))
1339 mScrollDirection = SCROLL_TOP;
1340 mHandleScrolling = type;
1343 else if(mVerticalScrollingEnabled &&
1344 (yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold))
1346 mScrollDirection = SCROLL_BOTTOM;
1347 mHandleScrolling = type;
1352 mHandleScrolling = HANDLE_TYPE_COUNT;
1354 mController.DecorationEvent(type, HANDLE_PRESSED, x, y);
1357 mIsHandlePanning = true;
1359 else if((GestureState::FINISHED == state) ||
1360 (GestureState::CANCELLED == state))
1363 (mScrollTimer.IsRunning() || mNotifyEndOfScroll))
1365 mNotifyEndOfScroll = false;
1366 mHandleScrolling = HANDLE_TYPE_COUNT;
1368 mController.DecorationEvent(type, HANDLE_STOP_SCROLLING, x, y);
1372 mController.DecorationEvent(type, HANDLE_RELEASED, x, y);
1377 handle.actor.SetImage(mHandleImages[type][HANDLE_IMAGE_RELEASED]);
1379 handle.pressed = false;
1381 mIsHandlePanning = false;
1385 void OnPan(Actor actor, const PanGesture& gesture)
1387 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1388 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1389 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1391 if(actor == grabHandle.grabArea)
1393 DoPan(grabHandle, GRAB_HANDLE, gesture);
1395 else if(actor == primarySelectionHandle.grabArea)
1397 DoPan(primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture);
1399 else if(actor == secondarySelectionHandle.grabArea)
1401 DoPan(secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture);
1405 bool OnGrabHandleTouched(Actor actor, const TouchEvent& touch)
1407 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1409 // Switch between pressed/release grab-handle images
1410 if(touch.GetPointCount() > 0 &&
1413 const PointState::Type state = touch.GetState(0);
1415 if(PointState::DOWN == state)
1417 grabHandle.pressed = true;
1419 else if((PointState::UP == state) ||
1420 (PointState::INTERRUPTED == state))
1422 grabHandle.pressed = false;
1425 SetHandleImage(GRAB_HANDLE);
1431 bool OnHandleOneTouched(Actor actor, const TouchEvent& touch)
1433 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1435 // Switch between pressed/release selection handle images
1436 if(touch.GetPointCount() > 0 &&
1437 primarySelectionHandle.actor)
1439 const PointState::Type state = touch.GetState(0);
1441 if(PointState::DOWN == state)
1443 primarySelectionHandle.pressed = true;
1444 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1446 else if((PointState::UP == state) ||
1447 (PointState::INTERRUPTED == state))
1449 primarySelectionHandle.pressed = false;
1450 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1451 mIsHandlePanning = false;
1452 mHandleReleased = LEFT_SELECTION_HANDLE;
1455 SetHandleImage(LEFT_SELECTION_HANDLE);
1461 bool OnHandleTwoTouched(Actor actor, const TouchEvent& touch)
1463 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1465 // Switch between pressed/release selection handle images
1466 if(touch.GetPointCount() > 0 &&
1467 secondarySelectionHandle.actor)
1469 const PointState::Type state = touch.GetState(0);
1471 if(PointState::DOWN == state)
1473 secondarySelectionHandle.pressed = true;
1474 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1476 else if((PointState::UP == state) ||
1477 (PointState::INTERRUPTED == state))
1479 secondarySelectionHandle.pressed = false;
1480 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1481 mIsHandlePanning = false;
1482 mHandleReleased = RIGHT_SELECTION_HANDLE;
1485 SetHandleImage(RIGHT_SELECTION_HANDLE);
1491 void HandleResetPosition(PropertyNotification& source)
1493 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1495 if(grabHandle.active)
1497 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1498 SetGrabHandlePosition();
1500 // Sets the grab handle image according if it's pressed, flipped, etc.
1501 SetHandleImage(GRAB_HANDLE);
1505 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1506 SetSelectionHandlePosition(LEFT_SELECTION_HANDLE);
1508 // Sets the primary handle image according if it's pressed, flipped, etc.
1509 SetHandleImage(LEFT_SELECTION_HANDLE);
1511 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1512 SetSelectionHandlePosition(RIGHT_SELECTION_HANDLE);
1514 // Sets the secondary handle image according if it's pressed, flipped, etc.
1515 SetHandleImage(RIGHT_SELECTION_HANDLE);
1519 void SetupActiveLayerPropertyNotifications()
1526 // Vertical notifications.
1528 // Disconnect any previous connected callback.
1529 if(mHandleVerticalLessThanNotification)
1531 mHandleVerticalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1532 mActiveLayer.RemovePropertyNotification(mHandleVerticalLessThanNotification);
1535 if(mHandleVerticalGreaterThanNotification)
1537 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1538 mActiveLayer.RemovePropertyNotification(mHandleVerticalGreaterThanNotification);
1541 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1542 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1543 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1545 if(grabHandle.active)
1547 if(grabHandle.verticallyFlipped)
1549 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1550 mHandleVerticalGreaterThanNotification.Reset();
1552 // The vertical distance from the center of the active layer to the top edje of the display.
1553 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1555 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1556 LessThanCondition(mBoundingBox.y + topHeight));
1558 // Notifies the change from false to true and from true to false.
1559 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1561 // Connects the signals with the callbacks.
1562 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1566 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1567 mHandleVerticalLessThanNotification.Reset();
1569 // The vertical distance from the center of the active layer to the bottom edje of the display.
1570 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1572 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1573 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1575 // Notifies the change from false to true and from true to false.
1576 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1578 // Connects the signals with the callbacks.
1579 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1582 else // The selection handles are active
1584 if(primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped)
1586 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1587 mHandleVerticalGreaterThanNotification.Reset();
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 + std::max(-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 else if(!primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped)
1603 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1604 mHandleVerticalLessThanNotification.Reset();
1606 // The vertical distance from the center of the active layer to the bottom edje of the display.
1607 const float bottomHeight = -0.5f * mControlSize.height + std::max(primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1608 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height);
1610 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1611 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1613 // Notifies the change from false to true and from true to false.
1614 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1616 // Connects the signals with the callbacks.
1617 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1621 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1623 // The vertical distance from the center of the active layer to the top edje of the display.
1624 const float topHeight = 0.5f * mControlSize.height + (primaryHandle.verticallyFlipped ? -primaryHandle.position.y + primaryHandle.size.height : -secondaryHandle.position.y + secondaryHandle.size.height);
1626 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1627 LessThanCondition(mBoundingBox.y + topHeight));
1629 // Notifies the change from false to true and from true to false.
1630 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1632 // Connects the signals with the callbacks.
1633 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1635 // The vertical distance from the center of the active layer to the bottom edje of the display.
1636 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);
1638 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1639 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1641 // Notifies the change from false to true and from true to false.
1642 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1644 // Connects the signals with the callbacks.
1645 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1649 // Horizontal notifications.
1651 // Disconnect any previous connected callback.
1652 if(mHandleHorizontalLessThanNotification)
1654 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1655 mActiveLayer.RemovePropertyNotification(mHandleHorizontalLessThanNotification);
1658 if(mHandleHorizontalGreaterThanNotification)
1660 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1661 mActiveLayer.RemovePropertyNotification(mHandleHorizontalGreaterThanNotification);
1664 if(primaryHandle.active || secondaryHandle.active)
1666 // The horizontal distance from the center of the active layer to the left edje of the display.
1667 const float leftWidth = 0.5f * mControlSize.width + std::max(-primaryHandle.position.x + primaryHandle.size.width,
1668 -secondaryHandle.position.x + secondaryHandle.size.width);
1670 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1671 LessThanCondition(mBoundingBox.x + leftWidth));
1673 // Notifies the change from false to true and from true to false.
1674 mHandleHorizontalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1676 // Connects the signals with the callbacks.
1677 mHandleHorizontalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1679 // The horizontal distance from the center of the active layer to the right edje of the display.
1680 const float rightWidth = -0.5f * mControlSize.width + std::max(primaryHandle.position.x + primaryHandle.size.width,
1681 secondaryHandle.position.x + secondaryHandle.size.width);
1683 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1684 GreaterThanCondition(mBoundingBox.z - rightWidth));
1686 // Notifies the change from false to true and from true to false.
1687 mHandleHorizontalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1689 // Connects the signals with the callbacks.
1690 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1696 float AlternatePopUpPositionRelativeToCursor(bool topBottom)
1698 float alternativePosition = 0.0f;
1700 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1702 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1703 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1704 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1705 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1707 if(primaryHandle.active || secondaryHandle.active)
1709 float handleY = 0.f;
1710 float maxHandleHeight = 0.f;
1712 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1713 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1715 if(primaryVisible && secondaryVisible)
1717 handleY = std::max(primaryHandle.position.y, secondaryHandle.position.y);
1718 maxHandleHeight = std::max(primaryHandle.size.height, secondaryHandle.size.height);
1720 else if(primaryVisible && !secondaryVisible)
1722 handleY = primaryHandle.position.y;
1723 maxHandleHeight = primaryHandle.size.height;
1725 else if(!primaryVisible && secondaryVisible)
1727 handleY = secondaryHandle.position.y;
1728 maxHandleHeight = secondaryHandle.size.height;
1731 alternativePosition = handleY + (topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight);
1735 alternativePosition = cursor.position.y + (topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height);
1738 return alternativePosition;
1741 void PopUpLeavesTopBoundary(PropertyNotification& source)
1743 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1745 // Sets the position of the popup below.
1746 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, true)));
1749 void PopUpLeavesBottomBoundary(PropertyNotification& source)
1751 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1753 // Sets the position of the popup above.
1754 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, false)));
1757 void SetUpPopupPositionNotifications(const Vector3& popupHalfSize)
1759 // Disconnect any previous connected callback.
1760 if(mPopupTopExceedNotification)
1762 mPopupTopExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1763 mCopyPastePopup.actor.RemovePropertyNotification(mPopupTopExceedNotification);
1766 if(mPopupBottomExceedNotification)
1768 mPopupBottomExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1769 mCopyPastePopup.actor.RemovePropertyNotification(mPopupBottomExceedNotification);
1772 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1774 // Exceeding vertical boundary
1776 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1777 LessThanCondition(mBoundingBox.y + popupHalfSize.height));
1779 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1780 GreaterThanCondition(mBoundingBox.w - popupHalfSize.height));
1782 // Notifies the change from false to true and from true to false.
1783 mPopupTopExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1784 mPopupBottomExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1786 mPopupTopExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1787 mPopupBottomExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1790 void SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
1792 ImageDimensions dimensions = Dali::GetOriginalImageSize(imageFileName);
1794 HandleImpl& handle = mHandle[handleType];
1795 handle.size = Size(dimensions.GetWidth(), dimensions.GetHeight());
1797 mHandleImages[handleType][handleImageType] = imageFileName;
1800 void SetScrollThreshold(float threshold)
1802 mScrollThreshold = threshold;
1805 float GetScrollThreshold() const
1807 return mScrollThreshold;
1810 void SetScrollSpeed(float speed)
1812 mScrollSpeed = speed;
1813 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1816 float GetScrollSpeed() const
1818 return mScrollSpeed;
1821 void NotifyEndOfScroll()
1827 mNotifyEndOfScroll = true;
1832 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1834 * It only starts the timer if it's already created.
1836 void StartScrollTimer()
1840 mScrollTimer = Timer::New(SCROLL_TICK_INTERVAL);
1841 mScrollTimer.TickSignal().Connect(this, &Decorator::Impl::OnScrollTimerTick);
1844 if(!mScrollTimer.IsRunning())
1846 mScrollTimer.Start();
1851 * Stops the timer used to scroll the text.
1853 void StopScrollTimer()
1857 mScrollTimer.Stop();
1862 * Callback called by the timer used to scroll the text.
1864 * It calculates and sets a new scroll position.
1866 bool OnScrollTimerTick()
1868 if(HANDLE_TYPE_COUNT != mHandleScrolling)
1873 switch(mScrollDirection)
1877 x = mScrollDistance;
1882 x = -mScrollDistance;
1887 y = mScrollDistance;
1892 y = -mScrollDistance;
1899 mController.DecorationEvent(mHandleScrolling,
1908 ControllerInterface& mController;
1910 TapGestureDetector mTapDetector;
1911 PanGestureDetector mPanDetector;
1912 LongPressGestureDetector mLongPressDetector;
1914 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1915 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1917 Actor mActiveLayer; ///< Actor for active handles and alike that ensures they are above all else.
1918 Actor mCursorLayer; ///< Actor for cursor layer. this is for cursor clipping.
1919 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1920 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1921 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1922 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1923 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1924 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1925 Control mPrimaryCursor;
1926 Control mSecondaryCursor;
1928 Actor mHighlightActor; ///< Actor to display highlight
1929 Renderer mHighlightRenderer;
1930 Shader mHighlightShader; ///< Shader used for highlight
1931 Property::Map mQuadVertexFormat;
1932 PopupImpl mCopyPastePopup;
1933 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1934 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1936 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1937 Vector4 mHandleColor;
1939 CursorImpl mCursor[CURSOR_COUNT];
1940 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1942 VertexBuffer mQuadVertices;
1943 Geometry mQuadGeometry;
1944 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1946 Vector4 mBoundingBox; ///< The bounding box in world coords.
1947 Vector4 mHighlightColor; ///< Color of the highlight
1948 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1949 Size mHighlightSize; ///< The size of the highlighted text.
1950 Size mControlSize; ///< The control's size. Set by the Relayout.
1951 float mHighlightOutlineOffset; ///< The outline's offset.
1953 unsigned int mActiveCursor;
1954 unsigned int mCursorBlinkInterval;
1955 float mCursorBlinkDuration;
1956 float mCursorWidth; ///< The width of the cursors in pixels.
1957 HandleType mHandleScrolling; ///< The handle which is scrolling.
1958 HandleType mHandleReleased; ///< The last handle released.
1959 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1960 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1961 float mScrollSpeed; ///< The scroll speed in pixels per second.
1962 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1963 int mTextDepth; ///< The depth used to render the text.
1965 bool mActiveCopyPastePopup : 1;
1966 bool mPopupSetNewPosition : 1;
1967 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1968 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1969 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1970 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1971 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1972 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1973 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1974 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1975 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1976 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1977 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1978 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1979 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1980 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1981 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1982 bool mHidePrimaryCursorAndGrabHandle : 1; ///< Whether the primary cursor and grab are hidden always.
1985 DecoratorPtr Decorator::New(ControllerInterface& controller,
1986 TextSelectionPopupCallbackInterface& callbackInterface)
1988 return DecoratorPtr(new Decorator(controller,
1989 callbackInterface));
1992 void Decorator::SetBoundingBox(const Rect<int>& boundingBox)
1994 LocalToWorldCoordinatesBoundingBox(boundingBox, mImpl->mBoundingBox);
1997 void Decorator::GetBoundingBox(Rect<int>& boundingBox) const
1999 WorldToLocalCoordinatesBoundingBox(mImpl->mBoundingBox, boundingBox);
2002 void Decorator::Relayout(const Vector2& size, RelayoutContainer& container)
2004 mImpl->Relayout(size, container);
2007 void Decorator::UpdatePositions(const Vector2& scrollOffset)
2009 mImpl->UpdatePositions(scrollOffset);
2014 void Decorator::SetActiveCursor(ActiveCursor activeCursor)
2016 mImpl->mActiveCursor = activeCursor;
2019 unsigned int Decorator::GetActiveCursor() const
2021 return mImpl->mActiveCursor;
2024 void Decorator::SetPosition(Cursor cursor, float x, float y, float cursorHeight, float lineHeight)
2026 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2028 cursorImpl.position.x = x;
2029 cursorImpl.position.y = y;
2030 cursorImpl.cursorHeight = cursorHeight;
2031 cursorImpl.lineHeight = lineHeight;
2034 void Decorator::GetPosition(Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight) const
2036 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2038 x = cursorImpl.position.x;
2039 y = cursorImpl.position.y;
2040 cursorHeight = cursorImpl.cursorHeight;
2041 lineHeight = cursorImpl.lineHeight;
2044 const Vector2& Decorator::GetPosition(Cursor cursor) const
2046 return mImpl->mCursor[cursor].position;
2049 void Decorator::SetGlyphOffset(Cursor cursor, float glyphOffset)
2051 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2053 cursorImpl.glyphOffset = glyphOffset;
2056 const float Decorator::GetGlyphOffset(Cursor cursor) const
2058 return mImpl->mCursor[cursor].glyphOffset;
2061 void Decorator::SetCursorColor(Cursor cursor, const Dali::Vector4& color)
2063 mImpl->mCursor[cursor].color = color;
2065 if(cursor == PRIMARY_CURSOR && mImpl->mPrimaryCursor)
2067 mImpl->mPrimaryCursor.SetBackgroundColor(color);
2069 else if(cursor == SECONDARY_CURSOR && mImpl->mSecondaryCursor)
2071 mImpl->mSecondaryCursor.SetBackgroundColor(color);
2075 const Dali::Vector4& Decorator::GetColor(Cursor cursor) const
2077 return mImpl->mCursor[cursor].color;
2080 void Decorator::StartCursorBlink()
2082 if(!mImpl->mCursorBlinkTimer)
2084 mImpl->mCursorBlinkTimer = Timer::New(mImpl->mCursorBlinkInterval);
2085 mImpl->mCursorBlinkTimer.TickSignal().Connect(mImpl, &Decorator::Impl::OnCursorBlinkTimerTick);
2088 if(!mImpl->mCursorBlinkTimer.IsRunning())
2090 mImpl->mCursorBlinkTimer.Start();
2094 void Decorator::StopCursorBlink()
2096 if(mImpl->mCursorBlinkTimer)
2098 mImpl->mCursorBlinkTimer.Stop();
2101 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2104 void Decorator::DelayCursorBlink()
2106 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2107 mImpl->mDelayCursorBlink = true;
2110 void Decorator::SetCursorBlinkInterval(float seconds)
2112 mImpl->mCursorBlinkInterval = static_cast<unsigned int>(seconds * TO_MILLISECONDS); // Convert to milliseconds
2115 float Decorator::GetCursorBlinkInterval() const
2117 return static_cast<float>(mImpl->mCursorBlinkInterval) * TO_SECONDS;
2120 void Decorator::SetCursorBlinkDuration(float seconds)
2122 mImpl->mCursorBlinkDuration = seconds;
2125 float Decorator::GetCursorBlinkDuration() const
2127 return mImpl->mCursorBlinkDuration;
2130 void Decorator::SetCursorWidth(int width)
2132 mImpl->mCursorWidth = static_cast<float>(width);
2134 if(mImpl->mPrimaryCursorVisible && mImpl->mPrimaryCursor)
2136 mImpl->mPrimaryCursor.SetProperty(Actor::Property::SIZE, Size(mImpl->mCursorWidth, mImpl->mCursor[PRIMARY_CURSOR].cursorHeight));
2138 if(mImpl->mSecondaryCursorVisible && mImpl->mSecondaryCursor)
2140 mImpl->mSecondaryCursor.SetProperty(Actor::Property::SIZE, Size(mImpl->mCursorWidth, mImpl->mCursor[SECONDARY_CURSOR].cursorHeight));
2144 int Decorator::GetCursorWidth() const
2146 return static_cast<int>(mImpl->mCursorWidth);
2149 void Decorator::SetEditable(bool editable)
2151 mImpl->mHidePrimaryCursorAndGrabHandle = !editable;
2152 // If editable is false, all decorators should be disabled.
2155 if(IsHighlightActive())
2157 SetHighlightActive(false);
2159 if(IsHandleActive(LEFT_SELECTION_HANDLE))
2161 SetHandleActive(LEFT_SELECTION_HANDLE, false);
2163 if(IsHandleActive(RIGHT_SELECTION_HANDLE))
2165 SetHandleActive(RIGHT_SELECTION_HANDLE, false);
2169 SetPopupActive(false);
2175 void Decorator::SetHandleActive(HandleType handleType, bool active)
2177 mImpl->mHandle[handleType].active = active;
2181 if((LEFT_SELECTION_HANDLE == handleType) || (RIGHT_SELECTION_HANDLE == handleType))
2183 mImpl->mIsHandlePreviouslyCrossed = false;
2186 // TODO: this is a work-around.
2187 // The problem is the handle actor does not receive the touch event with the Interrupt
2188 // state when the power button is pressed and the application goes to background.
2189 mImpl->mHandle[handleType].pressed = false;
2190 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2191 ImageView imageView = mImpl->mHandle[handleType].actor;
2192 if(imageReleased && imageView)
2194 imageView.SetImage(mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED]);
2199 bool Decorator::IsHandleActive(HandleType handleType) const
2201 return mImpl->mHandle[handleType].active;
2204 void Decorator::SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
2206 mImpl->SetHandleImage(handleType, handleImageType, imageFileName);
2209 const std::string& Decorator::GetHandleImage(HandleType handleType, HandleImageType handleImageType) const
2211 return mImpl->mHandleImages[handleType][handleImageType];
2214 void Decorator::SetHandleColor(const Vector4& color)
2216 mImpl->mHandleColor = color;
2218 Impl::HandleImpl& grabHandle = mImpl->mHandle[GRAB_HANDLE];
2219 Impl::HandleImpl& primaryHandle = mImpl->mHandle[LEFT_SELECTION_HANDLE];
2220 Impl::HandleImpl& secondaryHandle = mImpl->mHandle[RIGHT_SELECTION_HANDLE];
2222 if(grabHandle.actor)
2224 grabHandle.actor.SetProperty(Actor::Property::COLOR, color);
2226 if(primaryHandle.actor)
2228 primaryHandle.actor.SetProperty(Actor::Property::COLOR, color);
2230 if(secondaryHandle.actor)
2232 secondaryHandle.actor.SetProperty(Actor::Property::COLOR, color);
2236 const Vector4& Decorator::GetHandleColor() const
2238 return mImpl->mHandleColor;
2241 void Decorator::SetPosition(HandleType handleType, float x, float y, float height)
2243 // Adjust handle's displacement
2244 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2246 handle.position.x = x;
2247 handle.position.y = y;
2248 handle.lineHeight = height;
2250 if(mImpl->mSmoothHandlePanEnabled)
2252 handle.grabDisplacementX = 0.f;
2253 handle.grabDisplacementY = 0.f;
2257 void Decorator::GetPosition(HandleType handleType, float& x, float& y, float& height) const
2259 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2261 x = handle.position.x;
2262 y = handle.position.y;
2263 height = handle.lineHeight;
2266 const Vector2& Decorator::GetPosition(HandleType handleType) const
2268 return mImpl->mHandle[handleType].position;
2271 void Decorator::FlipHandleVertically(HandleType handleType, bool flip)
2273 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2276 bool Decorator::IsHandleVerticallyFlipped(HandleType handleType) const
2278 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2281 void Decorator::FlipSelectionHandlesOnCrossEnabled(bool enable)
2283 mImpl->mFlipSelectionHandlesOnCross = enable;
2286 void Decorator::SetSelectionHandleFlipState(bool indicesSwapped, bool left, bool right)
2288 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2289 mImpl->mFlipLeftSelectionHandleDirection = left;
2290 mImpl->mFlipRightSelectionHandleDirection = right;
2293 void Decorator::AddHighlight(unsigned int index, const Vector4& quad)
2295 *(mImpl->mHighlightQuadList.Begin() + index) = quad;
2298 void Decorator::SetHighLightBox(const Vector2& position, const Size& size, float outlineOffset)
2300 mImpl->mHighlightPosition = position;
2301 mImpl->mHighlightSize = size;
2302 mImpl->mHighlightOutlineOffset = outlineOffset;
2305 void Decorator::ClearHighlights()
2307 mImpl->mHighlightQuadList.Clear();
2308 mImpl->mHighlightPosition = Vector2::ZERO;
2309 mImpl->mHighlightOutlineOffset = 0.f;
2312 void Decorator::ResizeHighlightQuads(unsigned int numberOfQuads)
2314 mImpl->mHighlightQuadList.Resize(numberOfQuads);
2317 void Decorator::SetHighlightColor(const Vector4& color)
2319 mImpl->mHighlightColor = color;
2321 if(mImpl->mHighlightActor)
2323 mImpl->mHighlightActor.SetProperty(Actor::Property::COLOR, color);
2327 const Vector4& Decorator::GetHighlightColor() const
2329 return mImpl->mHighlightColor;
2332 void Decorator::SetHighlightActive(bool active)
2334 mImpl->mIsHighlightBoxActive = active;
2337 bool Decorator::IsHighlightActive() const
2339 return mImpl->mIsHighlightBoxActive;
2342 bool Decorator::IsHighlightVisible() const
2344 return (mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent());
2347 void Decorator::SetTextDepth(int textDepth)
2349 mImpl->mTextDepth = textDepth;
2352 void Decorator::SetPopupActive(bool active)
2354 mImpl->mActiveCopyPastePopup = active;
2357 bool Decorator::IsPopupActive() const
2359 return mImpl->mActiveCopyPastePopup;
2362 void Decorator::SetEnabledPopupButtons(TextSelectionPopup::Buttons& enabledButtonsBitMask)
2364 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2365 mImpl->CreateSelectionPopup();
2366 mImpl->mCopyPastePopup.actor.EnableButtons(mImpl->mEnabledPopupButtons);
2369 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2371 return mImpl->mEnabledPopupButtons;
2374 void Decorator::SetSelectionPopupStyle(const Property::Map& options)
2376 mImpl->CreateSelectionPopup();
2377 mImpl->mCopyPastePopup.actor.SetProperties(options);
2380 void Decorator::GetSelectionPopupStyle(Property::Map& options)
2382 if(mImpl->mCopyPastePopup.actor)
2384 mImpl->mCopyPastePopup.actor.GetProperties(options);
2390 void Decorator::SetScrollThreshold(float threshold)
2392 mImpl->SetScrollThreshold(threshold);
2395 float Decorator::GetScrollThreshold() const
2397 return mImpl->GetScrollThreshold();
2400 void Decorator::SetScrollSpeed(float speed)
2402 mImpl->SetScrollSpeed(speed);
2405 float Decorator::GetScrollSpeed() const
2407 return mImpl->GetScrollSpeed();
2410 void Decorator::NotifyEndOfScroll()
2412 mImpl->NotifyEndOfScroll();
2415 void Decorator::SetHorizontalScrollEnabled(bool enable)
2417 mImpl->mHorizontalScrollingEnabled = enable;
2420 bool Decorator::IsHorizontalScrollEnabled() const
2422 return mImpl->mHorizontalScrollingEnabled;
2425 void Decorator::SetVerticalScrollEnabled(bool enable)
2427 mImpl->mVerticalScrollingEnabled = enable;
2430 bool Decorator::IsVerticalScrollEnabled() const
2432 return mImpl->mVerticalScrollingEnabled;
2435 void Decorator::SetSmoothHandlePanEnabled(bool enable)
2437 mImpl->mSmoothHandlePanEnabled = enable;
2440 bool Decorator::IsSmoothHandlePanEnabled() const
2442 return mImpl->mSmoothHandlePanEnabled;
2445 Decorator::~Decorator()
2450 Decorator::Decorator(ControllerInterface& controller,
2451 TextSelectionPopupCallbackInterface& callbackInterface)
2454 mImpl = new Decorator::Impl(controller, callbackInterface);
2459 } // namespace Toolkit