diff --git a/Pixiview/Illust/ViewIllustPage.xaml b/Pixiview/Illust/ViewIllustPage.xaml index cbc3c19..2849fba 100644 --- a/Pixiview/Illust/ViewIllustPage.xaml +++ b/Pixiview/Illust/ViewIllustPage.xaml @@ -1,9 +1,7 @@  + + + + + - + + diff --git a/Pixiview/Illust/ViewIllustPage.xaml.cs b/Pixiview/Illust/ViewIllustPage.xaml.cs index d9016f3..6d551f2 100644 --- a/Pixiview/Illust/ViewIllustPage.xaml.cs +++ b/Pixiview/Illust/ViewIllustPage.xaml.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using System.Windows.Input; using Pixiview.Resources; using Pixiview.UI; using Pixiview.UI.Theme; @@ -22,10 +21,51 @@ namespace Pixiview.Illust nameof(Illusts), typeof(IllustDetailItem[]), typeof(ViewIllustPage)); public static readonly BindableProperty PagePositionTextProperty = BindableProperty.Create( nameof(PagePositionText), typeof(string), typeof(ViewIllustPage)); - public static readonly BindableProperty IsPageVisibleProperty = BindableProperty.Create( - nameof(IsPageVisible), typeof(bool), typeof(ViewIllustPage)); - public static readonly BindableProperty IllustItemProperty = BindableProperty.Create( - nameof(IllustItem), typeof(IllustItem), typeof(ViewIllustPage)); + public static readonly BindableProperty CurrentPageProperty = BindableProperty.Create( + nameof(CurrentPage), typeof(int), typeof(ViewIllustPage), propertyChanged: OnCurrentPagePropertyChanged); + public static readonly BindableProperty IsScrollAnimatedProperty = BindableProperty.Create( + nameof(IsScrollAnimated), typeof(bool), typeof(ViewIllustPage), true); + public static readonly BindableProperty IsAnimateSliderVisibleProperty = BindableProperty.Create( + nameof(IsAnimateSliderVisible), typeof(bool), typeof(ViewIllustPage)); + public static readonly BindableProperty IsAnimateSliderEnabledProperty = BindableProperty.Create( + nameof(IsAnimateSliderEnabled), typeof(bool), typeof(ViewIllustPage)); + public static readonly BindableProperty CurrentAnimeFrameProperty = BindableProperty.Create( + nameof(CurrentAnimeFrame), typeof(double), typeof(ViewIllustPage), propertyChanged: OnCurrentAnimeFramePropertyChanged); + public static readonly BindableProperty MaximumFrameProperty = BindableProperty.Create( + nameof(MaximumFrame), typeof(double), typeof(ViewIllustPage), 1.0); + + private static void OnCurrentPagePropertyChanged(BindableObject obj, object old, object @new) + { + var page = (ViewIllustPage)obj; + var index = (int)@new; + var items = page.Illusts; + var length = items.Length; + page.PagePositionText = $"{index + 1}/{length}"; + + var item = items[index]; + if (!item.Loading && item.Image == null) + { + Task.Run(() => page.DoLoadImage(index)); + } + if (index < length - 1) + { + item = items[index + 1]; + if (!item.Loading && item.Image == null) + { + Task.Run(() => page.DoLoadImage(index + 1)); + } + } + } + + private static void OnCurrentAnimeFramePropertyChanged(BindableObject obj, object old, object @new) + { + var page = (ViewIllustPage)obj; + if (page.ugoira != null && page.IsAnimateSliderEnabled) + { + var frame = (double)@new; + page.ugoira.ToggleFrame((int)frame); + } + } public ImageSource FavoriteIcon { @@ -43,21 +83,41 @@ namespace Pixiview.Illust get => (string)GetValue(PagePositionTextProperty); set => SetValue(PagePositionTextProperty, value); } - public bool IsPageVisible + public int CurrentPage { - get => (bool)GetValue(IsPageVisibleProperty); - set => SetValue(IsPageVisibleProperty, value); + get => (int)GetValue(CurrentPageProperty); + private set => SetValue(CurrentPageProperty, value); } - public IllustItem IllustItem + public bool IsScrollAnimated { - get => (IllustItem)GetValue(IllustItemProperty); - set => SetValue(IllustItemProperty, value); + get => (bool)GetValue(IsScrollAnimatedProperty); + set => SetValue(IsScrollAnimatedProperty, value); + } + public bool IsAnimateSliderVisible + { + get => (bool)GetValue(IsAnimateSliderVisibleProperty); + set => SetValue(IsAnimateSliderVisibleProperty, value); + } + public bool IsAnimateSliderEnabled + { + get => (bool)GetValue(IsAnimateSliderEnabledProperty); + set => SetValue(IsAnimateSliderEnabledProperty, value); + } + public double CurrentAnimeFrame + { + get => (double)GetValue(CurrentAnimeFrameProperty); + set => SetValue(CurrentAnimeFrameProperty, value); + } + public double MaximumFrame + { + get => (double)GetValue(MaximumFrameProperty); + set => SetValue(MaximumFrameProperty, value); } - public int CurrentPage { get; private set; } + public IllustItem IllustItem { get; private set; } + public bool IsPageVisible { get; private set; } private readonly bool saveFavorites; - private readonly ICommand longPressed; private readonly ImageSource fontIconLove; private readonly ImageSource fontIconNotLove; private IllustUgoiraData ugoiraData; @@ -67,7 +127,6 @@ namespace Pixiview.Illust { IllustItem = illust; saveFavorites = save; - longPressed = new Command(Illust_LongPressed); BindingContext = this; fontIconLove = (ImageSource)Application.Current.Resources[ThemeBase.FontIconLove]; @@ -77,7 +136,9 @@ namespace Pixiview.Illust ? fontIconLove : fontIconNotLove; - Resources.Add("carouselView", GetCarouseTemplate()); + var pageVisible = illust != null && illust.PageCount > 1; + IsPageVisible = pageVisible; + Resources.Add("carouselView", GetCarouseTemplate(pageVisible)); InitializeComponent(); @@ -115,65 +176,111 @@ namespace Pixiview.Illust Screen.SetHomeIndicatorAutoHidden(Shell.Current, false); } - private DataTemplate GetCarouseTemplate() + private DataTemplate GetCarouseTemplate(bool multiPages) { - var isAnime = IllustItem.IllustType == IllustType.Anime; var tap = new TapGestureRecognizer(); tap.Tapped += Image_Tapped; return new DataTemplate(() => { + // image var image = new Image { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, - Aspect = Aspect.AspectFit + Aspect = Aspect.AspectFit, + GestureRecognizers = { tap } } .Binding(Image.SourceProperty, nameof(IllustDetailItem.Image)); - if (isAnime) + + // downloading + var downloading = new Frame { - image.GestureRecognizers.Add(tap); + HasShadow = false, + Margin = default, + Padding = new Thickness(20), + CornerRadius = 8, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center, + Content = new ActivityIndicator + { + IsRunning = true, + IsVisible = true + } + .DynamicResource(ActivityIndicator.ColorProperty, ThemeBase.WindowColor) } - else + .Binding(IsVisibleProperty, nameof(IllustDetailItem.Loading)) + .DynamicResource(BackgroundColorProperty, ThemeBase.MaskColor); + + // loading original + var original = new ActivityIndicator { - image.SetBinding(LongPressEffect.CommandProperty, nameof(IllustDetailItem.LongPressed)); - image.SetBinding(LongPressEffect.CommandParameterProperty, "."); - image.Effects.Add(new LongPressEffect()); + IsRunning = true, + Margin = new Thickness(10), + HorizontalOptions = LayoutOptions.Start, + VerticalOptions = LayoutOptions.Start + } + .Binding(IsVisibleProperty, nameof(IllustDetailItem.Downloading)) + .DynamicResource(ActivityIndicator.ColorProperty, ThemeBase.TextColor); + + if (multiPages) + { + var tapPrevious = new TapGestureRecognizer(); + tapPrevious.Tapped += TapPrevious_Tapped; + var tapNext = new TapGestureRecognizer(); + tapNext.Tapped += TapNext_Tapped; + + return new Grid + { + Children = + { + // image + image, + + // tap holder + new Grid + { + RowDefinitions = + { + new RowDefinition(), + new RowDefinition(), + new RowDefinition() + }, + Children = + { + new Label + { + GestureRecognizers = { tapPrevious } + }, + new Label + { + GestureRecognizers = { tapNext } + } + .GridRow(2) + } + }, + + // downloading + downloading, + + // loading original + original + } + }; } return new Grid { Children = { + // image image, - new Frame - { - HasShadow = false, - Margin = default, - Padding = new Thickness(20), - CornerRadius = 8, - HorizontalOptions = LayoutOptions.Center, - VerticalOptions = LayoutOptions.Center, - Content = new ActivityIndicator - { - IsRunning = true, - IsVisible = true - } - .DynamicResource(ActivityIndicator.ColorProperty, ThemeBase.WindowColor) - } - .Binding(IsVisibleProperty, nameof(IllustDetailItem.Loading)) - .DynamicResource(BackgroundColorProperty, ThemeBase.MaskColor), + // downloading + downloading, - new ActivityIndicator - { - IsRunning = true, - Margin = new Thickness(10), - HorizontalOptions = LayoutOptions.Start, - VerticalOptions = LayoutOptions.Start - } - .Binding(IsVisibleProperty, nameof(IllustDetailItem.Downloading)) - .DynamicResource(ActivityIndicator.ColorProperty, ThemeBase.TextColor) + // loading original + original } }; }); @@ -189,20 +296,14 @@ namespace Pixiview.Illust var items = new IllustDetailItem[illust.PageCount]; if (items.Length > 1) { - IsPageVisible = true; PagePositionText = $"1/{items.Length}"; } - else - { - IsPageVisible = false; - } for (var i = 0; i < items.Length; i++) { items[i] = new IllustDetailItem { - Id = illust.Id, - LongPressed = longPressed + Id = illust.Id }; if (i == 0) { @@ -234,8 +335,7 @@ namespace Pixiview.Illust { tmp[i] = new IllustDetailItem { - Id = illustItem.Id, - LongPressed = longPressed + Id = illustItem.Id }; } Illusts = items = tmp; @@ -277,6 +377,12 @@ namespace Pixiview.Illust { // anime ugoiraData = Stores.LoadIllustUgoiraData(illustItem.Id); + if (ugoiraData != null && ugoiraData.body != null) + { + var length = ugoiraData.body.frames.Length; + MaximumFrame = length > 0 ? length : 1; + IsAnimateSliderVisible = true; + } } } @@ -309,24 +415,29 @@ namespace Pixiview.Illust private void CarouselView_PositionChanged(object sender, PositionChangedEventArgs e) { - var index = e.CurrentPosition; - CurrentPage = index; - var items = Illusts; - var length = items.Length; - PagePositionText = $"{index + 1}/{length}"; + CurrentPage = e.CurrentPosition; + } - var item = items[index]; - if (!item.Loading && item.Image == null) + private void TapPrevious_Tapped(object sender, EventArgs e) + { + var index = CurrentPage; + if (index > 0) { - Task.Run(() => DoLoadImage(index)); + IsScrollAnimated = false; + CurrentPage = index - 1; + IsScrollAnimated = true; } - if (index < length - 1) + } + + private void TapNext_Tapped(object sender, EventArgs e) + { + var index = CurrentPage; + var illusts = Illusts; + if (illusts != null && index < illusts.Length - 1) { - item = items[index + 1]; - if (!item.Loading && item.Image == null) - { - Task.Run(() => DoLoadImage(index + 1)); - } + IsScrollAnimated = false; + CurrentPage = index + 1; + IsScrollAnimated = true; } } @@ -360,6 +471,7 @@ namespace Pixiview.Illust if (ugoira != null) { var playing = !ugoira.IsPlaying; + IsAnimateSliderEnabled = !playing; ugoira.TogglePlay(playing); illustItem.IsPlaying = playing; } @@ -381,10 +493,20 @@ namespace Pixiview.Illust private void OnUgoiraFrameChanged(object sender, UgoiraEventArgs e) { e.DetailItem.Image = e.Image; + CurrentAnimeFrame = e.FrameIndex; } - private async void Illust_LongPressed(IllustDetailItem item) + private async void More_Clicked(object sender, EventArgs e) { + int p = CurrentPage; + var illusts = Illusts; + if (illusts == null || p < 0 || p >= illusts.Length) + { + return; + } + + var item = illusts[p]; + List extras = new List(); var share = ResourceHelper.Share; var preview = Stores.GetPreviewImagePath(item.PreviewUrl); @@ -496,7 +618,6 @@ namespace Pixiview.Illust set => SetValue(DownloadingProperty, value); } public string Id { get; set; } - public ICommand LongPressed { get; set; } public string PreviewUrl { get; set; } public string OriginalUrl { get; set; } } diff --git a/Pixiview/Utils/HttpUtility.cs b/Pixiview/Utils/HttpUtility.cs index c2c02b5..f22e1ec 100644 --- a/Pixiview/Utils/HttpUtility.cs +++ b/Pixiview/Utils/HttpUtility.cs @@ -302,6 +302,7 @@ namespace Pixiview.Utils private int index = 0; public bool IsPlaying { get; private set; } + public readonly int FrameCount; public event EventHandler FrameChanged; public Ugoira(IllustUgoiraData illust, IllustDetailItem item) @@ -309,6 +310,7 @@ namespace Pixiview.Utils ugoira = illust.body; detailItem = item; frames = new ImageSource[ugoira.frames.Length]; + FrameCount = frames.Length; timer = new Timer(OnTimerCallback, null, Timeout.Infinite, Timeout.Infinite); Task.Run(LoadFrames); @@ -332,6 +334,30 @@ namespace Pixiview.Utils } } + public void ToggleFrame(int frame) + { + if (IsPlaying) + { + // TODO: doesn't support change current frame when playing + return; + } + if (frame < 0 || frame >= frames.Length) + { + return; + } + var image = frames[frame]; + if (image != null) + { + index = frame; + FrameChanged?.Invoke(this, new UgoiraEventArgs + { + DetailItem = detailItem, + Image = image, + FrameIndex = frame + }); + } + } + private void OnTimerCallback(object state) { if (!IsPlaying) @@ -350,7 +376,8 @@ namespace Pixiview.Utils FrameChanged?.Invoke(this, new UgoiraEventArgs { DetailItem = detailItem, - Image = frame + Image = frame, + FrameIndex = i }); i++; if (i >= frames.Length) @@ -498,5 +525,6 @@ namespace Pixiview.Utils { public IllustDetailItem DetailItem { get; set; } public ImageSource Image { get; set; } + public int FrameIndex { get; set; } } }