using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; using Xamarin.Forms; namespace Tizen.Xamarin.Forms.Extension { /// /// The ContextPopup class allows a contextual popup to be anchored at a view. /// /// /// /// ContextPopup popup = new ContextPopup /// { /// DirectionPriorities = new ContextPopupDirectionPriorities(ContextPopupDirection.Down, ContextPopupDirection.Right, ContextPopupDirection.Left, ContextPopupDirection.Up), /// }; /// popup.Items.Add(new ContextPopupItem("Text only item")); /// popup.Items.Add(new ContextPopupItem("Home icon", "home")); /// popup.Items.Add(new ContextPopupItem("Chat", StandardIconResource.MenuChat.Name)); /// popup.SelectedIndexChanged += (s, e) => /// { /// var ctxPopup = s as ContextPopup; /// Debug.WriteLine("Item with index {0} selected", ctxPopup.SelectedIndex); /// Debug.WriteLine("It has label: " + (ctxPopup.SelectedItem as ContextPopupItem).Label); /// }; /// /// Button btn = new Button /// { /// Text = "Toggle popup" /// }; /// btn.Clicked += (s, e) => /// { /// popup.Show(s as Button); /// }; /// /// public class ContextPopup : BindableObject { IContextPopup _contextPopup; ObservableCollection _items; static ContextPopupDirectionPriorities _priorities = new ContextPopupDirectionPriorities(ContextPopupDirection.Up, ContextPopupDirection.Left, ContextPopupDirection.Right, ContextPopupDirection.Down); [Obsolete("OrientationProperty is obsolete as of version 2.3.5-r256-001. The orientation is always vertical.")] public static readonly BindableProperty OrientationProperty = BindableProperty.Create(nameof(Orientation), typeof(ContextPopupOrientation), typeof(ContextPopup), defaultValue: ContextPopupOrientation.Vertical); /// /// BindableProperty. Identifies the IsAutoHidingEnabled bindable property. /// public static readonly BindableProperty IsAutoHidingEnabledProperty = BindableProperty.Create(nameof(IsAutoHidingEnabled), typeof(bool), typeof(ContextPopup), defaultValue: true); /// /// BindableProperty. Identifies the DirectionPriorities bindable property. /// public static readonly BindableProperty DirectionPrioritiesProperty = BindableProperty.Create(nameof(DirectionPriorities), typeof(ContextPopupDirectionPriorities), typeof(ContextPopup), defaultValue: _priorities); /// /// BindableProperty. Identifies the SelectedIndex bindable property. /// public static readonly BindableProperty SelectedIndexProperty = BindableProperty.Create(nameof(SelectedIndex), typeof(int), typeof(ContextPopup), defaultValue: -1, propertyChanged: OnSelectedIndexChanged, coerceValue: CoerceSelectedIndex); /// /// BindableProperty. Identifies the SelectedItem bindable property. /// public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create(nameof(SelectedItem), typeof(object), typeof(ContextPopup), null, propertyChanged: OnSelectedItemChanged); [Obsolete("ItemsSourceProperty is obsolete as of version 2.3.5-r256-001. Please use Items instead.")] public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IList), typeof(ContextPopup), default(IList)); /// /// The constructor, which creates a new ContextPopup instance. /// public ContextPopup() { _contextPopup = DependencyService.Get(DependencyFetchTarget.NewInstance); _contextPopup.Dismissed += (s, e) => Dismissed?.Invoke(this, EventArgs.Empty); _contextPopup.ItemSelected += (s, e) => ItemSelected?.Invoke(this, EventArgs.Empty); _items = new ObservableCollection(); _items.CollectionChanged += ItemsCollectionChanged; SetBinding(IsAutoHidingEnabledProperty, new Binding(nameof(IsAutoHidingEnabled), mode: BindingMode.TwoWay, source: _contextPopup)); SetBinding(DirectionPrioritiesProperty, new Binding(nameof(DirectionPriorities), mode: BindingMode.TwoWay, source: _contextPopup)); SetBinding(SelectedItemProperty, new Binding(nameof(SelectedItem), mode: BindingMode.TwoWay, source: _contextPopup)); } /// /// Occurs when the ContextPopup is dismissed. /// public event EventHandler Dismissed; /// /// Occurs when the index of the selected ContextPopupItem changes. /// public event EventHandler SelectedIndexChanged; /// /// Occurs when a ContextPopupItem is selected. /// public event EventHandler ItemSelected; [Obsolete("Orientation is obsolete as of version 2.3.5-r256-001. The orientation is always vertical.")] public ContextPopupOrientation Orientation { get { return (ContextPopupOrientation)GetValue(OrientationProperty); } set { SetValue(OrientationProperty, value); } } /// /// Gets or sets whether ContextPopup should be hidden automatically or not when parent of ContextPopup is resized. /// /// /// Setting IsAutoHidingEnabled to false will not be dismissed automatically whenever such as mouse clicked its background area, language i s changed, and its parent geometry is updated(changed). /// public bool IsAutoHidingEnabled { get { return (bool)GetValue(IsAutoHidingEnabledProperty); } set { SetValue(IsAutoHidingEnabledProperty, value); } } /// /// Gets or sets the direction priorities for the ContextPopup.
/// The position of the ContextPopup depends on the available space. /// Therefore, DirectionPriorities are used to specify desired first, second, third, and fourth priorities for positioning the ContextPopup. ///
public ContextPopupDirectionPriorities DirectionPriorities { get { return (ContextPopupDirectionPriorities)GetValue(DirectionPrioritiesProperty); } set { SetValue(DirectionPrioritiesProperty, value); } } /// /// Gets or sets the index of the selected item of the ContextPopup. /// It is -1 when no item is selected. /// public int SelectedIndex { get { return (int)GetValue(SelectedIndexProperty); } set { SetValue(SelectedIndexProperty, value); } } /// /// Gets or sets the selected item of the ContextPopup. /// public ContextPopupItem SelectedItem { get { return (ContextPopupItem)GetValue(SelectedItemProperty); } set { SetValue(SelectedItemProperty, value); } } [Obsolete("ItemsSource is obsolete as of version 2.3.5-r256-001. Please use Items instead.")] public IList ItemsSource { get { return (IList)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } /// /// Gets the list of items in the ContextPopup. /// public IList Items { get { return _items; } } /// /// Shows the ContextPopup. The ContextPopup is positioned at the horizontal and the vertical position of a specific anchor. /// /// The view to which the popup should be anchored. public void Show(View anchor) { Show(anchor, 0, 0); } /// /// Shows the ContextPopup. The ContextPopup is positioned at the horizontal and the vertical position of a specific anchor with offsets. /// /// The view to which the popup should be anchored. /// The horizontal offset from the anchor. /// The vertical offset from the anchor. public void Show(View anchor, int xOffset, int yOffset) { _contextPopup.Show(anchor, xOffset, yOffset); } /// /// Shows the ContextPopup. The ContextPopup is positioned at the horizontal and the vertical position of a specific anchor with offsets. /// /// The view to which the popup should be anchored. /// The horizontal offset from the anchor. /// The vertical offset from the anchor. public void Show(View anchor, double xOffset, double yOffset) { Show(anchor, (int)xOffset, (int)yOffset); } /// /// Dismisses the ContextPopup. /// public void Dismiss() { _contextPopup.Dismiss(); } /// /// Gets the direction of the ContextPopup if it is shown. /// This method returns false if it is not shown and the output argument is a default value. /// /// The direction of the ContextPopup. /// true if the ContextPopup is shown, false otherwise. public bool TryGetContextPopupDirection(out ContextPopupDirection direction) { direction = default(ContextPopupDirection); return _contextPopup.TryGetContextPopupDirection(out direction); } void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: AddItems(e); break; case NotifyCollectionChangedAction.Remove: RemoveItems(e); break; default: // Move, replace, reset ResetItems(); break; } SelectedIndex = SelectedIndex.Clamp(-1, Items.Count - 1); UpdateSelectedItem(); } void ResetItems() { _contextPopup.ClearItems(); } void RemoveItems(NotifyCollectionChangedEventArgs e) { _contextPopup.RemoveItems(e.OldItems.OfType()); } void AddItems(NotifyCollectionChangedEventArgs e) { _contextPopup.AddItems(e.NewItems.OfType()); } static void OnSelectedIndexChanged(BindableObject bindable, object oldValue, object newValue) { var contextPopup = (ContextPopup)bindable; contextPopup.UpdateSelectedItem(); contextPopup.SelectedIndexChanged?.Invoke(contextPopup, EventArgs.Empty); } static object CoerceSelectedIndex(BindableObject bindable, object value) { var contextPopup = (ContextPopup)bindable; return contextPopup.Items == null ? -1 : ((int)value).Clamp(-1, contextPopup.Items.Count - 1); } void UpdateSelectedItem() { if (SelectedIndex == -1) { SelectedItem = null; return; } SelectedItem = Items[SelectedIndex]; } static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue) { var contextPopup = (ContextPopup)bindable; contextPopup.UpdateSelectedIndex(newValue); } void UpdateSelectedIndex(object selectedItem) { SelectedIndex = Items.IndexOf(selectedItem as ContextPopupItem); } } internal static class NumericExtensions { public static int Clamp(this int self, int min, int max) { return Math.Min(max, Math.Max(self, min)); } } }