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>
46 #define DECORATOR_DEBUG
49 // todo Move this to adaptor??
50 #define GET_LOCALE_TEXT(string) dgettext("elementary", string)
56 const char* DEFAULT_GRAB_HANDLE_IMAGE( DALI_IMAGE_DIR "insertpoint-icon.png" );
57 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
58 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
59 //const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
60 //const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
61 const char* DEFAULT_CURSOR_IMAGE( DALI_IMAGE_DIR "decorator-cursor.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 Layer 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 on all buttons via this actor
233 ImageActor mBackground; // The background popup panel
234 ImageActor mTail; // The tail for the popup
235 ImageActor mTailEffect; // the tail effect
236 ImageActor mTailLine; // The border/outline around the tail
238 Size mVisiblePopUpSize; // Visible Size of popup excluding content that needs scrolling.
239 Size mRequiredPopUpSize; // Total size of popup including any invisible margin
240 float mMaxWidth; // Max width of the Popup
242 Vector4 mNinePatchMargins; // Margins between the edge of the cropped image and the nine patch rect (left, right, top, bottom).
244 Size mContentSize; // Size of Content (i.e. Buttons)
245 //Animation mAnimation; // Popup Hide/Show animation.
247 std::vector<ButtonRequirement> mOrderListOfButtons; // List of buttons in the order to be displayed and a flag to indicate if needed.
249 Vector4 mBackgroundColor; // Color of the background of the text input popup
250 Vector4 mBackgroundPressedColor; // Color of the option background.
251 Vector4 mLineColor; // Color of the line around the text input popup
252 Vector4 mIconColor; // Color of the popup icon.
253 Vector4 mIconPressedColor; // Color of the popup icon when pressed.
254 Vector4 mTextColor; // Color of the popup text.
255 Vector4 mTextPressedColor; // Color of the popup text when pressed.
257 // Priority of Options/Buttons in the Cut and Paste pop-up, higher priority buttons are displayed first, left to right.
258 std::size_t mSelectOptionPriority; // Position of Select Button
259 std::size_t mSelectAllOptionPriority; // Position of Select All button
260 std::size_t mCutOptionPriority; // Position of Cut button
261 std::size_t mCopyOptionPriority; // Position of Copy button
262 std::size_t mPasteOptionPriority; // Position of Paste button
263 std::size_t mClipboardOptionPriority; // Position of Clipboard button
265 bool mShowIcons; // Flag to show icons
269 Impl( Dali::Toolkit::Internal::Control& parent, Observer& observer )
272 mActiveCursor(ACTIVE_CURSOR_NONE),
273 mActiveGrabHandle(false),
274 mActiveSelection( false ),
275 mActiveCopyPastePopup( false ),
276 mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
277 mCursorBlinkDuration( 0.0f ),
278 mCursorBlinkStatus( true ),
279 mGrabDisplacementX( 0.0f ),
280 mGrabDisplacementY( 0.0f ),
281 mBoundingBox( Rect<int>() )
286 * Relayout of the decorations owned by the decorator.
287 * @param[in] size The Size of the UI control the decorater is adding it's decorations to.
289 void Relayout( const Vector2& size )
293 // Show or hide the grab handle
294 if( mActiveGrabHandle )
301 mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].x, mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height );
303 else if( mGrabHandle )
305 UnparentAndReset( mGrabHandle );
308 // Show or hide the selection handles/highlight
309 if( mActiveSelection )
314 CreateSelectionHandles();
316 SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
317 primary.actor.SetPosition( primary.x, primary.y + primary.cursorHeight );
319 SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
320 secondary.actor.SetPosition( secondary.x, secondary.y + secondary.cursorHeight );
322 //CreateHighlight(); TODO
326 UnparentAndReset( mSelectionHandle[ PRIMARY_SELECTION_HANDLE ].actor );
327 UnparentAndReset( mSelectionHandle[ SECONDARY_SELECTION_HANDLE ].actor );
330 if ( mActiveCopyPastePopup )
334 mCopyPastePopup.mRoot.SetPosition( Vector3( 180.0f, -100.0f, 0.0f ) ); //todo grabhandle or selection handle postions to be used
342 void CreateCursor( ImageActor& cursor )
346 mCursorImage = ResourceImage::New( DEFAULT_CURSOR_IMAGE );
348 cursor = ImageActor::New( mCursorImage );
349 cursor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
352 // Add or Remove cursor(s) from parent
355 Actor parent = mParent.Self();
356 /* Create Primary and or Secondary Cursor(s) if active and add to parent */
357 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY )
359 if ( !mPrimaryCursor )
361 CreateCursor( mPrimaryCursor );
362 #ifdef DECORATOR_DEBUG
363 mPrimaryCursor.SetName( "PrimaryCursorActor" );
365 parent.Add( mPrimaryCursor);
368 mPrimaryCursor.SetPosition( mCursor[PRIMARY_CURSOR].x, mCursor[PRIMARY_CURSOR].y );
370 else if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
372 if ( !mSecondaryCursor )
374 CreateCursor( mSecondaryCursor );
375 #ifdef DECORATOR_DEBUG
376 mSecondaryCursor.SetName( "SecondaryCursorActor" );
378 parent.Add( mSecondaryCursor);
383 /* ACTIVE_CURSOR_NONE so unparent cursors*/
384 if ( mPrimaryCursor )
386 UnparentAndReset( mPrimaryCursor );
389 if ( mSecondaryCursor )
391 UnparentAndReset( mSecondaryCursor );
396 bool OnCursorBlinkTimerTick()
399 if ( ACTIVE_CURSOR_PRIMARY )
401 mPrimaryCursor.SetVisible( mCursorBlinkStatus );
403 else if ( ACTIVE_CURSOR_BOTH )
405 mPrimaryCursor.SetVisible( mCursorBlinkStatus );
406 mSecondaryCursor.SetVisible( mCursorBlinkStatus );
409 mCursorBlinkStatus = !mCursorBlinkStatus;
414 void SetupTouchEvents()
418 mTapDetector = TapGestureDetector::New();
419 mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
422 if ( !mPanGestureDetector )
424 mPanGestureDetector = PanGestureDetector::New();
425 mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
429 void CreateActiveLayer()
433 Actor parent = mParent.Self();
435 mActiveLayer = Layer::New();
436 #ifdef DECORATOR_DEBUG
437 mActiveLayer.SetName ( "ActiveLayerActor" );
440 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
441 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
442 mActiveLayer.SetSizeMode( SIZE_EQUAL_TO_PARENT );
443 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
445 parent.Add( mActiveLayer );
448 mActiveLayer.RaiseToTop();
451 void CreateGrabHandle()
455 if ( !mGrabHandleImage )
457 mGrabHandleImage = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE );
460 mGrabHandle = ImageActor::New( mGrabHandleImage );
461 #ifdef DECORATOR_DEBUG
462 mGrabHandle.SetName( "GrabHandleActor" );
464 mGrabHandle.SetParentOrigin( ParentOrigin::TOP_LEFT );
465 mGrabHandle.SetAnchorPoint( AnchorPoint::TOP_CENTER );
466 mGrabHandle.SetDrawMode( DrawMode::OVERLAY );
468 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
469 #ifdef DECORATOR_DEBUG
470 mGrabArea.SetName( "GrabArea" );
472 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
473 mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
474 mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
475 mGrabHandle.Add(mGrabArea);
477 mTapDetector.Attach( mGrabArea );
478 mPanGestureDetector.Attach( mGrabArea );
480 mActiveLayer.Add(mGrabHandle);
484 void CreateSelectionHandles()
486 SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
487 if ( !primary.actor )
489 if ( !primary.releasedImage )
491 primary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
494 primary.actor = ImageActor::New( primary.releasedImage );
495 #ifdef DECORATOR_DEBUG
496 primary.actor.SetName("SelectionHandleOne");
498 primary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
499 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
500 primary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
501 primary.flipped = false;
503 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
504 #ifdef DECORATOR_DEBUG
505 primary.grabArea.SetName("SelectionHandleOneGrabArea");
507 primary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
508 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
509 primary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
511 mTapDetector.Attach( primary.grabArea );
512 mPanGestureDetector.Attach( primary.grabArea );
513 primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
515 primary.actor.Add( primary.grabArea );
516 mActiveLayer.Add( primary.actor );
519 SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
520 if ( !secondary.actor )
522 if ( !secondary.releasedImage )
524 secondary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
527 secondary.actor = ImageActor::New( secondary.releasedImage );
528 #ifdef DECORATOR_DEBUG
529 secondary.actor.SetName("SelectionHandleTwo");
531 secondary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
532 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
533 secondary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
534 secondary.flipped = false;
536 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
537 #ifdef DECORATOR_DEBUG
538 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
540 secondary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
541 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
542 secondary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
544 mTapDetector.Attach( secondary.grabArea );
545 mPanGestureDetector.Attach( secondary.grabArea );
546 secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
548 secondary.actor.Add( secondary.grabArea );
549 mActiveLayer.Add( secondary.actor );
552 //SetUpHandlePropertyNotifications(); TODO
555 void OnTap( Actor actor, const TapGesture& tap )
557 if( actor == mGrabHandle )
563 void OnPan( Actor actor, const PanGesture& gesture )
565 if( actor == mGrabArea )
567 if( Gesture::Started == gesture.state )
569 mGrabDisplacementX = mGrabDisplacementY = 0;
572 mGrabDisplacementX += gesture.displacement.x;
573 mGrabDisplacementY += gesture.displacement.y;
575 float x = mCursor[PRIMARY_CURSOR].x + mGrabDisplacementX;
576 float y = mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height*0.5f + mGrabDisplacementY;
578 if( Gesture::Started == gesture.state ||
579 Gesture::Continuing == gesture.state )
581 mObserver.GrabHandleEvent( GRAB_HANDLE_PRESSED, x, y );
583 else if( Gesture::Finished == gesture.state ||
584 Gesture::Cancelled == gesture.state )
586 mObserver.GrabHandleEvent( GRAB_HANDLE_RELEASED, x, y );
591 bool OnHandleOneTouched( Actor actor, const TouchEvent& touch )
597 bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch )
607 void CreateOrderedListOfPopupOptions()
609 mCopyPastePopup.mOrderListOfButtons.clear();
611 // Create button for each possible option using Option priority
612 ResourceImage cutIcon = ResourceImage::New( OPTION_ICON_CUT );
613 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsCut, mCopyPastePopup.mCutOptionPriority, OPTION_CUT, GET_LOCALE_TEXT("IDS_COM_BODY_CUT"), cutIcon, true ) );
615 ResourceImage copyIcon = ResourceImage::New( OPTION_ICON_COPY );
616 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsCopy, mCopyPastePopup.mCopyOptionPriority, OPTION_COPY, GET_LOCALE_TEXT("IDS_COM_BODY_COPY"), copyIcon, true ) );
618 ResourceImage pasteIcon = ResourceImage::New( OPTION_ICON_PASTE );
619 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsPaste, mCopyPastePopup.mPasteOptionPriority, OPTION_PASTE, GET_LOCALE_TEXT("IDS_COM_BODY_PASTE"), pasteIcon, true ) );
621 ResourceImage selectIcon = ResourceImage::New( OPTION_ICON_SELECT );
622 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsSelect, mCopyPastePopup.mSelectOptionPriority, OPTION_SELECT_WORD, GET_LOCALE_TEXT("IDS_COM_SK_SELECT"), selectIcon, true ) );
624 ResourceImage selectAllIcon = ResourceImage::New( OPTION_ICON_SELECT_ALL );
625 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsSelectAll, mCopyPastePopup.mSelectAllOptionPriority, OPTION_SELECT_ALL, GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL"), selectAllIcon, true ) );
627 ResourceImage clipboardIcon = ResourceImage::New( OPTION_ICON_CLIPBOARD );
628 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsClipboard, mCopyPastePopup.mClipboardOptionPriority, OPTION_CLIPBOARD, GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD"), clipboardIcon, true ) );
630 // Sort the buttons according their priorities.
631 std::sort( mCopyPastePopup.mOrderListOfButtons.begin(), mCopyPastePopup.mOrderListOfButtons.end(), PriorityCompare );
634 void CreateBackground( PopupImpl& popup )
636 // Create background-panel if not already created (required if we have at least one option)
637 if ( !popup.mBackground )
639 ResourceImage bgImg = ResourceImage::New( POPUP_BACKGROUND );
640 popup.mBackground = ImageActor::New( bgImg );
641 popup.mBackground.SetColor( popup.mBackgroundColor );
642 popup.mBackground.SetParentOrigin( ParentOrigin::CENTER );
644 NinePatchImage backgroundImageNinePatch = NinePatchImage::DownCast( bgImg );
645 if( backgroundImageNinePatch )
647 const Size ninePatchImageSize = Size( static_cast<float>( bgImg.GetWidth() ), static_cast<float>( bgImg.GetHeight() ) );
648 Rect<int> childRect = backgroundImageNinePatch.GetChildRectangle();
650 // -1u because of the cropping.
651 popup.mNinePatchMargins.x = childRect.x - 1u;
652 popup.mNinePatchMargins.y = ninePatchImageSize.width - ( childRect.x + childRect.width ) - 1u;
653 popup.mNinePatchMargins.z = childRect.y - 1u;
654 popup.mNinePatchMargins.w = ninePatchImageSize.height - ( childRect.y + childRect.height ) - 1u;
657 ResourceImage::Image bgEffectImg = ResourceImage::New( POPUP_BACKGROUND_EFFECT );
658 ImageActor backgroundEffect = ImageActor::New( bgEffectImg );
659 backgroundEffect.SetParentOrigin( ParentOrigin::CENTER );
661 ResourceImage::Image bgLine = ResourceImage::New( POPUP_BACKGROUND_LINE );
662 ImageActor backgroundLine = ImageActor::New( bgLine );
663 backgroundLine.SetParentOrigin( ParentOrigin::CENTER );
664 backgroundLine.SetColor( popup.mLineColor );
666 popup.mBackground.Add( backgroundEffect );
667 popup.mBackground.Add( backgroundLine );
671 void AddOption( Actor& parent, const std::string& name, const std::string& caption, const Image iconImage, bool finalOption, bool showIcons )
673 // 1. Create the backgrounds for the popup option both normal and pressed.
674 // Both containers will be added to a button.
675 Actor optionContainer = Actor::New();
676 optionContainer.SetDrawMode( DrawMode::OVERLAY );
677 //optionContainer.SetParentOrigin( ParentOrigin::CENTER );
678 optionContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT );
680 ImageActor optionPressedContainer = Toolkit::CreateSolidColorActor( mCopyPastePopup.mBackgroundPressedColor );
681 optionPressedContainer.SetDrawMode( DrawMode::OVERLAY );
682 //optionPressedContainer.SetParentOrigin( ParentOrigin::CENTER );
683 optionPressedContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT );
686 Toolkit::TextLabel captionTextLabel = Toolkit::TextLabel::New();
687 captionTextLabel.SetProperty( TextLabel::Property::TEXT, caption );
688 optionContainer.Add( captionTextLabel );
690 Toolkit::TextLabel pressedCaptionTextLabel = Toolkit::TextLabel::New();
691 pressedCaptionTextLabel.SetProperty( TextLabel::Property::TEXT, caption );
692 optionPressedContainer.Add( pressedCaptionTextLabel );
694 // Calculates the icon/text position.
695 float iconTextOffsetY = 0.0f;
699 // 3. Create the icons
700 ImageActor pressedIcon = ImageActor::New( iconImage );
701 ImageActor icon = ImageActor::New( iconImage );
703 optionContainer.Add( icon );
704 optionPressedContainer.Add( pressedIcon );
706 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 ) );
708 icon.SetParentOrigin( ParentOrigin::TOP_CENTER );
709 icon.SetAnchorPoint( AnchorPoint::TOP_CENTER );
710 icon.SetY( iconTextOffsetY );
712 pressedIcon.SetParentOrigin( ParentOrigin::TOP_CENTER );
713 pressedIcon.SetAnchorPoint( AnchorPoint::TOP_CENTER );
714 pressedIcon.SetY( iconTextOffsetY );
716 // Layout icon + gap + text
717 captionTextLabel.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
718 pressedCaptionTextLabel.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
719 pressedCaptionTextLabel.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
720 captionTextLabel.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
721 pressedCaptionTextLabel.SetY( -iconTextOffsetY );
722 captionTextLabel.SetY( -iconTextOffsetY );
726 // Centre option text
727 captionTextLabel.SetAnchorPoint( AnchorPoint::CENTER );
728 captionTextLabel.SetParentOrigin( ParentOrigin::CENTER );
729 pressedCaptionTextLabel.SetAnchorPoint( AnchorPoint::CENTER );
730 pressedCaptionTextLabel.SetParentOrigin( ParentOrigin::CENTER );
733 // Calculate the size of the text.
734 Vector3 textSize = captionTextLabel.GetNaturalSize();
735 textSize.width = std::min( textSize.width, OPTION_MAX_WIDTH - 2.f * OPTION_MARGIN_WIDTH );
737 // Set the size to the text. Text will be ellipsized if exceeds the max width.
738 captionTextLabel.SetSize( textSize );
739 pressedCaptionTextLabel.SetSize( textSize );
741 // 4. Calculate the size of option.
743 // The width is the max size of the text or the icon plus the margins clamped between the option min and max size.
744 // The height is the whole popup height minus the ninepatch margins.
745 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 ) ),
746 DEFAULT_POPUP_MAX_SIZE.height - mCopyPastePopup.mNinePatchMargins.z - mCopyPastePopup.mNinePatchMargins.w );
748 optionContainer.SetSize( optionSize );
749 optionPressedContainer.SetSize( optionSize );
751 // 5. Create a option.
752 Toolkit::PushButton option = Toolkit::PushButton::New();
753 option.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
754 option.SetSize( optionSize );
755 option.SetAnchorPoint( AnchorPoint::TOP_LEFT );
756 option.SetX( mCopyPastePopup.mContentSize.width );
757 option.SetName( name );
758 option.SetAnimationTime( 0.0f );
759 //option.ClickedSignal().Connect( this, &TextInputPopup::OnButtonPressed );
761 parent.Add( option );
763 // 6. Set the normal option image.
764 option.SetButtonImage( optionContainer );
766 // 7. Set the pressed option image
767 option.SetSelectedImage( optionPressedContainer );
769 // 8. Update the content size.
770 mCopyPastePopup.mContentSize.width += optionSize.width;
771 mCopyPastePopup.mContentSize.height = std::max ( optionSize.height, mCopyPastePopup.mContentSize.height );
773 // 9. Add the divider
776 const Size size( POPUP_DIVIDER_WIDTH, mCopyPastePopup.mContentSize.height );
778 ImageActor divider = Toolkit::CreateSolidColorActor( Color::WHITE );
779 divider.SetSize (size);
780 divider.SetParentOrigin( ParentOrigin::TOP_LEFT );
781 divider.SetAnchorPoint( AnchorPoint::TOP_LEFT );
782 divider.SetPosition( mCopyPastePopup.mContentSize.width - POPUP_DIVIDER_WIDTH, 0.0f );
783 parent.Add( divider );
787 void SetUpPopup( Actor& parent, Size& size )
789 // Create Layer and Stencil.
790 parent = Layer::New();
791 Layer layer = Layer::New();
792 ImageActor stencil = CreateSolidColorActor( Color::RED );
793 stencil.SetDrawMode( DrawMode::STENCIL );
794 stencil.SetVisible( true );
795 Actor scrollview = Actor::New();
797 //todo Use Size negotiation
798 layer.SetSize( size ); // matches stencil size
799 parent.SetSize( size ); // matches stencil size
800 stencil.SetSize( size );
801 scrollview.SetSize( size );
802 mCopyPastePopup.mButtons.SetSize( size );
804 layer.SetAnchorPoint(AnchorPoint::TOP_LEFT);
805 scrollview.SetAnchorPoint(AnchorPoint::TOP_LEFT);
806 //mCopyPastePopup.mBackground.SetAnchorPoint(AnchorPoint::TOP_LEFT);
807 mCopyPastePopup.mButtons.SetAnchorPoint( AnchorPoint::TOP_LEFT );
809 parent.Add( mCopyPastePopup.mBackground );
811 layer.Add( stencil );
812 layer.Add( scrollview );
813 scrollview.Add( mCopyPastePopup.mButtons );
816 void AddPopupOptions( bool createTail, bool showIcons )
818 mCopyPastePopup.mShowIcons = showIcons;
820 mCopyPastePopup.mContentSize = Vector2::ZERO;
822 mCopyPastePopup.mButtons = Actor::New();
824 // Add the options into the buttons container.
826 // 1. Determine how many buttons are active and should be added to container.
827 std::size_t numberOfOptions = 0u;
828 for( std::vector<ButtonRequirement>::const_iterator it = mCopyPastePopup.mOrderListOfButtons.begin(), endIt = mCopyPastePopup.mOrderListOfButtons.end(); ( it != endIt ); ++it )
830 const ButtonRequirement& button( *it );
837 // 2. Iterate list of buttons and add active ones.
838 std::size_t optionsAdded = 0u;
839 for( std::vector<ButtonRequirement>::const_iterator it = mCopyPastePopup.mOrderListOfButtons.begin(), endIt = mCopyPastePopup.mOrderListOfButtons.end(); ( it != endIt ); ++it )
841 const ButtonRequirement& button( *it );
842 if ( button.enabled )
845 AddOption( mCopyPastePopup.mButtons, button.name, button.caption, button.icon, optionsAdded == numberOfOptions, mCopyPastePopup.mShowIcons );
849 // Calculate the size of the whole popup which may not be all visible.
850 mCopyPastePopup.mRequiredPopUpSize = Size( std::min( mCopyPastePopup.mMaxWidth, mCopyPastePopup.mContentSize.width + mCopyPastePopup.mNinePatchMargins.x + mCopyPastePopup.mNinePatchMargins.y ), DEFAULT_POPUP_MAX_SIZE. height );
852 // Set the size of the background, background line and background effect.
853 mCopyPastePopup.mBackground.SetSize( mCopyPastePopup.mRequiredPopUpSize);
854 for( std::size_t index = 0u, childCount = mCopyPastePopup.mBackground.GetChildCount(); index < childCount; ++index )
856 mCopyPastePopup.mBackground.GetChildAt( index ).SetSize( mCopyPastePopup.mRequiredPopUpSize );
859 // Size of the contents within the popup
860 mCopyPastePopup.mVisiblePopUpSize = Size( mCopyPastePopup.mRequiredPopUpSize.width - mCopyPastePopup.mNinePatchMargins.x - mCopyPastePopup.mNinePatchMargins.y, mCopyPastePopup.mRequiredPopUpSize.height - mCopyPastePopup.mNinePatchMargins.z - mCopyPastePopup.mNinePatchMargins.w );
866 if ( !mCopyPastePopup.mRoot )
868 mActiveCopyPastePopup = true;
869 CreateOrderedListOfPopupOptions(); //todo Currently causes all options to be shown
870 CreateBackground( mCopyPastePopup );
871 AddPopupOptions( true, true );
872 SetUpPopup( mCopyPastePopup.mRoot, mCopyPastePopup.mVisiblePopUpSize );
873 Actor parent = mParent.Self();
874 parent.Add( mCopyPastePopup.mRoot );
880 if ( mCopyPastePopup.mRoot )
882 mActiveCopyPastePopup = false;
883 UnparentAndReset( mCopyPastePopup.mButtons );
884 UnparentAndReset( mCopyPastePopup.mRoot );
888 Internal::Control& mParent;
891 Layer mActiveLayer; // Layer for active handles and alike that ensures they are above all else.
893 unsigned int mActiveCursor;
894 bool mActiveGrabHandle;
895 bool mActiveSelection;
896 bool mActiveCopyPastePopup;
898 CursorImpl mCursor[CURSOR_COUNT];
900 Timer mCursorBlinkTimer; // Timer to signal cursor to blink
901 unsigned int mCursorBlinkInterval;
902 float mCursorBlinkDuration;
903 bool mCursorBlinkStatus; // Flag to switch between blink on and blink off
905 ImageActor mPrimaryCursor;
906 ImageActor mSecondaryCursor;
908 ImageActor mGrabHandle;
910 float mGrabDisplacementX;
911 float mGrabDisplacementY;
913 SelectionHandleImpl mSelectionHandle[SELECTION_HANDLE_COUNT];
915 PopupImpl mCopyPastePopup;
918 Image mGrabHandleImage;
920 TapGestureDetector mTapDetector;
921 PanGestureDetector mPanGestureDetector;
923 Rect<int> mBoundingBox;
926 DecoratorPtr Decorator::New( Internal::Control& parent, Observer& observer )
928 return DecoratorPtr( new Decorator(parent, observer) );
931 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
933 mImpl->mBoundingBox = boundingBox;
936 const Rect<int>& Decorator::GetBoundingBox() const
938 return mImpl->mBoundingBox;
941 void Decorator::Relayout( const Vector2& size )
943 mImpl->Relayout( size );
948 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
950 mImpl->mActiveCursor = activeCursor;
953 unsigned int Decorator::GetActiveCursor() const
955 return mImpl->mActiveCursor;
958 void Decorator::SetPosition( Cursor cursor, float x, float y, float height )
960 // Adjust grab handle displacement
961 mImpl->mGrabDisplacementX -= x - mImpl->mCursor[cursor].x;
962 mImpl->mGrabDisplacementY -= y - mImpl->mCursor[cursor].y;
964 mImpl->mCursor[cursor].x = x;
965 mImpl->mCursor[cursor].y = y;
966 mImpl->mCursor[cursor].height = height;
969 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& height ) const
971 x = mImpl->mCursor[cursor].x;
972 y = mImpl->mCursor[cursor].y;
973 height = mImpl->mCursor[cursor].height;
976 void Decorator::SetCursorImage( Dali::Image image )
978 mImpl->mCursorImage = image;
981 Dali::Image Decorator::GetCursorImage() const
983 return mImpl->mCursorImage;
986 void Decorator::SetColor( Cursor cursor, const Dali::Vector4& color )
988 mImpl->mCursor[cursor].color = color;
991 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
993 return mImpl->mCursor[cursor].color;
996 void Decorator::StartCursorBlink()
998 if ( !mImpl->mCursorBlinkTimer )
1000 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1001 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1004 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1006 mImpl->mCursorBlinkTimer.Start();
1010 void Decorator::StopCursorBlink()
1012 if ( mImpl->mCursorBlinkTimer )
1014 mImpl->mCursorBlinkTimer.Stop();
1018 void Decorator::SetCursorBlinkInterval( float seconds )
1020 mImpl->mCursorBlinkInterval = seconds*MILLISECONDS; // Convert to milliseconds
1023 float Decorator::GetCursorBlinkInterval() const
1025 return mImpl->mCursorBlinkInterval;
1028 void Decorator::SetCursorBlinkDuration( float seconds )
1030 mImpl->mCursorBlinkDuration = seconds;
1033 float Decorator::GetCursorBlinkDuration() const
1035 return mImpl->mCursorBlinkDuration;
1040 void Decorator::SetGrabHandleActive( bool active )
1042 mImpl->mActiveGrabHandle = active;
1045 bool Decorator::IsGrabHandleActive() const
1047 return mImpl->mActiveGrabHandle;
1050 void Decorator::SetGrabHandleImage( Dali::Image image )
1052 mImpl->mGrabHandleImage = image;
1055 Dali::Image Decorator::GetGrabHandleImage() const
1057 return mImpl->mGrabHandleImage;
1062 void Decorator::SetSelectionActive( bool active )
1064 mImpl->mActiveSelection = active;
1067 bool Decorator::IsSelectionActive() const
1069 return mImpl->mActiveSelection;
1072 void Decorator::SetPosition( SelectionHandle handle, float x, float y, float height )
1074 mImpl->mSelectionHandle[handle].x = x;
1075 mImpl->mSelectionHandle[handle].y = y;
1076 mImpl->mSelectionHandle[handle].cursorHeight = height;
1079 void Decorator::GetPosition( SelectionHandle handle, float& x, float& y, float& height ) const
1081 x = mImpl->mSelectionHandle[handle].x;
1082 y = mImpl->mSelectionHandle[handle].y;
1083 height = mImpl->mSelectionHandle[handle].cursorHeight;
1086 void Decorator::SetImage( SelectionHandle handle, SelectionHandleState state, Dali::Image image )
1088 if( SELECTION_HANDLE_PRESSED == state )
1090 mImpl->mSelectionHandle[handle].pressedImage = image;
1094 mImpl->mSelectionHandle[handle].releasedImage = image;
1098 Dali::Image Decorator::GetImage( SelectionHandle handle, SelectionHandleState state ) const
1100 if( SELECTION_HANDLE_PRESSED == state )
1102 return mImpl->mSelectionHandle[handle].pressedImage;
1105 return mImpl->mSelectionHandle[handle].releasedImage;
1108 void Decorator::ShowPopup()
1110 if ( !mImpl->mCopyPastePopup.mRoot )
1112 mImpl->CreatePopup();
1116 void Decorator::HidePopup()
1118 if ( mImpl->mCopyPastePopup.mRoot )
1120 mImpl->DestroyPopup();
1124 Decorator::~Decorator()
1129 Decorator::Decorator( Dali::Toolkit::Internal::Control& parent, Observer& observer )
1132 mImpl = new Decorator::Impl( parent, observer );
1137 } // namespace Toolkit