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