336 lines
10 KiB
C#
336 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using Microsoft.Win32;
|
|
using TechnologyAssembler.Core.IO;
|
|
|
|
namespace HashCalculator.GUI
|
|
{
|
|
internal abstract class NotifyPropertyChanged : INotifyPropertyChanged
|
|
{
|
|
#region INotifyPropertyChanged
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
|
|
protected void Notify(string name)
|
|
{
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
|
}
|
|
|
|
protected void SetField<X>(ref X field, X value, [CallerMemberName] string? propertyName = null)
|
|
{
|
|
if (propertyName == null)
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
if (EqualityComparer<X>.Default.Equals(field, value))
|
|
{
|
|
return;
|
|
}
|
|
|
|
field = value;
|
|
|
|
Notify(propertyName);
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Performance", "CA1812")]
|
|
internal class ViewModel : NotifyPropertyChanged
|
|
{
|
|
private static readonly Random _random = new Random();
|
|
|
|
public MainInputViewModel MainInput { get; }
|
|
public BigInputViewModel BigEntryInput { get; }
|
|
|
|
private ObservableCollection<DisplayAssetEntry> _entries = new ObservableCollection<DisplayAssetEntry>();
|
|
public ObservableCollection<DisplayAssetEntry> Entries
|
|
{
|
|
get => _entries;
|
|
set => SetField(ref _entries, value);
|
|
}
|
|
|
|
private string _statusText = SuggestionString("不知道该显示些什么呢……");
|
|
public string StatusText
|
|
{
|
|
get => _statusText;
|
|
set => SetField(ref _statusText, value);
|
|
}
|
|
|
|
private string _traceText = string.Empty;
|
|
public string TraceText
|
|
{
|
|
get => _traceText;
|
|
set => SetField(ref _traceText, value);
|
|
}
|
|
|
|
public ViewModel()
|
|
{
|
|
var controller = new Controller(this);
|
|
MainInput = new MainInputViewModel(controller);
|
|
BigEntryInput = new BigInputViewModel(controller);
|
|
}
|
|
|
|
public static string SuggestionString(string original)
|
|
{
|
|
var generated = $"{_random.NextDouble()}";
|
|
System.Diagnostics.Debug.WriteLine(generated);
|
|
if (generated.Contains("38") || generated.Contains("16"))
|
|
{
|
|
return "你们都是喂鱼的马甲!(";
|
|
}
|
|
if (generated.IndexOf('2') == 2)
|
|
{
|
|
return "本来以为两小时就能写完这个小工具,没想到写了两个星期,开始怀疑自己的智商orz";
|
|
}
|
|
return original;
|
|
}
|
|
}
|
|
|
|
internal class InputBarViewModel : NotifyPropertyChanged
|
|
{
|
|
private IEnumerable<InputEntry>? _items;
|
|
public virtual IEnumerable<InputEntry>? Items
|
|
{
|
|
get => _items;
|
|
set => SetField(ref _items, value);
|
|
}
|
|
|
|
private InputEntry? _selectedItem;
|
|
public virtual InputEntry? SelectedItem
|
|
{
|
|
get => _selectedItem;
|
|
set => SetField(ref _selectedItem, value);
|
|
}
|
|
|
|
private int _selectedIndex;
|
|
public virtual int SelectedIndex
|
|
{
|
|
get => _selectedIndex;
|
|
set => SetField(ref _selectedIndex, value);
|
|
}
|
|
|
|
private string? _text;
|
|
public virtual string? Text
|
|
{
|
|
get => _text;
|
|
set => SetField(ref _text, value);
|
|
}
|
|
}
|
|
|
|
internal class MainInputViewModel : InputBarViewModel
|
|
{
|
|
private readonly FileSystemSuggestions _suggestions = new FileSystemSuggestions();
|
|
|
|
public override int SelectedIndex
|
|
{
|
|
get => base.SelectedIndex;
|
|
set
|
|
{
|
|
if (value == 0)
|
|
{
|
|
if (Items.Count() <= 1)
|
|
{
|
|
value = -1;
|
|
}
|
|
else
|
|
{
|
|
value = base.SelectedIndex < value ? 1 : -1;
|
|
}
|
|
}
|
|
base.SelectedIndex = value;
|
|
}
|
|
}
|
|
|
|
public override string? Text
|
|
{
|
|
get => base.Text;
|
|
set
|
|
{
|
|
base.Text = value;
|
|
if (value != SelectedItem?.ToString())
|
|
{
|
|
UpdateList(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
public Command<MainWindow> BrowseCommand { get; }
|
|
public Command<ViewModel> SelectCommand { get; }
|
|
|
|
public MainInputViewModel(Controller controller)
|
|
{
|
|
Items = Enumerable.Empty<InputEntry>();
|
|
BrowseCommand = new Command<MainWindow>(window =>
|
|
{
|
|
var dialog = new OpenFileDialog
|
|
{
|
|
Multiselect = false,
|
|
ValidateNames = true
|
|
};
|
|
if (dialog.ShowDialog(window) == true)
|
|
{
|
|
Text = dialog.FileName;
|
|
}
|
|
return Task.CompletedTask;
|
|
});
|
|
SelectCommand = new Command<ViewModel>(async viewModel =>
|
|
{
|
|
if (SelectedItem?.IsValid != true)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
BrowseCommand.CanExecuteValue = false;
|
|
SelectCommand.CanExecuteValue = false;
|
|
await controller.OnMainInputDecided(SelectedItem).ConfigureAwait(true);
|
|
}
|
|
finally
|
|
{
|
|
BrowseCommand.CanExecuteValue = true;
|
|
SelectCommand.CanExecuteValue = true;
|
|
SelectedIndex = -1;
|
|
}
|
|
});
|
|
}
|
|
|
|
private void UpdateList(string? path)
|
|
{
|
|
bool candidateFound = false;
|
|
var result = _suggestions.ProvideFileSystemSuggestions(path);
|
|
InputEntry? first = result.FirstOrDefault();
|
|
if (first != null)
|
|
{
|
|
if (new FileInfo(path!).FullName == new FileInfo(first.Value).FullName)
|
|
{
|
|
candidateFound = true;
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(path))
|
|
{
|
|
result = result.Prepend(new InputEntry(InputEntryType.Text, path!, path!));
|
|
}
|
|
Items = result;
|
|
|
|
if (candidateFound)
|
|
{
|
|
SelectedIndex = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class BigInputViewModel : InputBarViewModel
|
|
{
|
|
public override IEnumerable<InputEntry>? Items
|
|
{
|
|
get => base.Items;
|
|
set => base.Items = value;
|
|
}
|
|
|
|
public override InputEntry? SelectedItem
|
|
{
|
|
get => base.SelectedItem;
|
|
set
|
|
{
|
|
base.SelectedItem = value;
|
|
SelectCommand.CanExecuteValue = value != null;
|
|
}
|
|
}
|
|
|
|
public override string? Text
|
|
{
|
|
get => base.Text;
|
|
set
|
|
{
|
|
base.Text = value;
|
|
if (value != SelectedItem?.ToString())
|
|
{
|
|
UpdateList(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private InputEntry? _lastProcessedManifest;
|
|
public InputEntry? LastProcessedManifest
|
|
{
|
|
get => _lastProcessedManifest;
|
|
set => SetField(ref _lastProcessedManifest, value);
|
|
}
|
|
|
|
private IEnumerable<InputEntry>? _allManifests;
|
|
public IEnumerable<InputEntry>? AllManifests
|
|
{
|
|
get => _allManifests;
|
|
set
|
|
{
|
|
SetField(ref _allManifests, value);
|
|
Items = value;
|
|
LastProcessedManifest = null;
|
|
Text = null;
|
|
}
|
|
}
|
|
|
|
public Command<ViewModel> SelectCommand { get; }
|
|
|
|
[SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")]
|
|
public BigInputViewModel(Controller controller)
|
|
{
|
|
SelectCommand = new Command<ViewModel>(async viewModel =>
|
|
{
|
|
var mainInput = viewModel.MainInput;
|
|
try
|
|
{
|
|
SelectCommand.CanExecuteValue = false;
|
|
mainInput.BrowseCommand.CanExecuteValue = false;
|
|
mainInput.SelectCommand.CanExecuteValue = false;
|
|
LastProcessedManifest = SelectedItem;
|
|
await controller.ProcessManifest(SelectedItem!.Value).ConfigureAwait(true);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
CopyableBox.ShowDialog(_ =>
|
|
{
|
|
return Task.FromResult($"在加载 manifest 时发生错误:{exception}");
|
|
});
|
|
}
|
|
finally
|
|
{
|
|
SelectCommand.CanExecuteValue = true;
|
|
mainInput.BrowseCommand.CanExecuteValue = true;
|
|
mainInput.SelectCommand.CanExecuteValue = true;
|
|
}
|
|
})
|
|
{
|
|
CanExecuteValue = false
|
|
};
|
|
}
|
|
|
|
private void UpdateList(string? input)
|
|
{
|
|
input ??= string.Empty;
|
|
input = input.Replace(VirtualFileSystem.AltDirectorySeparatorChar, VirtualFileSystem.DirectorySeparatorChar);
|
|
|
|
var filtered = from manifest in AllManifests
|
|
where manifest.Value.IndexOf(input, StringComparison.OrdinalIgnoreCase) != -1
|
|
select manifest;
|
|
Items = filtered;
|
|
if (Items.FirstOrDefault()?.Value.Equals(input, StringComparison.OrdinalIgnoreCase) == true)
|
|
{
|
|
SelectedIndex = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|