CI / Go Backend (push) Canceled after 0s
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
887 lines
32 KiB
C#
887 lines
32 KiB
C#
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Input;
|
||
using System.Windows.Media;
|
||
using Microsoft.VisualBasic;
|
||
using PCL.Core.App;
|
||
using PCL.Core.Utils;
|
||
using PCL.Core.Utils.Exts;
|
||
using PCL.Core.App.Localization;
|
||
|
||
namespace PCL;
|
||
|
||
public partial class MyLocalCompItem
|
||
{
|
||
private string GetUpdateCompareDescription()
|
||
{
|
||
var currentName = Entry.compFile.FileName.Replace(".jar", "");
|
||
var newestName = Entry.UpdateFile.FileName.Replace(".jar", "");
|
||
// 简化名称对比
|
||
var currentSegs = currentName.Split('-').ToList();
|
||
var newestSegs = newestName.Split('-').ToList();
|
||
var shortened = false;
|
||
foreach (var Seg in currentSegs.ToList())
|
||
{
|
||
if (!newestSegs.Contains(Seg))
|
||
continue;
|
||
currentSegs.Remove(Seg);
|
||
newestSegs.Remove(Seg);
|
||
shortened = true;
|
||
}
|
||
|
||
if (shortened && currentSegs.Any() && newestSegs.Any())
|
||
{
|
||
currentName = currentSegs.Join("-");
|
||
newestName = newestSegs.Join("-");
|
||
Entry._Version = currentName; // 使用网络信息作为显示的版本号
|
||
}
|
||
|
||
return
|
||
Lang.Text("Instance.Resource.Item.UpdateCompare", currentName, Lang.TimeSpan(Entry.compFile.ReleaseDate - DateTime.Now), newestName, Lang.TimeSpan(Entry.UpdateFile.ReleaseDate - DateTime.Now));
|
||
}
|
||
|
||
public void Refresh()
|
||
{
|
||
Dispatcher.BeginInvoke(new Func<Task>(async () =>
|
||
{
|
||
// 更新
|
||
if (Entry.CanUpdate)
|
||
{
|
||
BtnUpdate.Visibility = Visibility.Visible;
|
||
BtnUpdate.ToolTip = $"{GetUpdateCompareDescription()}\r\n{Lang.Text("Instance.Resource.Item.UpdateToolTip")}";
|
||
}
|
||
else
|
||
{
|
||
BtnUpdate.Visibility = Visibility.Collapsed;
|
||
}
|
||
|
||
// 标题与描述
|
||
string descFileName;
|
||
if (Entry.IsFolder)
|
||
// 文件夹项的特殊处理
|
||
descFileName = Entry.Name;
|
||
else
|
||
switch (Entry.State)
|
||
{
|
||
case ModLocalComp.LocalCompFile.LocalFileStatus.Fine:
|
||
{
|
||
descFileName = ModBase.GetFileNameWithoutExtentionFromPath(Entry.path);
|
||
break;
|
||
}
|
||
case ModLocalComp.LocalCompFile.LocalFileStatus.Disabled:
|
||
{
|
||
descFileName =
|
||
ModBase.GetFileNameWithoutExtentionFromPath(Entry.path.Replace(".disabled", "")
|
||
.Replace(".old", "")); // McMod.McModState.Unavailable
|
||
break;
|
||
}
|
||
|
||
default:
|
||
{
|
||
descFileName = ModBase.GetFileNameFromPath(Entry.path);
|
||
break;
|
||
}
|
||
}
|
||
|
||
string newDescription;
|
||
var compTemp = Entry.Comp;
|
||
if (Entry.IsFolder)
|
||
{
|
||
// 文件夹项的特殊显示
|
||
Title = Entry.Name;
|
||
newDescription = Entry.Description;
|
||
}
|
||
else if (Config.Download.Comp.UiCompNameSolution == 1)
|
||
{
|
||
// 标题显示文件名,详情显示译名
|
||
// 标题
|
||
Title = descFileName;
|
||
SubTitle = "";
|
||
// 描述
|
||
if (Entry.Comp is null)
|
||
{
|
||
newDescription = Entry.Name;
|
||
}
|
||
else
|
||
{
|
||
var titles = await Task.Run(() => compTemp.GetControlTitle(false));
|
||
newDescription = titles.Key + titles.Value;
|
||
}
|
||
|
||
newDescription = newDescription.Replace(" | ", " / ");
|
||
if (Entry.Version is not null)
|
||
newDescription += $" ({Entry.Version})";
|
||
}
|
||
else
|
||
{
|
||
// 标题显示译名,详情显示文件名
|
||
// 标题
|
||
if (Entry.Comp is null)
|
||
{
|
||
Title = Entry.Name;
|
||
SubTitle = Entry.Version is null ? "" : " | " + Entry.Version;
|
||
}
|
||
else
|
||
{
|
||
var titles = await Task.Run(() => compTemp.GetControlTitle(false));
|
||
Title = titles.Key;
|
||
SubTitle = titles.Value + (Entry.Version is null ? "" : " | " + Entry.Version);
|
||
}
|
||
|
||
// 描述
|
||
newDescription = descFileName;
|
||
}
|
||
|
||
if (Entry.Comp is not null)
|
||
newDescription += ": " + Entry.Comp.Description.Replace("\r", "").Replace("\n", "");
|
||
else if (Entry.Description is not null)
|
||
newDescription += ": " + Entry.Description.Replace("\r", "").Replace("\n", "");
|
||
else if (!Entry.IsFileAvailable) newDescription += ": " + Lang.Text("Instance.Resource.Item.InfoUnavailable");
|
||
Description = newDescription;
|
||
if (Checked)
|
||
LabTitle.SetResourceReference(TextBlock.ForegroundProperty,
|
||
Entry.State == ModLocalComp.LocalCompFile.LocalFileStatus.Fine ? "ColorBrush2" : "ColorBrush5");
|
||
else
|
||
LabTitle.SetResourceReference(TextBlock.ForegroundProperty,
|
||
Entry.State == ModLocalComp.LocalCompFile.LocalFileStatus.Fine ? "ColorBrush1" : "ColorBrushGray4");
|
||
// 主 Logo
|
||
Logo = Entry.GetLogo();
|
||
|
||
// 图标右下角的 Logo
|
||
if (Entry.State == ModLocalComp.LocalCompFile.LocalFileStatus.Fine)
|
||
{
|
||
if (imgState is not null)
|
||
{
|
||
Children.Remove(imgState);
|
||
imgState = null;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (imgState is null)
|
||
{
|
||
imgState = new Image
|
||
{
|
||
Width = 20d,
|
||
Height = 20d,
|
||
Margin = new Thickness(0d, 0d, -5, -3),
|
||
IsHitTestVisible = false,
|
||
HorizontalAlignment = HorizontalAlignment.Right,
|
||
VerticalAlignment = VerticalAlignment.Bottom
|
||
};
|
||
RenderOptions.SetBitmapScalingMode(imgState, BitmapScalingMode.HighQuality);
|
||
SetColumn(imgState, 1);
|
||
SetRow(imgState, 1);
|
||
SetRowSpan(imgState, 2);
|
||
Children.Add(imgState);
|
||
// <Image x:Name="ImgState" RenderOptions.BitmapScalingMode="HighQuality" Width="16" Height="16" Margin="0,0,-3,-1"
|
||
// Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" IsHitTestVisible="False"
|
||
// HorizontalAlignment="Right" VerticalAlignment="Bottom"
|
||
// Source="/Images/Icons/Unavailable.png" />
|
||
}
|
||
|
||
imgState.Source = new MyBitmap(ModBase.pathImage + $"Icons/{Entry.State}.png");
|
||
}
|
||
|
||
// 标签
|
||
if (Entry.IsFolder)
|
||
// 为文件夹添加标签
|
||
Tags = new List<string> { Lang.Text("Instance.Resource.Item.FolderTag") };
|
||
else if (Entry.Comp is not null) Tags = Entry.Comp.Tags;
|
||
}));
|
||
}
|
||
|
||
public void RefreshColor(object sender, EventArgs e)
|
||
{
|
||
InitLate(sender, e);
|
||
// 触发颜色动画
|
||
var time = IsMouseOver ? 120 : 180;
|
||
var ani = new List<ModAnimation.AniData>();
|
||
// ButtonStack
|
||
if (buttonStack is not null)
|
||
{
|
||
if (IsMouseOver)
|
||
{
|
||
ani.Add(ModAnimation.AaOpacity(buttonStack, 1d - buttonStack.Opacity, (int)Math.Round(time * 0.7d),
|
||
(int)Math.Round(time * 0.3d)));
|
||
ani.Add(ModAnimation.AaDouble(
|
||
i => ColumnPaddingRight.Width =
|
||
new GridLength(Math.Max(0, ColumnPaddingRight.Width.Value + (double)i)),
|
||
5 + Buttons.Count() * 25 - ColumnPaddingRight.Width.Value, (int)Math.Round(time * 0.3d),
|
||
(int)Math.Round(time * 0.7d)));
|
||
}
|
||
else
|
||
{
|
||
ani.Add(ModAnimation.AaOpacity(buttonStack, -buttonStack.Opacity, (int)Math.Round(time * 0.4d)));
|
||
ani.Add(ModAnimation.AaDouble(
|
||
i => ColumnPaddingRight.Width =
|
||
new GridLength(Math.Max(0, ColumnPaddingRight.Width.Value + (double)i)),
|
||
4d - ColumnPaddingRight.Width.Value, (int)Math.Round(time * 0.4d)));
|
||
}
|
||
}
|
||
|
||
// RectBack
|
||
if (IsMouseOver || Checked)
|
||
{
|
||
ani.AddRange(new[]
|
||
{
|
||
ModAnimation.AaColor(RectBack, Border.BackgroundProperty, isMouseDown ? "ColorBrush6" : "ColorBrushBg1",
|
||
time),
|
||
ModAnimation.AaOpacity(RectBack, 1d - RectBack.Opacity, time, ease: new ModAnimation.AniEaseOutFluent())
|
||
});
|
||
if (isMouseDown)
|
||
ani.Add(ModAnimation.AaScaleTransform(RectBack,
|
||
0.996d - ((ScaleTransform)RectBack.RenderTransform).ScaleX, (int)Math.Round(time * 1.2d),
|
||
ease: new ModAnimation.AniEaseOutFluent()));
|
||
else
|
||
ani.Add(ModAnimation.AaScaleTransform(RectBack, 1d - ((ScaleTransform)RectBack.RenderTransform).ScaleX,
|
||
(int)Math.Round(time * 1.2d), ease: new ModAnimation.AniEaseOutFluent()));
|
||
}
|
||
else
|
||
{
|
||
ani.AddRange(new[]
|
||
{
|
||
ModAnimation.AaOpacity(RectBack, -RectBack.Opacity, time),
|
||
ModAnimation.AaScaleTransform(RectBack, 0.996d - ((ScaleTransform)RectBack.RenderTransform).ScaleX,
|
||
time, ease: new ModAnimation.AniEaseOutFluent()),
|
||
ModAnimation.AaScaleTransform(RectBack, -0.196d, 1, after: true)
|
||
});
|
||
}
|
||
|
||
ModAnimation.AniStart(ani, "LocalModItem Color " + Uuid);
|
||
}
|
||
|
||
// 触发虚拟化内容
|
||
private void InitLate(object sender, EventArgs e)
|
||
{
|
||
if (buttonHandler is not null)
|
||
{
|
||
buttonHandler((MyLocalCompItem)sender, e);
|
||
buttonHandler = null;
|
||
}
|
||
}
|
||
|
||
// 显示更新日志
|
||
private void BtnUpdate_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
|
||
{
|
||
e.Handled = true;
|
||
ShowUpdateLog();
|
||
}
|
||
|
||
private void ShowUpdateLog()
|
||
{
|
||
if (Entry.Comp is not null)
|
||
{
|
||
if (!Information.IsNumeric(Entry.Comp.Id))
|
||
{
|
||
var modrinthUrl = Entry.changelogUrls.FirstOrDefault(x => x.Contains("modrinth.com"));
|
||
if (modrinthUrl is not null)
|
||
{
|
||
ModBase.OpenWebsite(modrinthUrl);
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
var curseForgeUrl = Entry.changelogUrls.FirstOrDefault(x => x.Contains("curseforge.com"));
|
||
if (curseForgeUrl is not null)
|
||
{
|
||
ModBase.OpenWebsite(curseForgeUrl);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
ModBase.Log(
|
||
Lang.Text("Instance.Resource.Item.OpenChangelogFailed"),
|
||
ModBase.LogLevel.Hint,
|
||
userSummary: Lang.Text("Instance.Resource.Item.OpenChangelogFailed"));
|
||
}
|
||
|
||
// 触发更新
|
||
private void BtnUpdate_Click(object sender, EventArgs e)
|
||
{
|
||
switch (ModMain.MyMsgBox(
|
||
$"{Lang.Text("Instance.Resource.Item.UpdateConfirm.Message", Entry.Name)}\r\n\r\n{GetUpdateCompareDescription()}",
|
||
Lang.Text("Instance.Resource.Item.UpdateConfirm.Title"),
|
||
Lang.Text("Instance.Resource.Item.Update"), Lang.Text("Instance.Resource.Item.ViewChangelog"), Lang.Text("Common.Action.Cancel")))
|
||
{
|
||
case 1: // 更新
|
||
{
|
||
switch (Entry.Comp.Type)
|
||
{
|
||
case ModComp.CompType.Mod:
|
||
{
|
||
ModMain.frmInstanceMod ??= new PageInstanceCompResource(ModComp.CompType.Mod);
|
||
ModMain.frmInstanceMod.UpdateResource(new[] { Entry });
|
||
break;
|
||
}
|
||
case ModComp.CompType.ResourcePack:
|
||
{
|
||
ModMain.frmInstanceResourcePack ??= new PageInstanceCompResource(ModComp.CompType.ResourcePack);
|
||
ModMain.frmInstanceResourcePack.UpdateResource(new[] { Entry });
|
||
break;
|
||
}
|
||
case ModComp.CompType.Shader:
|
||
{
|
||
ModMain.frmInstanceShader ??= new PageInstanceCompResource(ModComp.CompType.Shader);
|
||
ModMain.frmInstanceShader.UpdateResource(new[] { Entry });
|
||
break;
|
||
}
|
||
case ModComp.CompType.DataPack:
|
||
{
|
||
ModMain.frmInstanceSavesDatapack ??= new PageInstanceSavesDatapack();
|
||
ModMain.frmInstanceSavesDatapack.UpdateResource(new[] { Entry });
|
||
break;
|
||
}
|
||
}
|
||
|
||
break;
|
||
}
|
||
case 2: // 查看更新日志
|
||
{
|
||
ShowUpdateLog();
|
||
break;
|
||
}
|
||
case 3: // 取消
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 自适应(#4465)
|
||
private void PanTitle_SizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)
|
||
{
|
||
// 0:全部舒展:Auto - Auto - (Auto) - 1*
|
||
// 1:压缩 Subtitle:Auto - 1* - (Auto) - 0
|
||
// 2:继续压缩 Title:1* - 0 - (Auto) - 0
|
||
var currentCompressLevel =
|
||
ColumnExtend.Width.IsStar ? 0 : ColumnTitle.Width.IsStar ? 2 : 1; // Subtitle 可能是 Collapsed
|
||
var newCompressLevel = default(int);
|
||
switch (currentCompressLevel)
|
||
{
|
||
case 0:
|
||
{
|
||
if (ColumnExtend.ActualWidth < 0.5d)
|
||
newCompressLevel = LabSubtitle.Visibility == Visibility.Collapsed ? 2 : 1;
|
||
else
|
||
return;
|
||
|
||
break;
|
||
}
|
||
case 1:
|
||
{
|
||
if (ColumnSubtitle.ActualWidth < 0.5d)
|
||
newCompressLevel = 2;
|
||
else if (!LabSubtitle.IsTextTrimmed())
|
||
newCompressLevel = 0;
|
||
else
|
||
return;
|
||
|
||
break;
|
||
}
|
||
case 2:
|
||
{
|
||
if (!LabTitle.IsTextTrimmed())
|
||
newCompressLevel = LabSubtitle.Visibility == Visibility.Collapsed ? 0 : 1;
|
||
else
|
||
return;
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
switch (newCompressLevel)
|
||
{
|
||
case 0:
|
||
{
|
||
// 全部舒展:Auto - Auto - (Auto) - 1*
|
||
ColumnTitle.Width = GridLength.Auto;
|
||
ColumnSubtitle.Width = GridLength.Auto;
|
||
ColumnExtend.Width = new GridLength(1d, GridUnitType.Star);
|
||
break;
|
||
}
|
||
case 1:
|
||
{
|
||
// 压缩 Subtitle:Auto - 1* - (Auto) - 0
|
||
ColumnTitle.Width = GridLength.Auto;
|
||
ColumnSubtitle.Width = new GridLength(1d, GridUnitType.Star);
|
||
ColumnExtend.Width = new GridLength(0d, GridUnitType.Pixel);
|
||
break;
|
||
}
|
||
case 2:
|
||
{
|
||
// 继续压缩 Title:1* - 0 - (Auto) - 0
|
||
ColumnTitle.Width = new GridLength(1d, GridUnitType.Star);
|
||
ColumnSubtitle.Width = new GridLength(0d, GridUnitType.Pixel);
|
||
ColumnExtend.Width = new GridLength(0d, GridUnitType.Pixel);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
#region 基础属性
|
||
|
||
public int Uuid = ModBase.GetUuid();
|
||
|
||
// Logo
|
||
public string Logo
|
||
{
|
||
get => PathLogo.Source;
|
||
set => PathLogo.Source = value;
|
||
}
|
||
|
||
// 标题
|
||
public string Title
|
||
{
|
||
get => field;
|
||
set
|
||
{
|
||
var rawValue = value;
|
||
switch (Entry.State)
|
||
{
|
||
case ModLocalComp.LocalCompFile.LocalFileStatus.Fine:
|
||
{
|
||
LabTitle.TextDecorations = null;
|
||
break;
|
||
}
|
||
case ModLocalComp.LocalCompFile.LocalFileStatus.Disabled:
|
||
{
|
||
LabTitle.TextDecorations = TextDecorations.Strikethrough;
|
||
break;
|
||
}
|
||
case ModLocalComp.LocalCompFile.LocalFileStatus.Unavailable:
|
||
{
|
||
LabTitle.TextDecorations = TextDecorations.Strikethrough;
|
||
value += Lang.Text("Instance.Resource.Item.ErrorSuffix");
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ((LabTitle.Text ?? "") == (value ?? ""))
|
||
return;
|
||
LabTitle.Text = value;
|
||
field = rawValue;
|
||
}
|
||
}
|
||
|
||
// 副标题
|
||
public string SubTitle
|
||
{
|
||
get => LabSubtitle?.Text ?? "";
|
||
set
|
||
{
|
||
if ((LabSubtitle.Text ?? "") == (value ?? ""))
|
||
return;
|
||
LabSubtitle.Text = value;
|
||
LabSubtitle.Visibility = string.IsNullOrEmpty(value) ? Visibility.Collapsed : Visibility.Visible;
|
||
}
|
||
}
|
||
|
||
// 描述
|
||
public string Description
|
||
{
|
||
get => LabInfo.Text;
|
||
set
|
||
{
|
||
if ((LabInfo.Text ?? "") == (value ?? ""))
|
||
return;
|
||
LabInfo.Text = value;
|
||
}
|
||
}
|
||
|
||
// Tag
|
||
public List<string> Tags
|
||
{
|
||
set
|
||
{
|
||
PanTags.Children.Clear();
|
||
PanTags.Visibility = value.Any() ? Visibility.Visible : Visibility.Collapsed;
|
||
foreach (var TagText in value)
|
||
{
|
||
var newTag = new Border
|
||
{
|
||
Background = new SolidColorBrush(Color.FromArgb(12, 0, 0, 0)),
|
||
Padding = new Thickness(3d, 1d, 3d, 1d),
|
||
CornerRadius = new CornerRadius(3d),
|
||
Margin = new Thickness(0d, 0d, 3d, 0d),
|
||
SnapsToDevicePixels = true,
|
||
UseLayoutRounding = false
|
||
};
|
||
var tagTextBlock = new TextBlock
|
||
{
|
||
Text = TagText,
|
||
Foreground = new SolidColorBrush(ThemeManager.IsDarkMode
|
||
? Color.FromArgb(88, 255, 255, 255)
|
||
: Color.FromArgb(88, 136, 136, 136)),
|
||
FontSize = 11d
|
||
};
|
||
newTag.Child = tagTextBlock;
|
||
PanTags.Children.Add(newTag);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 相关联的 Mod
|
||
public ModLocalComp.LocalCompFile Entry
|
||
{
|
||
get => (ModLocalComp.LocalCompFile)Tag;
|
||
set => Tag = value;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 点击与勾选
|
||
|
||
// 触发点击事件
|
||
public event ClickEventHandler? Click;
|
||
|
||
public delegate void ClickEventHandler(object sender, MouseButtonEventArgs e);
|
||
|
||
public MyLocalCompItem()
|
||
{
|
||
InitializeComponent();
|
||
PreviewMouseLeftButtonUp += Button_MouseUp;
|
||
PreviewMouseLeftButtonDown += Button_MouseDown;
|
||
MouseLeave += Button_MouseLeave;
|
||
PreviewMouseLeftButtonUp += Button_MouseLeave;
|
||
MouseLeftButtonDown += Button_MouseSwipeStart;
|
||
MouseEnter += Button_MouseSwipe;
|
||
MouseLeave += Button_MouseSwipe;
|
||
MouseLeftButtonUp += Button_MouseSwipe;
|
||
Loaded += (_, _) => Refresh();
|
||
MouseEnter += RefreshColor;
|
||
MouseLeave += RefreshColor;
|
||
MouseLeftButtonDown += RefreshColor;
|
||
MouseLeftButtonUp += RefreshColor;
|
||
Changed += RefreshColor;
|
||
// Handles
|
||
BtnUpdate.PreviewMouseRightButtonUp += BtnUpdate_PreviewMouseRightButtonUp;
|
||
BtnUpdate.Click += BtnUpdate_Click;
|
||
PanTitle.SizeChanged += PanTitle_SizeChanged;
|
||
}
|
||
|
||
private void Button_MouseUp(object sender, MouseButtonEventArgs e)
|
||
{
|
||
if (isMouseDown)
|
||
{
|
||
Click?.Invoke(sender, e);
|
||
if (e.Handled)
|
||
return;
|
||
ModBase.Log("[Control] 按下本地 Mod 列表项:" + LabTitle.Text);
|
||
}
|
||
}
|
||
|
||
// 鼠标点击判定
|
||
private bool isMouseDown;
|
||
|
||
private void Button_MouseDown(object sender, MouseButtonEventArgs e)
|
||
{
|
||
if (!IsMouseDirectlyOver)
|
||
return;
|
||
isMouseDown = true;
|
||
if (buttonStack is not null)
|
||
buttonStack.IsHitTestVisible = false;
|
||
}
|
||
|
||
private void Button_MouseLeave(object sender, object e)
|
||
{
|
||
isMouseDown = false;
|
||
if (buttonStack is not null)
|
||
buttonStack.IsHitTestVisible = true;
|
||
}
|
||
|
||
// 滑动选中
|
||
public class SwipeSelect
|
||
{
|
||
public int Start { get; set; }
|
||
public int End { get; set; }
|
||
|
||
public bool Swiping
|
||
{
|
||
get => field;
|
||
set
|
||
{
|
||
field = value;
|
||
if (TargetFrm is not null)
|
||
try
|
||
{
|
||
var cardSelect = Interaction.CallByName(TargetFrm, "CardSelect", CallType.Get);
|
||
Interaction.CallByName(cardSelect, "IsHitTestVisible", CallType.Set, !value);
|
||
}
|
||
catch
|
||
{
|
||
}
|
||
}
|
||
}
|
||
|
||
public bool SwipeToState { get; set; }
|
||
public object TargetFrm { get; set; }
|
||
}
|
||
|
||
public SwipeSelect CurrentSwipe { get; set; }
|
||
|
||
private void Button_MouseSwipeStart(object sender, object e)
|
||
{
|
||
if (Parent is null)
|
||
return; // Mod 可能已被删除(#3824)
|
||
// 开始滑动
|
||
var index = ((StackPanel)Parent).Children.IndexOf(this);
|
||
CurrentSwipe.Start = index;
|
||
CurrentSwipe.End = index;
|
||
CurrentSwipe.Swiping = true;
|
||
CurrentSwipe.SwipeToState = !Checked;
|
||
}
|
||
|
||
private void Button_MouseSwipe(object sender, object e)
|
||
{
|
||
if (Parent is null)
|
||
return; // Mod 可能已被删除(#3824)
|
||
// 结束滑动
|
||
if (Mouse.LeftButton != MouseButtonState.Pressed || !(Mouse.DirectlyOver is MyLocalCompItem)) // #5771
|
||
{
|
||
CurrentSwipe.Swiping = false;
|
||
return;
|
||
}
|
||
|
||
// 计算滑动范围
|
||
var elements = ((StackPanel)Parent).Children;
|
||
var index = elements.IndexOf(this);
|
||
CurrentSwipe.Start =
|
||
(int)Math.Round(ModBase.MathClamp(Math.Min(CurrentSwipe.Start, index), 0d, elements.Count - 1));
|
||
CurrentSwipe.End =
|
||
(int)Math.Round(ModBase.MathClamp(Math.Max(CurrentSwipe.End, index), 0d, elements.Count - 1));
|
||
// 勾选所有范围中的项
|
||
if (CurrentSwipe.Start == CurrentSwipe.End)
|
||
return;
|
||
for (int i = CurrentSwipe.Start, loopTo = CurrentSwipe.End; i <= loopTo; i++)
|
||
{
|
||
var item = (MyLocalCompItem)elements[i];
|
||
item.InitLate(item, (EventArgs)e);
|
||
item.Checked = CurrentSwipe.SwipeToState;
|
||
}
|
||
}
|
||
|
||
// 勾选状态
|
||
public event CheckEventHandler? Check;
|
||
|
||
public delegate void CheckEventHandler(object sender, ModBase.RouteEventArgs e);
|
||
|
||
public event ChangedEventHandler? Changed;
|
||
|
||
public delegate void ChangedEventHandler(object sender, ModBase.RouteEventArgs e);
|
||
|
||
public bool Checked
|
||
{
|
||
get => field;
|
||
set
|
||
{
|
||
try
|
||
{
|
||
// 触发属性值修改
|
||
var rawValue = field;
|
||
if (value == field)
|
||
return;
|
||
field = value;
|
||
var ChangedEventArgs = new ModBase.RouteEventArgs();
|
||
if (IsInitialized)
|
||
{
|
||
Changed?.Invoke(this, ChangedEventArgs);
|
||
if (ChangedEventArgs.handled)
|
||
{
|
||
field = rawValue;
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (value)
|
||
{
|
||
var checkEventArgs = new ModBase.RouteEventArgs();
|
||
Check?.Invoke(this, checkEventArgs);
|
||
if (checkEventArgs.handled)
|
||
return;
|
||
}
|
||
|
||
// 更改动画
|
||
if (this.IsVisibleInWindow(ModMain.frmMain))
|
||
{
|
||
var anim = new List<ModAnimation.AniData>();
|
||
if (Checked)
|
||
{
|
||
// 由无变有
|
||
var delta = 32d - RectCheck.ActualHeight;
|
||
anim.Add(ModAnimation.AaHeight(RectCheck, delta * 0.4d, 200,
|
||
ease: new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.Weak)));
|
||
anim.Add(ModAnimation.AaHeight(RectCheck, delta * 0.6d, 300,
|
||
ease: new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)));
|
||
anim.Add(ModAnimation.AaOpacity(RectCheck, 1d - RectCheck.Opacity, 30));
|
||
RectCheck.VerticalAlignment = VerticalAlignment.Center;
|
||
RectCheck.Margin = new Thickness(-3, 0d, 0d, 0d);
|
||
anim.Add(ModAnimation.AaColor(LabTitle, TextBlock.ForegroundProperty,
|
||
Entry.State == ModLocalComp.LocalCompFile.LocalFileStatus.Fine
|
||
? "ColorBrush2"
|
||
: "ColorBrush5", 200));
|
||
}
|
||
else
|
||
{
|
||
// 由有变无
|
||
anim.Add(ModAnimation.AaHeight(RectCheck, -RectCheck.ActualHeight, 120,
|
||
ease: new ModAnimation.AniEaseInFluent(ModAnimation.AniEasePower.Weak)));
|
||
anim.Add(ModAnimation.AaOpacity(RectCheck, -RectCheck.Opacity, 70, 40));
|
||
RectCheck.VerticalAlignment = VerticalAlignment.Center;
|
||
anim.Add(ModAnimation.AaColor(LabTitle, TextBlock.ForegroundProperty,
|
||
LabTitle.TextDecorations is null ? "ColorBrush1" : "ColorBrushGray4", 120));
|
||
}
|
||
|
||
ModAnimation.AniStart(anim, "MyLocalCompItem Checked " + Uuid);
|
||
}
|
||
else
|
||
{
|
||
// 不在窗口上时直接设置
|
||
RectCheck.VerticalAlignment = VerticalAlignment.Center;
|
||
RectCheck.Margin = new Thickness(-3, 0d, 0d, 0d);
|
||
if (Checked)
|
||
{
|
||
RectCheck.Height = 32d;
|
||
RectCheck.Opacity = 1d;
|
||
LabTitle.SetResourceReference(TextBlock.ForegroundProperty,
|
||
Entry.State == ModLocalComp.LocalCompFile.LocalFileStatus.Fine
|
||
? "ColorBrush2"
|
||
: "ColorBrush5");
|
||
}
|
||
else
|
||
{
|
||
RectCheck.Height = 0d;
|
||
RectCheck.Opacity = 0d;
|
||
LabTitle.SetResourceReference(TextBlock.ForegroundProperty,
|
||
Entry.State == ModLocalComp.LocalCompFile.LocalFileStatus.Fine
|
||
? "ColorBrush1"
|
||
: "ColorBrushGray4");
|
||
}
|
||
|
||
ModAnimation.AniStop("MyLocalCompItem Checked " + Uuid);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ModBase.Log(ex, "设置 Checked 失败");
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 后加载内容
|
||
|
||
// 右下角状态指示图标
|
||
private Image imgState;
|
||
|
||
// 指向背景
|
||
public Border RectBack
|
||
{
|
||
get
|
||
{
|
||
if (field is null)
|
||
{
|
||
var rect = new Border
|
||
{
|
||
Name = "RectBack",
|
||
CornerRadius = new CornerRadius(3d),
|
||
RenderTransform = new ScaleTransform(0.8d, 0.8d),
|
||
RenderTransformOrigin = new Point(0.5d, 0.5d),
|
||
BorderThickness = new Thickness(ModBase.GetWPFSize(1d)),
|
||
SnapsToDevicePixels = true,
|
||
IsHitTestVisible = false,
|
||
Opacity = 0d
|
||
};
|
||
rect.SetResourceReference(Border.BackgroundProperty, "ColorBrush7");
|
||
rect.SetResourceReference(Border.BorderBrushProperty, "ColorBrush6");
|
||
SetColumnSpan(rect, 999);
|
||
SetRowSpan(rect, 999);
|
||
Children.Insert(0, rect);
|
||
field = rect;
|
||
// <!--<corelocal:BlurBorder x:Name = "RectBack" CornerRadius="3" RenderTransformOrigin="0.5,0.5" SnapsToDevicePixels="True"
|
||
// IsHitTestVisible = "False" Opacity="0" BorderThickness="1"
|
||
// Grid.ColumnSpan = "4" Background="{DynamicResource ColorBrush7}" BorderBrush="{DynamicResource ColorBrush6}"/>-->
|
||
}
|
||
|
||
return field;
|
||
}
|
||
}
|
||
|
||
// 按钮
|
||
public Action<MyLocalCompItem, EventArgs> buttonHandler;
|
||
public FrameworkElement buttonStack;
|
||
public IEnumerable<MyIconButton> Buttons
|
||
{
|
||
get => field;
|
||
set
|
||
{
|
||
field = value;
|
||
// 移除原 Stack
|
||
if (buttonStack is not null)
|
||
{
|
||
Children.Remove(buttonStack);
|
||
buttonStack = null;
|
||
}
|
||
|
||
if (!value.Any())
|
||
return;
|
||
// 添加新 Stack
|
||
buttonStack = new StackPanel
|
||
{
|
||
Opacity = 0d,
|
||
Margin = new Thickness(0d, 0d, 5d, 0d),
|
||
SnapsToDevicePixels = false,
|
||
Orientation = (Orientation)System.Windows.Forms.Orientation.Horizontal,
|
||
HorizontalAlignment = HorizontalAlignment.Right,
|
||
VerticalAlignment = VerticalAlignment.Center,
|
||
UseLayoutRounding = false
|
||
};
|
||
SetColumnSpan(buttonStack, 10);
|
||
SetRowSpan(buttonStack, 10);
|
||
// 构造按钮
|
||
foreach (var Btn in value)
|
||
{
|
||
if (Btn.Height.Equals(double.NaN))
|
||
Btn.Height = 25d;
|
||
if (Btn.Width.Equals(double.NaN))
|
||
Btn.Width = 25d;
|
||
((StackPanel)buttonStack).Children.Add(Btn);
|
||
}
|
||
|
||
Children.Add(buttonStack);
|
||
}
|
||
}
|
||
|
||
// 勾选条
|
||
public Border RectCheck
|
||
{
|
||
get
|
||
{
|
||
if (field is null)
|
||
{
|
||
field = new Border
|
||
{
|
||
Width = 5d,
|
||
Height = Checked ? double.NaN : 0d,
|
||
CornerRadius = new CornerRadius(2d, 2d, 2d, 2d),
|
||
VerticalAlignment = Checked ? VerticalAlignment.Stretch : VerticalAlignment.Center,
|
||
HorizontalAlignment = HorizontalAlignment.Left,
|
||
UseLayoutRounding = false,
|
||
SnapsToDevicePixels = false,
|
||
Margin = Checked ? new Thickness(-3, 6d, 0d, 6d) : new Thickness(-3, 0d, 0d, 0d)
|
||
};
|
||
field.SetResourceReference(Border.BackgroundProperty, "ColorBrush3");
|
||
SetRowSpan(field, 10);
|
||
Children.Add(field);
|
||
}
|
||
|
||
return field;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|