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 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 )
292 // TODO - Remove this if nothing is active
295 // Show or hide the cursors
299 mPrimaryCursor.SetPosition( mCursor[PRIMARY_CURSOR].x, mCursor[PRIMARY_CURSOR].y );
301 if( mSecondaryCursor )
303 mSecondaryCursor.SetPosition( mCursor[SECONDARY_CURSOR].x, mCursor[SECONDARY_CURSOR].y );
306 // Show or hide the grab handle
307 if( mActiveGrabHandle )
313 mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].x, mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height );
315 else if( mGrabHandle )
317 UnparentAndReset( mGrabHandle );
320 // Show or hide the selection handles/highlight
321 if( mActiveSelection )
325 CreateSelectionHandles();
327 SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
328 primary.actor.SetPosition( primary.x, primary.y + primary.cursorHeight );
330 SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
331 secondary.actor.SetPosition( secondary.x, secondary.y + secondary.cursorHeight );
333 //CreateHighlight(); TODO
337 UnparentAndReset( mSelectionHandle[ PRIMARY_SELECTION_HANDLE ].actor );
338 UnparentAndReset( mSelectionHandle[ SECONDARY_SELECTION_HANDLE ].actor );
341 if ( mActiveCopyPastePopup )
344 mCopyPastePopup.mRoot.SetPosition( Vector3( 180.0f, -100.0f, 0.0f ) ); //todo grabhandle or selection handle postions to be used
352 void CreateCursor( ImageActor& cursor )
356 mCursorImage = ResourceImage::New( DEFAULT_CURSOR_IMAGE );
358 cursor = ImageActor::New( mCursorImage );
359 cursor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
362 // Add or Remove cursor(s) from parent
365 if( mActiveCursor == ACTIVE_CURSOR_NONE )
367 UnparentAndReset( mPrimaryCursor );
368 UnparentAndReset( mSecondaryCursor );
372 /* Create Primary and or Secondary Cursor(s) if active and add to parent */
373 if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
374 mActiveCursor == ACTIVE_CURSOR_BOTH )
376 if ( !mPrimaryCursor )
378 CreateCursor( mPrimaryCursor );
379 #ifdef DECORATOR_DEBUG
380 mPrimaryCursor.SetName( "PrimaryCursorActor" );
382 mActiveLayer.Add( mPrimaryCursor);
386 if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
388 if ( !mSecondaryCursor )
390 CreateCursor( mSecondaryCursor );
391 #ifdef DECORATOR_DEBUG
392 mSecondaryCursor.SetName( "SecondaryCursorActor" );
394 mActiveLayer.Add( mSecondaryCursor);
400 bool OnCursorBlinkTimerTick()
403 if ( mPrimaryCursor )
405 mPrimaryCursor.SetVisible( mCursorBlinkStatus );
407 if ( mSecondaryCursor )
409 mSecondaryCursor.SetVisible( mCursorBlinkStatus );
412 mCursorBlinkStatus = !mCursorBlinkStatus;
417 void SetupTouchEvents()
421 mTapDetector = TapGestureDetector::New();
422 mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
425 if ( !mPanGestureDetector )
427 mPanGestureDetector = PanGestureDetector::New();
428 mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
432 void CreateActiveLayer()
436 Actor parent = mTextControlParent.Self();
438 mActiveLayer = Layer::New();
439 #ifdef DECORATOR_DEBUG
440 mActiveLayer.SetName ( "ActiveLayerActor" );
443 mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
444 mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
445 mActiveLayer.SetSizeMode( SIZE_EQUAL_TO_PARENT );
446 mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
448 parent.Add( mActiveLayer );
451 mActiveLayer.RaiseToTop();
454 void CreateGrabHandle()
458 if ( !mGrabHandleImage )
460 mGrabHandleImage = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE );
463 mGrabHandle = ImageActor::New( mGrabHandleImage );
464 #ifdef DECORATOR_DEBUG
465 mGrabHandle.SetName( "GrabHandleActor" );
467 mGrabHandle.SetParentOrigin( ParentOrigin::TOP_LEFT );
468 mGrabHandle.SetAnchorPoint( AnchorPoint::TOP_CENTER );
469 mGrabHandle.SetDrawMode( DrawMode::OVERLAY );
471 mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
472 #ifdef DECORATOR_DEBUG
473 mGrabArea.SetName( "GrabArea" );
475 mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
476 mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
477 mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
478 mGrabHandle.Add(mGrabArea);
480 mTapDetector.Attach( mGrabArea );
481 mPanGestureDetector.Attach( mGrabArea );
483 mActiveLayer.Add(mGrabHandle);
487 void CreateSelectionHandles()
489 SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ];
490 if ( !primary.actor )
492 if ( !primary.releasedImage )
494 primary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
497 primary.actor = ImageActor::New( primary.releasedImage );
498 #ifdef DECORATOR_DEBUG
499 primary.actor.SetName("SelectionHandleOne");
501 primary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
502 primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
503 primary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
504 primary.flipped = false;
506 primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
507 #ifdef DECORATOR_DEBUG
508 primary.grabArea.SetName("SelectionHandleOneGrabArea");
510 primary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
511 primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
512 primary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
514 mTapDetector.Attach( primary.grabArea );
515 mPanGestureDetector.Attach( primary.grabArea );
516 primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
518 primary.actor.Add( primary.grabArea );
519 mActiveLayer.Add( primary.actor );
522 SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ];
523 if ( !secondary.actor )
525 if ( !secondary.releasedImage )
527 secondary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
530 secondary.actor = ImageActor::New( secondary.releasedImage );
531 #ifdef DECORATOR_DEBUG
532 secondary.actor.SetName("SelectionHandleTwo");
534 secondary.actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
535 secondary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
536 secondary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
537 secondary.flipped = false;
539 secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
540 #ifdef DECORATOR_DEBUG
541 secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
543 secondary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
544 secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
545 secondary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
547 mTapDetector.Attach( secondary.grabArea );
548 mPanGestureDetector.Attach( secondary.grabArea );
549 secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
551 secondary.actor.Add( secondary.grabArea );
552 mActiveLayer.Add( secondary.actor );
555 //SetUpHandlePropertyNotifications(); TODO
558 void OnTap( Actor actor, const TapGesture& tap )
560 if( actor == mGrabHandle )
566 void OnPan( Actor actor, const PanGesture& gesture )
568 if( actor == mGrabArea )
570 if( Gesture::Started == gesture.state )
572 mGrabDisplacementX = mGrabDisplacementY = 0;
575 mGrabDisplacementX += gesture.displacement.x;
576 mGrabDisplacementY += gesture.displacement.y;
578 float x = mCursor[PRIMARY_CURSOR].x + mGrabDisplacementX;
579 float y = mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height*0.5f + mGrabDisplacementY;
581 if( Gesture::Started == gesture.state ||
582 Gesture::Continuing == gesture.state )
584 mObserver.GrabHandleEvent( GRAB_HANDLE_PRESSED, x, y );
586 else if( Gesture::Finished == gesture.state ||
587 Gesture::Cancelled == gesture.state )
589 mObserver.GrabHandleEvent( GRAB_HANDLE_RELEASED, x, y );
594 bool OnHandleOneTouched( Actor actor, const TouchEvent& touch )
600 bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch )
610 void CreateOrderedListOfPopupOptions()
612 mCopyPastePopup.mOrderListOfButtons.clear();
614 // Create button for each possible option using Option priority
615 ResourceImage cutIcon = ResourceImage::New( OPTION_ICON_CUT );
616 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsCut, mCopyPastePopup.mCutOptionPriority, OPTION_CUT, GET_LOCALE_TEXT("IDS_COM_BODY_CUT"), cutIcon, true ) );
618 ResourceImage copyIcon = ResourceImage::New( OPTION_ICON_COPY );
619 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsCopy, mCopyPastePopup.mCopyOptionPriority, OPTION_COPY, GET_LOCALE_TEXT("IDS_COM_BODY_COPY"), copyIcon, true ) );
621 ResourceImage pasteIcon = ResourceImage::New( OPTION_ICON_PASTE );
622 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsPaste, mCopyPastePopup.mPasteOptionPriority, OPTION_PASTE, GET_LOCALE_TEXT("IDS_COM_BODY_PASTE"), pasteIcon, true ) );
624 ResourceImage selectIcon = ResourceImage::New( OPTION_ICON_SELECT );
625 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsSelect, mCopyPastePopup.mSelectOptionPriority, OPTION_SELECT_WORD, GET_LOCALE_TEXT("IDS_COM_SK_SELECT"), selectIcon, true ) );
627 ResourceImage selectAllIcon = ResourceImage::New( OPTION_ICON_SELECT_ALL );
628 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsSelectAll, mCopyPastePopup.mSelectAllOptionPriority, OPTION_SELECT_ALL, GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL"), selectAllIcon, true ) );
630 ResourceImage clipboardIcon = ResourceImage::New( OPTION_ICON_CLIPBOARD );
631 mCopyPastePopup.mOrderListOfButtons.push_back( ButtonRequirement( ButtonsClipboard, mCopyPastePopup.mClipboardOptionPriority, OPTION_CLIPBOARD, GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD"), clipboardIcon, true ) );
633 // Sort the buttons according their priorities.
634 std::sort( mCopyPastePopup.mOrderListOfButtons.begin(), mCopyPastePopup.mOrderListOfButtons.end(), PriorityCompare );
637 void CreateBackground( PopupImpl& popup )
639 // Create background-panel if not already created (required if we have at least one option)
640 if ( !popup.mBackground )
642 ResourceImage bgImg = ResourceImage::New( POPUP_BACKGROUND );
643 popup.mBackground = ImageActor::New( bgImg );
644 popup.mBackground.SetColor( popup.mBackgroundColor );
645 popup.mBackground.SetParentOrigin( ParentOrigin::CENTER );
647 NinePatchImage backgroundImageNinePatch = NinePatchImage::DownCast( bgImg );
648 if( backgroundImageNinePatch )
650 const Size ninePatchImageSize = Size( static_cast<float>( bgImg.GetWidth() ), static_cast<float>( bgImg.GetHeight() ) );
651 Rect<int> childRect = backgroundImageNinePatch.GetChildRectangle();
653 // -1u because of the cropping.
654 popup.mNinePatchMargins.x = childRect.x - 1u;
655 popup.mNinePatchMargins.y = ninePatchImageSize.width - ( childRect.x + childRect.width ) - 1u;
656 popup.mNinePatchMargins.z = childRect.y - 1u;
657 popup.mNinePatchMargins.w = ninePatchImageSize.height - ( childRect.y + childRect.height ) - 1u;
660 ResourceImage::Image bgEffectImg = ResourceImage::New( POPUP_BACKGROUND_EFFECT );
661 ImageActor backgroundEffect = ImageActor::New( bgEffectImg );
662 backgroundEffect.SetParentOrigin( ParentOrigin::CENTER );
664 ResourceImage::Image bgLine = ResourceImage::New( POPUP_BACKGROUND_LINE );
665 ImageActor backgroundLine = ImageActor::New( bgLine );
666 backgroundLine.SetParentOrigin( ParentOrigin::CENTER );
667 backgroundLine.SetColor( popup.mLineColor );
669 popup.mBackground.Add( backgroundEffect );
670 popup.mBackground.Add( backgroundLine );
674 void AddOption( Actor& parent, const std::string& name, const std::string& caption, const Image iconImage, bool finalOption, bool showIcons )
676 // 1. Create the backgrounds for the popup option both normal and pressed.
677 // Both containers will be added to a button.
678 Actor optionContainer = Actor::New();
679 optionContainer.SetDrawMode( DrawMode::OVERLAY );
680 //optionContainer.SetParentOrigin( ParentOrigin::CENTER );
681 optionContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT );
683 ImageActor optionPressedContainer = Toolkit::CreateSolidColorActor( mCopyPastePopup.mBackgroundPressedColor );
684 optionPressedContainer.SetDrawMode( DrawMode::OVERLAY );
685 //optionPressedContainer.SetParentOrigin( ParentOrigin::CENTER );
686 optionPressedContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT );
689 Toolkit::TextLabel captionTextLabel = Toolkit::TextLabel::New();
690 captionTextLabel.SetProperty( TextLabel::Property::TEXT, caption );
691 optionContainer.Add( captionTextLabel );
693 Toolkit::TextLabel pressedCaptionTextLabel = Toolkit::TextLabel::New();
694 pressedCaptionTextLabel.SetProperty( TextLabel::Property::TEXT, caption );
695 optionPressedContainer.Add( pressedCaptionTextLabel );
697 // Calculates the icon/text position.
698 float iconTextOffsetY = 0.0f;
702 // 3. Create the icons
703 ImageActor pressedIcon = ImageActor::New( iconImage );
704 ImageActor icon = ImageActor::New( iconImage );
706 optionContainer.Add( icon );
707 optionPressedContainer.Add( pressedIcon );
709 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 ) );
711 icon.SetParentOrigin( ParentOrigin::TOP_CENTER );
712 icon.SetAnchorPoint( AnchorPoint::TOP_CENTER );
713 icon.SetY( iconTextOffsetY );
715 pressedIcon.SetParentOrigin( ParentOrigin::TOP_CENTER );
716 pressedIcon.SetAnchorPoint( AnchorPoint::TOP_CENTER );
717 pressedIcon.SetY( iconTextOffsetY );
719 // Layout icon + gap + text
720 captionTextLabel.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
721 pressedCaptionTextLabel.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
722 pressedCaptionTextLabel.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
723 captionTextLabel.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
724 pressedCaptionTextLabel.SetY( -iconTextOffsetY );
725 captionTextLabel.SetY( -iconTextOffsetY );
729 // Centre option text
730 captionTextLabel.SetAnchorPoint( AnchorPoint::CENTER );
731 captionTextLabel.SetParentOrigin( ParentOrigin::CENTER );
732 pressedCaptionTextLabel.SetAnchorPoint( AnchorPoint::CENTER );
733 pressedCaptionTextLabel.SetParentOrigin( ParentOrigin::CENTER );
736 // Calculate the size of the text.
737 Vector3 textSize = captionTextLabel.GetNaturalSize();
738 textSize.width = std::min( textSize.width, OPTION_MAX_WIDTH - 2.f * OPTION_MARGIN_WIDTH );
740 // Set the size to the text. Text will be ellipsized if exceeds the max width.
741 captionTextLabel.SetSize( textSize );
742 pressedCaptionTextLabel.SetSize( textSize );
744 // 4. Calculate the size of option.
746 // The width is the max size of the text or the icon plus the margins clamped between the option min and max size.
747 // The height is the whole popup height minus the ninepatch margins.
748 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 ) ),
749 DEFAULT_POPUP_MAX_SIZE.height - mCopyPastePopup.mNinePatchMargins.z - mCopyPastePopup.mNinePatchMargins.w );
751 optionContainer.SetSize( optionSize );
752 optionPressedContainer.SetSize( optionSize );
754 // 5. Create a option.
755 Toolkit::PushButton option = Toolkit::PushButton::New();
756 option.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
757 option.SetSize( optionSize );
758 option.SetAnchorPoint( AnchorPoint::TOP_LEFT );
759 option.SetX( mCopyPastePopup.mContentSize.width );
760 option.SetName( name );
761 option.SetAnimationTime( 0.0f );
762 //option.ClickedSignal().Connect( this, &TextInputPopup::OnButtonPressed );
764 parent.Add( option );
766 // 6. Set the normal option image.
767 option.SetButtonImage( optionContainer );
769 // 7. Set the pressed option image
770 option.SetSelectedImage( optionPressedContainer );
772 // 8. Update the content size.
773 mCopyPastePopup.mContentSize.width += optionSize.width;
774 mCopyPastePopup.mContentSize.height = std::max ( optionSize.height, mCopyPastePopup.mContentSize.height );
776 // 9. Add the divider
779 const Size size( POPUP_DIVIDER_WIDTH, mCopyPastePopup.mContentSize.height );
781 ImageActor divider = Toolkit::CreateSolidColorActor( Color::WHITE );
782 divider.SetSize (size);
783 divider.SetParentOrigin( ParentOrigin::TOP_LEFT );
784 divider.SetAnchorPoint( AnchorPoint::TOP_LEFT );
785 divider.SetPosition( mCopyPastePopup.mContentSize.width - POPUP_DIVIDER_WIDTH, 0.0f );
786 parent.Add( divider );
790 void SetUpPopup( Actor& popupRootActor, Size& size )
792 // Create Layer and Stencil.
793 popupRootActor = Actor::New();
794 mCopyPastePopup.mStencilLayer = Layer::New();
795 ImageActor stencil = CreateSolidColorActor( Color::RED );
796 stencil.SetDrawMode( DrawMode::STENCIL );
797 stencil.SetVisible( true );
798 Actor scrollview = Actor::New();
800 //todo Use Size negotiation
801 mCopyPastePopup.mStencilLayer.SetSize( size ); // matches stencil size
802 popupRootActor.SetSize( size ); // matches stencil size
803 stencil.SetSize( size );
804 scrollview.SetSize( size );
805 mCopyPastePopup.mButtons.SetSize( size );
807 mCopyPastePopup.mStencilLayer.SetAnchorPoint(AnchorPoint::TOP_LEFT);
808 scrollview.SetAnchorPoint(AnchorPoint::TOP_LEFT);
809 mCopyPastePopup.mButtons.SetAnchorPoint( AnchorPoint::TOP_LEFT );
811 mActiveLayer.Add( mCopyPastePopup.mRoot );
812 popupRootActor.Add( mCopyPastePopup.mBackground );
813 popupRootActor.Add( mCopyPastePopup.mStencilLayer );
814 mCopyPastePopup.mStencilLayer.Add( stencil );
815 mCopyPastePopup.mStencilLayer.Add( scrollview );
816 scrollview.Add( mCopyPastePopup.mButtons );
819 void AddPopupOptions( bool createTail, bool showIcons )
821 mCopyPastePopup.mShowIcons = showIcons;
823 mCopyPastePopup.mContentSize = Vector2::ZERO;
825 mCopyPastePopup.mButtons = Actor::New();
827 // Add the options into the buttons container.
829 // 1. Determine how many buttons are active and should be added to container.
830 std::size_t numberOfOptions = 0u;
831 for( std::vector<ButtonRequirement>::const_iterator it = mCopyPastePopup.mOrderListOfButtons.begin(), endIt = mCopyPastePopup.mOrderListOfButtons.end(); ( it != endIt ); ++it )
833 const ButtonRequirement& button( *it );
840 // 2. Iterate list of buttons and add active ones.
841 std::size_t optionsAdded = 0u;
842 for( std::vector<ButtonRequirement>::const_iterator it = mCopyPastePopup.mOrderListOfButtons.begin(), endIt = mCopyPastePopup.mOrderListOfButtons.end(); ( it != endIt ); ++it )
844 const ButtonRequirement& button( *it );
845 if ( button.enabled )
848 AddOption( mCopyPastePopup.mButtons, button.name, button.caption, button.icon, optionsAdded == numberOfOptions, mCopyPastePopup.mShowIcons );
852 // Calculate the size of the whole popup which may not be all visible.
853 mCopyPastePopup.mRequiredPopUpSize = Size( std::min( mCopyPastePopup.mMaxWidth, mCopyPastePopup.mContentSize.width + mCopyPastePopup.mNinePatchMargins.x + mCopyPastePopup.mNinePatchMargins.y ), DEFAULT_POPUP_MAX_SIZE. height );
855 // Set the size of the background, background line and background effect.
856 mCopyPastePopup.mBackground.SetSize( mCopyPastePopup.mRequiredPopUpSize);
857 for( std::size_t index = 0u, childCount = mCopyPastePopup.mBackground.GetChildCount(); index < childCount; ++index )
859 mCopyPastePopup.mBackground.GetChildAt( index ).SetSize( mCopyPastePopup.mRequiredPopUpSize );
862 // Size of the contents within the popup
863 mCopyPastePopup.mVisiblePopUpSize = Size( mCopyPastePopup.mRequiredPopUpSize.width - mCopyPastePopup.mNinePatchMargins.x - mCopyPastePopup.mNinePatchMargins.y, mCopyPastePopup.mRequiredPopUpSize.height - mCopyPastePopup.mNinePatchMargins.z - mCopyPastePopup.mNinePatchMargins.w );
869 if ( !mCopyPastePopup.mRoot )
871 mActiveCopyPastePopup = true;
872 CreateOrderedListOfPopupOptions(); //todo Currently causes all options to be shown
873 CreateBackground( mCopyPastePopup );
874 AddPopupOptions( true, true );
875 SetUpPopup( mCopyPastePopup.mRoot, mCopyPastePopup.mVisiblePopUpSize );
878 mCopyPastePopup.mStencilLayer.RaiseToTop();
883 if ( mCopyPastePopup.mRoot )
885 mActiveCopyPastePopup = false;
886 UnparentAndReset( mCopyPastePopup.mButtons );
887 UnparentAndReset( mCopyPastePopup.mRoot );
891 Internal::Control& mTextControlParent;
894 Layer mActiveLayer; // Layer for active handles and alike that ensures they are above all else.
896 unsigned int mActiveCursor;
897 bool mActiveGrabHandle;
898 bool mActiveSelection;
899 bool mActiveCopyPastePopup;
901 CursorImpl mCursor[CURSOR_COUNT];
903 Timer mCursorBlinkTimer; // Timer to signal cursor to blink
904 unsigned int mCursorBlinkInterval;
905 float mCursorBlinkDuration;
906 bool mCursorBlinkStatus; // Flag to switch between blink on and blink off
908 ImageActor mPrimaryCursor;
909 ImageActor mSecondaryCursor;
911 ImageActor mGrabHandle;
913 float mGrabDisplacementX;
914 float mGrabDisplacementY;
916 SelectionHandleImpl mSelectionHandle[SELECTION_HANDLE_COUNT];
918 PopupImpl mCopyPastePopup;
921 Image mGrabHandleImage;
923 TapGestureDetector mTapDetector;
924 PanGestureDetector mPanGestureDetector;
926 Rect<int> mBoundingBox;
929 DecoratorPtr Decorator::New( Internal::Control& parent, Observer& observer )
931 return DecoratorPtr( new Decorator(parent, observer) );
934 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
936 mImpl->mBoundingBox = boundingBox;
939 const Rect<int>& Decorator::GetBoundingBox() const
941 return mImpl->mBoundingBox;
944 void Decorator::Relayout( const Vector2& size )
946 mImpl->Relayout( size );
951 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
953 mImpl->mActiveCursor = activeCursor;
956 unsigned int Decorator::GetActiveCursor() const
958 return mImpl->mActiveCursor;
961 void Decorator::SetPosition( Cursor cursor, float x, float y, float height )
963 // Adjust grab handle displacement
964 mImpl->mGrabDisplacementX -= x - mImpl->mCursor[cursor].x;
965 mImpl->mGrabDisplacementY -= y - mImpl->mCursor[cursor].y;
967 mImpl->mCursor[cursor].x = x;
968 mImpl->mCursor[cursor].y = y;
969 mImpl->mCursor[cursor].height = height;
972 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& height ) const
974 x = mImpl->mCursor[cursor].x;
975 y = mImpl->mCursor[cursor].y;
976 height = mImpl->mCursor[cursor].height;
979 void Decorator::SetCursorImage( Dali::Image image )
981 mImpl->mCursorImage = image;
984 Dali::Image Decorator::GetCursorImage() const
986 return mImpl->mCursorImage;
989 void Decorator::SetColor( Cursor cursor, const Dali::Vector4& color )
991 mImpl->mCursor[cursor].color = color;
994 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
996 return mImpl->mCursor[cursor].color;
999 void Decorator::StartCursorBlink()
1001 if ( !mImpl->mCursorBlinkTimer )
1003 mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1004 mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1007 if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1009 mImpl->mCursorBlinkTimer.Start();
1013 void Decorator::StopCursorBlink()
1015 if ( mImpl->mCursorBlinkTimer )
1017 mImpl->mCursorBlinkTimer.Stop();
1021 void Decorator::SetCursorBlinkInterval( float seconds )
1023 mImpl->mCursorBlinkInterval = seconds*MILLISECONDS; // Convert to milliseconds
1026 float Decorator::GetCursorBlinkInterval() const
1028 return mImpl->mCursorBlinkInterval;
1031 void Decorator::SetCursorBlinkDuration( float seconds )
1033 mImpl->mCursorBlinkDuration = seconds;
1036 float Decorator::GetCursorBlinkDuration() const
1038 return mImpl->mCursorBlinkDuration;
1043 void Decorator::SetGrabHandleActive( bool active )
1045 mImpl->mActiveGrabHandle = active;
1048 bool Decorator::IsGrabHandleActive() const
1050 return mImpl->mActiveGrabHandle;
1053 void Decorator::SetGrabHandleImage( Dali::Image image )
1055 mImpl->mGrabHandleImage = image;
1058 Dali::Image Decorator::GetGrabHandleImage() const
1060 return mImpl->mGrabHandleImage;
1065 void Decorator::SetSelectionActive( bool active )
1067 mImpl->mActiveSelection = active;
1070 bool Decorator::IsSelectionActive() const
1072 return mImpl->mActiveSelection;
1075 void Decorator::SetPosition( SelectionHandle handle, float x, float y, float height )
1077 mImpl->mSelectionHandle[handle].x = x;
1078 mImpl->mSelectionHandle[handle].y = y;
1079 mImpl->mSelectionHandle[handle].cursorHeight = height;
1082 void Decorator::GetPosition( SelectionHandle handle, float& x, float& y, float& height ) const
1084 x = mImpl->mSelectionHandle[handle].x;
1085 y = mImpl->mSelectionHandle[handle].y;
1086 height = mImpl->mSelectionHandle[handle].cursorHeight;
1089 void Decorator::SetImage( SelectionHandle handle, SelectionHandleState state, Dali::Image image )
1091 if( SELECTION_HANDLE_PRESSED == state )
1093 mImpl->mSelectionHandle[handle].pressedImage = image;
1097 mImpl->mSelectionHandle[handle].releasedImage = image;
1101 Dali::Image Decorator::GetImage( SelectionHandle handle, SelectionHandleState state ) const
1103 if( SELECTION_HANDLE_PRESSED == state )
1105 return mImpl->mSelectionHandle[handle].pressedImage;
1108 return mImpl->mSelectionHandle[handle].releasedImage;
1111 void Decorator::ShowPopup()
1113 if ( !mImpl->mCopyPastePopup.mRoot )
1115 mImpl->CreatePopup();
1119 void Decorator::HidePopup()
1121 if ( mImpl->mCopyPastePopup.mRoot )
1123 mImpl->DestroyPopup();
1127 Decorator::~Decorator()
1132 Decorator::Decorator( Dali::Toolkit::Internal::Control& parent, Observer& observer )
1135 mImpl = new Decorator::Impl( parent, observer );
1140 } // namespace Toolkit