X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Fdecorator%2Ftext-decorator.cpp;h=b2fcda09370a3f38e59bfd92b486d60a9b202698;hp=4df7c51d6e999e74be5965c00fb7664b8e19660a;hb=e0c3ff90edd572ae3ba104d207deb1586eae4aed;hpb=fc2392c09ab526295f52c430ead7879e1e91a096 diff --git a/dali-toolkit/internal/text/decorator/text-decorator.cpp b/dali-toolkit/internal/text/decorator/text-decorator.cpp index 4df7c51..b2fcda0 100644 --- a/dali-toolkit/internal/text/decorator/text-decorator.cpp +++ b/dali-toolkit/internal/text/decorator/text-decorator.cpp @@ -19,147 +19,133 @@ #include // EXTERNAL INCLUDES +#include #include #include #include #include +#include +#include #include +#include #include #include +#include #include #include +#include +#include #include +#include #include #include -#include +#include #include -#include // INTERNAL INCLUDES #include #include +#include #include #include -#include +#include #ifdef DEBUG_ENABLED #define DECORATOR_DEBUG + +#endif + +namespace Dali +{ +namespace Internal +{ +namespace +{ +#ifdef DECORATOR_DEBUG +Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") ); #endif +} +} +} -// todo Move this to adaptor?? -#define GET_LOCALE_TEXT(string) dgettext("elementary", string) // Local Data namespace { -const char* DEFAULT_GRAB_HANDLE_IMAGE( DALI_IMAGE_DIR "insertpoint-icon.png" ); +const char* DEFAULT_GRAB_HANDLE_IMAGE_RELEASED( DALI_IMAGE_DIR "insertpoint-icon.png" ); +const char* DEFAULT_GRAB_HANDLE_IMAGE_PRESSED( DALI_IMAGE_DIR "insertpoint-icon-pressed.png" ); const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" ); const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" ); -//const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" ); -//const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" ); -const char* DEFAULT_CURSOR_IMAGE( DALI_IMAGE_DIR "decorator-cursor.png"); const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f ); const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f ); -const std::size_t CURSOR_BLINK_INTERVAL = 500; // Cursor blink interval -const std::size_t MILLISECONDS = 1000; - -const std::string POPUP_BACKGROUND( DALI_IMAGE_DIR "popup_bubble_bg.#.png" ); -const std::string POPUP_BACKGROUND_EFFECT( DALI_IMAGE_DIR "popup_bubble_bg_ef.#.png" ); -const std::string POPUP_BACKGROUND_LINE( DALI_IMAGE_DIR "popup_bubble_bg_line.#.png" ); - -const Dali::Vector4 DEFAULT_POPUP_BACKGROUND( Dali::Vector4( .20f, 0.29f, 0.44f, 1.0f ) ); -const Dali::Vector4 DEFAULT_POPUP_BACKGROUND_PRESSED( Dali::Vector4( 0.07f, 0.10f, 0.17f, 1.0f ) ); -const Dali::Vector4 DEFAULT_POPUP_LINE_COLOR( Dali::Vector4( 0.36f, 0.45f, 0.59f, 1.0f ) ); -const Dali::Vector4 DEFAULT_OPTION_ICON( Dali::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) ); -const Dali::Vector4 DEFAULT_OPTION_ICON_PRESSED( Dali::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) ); -const Dali::Vector4 DEFAULT_OPTION_TEXT( Dali::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) ); -const Dali::Vector4 DEFAULT_OPTION_TEXT_PRESSED( Dali::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) ); - -const std::string OPTION_ICON_CLIPBOARD( DALI_IMAGE_DIR "copy_paste_icon_clipboard.png" ); -const std::string OPTION_ICON_COPY( DALI_IMAGE_DIR "copy_paste_icon_copy.png" ); -const std::string OPTION_ICON_CUT( DALI_IMAGE_DIR "copy_paste_icon_cut.png" ); -const std::string OPTION_ICON_PASTE( DALI_IMAGE_DIR "copy_paste_icon_paste.png" ); -const std::string OPTION_ICON_SELECT( DALI_IMAGE_DIR "copy_paste_icon_select.png" ); -const std::string OPTION_ICON_SELECT_ALL( DALI_IMAGE_DIR "copy_paste_icon_select_all.png" ); - -const Dali::Vector2 DEFAULT_POPUP_MAX_SIZE( 470.0f, 120.0f ); ///< The maximum size of the popup. - -const float OPTION_TEXT_LINE_HEIGHT( 32.0f ); ///< The line height of the text. -const Dali::Vector2 OPTION_ICON_SIZE( 0.f, 0.f ); ///< The size of the icon. -const float OPTION_GAP_ICON_TEXT( 6.f ); ///< The gap between the icon and the text -const float OPTION_MARGIN_WIDTH( 10.f ); ///< The margin between the right or lefts edge and the text or icon. -const float OPTION_MAX_WIDTH( DEFAULT_POPUP_MAX_SIZE.width / 6 ); ///< The maximum width of the option (currently set to the max) -const float OPTION_MIN_WIDTH( 86.0f ); ///< The minimum width of the option. - -const float POPUP_DIVIDER_WIDTH( 1.f ); ///< The size of the divider. - -const Dali::Vector2 POPUP_TAIL_SIZE( 20.0f, 16.0f ); ///< The size of the tail. -const float POPUP_TAIL_Y_OFFSET( 5.f ); ///< The y offset of the tail (when its position is on the bottom). -const float POPUP_TAIL_TOP_Y_OFFSET( 3.f ); ///< The y offset of the tail (when its position is on the top). - -const float HIDE_POPUP_ANIMATION_DURATION( 0.2f ); ///< Duration of popup hide animation in seconds. -const float SHOW_POPUP_ANIMATION_DURATION( 0.2f ); ///< Duration of popup show animation in seconds. - -const char* const OPTION_SELECT_WORD = "option-select_word"; // "Select Word" popup option. -const char* const OPTION_SELECT_ALL("option-select_all"); // "Select All" popup option. -const char* const OPTION_CUT("option-cut"); // "Cut" popup option. -const char* const OPTION_COPY("option-copy"); // "Copy" popup option. -const char* const OPTION_PASTE("option-paste"); // "Paste" popup option. -const char* const OPTION_CLIPBOARD("option-clipboard"); // "Clipboard" popup option. - -enum Buttons -{ - ButtonsCut, - ButtonsCopy, - ButtonsPaste, - ButtonsSelect, - ButtonsSelectAll, - ButtonsClipboard, - ButtonsEnumEnd -}; +const Dali::Vector4 LIGHT_BLUE( (0xb2 / 255.0f), (0xeb / 255.0f), (0xf2 / 255.0f), 0.5f ); // The text highlight color. + +const unsigned int CURSOR_BLINK_INTERVAL = 500u; // Cursor blink interval +const float TO_MILLISECONDS = 1000.f; +const float TO_SECONDS = 1.f / TO_MILLISECONDS; + +const float DISPLAYED_HIGHLIGHT_Z_OFFSET( -0.05f ); -struct ButtonRequirement +const unsigned int SCROLL_TICK_INTERVAL = 50u; + +const float SCROLL_THRESHOLD = 10.f; +const float SCROLL_SPEED = 300.f; +const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS; + +/** + * structure to hold coordinates of each quad, which will make up the mesh. + */ +struct QuadCoordinates { - ButtonRequirement() - : id( ButtonsEnumEnd ), - priority( 0u ), - name(), - caption(), - icon(), - enabled( false ) - {} - - ButtonRequirement( Buttons buttonId, - std::size_t buttonPriority, - const std::string& buttonName, - const std::string& buttonCaption, - Dali::ResourceImage buttonIcon, - bool buttonEnabled ) - : id( buttonId ), - priority( buttonPriority ), - name( buttonName ), - caption( buttonCaption ), - icon( buttonIcon ), - enabled( buttonEnabled ) - {} - - Buttons id; - std::size_t priority; - std::string name; - std::string caption; - Dali::ResourceImage icon; - bool enabled; + /** + * Default constructor + */ + QuadCoordinates() + { + } + + /** + * Constructor + * @param[in] x1 left co-ordinate + * @param[in] y1 top co-ordinate + * @param[in] x2 right co-ordinate + * @param[in] y2 bottom co-ordinate + */ + QuadCoordinates(float x1, float y1, float x2, float y2) + : min(x1, y1), + max(x2, y2) + { + } + + Dali::Vector2 min; ///< top-left (minimum) position of quad + Dali::Vector2 max; ///< bottom-right (maximum) position of quad }; -// Comparison function for ButtonRequirement Priority -bool PriorityCompare( ButtonRequirement const& a, ButtonRequirement const& b) +typedef std::vector QuadContainer; + +/** + * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box. + * @param[in] boundingRectangle local bounding + * @param[out] Vector4 World coordinate bounding Box. + */ +void LocalToWorldCoordinatesBoundingBox( const Dali::Rect& boundingRectangle, Dali::Vector4& boundingBox ) { - return a.priority < b.priority; + // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications. + Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize(); + + const float originX = boundingRectangle.x - 0.5f * stageSize.width; + const float originY = boundingRectangle.y - 0.5f * stageSize.height; + + boundingBox = Dali::Vector4( originX, + originY, + originX + boundingRectangle.width, + originY + boundingRectangle.height ); } + } // end of namespace namespace Dali @@ -173,119 +159,79 @@ namespace Text struct Decorator::Impl : public ConnectionTracker { + enum ScrollDirection + { + SCROLL_NONE, + SCROLL_RIGHT, + SCROLL_LEFT, + SCROLL_TOP, + SCROLL_BOTTOM + }; + struct CursorImpl { CursorImpl() - : x(0.0f), - y(0.0f), - height(0.0f), - color(Dali::Color::WHITE) + : color( Dali::Color::BLACK ), + position(), + cursorHeight( 0.0f ), + lineHeight( 0.0f ) { } - float x; - float y; - float height; - Vector4 color; + Vector2 position; + float cursorHeight; + float lineHeight; }; - struct SelectionHandleImpl + struct HandleImpl { - SelectionHandleImpl() - : x(0.0f), - y(0.0f), - cursorHeight(0.0f), - flipped(false) + HandleImpl() + : position(), + lineHeight( 0.0f ), + grabDisplacementX( 0.f ), + grabDisplacementY( 0.f ), + active( false ), + visible( false ), + flipped( false ) { } - float x; - float y; - float cursorHeight; ///< Not the handle height - bool flipped; - ImageActor actor; Actor grabArea; - Image pressedImage; - Image releasedImage; - }; - - struct PopupImpl - { - PopupImpl() - : mVisiblePopUpSize( Vector2( 100.0f, 100.0f ) ), - mRequiredPopUpSize( Vector2( 100.0f, 100.0f ) ), - mMaxWidth( DEFAULT_POPUP_MAX_SIZE.width ), - mBackgroundColor( DEFAULT_POPUP_BACKGROUND ), - mBackgroundPressedColor( DEFAULT_POPUP_BACKGROUND_PRESSED ), - mLineColor( DEFAULT_POPUP_LINE_COLOR ), - mIconColor( DEFAULT_OPTION_ICON ), - mIconPressedColor( DEFAULT_OPTION_ICON_PRESSED ), - mTextColor( DEFAULT_OPTION_TEXT ), - mTextPressedColor( DEFAULT_OPTION_TEXT_PRESSED ) - { - } - - Actor mRoot; // The actor which all popup content is added to (i.e. panel and buttons) - Actor mButtons; // Actor which holds all the buttons, sensitivity can be set oActor buttons via this actor - Layer mStencilLayer; // Layer to enable clipping when buttons exceed popup - ImageActor mBackground; // The background popup panel - ImageActor mTail; // The tail for the popup - ImageActor mTailEffect; // the tail effect - ImageActor mTailLine; // The border/outline around the tail - - Size mVisiblePopUpSize; // Visible Size of popup excluding content that needs scrolling. - Size mRequiredPopUpSize; // Total size of popup including any invisible margin - float mMaxWidth; // Max width of the Popup - - Vector4 mNinePatchMargins; // Margins between the edge of the cropped image and the nine patch rect (left, right, top, bottom). - - Size mContentSize; // Size of Content (i.e. Buttons) - //Animation mAnimation; // Popup Hide/Show animation. - - std::vector mOrderListOfButtons; // List of buttons in the order to be displayed and a flag to indicate if needed. - - Vector4 mBackgroundColor; // Color of the background of the text input popup - Vector4 mBackgroundPressedColor; // Color of the option background. - Vector4 mLineColor; // Color of the line around the text input popup - Vector4 mIconColor; // Color of the popup icon. - Vector4 mIconPressedColor; // Color of the popup icon when pressed. - Vector4 mTextColor; // Color of the popup text. - Vector4 mTextPressedColor; // Color of the popup text when pressed. - - // Priority of Options/Buttons in the Cut and Paste pop-up, higher priority buttons are displayed first, left to right. - std::size_t mSelectOptionPriority; // Position of Select Button - std::size_t mSelectAllOptionPriority; // Position of Select All button - std::size_t mCutOptionPriority; // Position of Cut button - std::size_t mCopyOptionPriority; // Position of Copy button - std::size_t mPasteOptionPriority; // Position of Paste button - std::size_t mClipboardOptionPriority; // Position of Clipboard button - - bool mShowIcons; // Flag to show icons + Vector2 position; + float lineHeight; ///< Not the handle height + float grabDisplacementX; + float grabDisplacementY; + bool active : 1; + bool visible : 1; + bool flipped : 1; }; - - Impl( Dali::Toolkit::Internal::Control& parent, Observer& observer ) - : mTextControlParent(parent), - mObserver(observer), - mActiveCursor(ACTIVE_CURSOR_NONE), - mActiveGrabHandle(false), - mActiveSelection( false ), - mActiveCopyPastePopup( false ), + Impl( ControllerInterface& controller ) + : mController( controller ), + mBoundingBox( Rect() ), + mHighlightColor( LIGHT_BLUE ), + mActiveCursor( ACTIVE_CURSOR_NONE ), mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ), mCursorBlinkDuration( 0.0f ), + mHandleScrolling( HANDLE_TYPE_COUNT ), + mScrollDirection( SCROLL_NONE ), + mScrollThreshold( SCROLL_THRESHOLD ), + mScrollSpeed( SCROLL_SPEED ), + mScrollDistance( SCROLL_DISTANCE ), + mActiveCopyPastePopup( false ), mCursorBlinkStatus( true ), - mGrabDisplacementX( 0.0f ), - mGrabDisplacementY( 0.0f ), - mBoundingBox( Rect() ) + mPrimaryCursorVisible( false ), + mSecondaryCursorVisible( false ), + mSwapSelectionHandles( false ) { } /** * Relayout of the decorations owned by the decorator. - * @param[in] size The Size of the UI control the decorater is adding it's decorations to. + * @param[in] size The Size of the UI control the decorator is adding it's decorations to. */ void Relayout( const Vector2& size ) { @@ -296,66 +242,206 @@ struct Decorator::Impl : public ConnectionTracker CreateCursors(); if( mPrimaryCursor ) { - mPrimaryCursor.SetPosition( mCursor[PRIMARY_CURSOR].x, mCursor[PRIMARY_CURSOR].y ); + const CursorImpl& cursor = mCursor[PRIMARY_CURSOR]; + mPrimaryCursorVisible = ( cursor.position.x <= size.width ) && ( cursor.position.x >= 0.f ); + if( mPrimaryCursorVisible ) + { + Vector2 position = cursor.position; + if( GRAB_HANDLE == mHandleScrolling ) + { + if( mScrollDirection == SCROLL_RIGHT ) + { + position.x = 0.f; + } + else + { + position.x = size.width; + } + } + + mPrimaryCursor.SetPosition( position.x, + position.y ); + mPrimaryCursor.SetSize( Size( 1.0f, cursor.cursorHeight ) ); + } + mPrimaryCursor.SetVisible( mPrimaryCursorVisible ); } if( mSecondaryCursor ) { - mSecondaryCursor.SetPosition( mCursor[SECONDARY_CURSOR].x, mCursor[SECONDARY_CURSOR].y ); + const CursorImpl& cursor = mCursor[SECONDARY_CURSOR]; + mSecondaryCursorVisible = ( cursor.position.x <= size.width ) && ( cursor.position.x >= 0.f ); + if( mSecondaryCursorVisible ) + { + mSecondaryCursor.SetPosition( cursor.position.x, + cursor.position.y ); + mSecondaryCursor.SetSize( Size( 1.0f, cursor.cursorHeight ) ); + } + mSecondaryCursor.SetVisible( mSecondaryCursorVisible ); } // Show or hide the grab handle - if( mActiveGrabHandle ) + HandleImpl& grabHandle = mHandle[GRAB_HANDLE]; + if( grabHandle.active ) { - SetupTouchEvents(); + Vector2 position = grabHandle.position; + + if( GRAB_HANDLE == mHandleScrolling ) + { + if( mScrollDirection == SCROLL_RIGHT ) + { + position.x = 0.f; + } + else + { + position.x = size.width; + } + } + + const bool isVisible = ( position.x <= size.width ) && ( position.x >= 0.f ); - CreateGrabHandle(); + if( isVisible ) + { + SetupTouchEvents(); - mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].x, mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height ); + CreateGrabHandle(); + + grabHandle.actor.SetPosition( position.x, + position.y + grabHandle.lineHeight ); + } + grabHandle.actor.SetVisible( isVisible ); } - else if( mGrabHandle ) + else if( grabHandle.actor ) { - UnparentAndReset( mGrabHandle ); + UnparentAndReset( grabHandle.actor ); } // Show or hide the selection handles/highlight - if( mActiveSelection ) + HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ]; + HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ]; + if( primary.active || secondary.active ) { - SetupTouchEvents(); + Vector2 primaryPosition = primary.position; + Vector2 secondaryPosition = secondary.position; + + if( LEFT_SELECTION_HANDLE == mHandleScrolling ) + { + if( mScrollDirection == SCROLL_RIGHT ) + { + primaryPosition.x = 0.f; + } + else + { + primaryPosition.x = size.width; + } + } + else if( RIGHT_SELECTION_HANDLE == mHandleScrolling ) + { + if( mScrollDirection == SCROLL_RIGHT ) + { + secondaryPosition.x = 0.f; + } + else + { + secondaryPosition.x = size.width; + } + } + + const bool isPrimaryVisible = ( primaryPosition.x <= size.width ) && ( primaryPosition.x >= 0.f ); + const bool isSecondaryVisible = ( secondaryPosition.x <= size.width ) && ( secondaryPosition.x >= 0.f ); + + if( isPrimaryVisible || isSecondaryVisible ) + { + SetupTouchEvents(); + + CreateSelectionHandles(); + + if( isPrimaryVisible ) + { + primary.actor.SetPosition( primaryPosition.x, + primaryPosition.y + primary.lineHeight ); + + const bool flip = mSwapSelectionHandles ^ primary.flipped; + primary.actor.SetImage( flip ? mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] : mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ); + + primary.actor.SetAnchorPoint( flip ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT ); + } - CreateSelectionHandles(); + if( isSecondaryVisible ) + { + secondary.actor.SetPosition( secondaryPosition.x, + secondaryPosition.y + secondary.lineHeight ); - SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ]; - primary.actor.SetPosition( primary.x, primary.y + primary.cursorHeight ); + const bool flip = mSwapSelectionHandles ^ secondary.flipped; - SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ]; - secondary.actor.SetPosition( secondary.x, secondary.y + secondary.cursorHeight ); + secondary.actor.SetImage( ( mSwapSelectionHandles ^ secondary.flipped ) ? mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] : mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ); + secondary.actor.SetAnchorPoint( flip ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT ); + } + } + primary.actor.SetVisible( isPrimaryVisible ); + secondary.actor.SetVisible( isSecondaryVisible ); - //CreateHighlight(); TODO + CreateHighlight(); + UpdateHighlight(); } else { - UnparentAndReset( mSelectionHandle[ PRIMARY_SELECTION_HANDLE ].actor ); - UnparentAndReset( mSelectionHandle[ SECONDARY_SELECTION_HANDLE ].actor ); + UnparentAndReset( primary.actor ); + UnparentAndReset( secondary.actor ); + UnparentAndReset( mHighlightMeshActor ); } if ( mActiveCopyPastePopup ) { - CreatePopup(); - mCopyPastePopup.mRoot.SetPosition( Vector3( 180.0f, -100.0f, 0.0f ) ); //todo grabhandle or selection handle postions to be used + if ( !mCopyPastePopup ) + { + mCopyPastePopup = TextSelectionPopup::New(); +#ifdef DECORATOR_DEBUG + mCopyPastePopup.SetName("mCopyPastePopup"); +#endif + mCopyPastePopup.SetAnchorPoint( AnchorPoint::CENTER ); + mCopyPastePopup.OnRelayoutSignal().Connect( this, &Decorator::Impl::PopUpRelayoutComplete ); // Position popup after size negotiation + mActiveLayer.Add ( mCopyPastePopup ); + } } else { - DestroyPopup(); + if ( mCopyPastePopup ) + { + UnparentAndReset( mCopyPastePopup ); + } } } - void CreateCursor( ImageActor& cursor ) + void UpdatePositions( const Vector2& scrollOffset ) { - if ( !mCursorImage ) - { - mCursorImage = ResourceImage::New( DEFAULT_CURSOR_IMAGE ); - } - cursor = ImageActor::New( mCursorImage ); + mCursor[PRIMARY_CURSOR].position += scrollOffset; + mCursor[SECONDARY_CURSOR].position += scrollOffset; + mHandle[ GRAB_HANDLE ].position += scrollOffset; + mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset; + mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset; + mHighlightPosition += scrollOffset; + } + + void PopUpRelayoutComplete( Actor actor ) + { + // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box. + + mCopyPastePopup.OnRelayoutSignal().Disconnect( this, &Decorator::Impl::PopUpRelayoutComplete ); + + Vector3 popupPosition( mCursor[PRIMARY_CURSOR].position.x, mCursor[PRIMARY_CURSOR].position.y -100.0f , 0.0f); //todo 100 to be an offset Property + + Vector3 popupSize = Vector3( mCopyPastePopup.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.GetRelayoutSize( Dimension::HEIGHT ), 0.0f ); + + GetConstrainedPopupPosition( popupPosition, popupSize, AnchorPoint::CENTER, mActiveLayer, mBoundingBox ); + + SetUpPopUpPositionNotifications(); + + mCopyPastePopup.SetPosition( popupPosition ); //todo grabhandle(cursor) or selection handle positions to be used + } + + void CreateCursor( ImageActor& cursor, const Vector4& color ) + { + cursor = CreateSolidColorActor( color ); + cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one. cursor.SetAnchorPoint( AnchorPoint::TOP_CENTER ); } @@ -375,11 +461,11 @@ struct Decorator::Impl : public ConnectionTracker { if ( !mPrimaryCursor ) { - CreateCursor( mPrimaryCursor ); + CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color ); #ifdef DECORATOR_DEBUG mPrimaryCursor.SetName( "PrimaryCursorActor" ); #endif - mActiveLayer.Add( mPrimaryCursor); + mActiveLayer.Add( mPrimaryCursor ); } } @@ -387,13 +473,17 @@ struct Decorator::Impl : public ConnectionTracker { if ( !mSecondaryCursor ) { - CreateCursor( mSecondaryCursor ); + CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color ); #ifdef DECORATOR_DEBUG mSecondaryCursor.SetName( "SecondaryCursorActor" ); #endif - mActiveLayer.Add( mSecondaryCursor); + mActiveLayer.Add( mSecondaryCursor ); } } + else + { + UnparentAndReset( mSecondaryCursor ); + } } } @@ -402,11 +492,11 @@ struct Decorator::Impl : public ConnectionTracker // Cursor blinking if ( mPrimaryCursor ) { - mPrimaryCursor.SetVisible( mCursorBlinkStatus ); + mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus ); } if ( mSecondaryCursor ) { - mSecondaryCursor.SetVisible( mCursorBlinkStatus ); + mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus ); } mCursorBlinkStatus = !mCursorBlinkStatus; @@ -433,19 +523,17 @@ struct Decorator::Impl : public ConnectionTracker { if( !mActiveLayer ) { - Actor parent = mTextControlParent.Self(); - mActiveLayer = Layer::New(); #ifdef DECORATOR_DEBUG mActiveLayer.SetName ( "ActiveLayerActor" ); #endif - mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER); - mActiveLayer.SetParentOrigin( ParentOrigin::CENTER); - mActiveLayer.SetSizeMode( SIZE_EQUAL_TO_PARENT ); + mActiveLayer.SetParentOrigin( ParentOrigin::CENTER ); + mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS ); mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION ); - parent.Add( mActiveLayer ); + // Add the active layer telling the controller it doesn't need clipping. + mController.AddDecoration( mActiveLayer, false ); } mActiveLayer.RaiseToTop(); @@ -453,52 +541,66 @@ struct Decorator::Impl : public ConnectionTracker void CreateGrabHandle() { - if( !mGrabHandle ) + HandleImpl& grabHandle = mHandle[GRAB_HANDLE]; + if( !grabHandle.actor ) { - if ( !mGrabHandleImage ) + if( !mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] ) + { + mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE_RELEASED ); + } + if( !mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_PRESSED] ) { - mGrabHandleImage = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE ); + mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_PRESSED] = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE_PRESSED ); } - mGrabHandle = ImageActor::New( mGrabHandleImage ); + grabHandle.actor = ImageActor::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] ); + grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + grabHandle.actor.SetDrawMode( DrawMode::OVERLAY ); + // Area that Grab handle responds to, larger than actual handle so easier to move #ifdef DECORATOR_DEBUG - mGrabHandle.SetName( "GrabHandleActor" ); + grabHandle.actor.SetName( "GrabHandleActor" ); + if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) ) + { + grabHandle.grabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 ); + grabHandle.grabArea.SetName( "GrabArea" ); + } + else + { + grabHandle.grabArea = Actor::New(); + grabHandle.grabArea.SetName( "GrabArea" ); + } +#else + grabHandle.grabArea = Actor::New(); #endif - mGrabHandle.SetParentOrigin( ParentOrigin::TOP_LEFT ); - mGrabHandle.SetAnchorPoint( AnchorPoint::TOP_CENTER ); - mGrabHandle.SetDrawMode( DrawMode::OVERLAY ); - mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move -#ifdef DECORATOR_DEBUG - mGrabArea.SetName( "GrabArea" ); -#endif - mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); - mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT ); - mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ); - mGrabHandle.Add(mGrabArea); + grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER ); + grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS ); + grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ); + grabHandle.actor.Add( grabHandle.grabArea ); - mTapDetector.Attach( mGrabArea ); - mPanGestureDetector.Attach( mGrabArea ); + grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched ); + mTapDetector.Attach( grabHandle.grabArea ); + mPanGestureDetector.Attach( grabHandle.grabArea ); - mActiveLayer.Add(mGrabHandle); + mActiveLayer.Add( grabHandle.actor ); } } void CreateSelectionHandles() { - SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ]; - if ( !primary.actor ) + HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ]; + if( !primary.actor ) { - if ( !primary.releasedImage ) + if( !mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ) { - primary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE ); + mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE ); } - primary.actor = ImageActor::New( primary.releasedImage ); + primary.actor = ImageActor::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ); #ifdef DECORATOR_DEBUG primary.actor.SetName("SelectionHandleOne"); #endif - primary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT ); primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text. primary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text primary.flipped = false; @@ -507,7 +609,7 @@ struct Decorator::Impl : public ConnectionTracker #ifdef DECORATOR_DEBUG primary.grabArea.SetName("SelectionHandleOneGrabArea"); #endif - primary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT ); + primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS ); primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ); primary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); @@ -519,20 +621,19 @@ struct Decorator::Impl : public ConnectionTracker mActiveLayer.Add( primary.actor ); } - SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ]; - if ( !secondary.actor ) + HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ]; + if( !secondary.actor ) { - if ( !secondary.releasedImage ) + if( !mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ) { - secondary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO ); + mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO ); } - secondary.actor = ImageActor::New( secondary.releasedImage ); + secondary.actor = ImageActor::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] ); #ifdef DECORATOR_DEBUG secondary.actor.SetName("SelectionHandleTwo"); #endif - secondary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT ); - secondary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text. + secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text. secondary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text secondary.flipped = false; @@ -540,7 +641,7 @@ struct Decorator::Impl : public ConnectionTracker #ifdef DECORATOR_DEBUG secondary.grabArea.SetName("SelectionHandleTwoGrabArea"); #endif - secondary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT ); + secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS ); secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ); secondary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); @@ -551,384 +652,454 @@ struct Decorator::Impl : public ConnectionTracker secondary.actor.Add( secondary.grabArea ); mActiveLayer.Add( secondary.actor ); } - - //SetUpHandlePropertyNotifications(); TODO } - void OnTap( Actor actor, const TapGesture& tap ) + void CreateHighlight() { - if( actor == mGrabHandle ) + if ( !mHighlightMeshActor ) { - // TODO + mHighlightMaterial = Material::New( "HighlightMaterial" ); + mHighlightMaterial.SetDiffuseColor( mHighlightColor ); + + mHighlightMeshData.SetMaterial( mHighlightMaterial ); + mHighlightMeshData.SetHasNormals( true ); + + mHighlightMesh = Mesh::New( mHighlightMeshData ); + + mHighlightMeshActor = MeshActor::New( mHighlightMesh ); +#ifdef DECORATOR_DEBUG + mHighlightMeshActor.SetName( "HighlightMeshActor" ); +#endif + mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + + // Add the highlight box telling the controller it needs clipping. + mController.AddDecoration( mHighlightMeshActor, true ); } + + mHighlightMeshActor.SetPosition( mHighlightPosition.x, mHighlightPosition.y, DISPLAYED_HIGHLIGHT_Z_OFFSET ); } - void OnPan( Actor actor, const PanGesture& gesture ) + void UpdateHighlight() { - if( actor == mGrabArea ) + // Construct a Mesh with a texture to be used as the highlight 'box' for selected text + // + // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads. + // + // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ] + // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ] + // [ BOTTOM] [ MIDDLE ] [ MIDDLE ] + // [BOTTOM] [ MIDDLE ] + // [BOTTOM] + // + // Each quad is created as 2 triangles. + // Middle is just 1 quad regardless of its size. + // + // (0,0) (0,0) + // 0* *2 0* *2 + // TOP TOP + // 3* *1 3* *1 + // 4* *1 4* *6 + // MIDDLE BOTTOM + // 6* *5 7* *5 + // 6* *8 + // BOTTOM + // 9* *7 + // + + if ( mHighlightMesh && mHighlightMaterial && !mHighlightQuadList.empty() ) { - if( Gesture::Started == gesture.state ) - { - mGrabDisplacementX = mGrabDisplacementY = 0; - } + MeshData::VertexContainer vertices; + Dali::MeshData::FaceIndices faceIndices; - mGrabDisplacementX += gesture.displacement.x; - mGrabDisplacementY += gesture.displacement.y; + std::vector::iterator iter = mHighlightQuadList.begin(); + std::vector::iterator endIter = mHighlightQuadList.end(); - float x = mCursor[PRIMARY_CURSOR].x + mGrabDisplacementX; - float y = mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height*0.5f + mGrabDisplacementY; + // vertex position defaults to (0 0 0) + MeshData::Vertex vertex; + // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor. + vertex.nZ = 1.0f; - if( Gesture::Started == gesture.state || - Gesture::Continuing == gesture.state ) + for(std::size_t v = 0; iter != endIter; ++iter,v+=4 ) { - mObserver.GrabHandleEvent( GRAB_HANDLE_PRESSED, x, y ); + // Add each quad geometry (a sub-selection) to the mesh data. + + // 0-----1 + // |\ | + // | \ A | + // | \ | + // | B \ | + // | \| + // 2-----3 + + QuadCoordinates& quad = *iter; + // top-left (v+0) + vertex.x = quad.min.x; + vertex.y = quad.min.y; + vertices.push_back( vertex ); + + // top-right (v+1) + vertex.x = quad.max.x; + vertex.y = quad.min.y; + vertices.push_back( vertex ); + + // bottom-left (v+2) + vertex.x = quad.min.x; + vertex.y = quad.max.y; + vertices.push_back( vertex ); + + // bottom-right (v+3) + vertex.x = quad.max.x; + vertex.y = quad.max.y; + vertices.push_back( vertex ); + + // triangle A (3, 1, 0) + faceIndices.push_back( v + 3 ); + faceIndices.push_back( v + 1 ); + faceIndices.push_back( v ); + + // triangle B (0, 2, 3) + faceIndices.push_back( v ); + faceIndices.push_back( v + 2 ); + faceIndices.push_back( v + 3 ); + + mHighlightMeshData.SetFaceIndices( faceIndices ); } - else if( Gesture::Finished == gesture.state || - Gesture::Cancelled == gesture.state ) - { - mObserver.GrabHandleEvent( GRAB_HANDLE_RELEASED, x, y ); - } - } - } - bool OnHandleOneTouched( Actor actor, const TouchEvent& touch ) - { - // TODO - return false; + BoneContainer bones(0); // passed empty as bones not required + mHighlightMeshData.SetData( vertices, faceIndices, bones, mHighlightMaterial ); + mHighlightMesh.UpdateMeshData( mHighlightMeshData ); + } } - bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch ) + void OnTap( Actor actor, const TapGesture& tap ) { - // TODO - return false; + if( actor == mHandle[GRAB_HANDLE].actor ) + { + // TODO + } } - /** - * Popup - */ - - void CreateOrderedListOfPopupOptions() + void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture ) { - mCopyPastePopup.mOrderListOfButtons.clear(); - - // Create button for each possible option using Option priority - ResourceImage cutIcon = ResourceImage::New( OPTION_ICON_CUT ); - mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsCut, mCopyPastePopup.mCutOptionPriority, OPTION_CUT, GET_LOCALE_TEXT("IDS_COM_BODY_CUT"), cutIcon, true ) ); + if( Gesture::Started == gesture.state ) + { + handle.grabDisplacementX = handle.grabDisplacementY = 0; + } - ResourceImage copyIcon = ResourceImage::New( OPTION_ICON_COPY ); - mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsCopy, mCopyPastePopup.mCopyOptionPriority, OPTION_COPY, GET_LOCALE_TEXT("IDS_COM_BODY_COPY"), copyIcon, true ) ); + handle.grabDisplacementX += gesture.displacement.x; + handle.grabDisplacementY += gesture.displacement.y; - ResourceImage pasteIcon = ResourceImage::New( OPTION_ICON_PASTE ); - mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsPaste, mCopyPastePopup.mPasteOptionPriority, OPTION_PASTE, GET_LOCALE_TEXT("IDS_COM_BODY_PASTE"), pasteIcon, true ) ); + const float x = handle.position.x + handle.grabDisplacementX; + const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY; - ResourceImage selectIcon = ResourceImage::New( OPTION_ICON_SELECT ); - mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsSelect, mCopyPastePopup.mSelectOptionPriority, OPTION_SELECT_WORD, GET_LOCALE_TEXT("IDS_COM_SK_SELECT"), selectIcon, true ) ); + if( Gesture::Started == gesture.state || + Gesture::Continuing == gesture.state ) + { + Vector2 targetSize; + mController.GetTargetSize( targetSize ); - ResourceImage selectAllIcon = ResourceImage::New( OPTION_ICON_SELECT_ALL ); - mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsSelectAll, mCopyPastePopup.mSelectAllOptionPriority, OPTION_SELECT_ALL, GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL"), selectAllIcon, true ) ); + if( x < mScrollThreshold ) + { + mScrollDirection = SCROLL_RIGHT; + mHandleScrolling = type; + StartScrollTimer(); + } + else if( x > targetSize.width - mScrollThreshold ) + { + mScrollDirection = SCROLL_LEFT; + mHandleScrolling = type; + StartScrollTimer(); + } + else + { + mHandleScrolling = HANDLE_TYPE_COUNT; + StopScrollTimer(); + mController.DecorationEvent( type, HANDLE_PRESSED, x, y ); + } + } + else if( Gesture::Finished == gesture.state || + Gesture::Cancelled == gesture.state ) + { + if( mScrollTimer && mScrollTimer.IsRunning() ) + { + mHandleScrolling = HANDLE_TYPE_COUNT; + StopScrollTimer(); + mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y ); + } + else + { + mController.DecorationEvent( type, HANDLE_RELEASED, x, y ); + } + handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] ); + } + } - ResourceImage clipboardIcon = ResourceImage::New( OPTION_ICON_CLIPBOARD ); - mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsClipboard, mCopyPastePopup.mClipboardOptionPriority, OPTION_CLIPBOARD, GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD"), clipboardIcon, true ) ); + void OnPan( Actor actor, const PanGesture& gesture ) + { + HandleImpl& grabHandle = mHandle[GRAB_HANDLE]; + HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE]; + HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE]; - // Sort the buttons according their priorities. - std::sort( mCopyPastePopup.mOrderListOfButtons.begin(), mCopyPastePopup.mOrderListOfButtons.end(), PriorityCompare ); + if( actor == grabHandle.grabArea ) + { + DoPan( grabHandle, GRAB_HANDLE, gesture ); + } + else if( actor == primarySelectionHandle.grabArea ) + { + DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture ); + } + else if( actor == secondarySelectionHandle.grabArea ) + { + DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture ); + } } - void CreateBackground( PopupImpl& popup ) + bool OnGrabHandleTouched( Actor actor, const TouchEvent& event ) { - // Create background-panel if not already created (required if we have at least one option) - if ( !popup.mBackground ) + // Switch between pressed/release grab-handle images + if( event.GetPointCount() > 0 && + mHandle[GRAB_HANDLE].actor ) { - ResourceImage bgImg = ResourceImage::New( POPUP_BACKGROUND ); - popup.mBackground = ImageActor::New( bgImg ); - popup.mBackground.SetColor( popup.mBackgroundColor ); - popup.mBackground.SetParentOrigin( ParentOrigin::CENTER ); + const TouchPoint& point = event.GetPoint(0); - NinePatchImage backgroundImageNinePatch = NinePatchImage::DownCast( bgImg ); - if( backgroundImageNinePatch ) + if( TouchPoint::Down == point.state && + mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_PRESSED] ) + { + mHandle[GRAB_HANDLE].actor.SetImage( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_PRESSED] ); + } + else if( TouchPoint::Up == point.state && + mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] ) { - const Size ninePatchImageSize = Size( static_cast( bgImg.GetWidth() ), static_cast( bgImg.GetHeight() ) ); - Rect childRect = backgroundImageNinePatch.GetChildRectangle(); - - // -1u because of the cropping. - popup.mNinePatchMargins.x = childRect.x - 1u; - popup.mNinePatchMargins.y = ninePatchImageSize.width - ( childRect.x + childRect.width ) - 1u; - popup.mNinePatchMargins.z = childRect.y - 1u; - popup.mNinePatchMargins.w = ninePatchImageSize.height - ( childRect.y + childRect.height ) - 1u; + mHandle[GRAB_HANDLE].actor.SetImage( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] ); } + } - ResourceImage::Image bgEffectImg = ResourceImage::New( POPUP_BACKGROUND_EFFECT ); - ImageActor backgroundEffect = ImageActor::New( bgEffectImg ); - backgroundEffect.SetParentOrigin( ParentOrigin::CENTER ); + // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area + return true; + } - ResourceImage::Image bgLine = ResourceImage::New( POPUP_BACKGROUND_LINE ); - ImageActor backgroundLine = ImageActor::New( bgLine ); - backgroundLine.SetParentOrigin( ParentOrigin::CENTER ); - backgroundLine.SetColor( popup.mLineColor ); + bool OnHandleOneTouched( Actor actor, const TouchEvent& touch ) + { + // TODO + // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area + return true; + } - popup.mBackground.Add( backgroundEffect ); - popup.mBackground.Add( backgroundLine ); - } + bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch ) + { + // TODO + // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area + return true; } - void AddOption( Actor& parent, const std::string& name, const std::string& caption, const Image iconImage, bool finalOption, bool showIcons ) + // Popup + + float AlternatePopUpPositionRelativeToCursor() { - // 1. Create the backgrounds for the popup option both normal and pressed. - // Both containers will be added to a button. - Actor optionContainer = Actor::New(); - optionContainer.SetDrawMode( DrawMode::OVERLAY ); - //optionContainer.SetParentOrigin( ParentOrigin::CENTER ); - optionContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT ); - - ImageActor optionPressedContainer = Toolkit::CreateSolidColorActor( mCopyPastePopup.mBackgroundPressedColor ); - optionPressedContainer.SetDrawMode( DrawMode::OVERLAY ); - //optionPressedContainer.SetParentOrigin( ParentOrigin::CENTER ); - optionPressedContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT ); - - // 2. Add text. - Toolkit::TextLabel captionTextLabel = Toolkit::TextLabel::New(); - captionTextLabel.SetProperty( TextLabel::Property::TEXT, caption ); - optionContainer.Add( captionTextLabel ); - - Toolkit::TextLabel pressedCaptionTextLabel = Toolkit::TextLabel::New(); - pressedCaptionTextLabel.SetProperty( TextLabel::Property::TEXT, caption ); - optionPressedContainer.Add( pressedCaptionTextLabel ); - - // Calculates the icon/text position. - float iconTextOffsetY = 0.0f; - - if ( showIcons ) + float alternativePosition=0.0f;; + + if ( mPrimaryCursor ) // Secondary cursor not used for paste + { + Cursor cursor = PRIMARY_CURSOR; + alternativePosition = mCursor[cursor].position.y; + } + + const float popupHeight = 120.0f; // todo Set as a MaxSize Property in Control or retrieve from CopyPastePopup class. + + if( mHandle[GRAB_HANDLE].active ) { - // 3. Create the icons - ImageActor pressedIcon = ImageActor::New( iconImage ); - ImageActor icon = ImageActor::New( iconImage ); - - optionContainer.Add( icon ); - optionPressedContainer.Add( pressedIcon ); - - iconTextOffsetY = 0.5f * ( ( DEFAULT_POPUP_MAX_SIZE.height - mCopyPastePopup.mNinePatchMargins.z - mCopyPastePopup.mNinePatchMargins.w ) - ( OPTION_ICON_SIZE.height + OPTION_GAP_ICON_TEXT + OPTION_TEXT_LINE_HEIGHT ) ); - - icon.SetParentOrigin( ParentOrigin::TOP_CENTER ); - icon.SetAnchorPoint( AnchorPoint::TOP_CENTER ); - icon.SetY( iconTextOffsetY ); - - pressedIcon.SetParentOrigin( ParentOrigin::TOP_CENTER ); - pressedIcon.SetAnchorPoint( AnchorPoint::TOP_CENTER ); - pressedIcon.SetY( iconTextOffsetY ); - - // Layout icon + gap + text - captionTextLabel.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); - pressedCaptionTextLabel.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); - pressedCaptionTextLabel.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); - captionTextLabel.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); - pressedCaptionTextLabel.SetY( -iconTextOffsetY ); - captionTextLabel.SetY( -iconTextOffsetY ); + // If grab handle enabled then position pop-up below the grab handle. + const Vector2 grabHandleSize( 59.0f, 56.0f ); // todo + const float BOTTOM_HANDLE_BOTTOM_OFFSET = 1.5; //todo Should be a property + alternativePosition += grabHandleSize.height + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET ; } else { - // Centre option text - captionTextLabel.SetAnchorPoint( AnchorPoint::CENTER ); - captionTextLabel.SetParentOrigin( ParentOrigin::CENTER ); - pressedCaptionTextLabel.SetAnchorPoint( AnchorPoint::CENTER ); - pressedCaptionTextLabel.SetParentOrigin( ParentOrigin::CENTER ); + alternativePosition += popupHeight; } - // Calculate the size of the text. - Vector3 textSize = captionTextLabel.GetNaturalSize(); - textSize.width = std::min( textSize.width, OPTION_MAX_WIDTH - 2.f * OPTION_MARGIN_WIDTH ); - - // Set the size to the text. Text will be ellipsized if exceeds the max width. - captionTextLabel.SetSize( textSize ); - pressedCaptionTextLabel.SetSize( textSize ); - - // 4. Calculate the size of option. + return alternativePosition; + } - // The width is the max size of the text or the icon plus the margins clamped between the option min and max size. - // The height is the whole popup height minus the ninepatch margins. - const Vector2 optionSize( std::min( OPTION_MAX_WIDTH, std::max( OPTION_MIN_WIDTH, std::max( textSize.width, OPTION_ICON_SIZE.width ) + 2.f * OPTION_MARGIN_WIDTH ) ), - DEFAULT_POPUP_MAX_SIZE.height - mCopyPastePopup.mNinePatchMargins.z - mCopyPastePopup.mNinePatchMargins.w ); + void PopUpLeavesVerticalBoundary( PropertyNotification& source ) + { + float alternativeYPosition=0.0f; + // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted + // if can't be positioned above, then position below row. + alternativeYPosition = AlternatePopUpPositionRelativeToCursor(); - optionContainer.SetSize( optionSize ); - optionPressedContainer.SetSize( optionSize ); + mCopyPastePopup.SetY( alternativeYPosition ); + } - // 5. Create a option. - Toolkit::PushButton option = Toolkit::PushButton::New(); - option.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed ); - option.SetSize( optionSize ); - option.SetAnchorPoint( AnchorPoint::TOP_LEFT ); - option.SetX( mCopyPastePopup.mContentSize.width ); - option.SetName( name ); - option.SetAnimationTime( 0.0f ); - //option.ClickedSignal().Connect( this, &TextInputPopup::OnButtonPressed ); - parent.Add( option ); + void SetUpPopUpPositionNotifications( ) + { + // Note Property notifications ignore any set anchor point so conditions must allow for this. Default is Top Left. - // 6. Set the normal option image. - option.SetButtonImage( optionContainer ); + // Exceeding vertical boundary - // 7. Set the pressed option image - option.SetSelectedImage( optionPressedContainer ); + Vector4 worldCoordinatesBoundingBox; + LocalToWorldCoordinatesBoundingBox( mBoundingBox, worldCoordinatesBoundingBox ); - // 8. Update the content size. - mCopyPastePopup.mContentSize.width += optionSize.width; - mCopyPastePopup.mContentSize.height = std::max ( optionSize.height, mCopyPastePopup.mContentSize.height ); + float popupHeight = mCopyPastePopup.GetRelayoutSize( Dimension::HEIGHT); - // 9. Add the divider - if( !finalOption ) - { - const Size size( POPUP_DIVIDER_WIDTH, mCopyPastePopup.mContentSize.height ); - - ImageActor divider = Toolkit::CreateSolidColorActor( Color::WHITE ); - divider.SetSize (size); - divider.SetParentOrigin( ParentOrigin::TOP_LEFT ); - divider.SetAnchorPoint( AnchorPoint::TOP_LEFT ); - divider.SetPosition( mCopyPastePopup.mContentSize.width - POPUP_DIVIDER_WIDTH, 0.0f ); - parent.Add( divider ); - } - } + PropertyNotification verticalExceedNotification = mCopyPastePopup.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y, + OutsideCondition( worldCoordinatesBoundingBox.y + popupHeight/2, + worldCoordinatesBoundingBox.w - popupHeight/2 ) ); - void SetUpPopup( Actor& popupRootActor, Size& size ) - { - // Create Layer and Stencil. - popupRootActor = Actor::New(); - mCopyPastePopup.mStencilLayer = Layer::New(); - ImageActor stencil = CreateSolidColorActor( Color::RED ); - stencil.SetDrawMode( DrawMode::STENCIL ); - stencil.SetVisible( true ); - Actor scrollview = Actor::New(); - - //todo Use Size negotiation - mCopyPastePopup.mStencilLayer.SetSize( size ); // matches stencil size - popupRootActor.SetSize( size ); // matches stencil size - stencil.SetSize( size ); - scrollview.SetSize( size ); - mCopyPastePopup.mButtons.SetSize( size ); - - mCopyPastePopup.mStencilLayer.SetAnchorPoint(AnchorPoint::TOP_LEFT); - scrollview.SetAnchorPoint(AnchorPoint::TOP_LEFT); - mCopyPastePopup.mButtons.SetAnchorPoint( AnchorPoint::TOP_LEFT ); - - mActiveLayer.Add( mCopyPastePopup.mRoot ); - popupRootActor.Add( mCopyPastePopup.mBackground ); - popupRootActor.Add( mCopyPastePopup.mStencilLayer ); - mCopyPastePopup.mStencilLayer.Add( stencil ); - mCopyPastePopup.mStencilLayer.Add( scrollview ); - scrollview.Add( mCopyPastePopup.mButtons ); + verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary ); } - void AddPopupOptions( bool createTail, bool showIcons ) + void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, Vector3& popupSize, Vector3 anchorPoint, Actor& parent, Rect& boundingBox ) { - mCopyPastePopup.mShowIcons = showIcons; - - mCopyPastePopup.mContentSize = Vector2::ZERO; + DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() ) - mCopyPastePopup.mButtons = Actor::New(); + // Parent must already by added to Stage for these Get calls to work + Vector3 parentAnchorPoint = parent.GetCurrentAnchorPoint(); + Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize()*parentAnchorPoint; + Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition; // Parent World position plus popup local position gives World Position + Vector3 popupDistanceFromAnchorPoint = popupSize*anchorPoint; - // Add the options into the buttons container. + // Bounding rectangle is supplied as screen coordinates, bounding will be done in world coordinates. + Vector4 boundingRectangleWorld; + LocalToWorldCoordinatesBoundingBox( boundingBox, boundingRectangleWorld ); - // 1. Determine how many buttons are active and should be added to container. - std::size_t numberOfOptions = 0u; - for( std::vector::const_iterator it = mCopyPastePopup.mOrderListOfButtons.begin(), endIt = mCopyPastePopup.mOrderListOfButtons.end(); ( it != endIt ); ++it ) + // Calculate distance to move popup (in local space) so fits within the boundary + float xOffSetToKeepWithinBounds = 0.0f; + if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x ) { - const ButtonRequirement& button( *it ); - if( button.enabled ) - { - ++numberOfOptions; - } + xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x ); } - - // 2. Iterate list of buttons and add active ones. - std::size_t optionsAdded = 0u; - for( std::vector::const_iterator it = mCopyPastePopup.mOrderListOfButtons.begin(), endIt = mCopyPastePopup.mOrderListOfButtons.end(); ( it != endIt ); ++it ) + else if ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z ) { - const ButtonRequirement& button( *it ); - if ( button.enabled ) - { - ++optionsAdded; - AddOption( mCopyPastePopup.mButtons, button.name, button.caption, button.icon, optionsAdded == numberOfOptions, mCopyPastePopup.mShowIcons ); - } + xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x + popupDistanceFromAnchorPoint.x ); } - // Calculate the size of the whole popup which may not be all visible. - mCopyPastePopup.mRequiredPopUpSize = Size( std::min( mCopyPastePopup.mMaxWidth, mCopyPastePopup.mContentSize.width + mCopyPastePopup.mNinePatchMargins.x + mCopyPastePopup.mNinePatchMargins.y ), DEFAULT_POPUP_MAX_SIZE. height ); - - // Set the size of the background, background line and background effect. - mCopyPastePopup.mBackground.SetSize( mCopyPastePopup.mRequiredPopUpSize); - for( std::size_t index = 0u, childCount = mCopyPastePopup.mBackground.GetChildCount(); index < childCount; ++index ) + // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind. + if ( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y ) { - mCopyPastePopup.mBackground.GetChildAt( index ).SetSize( mCopyPastePopup.mRequiredPopUpSize ); + requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor(); } - // Size of the contents within the popup - mCopyPastePopup.mVisiblePopUpSize = Size( mCopyPastePopup.mRequiredPopUpSize.width - mCopyPastePopup.mNinePatchMargins.x - mCopyPastePopup.mNinePatchMargins.y, mCopyPastePopup.mRequiredPopUpSize.height - mCopyPastePopup.mNinePatchMargins.z - mCopyPastePopup.mNinePatchMargins.w ); + requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds; + } + + void SetScrollThreshold( float threshold ) + { + mScrollThreshold = threshold; + } + + float GetScrollThreshold() const + { + return mScrollThreshold; + } + + void SetScrollSpeed( float speed ) + { + mScrollSpeed = speed; + mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS; + } + float GetScrollSpeed() const + { + return mScrollSpeed; } - void CreatePopup() + /** + * Creates and starts a timer to scroll the text when handles are close to the edges of the text. + * + * It only starts the timer if it's already created. + */ + void StartScrollTimer() { - if ( !mCopyPastePopup.mRoot ) + if( !mScrollTimer ) { - mActiveCopyPastePopup = true; - CreateOrderedListOfPopupOptions(); //todo Currently causes all options to be shown - CreateBackground( mCopyPastePopup ); - AddPopupOptions( true, true ); - SetUpPopup( mCopyPastePopup.mRoot, mCopyPastePopup.mVisiblePopUpSize ); + mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL ); + mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick ); } - mCopyPastePopup.mStencilLayer.RaiseToTop(); + if( !mScrollTimer.IsRunning() ) + { + mScrollTimer.Start(); + } } - void DestroyPopup() + /** + * Stops the timer used to scroll the text. + */ + void StopScrollTimer() { - if ( mCopyPastePopup.mRoot ) + if( mScrollTimer ) { - mActiveCopyPastePopup = false; - UnparentAndReset( mCopyPastePopup.mButtons ); - UnparentAndReset( mCopyPastePopup.mRoot ); + mScrollTimer.Stop(); } } - Internal::Control& mTextControlParent; - Observer& mObserver; - - Layer mActiveLayer; // Layer for active handles and alike that ensures they are above all else. - - unsigned int mActiveCursor; - bool mActiveGrabHandle; - bool mActiveSelection; - bool mActiveCopyPastePopup; - - CursorImpl mCursor[CURSOR_COUNT]; - - Timer mCursorBlinkTimer; // Timer to signal cursor to blink - unsigned int mCursorBlinkInterval; - float mCursorBlinkDuration; - bool mCursorBlinkStatus; // Flag to switch between blink on and blink off - - ImageActor mPrimaryCursor; - ImageActor mSecondaryCursor; - - ImageActor mGrabHandle; - Actor mGrabArea; - float mGrabDisplacementX; - float mGrabDisplacementY; - - SelectionHandleImpl mSelectionHandle[SELECTION_HANDLE_COUNT]; - - PopupImpl mCopyPastePopup; - - Image mCursorImage; - Image mGrabHandleImage; + /** + * Callback called by the timer used to scroll the text. + * + * It calculates and sets a new scroll position. + */ + bool OnScrollTimerTick() + { + if( HANDLE_TYPE_COUNT != mHandleScrolling ) + { + mController.DecorationEvent( mHandleScrolling, + HANDLE_SCROLLING, + mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance, + 0.f ); + } - TapGestureDetector mTapDetector; - PanGestureDetector mPanGestureDetector; + return true; + } - Rect mBoundingBox; + ControllerInterface& mController; + + TapGestureDetector mTapDetector; + PanGestureDetector mPanGestureDetector; + Timer mCursorBlinkTimer; ///< Timer to signal cursor to blink + Timer mScrollTimer; ///< Timer used to scroll the text when the grab handle is moved close to the edges. + + Layer mActiveLayer; ///< Layer for active handles and alike that ensures they are above all else. + ImageActor mPrimaryCursor; + ImageActor mSecondaryCursor; + MeshActor mHighlightMeshActor; ///< Mesh Actor to display highlight + TextSelectionPopup mCopyPastePopup; + + Image mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT]; + Image mCursorImage; + Mesh mHighlightMesh; ///< Mesh for highlight + MeshData mHighlightMeshData; ///< Mesh Data for highlight + Material mHighlightMaterial; ///< Material used for highlight + + CursorImpl mCursor[CURSOR_COUNT]; + HandleImpl mHandle[HANDLE_TYPE_COUNT]; + QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight + Vector2 mHighlightPosition; ///< The position of the highlight actor. + + Rect mBoundingBox; + Vector4 mHighlightColor; ///< Color of the highlight + + unsigned int mActiveCursor; + unsigned int mCursorBlinkInterval; + float mCursorBlinkDuration; + HandleType mHandleScrolling; ///< The handle which is scrolling. + ScrollDirection mScrollDirection; ///< The direction of the scroll. + float mScrollThreshold; ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events. + float mScrollSpeed; ///< The scroll speed in pixels per second. + float mScrollDistance; ///< Distance the text scrolls during a scroll interval. + unsigned int mScrollInterval; ///< Time in milliseconds of a scroll interval. + + bool mActiveCopyPastePopup : 1; + bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off. + bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible. + bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible. + bool mSwapSelectionHandles : 1; ///< Whether to swap the selection handle images. }; -DecoratorPtr Decorator::New( Internal::Control& parent, Observer& observer ) +DecoratorPtr Decorator::New( ControllerInterface& controller ) { - return DecoratorPtr( new Decorator(parent, observer) ); + return DecoratorPtr( new Decorator(controller) ); } void Decorator::SetBoundingBox( const Rect& boundingBox ) @@ -946,6 +1117,11 @@ void Decorator::Relayout( const Vector2& size ) mImpl->Relayout( size ); } +void Decorator::UpdatePositions( const Vector2& scrollOffset ) +{ + mImpl->UpdatePositions( scrollOffset ); +} + /** Cursor **/ void Decorator::SetActiveCursor( ActiveCursor activeCursor ) @@ -958,32 +1134,25 @@ unsigned int Decorator::GetActiveCursor() const return mImpl->mActiveCursor; } -void Decorator::SetPosition( Cursor cursor, float x, float y, float height ) +void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight ) { - // Adjust grab handle displacement - mImpl->mGrabDisplacementX -= x - mImpl->mCursor[cursor].x; - mImpl->mGrabDisplacementY -= y - mImpl->mCursor[cursor].y; - - mImpl->mCursor[cursor].x = x; - mImpl->mCursor[cursor].y = y; - mImpl->mCursor[cursor].height = height; -} - -void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& height ) const -{ - x = mImpl->mCursor[cursor].x; - y = mImpl->mCursor[cursor].y; - height = mImpl->mCursor[cursor].height; + mImpl->mCursor[cursor].position.x = x; + mImpl->mCursor[cursor].position.y = y; + mImpl->mCursor[cursor].cursorHeight = cursorHeight; + mImpl->mCursor[cursor].lineHeight = lineHeight; } -void Decorator::SetCursorImage( Dali::Image image ) +void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const { - mImpl->mCursorImage = image; + x = mImpl->mCursor[cursor].position.x; + y = mImpl->mCursor[cursor].position.y; + cursorHeight = mImpl->mCursor[cursor].cursorHeight; + lineHeight = mImpl->mCursor[cursor].lineHeight; } -Dali::Image Decorator::GetCursorImage() const +const Vector2& Decorator::GetPosition( Cursor cursor ) const { - return mImpl->mCursorImage; + return mImpl->mCursor[cursor].position; } void Decorator::SetColor( Cursor cursor, const Dali::Vector4& color ) @@ -1020,12 +1189,12 @@ void Decorator::StopCursorBlink() void Decorator::SetCursorBlinkInterval( float seconds ) { - mImpl->mCursorBlinkInterval = seconds*MILLISECONDS; // Convert to milliseconds + mImpl->mCursorBlinkInterval = static_cast( seconds * TO_MILLISECONDS ); // Convert to milliseconds } float Decorator::GetCursorBlinkInterval() const { - return mImpl->mCursorBlinkInterval; + return static_cast( mImpl->mCursorBlinkInterval ) * TO_SECONDS; } void Decorator::SetCursorBlinkDuration( float seconds ) @@ -1038,90 +1207,111 @@ float Decorator::GetCursorBlinkDuration() const return mImpl->mCursorBlinkDuration; } -/** GrabHandle **/ +/** Handles **/ -void Decorator::SetGrabHandleActive( bool active ) +void Decorator::SetHandleActive( HandleType handleType, bool active ) { - mImpl->mActiveGrabHandle = active; + mImpl->mHandle[handleType].active = active; } -bool Decorator::IsGrabHandleActive() const +bool Decorator::IsHandleActive( HandleType handleType ) const { - return mImpl->mActiveGrabHandle; + return mImpl->mHandle[handleType].active ; } -void Decorator::SetGrabHandleImage( Dali::Image image ) +void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image ) { - mImpl->mGrabHandleImage = image; + mImpl->mHandleImages[handleType][handleImageType] = image; } -Dali::Image Decorator::GetGrabHandleImage() const +Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const { - return mImpl->mGrabHandleImage; + return mImpl->mHandleImages[handleType][handleImageType]; } -/** Selection **/ +void Decorator::SetPosition( HandleType handleType, float x, float y, float height ) +{ + // Adjust grab handle displacement + Impl::HandleImpl& handle = mImpl->mHandle[handleType]; + + handle.grabDisplacementX -= x - handle.position.x; + handle.grabDisplacementY -= y - handle.position.y; + + handle.position.x = x; + handle.position.y = y; + handle.lineHeight = height; +} -void Decorator::SetSelectionActive( bool active ) +void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const { - mImpl->mActiveSelection = active; + Impl::HandleImpl& handle = mImpl->mHandle[handleType]; + + x = handle.position.x; + y = handle.position.y; + height = handle.lineHeight; } -bool Decorator::IsSelectionActive() const +const Vector2& Decorator::GetPosition( HandleType handleType ) const { - return mImpl->mActiveSelection; + return mImpl->mHandle[handleType].position; } -void Decorator::SetPosition( SelectionHandle handle, float x, float y, float height ) +void Decorator::SwapSelectionHandlesEnabled( bool enable ) { - mImpl->mSelectionHandle[handle].x = x; - mImpl->mSelectionHandle[handle].y = y; - mImpl->mSelectionHandle[handle].cursorHeight = height; + mImpl->mSwapSelectionHandles = enable; } -void Decorator::GetPosition( SelectionHandle handle, float& x, float& y, float& height ) const +void Decorator::AddHighlight( float x1, float y1, float x2, float y2 ) { - x = mImpl->mSelectionHandle[handle].x; - y = mImpl->mSelectionHandle[handle].y; - height = mImpl->mSelectionHandle[handle].cursorHeight; + mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) ); } -void Decorator::SetImage( SelectionHandle handle, SelectionHandleState state, Dali::Image image ) +void Decorator::ClearHighlights() { - if( SELECTION_HANDLE_PRESSED == state ) - { - mImpl->mSelectionHandle[handle].pressedImage = image; - } - else - { - mImpl->mSelectionHandle[handle].releasedImage = image; - } + mImpl->mHighlightQuadList.clear(); + mImpl->mHighlightPosition = Vector2::ZERO; } -Dali::Image Decorator::GetImage( SelectionHandle handle, SelectionHandleState state ) const +void Decorator::SetHighlightColor( const Vector4& color ) { - if( SELECTION_HANDLE_PRESSED == state ) - { - return mImpl->mSelectionHandle[handle].pressedImage; - } + mImpl->mHighlightColor = color; +} - return mImpl->mSelectionHandle[handle].releasedImage; +const Vector4& Decorator::GetHighlightColor() const +{ + return mImpl->mHighlightColor; } -void Decorator::ShowPopup() +void Decorator::SetPopupActive( bool active ) { - if ( !mImpl->mCopyPastePopup.mRoot ) - { - mImpl->CreatePopup(); - } + mImpl->mActiveCopyPastePopup = active; } -void Decorator::HidePopup() +bool Decorator::IsPopupActive() const { - if ( mImpl->mCopyPastePopup.mRoot ) - { - mImpl->DestroyPopup(); - } + return mImpl->mActiveCopyPastePopup ; +} + +/** Scroll **/ + +void Decorator::SetScrollThreshold( float threshold ) +{ + mImpl->SetScrollThreshold( threshold ); +} + +float Decorator::GetScrollThreshold() const +{ + return mImpl->GetScrollThreshold(); +} + +void Decorator::SetScrollSpeed( float speed ) +{ + mImpl->SetScrollSpeed( speed ); +} + +float Decorator::GetScrollSpeed() const +{ + return mImpl->GetScrollSpeed(); } Decorator::~Decorator() @@ -1129,10 +1319,10 @@ Decorator::~Decorator() delete mImpl; } -Decorator::Decorator( Dali::Toolkit::Internal::Control& parent, Observer& observer ) +Decorator::Decorator( ControllerInterface& controller ) : mImpl( NULL ) { - mImpl = new Decorator::Impl( parent, observer ); + mImpl = new Decorator::Impl( controller ); } } // namespace Text