2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/decorator/text-decorator.h>
22 #include <dali/devel-api/adaptor-framework/image-loading.h>
23 #include <dali/devel-api/common/stage.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/actors/layer.h>
26 #include <dali/public-api/adaptor-framework/timer.h>
27 #include <dali/public-api/events/pan-gesture.h>
28 #include <dali/public-api/events/touch-event.h>
29 #include <dali/public-api/object/property-notification.h>
30 #include <dali/public-api/rendering/geometry.h>
31 #include <dali/public-api/rendering/renderer.h>
34 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
35 #include <dali-toolkit/devel-api/controls/control-devel.h>
36 #include <dali-toolkit/internal/controls/image-view/image-view-impl.h>
37 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
38 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
41 #define DECORATOR_DEBUG
51 #ifdef DECORATOR_DEBUG
52 Integration::Log::Filter* gLogFilter(Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR"));
55 } // namespace Internal
61 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE(1.25f, 1.5f, 1.0f);
62 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE(1.25f, 1.5f, 1.0f);
63 const Dali::Vector3 ACTIVE_LAYER_ANCHOR_POINT(0.5f, 0.5f, 0.5f);
65 const Dali::Vector4 LIGHT_BLUE(0.75f, 0.96f, 1.f, 1.f); // The text highlight color. TODO: due some problems, maybe with the blending function in the text clipping, the color is fully opaque.
67 const Dali::Vector4 HANDLE_COLOR(0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f);
69 const unsigned int CURSOR_BLINK_INTERVAL = 500u; ///< Cursor blink interval in milliseconds.
70 const float TO_MILLISECONDS = 1000.f; ///< Converts from seconds to milliseconds.
71 const float TO_SECONDS = 1.f / TO_MILLISECONDS; ///< Converts from milliseconds to seconds.
73 const unsigned int SCROLL_TICK_INTERVAL = 50u; ///< Scroll interval in milliseconds.
74 const float SCROLL_THRESHOLD = 10.f; ///< Threshold in pixels close to the edges of the decorator boundaries from where the scroll timer starts to emit signals.
75 const float SCROLL_SPEED = 300.f; ///< The scroll speed in pixels/second.
77 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; ///< Distance in pixels scrolled in one second.
79 const float CURSOR_WIDTH = 1.f; ///< The cursor's width in pixels.
81 const float POPUP_PADDING = 2.f; ///< Padding space between the highlight box and the text's popup.
83 typedef Dali::Vector<Dali::Vector4> QuadContainer;
86 * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
87 * @param[in] boundingRectangle local bounding
88 * @param[out] Vector4 World coordinate bounding Box.
90 void LocalToWorldCoordinatesBoundingBox(const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox)
92 // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
93 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
95 const float originX = boundingRectangle.x - 0.5f * stageSize.width;
96 const float originY = boundingRectangle.y - 0.5f * stageSize.height;
98 boundingBox = Dali::Vector4(originX,
100 originX + boundingRectangle.width,
101 originY + boundingRectangle.height);
104 void WorldToLocalCoordinatesBoundingBox(const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle)
106 // Convert to local coordinates and store as a Dali::Rect.
107 Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
109 boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
110 boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
111 boundingRectangle.width = boundingBox.z - boundingBox.x;
112 boundingRectangle.height = boundingBox.w - boundingBox.y;
115 } // end of namespace
123 struct Decorator::Impl : public ConnectionTracker
137 : color(Dali::Color::BLACK),
159 grabDisplacementX(0.f),
160 grabDisplacementY(0.f),
162 horizontallyVisible(false),
163 verticallyVisible(false),
165 verticallyFlippedPreferred(false),
166 horizontallyFlipped(false),
167 verticallyFlipped(false),
168 verticallyFlippedOnTouch(false)
174 ImageView markerActor;
177 Vector2 globalPosition;
179 float lineHeight; ///< Not the handle height
180 float grabDisplacementX;
181 float grabDisplacementY;
183 bool horizontallyVisible : 1;
184 bool verticallyVisible : 1;
186 bool verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
187 bool horizontallyFlipped : 1; ///< Whether the handle has been horizontally flipped.
188 bool verticallyFlipped : 1; ///< Whether the handle has been vertically flipped.
189 bool verticallyFlippedOnTouch : 1; ///< Whether the handle is vertically flipped on touch.
199 TextSelectionPopup actor;
203 Impl(ControllerInterface& controller,
204 TextSelectionPopupCallbackInterface& callbackInterface)
205 : mController(controller),
206 mEnabledPopupButtons(TextSelectionPopup::NONE),
207 mTextSelectionPopupCallbackInterface(callbackInterface),
208 mHandleColor(HANDLE_COLOR),
210 mHighlightColor(LIGHT_BLUE),
211 mHighlightPosition(Vector2::ZERO),
212 mHighlightSize(Vector2::ZERO),
213 mControlSize(Vector2::ZERO),
214 mHighlightOutlineOffset(0.f),
215 mActiveCursor(ACTIVE_CURSOR_NONE),
216 mCursorBlinkInterval(CURSOR_BLINK_INTERVAL),
217 mCursorBlinkDuration(0.0f),
218 mCursorWidth(CURSOR_WIDTH),
219 mHandleScrolling(HANDLE_TYPE_COUNT),
220 mHandleReleased(HANDLE_TYPE_COUNT),
221 mScrollDirection(SCROLL_NONE),
222 mScrollThreshold(SCROLL_THRESHOLD),
223 mScrollSpeed(SCROLL_SPEED),
224 mScrollDistance(SCROLL_DISTANCE),
226 mActiveCopyPastePopup(false),
227 mPopupSetNewPosition(true),
228 mCursorBlinkStatus(true),
229 mDelayCursorBlink(false),
230 mPrimaryCursorVisible(false),
231 mSecondaryCursorVisible(false),
232 mFlipSelectionHandlesOnCross(false),
233 mFlipLeftSelectionHandleDirection(false),
234 mFlipRightSelectionHandleDirection(false),
235 mIsHandlePanning(false),
236 mIsHandleCurrentlyCrossed(false),
237 mIsHandlePreviouslyCrossed(false),
238 mNotifyEndOfScroll(false),
239 mHorizontalScrollingEnabled(false),
240 mVerticalScrollingEnabled(false),
241 mSmoothHandlePanEnabled(false),
242 mIsHighlightBoxActive(false),
243 mHidePrimaryCursorAndGrabHandle(false)
245 mQuadVertexFormat["aPosition"] = Property::VECTOR2;
246 mHighlightShader = Shader::New(SHADER_TEXT_DECORATOR_SHADER_VERT, SHADER_TEXT_DECORATOR_SHADER_FRAG);
251 * Relayout of the decorations owned by the decorator.
252 * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
254 void Relayout(const Vector2& size)
258 // TODO - Remove this if nothing is active
259 CreateLayer(mActiveLayer, DecorationType::ACTIVE_LAYER);
260 CreateLayer(mCursorLayer, DecorationType::CURSOR_LAYER);
262 // Show or hide the cursors
267 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
268 mPrimaryCursorVisible = (!mHidePrimaryCursorAndGrabHandle) && ((mControlSize.width - (cursor.position.x + mCursorWidth) > -Math::MACHINE_EPSILON_1000) &&
269 (cursor.position.x > -Math::MACHINE_EPSILON_1000) &&
270 (mControlSize.height - cursor.position.y > -Math::MACHINE_EPSILON_1000) &&
271 (cursor.position.y + cursor.cursorHeight > -Math::MACHINE_EPSILON_1000));
272 if(mPrimaryCursorVisible)
274 mPrimaryCursor.SetProperty(Actor::Property::POSITION, Vector2(cursor.position.x, cursor.position.y));
275 mPrimaryCursor.SetProperty(Actor::Property::SIZE, Size(mCursorWidth, cursor.cursorHeight));
277 mPrimaryCursor.SetProperty(Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus);
281 const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
282 mSecondaryCursorVisible = ((mControlSize.width - (cursor.position.x + mCursorWidth) > -Math::MACHINE_EPSILON_1000) &&
283 (cursor.position.x > -Math::MACHINE_EPSILON_1000) &&
284 (mControlSize.height - cursor.position.y > -Math::MACHINE_EPSILON_1000) &&
285 (cursor.position.y + cursor.cursorHeight > -Math::MACHINE_EPSILON_1000));
286 if(mSecondaryCursorVisible)
288 mSecondaryCursor.SetProperty(Actor::Property::POSITION, Vector2(cursor.position.x, cursor.position.y));
289 mSecondaryCursor.SetProperty(Actor::Property::SIZE, Size(mCursorWidth, cursor.cursorHeight));
291 mSecondaryCursor.SetProperty(Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus);
294 // Show or hide the grab handle
295 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
296 bool newGrabHandlePosition = false;
297 grabHandle.horizontallyVisible = false;
298 grabHandle.verticallyVisible = false;
299 if(grabHandle.active)
301 grabHandle.horizontallyVisible = ((mControlSize.width - (grabHandle.position.x + floor(0.5f * mCursorWidth)) > -Math::MACHINE_EPSILON_1000) &&
302 (grabHandle.position.x > -Math::MACHINE_EPSILON_1000));
303 grabHandle.verticallyVisible = ((fabsf(mControlSize.height - grabHandle.lineHeight) - grabHandle.position.y > -Math::MACHINE_EPSILON_1000) &&
304 (grabHandle.position.y + grabHandle.lineHeight > -Math::MACHINE_EPSILON_1000));
306 const bool isVisible = grabHandle.horizontallyVisible && grabHandle.verticallyVisible && (!mHidePrimaryCursorAndGrabHandle);
311 // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
312 SetGrabHandlePosition();
314 // Sets the grab handle image according if it's pressed, flipped, etc.
315 SetHandleImage(GRAB_HANDLE);
317 newGrabHandlePosition = true;
322 grabHandle.actor.SetProperty(Actor::Property::VISIBLE, isVisible);
325 else if(grabHandle.actor)
327 grabHandle.actor.Unparent();
330 // Show or hide the selection handles/highlight
331 HandleImpl& primary = mHandle[LEFT_SELECTION_HANDLE];
332 HandleImpl& secondary = mHandle[RIGHT_SELECTION_HANDLE];
333 bool newPrimaryHandlePosition = false;
334 bool newSecondaryHandlePosition = false;
336 primary.horizontallyVisible = ((mControlSize.width - primary.position.x > -Math::MACHINE_EPSILON_1000) &&
337 (primary.position.x > -Math::MACHINE_EPSILON_1000));
338 primary.verticallyVisible = ((fabsf(mControlSize.height - primary.lineHeight) - primary.position.y > -Math::MACHINE_EPSILON_1000) &&
339 (primary.position.y + (primary.verticallyFlipped ? 0.f : primary.lineHeight) > -Math::MACHINE_EPSILON_1000));
340 secondary.horizontallyVisible = ((mControlSize.width - secondary.position.x > -Math::MACHINE_EPSILON_1000) &&
341 (secondary.position.x > -Math::MACHINE_EPSILON_1000));
342 secondary.verticallyVisible = ((fabsf(mControlSize.height - secondary.lineHeight) - secondary.position.y > -Math::MACHINE_EPSILON_1000) &&
343 (secondary.position.y + (secondary.verticallyFlipped ? 0.f : secondary.lineHeight) > -Math::MACHINE_EPSILON_1000));
345 const bool primaryVisible = primary.horizontallyVisible && primary.verticallyVisible;
346 const bool secondaryVisible = secondary.horizontallyVisible && secondary.verticallyVisible;
348 if(primary.active || secondary.active)
350 if(primaryVisible || secondaryVisible)
352 CreateSelectionHandles();
356 SetSelectionHandlePosition(LEFT_SELECTION_HANDLE);
358 // Sets the primary handle image according if it's pressed, flipped, etc.
359 SetHandleImage(LEFT_SELECTION_HANDLE);
361 SetSelectionHandleMarkerSize(primary);
363 newPrimaryHandlePosition = true;
368 SetSelectionHandlePosition(RIGHT_SELECTION_HANDLE);
370 // Sets the secondary handle image according if it's pressed, flipped, etc.
371 SetHandleImage(RIGHT_SELECTION_HANDLE);
373 SetSelectionHandleMarkerSize(secondary);
375 newSecondaryHandlePosition = true;
381 primary.actor.SetProperty(Actor::Property::VISIBLE, primaryVisible);
385 secondary.actor.SetProperty(Actor::Property::VISIBLE, secondaryVisible);
392 primary.actor.Unparent();
396 secondary.actor.Unparent();
400 if(mIsHighlightBoxActive)
409 mHighlightActor.Unparent();
413 if(newGrabHandlePosition ||
414 newPrimaryHandlePosition ||
415 newSecondaryHandlePosition)
417 // Setup property notifications to find whether the handles leave the boundaries of the current display.
418 SetupActiveLayerPropertyNotifications();
421 if(mActiveCopyPastePopup &&
422 (primaryVisible || secondaryVisible))
425 mPopupSetNewPosition = true;
429 if(mCopyPastePopup.actor)
431 mCopyPastePopup.actor.HidePopup();
432 mPopupSetNewPosition = true;
437 void UpdatePositions(const Vector2& scrollOffset)
439 mCursor[PRIMARY_CURSOR].position += scrollOffset;
440 mCursor[SECONDARY_CURSOR].position += scrollOffset;
441 mHandle[GRAB_HANDLE].position += scrollOffset;
442 mHandle[LEFT_SELECTION_HANDLE].position += scrollOffset;
443 mHandle[RIGHT_SELECTION_HANDLE].position += scrollOffset;
444 mHighlightPosition += scrollOffset;
449 if(!mCopyPastePopup.actor)
454 if(!mCopyPastePopup.actor.GetParent())
456 mActiveLayer.Add(mCopyPastePopup.actor);
459 mCopyPastePopup.actor.RaiseAbove(mActiveLayer);
460 mCopyPastePopup.actor.ShowPopup();
463 float CalculateVerticalPopUpPosition(float halfHeight, bool preferBelow)
465 float yPosition = 0.f;
467 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
468 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
469 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
471 if(primaryHandle.active || secondaryHandle.active)
473 // The origin of the decorator's coordinate system in world coords.
474 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION) - mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * ACTIVE_LAYER_ANCHOR_POINT;
478 // Find out if there is enough space for the popup at the bottom.
479 const float primaryBelowY = primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height;
480 const float secondaryBelowY = secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height;
482 float maxY = std::max(primaryBelowY, secondaryBelowY);
484 yPosition = halfHeight + maxY;
486 if(originWorldCoords.y + yPosition + halfHeight > mBoundingBox.w)
488 // Does not fit below.
490 // Try to fit first below the non active handle. Otherwise above the active handle.
491 if(RIGHT_SELECTION_HANDLE == mHandleReleased)
493 if(primaryBelowY < secondaryBelowY)
495 yPosition = halfHeight + primaryBelowY;
499 yPosition = primaryHandle.position.y - primaryHandle.size.height - halfHeight;
502 else if(LEFT_SELECTION_HANDLE == mHandleReleased)
504 if(secondaryBelowY < primaryBelowY)
506 yPosition = halfHeight + secondaryBelowY;
510 yPosition = secondaryHandle.position.y - secondaryHandle.size.height - halfHeight;
514 // Check the handle is whithin the decoration box.
515 if(originWorldCoords.y + yPosition < mBoundingBox.y)
517 yPosition = mBoundingBox.y - originWorldCoords.y + halfHeight;
520 if(originWorldCoords.y + yPosition > mBoundingBox.w)
522 yPosition = mBoundingBox.w - originWorldCoords.y - halfHeight;
528 // Find out if there is enough space for the popup at the top.
529 const float primaryTopY = primaryHandle.position.y - primaryHandle.size.height;
530 const float secondaryTopY = secondaryHandle.position.y - secondaryHandle.size.height;
532 float minY = std::min(primaryTopY, secondaryTopY);
534 yPosition = -halfHeight + minY;
536 } // ( primaryHandle.active || secondaryHandle.active )
537 else if(grabHandle.active)
541 yPosition = halfHeight + grabHandle.lineHeight + grabHandle.size.height + grabHandle.position.y;
545 yPosition = -halfHeight + grabHandle.position.y - POPUP_PADDING;
552 void ConstrainPopupPosition(const Vector3& popupHalfSize)
554 // Check if the popup is within the boundaries of the decoration box.
556 // Check first the horizontal dimension. If is not within the boundaries, it calculates the offset.
558 // The origin of the decorator's coordinate system in world coords.
559 const Vector3 originWorldCoords = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION) - mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::SIZE) * ACTIVE_LAYER_ANCHOR_POINT;
561 // The popup's position in world coords.
562 Vector3 popupPositionWorldCoords = originWorldCoords + mCopyPastePopup.position;
564 if(popupPositionWorldCoords.x - popupHalfSize.width < mBoundingBox.x)
566 mCopyPastePopup.position.x += mBoundingBox.x - (popupPositionWorldCoords.x - popupHalfSize.width);
568 else if(popupPositionWorldCoords.x + popupHalfSize.width > mBoundingBox.z)
570 mCopyPastePopup.position.x += mBoundingBox.z - (popupPositionWorldCoords.x + popupHalfSize.width);
573 // Check the vertical dimension. If the popup doesn't fit above the handles, it looks for a valid position below.
574 if(popupPositionWorldCoords.y - popupHalfSize.height < mBoundingBox.y)
576 mCopyPastePopup.position.y = CalculateVerticalPopUpPosition(popupHalfSize.height, true); // true -> prefer to set the popup's position below.
580 void SetPopupPosition(Actor actor)
582 if(!mActiveCopyPastePopup)
587 // Retrieves the popup's size after relayout.
588 const Vector3 popupSize(mCopyPastePopup.actor.GetRelayoutSize(Dimension::WIDTH), mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT), 0.0f);
589 const Vector3 popupHalfSize = popupSize * 0.5f;
591 if(mPopupSetNewPosition)
593 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
594 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
595 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
597 if(primaryHandle.active || secondaryHandle.active)
599 const float minHandleXPosition = std::min(primaryHandle.position.x, secondaryHandle.position.x);
600 const float maxHandleXPosition = std::max(primaryHandle.position.x, secondaryHandle.position.x);
602 mCopyPastePopup.position.x = minHandleXPosition + ((maxHandleXPosition - minHandleXPosition) * 0.5f);
604 const float primaryY = -popupHalfSize.height + primaryHandle.position.y - (primaryHandle.verticallyFlipped ? primaryHandle.size.height : POPUP_PADDING);
605 const float secondaryY = -popupHalfSize.height + secondaryHandle.position.y - (secondaryHandle.verticallyFlipped ? secondaryHandle.size.height : POPUP_PADDING);
607 mCopyPastePopup.position.y = std::min(primaryY, secondaryY);
609 else if(grabHandle.active)
611 mCopyPastePopup.position.x = grabHandle.position.x;
613 mCopyPastePopup.position.y = -popupHalfSize.height + grabHandle.position.y - (grabHandle.verticallyFlipped ? grabHandle.size.height : POPUP_PADDING);
615 } // mPopupSetNewPosition
617 // It may change the popup's position to fit within the decoration box.
618 ConstrainPopupPosition(popupHalfSize);
620 SetUpPopupPositionNotifications(popupHalfSize);
622 // Prevent pixel mis-alignment by rounding down.
623 mCopyPastePopup.position.x = floorf(mCopyPastePopup.position.x);
624 mCopyPastePopup.position.y = floorf(mCopyPastePopup.position.y);
626 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION, mCopyPastePopup.position);
627 mPopupSetNewPosition = false;
630 void CreateCursor(Control& cursor, const Vector4& color)
632 cursor = Control::New();
633 cursor.SetBackgroundColor(color);
634 cursor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
635 cursor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
636 cursor.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIDDEN, true);
639 // Add or Remove cursor(s) from parent
642 if(mActiveCursor == ACTIVE_CURSOR_NONE)
646 mPrimaryCursor.Unparent();
650 mSecondaryCursor.Unparent();
655 // Create Primary and or Secondary Cursor(s) if active and add to parent
656 if(mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
657 mActiveCursor == ACTIVE_CURSOR_BOTH)
661 CreateCursor(mPrimaryCursor, mCursor[PRIMARY_CURSOR].color);
662 #ifdef DECORATOR_DEBUG
663 mPrimaryCursor.SetProperty(Dali::Actor::Property::NAME, "PrimaryCursorActor");
667 if(!mPrimaryCursor.GetParent())
669 mCursorLayer.Add(mPrimaryCursor);
673 if(mActiveCursor == ACTIVE_CURSOR_BOTH)
675 if(!mSecondaryCursor)
677 CreateCursor(mSecondaryCursor, mCursor[SECONDARY_CURSOR].color);
678 #ifdef DECORATOR_DEBUG
679 mSecondaryCursor.SetProperty(Dali::Actor::Property::NAME, "SecondaryCursorActor");
683 if(!mSecondaryCursor.GetParent())
685 mCursorLayer.Add(mSecondaryCursor);
692 mSecondaryCursor.Unparent();
698 bool OnCursorBlinkTimerTick()
700 if(!mDelayCursorBlink)
705 mPrimaryCursor.SetProperty(Actor::Property::VISIBLE, mPrimaryCursorVisible && mCursorBlinkStatus);
709 mSecondaryCursor.SetProperty(Actor::Property::VISIBLE, mSecondaryCursorVisible && mCursorBlinkStatus);
712 mCursorBlinkStatus = !mCursorBlinkStatus;
717 mDelayCursorBlink = false;
725 // Will consume tap gestures on handles.
726 mTapDetector = TapGestureDetector::New();
728 // Will consume double tap gestures on handles.
729 mTapDetector.SetMaximumTapsRequired(2u);
731 // Will consume long press gestures on handles.
732 mLongPressDetector = LongPressGestureDetector::New();
734 // Detects pan gestures on handles.
735 mPanDetector = PanGestureDetector::New();
736 mPanDetector.DetectedSignal().Connect(this, &Decorator::Impl::OnPan);
739 void CreateLayer(Actor& layer, DecorationType type)
743 layer = Actor::New();
744 #ifdef DECORATOR_DEBUG
745 if(type == DecorationType::ACTIVE_LAYER)
747 layer.SetProperty(Actor::Property::NAME, "ActiveLayerActor");
749 else if(type == DecorationType::CURSOR_LAYER)
751 layer.SetProperty(Actor::Property::NAME, "CursorLayerActor");
754 bool needsClipping = false;
755 if(type == DecorationType::CURSOR_LAYER)
757 needsClipping = true;
760 layer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
761 layer.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
763 mController.AddDecoration(layer, type, needsClipping);
769 void SetSelectionHandleMarkerSize(HandleImpl& handle)
771 if(handle.markerActor)
773 handle.markerActor.SetProperty(Actor::Property::SIZE, Vector2(0, handle.lineHeight));
777 void CreateGrabHandle()
779 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
780 if(!grabHandle.actor)
782 if(mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED].size())
784 grabHandle.actor = ImageView::New(mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED]);
785 GetImpl(grabHandle.actor).SetDepthIndex(DepthIndex::DECORATION);
786 grabHandle.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
787 grabHandle.actor.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
789 // Area that Grab handle responds to, larger than actual handle so easier to move
790 #ifdef DECORATOR_DEBUG
791 grabHandle.actor.SetProperty(Dali::Actor::Property::NAME, "GrabHandleActor");
792 if(Dali::Internal::gLogFilter->IsEnabledFor(Debug::Verbose))
794 grabHandle.grabArea = Control::New();
795 Toolkit::Control control = Toolkit::Control::DownCast(grabHandle.grabArea);
796 control.SetBackgroundColor(Vector4(1.0f, 1.0f, 1.0f, 0.5f));
797 grabHandle.grabArea.SetProperty(Dali::Actor::Property::NAME, "GrabArea");
801 grabHandle.grabArea = Actor::New();
802 grabHandle.grabArea.SetProperty(Dali::Actor::Property::NAME, "GrabArea");
805 grabHandle.grabArea = Actor::New();
808 grabHandle.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
809 grabHandle.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
810 grabHandle.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
811 grabHandle.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_GRAB_HANDLE_RELATIVE_SIZE);
812 grabHandle.actor.Add(grabHandle.grabArea);
813 grabHandle.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
815 grabHandle.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnGrabHandleTouched);
817 // The grab handle's actor is attached to the tap and long press detectors in order to consume these events.
818 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
819 mTapDetector.Attach(grabHandle.actor);
820 mLongPressDetector.Attach(grabHandle.actor);
822 // The grab handle's area is attached to the pan detector.
823 // The OnPan() method is connected to the signals emitted by the pan detector.
824 mPanDetector.Attach(grabHandle.grabArea);
826 mActiveLayer.Add(grabHandle.actor);
830 if(grabHandle.actor && !grabHandle.actor.GetParent())
832 mActiveLayer.Add(grabHandle.actor);
836 void CreateHandleMarker(HandleImpl& handle, const std::string& image, HandleType handleType)
840 handle.markerActor = ImageView::New(image);
841 handle.markerActor.SetProperty(Actor::Property::COLOR, mHandleColor);
842 handle.actor.Add(handle.markerActor);
844 handle.markerActor.SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
846 if(LEFT_SELECTION_HANDLE == handleType)
848 handle.markerActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_RIGHT);
849 handle.markerActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_RIGHT);
851 else if(RIGHT_SELECTION_HANDLE == handleType)
853 handle.markerActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_LEFT);
854 handle.markerActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
859 void CreateSelectionHandles()
861 HandleImpl& primary = mHandle[LEFT_SELECTION_HANDLE];
864 if(mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size())
866 primary.actor = ImageView::New(mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]);
867 #ifdef DECORATOR_DEBUG
868 primary.actor.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleOne");
870 primary.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_RIGHT); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
871 primary.actor.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
872 GetImpl(primary.actor).SetDepthIndex(DepthIndex::DECORATION);
873 primary.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
875 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
876 #ifdef DECORATOR_DEBUG
877 primary.grabArea.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleOneGrabArea");
879 primary.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
880 primary.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
881 primary.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
882 primary.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE);
884 primary.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnHandleOneTouched);
886 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
887 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
888 mTapDetector.Attach(primary.actor);
889 mLongPressDetector.Attach(primary.actor);
891 // The handle's area is attached to the pan detector.
892 // The OnPan() method is connected to the signals emitted by the pan detector.
893 mPanDetector.Attach(primary.grabArea);
895 primary.actor.Add(primary.grabArea);
897 CreateHandleMarker(primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE);
901 if(primary.actor && !primary.actor.GetParent())
903 mActiveLayer.Add(primary.actor);
906 HandleImpl& secondary = mHandle[RIGHT_SELECTION_HANDLE];
909 if(mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED].size())
911 secondary.actor = ImageView::New(mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED]);
912 #ifdef DECORATOR_DEBUG
913 secondary.actor.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleTwo");
915 secondary.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
916 secondary.actor.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
917 GetImpl(secondary.actor).SetDepthIndex(DepthIndex::DECORATION);
918 secondary.actor.SetProperty(Actor::Property::COLOR, mHandleColor);
920 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
921 #ifdef DECORATOR_DEBUG
922 secondary.grabArea.SetProperty(Dali::Actor::Property::NAME, "SelectionHandleTwoGrabArea");
924 secondary.grabArea.SetResizePolicy(ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS);
925 secondary.grabArea.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
926 secondary.grabArea.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
927 secondary.grabArea.SetProperty(Actor::Property::SIZE_MODE_FACTOR, DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE);
929 secondary.grabArea.TouchedSignal().Connect(this, &Decorator::Impl::OnHandleTwoTouched);
931 // The handle's actor is attached to the tap and long press detectors in order to consume these events.
932 // Note that no callbacks are connected to any signal emitted by the tap and long press detectors.
933 mTapDetector.Attach(secondary.actor);
934 mLongPressDetector.Attach(secondary.actor);
936 // The handle's area is attached to the pan detector.
937 // The OnPan() method is connected to the signals emitted by the pan detector.
938 mPanDetector.Attach(secondary.grabArea);
940 secondary.actor.Add(secondary.grabArea);
942 CreateHandleMarker(secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE);
946 if(secondary.actor && !secondary.actor.GetParent())
948 mActiveLayer.Add(secondary.actor);
952 void CreateSelectionPopup()
954 if(!mCopyPastePopup.actor)
956 mCopyPastePopup.actor = TextSelectionPopup::New(&mTextSelectionPopupCallbackInterface);
957 #ifdef DECORATOR_DEBUG
958 mCopyPastePopup.actor.SetProperty(Dali::Actor::Property::NAME, "mCopyPastePopup");
960 mCopyPastePopup.actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
961 mCopyPastePopup.actor.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D);
962 mCopyPastePopup.actor.OnRelayoutSignal().Connect(this, &Decorator::Impl::SetPopupPosition); // Position popup after size negotiation
966 void CalculateHandleWorldCoordinates(HandleImpl& handle, Vector2& position)
968 // Gets the world position of the active layer. The active layer is where the handles are added.
969 const Vector3 parentWorldPosition = mActiveLayer.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION);
971 // The grab handle position in world coords.
972 // The active layer's world position is the center of the active layer. The origin of the
973 // coord system of the handles is the top left of the active layer.
974 position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x + (mSmoothHandlePanEnabled ? handle.grabDisplacementX : 0.f);
975 position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y + (mSmoothHandlePanEnabled ? handle.grabDisplacementY : 0.f);
978 void SetGrabHandlePosition()
980 // Reference to the grab handle.
981 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
983 // Transforms the handle position into world coordinates.
984 // @note This is not the same value as grabHandle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
985 // as it's transforming the handle's position set by the text-controller and not
986 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
987 // retrieves the position of the center of the actor but the handle's position set
988 // by the text controller is not the center of the actor.
989 Vector2 grabHandleWorldPosition;
990 CalculateHandleWorldCoordinates(grabHandle, grabHandleWorldPosition);
992 // Check if the grab handle exceeds the boundaries of the decoration box.
993 // At the moment only the height is checked for the grab handle.
995 grabHandle.verticallyFlipped = (grabHandle.verticallyFlippedPreferred &&
996 ((grabHandleWorldPosition.y - grabHandle.size.height) > mBoundingBox.y)) ||
997 (grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w);
999 // The grab handle 'y' position in local coords.
1000 // If the grab handle exceeds the bottom of the decoration box,
1001 // set the 'y' position to the top of the line.
1002 // The SetGrabHandleImage() method will change the orientation.
1003 const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
1005 ApplyDisplacement(grabHandle, yLocalPosition);
1008 void SetSelectionHandlePosition(HandleType type)
1010 const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
1012 // Reference to the selection handle.
1013 HandleImpl& handle = mHandle[type];
1015 // Transforms the handle position into world coordinates.
1016 // @note This is not the same value as handle.actor.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1017 // as it's transforming the handle's position set by the text-controller and not
1018 // the final position set to the actor. Another difference is the.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION )
1019 // retrieves the position of the center of the actor but the handle's position set
1020 // by the text controller is not the center of the actor.
1021 Vector2 handleWorldPosition;
1022 CalculateHandleWorldCoordinates(handle, handleWorldPosition);
1024 // Whether to flip the handle (horizontally).
1025 bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
1027 // Whether to flip the handles if they are crossed.
1028 bool crossFlip = false;
1029 if(mFlipSelectionHandlesOnCross || !mIsHandlePanning)
1031 crossFlip = mIsHandleCurrentlyCrossed;
1034 // Whether the handle was crossed before start the panning.
1035 const bool isHandlePreviouslyCrossed = mFlipSelectionHandlesOnCross ? false : mIsHandlePreviouslyCrossed;
1037 // Does not flip if both conditions are true (double flip)
1038 flipHandle = flipHandle != (crossFlip || isHandlePreviouslyCrossed);
1040 // Will flip the handles vertically if the user prefers it.
1041 bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
1043 if(crossFlip || isHandlePreviouslyCrossed)
1047 verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
1051 verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
1055 // Check if the selection handle exceeds the boundaries of the decoration box.
1056 const bool exceedsLeftEdge = (isPrimaryHandle ? !flipHandle : flipHandle) && (handleWorldPosition.x - handle.size.width < mBoundingBox.x);
1057 const bool exceedsRightEdge = (isPrimaryHandle ? flipHandle : !flipHandle) && (handleWorldPosition.x + handle.size.width > mBoundingBox.z);
1059 // Does not flip if both conditions are true (double flip)
1060 flipHandle = flipHandle != (exceedsLeftEdge || exceedsRightEdge);
1064 if(handle.actor && !handle.horizontallyFlipped)
1066 // Change the anchor point to flip the image.
1067 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT);
1069 handle.horizontallyFlipped = true;
1074 if(handle.actor && handle.horizontallyFlipped)
1076 // Reset the anchor point.
1077 handle.actor.SetProperty(Actor::Property::ANCHOR_POINT, isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT);
1079 handle.horizontallyFlipped = false;
1083 // Whether to flip the handle vertically.
1084 handle.verticallyFlipped = (verticallyFlippedPreferred &&
1085 ((handleWorldPosition.y - handle.size.height) > mBoundingBox.y)) ||
1086 (handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w);
1088 // The primary selection handle 'y' position in local coords.
1089 // If the handle exceeds the bottom of the decoration box,
1090 // set the 'y' position to the top of the line.
1091 // The SetHandleImage() method will change the orientation.
1092 const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
1094 ApplyDisplacement(handle, yLocalPosition);
1097 void ApplyDisplacement(HandleImpl& handle, float yLocalPosition)
1101 float adjustedDisplacementX = 0.0f;
1102 float adjustedDisplacementY = 0.0f;
1103 if (mSmoothHandlePanEnabled)
1105 adjustedDisplacementX = CalculateAdjustedDisplacement(handle.position.x, handle.grabDisplacementX, mControlSize.x);
1106 adjustedDisplacementY = CalculateAdjustedDisplacement(handle.position.y, handle.grabDisplacementY, (mControlSize.y - handle.lineHeight));
1108 handle.actor.SetProperty(Actor::Property::POSITION,
1109 Vector2(handle.position.x + floor(0.5f * mCursorWidth) + adjustedDisplacementX,
1110 yLocalPosition + adjustedDisplacementY));
1114 float CalculateAdjustedDisplacement(float position, float displacement, float edge)
1116 //Apply the displacement (on the X-axis & the Y-axis)
1117 //as long as it does not exceed the control's edge.
1118 float adjustedDisplacement = 0.0f;
1119 if(position + displacement < 0.0f)
1121 // -position to cancel it out and relocate to 0.
1122 adjustedDisplacement = -position;
1124 else if(position + displacement > edge)
1126 // move in a displacement which is sufficient to reach the edge.
1127 adjustedDisplacement = edge - position;
1131 // move normally in the displacement.
1132 adjustedDisplacement = displacement;
1134 return adjustedDisplacement;
1137 void SetHandleImage(HandleType type)
1139 HandleImpl& handle = mHandle[type];
1141 HandleType markerType = HANDLE_TYPE_COUNT;
1142 // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
1143 if(LEFT_SELECTION_HANDLE == type)
1145 type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
1146 markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
1148 else if(RIGHT_SELECTION_HANDLE == type)
1150 type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
1151 markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
1154 // Chooses between the released or pressed image. It checks whether the pressed image exists.
1157 const HandleImageType imageType = (handle.pressed ? (mHandleImages[type][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1159 handle.actor.SetImage(mHandleImages[type][imageType]);
1162 if(HANDLE_TYPE_COUNT != markerType)
1164 if(handle.markerActor)
1166 const HandleImageType markerImageType = (handle.pressed ? (mHandleImages[markerType][HANDLE_IMAGE_PRESSED].size() ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED) : HANDLE_IMAGE_RELEASED);
1167 handle.markerActor.SetImage(mHandleImages[markerType][markerImageType]);
1171 // Whether to flip the handle vertically.
1174 handle.actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS));
1178 void CreateHighlight()
1180 if(!mHighlightActor)
1182 mHighlightActor = Actor::New();
1184 mHighlightActor.SetProperty(Dali::Actor::Property::NAME, "HighlightActor");
1185 mHighlightActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1186 mHighlightActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1187 mHighlightActor.SetProperty(Actor::Property::COLOR, mHighlightColor);
1188 mHighlightActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_COLOR);
1191 // Add the highlight box telling the controller it needs clipping.
1192 mController.AddDecoration(mHighlightActor, DecorationType::NONE_LAYER, true);
1195 void UpdateHighlight()
1199 // Sets the position of the highlight actor inside the decorator.
1200 mHighlightActor.SetProperty(Actor::Property::POSITION, Vector2(mHighlightPosition.x + mHighlightOutlineOffset, mHighlightPosition.y + mHighlightOutlineOffset));
1202 const unsigned int numberOfQuads = mHighlightQuadList.Count();
1203 if(0u != numberOfQuads)
1205 // Set the size of the highlighted text to the actor.
1206 mHighlightActor.SetProperty(Actor::Property::SIZE, mHighlightSize);
1208 // Used to translate the vertices given in decorator's coords to the mHighlightActor's local coords.
1209 const float offsetX = mHighlightPosition.x + 0.5f * mHighlightSize.width;
1210 const float offsetY = mHighlightPosition.y + 0.5f * mHighlightSize.height;
1212 Vector<Vector2> vertices;
1213 Vector<unsigned short> indices;
1215 vertices.Reserve(4u * numberOfQuads);
1216 indices.Reserve(6u * numberOfQuads);
1218 // Index to the vertex.
1219 unsigned int v = 0u;
1221 // Traverse all quads.
1222 for(Vector<Vector4>::ConstIterator it = mHighlightQuadList.Begin(),
1223 endIt = mHighlightQuadList.End();
1227 const Vector4& quad = *it;
1232 vertex.x = quad.x - offsetX;
1233 vertex.y = quad.y - offsetY;
1234 vertices.PushBack(vertex);
1237 vertex.x = quad.z - offsetX;
1238 vertex.y = quad.y - offsetY;
1239 vertices.PushBack(vertex);
1241 // bottom-left (v+2)
1242 vertex.x = quad.x - offsetX;
1243 vertex.y = quad.w - offsetY;
1244 vertices.PushBack(vertex);
1246 // bottom-right (v+3)
1247 vertex.x = quad.z - offsetX;
1248 vertex.y = quad.w - offsetY;
1249 vertices.PushBack(vertex);
1251 // triangle A (3, 1, 0)
1252 indices.PushBack(v + 3);
1253 indices.PushBack(v + 1);
1254 indices.PushBack(v);
1256 // triangle B (0, 2, 3)
1257 indices.PushBack(v);
1258 indices.PushBack(v + 2);
1259 indices.PushBack(v + 3);
1264 mQuadVertices = VertexBuffer::New(mQuadVertexFormat);
1267 mQuadVertices.SetData(&vertices[0], vertices.Size());
1271 mQuadGeometry = Geometry::New();
1272 mQuadGeometry.AddVertexBuffer(mQuadVertices);
1274 mQuadGeometry.SetIndexBuffer(&indices[0], indices.Size());
1276 if(!mHighlightRenderer)
1278 mHighlightRenderer = Dali::Renderer::New(mQuadGeometry, mHighlightShader);
1279 mHighlightActor.AddRenderer(mHighlightRenderer);
1283 mHighlightQuadList.Clear();
1285 if(mHighlightRenderer)
1287 mHighlightRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mTextDepth - 2); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1292 void DoPan(HandleImpl& handle, HandleType type, const PanGesture& gesture)
1294 GestureState state = gesture.GetState();
1295 if(GestureState::STARTED == state)
1297 handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1299 handle.globalPosition.x = handle.position.x;
1300 handle.globalPosition.y = handle.position.y;
1303 const Vector2& displacement = gesture.GetDisplacement();
1304 handle.grabDisplacementX += displacement.x;
1305 handle.grabDisplacementY += (handle.verticallyFlipped ? -displacement.y : displacement.y);
1307 const float x = handle.globalPosition.x + handle.grabDisplacementX;
1308 const float y = handle.globalPosition.y + handle.grabDisplacementY + 0.5f * handle.lineHeight;
1309 const float yVerticallyFlippedCorrected = y - (handle.verticallyFlippedOnTouch ? handle.lineHeight : 0.f);
1311 if((GestureState::STARTED == state) ||
1312 (GestureState::CONTINUING == state))
1315 mController.GetTargetSize(targetSize);
1317 if(mHorizontalScrollingEnabled &&
1318 (x < mScrollThreshold))
1320 mScrollDirection = SCROLL_RIGHT;
1321 mHandleScrolling = type;
1324 else if(mHorizontalScrollingEnabled &&
1325 (x > targetSize.width - mScrollThreshold))
1327 mScrollDirection = SCROLL_LEFT;
1328 mHandleScrolling = type;
1331 else if(mVerticalScrollingEnabled &&
1332 (yVerticallyFlippedCorrected < mScrollThreshold))
1334 mScrollDirection = SCROLL_TOP;
1335 mHandleScrolling = type;
1338 else if(mVerticalScrollingEnabled &&
1339 (yVerticallyFlippedCorrected + handle.lineHeight > targetSize.height - mScrollThreshold))
1341 mScrollDirection = SCROLL_BOTTOM;
1342 mHandleScrolling = type;
1347 mHandleScrolling = HANDLE_TYPE_COUNT;
1349 mController.DecorationEvent(type, HANDLE_PRESSED, x, y);
1352 mIsHandlePanning = true;
1354 else if((GestureState::FINISHED == state) ||
1355 (GestureState::CANCELLED == state))
1358 (mScrollTimer.IsRunning() || mNotifyEndOfScroll))
1360 mNotifyEndOfScroll = false;
1361 mHandleScrolling = HANDLE_TYPE_COUNT;
1363 mController.DecorationEvent(type, HANDLE_STOP_SCROLLING, x, y);
1367 mController.DecorationEvent(type, HANDLE_RELEASED, x, y);
1372 handle.actor.SetImage(mHandleImages[type][HANDLE_IMAGE_RELEASED]);
1374 handle.pressed = false;
1376 mIsHandlePanning = false;
1380 void OnPan(Actor actor, const PanGesture& gesture)
1382 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1383 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1384 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1386 if(actor == grabHandle.grabArea)
1388 DoPan(grabHandle, GRAB_HANDLE, gesture);
1390 else if(actor == primarySelectionHandle.grabArea)
1392 DoPan(primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture);
1394 else if(actor == secondarySelectionHandle.grabArea)
1396 DoPan(secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture);
1400 bool OnGrabHandleTouched(Actor actor, const TouchEvent& touch)
1402 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1404 // Switch between pressed/release grab-handle images
1405 if(touch.GetPointCount() > 0 &&
1408 const PointState::Type state = touch.GetState(0);
1410 if(PointState::DOWN == state)
1412 grabHandle.pressed = true;
1414 else if((PointState::UP == state) ||
1415 (PointState::INTERRUPTED == state))
1417 grabHandle.pressed = false;
1420 SetHandleImage(GRAB_HANDLE);
1426 bool OnHandleOneTouched(Actor actor, const TouchEvent& touch)
1428 HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1430 // Switch between pressed/release selection handle images
1431 if(touch.GetPointCount() > 0 &&
1432 primarySelectionHandle.actor)
1434 const PointState::Type state = touch.GetState(0);
1436 if(PointState::DOWN == state)
1438 primarySelectionHandle.pressed = true;
1439 primarySelectionHandle.verticallyFlippedOnTouch = primarySelectionHandle.verticallyFlipped;
1441 else if((PointState::UP == state) ||
1442 (PointState::INTERRUPTED == state))
1444 primarySelectionHandle.pressed = false;
1445 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1446 mIsHandlePanning = false;
1447 mHandleReleased = LEFT_SELECTION_HANDLE;
1450 SetHandleImage(LEFT_SELECTION_HANDLE);
1456 bool OnHandleTwoTouched(Actor actor, const TouchEvent& touch)
1458 HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1460 // Switch between pressed/release selection handle images
1461 if(touch.GetPointCount() > 0 &&
1462 secondarySelectionHandle.actor)
1464 const PointState::Type state = touch.GetState(0);
1466 if(PointState::DOWN == state)
1468 secondarySelectionHandle.pressed = true;
1469 secondarySelectionHandle.verticallyFlippedOnTouch = secondarySelectionHandle.verticallyFlipped;
1471 else if((PointState::UP == state) ||
1472 (PointState::INTERRUPTED == state))
1474 secondarySelectionHandle.pressed = false;
1475 mIsHandlePreviouslyCrossed = mIsHandleCurrentlyCrossed;
1476 mIsHandlePanning = false;
1477 mHandleReleased = RIGHT_SELECTION_HANDLE;
1480 SetHandleImage(RIGHT_SELECTION_HANDLE);
1486 void HandleResetPosition(PropertyNotification& source)
1488 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1490 if(grabHandle.active)
1492 // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1493 SetGrabHandlePosition();
1495 // Sets the grab handle image according if it's pressed, flipped, etc.
1496 SetHandleImage(GRAB_HANDLE);
1500 // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1501 SetSelectionHandlePosition(LEFT_SELECTION_HANDLE);
1503 // Sets the primary handle image according if it's pressed, flipped, etc.
1504 SetHandleImage(LEFT_SELECTION_HANDLE);
1506 // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1507 SetSelectionHandlePosition(RIGHT_SELECTION_HANDLE);
1509 // Sets the secondary handle image according if it's pressed, flipped, etc.
1510 SetHandleImage(RIGHT_SELECTION_HANDLE);
1514 void SetupActiveLayerPropertyNotifications()
1521 // Vertical notifications.
1523 // Disconnect any previous connected callback.
1524 if(mHandleVerticalLessThanNotification)
1526 mHandleVerticalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1527 mActiveLayer.RemovePropertyNotification(mHandleVerticalLessThanNotification);
1530 if(mHandleVerticalGreaterThanNotification)
1532 mHandleVerticalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1533 mActiveLayer.RemovePropertyNotification(mHandleVerticalGreaterThanNotification);
1536 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1537 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1538 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1540 if(grabHandle.active)
1542 if(grabHandle.verticallyFlipped)
1544 // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1545 mHandleVerticalGreaterThanNotification.Reset();
1547 // The vertical distance from the center of the active layer to the top edje of the display.
1548 const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1550 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1551 LessThanCondition(mBoundingBox.y + topHeight));
1553 // Notifies the change from false to true and from true to false.
1554 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1556 // Connects the signals with the callbacks.
1557 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1561 // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1562 mHandleVerticalLessThanNotification.Reset();
1564 // The vertical distance from the center of the active layer to the bottom edje of the display.
1565 const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1567 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1568 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1570 // Notifies the change from false to true and from true to false.
1571 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1573 // Connects the signals with the callbacks.
1574 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1577 else // The selection handles are active
1579 if(primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped)
1581 // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1582 mHandleVerticalGreaterThanNotification.Reset();
1584 // The vertical distance from the center of the active layer to the top edje of the display.
1585 const float topHeight = 0.5f * mControlSize.height + std::max(-primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height);
1587 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1588 LessThanCondition(mBoundingBox.y + topHeight));
1590 // Notifies the change from false to true and from true to false.
1591 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1593 // Connects the signals with the callbacks.
1594 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1596 else if(!primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped)
1598 // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1599 mHandleVerticalLessThanNotification.Reset();
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 + std::max(primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1603 secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height);
1605 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1606 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1608 // Notifies the change from false to true and from true to false.
1609 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1611 // Connects the signals with the callbacks.
1612 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1616 // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1618 // The vertical distance from the center of the active layer to the top edje of the display.
1619 const float topHeight = 0.5f * mControlSize.height + (primaryHandle.verticallyFlipped ? -primaryHandle.position.y + primaryHandle.size.height : -secondaryHandle.position.y + secondaryHandle.size.height);
1621 mHandleVerticalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1622 LessThanCondition(mBoundingBox.y + topHeight));
1624 // Notifies the change from false to true and from true to false.
1625 mHandleVerticalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1627 // Connects the signals with the callbacks.
1628 mHandleVerticalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1630 // The vertical distance from the center of the active layer to the bottom edje of the display.
1631 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);
1633 mHandleVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1634 GreaterThanCondition(mBoundingBox.w - bottomHeight));
1636 // Notifies the change from false to true and from true to false.
1637 mHandleVerticalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1639 // Connects the signals with the callbacks.
1640 mHandleVerticalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1644 // Horizontal notifications.
1646 // Disconnect any previous connected callback.
1647 if(mHandleHorizontalLessThanNotification)
1649 mHandleHorizontalLessThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1650 mActiveLayer.RemovePropertyNotification(mHandleHorizontalLessThanNotification);
1653 if(mHandleHorizontalGreaterThanNotification)
1655 mHandleHorizontalGreaterThanNotification.NotifySignal().Disconnect(this, &Decorator::Impl::HandleResetPosition);
1656 mActiveLayer.RemovePropertyNotification(mHandleHorizontalGreaterThanNotification);
1659 if(primaryHandle.active || secondaryHandle.active)
1661 // The horizontal distance from the center of the active layer to the left edje of the display.
1662 const float leftWidth = 0.5f * mControlSize.width + std::max(-primaryHandle.position.x + primaryHandle.size.width,
1663 -secondaryHandle.position.x + secondaryHandle.size.width);
1665 mHandleHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1666 LessThanCondition(mBoundingBox.x + leftWidth));
1668 // Notifies the change from false to true and from true to false.
1669 mHandleHorizontalLessThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1671 // Connects the signals with the callbacks.
1672 mHandleHorizontalLessThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1674 // The horizontal distance from the center of the active layer to the right edje of the display.
1675 const float rightWidth = -0.5f * mControlSize.width + std::max(primaryHandle.position.x + primaryHandle.size.width,
1676 secondaryHandle.position.x + secondaryHandle.size.width);
1678 mHandleHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification(Actor::Property::WORLD_POSITION_X,
1679 GreaterThanCondition(mBoundingBox.z - rightWidth));
1681 // Notifies the change from false to true and from true to false.
1682 mHandleHorizontalGreaterThanNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1684 // Connects the signals with the callbacks.
1685 mHandleHorizontalGreaterThanNotification.NotifySignal().Connect(this, &Decorator::Impl::HandleResetPosition);
1691 float AlternatePopUpPositionRelativeToCursor(bool topBottom)
1693 float alternativePosition = 0.0f;
1695 const float halfPopupHeight = 0.5f * mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1697 const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1698 const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1699 const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1700 const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1702 if(primaryHandle.active || secondaryHandle.active)
1704 float handleY = 0.f;
1705 float maxHandleHeight = 0.f;
1707 const bool primaryVisible = primaryHandle.horizontallyVisible && primaryHandle.verticallyVisible;
1708 const bool secondaryVisible = secondaryHandle.horizontallyVisible && secondaryHandle.verticallyVisible;
1710 if(primaryVisible && secondaryVisible)
1712 handleY = std::max(primaryHandle.position.y, secondaryHandle.position.y);
1713 maxHandleHeight = std::max(primaryHandle.size.height, secondaryHandle.size.height);
1715 else if(primaryVisible && !secondaryVisible)
1717 handleY = primaryHandle.position.y;
1718 maxHandleHeight = primaryHandle.size.height;
1720 else if(!primaryVisible && secondaryVisible)
1722 handleY = secondaryHandle.position.y;
1723 maxHandleHeight = secondaryHandle.size.height;
1726 alternativePosition = handleY + (topBottom ? halfPopupHeight + maxHandleHeight + cursor.lineHeight : -halfPopupHeight - maxHandleHeight);
1730 alternativePosition = cursor.position.y + (topBottom ? halfPopupHeight + grabHandle.size.height + cursor.lineHeight : -halfPopupHeight - grabHandle.size.height);
1733 return alternativePosition;
1736 void PopUpLeavesTopBoundary(PropertyNotification& source)
1738 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1740 // Sets the position of the popup below.
1741 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, true)));
1744 void PopUpLeavesBottomBoundary(PropertyNotification& source)
1746 const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize(Dimension::HEIGHT);
1748 // Sets the position of the popup above.
1749 mCopyPastePopup.actor.SetProperty(Actor::Property::POSITION_Y, floorf(CalculateVerticalPopUpPosition(0.5f * popupHeight, false)));
1752 void SetUpPopupPositionNotifications(const Vector3& popupHalfSize)
1754 // Disconnect any previous connected callback.
1755 if(mPopupTopExceedNotification)
1757 mPopupTopExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1758 mCopyPastePopup.actor.RemovePropertyNotification(mPopupTopExceedNotification);
1761 if(mPopupBottomExceedNotification)
1763 mPopupBottomExceedNotification.NotifySignal().Disconnect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1764 mCopyPastePopup.actor.RemovePropertyNotification(mPopupBottomExceedNotification);
1767 // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left.
1769 // Exceeding vertical boundary
1771 mPopupTopExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1772 LessThanCondition(mBoundingBox.y + popupHalfSize.height));
1774 mPopupBottomExceedNotification = mCopyPastePopup.actor.AddPropertyNotification(Actor::Property::WORLD_POSITION_Y,
1775 GreaterThanCondition(mBoundingBox.w - popupHalfSize.height));
1777 // Notifies the change from false to true and from true to false.
1778 mPopupTopExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1779 mPopupBottomExceedNotification.SetNotifyMode(PropertyNotification::NOTIFY_ON_CHANGED);
1781 mPopupTopExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesTopBoundary);
1782 mPopupBottomExceedNotification.NotifySignal().Connect(this, &Decorator::Impl::PopUpLeavesBottomBoundary);
1785 void SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
1787 ImageDimensions dimensions = Dali::GetOriginalImageSize(imageFileName);
1789 HandleImpl& handle = mHandle[handleType];
1790 handle.size = Size(dimensions.GetWidth(), dimensions.GetHeight());
1792 mHandleImages[handleType][handleImageType] = imageFileName;
1795 void SetScrollThreshold(float threshold)
1797 mScrollThreshold = threshold;
1800 float GetScrollThreshold() const
1802 return mScrollThreshold;
1805 void SetScrollSpeed(float speed)
1807 mScrollSpeed = speed;
1808 mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1811 float GetScrollSpeed() const
1813 return mScrollSpeed;
1816 void NotifyEndOfScroll()
1822 mNotifyEndOfScroll = true;
1827 * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1829 * It only starts the timer if it's already created.
1831 void StartScrollTimer()
1835 mScrollTimer = Timer::New(SCROLL_TICK_INTERVAL);
1836 mScrollTimer.TickSignal().Connect(this, &Decorator::Impl::OnScrollTimerTick);
1839 if(!mScrollTimer.IsRunning())
1841 mScrollTimer.Start();
1846 * Stops the timer used to scroll the text.
1848 void StopScrollTimer()
1852 mScrollTimer.Stop();
1857 * Callback called by the timer used to scroll the text.
1859 * It calculates and sets a new scroll position.
1861 bool OnScrollTimerTick()
1863 if(HANDLE_TYPE_COUNT != mHandleScrolling)
1868 switch(mScrollDirection)
1872 x = mScrollDistance;
1877 x = -mScrollDistance;
1882 y = mScrollDistance;
1887 y = -mScrollDistance;
1894 mController.DecorationEvent(mHandleScrolling,
1903 ControllerInterface& mController;
1905 TapGestureDetector mTapDetector;
1906 PanGestureDetector mPanDetector;
1907 LongPressGestureDetector mLongPressDetector;
1909 Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink
1910 Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1912 Actor mActiveLayer; ///< Actor for active handles and alike that ensures they are above all else.
1913 Actor mCursorLayer; ///< Actor for cursor layer. this is for cursor clipping.
1914 PropertyNotification mHandleVerticalLessThanNotification; ///< Notifies when the 'y' coord of the active layer is less than a given value.
1915 PropertyNotification mHandleVerticalGreaterThanNotification; ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1916 PropertyNotification mHandleHorizontalLessThanNotification; ///< Notifies when the 'x' coord of the active layer is less than a given value.
1917 PropertyNotification mHandleHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1918 PropertyNotification mPopupTopExceedNotification; ///< Notifies when the popup leaves the bounding box through the top.
1919 PropertyNotification mPopupBottomExceedNotification; ///< Notifies when the popup leaves the bounding box through the bottom.
1920 Control mPrimaryCursor;
1921 Control mSecondaryCursor;
1923 Actor mHighlightActor; ///< Actor to display highlight
1924 Renderer mHighlightRenderer;
1925 Shader mHighlightShader; ///< Shader used for highlight
1926 Property::Map mQuadVertexFormat;
1927 PopupImpl mCopyPastePopup;
1928 TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1929 TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1931 std::string mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1932 Vector4 mHandleColor;
1934 CursorImpl mCursor[CURSOR_COUNT];
1935 HandleImpl mHandle[HANDLE_TYPE_COUNT];
1937 VertexBuffer mQuadVertices;
1938 Geometry mQuadGeometry;
1939 QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight.
1941 Vector4 mBoundingBox; ///< The bounding box in world coords.
1942 Vector4 mHighlightColor; ///< Color of the highlight
1943 Vector2 mHighlightPosition; ///< The position of the highlight actor.
1944 Size mHighlightSize; ///< The size of the highlighted text.
1945 Size mControlSize; ///< The control's size. Set by the Relayout.
1946 float mHighlightOutlineOffset; ///< The outline's offset.
1948 unsigned int mActiveCursor;
1949 unsigned int mCursorBlinkInterval;
1950 float mCursorBlinkDuration;
1951 float mCursorWidth; ///< The width of the cursors in pixels.
1952 HandleType mHandleScrolling; ///< The handle which is scrolling.
1953 HandleType mHandleReleased; ///< The last handle released.
1954 ScrollDirection mScrollDirection; ///< The direction of the scroll.
1955 float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1956 float mScrollSpeed; ///< The scroll speed in pixels per second.
1957 float mScrollDistance; ///< Distance the text scrolls during a scroll interval.
1958 int mTextDepth; ///< The depth used to render the text.
1960 bool mActiveCopyPastePopup : 1;
1961 bool mPopupSetNewPosition : 1;
1962 bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
1963 bool mDelayCursorBlink : 1; ///< Used to avoid cursor blinking when entering text.
1964 bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
1965 bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1966 bool mFlipSelectionHandlesOnCross : 1; ///< Whether to flip the selection handles as soon as they cross.
1967 bool mFlipLeftSelectionHandleDirection : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1968 bool mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1969 bool mIsHandlePanning : 1; ///< Whether any of the handles is moving.
1970 bool mIsHandleCurrentlyCrossed : 1; ///< Whether the handles are crossed.
1971 bool mIsHandlePreviouslyCrossed : 1; ///< Whether the handles where crossed at the last handle touch up.
1972 bool mNotifyEndOfScroll : 1; ///< Whether to notify the end of the scroll.
1973 bool mHorizontalScrollingEnabled : 1; ///< Whether the horizontal scrolling is enabled.
1974 bool mVerticalScrollingEnabled : 1; ///< Whether the vertical scrolling is enabled.
1975 bool mSmoothHandlePanEnabled : 1; ///< Whether to pan smoothly the handles.
1976 bool mIsHighlightBoxActive : 1; ///< Whether the highlight box is active.
1977 bool mHidePrimaryCursorAndGrabHandle : 1; ///< Whether the primary cursor and grab are hidden always.
1980 DecoratorPtr Decorator::New(ControllerInterface& controller,
1981 TextSelectionPopupCallbackInterface& callbackInterface)
1983 return DecoratorPtr(new Decorator(controller,
1984 callbackInterface));
1987 void Decorator::SetBoundingBox(const Rect<int>& boundingBox)
1989 LocalToWorldCoordinatesBoundingBox(boundingBox, mImpl->mBoundingBox);
1992 void Decorator::GetBoundingBox(Rect<int>& boundingBox) const
1994 WorldToLocalCoordinatesBoundingBox(mImpl->mBoundingBox, boundingBox);
1997 void Decorator::Relayout(const Vector2& size)
1999 mImpl->Relayout(size);
2002 void Decorator::UpdatePositions(const Vector2& scrollOffset)
2004 mImpl->UpdatePositions(scrollOffset);
2009 void Decorator::SetActiveCursor(ActiveCursor activeCursor)
2011 mImpl->mActiveCursor = activeCursor;
2014 unsigned int Decorator::GetActiveCursor() const
2016 return mImpl->mActiveCursor;
2019 void Decorator::SetPosition(Cursor cursor, float x, float y, float cursorHeight, float lineHeight)
2021 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2023 cursorImpl.position.x = x;
2024 cursorImpl.position.y = y;
2025 cursorImpl.cursorHeight = cursorHeight;
2026 cursorImpl.lineHeight = lineHeight;
2029 void Decorator::GetPosition(Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight) const
2031 const Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2033 x = cursorImpl.position.x;
2034 y = cursorImpl.position.y;
2035 cursorHeight = cursorImpl.cursorHeight;
2036 lineHeight = cursorImpl.lineHeight;
2039 const Vector2& Decorator::GetPosition(Cursor cursor) const
2041 return mImpl->mCursor[cursor].position;
2044 void Decorator::SetGlyphOffset(Cursor cursor, float glyphOffset)
2046 Impl::CursorImpl& cursorImpl = mImpl->mCursor[cursor];
2048 cursorImpl.glyphOffset = glyphOffset;
2051 const float Decorator::GetGlyphOffset(Cursor cursor) const
2053 return mImpl->mCursor[cursor].glyphOffset;
2056 void Decorator::SetCursorColor(Cursor cursor, const Dali::Vector4& color)
2058 mImpl->mCursor[cursor].color = color;
2061 const Dali::Vector4& Decorator::GetColor(Cursor cursor) const
2063 return mImpl->mCursor[cursor].color;
2066 void Decorator::StartCursorBlink()
2068 if(!mImpl->mCursorBlinkTimer)
2070 mImpl->mCursorBlinkTimer = Timer::New(mImpl->mCursorBlinkInterval);
2071 mImpl->mCursorBlinkTimer.TickSignal().Connect(mImpl, &Decorator::Impl::OnCursorBlinkTimerTick);
2074 if(!mImpl->mCursorBlinkTimer.IsRunning())
2076 mImpl->mCursorBlinkTimer.Start();
2080 void Decorator::StopCursorBlink()
2082 if(mImpl->mCursorBlinkTimer)
2084 mImpl->mCursorBlinkTimer.Stop();
2087 mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
2090 void Decorator::DelayCursorBlink()
2092 mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
2093 mImpl->mDelayCursorBlink = true;
2096 void Decorator::SetCursorBlinkInterval(float seconds)
2098 mImpl->mCursorBlinkInterval = static_cast<unsigned int>(seconds * TO_MILLISECONDS); // Convert to milliseconds
2101 float Decorator::GetCursorBlinkInterval() const
2103 return static_cast<float>(mImpl->mCursorBlinkInterval) * TO_SECONDS;
2106 void Decorator::SetCursorBlinkDuration(float seconds)
2108 mImpl->mCursorBlinkDuration = seconds;
2111 float Decorator::GetCursorBlinkDuration() const
2113 return mImpl->mCursorBlinkDuration;
2116 void Decorator::SetCursorWidth(int width)
2118 mImpl->mCursorWidth = static_cast<float>(width);
2121 int Decorator::GetCursorWidth() const
2123 return static_cast<int>(mImpl->mCursorWidth);
2126 void Decorator::SetEditable(bool editable)
2128 mImpl->mHidePrimaryCursorAndGrabHandle = !editable;
2129 // If editable is false, all decorators should be disabled.
2132 if(IsHighlightActive())
2134 SetHighlightActive(false);
2136 if(IsHandleActive(LEFT_SELECTION_HANDLE))
2138 SetHandleActive(LEFT_SELECTION_HANDLE, false);
2140 if(IsHandleActive(RIGHT_SELECTION_HANDLE))
2142 SetHandleActive(RIGHT_SELECTION_HANDLE, false);
2146 SetPopupActive(false);
2150 mImpl->Relayout(mImpl->mControlSize);
2154 void Decorator::SetHandleActive(HandleType handleType, bool active)
2156 mImpl->mHandle[handleType].active = active;
2160 if((LEFT_SELECTION_HANDLE == handleType) || (RIGHT_SELECTION_HANDLE == handleType))
2162 mImpl->mIsHandlePreviouslyCrossed = false;
2165 // TODO: this is a work-around.
2166 // The problem is the handle actor does not receive the touch event with the Interrupt
2167 // state when the power button is pressed and the application goes to background.
2168 mImpl->mHandle[handleType].pressed = false;
2169 const bool imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED].size();
2170 ImageView imageView = mImpl->mHandle[handleType].actor;
2171 if(imageReleased && imageView)
2173 imageView.SetImage(mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED]);
2178 bool Decorator::IsHandleActive(HandleType handleType) const
2180 return mImpl->mHandle[handleType].active;
2183 void Decorator::SetHandleImage(HandleType handleType, HandleImageType handleImageType, const std::string& imageFileName)
2185 mImpl->SetHandleImage(handleType, handleImageType, imageFileName);
2188 const std::string& Decorator::GetHandleImage(HandleType handleType, HandleImageType handleImageType) const
2190 return mImpl->mHandleImages[handleType][handleImageType];
2193 void Decorator::SetHandleColor(const Vector4& color)
2195 mImpl->mHandleColor = color;
2198 const Vector4& Decorator::GetHandleColor() const
2200 return mImpl->mHandleColor;
2203 void Decorator::SetPosition(HandleType handleType, float x, float y, float height)
2205 // Adjust handle's displacement
2206 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2208 handle.position.x = x;
2209 handle.position.y = y;
2210 handle.lineHeight = height;
2212 if(mImpl->mSmoothHandlePanEnabled)
2214 handle.grabDisplacementX = 0.f;
2215 handle.grabDisplacementY = 0.f;
2219 void Decorator::GetPosition(HandleType handleType, float& x, float& y, float& height) const
2221 Impl::HandleImpl& handle = mImpl->mHandle[handleType];
2223 x = handle.position.x;
2224 y = handle.position.y;
2225 height = handle.lineHeight;
2228 const Vector2& Decorator::GetPosition(HandleType handleType) const
2230 return mImpl->mHandle[handleType].position;
2233 void Decorator::FlipHandleVertically(HandleType handleType, bool flip)
2235 mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
2238 bool Decorator::IsHandleVerticallyFlipped(HandleType handleType) const
2240 return mImpl->mHandle[handleType].verticallyFlippedPreferred;
2243 void Decorator::FlipSelectionHandlesOnCrossEnabled(bool enable)
2245 mImpl->mFlipSelectionHandlesOnCross = enable;
2248 void Decorator::SetSelectionHandleFlipState(bool indicesSwapped, bool left, bool right)
2250 mImpl->mIsHandleCurrentlyCrossed = indicesSwapped;
2251 mImpl->mFlipLeftSelectionHandleDirection = left;
2252 mImpl->mFlipRightSelectionHandleDirection = right;
2255 void Decorator::AddHighlight(unsigned int index, const Vector4& quad)
2257 *(mImpl->mHighlightQuadList.Begin() + index) = quad;
2260 void Decorator::SetHighLightBox(const Vector2& position, const Size& size, float outlineOffset)
2262 mImpl->mHighlightPosition = position;
2263 mImpl->mHighlightSize = size;
2264 mImpl->mHighlightOutlineOffset = outlineOffset;
2267 void Decorator::ClearHighlights()
2269 mImpl->mHighlightQuadList.Clear();
2270 mImpl->mHighlightPosition = Vector2::ZERO;
2271 mImpl->mHighlightOutlineOffset = 0.f;
2274 void Decorator::ResizeHighlightQuads(unsigned int numberOfQuads)
2276 mImpl->mHighlightQuadList.Resize(numberOfQuads);
2279 void Decorator::SetHighlightColor(const Vector4& color)
2281 mImpl->mHighlightColor = color;
2284 const Vector4& Decorator::GetHighlightColor() const
2286 return mImpl->mHighlightColor;
2289 void Decorator::SetHighlightActive(bool active)
2291 mImpl->mIsHighlightBoxActive = active;
2294 bool Decorator::IsHighlightActive() const
2296 return mImpl->mIsHighlightBoxActive;
2299 bool Decorator::IsHighlightVisible() const
2301 return (mImpl->mHighlightActor && mImpl->mHighlightActor.GetParent());
2304 void Decorator::SetTextDepth(int textDepth)
2306 mImpl->mTextDepth = textDepth;
2309 void Decorator::SetPopupActive(bool active)
2311 mImpl->mActiveCopyPastePopup = active;
2314 bool Decorator::IsPopupActive() const
2316 return mImpl->mActiveCopyPastePopup;
2319 void Decorator::SetEnabledPopupButtons(TextSelectionPopup::Buttons& enabledButtonsBitMask)
2321 mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
2322 mImpl->CreateSelectionPopup();
2323 mImpl->mCopyPastePopup.actor.EnableButtons(mImpl->mEnabledPopupButtons);
2326 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
2328 return mImpl->mEnabledPopupButtons;
2331 void Decorator::SetSelectionPopupStyle(const Property::Map& options)
2333 mImpl->CreateSelectionPopup();
2334 mImpl->mCopyPastePopup.actor.SetProperties(options);
2337 void Decorator::GetSelectionPopupStyle(Property::Map& options)
2339 if(mImpl->mCopyPastePopup.actor)
2341 mImpl->mCopyPastePopup.actor.GetProperties(options);
2347 void Decorator::SetScrollThreshold(float threshold)
2349 mImpl->SetScrollThreshold(threshold);
2352 float Decorator::GetScrollThreshold() const
2354 return mImpl->GetScrollThreshold();
2357 void Decorator::SetScrollSpeed(float speed)
2359 mImpl->SetScrollSpeed(speed);
2362 float Decorator::GetScrollSpeed() const
2364 return mImpl->GetScrollSpeed();
2367 void Decorator::NotifyEndOfScroll()
2369 mImpl->NotifyEndOfScroll();
2372 void Decorator::SetHorizontalScrollEnabled(bool enable)
2374 mImpl->mHorizontalScrollingEnabled = enable;
2377 bool Decorator::IsHorizontalScrollEnabled() const
2379 return mImpl->mHorizontalScrollingEnabled;
2382 void Decorator::SetVerticalScrollEnabled(bool enable)
2384 mImpl->mVerticalScrollingEnabled = enable;
2387 bool Decorator::IsVerticalScrollEnabled() const
2389 return mImpl->mVerticalScrollingEnabled;
2392 void Decorator::SetSmoothHandlePanEnabled(bool enable)
2394 mImpl->mSmoothHandlePanEnabled = enable;
2397 bool Decorator::IsSmoothHandlePanEnabled() const
2399 return mImpl->mSmoothHandlePanEnabled;
2402 Decorator::~Decorator()
2407 Decorator::Decorator(ControllerInterface& controller,
2408 TextSelectionPopupCallbackInterface& callbackInterface)
2411 mImpl = new Decorator::Impl(controller, callbackInterface);
2416 } // namespace Toolkit