using System; using ElmSharp; using EBox = ElmSharp.Box; namespace Xamarin.Forms.Platform.Tizen.Native { /// /// The native widget which provides Xamarin.MasterDetailPage features. /// public class MasterDetailPage : EBox { /// /// The portion of the screen that the MasterPage takes in Split mode. /// static readonly double s_splitRatio = 0.35; /// /// The portion of the screen that the MasterPage takes in Popover mode. /// static readonly double s_popoverRatio = 0.8; /// /// The default master behavior (a.k.a mode). /// static readonly MasterBehavior s_defaultMasterBehavior = (Device.Idiom == TargetIdiom.Phone) ? MasterBehavior.Popover : MasterBehavior.SplitOnLandscape; /// /// The MasterPage native container. /// readonly Canvas _masterCanvas; /// /// The DetailPage native container. /// readonly Canvas _detailCanvas; /// /// The container for _masterCanvas and _detailCanvas used in split mode. /// readonly Panes _splitPane; /// /// The container for _masterCanvas used in popover mode. /// readonly Panel _drawer; /// /// The property value. /// MasterBehavior _masterBehavior = s_defaultMasterBehavior; /// /// The actual MasterDetailPage mode - either split or popover. It depends on _masterBehavior and screen orientation. /// MasterBehavior _internalMasterBehavior = MasterBehavior.Popover; /// /// The property value. /// EvasObject _master; /// /// The property value. /// EvasObject _detail; /// /// The main widget - either or , depending on the mode. /// EvasObject _mainWidget; /// /// The property value. /// bool _isGestureEnabled; /// /// Initializes a new instance of the class. /// /// Parent evas object. public MasterDetailPage(EvasObject parent) : base(parent) { // we control the layout ourselves Resized += (sender, e) => { var g = Geometry; // main widget should fill the area of the MasterDetailPage if (_mainWidget != null) { _mainWidget.Geometry = g; } g.Width = (int)((s_popoverRatio * (double)g.Width)); _drawer.Geometry = g; }; // create the controls which will hold the master and detail pages _masterCanvas = new Canvas(this); _masterCanvas.SetAlignment(-1.0, -1.0); // fill _masterCanvas.SetWeight(1.0, 1.0); // expand _masterCanvas.LayoutUpdated += (sender, e) => { UpdatePageGeometry(_master); }; _detailCanvas = new Canvas(this); _detailCanvas.SetAlignment(-1.0, -1.0); // fill _detailCanvas.SetWeight(1.0, 1.0); // expand _detailCanvas.LayoutUpdated += (sender, e) => { UpdatePageGeometry(_detail); }; _splitPane = new Panes(this) { IsFixed = true, IsHorizontal = false, Proportion = s_splitRatio, }; _drawer = new Panel(Forms.Context.MainWindow) { Direction = PanelDirection.Left, }; _drawer.SetScrollable(_isGestureEnabled); _drawer.SetScrollableArea(1.0); _drawer.Toggled += (object sender, EventArgs e) => { IsPresentedChanged?.Invoke(this, EventArgs.Empty); }; ConfigureLayout(); // in case of the screen rotation we may need to update the choice between split // and popover behaviors and reconfigure the layout Forms.Context.MainWindow.RotationChanged += (sender, e) => { UpdateMasterBehavior(); }; } /// /// Occurs when the MasterPage is shown or hidden. /// public event EventHandler IsPresentedChanged; /// /// Gets or sets the MasterDetailPage behavior. /// /// The behavior of the MasterDetailPage requested by the user. public MasterBehavior MasterBehavior { get { return _masterBehavior; } set { if (_masterBehavior != value) { _masterBehavior = value; UpdateMasterBehavior(); } } } /// /// Gets or sets the content of the MasterPage. /// /// The MasterPage. public EvasObject Master { get { return _master; } set { if (_master != value) { _master = value; UpdatePageGeometry(_master); _masterCanvas.Children.Clear(); _masterCanvas.Children.Add(_master); } } } /// /// Gets or sets the content of the DetailPage. /// /// The DetailPage. public EvasObject Detail { get { return _detail; } set { if (_detail != value) { _detail = value; UpdatePageGeometry(_detail); _detailCanvas.Children.Clear(); _detailCanvas.Children.Add(_detail); } } } /// /// Gets or sets a value indicating whether the MasterPage is shown. /// /// true if the MasterPage is presented. public bool IsPresented { get { return _drawer.IsOpen; } set { if (_drawer.IsOpen != value) { _drawer.IsOpen = value; } } } /// /// Gets or sets a value indicating whether a MasterDetailPage allows showing MasterPage with swipe gesture. /// /// true if the MasterPage can be revealed with a gesture. public bool IsGestureEnabled { get { return _isGestureEnabled; } set { if (_isGestureEnabled != value) { _isGestureEnabled = value; _drawer.SetScrollable(_isGestureEnabled); } } } /// /// Provides destruction for native element and contained elements. /// protected override void OnUnrealize() { _drawer.Unrealize(); base.OnUnrealize(); } /// /// Updates the geometry of the selected page. /// /// Master or Detail page to be updated. void UpdatePageGeometry(EvasObject page) { if (page != null) { if (_master == page) { // update the geometry of the master page page.Geometry = _masterCanvas.Geometry; } else if (_detail == page) { // update the geometry of the detail page page.Geometry = _detailCanvas.Geometry; } } } /// /// Updates according to set by the user and current screen orientation. /// void UpdateMasterBehavior() { var behavior = (_masterBehavior == MasterBehavior.Default) ? s_defaultMasterBehavior : _masterBehavior; // Screen orientation affects those 2 behaviors if (behavior == MasterBehavior.SplitOnLandscape || behavior == MasterBehavior.SplitOnPortrait) { var rotation = Forms.Context.MainWindow.Rotation; if (((rotation == 90 || rotation == 270) && behavior == MasterBehavior.SplitOnLandscape) || ((rotation == 0 || rotation == 90) && behavior == MasterBehavior.SplitOnPortrait)) { behavior = MasterBehavior.Split; } else { behavior = MasterBehavior.Popover; } } if (behavior != _internalMasterBehavior) { _internalMasterBehavior = behavior; ConfigureLayout(); } } /// /// Composes the structure of all the necessary widgets. /// void ConfigureLayout() { _drawer.SetContent(null, true); _drawer.Hide(); _splitPane.SetPartContent("left", null, true); _splitPane.SetPartContent("right", null, true); _splitPane.Hide(); UnPackAll(); // the structure for split mode and for popover mode looks differently if (_internalMasterBehavior == MasterBehavior.Split) { _splitPane.SetPartContent("left", _masterCanvas, true); _splitPane.SetPartContent("right", _detailCanvas, true); PackEnd(_splitPane); _splitPane.Show(); _mainWidget = _splitPane; IsPresented = true; } else { _drawer.SetContent(_masterCanvas, true); PackEnd(_detailCanvas); _drawer.Show(); _mainWidget = _detailCanvas; _drawer.IsOpen = IsPresented; } _masterCanvas.Show(); _detailCanvas.Show(); } } }