Catalog/imagecatalog/AvaloniaViews/FaceAiTabView.axaml.cs
Maddo 398cfa310e
Some checks failed
Build Windows Avalonia / build (push) Failing after 1m46s
Build Windows Avalonia / release (push) Has been skipped
Refactor path handling in UI components
- Removed redundant folder and file opening methods from AiTabView and FaceAiTabView.
- Introduced PathPickerField control to streamline path selection and opening functionality across multiple views.
- Updated FaceAiTabView and GeneralTabView to utilize PathPickerField for source and destination path selection.
- Created PathShellService to encapsulate logic for opening paths in the file explorer.
- Simplified XAML structure by replacing manual grid definitions with PathPickerField components.
- Removed unused namespaces and cleaned up code for better readability and maintainability.
2026-05-24 19:07:17 +02:00

325 lines
11 KiB
C#

using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using ImageCatalog_2.Models;
using ImageCatalog_2.Services;
using System;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace ImageCatalog_2.AvaloniaViews;
public partial class FaceAiTabView : Avalonia.Controls.UserControl
{
private INotifyPropertyChanged? _faceAiPropertySource;
public FaceAiTabView()
{
InitializeComponent();
DataContextChanged += OnDataContextChanged;
}
private void OnDataContextChanged(object? sender, EventArgs e)
{
if (_faceAiPropertySource is not null)
{
_faceAiPropertySource.PropertyChanged -= OnFaceAiPropertyChanged;
}
_faceAiPropertySource = DataContext as INotifyPropertyChanged;
if (_faceAiPropertySource is not null)
{
_faceAiPropertySource.PropertyChanged += OnFaceAiPropertyChanged;
}
}
private void OnFaceAiPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (string.Equals(e.PropertyName, nameof(DataModel.FaceCommandOutput), StringComparison.Ordinal))
{
ScrollOutputTextBoxToEnd("FaceOutputTextBox");
return;
}
if (string.Equals(e.PropertyName, nameof(DataModel.FaceMatcherCommandOutput), StringComparison.Ordinal))
{
ScrollOutputTextBoxToEnd("FaceMatcherOutputTextBox");
}
}
private void ScrollOutputTextBoxToEnd(string controlName)
{
var outputBox = this.FindControl<Avalonia.Controls.TextBox>(controlName);
if (outputBox is null)
{
return;
}
Dispatcher.UIThread.Post(() =>
{
var textLength = outputBox.Text?.Length ?? 0;
outputBox.CaretIndex = textLength;
});
}
private async void OpenFaceMatcherPreview_Click(object? sender, RoutedEventArgs e)
{
if (sender is not Button { Tag: FaceMatcherResultItem item })
{
return;
}
await OpenFaceMatcherPreviewAsync(item);
}
private async Task OpenFaceMatcherPreviewAsync(FaceMatcherResultItem item)
{
var owner = TopLevel.GetTopLevel(this) as Window;
var dialog = BuildFaceMatcherPreviewDialog(item);
if (owner is not null)
{
await dialog.ShowDialog(owner);
return;
}
dialog.Show();
}
private async void FaceMatcherResults_DoubleTapped(object? sender, TappedEventArgs e)
{
if (sender is not Avalonia.Controls.DataGrid { SelectedItem: FaceMatcherResultItem item })
{
return;
}
await OpenFaceMatcherPreviewAsync(item);
}
private Window BuildFaceMatcherPreviewDialog(FaceMatcherResultItem item)
{
var dialog = new Window
{
Title = $"Preview match: {item.PhotoId}",
Width = 1180,
Height = 900,
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
Bitmap? bitmap = null;
var dimensionText = "n/d";
if (!string.IsNullOrWhiteSpace(item.ResolvedImagePath) && File.Exists(item.ResolvedImagePath))
{
try
{
bitmap = new Bitmap(item.ResolvedImagePath);
dimensionText = $"{bitmap.PixelSize.Width} x {bitmap.PixelSize.Height}px";
}
catch
{
bitmap = null;
}
}
var fileInfo = !string.IsNullOrWhiteSpace(item.ResolvedImagePath) && File.Exists(item.ResolvedImagePath)
? new FileInfo(item.ResolvedImagePath)
: null;
var debugBuilder = new StringBuilder();
debugBuilder.AppendLine($"File matcher: {item.PhotoId}");
debugBuilder.AppendLine($"Score: {item.ScoreDisplay}");
debugBuilder.AppendLine($"Path risolto: {item.ResolvedImagePath}");
debugBuilder.AppendLine($"Candidati trovati in destinazione: {item.CandidateCount}");
debugBuilder.AppendLine($"Dimensioni immagine: {dimensionText}");
if (fileInfo is not null)
{
debugBuilder.AppendLine($"Dimensione file: {fileInfo.Length / 1024.0:F1} KB");
debugBuilder.AppendLine($"Ultima modifica: {fileInfo.LastWriteTime:yyyy-MM-dd HH:mm:ss}");
}
debugBuilder.AppendLine($"Immagine ricerca: {item.SearchImagePath}");
debugBuilder.AppendLine($"CSV risultati: {item.CsvPath}");
debugBuilder.AppendLine($"Log matcher: {item.LogPath}");
if (!string.IsNullOrWhiteSpace(item.DebugSummary))
{
debugBuilder.AppendLine($"Dettagli riga: {item.DebugSummary}");
}
if (!string.IsNullOrWhiteSpace(item.RawRow))
{
debugBuilder.AppendLine($"Raw CSV: {item.RawRow}");
}
var layout = new Grid
{
Margin = new Avalonia.Thickness(16),
RowDefinitions = new RowDefinitions("Auto,*,Auto")
};
var header = new StackPanel { Spacing = 6 };
header.Children.Add(new TextBlock
{
Text = item.PhotoId,
FontWeight = FontWeight.Bold,
FontSize = 18
});
header.Children.Add(new TextBlock
{
Text = string.IsNullOrWhiteSpace(item.ScoreDisplay)
? "Score: n/d"
: $"Score: {item.ScoreDisplay}%",
FontWeight = FontWeight.SemiBold,
Opacity = 0.9
});
header.Children.Add(new TextBlock
{
Text = string.IsNullOrWhiteSpace(item.ResolvedImagePath)
? "Nessun file immagine risolto nella cartella Destinazione."
: item.ResolvedImagePath,
TextWrapping = TextWrapping.Wrap,
Opacity = 0.8
});
layout.Children.Add(header);
var contentGrid = new Grid
{
Margin = new Avalonia.Thickness(0, 12, 0, 12),
RowDefinitions = new RowDefinitions("Auto,*,Auto")
};
Grid.SetRow(contentGrid, 1);
var zoomLevel = 1.0;
var zoomText = new TextBlock
{
Text = "100%",
VerticalAlignment = VerticalAlignment.Center,
MinWidth = 52,
TextAlignment = TextAlignment.Center
};
var imageControl = bitmap is null
? null
: new Image
{
Source = bitmap,
Stretch = Stretch.None,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
RenderTransform = new ScaleTransform(1, 1)
};
void UpdateZoom(double delta)
{
if (imageControl is null)
{
return;
}
zoomLevel = Math.Clamp(zoomLevel + delta, 0.1, 8.0);
imageControl.RenderTransform = new ScaleTransform(zoomLevel, zoomLevel);
zoomText.Text = $"{zoomLevel * 100:0}%";
}
var toolbar = new StackPanel
{
Orientation = Orientation.Horizontal,
Spacing = 8,
Margin = new Avalonia.Thickness(0, 0, 0, 12)
};
var zoomOutButton = new Button { Content = "Zoom -", MinWidth = 80, IsEnabled = imageControl is not null };
zoomOutButton.Click += (_, _) => UpdateZoom(-0.1);
toolbar.Children.Add(zoomOutButton);
var zoomInButton = new Button { Content = "Zoom +", MinWidth = 80, IsEnabled = imageControl is not null };
zoomInButton.Click += (_, _) => UpdateZoom(0.1);
toolbar.Children.Add(zoomInButton);
var resetZoomButton = new Button { Content = "100%", MinWidth = 72, IsEnabled = imageControl is not null };
resetZoomButton.Click += (_, _) =>
{
if (imageControl is null)
{
return;
}
zoomLevel = 1.0;
imageControl.RenderTransform = new ScaleTransform(1, 1);
zoomText.Text = "100%";
};
toolbar.Children.Add(resetZoomButton);
toolbar.Children.Add(zoomText);
contentGrid.Children.Add(toolbar);
var previewBorder = new Border
{
BorderBrush = Brushes.Gray,
BorderThickness = new Avalonia.Thickness(1),
Padding = new Avalonia.Thickness(8),
Child = new ScrollViewer
{
HorizontalScrollBarVisibility = Avalonia.Controls.Primitives.ScrollBarVisibility.Auto,
VerticalScrollBarVisibility = Avalonia.Controls.Primitives.ScrollBarVisibility.Auto,
Content = bitmap is null
? new TextBlock
{
Text = "Anteprima non disponibile",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
}
: imageControl
}
};
Grid.SetRow(previewBorder, 1);
contentGrid.Children.Add(previewBorder);
var debugBox = new TextBox
{
Text = debugBuilder.ToString(),
IsReadOnly = true,
AcceptsReturn = true,
TextWrapping = TextWrapping.Wrap,
FontFamily = new FontFamily("Cascadia Mono, Consolas, monospace"),
MinHeight = 180,
Margin = new Avalonia.Thickness(0, 12, 0, 0)
};
Grid.SetRow(debugBox, 2);
contentGrid.Children.Add(debugBox);
layout.Children.Add(contentGrid);
var footer = new StackPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Right,
Spacing = 8
};
Grid.SetRow(footer, 2);
var openFileButton = new Button { Content = "Apri file" };
openFileButton.Click += (_, _) => PathShellService.OpenInExplorer(item.ResolvedImagePath);
footer.Children.Add(openFileButton);
var openFolderButton = new Button { Content = "Apri cartella" };
openFolderButton.Click += (_, _) =>
{
var directory = Path.GetDirectoryName(item.ResolvedImagePath);
PathShellService.OpenInExplorer(string.IsNullOrWhiteSpace(directory) ? item.ResolvedImagePath : directory);
};
footer.Children.Add(openFolderButton);
var closeButton = new Button { Content = "Chiudi", MinWidth = 88 };
closeButton.Click += (_, _) => dialog.Close();
footer.Children.Add(closeButton);
layout.Children.Add(footer);
dialog.Content = layout;
return dialog;
}
}