Ruben 7 mesiacov pred
rodič
commit
1053f2d169

+ 93 - 15
src/PicView.Avalonia/Views/FileAssociationsView.axaml.cs

@@ -33,6 +33,11 @@ public partial class FileAssociationsView : UserControl
                         continue;
                     }
 
+                    if (!checkBox.IsVisible)
+                    {
+                        continue;
+                    }
+
                     var tag = checkBox.Tag?.ToString();
                     if (tag.StartsWith(".zip") || tag.StartsWith(".rar") || tag.StartsWith(".7z") || tag.StartsWith(".gzip")) 
                     {
@@ -48,9 +53,18 @@ public partial class FileAssociationsView : UserControl
             
             UnSelectAllButton.Click += delegate
             {
-                foreach (var checkBox in FileTypesContainer.Children.OfType<CheckBox>())
+                var checkBoxes = FileTypesContainer.Children.OfType<CheckBox>();
+                var enumerable = checkBoxes as CheckBox[] ?? checkBoxes.ToArray();
+                if (enumerable.All(x => x.IsChecked == null && x.IsVisible))
+                {
+                    foreach (var checkBox in enumerable)
+                    {
+                        checkBox.IsChecked = false;
+                    }
+                }
+                else
                 {
-                    if (checkBox is not null)
+                    foreach (var checkBox in enumerable.Where(x => x.IsVisible))
                     {
                         checkBox.IsChecked = null;
                     }
@@ -106,7 +120,8 @@ public partial class FileAssociationsView : UserControl
                 Classes = { "altHover", "y" },
                 Tag = "group",
                 Name = fileTypeGroup.Name.Trim(),
-                IsChecked = fileTypeGroup.IsSelected,
+                IsThreeState = true,
+                IsChecked = fileTypeGroup.IsSelected
             };
                 
             var groupTextBlock = new TextBlock
@@ -170,10 +185,11 @@ public partial class FileAssociationsView : UserControl
                 // Bind the checkbox to the file type's IsSelected property
                 fileCheckBox.IsCheckedChanged += delegate
                 {
-                    if (fileCheckBox.IsChecked.HasValue)
-                    {
-                        fileType.IsSelected = fileCheckBox.IsChecked.Value;
-                    }
+                    // Update the model - important to handle null state correctly
+                    fileType.IsSelected = fileCheckBox.IsChecked;
+    
+                    // Now update the group checkbox state
+                    UpdateGroupCheckboxState(fileTypeGroup);
                 };
                     
                 // Subscribe to changes in the file type's IsSelected property
@@ -195,9 +211,67 @@ public partial class FileAssociationsView : UserControl
                     .DisposeWith(_disposables);
             }
         }
+    }
+    
+    private void UpdateGroupCheckboxState(FileTypeGroup group)
+    {
+        // Find all checkboxes that are part of this group
+        var fileTypeCheckboxes = FileTypesContainer.Children.OfType<CheckBox>()
+            .Where(c => c.Tag != null && c.Tag.ToString() != "group" && 
+                        c.IsVisible && IsCheckboxInGroup(c, group));
+                   
+        var allTrue = true;
+        var allFalse = true;
+        var anyNull = false;
+    
+        foreach (var cb in fileTypeCheckboxes)
+        {
+            if (!cb.IsChecked.HasValue || cb.IsChecked == null)
+            {
+                anyNull = true;
+                allTrue = false;
+                allFalse = false;
+            }
+            else if (cb.IsChecked.Value)
+            {
+                allFalse = false;
+            }
+            else
+            {
+                allTrue = false;
+            }
+        }
+    
+        // Find the group checkbox
+        var groupCheckbox = FileTypesContainer.Children.OfType<CheckBox>()
+            .FirstOrDefault(c => c.Tag?.ToString() == "group" && c.Name == group.Name.Trim());
+    
+        if (groupCheckbox != null)
+        {
+            // Set state based on children
+            if (anyNull)
+                groupCheckbox.IsChecked = null;
+            else if (allTrue)
+                groupCheckbox.IsChecked = true;
+            else if (allFalse)
+                groupCheckbox.IsChecked = false;
+            else
+                groupCheckbox.IsChecked = null; // Mixed state
             
-        // Initial filter
-        FilterCheckBoxes(vm.AssociationsViewModel.FilterText);
+            // Update the ViewModel
+            group.IsSelected = groupCheckbox.IsChecked;
+        }
+    }
+
+    private bool IsCheckboxInGroup(CheckBox checkbox, FileTypeGroup group)
+    {
+        // You can determine this by position in the UI or by extension tag
+        var extension = checkbox.Tag?.ToString();
+        if (string.IsNullOrEmpty(extension))
+            return false;
+        
+        return group.FileTypes.Any(ft => ft.Extensions.Contains(extension) || 
+                                         extension.Contains(ft.Extensions.FirstOrDefault() ?? ""));
     }
         
     private void UpdateCheckBoxesFromViewModel()
@@ -216,18 +290,22 @@ public partial class FileAssociationsView : UserControl
             }
 
             // Check if all children are selected
-            var allSelected = true;
-            var anySelected = false;
+            bool? allSelected = true;
+            bool? anySelected = false;
                     
             foreach (var fileType in group.FileTypes)
             {
-                if (!fileType.IsSelected)
+                if (!fileType.IsSelected.HasValue)
+                    anySelected = null;
+                else if (!fileType.IsSelected.Value)
                     allSelected = false;
                 else
                     anySelected = true;
             }
-                    
-            groupCheckBox.IsChecked = allSelected ? true : anySelected ? null : false;
+            
+            // Update the group checkbox
+            groupCheckBox.IsChecked = allSelected;
+            groupCheckBox.IsThreeState = anySelected == null;
         }
     }
         
@@ -263,7 +341,7 @@ public partial class FileAssociationsView : UserControl
         }
     }
         
-    private void FilterCheckBoxes(string filterText)
+    private void FilterCheckBoxes(string? filterText)
     {
         if (string.IsNullOrWhiteSpace(filterText))
         {

+ 43 - 18
src/PicView.Core/ViewModels/FileAssociationsViewModel.cs

@@ -15,7 +15,7 @@ public class FileAssociationsViewModel : ReactiveObject
 
     public ReadOnlyObservableCollection<FileTypeGroup> FileTypeGroups => _fileTypeGroups;
 
-    public string FilterText
+    public string? FilterText
     {
         get;
         set => this.RaiseAndSetIfChanged(ref field, value);
@@ -45,7 +45,7 @@ public class FileAssociationsViewModel : ReactiveObject
         ClearFilterCommand = ReactiveCommand.Create(() => FilterText = string.Empty);
     }
     
-    private Func<FileTypeGroup, bool> BuildFilter(string filter)
+    private Func<FileTypeGroup, bool> BuildFilter(string? filter)
     {
         if (string.IsNullOrWhiteSpace(filter))
         {
@@ -75,23 +75,48 @@ public class FileAssociationsViewModel : ReactiveObject
             return anyVisible;
         };
     }
+    
+    private void SyncUIStateToViewModel()
+    {
+        // Force property notifications to ensure all changes are processed
+        foreach (var group in FileTypeGroups)
+        {
+            group.IsSelected = group.IsSelected;
+            foreach (var fileType in group.FileTypes)
+            {
+                fileType.IsSelected = fileType.IsSelected;
+            }
+        }
+    }
 
     private async Task ApplyFileAssociations()
     {
-        // Call your FileAssociationManager implementation here
+        // Ensure all UI changes are synced to the ViewModel
+        SyncUIStateToViewModel();
+        
+        // Now process the associations
         foreach (var group in FileTypeGroups)
         {
             foreach (var fileType in group.FileTypes)
             {
                 foreach (var extension in fileType.Extensions)
                 {
-                    if (fileType.IsSelected)
-                    {
-                        await FileAssociationManager.AssociateFile(extension);
-                    }
-                    else
+                    // Make sure to properly handle extensions that contain commas
+                    var individualExtensions = extension.Split([',', ' '], StringSplitOptions.RemoveEmptyEntries);
+    
+                    foreach (var ext in individualExtensions)
                     {
-                        await FileAssociationManager.UnassociateFile(extension);
+                        var cleanExt = ext.Trim();
+                        if (!cleanExt.StartsWith('.'))
+                            cleanExt = "." + cleanExt;
+            
+                        if (fileType.IsSelected.HasValue)
+                        {
+                            if (fileType.IsSelected.Value)
+                                await FileAssociationManager.AssociateFile(cleanExt);
+                            else
+                                await FileAssociationManager.UnassociateFile(cleanExt);
+                        }
                     }
                 }
             }
@@ -167,11 +192,11 @@ public class FileAssociationsViewModel : ReactiveObject
             ]),
             
             new FileTypeGroup(TranslationManager.GetTranslation("Archives"), [
-                new FileTypeItem("Zip", [".zip"], false),
-                new FileTypeItem("Rar", [".rar"], false),
-                new FileTypeItem("Gzip", [".gzip"], false),
-                new FileTypeItem("CDisplay RAR Archived Comic Book", [".cbr, .cbz, .cb7"])
-            ], false)
+                new FileTypeItem("Zip", [".zip"], null),
+                new FileTypeItem("Rar", [".rar"], null),
+                new FileTypeItem("Gzip", [".gzip"], null),
+                new FileTypeItem("CDisplay Archived Comic Book", [".cbr, .cbz, .cb7"])
+            ], null)
         };
         
         _fileTypeGroupsList.Edit(list =>
@@ -187,13 +212,13 @@ public class FileTypeGroup : ReactiveObject
     public string Name { get; set; }
     public ObservableCollection<FileTypeItem> FileTypes { get; }
 
-    public bool IsSelected
+    public bool? IsSelected
     {
         get;
         set => this.RaiseAndSetIfChanged(ref field, value);
     }
 
-    public FileTypeGroup(string name, IEnumerable<FileTypeItem> fileTypes, bool isSelected = true)
+    public FileTypeGroup(string name, IEnumerable<FileTypeItem> fileTypes, bool? isSelected = true)
     {
         Name = name;
         FileTypes = new ObservableCollection<FileTypeItem>(fileTypes);
@@ -208,7 +233,7 @@ public class FileTypeItem : ReactiveObject
     
     public string Extension => string.Join(", ", Extensions);
 
-    public bool IsSelected
+    public bool? IsSelected
     {
         get;
         set => this.RaiseAndSetIfChanged(ref field, value);
@@ -220,7 +245,7 @@ public class FileTypeItem : ReactiveObject
         set => this.RaiseAndSetIfChanged(ref field, value);
     } = true;
 
-    public FileTypeItem(string description, string[] extensions, bool isSelected = true)
+    public FileTypeItem(string description, string[] extensions, bool? isSelected = true)
     {
         Description = description;
         Extensions = extensions;