From 77441f4a31e04a6927f160a45769292492c318ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-S=C3=A9bastien=20Guay?= Date: Thu, 28 Nov 2024 22:19:34 -0500 Subject: [PATCH 1/2] FrostyModManager: Add column for applied state and filter applied/not applied mods --- FrostyModManager/FrostyModManager.csproj | 2 + FrostyModManager/Images/CircleCheck.png | Bin 0 -> 703 bytes FrostyModManager/Images/CircleX.png | Bin 0 -> 712 bytes FrostyModManager/Windows/MainWindow.xaml | 61 +++++++-- FrostyModManager/Windows/MainWindow.xaml.cs | 143 +++++++++++++++++++- 5 files changed, 190 insertions(+), 16 deletions(-) create mode 100644 FrostyModManager/Images/CircleCheck.png create mode 100644 FrostyModManager/Images/CircleX.png diff --git a/FrostyModManager/FrostyModManager.csproj b/FrostyModManager/FrostyModManager.csproj index 9267e349c..9e5c27ace 100644 --- a/FrostyModManager/FrostyModManager.csproj +++ b/FrostyModManager/FrostyModManager.csproj @@ -292,6 +292,8 @@ + + PreserveNewest diff --git a/FrostyModManager/Images/CircleCheck.png b/FrostyModManager/Images/CircleCheck.png new file mode 100644 index 0000000000000000000000000000000000000000..d15525268505974e1ebaed8e41c2d0cefb9a1f1d GIT binary patch literal 703 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucL9r6oh?3y^w370~qEv=}#LT=BJwMkF1yeo4 z@6F#Q166EEjqptK^weVD0CHFvq!?Kl7=bJ=AeM%*L2l7tWCn{f0ojI(ObmQLItqw0 z+gZTk89+7&Bmgl;Ka57Pl7X3lVFEh?3sBy`$k>2!0mMv@de#LHb0z`VAixAPg$b-O z$kGDHg6c9fFaXIWFHt)ZA&$BGH<0S8Y-IlkfLYElwwnmg-Mh^P3Yro9_71P!)!Z%_Gi?wQ2oA51?VKEC6h zR+bdyeln#;fEL?%uDe^RtX|FS~YU6DM8cd^2SF0mi`6MYZ=Px5SP zvyNz=u`IQKOE1Cu@vWep$r_$T;g!$hoHD0Ou4%4W?!RNnyrt)oL+mTGPt1^4l@e&y SwmabtiV06wKbLh*2~7a_@afI~ literal 0 HcmV?d00001 diff --git a/FrostyModManager/Images/CircleX.png b/FrostyModManager/Images/CircleX.png new file mode 100644 index 0000000000000000000000000000000000000000..6d8c80cdc51b6334dd84754aa5780a8dd0478a9b GIT binary patch literal 712 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucL9r6oh?3y^w370~qEv=}#LT=BJwMkF1yeo4 z@6F#Q166EEjqptK^weVD0CHFvq!?Kl7=bJ=AeM%*L2l7tWCn{f0ojI(ObmQLItqw0 z+gZTk89+7&Bmgl;Ka57Pl7X3lVFEh?3sBy`$k>2!0mMv@de#LHb0z`VAixAPg$b-O z$kGDHg6c9fFaXIWFHt)Zp1y!b)J8&bA7|U z4+Zy&?@hk*CE)G(rJq-NYEIs@uKkU9!I~z+RSV5_{G4QDKF34h-PvOzo->^yn5Q3L z2u={vY3BaGb;(9foT;i*c1>Zn!--7~WW{?6RxdZ;DdzA$@%Rqwa;=63-3sUb3FNF^ z5br2pv!(UKgyRQn6+{oz9Y2+R?pcGkotnw&zQ;Yqo?k^@yqa};i~6sX{xMmcc2ju{ z|48%p`sDfRYu#$qo5xzWUrIl}c|Fr5#=Ir96SC$kJ*fP1#)Ugf?r+W?*vPd&qwAAX zhf$?YHUG^%u3w_AMH~mEPZX_Q&AFiL@ve7On{WPE5u&BJioK;ho#Aifxr0e7L*{MM z(QdvyJwUvm`tG*3qCMxz=DiSRyRyixb%Abv({8uLf!}NYv;MpGdC#K9yB14a^t~Lj e;O2h?-`|YtSFF})v4{$RqQukH&t;ucLK6URF!cuj literal 0 HcmV?d00001 diff --git a/FrostyModManager/Windows/MainWindow.xaml b/FrostyModManager/Windows/MainWindow.xaml index a0db3c5af..a7ed73a72 100644 --- a/FrostyModManager/Windows/MainWindow.xaml +++ b/FrostyModManager/Windows/MainWindow.xaml @@ -745,14 +745,34 @@ - + + + + + + + + + + + + @@ -1088,8 +1121,16 @@ + Foreground="{StaticResource FontColor}" Margin="0,0,0,26" /> + diff --git a/FrostyModManager/Windows/MainWindow.xaml.cs b/FrostyModManager/Windows/MainWindow.xaml.cs index 05bff42ca..08e3cd1ee 100644 --- a/FrostyModManager/Windows/MainWindow.xaml.cs +++ b/FrostyModManager/Windows/MainWindow.xaml.cs @@ -31,6 +31,7 @@ using Newtonsoft.Json; using System.Threading.Tasks; using System.Media; +using System.Reflection; namespace FrostyModManager { @@ -192,6 +193,29 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu throw new NotImplementedException(); } } + public class ModAppliedConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + String title = (String)value; + MainWindow mainWindow = (MainWindow)parameter; + + if (mainWindow.selectedPack != null) + { + if (mainWindow.selectedPack.AppliedMods.Exists(x => x.ModName == title)) + { + return Visibility.Visible; + } + } + + return Visibility.Hidden; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } public enum ModPrimaryActionType { @@ -281,7 +305,7 @@ public partial class MainWindow : FrostyWindow { private List availableMods = new List(); private List packs = new List(); - private FrostyPack selectedPack; + public FrostyPack selectedPack { get; private set; } private FileSystem fs; private DirectoryInfo modsDir = new DirectoryInfo(Path.Combine("Mods", ProfilesLibrary.ProfileName)); @@ -397,6 +421,9 @@ private void FrostyWindow_FrostyLoaded(object sender, EventArgs e) availableMods = availableMods.OrderBy(o => o.Filename).ToList(); availableModsList.ItemsSource = availableMods; + // Re-run filter to update the status bar text. + RefreshFilter(); + CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(availableModsList.ItemsSource); PropertyGroupDescription groupDescription = new PropertyGroupDescription("ModDetails.Category", null, StringComparison.OrdinalIgnoreCase); view.GroupDescriptions.Add(groupDescription); @@ -490,6 +517,38 @@ private void FrostyWindow_FrostyLoaded(object sender, EventArgs e) orderComboBox.SelectedIndex = 1; } + GridViewColumn appliedBindingColumn = (availableModsList.View as GridView).Columns[2]; + //DependencyObject appliedBindingColumnContent = appliedBindingColumn.CellTemplate.LoadContent(); + //DependencyObject appliedBindingColumnTextBlock = VisualTreeHelper.GetChild(appliedBindingColumnContent, 0); + //BindingOperations.SetBinding(appliedBindingColumnTextBlock, TextBlock.TextProperty, + // new Binding("ModDetails.Title") + // { + // Converter = new ModAppliedConverter(), + // ConverterParameter = this, + // Source = view, + // Mode = BindingMode.OneWay, + // }); + //DependencyObject otherColumnContent = (availableModsList.View as GridView).Columns[1].CellTemplate.LoadContent(); + //Binding otherColumnBinding = BindingOperations.GetBinding(VisualTreeHelper.GetChild(otherColumnContent, 0), TextBlock.TextProperty); + + FrameworkElementFactory factory = new FrameworkElementFactory(typeof(Image)); + factory.SetValue(Image.SourceProperty, new ImageSourceConverter().ConvertFromString("pack://application:,,,/FrostyModManager;component/Images/CircleCheck.png") as ImageSource); + factory.SetValue(Image.HeightProperty, 16.0d); + factory.SetValue(Image.WidthProperty, 16.0d); + factory.SetValue(Image.HorizontalAlignmentProperty, HorizontalAlignment.Center); + factory.SetValue(Image.VerticalAlignmentProperty, VerticalAlignment.Center); + factory.SetBinding(Image.VisibilityProperty, new Binding("ModDetails.Title") + { + Converter = new ModAppliedConverter(), + ConverterParameter = this, + //Source = availableModsList.ItemsSource, + Mode = BindingMode.OneWay, + }); + DataTemplate dt = new DataTemplate(); + dt.VisualTree = factory; + appliedBindingColumn.CellTemplate = dt; + view.Refresh(); + GC.Collect(); } @@ -617,6 +676,9 @@ private void removeButton_Click(object sender, RoutedEventArgs e) appliedModsList.SelectedIndex = selectedIndex; updateAppliedModButtons(); + + // Re-run filter since we might be filtering on applied mods. + RefreshFilter(); } private void upButton_Click(object sender, RoutedEventArgs e) @@ -839,6 +901,9 @@ private void uninstallModButton_Click(object sender, RoutedEventArgs e) appliedModsList.Items.Refresh(); FrostyMessageBox.Show("Mod(s) has been successfully uninstalled", "Frosty Mod Manager"); + + // Re-run filter since we might be filtering on applied mods. + RefreshFilter(); } private int VerifyMod(Stream stream) @@ -1514,6 +1579,9 @@ private void InstallMods(string[] filenames) FrostyMessageBox.Show("Pack has been successfully imported", "Frosty Mod Manager"); } } + + // Re-run filter since we might be filtering on applied mods. + RefreshFilter(); } private bool IsCompressed(FileInfo fi) => fi.Extension == ".rar" || fi.Extension == ".zip" || fi.Extension == ".7z" || fi.Extension == ".fbpack"; @@ -1574,6 +1642,9 @@ private void availableModsList_MouseDoubleClick(object sender, MouseButtonEventA // focus on tab item appliedModsTabItem.IsSelected = true; + + // Re-run filter since we might be filtering on applied mods. + RefreshFilter(); } public List AllChildren(DependencyObject parent) @@ -1608,6 +1679,9 @@ private void addModButton_Click(object sender, RoutedEventArgs e) // focus on tab item appliedModsTabItem.IsSelected = true; + + // Re-run filter since we might be filtering on applied mods. + RefreshFilter(); } private void SelectedProfile_AppliedModsUpdated(object sender, RoutedEventArgs e) @@ -1843,18 +1917,72 @@ private void logTextBox_TextChanged(object sender, TextChangedEventArgs e) private void availableModsFilter_KeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) - availableModsFilter_LostFocus(this, new RoutedEventArgs()); + RefreshFilter(); } private void availableModsFilter_LostFocus(object sender, RoutedEventArgs e) { - if (availableModsFilterTextBox.Text == "") + RefreshFilter(); + } + + private void RefreshFilter() + { + Func nameFilter = a => { - availableModsList.Items.Filter = null; - return; + if (availableModsFilterTextBox.Text != "") + { + return (a).ModDetails.Title.ToLower().Contains(availableModsFilterTextBox.Text.ToLower()); + } + + return true; + }; + + Func appliedOrNotFilter = a => + { + if (appliedModsFilterButton.IsChecked.GetValueOrDefault()) + { + return selectedPack.AppliedMods.Exists(x => x.ModName == ((IFrostyMod)a).ModDetails.Title); + } + else if (notAppliedModsFilterButton.IsChecked.GetValueOrDefault()) + { + return !selectedPack.AppliedMods.Exists(x => x.ModName == ((IFrostyMod)a).ModDetails.Title); + } + + return true; + }; + + availableModsList.Items.Filter = new Predicate((object a) => appliedOrNotFilter((IFrostyMod)a) && nameFilter((IFrostyMod)a)); + + if (availableModsFilterTextBox.Text != "" || appliedModsFilterButton.IsChecked.GetValueOrDefault() || notAppliedModsFilterButton.IsChecked.GetValueOrDefault()) + { + availableModsStatusBar.Text = string.Format("{0} mods pass filter.", availableModsList.Items.Count); + } + else + { + availableModsStatusBar.Text = string.Format("{0} mods available.", availableModsList.Items.Count); + } + } + + private void appliedModsFilterButton_Click(object sender, RoutedEventArgs e) + { + // NotApplied + Applied both checked makes no sense, same as neither checked. + if (appliedModsFilterButton.IsChecked.GetValueOrDefault()) + { + notAppliedModsFilterButton.IsChecked = false; } - availableModsList.Items.Filter = new Predicate((object a) => ((IFrostyMod)a).ModDetails.Title.ToLower().Contains(availableModsFilterTextBox.Text.ToLower())); + RefreshFilter(); + } + + private void notAppliedModsFilterButton_Click(object sender, RoutedEventArgs e) + { + // NotApplied + Applied both checked makes no sense, same as neither checked. + if (notAppliedModsFilterButton.IsChecked.GetValueOrDefault()) + { + appliedModsFilterButton.IsChecked = false; + } + + RefreshFilter(); } private void PART_ShowOnlyReplacementsCheckBox_Checked(object sender, RoutedEventArgs e) @@ -1978,6 +2106,9 @@ private void collectionModsList_MouseDoubleClick(object sender, MouseButtonEvent // focus on tab item appliedModsTabItem.IsSelected = true; + + // Re-run filter since we might be filtering on applied mods. + RefreshFilter(); } private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e) From 6c59c466298f33e6c3d000ae4c766df95048fe0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-S=C3=A9bastien=20Guay?= Date: Thu, 28 Nov 2024 22:49:22 -0500 Subject: [PATCH 2/2] cleanup --- FrostyModManager/Windows/MainWindow.xaml | 11 +---------- FrostyModManager/Windows/MainWindow.xaml.cs | 17 +---------------- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/FrostyModManager/Windows/MainWindow.xaml b/FrostyModManager/Windows/MainWindow.xaml index a7ed73a72..b297db4ce 100644 --- a/FrostyModManager/Windows/MainWindow.xaml +++ b/FrostyModManager/Windows/MainWindow.xaml @@ -975,16 +975,7 @@ - + diff --git a/FrostyModManager/Windows/MainWindow.xaml.cs b/FrostyModManager/Windows/MainWindow.xaml.cs index 08e3cd1ee..e5eb5df41 100644 --- a/FrostyModManager/Windows/MainWindow.xaml.cs +++ b/FrostyModManager/Windows/MainWindow.xaml.cs @@ -517,20 +517,6 @@ private void FrostyWindow_FrostyLoaded(object sender, EventArgs e) orderComboBox.SelectedIndex = 1; } - GridViewColumn appliedBindingColumn = (availableModsList.View as GridView).Columns[2]; - //DependencyObject appliedBindingColumnContent = appliedBindingColumn.CellTemplate.LoadContent(); - //DependencyObject appliedBindingColumnTextBlock = VisualTreeHelper.GetChild(appliedBindingColumnContent, 0); - //BindingOperations.SetBinding(appliedBindingColumnTextBlock, TextBlock.TextProperty, - // new Binding("ModDetails.Title") - // { - // Converter = new ModAppliedConverter(), - // ConverterParameter = this, - // Source = view, - // Mode = BindingMode.OneWay, - // }); - //DependencyObject otherColumnContent = (availableModsList.View as GridView).Columns[1].CellTemplate.LoadContent(); - //Binding otherColumnBinding = BindingOperations.GetBinding(VisualTreeHelper.GetChild(otherColumnContent, 0), TextBlock.TextProperty); - FrameworkElementFactory factory = new FrameworkElementFactory(typeof(Image)); factory.SetValue(Image.SourceProperty, new ImageSourceConverter().ConvertFromString("pack://application:,,,/FrostyModManager;component/Images/CircleCheck.png") as ImageSource); factory.SetValue(Image.HeightProperty, 16.0d); @@ -541,13 +527,12 @@ private void FrostyWindow_FrostyLoaded(object sender, EventArgs e) { Converter = new ModAppliedConverter(), ConverterParameter = this, - //Source = availableModsList.ItemsSource, Mode = BindingMode.OneWay, }); DataTemplate dt = new DataTemplate(); dt.VisualTree = factory; + GridViewColumn appliedBindingColumn = (availableModsList.View as GridView).Columns[2]; appliedBindingColumn.CellTemplate = dt; - view.Refresh(); GC.Collect(); }