From 02d59bc1d22875d2e05d8f5b799d877b3d5c4212 Mon Sep 17 00:00:00 2001 From: Will B Date: Tue, 3 Feb 2026 19:41:27 -0700 Subject: [PATCH 1/5] Initial commit moving from old branch - pending local testing --- .../ViewModel/AssetCollectionViewModel.cs | 107 ++++++++++++++---- 1 file changed, 83 insertions(+), 24 deletions(-) diff --git a/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs b/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs index 532e3e2230..23df577c46 100644 --- a/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs +++ b/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs @@ -1,13 +1,10 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. -using System; -using System.Collections.Generic; using System.Collections.Specialized; using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; +using Stride.Core; +using Stride.Core.Annotations; using Stride.Core.Assets.Analysis; using Stride.Core.Assets.Editor.Components.AddAssets; using Stride.Core.Assets.Editor.Components.Properties; @@ -19,8 +16,6 @@ using Stride.Core.Assets.Editor.ViewModel.Progress; using Stride.Core.Assets.Templates; using Stride.Core.Assets.Tracking; -using Stride.Core; -using Stride.Core.Annotations; using Stride.Core.Diagnostics; using Stride.Core.Extensions; using Stride.Core.IO; @@ -30,9 +25,9 @@ using Stride.Core.Presentation.Dirtiables; using Stride.Core.Presentation.Interop; using Stride.Core.Presentation.Services; -using Stride.Core.Translation; using Stride.Core.Presentation.ViewModels; -using System.Collections; +using Stride.Core.Presentation.Windows; +using Stride.Core.Translation; namespace Stride.Core.Assets.Editor.ViewModel { @@ -587,39 +582,103 @@ [new FilePickerFilter("") { Patterns = [file.GetFileExtension()]}], private async Task> InvokeAddAssetTemplate(LoggerResult logger, string name, DirectoryBaseViewModel directory, TemplateAssetDescription templateDescription, [CanBeNull] IList files, PropertyContainer? customParameters) { - List newAssets = new List(); + const int DialogClosed = 0; + const int DialogYes = 1; + const int DialogNo = 2; + const int DialogYesToAll = 3; + const int DialogNoToAll = 4; + + var newAssets = new List(); + IReadOnlyList copyPromptButtons = DialogHelper.CreateButtons(files is not null && files.Count > 1 ? + [ + Tr._p("Button", "Yes"), + Tr._p("Button", "No"), + Tr._p("Button", "Yes to all"), + Tr._p("Button", "No to all") + ] + : + [ + Tr._p("Button", "Yes"), + Tr._p("Button", "No") + ], 1, 2); + + IReadOnlyList overwritePromptButtons = DialogHelper.CreateButtons(files is not null && files.Count > 1 ? + [ + Tr._p("Button", "Yes"), + Tr._p("Button", "No"), + Tr._p("Button", "Yes to all") + ] + : + [ + Tr._p("Button", "Yes"), + Tr._p("Button", "No") + ], 1, 2); + + var yesToAll = false; + var overwriteAll = false; + var finalPath = string.Empty; + if (files is not null) { - for (int i = 0; i < files.Count; i++) + for (var i = 0; i < files.Count; i++) { var file = files[i]; - bool inResourceFolder = directory.Package.Package.ResourceFolders.Any(x => file.FullPath.StartsWith(x.FullPath, StringComparison.Ordinal)); + var inResourceFolder = directory.Package.Package.ResourceFolders.Any(x => file.FullPath.StartsWith(x.FullPath, StringComparison.Ordinal)); if (inResourceFolder) + { continue; + } + + if (!yesToAll) + { + var message = Tr._p("Message", "Source file '{0}' is not inside of your project's resource folders, do you want to copy it?").ToFormat(file.FullPath); - var message = Tr._p("Message", "Source file '{0}' is not inside of your project's resource folders, do you want to copy it?").ToFormat(file.FullPath); + var copyResult = await Dialogs.MessageBoxAsync(message, copyPromptButtons, MessageBoxImage.Warning); - var copyResult = await Dialogs.MessageBoxAsync(message, MessageBoxButton.YesNo, MessageBoxImage.Warning); + if (copyResult is DialogClosed or DialogNo) + { + continue; + } - if (copyResult != MessageBoxResult.Yes) - continue; + if (copyResult is DialogNoToAll) + { + break; + } + + if (copyResult is DialogYesToAll) + { + yesToAll = true; + } - string finalPath = await GetAssetCopyDirectory(directory, file); + if (copyResult is DialogYes or DialogYesToAll) + { + finalPath = await GetAssetCopyDirectory(directory, file); + } + } + else + { + // If "Yes to all" we're going to assume they want to use the same directory as the initial file. + finalPath = Path.Combine(Path.GetDirectoryName(finalPath), file.GetFileName()); + } try { Directory.CreateDirectory(Path.GetDirectoryName(finalPath)); if (File.Exists(finalPath)) { - message = Tr._p("Message", "The file '{0}' already exists, it will get overwritten if you continue, do you really want to proceed?").ToFormat(finalPath); + if (!overwriteAll) + { + var message = Tr._p("Message", "The file '{0}' already exists, it will get overwritten if you continue, do you really want to proceed?").ToFormat(finalPath); - copyResult = await Dialogs.MessageBoxAsync(message, MessageBoxButton.YesNo, MessageBoxImage.Warning); + var copyResult = await Dialogs.MessageBoxAsync(message, overwritePromptButtons, MessageBoxImage.Warning); - // Abort if the user says no or closes the prompt - if (copyResult != MessageBoxResult.Yes) - { - return newAssets; + overwriteAll = copyResult is DialogYesToAll; + + if (copyResult is DialogClosed or DialogNo) + { + return newAssets; + } } File.Copy(file.FullPath, finalPath, true); } @@ -632,7 +691,7 @@ private async Task> InvokeAddAssetTemplate(LoggerResult log } catch (Exception ex) { - message = Tr._p("Message", $"An error occurred while copying the asset to the resources folder : {ex.Message}"); + var message = Tr._p("Message", $"An error occurred while copying the asset to the resources folder : {ex.Message}"); await Dialogs.MessageBoxAsync(message, MessageBoxButton.OK, MessageBoxImage.Error); return newAssets; } From 4d51fd45cf2728490bd310c909b37db3e30457a1 Mon Sep 17 00:00:00 2001 From: Will B Date: Thu, 26 Feb 2026 11:09:01 -0700 Subject: [PATCH 2/5] Update logic to not display "to all" buttons on the last file in the collection Also some minor grammar/formatting --- .../ViewModel/AssetCollectionViewModel.cs | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs b/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs index 23df577c46..6fece2f76b 100644 --- a/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs +++ b/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs @@ -547,7 +547,7 @@ private string ComputeNamespace(DirectoryBaseViewModel directory) private async Task GetAssetCopyDirectory(DirectoryBaseViewModel directory, UFile file) { var path = directory.Path; - var message = Tr._p("Message", "Do you want to place the resource in the default location ?"); + var message = Tr._p("Message", "Do you want to place the resource in the default location?"); var finalPath = Path.GetFullPath(Path.Combine(directory.Package.Package.ResourceFolders[0], path, file.GetFileName())); var pathResult = await Dialogs.MessageBoxAsync(message, MessageBoxButton.YesNo, MessageBoxImage.Question); if (pathResult == MessageBoxResult.No) @@ -589,26 +589,22 @@ private async Task> InvokeAddAssetTemplate(LoggerResult log const int DialogNoToAll = 4; var newAssets = new List(); - IReadOnlyList copyPromptButtons = DialogHelper.CreateButtons(files is not null && files.Count > 1 ? + IReadOnlyList copyPromptWithToAllButtons = DialogHelper.CreateButtons( [ Tr._p("Button", "Yes"), Tr._p("Button", "No"), Tr._p("Button", "Yes to all"), Tr._p("Button", "No to all") - ] - : - [ - Tr._p("Button", "Yes"), - Tr._p("Button", "No") ], 1, 2); - IReadOnlyList overwritePromptButtons = DialogHelper.CreateButtons(files is not null && files.Count > 1 ? + IReadOnlyList overwritePromptWithToAllButtons = DialogHelper.CreateButtons( [ Tr._p("Button", "Yes"), Tr._p("Button", "No"), Tr._p("Button", "Yes to all") - ] - : + ], 1, 2); + + IReadOnlyList dialogDefaultButtons = DialogHelper.CreateButtons( [ Tr._p("Button", "Yes"), Tr._p("Button", "No") @@ -634,7 +630,7 @@ private async Task> InvokeAddAssetTemplate(LoggerResult log { var message = Tr._p("Message", "Source file '{0}' is not inside of your project's resource folders, do you want to copy it?").ToFormat(file.FullPath); - var copyResult = await Dialogs.MessageBoxAsync(message, copyPromptButtons, MessageBoxImage.Warning); + var copyResult = await Dialogs.MessageBoxAsync(message, files.Count > 1 && i != files.Count - 1 ? copyPromptWithToAllButtons : dialogDefaultButtons, MessageBoxImage.Warning); if (copyResult is DialogClosed or DialogNo) { @@ -671,7 +667,7 @@ private async Task> InvokeAddAssetTemplate(LoggerResult log { var message = Tr._p("Message", "The file '{0}' already exists, it will get overwritten if you continue, do you really want to proceed?").ToFormat(finalPath); - var copyResult = await Dialogs.MessageBoxAsync(message, overwritePromptButtons, MessageBoxImage.Warning); + var copyResult = await Dialogs.MessageBoxAsync(message, files.Count > 1 && i != files.Count - 1 ? overwritePromptWithToAllButtons : dialogDefaultButtons, MessageBoxImage.Warning); overwriteAll = copyResult is DialogYesToAll; @@ -691,7 +687,7 @@ private async Task> InvokeAddAssetTemplate(LoggerResult log } catch (Exception ex) { - var message = Tr._p("Message", $"An error occurred while copying the asset to the resources folder : {ex.Message}"); + var message = Tr._p("Message", "An error occurred while copying the asset to the resources folder: {0}").ToFormat(ex.Message); await Dialogs.MessageBoxAsync(message, MessageBoxButton.OK, MessageBoxImage.Error); return newAssets; } From ca106b1302002a8c58441a13f657f5ae252238bf Mon Sep 17 00:00:00 2001 From: Will B Date: Thu, 26 Feb 2026 11:30:53 -0700 Subject: [PATCH 3/5] Remove File.Count check - unnecessary --- .../ViewModel/AssetCollectionViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs b/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs index 6fece2f76b..af3ca35005 100644 --- a/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs +++ b/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs @@ -630,7 +630,7 @@ private async Task> InvokeAddAssetTemplate(LoggerResult log { var message = Tr._p("Message", "Source file '{0}' is not inside of your project's resource folders, do you want to copy it?").ToFormat(file.FullPath); - var copyResult = await Dialogs.MessageBoxAsync(message, files.Count > 1 && i != files.Count - 1 ? copyPromptWithToAllButtons : dialogDefaultButtons, MessageBoxImage.Warning); + var copyResult = await Dialogs.MessageBoxAsync(message, i != files.Count - 1 ? copyPromptWithToAllButtons : dialogDefaultButtons, MessageBoxImage.Warning); if (copyResult is DialogClosed or DialogNo) { @@ -667,7 +667,7 @@ private async Task> InvokeAddAssetTemplate(LoggerResult log { var message = Tr._p("Message", "The file '{0}' already exists, it will get overwritten if you continue, do you really want to proceed?").ToFormat(finalPath); - var copyResult = await Dialogs.MessageBoxAsync(message, files.Count > 1 && i != files.Count - 1 ? overwritePromptWithToAllButtons : dialogDefaultButtons, MessageBoxImage.Warning); + var copyResult = await Dialogs.MessageBoxAsync(message, i != files.Count - 1 ? overwritePromptWithToAllButtons : dialogDefaultButtons, MessageBoxImage.Warning); overwriteAll = copyResult is DialogYesToAll; From d84e7974ba52af7fe43221b53d8868762e7c6cdd Mon Sep 17 00:00:00 2001 From: Will B Date: Mon, 6 Apr 2026 09:30:53 -0600 Subject: [PATCH 4/5] Revert out message change so translations don't break --- .../ViewModel/AssetCollectionViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs b/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs index af3ca35005..25f3011e2b 100644 --- a/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs +++ b/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs @@ -547,7 +547,7 @@ private string ComputeNamespace(DirectoryBaseViewModel directory) private async Task GetAssetCopyDirectory(DirectoryBaseViewModel directory, UFile file) { var path = directory.Path; - var message = Tr._p("Message", "Do you want to place the resource in the default location?"); + var message = Tr._p("Message", "Do you want to place the resource in the default location ?"); var finalPath = Path.GetFullPath(Path.Combine(directory.Package.Package.ResourceFolders[0], path, file.GetFileName())); var pathResult = await Dialogs.MessageBoxAsync(message, MessageBoxButton.YesNo, MessageBoxImage.Question); if (pathResult == MessageBoxResult.No) From dbf5ca22e651c8f6a35be44d1f48428a2562c677 Mon Sep 17 00:00:00 2001 From: Will B Date: Fri, 10 Apr 2026 10:30:18 -0600 Subject: [PATCH 5/5] Modify overwrite prompt so we only abort when cancel/close is pressed, No will move to the next file --- .../ViewModel/AssetCollectionViewModel.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs b/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs index 25f3011e2b..f411f5317d 100644 --- a/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs +++ b/sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs @@ -671,10 +671,15 @@ private async Task> InvokeAddAssetTemplate(LoggerResult log overwriteAll = copyResult is DialogYesToAll; - if (copyResult is DialogClosed or DialogNo) + if (copyResult is DialogClosed) { return newAssets; } + + if (copyResult is DialogNo) + { + continue; + } } File.Copy(file.FullPath, finalPath, true); }