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