mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-21 09:45:48 +08:00
更新任务选择窗口,调整任务添加逻辑,支持单个任务选择和目录结构保持
This commit is contained in:
@@ -32,17 +32,18 @@ public partial class PathingTaskSelectionWindow : FluentWindow
|
||||
/// 选中的任务
|
||||
/// </summary>
|
||||
public PathingTaskInfo? SelectedTask { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 选中的任务
|
||||
/// </summary>
|
||||
public GearTaskViewModel? SelectedGearTask { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 对话框结果
|
||||
/// </summary>
|
||||
public bool DialogResult { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 添加的任务列表
|
||||
/// </summary>
|
||||
public List<GearTaskViewModel> AddedTasks { get; private set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 目录到符号图标的转换器
|
||||
/// </summary>
|
||||
@@ -64,10 +65,10 @@ public partial class PathingTaskSelectionWindow : FluentWindow
|
||||
/// <summary>
|
||||
/// 任务添加事件处理
|
||||
/// </summary>
|
||||
private void OnTaskAdded(List<GearTaskViewModel> tasks)
|
||||
private void OnTaskAdded(GearTaskViewModel? task)
|
||||
{
|
||||
AddedTasks.AddRange(tasks);
|
||||
|
||||
SelectedGearTask = task;
|
||||
|
||||
// 添加任务后自动关闭窗口
|
||||
CloseWithResult();
|
||||
}
|
||||
@@ -185,101 +186,101 @@ public class NullToVisibilityConverter : IValueConverter
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字符串到ImageSource的转换器,处理空字符串情况,支持WebP格式
|
||||
/// </summary>
|
||||
public class StringToImageSourceConverter : IValueConverter
|
||||
{
|
||||
private static readonly HttpClient HttpClient = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is string url && !string.IsNullOrEmpty(url))
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查是否为WebP格式或其他需要特殊处理的格式
|
||||
if (url.Contains(".webp", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return LoadWebPImage(url);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 对于其他格式,使用原有的BitmapImage
|
||||
return new BitmapImage(new Uri(url));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static BitmapSource? LoadWebPImage(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] imageData;
|
||||
|
||||
if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 从网络加载
|
||||
imageData = HttpClient.GetByteArrayAsync(url).Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 从本地文件加载
|
||||
imageData = File.ReadAllBytes(url);
|
||||
}
|
||||
|
||||
using var image = Image.Load<Rgba32>(imageData);
|
||||
|
||||
// 转换为WPF可用的BitmapSource
|
||||
var bitmap = new WriteableBitmap(image.Width, image.Height, 96, 96, PixelFormats.Bgra32, null);
|
||||
|
||||
bitmap.Lock();
|
||||
try
|
||||
{
|
||||
var backBuffer = bitmap.BackBuffer;
|
||||
var stride = bitmap.BackBufferStride;
|
||||
|
||||
image.ProcessPixelRows(accessor =>
|
||||
{
|
||||
for (int y = 0; y < accessor.Height; y++)
|
||||
{
|
||||
var pixelRow = accessor.GetRowSpan(y);
|
||||
var targetPtr = backBuffer + y * stride;
|
||||
|
||||
for (int x = 0; x < pixelRow.Length; x++)
|
||||
{
|
||||
var pixel = pixelRow[x];
|
||||
// 转换RGBA到BGRA
|
||||
var bgra = (pixel.A << 24) | (pixel.R << 16) | (pixel.G << 8) | pixel.B;
|
||||
System.Runtime.InteropServices.Marshal.WriteInt32(targetPtr + x * 4, bgra);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
bitmap.AddDirtyRect(new Int32Rect(0, 0, image.Width, image.Height));
|
||||
bitmap.Unlock();
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine($"Failed to load WebP image: {e}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
// /// <summary>
|
||||
// /// 字符串到ImageSource的转换器,处理空字符串情况,支持WebP格式
|
||||
// /// </summary>
|
||||
// public class StringToImageSourceConverter : IValueConverter
|
||||
// {
|
||||
// private static readonly HttpClient HttpClient = new();
|
||||
//
|
||||
// public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
// {
|
||||
// if (value is string url && !string.IsNullOrEmpty(url))
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // 检查是否为WebP格式或其他需要特殊处理的格式
|
||||
// if (url.Contains(".webp", StringComparison.OrdinalIgnoreCase))
|
||||
// {
|
||||
// return LoadWebPImage(url);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // 对于其他格式,使用原有的BitmapImage
|
||||
// return new BitmapImage(new Uri(url));
|
||||
// }
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// Debug.WriteLine(e);
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// private static BitmapSource? LoadWebPImage(string url)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// byte[] imageData;
|
||||
//
|
||||
// if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||
// {
|
||||
// // 从网络加载
|
||||
// imageData = HttpClient.GetByteArrayAsync(url).Result;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // 从本地文件加载
|
||||
// imageData = File.ReadAllBytes(url);
|
||||
// }
|
||||
//
|
||||
// using var image = Image.Load<Rgba32>(imageData);
|
||||
//
|
||||
// // 转换为WPF可用的BitmapSource
|
||||
// var bitmap = new WriteableBitmap(image.Width, image.Height, 96, 96, PixelFormats.Bgra32, null);
|
||||
//
|
||||
// bitmap.Lock();
|
||||
// try
|
||||
// {
|
||||
// var backBuffer = bitmap.BackBuffer;
|
||||
// var stride = bitmap.BackBufferStride;
|
||||
//
|
||||
// image.ProcessPixelRows(accessor =>
|
||||
// {
|
||||
// for (int y = 0; y < accessor.Height; y++)
|
||||
// {
|
||||
// var pixelRow = accessor.GetRowSpan(y);
|
||||
// var targetPtr = backBuffer + y * stride;
|
||||
//
|
||||
// for (int x = 0; x < pixelRow.Length; x++)
|
||||
// {
|
||||
// var pixel = pixelRow[x];
|
||||
// // 转换RGBA到BGRA
|
||||
// var bgra = (pixel.A << 24) | (pixel.R << 16) | (pixel.G << 8) | pixel.B;
|
||||
// System.Runtime.InteropServices.Marshal.WriteInt32(targetPtr + x * 4, bgra);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// bitmap.AddDirtyRect(new Int32Rect(0, 0, image.Width, image.Height));
|
||||
// bitmap.Unlock();
|
||||
// }
|
||||
//
|
||||
// return bitmap;
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// Debug.WriteLine($"Failed to load WebP image: {e}");
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
// }
|
||||
@@ -371,14 +371,9 @@ public partial class GearTaskListPageViewModel : ViewModel
|
||||
|
||||
pathingSelectionWindow.ShowDialog();
|
||||
|
||||
if (pathingSelectionWindow.DialogResult && pathingSelectionWindow.SelectedTask != null)
|
||||
if (pathingSelectionWindow.DialogResult && pathingSelectionWindow.SelectedGearTask != null)
|
||||
{
|
||||
var selectedTask = pathingSelectionWindow.SelectedTask;
|
||||
newTask = new GearTaskViewModel(selectedTask.Name)
|
||||
{
|
||||
TaskType = "Pathing",
|
||||
Path = selectedTask.RelativePath
|
||||
};
|
||||
newTask = pathingSelectionWindow.SelectedGearTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -71,7 +71,7 @@ public partial class PathingTaskSelectionViewModel : ViewModel
|
||||
LoadPathingTasks();
|
||||
}
|
||||
|
||||
public Action<List<GearTaskViewModel>>? OnTaskAdded { get; set; }
|
||||
public Action<GearTaskViewModel?>? OnTaskAdded { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 加载图标字典
|
||||
@@ -415,7 +415,7 @@ public partial class PathingTaskSelectionViewModel : ViewModel
|
||||
};
|
||||
|
||||
// 触发添加事件或通过其他方式返回给调用方
|
||||
OnTaskAdded?.Invoke([gearTaskViewModel]);
|
||||
OnTaskAdded?.Invoke(gearTaskViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,8 +427,8 @@ public partial class PathingTaskSelectionViewModel : ViewModel
|
||||
{
|
||||
if (SelectedTask?.IsDirectory == true)
|
||||
{
|
||||
var tasks = GetAllFolderTasksInDirectoryWithStructure(SelectedTask);
|
||||
OnTaskAdded?.Invoke(tasks);
|
||||
var task = GetAllFolderTasksInDirectoryWithStructure(SelectedTask);
|
||||
OnTaskAdded?.Invoke(task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,23 +440,29 @@ public partial class PathingTaskSelectionViewModel : ViewModel
|
||||
{
|
||||
if (SelectedTask?.IsDirectory == true)
|
||||
{
|
||||
var rootGearTaskViewModel = new GearTaskViewModel
|
||||
{
|
||||
Name = SelectedTask.Name,
|
||||
IsDirectory = true
|
||||
};
|
||||
|
||||
// 逐个添加:添加目录下所有JSON文件作为独立任务
|
||||
var taskInfos = GetAllJsonFilesInDirectory(SelectedTask);
|
||||
var tasks = new List<GearTaskViewModel>();
|
||||
foreach (var taskInfo in taskInfos)
|
||||
{
|
||||
var gearTaskViewModel = new GearTaskViewModel
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(taskInfo.Name),
|
||||
TaskType = "Pathing",
|
||||
Path = @$"{{pathingRepoFolder}}\{taskInfo.RelativePath}\",
|
||||
IsDirectory = false
|
||||
};
|
||||
tasks.Add(gearTaskViewModel);
|
||||
rootGearTaskViewModel.Children.Add(gearTaskViewModel);
|
||||
}
|
||||
|
||||
if (tasks.Count > 0)
|
||||
if (rootGearTaskViewModel.Children.Count > 0)
|
||||
{
|
||||
OnTaskAdded?.Invoke(tasks);
|
||||
OnTaskAdded?.Invoke(rootGearTaskViewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -470,8 +476,8 @@ public partial class PathingTaskSelectionViewModel : ViewModel
|
||||
if (SelectedTask?.IsDirectory == true)
|
||||
{
|
||||
// 保持目录结构添加所有子任务
|
||||
var tasks = GetAllFileTasksInDirectoryWithStructure(SelectedTask);
|
||||
OnTaskAdded?.Invoke(tasks);
|
||||
var task = GetAllFileTasksInDirectoryWithStructure(SelectedTask);
|
||||
OnTaskAdded?.Invoke(task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,88 +510,88 @@ public partial class PathingTaskSelectionViewModel : ViewModel
|
||||
/// <summary>
|
||||
/// 获取目录下所有json文件任务并保持结构
|
||||
/// </summary>
|
||||
private List<GearTaskViewModel> GetAllFileTasksInDirectoryWithStructure(PathingTaskInfo directory)
|
||||
private GearTaskViewModel? GetAllFileTasksInDirectoryWithStructure(PathingTaskInfo node)
|
||||
{
|
||||
var tasks = new List<GearTaskViewModel>();
|
||||
|
||||
if (directory.Children is { Count: > 0 })
|
||||
if (node.IsDirectory)
|
||||
{
|
||||
foreach (var child in directory.Children)
|
||||
// 添加子目录作为组
|
||||
var groupTask = new GearTaskViewModel
|
||||
{
|
||||
if (child.IsDirectory)
|
||||
{
|
||||
// 添加子目录作为组
|
||||
var groupTask = new GearTaskViewModel
|
||||
{
|
||||
Name = child.Name,
|
||||
IsDirectory = true
|
||||
};
|
||||
tasks.Add(groupTask);
|
||||
Name = node.Name,
|
||||
IsDirectory = true
|
||||
};
|
||||
|
||||
// 递归添加子任务
|
||||
tasks.AddRange(GetAllFileTasksInDirectoryWithStructure(child));
|
||||
}
|
||||
else if (child.FullPath.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
|
||||
foreach (var pathingTaskInfo in node.Children)
|
||||
{
|
||||
var gearTask = GetAllFileTasksInDirectoryWithStructure(pathingTaskInfo);
|
||||
if (gearTask != null)
|
||||
{
|
||||
// 添加JSON文件作为任务
|
||||
var fileTask = new GearTaskViewModel
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(child.Name),
|
||||
Path = @$"{{pathingRepoFolder}}\{child.RelativePath}\",
|
||||
IsDirectory = false
|
||||
};
|
||||
tasks.Add(fileTask);
|
||||
groupTask.Children.Add(gearTask);
|
||||
}
|
||||
}
|
||||
|
||||
return groupTask;
|
||||
}
|
||||
else if (node.FullPath.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 添加JSON文件作为任务
|
||||
var fileTask = new GearTaskViewModel
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(node.Name),
|
||||
TaskType = "Pathing",
|
||||
Path = @$"{{pathingRepoFolder}}\{node.RelativePath}\",
|
||||
IsDirectory = false
|
||||
};
|
||||
return fileTask;
|
||||
}
|
||||
|
||||
return tasks;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取目录下所有文件夹任务并保持结构
|
||||
/// </summary>
|
||||
private List<GearTaskViewModel> GetAllFolderTasksInDirectoryWithStructure(PathingTaskInfo directory)
|
||||
private GearTaskViewModel? GetAllFolderTasksInDirectoryWithStructure(PathingTaskInfo directory)
|
||||
{
|
||||
var tasks = new List<GearTaskViewModel>();
|
||||
|
||||
if (directory.Children is { Count: > 0 })
|
||||
{
|
||||
foreach (var child in directory.Children)
|
||||
// 判断 directory 的子节点是否是文件
|
||||
var hasJsonFile = directory.Children.Any(grandChild => !grandChild.IsDirectory && grandChild.FullPath.EndsWith(".json", StringComparison.OrdinalIgnoreCase));
|
||||
if (hasJsonFile)
|
||||
{
|
||||
if (child.IsDirectory)
|
||||
// 添加子目录作为任务
|
||||
var groupTask = new GearTaskViewModel
|
||||
{
|
||||
// 判断 child 的子节点是否是文件
|
||||
var hasJsonFile = child.Children.Any(grandChild => !grandChild.IsDirectory && grandChild.FullPath.EndsWith(".json", StringComparison.OrdinalIgnoreCase));
|
||||
if (hasJsonFile)
|
||||
Name = directory.Name,
|
||||
TaskType = "Pathing",
|
||||
Path = @$"{{pathingRepoFolder}}\{directory.RelativePath}\",
|
||||
IsDirectory = false
|
||||
};
|
||||
return groupTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 添加子目录作为组
|
||||
var groupTask = new GearTaskViewModel
|
||||
{
|
||||
Name = directory.Name,
|
||||
IsDirectory = true
|
||||
};
|
||||
foreach (var pathingTaskInfo in directory.Children)
|
||||
{
|
||||
var gearTask = GetAllFolderTasksInDirectoryWithStructure(pathingTaskInfo);
|
||||
if (gearTask != null)
|
||||
{
|
||||
// 添加子目录作为任务
|
||||
var groupTask = new GearTaskViewModel
|
||||
{
|
||||
Name = child.Name,
|
||||
TaskType = "Pathing",
|
||||
Path = @$"{{pathingRepoFolder}}\{child.RelativePath}\",
|
||||
IsDirectory = false
|
||||
};
|
||||
tasks.Add(groupTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 添加子目录作为组
|
||||
var groupTask = new GearTaskViewModel
|
||||
{
|
||||
Name = child.Name,
|
||||
IsDirectory = true
|
||||
};
|
||||
tasks.Add(groupTask);
|
||||
// 递归添加子任务
|
||||
tasks.AddRange(GetAllFolderTasksInDirectoryWithStructure(child));
|
||||
groupTask.Children.Add(gearTask);
|
||||
}
|
||||
}
|
||||
return groupTask;
|
||||
}
|
||||
}
|
||||
|
||||
return tasks;
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user