CI / Go Backend (push) Canceled after 0s
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
341 lines
11 KiB
C#
341 lines
11 KiB
C#
using System.IO;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Input;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
using Microsoft.VisualBasic.FileIO;
|
|
using PCL.Core.App;
|
|
using SearchOption = System.IO.SearchOption;
|
|
using PCL.Core.App.Localization;
|
|
using PCL.Core.UI;
|
|
|
|
namespace PCL;
|
|
|
|
public partial class PageInstanceScreenshot : IRefreshable
|
|
{
|
|
private bool _AppendLock;
|
|
private int _Offset;
|
|
|
|
private List<string> fileList = new();
|
|
|
|
private bool isLoad;
|
|
private string screenshotPath;
|
|
|
|
public PageInstanceScreenshot()
|
|
{
|
|
InitializeComponent();
|
|
Loaded += PageSetupLaunch_Loaded;
|
|
PanBack.ScrollChanged += RequireAppend;
|
|
BtnOpenFolder.Click += BtnOpenFolder_Click;
|
|
BtnOpenFolderTop.Click += BtnOpenFolder_Click;
|
|
}
|
|
|
|
void IRefreshable.Refresh()
|
|
{
|
|
RefreshSelf();
|
|
}
|
|
|
|
private void RefreshSelf()
|
|
{
|
|
var ignore = RefreshAsync();
|
|
}
|
|
|
|
public static async Task RefreshAsync()
|
|
{
|
|
if (ModMain.frmInstanceScreenshot is not null)
|
|
await ModMain.frmInstanceScreenshot.ReloadAsync();
|
|
ModMain.frmInstanceLeft.ItemScreenshot.Checked = true;
|
|
HintService.Hint(Lang.Text("Instance.Saves.Status.Refreshing"), log: false);
|
|
}
|
|
|
|
private void PageSetupLaunch_Loaded(object sender, RoutedEventArgs e)
|
|
{
|
|
// 重复加载部分
|
|
PanBack.ScrollToHome();
|
|
screenshotPath = PageInstanceLeft.McInstance.PathIndie + @"screenshots\";
|
|
if (!Directory.Exists(screenshotPath))
|
|
Directory.CreateDirectory(screenshotPath);
|
|
Dispatcher.BeginInvoke(new Func<Task>(ReloadAsync));
|
|
|
|
// 非重复加载部分
|
|
if (isLoad)
|
|
return;
|
|
isLoad = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 确保当前页面上的信息已正确显示。
|
|
/// </summary>
|
|
public async Task ReloadAsync()
|
|
{
|
|
ModAnimation.AniControlEnabled += 1;
|
|
PanBack.ScrollToHome();
|
|
await LoadFileListAsync();
|
|
ModAnimation.AniControlEnabled -= 1;
|
|
}
|
|
|
|
private void RefreshTip()
|
|
{
|
|
if (fileList.Count.Equals(0))
|
|
{
|
|
PanNoPic.Visibility = Visibility.Visible;
|
|
PanContent.Visibility = Visibility.Collapsed;
|
|
}
|
|
else
|
|
{
|
|
PanNoPic.Visibility = Visibility.Collapsed;
|
|
PanContent.Visibility = Visibility.Visible;
|
|
}
|
|
}
|
|
|
|
private static string[] allowedSuffix = { "*.png", "*.jpg", "*.jpeg", "*.bmp", "*.webp", "*.tiff" };
|
|
|
|
private async Task LoadFileListAsync()
|
|
{
|
|
ModBase.Log("[Screenshot] 刷新截图文件");
|
|
fileList.Clear();
|
|
if (Directory.Exists(screenshotPath))
|
|
{
|
|
fileList = allowedSuffix
|
|
.SelectMany(suffix => Directory.EnumerateFiles(screenshotPath, suffix, SearchOption.TopDirectoryOnly))
|
|
.OrderByDescending(f => File.GetCreationTime(f))
|
|
.ToList();
|
|
}
|
|
PanList.Children.Clear();
|
|
RefreshTip();
|
|
//FileList = FileList.Where(e => !e.ContainsF(@"\debug\")).ToList(); // 排除资源包调试输出
|
|
//FileList.Sort((a, b) => new FileInfo(a).CreationTime > new FileInfo(b).CreationTime);
|
|
ModBase.Log("[Screenshot] 共发现 " + fileList.Count + " 个截图文件");
|
|
if (fileList.Count == 0)
|
|
return;
|
|
await ListAppendAsync(20, 0);
|
|
}
|
|
|
|
private void RequireAppend(object sender, ScrollChangedEventArgs e)
|
|
{
|
|
if (fileList.Count != 0 && !_AppendLock && PanBack.VerticalOffset + PanBack.ViewportHeight >= PanBack.ExtentHeight)
|
|
{
|
|
Dispatcher.BeginInvoke(new Func<Task>(async () => await ListAppendAsync()));
|
|
}
|
|
}
|
|
|
|
private async Task ListAppendAsync(int count = 20, int offset = -1)
|
|
{
|
|
_AppendLock = true;
|
|
if (offset == -1)
|
|
{
|
|
if (_Offset * count > fileList.Count)
|
|
return;
|
|
offset = _Offset + 1;
|
|
_Offset += 1;
|
|
}
|
|
else
|
|
{
|
|
_Offset = offset;
|
|
}
|
|
|
|
if (count * offset > fileList.Count)
|
|
return;
|
|
for (int j = count * offset, loopTo = count * (offset + 1) - 1; j <= loopTo; j++)
|
|
{
|
|
if (j >= fileList.Count)
|
|
break;
|
|
var i = fileList.ElementAt(j);
|
|
try
|
|
{
|
|
if (!File.Exists(i))
|
|
continue; // 文件在加载途中消失了
|
|
if (File.GetAttributes(i).HasFlag(FileAttributes.Hidden))
|
|
continue; // 隐藏文件
|
|
if (new FileInfo(i).Length == 0L)
|
|
continue; // 空文件
|
|
var myCard = new MyCard
|
|
{
|
|
Margin = new Thickness(7),
|
|
Tag = i,
|
|
ToolTip = i.Replace(screenshotPath, "") // 适配高清截图模组
|
|
};
|
|
var grid = new Grid();
|
|
myCard.Children.Add(grid);
|
|
|
|
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(9d) });
|
|
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(120d) });
|
|
grid.RowDefinitions.Add(new RowDefinition());
|
|
|
|
// 图片
|
|
var image = new Image();
|
|
image.Source = await Task.Run(() =>
|
|
{
|
|
var bitmapImage = new BitmapImage();
|
|
var loadSource = i;
|
|
using (var fs = new FileStream(loadSource, FileMode.Open, FileAccess.Read))
|
|
{
|
|
bitmapImage.BeginInit();
|
|
bitmapImage.DecodePixelHeight = 200;
|
|
bitmapImage.DecodePixelWidth = 400;
|
|
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
|
|
bitmapImage.StreamSource = fs;
|
|
bitmapImage.EndInit();
|
|
bitmapImage.Freeze();
|
|
}
|
|
|
|
return bitmapImage;
|
|
});
|
|
image.Stretch = Stretch.Uniform; // 使图片自适应控件大小
|
|
image.Cursor = Cursors.Hand;
|
|
image.MouseLeftButtonDown += (sender, e) =>
|
|
{
|
|
try
|
|
{
|
|
Basics.OpenPath(i);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ModBase.Log(
|
|
ex,
|
|
Lang.Text("Instance.Screenshot.OpenFailed"),
|
|
ModBase.LogLevel.Hint,
|
|
userSummary: Lang.Text("Instance.Screenshot.OpenFailed"));
|
|
}
|
|
}; // 使用系统默认程序打开
|
|
Grid.SetRow(image, 1);
|
|
grid.Children.Add(image);
|
|
|
|
// 按钮
|
|
var stackPanel = new StackPanel();
|
|
stackPanel.Orientation = Orientation.Horizontal;
|
|
stackPanel.HorizontalAlignment = HorizontalAlignment.Center;
|
|
stackPanel.Margin = new Thickness(3d, 5d, 3d, 5d);
|
|
Grid.SetRow(stackPanel, 2);
|
|
grid.Children.Add(stackPanel);
|
|
|
|
var btnOpen = new MyIconTextButton
|
|
{
|
|
Name = "BtnOpen",
|
|
Text = Lang.Text("Common.Action.Open"),
|
|
LogoScale = 0.8d,
|
|
SvgIcon = "lucide/folder-open",
|
|
Tag = i
|
|
};
|
|
btnOpen.Click += (s, ev) => BtnOpen_Click((MyIconTextButton)s, ev);
|
|
stackPanel.Children.Add(btnOpen);
|
|
var btnDelete = new MyIconTextButton
|
|
{
|
|
Name = "BtnDelete",
|
|
Text = Lang.Text("Common.Action.Delete"),
|
|
LogoScale = 0.8d,
|
|
SvgIcon = "lucide/trash-2",
|
|
Tag = i
|
|
};
|
|
btnDelete.Click += (s, ev) => BtnDelete_Click((MyIconTextButton)s, ev);
|
|
stackPanel.Children.Add(btnDelete);
|
|
var btnCopy = new MyIconTextButton
|
|
{
|
|
Name = "BtnCopy",
|
|
Text = Lang.Text("Common.Action.Copy"),
|
|
LogoScale = 0.8d,
|
|
SvgIcon = "lucide/copy",
|
|
Tag = i
|
|
};
|
|
btnCopy.Click += (s, ev) => BtnCopy_Click((MyIconTextButton)s, ev);
|
|
stackPanel.Children.Add(btnCopy);
|
|
PanList.Children.Add(myCard);
|
|
myCard.Opacity = 0d;
|
|
ModAnimation.AniStart(new[] { ModAnimation.AaOpacity(myCard, 1d, 200) });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ModBase.Log(ex, $"[Screenshot] 创建 {i} 截图预览失败,图像可能损坏");
|
|
}
|
|
}
|
|
|
|
_AppendLock = false;
|
|
}
|
|
|
|
private void RemoveItem(string path)
|
|
{
|
|
try
|
|
{
|
|
foreach (var i in PanList.Children)
|
|
if (((MyCard)i).Tag.Equals(path))
|
|
{
|
|
PanList.Children.Remove((UIElement)i);
|
|
break;
|
|
}
|
|
|
|
fileList.Remove(path);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ModBase.Log(ex, "未能找到对应 UI");
|
|
}
|
|
}
|
|
|
|
private string GetPathFromSender(MyIconTextButton sender)
|
|
{
|
|
return (string)sender.Tag;
|
|
}
|
|
|
|
private void BtnOpen_Click(MyIconTextButton sender, EventArgs e)
|
|
{
|
|
ModBase.OpenExplorer(GetPathFromSender(sender));
|
|
}
|
|
|
|
private void BtnDelete_Click(MyIconTextButton sender, EventArgs e)
|
|
{
|
|
var path = GetPathFromSender(sender);
|
|
try
|
|
{
|
|
FileSystem.DeleteFile(path, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);
|
|
RemoveItem(path);
|
|
RefreshTip();
|
|
HintService.Hint(Lang.Text("Instance.Screenshot.Deleted"));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ModBase.Log(
|
|
ex,
|
|
Lang.Text("Instance.Screenshot.DeleteFailed"),
|
|
ModBase.LogLevel.Hint,
|
|
userSummary: Lang.Text("Instance.Screenshot.DeleteFailed"));
|
|
}
|
|
}
|
|
|
|
private void BtnCopy_Click(MyIconTextButton sender, EventArgs e)
|
|
{
|
|
var imagePath = GetPathFromSender(sender);
|
|
if (File.Exists(imagePath))
|
|
{
|
|
var tryTime = 0;
|
|
while (tryTime <= 5)
|
|
try
|
|
{
|
|
ModBase.Log("[Screenshot] 尝试复制" + imagePath + "到剪贴板");
|
|
Clipboard.SetImage(new BitmapImage(new Uri(imagePath)));
|
|
HintService.Hint(Lang.Text("Instance.Screenshot.CopiedToClipboard"));
|
|
tryTime = 6;
|
|
return;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
tryTime += 1;
|
|
ModBase.Log(ex, $"[Screenshot]第 {tryTime} 次复制尝试失败");
|
|
}
|
|
|
|
HintService.Hint(Lang.Text("Instance.Screenshot.CopyFailed"), HintType.Error);
|
|
}
|
|
else
|
|
{
|
|
HintService.Hint(Lang.Text("Instance.Screenshot.FileNotFound"));
|
|
}
|
|
}
|
|
|
|
private void BtnOpenFolder_Click(object sender, MouseButtonEventArgs e)
|
|
{
|
|
if (!Directory.Exists(screenshotPath))
|
|
Directory.CreateDirectory(screenshotPath);
|
|
ModBase.OpenExplorer(screenshotPath);
|
|
}
|
|
}
|