diff --git a/Source/LibationAvalonia/Controls/DataGridMyRatingColumn.cs b/Source/LibationAvalonia/Controls/DataGridMyRatingColumn.cs index 3fefa38c..b563883e 100644 --- a/Source/LibationAvalonia/Controls/DataGridMyRatingColumn.cs +++ b/Source/LibationAvalonia/Controls/DataGridMyRatingColumn.cs @@ -2,14 +2,24 @@ using Avalonia.Controls; using Avalonia.Interactivity; using DataLayer; -using LibationAvalonia.ViewModels; using ReactiveUI; using System; namespace LibationAvalonia.Controls { + public class StarStringConverter : Avalonia.Data.Converters.IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + => value is Rating rating ? rating.ToStarString() : string.Empty; + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + => throw new NotImplementedException(); + } + public class DataGridMyRatingColumn : DataGridBoundColumn { + [Avalonia.Data.AssignBinding] + public Avalonia.Data.IBinding BackgroundBinding { get; set; } private static Rating DefaultRating => new Rating(0, 0, 0); public DataGridMyRatingColumn() { @@ -24,29 +34,20 @@ namespace LibationAvalonia.Controls IsEditingMode = false }; - ToolTip.SetTip(myRatingElement, "Click to change ratings"); cell?.AttachContextMenu(); + if (!IsReadOnly) + ToolTip.SetTip(myRatingElement, "Click to change ratings"); + if (Binding != null) { myRatingElement.Bind(BindingTarget, Binding); } - - void setControlBackground(object dataContext) + if (BackgroundBinding != null) { - if (dataContext is GridEntry ge) - myRatingElement.Background = ge.BackgroundBrush; + myRatingElement.Bind(MyRatingCellEditor.BackgroundProperty, BackgroundBinding); } - setControlBackground(cell?.DataContext); - - var subscriber = - cell - ?.ObservableForProperty(g => g.DataContext) - ?.Subscribe(ctx => setControlBackground(ctx?.Value)); - - myRatingElement.DetachedFromVisualTree += (_, _) => subscriber?.Dispose(); - return myRatingElement; } @@ -57,6 +58,10 @@ namespace LibationAvalonia.Controls Name = "CellMyRatingEditor", IsEditingMode = true }; + if (BackgroundBinding != null) + { + myRatingElement.Bind(MyRatingCellEditor.BackgroundProperty, BackgroundBinding); + } return myRatingElement; } diff --git a/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml b/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml index aa2e50f6..c477ba16 100644 --- a/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml +++ b/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml @@ -17,7 +17,7 @@ - + diff --git a/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs b/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs index 3c7e889f..c5100f14 100644 --- a/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs +++ b/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs @@ -1,6 +1,8 @@ using Avalonia; using Avalonia.Controls; using DataLayer; +using ReactiveUI; +using System; using System.Linq; namespace LibationAvalonia.Controls @@ -9,6 +11,7 @@ namespace LibationAvalonia.Controls { private const string SOLID_STAR = "★"; private const string HOLLOW_STAR = "☆"; + private const string HALF_STAR = "½"; public static readonly StyledProperty RatingProperty = AvaloniaProperty.Register(nameof(Rating)); @@ -19,39 +22,41 @@ namespace LibationAvalonia.Controls public MyRatingCellEditor() { InitializeComponent(); + + var subscriber = this.ObservableForProperty(p => p.Rating).Subscribe(o => DisplayStarRating(o.Value ?? new Rating(0, 0, 0))); + Unloaded += (_, _) => subscriber.Dispose(); + if (Design.IsDesignMode) Rating = new Rating(5, 4, 3); } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + private void DisplayStarRating(Rating rating) { - if (change.Property.Name == nameof(Rating) && Rating is not null) - { - var blankValue = IsEditingMode ? HOLLOW_STAR : string.Empty; + var blankValue = IsEditingMode ? HOLLOW_STAR : string.Empty; - int rating = 0; - foreach (TextBlock star in panelOverall.Children) - star.Tag = star.Text = Rating.OverallRating > rating++ ? SOLID_STAR : blankValue; + string getStar(float score, int starIndex) + => Math.Floor(score) > starIndex ? SOLID_STAR + : score < starIndex ? blankValue + : score - starIndex < 0.25 ? blankValue + : score - starIndex > 0.75 ? SOLID_STAR + : HALF_STAR; - rating = 0; - foreach (TextBlock star in panelPerform.Children) - star.Tag = star.Text = Rating.PerformanceRating > rating++ ? SOLID_STAR : blankValue; + int starIndex = 0; + foreach (TextBlock star in panelOverall.Children) + star.Tag = star.Text = getStar(rating.OverallRating, starIndex++); - rating = 0; - foreach (TextBlock star in panelStory.Children) - star.Tag = star.Text = Rating.StoryRating > rating++ ? SOLID_STAR : blankValue; + starIndex = 0; + foreach (TextBlock star in panelPerform.Children) + star.Tag = star.Text = getStar(rating.PerformanceRating, starIndex++); - SetVisible(); - } - base.OnPropertyChanged(change); - } + starIndex = 0; + foreach (TextBlock star in panelStory.Children) + star.Tag = star.Text = getStar(rating.StoryRating, starIndex++); - private void SetVisible() - { ratingsGrid.IsEnabled = IsEditingMode; - tblockOverall.IsVisible = panelOverall.IsVisible = IsEditingMode || Rating?.OverallRating > 0; - tblockPerform.IsVisible = panelPerform.IsVisible = IsEditingMode || Rating?.PerformanceRating > 0; - tblockStory.IsVisible = panelStory.IsVisible = IsEditingMode || Rating?.StoryRating > 0; + tblockOverall.IsVisible = panelOverall.IsVisible = IsEditingMode || rating.OverallRating > 0; + tblockPerform.IsVisible = panelPerform.IsVisible = IsEditingMode || rating.PerformanceRating > 0; + tblockStory.IsVisible = panelStory.IsVisible = IsEditingMode || rating.StoryRating > 0; } public void Panel_PointerExited(object sender, Avalonia.Input.PointerEventArgs e) diff --git a/Source/LibationAvalonia/ViewModels/GridEntry.cs b/Source/LibationAvalonia/ViewModels/GridEntry.cs index 4219699e..b20f12cd 100644 --- a/Source/LibationAvalonia/ViewModels/GridEntry.cs +++ b/Source/LibationAvalonia/ViewModels/GridEntry.cs @@ -44,8 +44,7 @@ namespace LibationAvalonia.ViewModels public string Category { get; protected set; } public string Misc { get; protected set; } public string Description { get; protected set; } - public string ProductRating { get; protected set; } - public string MyRatingString => MyRating?.ToStarString()?.DefaultIfNullOrWhiteSpace(""); + public Rating ProductRating { get; protected set; } protected Rating _myRating; public Rating MyRating { @@ -68,7 +67,6 @@ namespace LibationAvalonia.ViewModels public abstract bool IsSeries { get; } public abstract bool IsEpisode { get; } public abstract bool IsBook { get; } - public abstract double Opacity { get; } public IBrush BackgroundBrush => IsEpisode ? App.SeriesEntryGridBackgroundBrush : Brushes.Transparent; #endregion diff --git a/Source/LibationAvalonia/ViewModels/LibraryBookEntry.cs b/Source/LibationAvalonia/ViewModels/LibraryBookEntry.cs index c79f4cfd..e00c9461 100644 --- a/Source/LibationAvalonia/ViewModels/LibraryBookEntry.cs +++ b/Source/LibationAvalonia/ViewModels/LibraryBookEntry.cs @@ -53,7 +53,6 @@ namespace LibationAvalonia.ViewModels public override bool IsSeries => false; public override bool IsEpisode => Parent is not null; public override bool IsBook => Parent is null; - public override double Opacity => Book.UserDefinedItem.Tags.ToLower().Contains("hidden") ? 0.4 : 1; #endregion @@ -69,7 +68,7 @@ namespace LibationAvalonia.ViewModels //the reference doesn't change. Clone the rating so that it updates within Avalonia properly. _myRating = new Rating(Book.UserDefinedItem.Rating.OverallRating, Book.UserDefinedItem.Rating.PerformanceRating, Book.UserDefinedItem.Rating.StoryRating); PurchaseDate = libraryBook.DateAdded.ToString("d"); - ProductRating = Book.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace(""); + ProductRating = Book.Rating ?? new Rating(0, 0, 0); Authors = Book.AuthorNames(); Narrators = Book.NarratorNames(); Category = string.Join(" > ", Book.CategoriesNames()); @@ -102,7 +101,6 @@ namespace LibationAvalonia.ViewModels case nameof(udi.Tags): Book.UserDefinedItem.Tags = udi.Tags; this.RaisePropertyChanged(nameof(BookTags)); - this.RaisePropertyChanged(nameof(Opacity)); break; case nameof(udi.BookStatus): Book.UserDefinedItem.BookStatus = udi.BookStatus; diff --git a/Source/LibationAvalonia/ViewModels/SeriesEntry.cs b/Source/LibationAvalonia/ViewModels/SeriesEntry.cs index c0ca2965..9f2d9749 100644 --- a/Source/LibationAvalonia/ViewModels/SeriesEntry.cs +++ b/Source/LibationAvalonia/ViewModels/SeriesEntry.cs @@ -49,7 +49,6 @@ namespace LibationAvalonia.ViewModels public override bool IsSeries => true; public override bool IsEpisode => false; public override bool IsBook => false; - public override double Opacity => 1; #endregion @@ -71,7 +70,7 @@ namespace LibationAvalonia.ViewModels //Ratings are changed using Update(), which is a problem for Avalonia data bindings because //the reference doesn't change. Clone the rating so that it updates within Avalonia properly. _myRating = new Rating(Book.UserDefinedItem.Rating.OverallRating, Book.UserDefinedItem.Rating.PerformanceRating, Book.UserDefinedItem.Rating.StoryRating); - ProductRating = Book.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace(""); + ProductRating = Book.Rating ?? new Rating(0, 0, 0); Authors = Book.AuthorNames(); Narrators = Book.NarratorNames(); Category = string.Join(" > ", Book.CategoriesNames()); diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml b/Source/LibationAvalonia/Views/ProductsDisplay.axaml index 28967546..87dfb645 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml @@ -19,10 +19,12 @@ CanUserReorderColumns="True"> - + + + + + - + @@ -83,7 +89,7 @@ - + @@ -93,7 +99,7 @@ - + @@ -103,7 +109,7 @@ - + @@ -113,7 +119,7 @@ - + @@ -123,7 +129,7 @@ - + @@ -133,39 +139,45 @@ - + - - - - - - - - - + - + - + - + @@ -187,6 +199,5 @@ - diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs index 4d1854b8..7d3a3bc3 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs @@ -273,13 +273,13 @@ namespace LibationAvalonia.Views #region Button Click Handlers - public void LiberateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args) + public async void LiberateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args) { var button = args.Source as Button; if (button.DataContext is SeriesEntry sEntry) { - _viewModel.ToggleSeriesExpanded(sEntry); + await _viewModel.ToggleSeriesExpanded(sEntry); //Expanding and collapsing reset the list, which will cause focus to shift //to the topright cell. Reset focus onto the clicked button's cell. diff --git a/Source/LibationWinForms/Dialogs/UpgradeNotificationDialog.Designer.cs b/Source/LibationWinForms/Dialogs/UpgradeNotificationDialog.Designer.cs index 581e0b9e..f1d8ca56 100644 --- a/Source/LibationWinForms/Dialogs/UpgradeNotificationDialog.Designer.cs +++ b/Source/LibationWinForms/Dialogs/UpgradeNotificationDialog.Designer.cs @@ -208,11 +208,7 @@ #endregion - private System.Windows.Forms.Label label1; private System.Windows.Forms.TextBox releaseNotesTbox; - private System.Windows.Forms.GroupBox groupBox1; - private System.Windows.Forms.LinkLabel linkLabel3; - private System.Windows.Forms.LinkLabel linkLabel2; private System.Windows.Forms.LinkLabel packageDlLink; private System.Windows.Forms.Button dontRemindBtn; private System.Windows.Forms.Button yesBtn;