From 4ca3fccc85c33ba657f5735ec07f2726d1719c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AF=BC=EC=84=B1=ED=98=84/Common=20Platform=20Lab=28SR?= =?UTF-8?q?=29/Staff=20Engineer/=EC=82=BC=EC=84=B1=EC=A0=84=EC=9E=90?= Date: Thu, 26 Aug 2021 17:02:40 +0900 Subject: [PATCH] Add ShellSearchView (#121) * Add ShellSearchView * Fix the layout issue in ShellSearchResultList * Enable nullable * Fix maximum height of ShellSearchResultList --- .../Core/Platform/Tizen/Shell/ShellNavBar.cs | 52 ++- .../Tizen/Shell/ShellSearchResultList.cs | 99 +++++ .../Platform/Tizen/Shell/ShellSearchView.cs | 377 ++++++++++++++++++ .../Tizen.UIExtensions/SearchBarExtensions.cs | 21 + 4 files changed, 546 insertions(+), 3 deletions(-) create mode 100644 src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchResultList.cs create mode 100644 src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchView.cs create mode 100644 src/Controls/src/Core/Platform/Tizen/Shell/Tizen.UIExtensions/SearchBarExtensions.cs diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ShellNavBar.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ShellNavBar.cs index 4752f43f7ce4..2663239e3ab8 100644 --- a/src/Controls/src/Core/Platform/Tizen/Shell/ShellNavBar.cs +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ShellNavBar.cs @@ -17,7 +17,10 @@ public class ShellNavBar : EBox, IFlyoutBehaviorObserver, IDisposable TImage _menuIcon = null; TButton _menuButton = null; TLabel _title = null; + ShellSearchView _searchView = null; EvasObject _nativeTitleView = null; + + SearchHandler _searchHandler = null; View _titleView = null; Page _page = null; @@ -102,6 +105,20 @@ public FlyoutBehavior FlyoutBehavior } } + public SearchHandler SearchHandler + { + get + { + return _searchHandler; + } + set + { + _searchHandler = value; + UpdateSearchHandler(_searchHandler); + UpdateChildren(); + } + } + public View TitleView { get @@ -177,7 +194,7 @@ public void SetPage(Page page) { _page = page; Title = page.Title; - //SearchHandler = Shell.GetSearchHandler(page); + SearchHandler = Shell.GetSearchHandler(page); TitleView = Shell.GetTitleView(page); UpdateMenuIcon(); } @@ -284,17 +301,42 @@ void UpdateTitleView(View titleView) } } + void UpdateSearchHandler(SearchHandler handler) + { + if (_searchView != null) + { + UnPack(_searchView.NativeView); + _searchView.Dispose(); + _searchView = null; + } + + if (handler != null) + { + _searchView = new ShellSearchView(handler, MauiContext); + _searchView.NativeView.Show(); + PackEnd(_searchView.NativeView); + } + } + void UpdateChildren() { - if (_titleView != null) + if (_searchHandler != null) + { + _searchView.NativeView.Show(); + _title?.Hide(); + _nativeTitleView?.Hide(); + } + else if (_titleView != null) { _nativeTitleView.Show(); _title?.Hide(); + _searchView?.NativeView?.Hide(); } else { _title.Show(); _nativeTitleView?.Hide(); + _searchView?.NativeView?.Hide(); } } @@ -324,7 +366,11 @@ void OnLayout() contentBound.Width -= (menuBound.Width + menuMargin + titleHMargin * 2); contentBound.Height -= titleVMargin * 2; - if (_titleView != null) + if (_searchView != null) + { + _searchView.NativeView.Geometry = contentBound; + } + else if (_titleView != null) { _nativeTitleView.Geometry = contentBound; } diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchResultList.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchResultList.cs new file mode 100644 index 000000000000..425b64965d9a --- /dev/null +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchResultList.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using ElmSharp; +using Tizen.UIExtensions.ElmSharp; +using EColor = ElmSharp.Color; + +namespace Microsoft.Maui.Controls.Platform +{ + public class ShellSearchResultList : GenList + { + GenItemClass _defaultClass = null; + IReadOnlyList _itemsSource; + + public ShellSearchResultList(IMauiContext context) : base(context?.Context?.BaseLayout) + { + MauiContext = context; + + SetAlignment(-1, -1); + SetWeight(1, 1); + AllowFocus(true); + + Homogeneous = true; + SelectionMode = GenItemSelectionMode.Always; + BackgroundColor = EColor.White; + + _defaultClass = new GenItemClass(ThemeConstants.GenItemClass.Styles.Full) + { + GetContentHandler = GetContent, + }; + } + + public int Height { get; private set; } + + public IReadOnlyList ItemsSource + { + get => _itemsSource; + set + { + Clear(); + Height = 0; + + _itemsSource = value; + foreach (var item in _itemsSource) + { + Append(item); + } + } + } + + protected IMauiContext MauiContext { get; private set; } + + protected EvasObject NativeParent + { + get => MauiContext?.Context?.BaseLayout; + } + + public void UpdateLayout() + { + if (FirstItem != null && Height == 0) + { + var view = FirstItem.Data as View; + var native = view.ToNative(MauiContext); + var measured = view.Measure(DPExtensions.ConvertToScaledDP(Geometry.Width), double.PositiveInfinity); + Height = DPExtensions.ConvertToScaledPixel(measured.Request.Height); + } + + var bound = Geometry; + bound.Height = Math.Min(Height * _itemsSource.Count, bound.Width); + Geometry = bound; + + UpdateRealizedItems(); + } + + public DataTemplate ItemTemplate { get; set; } + + EvasObject GetContent(object data, string part) + { + var view = data as View; + var native = view.ToNative(MauiContext); + + if (Height == 0) + { + var measured = view.Measure(DPExtensions.ConvertToScaledDP(Geometry.Width), double.PositiveInfinity); + Height = DPExtensions.ConvertToScaledPixel(measured.Request.Height); + } + + native.MinimumHeight = Height; + return native; + } + + void Append(object data) + { + var view = ItemTemplate.CreateContent() as View; + view.Parent = Shell.Current; + view.BindingContext = data; + Append(_defaultClass, view); + } + } +} diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchView.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchView.cs new file mode 100644 index 000000000000..a85dba4615ef --- /dev/null +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchView.cs @@ -0,0 +1,377 @@ +#nullable enable + +using System; +using System.ComponentModel; +using ElmSharp; +using Tizen.UIExtensions.Shell; +using EColor = ElmSharp.Color; +using TSearchBar = Tizen.UIExtensions.ElmSharp.SearchBar; +using TTextChangedEventArgs = Tizen.UIExtensions.Common.TextChangedEventArgs; + +namespace Microsoft.Maui.Controls.Platform +{ + public class ShellSearchView : IDisposable + { + bool disposedValue; + ShellSearchResultList? _searchResultList; + + public ShellSearchView(SearchHandler searchHandler, IMauiContext context) + { + Element = searchHandler; + MauiContext = context; + + Element.FocusChangeRequested += OnFocusChangedRequested; + Element.PropertyChanged += OnElementPropertyChanged; + (Element as ISearchHandlerController).ListProxyChanged += OnSearchResultListChanged; + + if (NativeParent != null) + { + Control = new TSearchBar(NativeParent) + { + IsSingleLine = true, + }; + Control.Show(); + Control.SetInputPanelReturnKeyType(InputPanelReturnKeyType.Search); + Control.TextChanged += OnTextChanged; + Control.Activated += OnActivated; + Control.Focused += OnFocused; + Control.Unfocused += OnFocused; + } + + UpdateKeyboard(); + UpdatePlaceholder(); + UpdatePlaceholderColor(); + UpdateHorizontalTextAlignment(); + UpdateTextColor(); + UpdateFontAttributes(); + UpdateFontFamily(); + UpdateFontSize(); + UpdateBackgroundColor(); + UpdateQuery(); + UpdateIsSearchEnabled(); + UpdateSearchResult(); + } + + public SearchHandler Element { get; } + + public EvasObject? NativeView => Control; + + protected IMauiContext MauiContext { get; private set; } + + protected EvasObject? NativeParent + { + get => MauiContext.Context?.BaseLayout; + } + + ISearchHandlerController SearchHandlerController => Element; + + TSearchBar? Control { get; } + + ~ShellSearchView() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + Element.FocusChangeRequested -= OnFocusChangedRequested; + Element.PropertyChanged -= OnElementPropertyChanged; + (Element as ISearchHandlerController).ListProxyChanged -= OnSearchResultListChanged; + + if (Control != null) + { + Control.TextChanged -= OnTextChanged; + Control.Activated -= OnActivated; + Control.Focused -= OnFocused; + Control.Unfocused -= OnFocused; + Control.Unrealize(); + } + } + disposedValue = true; + } + } + + void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Element.Keyboard)) + { + UpdateKeyboard(); + } + else if (e.PropertyName == nameof(Element.Placeholder)) + { + UpdatePlaceholder(); + } + else if (e.PropertyName == nameof(Element.PlaceholderColor)) + { + UpdatePlaceholderColor(); + } + else if (e.PropertyName == nameof(Element.HorizontalTextAlignment)) + { + UpdateHorizontalTextAlignment(); + } + else if (e.PropertyName == nameof(Element.TextColor)) + { + UpdateTextColor(); + } + else if (e.PropertyName == nameof(Element.FontAttributes)) + { + UpdateFontAttributes(); + } + else if (e.PropertyName == nameof(Element.FontFamily)) + { + UpdateFontFamily(); + } + else if (e.PropertyName == nameof(Element.FontSize)) + { + UpdateFontSize(); + } + else if (e.PropertyName == nameof(Element.BackgroundColor)) + { + UpdateBackgroundColor(); + } + else if (e.PropertyName == nameof(Element.Query)) + { + UpdateQuery(); + } + else if (e.PropertyName == nameof(Element.IsSearchEnabled)) + { + UpdateIsSearchEnabled(); + } + else if (e.PropertyName == nameof(Element.ShowsResults)) + { + UpdateSearchResult(); + } + } + + void OnSearchResultListChanged(object sender, ListProxyChangedEventArgs e) + { + UpdateSearchResult(); + } + + void InitializeSearchResultList() + { + if (_searchResultList != null) + { + return; + } + _searchResultList = new ShellSearchResultList(MauiContext); + _searchResultList.Show(); + _searchResultList.ItemSelected += OnResultItemSelected; + } + + void OnResultItemSelected(object sender, GenListItemEventArgs e) + { + var data = (e.Item.Data as View)?.BindingContext; + + if (data != null) + { + SearchHandlerController.ItemSelected(data); + Device.BeginInvokeOnMainThread(() => + { + DeinitializeSearchResultList(); + }); + } + } + + void DeinitializeSearchResultList() + { + if (_searchResultList == null) + { + return; + } + + _searchResultList.ItemSelected -= OnResultItemSelected; + _searchResultList.Unrealize(); + _searchResultList = null; + } + + void UpdateSearchResult() + { + if (SearchHandlerController == null) + return; + + if (!Element.ShowsResults) + { + DeinitializeSearchResultList(); + return; + } + + if (Control != null && + Control.IsFocused && SearchHandlerController.ListProxy != null && + SearchHandlerController.ListProxy.Count > 0 && + Element.ItemTemplate != null) + { + InitializeSearchResultList(); + if (_searchResultList != null) + { + _searchResultList.ItemTemplate = Element.ItemTemplate; + _searchResultList.ItemsSource = SearchHandlerController.ListProxy; + UpdateSearchResultLayout(); + } + } + else + { + DeinitializeSearchResultList(); + } + } + + void UpdateIsSearchEnabled() + { + if (Control == null) + return; + + Control.IsEnabled = Element.IsSearchEnabled; + } + + void UpdateQuery() + { + if (Control == null) + return; + + Control.Text = (Element.Query != null) ? Element.Query : ""; + } + + void UpdateFontAttributes() + { + if (Control == null) + return; + + Control.FontAttributes = Element.FontAttributes.ToNative(); + } + + void UpdateFontFamily() + { + if (Control == null) + return; + + Control.FontFamily = Element.FontFamily; + } + + void UpdateFontSize() + { + if (Control == null) + return; + + Control.FontSize = Element.FontSize; + } + + void UpdateBackgroundColor() + { + if (Control == null) + return; + + var color = Element.BackgroundColor.ToNativeEFL(); + Control.BackgroundColor = color == EColor.Default ? EColor.White : color; + } + + void UpdateTextColor() + { + if (Control == null) + return; + + Control.TextColor = Element.TextColor.ToNative(); + } + + void UpdateHorizontalTextAlignment() + { + if (Control == null) + return; + + Control.HorizontalTextAlignment = Element.HorizontalTextAlignment.ToNative(); + } + + void OnFocusChangedRequested(object sender, VisualElement.FocusRequestArgs e) + { + if (Control == null) + return; + + Control.SetFocus(e.Focus); + e.Result = true; + } + + void UpdateKeyboard() + { + if (Control == null) + return; + + Control.Keyboard = Element.Keyboard.ToNative(); + } + + void UpdatePlaceholder() + { + if (Control == null) + return; + + Control.Placeholder = Element.Placeholder; + } + void UpdatePlaceholderColor() + { + if (Control == null) + return; + + Control.PlaceholderColor = Element.PlaceholderColor.ToNative(); + } + + void OnFocused(object sender, EventArgs e) + { + if (Control == null) + return; + + Element.SetIsFocused(Control.IsFocused); + if (Control.IsFocused) + { + UpdateSearchResult(); + } + else + { + if (_searchResultList != null) + { + _searchResultList.Hide(); + } + Device.BeginInvokeOnMainThread(() => + { + Device.StartTimer(TimeSpan.FromMilliseconds(100), () => + { + DeinitializeSearchResultList(); + return false; + }); + }); + } + } + + void OnActivated(object sender, EventArgs e) + { + if (Control == null) + return; + + Control.HideInputPanel(); + (Element as ISearchHandlerController).QueryConfirmed(); + } + + void OnTextChanged(object sender, TTextChangedEventArgs e) + { + Element.SetValueCore(SearchHandler.QueryProperty, (sender as TSearchBar)?.Text); + } + + void UpdateSearchResultLayout() + { + if (_searchResultList != null && NativeView != null) + { + var bound = NativeView.Geometry; + bound.Y += NativeView.Geometry.Height; + _searchResultList.Geometry = bound; + _searchResultList.UpdateLayout(); + } + } + } +} diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/Tizen.UIExtensions/SearchBarExtensions.cs b/src/Controls/src/Core/Platform/Tizen/Shell/Tizen.UIExtensions/SearchBarExtensions.cs new file mode 100644 index 000000000000..c49eb6095309 --- /dev/null +++ b/src/Controls/src/Core/Platform/Tizen/Shell/Tizen.UIExtensions/SearchBarExtensions.cs @@ -0,0 +1,21 @@ +using Microsoft.Maui.Controls; +using TFontAttributes = Tizen.UIExtensions.Common.FontAttributes; + +namespace Tizen.UIExtensions.Shell +{ + public static class SearchBarExtensions + { + + public static TFontAttributes ToNative(this FontAttributes fontAttribute) + { + TFontAttributes attributes = TFontAttributes.None; + if (fontAttribute == FontAttributes.Italic) + attributes = attributes | TFontAttributes.Italic; + + if (fontAttribute == FontAttributes.Bold) + attributes = attributes | TFontAttributes.Bold; + + return attributes; + } + } +}