[NUI] Apply Menu and MenuItem UX - Sizes and Colors
authorJaehyun Cho <jae_hyun.cho@samsung.com>
Thu, 21 Apr 2022 12:57:10 +0000 (21:57 +0900)
committerdongsug-song <35130733+dongsug-song@users.noreply.github.com>
Tue, 26 Apr 2022 04:41:13 +0000 (13:41 +0900)
The latest Menu and MenuItem UX have been applied.
To apply the latest Menu UX, MenuStyle has been added.
Sizes and Colors have been changed.

Previously, Menu with many MenuItems could cover AppBar's More button.
Noew, Menu with many MenuItems never cover AppBar's More button.

src/Tizen.NUI.Components/Controls/Menu.cs
src/Tizen.NUI.Components/Controls/MenuItem.cs
src/Tizen.NUI.Components/Style/MenuStyle.cs [new file with mode: 0755]
src/Tizen.NUI.Components/Theme/DefaultThemeCommon.cs
src/Tizen.NUI.Components/res/nui_component_menu_item_bg.png [deleted file]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/MenuSample.cs

index 56e6efc..51e220e 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2022 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -36,6 +36,8 @@ namespace Tizen.NUI.Components
         private MenuItemGroup menuItemGroup = null;
         private RelativePosition horizontalPosition = RelativePosition.Center;
         private RelativePosition verticalPosition = RelativePosition.Center;
+        private MenuStyle menuStyle = null;
+        private bool styleApplied = false;
 
         /// <summary>
         /// Creates a new instance of Menu.
@@ -46,6 +48,16 @@ namespace Tizen.NUI.Components
             Initialize();
         }
 
+        /// <summary>
+        /// Creates a new instance of Menu.
+        /// </summary>
+        /// <param name="style">Creates Menu by special style defined in UX.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Menu(string style) : base(style)
+        {
+            Initialize();
+        }
+
         /// <inheritdoc/>
         [EditorBrowsable(EditorBrowsableState.Never)]
         protected override void Dispose(DisposeTypes type)
@@ -82,6 +94,26 @@ namespace Tizen.NUI.Components
             base.Dispose(type);
         }
 
+        /// <summary>
+        /// Applies style to MenuItem.
+        /// </summary>
+        /// <param name="viewStyle">The style to apply.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override void ApplyStyle(ViewStyle viewStyle)
+        {
+            styleApplied = false;
+
+            base.ApplyStyle(viewStyle);
+
+            menuStyle = viewStyle as MenuStyle;
+            if (menuStyle != null)
+            {
+                Content?.ApplyStyle(menuStyle.Content);
+            }
+
+            styleApplied = true;
+        }
+
         /// <summary>The Menu's relative position to Anchor.</summary>
         /// <since_tizen> 9 </since_tizen>
         public enum RelativePosition
@@ -276,6 +308,15 @@ namespace Tizen.NUI.Components
             }
         }
 
+        /// <summary>
+        /// Gets Menu style.
+        /// </summary>
+        /// <returns>The default Menu style.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override ViewStyle CreateViewStyle()
+        {
+            return new MenuStyle();
+        }
 
         /// <summary>
         /// Content of Menu.
@@ -434,6 +475,10 @@ namespace Tizen.NUI.Components
             ExcludeLayouting = true;
 
             Content = CreateDefaultContent();
+            if (styleApplied)
+            {
+                Content.ApplyStyle(menuStyle.Content);
+            }
 
             Scrim = CreateDefaultScrim();
 
@@ -454,9 +499,7 @@ namespace Tizen.NUI.Components
                 ScrollingDirection = ScrollableBase.Direction.Vertical,
                 ScrollEnabled = true,
                 HideScrollbar = false,
-
-                // FIXME: This color should be in DefaultThemeCommon.cs.
-                BackgroundColor = new Color("#EEEFF1"),
+                ClippingMode = ClippingModeType.ClipChildren,
             };
         }
 
@@ -499,10 +542,9 @@ namespace Tizen.NUI.Components
             {
                 return;
             }
-
-            if (Size2D.Equals(Content.Size2D) == false)
+            if (Size.Equals(Content.Size) == false)
             {
-                Size2D = new Size2D(Content.Size2D.Width, Content.Size2D.Height);
+                Size = new Size(Content.SizeWidth, Content.SizeHeight);
             }
         }
 
@@ -534,86 +576,88 @@ namespace Tizen.NUI.Components
                 return;
             }
 
-            if ((Size2D.Width == 0) && (Size2D.Height == 0))
+            if (SizeWidth.Equals(0) && SizeHeight.Equals(0))
             {
                 return;
             }
 
-            int menuScreenPosX = 0;
-            int menuScreenPosY = 0;
+            float menuScreenPosX = 0;
+            float menuScreenPosY = 0;
 
             if (HorizontalPositionToAnchor == RelativePosition.Start)
             {
                 if (GetRootView().LayoutDirection == ViewLayoutDirectionType.LTR)
                 {
-                    menuScreenPosX = (int)Anchor.ScreenPosition.X - Size2D.Width;
+                    menuScreenPosX = Anchor.ScreenPosition.X - SizeWidth;
                 }
                 else
                 {
-                    menuScreenPosX = (int)Anchor.ScreenPosition.X + Anchor.Margin.Start + Anchor.Size2D.Width + Anchor.Margin.End;
+                    menuScreenPosX = Anchor.ScreenPosition.X + Anchor.Margin.Start + Anchor.SizeWidth + Anchor.Margin.End;
                 }
             }
             else if (HorizontalPositionToAnchor == RelativePosition.Center)
             {
-                menuScreenPosX = (int)Anchor.ScreenPosition.X + Anchor.Margin.Start + (Anchor.Size2D.Width / 2) - (Size2D.Width / 2);
+                menuScreenPosX = Anchor.ScreenPosition.X + Anchor.Margin.Start + (Anchor.SizeWidth / 2) - (SizeWidth / 2);
             }
             else
             {
                 if (GetRootView().LayoutDirection == ViewLayoutDirectionType.LTR)
                 {
-                    menuScreenPosX = (int)Anchor.ScreenPosition.X + Anchor.Margin.Start + Anchor.Size2D.Width + Anchor.Margin.End;
+                    menuScreenPosX = Anchor.ScreenPosition.X + Anchor.Margin.Start + Anchor.SizeWidth + Anchor.Margin.End;
                 }
                 else
                 {
-                    menuScreenPosX = (int)Anchor.ScreenPosition.X - Size2D.Width;
+                    menuScreenPosX = Anchor.ScreenPosition.X - SizeWidth;
                 }
             }
 
             if (VerticalPositionToAnchor == RelativePosition.Start)
             {
-                menuScreenPosY = (int)Anchor.ScreenPosition.Y - Size2D.Height;
+                menuScreenPosY = Anchor.ScreenPosition.Y - SizeHeight;
             }
             else if (VerticalPositionToAnchor == RelativePosition.Center)
             {
-                menuScreenPosY = (int)Anchor.ScreenPosition.Y + Anchor.Margin.Top + (Anchor.Size2D.Height / 2) - (Size2D.Height / 2);
+                menuScreenPosY = Anchor.ScreenPosition.Y + Anchor.Margin.Top + (Anchor.SizeHeight / 2) - (SizeHeight / 2);
             }
             else
             {
-                menuScreenPosY = (int)Anchor.ScreenPosition.Y + Anchor.Margin.Top + Anchor.Size2D.Height + Anchor.Margin.Bottom;
+                menuScreenPosY = Anchor.ScreenPosition.Y + Anchor.Margin.Top + Anchor.SizeHeight + Anchor.Margin.Bottom;
             }
 
-            int menuSizeW = Size2D.Width;
-            int menuSizeH = Size2D.Height;
+            float menuSizeW = SizeWidth;
+            float menuSizeH = SizeHeight;
 
             // Check if menu is not inside parent's boundary in x coordinate system.
-            if (menuScreenPosX + Size2D.Width > Window.Size.Width)
+            if (menuScreenPosX + SizeWidth > Window.Size.Width)
             {
-                menuScreenPosX = Window.Size.Width - Size2D.Width;
-
-                if (menuScreenPosX < 0)
+                if (HorizontalPositionToAnchor == RelativePosition.Center)
+                {
+                    menuScreenPosX = Window.Size.Width - SizeWidth;
+                }
+                else
                 {
-                    menuScreenPosX = 0;
-                    menuSizeW = Window.Size.Width;
+                    menuSizeW = Window.Size.Width - menuScreenPosX;
                 }
             }
-            else if (menuScreenPosX < 0)
+            if (menuScreenPosX < 0)
             {
                 menuScreenPosX = 0;
                 menuSizeW = Window.Size.Width;
             }
 
             // Check if menu is not inside parent's boundary in y coordinate system.
-            if (menuScreenPosY + Size2D.Height > Window.Size.Height)
+            if (menuScreenPosY + SizeHeight > Window.Size.Height)
             {
-                menuScreenPosY = Window.Size.Height - Size2D.Height;
-
-                if (menuScreenPosY < 0)
+                if (VerticalPositionToAnchor == RelativePosition.Center)
+                {
+                    menuScreenPosY = Window.Size.Height - SizeHeight;
+                }
+                else
                 {
-                    menuScreenPosY = 0;
-                    menuSizeH = Window.Size.Height;
+                    menuSizeH = Window.Size.Height - menuScreenPosY;
                 }
             }
-            else if (menuScreenPosY < 0)
+            if (menuScreenPosY < 0)
             {
                 menuScreenPosY = 0;
                 menuSizeH = Window.Size.Height;
@@ -623,10 +667,10 @@ namespace Tizen.NUI.Components
             var menuPosX = menuScreenPosX;
             var menuPosY = menuScreenPosY;
 
-            if ((Position2D.X != menuPosX) || (Position2D.Y != menuPosY) || (Size2D.Width != menuSizeW) || (Size2D.Height != menuSizeH))
+            if (!PositionX.Equals(menuPosX) || !PositionY.Equals(menuPosY) || !SizeWidth.Equals(menuSizeW) || !SizeHeight.Equals(menuSizeH))
             {
-                Position2D = new Position2D(menuPosX, menuPosY);
-                Size2D = new Size2D(menuSizeW, menuSizeH);
+                Position = new Position(menuPosX, menuPosY);
+                Size = new Size(menuSizeW, menuSizeH);
             }
         }
 
@@ -639,9 +683,9 @@ namespace Tizen.NUI.Components
             }
 
             // Menu's Position should be updated before doing this calculation.
-            if ((Scrim.Position2D.X != -Position2D.X) || (Scrim.Position2D.Y != -Position2D.Y))
+            if (!Scrim.PositionX.Equals(-PositionX) || !Scrim.PositionY.Equals(-PositionY))
             {
-                Scrim.Position2D = new Position2D(-Position2D.X, -Position2D.Y);
+                Scrim.Position = new Position(-PositionX, -PositionY);
             }
         }
 
index d9b0ac0..2060f3c 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2022 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  *
  */
-
+using System;
 using System.ComponentModel;
 using System.Diagnostics.CodeAnalysis;
 using Tizen.NUI.BaseComponents;
@@ -85,9 +85,9 @@ namespace Tizen.NUI.Components
 
         /// <inheritdoc/>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        protected override void OnUpdate()
+        public override void OnRelayout(Vector2 size, RelayoutContainer container)
         {
-            base.OnUpdate();
+            base.OnRelayout(size, container);
             LayoutItems();
         }
 
@@ -185,9 +185,41 @@ namespace Tizen.NUI.Components
                 return;
             }
 
-            if ((Icon == null) && (TextLabel == null))
+            bool isEmptyIcon = false;
+            bool isEmptyText = false;
+
+            if (String.IsNullOrEmpty(Icon.ResourceUrl))
             {
-                return;
+                isEmptyIcon = true;
+            }
+
+            if (String.IsNullOrEmpty(TextLabel.Text))
+            {
+                isEmptyText = true;
+            }
+
+            if (isEmptyIcon)
+            {
+                if (Children.Contains(Icon))
+                {
+                    Remove(Icon);
+                }
+            }
+            else if (Children.Contains(Icon) == false)
+            {
+                Add(Icon);
+            }
+
+            if (isEmptyText)
+            {
+                if (Children.Contains(TextLabel))
+                {
+                    Remove(TextLabel);
+                }
+            }
+            else if (Children.Contains(TextLabel) == false)
+            {
+                Add(TextLabel);
             }
 
             // Icon is added in Button.LayoutItems().
@@ -201,40 +233,16 @@ namespace Tizen.NUI.Components
             {
                 Add(TextLabel);
             }
-
-            switch (IconRelativeOrientation)
-            {
-                // TODO: Other orientation cases are not implemented yet.
-                case IconOrientation.Left:
-                    if (LayoutDirection == ViewLayoutDirectionType.LTR)
-                    {
-                        int iconPosX = Padding.Start + (Icon?.Margin.Start ?? 0);
-                        int iconPosY = Padding.Top + (Icon?.Margin.Top ?? 0);
-                        int iconSizeW = Icon?.Size2D.Width ?? 0;
-                        int iconSizeH = Icon?.Size2D.Height ?? 0;
-
-                        if (Icon != null)
-                        {
-                            Icon.Position2D = new Position2D(iconPosX, iconPosY);
-                        }
-
-                        if (TextLabel != null)
-                        {
-                            int textPosX = iconPosX + iconSizeW + TextLabel.Margin.Start;
-                            int textPosY = Padding.Top + TextLabel.Margin.Top;
-
-                            TextLabel.Position2D = new Position2D(textPosX, textPosY);
-                        }
-                    }
-                    break;
-                default:
-                    break;
-            }
         }
 
         private void Initialize()
         {
-            Layout = new AbsoluteLayout();
+            Layout = new LinearLayout()
+            {
+                LinearOrientation = LinearLayout.Orientation.Horizontal,
+                HorizontalAlignment = HorizontalAlignment.Begin,
+                VerticalAlignment = VerticalAlignment.Center,
+            };
         }
 
         /// <summary>
diff --git a/src/Tizen.NUI.Components/Style/MenuStyle.cs b/src/Tizen.NUI.Components/Style/MenuStyle.cs
new file mode 100755 (executable)
index 0000000..da569d4
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright(c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+using System.ComponentModel;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Binding;
+
+namespace Tizen.NUI.Components
+{
+    /// <summary>
+    /// MenuStyle is a class which saves Menu's ux data.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class MenuStyle : ControlStyle
+    {
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        static MenuStyle() { }
+
+        /// <summary>
+        /// Creates a new instance of an MenuStyle.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public MenuStyle() : base()
+        {
+        }
+
+        /// <summary>
+        /// Creates a new instance of an MenuStyle with style.
+        /// The given style is copied to the new instance.
+        /// </summary>
+        /// <param name="style">Creates MenuStyle by style customized by user.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public MenuStyle(MenuStyle style) : base(style)
+        {
+        }
+
+        /// <summary>
+        /// Gets or sets the Menu Content style.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public ViewStyle Content { get; set; } = new ViewStyle();
+
+        /// <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override void CopyFrom(BindableObject bindableObject)
+        {
+            base.CopyFrom(bindableObject);
+
+            if (bindableObject is MenuStyle menuStyle)
+            {
+                Content.CopyFrom(menuStyle.Content);
+            }
+        }
+    }
+}
index 6d7bc8c..d7375e3 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2022 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -692,40 +692,57 @@ namespace Tizen.NUI.Components
                 }
             });
 
+            // Menu base style
+            theme.AddStyleWithoutClone("Tizen.NUI.Components.Menu", new MenuStyle()
+            {
+                Content = new ViewStyle()
+                {
+                    BackgroundColor = new Color("#FFFEFE"),
+                    CornerRadius = 24.0f,
+                    BoxShadow = new Shadow(8.0f, new Color(0.0f, 0.0f, 0.0f, 0.16f), new Vector2(0.0f, 2.0f)),
+                    // FIXME: ScrollableBase with LinearLayout's Padding.Start is applied both Start and End.
+                    //        ScrollableBase with LinearLayout's Padding.Top is applied both Top and Bottom.
+                    Padding = new Extents(32, 0, 16, 0),
+                },
+            });
+
             // MenuItem base style
             theme.AddStyleWithoutClone("Tizen.NUI.Components.MenuItem", new ButtonStyle()
             {
-                Size = new Size(480, -2),
-                MinimumSize = new Size2D(0, 72),
+                Size = new Size(324, -2),
+                MinimumSize = new Size2D(0, 64),
+                BackgroundColor = new Color("#FFFEFE"),
                 CornerRadius = 0,
-                BackgroundImage = FrameworkInformation.ResourcePath + "nui_component_menu_item_bg.png",
-                Padding = new Extents(16, 16, 16, 16),
+                // FIXME: ClippingModeType.ClipChildren cannot support anti-aliasing
+                //        So not to show left bottom corner of MenuItem, MenuItem.Padding.Start is 0 and Menu.Content.Padding.Start is 32.
+                //        (instead of MenuItem.Padding.Start 16 and Menu.Content.Padding.Start is 16)
+                Padding = new Extents(0, 0, 24, 24),
                 Text = new TextLabelStyle()
                 {
-                    PixelSize = 32,
+                    PixelSize = 24,
                     MultiLine = true,
                     HorizontalAlignment = HorizontalAlignment.Begin,
                     VerticalAlignment = VerticalAlignment.Center,
                     TextColor = new Selector<Color>()
                     {
-                        Normal = new Color("#001447"),
-                        Focused = new Color("#00338B"),
-                        Pressed = new Color("#1B69CA"),
-                        Disabled = new Color("#C3CAD2"),
-                        Selected = new Color("#1B69CA"),
+                        Normal = new Color("#090E21"),
+                        Focused = new Color("#FF6200"),
+                        Pressed = new Color("#FF6200"),
+                        Disabled = new Color("#CACACA"),
+                        Selected = new Color("#FF6200"),
                     },
                     ThemeChangeSensitive = false
                 },
                 Icon = new ImageViewStyle()
                 {
-                    Size = new Size(-2, 48),
+                    Size = new Size(32, 32),
                     Color = new Selector<Color>()
                     {
-                        Normal = new Color("#001447"),
-                        Focused = new Color("#00338B"),
-                        Pressed = new Color("#1B69CA"),
-                        Disabled = new Color("#C3CAD2"),
-                        Selected = new Color("#1B69CA"),
+                        Normal = new Color("#090E21"),
+                        Focused = new Color("#FF6200"),
+                        Pressed = new Color("#FF6200"),
+                        Disabled = new Color("#CACACA"),
+                        Selected = new Color("#FF6200"),
                     },
                 },
             });
diff --git a/src/Tizen.NUI.Components/res/nui_component_menu_item_bg.png b/src/Tizen.NUI.Components/res/nui_component_menu_item_bg.png
deleted file mode 100755 (executable)
index 81a65cc..0000000
Binary files a/src/Tizen.NUI.Components/res/nui_component_menu_item_bg.png and /dev/null differ
index 196a046..478d305 100755 (executable)
@@ -5,6 +5,8 @@ namespace Tizen.NUI.Samples
 {
     public class MenuSample : IExample
     {
+        private static readonly int itemCount = 20;
+
         public void Activate()
         {
             var window = NUIApplication.GetDefaultWindow();
@@ -37,29 +39,17 @@ namespace Tizen.NUI.Samples
             };
             navigator.Push(page);
 
-            var menuItem = new MenuItem() { Text = "Menu" };
-            menuItem.SelectedChanged += (object sender, SelectedChangedEventArgs args) =>
-            {
-                global::System.Console.WriteLine($"1st MenuItem's IsSelected is changed to {args.IsSelected}.");
-            };
+            MenuItem[] menuItems = new MenuItem[itemCount];
 
-            var menuItem2 = new MenuItem() { Text = "Menu2" };
-            menuItem2.SelectedChanged += (object sender, SelectedChangedEventArgs args) =>
+            for (int i = 0; i < itemCount; i++)
             {
-                global::System.Console.WriteLine($"2nd MenuItem's IsSelected is changed to {args.IsSelected}.");
-            };
-
-            var menuItem3 = new MenuItem() { Text = "Menu3" };
-            menuItem3.SelectedChanged += (object sender, SelectedChangedEventArgs args) =>
-            {
-                global::System.Console.WriteLine($"3rd MenuItem's IsSelected is changed to {args.IsSelected}.");
-            };
-
-            var menuItem4 = new MenuItem() { Text = "Menu4" };
-            menuItem4.SelectedChanged += (object sender, SelectedChangedEventArgs args) =>
-            {
-                global::System.Console.WriteLine($"4th MenuItem's IsSelected is changed to {args.IsSelected}.");
-            };
+                menuItems[i] = new MenuItem() { Text = "Menu" + (i + 1) };
+                menuItems[i].SelectedChanged += (object sender, SelectedChangedEventArgs args) =>
+                {
+                    var menuItem = sender as MenuItem;
+                    global::System.Console.WriteLine($"{menuItem.Text}'s IsSelected is changed to {args.IsSelected}.");
+                };
+            }
 
             moreButton.Clicked += (object sender, ClickedEventArgs args) =>
             {
@@ -68,7 +58,7 @@ namespace Tizen.NUI.Samples
                     Anchor = moreButton,
                     HorizontalPositionToAnchor = Menu.RelativePosition.Center,
                     VerticalPositionToAnchor = Menu.RelativePosition.End,
-                    Items = new MenuItem[] { menuItem, menuItem2, menuItem3, menuItem4 },
+                    Items = menuItems,
                 };
                 menu.Post();
             };