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