--- /dev/null
+/*
+ * Copyright(c) 2021 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.Collections.Generic;
+using System.ComponentModel;
+using Tizen.NUI.BaseComponents;
+
+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)]
+ public class Menu : Control
+ {
+ private ScrollableBase scrollableBase = null;
+ private IEnumerable<MenuItem> menuItems = null;
+ private MenuItemGroup menuItemGroup = null;
+ private Position2D anchorPosition = new Position2D(0, 0);
+
+ /// <summary>
+ /// Creates a new instance of Menu.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Menu() : base()
+ {
+ Initialize();
+ }
+
+ /// <summary>
+ /// Dispose Menu and all children on it.
+ /// </summary>
+ /// <param name="type">Dispose type.</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected override void Dispose(DisposeTypes type)
+ {
+ if (disposed)
+ {
+ return;
+ }
+
+ if (type == DisposeTypes.Explicit)
+ {
+ if (menuItems != null)
+ {
+ foreach (MenuItem menuItem in menuItems)
+ {
+ Utility.Dispose(menuItem);
+ }
+ }
+
+ menuItemGroup = null;
+
+ if (anchorPosition != null)
+ {
+ anchorPosition.Dispose();
+ anchorPosition = null;
+ }
+
+ Utility.Dispose(scrollableBase);
+ }
+
+ base.Dispose(type);
+ }
+
+ /// <summary>
+ /// Menu items in Menu.
+ /// </summary>
+ public IEnumerable<MenuItem> Items
+ {
+ get
+ {
+ return menuItems;
+ }
+
+ set
+ {
+ if (menuItems != null)
+ {
+ foreach (var oldItem in menuItems)
+ {
+ if (scrollableBase.Children?.Contains(oldItem) == true)
+ {
+ scrollableBase.Children.Remove(oldItem);
+ }
+ }
+ }
+
+ menuItems = value;
+
+ if (menuItems == null)
+ {
+ return;
+ }
+
+ foreach (var item in menuItems)
+ {
+ scrollableBase.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.
+ /// </summary>
+ public Position2D AnchorPosition
+ {
+ get
+ {
+ return anchorPosition;
+ }
+
+ set
+ {
+ if (anchorPosition == value)
+ {
+ return;
+ }
+
+ anchorPosition = value;
+ if (anchorPosition == null)
+ {
+ return;
+ }
+
+ CalculateSizeAndPosition();
+ }
+ }
+
+ /// <inheritdoc/>
+ public override void OnRelayout(Vector2 size, RelayoutContainer container)
+ {
+ base.OnRelayout(size, container);
+
+ CalculateSizeAndPosition();
+ }
+
+ private void Initialize()
+ {
+ Layout = new AbsoluteLayout();
+
+ WidthSpecification = LayoutParamPolicies.WrapContent;
+
+ scrollableBase = new ScrollableBase()
+ {
+ Layout = new LinearLayout()
+ {
+ LinearOrientation = LinearLayout.Orientation.Vertical,
+ },
+ ScrollingDirection = ScrollableBase.Direction.Vertical,
+ ScrollEnabled = true,
+ HideScrollbar = false,
+ };
+ Add(scrollableBase);
+
+ menuItemGroup = new MenuItemGroup();
+ }
+
+ private void CalculateSizeAndPosition()
+ {
+ var parent = GetParent() as View;
+
+ if ((AnchorPosition == null) || (parent == null))
+ {
+ return;
+ }
+
+ if (Items == null)
+ {
+ return;
+ }
+
+ if ((Size2D.Width == 0) && (Size2D.Height == 0))
+ {
+ return;
+ }
+
+ var parentPosition = new Position2D((int)parent.ScreenPosition.X, (int)parent.ScreenPosition.Y);
+ var parentSize = new Size2D(parent.Size2D.Width, parent.Size2D.Height);
+
+ 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)
+ {
+ menuPosX = parentPosition.X + parentSize.Width - Size2D.Width;
+
+ if (menuPosX < parentPosition.X)
+ {
+ menuPosX = parentPosition.X;
+ menuSizeW = parentSize.Width;
+ }
+ }
+
+ // Check if menu is not inside parent's boundary in y coordinate system.
+ if (AnchorPosition.Y + Size2D.Height > parentPosition.Y + parentSize.Height)
+ {
+ menuPosY = parentPosition.Y + parentSize.Height - Size2D.Height;
+
+ if (menuPosY < parentPosition.Y)
+ {
+ menuPosY = parentPosition.Y;
+ menuSizeH = parentSize.Height;
+ }
+ }
+
+ Position2D = new Position2D(menuPosX, menuPosY);
+ Size2D = new Size2D(menuSizeW, menuSizeH);
+
+ parentPosition.Dispose();
+ parentSize.Dispose();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2021 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 System.Diagnostics.CodeAnalysis;
+using Tizen.NUI.BaseComponents;
+
+namespace Tizen.NUI.Components
+{
+ /// <summary>
+ /// MenuItem is a class which is used to show a list of items in Menu.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class MenuItem : SelectButton
+ {
+ private bool selectedAgain = false;
+
+ private bool styleApplied = false;
+
+ /// <summary>
+ /// Creates a new instance of MenuItem.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public MenuItem()
+ {
+ Initialize();
+ }
+
+ /// <summary>
+ /// Creates a new instance of MenuItem.
+ /// </summary>
+ /// <param name="style">Creates MenuItem by special style defined in UX.</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public MenuItem(string style) : base(style)
+ {
+ Initialize();
+ }
+
+ /// <summary>
+ /// Dispose MenuItem and all children on it.
+ /// </summary>
+ /// <param name="type">Dispose type.</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected override void Dispose(DisposeTypes type)
+ {
+ if (disposed)
+ {
+ return;
+ }
+
+ if (type == DisposeTypes.Explicit)
+ {
+ }
+
+ 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);
+
+ styleApplied = true;
+
+ //Calculate position based on Achor's position.
+ LayoutItems();
+ }
+
+ /// <inheritdoc/>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected override void OnUpdate()
+ {
+ base.OnUpdate();
+ LayoutItems();
+ }
+
+ /// <inheritdoc/>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override bool OnKey(Key key)
+ {
+ if ((IsEnabled == false) || (key == null))
+ {
+ return false;
+ }
+
+ if (key.State == Key.StateType.Up)
+ {
+ if (key.KeyPressedName == "Return")
+ {
+ if (IsSelected == true)
+ {
+ selectedAgain = true;
+ }
+ }
+ }
+
+ bool ret = base.OnKey(key);
+
+ if (selectedAgain == true)
+ {
+ IsSelected = true;
+ selectedAgain = false;
+ }
+
+ return ret;
+ }
+
+ /// <inheritdoc/>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected override bool HandleControlStateOnTouch(Touch touch)
+ {
+ if ((IsEnabled == false) || (touch == null))
+ {
+ return false;
+ }
+
+ PointStateType state = touch.GetState(0);
+ switch (state)
+ {
+ case PointStateType.Up:
+ if (IsSelected == true)
+ {
+ selectedAgain = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ bool ret = base.HandleControlStateOnTouch(touch);
+
+ if (selectedAgain == true)
+ {
+ IsSelected = true;
+ selectedAgain = false;
+ }
+
+ return ret;
+ }
+
+ /// <inheritdoc/>
+ [SuppressMessage("Microsoft.Design",
+ "CA1062: Validate arguments of public methods",
+ MessageId = "controlStateChangedInfo",
+ Justification = "OnControlStateChanged is called when controlState is changed so controlStateChangedInfo cannot be null.")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected override void OnControlStateChanged(ControlStateChangedEventArgs controlStateChangedInfo)
+ {
+ if (controlStateChangedInfo.PreviousState.Contains(ControlState.Selected) != controlStateChangedInfo.CurrentState.Contains(ControlState.Selected))
+ {
+ // MenuItem does not invoke SelectedChanged if button or key is
+ // unpressed while its state is selected.
+ if (selectedAgain == true)
+ {
+ return;
+ }
+
+ base.OnControlStateChanged(controlStateChangedInfo);
+ }
+ }
+
+ /// <inheritdoc/>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected override void LayoutItems()
+ {
+ if (styleApplied == false)
+ {
+ return;
+ }
+
+ if ((Icon == null) && (TextLabel == null))
+ {
+ return;
+ }
+
+ // Icon is added in Button.LayoutItems().
+ if ((Icon != null) && (Children.Contains(Icon) == false))
+ {
+ Add(Icon);
+ }
+
+ // TextLabel is added in Button.LayoutItems().
+ if ((TextLabel != null) && (Children.Contains(TextLabel) == false))
+ {
+ 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();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2021 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;
+using System.ComponentModel;
+
+namespace Tizen.NUI.Components
+{
+ /// <summary>
+ /// MenuItemGroup class is used to group together a set of MenuItems.
+ /// It enables a user to select exclusively single MenuItem of a group.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class MenuItemGroup : SelectGroup
+ {
+ /// <summary>
+ /// Constructs MenuItemGroup
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public MenuItemGroup() : base()
+ {
+ EnableMultiSelection = false;
+ }
+
+ /// <summary>
+ /// Adds a menu item to the end of MenuItemGroup.
+ /// </summary>
+ /// <param name="menuItem">A menu item to be added to the group.</param>
+ /// <exception cref="ArgumentNullException">Thrown when the argument menuItem is null.</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void Add(MenuItem menuItem)
+ {
+ if (menuItem == null)
+ {
+ throw new ArgumentNullException(nameof(menuItem), "menuItem should not be null.");
+ }
+
+ base.AddSelection(menuItem);
+ }
+
+ /// <summary>
+ /// Removes a menu item from MenuItemGroup.
+ /// </summary>
+ /// <param name="menuItem">A menu item to be removed from the group.</param>
+ /// <exception cref="ArgumentNullException">Thrown when the argument menuItem is null.</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void Remove(MenuItem menuItem)
+ {
+ if (menuItem == null)
+ {
+ throw new ArgumentNullException(nameof(menuItem), "menuItem should not be null.");
+ }
+
+ base.RemoveSelection(menuItem);
+ }
+ }
+}
},
ActionView = new ViewStyle()
{
- Size = new Size(-1, 48),
+ Size = new Size(-1, 120),
CornerRadius = 0,
BackgroundColor = new Color(0, 0, 0, 0),
},
ActionButton = new ButtonStyle()
{
- Size = new Size(-1, 48),
+ Size = new Size(-1, 120),
CornerRadius = 0,
BackgroundColor = new Color(0, 0, 0, 0),
Text = new TextLabelStyle()
},
});
+ theme.AddStyleWithoutClone("Tizen.NUI.Components.MenuItem", new ButtonStyle()
+ {
+ Size = new Size(480, -2),
+ MinimumSize = new Size2D(0, 72),
+ CornerRadius = 0,
+ BackgroundImage = FrameworkInformation.ResourcePath + "nui_component_menu_item_bg.png",
+ Padding = new Extents(16, 16, 16, 16),
+ Text = new TextLabelStyle()
+ {
+ PixelSize = 32,
+ MultiLine = true,
+ HorizontalAlignment = HorizontalAlignment.Begin,
+ VerticalAlignment = VerticalAlignment.Center,
+ TextColor = new Selector<Color>()
+ {
+ Normal = new Color("#001447FF"),
+ Focused = new Color("#00338BFF"),
+ Pressed = new Color("#1B69CAFF"),
+ Disabled = new Color("#C3CAD2FF"),
+ },
+ },
+ Icon = new ImageViewStyle()
+ {
+ Size = new Size(-2, 48),
+ Color = new Selector<Color>()
+ {
+ Normal = new Color("#001447FF"),
+ Focused = new Color("#00338BFF"),
+ Pressed = new Color("#1B69CAFF"),
+ Disabled = new Color("#C3CAD2FF"),
+ },
+ },
+ });
+
return theme;
}
}
--- /dev/null
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Components;
+
+namespace Tizen.NUI.Samples
+{
+ public class MenuSample : IExample
+ {
+ public void Activate()
+ {
+ var window = NUIApplication.GetDefaultWindow();
+ var navigator = window.GetDefaultNavigator();
+
+ var pageContent = new Button()
+ {
+ Text = "Page Content",
+ CornerRadius = 0,
+ WidthSpecification = LayoutParamPolicies.MatchParent,
+ HeightSpecification = LayoutParamPolicies.MatchParent,
+ };
+
+ var moreButton = new Button()
+ {
+ Text = "More",
+ };
+
+ var appBar = new AppBar()
+ {
+ AutoNavigationContent = false,
+ Title = "Title",
+ Actions = new View[] { moreButton, },
+ };
+
+ var page = new ContentPage()
+ {
+ AppBar = appBar,
+ Content = pageContent,
+ };
+ navigator.Push(page);
+
+ var menuItem = new MenuItem() { Text = "Menu" };
+ var menuItem2 = new MenuItem() { Text = "Menu2" };
+ var menuItem3 = new MenuItem() { Text = "Menu3" };
+ var menuItem4 = new MenuItem() { Text = "Menu4" };
+
+ moreButton.Clicked += (object sender, ClickedEventArgs args) =>
+ {
+ DialogPage.ShowMenu(moreButton, menuItem, menuItem2, menuItem3, menuItem4);
+ };
+ }
+
+ public void Deactivate()
+ {
+ var window = NUIApplication.GetDefaultWindow();
+ var navigator = window.GetDefaultNavigator();
+ var newPageCount = window.GetDefaultNavigator().NavigationPages.Count;
+
+ for (int i = 0; i < newPageCount; i++)
+ {
+ navigator.Pop();
+ }
+ }
+ }
+}