|
@@ -1,6 +1,7 @@
|
|
using Avalonia;
|
|
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Input;
|
|
using Avalonia.Input;
|
|
|
|
+using Avalonia.LogicalTree;
|
|
using Avalonia.Media;
|
|
using Avalonia.Media;
|
|
using PicView.Avalonia.UI;
|
|
using PicView.Avalonia.UI;
|
|
using PicView.Avalonia.ViewModels;
|
|
using PicView.Avalonia.ViewModels;
|
|
@@ -16,11 +17,11 @@ public partial class FileAssociationsView : UserControl
|
|
{
|
|
{
|
|
private readonly List<(CheckBox CheckBox, string SearchText)> _allCheckBoxes = [];
|
|
private readonly List<(CheckBox CheckBox, string SearchText)> _allCheckBoxes = [];
|
|
private readonly CompositeDisposable _disposables = new();
|
|
private readonly CompositeDisposable _disposables = new();
|
|
-
|
|
|
|
|
|
+
|
|
public FileAssociationsView()
|
|
public FileAssociationsView()
|
|
{
|
|
{
|
|
InitializeComponent();
|
|
InitializeComponent();
|
|
-
|
|
|
|
|
|
+
|
|
FileTypesScrollViewer.Height = ScreenHelper.ScreenSize.WorkingAreaHeight switch
|
|
FileTypesScrollViewer.Height = ScreenHelper.ScreenSize.WorkingAreaHeight switch
|
|
{
|
|
{
|
|
> 500 and <= 650 => 340,
|
|
> 500 and <= 650 => 340,
|
|
@@ -43,7 +44,7 @@ public partial class FileAssociationsView : UserControl
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
private void InitializeCheckBoxesCollection()
|
|
private void InitializeCheckBoxesCollection()
|
|
{
|
|
{
|
|
var container = FileTypesContainer;
|
|
var container = FileTypesContainer;
|
|
@@ -52,14 +53,18 @@ public partial class FileAssociationsView : UserControl
|
|
{
|
|
{
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
vm.AssociationsViewModel ??= new FileAssociationsViewModel();
|
|
vm.AssociationsViewModel ??= new FileAssociationsViewModel();
|
|
-
|
|
|
|
|
|
+
|
|
// Subscribe to changes in the filter text
|
|
// Subscribe to changes in the filter text
|
|
Observable.EveryValueChanged(vm.AssociationsViewModel, x => x.FilterText.Value, UIHelper.GetFrameProvider)
|
|
Observable.EveryValueChanged(vm.AssociationsViewModel, x => x.FilterText.Value, UIHelper.GetFrameProvider)
|
|
.Subscribe(FilterCheckBoxes)
|
|
.Subscribe(FilterCheckBoxes)
|
|
.AddTo(_disposables);
|
|
.AddTo(_disposables);
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ vm.AssociationsViewModel.ResetCommand.Subscribe(UpdateCheckBoxesFromViewModel).AddTo(_disposables);
|
|
|
|
+ vm.AssociationsViewModel.SelectAllCommand.Subscribe(UpdateCheckBoxesFromViewModel).AddTo(_disposables);
|
|
|
|
+ vm.AssociationsViewModel.UnselectAllCommand.Subscribe(UpdateCheckBoxesFromViewModel).AddTo(_disposables);
|
|
|
|
+
|
|
// Create checkboxes for each file type group and item
|
|
// Create checkboxes for each file type group and item
|
|
foreach (var fileTypeGroup in vm.AssociationsViewModel.FileTypeGroups)
|
|
foreach (var fileTypeGroup in vm.AssociationsViewModel.FileTypeGroups)
|
|
{
|
|
{
|
|
@@ -87,6 +92,7 @@ public partial class FileAssociationsView : UserControl
|
|
fileTypeGroup.Name = TranslationManager.Translation.Archives!;
|
|
fileTypeGroup.Name = TranslationManager.Translation.Archives!;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
// Create group header checkbox
|
|
// Create group header checkbox
|
|
var groupCheckBox = new CheckBox
|
|
var groupCheckBox = new CheckBox
|
|
{
|
|
{
|
|
@@ -96,22 +102,22 @@ public partial class FileAssociationsView : UserControl
|
|
IsThreeState = true,
|
|
IsThreeState = true,
|
|
IsChecked = fileTypeGroup.IsSelected.CurrentValue
|
|
IsChecked = fileTypeGroup.IsSelected.CurrentValue
|
|
};
|
|
};
|
|
-
|
|
|
|
|
|
+
|
|
var groupTextBlock = new TextBlock
|
|
var groupTextBlock = new TextBlock
|
|
{
|
|
{
|
|
Classes = { "txt" },
|
|
Classes = { "txt" },
|
|
Text = fileTypeGroup.Name,
|
|
Text = fileTypeGroup.Name,
|
|
FontFamily = new FontFamily("avares://PicView.Avalonia/Assets/Fonts/Roboto-Bold.ttf#Roboto")
|
|
FontFamily = new FontFamily("avares://PicView.Avalonia/Assets/Fonts/Roboto-Bold.ttf#Roboto")
|
|
};
|
|
};
|
|
-
|
|
|
|
|
|
+
|
|
groupCheckBox.Content = groupTextBlock;
|
|
groupCheckBox.Content = groupTextBlock;
|
|
-
|
|
|
|
|
|
+
|
|
// Add to container
|
|
// Add to container
|
|
container.Children.Add(groupCheckBox);
|
|
container.Children.Add(groupCheckBox);
|
|
-
|
|
|
|
|
|
+
|
|
// Add to the collection for filtering
|
|
// Add to the collection for filtering
|
|
_allCheckBoxes.Add((groupCheckBox, fileTypeGroup.Name));
|
|
_allCheckBoxes.Add((groupCheckBox, fileTypeGroup.Name));
|
|
-
|
|
|
|
|
|
+
|
|
// Handle group checkbox changes to update all items in the group
|
|
// Handle group checkbox changes to update all items in the group
|
|
groupCheckBox.Click += delegate
|
|
groupCheckBox.Click += delegate
|
|
{
|
|
{
|
|
@@ -121,9 +127,8 @@ public partial class FileAssociationsView : UserControl
|
|
{
|
|
{
|
|
fileType.IsSelected.Value = isChecked;
|
|
fileType.IsSelected.Value = isChecked;
|
|
}
|
|
}
|
|
- UpdateCheckBoxesFromViewModel();
|
|
|
|
};
|
|
};
|
|
-
|
|
|
|
|
|
+
|
|
// Create checkboxes for each file type item in the group
|
|
// Create checkboxes for each file type item in the group
|
|
foreach (var fileType in fileTypeGroup.FileTypes)
|
|
foreach (var fileType in fileTypeGroup.FileTypes)
|
|
{
|
|
{
|
|
@@ -134,64 +139,57 @@ public partial class FileAssociationsView : UserControl
|
|
IsChecked = fileType.IsSelected.CurrentValue,
|
|
IsChecked = fileType.IsSelected.CurrentValue,
|
|
IsThreeState = true
|
|
IsThreeState = true
|
|
};
|
|
};
|
|
-
|
|
|
|
|
|
+
|
|
var fileTextBlock = new TextBlock
|
|
var fileTextBlock = new TextBlock
|
|
{
|
|
{
|
|
Classes = { "txt" },
|
|
Classes = { "txt" },
|
|
Text = $"{fileType.Description} ({fileType.Extension})",
|
|
Text = $"{fileType.Description} ({fileType.Extension})",
|
|
Margin = new Thickness(0),
|
|
Margin = new Thickness(0),
|
|
- Padding = new Thickness(0, 1, 5, 0),
|
|
|
|
|
|
+ Padding = new Thickness(0, 1, 5, 0)
|
|
};
|
|
};
|
|
-
|
|
|
|
|
|
+
|
|
fileCheckBox.Content = fileTextBlock;
|
|
fileCheckBox.Content = fileTextBlock;
|
|
-
|
|
|
|
|
|
+
|
|
// Add to container
|
|
// Add to container
|
|
container.Children.Add(fileCheckBox);
|
|
container.Children.Add(fileCheckBox);
|
|
-
|
|
|
|
|
|
+
|
|
// Add to the collection for filtering
|
|
// Add to the collection for filtering
|
|
_allCheckBoxes.Add((fileCheckBox, $"{fileType.Description} {fileType.Extension}"));
|
|
_allCheckBoxes.Add((fileCheckBox, $"{fileType.Description} {fileType.Extension}"));
|
|
-
|
|
|
|
|
|
+
|
|
// Bind the checkbox to the file type's IsSelected property
|
|
// Bind the checkbox to the file type's IsSelected property
|
|
fileCheckBox.IsCheckedChanged += delegate
|
|
fileCheckBox.IsCheckedChanged += delegate
|
|
{
|
|
{
|
|
// Update the model - important to handle null state correctly
|
|
// Update the model - important to handle null state correctly
|
|
fileType.IsSelected.Value = fileCheckBox.IsChecked;
|
|
fileType.IsSelected.Value = fileCheckBox.IsChecked;
|
|
-
|
|
|
|
|
|
+
|
|
// Now update the group checkbox state
|
|
// Now update the group checkbox state
|
|
UpdateGroupCheckboxState(fileTypeGroup);
|
|
UpdateGroupCheckboxState(fileTypeGroup);
|
|
};
|
|
};
|
|
-
|
|
|
|
|
|
+
|
|
// Subscribe to changes in the file type's IsSelected property
|
|
// Subscribe to changes in the file type's IsSelected property
|
|
Observable.EveryValueChanged(fileType, x => x.IsSelected, UIHelper.GetFrameProvider)
|
|
Observable.EveryValueChanged(fileType, x => x.IsSelected, UIHelper.GetFrameProvider)
|
|
- .Subscribe( isSelected =>
|
|
|
|
- {
|
|
|
|
- fileCheckBox.IsChecked = isSelected.CurrentValue;
|
|
|
|
- })
|
|
|
|
|
|
+ .Subscribe(isSelected => { fileCheckBox.IsChecked = isSelected.CurrentValue; })
|
|
.AddTo(_disposables);
|
|
.AddTo(_disposables);
|
|
-
|
|
|
|
|
|
+
|
|
// Subscribe to changes in the file type's IsVisible property
|
|
// Subscribe to changes in the file type's IsVisible property
|
|
Observable.EveryValueChanged(fileType, x => x.IsVisible, UIHelper.GetFrameProvider)
|
|
Observable.EveryValueChanged(fileType, x => x.IsVisible, UIHelper.GetFrameProvider)
|
|
- .Subscribe(isVisible =>
|
|
|
|
- {
|
|
|
|
- fileCheckBox.IsVisible = isVisible.CurrentValue;
|
|
|
|
- })
|
|
|
|
|
|
+ .Subscribe(isVisible => { fileCheckBox.IsVisible = isVisible.CurrentValue; })
|
|
.AddTo(_disposables);
|
|
.AddTo(_disposables);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
private void UpdateGroupCheckboxState(FileTypeGroup group)
|
|
private void UpdateGroupCheckboxState(FileTypeGroup group)
|
|
{
|
|
{
|
|
// Find all checkboxes that are part of this group
|
|
// Find all checkboxes that are part of this group
|
|
var fileTypeCheckboxes = FileTypesContainer.Children.OfType<CheckBox>()
|
|
var fileTypeCheckboxes = FileTypesContainer.Children.OfType<CheckBox>()
|
|
- .Where(c => c.Tag != null && c.Tag.ToString() != "group" &&
|
|
|
|
|
|
+ .Where(c => c.Tag != null && c.Tag.ToString() != "group" &&
|
|
c.IsVisible && IsCheckboxInGroup(c, group));
|
|
c.IsVisible && IsCheckboxInGroup(c, group));
|
|
-
|
|
|
|
|
|
+
|
|
var allTrue = true;
|
|
var allTrue = true;
|
|
var allFalse = true;
|
|
var allFalse = true;
|
|
var anyNull = false;
|
|
var anyNull = false;
|
|
-
|
|
|
|
|
|
+
|
|
foreach (var cb in fileTypeCheckboxes)
|
|
foreach (var cb in fileTypeCheckboxes)
|
|
{
|
|
{
|
|
if (!cb.IsChecked.HasValue || cb.IsChecked == null)
|
|
if (!cb.IsChecked.HasValue || cb.IsChecked == null)
|
|
@@ -209,7 +207,7 @@ public partial class FileAssociationsView : UserControl
|
|
allTrue = false;
|
|
allTrue = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
// Find the group checkbox
|
|
// Find the group checkbox
|
|
var groupCheckbox = FileTypesContainer.Children.OfType<CheckBox>()
|
|
var groupCheckbox = FileTypesContainer.Children.OfType<CheckBox>()
|
|
.FirstOrDefault(c => c.Tag?.ToString() == "group" && c.Name == group.Name.Trim());
|
|
.FirstOrDefault(c => c.Tag?.ToString() == "group" && c.Name == group.Name.Trim());
|
|
@@ -220,14 +218,22 @@ public partial class FileAssociationsView : UserControl
|
|
}
|
|
}
|
|
|
|
|
|
if (anyNull)
|
|
if (anyNull)
|
|
|
|
+ {
|
|
groupCheckbox.IsChecked = null;
|
|
groupCheckbox.IsChecked = null;
|
|
|
|
+ }
|
|
else if (allTrue)
|
|
else if (allTrue)
|
|
|
|
+ {
|
|
groupCheckbox.IsChecked = true;
|
|
groupCheckbox.IsChecked = true;
|
|
|
|
+ }
|
|
else if (allFalse)
|
|
else if (allFalse)
|
|
|
|
+ {
|
|
groupCheckbox.IsChecked = false;
|
|
groupCheckbox.IsChecked = false;
|
|
|
|
+ }
|
|
else
|
|
else
|
|
|
|
+ {
|
|
groupCheckbox.IsChecked = null;
|
|
groupCheckbox.IsChecked = null;
|
|
-
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
// Update the ViewModel
|
|
// Update the ViewModel
|
|
group.IsSelected.Value = groupCheckbox.IsChecked;
|
|
group.IsSelected.Value = groupCheckbox.IsChecked;
|
|
}
|
|
}
|
|
@@ -237,71 +243,68 @@ public partial class FileAssociationsView : UserControl
|
|
// You can determine this by position in the UI or by extension tag
|
|
// You can determine this by position in the UI or by extension tag
|
|
var extension = checkbox.Tag?.ToString();
|
|
var extension = checkbox.Tag?.ToString();
|
|
if (string.IsNullOrEmpty(extension))
|
|
if (string.IsNullOrEmpty(extension))
|
|
|
|
+ {
|
|
return false;
|
|
return false;
|
|
-
|
|
|
|
- return group.FileTypes.Any(ft => ft.Extensions.Contains(extension) ||
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return group.FileTypes.Any(ft => ft.Extensions.Contains(extension) ||
|
|
extension.Contains(ft.Extensions.FirstOrDefault() ?? ""));
|
|
extension.Contains(ft.Extensions.FirstOrDefault() ?? ""));
|
|
}
|
|
}
|
|
-
|
|
|
|
- private void UpdateCheckBoxesFromViewModel()
|
|
|
|
|
|
+
|
|
|
|
+ private void UpdateCheckBoxesFromViewModel(Unit unit)
|
|
{
|
|
{
|
|
if (DataContext is not MainViewModel vm)
|
|
if (DataContext is not MainViewModel vm)
|
|
|
|
+ {
|
|
return;
|
|
return;
|
|
-
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
foreach (var group in vm.AssociationsViewModel.FileTypeGroups)
|
|
foreach (var group in vm.AssociationsViewModel.FileTypeGroups)
|
|
{
|
|
{
|
|
- // Find the group checkbox
|
|
|
|
- var groupName = $"{group.Name.Replace(" ", "")}Group";
|
|
|
|
- var groupCheckBox = FindLogicalDescendant<CheckBox>(groupName);
|
|
|
|
- if (groupCheckBox == null)
|
|
|
|
|
|
+
|
|
|
|
+ if (group?.Name is null)
|
|
{
|
|
{
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
+ // Find the group checkbox
|
|
|
|
+ var boxes = FileTypesContainer.GetLogicalChildren().OfType<CheckBox>();
|
|
|
|
+ var checkBoxes = boxes.ToList();
|
|
|
|
|
|
- // Check if all children are selected
|
|
|
|
- bool? allSelected = true;
|
|
|
|
- bool? anySelected = false;
|
|
|
|
-
|
|
|
|
foreach (var fileType in group.FileTypes)
|
|
foreach (var fileType in group.FileTypes)
|
|
{
|
|
{
|
|
- if (!fileType.IsSelected.CurrentValue.HasValue)
|
|
|
|
- anySelected = null;
|
|
|
|
- else if (!fileType.IsSelected.CurrentValue.Value)
|
|
|
|
- allSelected = false;
|
|
|
|
- else
|
|
|
|
- anySelected = true;
|
|
|
|
|
|
+ foreach (var checkBox in checkBoxes.Where(x => x.Tag.Equals(fileType.Extension)))
|
|
|
|
+ {
|
|
|
|
+ checkBox.IsChecked = fileType.IsSelected.Value ?? false;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
-
|
|
|
|
- // Update the group checkbox
|
|
|
|
- groupCheckBox.IsChecked = allSelected;
|
|
|
|
- groupCheckBox.IsThreeState = anySelected == null;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
- private T? FindLogicalDescendant<T>(string name) where T : Control
|
|
|
|
|
|
+
|
|
|
|
+ private T? FindLogicalDescendant<T>(string tag) where T : Control
|
|
{
|
|
{
|
|
foreach (var child in LogicalChildren)
|
|
foreach (var child in LogicalChildren)
|
|
{
|
|
{
|
|
switch (child)
|
|
switch (child)
|
|
{
|
|
{
|
|
- case T control when control.Name == name:
|
|
|
|
|
|
+ case T control when control.Tag == tag:
|
|
return control;
|
|
return control;
|
|
case not null:
|
|
case not null:
|
|
{
|
|
{
|
|
foreach (var grandchild in child.LogicalChildren)
|
|
foreach (var grandchild in child.LogicalChildren)
|
|
{
|
|
{
|
|
- if (grandchild is T grandControl && grandControl.Name == name)
|
|
|
|
|
|
+ if (grandchild is T grandControl && grandControl.Tag == tag)
|
|
|
|
+ {
|
|
return grandControl;
|
|
return grandControl;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
return null;
|
|
return null;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
private void FilterCheckBoxes(string? filterText)
|
|
private void FilterCheckBoxes(string? filterText)
|
|
{
|
|
{
|
|
if (string.IsNullOrWhiteSpace(filterText))
|
|
if (string.IsNullOrWhiteSpace(filterText))
|
|
@@ -311,9 +314,10 @@ public partial class FileAssociationsView : UserControl
|
|
{
|
|
{
|
|
checkBox.IsVisible = true;
|
|
checkBox.IsVisible = true;
|
|
}
|
|
}
|
|
|
|
+
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
foreach (var (checkBox, searchText) in _allCheckBoxes)
|
|
foreach (var (checkBox, searchText) in _allCheckBoxes)
|
|
{
|
|
{
|
|
checkBox.IsVisible = searchText.Contains(filterText, StringComparison.InvariantCultureIgnoreCase);
|
|
checkBox.IsVisible = searchText.Contains(filterText, StringComparison.InvariantCultureIgnoreCase);
|