Make Menu APIs public
authorJaehyun Cho <jae_hyun.cho@samsung.com>
Fri, 16 Apr 2021 03:49:02 +0000 (12:49 +0900)
committerdongsug-song <35130733+dongsug-song@users.noreply.github.com>
Fri, 7 May 2021 03:54:19 +0000 (12:54 +0900)
This is ACR patch to make Menu APIs public.

This pull request includes properties and methods of Menu and MenuItem
classes.

Menu.AnchorPosition is replaced with Menu.Anchor.
Menu.HorizontalPositionToAnchor and Menu.VerticalPositionToAnchor are
added for Menu.Anchor.

src/Tizen.NUI.Components/Controls/Menu.cs
src/Tizen.NUI.Components/Controls/MenuItem.cs
src/Tizen.NUI.Components/Controls/Navigation/DialogPage.cs
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/MenuSample.cs

index 23fe277..2647c02 100755 (executable)
@@ -24,27 +24,29 @@ namespace Tizen.NUI.Components
     /// <summary>
     /// Menu is a class which contains a set of MenuItems and has one of them selected.
     /// </summary>
-    [EditorBrowsable(EditorBrowsableState.Never)]
+    /// <since_tizen> 9 </since_tizen>
     public class Menu : Control
     {
-        private ScrollableBase scrollableBase = null;
+        private Window window = null;
+        private Layer layer = null;
+        private View content = null;
+        private View scrim = null;
+        private View anchor = null;
         private IEnumerable<MenuItem> menuItems = null;
         private MenuItemGroup menuItemGroup = null;
-        private Position2D anchorPosition = new Position2D(0, 0);
+        private RelativePosition horizontalPosition = RelativePosition.Center;
+        private RelativePosition verticalPosition = RelativePosition.Center;
 
         /// <summary>
         /// Creates a new instance of Menu.
         /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 9 </since_tizen>
         public Menu() : base()
         {
             Initialize();
         }
 
-        /// <summary>
-        /// Dispose Menu and all children on it.
-        /// </summary>
-        /// <param name="type">Dispose type.</param>
+        /// <inheritdoc/>
         [EditorBrowsable(EditorBrowsableState.Never)]
         protected override void Dispose(DisposeTypes type)
         {
@@ -55,31 +57,63 @@ namespace Tizen.NUI.Components
 
             if (type == DisposeTypes.Explicit)
             {
-                if (menuItems != null)
+                if (Content != null)
                 {
-                    foreach (MenuItem menuItem in menuItems)
+                    if (menuItems != null)
                     {
-                        Utility.Dispose(menuItem);
+                        foreach (MenuItem menuItem in menuItems)
+                        {
+                            Content.Remove(menuItem);
+                        }
                     }
+
+                    Utility.Dispose(Content);
                 }
 
-                menuItemGroup = null;
+                Utility.Dispose(Scrim);
 
-                if (anchorPosition != null)
-                {
-                    anchorPosition.Dispose();
-                    anchorPosition = null;
-                }
+                menuItemGroup = null;
 
-                Utility.Dispose(scrollableBase);
+                layer.Remove(this);
+                Window.RemoveLayer(layer);
+                layer.Dispose();
             }
 
             base.Dispose(type);
         }
 
+        /// <summary>The Menu's relative position to Anchor.</summary>
+        /// <since_tizen> 9 </since_tizen>
+        public enum RelativePosition
+        {
+            /// <summary>
+            /// At the start of the Anchor.
+            /// If this is used with <see cref="HorizontalPositionToAnchor"/>, then Menu is positioned to the left (LTR) of the Anchor or right (RTL) of the Anchor.
+            /// If this is used with <see cref="VerticalPositionToAnchor"/>, then Menu is positioned to the top of the Anchor.
+            ///</summary>
+            /// <since_tizen> 9 </since_tizen>
+            Start = 0,
+            /// <summary>
+            /// At the center of the Anchor.
+            /// If this is used with <see cref="HorizontalPositionToAnchor"/> or <see cref="VerticalPositionToAnchor"/>, then Menu is positioned to the middle of the Anchor.
+            /// </summary>
+            /// <since_tizen> 9 </since_tizen>
+            Center = 1,
+            /// <summary>
+            /// At the end of the Anchor.
+            /// If this is used with <see cref="HorizontalPositionToAnchor"/>, then Menu is positioned to the right (LTR) of the Anchor or left (RTL) of the Anchor.
+            /// If this is used with <see cref="VerticalPositionToAnchor"/>, then Menu is positioned to the bottom of the Anchor.
+            /// </summary>
+            /// <since_tizen> 9 </since_tizen>
+            End = 2,
+        }
+
         /// <summary>
         /// Menu items in Menu.
+        /// Menu items are not automatically disposed when Menu is disposed.
+        /// Therefore, please dispose Menu items when you dispose Menu.
         /// </summary>
+        /// <since_tizen> 9 </since_tizen>
         public IEnumerable<MenuItem> Items
         {
             get
@@ -93,9 +127,9 @@ namespace Tizen.NUI.Components
                 {
                     foreach (var oldItem in menuItems)
                     {
-                        if (scrollableBase.Children?.Contains(oldItem) == true)
+                        if (content.Children?.Contains(oldItem) == true)
                         {
-                            scrollableBase.Children.Remove(oldItem);
+                            content.Remove(oldItem);
                         }
                     }
                 }
@@ -109,43 +143,236 @@ namespace Tizen.NUI.Components
 
                 foreach (var item in menuItems)
                 {
-                    scrollableBase.Add(item);
+                    content.Add(item);
                     menuItemGroup.Add(item);
                 }
             }
         }
 
         /// <summary>
-        /// Anchor position of Menu.
-        /// Menu is displayed at the anchor position.
-        /// If there is no enough space to display menu at the anchor position,
-        /// then menu is displayed at the proper position near anchor position.
+        /// Anchor of Menu.
+        /// Menu is displayed at the anchor's position.
+        /// If there is not enough space to display menu at the anchor's position,
+        /// then menu is displayed at the proper position near anchor's position.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public View Anchor
+        {
+            get
+            {
+                return anchor;
+            }
+
+            set
+            {
+                if (anchor == value)
+                {
+                    return;
+                }
+
+                anchor = value;
+                if (anchor == null)
+                {
+                    return;
+                }
+
+                CalculateSizeAndPosition();
+            }
+        }
+
+        /// <summary>
+        /// The horizontal position of Menu relative to Anchor.
+        /// If Anchor is not set, then RelativePosition does not work.
+        /// If RelativePosition is Start, then Menu is displayed at the start of Anchor.
+        /// If RelativePosition is Center, then Menu is displayed at the center of Anchor.
+        /// If RelativePosition is End, then Menu is displayed at the end of Anchor.
+        /// If there is not enough space to display menu at the anchor's position,
+        /// then menu is displayed at the proper position near anchor's position.
         /// </summary>
-        public Position2D AnchorPosition
+        /// <since_tizen> 9 </since_tizen>
+        public RelativePosition HorizontalPositionToAnchor
         {
             get
             {
-                return anchorPosition;
+                return horizontalPosition;
             }
 
             set
             {
-                if (anchorPosition == value)
+                if (horizontalPosition == value)
                 {
                     return;
                 }
 
-                anchorPosition = value;
-                if (anchorPosition == null)
+                horizontalPosition = value;
+
+                CalculateSizeAndPosition();
+            }
+        }
+
+        /// <summary>
+        /// The vertical position of Menu relative to Anchor.
+        /// If Anchor is not set, then RelativePosition does not work.
+        /// If RelativePosition is Start, then Menu is displayed at the start of Anchor.
+        /// If RelativePosition is Center, then Menu is displayed at the center of Anchor.
+        /// If RelativePosition is End, then Menu is displayed at the end of Anchor.
+        /// If there is not enough space to display menu at the anchor's position,
+        /// then menu is displayed at the proper position near anchor's position.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public RelativePosition VerticalPositionToAnchor
+        {
+            get
+            {
+                return verticalPosition;
+            }
+
+            set
+            {
+                if (verticalPosition == value)
                 {
                     return;
                 }
 
+                verticalPosition = value;
+
                 CalculateSizeAndPosition();
             }
         }
 
+
+        /// <summary>
+        /// Content of Menu.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected View Content
+        {
+            get
+            {
+                return content;
+            }
+            set
+            {
+                if (content == value)
+                {
+                    return;
+                }
+
+                if (content != null)
+                {
+                    Remove(content);
+                }
+
+                content = value;
+                if (content == null)
+                {
+                    return;
+                }
+
+                Add(content);
+
+                if (Scrim != null)
+                {
+                    content.RaiseAbove(Scrim);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Scrim of Menu.
+        /// Scrim is the screen region outside Menu.
+        /// If Scrim is touched, then Menu is dismissed.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected View Scrim
+        {
+            get
+            {
+                return scrim;
+            }
+            set
+            {
+                if (scrim == value)
+                {
+                    return;
+                }
+
+                if (scrim != null)
+                {
+                    Remove(scrim);
+                }
+
+                scrim = value;
+                if (scrim == null)
+                {
+                    return;
+                }
+
+                Add(scrim);
+
+                if (Content != null)
+                {
+                    Content.RaiseAbove(scrim);
+                }
+            }
+        }
+
+        private Window Window
+        {
+            get
+            {
+                if (window == null)
+                {
+                    window = NUIApplication.GetDefaultWindow();
+                }
+
+                return window;
+            }
+            set
+            {
+                if (window == value)
+                {
+                    return;
+                }
+
+                window = value;
+            }
+        }
+
+        /// <summary>
+        /// Post the Menu.
+        /// The Menu is displayed.
+        /// </summary>
+        /// <param name="window">The Window where Menu is displayed.</param>
+        /// <since_tizen> 9 </since_tizen>
+        public void Post(Window window = null)
+        {
+            if (window == null)
+            {
+                window = NUIApplication.GetDefaultWindow();
+            }
+
+            Window = window;
+
+            Window.AddLayer(layer);
+            layer.RaiseToTop();
+
+            CalculateSizeAndPosition();
+        }
+
+        /// <summary>
+        /// Dismiss the Menu.
+        /// The Menu becomes hidden and disposed.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public void Dismiss()
+        {
+            Hide();
+            Dispose();
+        }
+
         /// <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
         public override void OnRelayout(Vector2 size, RelayoutContainer container)
         {
             base.OnRelayout(size, container);
@@ -158,8 +385,25 @@ namespace Tizen.NUI.Components
             Layout = new AbsoluteLayout();
 
             WidthSpecification = LayoutParamPolicies.WrapContent;
+            HeightSpecification = LayoutParamPolicies.WrapContent;
+
+            // Menu is added to Anchor so Menu should exclude layouting because
+            // if Anchor has Layout, then Menu is displayed at an incorrect position.
+            ExcludeLayouting = true;
 
-            scrollableBase = new ScrollableBase()
+            Content = CreateDefaultContent();
+
+            Scrim = CreateDefaultScrim();
+
+            menuItemGroup = new MenuItemGroup();
+
+            layer = new Layer();
+            layer.Add(this);
+        }
+
+        private ScrollableBase CreateDefaultContent()
+        {
+            return new ScrollableBase()
             {
                 Layout = new LinearLayout()
                 {
@@ -169,16 +413,73 @@ namespace Tizen.NUI.Components
                 ScrollEnabled = true,
                 HideScrollbar = false,
             };
-            Add(scrollableBase);
+        }
 
-            menuItemGroup = new MenuItemGroup();
+        private View CreateDefaultScrim()
+        {
+            var scrim = new VisualView()
+            {
+                // Scrim is added to Menu so Scrim should exclude layouting
+                // not to enlarge Menu size.
+                ExcludeLayouting = true,
+                BackgroundColor = Color.Transparent,
+                Size = new Size(NUIApplication.GetDefaultWindow().Size),
+            };
+
+            scrim.TouchEvent += (object source, TouchEventArgs e) =>
+            {
+                if (e.Touch.GetState(0) == PointStateType.Up)
+                {
+                    this.Dismiss();
+                }
+                return true;
+            };
+
+            return scrim;
         }
 
         private void CalculateSizeAndPosition()
         {
-            var parent = GetParent() as View;
+            CalculateMenuSize();
+
+            CalculateMenuPosition();
+
+            CalculateScrimPosition();
+        }
+
+        // Calculate menu's size based on content's size
+        private void CalculateMenuSize()
+        {
+            if (Content == null)
+            {
+                return;
+            }
+
+            if (Size2D.Equals(Content.Size2D) == false)
+            {
+                Size2D = new Size2D(Content.Size2D.Width, Content.Size2D.Height);
+            }
+        }
+
+        private View GetRootView()
+        {
+            View root = this;
+            View parent = GetParent() as View;
 
-            if ((AnchorPosition == null) || (parent == null))
+            while (parent)
+            {
+                root = parent;
+                parent = parent.GetParent() as View;
+            }
+
+            return root;
+        }
+
+        // Calculate menu's position based on Anchor and parent's positions.
+        // If there is not enought space, then menu's size can be also resized.
+        private void CalculateMenuPosition()
+        {
+            if ((Anchor == null) || (Content == null))
             {
                 return;
             }
@@ -193,43 +494,110 @@ namespace Tizen.NUI.Components
                 return;
             }
 
-            var parentPosition = new Position2D((int)parent.ScreenPosition.X, (int)parent.ScreenPosition.Y);
-            var parentSize = new Size2D(parent.Size2D.Width, parent.Size2D.Height);
+            int menuScreenPosX = 0;
+            int menuScreenPosY = 0;
+
+            if (HorizontalPositionToAnchor == RelativePosition.Start)
+            {
+                if (GetRootView().LayoutDirection == ViewLayoutDirectionType.LTR)
+                {
+                    menuScreenPosX = (int)Anchor.ScreenPosition.X - Size2D.Width;
+                }
+                else
+                {
+                    menuScreenPosX = (int)Anchor.ScreenPosition.X + Anchor.Margin.Start + Anchor.Size2D.Width + Anchor.Margin.End;
+                }
+            }
+            else if (HorizontalPositionToAnchor == RelativePosition.Center)
+            {
+                menuScreenPosX = (int)Anchor.ScreenPosition.X + Anchor.Margin.Start + (Anchor.Size2D.Width / 2) - (Size2D.Width / 2);
+            }
+            else
+            {
+                if (GetRootView().LayoutDirection == ViewLayoutDirectionType.LTR)
+                {
+                    menuScreenPosX = (int)Anchor.ScreenPosition.X + Anchor.Margin.Start + Anchor.Size2D.Width + Anchor.Margin.End;
+                }
+                else
+                {
+                    menuScreenPosX = (int)Anchor.ScreenPosition.X - Size2D.Width;
+                }
+            }
+
+            if (VerticalPositionToAnchor == RelativePosition.Start)
+            {
+                menuScreenPosY = (int)Anchor.ScreenPosition.Y - Size2D.Height;
+            }
+            else if (VerticalPositionToAnchor == RelativePosition.Center)
+            {
+                menuScreenPosY = (int)Anchor.ScreenPosition.Y + Anchor.Margin.Top + (Anchor.Size2D.Height / 2) - (Size2D.Height / 2);
+            }
+            else
+            {
+                menuScreenPosY = (int)Anchor.ScreenPosition.Y + Anchor.Margin.Top + Anchor.Size2D.Height + Anchor.Margin.Bottom;
+            }
 
-            int menuPosX = AnchorPosition.X;
-            int menuPosY = AnchorPosition.Y;
             int menuSizeW = Size2D.Width;
             int menuSizeH = Size2D.Height;
 
             // Check if menu is not inside parent's boundary in x coordinate system.
-            if (AnchorPosition.X + Size2D.Width > parentPosition.X + parentSize.Width)
+            if (menuScreenPosX + Size2D.Width > Window.Size.Width)
             {
-                menuPosX = parentPosition.X + parentSize.Width - Size2D.Width;
+                menuScreenPosX = Window.Size.Width - Size2D.Width;
 
-                if (menuPosX < parentPosition.X)
+                if (menuScreenPosX < 0)
                 {
-                    menuPosX = parentPosition.X;
-                    menuSizeW = parentSize.Width;
+                    menuScreenPosX = 0;
+                    menuSizeW = Window.Size.Width;
                 }
             }
+            else if (menuScreenPosX < 0)
+            {
+                menuScreenPosX = 0;
+                menuSizeW = Window.Size.Width;
+            }
 
             // Check if menu is not inside parent's boundary in y coordinate system.
-            if (AnchorPosition.Y + Size2D.Height > parentPosition.Y + parentSize.Height)
+            if (menuScreenPosY + Size2D.Height > Window.Size.Height)
             {
-                menuPosY = parentPosition.Y + parentSize.Height - Size2D.Height;
+                menuScreenPosY = Window.Size.Height - Size2D.Height;
 
-                if (menuPosY < parentPosition.Y)
+                if (menuScreenPosY < 0)
                 {
-                    menuPosY = parentPosition.Y;
-                    menuSizeH = parentSize.Height;
+                    menuScreenPosY = 0;
+                    menuSizeH = Window.Size.Height;
                 }
             }
+            else if (menuScreenPosY < 0)
+            {
+                menuScreenPosY = 0;
+                menuSizeH = Window.Size.Height;
+            }
 
-            Position2D = new Position2D(menuPosX, menuPosY);
-            Size2D = new Size2D(menuSizeW, menuSizeH);
+            // Position is relative to parent's coordinate system.
+            var menuPosX = menuScreenPosX;
+            var menuPosY = menuScreenPosY;
 
-            parentPosition.Dispose();
-            parentSize.Dispose();
+            if ((Position2D.X != menuPosX) || (Position2D.Y != menuPosY) || (Size2D.Width != menuSizeW) || (Size2D.Height != menuSizeH))
+            {
+                Position2D = new Position2D(menuPosX, menuPosY);
+                Size2D = new Size2D(menuSizeW, menuSizeH);
+            }
+        }
+
+        // Calculate scrim's position based on menu's position
+        private void CalculateScrimPosition()
+        {
+            if (Scrim == null)
+            {
+                return;
+            }
+
+            // Menu's Position should be updated before doing this calculation.
+            if ((Scrim.Position2D.X != -Position2D.X) || (Scrim.Position2D.Y != -Position2D.Y))
+            {
+                Scrim.Position2D = new Position2D(-Position2D.X, -Position2D.Y);
+            }
         }
     }
 }
index 0d5b131..004ba5c 100755 (executable)
@@ -24,7 +24,7 @@ namespace Tizen.NUI.Components
     /// <summary>
     /// MenuItem is a class which is used to show a list of items in Menu.
     /// </summary>
-    [EditorBrowsable(EditorBrowsableState.Never)]
+    /// <since_tizen> 9 </since_tizen>
     public class MenuItem : SelectButton
     {
         private bool selectedAgain = false;
@@ -34,7 +34,7 @@ namespace Tizen.NUI.Components
         /// <summary>
         /// Creates a new instance of MenuItem.
         /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 9 </since_tizen>
         public MenuItem()
         {
             Initialize();
@@ -50,10 +50,7 @@ namespace Tizen.NUI.Components
             Initialize();
         }
 
-        /// <summary>
-        /// Dispose MenuItem and all children on it.
-        /// </summary>
-        /// <param name="type">Dispose type.</param>
+        /// <inheritdoc/>
         [EditorBrowsable(EditorBrowsableState.Never)]
         protected override void Dispose(DisposeTypes type)
         {
index 47444c3..10da0c9 100755 (executable)
@@ -303,65 +303,5 @@ namespace Tizen.NUI.Components
 
             NUIApplication.GetDefaultWindow().GetDefaultNavigator().Push(dialogPage);
         }
-
-        /// <summary>
-        /// Shows a menu by pushing a dialog page containing menu to default navigator.
-        /// </summary>
-        /// <param name="anchor">The anchor view where menu is displayed.</param>
-        /// <param name="items">The menu items.</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        [SuppressMessage("Microsoft.Reliability",
-                         "CA2000:DisposeObjectsBeforeLosingScope",
-                         Justification = "The pushed views are added to NavigationPages and are disposed in Navigator.Dispose().")]
-        public static void ShowMenu(View anchor, params MenuItem[] items)
-        {
-            if (items == null)
-            {
-                return;
-            }
-
-            Position2D anchorPosition = new Position2D((int)(anchor?.ScreenPosition.X ?? 0), (int)(anchor?.ScreenPosition.Y ?? 0) + (anchor?.Size2D.Height ?? 0) + (anchor?.Margin.Bottom ?? 0));
-
-            var dialogPage = new DialogPage()
-            {
-                Content = new Menu()
-                {
-                    Items = items,
-                    AnchorPosition = anchorPosition,
-                },
-                ScrimColor = Color.Transparent,
-            };
-
-            NUIApplication.GetDefaultWindow().GetDefaultNavigator().Push(dialogPage);
-        }
-
-        /// <summary>
-        /// Shows a menu by pushing a dialog page containing menu to default navigator.
-        /// </summary>
-        /// <param name="anchorPosition">The anchor position where menu is displayed.</param>
-        /// <param name="items">The menu items.</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        [SuppressMessage("Microsoft.Reliability",
-                         "CA2000:DisposeObjectsBeforeLosingScope",
-                         Justification = "The pushed views are added to NavigationPages and are disposed in Navigator.Dispose().")]
-        public static void ShowMenu(Position2D anchorPosition, params MenuItem[] items)
-        {
-            if (items == null)
-            {
-                return;
-            }
-
-            var dialogPage = new DialogPage()
-            {
-                Content = new Menu()
-                {
-                    Items = items,
-                    AnchorPosition = anchorPosition,
-                },
-                ScrimColor = Color.Transparent,
-            };
-
-            NUIApplication.GetDefaultWindow().GetDefaultNavigator().Push(dialogPage);
-        }
     }
 }
index d4dba94..d58761e 100755 (executable)
@@ -44,7 +44,14 @@ namespace Tizen.NUI.Samples
 
             moreButton.Clicked += (object sender, ClickedEventArgs args) =>
             {
-                DialogPage.ShowMenu(moreButton, menuItem, menuItem2, menuItem3, menuItem4);
+                var menu = new Menu()
+                {
+                    Anchor = moreButton,
+                    HorizontalPositionToAnchor = Menu.RelativePosition.Center,
+                    VerticalPositionToAnchor = Menu.RelativePosition.End,
+                    Items = new MenuItem[] { menuItem, menuItem2, menuItem3, menuItem4 },
+                };
+                menu.Post();
             };
         }