/*
* Copyright (c) 2018 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.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Tizen.NUI.Binding.Internals;
using Tizen.NUI.Binding;
namespace Tizen.NUI
{
///
/// A BaseHandle that occupies the entire screen.
///
// [RenderWith(typeof(_PageRenderer))]
[EditorBrowsable(EditorBrowsableState.Never)]
public class Page : BaseHandle, IPageController, IElementConfiguration
{
///
/// For internal use.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
public const string BusySetSignalName = "NUI.BusySet";
///
/// For internal use.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
public const string AlertSignalName = "NUI.SendAlert";
///
/// For internal use.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
public const string ActionSheetSignalName = "NUI.ShowActionSheet";
internal static readonly BindableProperty IgnoresContainerAreaProperty = BindableProperty.Create(nameof(IgnoresContainerArea), typeof(bool), typeof(Page), false);
///
/// Identifies the IsBusy property.
///
internal static readonly BindableProperty IsBusyProperty = BindableProperty.Create(nameof(IsBusy), typeof(bool), typeof(Page), false, propertyChanged: (bo, o, n) => ((Page)bo).OnPageBusyChanged());
Rectangle _containerArea;
bool _hasAppeared;
ReadOnlyCollection _logicalChildren;
///
/// Creates a new Page element with default values.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
public Page()
{
// ToolbarItems = toolbarItems;
InternalChildren.CollectionChanged += InternalChildrenOnCollectionChanged;
}
///
/// Marks the Page as busy. This will cause the platform specific global activity indicator to show a busy state.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsBusy
{
get { return (bool)GetValue(IsBusyProperty); }
set { SetValue(IsBusyProperty, value); }
}
///
/// For internal use.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public Rectangle ContainerArea
{
get { return _containerArea; }
set
{
if (_containerArea == value)
return;
_containerArea = value;
ForceLayout();
}
}
///
/// For internal use.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IgnoresContainerArea
{
get { return (bool)GetValue(IgnoresContainerAreaProperty); }
set { SetValue(IgnoresContainerAreaProperty, value); }
}
///
/// For internal use.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public ObservableCollection InternalChildren { get; } = new ObservableCollection();
internal override ReadOnlyCollection LogicalChildrenInternal =>
_logicalChildren ?? (_logicalChildren = new ReadOnlyCollection(InternalChildren));
///
/// ndicates that the Page is about to appear.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler Appearing;
///
/// Indicates that the Page is about to cease displaying.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler Disappearing;
///
/// Displays a native platform action sheet, allowing the application user to choose from several buttons.
///
/// Title of the displayed action sheet. Must not be null.
/// Text to be displayed in the 'Cancel' button. Can be null to hide the cancel action.
/// Text to be displayed in the 'Destruct' button. Can be null to hide the destructive option.
/// Text labels for additional buttons. Must not be null.
/// An awaitable Task that displays an action sheet and returns the Text of the button pressed by the user.
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
public Task DisplayActionSheet(string title, string cancel, string destruction, params string[] buttons)
{
var args = new ActionSheetArguments(title, cancel, destruction, buttons);
MessagingCenter.Send(this, ActionSheetSignalName, args);
return args.Result.Task;
}
///
/// Presents an alert dialog to the application user with a single cancel button.
///
/// The title of the alert dialog.
/// The body text of the alert dialog.
/// Text to be displayed on the 'Cancel' button.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
public Task DisplayAlert(string title, string message, string cancel)
{
return DisplayAlert(title, message, null, cancel);
}
///
/// resents an alert dialog to the application user with an accept and a cancel button.
///
/// The title of the alert dialog.
/// The body text of the alert dialog.
/// Text to be displayed on the 'Accept' button.
/// Text to be displayed on the 'Cancel' button.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
public Task DisplayAlert(string title, string message, string accept, string cancel)
{
if (string.IsNullOrEmpty(cancel))
throw new ArgumentNullException(nameof(cancel));
var args = new AlertArguments(title, message, accept, cancel);
MessagingCenter.Send(this, AlertSignalName, args);
return args.Result.Task;
}
///
/// Forces the Page to perform a layout pass.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
public void ForceLayout()
{
}
///
/// Calls OnBackButtonPressed().
///
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
public bool SendBackButtonPressed()
{
return OnBackButtonPressed();
}
///
/// When overridden, allows application developers to customize behavior immediately prior to the Page becoming visible.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnAppearing()
{
}
///
/// Application developers can override this method to provide behavior when the back button is pressed.
///
/// true if consumed
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual bool OnBackButtonPressed()
{
var application = RealParent as Application;
var canceled = false;
EventHandler handler = (sender, args) => { canceled = true; };
return !canceled;
}
///
/// Invoked whenever the binding context of the Page changes. Override this method to add class handling for this event.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
}
///
/// Indicates that the preferred size of a child Element has changed.
///
/// The object that raised the event.
/// The event arguments.
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnChildMeasureInvalidated(object sender, EventArgs e)
{
InvalidationTrigger trigger = (e as InvalidationEventArgs)?.Trigger ?? InvalidationTrigger.Undefined;
OnChildMeasureInvalidated((BaseHandle)sender, trigger);
}
///
/// When overridden, allows the application developer to customize behavior as the Page disappears.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnDisappearing()
{
}
///
/// Called when the Page's Parent property has changed.
///
/// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
[EditorBrowsable(EditorBrowsableState.Never)]
protected override void OnParentSet()
{
base.OnParentSet();
}
internal virtual void OnChildMeasureInvalidated(BaseHandle child, InvalidationTrigger trigger)
{
var container = this as IPageContainer;
if (container != null)
{
Page page = container.CurrentPage;
if (page != null)
{
return;
}
}
else
{
for (var i = 0; i < LogicalChildren.Count; i++)
{
var v = LogicalChildren[i] as BaseHandle;
if (v != null)
{
return;
}
}
}
}
///
/// For intarnal use.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public void SendAppearing()
{
if (_hasAppeared)
return;
_hasAppeared = true;
if (IsBusy)
MessagingCenter.Send(this, BusySetSignalName, true);
OnAppearing();
Appearing?.Invoke(this, EventArgs.Empty);
var pageContainer = this as IPageContainer;
pageContainer?.CurrentPage?.SendAppearing();
}
///
/// For intarnal use.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public void SendDisappearing()
{
if (!_hasAppeared)
return;
_hasAppeared = false;
if (IsBusy)
MessagingCenter.Send(this, BusySetSignalName, false);
var pageContainer = this as IPageContainer;
pageContainer?.CurrentPage?.SendDisappearing();
OnDisappearing();
Disappearing?.Invoke(this, EventArgs.Empty);
}
Application FindApplication(Element element)
{
if (element == null)
return null;
return (element.Parent is Application app) ? app : FindApplication(element.Parent);
}
void InternalChildrenOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
{
foreach (BaseHandle item in e.OldItems.OfType())
{
OnInternalRemoved(item);
}
}
if (e.NewItems != null)
{
foreach (BaseHandle item in e.NewItems.OfType())
{
OnInternalAdded(item);
}
}
}
private void OnInternalAdded(BaseHandle view)
{
OnChildAdded(view);
}
private void OnInternalRemoved(BaseHandle view)
{
OnChildRemoved(view);
}
void OnPageBusyChanged()
{
if (!_hasAppeared)
return;
MessagingCenter.Send(this, BusySetSignalName, IsBusy);
}
void OnToolbarItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
if (null != args)
{
if (args.Action != NotifyCollectionChangedAction.Add)
return;
if (args.NewItems != null)
{
foreach (IElement item in args.NewItems)
item.Parent = this;
}
}
}
bool ShouldLayoutChildren()
{
if (!LogicalChildren.Any())
{
return false;
}
var container = this as IPageContainer;
if (container?.CurrentPage != null)
{
return true;
}
var any = false;
for (var i = 0; i < LogicalChildren.Count; i++)
{
var v = LogicalChildren[i] as BaseHandle;
if (v != null)
{
any = true;
break;
}
}
return !any;
}
}
}