1
1
Эх сурвалжийг харах

Create view models for file association #157

Ruben 7 сар өмнө
parent
commit
df0264f7f3

+ 2 - 0
src/PicView.Avalonia.MacOS/App.axaml.cs

@@ -17,6 +17,7 @@ using PicView.Core.FileHandling;
 using PicView.Core.Localization;
 using PicView.Core.MacOS;
 using PicView.Core.MacOS.Wallpaper;
+using PicView.Core.ViewModels;
 
 namespace PicView.Avalonia.MacOS;
 
@@ -273,6 +274,7 @@ public class App : Application, IPlatformSpecificService
             }
             if (_settingsWindow is null)
             {
+                _vm.AssociationsViewModel ??= new FileAssociationsViewModel();
                 _settingsWindow = new SettingsWindow
                 {
                     DataContext = _vm,

+ 2 - 0
src/PicView.Avalonia.Win32/App.axaml.cs

@@ -17,6 +17,7 @@ using PicView.Avalonia.WindowBehavior;
 using PicView.Core.FileHandling;
 using PicView.Core.Localization;
 using PicView.Core.ProcessHandling;
+using PicView.Core.ViewModels;
 using PicView.Core.WindowsNT;
 using PicView.Core.WindowsNT.FileHandling;
 using PicView.Core.WindowsNT.Taskbar;
@@ -313,6 +314,7 @@ public partial class App : Application, IPlatformSpecificService
             }
             if (_settingsWindow is null)
             {
+                _vm.AssociationsViewModel ??= new FileAssociationsViewModel();
                 _settingsWindow = new SettingsWindow
                 {
                     DataContext = _vm,

+ 2 - 0
src/PicView.Avalonia/ViewModels/MainViewModel.cs

@@ -35,6 +35,8 @@ public class MainViewModel : ReactiveObject
     public PicViewerModel PicViewer { get; } = new();
     
     public ExifViewModel Exif { get; } = new();
+    
+    public FileAssociationsViewModel? AssociationsViewModel { get; set; }
 
     public MainViewModel(IPlatformSpecificService? platformSpecificService)
     {

+ 21 - 460
src/PicView.Avalonia/Views/FileAssociationsView.axaml

@@ -76,19 +76,27 @@
                     Foreground="{DynamicResource MainTextColor}"
                     HorizontalAlignment="Left"
                     Padding="23,7,4,6"
+                    Text="{CompiledBinding AssociationsViewModel.FilterText,
+                                           FallbackValue='',
+                                           Mode=TwoWay}"
                     Watermark="Filter..."
-                    Width="269" />
+                    Width="269"
+                    x:Name="FilterBox" />
 
                 <customControls:IconButton
-                    Classes="altHover y"
+                    Classes="altHover"
                     Data="{StaticResource CloseGeometry}"
                     DockPanel.Dock="Right"
                     Foreground="{DynamicResource MainTextColorFaded}"
                     HorizontalAlignment="Right"
-                    Padding="10,3,10,3" />
+                    Padding="10,3,10,3"
+                    x:Name="ClearButton" />
             </Panel>
 
-            <Button Classes="altHover y" Padding="10,2,10,2">
+            <Button
+                Classes="altHover"
+                Padding="10,2,10,2"
+                x:Name="SelectAllButton">
                 <TextBlock
                     Classes="txt"
                     Foreground="{StaticResource SecondaryTextColor}"
@@ -102,7 +110,10 @@
                 Margin="5,0,5,0"
                 Width="2" />
 
-            <Button Classes="altHover y" Padding="10,2,10,2">
+            <Button
+                Classes="altHover"
+                Padding="10,2,10,2"
+                x:Name="UnSelectAllButton">
                 <TextBlock
                     Classes="txt"
                     Foreground="{StaticResource SecondaryTextColor}"
@@ -111,8 +122,11 @@
         </StackPanel>
 
 
-        <customControls:AutoScrollViewer Background="Transparent" Height="340">
-            <StackPanel>
+        <customControls:AutoScrollViewer
+            Background="Transparent"
+            Height="340"
+            x:Name="FileTypesScrollViewer">
+            <StackPanel x:Name="FileTypesContainer">
                 <StackPanel.Styles>
                     <Style Selector="CheckBox.x">
                         <Setter Property="Margin" Value="15,0,0,5" />
@@ -122,459 +136,6 @@
                     </Style>
                 </StackPanel.Styles>
 
-                <CheckBox Classes="altHover y" x:Name="NormalGroup">
-                    <TextBlock
-                        Classes="txt"
-                        FontFamily="/Assets/Fonts/Roboto-Bold.ttf#Roboto"
-                        Text="Normal" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Joint Photographic Experts Group (.jpg, .jpeg, .jpe)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="JPEG File Interchange Format (.jfif)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Portable Network Graphics (.png)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Windows Bitmap (.bmp)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Graphics Interchange Format (.gif)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="WebP (.webp)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Wireless Bitmap (.wbmp)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Advanced Video Interlace Format (.avif)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Icon (.ico)" />
-                </CheckBox>
-
-
-                <CheckBox Classes="altHover y" x:Name="GraphicsGroup">
-                    <TextBlock
-                        Classes="txt"
-                        FontFamily="/Assets/Fonts/Roboto-Bold.ttf#Roboto"
-                        Text="Graphics" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Scalable Vector Graphics (.svg, svgz)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Photoshop (.psd, .psb)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="XCF (.xcf)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Tagged Image File Format (.tif, .tiff)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="High-Enhanced Image File (.heic, .heif)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="JPEG XL (.jxl)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="JPEG 2000 (.jp2)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="High Dynamic Range (.hdr)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Quite OK Image (.qoi)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Direct Draw Surface (.dds)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Truevision Targa (.tga)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Industrial Light &amp; Magic OpenEXR (.exr)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover y" x:Name="RawGroup">
-                    <TextBlock
-                        Classes="txt"
-                        FontFamily="/Assets/Fonts/Roboto-Bold.ttf#Roboto"
-                        Text="Raw" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Raw (.raw)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Framed Raster (.3fr)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Sony Digital Camera RAW (.arw)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Canon Digital Camera RAW (.cr2, .cr3, .crw)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Kodak Raw (.dcr, .kdc)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Digital Negative RAW (.dng)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Epson Raw Image (.erf)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Minolta Raw Image (.mdc)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Mamiya Raw Image (.mef)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Leaf/Aptus/Mamiya MOS Raw Image (.mos)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Minolta Dimage RAW (.mrw)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Nikon Raw Image (.nef)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Nokia RAW Bitmap (.nrw)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Olympus Digital Camera RAW (.orf)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Pentax Raw Image (.pef)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Panasonic RAW (.rw2)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Sony SRF Raw (.srf)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Sigma Foveon X3 (.x3f)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Kodak FlashPix Bitmap (.fpx)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Kodak PhotoCD Bitmap (.pcd)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Kodak Raw (.dcr)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Windows Metafile Image (.wmf, .emf)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover y" x:Name="UncommonGroup">
-                    <TextBlock
-                        Classes="txt"
-                        FontFamily="/Assets/Fonts/Roboto-Bold.ttf#Roboto"
-                        Text="Uncommon" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="WordPerfect Graphics (.wpg)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Paintbrush bitmap graphics (.pcx)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="X Bitmap (.xbm)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="PX PixMap Bitmap (.xpm)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Dr. Halo (.cut)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Free Lossless Image Format Bitmap (.flif)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Truevision Thumb (.thm)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Portable GrayMap Bitmap (.ppm)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Portable PixMap  Bitmap (.ppm)" />
-                </CheckBox>
-
-                <CheckBox Classes="altHover x">
-                    <TextBlock
-                        Classes="txt"
-                        Margin="0"
-                        Padding="0,1,5,0"
-                        Text="Base64 (.b64)" />
-                </CheckBox>
-
             </StackPanel>
         </customControls:AutoScrollViewer>
 

+ 47 - 5
src/PicView.Avalonia/Views/FileAssociationsView.axaml.cs

@@ -1,11 +1,53 @@
 using Avalonia.Controls;
+using PicView.Avalonia.ViewModels;
 
 namespace PicView.Avalonia.Views;
 
-public partial class FileAssociationsView : UserControl
-{
-    public FileAssociationsView()
+ public partial class FileAssociationsView : UserControl
     {
-        InitializeComponent();
+        private readonly List<(CheckBox CheckBox, string SearchText)> _allCheckBoxes = [];
+        
+        public FileAssociationsView()
+        {
+            InitializeComponent();
+            
+            FilterBox.TextChanged += FilterBox_TextChanged;
+                
+            // Clear button functionality
+            var clearButton = ClearButton;
+            if (clearButton != null)
+            {
+                clearButton.Click += (s, e) => 
+                { 
+                    FilterBox.Text = string.Empty;
+                    FilterCheckBoxes(string.Empty);
+                };
+            }
+            
+            // Initialize the collection of checkboxes for filtering
+            InitializeCheckBoxesCollection();
+        }
+        
+        private void InitializeCheckBoxesCollection()
+        {
+            var container = FileTypesContainer;
+
+            if (DataContext is not MainViewModel vm)
+            {
+                return;
+            }
+            
+            foreach (var fileTypeGroup in vm.AssociationsViewModel.FileTypeGroups)
+            {
+            }
+        }
+        
+        private void FilterBox_TextChanged(object? sender, EventArgs e)
+        {
+            FilterCheckBoxes(FilterBox.Text);
+        }
+        
+        private void FilterCheckBoxes(string filterText)
+        {
+        }
     }
-}

+ 234 - 0
src/PicView.Core/ViewModels/FileAssociationsViewModel.cs

@@ -0,0 +1,234 @@
+using System.Collections.ObjectModel;
+using System.Reactive;
+using System.Reactive.Linq;
+using DynamicData;
+using PicView.Core.FileHandling;
+using PicView.Core.Localization;
+using ReactiveUI;
+
+namespace PicView.Core.ViewModels;
+
+public class FileAssociationsViewModel : ReactiveObject
+{
+    private readonly ReadOnlyObservableCollection<FileTypeGroup> _fileTypeGroups;
+    private readonly SourceList<FileTypeGroup> _fileTypeGroupsList = new();
+    private string _filterText = string.Empty;
+    
+    public ReadOnlyObservableCollection<FileTypeGroup> FileTypeGroups => _fileTypeGroups;
+    
+    public string FilterText
+    {
+        get => _filterText;
+        set => this.RaiseAndSetIfChanged(ref _filterText, value);
+    }
+    
+    public ReactiveCommand<Unit, Unit> SelectAllCommand { get; }
+    public ReactiveCommand<Unit, Unit> UnselectAllCommand { get; }
+    public ReactiveCommand<Unit, Unit> ApplyCommand { get; }
+    public ReactiveCommand<Unit, Unit> ResetCommand { get; }
+    public ReactiveCommand<Unit, string> ClearFilterCommand { get; }
+    
+    public FileAssociationsViewModel()
+    {
+        // Create file type groups and populate with data
+        InitializeFileTypes();
+        
+        // Setup the filtering
+        var filter = this.WhenAnyValue(x => x.FilterText)
+            .Throttle(TimeSpan.FromMilliseconds(200))
+            .Select(BuildFilter);
+            
+        _fileTypeGroupsList.Connect()
+            .AutoRefresh()
+            .Filter(filter)
+            .Bind(out _fileTypeGroups)
+            .Subscribe();
+            
+        // Initialize commands
+        SelectAllCommand = ReactiveCommand.Create(() => SetAllVisibleCheckboxes(true));
+        UnselectAllCommand = ReactiveCommand.Create(() => SetAllVisibleCheckboxes(false));
+        ApplyCommand = ReactiveCommand.CreateFromTask(async () => await ApplyFileAssociations());
+        ResetCommand = ReactiveCommand.Create(ResetAssociations);
+        ClearFilterCommand = ReactiveCommand.Create(() => FilterText = string.Empty);
+    }
+    
+    private Func<FileTypeGroup, bool> BuildFilter(string filter)
+    {
+        if (string.IsNullOrWhiteSpace(filter))
+            return _ => true;
+            
+        return group => {
+            // Update visibility of items based on filter
+            var anyVisible = false;
+            foreach (var item in group.FileTypes)
+            {
+                item.IsVisible = item.Description.Contains(filter, StringComparison.OrdinalIgnoreCase) || 
+                                 item.Extension.Contains(filter, StringComparison.OrdinalIgnoreCase);
+                if (item.IsVisible)
+                    anyVisible = true;
+            }
+            
+            // Only show groups that have at least one visible item
+            return anyVisible;
+        };
+    }
+    
+    private void SetAllVisibleCheckboxes(bool isChecked)
+    {
+        foreach (var group in FileTypeGroups)
+        {
+            foreach (var fileType in group.FileTypes.Where(x => x.IsVisible))
+            {
+                fileType.IsSelected = isChecked;
+            }
+        }
+    }
+    
+    private async Task ApplyFileAssociations()
+    {
+        // Call your FileAssociationManager implementation here
+        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
+                    {
+                        await FileAssociationManager.UnassociateFile(extension);
+                    }
+                }
+            }
+        }
+    }
+    
+    private void ResetAssociations()
+    {
+        // Reset to current system associations
+        // This would need to query your FileAssociationManager
+    }
+    
+    private void InitializeFileTypes()
+    {
+        var groups = new[]
+        {
+            new FileTypeGroup(TranslationManager.Translation.Normal, [
+                new FileTypeItem("Joint Photographic Experts Group", [".jpg", ".jpeg", ".jpe"]),
+                new FileTypeItem("JPEG File Interchange Format", [".jfif"]),
+                new FileTypeItem("Portable Network Graphics", [".png"]),
+                new FileTypeItem("Windows Bitmap", [".bmp"]),
+                new FileTypeItem("Graphics Interchange Format", [".gif"]),
+                new FileTypeItem("WebP", [".webp"]),
+                new FileTypeItem("Wireless Bitmap", [".wbmp"]),
+                new FileTypeItem("Advanced Video Interlace Format", [".avif"]),
+                new FileTypeItem("Icon", [".ico"])
+            ]),
+            
+            new FileTypeGroup("Graphics", [
+                new FileTypeItem("Scalable Vector Graphics", [".svg", ".svgz"]),
+                new FileTypeItem("Photoshop", [".psd", ".psb"]),
+                new FileTypeItem("XCF", [".xcf"]),
+                new FileTypeItem("Tagged Image File Format", [".tif", ".tiff"]),
+                new FileTypeItem("High-Enhanced Image File", [".heic", ".heif"]),
+                new FileTypeItem("JPEG XL", [".jxl"]),
+                new FileTypeItem("JPEG 2000", [".jp2"]),
+                new FileTypeItem("High Dynamic Range", [".hdr"]),
+                new FileTypeItem("Quite OK Image", [".qoi"]),
+                new FileTypeItem("Direct Draw Surface", [".dds"]),
+                new FileTypeItem("Truevision Targa", [".tga"]),
+                new FileTypeItem("Industrial Light & Magic OpenEXR", [".exr"])
+            ]),
+            
+            new FileTypeGroup("Raw", [
+                new FileTypeItem("Raw (.raw)", [".raw"]),
+                new FileTypeItem("Framed Raster (.3fr)", [".3fr"]),
+                new FileTypeItem("Sony Digital Camera RAW (.arw)", [".arw"]),
+                new FileTypeItem("Canon Digital Camera RAW (.cr2, .cr3, .crw)", [".cr2, .cr3, .crw"]),
+                new FileTypeItem("Kodak Raw (.dcr, .kdc)", [".dcr", ".kdc"]),
+                new FileTypeItem("Digital Negative RAW (.dng)", [".dng"]),
+                new FileTypeItem("Epson Raw Image (.erf)", [".erf"]),
+                new FileTypeItem("Minolta Raw Image (.mdc)", [".mdc"]),
+                new FileTypeItem("Nikon Raw Image (.nef)", [".nef"]),
+                new FileTypeItem("Mamiya Raw Image (.mef)", [".mef"]),
+                new FileTypeItem("Leaf/Aptus/Mamiya MOS Raw Image (.mos)", [".mos"]),
+                new FileTypeItem("Minolta Dimage RAW (.mrw)", [".mrw"]),
+                new FileTypeItem("Nikon Raw Image (.nef)", [".nef"]),
+                new FileTypeItem("Nokia RAW Bitmap (.nrw)", [".nrw"]),
+                new FileTypeItem("Olympus Raw Image (.orf)", [".orf"]),
+                new FileTypeItem("Pentax Raw Image (.pef)", [".pef"]),
+                new FileTypeItem("Sony SRF Raw (.srf)", [".srf"]),
+                new FileTypeItem("Sigma Foveon X3 (.x3f)", [".x3f"]),
+                new FileTypeItem("Kodak FlashPix Bitmap (.fpx)", [".fpx"]),
+                new FileTypeItem("Kodak PhotoCD Bitmap (.pcd)", [".pcd"]),
+                new FileTypeItem("Kodak Raw (.dcr)", [".dcr"]),
+                new FileTypeItem("Windows Metafile Image (.wmf, .emf)", [".wmf", ".emf"]),
+            ]),
+            
+            new FileTypeGroup("Uncommon", [
+                new FileTypeItem("Wordperfect Graphics (.wpg)", [".wpg"]),
+                new FileTypeItem("Paintbrush bitmap graphics (.pcx)", [".pcx"]),
+                new FileTypeItem("X Bitmap (.xbm)", [".xbm"]),
+                new FileTypeItem("PX PixMap Bitmap (.xpm)", [".xpm"]),
+                new FileTypeItem("Dr. Halo (.cut)", [".cut"]),
+                new FileTypeItem("Truevision Thumb (.thm)", [".thm"]),
+                new FileTypeItem("Portable GrayMap Bitmap (.ppm)", [".ppm"]),
+                new FileTypeItem("Portable PixMap Bitmap (.pbm)", [".pbm"]),
+                new FileTypeItem("Base64 (.b64)", [".b64"])
+            ]),
+            
+            new FileTypeGroup("Archive", [
+                new FileTypeItem("Zip (.zip)", [".zip"], false),
+                new FileTypeItem("Rar (.rar)", [".rar"], false),
+                new FileTypeItem("Gzip (.gzip)", [".gzip"], false),
+                new FileTypeItem("CDisplay RAR Archived Comic Book (.cbr)", [".cbr, .cbz, .cb7"])
+            ], false)
+        };
+        
+        _fileTypeGroupsList.Edit(list =>
+        {
+            list.Clear();
+            list.AddRange(groups);
+        });
+    }
+}
+
+public class FileTypeGroup(string name, IEnumerable<FileTypeItem> fileTypes, bool isSelected = true)
+    : ReactiveObject
+{
+    public string Name { get; } = name;
+    public ObservableCollection<FileTypeItem> FileTypes { get; } = new(fileTypes);
+
+    public bool IsSelected { get;  } = isSelected;
+}
+
+public class FileTypeItem : ReactiveObject
+{
+    public string Description { get; }
+    public string[] Extensions { get; }
+    
+    public string Extension => string.Join(", ", Extensions);
+
+    public bool IsSelected
+    {
+        get;
+        set => this.RaiseAndSetIfChanged(ref field, value);
+    }
+    
+    public bool IsVisible
+    {
+        get;
+        set => this.RaiseAndSetIfChanged(ref field, value);
+    } = true;
+    
+    public FileTypeItem(string description, string[] extensions, bool isSelected = true)
+    {
+        Description = description;
+        Extensions = extensions;
+        IsSelected = isSelected;
+    }
+}
+