diff --git a/.gitignore b/.gitignore index 9a1e321..432b9b3 100644 --- a/.gitignore +++ b/.gitignore @@ -255,4 +255,3 @@ paket-files/ # JetBrains Rider .idea/ *.sln.iml -.vscode/settings.json diff --git a/Catalog.Communication/DependencyInjection/CatalogCommunicationServiceCollectionExtensions.cs b/Catalog.Communication/DependencyInjection/CatalogCommunicationServiceCollectionExtensions.cs index bb5a5b1..231242d 100644 --- a/Catalog.Communication/DependencyInjection/CatalogCommunicationServiceCollectionExtensions.cs +++ b/Catalog.Communication/DependencyInjection/CatalogCommunicationServiceCollectionExtensions.cs @@ -2,8 +2,6 @@ using System.Net; using Catalog.Communication.Abstractions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; namespace Catalog.Communication.DependencyInjection; @@ -27,30 +25,22 @@ public static class CatalogCommunicationServiceCollectionExtensions services.TryAddSingleton(); - // Create the HttpClient only when the communication client is requested. - // This avoids constructing the DefaultHttpClientFactory (and its background cleanup timer) - // if the race-upload feature is never used. - services.AddTransient(sp => - { - var options = sp.GetRequiredService>().Value; - var logger = sp.GetService>() ?? NullLogger.Instance; - var cookieContainer = sp.GetRequiredService(); - - var handler = new HttpClientHandler + services + .AddHttpClient((sp, client) => { - UseCookies = true, - CookieContainer = cookieContainer, - AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.Brotli, - }; - - var httpClient = new HttpClient(handler, disposeHandler: true) + var options = sp.GetRequiredService>().Value; + client.BaseAddress = options.BaseUri; + }) + .ConfigurePrimaryHttpMessageHandler(sp => { - BaseAddress = options.BaseUri, - Timeout = options.RequestTimeout, - }; - - return new RaceUploadCommunicationClient(httpClient, sp.GetRequiredService>(), logger); - }); + var cookieContainer = sp.GetRequiredService(); + return new HttpClientHandler + { + UseCookies = true, + CookieContainer = cookieContainer, + AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.Brotli, + }; + }); return services; } diff --git a/Catalog.Communication/RaceUploadCommunicationClient.cs b/Catalog.Communication/RaceUploadCommunicationClient.cs index 9657dfe..fc3987a 100644 --- a/Catalog.Communication/RaceUploadCommunicationClient.cs +++ b/Catalog.Communication/RaceUploadCommunicationClient.cs @@ -10,7 +10,7 @@ using Microsoft.Extensions.Options; namespace Catalog.Communication; -public sealed class RaceUploadCommunicationClient : IRaceUploadCommunicationClient, IDisposable +public sealed class RaceUploadCommunicationClient : IRaceUploadCommunicationClient { private const string AdminMenuPath = "admin/menu/Menu4.abl"; private const string PublicLogonPath = "Logon.abl"; @@ -27,7 +27,6 @@ public sealed class RaceUploadCommunicationClient : IRaceUploadCommunicationClie private readonly HttpClient _httpClient; private readonly ILogger _logger; private readonly IOptions _options; - private bool _disposed; public RaceUploadCommunicationClient( HttpClient httpClient, @@ -39,18 +38,6 @@ public sealed class RaceUploadCommunicationClient : IRaceUploadCommunicationClie _logger = logger; } - public void Dispose() - { - if (_disposed) - { - return; - } - - _httpClient.Dispose(); - _disposed = true; - GC.SuppressFinalize(this); - } - public Task LoginAdminAsync(AdminLoginRequest request, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(request); diff --git a/Catalog.sln b/Catalog.sln index 5d1d09f..b92613e 100644 --- a/Catalog.sln +++ b/Catalog.sln @@ -17,8 +17,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaddoShared.Benchmarks", "M EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Catalog.Communication", "Catalog.Communication\Catalog.Communication.csproj", "{EF5D3B7E-F380-4976-A0A9-085FEA157F79}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaddoShared.ImageSharpTests", "MaddoShared.ImageSharpTests\MaddoShared.ImageSharpTests.csproj", "{1528903F-3BF9-599C-2DD0-0AF7B5706675}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -89,26 +87,14 @@ Global {EF5D3B7E-F380-4976-A0A9-085FEA157F79}.Release|x64.Build.0 = Release|Any CPU {EF5D3B7E-F380-4976-A0A9-085FEA157F79}.Release|x86.ActiveCfg = Release|Any CPU {EF5D3B7E-F380-4976-A0A9-085FEA157F79}.Release|x86.Build.0 = Release|Any CPU - {1528903F-3BF9-599C-2DD0-0AF7B5706675}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1528903F-3BF9-599C-2DD0-0AF7B5706675}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1528903F-3BF9-599C-2DD0-0AF7B5706675}.Debug|x64.ActiveCfg = Debug|Any CPU - {1528903F-3BF9-599C-2DD0-0AF7B5706675}.Debug|x64.Build.0 = Debug|Any CPU - {1528903F-3BF9-599C-2DD0-0AF7B5706675}.Debug|x86.ActiveCfg = Debug|Any CPU - {1528903F-3BF9-599C-2DD0-0AF7B5706675}.Debug|x86.Build.0 = Debug|Any CPU - {1528903F-3BF9-599C-2DD0-0AF7B5706675}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1528903F-3BF9-599C-2DD0-0AF7B5706675}.Release|Any CPU.Build.0 = Release|Any CPU - {1528903F-3BF9-599C-2DD0-0AF7B5706675}.Release|x64.ActiveCfg = Release|Any CPU - {1528903F-3BF9-599C-2DD0-0AF7B5706675}.Release|x64.Build.0 = Release|Any CPU - {1528903F-3BF9-599C-2DD0-0AF7B5706675}.Release|x86.ActiveCfg = Release|Any CPU - {1528903F-3BF9-599C-2DD0-0AF7B5706675}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {AEBFE9E3-277C-4A7B-8448-145D1B11998B} = {A3D50937-74F6-4DC8-8D89-B534B484C0F9} - {59952BE8-20B4-4BF2-9367-705F41395265} = {5F0BEF23-B1EA-4100-A772-DC455D40B1C1} {EF5D3B7E-F380-4976-A0A9-085FEA157F79} = {A3D50937-74F6-4DC8-8D89-B534B484C0F9} + {59952BE8-20B4-4BF2-9367-705F41395265} = {5F0BEF23-B1EA-4100-A772-DC455D40B1C1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0E3ABC63-8601-4DAC-AFEA-33F3E8E36757} diff --git a/MaddoShared.ImageSharpTests/Helpers/CreatorFactory.cs b/MaddoShared.ImageSharpTests/Helpers/CreatorFactory.cs deleted file mode 100644 index cb25390..0000000 --- a/MaddoShared.ImageSharpTests/Helpers/CreatorFactory.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.IO; -using Microsoft.Extensions.Logging.Abstractions; -using SixLabors.ImageSharp.PixelFormats; - -namespace MaddoShared.ImageSharpTests.Helpers -{ - public static class CreatorFactory - { - public static MaddoShared.PicSettings CreateDefaultPicSettings() - { - return new MaddoShared.PicSettings - { - DimStandard = 48, - DimStandardMiniatura = 12, - LarghezzaSmall = 150, - AltezzaSmall = 150, - LarghezzaBig = 800, - AltezzaBig = 600, - Trasparenza = 0, - IlFont = "Arial", - Grassetto = false, - Posizione = "CENTRO", - Allineamento = "CENTRO", - Margine = 10, - MargVert = 10, - TestoMin = false, - AggNumTempMin = false, - CreaMiniature = false, - LogoAggiungi = false, - LogoAltezza = 100, - LogoLarghezza = 100, - LogoMargine = "0", - JpegQuality = 90, - JpegQualityMin = 75, - }; - } - - public static MaddoShared.ImageCreatorImageSharp CreateImageCreator(MaddoShared.PicSettings settings) - { - var logger = NullLogger.Instance; - return new MaddoShared.ImageCreatorImageSharp(settings, logger); - } - } -} diff --git a/MaddoShared.ImageSharpTests/Helpers/PixelInspector.cs b/MaddoShared.ImageSharpTests/Helpers/PixelInspector.cs deleted file mode 100644 index 5642ec7..0000000 --- a/MaddoShared.ImageSharpTests/Helpers/PixelInspector.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; - -namespace MaddoShared.ImageSharpTests.Helpers -{ - public static class PixelInspector - { - public static int CountNonBackgroundPixels(string path, int x, int y, int width, int height, Rgba32 background, int tolerance = 0) - { - using var img = SixLabors.ImageSharp.Image.Load(path); - var bx = Math.Max(0, x); - var by = Math.Max(0, y); - var bw = Math.Min(width, img.Width - bx); - var bh = Math.Min(height, img.Height - by); - if (bw <= 0 || bh <= 0) return 0; - - int count = 0; - img.ProcessPixelRows(accessor => - { - for (int yy = by; yy < by + bh; yy++) - { - var row = accessor.GetRowSpan(yy); - for (int xx = bx; xx < bx + bw; xx++) - { - var p = row[xx]; - if (!IsApproximatelyEqual(p, background, tolerance)) count++; - } - } - }); - - return count; - } - - private static bool IsApproximatelyEqual(Rgba32 a, Rgba32 b, int tol) - { - return Math.Abs(a.R - b.R) <= tol && Math.Abs(a.G - b.G) <= tol && Math.Abs(a.B - b.B) <= tol && Math.Abs(a.A - b.A) <= tol; - } - } -} diff --git a/MaddoShared.ImageSharpTests/Helpers/TempWorkspace.cs b/MaddoShared.ImageSharpTests/Helpers/TempWorkspace.cs deleted file mode 100644 index fba6bd8..0000000 --- a/MaddoShared.ImageSharpTests/Helpers/TempWorkspace.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.IO; - -namespace MaddoShared.ImageSharpTests.Helpers -{ - public sealed class TempWorkspace : IDisposable - { - public DirectoryInfo Root { get; } - public DirectoryInfo SourceDir { get; } - public DirectoryInfo DestDir { get; } - - public TempWorkspace() - { - var root = Path.Combine(Path.GetTempPath(), "MaddoShared.ImageSharpTests", Guid.NewGuid().ToString("N")); - Root = Directory.CreateDirectory(root); - SourceDir = Directory.CreateDirectory(Path.Combine(Root.FullName, "Source")); - DestDir = Directory.CreateDirectory(Path.Combine(Root.FullName, "Dest")); - } - - public void Dispose() - { - try - { - if (Root.Exists) - Root.Delete(true); - } - catch - { - // best-effort cleanup - } - } - } -} diff --git a/MaddoShared.ImageSharpTests/Helpers/TestImageFactory.cs b/MaddoShared.ImageSharpTests/Helpers/TestImageFactory.cs deleted file mode 100644 index 4f15dd8..0000000 --- a/MaddoShared.ImageSharpTests/Helpers/TestImageFactory.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.IO; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Metadata.Profiles.Exif; - -namespace MaddoShared.ImageSharpTests.Helpers -{ - public static class TestImageFactory - { - public static string CreateSolidJpeg(string directory, string fileName, int width, int height, Rgba32 color) - { - Directory.CreateDirectory(directory); - var path = Path.Combine(directory, fileName); - using var img = new Image(width, height, color); - var encoder = new JpegEncoder { Quality = 90 }; - img.Save(path, encoder); - return path; - } - - public static string CreateSolidPng(string directory, string fileName, int width, int height, Rgba32 color) - { - Directory.CreateDirectory(directory); - var path = Path.Combine(directory, fileName); - using var img = new Image(width, height, color); - img.SaveAsPng(path); - return path; - } - - public static string CreateJpegWithExifOrientation(string directory, string fileName, int width, int height, Rgba32 color, ushort orientation) - { - Directory.CreateDirectory(directory); - var path = Path.Combine(directory, fileName); - using var img = new Image(width, height, color); - // Add EXIF orientation - var profile = new ExifProfile(); - profile.SetValue(ExifTag.Orientation, orientation); - img.Metadata.ExifProfile = profile; - var encoder = new JpegEncoder { Quality = 90 }; - img.Save(path, encoder); - return path; - } - } -} diff --git a/MaddoShared.ImageSharpTests/MaddoShared.ImageSharpTests.csproj b/MaddoShared.ImageSharpTests/MaddoShared.ImageSharpTests.csproj deleted file mode 100644 index 52f36f6..0000000 --- a/MaddoShared.ImageSharpTests/MaddoShared.ImageSharpTests.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - net10.0 - latest - enable - enable - false - - - - - - - - - - - - - - - - - - - diff --git a/MaddoShared.ImageSharpTests/Tests/ImageResizingTests.cs b/MaddoShared.ImageSharpTests/Tests/ImageResizingTests.cs deleted file mode 100644 index ddc75ce..0000000 --- a/MaddoShared.ImageSharpTests/Tests/ImageResizingTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.IO; -using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SixLabors.ImageSharp.PixelFormats; -using MaddoShared.ImageSharpTests.Helpers; -using Shouldly; - -namespace MaddoShared.ImageSharpTests.Tests -{ - [TestClass] - public class ImageResizingTests - { - [TestMethod] - public async Task BigImageResizesRespectSettings() - { - using var ws = new TempWorkspace(); - - // create a large input image - var inputPath = TestImageFactory.CreateSolidJpeg(ws.SourceDir.FullName, "input.jpg", 1600, 1200, new Rgba32(200, 200, 200, 255)); - - var pic = CreatorFactory.CreateDefaultPicSettings(); - pic.LarghezzaBig = 800; - pic.AltezzaBig = 600; - pic.CreaMiniature = false; - - var svc = CreatorFactory.CreateImageCreator(pic); - - var state = new MaddoShared.ImageState - { - WorkFile = new FileInfo(inputPath), - DestDir = ws.DestDir, - SourceDir = ws.SourceDir - }; - - await svc.CreateImageAsync(state, null); - - var outPath = Path.Combine(ws.DestDir.FullName, state.NomeFileBig); - using var outImg = SixLabors.ImageSharp.Image.Load(outPath); - - outImg.Width.ShouldBe(800); - outImg.Height.ShouldBe(600); - } - } -} diff --git a/MaddoShared.ImageSharpTests/Tests/TextPositioningTests.cs b/MaddoShared.ImageSharpTests/Tests/TextPositioningTests.cs deleted file mode 100644 index c93460e..0000000 --- a/MaddoShared.ImageSharpTests/Tests/TextPositioningTests.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.IO; -using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SixLabors.ImageSharp.PixelFormats; -using MaddoShared.ImageSharpTests.Helpers; -using Shouldly; - -namespace MaddoShared.ImageSharpTests.Tests -{ - [TestClass] - public class TextPositioningTests - { - [TestMethod] - public async Task TextAtBottom_IncreasesNonBackgroundPixelCountInBottomBand() - { - using var ws = new TempWorkspace(); - - // create white background input - var inputPath = TestImageFactory.CreateSolidJpeg(ws.SourceDir.FullName, "input.jpg", 800, 600, new Rgba32(255, 255, 255, 255)); - - var pic = CreatorFactory.CreateDefaultPicSettings(); - pic.Posizione = "BASSO"; - pic.DimStandard = 48; // big text - pic.TestoFirmaStart = "SAMPLE TEXT"; - pic.CreaMiniature = false; - - var svc = CreatorFactory.CreateImageCreator(pic); - - var state = new MaddoShared.ImageState - { - WorkFile = new FileInfo(inputPath), - DestDir = ws.DestDir, - SourceDir = ws.SourceDir - }; - - await svc.CreateImageAsync(state, null); - - var outPath = Path.Combine(ws.DestDir.FullName, state.NomeFileBig); - - // bottom band (lower 25% of image) - var bottomY = (int)(600 * 0.75); - var bottomCount = PixelInspector.CountNonBackgroundPixels(outPath, 0, bottomY, 800, 600 - bottomY, new Rgba32(255, 255, 255, 255), tolerance: 10); - - // top band (upper 25%) - var topCount = PixelInspector.CountNonBackgroundPixels(outPath, 0, 0, 800, (int)(600 * 0.25), new Rgba32(255, 255, 255, 255), tolerance: 10); - - (bottomCount > 50).ShouldBeTrue($"Expected text pixels in bottom band, found {bottomCount}"); - (bottomCount > topCount).ShouldBeTrue("Expected more non-background pixels at bottom than top"); - } - } -} diff --git a/MaddoShared.Tests/DataModelCharacterizationTests.cs b/MaddoShared.Tests/DataModelCharacterizationTests.cs deleted file mode 100644 index 7ce601e..0000000 --- a/MaddoShared.Tests/DataModelCharacterizationTests.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System; -using System.Threading.Tasks; -using ImageCatalog_2; -using ImageCatalog_2.Services; -using MaddoShared; -using Microsoft.Extensions.Logging; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NSubstitute; -using Shouldly; - -namespace MaddoShared.Tests; - -[TestClass] -public class DataModelCharacterizationTests -{ - [TestMethod] - public void SelectSourceFolderCommand_RaisesEvent() - { - var model = CreateModel(); - var raised = false; - model.SelectSourceFolderRequested += (_, _) => raised = true; - - model.SelectSourceFolderCommand.Execute(null); - - raised.ShouldBeTrue(); - } - - [TestMethod] - public async Task SaveSettingsToFileAsync_DelegatesToSettingsService() - { - var settingsService = Substitute.For(); - settingsService - .SaveSettingsAsync(Arg.Any(), Arg.Any()) - .Returns(Task.CompletedTask); - - var model = CreateModel(settingsService: settingsService); - - await model.SaveSettingsToFileAsync("settings.xml"); - - await settingsService.Received(1) - .SaveSettingsAsync("settings.xml", model); - } - - [TestMethod] - public async Task LoadSettingsFromFileAsync_DelegatesToSettingsService() - { - var settingsService = Substitute.For(); - settingsService - .LoadSettingsAsync(Arg.Any(), Arg.Any()) - .Returns(Task.CompletedTask); - - var model = CreateModel(settingsService: settingsService); - - await model.LoadSettingsFromFileAsync("settings.xml"); - - await settingsService.Received(1) - .LoadSettingsAsync("settings.xml", model); - } - - [TestMethod] - public void ThumbnailOptionIndex_UpdatesAuthoritativeThumbnailState() - { - var model = CreateModel(); - - model.ThumbnailOptionIndex = (int)DataModel.ThumbnailOptionEnum.RaceTime; - - model.ThumbnailOption.ShouldBe(DataModel.ThumbnailOptionEnum.RaceTime); - model.AddRaceTimeToThumbnails.ShouldBeTrue(); - model.ThumbnailMode.ShouldBe("RaceTime"); - } - - [TestMethod] - public void ProcessingChildChange_RaisesDataModelPropertyChanged() - { - var model = CreateModel(); - string? changed = null; - model.PropertyChanged += (_, args) => changed = args.PropertyName; - - model.Processing.SpeedCounter = "12.00 f/s"; - - changed.ShouldBe(nameof(DataModel.SpeedCounter)); - model.SpeedCounter.ShouldBe("12.00 f/s"); - } - - [TestMethod] - public void PathsNormalize_UpdatesFlattenedSourceAndDestination() - { - var model = CreateModel(); - model.SourcePath = "\"C:/input\""; - model.DestinationPath = "C:/output"; - - model.Paths.NormalizePaths(); - - model.SourcePath.ShouldBe($"C:{System.IO.Path.DirectorySeparatorChar}input{System.IO.Path.DirectorySeparatorChar}"); - model.DestinationPath.ShouldBe($"C:{System.IO.Path.DirectorySeparatorChar}output{System.IO.Path.DirectorySeparatorChar}"); - } - - [TestMethod] - public void AiChildChange_RaisesDataModelPropertyChanged() - { - var model = CreateModel(); - string? changed = null; - model.PropertyChanged += (_, args) => changed = args.PropertyName; - - model.Ai.ModelsFolderPath = "K:/models"; - - changed.ShouldBe(nameof(DataModel.ModelsFolderPath)); - model.ModelsFolderPath.ShouldBe("K:/models"); - } - - [TestMethod] - public void RaceUploadChildChange_RaisesDataModelPropertyChanged() - { - var model = CreateModel(); - string? changed = null; - model.PropertyChanged += (_, args) => changed = args.PropertyName; - - model.RaceUpload.ApiLogin = "admin"; - - changed.ShouldBe(nameof(DataModel.ApiLogin)); - model.ApiLogin.ShouldBe("admin"); - } - - [TestMethod] - public void VisualChildChange_RaisesDataModelPropertyChanged() - { - var model = CreateModel(); - string? changed = null; - model.PropertyChanged += (_, args) => changed = args.PropertyName; - - model.Visual.FontSize = 42; - - changed.ShouldBe(nameof(DataModel.FontSize)); - model.FontSize.ShouldBe(42); - } - - private static DataModel CreateModel( - ISettingsService? settingsService = null, - ITestService? testService = null) - { - var mapper = Substitute.For(); - var picSettings = new PicSettings(); - - var imageCreator = Substitute.For(); - imageCreator - .CreateImageAsync(Arg.Any(), Arg.Any()) - .Returns(Task.CompletedTask); - - var imageCreationService = new ImageCreationService( - Substitute.For>(), - picSettings, - imageCreator); - - var imageProcessingCoordinator = new ImageProcessingCoordinator( - imageCreationService, - Substitute.For>()); - - var aiExtractionService = new AiExtractionService( - Substitute.For>()); - - return new DataModel( - testService ?? Substitute.For(), - settingsService ?? Substitute.For(), - imageCreationService, - aiExtractionService, - imageProcessingCoordinator, - picSettings, - mapper, - Substitute.For>(), - versionProvider: null); - } -} diff --git a/MaddoShared.Tests/ImageCreatorSharpTests.cs b/MaddoShared.Tests/ImageCreatorSharpTests.cs index ae10edc..8d0dc91 100644 --- a/MaddoShared.Tests/ImageCreatorSharpTests.cs +++ b/MaddoShared.Tests/ImageCreatorSharpTests.cs @@ -5,8 +5,8 @@ using System.IO; using System.Reflection; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Extensions.Logging; -using NSubstitute; -using Shouldly; +using Moq; +using FluentAssertions; using MaddoShared; namespace MaddoShared.Tests @@ -37,7 +37,7 @@ namespace MaddoShared.Tests customize?.Invoke(settings); - var logger = Substitute.For>(); + var logger = new Mock>().Object; return new ImageCreatorGDI(settings, logger); } @@ -46,12 +46,12 @@ namespace MaddoShared.Tests { var svc = CreateService(); var mi = svc.GetType().GetMethod("CalculateThumbnailSize", BindingFlags.NonPublic | BindingFlags.Instance); - mi.ShouldNotBeNull(); + mi.Should().NotBeNull(); var size = (Size)mi.Invoke(svc, new object[] { 400, 200, 200, "Larghezza" }); - size.Width.ShouldBe(200); - size.Height.ShouldBe(100); + size.Width.Should().Be(200); + size.Height.Should().Be(100); } [TestMethod] @@ -59,12 +59,12 @@ namespace MaddoShared.Tests { var svc = CreateService(); var mi = svc.GetType().GetMethod("CalculateThumbnailSize", BindingFlags.NonPublic | BindingFlags.Instance); - mi.ShouldNotBeNull(); + mi.Should().NotBeNull(); var size = (Size)mi.Invoke(svc, new object[] { 200, 400, 200, "Altezza" }); - size.Width.ShouldBe(100); - size.Height.ShouldBe(200); + size.Width.Should().Be(100); + size.Height.Should().Be(200); } [TestMethod] @@ -72,13 +72,13 @@ namespace MaddoShared.Tests { var svc = CreateService(); var mi = svc.GetType().GetMethod("IsSameDirectory", BindingFlags.NonPublic | BindingFlags.Instance); - mi.ShouldNotBeNull(); + mi.Should().NotBeNull(); bool same = (bool)mi.Invoke(svc, new object[] { @"C:\Temp", @"c:\temp" }); - same.ShouldBeTrue(); + same.Should().BeTrue(); bool notSame = (bool)mi.Invoke(svc, new object[] { @"C:\TempA", @"c:\temp" }); - notSame.ShouldBeFalse(); + notSame.Should().BeFalse(); } [TestMethod] @@ -86,12 +86,12 @@ namespace MaddoShared.Tests { var svc = CreateService(s => s.Codice = "_X"); var mi = svc.GetType().GetMethod("UpdateFilenameWithCode", BindingFlags.NonPublic | BindingFlags.Instance); - mi.ShouldNotBeNull(); + mi.Should().NotBeNull(); var state = new ImageState { NomeFileSmall = "photo123.jpg" }; mi.Invoke(svc, new object[] { state }); - state.NomeFileSmall.ShouldBe("photo123_X.jpg"); + state.NomeFileSmall.Should().Be("photo123_X.jpg"); } [DataTestMethod] @@ -103,16 +103,16 @@ namespace MaddoShared.Tests var svc = CreateService(s => { s.Allineamento = alignment; s.Margine = 20; }); var mi = svc.GetType().GetMethod("CalculateHorizontalAlignment", BindingFlags.NonPublic | BindingFlags.Instance); - mi.ShouldNotBeNull(); + mi.Should().NotBeNull(); var center = (float)mi.Invoke(svc, new object[] { 800, 100f }); if (alignment == "SINISTRA") - center.ShouldBeInRange(0f, 400f); + center.Should().BeInRange(0f, 400f, "Expected left alignment range"); if (alignment == "DESTRA") - center.ShouldBeInRange(400f, 800f); + center.Should().BeInRange(400f, 800f, "Expected right alignment range"); if (alignment == "CENTRO") - center.ShouldBe(800 / 2f, 0.0001f); + center.Should().BeApproximately(800 / 2f, 0.0001f); } [TestMethod] @@ -120,14 +120,14 @@ namespace MaddoShared.Tests { var svc = CreateService(s => s.Posizione = "ALTO"); var mi = svc.GetType().GetMethod("SetVerticalPosition", BindingFlags.NonPublic | BindingFlags.Instance); - mi.ShouldNotBeNull(); + mi.Should().NotBeNull(); var state = new ImageState(); // ALTO mi.Invoke(svc, new object[] { 500, 20f, state }); - state.YPosFromBottom1.ShouldBe(10f); - state.YPosFromBottom4.ShouldBe(10f); + state.YPosFromBottom1.Should().Be(10f); + state.YPosFromBottom4.Should().Be(10f); // BASSO state = new ImageState(); @@ -137,8 +137,8 @@ namespace MaddoShared.Tests var expected1 = (float)(200 - 20 - (200 * 10 / 100.0)); var expected4 = (float)(200 - 20 - (200 * 5 / 100.0)); - state.YPosFromBottom1.ShouldBe(expected1, 0.001f); - state.YPosFromBottom4.ShouldBe(expected4, 0.001f); + state.YPosFromBottom1.Should().BeApproximately(expected1, 0.001f); + state.YPosFromBottom4.Should().BeApproximately(expected4, 0.001f); } [TestMethod] @@ -146,7 +146,7 @@ namespace MaddoShared.Tests { var svc = CreateService(); var mi = svc.GetType().GetMethod("FormatTimeText", BindingFlags.NonPublic | BindingFlags.Instance); - mi.ShouldNotBeNull(); + mi.Should().NotBeNull(); var state = new ImageState { @@ -156,13 +156,13 @@ namespace MaddoShared.Tests DataFoto = new DateTime(2024, 01, 01, 11, 59, 0) }; var withoutName = (string)mi.Invoke(svc, new object[] { state, false }); - withoutName.ShouldStartWith(Environment.NewLine); - withoutName.ShouldContain("T:"); + withoutName.Should().StartWith(Environment.NewLine); + withoutName.Should().Contain("T:"); var withName = (string)mi.Invoke(svc, new object[] { state, true }); - withName.ShouldContain("file.jpg"); - withName.ShouldContain("T:"); - withName.ShouldContain(Environment.NewLine); + withName.Should().Contain("file.jpg"); + withName.Should().Contain("T:"); + withName.Should().Contain(Environment.NewLine); } [TestMethod] @@ -170,18 +170,18 @@ namespace MaddoShared.Tests { var svc = CreateService(); var miPrep = svc.GetType().GetMethod("PrepareSignatureText", BindingFlags.NonPublic | BindingFlags.Instance); - miPrep.ShouldNotBeNull(); + miPrep.Should().NotBeNull(); var state = new ImageState { NomeFileBig = "bigname.jpg" }; svc = CreateService(s => s.TestoMin = true); miPrep.Invoke(svc, new object[] { state }); - state.TestoFirmaPiccola.ShouldBe("bigname.jpg"); + state.TestoFirmaPiccola.Should().Be("bigname.jpg"); state.TestoFirmaPiccola = ""; svc = CreateService(s => { s.TestoMin = false; s.AggNumTempMin = true; }); miPrep.Invoke(svc, new object[] { state }); - state.TestoFirmaPiccola.ShouldBe("bigname.jpg "); + state.TestoFirmaPiccola.Should().Be("bigname.jpg "); } [TestMethod] @@ -189,15 +189,15 @@ namespace MaddoShared.Tests { var svc = CreateService(s => { s.UsaOrarioMiniatura = false; s.TestoMin = false; s.AggTempoGaraMin = false; s.AggNumTempMin = false; }); var mi = svc.GetType().GetMethod("ShouldRenderText", BindingFlags.NonPublic | BindingFlags.Instance); - mi.ShouldNotBeNull(); + mi.Should().NotBeNull(); var res = (bool)mi.Invoke(svc, Array.Empty()); - res.ShouldBeFalse(); + res.Should().BeFalse(); svc = CreateService(s => s.TestoMin = true); mi = svc.GetType().GetMethod("ShouldRenderText", BindingFlags.NonPublic | BindingFlags.Instance); res = (bool)mi.Invoke(svc, Array.Empty()); - res.ShouldBeTrue(); + res.Should().BeTrue(); } [TestMethod] @@ -209,29 +209,24 @@ namespace MaddoShared.Tests using var g = Graphics.FromImage(bmp); var miFind = svc.GetType().GetMethod("FindBestFontSize", BindingFlags.NonPublic | BindingFlags.Instance); - miFind.ShouldNotBeNull(); + miFind.Should().NotBeNull(); int best = (int)miFind.Invoke(svc, new object[] { g, "A very long text that won't fit", "Arial", 40, false, 50, 5 }); - best.ShouldBeInRange(5, 40); + best.Should().BeInRange(5, 40); + + var miAdjust = svc.GetType().GetMethod("AdjustFontToFitWidth", BindingFlags.NonPublic | BindingFlags.Instance); + miAdjust.Should().NotBeNull(); - // The helper AdjustFontToFitWidth was in an earlier refactor; replicate its logic here var imageState = new ImageState { DimensioneStandardMiniatura = 30, TestoFirmaPiccola = "A very long test string" }; var initialFont = new Font("Arial", imageState.DimensioneStandardMiniatura); var textSize = g.MeasureString(imageState.TestoFirmaPiccola, initialFont); - int tempFontSize = imageState.DimensioneStandardMiniatura; - while ((textSize.Width > 50) && tempFontSize > 5) - { - tempFontSize = (tempFontSize > 20) ? tempFontSize - 5 : tempFontSize - 1; - using var tempFont = new Font("Arial", tempFontSize); - textSize = g.MeasureString(imageState.TestoFirmaPiccola, tempFont); - } + object[] parameters = new object[] { g, 50, imageState, textSize }; + miAdjust.Invoke(svc, parameters); - var updatedSize = textSize; - imageState.DimensioneStandardMiniatura = tempFontSize; - - imageState.DimensioneStandardMiniatura.ShouldBeLessThanOrEqualTo(30); - (updatedSize.Width <= 50 || imageState.DimensioneStandardMiniatura <= 5).ShouldBeTrue(); + var updatedSize = (SizeF)parameters[3]; + imageState.DimensioneStandardMiniatura.Should().BeLessThanOrEqualTo(30); + (updatedSize.Width <= 50 || imageState.DimensioneStandardMiniatura <= 5).Should().BeTrue(); } } } diff --git a/MaddoShared.Tests/MaddoShared.Tests.csproj b/MaddoShared.Tests/MaddoShared.Tests.csproj index 1d562c8..b2f98b7 100644 --- a/MaddoShared.Tests/MaddoShared.Tests.csproj +++ b/MaddoShared.Tests/MaddoShared.Tests.csproj @@ -15,14 +15,13 @@ - - + + - diff --git a/MaddoShared/ImageCreatorImageSharp.cs b/MaddoShared/ImageCreatorAlternate.cs similarity index 100% rename from MaddoShared/ImageCreatorImageSharp.cs rename to MaddoShared/ImageCreatorAlternate.cs diff --git a/MaddoShared/ImageCreatorGDI.cs b/MaddoShared/ImageCreatorSharp.cs similarity index 91% rename from MaddoShared/ImageCreatorGDI.cs rename to MaddoShared/ImageCreatorSharp.cs index 15fd6f6..0b28d9f 100644 --- a/MaddoShared/ImageCreatorGDI.cs +++ b/MaddoShared/ImageCreatorSharp.cs @@ -228,23 +228,23 @@ public class ImageCreatorGDI(PicSettings picSettings, ILogger l } } - private void CreateThumbnails(Image sourceImage, ImageState imgState, Bitmap imgOutputBig, ImageFormat format) - { - // Only skip thumbnail generation when the global "create thumbnails" flag is false. - // Whether thumbnails include text is handled by ShouldRenderText/CreateThumbnailWithText - if (!picSettings.CreaMiniature) - return; + private void CreateThumbnails(Image sourceImage, ImageState imgState, Bitmap imgOutputBig, ImageFormat format) + { + // Only skip thumbnail generation when the global "create thumbnails" flag is false. + // Whether thumbnails include text is handled by ShouldRenderText/CreateThumbnailWithText + if (!picSettings.CreaMiniature) + return; - PrepareSignatureText(imgState); + PrepareSignatureText(imgState); - if (IsSameDirectory(picSettings.DirectorySorgente, picSettings.DirectoryDestinazione)) - UpdateFilenameWithCode(imgState); + if (IsSameDirectory(picSettings.DirectorySorgente, picSettings.DirectoryDestinazione)) + UpdateFilenameWithCode(imgState); - if (ShouldRenderText()) - CreateThumbnailWithText(sourceImage, imgState, imgOutputBig, format); - else - CreateSimpleThumbnail(sourceImage, imgState, format); - } + if (ShouldRenderText()) + CreateThumbnailWithText(sourceImage, imgState, imgOutputBig, format); + else + CreateSimpleThumbnail(sourceImage, imgState, format); + } private void PrepareSignatureText(ImageState imgState) { @@ -294,7 +294,7 @@ public class ImageCreatorGDI(PicSettings picSettings, ILogger l // This leaves room for margins and prevents clipping int tempFontSize = imgState.DimensioneStandardMiniatura; float maxTextHeight = image.Height * 0.15f; - + while ((textSize.Width > image.Width * 0.95f || textSize.Height > maxTextHeight) && tempFontSize > 5) { tempFontSize = (tempFontSize > 20) ? tempFontSize - 5 : tempFontSize - 1; @@ -375,20 +375,20 @@ public class ImageCreatorGDI(PicSettings picSettings, ILogger l switch (picSettings.Posizione.ToUpper()) { case "ALTO": - { - imgState.YPosFromBottom = picSettings.Margine; - imgState.YPosFromBottom3 = picSettings.MargVert; - break; - } + { + imgState.YPosFromBottom = picSettings.Margine; + imgState.YPosFromBottom3 = picSettings.MargVert; + break; + } case "BASSO": - { - imgState.YPosFromBottom = - Convert.ToSingle((g.Height - crSize.Height - (g.Height * picSettings.Margine / 100.0))); - imgState.YPosFromBottom3 = - Convert.ToSingle((g.Height - crSize.Height - (g.Height * picSettings.MargVert / 100.0))); - break; - } + { + imgState.YPosFromBottom = + Convert.ToSingle((g.Height - crSize.Height - (g.Height * picSettings.Margine / 100.0))); + imgState.YPosFromBottom3 = + Convert.ToSingle((g.Height - crSize.Height - (g.Height * picSettings.MargVert / 100.0))); + break; + } } float xCenterOfImg = 0; @@ -396,27 +396,27 @@ public class ImageCreatorGDI(PicSettings picSettings, ILogger l switch (picSettings.Allineamento.ToUpper()) { case "SINISTRA": - { - xCenterOfImg = Convert.ToSingle((picSettings.Margine + (larghezzaStandard / (double)2))); - if ((larghezzaStandard / (double)2) > (g.Width / (double)2) - picSettings.Margine) - xCenterOfImg = Convert.ToSingle((g.Width / (double)2)); - break; - } + { + xCenterOfImg = Convert.ToSingle((picSettings.Margine + (larghezzaStandard / (double)2))); + if ((larghezzaStandard / (double)2) > (g.Width / (double)2) - picSettings.Margine) + xCenterOfImg = Convert.ToSingle((g.Width / (double)2)); + break; + } case "CENTRO": - { - xCenterOfImg = Convert.ToSingle((g.Width / (double)2)); - break; - } + { + xCenterOfImg = Convert.ToSingle((g.Width / (double)2)); + break; + } case "DESTRA": - { - xCenterOfImg = - Convert.ToSingle((g.Width - picSettings.Margine - (larghezzaStandard / (double)2))); - if ((larghezzaStandard / (double)2) > (g.Width / (double)2) - picSettings.Margine) - xCenterOfImg = Convert.ToSingle((g.Width / (double)2)); - break; - } + { + xCenterOfImg = + Convert.ToSingle((g.Width - picSettings.Margine - (larghezzaStandard / (double)2))); + if ((larghezzaStandard / (double)2) > (g.Width / (double)2) - picSettings.Margine) + xCenterOfImg = Convert.ToSingle((g.Width / (double)2)); + break; + } } strFormat.Alignment = StringAlignment.Center; @@ -528,7 +528,7 @@ public class ImageCreatorGDI(PicSettings picSettings, ILogger l { logoTransparencyValue = 100; } - + var colorMatrixElements = new[] { new[] { 1.0F, 0.0F, 0.0F, 0.0F, 0.0F }, new[] { 0.0F, 1.0F, 0.0F, 0.0F, 0.0F }, @@ -571,44 +571,44 @@ public class ImageCreatorGDI(PicSettings picSettings, ILogger l { case "SINISTRA": case "NESSUNA": - { - xPosOfWm = margineUsato; - break; - } + { + xPosOfWm = margineUsato; + break; + } case "CENTRO": - { - xPosOfWm = System.Convert.ToInt32((imgOutputBig.Width - nuovaSize.Width) / (double)2); - break; - } + { + xPosOfWm = System.Convert.ToInt32((imgOutputBig.Width - nuovaSize.Width) / (double)2); + break; + } case "DESTRA": - { - xPosOfWm = ((imgOutputBig.Width - nuovaSize.Width) - margineUsato); - break; - } + { + xPosOfWm = ((imgOutputBig.Width - nuovaSize.Width) - margineUsato); + break; + } } switch (logoV) { case "ALTO": case "NESSUNA": - { - yPosOfWm = margineUsato; - break; - } + { + yPosOfWm = margineUsato; + break; + } case "CENTRO": - { - yPosOfWm = System.Convert.ToInt32((imgOutputBig.Height - nuovaSize.Height) / (double)2); - break; - } + { + yPosOfWm = System.Convert.ToInt32((imgOutputBig.Height - nuovaSize.Height) / (double)2); + break; + } case "BASSO": - { - yPosOfWm = ((imgOutputBig.Height - nuovaSize.Height) - margineUsato); - break; - } + { + yPosOfWm = ((imgOutputBig.Height - nuovaSize.Height) - margineUsato); + break; + } } grWatermark.DrawImage(logo, new Rectangle(xPosOfWm, yPosOfWm, nuovaSize.Width, nuovaSize.Height), 0, 0, @@ -776,7 +776,7 @@ public class ImageCreatorGDI(PicSettings picSettings, ILogger l { // Use 1% of image height as minimum margin, or 10px, whichever is larger float minMargin = Math.Max(10f, imgHeight * 0.01f); - + switch (picSettings.Posizione.ToUpper()) { case "ALTO": @@ -787,18 +787,18 @@ public class ImageCreatorGDI(PicSettings picSettings, ILogger l case "BASSO": var bottomMargin1 = (float)(imgHeight * picSettings.Margine / 100.0); var bottomMargin4 = (float)(imgHeight * picSettings.MargVert / 100.0); - + // Position from bottom: bottom edge of text at desired margin from bottom // Y = imageHeight - textHeight - bottomMargin var desiredY1 = imgHeight - textHeight - bottomMargin1; var desiredY4 = imgHeight - textHeight - bottomMargin4; - + // Ensure text stays completely within bounds: // - Top edge must be >= minMargin (not clipped at top) // - Bottom edge must be <= imgHeight - minMargin (not clipped at bottom) var maxAllowedY1 = imgHeight - textHeight - minMargin; // Maximum Y to keep bottom margin var maxAllowedY4 = imgHeight - textHeight - minMargin; - + imgState.YPosFromBottom1 = Math.Max(minMargin, Math.Min(desiredY1, maxAllowedY1)); imgState.YPosFromBottom4 = Math.Max(minMargin, Math.Min(desiredY4, maxAllowedY4)); break; diff --git a/docs/image-generation-tests-plan.md b/docs/image-generation-tests-plan.md deleted file mode 100644 index dcddcd8..0000000 --- a/docs/image-generation-tests-plan.md +++ /dev/null @@ -1,85 +0,0 @@ -# Image generation test plan — ImageSharp-only (multiplatform) - -Goal ------ -Create an automated, cross-platform test project that validates `ImageCreatorImageSharp` behavior by generating synthetic input images and pixel-inspecting outputs produced by the library. - -Decisions ---------- -- Test only `ImageCreatorImageSharp` (multiplatform). -- Test project targets `net10.0` (not Windows-only) and uses SixLabors.ImageSharp for both generation and verification; avoid `System.Drawing.Common` in tests. -- Inputs are programmatically generated images (no checked-in large binaries). -- Verification uses pixel inspection (sample regions, color averages, non-background pixel counts). - -Project -------- -- Name: `MaddoShared.ImageSharpTests` (folder: `MaddoShared.ImageSharpTests/`) -- TargetFramework: `net10.0` (no Windows-only flags) -- Package references: - - `MSTest.TestFramework` / `MSTest.TestAdapter` / `Microsoft.NET.Test.Sdk` - - `FluentAssertions` - - `Moq` (if needed for loggers) - - `SixLabors.ImageSharp` and `SixLabors.ImageSharp.Drawing` (for image creation and pixel inspection) - - `Microsoft.Extensions.Logging.Abstractions` (lightweight logging) -- ProjectReference: `../MaddoShared/MaddoShared.csproj` - -Helpers (tests) ----------------- -- `TempWorkspace` — creates temporary `Source` and `Dest` folders, cleans up on dispose. -- `TestImageFactory` — creates synthetic JPEG/PNG inputs and in-memory logo PNG bytes. Also can write EXIF orientation values. -- `PixelInspector` — loads output using ImageSharp and exposes: - - `CountNonBackgroundPixels(path, Rectangle region, Rgba32 background, int tolerance)` - - `SampleAverageColor(path, Rectangle region)` - - Region helpers: top/bottom/left/right/center/quadrant rectangles for given image sizes -- `CreatorFactory` — builds `PicSettings` defaults and `ImageCreatorImageSharp` instances. - -Test cases (high-level) ------------------------ -1. Image resizing: verify big and small output dimensions respect settings. -2. Text positioning: for `Posizione` = ALTO/CENTRO/BASSO and `Allineamento` = SINISTRA/CENTRO/DESTRA assert text pixels appear in expected regions. -3. Text content: baseline (empty) vs non-empty comparisons to ensure text changes output; EXIF-vertical photo selects `TestoFirmaV`. -4. Logo positioning: use a solid-color logo (e.g., pure red PNG) and verify red pixels appear in the expected quadrant for combinations of `LogoPosizioneH` × `LogoPosizioneV` and with margins (absolute and percentage). -5. Logo features: opacity (logo color blending) and color-key transparency. -6. EXIF orientation: ensure rotation is applied and output contains no EXIF orientation tag. - -Verification approach ---------------------- -- For text: compare non-background pixel count in the target band vs opposite band (thresholded) to avoid brittle exact glyph placement checks. -- For logo: sample the quadrant where the logo should be; count logo-colored pixels and assert above threshold. -- For opacity: sample average color in logo region and assert it is blended when opacity <100%. - -Scope boundaries ----------------- -- In scope: `ImageCreatorImageSharp` behavior: resize, EXIF rotation, text presence/position, logo position/opactiy, thumbnails. -- Out of scope: `ImageCreatorGDI` (excluded), OCR verification of exact text glyphs, font-subpixel metrics, performance testing. - -Implementation notes --------------------- -- Keep tests deterministic by creating solid-color inputs and simple logos. -- Use modest image sizes (e.g., 800×600) so tests run fast. -- Carefully choose thresholds for pixel-count assertions to be robust across fonts and rendering differences. - -Run & validate ---------------- -- Build: `dotnet build MaddoShared.ImageSharpTests` -- Test: `dotnet test MaddoShared.ImageSharpTests` - -Files to add ------------- -- `MaddoShared.ImageSharpTests/MaddoShared.ImageSharpTests.csproj` -- `MaddoShared.ImageSharpTests/Helpers/TempWorkspace.cs` -- `MaddoShared.ImageSharpTests/Helpers/TestImageFactory.cs` -- `MaddoShared.ImageSharpTests/Helpers/PixelInspector.cs` -- `MaddoShared.ImageSharpTests/Helpers/CreatorFactory.cs` -- `MaddoShared.ImageSharpTests/Tests/ImageResizingTests.cs` -- `MaddoShared.ImageSharpTests/Tests/TextPositioningTests.cs` -- `MaddoShared.ImageSharpTests/Tests/LogoPositioningTests.cs` -- `MaddoShared.ImageSharpTests/Tests/ExifOrientationTests.cs` -- `docs/image-generation-tests-plan.md` (this file) - -Next steps ----------- -1. Create the `MaddoShared.ImageSharpTests` project and add the helper files. -2. Implement the first set of tests (resizing + a simple text presence test) to validate the testing harness. -3. Iterate thresholds and add remaining tests. - diff --git a/imagecatalog/AvaloniaApp.axaml b/imagecatalog/AvaloniaApp.axaml index 97bd721..8ce9be1 100644 --- a/imagecatalog/AvaloniaApp.axaml +++ b/imagecatalog/AvaloniaApp.axaml @@ -1,106 +1,8 @@ - - - - - #F3F5F8 - #FFFFFF - #F7F8FA - #EEF2F6 - #D2D8E0 - - - #1B2027 - #242A33 - #2D343F - #38414E - #4B5563 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/imagecatalog/AvaloniaMainWindow.axaml b/imagecatalog/AvaloniaMainWindow.axaml index ba21b00..4517b63 100644 --- a/imagecatalog/AvaloniaMainWindow.axaml +++ b/imagecatalog/AvaloniaMainWindow.axaml @@ -1,165 +1,92 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:views="clr-namespace:ImageCatalog_2.AvaloniaViews" + x:Class="ImageCatalog_2.AvaloniaMainWindow" + mc:Ignorable="d" + Title="Image Catalog - Avalonia" Height="540" Width="800"> - + - - - - - - - - + + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - + + + - - - + IsEnabled="{Binding UiEnabled}" Content="Avvia" /> + - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/imagecatalog/AvaloniaViews/FaceAiTabView.axaml b/imagecatalog/AvaloniaViews/FaceAiTabView.axaml index 7f7f3ae..e2e22db 100644 --- a/imagecatalog/AvaloniaViews/FaceAiTabView.axaml +++ b/imagecatalog/AvaloniaViews/FaceAiTabView.axaml @@ -1,66 +1,34 @@ - + - - - + + - - + + + + + + - - - - + - + - +