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));
}
}
}