2 * Copyright (c) 2015 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/public-api/actors/actor.h>
23 #include <dali/public-api/adaptor-framework/timer.h>
24 #include <dali/public-api/actors/image-actor.h>
25 #include <dali/public-api/actors/layer.h>
26 #include <dali/public-api/common/constants.h>
27 #include <dali/public-api/events/tap-gesture.h>
28 #include <dali/public-api/events/tap-gesture-detector.h>
29 #include <dali/public-api/events/pan-gesture.h>
30 #include <dali/public-api/events/pan-gesture-detector.h>
31 #include <dali/public-api/images/resource-image.h>
32 #include <dali/public-api/math/vector2.h>
33 #include <dali/public-api/math/vector4.h>
34 #include <dali/public-api/images/nine-patch-image.h>
35 #include <dali/public-api/signals/connection-tracker.h>
39 #include <dali-toolkit/public-api/controls/control.h>
40 #include <dali-toolkit/public-api/controls/control-impl.h>
41 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
42 #include <dali-toolkit/public-api/controls/text-controls/text-label.h>
43 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
44 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
47 #define DECORATOR_DEBUG
50 // todo Move this to adaptor??
51 #define GET_LOCALE_TEXT(string) dgettext("elementary", string)
57 const char* DEFAULT_GRAB_HANDLE_IMAGE( DALI_IMAGE_DIR "insertpoint-icon.png" );
58 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
59 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
60 //const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
61 //const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
63 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
64 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
66 const std::size_t CURSOR_BLINK_INTERVAL = 500; // Cursor blink interval
67 const std::size_t MILLISECONDS = 1000;
69 const std::string POPUP_BACKGROUND( DALI_IMAGE_DIR "popup_bubble_bg.#.png" );
70 const std::string POPUP_BACKGROUND_EFFECT( DALI_IMAGE_DIR "popup_bubble_bg_ef.#.png" );
71 const std::string POPUP_BACKGROUND_LINE( DALI_IMAGE_DIR "popup_bubble_bg_line.#.png" );
73 const Dali::Vector4 DEFAULT_POPUP_BACKGROUND( Dali::Vector4( .20f, 0.29f, 0.44f, 1.0f ) );
74 const Dali::Vector4 DEFAULT_POPUP_BACKGROUND_PRESSED( Dali::Vector4( 0.07f, 0.10f, 0.17f, 1.0f ) );
75 const Dali::Vector4 DEFAULT_POPUP_LINE_COLOR( Dali::Vector4( 0.36f, 0.45f, 0.59f, 1.0f ) );
76 const Dali::Vector4 DEFAULT_OPTION_ICON( Dali::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
77 const Dali::Vector4 DEFAULT_OPTION_ICON_PRESSED( Dali::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
78 const Dali::Vector4 DEFAULT_OPTION_TEXT( Dali::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
79 const Dali::Vector4 DEFAULT_OPTION_TEXT_PRESSED( Dali::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
81 const std::string OPTION_ICON_CLIPBOARD( DALI_IMAGE_DIR "copy_paste_icon_clipboard.png" );
82 const std::string OPTION_ICON_COPY( DALI_IMAGE_DIR "copy_paste_icon_copy.png" );
83 const std::string OPTION_ICON_CUT( DALI_IMAGE_DIR "copy_paste_icon_cut.png" );
84 const std::string OPTION_ICON_PASTE( DALI_IMAGE_DIR "copy_paste_icon_paste.png" );
85 const std::string OPTION_ICON_SELECT( DALI_IMAGE_DIR "copy_paste_icon_select.png" );
86 const std::string OPTION_ICON_SELECT_ALL( DALI_IMAGE_DIR "copy_paste_icon_select_all.png" );
88 const Dali::Vector2 DEFAULT_POPUP_MAX_SIZE( 470.0f, 120.0f ); ///< The maximum size of the popup.
90 const float OPTION_TEXT_LINE_HEIGHT( 32.0f ); ///< The line height of the text.
91 const Dali::Vector2 OPTION_ICON_SIZE( 0.f, 0.f ); ///< The size of the icon.
92 const float OPTION_GAP_ICON_TEXT( 6.f ); ///< The gap between the icon and the text
93 const float OPTION_MARGIN_WIDTH( 10.f ); ///< The margin between the right or lefts edge and the text or icon.
94 const float OPTION_MAX_WIDTH( DEFAULT_POPUP_MAX_SIZE.width / 6 ); ///< The maximum width of the option (currently set to the max)
95 const float OPTION_MIN_WIDTH( 86.0f ); ///< The minimum width of the option.
97 const float POPUP_DIVIDER_WIDTH( 1.f ); ///< The size of the divider.
99 const Dali::Vector2 POPUP_TAIL_SIZE( 20.0f, 16.0f ); ///< The size of the tail.
100 const float POPUP_TAIL_Y_OFFSET( 5.f ); ///< The y offset of the tail (when its position is on the bottom).
101 const float POPUP_TAIL_TOP_Y_OFFSET( 3.f ); ///< The y offset of the tail (when its position is on the top).
103 const float HIDE_POPUP_ANIMATION_DURATION( 0.2f ); ///< Duration of popup hide animation in seconds.
104 const float SHOW_POPUP_ANIMATION_DURATION( 0.2f ); ///< Duration of popup show animation in seconds.
106 const char* const OPTION_SELECT_WORD = "option-select_word"; // "Select Word" popup option.
107 const char* const OPTION_SELECT_ALL("option-select_all"); // "Select All" popup option.
108 const char* const OPTION_CUT("option-cut"); // "Cut" popup option.
109 const char* const OPTION_COPY("option-copy"); // "Copy" popup option.
110 const char* const OPTION_PASTE("option-paste"); // "Paste" popup option.
111 const char* const OPTION_CLIPBOARD("option-clipboard"); // "Clipboard" popup option.
124 struct ButtonRequirement
127 : id( ButtonsEnumEnd ),
135 ButtonRequirement( Buttons buttonId,
136 std::size_t buttonPriority,
137 const std::string& buttonName,
138 const std::string& buttonCaption,
139 Dali::ResourceImage buttonIcon,
142 priority( buttonPriority ),
144 caption( buttonCaption ),
146 enabled( buttonEnabled )
150 std::size_t priority;
153 Dali::ResourceImage icon;
157 // Comparison function for ButtonRequirement Priority
158 bool PriorityCompare( ButtonRequirement const& a, ButtonRequirement const& b)
160 return a.priority < b.priority;
163 } // end of namespace
174 struct Decorator::Impl : public ConnectionTracker
182 color(Dali::Color::WHITE)
193 struct SelectionHandleImpl
195 SelectionHandleImpl()
205 float cursorHeight; ///< Not the handle height
218 : mVisiblePopUpSize( Vector2( 100.0f, 100.0f ) ),
219 mRequiredPopUpSize( Vector2( 100.0f, 100.0f ) ),
220 mMaxWidth( DEFAULT_POPUP_MAX_SIZE.width ),
221 mBackgroundColor( DEFAULT_POPUP_BACKGROUND ),
222 mBackgroundPressedColor( DEFAULT_POPUP_BACKGROUND_PRESSED ),
223 mLineColor( DEFAULT_POPUP_LINE_COLOR ),
224 mIconColor( DEFAULT_OPTION_ICON ),
225 mIconPressedColor( DEFAULT_OPTION_ICON_PRESSED ),
226 mTextColor( DEFAULT_OPTION_TEXT ),
227 mTextPressedColor( DEFAULT_OPTION_TEXT_PRESSED )
231 Actor mRoot; // The actor which all popup content is added to (i.e. panel and buttons)
232 Actor mButtons; // Actor which holds all the buttons, sensitivity can be set oActor buttons via this actor
233 Layer mStencilLayer; // Layer to enable clipping when buttons exceed popup
234 ImageActor mBackground; // The background popup panel
235 ImageActor mTail; // The tail for the popup
236 ImageActor mTailEffect; // the tail effect
237 ImageActor mTailLine; // The border/outline around the tail
239 Size mVisiblePopUpSize; // Visible Size of popup excluding content that needs scrolling.
240 Size mRequiredPopUpSize; // Total size of popup including any invisible margin
241 float mMaxWidth; // Max width of the Popup
243 Vector4 mNinePatchMargins; // Margins between the edge of the cropped image and the nine patch rect (left, right, top, bottom).
245 Size mContentSize; // Size of Content (i.e. Buttons)
246 //Animation mAnimation; // Popup Hide/Show animation.
248 std::vector<ButtonRequirement> mOrderListOfButtons; // List of buttons in the order to be displayed and a flag to indicate if needed.
250 Vector4 mBackgroundColor; // Color of the background of the text input popup
251 Vector4 mBackgroundPressedColor; // Color of the option background.
252 Vector4 mLineColor; // Color of the line around the text input popup
253 Vector4 mIconColor; // Color of the popup icon.
254 Vector4 mIconPressedColor; // Color of the popup icon when pressed.
255 Vector4 mTextColor; // Color of the popup text.
256 Vector4 mTextPressedColor; // Color of the popup text when pressed.
258 // Priority of Options/Buttons in the Cut and Paste pop-up, higher priority buttons are displayed first, left to right.
259 std::size_t mSelectOptionPriority; // Position of Select Button
260 std::size_t mSelectAllOptionPriority; // Position of Select All button
261 std::size_t mCutOptionPriority; // Position of Cut button
262 std::size_t mCopyOptionPriority; // Position of Copy button
263 std::size_t mPasteOptionPriority; // Position of Paste button
264 std::size_t mClipboardOptionPriority; // Position of Clipboard button
266 bool mShowIcons; // Flag to show icons
270 Impl( Dali::Toolkit::Internal::Control& parent, Observer& observer )
271 : mTextControlParent(parent),
273 mActiveCursor(ACTIVE_CURSOR_NONE),
274 mActiveGrabHandle(false),
275 mActiveSelection( false ),
276 mActiveCopyPastePopup( false ),
277 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
278 mCursorBlinkDuration( 0.0f ),
279 mCursorBlinkStatus( true ),
280 mGrabDisplacementX( 0.0f ),
281 mGrabDisplacementY( 0.0f ),
282 mBoundingBox( Rect<int>() )
287 * Relayout of the decorations owned by the decorator.
288 * @param[in] size The Size of the UI control the decorater is adding it's decorations to.
290 void Relayout( const Vector2& size, const Vector2& scrollPosition )
292 // TODO - Remove this if nothing is active
295 // Show or hide the cursors
299 mPrimaryCursor.SetPosition( mCursor[PRIMARY_CURSOR].x + scrollPosition.x,
300 mCursor[PRIMARY_CURSOR].y + scrollPosition.y );
301 mPrimaryCursor.SetSize( 1.0f, mCursor[PRIMARY_CURSOR].height );
303 if( mSecondaryCursor )
305 mSecondaryCursor.SetPosition( mCursor[SECONDARY_CURSOR].x + scrollPosition.x,
306 mCursor[SECONDARY_CURSOR].y + scrollPosition.y );
307 mSecondaryCursor.SetSize( 1.0f, mCursor[SECONDARY_CURSOR].height );
310 // Show or hide the grab handle
311 if( mActiveGrabHandle )
317 mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].x + scrollPosition.x,
318 mCursor[PRIMARY_CURSOR].y + scrollPosition.y + mCursor[PRIMARY_CURSOR].height );
320 else if( mGrabHandle )
322 UnparentAndReset( mGrabHandle );
325 // Show or hide the selection handles/highlight
326 if( mActiveSelection )
330 CreateSelectionHandles();
332 SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
333 primary.actor.SetPosition( primary.x + scrollPosition.x,
334 primary.y + scrollPosition.y + primary.cursorHeight );
336 SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
337 secondary.actor.SetPosition( secondary.x + scrollPosition.x,
338 secondary.y + scrollPosition.y + secondary.cursorHeight );
340 //CreateHighlight(); TODO
344 UnparentAndReset( mSelectionHandle[ PRIMARY_SELECTION_HANDLE ].actor );
345 UnparentAndReset( mSelectionHandle[ SECONDARY_SELECTION_HANDLE ].actor );
348 if ( mActiveCopyPastePopup )
351 mCopyPastePopup.mRoot.SetPosition( Vector3( 180.0f, -100.0f, 0.0f ) ); //todo grabhandle or selection handle postions to be used
359 void CreateCursor( ImageActor& cursor )
361 cursor = CreateSolidColorActor( Color::WHITE );
362 cursor.SetParentOrigin( ParentOrigin::TOP_LEFT );
363 cursor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
366 // Add or Remove cursor(s) from parent
369 if( mActiveCursor == ACTIVE_CURSOR_NONE )
371 UnparentAndReset( mPrimaryCursor );
372 UnparentAndReset( mSecondaryCursor );
376 /* Create Primary and or Secondary Cursor(s) if active and add to parent */
377 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
378 mActiveCursor == ACTIVE_CURSOR_BOTH )
380 if ( !mPrimaryCursor )
382 CreateCursor( mPrimaryCursor );
383 #ifdef DECORATOR_DEBUG
384 mPrimaryCursor.SetName( "PrimaryCursorActor" );
386 mActiveLayer.Add( mPrimaryCursor);
390 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
392 if ( !mSecondaryCursor )
394 CreateCursor( mSecondaryCursor );
395 #ifdef DECORATOR_DEBUG
396 mSecondaryCursor.SetName( "SecondaryCursorActor" );
398 mActiveLayer.Add( mSecondaryCursor);
404 bool OnCursorBlinkTimerTick()
407 if ( mPrimaryCursor )
409 mPrimaryCursor.SetVisible( mCursorBlinkStatus );
411 if ( mSecondaryCursor )
413 mSecondaryCursor.SetVisible( mCursorBlinkStatus );
416 mCursorBlinkStatus = !mCursorBlinkStatus;
421 void SetupTouchEvents()
425 mTapDetector = TapGestureDetector::New();
426 mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
429 if ( !mPanGestureDetector )
431 mPanGestureDetector = PanGestureDetector::New();
432 mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
436 void CreateActiveLayer()
440 Actor parent = mTextControlParent.Self();
442 mActiveLayer = Layer::New();
443 #ifdef DECORATOR_DEBUG
444 mActiveLayer.SetName ( "ActiveLayerActor" );
447 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
448 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
449 mActiveLayer.SetSizeMode( SIZE_EQUAL_TO_PARENT );
450 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
452 parent.Add( mActiveLayer );
455 mActiveLayer.RaiseToTop();
458 void CreateGrabHandle()
462 if ( !mGrabHandleImage )
464 mGrabHandleImage = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE );
467 mGrabHandle = ImageActor::New( mGrabHandleImage );
468 #ifdef DECORATOR_DEBUG
469 mGrabHandle.SetName( "GrabHandleActor" );
471 mGrabHandle.SetParentOrigin( ParentOrigin::TOP_LEFT );
472 mGrabHandle.SetAnchorPoint( AnchorPoint::TOP_CENTER );
473 mGrabHandle.SetDrawMode( DrawMode::OVERLAY );
475 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
476 #ifdef DECORATOR_DEBUG
477 mGrabArea.SetName( "GrabArea" );
479 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
480 mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
481 mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
482 mGrabHandle.Add(mGrabArea);
484 mTapDetector.Attach( mGrabArea );
485 mPanGestureDetector.Attach( mGrabArea );
487 mActiveLayer.Add(mGrabHandle);
491 void CreateSelectionHandles()
493 SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
494 if ( !primary.actor )
496 if ( !primary.releasedImage )
498 primary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
501 primary.actor = ImageActor::New( primary.releasedImage );
502 #ifdef DECORATOR_DEBUG
503 primary.actor.SetName("SelectionHandleOne");
505 primary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
506 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
507 primary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
508 primary.flipped = false;
510 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
511 #ifdef DECORATOR_DEBUG
512 primary.grabArea.SetName("SelectionHandleOneGrabArea");
514 primary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
515 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
516 primary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
518 mTapDetector.Attach( primary.grabArea );
519 mPanGestureDetector.Attach( primary.grabArea );
520 primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
522 primary.actor.Add( primary.grabArea );
523 mActiveLayer.Add( primary.actor );
526 SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
527 if ( !secondary.actor )
529 if ( !secondary.releasedImage )
531 secondary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
534 secondary.actor = ImageActor::New( secondary.releasedImage );
535 #ifdef DECORATOR_DEBUG
536 secondary.actor.SetName("SelectionHandleTwo");
538 secondary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
539 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
540 secondary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
541 secondary.flipped = false;
543 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
544 #ifdef DECORATOR_DEBUG
545 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
547 secondary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
548 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
549 secondary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
551 mTapDetector.Attach( secondary.grabArea );
552 mPanGestureDetector.Attach( secondary.grabArea );
553 secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
555 secondary.actor.Add( secondary.grabArea );
556 mActiveLayer.Add( secondary.actor );
559 //SetUpHandlePropertyNotifications(); TODO
562 void OnTap( Actor actor, const TapGesture& tap )
564 if( actor == mGrabHandle )
570 void OnPan( Actor actor, const PanGesture& gesture )
572 if( actor == mGrabArea )
574 if( Gesture::Started == gesture.state )
576 mGrabDisplacementX = mGrabDisplacementY = 0;
579 mGrabDisplacementX += gesture.displacement.x;
580 mGrabDisplacementY += gesture.displacement.y;
582 float x = mCursor[PRIMARY_CURSOR].x + mGrabDisplacementX;
583 float y = mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height*0.5f + mGrabDisplacementY;
585 if( Gesture::Started == gesture.state ||
586 Gesture::Continuing == gesture.state )
588 mObserver.GrabHandleEvent( GRAB_HANDLE_PRESSED, x, y );
590 else if( Gesture::Finished == gesture.state ||
591 Gesture::Cancelled == gesture.state )
593 mObserver.GrabHandleEvent( GRAB_HANDLE_RELEASED, x, y );
598 bool OnHandleOneTouched( Actor actor, const TouchEvent& touch )
604 bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch )
614 void CreateOrderedListOfPopupOptions()
616 mCopyPastePopup.mOrderListOfButtons.clear();
618 // Create button for each possible option using Option priority
619 ResourceImage cutIcon = ResourceImage::New( OPTION_ICON_CUT );
620 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsCut, mCopyPastePopup.mCutOptionPriority, OPTION_CUT, GET_LOCALE_TEXT("IDS_COM_BODY_CUT"), cutIcon, true ) );
622 ResourceImage copyIcon = ResourceImage::New( OPTION_ICON_COPY );
623 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsCopy, mCopyPastePopup.mCopyOptionPriority, OPTION_COPY, GET_LOCALE_TEXT("IDS_COM_BODY_COPY"), copyIcon, true ) );
625 ResourceImage pasteIcon = ResourceImage::New( OPTION_ICON_PASTE );
626 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsPaste, mCopyPastePopup.mPasteOptionPriority, OPTION_PASTE, GET_LOCALE_TEXT("IDS_COM_BODY_PASTE"), pasteIcon, true ) );
628 ResourceImage selectIcon = ResourceImage::New( OPTION_ICON_SELECT );
629 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsSelect, mCopyPastePopup.mSelectOptionPriority, OPTION_SELECT_WORD, GET_LOCALE_TEXT("IDS_COM_SK_SELECT"), selectIcon, true ) );
631 ResourceImage selectAllIcon = ResourceImage::New( OPTION_ICON_SELECT_ALL );
632 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsSelectAll, mCopyPastePopup.mSelectAllOptionPriority, OPTION_SELECT_ALL, GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL"), selectAllIcon, true ) );
634 ResourceImage clipboardIcon = ResourceImage::New( OPTION_ICON_CLIPBOARD );
635 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsClipboard, mCopyPastePopup.mClipboardOptionPriority, OPTION_CLIPBOARD, GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD"), clipboardIcon, true ) );
637 // Sort the buttons according their priorities.
638 std::sort( mCopyPastePopup.mOrderListOfButtons.begin(), mCopyPastePopup.mOrderListOfButtons.end(), PriorityCompare );
641 void CreateBackground( PopupImpl& popup )
643 // Create background-panel if not already created (required if we have at least one option)
644 if ( !popup.mBackground )
646 ResourceImage bgImg = ResourceImage::New( POPUP_BACKGROUND );
647 popup.mBackground = ImageActor::New( bgImg );
648 popup.mBackground.SetColor( popup.mBackgroundColor );
649 popup.mBackground.SetParentOrigin( ParentOrigin::CENTER );
651 NinePatchImage backgroundImageNinePatch = NinePatchImage::DownCast( bgImg );
652 if( backgroundImageNinePatch )
654 const Size ninePatchImageSize = Size( static_cast<float>( bgImg.GetWidth() ), static_cast<float>( bgImg.GetHeight() ) );
655 Rect<int> childRect = backgroundImageNinePatch.GetChildRectangle();
657 // -1u because of the cropping.
658 popup.mNinePatchMargins.x = childRect.x - 1u;
659 popup.mNinePatchMargins.y = ninePatchImageSize.width - ( childRect.x + childRect.width ) - 1u;
660 popup.mNinePatchMargins.z = childRect.y - 1u;
661 popup.mNinePatchMargins.w = ninePatchImageSize.height - ( childRect.y + childRect.height ) - 1u;
664 ResourceImage::Image bgEffectImg = ResourceImage::New( POPUP_BACKGROUND_EFFECT );
665 ImageActor backgroundEffect = ImageActor::New( bgEffectImg );
666 backgroundEffect.SetParentOrigin( ParentOrigin::CENTER );
668 ResourceImage::Image bgLine = ResourceImage::New( POPUP_BACKGROUND_LINE );
669 ImageActor backgroundLine = ImageActor::New( bgLine );
670 backgroundLine.SetParentOrigin( ParentOrigin::CENTER );
671 backgroundLine.SetColor( popup.mLineColor );
673 popup.mBackground.Add( backgroundEffect );
674 popup.mBackground.Add( backgroundLine );
678 void AddOption( Actor& parent, const std::string& name, const std::string& caption, const Image iconImage, bool finalOption, bool showIcons )
680 // 1. Create the backgrounds for the popup option both normal and pressed.
681 // Both containers will be added to a button.
682 Actor optionContainer = Actor::New();
683 optionContainer.SetDrawMode( DrawMode::OVERLAY );
684 //optionContainer.SetParentOrigin( ParentOrigin::CENTER );
685 optionContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT );
687 ImageActor optionPressedContainer = Toolkit::CreateSolidColorActor( mCopyPastePopup.mBackgroundPressedColor );
688 optionPressedContainer.SetDrawMode( DrawMode::OVERLAY );
689 //optionPressedContainer.SetParentOrigin( ParentOrigin::CENTER );
690 optionPressedContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT );
693 Toolkit::TextLabel captionTextLabel = Toolkit::TextLabel::New();
694 captionTextLabel.SetProperty( TextLabel::Property::TEXT, caption );
695 optionContainer.Add( captionTextLabel );
697 Toolkit::TextLabel pressedCaptionTextLabel = Toolkit::TextLabel::New();
698 pressedCaptionTextLabel.SetProperty( TextLabel::Property::TEXT, caption );
699 optionPressedContainer.Add( pressedCaptionTextLabel );
701 // Calculates the icon/text position.
702 float iconTextOffsetY = 0.0f;
706 // 3. Create the icons
707 ImageActor pressedIcon = ImageActor::New( iconImage );
708 ImageActor icon = ImageActor::New( iconImage );
710 optionContainer.Add( icon );
711 optionPressedContainer.Add( pressedIcon );
713 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 ) );
715 icon.SetParentOrigin( ParentOrigin::TOP_CENTER );
716 icon.SetAnchorPoint( AnchorPoint::TOP_CENTER );
717 icon.SetY( iconTextOffsetY );
719 pressedIcon.SetParentOrigin( ParentOrigin::TOP_CENTER );
720 pressedIcon.SetAnchorPoint( AnchorPoint::TOP_CENTER );
721 pressedIcon.SetY( iconTextOffsetY );
723 // Layout icon + gap + text
724 captionTextLabel.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
725 pressedCaptionTextLabel.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
726 pressedCaptionTextLabel.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
727 captionTextLabel.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
728 pressedCaptionTextLabel.SetY( -iconTextOffsetY );
729 captionTextLabel.SetY( -iconTextOffsetY );
733 // Centre option text
734 captionTextLabel.SetAnchorPoint( AnchorPoint::CENTER );
735 captionTextLabel.SetParentOrigin( ParentOrigin::CENTER );
736 pressedCaptionTextLabel.SetAnchorPoint( AnchorPoint::CENTER );
737 pressedCaptionTextLabel.SetParentOrigin( ParentOrigin::CENTER );
740 // Calculate the size of the text.
741 Vector3 textSize = captionTextLabel.GetNaturalSize();
742 textSize.width = std::min( textSize.width, OPTION_MAX_WIDTH - 2.f * OPTION_MARGIN_WIDTH );
744 // Set the size to the text. Text will be ellipsized if exceeds the max width.
745 captionTextLabel.SetSize( textSize );
746 pressedCaptionTextLabel.SetSize( textSize );
748 // 4. Calculate the size of option.
750 // The width is the max size of the text or the icon plus the margins clamped between the option min and max size.
751 // The height is the whole popup height minus the ninepatch margins.
752 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 ) ),
753 DEFAULT_POPUP_MAX_SIZE.height - mCopyPastePopup.mNinePatchMargins.z - mCopyPastePopup.mNinePatchMargins.w );
755 optionContainer.SetSize( optionSize );
756 optionPressedContainer.SetSize( optionSize );
758 // 5. Create a option.
759 Toolkit::PushButton option = Toolkit::PushButton::New();
760 option.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
761 option.SetSize( optionSize );
762 option.SetAnchorPoint( AnchorPoint::TOP_LEFT );
763 option.SetX( mCopyPastePopup.mContentSize.width );
764 option.SetName( name );
765 option.SetAnimationTime( 0.0f );
766 //option.ClickedSignal().Connect( this, &TextInputPopup::OnButtonPressed );
768 parent.Add( option );
770 // 6. Set the normal option image.
771 option.SetButtonImage( optionContainer );
773 // 7. Set the pressed option image
774 option.SetSelectedImage( optionPressedContainer );
776 // 8. Update the content size.
777 mCopyPastePopup.mContentSize.width += optionSize.width;
778 mCopyPastePopup.mContentSize.height = std::max ( optionSize.height, mCopyPastePopup.mContentSize.height );
780 // 9. Add the divider
783 const Size size( POPUP_DIVIDER_WIDTH, mCopyPastePopup.mContentSize.height );
785 ImageActor divider = Toolkit::CreateSolidColorActor( Color::WHITE );
786 divider.SetSize (size);
787 divider.SetParentOrigin( ParentOrigin::TOP_LEFT );
788 divider.SetAnchorPoint( AnchorPoint::TOP_LEFT );
789 divider.SetPosition( mCopyPastePopup.mContentSize.width - POPUP_DIVIDER_WIDTH, 0.0f );
790 parent.Add( divider );
794 void SetUpPopup( Actor& popupRootActor, Size& size )
796 // Create Layer and Stencil.
797 popupRootActor = Actor::New();
798 mCopyPastePopup.mStencilLayer = Layer::New();
799 ImageActor stencil = CreateSolidColorActor( Color::RED );
800 stencil.SetDrawMode( DrawMode::STENCIL );
801 stencil.SetVisible( true );
802 Actor scrollview = Actor::New();
804 //todo Use Size negotiation
805 mCopyPastePopup.mStencilLayer.SetSize( size ); // matches stencil size
806 popupRootActor.SetSize( size ); // matches stencil size
807 stencil.SetSize( size );
808 scrollview.SetSize( size );
809 mCopyPastePopup.mButtons.SetSize( size );
811 mCopyPastePopup.mStencilLayer.SetAnchorPoint(AnchorPoint::TOP_LEFT);
812 scrollview.SetAnchorPoint(AnchorPoint::TOP_LEFT);
813 mCopyPastePopup.mButtons.SetAnchorPoint( AnchorPoint::TOP_LEFT );
815 mActiveLayer.Add( mCopyPastePopup.mRoot );
816 popupRootActor.Add( mCopyPastePopup.mBackground );
817 popupRootActor.Add( mCopyPastePopup.mStencilLayer );
818 mCopyPastePopup.mStencilLayer.Add( stencil );
819 mCopyPastePopup.mStencilLayer.Add( scrollview );
820 scrollview.Add( mCopyPastePopup.mButtons );
823 void AddPopupOptions( bool createTail, bool showIcons )
825 mCopyPastePopup.mShowIcons = showIcons;
827 mCopyPastePopup.mContentSize = Vector2::ZERO;
829 mCopyPastePopup.mButtons = Actor::New();
831 // Add the options into the buttons container.
833 // 1. Determine how many buttons are active and should be added to container.
834 std::size_t numberOfOptions = 0u;
835 for( std::vector<ButtonRequirement>::const_iterator it = mCopyPastePopup.mOrderListOfButtons.begin(), endIt = mCopyPastePopup.mOrderListOfButtons.end(); ( it != endIt ); ++it )
837 const ButtonRequirement& button( *it );
844 // 2. Iterate list of buttons and add active ones.
845 std::size_t optionsAdded = 0u;
846 for( std::vector<ButtonRequirement>::const_iterator it = mCopyPastePopup.mOrderListOfButtons.begin(), endIt = mCopyPastePopup.mOrderListOfButtons.end(); ( it != endIt ); ++it )
848 const ButtonRequirement& button( *it );
849 if ( button.enabled )
852 AddOption( mCopyPastePopup.mButtons, button.name, button.caption, button.icon, optionsAdded == numberOfOptions, mCopyPastePopup.mShowIcons );
856 // Calculate the size of the whole popup which may not be all visible.
857 mCopyPastePopup.mRequiredPopUpSize = Size( std::min( mCopyPastePopup.mMaxWidth, mCopyPastePopup.mContentSize.width + mCopyPastePopup.mNinePatchMargins.x + mCopyPastePopup.mNinePatchMargins.y ), DEFAULT_POPUP_MAX_SIZE. height );
859 // Set the size of the background, background line and background effect.
860 mCopyPastePopup.mBackground.SetSize( mCopyPastePopup.mRequiredPopUpSize);
861 for( std::size_t index = 0u, childCount = mCopyPastePopup.mBackground.GetChildCount(); index < childCount; ++index )
863 mCopyPastePopup.mBackground.GetChildAt( index ).SetSize( mCopyPastePopup.mRequiredPopUpSize );
866 // Size of the contents within the popup
867 mCopyPastePopup.mVisiblePopUpSize = Size( mCopyPastePopup.mRequiredPopUpSize.width - mCopyPastePopup.mNinePatchMargins.x - mCopyPastePopup.mNinePatchMargins.y, mCopyPastePopup.mRequiredPopUpSize.height - mCopyPastePopup.mNinePatchMargins.z - mCopyPastePopup.mNinePatchMargins.w );
873 if ( !mCopyPastePopup.mRoot )
875 mActiveCopyPastePopup = true;
876 CreateOrderedListOfPopupOptions(); //todo Currently causes all options to be shown
877 CreateBackground( mCopyPastePopup );
878 AddPopupOptions( true, true );
879 SetUpPopup( mCopyPastePopup.mRoot, mCopyPastePopup.mVisiblePopUpSize );
882 mCopyPastePopup.mStencilLayer.RaiseToTop();
887 if ( mCopyPastePopup.mRoot )
889 mActiveCopyPastePopup = false;
890 UnparentAndReset( mCopyPastePopup.mButtons );
891 UnparentAndReset( mCopyPastePopup.mRoot );
895 Internal::Control& mTextControlParent;
898 Layer mActiveLayer; // Layer for active handles and alike that ensures they are above all else.
900 unsigned int mActiveCursor;
901 bool mActiveGrabHandle;
902 bool mActiveSelection;
903 bool mActiveCopyPastePopup;
905 CursorImpl mCursor[CURSOR_COUNT];
907 Timer mCursorBlinkTimer; // Timer to signal cursor to blink
908 unsigned int mCursorBlinkInterval;
909 float mCursorBlinkDuration;
910 bool mCursorBlinkStatus; // Flag to switch between blink on and blink off
912 ImageActor mPrimaryCursor;
913 ImageActor mSecondaryCursor;
915 ImageActor mGrabHandle;
917 float mGrabDisplacementX;
918 float mGrabDisplacementY;
920 SelectionHandleImpl mSelectionHandle[SELECTION_HANDLE_COUNT];
922 PopupImpl mCopyPastePopup;
925 Image mGrabHandleImage;
927 TapGestureDetector mTapDetector;
928 PanGestureDetector mPanGestureDetector;
930 Rect<int> mBoundingBox;
933 DecoratorPtr Decorator::New( Internal::Control& parent, Observer& observer )
935 return DecoratorPtr( new Decorator(parent, observer) );
938 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
940 mImpl->mBoundingBox = boundingBox;
943 const Rect<int>& Decorator::GetBoundingBox() const
945 return mImpl->mBoundingBox;
948 void Decorator::Relayout( const Vector2& size, const Vector2& scrollPosition )
950 mImpl->Relayout( size, scrollPosition );
955 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
957 mImpl->mActiveCursor = activeCursor;
960 unsigned int Decorator::GetActiveCursor() const
962 return mImpl->mActiveCursor;
965 void Decorator::SetPosition( Cursor cursor, float x, float y, float height )
967 // Adjust grab handle displacement
968 mImpl->mGrabDisplacementX -= x - mImpl->mCursor[cursor].x;
969 mImpl->mGrabDisplacementY -= y - mImpl->mCursor[cursor].y;
971 mImpl->mCursor[cursor].x = x;
972 mImpl->mCursor[cursor].y = y;
973 mImpl->mCursor[cursor].height = height;
976 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& height ) const
978 x = mImpl->mCursor[cursor].x;
979 y = mImpl->mCursor[cursor].y;
980 height = mImpl->mCursor[cursor].height;
983 void Decorator::SetColor( Cursor cursor, const Dali::Vector4& color )
985 mImpl->mCursor[cursor].color = color;
988 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
990 return mImpl->mCursor[cursor].color;
993 void Decorator::StartCursorBlink()
995 if ( !mImpl->mCursorBlinkTimer )
997 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
998 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1001 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1003 mImpl->mCursorBlinkTimer.Start();
1007 void Decorator::StopCursorBlink()
1009 if ( mImpl->mCursorBlinkTimer )
1011 mImpl->mCursorBlinkTimer.Stop();
1015 void Decorator::SetCursorBlinkInterval( float seconds )
1017 mImpl->mCursorBlinkInterval = seconds*MILLISECONDS; // Convert to milliseconds
1020 float Decorator::GetCursorBlinkInterval() const
1022 return mImpl->mCursorBlinkInterval;
1025 void Decorator::SetCursorBlinkDuration( float seconds )
1027 mImpl->mCursorBlinkDuration = seconds;
1030 float Decorator::GetCursorBlinkDuration() const
1032 return mImpl->mCursorBlinkDuration;
1037 void Decorator::SetGrabHandleActive( bool active )
1039 mImpl->mActiveGrabHandle = active;
1042 bool Decorator::IsGrabHandleActive() const
1044 return mImpl->mActiveGrabHandle;
1047 void Decorator::SetGrabHandleImage( Dali::Image image )
1049 mImpl->mGrabHandleImage = image;
1052 Dali::Image Decorator::GetGrabHandleImage() const
1054 return mImpl->mGrabHandleImage;
1059 void Decorator::SetSelectionActive( bool active )
1061 mImpl->mActiveSelection = active;
1064 bool Decorator::IsSelectionActive() const
1066 return mImpl->mActiveSelection;
1069 void Decorator::SetPosition( SelectionHandle handle, float x, float y, float height )
1071 mImpl->mSelectionHandle[handle].x = x;
1072 mImpl->mSelectionHandle[handle].y = y;
1073 mImpl->mSelectionHandle[handle].cursorHeight = height;
1076 void Decorator::GetPosition( SelectionHandle handle, float& x, float& y, float& height ) const
1078 x = mImpl->mSelectionHandle[handle].x;
1079 y = mImpl->mSelectionHandle[handle].y;
1080 height = mImpl->mSelectionHandle[handle].cursorHeight;
1083 void Decorator::SetImage( SelectionHandle handle, SelectionHandleState state, Dali::Image image )
1085 if( SELECTION_HANDLE_PRESSED == state )
1087 mImpl->mSelectionHandle[handle].pressedImage = image;
1091 mImpl->mSelectionHandle[handle].releasedImage = image;
1095 Dali::Image Decorator::GetImage( SelectionHandle handle, SelectionHandleState state ) const
1097 if( SELECTION_HANDLE_PRESSED == state )
1099 return mImpl->mSelectionHandle[handle].pressedImage;
1102 return mImpl->mSelectionHandle[handle].releasedImage;
1105 void Decorator::ShowPopup()
1107 if ( !mImpl->mCopyPastePopup.mRoot )
1109 mImpl->CreatePopup();
1113 void Decorator::HidePopup()
1115 if ( mImpl->mCopyPastePopup.mRoot )
1117 mImpl->DestroyPopup();
1121 Decorator::~Decorator()
1126 Decorator::Decorator( Dali::Toolkit::Internal::Control& parent, Observer& observer )
1129 mImpl = new Decorator::Impl( parent, observer );
1134 } // namespace Toolkit