79f49d3ae5f164718a9b6761d7dd21fce923fbd3
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-input / text-input-popup-impl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 #include <dali-toolkit/internal/controls/text-input/text-input-popup-impl.h>
19
20 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
21 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
22
23 #include <libintl.h>
24
25 using namespace std;
26 using namespace Dali;
27
28 #define GET_LOCALE_TEXT(string) dgettext("sys_string", string)
29
30 namespace {
31
32 // Default Colors
33
34 const Vector4 DEFAULT_POPUP_BACKGROUND( Vector4( .20f, 0.29f, 0.44f, 1.0f ) );
35 const Vector4 DEFAULT_POPUP_BUTTON_PRESSED( Vector4( 0.07f, 0.10f, 0.17f, 1.0f ) );
36 const Vector4 DEFAULT_BORDER_COLOR( Vector4( 0.36f, 0.45f, 0.59f, 1.0f ) );
37 const Vector4 DEFAULT_POPUP_ICON( Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
38 const Vector4 DEFAULT_POPUP_ICON_PRESSED( Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
39 const Vector4 DEFAULT_POPUP_TEXT( Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
40 const Vector4 DEFAULT_POPUP_TEXT_PRESSED( Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
41 const Vector3 POPUP_BORDER( Vector3( 1.0f, 1.0f, 0.0f ) );
42
43 /* Functionality in place to have the end buttons using different images to inner button.
44  * Supply a centre image and then a left and right image, the centre image can have straight ends while
45  * the left image can be rounded on the left and straight on the right, the right image can be straight on the left and rounded on the right.
46  */
47
48 // Popup: Tails
49 const char* DEFAULT_POPUP_TAIL_BOTTOM( DALI_IMAGE_DIR "popup_bubble_tail_bottom.png" );
50 const char* DEFAULT_POPUP_TAIL_BOTTOM_OUTLINE( DALI_IMAGE_DIR "popup_bubble_tail_bottom_line.png" );
51 const float TAIL_Y_POSITION( -1.5f );
52
53 // Popup: Vertical Constraint
54 // TODO: Remove - this should come from application - it is not possible to get the
55 // height of the indicator actor from Dali-Toolkit.
56
57 const float POP_UP_SCREEN_EDGE_MARGIN( 4.0f );
58 const Vector2 DEFAULT_POPUP_INDICATOR_OFFSET(POP_UP_SCREEN_EDGE_MARGIN, 60.0f);
59
60 const Vector3 POPUP_TEXT_OFFSET( 0.0f, 0.0f, 0.0f );
61 const Vector3 POPUP_TEXT_ENLARGE( 12.0f, 28.0f, 0.0f );
62 const Vector3 POPUP_MINIMUM_SIZE( 128.0f, 124.0f, 0.0f );
63
64 const Vector3 BUTTON_TEXT_ENLARGE( 32.0f, 0.0f, 0.0f );
65 const Vector3 BUTTON_TEXT_MINIMUM_SIZE( 128.0f, 126.0f, 0.0f );
66 const Vector3 BUTTON_TEXT_MAXIMUM_SIZE( 190.0f, 126.0f, 0.0f );
67 const Vector3 TEXT_LABEL_MAX_SIZE( 160.0f, 30.0f, 0.0f );
68
69 const float DIVIDER_WIDTH(2.0f);                                            ///< Width of each button divider
70 const float DIVIDER_MARGIN(0.0f);                                           ///< Top/Bottom Margin between divider and edge of popup.
71
72 const float DEFAULT_UI_FONT_SIZE(7.0f);                                     ///< Standard font size for Text-Input's UI
73
74 const float HIDE_POPUP_ANIMATION_DURATION(0.2f);                            ///< Duration of popup hide animation in seconds.
75 const float SHOW_POPUP_ANIMATION_DURATION(0.2f);                            ///< Duration of popup show animation in seconds.
76
77 const Vector2 DEFAULT_ICON_SIZE( 45.0f, 45.0f );                            ///< Default icon size for image in options
78 const float TEXT_POSITION_OFFSET( -19.0f );                                 ///< Default offset for text label
79 const float ICON_POSITION_OFFSET( 19.0f );                                  ///< Default offset for icon
80
81 const char* DEFAULT_ICON_CLIPBOARD( DALI_IMAGE_DIR "copy_paste_icon_clipboard.png" );
82 const char* DEFAULT_ICON_COPY( DALI_IMAGE_DIR "copy_paste_icon_copy.png" );
83 const char* DEFAULT_ICON_CUT( DALI_IMAGE_DIR "copy_paste_icon_cut.png" );
84 const char* DEFAULT_ICON_PASTE( DALI_IMAGE_DIR "copy_paste_icon_paste.png" );
85 const char* DEFAULT_ICON_SELECT( DALI_IMAGE_DIR "copy_paste_icon_select.png" );
86 const char* DEFAULT_ICON_SELECT_ALL( DALI_IMAGE_DIR "copy_paste_icon_select_all.png" );
87
88 // TODO: This should be based on the content for example:
89 // 1. For selection: should be above top of highlighted selection, or below bottom of highlighted selection + end handle.
90 // 2. For cursor: should be above top of cursor, or below bottom of cursor + grab handle.
91 const std::string POPUP_ALTERNATIVE_OFFSET("popup-alternative-offset");       ///< Alternative offset property for confinenment constraint.
92
93
94 /**
95  * Confine Actor to boundaries of reference actor (e.g. Parent)
96  * Actor bounds (top-left position + size) are confined to reference Actor's
97  * bounds.
98  */
99 struct ConfinementConstraint
100 {
101   /**
102    * Confinement constraint constructor.
103    * @param[in] topLeftMargin (optional) Top-Left margins (defaults to 0.0f, 0.0f)
104    * @param[in] bottomRightMargin (optional) Bottom-Right margins (defaults to 0.0f, 0.0f)
105    * @paran[in[ flipHorizontal (optional) whether to flip Actor to other side if near edge
106    * @param[in] flipVertical (optional) whether to flip Actor to the other side if near edge
107    * @param[in] boundingRect Rectangle to bound Popup to.
108    *
109    */
110   ConfinementConstraint(Vector2 topLeftMargin = Vector2::ZERO, Vector2 bottomRightMargin = Vector2::ZERO, bool flipHorizontal = false, bool flipVertical = false, Rect<float> boundingRect = Rect<float>(0.0f, 0.0f, 0.0f, 0.0f) )
111   : mMinIndent(topLeftMargin),
112     mMaxIndent(bottomRightMargin),
113     mFlipHorizontal(flipHorizontal),
114     mFlipVertical(flipVertical),
115     mBoundingRect( boundingRect )
116   {
117   }
118
119   Vector3 operator()(const Vector3&    constPosition,
120                      const PropertyInput& sizeProperty,
121                      const PropertyInput& parentOriginProperty,
122                      const PropertyInput& anchorPointProperty,
123                      const PropertyInput& referenceSizeProperty,
124                      const PropertyInput& alternativeOffsetProperty)
125   {
126     const Vector3& size = sizeProperty.GetVector3();
127     const Vector3& origin = parentOriginProperty.GetVector3();
128     const Vector3& anchor = anchorPointProperty.GetVector3();
129     const Vector3& referenceSize = referenceSizeProperty.GetVector3();
130     const Vector2& alternativeOffset = alternativeOffsetProperty.GetVector2();
131
132     Vector3 newPosition(constPosition);
133
134     // Get actual position of Actor relative to parent's Top-Left.
135     Vector3 position(constPosition + origin * referenceSize);
136
137     // if top-left corner is outside of Top-Left bounds, then push back in screen.
138
139     Vector3 corner(position - size * anchor - mMinIndent);
140
141     newPosition.x -= std::max(corner.x, 0.0f);
142
143     if ( mFlipHorizontal )
144     {
145       if( corner.x < mBoundingRect.x + POP_UP_SCREEN_EDGE_MARGIN )
146       {
147         // Snap PopUp to left hand boundary so stays visible
148         corner.x = mBoundingRect.x + POP_UP_SCREEN_EDGE_MARGIN ;
149       }
150       else if ( ( corner.x + size.x ) > ( ( mBoundingRect.x + mBoundingRect.width ) - POP_UP_SCREEN_EDGE_MARGIN ))
151       {
152         // Calculate offset from left boundary PopUp must be placed at so it does not exceed right side boundary.
153         float requiredOffSetFromLeftBoundaryToFit = mBoundingRect.width - POP_UP_SCREEN_EDGE_MARGIN - size.x;
154         corner.x = mBoundingRect.x + requiredOffSetFromLeftBoundaryToFit - ( origin.x * referenceSize.x ) + ( size.x * anchor.x );
155        }
156       newPosition.x = corner.x;
157     }
158
159     if(mFlipVertical && corner.y < 0.0f)
160     {
161       corner.y = 0.0f;
162       newPosition.y += size.height + alternativeOffset.height;
163     }
164
165     newPosition.y -= std::min(corner.y, 0.0f);
166
167     // if bottom-right corner is outside of Bottom-Right bounds, then push back in screen.
168     corner += size - referenceSize + mMinIndent + mMaxIndent;
169
170     if(mFlipVertical && corner.y > 0.0f)
171     {
172       corner.y = 0.0f;
173       newPosition.y -= size.height + alternativeOffset.height;
174     }
175
176     return newPosition;
177   }
178
179   Vector3 mMinIndent;                                   ///< Top-Left Margin
180   Vector3 mMaxIndent;                                   ///< Bottom-Right Margin.
181   bool mFlipHorizontal;                                 ///< Whether to flip actor's position if exceeds horizontal screen bounds
182   bool mFlipVertical;                                   ///< Whether to flip actor's position if exceeds vertical screen bounds
183   Rect<float> mBoundingRect;                            ///< Bounding Rect Popup must stay within
184 };
185
186 /**
187  * Confine actor to the x axis boundaries of reference actor (e.g. Parent)
188  */
189 struct ParentXAxisConstraint
190 {
191   /**
192    * Confinement constraint constructor.
193    */
194   ParentXAxisConstraint( float handlesMidPoint = 0.0f )
195   : mHandlesMidPoint( handlesMidPoint )
196   {
197   }
198
199   float operator()(  const float          constXPosition,
200                      const PropertyInput& localWidthProperty,
201                      const PropertyInput& anchorPointXProperty )
202   {
203     const float size = localWidthProperty.GetFloat();
204     const float anchor = anchorPointXProperty.GetFloat();
205
206     float newPosition = Clamp( mHandlesMidPoint, constXPosition - size * anchor , constXPosition + size * anchor);
207
208     return newPosition;
209   }
210
211   float mHandlesMidPoint;
212 };
213
214
215 } // unnamed namespace
216
217 namespace Dali
218 {
219
220 namespace Toolkit
221 {
222
223 namespace Internal
224 {
225
226 const char* const TextInputPopup::SIGNAL_PRESSED = "pressed";
227 const char* const TextInputPopup::SIGNAL_HIDE_FINISHED = "hide-finished";
228 const char* const TextInputPopup::SIGNAL_SHOW_FINISHED = "show-finished";
229
230 const char* const TextInputPopup::OPTION_SELECT_WORD = "option-select_word";                       // "Select Word" popup option.
231 const char* const TextInputPopup::OPTION_SELECT_ALL("option-select_all");                          // "Select All" popup option.
232 const char* const TextInputPopup::OPTION_CUT("option-cut");                                        // "Cut" popup option.
233 const char* const TextInputPopup::OPTION_COPY("option-copy");                                      // "Copy" popup option.
234 const char* const TextInputPopup::OPTION_PASTE("option-paste");                                    // "Paste" popup option.
235 const char* const TextInputPopup::OPTION_CLIPBOARD("option-clipboard");                            // "Clipboard" popup option.
236
237 TextInputPopup::TextInputPopup()
238 : mState(StateHidden),
239   mRootActor(Layer::New()),
240   mPopupTailXPosition( 0.0f ),
241   mContentSize( Vector3::ZERO ),
242   mCutPasteButtonsColor( DEFAULT_POPUP_BACKGROUND ),
243   mCutPasteButtonsPressedColor( DEFAULT_POPUP_BUTTON_PRESSED ),
244   mBorderColor( DEFAULT_BORDER_COLOR ),
245   mCutPasteIconsColor( DEFAULT_POPUP_ICON ),
246   mCutPasteIconsPressedColor( DEFAULT_POPUP_ICON_PRESSED ),
247   mCutPasteTextsColor( DEFAULT_POPUP_TEXT ),
248   mCutPasteTextsPressedColor( DEFAULT_POPUP_TEXT_PRESSED ),
249   mSelectOptionPriority(1),
250   mSelectAllOptionPriority(2),
251   mCutOptionPriority(3),
252   mCopyOptionPriority(4),
253   mPasteOptionPriority(5),
254   mClipboardOptionPriority(6),
255   mPressedSignal(),
256   mHideFinishedSignal(),
257   mShowFinishedSignal()
258 {
259   mAlternativeOffsetProperty = mRootActor.RegisterProperty( POPUP_ALTERNATIVE_OFFSET, Vector2::ZERO );
260   mRootActor.SetParentOrigin( ParentOrigin::CENTER );
261   mRootActor.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
262   // constrain popup to size of parent.
263 }
264
265 Actor TextInputPopup::Self()
266 {
267   return mRootActor;
268 }
269
270 void TextInputPopup::AddToStage()
271 {
272   // TODO: Confinement constraint borders should be defined by the application.
273   // It should also not use the stage directly, instead it should add to parent container.
274   Stage::GetCurrent().Add(mRootActor);
275
276   ApplyConfinementConstraint();
277 }
278
279 void TextInputPopup::ApplyConfinementConstraint()
280 {
281   mRootActor.RemoveConstraints();
282   Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
283                                                     LocalSource( Actor::SIZE ),
284                                                     LocalSource( Actor::PARENT_ORIGIN ),
285                                                     LocalSource( Actor::ANCHOR_POINT ),
286                                                     ParentSource( Actor::SIZE ),
287                                                     LocalSource( mAlternativeOffsetProperty ),
288                                                     ConfinementConstraint( DEFAULT_POPUP_INDICATOR_OFFSET,
289                                                                            Vector2::ZERO,
290                                                                            true,
291                                                                            true, mBoundingRect ) );
292   mRootActor.ApplyConstraint(constraint);
293 }
294
295 void TextInputPopup::ApplyTailConstraint()
296 {
297   mTail.RemoveConstraints();
298   Constraint constraint = Constraint::New<float>( Actor::POSITION_X,
299                                                   LocalSource( Actor::SIZE_WIDTH ),
300                                                   LocalSource( Actor::ANCHOR_POINT_X ),
301                                                   ParentXAxisConstraint());
302 }
303
304 void TextInputPopup::CreateLayer( const Vector2& size )
305 {
306   mLayer = Layer::New();
307   mLayer.SetParentOrigin(ParentOrigin::CENTER);
308   mLayer.SetAnchorPoint(AnchorPoint::CENTER);
309   mLayer.SetSize( size ); // matches stencil size
310   mLayer.SetName("popup-mLayer");
311 }
312
313 void TextInputPopup::CreateStencil( const Vector2& size )
314 {
315   mStencil = CreateSolidColorActor( Color::BLUE );
316   mStencil.SetParentOrigin( Vector3( ParentOrigin::CENTER ) );
317   mStencil.SetAnchorPoint( AnchorPoint::CENTER );
318   mStencil.SetDrawMode( DrawMode::STENCIL );
319   mStencil.SetSize( size  ); // slightly smaller than layer and stencil so over shoot always inside.
320   mStencil.SetVisible( true );
321   mStencil.SetName("popup-stencil");
322 }
323
324 void TextInputPopup::OnScrollStarted( const Vector3& position )
325 {
326   mBackground.SetSensitive( false );
327 }
328
329 void TextInputPopup::OnScrollCompleted( const Vector3& position )
330 {
331   mBackground.SetSensitive( true );
332 }
333
334 void TextInputPopup::CreateScrollView( const Vector2& domainSize, const Vector2& visibleSize )
335 {
336   mScrollView = Toolkit::ScrollView::New();
337   mScrollView.SetName("popup-scroll-view");
338   mScrollView.SetAnchorPoint( AnchorPoint::TOP_LEFT );
339   mScrollView.SetParentOrigin( ParentOrigin::TOP_LEFT );
340   mScrollView.SetSize( visibleSize.x, visibleSize.y  );
341   mScrollView.SetScrollingDirection( PanGestureDetector::DIRECTION_HORIZONTAL, Degree( 40.0f ) );
342   mScrollView.SetAxisAutoLock( true );
343   mScrollView.ScrollStartedSignal().Connect( this, &TextInputPopup::OnScrollStarted );
344   mScrollView.ScrollCompletedSignal().Connect( this, &TextInputPopup::OnScrollCompleted );
345
346   RulerPtr rulerX = new DefaultRuler();  // IntrusivePtr which is unreferenced when ScrollView is destroyed.
347   RulerPtr rulerY = new DefaultRuler();  // IntrusivePtr which is unreferenced when ScrollView is destroyed.
348   rulerY->Disable();
349   rulerX->SetDomain( RulerDomain( 0, domainSize.width, true ) );
350   mScrollView.SetRulerX(rulerX);
351   mScrollView.SetRulerY(rulerY);
352 }
353
354 void TextInputPopup::RemoveFromStage()
355 {
356   Actor rootActor = Self();
357   Stage::GetCurrent().Remove( rootActor );
358 }
359
360 void TextInputPopup::Clear()
361 {
362   if ( mBackground )
363   {
364     UnparentAndReset( mTail );
365     UnparentAndReset( mStencil );
366     UnparentAndReset( mBackground );
367     UnparentAndReset( mScrollView );
368     UnparentAndReset( mLayer );
369     mButtonContainer.clear();
370     mDividerContainer.clear();
371
372     RemoveFromStage();
373     mRootActor.RemoveConstraints();
374
375     mState = StateHidden;
376   }
377 }
378
379 Toolkit::TextView TextInputPopup::CreateOptionText( const MarkupProcessor::StyledTextArray& styledCaption )
380 {
381   Toolkit::TextView label = Toolkit::TextView::New( styledCaption );
382   label.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
383   label.SetWidthExceedPolicy( Toolkit::TextView::Fade );
384   label.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
385   label.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
386   label.SetPosition( 0.0f, TEXT_POSITION_OFFSET );
387
388   return label;
389 }
390
391 ImageActor TextInputPopup::CreateOptionIcon( Image iconImage, const Vector4& color )
392 {
393   ImageActor icon = ImageActor::New( iconImage );
394
395   icon.SetSize( DEFAULT_ICON_SIZE );
396   icon.SetParentOrigin( ParentOrigin::TOP_CENTER );
397   icon.SetAnchorPoint( AnchorPoint::TOP_CENTER );
398   icon.SetPosition( 0.0f, ICON_POSITION_OFFSET );
399   icon.SetColor( color );
400
401   return icon;
402 }
403
404 void TextInputPopup::CreatePopUpBackground()
405 {
406   // Create background-panel if not already created (required if we have at least one option)
407   if ( !mBackground )
408   {
409     mBackground = Toolkit::CreateSolidColorActor( GetCutPastePopUpColor(), true, mBorderColor );
410     mBackground.SetAnchorPoint( AnchorPoint::TOP_LEFT );
411     mBackground.SetParentOrigin( ParentOrigin::TOP_LEFT );
412     mBackground.SetName("pop-up-background");
413     mContentSize = POPUP_TEXT_OFFSET;
414     Hide(false);
415     AddToStage();
416
417     // Add Tail too.
418     Image tailImage = Image::New( DEFAULT_POPUP_TAIL_BOTTOM );
419     Image tailImageOutline = Image::New( DEFAULT_POPUP_TAIL_BOTTOM_OUTLINE );
420
421     mTailOutline = ImageActor::New ( tailImageOutline );
422     mTailOutline.SetParentOrigin( ParentOrigin::CENTER );
423     mTailOutline.SetAnchorPoint( AnchorPoint::CENTER );
424     mTailOutline.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) );
425
426     mTail = ImageActor::New( tailImage );
427     mTail.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
428     mTail.SetAnchorPoint( AnchorPoint::TOP_CENTER );
429     // TODO: Make tail visible, and positioned in relation to original intended position of popup (i.e. before constrained effects)
430     mTail.SetVisible(true);
431     mTail.SetColor( mCutPasteButtonsColor );
432     mTailOutline.SetColor( mBorderColor );
433     mTail.Add( mTailOutline);
434   }
435 }
436
437 void TextInputPopup::CreateDivider()
438 {
439   if(mButtonContainer.size() > 0)
440   {
441     ImageActor divider = Toolkit::CreateSolidColorActor( mBorderColor );
442     divider.SetParentOrigin( ParentOrigin::TOP_LEFT );
443     divider.SetAnchorPoint( AnchorPoint::TOP_LEFT );
444     divider.SetPosition( Vector3( mContentSize.width, POPUP_TEXT_OFFSET.y, 0.0f ) );
445     divider.SetColor( mBorderColor );
446     // Keep track of all the dividers. As their height's need to be updated to the max. of all
447     // buttons currently added.
448     mDividerContainer.push_back(divider);
449     mBackground.Add( divider );
450     mContentSize.width += DIVIDER_WIDTH;
451   }
452 }
453
454 ImageActor TextInputPopup::CreatePressedBackground( const Vector3 requiredSize )
455 {
456   std::string pressedImageFilename;
457   Vector4 pressedImageBorder;
458   Vector2 pressedImageSize;
459
460   ImageActor pressedButtonBg = Toolkit::CreateSolidColorActor( GetCutPastePopUpPressedColor() );
461
462   pressedButtonBg.SetSize ( requiredSize );
463   pressedButtonBg.SetParentOrigin( ParentOrigin::CENTER );
464   pressedButtonBg.SetAnchorPoint( AnchorPoint::CENTER );
465
466   return pressedButtonBg;
467 }
468
469 TextInputPopup::ButtonRequirement TextInputPopup::CreateRequiredButton( TextInputPopup::Buttons buttonId, std::size_t orderOfPriority,
470                                                                         const std::string& name, const std::string& caption, Image iconImage, bool enabled )
471 {
472   TextInputPopup::ButtonRequirement currentButton;
473
474   currentButton.buttonId = buttonId;
475   currentButton.orderOfPriority = orderOfPriority;
476   currentButton.name = name;
477   currentButton.caption = caption;
478   currentButton.iconImage = iconImage;
479   currentButton.enabled = enabled;
480
481   return currentButton;
482 }
483
484 void TextInputPopup::CreateOrderedListOfOptions()
485 {
486   mOrderListOfButtons.clear();
487
488   for ( std::size_t index= 0; index < ButtonsEnumEnd; index++ )
489   {
490     TextInputPopup::ButtonRequirement currentButton;
491
492     // Create button for each possible option using Option priority
493     switch ( index )
494     {
495       case ButtonsCut:
496       {
497         Image cutIcon = Image::New( DEFAULT_ICON_CUT );
498         currentButton = CreateRequiredButton( ButtonsCut, mCutOptionPriority, OPTION_CUT, GET_LOCALE_TEXT("IDS_COM_BODY_CUT"), cutIcon, false );
499         break;
500       }
501       case ButtonsCopy:
502       {
503         Image copyIcon = Image::New( DEFAULT_ICON_COPY );
504         currentButton = CreateRequiredButton( ButtonsCopy, mCopyOptionPriority, OPTION_COPY, GET_LOCALE_TEXT("IDS_COM_BODY_COPY"), copyIcon, false );
505         break;
506       }
507       case ButtonsPaste:
508       {
509         Image pasteIcon = Image::New( DEFAULT_ICON_PASTE );
510         currentButton = CreateRequiredButton( ButtonsPaste, mPasteOptionPriority, OPTION_PASTE, GET_LOCALE_TEXT("IDS_COM_BODY_PASTE"), pasteIcon, false );
511         break;
512       }
513       case ButtonsSelect:
514       {
515         Image selectIcon = Image::New( DEFAULT_ICON_SELECT );
516         currentButton = CreateRequiredButton( ButtonsSelect, mSelectOptionPriority, OPTION_SELECT_WORD, GET_LOCALE_TEXT("IDS_COM_SK_SELECT"), selectIcon, false );
517         break;
518       }
519       case ButtonsSelectAll:
520       {
521         Image selectAllIcon = Image::New( DEFAULT_ICON_SELECT_ALL );
522         currentButton = CreateRequiredButton( ButtonsSelectAll, mSelectAllOptionPriority, OPTION_SELECT_ALL, GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL"), selectAllIcon, false );
523         break;
524       }
525       case ButtonsClipboard:
526       {
527         Image clipboardIcon = Image::New( DEFAULT_ICON_CLIPBOARD );
528         currentButton = CreateRequiredButton( ButtonsClipboard, mClipboardOptionPriority, OPTION_CLIPBOARD, GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD"), clipboardIcon, false );
529         break;
530       }
531       case ButtonsEnumEnd:
532       {
533         DALI_ASSERT_DEBUG( "ButtonsEnumEnd used but an invalid choice");
534         currentButton.orderOfPriority = 0;
535         break;
536       }
537     }
538
539     bool match = false;
540
541     // Insert button in list of buttons in order of priority setting.
542     for( std::vector<ButtonRequirement>::iterator it = mOrderListOfButtons.begin(), endIt = mOrderListOfButtons.end(); ( it != endIt && !match ); ++it )
543     {
544       const ButtonRequirement& button( *it );
545       if ( currentButton.orderOfPriority < button.orderOfPriority )
546       {
547         if ( currentButton.orderOfPriority != 0 ) // If order priority 0 then do not add button as not required.
548         {
549           mOrderListOfButtons.insert( it, currentButton );
550         }
551         match = true;
552       }
553     }
554
555     if ( !match)
556     {
557       mOrderListOfButtons.push_back( currentButton );
558     }
559   }
560 }
561
562 void TextInputPopup::AddOption(const std::string& name, const std::string& caption, const Image iconImage,  bool finalOption)
563 {
564   CreatePopUpBackground();
565
566   CreateDivider();
567
568   // Create a Button with Text, Icon and highlight when pressed
569
570   Toolkit::PushButton button = Toolkit::PushButton::New();
571   button.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
572   button.SetName( name );
573
574   // Create container for text and icon when not pressed
575   Actor iconTextContainer = Actor::New();
576   iconTextContainer.SetParentOrigin( ParentOrigin::TOP_LEFT );
577   iconTextContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT );
578
579   // 1. Add text.
580   TextStyle style;
581   style.SetFontPointSize( PointSize( DEFAULT_UI_FONT_SIZE ) );
582   style.SetTextColor( mCutPasteTextsColor );
583   MarkupProcessor::StyledTextArray styledCaption;
584   styledCaption.push_back( MarkupProcessor::StyledText( Text( caption ), style ) );
585   Toolkit::TextView label = CreateOptionText( styledCaption );
586   label.SetName( name );
587
588   iconTextContainer.Add( label );
589
590   // Get natural size of text and then constrain it to bounds.
591   const Vector3 textSize = label.GetNaturalSize();
592   const Vector3 constrainedTextSize = Min( textSize, TEXT_LABEL_MAX_SIZE );
593   Vector3 buttonSize( Max(constrainedTextSize + BUTTON_TEXT_ENLARGE, BUTTON_TEXT_MINIMUM_SIZE) );
594   buttonSize = ( Min(buttonSize, BUTTON_TEXT_MAXIMUM_SIZE) );
595   label.SetSize( Min( buttonSize, constrainedTextSize ) );
596
597   button.SetParentOrigin( ParentOrigin::TOP_LEFT );
598   button.SetAnchorPoint( AnchorPoint::TOP_LEFT );
599   button.SetSize( buttonSize );
600   button.SetPosition( Vector3( mContentSize.width, POPUP_BORDER.y, 0.0f ) );
601
602   // 2. Add icon
603   ImageActor icon = CreateOptionIcon( iconImage, mCutPasteIconsColor );
604
605   iconTextContainer.Add( icon );
606
607   // 3. Add highlight - Pressed state in Pushbutton needs a new image which means creating the text and icon again but including a highlight this time.
608   ImageActor pressedImageBg = CreatePressedBackground( buttonSize );
609
610   Actor iconPressedTextContainer = Actor::New();
611   iconPressedTextContainer.SetDrawMode( DrawMode::OVERLAY );
612
613   style.SetTextColor( mCutPasteTextsPressedColor );
614   styledCaption.clear();
615   styledCaption.push_back( MarkupProcessor::StyledText( Text( caption ), style ) );
616   Toolkit::TextView pressedLabel = CreateOptionText( styledCaption );
617   pressedLabel.SetSize( Min( buttonSize, TEXT_LABEL_MAX_SIZE ) );
618   ImageActor pressedIcon = CreateOptionIcon( iconImage, mCutPasteIconsPressedColor );
619
620   iconPressedTextContainer.Add( pressedImageBg );
621   iconPressedTextContainer.Add( pressedLabel );
622   iconPressedTextContainer.Add( pressedIcon );
623
624   // Set Pressed button Image
625   iconPressedTextContainer.SetSize( buttonSize );
626   button.SetPressedImage( iconPressedTextContainer );
627
628   // Set Normal button Image
629   iconTextContainer.SetSize( buttonSize );
630   button.SetButtonImage( iconTextContainer );
631   mBackground.Add( button );
632
633   // Update content size (represents size of all content i.e. from top-left of first button, to bottom-right of last button)
634   mContentSize.width += buttonSize.width;
635   mContentSize.height = std::max(mContentSize.height + ( POPUP_BORDER.y ), buttonSize.height);
636   mButtonContainer.push_back(button);
637
638   // resize all dividers based on the height content (i.e. max of all button heights)
639   const float dividerHeight = mContentSize.height - DIVIDER_MARGIN;
640   for(ActorIter i = mDividerContainer.begin(); i != mDividerContainer.end(); ++i)
641   {
642     i->SetSize( DIVIDER_WIDTH, dividerHeight );
643   }
644   button.ClickedSignal().Connect( this, &TextInputPopup::OnButtonPressed );
645 }
646
647 void TextInputPopup::Hide(bool animate)
648 {
649   if( mRootActor )
650   {
651     if(mAnimation)
652     {
653       mAnimation.Clear();
654       mAnimation.Reset();
655     }
656
657     if(animate)
658     {
659       mAnimation = Animation::New( HIDE_POPUP_ANIMATION_DURATION );
660       mAnimation.AnimateTo( Property(mRootActor, Actor::SCALE), Vector3::ZERO, AlphaFunctions::EaseOut );
661       mAnimation.AnimateTo( Property(mRootActor, Actor::COLOR_ALPHA), 0.0f, AlphaFunctions::EaseOut );
662       mAnimation.Play();
663
664       mAnimation.FinishedSignal().Connect( this, &TextInputPopup::OnHideFinished );
665       mState = StateHiding;
666     }
667     else
668     {
669       mRootActor.SetProperty(Actor::SCALE, Vector3::ZERO);
670       mRootActor.SetProperty(Actor::COLOR_ALPHA, 0.0f);
671       mState = StateHidden;
672     }
673   }
674 }
675
676 void TextInputPopup::Show(bool animate)
677 {
678   if( mRootActor )
679   {
680     mRootActor.SetSensitive( true );
681
682     mTail.SetPosition(Vector3( mPopupTailXPosition, TAIL_Y_POSITION, 0.0f));
683
684     if(mAnimation)
685     {
686       mAnimation.Clear();
687       mAnimation.Reset();
688     }
689
690     if(animate)
691     {
692       mAnimation = Animation::New( SHOW_POPUP_ANIMATION_DURATION );
693       mAnimation.AnimateTo( Property(mRootActor, Actor::SCALE), Vector3::ONE, AlphaFunctions::EaseOut );
694       mAnimation.AnimateTo( Property(mRootActor, Actor::COLOR_ALPHA), 1.0f, AlphaFunctions::EaseOut );
695       mAnimation.Play();
696
697       mAnimation.FinishedSignal().Connect( this, &TextInputPopup::OnShowFinished );
698       mState = StateShowing;
699     }
700     else
701     {
702       mRootActor.SetProperty(Actor::SCALE, Vector3::ONE);
703       mRootActor.SetProperty(Actor::COLOR_ALPHA, 1.0f);
704       mState = StateShown;
705     }
706   }
707 }
708
709 void TextInputPopup::SetAlternativeOffset(Vector2 offset)
710 {
711   mRootActor.SetProperty( mAlternativeOffsetProperty, offset );
712   ApplyConfinementConstraint();
713 }
714
715 TextInputPopup::State TextInputPopup::GetState(void) const
716 {
717   return mState;
718 }
719
720 Actor TextInputPopup::GetRootActor() const
721 {
722   return mRootActor;
723 }
724
725 // Styling
726
727 void TextInputPopup::SetCutPastePopUpColor( const Vector4& color )
728 {
729   mCutPasteButtonsColor = color;
730 }
731
732 const Vector4& TextInputPopup::GetCutPastePopUpColor() const
733 {
734   return mCutPasteButtonsColor;
735 }
736
737 void TextInputPopup::SetCutPastePopUpPressedColor( const Vector4& color )
738 {
739   mCutPasteButtonsPressedColor = color;
740 }
741
742 const Vector4& TextInputPopup::GetCutPastePopUpPressedColor() const
743 {
744   return mCutPasteButtonsPressedColor;
745 }
746
747 void TextInputPopup::SetCutPastePopUpBorderColor( const Vector4& color )
748 {
749   mBorderColor = color;
750 }
751
752 const Vector4& TextInputPopup::GetCutPastePopUpBorderColor() const
753 {
754   return mBorderColor;
755 }
756
757 void TextInputPopup::SetCutPastePopUpIconColor( const Vector4& color )
758 {
759   mCutPasteIconsColor = color;
760 }
761
762 const Vector4& TextInputPopup::GetCutPastePopUpIconColor() const
763 {
764   return mCutPasteIconsColor;
765 }
766
767 void TextInputPopup::SetCutPastePopUpIconPressedColor( const Vector4& color )
768 {
769   mCutPasteIconsPressedColor = color;
770 }
771
772 const Vector4& TextInputPopup::GetCutPastePopUpIconPressedColor()
773 {
774   return mCutPasteIconsPressedColor;
775 }
776
777 void TextInputPopup::SetCutPastePopUpTextColor( const Vector4& color )
778 {
779   mCutPasteTextsColor = color;
780 }
781
782 const Vector4& TextInputPopup::GetCutPastePopUpTextColor()
783 {
784   return mCutPasteTextsColor;
785 }
786
787 void TextInputPopup::SetCutPastePopUpTextPressedColor( const Vector4& color )
788 {
789   mCutPasteTextsPressedColor = color;
790 }
791
792 const Vector4& TextInputPopup::GetCutPastePopUpTextPressedColor()
793 {
794   return mCutPasteTextsPressedColor;
795 }
796
797 void TextInputPopup::TogglePopUpButtonOnOff( TextInputPopup::Buttons requiredButton, bool enable )
798 {
799   bool match ( false );
800   for( std::vector<ButtonRequirement>::iterator it = mOrderListOfButtons.begin(), endIt = mOrderListOfButtons.end(); ( it != endIt && !match ); ++it )
801    {
802      ButtonRequirement& button( *it );
803      if ( requiredButton == button.buttonId )
804      {
805        button.enabled = enable;
806        match = true;
807      }
808    }
809 }
810
811 void TextInputPopup::SetButtonPriorityPosition( TextInputPopup::Buttons button, unsigned int priority )
812 {
813   switch ( button )
814   {
815     case ButtonsCut:
816     {
817       mCutOptionPriority = priority;
818       break;
819     }
820     case ButtonsCopy:
821     {
822       mCopyOptionPriority = priority;
823       break;
824     }
825     case ButtonsPaste:
826     {
827       mPasteOptionPriority = priority;
828       break;
829     }
830     case ButtonsSelect:
831     {
832       mSelectOptionPriority = priority;
833       break;
834     }
835     case ButtonsSelectAll:
836     {
837       mSelectAllOptionPriority = priority;
838       break;
839     }
840     case ButtonsClipboard:
841     {
842       mClipboardOptionPriority = priority;
843       break;
844     }
845     case ButtonsEnumEnd:
846     {
847       DALI_ASSERT_DEBUG( "ButtonsEnumEnd used but an invalid choice");
848       break;
849     }
850   }
851   CreateOrderedListOfOptions(); // Update list of options as priority changed.
852 }
853
854 unsigned int TextInputPopup::GetButtonPriorityPosition( TextInputPopup::Buttons button ) const
855 {
856   unsigned int priority = 0;
857
858   switch ( button )
859   {
860     case ButtonsCut:
861     {
862       priority = mCutOptionPriority;
863       break;
864     }
865     case ButtonsCopy:
866     {
867       priority = mCopyOptionPriority;
868       break;
869     }
870     case ButtonsPaste:
871     {
872       priority = mPasteOptionPriority;
873       break;
874     }
875     case ButtonsSelect:
876     {
877       priority = mSelectOptionPriority;
878       break;
879     }
880     case ButtonsSelectAll:
881     {
882       priority = mSelectAllOptionPriority;
883       break;
884     }
885     case ButtonsClipboard:
886     {
887       priority = mClipboardOptionPriority;
888       break;
889     }
890     case ButtonsEnumEnd:
891     {
892       DALI_ASSERT_DEBUG( "ButtonsEnumEnd used but an invalid choice");
893       break;
894     }
895   }
896
897   return priority;
898 }
899
900 void TextInputPopup::AddPopupOptions()
901 {
902   for( std::vector<ButtonRequirement>::const_iterator it = mOrderListOfButtons.begin(), endIt = mOrderListOfButtons.end(); ( it != endIt ); ++it )
903   {
904     const ButtonRequirement& button( *it );
905     if (  button.enabled )
906     {
907       AddOption( button.name, button.caption, button.iconImage, false );
908     }
909   }
910
911   float visiblePopUpWidth = std::min( mContentSize.width - POP_UP_SCREEN_EDGE_MARGIN*2 , mBoundingRect.width - POP_UP_SCREEN_EDGE_MARGIN *2);
912   float visbilePopUpHeight = std::max( mContentSize.height, POPUP_MINIMUM_SIZE.height );
913   Vector2 visiblePopUpSize  = Vector2( visiblePopUpWidth, visbilePopUpHeight );
914
915   visiblePopUpWidth = std::max( visiblePopUpWidth,  POPUP_MINIMUM_SIZE.width );
916
917   mBackground.SetSize( mContentSize.width, mContentSize.height );
918   mRootActor.SetSize( visiblePopUpWidth, visbilePopUpHeight );   // Make Root Actor reflect the size of its content
919
920   CreateLayer( visiblePopUpSize );
921   CreateStencil( visiblePopUpSize );
922   CreateScrollView( Vector2( mContentSize.width, mContentSize.height ), visiblePopUpSize );
923
924   mLayer.Add( mStencil );
925   mLayer.Add( mScrollView );
926   mScrollView.Add( mBackground );
927   mRootActor.Add( mTail );
928
929   Self().Add(mLayer);
930
931   mLayer.Lower();
932 }
933
934 void TextInputPopup::SetPopupBoundary( const Rect<float>& boundingRectangle )
935 {
936   mBoundingRect =  boundingRectangle;
937 }
938
939 void TextInputPopup::SetTailPosition( const Vector3& position )
940 {
941   mPopupTailXPosition = position.x;
942   ApplyTailConstraint();
943 }
944
945 bool TextInputPopup::OnButtonPressed( Toolkit::Button button )
946 {
947   mPressedSignal.Emit( button );
948   return false;
949 }
950
951 void TextInputPopup::OnHideFinished(Animation& source)
952 {
953   source.FinishedSignal().Disconnect( this, &TextInputPopup::OnHideFinished );
954   Clear();
955   mState = StateHidden;
956   mHideFinishedSignal.Emit( *this );
957 }
958
959 void TextInputPopup::OnShowFinished(Animation& source)
960 {
961   source.FinishedSignal().Disconnect( this, &TextInputPopup::OnShowFinished );
962   mState = StateShown;
963   mShowFinishedSignal.Emit( *this );
964 }
965
966 TextInputPopup::PressedSignalV2& TextInputPopup::PressedSignal()
967 {
968   return mPressedSignal;
969 }
970
971 TextInputPopup::HideFinishedSignalV2& TextInputPopup::HideFinishedSignal()
972 {
973   return mHideFinishedSignal;
974 }
975
976 TextInputPopup::ShowFinishedSignalV2& TextInputPopup::ShowFinishedSignal()
977 {
978   return mShowFinishedSignal;
979 }
980
981 } // namespace Internal
982
983 } // namespace Toolkit
984
985 } // namespace Dali
986