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