From 6507f7cadfda845ae04ddb447626e2210f653972 Mon Sep 17 00:00:00 2001 From: Tsanie Date: Tue, 10 Aug 2021 17:17:32 +0800 Subject: [PATCH] adjust UI --- Gallery.Share/App.cs | 8 +- Gallery.Share/AppShell.xaml | 156 ++--- Gallery.Share/AppShell.xaml.cs | 47 +- Gallery.Share/Gallery.Share.projitems | 62 +- Gallery.Share/Models/Item.cs | 11 - Gallery.Share/Resources/Converters.cs | 22 + Gallery.Share/Resources/Helper.cs | 2 + Gallery.Share/Resources/Theme/DarkTheme.cs | 9 +- Gallery.Share/Resources/Theme/LightTheme.cs | 9 +- Gallery.Share/Resources/Theme/Theme.cs | 10 +- Gallery.Share/Resources/UI/AdaptedPage.cs | 148 +++++ Gallery.Share/Resources/UI/CardView.cs | 44 ++ Gallery.Share/Resources/UI/Definition.cs | 8 + .../Resources/UI/GalleryCollectionPage.cs | 557 ++++++++++++++++++ Gallery.Share/Services/GalleryCollection.cs | 57 ++ Gallery.Share/Services/IDataStore.cs | 15 - Gallery.Share/Services/MockDataStore.cs | 60 -- Gallery.Share/ViewModels/AboutViewModel.cs | 18 - Gallery.Share/ViewModels/BaseViewModel.cs | 56 -- .../ViewModels/ItemDetailViewModel.cs | 57 -- Gallery.Share/ViewModels/ItemsViewModel.cs | 86 --- Gallery.Share/ViewModels/LoginViewModel.cs | 24 - Gallery.Share/ViewModels/NewItemViewModel.cs | 65 -- Gallery.Share/Views/AboutPage.xaml | 52 -- Gallery.Share/Views/AboutPage.xaml.cs | 31 - Gallery.Share/Views/GalleryPage.xaml | 31 + Gallery.Share/Views/GalleryPage.xaml.cs | 66 +++ Gallery.Share/Views/ItemDetailPage.xaml | 14 - Gallery.Share/Views/ItemDetailPage.xaml.cs | 15 - Gallery.Share/Views/ItemsPage.xaml | 44 -- Gallery.Share/Views/ItemsPage.xaml.cs | 33 -- Gallery.Share/Views/LoginPage.xaml | 14 - Gallery.Share/Views/LoginPage.xaml.cs | 21 - Gallery.Share/Views/NewItemPage.xaml | 22 - Gallery.Share/Views/NewItemPage.xaml.cs | 22 - Gallery.UI/CustomViews.cs | 8 + Gallery.UI/Extensions.cs | 54 ++ Gallery.UI/FlowLayout.cs | 275 +++++++++ Gallery.UI/Gallery.UI.projitems | 18 + Gallery.UI/Gallery.UI.shproj | 11 + Gallery.UI/OptionCells.cs | 157 +++++ Gallery.UI/RoundViews.cs | 63 ++ Gallery.Util/Extensions.cs | 44 -- Gallery.Util/Interface/IGallerySource.cs | 7 + Gallery.Util/Model/GalleryItem.cs | 35 +- Gallery.Util/NetHelper.cs | 134 +++++ Gallery.Util/Store.cs | 69 +++ .../AppIcon.appiconset/Contents.json | 131 ++-- .../AppIcon.appiconset/Icon1024.png | Bin 70429 -> 24921 bytes .../AppIcon.appiconset/Icon120.png | Bin 3773 -> 3741 bytes .../AppIcon.appiconset/Icon152.png | Bin 4750 -> 4827 bytes .../AppIcon.appiconset/Icon167.png | Bin 4692 -> 5397 bytes .../AppIcon.appiconset/Icon180.png | Bin 5192 -> 3299 bytes .../AppIcon.appiconset/Icon20.png | Bin 1313 -> 599 bytes .../AppIcon.appiconset/Icon29.png | Bin 845 -> 861 bytes .../AppIcon.appiconset/Icon40.png | Bin 1101 -> 582 bytes .../AppIcon.appiconset/Icon58.png | Bin 1761 -> 1654 bytes .../AppIcon.appiconset/Icon60.png | Bin 2537 -> 1681 bytes .../AppIcon.appiconset/Icon76.png | Bin 2332 -> 2053 bytes .../AppIcon.appiconset/Icon80.png | Bin 2454 -> 1100 bytes .../AppIcon.appiconset/Icon87.png | Bin 2758 -> 2317 bytes .../IconSource.imageset/Contents.json | 26 + .../IconSource.imageset/source-solid.png | Bin 0 -> 294 bytes .../IconSource.imageset/source-solid@2x.png | Bin 0 -> 432 bytes .../IconSource.imageset/source-solid@3x.png | Bin 0 -> 622 bytes .../IconSourceRegular.imageset/Contents.json | 26 + .../source-regular.png | Bin 0 -> 309 bytes .../source-regular@2x.png | Bin 0 -> 506 bytes .../source-regular@3x.png | Bin 0 -> 668 bytes .../IconYandere.imageset/Contents.json | 26 + .../IconYandere.imageset/yandere-solid.png | Bin 0 -> 268 bytes .../IconYandere.imageset/yandere-solid@2x.png | Bin 0 -> 408 bytes .../IconYandere.imageset/yandere-solid@3x.png | Bin 0 -> 512 bytes .../IconYandereRegular.imageset/Contents.json | 26 + .../yandere-regular.png | Bin 0 -> 265 bytes .../yandere-regular@2x.png | Bin 0 -> 407 bytes .../yandere-regular@3x.png | Bin 0 -> 538 bytes Gallery.iOS/Gallery.iOS.csproj | 96 +-- Gallery.iOS/Renderers/AppShellRenderer.cs | 93 +++ .../AppShellSection/AppAppearanceTracker.cs | 168 ++++++ .../AppShellSectionRootHeader.cs | 325 ++++++++++ Gallery.iOS/Renderers/BlurryPanelRenderer.cs | 87 +++ Gallery.iOS/Renderers/CardViewRenderer.cs | 51 ++ Gallery.iOS/Renderers/CircleImageRenderer.cs | 33 ++ Gallery.iOS/Renderers/OptionEntryRenderer.cs | 22 + Gallery.iOS/Renderers/OptionPickerRenderer.cs | 22 + Gallery.iOS/Renderers/RoundImageRenderer.cs | 56 ++ Gallery.iOS/Renderers/RoundLabelRenderer.cs | 56 ++ Gallery.iOS/Resources/LaunchScreen.storyboard | 8 +- Gallery.iOS/Resources/download.png | Bin 0 -> 1911 bytes Gallery.iOS/Resources/icon_about.png | Bin 518 -> 0 bytes Gallery.iOS/Resources/icon_about@2x.png | Bin 1137 -> 0 bytes Gallery.iOS/Resources/icon_about@3x.png | Bin 2019 -> 0 bytes Gallery.iOS/Resources/icon_feed.png | Bin 415 -> 0 bytes Gallery.iOS/Resources/icon_feed@2x.png | Bin 863 -> 0 bytes Gallery.iOS/Resources/icon_feed@3x.png | Bin 1400 -> 0 bytes Gallery.sln | 2 + .../Gallery.Danbooru/GallerySource.cs | 45 +- .../Gallery.Gelbooru/GallerySource.cs | 16 +- .../Gallery.Yandere/GallerySource.cs | 15 + 100 files changed, 3138 insertions(+), 963 deletions(-) delete mode 100644 Gallery.Share/Models/Item.cs create mode 100644 Gallery.Share/Resources/Converters.cs create mode 100644 Gallery.Share/Resources/UI/AdaptedPage.cs create mode 100644 Gallery.Share/Resources/UI/CardView.cs create mode 100644 Gallery.Share/Resources/UI/GalleryCollectionPage.cs create mode 100644 Gallery.Share/Services/GalleryCollection.cs delete mode 100644 Gallery.Share/Services/IDataStore.cs delete mode 100644 Gallery.Share/Services/MockDataStore.cs delete mode 100644 Gallery.Share/ViewModels/AboutViewModel.cs delete mode 100644 Gallery.Share/ViewModels/BaseViewModel.cs delete mode 100644 Gallery.Share/ViewModels/ItemDetailViewModel.cs delete mode 100644 Gallery.Share/ViewModels/ItemsViewModel.cs delete mode 100644 Gallery.Share/ViewModels/LoginViewModel.cs delete mode 100644 Gallery.Share/ViewModels/NewItemViewModel.cs delete mode 100644 Gallery.Share/Views/AboutPage.xaml delete mode 100644 Gallery.Share/Views/AboutPage.xaml.cs create mode 100644 Gallery.Share/Views/GalleryPage.xaml create mode 100644 Gallery.Share/Views/GalleryPage.xaml.cs delete mode 100644 Gallery.Share/Views/ItemDetailPage.xaml delete mode 100644 Gallery.Share/Views/ItemDetailPage.xaml.cs delete mode 100644 Gallery.Share/Views/ItemsPage.xaml delete mode 100644 Gallery.Share/Views/ItemsPage.xaml.cs delete mode 100644 Gallery.Share/Views/LoginPage.xaml delete mode 100644 Gallery.Share/Views/LoginPage.xaml.cs delete mode 100644 Gallery.Share/Views/NewItemPage.xaml delete mode 100644 Gallery.Share/Views/NewItemPage.xaml.cs create mode 100644 Gallery.UI/CustomViews.cs create mode 100644 Gallery.UI/Extensions.cs create mode 100644 Gallery.UI/FlowLayout.cs create mode 100644 Gallery.UI/Gallery.UI.projitems create mode 100644 Gallery.UI/Gallery.UI.shproj create mode 100644 Gallery.UI/OptionCells.cs create mode 100644 Gallery.UI/RoundViews.cs create mode 100644 Gallery.iOS/Assets.xcassets/IconSource.imageset/Contents.json create mode 100644 Gallery.iOS/Assets.xcassets/IconSource.imageset/source-solid.png create mode 100644 Gallery.iOS/Assets.xcassets/IconSource.imageset/source-solid@2x.png create mode 100644 Gallery.iOS/Assets.xcassets/IconSource.imageset/source-solid@3x.png create mode 100644 Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/Contents.json create mode 100644 Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/source-regular.png create mode 100644 Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/source-regular@2x.png create mode 100644 Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/source-regular@3x.png create mode 100644 Gallery.iOS/Assets.xcassets/IconYandere.imageset/Contents.json create mode 100644 Gallery.iOS/Assets.xcassets/IconYandere.imageset/yandere-solid.png create mode 100644 Gallery.iOS/Assets.xcassets/IconYandere.imageset/yandere-solid@2x.png create mode 100644 Gallery.iOS/Assets.xcassets/IconYandere.imageset/yandere-solid@3x.png create mode 100644 Gallery.iOS/Assets.xcassets/IconYandereRegular.imageset/Contents.json create mode 100644 Gallery.iOS/Assets.xcassets/IconYandereRegular.imageset/yandere-regular.png create mode 100644 Gallery.iOS/Assets.xcassets/IconYandereRegular.imageset/yandere-regular@2x.png create mode 100644 Gallery.iOS/Assets.xcassets/IconYandereRegular.imageset/yandere-regular@3x.png create mode 100644 Gallery.iOS/Renderers/AppShellRenderer.cs create mode 100644 Gallery.iOS/Renderers/AppShellSection/AppAppearanceTracker.cs create mode 100644 Gallery.iOS/Renderers/AppShellSection/AppShellSectionRootHeader.cs create mode 100644 Gallery.iOS/Renderers/BlurryPanelRenderer.cs create mode 100644 Gallery.iOS/Renderers/CardViewRenderer.cs create mode 100644 Gallery.iOS/Renderers/CircleImageRenderer.cs create mode 100644 Gallery.iOS/Renderers/OptionEntryRenderer.cs create mode 100644 Gallery.iOS/Renderers/OptionPickerRenderer.cs create mode 100644 Gallery.iOS/Renderers/RoundImageRenderer.cs create mode 100644 Gallery.iOS/Renderers/RoundLabelRenderer.cs create mode 100644 Gallery.iOS/Resources/download.png delete mode 100644 Gallery.iOS/Resources/icon_about.png delete mode 100644 Gallery.iOS/Resources/icon_about@2x.png delete mode 100644 Gallery.iOS/Resources/icon_about@3x.png delete mode 100644 Gallery.iOS/Resources/icon_feed.png delete mode 100644 Gallery.iOS/Resources/icon_feed@2x.png delete mode 100644 Gallery.iOS/Resources/icon_feed@3x.png diff --git a/Gallery.Share/App.cs b/Gallery.Share/App.cs index 106229b..7f5e265 100644 --- a/Gallery.Share/App.cs +++ b/Gallery.Share/App.cs @@ -6,6 +6,7 @@ using Gallery.Util; using Gallery.Resources.Theme; using System.Collections.Generic; using Gallery.Util.Interface; +using Gallery.Resources.UI; namespace Gallery { @@ -26,12 +27,15 @@ namespace Gallery Preferences.Set(Config.IsProxiedKey, true); Preferences.Set(Config.ProxyHostKey, "192.168.25.9"); Preferences.Set(Config.ProxyPortKey, 1081); - - DependencyService.Register(); } private void InitResource() { + foreach (var source in GallerySources) + { + source.InitDynamicResources(Definition.IconSolidFamily, LightTheme.Instance, DarkTheme.Instance); + } + var theme = AppInfo.RequestedTheme; SetTheme(theme, true); } diff --git a/Gallery.Share/AppShell.xaml b/Gallery.Share/AppShell.xaml index 44a59fe..77dd9ef 100644 --- a/Gallery.Share/AppShell.xaml +++ b/Gallery.Share/AppShell.xaml @@ -1,62 +1,34 @@ - - + xmlns:local="clr-namespace:Gallery" + xmlns:r="clr-namespace:Gallery.Resources" + xmlns:ui="clr-namespace:Gallery.Resources.UI" + xmlns:util="clr-namespace:Gallery.Util;assembly=Gallery.Util" + x:Class="Gallery.AppShell" + x:Name="appShell" + BackgroundColor="{DynamicResource NavigationColor}" + FlyoutBackgroundColor="{DynamicResource WindowColor}" + x:DataType="{x:Type local:AppShell}" + BindingContext="{x:Reference appShell}"> - - + --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + - - + + --> diff --git a/Gallery.Share/AppShell.xaml.cs b/Gallery.Share/AppShell.xaml.cs index 2553471..fff358e 100644 --- a/Gallery.Share/AppShell.xaml.cs +++ b/Gallery.Share/AppShell.xaml.cs @@ -1,4 +1,7 @@ using System; +using Gallery.Resources; +using Gallery.Resources.UI; +using Gallery.Util; using Gallery.Views; using Xamarin.Forms; @@ -6,16 +9,52 @@ namespace Gallery { public partial class AppShell : Shell { + public static new AppShell Current => Shell.Current as AppShell; + + public static Thickness NavigationBarOffset { get; private set; } + public static Thickness TotalBarOffset { get; private set; } + public AppShell() { InitializeComponent(); - Routing.RegisterRoute(nameof(ItemDetailPage), typeof(ItemDetailPage)); - Routing.RegisterRoute(nameof(NewItemPage), typeof(NewItemPage)); + +#if DEBUG + Log.Print($"folder: {Store.PersonalFolder}"); + Log.Print($"cache: {Store.CacheFolder}"); +#endif + InitFlyouts(); } - private async void OnMenuItemClicked(object sender, EventArgs e) + private void InitFlyouts() { - await Current.GoToAsync("//LoginPage"); + foreach (var source in App.GallerySources) + { + var s = source; + var tab = new Tab + { + Title = source.Name, + Route = source.Route, + Items = + { + new ShellContent + { + ContentTemplate = new DataTemplate(() => new GalleryPage(s)) + } + } + } + .DynamicResource(BaseShellItem.FlyoutIconProperty, source.FlyoutIconKey); + flyoutItems.Items.Add(tab); + } + } + + public void SetNavigationBarHeight(double height) + { + NavigationBarOffset = new Thickness(0, height, 0, 0); + } + + public void SetStatusBarHeight(double navigation, double height) + { + TotalBarOffset = new Thickness(0, navigation + height, 0, 0); } } } diff --git a/Gallery.Share/Gallery.Share.projitems b/Gallery.Share/Gallery.Share.projitems index b0715d4..44f58a0 100644 --- a/Gallery.Share/Gallery.Share.projitems +++ b/Gallery.Share/Gallery.Share.projitems @@ -10,30 +10,6 @@ - - - - - - - - - - - Views\AboutPage.xaml - - - Views\ItemDetailPage.xaml - - - Views\ItemsPage.xaml - - - Views\LoginPage.xaml - - - Views\NewItemPage.xaml - AppShell.xaml @@ -44,11 +20,17 @@ + + + + + + + GalleryPage.xaml + - - @@ -56,32 +38,14 @@ - - Designer - MSBuild:UpdateDesignTimeXaml - - - Designer - MSBuild:UpdateDesignTimeXaml - - - Designer - MSBuild:UpdateDesignTimeXaml - - - Designer - MSBuild:UpdateDesignTimeXaml - - - Designer - MSBuild:UpdateDesignTimeXaml - Designer MSBuild:UpdateDesignTimeXaml - - - + + Designer + MSBuild:UpdateDesignTimeXaml + + \ No newline at end of file diff --git a/Gallery.Share/Models/Item.cs b/Gallery.Share/Models/Item.cs deleted file mode 100644 index 098c8e8..0000000 --- a/Gallery.Share/Models/Item.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Gallery.Models -{ - public class Item - { - public string Id { get; set; } - public string Text { get; set; } - public string Description { get; set; } - } -} diff --git a/Gallery.Share/Resources/Converters.cs b/Gallery.Share/Resources/Converters.cs new file mode 100644 index 0000000..20be4a2 --- /dev/null +++ b/Gallery.Share/Resources/Converters.cs @@ -0,0 +1,22 @@ +using System; +using System.Globalization; +using Gallery.Resources.UI; +using Xamarin.Forms; + +namespace Gallery.Resources +{ + public class FavoriteIconConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value == null ? + Definition.IconLove : + Definition.IconCircleLove; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Gallery.Share/Resources/Helper.cs b/Gallery.Share/Resources/Helper.cs index b121f2d..c7f85fa 100644 --- a/Gallery.Share/Resources/Helper.cs +++ b/Gallery.Share/Resources/Helper.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Xml; using Gallery.Util; +using Xamarin.Forms; using Xamarin.Forms.Xaml; namespace Gallery.Resources @@ -84,6 +85,7 @@ namespace Gallery.Resources } } + [ContentProperty(nameof(Text))] public class TextExtension : IMarkupExtension { public string Text { get; set; } diff --git a/Gallery.Share/Resources/Theme/DarkTheme.cs b/Gallery.Share/Resources/Theme/DarkTheme.cs index 1c0fab2..d1d4e95 100644 --- a/Gallery.Share/Resources/Theme/DarkTheme.cs +++ b/Gallery.Share/Resources/Theme/DarkTheme.cs @@ -27,9 +27,16 @@ namespace Gallery.Resources.Theme private void InitColors() { Add(StatusBarStyle, StatusBarStyles.WhiteText); + Add(WindowColor, Color.Black); + Add(TintColor, Color.FromRgb(0x94, 0x95, 0x9a)); + Add(TextColor, Color.White); + Add(SubTextColor, Color.LightGray); + Add(CardBackgroundColor, Color.FromRgb(0x33, 0x33, 0x33)); Add(NavigationColor, Color.FromRgb(0x11, 0x11, 0x11)); + Add(NavigationSelectedColor, Color.FromRgb(0x22, 0x22, 0x22)); + Add(OptionBackColor, Color.Black); + Add(OptionTintColor, Color.FromRgb(0x11, 0x11, 0x11)); - Add(Primary, Color.FromRgb(33, 150, 243)); } } } diff --git a/Gallery.Share/Resources/Theme/LightTheme.cs b/Gallery.Share/Resources/Theme/LightTheme.cs index 1dc9e3a..2e23760 100644 --- a/Gallery.Share/Resources/Theme/LightTheme.cs +++ b/Gallery.Share/Resources/Theme/LightTheme.cs @@ -27,9 +27,16 @@ namespace Gallery.Resources.Theme private void InitColors() { Add(StatusBarStyle, StatusBarStyles.DarkText); + Add(WindowColor, Color.White); + Add(TintColor, Color.FromRgb(0x87, 0x87, 0x8b)); // 0x7f, 0x99, 0xc6 + Add(TextColor, Color.Black); + Add(SubTextColor, Color.DimGray); + Add(CardBackgroundColor, Color.FromRgb(0xf3, 0xf3, 0xf3)); Add(NavigationColor, Color.FromRgb(0xf0, 0xf0, 0xf0)); + Add(NavigationSelectedColor, Color.LightGray); + Add(OptionBackColor, Color.FromRgb(0xf0, 0xf0, 0xf0)); + Add(OptionTintColor, Color.White); - Add(Primary, Color.FromRgb(33, 150, 243)); } } } diff --git a/Gallery.Share/Resources/Theme/Theme.cs b/Gallery.Share/Resources/Theme/Theme.cs index fc20c58..bb21106 100644 --- a/Gallery.Share/Resources/Theme/Theme.cs +++ b/Gallery.Share/Resources/Theme/Theme.cs @@ -6,7 +6,15 @@ namespace Gallery.Resources.Theme public abstract class Theme : ResourceDictionary { public const string StatusBarStyle = nameof(StatusBarStyle); + public const string WindowColor = nameof(WindowColor); + public const string TintColor = nameof(TintColor); + public const string TextColor = nameof(TextColor); + public const string SubTextColor = nameof(SubTextColor); + public const string CardBackgroundColor = nameof(CardBackgroundColor); public const string NavigationColor = nameof(NavigationColor); + public const string NavigationSelectedColor = nameof(NavigationSelectedColor); + public const string OptionBackColor = nameof(OptionBackColor); + public const string OptionTintColor = nameof(OptionTintColor); public const string IconLightFamily = nameof(IconLightFamily); public const string IconRegularFamily = nameof(IconRegularFamily); @@ -16,8 +24,6 @@ namespace Gallery.Resources.Theme public const string IconClose = nameof(IconClose); public const string FontIconRefresh = nameof(FontIconRefresh); - public const string Primary = nameof(Primary); - protected void InitResources() { Add(IconLightFamily, Definition.IconLightFamily); diff --git a/Gallery.Share/Resources/UI/AdaptedPage.cs b/Gallery.Share/Resources/UI/AdaptedPage.cs new file mode 100644 index 0000000..b620665 --- /dev/null +++ b/Gallery.Share/Resources/UI/AdaptedPage.cs @@ -0,0 +1,148 @@ +using System; +using Gallery.Services; +using Gallery.Util; +using Xamarin.Essentials; +using Xamarin.Forms; + +namespace Gallery.Resources.UI +{ + public class AdaptedPage : ContentPage + { + public static readonly BindableProperty TopMarginProperty = BindableProperty.Create(nameof(TopMargin), typeof(Thickness), typeof(AdaptedPage)); + + public Thickness TopMargin + { + get => (Thickness)GetValue(TopMarginProperty); + set => SetValue(TopMarginProperty, value); + } + + public event EventHandler Load; + public event EventHandler Unload; + + protected static readonly bool isPhone = DeviceInfo.Idiom == DeviceIdiom.Phone; + + public AdaptedPage() + { + SetDynamicResource(Screen.StatusBarStyleProperty, Theme.Theme.StatusBarStyle); + Shell.SetNavBarHasShadow(this, true); + } + + public virtual void OnLoad() => Load?.Invoke(this, EventArgs.Empty); + + public virtual void OnUnload() => Unload?.Invoke(this, EventArgs.Empty); + + public virtual void OnOrientationChanged(bool landscape) + { + var old = TopMargin; + Thickness @new; + if (Definition.IsFullscreenDevice) + { + @new = landscape ? + AppShell.NavigationBarOffset : + AppShell.TotalBarOffset; + } + else if (isPhone) + { + @new = landscape ? + Definition.TopOffset32 : + AppShell.TotalBarOffset; + } + else + { + // iPad + @new = AppShell.TotalBarOffset; + } + if (old != @new) + { + TopMargin = @new; + OnTopMarginChanged(old, @new); + } + } + + protected virtual void OnTopMarginChanged(Thickness old, Thickness @new) { } + + protected override void OnSizeAllocated(double width, double height) + { + base.OnSizeAllocated(width, height); + OnOrientationChanged(width > height); + } + + protected void AnimateToMargin(View element, Thickness margin, bool animate = true) + { + var m = margin; + var start = element.Margin.Top - m.Top; + element.Margin = m; + element.CancelAnimations(); + if (start > 0 && animate) + { + element.Animate("margin", top => + { + element.TranslationY = top; + }, + start, 0, +#if DEBUG + length: 500, +#else + length: 300, +#endif + easing: Easing.SinInOut, + finished: (v, r) => + { + element.TranslationY = 0; + }); + } + else if (element.TranslationY != 0) + { + element.TranslationY = 0; + } + } + + protected void Start(Action action) + { + if (Tap.IsBusy) + { + Log.Error($"{GetType()}.tap", "gesture recognizer is now busy..."); + return; + } + using (Tap.Start()) + { + action(); + } + } + + private class Tap : IDisposable + { + public static bool IsBusy + { + get + { + lock (sync) + { + return instance?.isBusy == true; + } + } + } + + private static readonly object sync = new(); + private static readonly Tap instance = new(); + + private Tap() { } + + public static Tap Start() + { + lock (sync) + { + instance.isBusy = true; + } + return instance; + } + + private bool isBusy = false; + + public void Dispose() + { + isBusy = false; + } + } + } +} diff --git a/Gallery.Share/Resources/UI/CardView.cs b/Gallery.Share/Resources/UI/CardView.cs new file mode 100644 index 0000000..9a905c6 --- /dev/null +++ b/Gallery.Share/Resources/UI/CardView.cs @@ -0,0 +1,44 @@ +using Gallery.Util.Model; +using Xamarin.Forms; + +namespace Gallery.Resources.UI +{ + public class CardView : ContentView + { + public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(float), typeof(CardView)); + public static readonly BindableProperty ShadowColorProperty = BindableProperty.Create(nameof(ShadowColor), typeof(Color), typeof(CardView)); + public static readonly BindableProperty ShadowRadiusProperty = BindableProperty.Create(nameof(ShadowRadius), typeof(float), typeof(CardView), 3f); + public static readonly BindableProperty ShadowOffsetProperty = BindableProperty.Create(nameof(ShadowOffset), typeof(Size), typeof(CardView)); + + public float CornerRadius + { + get => (float)GetValue(CornerRadiusProperty); + set => SetValue(CornerRadiusProperty, value); + } + public Color ShadowColor + { + get => (Color)GetValue(ShadowColorProperty); + set => SetValue(ShadowColorProperty, value); + } + public float ShadowRadius + { + get => (float)GetValue(ShadowRadiusProperty); + set => SetValue(ShadowRadiusProperty, value); + } + public Size ShadowOffset + { + get => (Size)GetValue(ShadowOffsetProperty); + set => SetValue(ShadowOffsetProperty, value); + } + + protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint) + { + if (BindingContext is GalleryItem item && + item.Width > 0 && item.ImageHeight.IsAuto) + { + item.ImageHeight = widthConstraint * item.Height / item.Width; + } + return base.OnMeasure(widthConstraint, heightConstraint); + } + } +} diff --git a/Gallery.Share/Resources/UI/Definition.cs b/Gallery.Share/Resources/UI/Definition.cs index 9e85ad1..8cd30fa 100644 --- a/Gallery.Share/Resources/UI/Definition.cs +++ b/Gallery.Share/Resources/UI/Definition.cs @@ -10,6 +10,12 @@ namespace Gallery.Resources.UI public const double FontSizeTitle = 18.0; public static readonly Thickness ScreenBottomPadding; + public static readonly Thickness TopOffset32 = new(0, 32, 0, 0); + public static readonly Color ColorLightShadow = Color.FromRgba(0, 0, 0, 0x20); + public static readonly Color ColorRedBackground = Color.FromRgb(0xfd, 0x43, 0x63); + public static readonly Color ColorDownloadBackground = Color.FromRgb(0xd7, 0xd9, 0xe0); + public static readonly ImageSource DownloadBackground = ImageSource.FromFile("download.png"); + public static readonly double FontSizeSmall = Device.GetNamedSize(NamedSize.Small, typeof(Label)); #if __IOS__ public const string IconLightFamily = "FontAwesome5Pro-Light"; @@ -26,6 +32,8 @@ namespace Gallery.Resources.UI #endif public const string IconRefresh = "\uf2f9"; + public const string IconLove = "\uf004"; + public const string IconCircleLove = "\uf4c7"; public const string IconClose = "\uf057"; static Definition() diff --git a/Gallery.Share/Resources/UI/GalleryCollectionPage.cs b/Gallery.Share/Resources/UI/GalleryCollectionPage.cs new file mode 100644 index 0000000..06202ac --- /dev/null +++ b/Gallery.Share/Resources/UI/GalleryCollectionPage.cs @@ -0,0 +1,557 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Gallery.Services; +using Gallery.Util; +using Gallery.Util.Interface; +using Gallery.Util.Model; +using Xamarin.Forms; + +namespace Gallery.Resources.UI +{ + public abstract class GalleryCollectionPage : GalleryScrollableCollectionPage + { + protected readonly IGallerySource source; + + public GalleryCollectionPage(IGallerySource source) + { + this.source = source; + } + } + + public interface IGalleryCollectionPage + { + GalleryCollection GalleryCollection { get; set; } + } + + public abstract class GalleryCollectionPage : AdaptedPage, IGalleryCollectionPage + { + const int EXPIRED_MINUTES = 5; + + protected const double loadingOffset = -40; + + public static readonly BindableProperty GalleryProperty = BindableProperty.Create(nameof(Gallery), typeof(GalleryCollection), typeof(GalleryCollectionPage)); + public static readonly BindableProperty ColumnsProperty = BindableProperty.Create(nameof(Columns), typeof(int), typeof(GalleryCollectionPage), + defaultValue: 2); + public static readonly BindableProperty IsLoadingProperty = BindableProperty.Create(nameof(IsLoading), typeof(bool), typeof(GalleryCollectionPage), + defaultValue: true); + public static readonly BindableProperty IsBottomLoadingProperty = BindableProperty.Create(nameof(IsBottomLoading), typeof(bool), typeof(GalleryCollectionPage)); + + public GalleryCollection Gallery + { + get => (GalleryCollection)GetValue(GalleryProperty); + set => SetValue(GalleryProperty, value); + } + public int Columns + { + get => (int)GetValue(ColumnsProperty); + set => SetValue(ColumnsProperty, value); + } + public bool IsLoading + { + get => (bool)GetValue(IsLoadingProperty); + set => SetValue(IsLoadingProperty, value); + } + public bool IsBottomLoading + { + get => (bool)GetValue(IsBottomLoadingProperty); + set => SetValue(IsBottomLoadingProperty, value); + } + + public GalleryCollection GalleryCollection { get; set; } + + protected virtual ActivityIndicator LoadingIndicator => null; + protected virtual double IndicatorMarginTop => 16; + + protected bool Expired => lastUpdated == default || (DateTime.Now - lastUpdated).TotalMinutes > EXPIRED_MINUTES; + + protected readonly Command commandGalleryItemTapped; + protected DateTime lastUpdated; + protected double topOffset; + protected string lastError; + + private readonly object sync = new(); + private readonly Stack tasks = new(); + private T galleryData; + + public GalleryCollectionPage() + { + commandGalleryItemTapped = new Command(OnGalleryItemTapped); + } + + private void OnGalleryItemTapped(GalleryItem item) + { + if (item == null) + { + return; + } + //Start(async () => + //{ + // var page = new GalleryItemPage(item); + // await Navigation.PushAsync(page); + //}); + } + + public override void OnUnload() + { + lock (sync) + { + while (tasks.TryPop(out var task)) + { + if (task != null) + { + task.Dispose(); + } + } + } + InvalidateCollection(); + Gallery = null; + lastUpdated = default; + } + + protected override void OnAppearing() + { + base.OnAppearing(); + + if (lastUpdated == default) + { + StartLoading(); + } + } + +#if __IOS__ + public override void OnOrientationChanged(bool landscape) + { + base.OnOrientationChanged(landscape); + + if (Definition.IsFullscreenDevice) + { + topOffset = landscape ? + AppShell.NavigationBarOffset.Top : + AppShell.TotalBarOffset.Top; + } + else if (isPhone) + { + topOffset = landscape ? + Definition.TopOffset32.Top : + AppShell.TotalBarOffset.Top; + } + else + { + // iPad + topOffset = AppShell.TotalBarOffset.Top; + } + } +#endif + + protected override void OnSizeAllocated(double width, double height) + { + base.OnSizeAllocated(width, height); + + int columns; + if (width > height) + { + columns = isPhone ? 4 : 6; + } + else + { + columns = isPhone ? 2 : 4; + } + if (Columns != columns) + { + Columns = columns; +#if DEBUG + Log.Print($"changing columns to {columns}"); +#endif + } + } + + protected abstract Task DoloadGalleryData(bool force); + protected abstract IEnumerable DoGetGalleryList(T data, out int tag); + + protected virtual GalleryCollection FilterGalleryCollection(GalleryCollection collection, bool bottom) + { + GalleryCollection = collection; + return collection; + } + + protected void InvalidateCollection() + { + var collection = GalleryCollection; + if (collection != null) + { + collection.Running = false; + GalleryCollection = null; + } + } + + protected virtual void StartLoading(bool force = false, bool isBottom = false) + { + if (force || Expired) + { + var indicator = LoadingIndicator; + if (indicator == null || isBottom) + { + if (isBottom) + { + IsBottomLoading = true; + } + else + { + InvalidateCollection(); + IsLoading = true; + } +#if __IOS__ + Device.StartTimer(TimeSpan.FromMilliseconds(48), () => +#else + Device.StartTimer(TimeSpan.FromMilliseconds(150), () => +#endif + { + _ = DoloadGallerySource(force, isBottom); + return false; + }); + } + else + { + InvalidateCollection(); + IsLoading = true; + + var offset = 16 - IndicatorMarginTop; + indicator.CancelAnimations(); + indicator.Animate("margin", top => + { + indicator.Margin = new Thickness(0, top, 0, offset); + }, + loadingOffset - offset, 16 - offset, + easing: Easing.CubicOut, + finished: (v, r) => + { + _ = DoloadGallerySource(force, isBottom); + }); + } + } + } + + protected virtual void DoGalleryLoaded(GalleryCollection collection, bool bottom) + { + collection = FilterGalleryCollection(collection, bottom); + + var indicator = LoadingIndicator; + if (indicator == null || bottom) + { + IsLoading = false; + IsBottomLoading = false; +#if __IOS__ + Device.StartTimer(TimeSpan.FromMilliseconds(48), () => +#else + Device.StartTimer(TimeSpan.FromMilliseconds(150), () => +#endif + { + Gallery = collection; + return false; + }); + } + else + { + var offset = 16 - IndicatorMarginTop; + indicator.CancelAnimations(); + indicator.Animate("margin", top => + { + indicator.Margin = new Thickness(0, top, 0, offset); + }, + 16 - offset, loadingOffset - offset, + easing: Easing.CubicIn, + finished: (v, r) => + { + indicator.Margin = new Thickness(0, v, 0, offset); + IsLoading = false; + IsBottomLoading = false; +#if __IOS__ + Device.StartTimer(TimeSpan.FromMilliseconds(48), () => +#else + Device.StartTimer(TimeSpan.FromMilliseconds(150), () => +#endif + { + Gallery = collection; + return false; + }); + }); + } + } + + protected async Task ScrollToTopAsync(ScrollView scrollView) + { + if (scrollView.ScrollY > -topOffset) + { +#if __IOS__ + await scrollView.ScrollToAsync(scrollView.ScrollX, -topOffset, true); +#else + await scrollView.ScrollToAsync(0, -topOffset, false); +#endif + } + } + + protected DataTemplate GetCardViewTemplate(string titleBinding = null) + { + return new DataTemplate(() => + { + var image = new RoundImage + { + BackgroundColor = Definition.ColorDownloadBackground, + CornerRadius = 10, + CornerMasks = CornerMask.Top, + HorizontalOptions = LayoutOptions.Fill, + Aspect = Aspect.AspectFill, + GestureRecognizers = + { + new TapGestureRecognizer + { + Command = commandGalleryItemTapped + } + .Binding(TapGestureRecognizer.CommandParameterProperty, ".") + } + } + .Binding(Image.SourceProperty, nameof(GalleryItem.PreviewImage)); + + var title = new Label + { + Padding = new Thickness(8, 2), + HorizontalOptions = LayoutOptions.FillAndExpand, + VerticalOptions = LayoutOptions.Center, + LineBreakMode = LineBreakMode.TailTruncation, + FontSize = Definition.FontSizeSmall + } + .DynamicResource(Label.TextColorProperty, Theme.Theme.TextColor); + + var favorite = new Label + { + WidthRequest = 26, + HorizontalOptions = LayoutOptions.End, + HorizontalTextAlignment = TextAlignment.End, + VerticalOptions = LayoutOptions.Center, + FontSize = Definition.FontSizeSmall, + TextColor = Definition.ColorRedBackground, + IsVisible = false + } + .Binding(Label.TextProperty, nameof(GalleryItem.BookmarkId), converter: new FavoriteIconConverter()) + .Binding(IsVisibleProperty, nameof(GalleryItem.IsFavorite)) + .DynamicResource(Label.FontFamilyProperty, Theme.Theme.IconSolidFamily); + + return new CardView + { + Padding = 0, + Margin = 0, + CornerRadius = 10, + ShadowColor = Definition.ColorLightShadow, + ShadowOffset = new Size(1, 1), + Content = new Grid + { + HorizontalOptions = LayoutOptions.Fill, + RowSpacing = 0, + RowDefinitions = + { + new RowDefinition().Binding(RowDefinition.HeightProperty, nameof(GalleryItem.ImageHeight)), + new RowDefinition { Height = 30 } + }, + Children = + { + image, + new Grid + { + ColumnDefinitions = + { + new ColumnDefinition(), + new ColumnDefinition { Width = 20 } + }, + VerticalOptions = LayoutOptions.Center, + Padding = new Thickness(0, 0, 8, 0), + Children = + { + title.Binding(Label.TextProperty, titleBinding ?? nameof(GalleryItem.TagDescription)), + favorite.GridColumn(1) + } + } + .GridRow(1) + } + } + } + .DynamicResource(BackgroundColorProperty, Theme.Theme.CardBackgroundColor); + }); + } + + protected async Task DoloadGallerySource(bool force = false, bool bottom = false) + { +#if DEBUG + Log.Print($"start loading data, force: {force}"); +#endif + galleryData = await DoloadGalleryData(force); + if (galleryData == null) + { + Log.Error("gallery.load", "failed to load gallery data."); + return; + } + if (force) + { + lastUpdated = DateTime.Now; + } + + var data = DoGetGalleryList(galleryData, out int tag).Where(i => i != null); + var collection = new GalleryCollection(data); + foreach (var item in collection) + { + if (item.PreviewImage == null) + { + var image = await Store.LoadPreviewImage(item.PreviewUrl, false); + if (image != null) + { + item.PreviewImage = image; + } + } + } + + DoGalleryLoaded(collection, bottom); + DoloadImages(collection, tag); + } + + private void DoloadImages(GalleryCollection collection, int tag) + { + lock (sync) + { + if (tasks.TryPeek(out var peek)) + { + if (peek != null && peek.TagIndex >= tag) + { + Log.Print($"tasks expired ({tasks.Count}, peek: {peek.TagIndex}, now: {tag}, will be disposed."); + while (tasks.TryPop(out var t)) + { + t?.Dispose(); + } + } + } + } + var list = collection.Where(i => i.PreviewImage == null).ToArray(); + var task = ParallelTask.Start("collection.load", 0, list.Length, 2, i => + { + if (!collection.Running) + { + return false; + } + var item = list[i]; + if (item.PreviewImage == null && item.PreviewUrl != null) + { + item.PreviewImage = Definition.DownloadBackground; + var image = Store.LoadPreviewImage(item.PreviewUrl, true, force: true).Result; + if (image != null) + { + item.PreviewImage = image; + } + } + return true; + }, tagIndex: tag); + + if (task != null) + { + lock (sync) + { + tasks.Push(task); + } + } + } + } + + public abstract class GalleryScrollableCollectionPage : GalleryCollectionPage + { + protected const int SCROLL_OFFSET = 33; + protected ScrollDirection scrollDirection = ScrollDirection.Stop; + protected double lastScrollY = double.MinValue; + + private double lastRefreshY = double.MinValue; + private double offset; + + protected bool IsScrollingDown(double y) + { + if (y > lastScrollY) + { + if (scrollDirection != ScrollDirection.Down) + { + scrollDirection = ScrollDirection.Down; + } + return true; + } + else + { + if (scrollDirection != ScrollDirection.Up) + { + scrollDirection = ScrollDirection.Up; + } + return false; + } + } + + protected void SetOffset(double off) + { + offset = off; + } + + protected abstract bool CheckRefresh(); + + protected override void StartLoading(bool force = false, bool isBottom = false) + { + if (!isBottom) + { + lastRefreshY = double.MinValue; + } + base.StartLoading(force, isBottom); + } + + protected override GalleryCollection FilterGalleryCollection(GalleryCollection collection, bool bottom) + { + var now = GalleryCollection; + if (now == null) + { + now = collection; + GalleryCollection = now; + } + else + { + now.AddRange(collection); + } + return now; + } + + protected void OnScrolled(double y) + { + lastScrollY = y; + if (scrollDirection == ScrollDirection.Up) + { + return; + } + if (y > 0 && offset > 0 && y - topOffset > offset) + { + if (IsLoading || IsBottomLoading) + { + return; + } + if (y - lastRefreshY > 200) + { + if (CheckRefresh()) + { + lastRefreshY = y; +#if DEBUG + Log.Print("start to load next page"); +#endif + StartLoading(true, true); + } + } + } + } + } + + public enum ScrollDirection + { + Stop, + Up, + Down + } +} diff --git a/Gallery.Share/Services/GalleryCollection.cs b/Gallery.Share/Services/GalleryCollection.cs new file mode 100644 index 0000000..940c9b2 --- /dev/null +++ b/Gallery.Share/Services/GalleryCollection.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using Gallery.Resources.UI; +using Gallery.Util.Model; +using Xamarin.Essentials; + +namespace Gallery.Services +{ + public class GalleryCollection : List, ICollectionChanged + { + private static GalleryCollection empty; + + public static GalleryCollection Empty + { + get + { + if (empty == null) + { + empty = new GalleryCollection(); + } + return empty; + } + } + + public event EventHandler CollectionChanged; + + public bool Running { get; set; } + + public GalleryCollection() : base() + { + Running = true; + } + + public GalleryCollection(IEnumerable gallery) : base(gallery) + { + Running = true; + } + + public void AddRange(List items) + { + var e = new CollectionChangedEventArgs + { + NewStartingIndex = Count, + NewItems = items + }; + base.AddRange(items); + if (MainThread.IsMainThread) + { + CollectionChanged?.Invoke(this, e); + } + else + { + MainThread.BeginInvokeOnMainThread(() => CollectionChanged?.Invoke(this, e)); + } + } + } +} diff --git a/Gallery.Share/Services/IDataStore.cs b/Gallery.Share/Services/IDataStore.cs deleted file mode 100644 index efed17d..0000000 --- a/Gallery.Share/Services/IDataStore.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Gallery.Services -{ - public interface IDataStore - { - Task AddItemAsync(T item); - Task UpdateItemAsync(T item); - Task DeleteItemAsync(string id); - Task GetItemAsync(string id); - Task> GetItemsAsync(bool forceRefresh = false); - } -} diff --git a/Gallery.Share/Services/MockDataStore.cs b/Gallery.Share/Services/MockDataStore.cs deleted file mode 100644 index fc5ca91..0000000 --- a/Gallery.Share/Services/MockDataStore.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Gallery.Models; - -namespace Gallery.Services -{ - public class MockDataStore : IDataStore - { - readonly List items; - - public MockDataStore() - { - items = new List() - { - new Item { Id = Guid.NewGuid().ToString(), Text = "First item", Description="This is an item description." }, - new Item { Id = Guid.NewGuid().ToString(), Text = "Second item", Description="This is an item description." }, - new Item { Id = Guid.NewGuid().ToString(), Text = "Third item", Description="This is an item description." }, - new Item { Id = Guid.NewGuid().ToString(), Text = "Fourth item", Description="This is an item description." }, - new Item { Id = Guid.NewGuid().ToString(), Text = "Fifth item", Description="This is an item description." }, - new Item { Id = Guid.NewGuid().ToString(), Text = "Sixth item", Description="This is an item description." } - }; - } - - public async Task AddItemAsync(Item item) - { - items.Add(item); - - return await Task.FromResult(true); - } - - public async Task UpdateItemAsync(Item item) - { - var oldItem = items.Where((Item arg) => arg.Id == item.Id).FirstOrDefault(); - items.Remove(oldItem); - items.Add(item); - - return await Task.FromResult(true); - } - - public async Task DeleteItemAsync(string id) - { - var oldItem = items.Where((Item arg) => arg.Id == id).FirstOrDefault(); - items.Remove(oldItem); - - return await Task.FromResult(true); - } - - public async Task GetItemAsync(string id) - { - return await Task.FromResult(items.FirstOrDefault(s => s.Id == id)); - } - - public async Task> GetItemsAsync(bool forceRefresh = false) - { - return await Task.FromResult(items); - } - } -} diff --git a/Gallery.Share/ViewModels/AboutViewModel.cs b/Gallery.Share/ViewModels/AboutViewModel.cs deleted file mode 100644 index 1fa890d..0000000 --- a/Gallery.Share/ViewModels/AboutViewModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Windows.Input; -using Xamarin.Essentials; -using Xamarin.Forms; - -namespace Gallery.ViewModels -{ - public class AboutViewModel : BaseViewModel - { - public AboutViewModel() - { - Title = "About"; - OpenWebCommand = new Command(async () => await Browser.OpenAsync("https://aka.ms/xamarin-quickstart")); - } - - public ICommand OpenWebCommand { get; } - } -} diff --git a/Gallery.Share/ViewModels/BaseViewModel.cs b/Gallery.Share/ViewModels/BaseViewModel.cs deleted file mode 100644 index 04c6118..0000000 --- a/Gallery.Share/ViewModels/BaseViewModel.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Runtime.CompilerServices; - -using Xamarin.Forms; - -using Gallery.Models; -using Gallery.Services; - -namespace Gallery.ViewModels -{ - public class BaseViewModel : INotifyPropertyChanged - { - public IDataStore DataStore => DependencyService.Get>(); - - bool isBusy = false; - public bool IsBusy - { - get { return isBusy; } - set { SetProperty(ref isBusy, value); } - } - - string title = string.Empty; - public string Title - { - get { return title; } - set { SetProperty(ref title, value); } - } - - protected bool SetProperty(ref T backingStore, T value, - [CallerMemberName] string propertyName = "", - Action onChanged = null) - { - if (EqualityComparer.Default.Equals(backingStore, value)) - return false; - - backingStore = value; - onChanged?.Invoke(); - OnPropertyChanged(propertyName); - return true; - } - - #region INotifyPropertyChanged - public event PropertyChangedEventHandler PropertyChanged; - protected void OnPropertyChanged([CallerMemberName] string propertyName = "") - { - var changed = PropertyChanged; - if (changed == null) - return; - - changed.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - #endregion - } -} diff --git a/Gallery.Share/ViewModels/ItemDetailViewModel.cs b/Gallery.Share/ViewModels/ItemDetailViewModel.cs deleted file mode 100644 index e4c7236..0000000 --- a/Gallery.Share/ViewModels/ItemDetailViewModel.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using Gallery.Models; -using Xamarin.Forms; - -namespace Gallery.ViewModels -{ - [QueryProperty(nameof(ItemId), nameof(ItemId))] - public class ItemDetailViewModel : BaseViewModel - { - private string itemId; - private string text; - private string description; - public string Id { get; set; } - - public string Text - { - get => text; - set => SetProperty(ref text, value); - } - - public string Description - { - get => description; - set => SetProperty(ref description, value); - } - - public string ItemId - { - get - { - return itemId; - } - set - { - itemId = value; - LoadItemId(value); - } - } - - public async void LoadItemId(string itemId) - { - try - { - var item = await DataStore.GetItemAsync(itemId); - Id = item.Id; - Text = item.Text; - Description = item.Description; - } - catch (Exception) - { - Debug.WriteLine("Failed to Load Item"); - } - } - } -} diff --git a/Gallery.Share/ViewModels/ItemsViewModel.cs b/Gallery.Share/ViewModels/ItemsViewModel.cs deleted file mode 100644 index 8ae6361..0000000 --- a/Gallery.Share/ViewModels/ItemsViewModel.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Threading.Tasks; - -using Xamarin.Forms; - -using Gallery.Models; -using Gallery.Views; - -namespace Gallery.ViewModels -{ - public class ItemsViewModel : BaseViewModel - { - private Item _selectedItem; - - public ObservableCollection Items { get; } - public Command LoadItemsCommand { get; } - public Command AddItemCommand { get; } - public Command ItemTapped { get; } - - public ItemsViewModel() - { - Title = "Browse"; - Items = new ObservableCollection(); - LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand()); - - ItemTapped = new Command(OnItemSelected); - - AddItemCommand = new Command(OnAddItem); - } - - async Task ExecuteLoadItemsCommand() - { - IsBusy = true; - - try - { - Items.Clear(); - var items = await DataStore.GetItemsAsync(true); - foreach (var item in items) - { - Items.Add(item); - } - } - catch (Exception ex) - { - Debug.WriteLine(ex); - } - finally - { - IsBusy = false; - } - } - - public void OnAppearing() - { - IsBusy = true; - SelectedItem = null; - } - - public Item SelectedItem - { - get => _selectedItem; - set - { - SetProperty(ref _selectedItem, value); - OnItemSelected(value); - } - } - - private async void OnAddItem(object obj) - { - await Shell.Current.GoToAsync(nameof(NewItemPage)); - } - - async void OnItemSelected(Item item) - { - if (item == null) - return; - - // This will push the ItemDetailPage onto the navigation stack - await Shell.Current.GoToAsync($"{nameof(ItemDetailPage)}?{nameof(ItemDetailViewModel.ItemId)}={item.Id}"); - } - } -} diff --git a/Gallery.Share/ViewModels/LoginViewModel.cs b/Gallery.Share/ViewModels/LoginViewModel.cs deleted file mode 100644 index 1aa8796..0000000 --- a/Gallery.Share/ViewModels/LoginViewModel.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Gallery.Views; -using System; -using System.Collections.Generic; -using System.Text; -using Xamarin.Forms; - -namespace Gallery.ViewModels -{ - public class LoginViewModel : BaseViewModel - { - public Command LoginCommand { get; } - - public LoginViewModel() - { - LoginCommand = new Command(OnLoginClicked); - } - - private async void OnLoginClicked(object obj) - { - // Prefixing with `//` switches to a different navigation stack instead of pushing to the active one - await Shell.Current.GoToAsync($"//{nameof(AboutPage)}"); - } - } -} diff --git a/Gallery.Share/ViewModels/NewItemViewModel.cs b/Gallery.Share/ViewModels/NewItemViewModel.cs deleted file mode 100644 index 6ba6069..0000000 --- a/Gallery.Share/ViewModels/NewItemViewModel.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Windows.Input; -using Gallery.Models; -using Xamarin.Forms; - -namespace Gallery.ViewModels -{ - public class NewItemViewModel : BaseViewModel - { - private string text; - private string description; - - public NewItemViewModel() - { - SaveCommand = new Command(OnSave, ValidateSave); - CancelCommand = new Command(OnCancel); - this.PropertyChanged += - (_, __) => SaveCommand.ChangeCanExecute(); - } - - private bool ValidateSave() - { - return !String.IsNullOrWhiteSpace(text) - && !String.IsNullOrWhiteSpace(description); - } - - public string Text - { - get => text; - set => SetProperty(ref text, value); - } - - public string Description - { - get => description; - set => SetProperty(ref description, value); - } - - public Command SaveCommand { get; } - public Command CancelCommand { get; } - - private async void OnCancel() - { - // This will pop the current page off the navigation stack - await Shell.Current.GoToAsync(".."); - } - - private async void OnSave() - { - Item newItem = new Item() - { - Id = Guid.NewGuid().ToString(), - Text = Text, - Description = Description - }; - - await DataStore.AddItemAsync(newItem); - - // This will pop the current page off the navigation stack - await Shell.Current.GoToAsync(".."); - } - } -} diff --git a/Gallery.Share/Views/AboutPage.xaml b/Gallery.Share/Views/AboutPage.xaml deleted file mode 100644 index 9610963..0000000 --- a/Gallery.Share/Views/AboutPage.xaml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - #96d1ff - - - - - - - - - - - - - - - - - - - - - - diff --git a/Gallery.Share/Views/NewItemPage.xaml.cs b/Gallery.Share/Views/NewItemPage.xaml.cs deleted file mode 100644 index fff2546..0000000 --- a/Gallery.Share/Views/NewItemPage.xaml.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using Xamarin.Forms; -using Xamarin.Forms.Xaml; - -using Gallery.Models; -using Gallery.ViewModels; - -namespace Gallery.Views -{ - public partial class NewItemPage : ContentPage - { - public Item Item { get; set; } - - public NewItemPage() - { - InitializeComponent(); - BindingContext = new NewItemViewModel(); - } - } -} diff --git a/Gallery.UI/CustomViews.cs b/Gallery.UI/CustomViews.cs new file mode 100644 index 0000000..a20993c --- /dev/null +++ b/Gallery.UI/CustomViews.cs @@ -0,0 +1,8 @@ +using Xamarin.Forms; + +namespace Gallery.Resources.UI +{ + public class BlurryPanel : ContentView { } + + public class CircleImage : Image { } +} diff --git a/Gallery.UI/Extensions.cs b/Gallery.UI/Extensions.cs new file mode 100644 index 0000000..8ab5ff5 --- /dev/null +++ b/Gallery.UI/Extensions.cs @@ -0,0 +1,54 @@ +using Xamarin.Forms; + +namespace Gallery.Resources.UI +{ + public static class Extensions + { + public const string TextColor = nameof(TextColor); + public const string SubTextColor = nameof(SubTextColor); + public const string OptionTintColor = nameof(OptionTintColor); + + public static T Binding(this T view, BindableProperty property, string name, BindingMode mode = BindingMode.Default, IValueConverter converter = null) where T : BindableObject + { + if (name == null) + { + view.SetValue(property, property.DefaultValue); + } + else + { + view.SetBinding(property, name, mode, converter); + } + return view; + } + + public static T DynamicResource(this T view, BindableProperty property, string key) where T : Element + { + view.SetDynamicResource(property, key); + return view; + } + + public static T GridRow(this T view, int row) where T : BindableObject + { + Grid.SetRow(view, row); + return view; + } + + public static T GridRowSpan(this T view, int rowSpan) where T : BindableObject + { + Grid.SetRowSpan(view, rowSpan); + return view; + } + + public static T GridColumn(this T view, int column) where T : BindableObject + { + Grid.SetColumn(view, column); + return view; + } + + public static T GridColumnSpan(this T view, int columnSpan) where T : BindableObject + { + Grid.SetColumnSpan(view, columnSpan); + return view; + } + } +} diff --git a/Gallery.UI/FlowLayout.cs b/Gallery.UI/FlowLayout.cs new file mode 100644 index 0000000..682eb1d --- /dev/null +++ b/Gallery.UI/FlowLayout.cs @@ -0,0 +1,275 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Xamarin.Forms; + +namespace Gallery.Resources.UI +{ + public class FlowLayout : AbsoluteLayout + { + public static readonly BindableProperty ColumnProperty = BindableProperty.Create(nameof(Column), typeof(int), typeof(FlowLayout), + defaultValue: 2, propertyChanged: OnColumnPropertyChanged); + public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create(nameof(RowSpacing), typeof(double), typeof(FlowLayout), defaultValue: 10.0); + public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create(nameof(ColumnSpacing), typeof(double), typeof(FlowLayout), defaultValue: 10.0); + + private static void OnColumnPropertyChanged(BindableObject obj, object old, object @new) + { + var flowLayout = (FlowLayout)obj; + if (old is int column && column != flowLayout.Column) + { + flowLayout.UpdateChildrenLayout(); + //flowLayout.InvalidateLayout(); + } + } + + public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(FlowLayout)); + public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IList), typeof(FlowLayout), + propertyChanged: OnItemsSourcePropertyChanged); + + private static void OnItemsSourcePropertyChanged(BindableObject obj, object old, object @new) + { + var flowLayout = (FlowLayout)obj; + if (old is ICollectionChanged oldNotify) + { + oldNotify.CollectionChanged -= flowLayout.OnCollectionChanged; + } + flowLayout.lastWidth = -1; + if (@new == null) + { + flowLayout.freezed = true; + flowLayout.cachedLayout.Clear(); + flowLayout.Children.Clear(); + flowLayout.freezed = false; + + flowLayout.InvalidateLayout(); + } + else if (@new is IList list) + { + flowLayout.freezed = true; + flowLayout.cachedLayout.Clear(); + flowLayout.Children.Clear(); + for (var i = 0; i < list.Count; i++) + { + var child = flowLayout.ItemTemplate.CreateContent(); + if (child is View view) + { + view.BindingContext = list[i]; + flowLayout.Children.Add(view); + } + } + if (@new is ICollectionChanged newNotify) + { + newNotify.CollectionChanged += flowLayout.OnCollectionChanged; + } + flowLayout.freezed = false; + + flowLayout.UpdateChildrenLayout(); + //flowLayout.InvalidateLayout(); + } + } + + public int Column + { + get => (int)GetValue(ColumnProperty); + set => SetValue(ColumnProperty, value); + } + + public double RowSpacing + { + get => (double)GetValue(RowSpacingProperty); + set => SetValue(RowSpacingProperty, value); + } + + public double ColumnSpacing + { + get => (double)GetValue(ColumnSpacingProperty); + set => SetValue(ColumnSpacingProperty, value); + } + + public DataTemplate ItemTemplate + { + get => (DataTemplate)GetValue(ItemTemplateProperty); + set => SetValue(ItemTemplateProperty, value); + } + + public IList ItemsSource + { + get => (IList)GetValue(ItemsSourceProperty); + set => SetValue(ItemsSourceProperty, value); + } + + + public event EventHandler MaxHeightChanged; + + public double ColumnWidth { get; private set; } + + private bool freezed; + private double maximumHeight; + private readonly Dictionary cachedLayout = new(); + + private double lastWidth = -1; + private SizeRequest lastSizeRequest; + + protected override void LayoutChildren(double x, double y, double width, double height) + { + if (freezed) + { + return; + } + var column = Column; + if (column < 1) + { + return; + } + var source = ItemsSource; + if (source == null || source.Count <= 0) + { + return; + } + var columnSpacing = ColumnSpacing; + var rowSpacing = RowSpacing; + + var columnHeights = new double[column]; + var columnSpacingTotal = columnSpacing * (column - 1); + var columnWidth = (width - columnSpacingTotal) / column; + ColumnWidth = columnWidth; + + foreach (var item in Children) + { + var measured = item.Measure(columnWidth, height, flags: MeasureFlags.IncludeMargins); + var col = 0; + for (var i = 1; i < column; i++) + { + if (columnHeights[i] < columnHeights[col]) + { + col = i; + } + } + + var rect = new Rectangle( + col * (columnWidth + columnSpacing), + columnHeights[col], + columnWidth, + measured.Request.Height); + if (cachedLayout.TryGetValue(item, out var r)) + { + if (r != rect) + { + cachedLayout[item] = rect; + item.Layout(rect); + //SetLayoutBounds(item, rect); + } + } + else + { + cachedLayout.Add(item, rect); + item.Layout(rect); + //SetLayoutBounds(item, rect); + } + columnHeights[col] += measured.Request.Height + rowSpacing; + } + } + + protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint) + { + var column = Column; + if (column < 1) + { + return base.OnMeasure(widthConstraint, heightConstraint); + } + if (lastWidth == widthConstraint) + { + return lastSizeRequest; + } + lastWidth = widthConstraint; + var columnSpacing = ColumnSpacing; + var rowSpacing = RowSpacing; + + var columnHeights = new double[column]; + var columnSpacingTotal = columnSpacing * (column - 1); + var columnWidth = (widthConstraint - columnSpacingTotal) / column; + ColumnWidth = columnWidth; + + foreach (var item in Children) + { + var measured = item.Measure(columnWidth, heightConstraint, flags: MeasureFlags.IncludeMargins); + var col = 0; + for (var i = 1; i < column; i++) + { + if (columnHeights[i] < columnHeights[col]) + { + col = i; + } + } + columnHeights[col] += measured.Request.Height + rowSpacing; + } + maximumHeight = columnHeights.Max(); + + if (maximumHeight > 0) + { + MaxHeightChanged?.Invoke(this, new HeightEventArgs { ContentHeight = maximumHeight }); + } + + lastSizeRequest = new SizeRequest(new Size(widthConstraint, maximumHeight)); + return lastSizeRequest; + } + + private void OnCollectionChanged(object sender, CollectionChangedEventArgs e) + { + lastWidth = -1; + if (e.OldItems != null) + { + freezed = true; + cachedLayout.Clear(); + var index = e.OldStartingIndex; + for (var i = index + e.OldItems.Count - 1; i >= index; i--) + { + Children.RemoveAt(i); + } + freezed = false; + } + + if (e.NewItems == null) + { + UpdateChildrenLayout(); + //InvalidateLayout(); + return; + } + + freezed = true; + var start = e.NewStartingIndex; + for (var i = 0; i < e.NewItems.Count; i++) + { + var child = ItemTemplate.CreateContent(); + if (child is View view) + { + view.BindingContext = e.NewItems[i]; + Children.Insert(start + i, view); + } + } + freezed = false; + + UpdateChildrenLayout(); + //InvalidateLayout(); + } + } + + public interface ICollectionChanged + { + event EventHandler CollectionChanged; + } + + public class CollectionChangedEventArgs : EventArgs + { + public int OldStartingIndex { get; set; } + public IList OldItems { get; set; } + public int NewStartingIndex { get; set; } + public IList NewItems { get; set; } + } + + public class HeightEventArgs : EventArgs + { + public double ContentHeight { get; set; } + } +} diff --git a/Gallery.UI/Gallery.UI.projitems b/Gallery.UI/Gallery.UI.projitems new file mode 100644 index 0000000..edb5abf --- /dev/null +++ b/Gallery.UI/Gallery.UI.projitems @@ -0,0 +1,18 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {73AB85FB-D11A-43FB-BBC5-54BED5A056D1} + + + Gallery.Resources.UI + + + + + + + + + \ No newline at end of file diff --git a/Gallery.UI/Gallery.UI.shproj b/Gallery.UI/Gallery.UI.shproj new file mode 100644 index 0000000..6de5074 --- /dev/null +++ b/Gallery.UI/Gallery.UI.shproj @@ -0,0 +1,11 @@ + + + + {73AB85FB-D11A-43FB-BBC5-54BED5A056D1} + + + + + + + \ No newline at end of file diff --git a/Gallery.UI/OptionCells.cs b/Gallery.UI/OptionCells.cs new file mode 100644 index 0000000..7ba861b --- /dev/null +++ b/Gallery.UI/OptionCells.cs @@ -0,0 +1,157 @@ +using System.Collections; +using Xamarin.Forms; + +namespace Gallery.Resources.UI +{ + public class OptionEntry : Entry { } + + public class OptionPicker : Picker { } + + public abstract class OptionCell : ViewCell + { + public static readonly BindableProperty TitleProperty = BindableProperty.Create(nameof(Title), typeof(string), typeof(OptionCell)); + + public string Title + { + get => (string)GetValue(TitleProperty); + set => SetValue(TitleProperty, value); + } + + protected abstract View Content { get; } + + public OptionCell() + { + View = new Grid + { + BindingContext = this, + Padding = new Thickness(20, 0), + ColumnDefinitions = + { + new ColumnDefinition { Width = new GridLength(.3, GridUnitType.Star) }, + new ColumnDefinition { Width = new GridLength(.7, GridUnitType.Star) } + }, + Children = + { + new Label + { + LineBreakMode = LineBreakMode.TailTruncation, + VerticalOptions = LayoutOptions.Center + } + .Binding(Label.TextProperty, nameof(Title)) + .DynamicResource(Label.TextColorProperty, Extensions.TextColor), + + Content.GridColumn(1) + } + } + .DynamicResource(VisualElement.BackgroundColorProperty, Extensions.OptionTintColor); + } + } + + public class OptionTextCell : OptionCell + { + public static readonly BindableProperty DetailProperty = BindableProperty.Create(nameof(Detail), typeof(string), typeof(OptionCell)); + + public string Detail + { + get => (string)GetValue(DetailProperty); + set => SetValue(DetailProperty, value); + } + + protected override View Content => + new Label + { + HorizontalOptions = LayoutOptions.End, + VerticalOptions = LayoutOptions.Center + } + .Binding(Label.TextProperty, nameof(Detail)) + .DynamicResource(Label.TextColorProperty, Extensions.SubTextColor); + } + + public class OptionSwitchCell : OptionCell + { + public static readonly BindableProperty IsToggledProperty = BindableProperty.Create(nameof(IsToggled), typeof(bool), typeof(OptionSwitchCell)); + + public bool IsToggled + { + get => (bool)GetValue(IsToggledProperty); + set => SetValue(IsToggledProperty, value); + } + + protected override View Content => + new Switch + { + HorizontalOptions = LayoutOptions.End, + VerticalOptions = LayoutOptions.Center + } + .Binding(Switch.IsToggledProperty, nameof(IsToggled), mode: BindingMode.TwoWay); + } + + public class OptionDropCell : OptionCell + { + public static readonly BindableProperty ItemsProperty = BindableProperty.Create(nameof(Items), typeof(IList), typeof(OptionDropCell)); + public static readonly BindableProperty SelectedIndexProperty = BindableProperty.Create(nameof(SelectedIndex), typeof(int), typeof(OptionDropCell)); + + public IList Items + { + get => (IList)GetValue(ItemsProperty); + set => SetValue(ItemsProperty, value); + } + + public int SelectedIndex + { + get => (int)GetValue(SelectedIndexProperty); + set => SetValue(SelectedIndexProperty, value); + } + + protected override View Content => + new OptionPicker + { + HorizontalOptions = LayoutOptions.Fill, + VerticalOptions = LayoutOptions.Center + } + .Binding(Picker.ItemsSourceProperty, nameof(Items)) + .Binding(Picker.SelectedIndexProperty, nameof(SelectedIndex), mode: BindingMode.TwoWay) + .DynamicResource(Picker.TextColorProperty, Extensions.TextColor) + .DynamicResource(VisualElement.BackgroundColorProperty, Extensions.OptionTintColor); + } + + public class OptionEntryCell : OptionCell + { + public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(OptionEntryCell)); + public static readonly BindableProperty KeyboardProperty = BindableProperty.Create(nameof(Keyboard), typeof(Keyboard), typeof(OptionEntryCell)); + public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(OptionEntryCell)); + + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public Keyboard Keyboard + { + get => (Keyboard)GetValue(KeyboardProperty); + set => SetValue(KeyboardProperty, value); + } + + public string Placeholder + { + get => (string)GetValue(PlaceholderProperty); + set => SetValue(PlaceholderProperty, value); + } + + protected override View Content => + new OptionEntry + { + HorizontalOptions = LayoutOptions.Fill, + HorizontalTextAlignment = TextAlignment.End, + VerticalOptions = LayoutOptions.Center, + ReturnType = ReturnType.Next + } + .Binding(Entry.TextProperty, nameof(Text), mode: BindingMode.TwoWay) + .Binding(Entry.PlaceholderProperty, nameof(Placeholder)) + .Binding(InputView.KeyboardProperty, nameof(Keyboard)) + .DynamicResource(Entry.TextProperty, Extensions.TextColor) + .DynamicResource(Entry.PlaceholderColorProperty, Extensions.SubTextColor) + .DynamicResource(VisualElement.BackgroundColorProperty, Extensions.OptionTintColor); + } +} diff --git a/Gallery.UI/RoundViews.cs b/Gallery.UI/RoundViews.cs new file mode 100644 index 0000000..8fa45e3 --- /dev/null +++ b/Gallery.UI/RoundViews.cs @@ -0,0 +1,63 @@ +using Xamarin.Forms; + +namespace Gallery.Resources.UI +{ + public class RoundImage : Image + { + public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(float), typeof(RoundImage)); + public static readonly BindableProperty CornerMasksProperty = BindableProperty.Create(nameof(CornerMasks), typeof(CornerMask), typeof(RoundImage)); + + public float CornerRadius + { + get => (float)GetValue(CornerRadiusProperty); + set => SetValue(CornerRadiusProperty, value); + } + public CornerMask CornerMasks + { + get => (CornerMask)GetValue(CornerMasksProperty); + set => SetValue(CornerMasksProperty, value); + } + + } + + public class RoundLabel : Label + { + public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(float), typeof(RoundLabel)); + public static new readonly BindableProperty BackgroundColorProperty = BindableProperty.Create(nameof(BackgroundColor), typeof(Color), typeof(RoundLabel), Color.Transparent); + + public float CornerRadius + { + get => (float)GetValue(CornerRadiusProperty); + set => SetValue(CornerRadiusProperty, value); + } + public new Color BackgroundColor + { + get => (Color)GetValue(BackgroundColorProperty); + set => SetValue(BackgroundColorProperty, value); + } + } + + public enum CornerMask + { + None = 0, + + LeftTop = 1, + RightTop = 2, + LeftBottom = 4, + RightBottom = 8, + + Top = LeftTop | RightTop, // 3 + Left = LeftTop | LeftBottom, // 5 + Slash = RightTop | LeftBottom, // 6 + BackSlash = LeftTop | RightBottom, // 9 + Right = RightTop | RightBottom, // 10 + Bottom = LeftBottom | RightBottom, // 12 + + ExceptRightBottom = LeftTop | RightTop | LeftBottom, // 7 + ExceptLeftBottom = LeftTop | RightTop | RightBottom, // 11 + ExceptRightTop = LeftTop | LeftBottom | RightBottom, // 13 + ExceptLeftTop = RightTop | LeftBottom | RightBottom, // 14 + + All = LeftTop | RightTop | LeftBottom | RightBottom // 15 + } +} diff --git a/Gallery.Util/Extensions.cs b/Gallery.Util/Extensions.cs index 65579cd..e91ee32 100644 --- a/Gallery.Util/Extensions.cs +++ b/Gallery.Util/Extensions.cs @@ -1,53 +1,9 @@ using System; -using Xamarin.Forms; namespace Gallery.Util { public static class Extensions { - public static T Binding(this T view, BindableProperty property, string name, BindingMode mode = BindingMode.Default, IValueConverter converter = null) where T : BindableObject - { - if (name == null) - { - view.SetValue(property, property.DefaultValue); - } - else - { - view.SetBinding(property, name, mode, converter); - } - return view; - } - - public static T DynamicResource(this T view, BindableProperty property, string key) where T : Element - { - view.SetDynamicResource(property, key); - return view; - } - - public static T GridRow(this T view, int row) where T : BindableObject - { - Grid.SetRow(view, row); - return view; - } - - public static T GridRowSpan(this T view, int rowSpan) where T : BindableObject - { - Grid.SetRowSpan(view, rowSpan); - return view; - } - - public static T GridColumn(this T view, int column) where T : BindableObject - { - Grid.SetColumn(view, column); - return view; - } - - public static T GridColumnSpan(this T view, int columnSpan) where T : BindableObject - { - Grid.SetColumnSpan(view, columnSpan); - return view; - } - public static int IndexOf(this T[] array, Predicate predicate) { for (var i = 0; i < array.Length; i++) diff --git a/Gallery.Util/Interface/IGallerySource.cs b/Gallery.Util/Interface/IGallerySource.cs index 8b5524b..6a489c2 100644 --- a/Gallery.Util/Interface/IGallerySource.cs +++ b/Gallery.Util/Interface/IGallerySource.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Gallery.Util.Model; +using Xamarin.Forms; namespace Gallery.Util.Interface { @@ -7,10 +8,16 @@ namespace Gallery.Util.Interface { string Name { get; } + string Route { get; } + + string FlyoutIconKey { get; } + string HomePage { get; } void SetCookie(); + void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark); + Task GetRecentItemsAsync(int page); } } diff --git a/Gallery.Util/Model/GalleryItem.cs b/Gallery.Util/Model/GalleryItem.cs index b4f5f50..9de299b 100644 --- a/Gallery.Util/Model/GalleryItem.cs +++ b/Gallery.Util/Model/GalleryItem.cs @@ -14,14 +14,41 @@ namespace Gallery.Util.Model public static readonly BindableProperty UserNameProperty = BindableProperty.Create(nameof(UserName), typeof(string), typeof(GalleryItem)); public static readonly BindableProperty CreatedTimeProperty = BindableProperty.Create(nameof(CreatedTime), typeof(DateTime), typeof(GalleryItem)); public static readonly BindableProperty UpdatedTimeProperty = BindableProperty.Create(nameof(UpdatedTime), typeof(DateTime), typeof(GalleryItem)); - public static readonly BindableProperty ImageHeightProperty = BindableProperty.Create(nameof(ImageHeight), typeof(GridLength), typeof(GalleryItem), GridLength.Auto); + public static readonly BindableProperty ImageHeightProperty = BindableProperty.Create(nameof(ImageHeight), typeof(GridLength), typeof(GalleryItem), + defaultValue: GridLength.Auto); + public static readonly BindableProperty IsFavoriteProperty = BindableProperty.Create(nameof(IsFavorite), typeof(bool), typeof(GalleryItem)); + public static readonly BindableProperty BookmarkIdProperty = BindableProperty.Create(nameof(BookmarkId), typeof(string), typeof(GalleryItem)); [JsonIgnore] - public string TagDescription { get; set; } + public string TagDescription + { + get => (string)GetValue(TagDescriptionProperty); + set => SetValue(TagDescriptionProperty, value); + } [JsonIgnore] - public ImageSource PreviewImage { get; set; } + public ImageSource PreviewImage + { + get => (ImageSource)GetValue(PreviewImageProperty); + set => SetValue(PreviewImageProperty, value); + } [JsonIgnore] - public GridLength ImageHeight { get; set; } + public GridLength ImageHeight + { + get => (GridLength)GetValue(ImageHeightProperty); + set => SetValue(ImageHeightProperty, value); + } + [JsonIgnore] + public bool IsFavorite + { + get => (bool)GetValue(IsFavoriteProperty); + set => SetValue(IsFavoriteProperty, value); + } + [JsonIgnore] + public string BookmarkId + { + get => (string)GetValue(BookmarkIdProperty); + set => SetValue(BookmarkIdProperty, value); + } public long Id { get; internal set; } private string[] tags; diff --git a/Gallery.Util/NetHelper.cs b/Gallery.Util/NetHelper.cs index 48e7f65..e5c8df1 100644 --- a/Gallery.Util/NetHelper.cs +++ b/Gallery.Util/NetHelper.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.IO; using System.Net.Http; using System.Net.Http.Headers; using System.Text.Json; @@ -90,6 +92,138 @@ namespace Gallery.Util } } + public static async Task DownloadImage(string url, string working, string folder) + { + try + { + var directory = Path.Combine(working, folder); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + var file = Path.Combine(directory, Path.GetFileName(url)); + var response = await Request(url, headers => + { + headers.Add("User-Agent", Config.UserAgent); + headers.Add("Accept", Config.AcceptImage); + }); + if (response == null) + { + return null; + } + using (response) + using (var fs = File.OpenWrite(file)) + { + await response.Content.CopyToAsync(fs); + } + return file; + } + catch (Exception ex) + { + Log.Error("image.download", ex.Message); + return null; + } + } + + public static async Task DownloadImageAsync(string url, string id, string working, string folder) + { + try + { + var directory = Path.Combine(working, folder); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + var file = Path.Combine(directory, Path.GetFileName(url)); + var proxy = Config.Proxy; + var handler = new HttpClientHandler + { + UseCookies = false + }; + if (proxy != null) + { + handler.Proxy = proxy; + handler.UseProxy = true; + } + var client = new HttpClient(handler, true) + { + Timeout = Config.Timeout + }; + long size; + DateTimeOffset lastModified; + using (var request = new HttpRequestMessage(HttpMethod.Head, url)) + { + var headers = request.Headers; + headers.Add("Accept", Config.AcceptImage); + headers.Add("Accept-Language", Config.AcceptLanguage); + headers.Add("User-Agent", Config.UserAgent); + using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + size = response.Content.Headers.ContentLength.Value; + lastModified = response.Content.Headers.LastModified.Value; +#if DEBUG + Log.Print($"content length: {size:n0} bytes, last modified: {lastModified}"); +#endif + } + + // segments + const int SIZE = 150000; + var list = new List<(long from, long to)>(); + for (long i = 0; i < size; i += SIZE) + { + long to; + if (i + SIZE >= size) + { + to = size - 1; + } + else + { + to = i + SIZE - 1; + } + list.Add((i, to)); + } + + var data = new byte[size]; + var task = new TaskCompletionSource(); + + ParallelTask.Start($"download.async.{id}", 0, list.Count, 2, i => + { + var (from, to) = list[i]; + using (var request = new HttpRequestMessage(HttpMethod.Get, url)) + { + var headers = request.Headers; + headers.Add("Accept", Config.AcceptImage); + headers.Add("Accept-Language", Config.AcceptLanguage); + headers.Add("Accept-Encoding", "identity"); + headers.IfRange = new RangeConditionHeaderValue(lastModified); + headers.Range = new RangeHeaderValue(from, to); + headers.Add("User-Agent", Config.UserAgent); + using var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result; + using var ms = new MemoryStream(data, (int)from, (int)(to - from + 1)); + response.Content.CopyToAsync(ms).Wait(); +#if DEBUG + Log.Print($"downloaded range: from({from:n0}) to ({to:n0})"); +#endif + } + return true; + }, + complete: o => + { + using (var fs = File.OpenWrite(file)) + { + fs.Write(data, 0, data.Length); + } + task.SetResult(file); + }); + + return await task.Task; + } + catch (Exception ex) + { + Log.Error("image.download.async", $"failed to download image, error: {ex.Message}"); + return null; + } + } + private static async Task Request(string url, Action headerHandler, HttpContent post = null) { #if DEBUG diff --git a/Gallery.Util/Store.cs b/Gallery.Util/Store.cs index 6aa8dd9..603b256 100644 --- a/Gallery.Util/Store.cs +++ b/Gallery.Util/Store.cs @@ -1,6 +1,9 @@ using System; +using System.IO; using System.Net; +using System.Threading.Tasks; using Xamarin.Essentials; +using Xamarin.Forms; namespace Gallery.Util { @@ -8,6 +11,65 @@ namespace Gallery.Util { public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal); public static readonly string CacheFolder = FileSystem.CacheDirectory; + + private const string imageFolder = "img-original"; + private const string previewFolder = "img-preview"; + + public static async Task LoadRawImage(string url) + { + return await LoadImageAsync(url, null, PersonalFolder, imageFolder, force: true); + } + + public static async Task LoadPreviewImage(string url, bool downloading, bool force = false) + { + return await LoadImage(url, CacheFolder, previewFolder, downloading, force: force); + } + + private static async Task LoadImage(string url, string working, string folder, bool downloading, bool force = false) + { + var file = Path.Combine(working, folder, Path.GetFileName(url)); + ImageSource image; + if (!force && File.Exists(file)) + { + image = ImageSource.FromFile(file); + } + else + { + image = null; + } + if (downloading && image == null) + { + file = await NetHelper.DownloadImage(url, working, folder); + if (file != null) + { + return ImageSource.FromFile(file); + } + } + return image; + } + + private static async Task LoadImageAsync(string url, string id, string working, string folder, bool force = false) + { + var file = Path.Combine(working, folder, Path.GetFileName(url)); + ImageSource image; + if (!force && File.Exists(file)) + { + image = ImageSource.FromFile(file); + } + else + { + image = null; + } + if (image == null) + { + file = await NetHelper.DownloadImageAsync(url, id, working, folder); + if (file != null) + { + image = ImageSource.FromFile(file); + } + } + return image; + } } public static class Config @@ -20,7 +82,14 @@ namespace Gallery.Util public const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"; public const string AcceptLanguage = "zh-cn"; + public const string AcceptImage = "image/png,image/*,*/*;q=0.8"; public static WebProxy Proxy; } + + public static class Routes + { + public const string Gallery = "gallery"; + public const string Option = "option"; + } } diff --git a/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json index fc9d330..21fed74 100644 --- a/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,117 +1,116 @@ -{ +{ "images": [ { - "scale": "2x", + "filename": "Icon40.png", "size": "20x20", - "idiom": "iphone", - "filename": "Icon40.png" + "scale": "2x", + "idiom": "iphone" }, { - "scale": "3x", + "filename": "Icon60.png", "size": "20x20", - "idiom": "iphone", - "filename": "Icon60.png" - }, - { - "scale": "2x", - "size": "29x29", - "idiom": "iphone", - "filename": "Icon58.png" - }, - { "scale": "3x", + "idiom": "iphone" + }, + { + "filename": "Icon58.png", "size": "29x29", - "idiom": "iphone", - "filename": "Icon87.png" - }, - { "scale": "2x", - "size": "40x40", - "idiom": "iphone", - "filename": "Icon80.png" + "idiom": "iphone" }, { + "filename": "Icon87.png", + "size": "29x29", "scale": "3x", - "size": "40x40", - "idiom": "iphone", - "filename": "Icon120.png" + "idiom": "iphone" }, { + "filename": "Icon80.png", + "size": "40x40", "scale": "2x", + "idiom": "iphone" + }, + { + "filename": "Icon120.png", + "size": "40x40", + "scale": "3x", + "idiom": "iphone" + }, + { + "filename": "Icon120.png", "size": "60x60", - "idiom": "iphone", - "filename": "Icon120.png" + "scale": "2x", + "idiom": "iphone" }, { - "scale": "3x", + "filename": "Icon180.png", "size": "60x60", - "idiom": "iphone", - "filename": "Icon180.png" + "scale": "3x", + "idiom": "iphone" }, { - "scale": "1x", + "filename": "Icon20.png", "size": "20x20", - "idiom": "ipad", - "filename": "Icon20.png" + "scale": "1x", + "idiom": "ipad" }, { - "scale": "2x", + "filename": "Icon40.png", "size": "20x20", - "idiom": "ipad", - "filename": "Icon40.png" + "scale": "2x", + "idiom": "ipad" }, { - "scale": "1x", + "filename": "Icon29.png", "size": "29x29", - "idiom": "ipad", - "filename": "Icon29.png" + "scale": "1x", + "idiom": "ipad" }, { - "scale": "2x", + "filename": "Icon58.png", "size": "29x29", - "idiom": "ipad", - "filename": "Icon58.png" + "scale": "2x", + "idiom": "ipad" }, { - "scale": "1x", + "filename": "Icon40.png", "size": "40x40", - "idiom": "ipad", - "filename": "Icon40.png" - }, - { - "scale": "2x", - "size": "40x40", - "idiom": "ipad", - "filename": "Icon80.png" - }, - { "scale": "1x", - "size": "76x76", - "idiom": "ipad", - "filename": "Icon76.png" + "idiom": "ipad" }, { + "filename": "Icon80.png", + "size": "40x40", "scale": "2x", - "size": "76x76", - "idiom": "ipad", - "filename": "Icon152.png" + "idiom": "ipad" }, { - "scale": "2x", + "filename": "Icon167.png", "size": "83.5x83.5", - "idiom": "ipad", - "filename": "Icon167.png" + "scale": "2x", + "idiom": "ipad" }, { + "filename": "Icon76.png", + "size": "76x76", "scale": "1x", + "idiom": "ipad" + }, + { + "filename": "Icon152.png", + "size": "76x76", + "scale": "2x", + "idiom": "ipad" + }, + { + "filename": "Icon1024.png", "size": "1024x1024", - "idiom": "ios-marketing", - "filename": "Icon1024.png" + "scale": "1x", + "idiom": "ios-marketing" } ], - "properties": {}, "info": { "version": 1, "author": "xcode" } -} +} \ No newline at end of file diff --git a/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png b/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png index 9174c989a9c8b8a5ca133228f4ed7c173fffd2ee..88e5af0fb5f66e4d67a20df9872bfa0fb54c7e08 100644 GIT binary patch literal 24921 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 DJ%@}` literal 70429 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 z zCn)b8&=oMu6ygwwJfdasJX|@L6?m1Dv0X9t>JAWO^UIj0#&(3UrHx;vP^3g= zL{(XT!?`D*pP8)WoGHYEZZc$!odTzb8n)q0|88*>6P z`?6&CSv_W7r2yF0beQ2*?V^_%pKktVAo`)T^26X@NpK_*-ni{D7{Sp{C0A<|16l(; zOL*xGW|*sKsiwHvE!h3QXe@^a#6W3}8!DQu-h?A_4gkeRYkt4NC~GR5P8eyp;9kVQ8$QG$5ad7Fo23Z~ak1jY~RXG{v?3G$RarFe`XePu3X{R+=mBOw&X zks)|Sc$RcG-jhn!`~-x|vg!&DA&@}QH^RNdyy9nq56yrU$^qAaS+F_NOaeFb)CVaH z?!UvPajgrK&zqdAs>&Def#wkcG_UhmYOVw^M`VZz@+4IWAVzK%`+za9rm2SD9={u@ zlx5D6UDL;lc7#9`+%vnlP3PescU=N`DHQPt_N55GNBMkVCRMR4?fvp zAFsvcHN4c9rb>J@{*IH>RTr9de%9i4Gd(cbFa9SP4anhoP;TA0!oZyB8?lNMDHPHK zCaOaFU9?x2A!o>p>mCF9r+hKs9Czu_P1l$LWU%}q#)=T3p`ZnYyeHmsewqw`}L^4LuHqfo+CG6<2n7#l^3;H^^!1 zsaieYFnN)Kc7Mv}^xE)4kXUw8<9I+jMB@QV9T9I8haLDt1Ne#exWUfGYG$4uMoEu& zo81#2up18Y40h%tIsOZglp(ltVsE*j1~$lVd|;rN)&${~o~-%KZnJp&3|OFR{^8E9 zJ;fCu53Ysw%}@VYWE*z7r)&4P=^B-SF%a@>*9g84<4aFUZT7x)qdsS+#2tu5NbpU@ zg;EwV)l-#sK>#r9>(0Figx{9lKm>KvRj;y<8 zc8SxMW4<11(s@QMV_}n9MRzA*62->vzxmHh1)GVASEJY7LVtRw`Rv{v`(Fuc00(&o z%m>gS2aJekmdNQ4p<{pD3HqZ-%4hdU1__xYhLi9mTJXD|E zE`t6SX)}l_DY5vO0Xrs#O6_DKtPKn0f+e~SprDYmJL_`<053iA5P`zn z4<5etc%aF58sHFr#M;U-9|=;l)J#Q2vS!Q9(d(EX6fubL%uA_lqa2%!cpNIv78QZ}Ayo(>C(ZpsRtKhzD--fpuoCch87cX-Bna9_{z%$b*dHM0?+T&Hk!+^UM`r|vq z2Id$??bX^|tfYaE+h#Nik(ZcN+wt)28q^gWe!y8jDCXrD<2qV#49x@5$8&Zrd5NTs zNYcix;9fe#PQQ;T?!6hG>9K{K+RCPqiGc9z%t{=`QaX>7O{l(+#7mJ1>Rae^J?82e z6cLqLypskTCyu>uc~$0-XZ^1Qvhwr+pKQ#CKImhGu*MGM*ZrROuAHWuT*yM$ieEy8*KLFMMdLZL|D+yDmy@3_PELTEVMI6nwfcYA3ZQ9wwKdtkT z;`;z7fU{U6>CS7kr3=A-()_G*G(Mjf2wXKe

Fpy)y!S(AQHSG#udd_8#b4sQu!R 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 PgFh2POM|ilu9yD@)d*=D diff --git a/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png b/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png index 9c60a1761dbf62cc2a45ff98b9fdb63ade16e4d9..523d3e1e7514029200bcd2806ba1f9c61dab5848 100644 GIT binary patch literal 3741 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 Dh-f%Y diff --git a/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png b/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png index 448d6efb577d07e227a5c62545173ddf6bd86b55..61cefe525dfa03c6946ca4e3335c6b041f228f37 100644 GIT binary patch literal 4827 zcmY*dcQhMr_iue;j~Xp%HMXcdVwM^qinKzA617*UTD6L=6t!2h_TI5q2t`9xsZk?T z%-T|;ifXO?yyt!Y`JHp0=X39Q&bjCFe4cUcxv`I6`iyj3bl0w3V>E#3!mlv%KZAzq zYUb<(@?XJiKM2y#)XUi~z|P0%nwF!Ny%W&D)6T^S?quf}N1dq#<5≫+a#iq|3`hwiT+t;{uf`b9z-9C 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 diff --git a/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png b/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png index 8524768f8d764da7e9c452a444208708f2d18ff1..a2981637018a72ea1007352fdcbcc6846dbf6269 100644 GIT binary patch literal 5397 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 literal 4692 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 diff --git a/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png b/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png index 60a64703c0f11d08705cd607f7751338707f5919..71662eab190aea29f9b885e205b2d468476f7a4e 100644 GIT binary patch literal 3299 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 nB>NYz<=gRNh#vVr4-JSOVl}T zxjV+Jz2oe@OJ8QK*4CP9%rVBAYffh@SgaqJOHzpfF_d(UauGNcwFXh0q%A?)a!Ljr&9z1 z0U5{R5#4SVk6d9*;8ZGwL?WRBG(G=191f_}YFI9p$mjF$cs!WT=UA`TvaTo!%w{ug zw;MX0jxdkKVt+4)FAST_CVIUd3WWl!R;w_jWQ)Zjk_ki*hWcGB7U6Qa^bGg=Jw~Gu zqR}XvPAAIcvTVv`v+(=h8iGc(w}fREJ;kx<#LEbB3d#n zs8*|zOLiQK#X<_R0K=dr(n>~=dsp^z}ERDUWM3M50xh>jr_ zxxhmq2z~oWXn^#M5-646oX6v_NG_F1l5;+vF_}y>d2AypQ4i_`(Cu~$D{dknqup)` z+vh`}XOynqB%jZx70|U!tJRWCY#EgS{i3g&ztc8`XZ^`!QYwMJ*?hwu!>kxr+d z!SoCn+du6VjYb2r*-X2`|KIb^uHL7Q1N)I}LFr=nPacPNJYR2rVBh)|@K1mN0P-$M U!Is?Swg3PC07*qoM6N<$g5{nOegFUf literal 1313 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc#=yY%t50_}ki%FU_)Q7iwV%v7MwAoJ}E zZNMr~#Gv-r=z}araty?$U{Rn~?YM08;lXCd<#R|ql7WHQ)YHW=#6qw)#M@suP~=~l zRjpGX*9l{_MO#H%C3w_acv%kdU+7&Vy|{3(^kTg`FPzNtRPqcAkL_>~-&L^OrSU|Q zhXPm7@*ipe3N~C!+b)&8vfRG+u*u5K<#Tr$KmU05^N)8LnL;V9Q~8~PyBVVG+@@7} zYS$#MUiM{=bNE{Ru0)BK8$Cppc~)ATarBs*({ya#^z(c&HWAi8!jW!a=4X70H%*-#5x%au zsg=XSFE^=wJ{mkMm8T`wda?q0lm;R>!l`pzrL ztuMwbc<6Y%(WkeFduh6asUGjqE%${q&rjb~_&UO%S;P8N{+uSwFDryLP1zGW+3j_f z-+8XI(h29&uG%k_UQsKmWSi^$KWlf_OX2n<@+^zIPHqloZR>ndabpUqzy&l`Hszg-v_utEW@*y?0a;sN3oPbGner ze%{P6CUMou7?<*D*<E1Hs=N}W(B%`*S+{dJ@wI{Ff*ftq=CCk??)fE$4Ii{AjteK#6>||kd z@R=E#th76N9-1C5=yrQ%w_oh=p{O}hQ@Up?dUI-zUWi!b87tj~(G5nDa?IwhzI~C> z>YQozDXnZ%!R4SW=Yk&RU8(S0b}HhV;NFRms=UnC*-P#`{p?|MaTB{#uj&UYoqJDj z-nakYy65wacUxFieq1$ES61iOt^g*RAKv*+6%xIR?=4hxynHQr_KY_-)cK^8m#n-H-ad6q(n9`*w)mf|ZIICf01QyHutIceae3m&j{^hjosYP60{`y5=LbPcC__c82(-mYTHLbrTo@VO zOt$=5;CEqgX5O23?z#8e`<|BP=jRFlH-69RmRHcm>y7glL4TVBt^EblPl4Xw-{axo zLGG8gM$O)ayeXI zUkf0?`uh6h_w`%3G{$ptb68wll<_ZtlFi=U9FEjYX&?{?AQp=moFb@QeRF$z+hI66J1c;P zhlfZelM+7WQg6(s`p)5Spj0Y}9_Hug4Ne(A3D{^fFn=;Kg0;0Zl*?r#5{ZsrrlzJa zF)<{=|GxD zC=QWcy^cbmfMT&I?ln+%lI8nU*IyLUX&_qDV3zL(Oh(ZoUS}PQ!ds z0PH||W7$yAbYgXNwQG!wnHIHlIG5}73@<^MPk*)eDfxUpFPCZ*3QJ0@VWx_aqR@IW zCA-nlQCwVHKn*)DhMXnvkvb(PHv!I7&Cmw9(B|f*C{HbFZCBB&)T|?E`|kSsx>-l6 zTh%v|KKm^})e_M5P_oi4GeC-tor`VM%&cZFmzI{K5in0G@O!51GVIwV&E7n`X8Wne z$UlsgKJ@>*pQd60OJ7g%42pk QLI3~&07*qoM6N<$g3j!pGXMYp delta 822 zcmV-61Ihf|2F(VLB!2{FK}|sb0I`n?{9y$E00RU`L_t(Y4eeFGPg7A8{$5+90aMVb zL<@wZ!HE2bgO)fnad30i#l%V71fz-l7r;bCN8KD?FmZEpV=&a17!V~UBwC6FV+9&Y zO6a4W@6gwu?>+~Vi7$CQ@19@ZJNKS@Y83_8WBB%%PwDna|9|VQ2mY4*)6ls9Be zjF;?uXB^|1#|;gn)61)Pvz)CtOP*6Qlf%@H746FF(SPGor434UDfP6|uMBx-9_Fh@ z1L^2&O14$HRf4J?>YjeusrFWH1d%2`D2XT38)7tH9cv9~zF^~hW~0ufvYh^v!`JMF zRyP)ls^mH;dJ~wBd8<)F8VvYxF5FyguX!Ij+Jf#-GdMI7$pw{M$3$-e^D%EVYDj5F z40P#v>3?;QZgeEiaQIAz#yzcF13d$?N47J zkA+(hKXF*Ad$I6S&yZ3_rRZrdUzt)2^DtjM8c6AIf3Oq&Vr2+Wcc*Ws%IEXnk=>Ri zlaWWsb+w)Etd#N0;|AlvzxVbFUZ&SHIg{u>pnv4RD0Q~#9UHHU*x2#B!&f?5@$kYa zH%_=#O3%B4T?mSmV^6ORhkhVX9{luCJ&CBNorL1gIL4b`B#pELajiRCb52T9sLtPI?Sa4rz>40?Xs=>KV_U3JV`m?CNK74AaoEuUWvk%@u8i5 z^e^}N1LwyW8_DZScWd*kbhpV(@%yBvo%tvFKW=fGb^`-mBme*a07*qoM6N<$g6?FH A`Tzg` diff --git a/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon40.png b/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon40.png index cc7edcf5cb47d55bcd90dd20694555e7cc40e08f..86bba9e0fcb74b919c5ce493429c00c87764a9d9 100644 GIT binary patch delta 557 zcmV+|0@D4>2*w1EB!2;OQb$4nuFf3k00004XF*Lt006O%3;baP00009a7bBm000id z000id0mpBsWB>pG+et)0R9Hvtm$9zGFbqZC|NjXez}St6iHW5P3j+%S3j;%*4#H2C zpWQeOs0_W*6>6Krr-|c~pYr8e0eiIq_Np83dcBs@=~TvX{C_jo+<%8=0TO(_-bXyP5!Rw_Dlmb}mTG^rL^P(tzjl*$pfPV7uMA&$L$K$_1z!2Wg(gING_nBDJcgwC%Mk3R^TxkE|LqVJInOj z<<$im@gmH&t#4j6@EoZMC^=>UwP>TC+4=^!e(pI^6@M_Y)>i^9y2M^Nv0ikOWDott z%8d7s5P6Y8jl6*(lM-^w;g276h(O+8@AmAEh3xL@_0;U45D>NG;vCz0>1h%Wd7u36 zu&tMos(^^WydZO3%R8+UAj%Znj6d?XQ$%gcoHCy2S1atEngB%<=WMN`U1l>xngyr@ vEX$?~H73{53Dc@>z)x!h?9~d`r&4|aIhn`2==1LZ00000NkvXXu0mjfaG(Z@ delta 1080 zcmV-81jqZv1kDJLB!2{FK}|sb0I`n?{9y$E00aa{L_t(o3GG)qOq4+oo_nIG0Zxd3 zBFe)@qXKH9kyv3QMjLHRG_kd?HX<6(XklV3ZA_#vJ{m1FQW=Sr3IP+v2cSGfgB*w; z9RI`l_HNIgyZ`>%gM?ghlkEMQnb~i@o!On;bD1oZ#^O$6ZhwMWhTu{&l!;&_f=i9S zQpczL9`vTV!qJ&Iy6~o#UXA^sznVehaydirJ+RX2rv3S=>FSYqiHq$ykr=H266>=Xs2z=lhJ06(8P9}~7L%KoN zuzI&gY;x;~#i_jcewb^aPC0%rU8yc5(s_e%Yvw+5u)znu*0JKiFeb1uhm$xGyz_CG z`ew8RsehGVNY!Y_pxYmYgsQG2pN?(F6AgU`Z1Aye4PoFP=zxtmoFrkPp68WSKWV2) zgKJ$R0#+b#C^(&KTn4q(SJB!PJ_>UAz(&Z;^3m9Ah-#m9(v{0T3Y%T@93bON34aMrZ=Ar&IRh5dA;zT|k3W|n4 z(hQrmMgmg`hyxpQIEf=cKS%NWj*&>Bu?Z3ek$QLA_c3@_i?e^T_#jb?O*9fN`02L`ZIMVTM~Pi-j@&vv91(GaQRG$#Eb-Q4ix2SzHQoMH)@)_sc}lriGv yBqry|5R{Qv>^Bp^#YRq!%S2E{VzJ+}kKjKzI6RdL?()R|0000B!3xnMObuGZ)S9NVRB^vL1b@YWgtmyVP|DhWnpA_ami&o000IU zNkl%S(Cini~GvEn;F~L~3fPNJ~o- z$;rtgD=SN+r>6_AH#BP><^R$uWJPJ1Y(j4#dO5g9H~H9j$c@0F&Z8rlh2Z%F0Sn zQ&S@&|K&iL)bjGOn4g~)_xJa|<(!Hdk1T*Ox|Y@5e=bL6W~OLqX%TsOd0z+g>FG&~ zkB^Iujg4O>uRtl#L$-VEucheY-~)No}PXZ2~vzw(b?H4 zRny2#xPMETxw*L!KA%rUQ&*?}%e4T@XY1?hLkE;qot&JM95Y4|kcEYX5~L~#oJ(47 zZf?qQqd*&tf_5GJza-7f%t+1`7Z-;H=-%F*SYKcNC0&b(ibPjemxzmtldloI>FH^C zK7f@02$oys$g;3gQ&Uor(E?Y!F13-#o6{KC?|<&@{s2kz`uqFE*4CB;77CZcHA(-4 zg$4Ib{o2}^jFgHBk%W`&?d>021>DHUh!iWLZjPX;rKFuv zV|3cu+T;TAeRcoQ3q+}GFl$?1e*Qep4wnp1-3s3M2C zeMyuk-d23UVBm=OoN9fd-pV7JH-Gg`EuwZrX@5{h?QWb&OZJ`S0k?a4dc@Mwl5|1r zc&xTrECBE805x|Qg93`m>TaVD8ajApZ*2Xk=8`qC~$TZmta^?Kntkqoa>|#U}=969i8q44KQe;vxkN6M1K?;=?Ey? z!j_qZClFA|#^~s%^ucyE^_)u<=(bo$aXJl-#fTK59|Z4o%r@sL0Mt-B+cce>i_xjE z`*Ed?(k_9ky4^d~x!c~}mVnevC9XjUq7VN9o?HN%nFxyTFx$%CeQ#XY|vqM5GT?LA&1*(DJ8md{Y zdoRYe%pppM3OkXn8j8={Vbkg9Y5^tXT3cJCeS>e|9*Q=A)a}x10cD_^9UUFAROqxk z+=wJV8yXs9L(TQwi`5`ydV71NksysjvD5e+NGG=qLvkb7P2q9tMqY4OZtVvX#lPm( zzHvnSk2TCA;(vZ)9)IBvszL+QYTx+9Kaul=<+p(FH5l_Awk}{KjUS*tK)*52e*vrq j)x8yw|1TlF{{$ES>gFH7R=NoB00000NkvXXu0mjfZUh(N delta 1745 zcmV;?1}^#b4B-usB!2{FK}|sb0I`n?{9y$E00x*zL_t(&1?^c|Y*bYg-80k2P-tm+ zw%7*+3_+}IBiI-c>W5Jy(eTlj`1(+O{Lv2;3Gx9%2tN7X%b187CH^$g#NY?Pv=o@>gEH-OX58z{oHMtVd!2LdorH{?l}zVx_J7%Xt+V&p_uN}wfA$oV-Y9!V&U% zc=j==#_}M2ylE}1m>{= zFF9_0x3Zn}(1RBcF%ZiOTni0PMd{$V%c5lE>MPQ``hSU;X!3>6De-2a^7n*|z4VK1 zfmn#?7OsW9&*c=FNj8mH!CBN>>nf?YZ6ozR(?ZYIloKnRWismvk3CmgE^J}HgXfy< z(U=emG2OzI&_8cZ(AWJpRmo!IjToDrp+KrRZLVBM6p!7JRhdnUH7{PErjPPmBb0BB zftZNx27j)E?m68{F&|$qS&SlLh<>DZM3vc=x~0@sUBP0L%>)7!`15^bL29dCCVoO` zVy+pYggF*sBDNbi7Wz%^H9B=;Qf1Q|6ynEYM4w)`qK5WvSVh&r0AViz#%%6Z7bkEe^w8M>x;hoNUvWu_GZJPVpI;bM zTsBpf(@U$Ch(-gk0T#WpsaB1{7ISQ~Y48mW;Nk?@LjM_?q{BV-D=veoOmJoncVDC1 zGk@uo-_ck{YZnx<*f_P-RM5JLWD|l7Z0)jX>6^KLlPtX9r1rp;B!&WdSWH^*t~ z4_!2Tds=vDNQQ}U(|>vW zR5y*ree~g(rW$+ZuJ=zOZ|U;|t88CT9TzKcaCPOy3iK7vaE!*t!C0GkqBr7V>Wj+ zu2d!``R4hvp6jCNpljkdcc7zRjeiSUi!-q@;14P$#|9U0(uKBAIDB<|4OJEU)2AD| zD7@lZsut3QrCjceaXzST|FwtWXlSx?b2IsIWSou#g7($P*LCjE>7S` zXh~s!b~bPTo0XSkRm2T(M`QZbv-`Imx*Ulqk*aI*aqa*=>61?nFb8w3wtt7QQosRR zoWPM#INr*48ZC>tWNbNJlARt}Qqw zVuOQII2MWkdz$OTS=DOgF}`eG`f_n%c+~PHz?jvJ8+@Hk!8lj`CMyN!W5jj?A3L$` zDjB@(`sHu>vM)rwK(d5DTz{1pTyl8}aCstgN|?c~j=iLp;1kq~brrP#p*02%`X5~j z#Z7YS_Z<|9r5{=&tR!ZpFsoz@gPH8bVsHbh2qftElTB1!%r^xt;Hq>H2Laz$j|=_m zhhH*t=65@8W;^*E_Clnnj<|^BRwR767K+KwuUXEILU=#AtA4o141e~G7>MNso`m87 z*lwIoY%Vumh^4a`6EWn&Hn%=K2}RI{`N_f4EBKKnmoQ+r=ft4qALWC6!Z%`gg=e84 z@OE<@mH7AxJ$FWV$&ndx^^SedzTgwSvjNv#*Bt!Vz;!xw{tBJte*`CUYqrq;j@7@P z{}KG!x_juc8oj%dOiJFfCNxu^yFH$j(!JXvbmHfQ>a0>O+2k{tOWD%ln$M`tD&>+* nKBKvmEgi1;jOwgXF4_DG_&r&PcxYTT00000NkvXXu0mjfluKWV diff --git a/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png b/Gallery.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png index 2dd52620a8f15577e56ec7fe8e671988dd17ab0f..76ca617e1b92779ac2d3f89b486bb34d83badb28 100644 GIT binary patch delta 1673 zcmV;426p-B6Oj#&9De{h{Mm8<000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|DhWnpA_ zami&o000IvNklpe4P&djWR<32t)u3~&ta-yR8(TG*H0#naQ1xV*glyzcMs{coO-ApZUPC$h7%MP_EE$jZu+|MsT< z1M2wrSZr@^i__Cn@%Hv6!N0%1%g?~XXvixrE>5JRq=>SzGErV$E^>2oBLY6ow~0I7 zXPWC0=HWrCt$(eF-Q8Vrad9Eyi#O`-a&odr zN=lM*9v>e+JsHa5{B2@lqUi4K7S+|&KL_~c=0;3SO@E2~{e8J1J8BY>MMXs-BO^n4 zh)+O|jg5(ulM}}=3TOtNZfR-J3KJ{fuvCEhL z0Cm=@_zk)C{QSK91o*e9G%7qXF%bY3>his@u~A-7*y^R{=Vv*OOV>+)#+YaYqphti zEKh3zW`CoTlau1??92}=^d+Bmb#+N~H4i9%baW&Q#YJu=hV6tcWJ>uTAfRDUU0q#d z0YfR$4smEULQYPO7#tjw^i_j-tE;P$38XA@zST`vR?v8QdU{$GD*5^OkpX;ocqrD_ z*ZtxT&r%=^3=DjN#d~vebDzo$NGrYFckzb2_kZl{tQa01)?$m{l*)^Xi;|8?fRqaN z_xDRWGp?_%#mLBrEI^otajb&n)c_<)4~sl+9u5u;WE`5~79iwCXr;i8j*iN*(k>rk zAeEE3%-`LN&CN~e2+t_ZW>;2L0z6(@TPrFmD*VLEs@&>#cmuDzLz7y9N>F(}M z=6`hlE*#}1MN;XJ89V{Zf~c~J6AX}=nkt%`oBhNL23S3-)SJ1Kh=hQ3ur~3|rkSr3 z0?I(#yS~2OQ7nf9ytA_-H=-7!oW#3|%EXb1Su0q&9#R!4zHLQuY=qr)#>I~kRgm6HAd&{b?g!!=;6MqV|GiRyY7 zh!j+^(izGJYv=3^0@R>-qL9&1Z}x}q1zXoq3`13O-3u^toZQ~t1}H1m{;Fwb{(sWa zlH45e4UMfVfE|Pb9&=i-um_BfkwV3PP{3>uN~;^QFdH&c-V9hZ_t4%hJSa-o7@MNT zX8q32&H$sK?7Fe+#JeFWZQY1EcTz=z)x+ErI@(zoapNG5e2Y;OXzRfqgL^Q~DTs)+ zhZGnz{z}9mRXYC2xwM^yi4)b++J7;ELIp$EDKJ8Y0;|%pw6xSVZ6TD*b8)f}o`=Vug6WWb=x+x(eQve)KjIU z7l(YDbm*Ll8cw~$GaVLod+6rXAl?yF0DFFF$>2E=y=pir>h(} z{KYS(c@Nm^tzY=+VdMb+(s3VqeK-a<1~>-z&jF4m&<075v=lA>S?l3H0R{m7p9W(C TjZ|F#0000Tfox2hG|f7uRZ{7dCNS$k!NW<#` zm*kmICFk!tEERe?wf;US8WE@{jE&0m>|Jvej|>M>;}l{M410%2UXA^??LK1KUtXD` zAK%hILYdpqOMmcketcZ)f3kN#5T!9wRYjRhhB{W?N${Y8Q3y5z0UQeXJPow3@Yn+L z&e|;HM@Ph7Gyn{xyg9enMh7?WYD~3Ta-D}Lll8j@T`=&=`)#xdowZgv6NWJ|3bc~t5>jE^Fpn?n?6GRRVyNK#ZTFa!LMhxq0|hqy^K+S9TW_92m}wD7 z*Z`WE!Z@91Z?>zY9O`PcTpb;w#y|IwE`JiXn{#jUP-eyq@d|`(W1+1-m`(e?yhM*4 z_)O{{v>}_P!;g4Dpes32up%OiQ{h^d^n}o+Syi;BZCNC0sH<&k3?>W^FiQg9zylB6 za9ylW@JX~dNm#kdyEu9>%#X+QCY!BgLFp^aVTSsgqos|mfwA7=m7zlu7 z%liXya-)O2=Z~KC5jM}cPEsj|pMNlj08prz2NO~(l4dC`oe+$W2LWRggj$SDoF<|` z2u3{@hYXMA*)v5b6zD9DU<7+^-sh#`|1mUfAyn||Cocs07EHk$fIzRnE`|aMJr{?4 zG-@>>)-VVN#^zgh_%?xO^{}a0$wD<18D=dIL9_GBWkX{Z0)o508noN}Wq)RbP!{l; zrdv!~PcZ>tD(nK&gh4>?n`@gw<12}|#>WVuEMQajaS3fv?r2#QNs}%3Ltfwm0)mlk zm`V8nBM62705ZPBpW0(A9MHm{EwNIfoYZ(X48HDlORbuTVn8sO4*qnx`DE~})`|dr zhd}fn=5JAg>s+fr^POygZhx(>1&aWL3Z7G2O*ioJVOy}h^)4aqg0;m31HvEcJ|Xp@ zRPj4T1+4A)xtBo~1m_kRbB&DWX~FKp63p8QD7{z=0hTbdp&oU&at0sioG1vj7|6)+ z++FW&N5Ra!lwnvstc?>2+DO4@#1;fhNVzcsN$(Z&h}QQM7mGJpBIE)8P{9L#7t zif0^QI9eCk$BKaGAO7_Pz1V-2MiaJA3+iXm+igp!il^5kNF_LlNjdGws+Gev1&K!YrK zvNPLms;57;Hdew?Xe%}tKL#Ac-qbR-Vyzqo57ILRim4DbFnw(s6p|go@E(~?bHK%`e|w{Cgh z1U*i!wv}u!PN7=<)<^&4-&@?CRfV>%SwO45|2>sK*wAjmCYBRVy0t^2;A3**&Ca%# zg>qu0L^!$HxR29%&gfAY*gSXh?!B(Mw0XCrW$D7uC^f%%B-B_{lr5r-T_2nD0P1n{ z*1-m8jzSH^6MuDR1C9bx3*Z4>E{Ma}32-=cflj4B>K^`LaEKmw>nIHucw_E(-4>|E zc!n(Uk}*XD7$Qj;PG1O!vC%m8Xs~6VtUWz5Q=s=AS`wLGap_VSeC*;Nt$U}R^41Sw zE+hLr(P;D@LkH(@G#%Y@+sg-<8x6JemaRbtKL;hpja7n+69e zy?kk+U4PqvE7^VyGE&Ro86LVp$25@2U@+RcY7zbV_BqDrD20-Yl@kLjPkfq_ zZC*T2*`$7!WiYtofO5;BlO@lpsWo#DjWc=e-n!y8YQN(q2P55U*#?9CJA9P9nNM{~ zT}slaHi2au``W4n^dSF{UoKQ+FoeqgiZ}}|as5oIux;$FA2rg7x!0#94aKY2U~t7R z5@3&W5@7%NtDFSjpQ`b_rVJcHwc<+oUNJ#&6t%jcj$Y->zxy9~6m4Anq!=70;96@- xy$)Y%ikGX;w87^L2q-2*)@x~L(JUw{E7REzpd_|UPfy1N3`VmEj0J{2e*D;KD6j$;jF_01 z&|vrt4u1fU105Y5>ej7WCjhdZ`g3D|y;DsrQu5!eMRKcHaIqeqWab91vN zpduGULfA9`%@)%#rZH=5Y)rd=9}u`*e}BJ@D{dh99=dE}V?)Qi_4V~*$zaoPaopZU zOp6T|WewSonB}HBU;t2SYpZ6KS4s5o<43*vynl9!SKIP~@w|ESMkf~2>=`RdGU=hA zA@%w5=Rd%LA+nn86$PAk@80PHp!r-9+RRV7u!sW#119xoHdZ8@o14?_<7I%**4B0c zELNBra&T}^jgF2UGcaFZ32VukBO@b5aWXPs#2j3Sg4xY3m`90%26BQ0qlSlvHOQ#& ze18Jvc1=P7N8SLFa>uR484U5k8MAngfcxszD;?lrUl_&jEVQjQ|8fgVD0ut!ZS3<$ z0&Ht*ORo--cN@$h8w%?f5MmY=5JBm;>bQz9R(f81p2^Ynv6(fw23dJ|c{%wkIX^$I zI}q;417KHIm#*W)R#sMYyplnBu?aBHXnzP=esah;0P*|x@0!ijrA(5)nYuIIXL0b9 zy1Kf^f;Kc@On?{!I8IH)#A&bGxpU|EJ(%(GZFLm!KnS6yB0o4L3i?V*K* z1@+;>2j6qXkwth+MMXt2fH7#;Ixtcz;Fvz#63`A7n2dg&B&ro_jEFg_Zj4a2M;1o5;g)L zagoX(F4J&J#`*2rw`77otYJmmnFRo-4+R|lj^6U>SnxOAW704yuHC+UTNl$ToROZ& zEdcJ#Sgiv_kn}5oYHDguWJZFi*MG`0IQm{N%d~SqykwymmkD6Fwx0ku0*q{hJ9-IK zGMls8$^g9Bh3hg=8{1@LX#gX;nn(`5i)$w0lJ0@=-p!jgPsA#K^>P!w%j96=X*%!q z>({yiLXrtG*vk35G+~kq3l%lVj>Ir`i^lDGq-sCFX+H;TP9PZoi*vwG>VMUSh6dH$ z-R(Q!YuB#D?#wz{Oo=eeJurSp!-$B5S+d{g=SD%yLN2f{axLN$X+Nm$qg})PqzPzI zg|Jcd6^2Qt6c)M%AnZci`WbB^WP)=mj-RYP6+_Tu^IgbiOu(X67 zV-daDP*!9G%{vpf6c?cDK?%g9qqwIuCh3KFZNfy&jJP88ZDPWej77F2jYsu+x8S8i z0BCe&P&rdCR_wHStj^j4&PGg+_nA8o#u!~z6;+{V;V1LL9w$-?>sh-Mvry;ix zvSpXoZUyB{RKa)FCbwZdefrdhy|Dlz2tRxFOlO*lz%syM%zb@*#>pdgsSjAay}ddx zpZCTguK4816D#-b8#TGuHR8z&TiPfoliL!kfNy#NvL;!_q-&6*k)UW!+RD1to&*W| zU_-uQ7H$Z$EO0;AaCVQ*BxOlII&&@_&ax#+FWkdfPM1g@|BGSi1Ew)8eS|rt{RI!n z=NB+{HoJiThT-f0yP%d$|1(OyOQRI9)Kb7wz*4|cz*4|c``-|(*%XHG!u3A^1^{jp Vamep^n&$uj002ovPDHLkV1kXW+f@Jn delta 2321 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 delta 2444 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 literal 2758 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 IM6N<$f@{GO5C8xG literal 0 HcmV?d00001 diff --git a/Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/Contents.json b/Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/Contents.json new file mode 100644 index 0000000..903ab49 --- /dev/null +++ b/Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images": [ + { + "idiom": "universal" + }, + { + "filename": "source-regular.png", + "scale": "1x", + "idiom": "universal" + }, + { + "filename": "source-regular@2x.png", + "scale": "2x", + "idiom": "universal" + }, + { + "filename": "source-regular@3x.png", + "scale": "3x", + "idiom": "universal" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} \ No newline at end of file diff --git a/Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/source-regular.png b/Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/source-regular.png new file mode 100644 index 0000000000000000000000000000000000000000..9da6fa817347898535ae0dd42c86db0e6dcae4f4 GIT binary patch literal 309 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!3HFQtmCqP6k~CayA#8@b22Z19F}xPUq=Rp zjs4tz5?O(K&H|6fVg?4j!ywFfJby(BQ1Fzei(`m||J_T`yoUk=ST1lKU|=(7U=&%v zz;1DXN$f%cXG{Xyw-&#mOzx%oeT))s9_J~a{%-$9&3j$f?c!QOKL*---y>dsXyJ*8 z#*9v{?WVcgPPPy;o!2FERQKtXBa@Y`bUL&>l44XSs%(9~VAaXlj~{VrO!Ll-pVN}G zL$~O;n%})TwT;@#4laMsHn$*RvV7y@?@wDY)Fbs)J>8=0QS?A;`Lx{LiyT+}a&@1P z{I+rj=LS`D+W(jKbLh*2~7az C%XB#a literal 0 HcmV?d00001 diff --git a/Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/source-regular@2x.png b/Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/source-regular@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ef894024dda979dc08a385fc89e6fe6c8c766cce GIT binary patch literal 506 zcmVPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0gp*UK~z{r?UsQ} z12GImI{*u?01L1H6EFcAumLNu0yD4#Td+*YllrBHNpeTqD?;j*zLd+^aq{h0*)jc3PWV}c-WVA-FUI2Gjb{y=2pLHY z)WT%*+>m^;))4H-&Xtjh)3C|fr+Yr(Q08D(#etP^7qhKdi?xeC34DyyzMDOta4PjF z#YJ1R?XQZy4scZuBW_c;B&4XU?->}<#0TpX&Vt%m_GXMYp07*qoM6N<$f-jHT3IG5A literal 0 HcmV?d00001 diff --git a/Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/source-regular@3x.png b/Gallery.iOS/Assets.xcassets/IconSourceRegular.imageset/source-regular@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6cd26c782f62a151c73ed5271f8824c300e22d74 GIT binary patch literal 668 zcmV;N0%QG&P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0x?NMK~!i%?U>$?Q()_v2CSzx8ukN(5 zv9Yo78@OwHY5e%&d*fr{P2;lo-xz`&ZyT4xCDFm9#3ROud^NwlH7)}ch`8{y3+kcq z&p|P&FfGLUMqP7NAPAu_ysFv8>kier*bn|F{xv=~($@096)Pajtyf~RD)?^nc8W|6 z#=`^*xS5f_gSk$IP<4tCj*3-Ib)Y=`@3p1@bFded@m8p^j~!jXPxW%w zo*bPI2^^6c5W%}W9M9e0S-1>1s&KNGpJ&g5{H)^C@X6@u3|Y9#s0OCb9eb@;;*i1* zYn4M5=V99+Sjg5^0r_(2H6X_O!y%!Py0000!lvI6;>1s;*b3=DjSL74G){)!Z!;6hIq#}Etux04cin+*iqv=i724491#SS1b~ z)Jb6EH8xBv=>Ouq<5|?T`acg;^_MRF{?*CT{4US4TMv)(RL;za`1EaGI+ZB KxvXPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0WC>HK~z{r?UsS9 z!Y~X4I{*v(1z3OunBXtM2CTp?EWs8m136S9%XO*KxI#dmbP{rta%!BW-I_JOnoVK1 z5PyZLYzw!A`7eZXEqFJ{Cj6xALEQZU&U^cW9%#H(vu zAsSeEMh56fbA-4jnc5r!A(p}$pleXGWFjMZB{dMD;0@3r7K$(?Bk`n~ zg)Tj|J|!cuz1Jj!2)zVaDk?+9{_mNnkjO~*O)u5RkQ5;+(~=Y+c2-utQ;1%XcTg** zg1+}Gp(U9Vl8>gW(|n4-X0bwu5ZTuHS|%Jt?kmFz0qgv>ctXI_Vfnb8MJNpPgq9!_ zKC->Z+0ZlLL_RfJsXg-^Ps*-&7Fp-unl+!2uDb)TbKy@f+H^wz0000&A literal 0 HcmV?d00001 diff --git a/Gallery.iOS/Assets.xcassets/IconYandere.imageset/yandere-solid@3x.png b/Gallery.iOS/Assets.xcassets/IconYandere.imageset/yandere-solid@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..af788b11eac3e5926f531c40f58cd21943496fba GIT binary patch literal 512 zcmV+b0{{JqP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0hLKaK~!i%?U`Xw z#4r#>D*yu+zyJnNfC3!g01H?^114~R8Q3?&GM%tVlS{JcG5cn|Ki5mTuSs)FZwrDT z2!h~$aVL3R9BLTKi{$Ghp_U;$NURgA3^jH0Cb2qS8EWe0U1D`km=^ax@tm&I)J;yz zx{>+J`tImV6D!@o9wRqRjH#D{W9j!xd6l$|bd%VV7z=_nmRRNgwdfr=^3e%Bf05ft*MbxN^++XYmriN za;{>fN3Tny6zf^lO^hoG2&Bh)q~NH&EWKhXrR?Q;rC^CzDy1MaLJHi)a#Evl6&teA ztHf1|HeQ5SN{F2(A(l2|!>M>|^;=Wg5B_3_ky{B^`?b}frbBXK`*(Lpp5gr>HRuID zBqv55C09#Hj4+?8hW>0EiD{Sy>AxQ{Q$6K!)QWkBy1K}1zgK6-?%_jXb-pUpGX$=V zz6D+#>KMA$W94Icw{B#waQ>@YfrB6jf*|!lvI6;>1s;*b3=DjSL74G){)!Z!;5<(k#}Etux03>STMPtTv>BMw8JK@K9JD;Z zD7K-2O{eJdB)yxHbTj||*x?z+^`>N(u5W$mYvpr%VS-sg5)#jO&I`sjZoi;m)_AaM z+bZ#cYVY@!q|_9O?<*BsbgW~QWA_KwT~B^1=p0FiKI>TbXzuRsK_W-0d24(f(`uON zO*=k6m|(c&z#J#BD@t`t;^ra#;t$>jD{d2+>)d-?d#SvCY`DcYr|?5Smoj*|`njxg HN@xNAT?<~{ literal 0 HcmV?d00001 diff --git a/Gallery.iOS/Assets.xcassets/IconYandereRegular.imageset/yandere-regular@2x.png b/Gallery.iOS/Assets.xcassets/IconYandereRegular.imageset/yandere-regular@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6491ebb14a97a3093a9fd731b36f2fb98e58e6f7 GIT binary patch literal 407 zcmV;I0cie-P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0W3*GK~z{r?Uro~ z!axuOD*yu+zyLllf&(mI0S%bI1!iF9$4i!S*Dhr*B;{T*32g6Ua@|6Qo}Pcr#@IJv zYkZWQ@o3D!2$y>R?ldF zCP=8%AV=V>ytuE_M6m%Xg)s62#$&2_>5(1F zh_O^yh=Gb~l}NfY9;>Pt}8D|9mW zz7!=*C@bp{0)Olo_*a{-5EE5z$@mAhD#XB+QvPr1B{iX+rL_>Jz#b5a7vdHnm5FcQ zP)hGPEkGPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0k26!K~!i%?U;>C z#4rqnI{*u?01L1H6EFcAumLNu0yD4#Td>UGCt>tt<;T}1aRGnQli-@9{_dK#$(AHZ zk|fFB;!g4$9MmwB7s=%$K`ldgkd#gk8Ps%`H%V#m$e^afyi0h{`=D?QsOd0PEDEk+ zi5RYTv_W1aH_k)gz@T$?@@JVvylBR_+=p4eDa}*b*@- zrBS&(Qb?#6<`f^ZIr0J>DrWnROp4V=vF|XLV`x7k_L;~vOcz@T*N|e|>EDXs|A^Qc zy7a`(w5O?(X0cBd&i!*-ML?dUFWbWq2TUrWJ54|{? + + + + + + + + + + + false - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - - - - - + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + @@ -168,6 +163,15 @@ Gallery.Danbooru + + + + + + + + + \ No newline at end of file diff --git a/Gallery.iOS/Renderers/AppShellRenderer.cs b/Gallery.iOS/Renderers/AppShellRenderer.cs new file mode 100644 index 0000000..2415d4c --- /dev/null +++ b/Gallery.iOS/Renderers/AppShellRenderer.cs @@ -0,0 +1,93 @@ +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Gallery.iOS.Renderers; +using Gallery.iOS.Renderers.AppShellSection; +using Gallery.Services; +using UIKit; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +[assembly: ExportRenderer(typeof(Shell), typeof(AppShellRenderer))] +namespace Gallery.iOS.Renderers +{ + public class AppShellRenderer : ShellRenderer + { + + public override bool PrefersHomeIndicatorAutoHidden => Screen.GetHomeIndicatorAutoHidden(Element); + + protected override IShellSectionRenderer CreateShellSectionRenderer(ShellSection shellSection) + { + var renderer = base.CreateShellSectionRenderer(shellSection); // new AppShellSectionRenderer(this); + if (renderer is ShellSectionRenderer sr && Element is AppShell shell) + { + shell.SetNavigationBarHeight(sr.NavigationBar.Frame.Height); + shell.SetStatusBarHeight( + sr.NavigationBar.Frame.Height, + UIApplication.SharedApplication.StatusBarFrame.Height); + } + return renderer; + } + + protected override IShellItemTransition CreateShellItemTransition() + { + return new AppShellItemTransition(); + } + + protected override IShellTabBarAppearanceTracker CreateTabBarAppearanceTracker() + { + return new AppShellTabBarAppearanceTracker(); + } + + protected override IShellNavBarAppearanceTracker CreateNavBarAppearanceTracker() + { + return new AppShellNavBarAppearanceTracker(); + } + + protected override void UpdateBackgroundColor() + { + NativeView.BackgroundColor = Color.Transparent.ToUIColor(); + } + } + + public class AppShellItemTransition : IShellItemTransition + { + [SuppressMessage("Code Notifications", "XI0001:Notifies you with advices on how to use Apple APIs", Justification = "")] + public Task Transition(IShellItemRenderer oldRenderer, IShellItemRenderer newRenderer) + { + var task = new TaskCompletionSource(); + var oldView = oldRenderer.ViewController.View; + var newView = newRenderer.ViewController.View; + newView.Alpha = 0; + + newView.Superview.InsertSubviewAbove(newView, oldView); + + UIView.Animate(0.2, 0, UIViewAnimationOptions.BeginFromCurrentState, () => newView.Alpha = 1, () => task.TrySetResult(true)); + + return task.Task; + } + } + + public class AppShellSectionRenderer : ShellSectionRenderer + { + public AppShellSectionRenderer(IShellContext context) : base(context) + { + } + + protected override IShellSectionRootRenderer CreateShellSectionRootRenderer(ShellSection shellSection, IShellContext shellContext) + { + return new AppShellSectionRootRenderer(shellSection, shellContext); + } + } + + public class AppShellSectionRootRenderer : ShellSectionRootRenderer + { + public AppShellSectionRootRenderer(ShellSection shellSection, IShellContext shellContext) : base(shellSection, shellContext) + { + } + + protected override IShellSectionRootHeader CreateShellSectionRootHeader(IShellContext shellContext) + { + return new AppShellSectionRootHeader(shellContext); + } + } +} diff --git a/Gallery.iOS/Renderers/AppShellSection/AppAppearanceTracker.cs b/Gallery.iOS/Renderers/AppShellSection/AppAppearanceTracker.cs new file mode 100644 index 0000000..0bcebbd --- /dev/null +++ b/Gallery.iOS/Renderers/AppShellSection/AppAppearanceTracker.cs @@ -0,0 +1,168 @@ +using System.Diagnostics.CodeAnalysis; +using CoreGraphics; +using UIKit; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +namespace Gallery.iOS.Renderers.AppShellSection +{ + public class AppShellNavBarAppearanceTracker : IShellNavBarAppearanceTracker + { + UIColor _defaultBarTint; + UIColor _defaultTint; + UIStringAttributes _defaultTitleAttributes; + float _shadowOpacity = float.MinValue; + CGColor _shadowColor; + + public void ResetAppearance(UINavigationController controller) + { + if (_defaultTint != null) + { + var navBar = controller.NavigationBar; + navBar.TintColor = _defaultBarTint; + navBar.TintColor = _defaultTint; + navBar.TitleTextAttributes = _defaultTitleAttributes; + } + } + + public void SetAppearance(UINavigationController controller, ShellAppearance appearance) + { + var background = appearance.BackgroundColor; + var foreground = appearance.ForegroundColor; + var titleColor = appearance.TitleColor; + + var navBar = controller.NavigationBar; + + if (_defaultTint == null) + { + _defaultBarTint = navBar.BarTintColor; + _defaultTint = navBar.TintColor; + _defaultTitleAttributes = navBar.TitleTextAttributes; + } + + if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0)) + { + navBar.TintColor = UIColor.SecondaryLabelColor; + } + else + { + if (!background.IsDefault) + navBar.BarTintColor = background.ToUIColor(); + if (!foreground.IsDefault) + navBar.TintColor = foreground.ToUIColor(); + if (!titleColor.IsDefault) + { + navBar.TitleTextAttributes = new UIStringAttributes + { + ForegroundColor = titleColor.ToUIColor() + }; + } + } + } + + public void SetHasShadow(UINavigationController controller, bool hasShadow) + { + var navigationBar = controller.NavigationBar; + if (_shadowOpacity == float.MinValue) + { + // Don't do anything if user hasn't changed the shadow to true + if (!hasShadow) + return; + + _shadowOpacity = navigationBar.Layer.ShadowOpacity; + _shadowColor = navigationBar.Layer.ShadowColor; + } + + if (hasShadow) + { + navigationBar.Layer.ShadowColor = UIColor.Black.CGColor; + navigationBar.Layer.ShadowOpacity = 1.0f; + } + else + { + navigationBar.Layer.ShadowColor = _shadowColor; + navigationBar.Layer.ShadowOpacity = _shadowOpacity; + } + } + + public void Dispose() + { + } + + public void UpdateLayout(UINavigationController controller) + { + } + } + + public class AppShellTabBarAppearanceTracker : IShellTabBarAppearanceTracker + { + UIColor _defaultBarTint; + UIColor _defaultTint; + UIColor _defaultUnselectedTint; + + public void ResetAppearance(UITabBarController controller) + { + if (_defaultTint == null) + return; + + var tabBar = controller.TabBar; + tabBar.BarTintColor = _defaultBarTint; + tabBar.TintColor = _defaultTint; + tabBar.UnselectedItemTintColor = _defaultUnselectedTint; + } + + public void SetAppearance(UITabBarController controller, ShellAppearance appearance) + { + IShellAppearanceElement appearanceElement = appearance; + var backgroundColor = appearanceElement.EffectiveTabBarBackgroundColor; + var unselectedColor = appearanceElement.EffectiveTabBarUnselectedColor; + var tintColor = appearanceElement.EffectiveTabBarForegroundColor; // appearanceElement.EffectiveTabBarTitleColor; + + var tabBar = controller.TabBar; + + if (_defaultTint == null) + { + _defaultBarTint = tabBar.BarTintColor; + _defaultTint = tabBar.TintColor; + _defaultUnselectedTint = tabBar.UnselectedItemTintColor; + } + + if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0)) + { + tabBar.TintColor = UIColor.LabelColor; + //tabBar.UnselectedItemTintColor = UIColor.TertiaryLabelColor; + } + else + { + if (!backgroundColor.IsDefault) + tabBar.BarTintColor = backgroundColor.ToUIColor(); + if (!tintColor.IsDefault) + tabBar.TintColor = tintColor.ToUIColor(); + if (!unselectedColor.IsDefault) + tabBar.UnselectedItemTintColor = unselectedColor.ToUIColor(); + } + } + + public void Dispose() + { + } + + [SuppressMessage("Code Notifications", "XI0001:Notifies you with advices on how to use Apple APIs", Justification = "")] + public void UpdateLayout(UITabBarController controller) + { + var tabBar = controller.TabBar; + if (tabBar != null && tabBar.Items != null && tabBar.Items.Length == 3) + { + var tabBarItem = tabBar.Items[0]; + tabBarItem.Image = UIImage.FromBundle("IconYandereRegular"); + tabBarItem.SelectedImage = UIImage.FromBundle("IconYandere"); + tabBarItem = tabBar.Items[1]; + tabBarItem.Image = UIImage.FromBundle("IconSourceRegular"); + tabBarItem.SelectedImage = UIImage.FromBundle("IconSource"); + tabBarItem = tabBar.Items[2]; + tabBarItem.Image = UIImage.FromBundle("IconSourceRegular"); + tabBarItem.SelectedImage = UIImage.FromBundle("IconSource"); + } + } + } +} diff --git a/Gallery.iOS/Renderers/AppShellSection/AppShellSectionRootHeader.cs b/Gallery.iOS/Renderers/AppShellSection/AppShellSectionRootHeader.cs new file mode 100644 index 0000000..6f17704 --- /dev/null +++ b/Gallery.iOS/Renderers/AppShellSection/AppShellSectionRootHeader.cs @@ -0,0 +1,325 @@ +using System; +using System.Collections.Specialized; +using System.ComponentModel; +using CoreGraphics; +using Foundation; +using UIKit; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +namespace Gallery.iOS.Renderers.AppShellSection +{ + public class AppShellSectionRootHeader : UICollectionViewController, IAppearanceObserver, IShellSectionRootHeader + { + #region IAppearanceObserver + + readonly Color _defaultBackgroundColor = new(0.964); + readonly Color _defaultForegroundColor = Color.Black; + readonly Color _defaultUnselectedColor = Color.Black.MultiplyAlpha(0.7); + + void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance) + { + if (appearance == null) + ResetAppearance(); + else + SetAppearance(appearance); + } + + protected virtual void ResetAppearance() + { + SetValues(_defaultBackgroundColor, _defaultForegroundColor, _defaultUnselectedColor); + } + + protected virtual void SetAppearance(ShellAppearance appearance) + { + SetValues(appearance.BackgroundColor.IsDefault ? _defaultBackgroundColor : appearance.BackgroundColor, + appearance.ForegroundColor.IsDefault ? _defaultForegroundColor : appearance.ForegroundColor, + appearance.UnselectedColor.IsDefault ? _defaultUnselectedColor : appearance.UnselectedColor); + } + + void SetValues(Color backgroundColor, Color foregroundColor, Color unselectedColor) + { + CollectionView.BackgroundColor = new Color(backgroundColor.R, backgroundColor.G, backgroundColor.B, .663).ToUIColor(); + _bar.BackgroundColor = foregroundColor.ToUIColor(); + + bool reloadData = _selectedColor != foregroundColor || _unselectedColor != unselectedColor; + + _selectedColor = foregroundColor; + _unselectedColor = unselectedColor; + + if (reloadData) + CollectionView.ReloadData(); + } + + #endregion IAppearanceObserver + + static readonly NSString CellId = new("HeaderCell"); + + readonly IShellContext _shellContext; + UIView _bar; + UIView _bottomShadow; + Color _selectedColor; + Color _unselectedColor; + bool _isDisposed; + + public AppShellSectionRootHeader(IShellContext shellContext) : base(new UICollectionViewFlowLayout()) + { + _shellContext = shellContext; + } + + public double SelectedIndex { get; set; } + public ShellSection ShellSection { get; set; } + IShellSectionController ShellSectionController => ShellSection; + + public UIViewController ViewController => this; + + public override bool CanMoveItem(UICollectionView collectionView, NSIndexPath indexPath) + { + return false; + } + + public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath) + { + var reusedCell = (UICollectionViewCell)collectionView.DequeueReusableCell(CellId, indexPath); + + if (reusedCell is not ShellSectionHeaderCell headerCell) + return reusedCell; + + var selectedItems = collectionView.GetIndexPathsForSelectedItems(); + + var shellContent = ShellSectionController.GetItems()[indexPath.Row]; + headerCell.Label.Text = shellContent.Title; + headerCell.Label.SetNeedsDisplay(); + + headerCell.SelectedColor = _selectedColor.ToUIColor(); + headerCell.UnSelectedColor = _unselectedColor.ToUIColor(); + + if (selectedItems.Length > 0 && selectedItems[0].Row == indexPath.Row) + headerCell.Selected = true; + else + headerCell.Selected = false; + + return headerCell; + } + + public override nint GetItemsCount(UICollectionView collectionView, nint section) + { + return ShellSectionController.GetItems().Count; + } + + public override void ItemDeselected(UICollectionView collectionView, NSIndexPath indexPath) + { + if (CollectionView.CellForItem(indexPath) is ShellSectionHeaderCell cell) + cell.Label.TextColor = _unselectedColor.ToUIColor(); + } + + public override void ItemSelected(UICollectionView collectionView, NSIndexPath indexPath) + { + var row = indexPath.Row; + + var item = ShellSectionController.GetItems()[row]; + + if (item != ShellSection.CurrentItem) + ShellSection.SetValueFromRenderer(ShellSection.CurrentItemProperty, item); + + if (CollectionView.CellForItem(indexPath) is ShellSectionHeaderCell cell) + cell.Label.TextColor = _selectedColor.ToUIColor(); + } + + public override nint NumberOfSections(UICollectionView collectionView) + { + return 1; + } + + public override bool ShouldSelectItem(UICollectionView collectionView, NSIndexPath indexPath) + { + var row = indexPath.Row; + var item = ShellSectionController.GetItems()[row]; + IShellController shellController = _shellContext.Shell; + + if (item == ShellSection.CurrentItem) + return true; + return shellController.ProposeNavigation(ShellNavigationSource.ShellContentChanged, (ShellItem)ShellSection.Parent, ShellSection, item, ShellSection.Stack, true); + } + + public override void ViewDidLayoutSubviews() + { + if (_isDisposed) + return; + + base.ViewDidLayoutSubviews(); + + LayoutBar(); + + _bottomShadow.Frame = new CGRect(0, CollectionView.Frame.Bottom, CollectionView.Frame.Width, 0.5); + } + + public override void ViewDidLoad() + { + if (_isDisposed) + return; + + base.ViewDidLoad(); + + CollectionView.ScrollsToTop = false; + CollectionView.Bounces = false; + CollectionView.AlwaysBounceHorizontal = false; + CollectionView.ShowsHorizontalScrollIndicator = false; + CollectionView.ClipsToBounds = false; + + _bar = new UIView(new CGRect(0, 0, 20, 20)); + _bar.Layer.ZPosition = 9001; //its over 9000! + CollectionView.AddSubview(_bar); + + _bottomShadow = new UIView(new CGRect(0, 0, 10, 1)) + { + BackgroundColor = Color.Black.MultiplyAlpha(0.3).ToUIColor() + }; + _bottomShadow.Layer.ZPosition = 9002; + CollectionView.AddSubview(_bottomShadow); + + var flowLayout = Layout as UICollectionViewFlowLayout; + flowLayout.ScrollDirection = UICollectionViewScrollDirection.Horizontal; + flowLayout.MinimumInteritemSpacing = 0; + flowLayout.MinimumLineSpacing = 0; + flowLayout.EstimatedItemSize = new CGSize(70, 35); + + CollectionView.RegisterClassForCell(GetCellType(), CellId); + + ((IShellController)_shellContext.Shell).AddAppearanceObserver(this, ShellSection); + ShellSectionController.ItemsCollectionChanged += OnShellSectionItemsChanged; + + UpdateSelectedIndex(); + ShellSection.PropertyChanged += OnShellSectionPropertyChanged; + } + + protected virtual Type GetCellType() + { + return typeof(ShellSectionHeaderCell); + } + + protected override void Dispose(bool disposing) + { + if (_isDisposed) + return; + + if (disposing) + { + ((IShellController)_shellContext.Shell).RemoveAppearanceObserver(this); + ShellSectionController.ItemsCollectionChanged -= OnShellSectionItemsChanged; + ShellSection.PropertyChanged -= OnShellSectionPropertyChanged; + + ShellSection = null; + _bar.RemoveFromSuperview(); + RemoveFromParentViewController(); + _bar.Dispose(); + _bar = null; + } + + _isDisposed = true; + base.Dispose(disposing); + } + + protected void LayoutBar() + { + if (SelectedIndex < 0) + return; + + if (ShellSectionController.GetItems().IndexOf(ShellSection.CurrentItem) != SelectedIndex) + return; + + var layout = CollectionView.GetLayoutAttributesForItem(NSIndexPath.FromItemSection((int)SelectedIndex, 0)); + + if (layout == null) + return; + + var frame = layout.Frame; + + if (_bar.Frame.Height != 2) + { + _bar.Frame = new CGRect(frame.X, frame.Bottom - 2, frame.Width, 2); + } + else + { + UIView.Animate(.25, () => _bar.Frame = new CGRect(frame.X, frame.Bottom - 2, frame.Width, 2)); + } + } + + protected virtual void OnShellSectionPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == ShellSection.CurrentItemProperty.PropertyName) + { + UpdateSelectedIndex(); + } + } + + protected virtual void UpdateSelectedIndex(bool animated = false) + { + if (ShellSection.CurrentItem == null) + return; + + SelectedIndex = ShellSectionController.GetItems().IndexOf(ShellSection.CurrentItem); + + if (SelectedIndex < 0) + return; + + LayoutBar(); + + CollectionView.SelectItem(NSIndexPath.FromItemSection((int)SelectedIndex, 0), false, UICollectionViewScrollPosition.CenteredHorizontally); + } + + void OnShellSectionItemsChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (_isDisposed) + return; + + CollectionView.ReloadData(); + } + + public class ShellSectionHeaderCell : UICollectionViewCell + { + public UIColor SelectedColor { get; set; } + public UIColor UnSelectedColor { get; set; } + + public ShellSectionHeaderCell() + { + + } + + [Export("initWithFrame:")] + public ShellSectionHeaderCell(CGRect frame) : base(frame) + { + Label = new UILabel + { + TextAlignment = UITextAlignment.Center, + Font = UIFont.BoldSystemFontOfSize(14) + }; + ContentView.AddSubview(Label); + } + + public override bool Selected + { + get => base.Selected; + set + { + base.Selected = value; + Label.TextColor = value ? SelectedColor : UnSelectedColor; + } + } + + public UILabel Label { get; } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + Label.Frame = Bounds; + } + + public override CGSize SizeThatFits(CGSize size) + { + return new CGSize(Label.SizeThatFits(size).Width + 30, 35); + } + } + } +} diff --git a/Gallery.iOS/Renderers/BlurryPanelRenderer.cs b/Gallery.iOS/Renderers/BlurryPanelRenderer.cs new file mode 100644 index 0000000..55ded8d --- /dev/null +++ b/Gallery.iOS/Renderers/BlurryPanelRenderer.cs @@ -0,0 +1,87 @@ +using CoreAnimation; +using Gallery.iOS.Renderers; +using Gallery.Resources.UI; +using UIKit; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +[assembly: ExportRenderer(typeof(BlurryPanel), typeof(BlurryPanelRenderer))] +namespace Gallery.iOS.Renderers +{ + public class BlurryPanelRenderer : ViewRenderer + { + private UIVisualEffectView nativeControl; + private CALayer bottom; + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + if (e.OldElement != null) + { + if (bottom != null) + { + if (bottom.SuperLayer != null) + { + bottom.RemoveFromSuperLayer(); + } + bottom.Dispose(); + bottom = null; + } + } + + if (e.NewElement != null) + { + e.NewElement.BackgroundColor = Color.Default; + if (Control == null) + { + var blur = UIBlurEffect.FromStyle(UIBlurEffectStyle.SystemMaterial); + nativeControl = new UIVisualEffectView(blur) + { + Frame = Frame + }; + SetNativeControl(nativeControl); + } + } + } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + if (nativeControl != null) + { + if (bottom == null) + { + bottom = new CALayer + { + BackgroundColor = UIColor.White.CGColor, + ShadowColor = UIColor.Black.CGColor, + ShadowOpacity = 1.0f + }; + } + if (bottom.SuperLayer == null) + { + nativeControl.Layer.InsertSublayer(bottom, 0); + } + bottom.Frame = new CoreGraphics.CGRect(0, Frame.Height - 5, Frame.Width, 5); + nativeControl.Frame = Frame; + } + } + + protected override void Dispose(bool disposing) + { + if (bottom != null) + { + if (bottom.SuperLayer != null) + { + bottom.RemoveFromSuperLayer(); + } + bottom.Dispose(); + bottom = null; + } + + base.Dispose(disposing); + } + } +} diff --git a/Gallery.iOS/Renderers/CardViewRenderer.cs b/Gallery.iOS/Renderers/CardViewRenderer.cs new file mode 100644 index 0000000..4de4aaa --- /dev/null +++ b/Gallery.iOS/Renderers/CardViewRenderer.cs @@ -0,0 +1,51 @@ +using Gallery.iOS.Renderers; +using Gallery.Resources.UI; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +[assembly: ExportRenderer(typeof(CardView), typeof(CardViewRenderer))] +namespace Gallery.iOS.Renderers +{ + public class CardViewRenderer : VisualElementRenderer + { + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + var layer = Layer; + var element = e.NewElement; + if (layer != null && element != null) + { + var cornerRadius = element.CornerRadius; + if (cornerRadius > 0) + { + layer.CornerRadius = cornerRadius; + } + + //if (element.BackgroundColor != default) + //{ + // layer.BackgroundColor = element.BackgroundColor.ToCGColor(); + //} + + var shadowColor = element.ShadowColor; + if (shadowColor != default) + { + layer.ShadowColor = shadowColor.ToCGColor(); + layer.ShadowOpacity = 1f; + + var radius = element.ShadowRadius; + if (radius > 0) + { + layer.ShadowRadius = radius; + } + + layer.ShadowOffset = element.ShadowOffset.ToSizeF(); + } + else + { + layer.ShadowOpacity = 0f; + } + } + } + } +} diff --git a/Gallery.iOS/Renderers/CircleImageRenderer.cs b/Gallery.iOS/Renderers/CircleImageRenderer.cs new file mode 100644 index 0000000..0afb1cf --- /dev/null +++ b/Gallery.iOS/Renderers/CircleImageRenderer.cs @@ -0,0 +1,33 @@ +using Gallery.iOS.Renderers; +using Gallery.Resources.UI; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +[assembly: ExportRenderer(typeof(CircleImage), typeof(CircleImageRenderer))] +namespace Gallery.iOS.Renderers +{ + public class CircleImageRenderer : ImageRenderer + { + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + var layer = Layer; + if (layer != null) + { + layer.MasksToBounds = true; + } + } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + var control = Control; + if (control != null) + { + control.Layer.CornerRadius = control.Frame.Size.Width / 2; + } + } + } +} diff --git a/Gallery.iOS/Renderers/OptionEntryRenderer.cs b/Gallery.iOS/Renderers/OptionEntryRenderer.cs new file mode 100644 index 0000000..a61809f --- /dev/null +++ b/Gallery.iOS/Renderers/OptionEntryRenderer.cs @@ -0,0 +1,22 @@ +using Gallery.iOS.Renderers; +using Gallery.Resources.UI; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +[assembly: ExportRenderer(typeof(OptionEntry), typeof(OptionEntryRenderer))] +namespace Gallery.iOS.Renderers +{ + public class OptionEntryRenderer : EntryRenderer + { + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + var control = Control; + if (control != null) + { + control.BorderStyle = UIKit.UITextBorderStyle.None; + } + } + } +} diff --git a/Gallery.iOS/Renderers/OptionPickerRenderer.cs b/Gallery.iOS/Renderers/OptionPickerRenderer.cs new file mode 100644 index 0000000..882e5e0 --- /dev/null +++ b/Gallery.iOS/Renderers/OptionPickerRenderer.cs @@ -0,0 +1,22 @@ +using Gallery.iOS.Renderers; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +[assembly: ExportRenderer(typeof(Picker), typeof(OptionPickerRenderer))] +namespace Gallery.iOS.Renderers +{ + public class OptionPickerRenderer : PickerRenderer + { + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + var control = Control; + if (control != null) + { + control.TextAlignment = UIKit.UITextAlignment.Right; + control.BorderStyle = UIKit.UITextBorderStyle.None; + } + } + } +} diff --git a/Gallery.iOS/Renderers/RoundImageRenderer.cs b/Gallery.iOS/Renderers/RoundImageRenderer.cs new file mode 100644 index 0000000..7ae019c --- /dev/null +++ b/Gallery.iOS/Renderers/RoundImageRenderer.cs @@ -0,0 +1,56 @@ +using CoreAnimation; +using Gallery.iOS.Renderers; +using Gallery.Resources.UI; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +[assembly: ExportRenderer(typeof(RoundImage), typeof(RoundImageRenderer))] +namespace Gallery.iOS.Renderers +{ + public class RoundImageRenderer : ImageRenderer + { + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + var layer = Layer; + if (layer != null && e.NewElement is RoundImage image) + { + bool flag = false; + if (image.CornerRadius > 0) + { + layer.CornerRadius = image.CornerRadius; + flag = true; + } + var mask = image.CornerMasks; + if (mask != CornerMask.None) + { + var m = default(CACornerMask); + if ((mask & CornerMask.LeftTop) == CornerMask.LeftTop) + { + m |= CACornerMask.MinXMinYCorner; + } + if ((mask & CornerMask.RightTop) == CornerMask.RightTop) + { + m |= CACornerMask.MaxXMinYCorner; + } + if ((mask & CornerMask.LeftBottom) == CornerMask.LeftBottom) + { + m |= CACornerMask.MinXMaxYCorner; + } + if ((mask & CornerMask.RightBottom) == CornerMask.RightBottom) + { + m |= CACornerMask.MaxXMaxYCorner; + } + + layer.MaskedCorners = m; + flag = true; + } + if (flag) + { + layer.MasksToBounds = true; + } + } + } + } +} diff --git a/Gallery.iOS/Renderers/RoundLabelRenderer.cs b/Gallery.iOS/Renderers/RoundLabelRenderer.cs new file mode 100644 index 0000000..f3e1310 --- /dev/null +++ b/Gallery.iOS/Renderers/RoundLabelRenderer.cs @@ -0,0 +1,56 @@ +using System.ComponentModel; +using Gallery.iOS.Renderers; +using Gallery.Resources.UI; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +[assembly: ExportRenderer(typeof(RoundLabel), typeof(RoundLabelRenderer))] +namespace Gallery.iOS.Renderers +{ + public class RoundLabelRenderer : LabelRenderer + { + protected override void OnElementChanged(ElementChangedEventArgs

"Gelbooru"; - + public string Route => "gelbooru"; + public string FlyoutIconKey => "Gelbooru"; public string HomePage => "https://gelbooru.com"; public Task GetRecentItemsAsync(int page) @@ -20,5 +22,17 @@ namespace Gallery.Gelbooru { throw new NotImplementedException(); } + + public void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark) + { + var icon = new FontImageSource + { + FontFamily = family, + Glyph = "\uf5d2", + Size = 18.0 + }; + light.Add(FlyoutIconKey, icon); + dark.Add(FlyoutIconKey, icon); + } } } diff --git a/GallerySources/Gallery.Yandere/GallerySource.cs b/GallerySources/Gallery.Yandere/GallerySource.cs index dd62ca6..b5fe3ca 100644 --- a/GallerySources/Gallery.Yandere/GallerySource.cs +++ b/GallerySources/Gallery.Yandere/GallerySource.cs @@ -4,12 +4,15 @@ using System.Threading.Tasks; using Gallery.Util; using Gallery.Util.Interface; using Gallery.Util.Model; +using Xamarin.Forms; namespace Gallery.Yandere { public class GallerySource : IGallerySource { public string Name => "Yande.re"; + public string Route => "yandere"; + public string FlyoutIconKey => "Yandere"; public string HomePage => "https://yande.re"; public async Task GetRecentItemsAsync(int page) @@ -61,5 +64,17 @@ namespace Gallery.Yandere { throw new NotImplementedException(); } + + public void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark) + { + var icon = new FontImageSource + { + FontFamily = family, + Glyph = "\uf302", + Size = 18.0 + }; + light.Add(FlyoutIconKey, icon); + dark.Add(FlyoutIconKey, icon); + } } }