[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / tooltip / tooltip.cpp
1 /*
2  * Copyright (c) 2024 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, std::move(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, std::move(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     if(DALI_LIKELY(value.Get(mBackgroundImage)))
293     {
294       mBackgroundBorder.Set(0, 0, 0, 0);
295     }
296   }
297   else if(type == Property::MAP)
298   {
299     const Property::Map* map = value.GetMap();
300     if(map)
301     {
302       const Property::Map::SizeType count = map->Count();
303       for(Property::Map::SizeType position = 0; position < count; ++position)
304       {
305         KeyValuePair     keyValue = map->GetKeyValue(position);
306         Property::Key&   key      = keyValue.first;
307         Property::Value& value    = keyValue.second;
308
309         if(key == Toolkit::Tooltip::Background::Property::VISUAL || key == PROPERTY_BACKGROUND_VISUAL)
310         {
311           value.Get(mBackgroundImage);
312         }
313         else if(key == Toolkit::Tooltip::Background::Property::BORDER || key == PROPERTY_BACKGROUND_BORDER)
314         {
315           if(!value.Get(mBackgroundBorder))
316           {
317             // If not a Property::RECTANGLE, then check if it's a Vector4 and set it accordingly
318             Vector4 valueVector4;
319             if(value.Get(valueVector4))
320             {
321               mBackgroundBorder.left   = valueVector4.x;
322               mBackgroundBorder.right  = valueVector4.y;
323               mBackgroundBorder.bottom = valueVector4.z;
324               mBackgroundBorder.top    = valueVector4.w;
325             }
326           }
327         }
328       }
329     }
330   }
331 }
332
333 void Tooltip::SetTail(const Property::Value& value)
334 {
335   Property::Type type = value.GetType();
336
337   if(type == Property::BOOLEAN)
338   {
339     value.Get(mTailVisibility);
340   }
341   else if(type == Property::MAP)
342   {
343     Property::Map map;
344     if(value.Get(map))
345     {
346       const Property::Map::SizeType count = map.Count();
347       for(Property::Map::SizeType position = 0; position < count; ++position)
348       {
349         KeyValuePair     keyValue = map.GetKeyValue(position);
350         Property::Key&   key      = keyValue.first;
351         Property::Value& value    = keyValue.second;
352
353         // Set the values manually rather than merging so that we only have to deal with Property indices when creating the actual tooltip.
354
355         if(key == Toolkit::Tooltip::Tail::Property::VISIBILITY || key == PROPERTY_TAIL_VISIBILITY)
356         {
357           value.Get(mTailVisibility);
358         }
359         else if(key == Toolkit::Tooltip::Tail::Property::ABOVE_VISUAL || key == PROPERTY_TAIL_ABOVE_VISUAL)
360         {
361           std::string path;
362           if(value.Get(path))
363           {
364             mTailImages[Toolkit::Tooltip::Tail::Property::ABOVE_VISUAL] = path;
365           }
366         }
367         else if(key == Toolkit::Tooltip::Tail::Property::BELOW_VISUAL || key == PROPERTY_TAIL_BELOW_VISUAL)
368         {
369           std::string path;
370           if(value.Get(path))
371           {
372             mTailImages[Toolkit::Tooltip::Tail::Property::BELOW_VISUAL] = path;
373           }
374         }
375       }
376     }
377   }
378 }
379
380 bool Tooltip::OnHovered(Actor /* actor */, const HoverEvent& hover)
381 {
382   const PointState::Type state = hover.GetState(0);
383   switch(state)
384   {
385     case PointState::STARTED:
386     case PointState::MOTION:
387     {
388       if(!mPopup)
389       {
390         if(!mTooltipTimer)
391         {
392           mHoverPoint   = hover.GetScreenPosition(0);
393           mTooltipTimer = Timer::New(mWaitTime);
394           mTooltipTimer.TickSignal().Connect(this, &Tooltip::OnTimeout);
395           mTooltipTimer.Start();
396         }
397         else
398         {
399           Vector2 movement = mHoverPoint - hover.GetScreenPosition(0);
400           if(std::abs(movement.Length()) > mMovementThreshold)
401           {
402             mTooltipTimer.Stop();
403             mTooltipTimer.Reset();
404
405             mHoverPoint   = hover.GetScreenPosition(0);
406             mTooltipTimer = Timer::New(mWaitTime);
407             mTooltipTimer.TickSignal().Connect(this, &Tooltip::OnTimeout);
408             mTooltipTimer.Start();
409           }
410         }
411       }
412       else if(mDisappearOnMovement)
413       {
414         // Popup is showing, and we're set to disappear on excessive movement so make sure we're still within the threshold.
415
416         Vector2 movement = mHoverPoint - hover.GetScreenPosition(0);
417         if(std::abs(movement.Length()) > mMovementThreshold)
418         {
419           // Exceeding the threshold, hide the popup.
420
421           if(mTooltipTimer)
422           {
423             mTooltipTimer.Stop();
424             mTooltipTimer.Reset();
425           }
426           if(mPopup)
427           {
428             mPopup.Unparent();
429             mPopup.Reset();
430           }
431         }
432       }
433       break;
434     }
435     case PointState::FINISHED:
436     case PointState::LEAVE:
437     case PointState::INTERRUPTED:
438     {
439       if(mTooltipTimer)
440       {
441         mTooltipTimer.Stop();
442         mTooltipTimer.Reset();
443       }
444       if(mPopup)
445       {
446         mPopup.Unparent();
447         mPopup.Reset();
448       }
449       break;
450     }
451
452     case PointState::STATIONARY:
453     {
454       break;
455     }
456   }
457
458   return true;
459 }
460
461 bool Tooltip::OnTimeout()
462 {
463   Toolkit::Control control = mControl.GetHandle();
464   if(!mPopup && control)
465   {
466     mPopup = Toolkit::Popup::New();
467
468     // General set up of popup
469     mPopup.SetResizePolicy(ResizePolicy::FIT_TO_CHILDREN, Dimension::ALL_DIMENSIONS);
470     mPopup.SetProperty(Toolkit::Popup::Property::CONTEXTUAL_MODE, "NON_CONTEXTUAL");
471     mPopup.SetProperty(Toolkit::Popup::Property::ANIMATION_MODE, "NONE");
472     mPopup.SetProperty(Toolkit::Popup::Property::BACKING_ENABLED, false);  // Disable the dimmed backing.
473     mPopup.SetProperty(Toolkit::Popup::Property::TOUCH_TRANSPARENT, true); // Let events pass through the popup
474     mPopup.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
475     mPopup.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
476
477     // Background
478     mPopup.SetProperty(Toolkit::Popup::Property::POPUP_BACKGROUND_IMAGE, mBackgroundImage);
479     mPopup.SetProperty(Toolkit::Popup::Property::POPUP_BACKGROUND_BORDER, mBackgroundBorder);
480
481     // Tail
482     mPopup.SetProperty(Toolkit::Popup::Property::TAIL_VISIBILITY, mTailVisibility);
483     mPopup.SetProperty(Toolkit::Popup::Property::TAIL_UP_IMAGE, mTailImages[Toolkit::Tooltip::Tail::Property::ABOVE_VISUAL]);
484     mPopup.SetProperty(Toolkit::Popup::Property::TAIL_DOWN_IMAGE, mTailImages[Toolkit::Tooltip::Tail::Property::BELOW_VISUAL]);
485
486     Vector3 tailPosition;
487     switch(mPositionType)
488     {
489       case Toolkit::Tooltip::Position::HOVER_POINT:
490       case Toolkit::Tooltip::Position::BELOW:
491       {
492         tailPosition = Vector3(0.5f, 0.0f, 0.0);
493         break;
494       }
495
496       case Toolkit::Tooltip::Position::ABOVE:
497       {
498         tailPosition = Vector3(0.5f, 1.0f, 0.0);
499         break;
500       }
501     }
502     mPopup.SetProperty(Toolkit::Popup::Property::TAIL_POSITION, tailPosition);
503
504     // Content
505     Actor content;
506     if(!mContentTextVisual.Empty())
507     {
508       content = Toolkit::Control::New();
509       content.SetProperty(Toolkit::Control::Property::BACKGROUND, mContentTextVisual);
510     }
511     else if(!mContentArray.Empty())
512     {
513       const unsigned int visuals = mContentArray.Size();
514       unsigned int       rows    = mLayout.x;
515       unsigned int       columns = mLayout.y;
516       if(Equals(mLayout.x, 1.0f, Math::MACHINE_EPSILON_1) &&
517          Equals(mLayout.y, 1.0f, Math::MACHINE_EPSILON_1) &&
518          visuals > 1)
519       {
520         rows    = mLayout.x;
521         columns = visuals;
522       }
523
524       Toolkit::TableView tableView = Toolkit::TableView::New(rows, columns);
525       tableView.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS);
526
527       for(unsigned int currentContent = 0, currentRow = 0; currentRow < rows && currentContent < visuals; ++currentRow)
528       {
529         tableView.SetFitHeight(currentRow);
530         for(unsigned int currentColumn = 0; currentColumn < columns && currentContent < visuals; ++currentColumn)
531         {
532           Actor child = Toolkit::Control::New();
533           child.SetProperty(Toolkit::Control::Property::BACKGROUND, mContentArray[currentContent]);
534
535           Toolkit::TableView::CellPosition cellPosition(currentRow, currentColumn);
536           tableView.AddChild(child, cellPosition);
537           tableView.SetCellAlignment(cellPosition, HorizontalAlignment::CENTER, VerticalAlignment::CENTER);
538           tableView.SetFitWidth(currentColumn);
539
540           ++currentContent;
541         }
542       }
543       content = tableView;
544     }
545     mPopup.SetContent(content);
546
547     // Connect to the relayout signal of the background of the popup as at that point we have the full size
548     Actor popupBackground = GetImpl(mPopup).GetPopupBackgroundImage();
549     if(popupBackground)
550     {
551       popupBackground.OnRelayoutSignal().Connect(this, &Tooltip::OnRelayout);
552     }
553
554     mPopup.SetDisplayState(Toolkit::Popup::SHOWN);
555
556     Stage::GetCurrent().Add(mPopup);
557   }
558
559   return false;
560 }
561
562 void Tooltip::OnRelayout(Actor actor)
563 {
564   if(mPopup && actor)
565   {
566     float popupWidth  = actor.GetRelayoutSize(Dimension::WIDTH);
567     float popupHeight = actor.GetRelayoutSize(Dimension::HEIGHT);
568     float tailHeight  = 0.0f;
569     Actor tail;
570
571     if(mTailVisibility)
572     {
573       // Popup's background has the tail, we want to know the tail size as well.
574       if(actor.GetChildCount())
575       {
576         tail = actor.GetChildAt(0);
577         if(tail)
578         {
579           tailHeight = tail.GetRelayoutSize(Dimension::HEIGHT);
580         }
581       }
582     }
583
584     Vector2 stageSize = Stage::GetCurrent().GetSize();
585     Vector3 position;
586
587     switch(mPositionType)
588     {
589       case Toolkit::Tooltip::Position::HOVER_POINT:
590       {
591         position = mHoverPoint + mHoverPointOffset;
592         position.y += tailHeight;
593         break;
594       }
595
596       case Toolkit::Tooltip::Position::ABOVE:
597       {
598         Toolkit::Control control = mControl.GetHandle();
599         if(control)
600         {
601           Vector3 worldPos = control.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION);
602           float   height   = control.GetRelayoutSize(Dimension::HEIGHT);
603
604           position.x = stageSize.width * 0.5f + worldPos.x - popupWidth * 0.5f;
605           position.y = stageSize.height * 0.5f + worldPos.y - height * 0.5f - popupHeight * 1.0f - tailHeight;
606         }
607         break;
608       }
609
610       case Toolkit::Tooltip::Position::BELOW:
611       {
612         Toolkit::Control control = mControl.GetHandle();
613         if(control)
614         {
615           Vector3 worldPos = control.GetCurrentProperty<Vector3>(Actor::Property::WORLD_POSITION);
616           float   height   = control.GetRelayoutSize(Dimension::HEIGHT);
617
618           position.x = stageSize.width * 0.5f + worldPos.x - popupWidth * 0.5f;
619           position.y = stageSize.height * 0.5f + worldPos.y + height * 0.5f + tailHeight;
620         }
621         break;
622       }
623     }
624
625     // Ensure the Popup is still on the screen
626
627     if(position.x < 0.0f)
628     {
629       position.x = 0.0f;
630     }
631     else if((position.x + popupWidth) > stageSize.width)
632     {
633       position.x -= position.x + popupWidth - stageSize.width;
634     }
635
636     bool yPosChanged = false;
637     if(position.y < 0.0f)
638     {
639       yPosChanged = true;
640       position.y  = 0.0f;
641     }
642     else if((position.y + popupHeight) > stageSize.height)
643     {
644       yPosChanged = true;
645       position.y -= position.y + popupHeight - stageSize.height;
646     }
647
648     if(yPosChanged && tail)
649     {
650       // If we change the y position, then the tail may be shown pointing to the wrong control so just hide it.
651       tail.SetProperty(Actor::Property::VISIBLE, false);
652     }
653
654     mPopup.SetProperty(Actor::Property::POSITION, position);
655   }
656 }
657
658 } // namespace Internal
659
660 } // namespace Toolkit
661
662 } // namespace Dali