Further Setter/Getter public API removal from Dali::Actor
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / tooltip / tooltip.cpp
1 /*
2  * Copyright (c) 2017 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 // CLASS HEADER
19 #include <dali-toolkit/internal/controls/tooltip/tooltip.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath>
23
24 #include <dali/public-api/events/hover-event.h>
25 #include <dali/public-api/adaptor-framework/timer.h>
26 #include <dali/public-api/common/stage.h>
27 #include <dali/devel-api/scripting/enum-helper.h>
28
29 // INTERNAL INCLUDES
30 #include <dali-toolkit/public-api/controls/table-view/table-view.h>
31 #include <dali-toolkit/public-api/controls/text-controls/text-label.h>
32 #include <dali-toolkit/public-api/visuals/visual-properties.h>
33 #include <dali-toolkit/devel-api/controls/tooltip/tooltip-properties.h>
34 #include <dali-toolkit/public-api/visuals/text-visual-properties.h>
35 #include <dali-toolkit/public-api/visuals/visual-properties.h>
36 #include <dali-toolkit/internal/controls/popup/popup-impl.h>
37 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
38
39 namespace Dali
40 {
41
42 namespace Toolkit
43 {
44
45 namespace Internal
46 {
47
48 namespace
49 {
50
51 DALI_ENUM_TO_STRING_TABLE_BEGIN( TOOLTIP_POSITION )
52 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::Tooltip::Position, ABOVE )
53 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::Tooltip::Position, BELOW )
54 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::Tooltip::Position, HOVER_POINT )
55 DALI_ENUM_TO_STRING_TABLE_END( TOOLTIP_POSITION )
56
57 const float MILLISECONDS_PER_SECOND = 1000.0f;
58
59 const char * const PROPERTY_CONTENT_NAME              = "content";
60 const char * const PROPERTY_LAYOUT_NAME               = "layout";
61 const char * const PROPERTY_WAIT_TIME_NAME            = "waitTime";
62 const char * const PROPERTY_BACKGROUND_NAME           = "background";
63 const char * const PROPERTY_TAIL_NAME                 = "tail";
64 const char * const PROPERTY_POSITION_NAME             = "position";
65 const char * const PROPERTY_HOVER_POINT_OFFSET_NAME   = "hoverPointOffset";
66 const char * const PROPERTY_MOVEMENT_THRESHOLD        = "movementThreshold";
67 const char * const PROPERTY_DISAPPEAR_ON_MOVEMENT     = "disappearOnMovement";
68
69 const char * const PROPERTY_BACKGROUND_VISUAL         = "visual";
70 const char * const PROPERTY_BACKGROUND_BORDER         = "border";
71
72 const char * const PROPERTY_TAIL_VISIBILITY           = "visibility";
73 const char * const PROPERTY_TAIL_ABOVE_VISUAL         = "aboveVisual";
74 const char * const PROPERTY_TAIL_BELOW_VISUAL         = "belowVisual";
75
76 } // unnamed namespace
77
78 TooltipPtr Tooltip::New( Toolkit::Control control )
79 {
80   return new Tooltip( control );
81 }
82
83 void Tooltip::SetProperties( const Property::Value& value )
84 {
85   Toolkit::Control control = mControl.GetHandle();
86   if( control )
87   {
88     Property::Map* properties = value.GetMap();
89     if( properties )
90     {
91       const Property::Map::SizeType count = properties->Count();
92       for( Property::Map::SizeType position = 0; position < count; ++position )
93       {
94         KeyValuePair keyValue = properties->GetKeyValue( position );
95         Property::Key& key = keyValue.first;
96         Property::Value& value = keyValue.second;
97
98         if( key == Toolkit::Tooltip::Property::CONTENT || key == PROPERTY_CONTENT_NAME )
99         {
100           SetContent( control, value );
101         }
102         else if( key == Toolkit::Tooltip::Property::LAYOUT || key == PROPERTY_LAYOUT_NAME )
103         {
104           value.Get( mLayout );
105         }
106         else if( key == Toolkit::Tooltip::Property::WAIT_TIME || key == PROPERTY_WAIT_TIME_NAME )
107         {
108           float waitTime = 0.0f;
109           if( value.Get( waitTime ) )
110           {
111             mWaitTime = waitTime * MILLISECONDS_PER_SECOND;
112           }
113         }
114         else if( key == Toolkit::Tooltip::Property::BACKGROUND || key == PROPERTY_BACKGROUND_NAME )
115         {
116           SetBackground( value );
117         }
118         else if( key == Toolkit::Tooltip::Property::TAIL || key == PROPERTY_TAIL_NAME )
119         {
120           SetTail( value );
121         }
122         else if( key == Toolkit::Tooltip::Property::POSITION || key == PROPERTY_POSITION_NAME )
123         {
124           Scripting::GetEnumerationProperty< Toolkit::Tooltip::Position::Type >( value, TOOLTIP_POSITION_TABLE, TOOLTIP_POSITION_TABLE_COUNT, mPositionType );
125         }
126         else if( key == Toolkit::Tooltip::Property::HOVER_POINT_OFFSET || key == PROPERTY_HOVER_POINT_OFFSET_NAME )
127         {
128           value.Get( mHoverPointOffset );
129         }
130         else if( key == Toolkit::Tooltip::Property::MOVEMENT_THRESHOLD || key == PROPERTY_MOVEMENT_THRESHOLD )
131         {
132           value.Get( mMovementThreshold );
133         }
134         else if( key == Toolkit::Tooltip::Property::DISAPPEAR_ON_MOVEMENT || key == PROPERTY_DISAPPEAR_ON_MOVEMENT )
135         {
136           value.Get( mDisappearOnMovement );
137         }
138       }
139     }
140     else
141     {
142       Property::Type type = value.GetType();
143       if( ( value.GetType() == Property::STRING ) || ( type == Property::ARRAY ) )
144       {
145         SetContent( control, value );
146       }
147     }
148   }
149 }
150
151 void Tooltip::CreatePropertyMap( Property::Map& map ) const
152 {
153   if( ! mContentTextVisual.Empty() )
154   {
155     Property::Map content = mContentTextVisual; // Need this copy as there's no Value constructor which takes in a 'const Property::Map&'.
156     map.Insert( Toolkit::Tooltip::Property::CONTENT, content );
157   }
158   else if( ! mContentArray.Empty() )
159   {
160     Property::Array content = mContentArray; // Need this copy as there's no Value constructor which takes in a 'const Property::Array&'.
161     map.Insert( Toolkit::Tooltip::Property::CONTENT, content );
162   }
163
164   map.Insert( Toolkit::Tooltip::Property::LAYOUT, mLayout );
165   map.Insert( Toolkit::Tooltip::Property::WAIT_TIME, static_cast<float>( mWaitTime ) / MILLISECONDS_PER_SECOND );
166   map.Insert( Toolkit::Tooltip::Property::BACKGROUND,
167               Property::Map().Add( Toolkit::Tooltip::Background::Property::VISUAL, mBackgroundImage )
168                              .Add( Toolkit::Tooltip::Background::Property::BORDER, mBackgroundBorder ) );
169   map.Insert( Toolkit::Tooltip::Property::TAIL,
170               Property::Map().Add( Toolkit::Tooltip::Tail::Property::VISIBILITY, mTailVisibility )
171                              .Add( Toolkit::Tooltip::Tail::Property::ABOVE_VISUAL, mTailImages[ Toolkit::Tooltip::Tail::Property::ABOVE_VISUAL ])
172                              .Add( Toolkit::Tooltip::Tail::Property::BELOW_VISUAL, mTailImages[ Toolkit::Tooltip::Tail::Property::BELOW_VISUAL ]) );
173   map.Insert( Toolkit::Tooltip::Property::POSITION, mPositionType );
174   map.Insert( Toolkit::Tooltip::Property::HOVER_POINT_OFFSET, mHoverPointOffset );
175   map.Insert( Toolkit::Tooltip::Property::MOVEMENT_THRESHOLD, mMovementThreshold );
176   map.Insert( Toolkit::Tooltip::Property::DISAPPEAR_ON_MOVEMENT, mDisappearOnMovement );
177 }
178
179 Tooltip::Tooltip( Toolkit::Control control )
180 : mPopup(),
181   mTooltipTimer(),
182   mControl( control ),
183   mContentTextVisual(),
184   mTailImages(),
185   mContentArray(),
186   mBackgroundBorder( 0, 0, 0, 0 ),
187   mLayout( 1, 1 ),
188   mHoverPoint(),
189   mHoverPointOffset( 10.0f, 10.0f ),
190   mBackgroundImage(),
191   mMovementThreshold( 5.0f ),
192   mWaitTime( 500 ),
193   mPositionType( Toolkit::Tooltip::Position::ABOVE ),
194   mTailVisibility( false ),
195   mDisappearOnMovement( false ),
196   mSignalsConnected( false )
197 {
198   mTailImages[ Toolkit::Tooltip::Tail::Property::ABOVE_VISUAL ] = "";
199   mTailImages[ Toolkit::Tooltip::Tail::Property::BELOW_VISUAL ] = "";
200 }
201
202 Tooltip::~Tooltip()
203 {
204   if( mPopup )
205   {
206     mPopup.Unparent();
207     mPopup.Reset();
208   }
209 }
210
211 void Tooltip::SetContent( Toolkit::Control& control, const Property::Value& value )
212 {
213   // Delete popup & timer
214
215   if( mTooltipTimer )
216   {
217     mTooltipTimer.Stop();
218     mTooltipTimer.Reset();
219   }
220
221   if( mPopup )
222   {
223     mPopup.Unparent();
224     mPopup.Reset();
225   }
226
227   bool connectSignals = false;
228
229   Property::Type type = value.GetType();
230   if( type == Property::MAP )
231   {
232     Property::Map* map = value.GetMap();
233     if( map )
234     {
235       mContentTextVisual.Merge( *map );
236
237       Property::Value* typeValue = map->Find( Toolkit::Visual::Property::TYPE, VISUAL_TYPE );
238       if( typeValue )
239       {
240         // Set to an invalid value so it definitely changes if set in Scripting::GetEnumerationProperty
241         Toolkit::Visual::Type visualType = static_cast< Toolkit::Visual::Type >( -1 );
242
243         if( Scripting::GetEnumerationProperty( *typeValue, VISUAL_TYPE_TABLE, VISUAL_TYPE_TABLE_COUNT, visualType ) )
244         {
245           if( visualType == Toolkit::Visual::TEXT )
246           {
247             // Visual Type is text, ensure we have a the TEXT property set before we connect to the signals.
248
249             if( map->Find( Toolkit::TextVisual::Property::TEXT, TEXT_PROPERTY ) )
250             {
251               mContentArray.Clear();
252               connectSignals = true;
253             }
254           }
255           else
256           {
257             // Visual Type is not text, so connect to the signals as we're displaying a non text visual.
258
259             mContentArray.Clear();
260             connectSignals = true;
261           }
262         }
263       }
264     }
265   }
266   else if( type == Property::ARRAY )
267   {
268     if( value.Get( mContentArray ) )
269     {
270       mContentTextVisual.Clear();
271       connectSignals = true;
272     }
273   }
274   else if( type == Property::STRING )
275   {
276     std::string text;
277     if( value.Get( text ) )
278     {
279       mContentTextVisual[ Toolkit::TextVisual::Property::TEXT ] = text;
280       mContentTextVisual[ Toolkit::Visual::Property::TYPE ] = Visual::TEXT;
281       mContentArray.Clear();
282       connectSignals = true;
283     }
284   }
285
286   if( connectSignals && ! mSignalsConnected )
287   {
288     control.HoveredSignal().Connect( this, &Tooltip::OnHovered );
289     control.SetProperty( Actor::Property::LEAVE_REQUIRED, true );
290     mSignalsConnected = true;
291   }
292 }
293
294 void Tooltip::SetBackground( const Property::Value& value )
295 {
296   Property::Type type = value.GetType();
297
298   if( type == Property::STRING )
299   {
300     value.Get( mBackgroundImage );
301     mBackgroundBorder.Set( 0, 0, 0, 0 );
302   }
303   else if( type == Property::MAP )
304   {
305     Property::Map* map = value.GetMap();
306     if( map )
307     {
308       const Property::Map::SizeType count = map->Count();
309       for( Property::Map::SizeType position = 0; position < count; ++position )
310       {
311         KeyValuePair keyValue = map->GetKeyValue( position );
312         Property::Key& key = keyValue.first;
313         Property::Value& value = keyValue.second;
314
315         if( key == Toolkit::Tooltip::Background::Property::VISUAL || key == PROPERTY_BACKGROUND_VISUAL )
316         {
317           value.Get( mBackgroundImage );
318         }
319         else if( key == Toolkit::Tooltip::Background::Property::BORDER || key == PROPERTY_BACKGROUND_BORDER )
320         {
321           if( ! value.Get( mBackgroundBorder ) )
322           {
323             // If not a Property::RECTANGLE, then check if it's a Vector4 and set it accordingly
324             Vector4 valueVector4;
325             if( value.Get( valueVector4 ) )
326             {
327               mBackgroundBorder.left   = valueVector4.x;
328               mBackgroundBorder.right  = valueVector4.y;
329               mBackgroundBorder.bottom = valueVector4.z;
330               mBackgroundBorder.top    = valueVector4.w;
331             }
332           }
333         }
334       }
335     }
336   }
337 }
338
339 void Tooltip::SetTail( const Property::Value& value )
340 {
341   Property::Type type = value.GetType();
342
343   if( type == Property::BOOLEAN )
344   {
345     value.Get( mTailVisibility );
346   }
347   else if( type == Property::MAP )
348   {
349     Property::Map map;
350     if( value.Get( map ) )
351     {
352       const Property::Map::SizeType count = map.Count();
353       for( Property::Map::SizeType position = 0; position < count; ++position )
354       {
355         KeyValuePair keyValue = map.GetKeyValue( position );
356         Property::Key& key = keyValue.first;
357         Property::Value& value = keyValue.second;
358
359         // Set the values manually rather than merging so that we only have to deal with Property indices when creating the actual tooltip.
360
361         if( key == Toolkit::Tooltip::Tail::Property::VISIBILITY || key == PROPERTY_TAIL_VISIBILITY )
362         {
363           value.Get( mTailVisibility );
364         }
365         else if( key == Toolkit::Tooltip::Tail::Property::ABOVE_VISUAL || key == PROPERTY_TAIL_ABOVE_VISUAL )
366         {
367           std::string path;
368           if( value.Get( path ) )
369           {
370             mTailImages[ Toolkit::Tooltip::Tail::Property::ABOVE_VISUAL ] = path;
371           }
372         }
373         else if( key == Toolkit::Tooltip::Tail::Property::BELOW_VISUAL || key == PROPERTY_TAIL_BELOW_VISUAL )
374         {
375           std::string path;
376           if( value.Get( path ) )
377           {
378             mTailImages[ Toolkit::Tooltip::Tail::Property::BELOW_VISUAL ] = path;
379           }
380         }
381       }
382     }
383   }
384 }
385
386 bool Tooltip::OnHovered( Actor /* actor */, const HoverEvent& hover )
387 {
388   const TouchPoint::State state = hover.points[0].state;
389   switch( state )
390   {
391     case TouchPoint::Started:
392     case TouchPoint::Motion:
393     {
394       if( ! mPopup )
395       {
396         if( ! mTooltipTimer )
397         {
398           mHoverPoint = hover.points[ 0 ].screen;
399           mTooltipTimer = Timer::New( mWaitTime );
400           mTooltipTimer.TickSignal().Connect( this, &Tooltip::OnTimeout );
401           mTooltipTimer.Start();
402         }
403         else
404         {
405           Vector2 movement = mHoverPoint - hover.points[ 0 ].screen;
406           if( std::abs( movement.Length() ) > mMovementThreshold )
407           {
408             mTooltipTimer.Stop();
409             mTooltipTimer.Reset();
410
411             mHoverPoint = hover.points[ 0 ].screen;
412             mTooltipTimer = Timer::New( mWaitTime );
413             mTooltipTimer.TickSignal().Connect( this, &Tooltip::OnTimeout );
414             mTooltipTimer.Start();
415           }
416         }
417       }
418       else if( mDisappearOnMovement )
419       {
420         // Popup is showing, and we're set to disappear on excessive movement so make sure we're still within the threshold.
421
422         Vector2 movement = mHoverPoint - hover.points[ 0 ].screen;
423         if( std::abs( movement.Length() ) > mMovementThreshold )
424         {
425           // Exceeding the threshold, hide the popup.
426
427           if( mTooltipTimer )
428           {
429             mTooltipTimer.Stop();
430             mTooltipTimer.Reset();
431           }
432           if( mPopup )
433           {
434             mPopup.Unparent();
435             mPopup.Reset();
436           }
437         }
438       }
439       break;
440     }
441     case TouchPoint::Finished:
442     case TouchPoint::Leave:
443     case TouchPoint::Interrupted:
444     {
445       if( mTooltipTimer )
446       {
447         mTooltipTimer.Stop();
448         mTooltipTimer.Reset();
449       }
450       if( mPopup )
451       {
452         mPopup.Unparent();
453         mPopup.Reset();
454       }
455       break;
456     }
457
458     case TouchPoint::Stationary:
459     case TouchPoint::Last:
460     {
461       break;
462     }
463   }
464
465   return true;
466 }
467
468 bool Tooltip::OnTimeout()
469 {
470   Toolkit::Control control = mControl.GetHandle();
471   if( ! mPopup && control )
472   {
473     mPopup = Toolkit::Popup::New();
474
475     // General set up of popup
476     mPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::ALL_DIMENSIONS );
477     mPopup.SetProperty( Toolkit::Popup::Property::CONTEXTUAL_MODE, "NON_CONTEXTUAL" );
478     mPopup.SetProperty( Toolkit::Popup::Property::ANIMATION_MODE, "NONE" );
479     mPopup.SetProperty( Toolkit::Popup::Property::BACKING_ENABLED, false ); // Disable the dimmed backing.
480     mPopup.SetProperty( Toolkit::Popup::Property::TOUCH_TRANSPARENT, true ); // Let events pass through the popup
481     mPopup.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
482     mPopup.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
483
484     // Background
485     mPopup.SetProperty( Toolkit::Popup::Property::POPUP_BACKGROUND_IMAGE, mBackgroundImage );
486     mPopup.SetProperty( Toolkit::Popup::Property::POPUP_BACKGROUND_BORDER, mBackgroundBorder );
487
488     // Tail
489     mPopup.SetProperty( Toolkit::Popup::Property::TAIL_VISIBILITY, mTailVisibility );
490     mPopup.SetProperty( Toolkit::Popup::Property::TAIL_UP_IMAGE,   mTailImages[ Toolkit::Tooltip::Tail::Property::ABOVE_VISUAL ] );
491     mPopup.SetProperty( Toolkit::Popup::Property::TAIL_DOWN_IMAGE, mTailImages[ Toolkit::Tooltip::Tail::Property::BELOW_VISUAL ] );
492
493     Vector3 tailPosition;
494     switch( mPositionType )
495     {
496       case Toolkit::Tooltip::Position::HOVER_POINT:
497       case Toolkit::Tooltip::Position::BELOW:
498       {
499         tailPosition = Vector3( 0.5f, 0.0f, 0.0 );
500         break;
501       }
502
503       case Toolkit::Tooltip::Position::ABOVE:
504       {
505         tailPosition = Vector3( 0.5f, 1.0f, 0.0 );
506         break;
507       }
508     }
509     mPopup.SetProperty( Toolkit::Popup::Property::TAIL_POSITION, tailPosition );
510
511     // Content
512     Actor content;
513     if( ! mContentTextVisual.Empty() )
514     {
515       content = Toolkit::Control::New();
516       content.SetProperty( Toolkit::Control::Property::BACKGROUND, mContentTextVisual );
517     }
518     else if( ! mContentArray.Empty() )
519     {
520       const unsigned int visuals = mContentArray.Size();
521       unsigned int rows = mLayout.x;
522       unsigned int columns = mLayout.y;
523       if( Equals( mLayout.x, 1.0f, Math::MACHINE_EPSILON_1 ) &&
524           Equals( mLayout.y, 1.0f, Math::MACHINE_EPSILON_1 ) &&
525           visuals > 1 )
526       {
527         rows = mLayout.x;
528         columns = visuals;
529       }
530
531       Toolkit::TableView tableView = Toolkit::TableView::New( rows, columns );
532       tableView.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
533
534       for( unsigned int currentContent = 0, currentRow = 0; currentRow < rows && currentContent < visuals; ++currentRow )
535       {
536         tableView.SetFitHeight( currentRow );
537         for( unsigned int currentColumn = 0; currentColumn < columns && currentContent < visuals; ++currentColumn )
538         {
539           Actor child = Toolkit::Control::New();
540           child.SetProperty( Toolkit::Control::Property::BACKGROUND, mContentArray[ currentContent ] );
541
542           Toolkit::TableView::CellPosition cellPosition( currentRow, currentColumn );
543           tableView.AddChild( child, cellPosition );
544           tableView.SetCellAlignment( cellPosition, HorizontalAlignment::CENTER, VerticalAlignment::CENTER );
545           tableView.SetFitWidth( currentColumn );
546
547           ++currentContent;
548         }
549       }
550       content = tableView;
551     }
552     mPopup.SetContent( content );
553
554     // Connect to the relayout signal of the background of the popup as at that point we have the full size
555     Actor popupBackground = GetImpl( mPopup ).GetPopupBackgroundImage();
556     if( popupBackground )
557     {
558       popupBackground.OnRelayoutSignal().Connect( this, &Tooltip::OnRelayout );
559     }
560
561     mPopup.SetDisplayState( Toolkit::Popup::SHOWN );
562
563     Stage::GetCurrent().Add( mPopup );
564   }
565
566   return false;
567 }
568
569 void Tooltip::OnRelayout( Actor actor )
570 {
571   if( mPopup && actor )
572   {
573     float popupWidth = actor.GetRelayoutSize( Dimension::WIDTH );
574     float popupHeight = actor.GetRelayoutSize( Dimension::HEIGHT );
575     float tailHeight = 0.0f;
576     Actor tail;
577
578     if( mTailVisibility )
579     {
580       // Popup's background has the tail, we want to know the tail size as well.
581       if( actor.GetChildCount() )
582       {
583         tail = actor.GetChildAt( 0 );
584         if( tail )
585         {
586           tailHeight = tail.GetRelayoutSize( Dimension::HEIGHT );
587         }
588       }
589     }
590
591     Vector2 stageSize = Stage::GetCurrent().GetSize();
592     Vector3 position;
593
594     switch( mPositionType )
595     {
596       case Toolkit::Tooltip::Position::HOVER_POINT:
597       {
598         position = mHoverPoint + mHoverPointOffset;
599         position.y += tailHeight;
600         break;
601       }
602
603       case Toolkit::Tooltip::Position::ABOVE:
604       {
605         Toolkit::Control control = mControl.GetHandle();
606         if( control )
607         {
608           Vector3 worldPos = control.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION );
609           float height = control.GetRelayoutSize( Dimension::HEIGHT );
610
611           position.x = stageSize.width * 0.5f + worldPos.x - popupWidth * 0.5f;
612           position.y = stageSize.height * 0.5f + worldPos.y - height * 0.5f - popupHeight * 1.0f - tailHeight;
613         }
614         break;
615       }
616
617       case Toolkit::Tooltip::Position::BELOW:
618       {
619         Toolkit::Control control = mControl.GetHandle();
620         if( control )
621         {
622           Vector3 worldPos = control.GetCurrentProperty< Vector3 >( Actor::Property::WORLD_POSITION );
623           float height = control.GetRelayoutSize( Dimension::HEIGHT );
624
625           position.x = stageSize.width * 0.5f + worldPos.x - popupWidth * 0.5f;
626           position.y = stageSize.height * 0.5f + worldPos.y + height * 0.5f + tailHeight;
627         }
628         break;
629       }
630     }
631
632     // Ensure the Popup is still on the screen
633
634     if( position.x < 0.0f )
635     {
636       position.x = 0.0f;
637     }
638     else if( ( position.x + popupWidth ) > stageSize.width )
639     {
640       position.x -= position.x + popupWidth - stageSize.width;
641     }
642
643     bool yPosChanged = false;
644     if( position.y < 0.0f )
645     {
646       yPosChanged = true;
647       position.y = 0.0f;
648     }
649     else if( ( position.y + popupHeight ) > stageSize.height )
650     {
651       yPosChanged = true;
652       position.y -= position.y + popupHeight - stageSize.height;
653     }
654
655     if( yPosChanged && tail )
656     {
657       // If we change the y position, then the tail may be shown pointing to the wrong control so just hide it.
658       tail.SetProperty( Actor::Property::VISIBLE, false );
659     }
660
661     mPopup.SetProperty( Actor::Property::POSITION, position );
662   }
663 }
664
665 } // namespace Internal
666
667 } // namespace Toolkit
668
669 } // namespace Dali