diff --git a/src/Tizen.Wearable.CircularUI.Forms.Renderer/ContentPopupRenderer.cs b/src/Tizen.Wearable.CircularUI.Forms.Renderer/ContentPopupRenderer.cs new file mode 100644 index 00000000..0fcbbf9e --- /dev/null +++ b/src/Tizen.Wearable.CircularUI.Forms.Renderer/ContentPopupRenderer.cs @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * 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; +using System.Threading.Tasks; +using Xamarin.Forms; +using Xamarin.Forms.Platform.Tizen; +using XForms = Xamarin.Forms.Forms; + +[assembly: Dependency(typeof(Tizen.Wearable.CircularUI.Forms.Renderer.ContentPopupRenderer))] + +namespace Tizen.Wearable.CircularUI.Forms.Renderer +{ + public class ContentPopupRenderer : IContentPopupRenderer + { + ElmSharp.Popup _popup; + ContentPopup _element; + TaskCompletionSource _tcs; + + public ContentPopupRenderer() + { + _popup = new ElmSharp.Popup(XForms.NativeParent); + _popup.Style = "circle"; + _popup.BackButtonPressed += OnBackButtonPressed; + _popup.Dismissed += OnDismissed; + } + + ~ContentPopupRenderer() + { + Dispose(false); + } + + public void SetElement(ContentPopup element) + { + if (element.Parent == null) + element.Parent = Application.Current; + element.PropertyChanged += OnElementPropertyChanged; + _element = element; + + UpdateContent(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public Task Open() + { + _popup.Show(); + _element.SetValueFromRenderer(ContentPopup.IsOpenProperty, true); + _tcs = new TaskCompletionSource(); + return _tcs.Task; + } + + protected void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == ContentPopup.ContentProperty.PropertyName) + UpdateContent(); + + if (e.PropertyName == ContentPopup.IsOpenProperty.PropertyName) + UpdateIsOpen(); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (_popup != null) + { + _popup.BackButtonPressed -= OnBackButtonPressed; + _popup.Dismissed -= OnDismissed; + _popup.Unrealize(); + _popup = null; + } + if (_element != null) + { + _element.PropertyChanged -= OnElementPropertyChanged; + _element = null; + } + } + } + + void OnBackButtonPressed(object sender, EventArgs e) + { + if (!_element.SendBackButtonPressed()) + _popup?.Hide(); + } + + void OnDismissed(object sender, EventArgs e) + { + _element.SendDismissed(); + _tcs?.SetResult(true); + } + + void UpdateContent() + { + if (_element.Content != null) + { + var renderer = Platform.GetOrCreateRenderer(_element.Content); + (renderer as LayoutRenderer)?.RegisterOnLayoutUpdated(); + var native = renderer.NativeView; + native.MinimumHeight = XForms.NativeParent.Geometry.Height; + native.MinimumWidth = XForms.NativeParent.Geometry.Width; + _popup.SetContent(native, false); + } + else + { + _popup.SetContent(null, false); + } + } + + void UpdateIsOpen() + { + if (!_element.IsOpen) + _popup?.Hide(); + } + } +} diff --git a/src/Tizen.Wearable.CircularUI.Forms/ContentPopup.cs b/src/Tizen.Wearable.CircularUI.Forms/ContentPopup.cs new file mode 100644 index 00000000..b2253af1 --- /dev/null +++ b/src/Tizen.Wearable.CircularUI.Forms/ContentPopup.cs @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * 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; +using Xamarin.Forms; + +namespace Tizen.Wearable.CircularUI.Forms +{ + /// + /// The ContentPopup is a Popup, which allows you to customize the View to be displayed. + /// + /// 4 + public class ContentPopup : Element + { + /// + /// BindableProperty. Identifies the Content bindable property. + /// + /// 4 + public static readonly BindableProperty ContentProperty = BindableProperty.Create(nameof(Content), typeof(View), typeof(ContentPopup), null, propertyChanged: (b, o, n) => ((ContentPopup)b).UpdateContent()); + + /// + /// BindableProperty. Identifies the IsShow bindable property. + /// + /// 4 + public static readonly BindableProperty IsOpenProperty = BindableProperty.Create(nameof(IsOpen), typeof(bool), typeof(ContentPopup), false, defaultBindingMode: BindingMode.TwoWay); + + /// + /// Occurs when the popup is dismissed. + /// + /// 4 + public event EventHandler Dismissed; + + /// + /// Gets or sets content view of the Popup. + /// + /// 4 + public View Content + { + get { return (View)GetValue(ContentProperty); } + set { SetValue(ContentProperty, value); } + } + + /// + /// Gets or sets the popup is opened. + /// + /// 4 + public bool IsOpen + { + get { return (bool)GetValue(IsOpenProperty); } + set { SetValue(IsOpenProperty, value); } + } + + /// + /// Dismisses the popup. + /// + /// 4 + public void Dismiss() + { + IsOpen = false; + } + + /// + /// For internal use. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public void SendDismissed() + { + Dismissed?.Invoke(this, EventArgs.Empty); + } + + /// + /// For internal use. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool SendBackButtonPressed() + { + return OnBackButtonPressed(); + } + + /// + /// To change the default behavior of the BackButton. Default behavior is dismiss. + /// + /// Default is false + protected virtual bool OnBackButtonPressed() + { + return false; + } + + protected override void OnBindingContextChanged() + { + base.OnBindingContextChanged(); + if (Content != null) + SetInheritedBindingContext(Content, BindingContext); + } + + void UpdateContent() + { + if (Content != null) + OnChildAdded(Content); + } + } +} diff --git a/src/Tizen.Wearable.CircularUI.Forms/ContentPopupManager.cs b/src/Tizen.Wearable.CircularUI.Forms/ContentPopupManager.cs new file mode 100644 index 00000000..82d907e5 --- /dev/null +++ b/src/Tizen.Wearable.CircularUI.Forms/ContentPopupManager.cs @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * 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.Threading.Tasks; +using Xamarin.Forms; + +namespace Tizen.Wearable.CircularUI.Forms +{ + public static class ContentPopupManager + { + public static async Task ShowPopup(this INavigation navigation, ContentPopup popup) + { + await ShowPopup(popup); + } + + public static async Task ShowPopup(ContentPopup popup) + { + if (popup == null) + return; + + using (var renderer = DependencyService.Get(DependencyFetchTarget.NewInstance)) + { + if (renderer == null) + return; + + renderer.SetElement(popup); + + await renderer.Open(); + } + } + } +} diff --git a/src/Tizen.Wearable.CircularUI.Forms/IContentPopupRenderer.cs b/src/Tizen.Wearable.CircularUI.Forms/IContentPopupRenderer.cs new file mode 100644 index 00000000..5cb62d36 --- /dev/null +++ b/src/Tizen.Wearable.CircularUI.Forms/IContentPopupRenderer.cs @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * 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.Threading.Tasks; + +namespace Tizen.Wearable.CircularUI.Forms +{ + /// + /// Base interface for ContentPopup renderer. + /// + /// 4 + public interface IContentPopupRenderer : IDisposable + { + /// + /// Sets the Element associated with this renderer. + /// + /// New element. + void SetElement(ContentPopup element); + + /// + /// Open a popup. + /// + /// Returns a Task with the dismiss result of the popup. + Task Open(); + } +} diff --git a/src/Tizen.Wearable.CircularUI.Forms/ITwoButtonPopup.cs b/src/Tizen.Wearable.CircularUI.Forms/ITwoButtonPopup.cs old mode 100755 new mode 100644 index 913caca4..79473668 --- a/src/Tizen.Wearable.CircularUI.Forms/ITwoButtonPopup.cs +++ b/src/Tizen.Wearable.CircularUI.Forms/ITwoButtonPopup.cs @@ -72,4 +72,4 @@ internal interface ITwoButtonPopup /// 4 void Dismiss(); } -} \ No newline at end of file +} diff --git a/test/WearableUIGallery/WearableUIGallery/TC/TCContentPopup.xaml b/test/WearableUIGallery/WearableUIGallery/TC/TCContentPopup.xaml new file mode 100644 index 00000000..953f1bbe --- /dev/null +++ b/test/WearableUIGallery/WearableUIGallery/TC/TCContentPopup.xaml @@ -0,0 +1,43 @@ + + + + + + + + +