From a69960fb17e1ada64f1951f2017dad35b121943d Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 01:16:42 +0800 Subject: [PATCH 01/24] =?UTF-8?q?feat(debug-toast):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=BC=B9=E7=AA=97=E5=8A=9F=E8=83=BD=E5=8F=8A?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=9C=AC=E5=9C=B0=E5=8C=96=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App/Localization/Languages/en-US.xaml | 7 +++++ .../App/Localization/Languages/zh-CN.xaml | 7 +++++ .../PageSetup/PageSetupLauncherMisc.xaml | 26 +++++++++++++++++++ .../PageSetup/PageSetupLauncherMisc.xaml.cs | 20 ++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/PCL.Core/App/Localization/Languages/en-US.xaml b/PCL.Core/App/Localization/Languages/en-US.xaml index c090e7a13e..ebdf3b78ab 100644 --- a/PCL.Core/App/Localization/Languages/en-US.xaml +++ b/PCL.Core/App/Localization/Languages/en-US.xaml @@ -2898,6 +2898,13 @@ Do not copy files from other folders while downloading When downloading, do not copy identical files that already exist in other Minecraft folders; re-download them instead. Only recommended for testing download speed. Debug options + Toast type + Info + Success + Error + Warning + Toast content + Show test toast diff --git a/PCL.Core/App/Localization/Languages/zh-CN.xaml b/PCL.Core/App/Localization/Languages/zh-CN.xaml index 9bae29f93c..d423fc23e2 100644 --- a/PCL.Core/App/Localization/Languages/zh-CN.xaml +++ b/PCL.Core/App/Localization/Languages/zh-CN.xaml @@ -2898,6 +2898,13 @@ 禁止在下载时从其他文件夹复制文件 在下载时不直接复制其他 MC 文件夹中已经存在的相同文件,而是重新下载文件。 只建议在测试下载速度时开启。 调试选项 + 弹窗类型 + 信息 + 成功 + 错误 + 警告 + 弹窗内容 + 弹出测试弹窗 diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml index 7c31f83870..11c746834b 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml @@ -154,6 +154,32 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml.cs b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml.cs index ea201c281a..dcf6a087ea 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml.cs +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml.cs @@ -182,6 +182,26 @@ private void ComboSystemActivity_SelectionChanged(object sender, SelectionChange 2) ComboSystemActivity.SelectedItem = e.RemovedItems[0]; } + // 测试弹窗(调试选项) + private void BtnToastTest_Click(object sender, MouseButtonEventArgs e) + { + var text = TextToastTestContent.Text; + if (string.IsNullOrWhiteSpace(text)) + { + HintService.Hint("请输入要测试的弹窗内容。", HintType.Warning, false); + return; + } + + var type = ComboToastTestType.SelectedIndex switch + { + 1 => HintType.Success, + 2 => HintType.Error, + 3 => HintType.Warning, + _ => HintType.Info + }; + HintService.Hint(text, type); + } + private void CheckDebugMode_OnChange(object sender, bool user) { CheckBoxChange(sender, user); From 96e679866ae90f2ee5c16e7dd9307bb8fd76b471 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 01:28:52 +0800 Subject: [PATCH 02/24] =?UTF-8?q?feat(toast):=20=E9=99=90=E5=88=B6?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E5=AE=BD=E9=AB=98=EF=BC=8C=E7=82=B9=E5=87=BB?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=AE=8C=E6=95=B4=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PCL.Core/App/Localization/Languages/en-US.xaml | 2 ++ PCL.Core/App/Localization/Languages/zh-CN.xaml | 2 ++ Plain Craft Launcher 2/Controls/MyToast.xaml | 1 + Plain Craft Launcher 2/Controls/MyToast.xaml.cs | 11 ++++++++++- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/PCL.Core/App/Localization/Languages/en-US.xaml b/PCL.Core/App/Localization/Languages/en-US.xaml index ebdf3b78ab..8ab0f841ca 100644 --- a/PCL.Core/App/Localization/Languages/en-US.xaml +++ b/PCL.Core/App/Localization/Languages/en-US.xaml @@ -66,6 +66,8 @@ Cancel Close OK + Toast details + Click to view full content Continue Copy Decline diff --git a/PCL.Core/App/Localization/Languages/zh-CN.xaml b/PCL.Core/App/Localization/Languages/zh-CN.xaml index d423fc23e2..0b71a46af7 100644 --- a/PCL.Core/App/Localization/Languages/zh-CN.xaml +++ b/PCL.Core/App/Localization/Languages/zh-CN.xaml @@ -66,6 +66,8 @@ 取消 关闭 确定 + 提示详情 + 点击查看完整内容 继续 复制 拒绝 diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml b/Plain Craft Launcher 2/Controls/MyToast.xaml index ac47e33575..17852e1767 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml @@ -28,6 +28,7 @@ TitleText.Text = value; } + // 点击展开,展示完整内容 + private void ShowDetail() + { + if (IsDismissing) return; + ModMain.MyMsgBox(Context, Lang.Text("Main.Toast.Detail.Title"), Lang.Text("Common.Action.Confirm")); + } + public string Icon { get; set; } = "lucide/info"; public HintType ToastType { get; set; } = HintType.Info; @@ -92,7 +100,7 @@ public void Show() if (Parent is not Panel) return; if (System.Windows.Application.Current.MainWindow is not null) - MaxWidth = System.Windows.Application.Current.MainWindow.ActualWidth * 0.9; + MaxWidth = Math.Min(System.Windows.Application.Current.MainWindow.ActualWidth * 0.9, 420d); Margin = new Thickness(0, 0, 16, 4); Opacity = 0; @@ -284,6 +292,7 @@ private void Toast_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs if (_dragPending && !_isDragging) { _dragPending = false; + ShowDetail(); return; } From b715dd0545b810d117807f1c071a4f0739544004 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 01:30:09 +0800 Subject: [PATCH 03/24] =?UTF-8?q?feat(toast):=20=E6=96=B0=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=E5=8F=A0=E7=BD=AE=E4=BA=8E=E6=97=A7=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E4=B8=8A=E6=96=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2/Modules/UI/HintService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plain Craft Launcher 2/Modules/UI/HintService.cs b/Plain Craft Launcher 2/Modules/UI/HintService.cs index e8e117d4fc..9a903aac97 100644 --- a/Plain Craft Launcher 2/Modules/UI/HintService.cs +++ b/Plain Craft Launcher 2/Modules/UI/HintService.cs @@ -64,7 +64,7 @@ internal static void Tick() var activeCount = ModMain.frmMain.PanHint.Children.OfType().Count(t => !t.IsDismissing); if (activeCount >= 5) { - var oldest = ModMain.frmMain.PanHint.Children.OfType().FirstOrDefault(t => !t.IsDismissing); + var oldest = ModMain.frmMain.PanHint.Children.OfType().LastOrDefault(t => !t.IsDismissing); oldest?.Dismiss(); return; } @@ -83,7 +83,7 @@ internal static void Tick() DisplayDuration = (800d + ModBase.MathClamp(currentHint.Text.Length, 5d, 23d) * 180d) * ModAnimation.aniSpeed }; - ModMain.frmMain.PanHint.Children.Add(toast); + ModMain.frmMain.PanHint.Children.Insert(0, toast); toast.Show(); if (currentHint.Log) From 699c30d0ec65817f0142fa8074ad1d08b36daf1e Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 01:31:19 +0800 Subject: [PATCH 04/24] =?UTF-8?q?feat(setup):=20=E8=B0=83=E8=AF=95?= =?UTF-8?q?=E9=80=89=E9=A1=B9=E6=94=AF=E6=8C=81=E8=BF=9E=E7=BB=AD=E8=A7=A6?= =?UTF-8?q?=E5=8F=91=E5=A4=9A=E6=9D=A1=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PCL.Core/App/Localization/Languages/en-US.xaml | 1 + PCL.Core/App/Localization/Languages/zh-CN.xaml | 1 + .../Pages/PageSetup/PageSetupLauncherMisc.xaml | 11 ++++++++--- .../PageSetup/PageSetupLauncherMisc.xaml.cs | 16 ++++++++++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/PCL.Core/App/Localization/Languages/en-US.xaml b/PCL.Core/App/Localization/Languages/en-US.xaml index 8ab0f841ca..4f50a0ac34 100644 --- a/PCL.Core/App/Localization/Languages/en-US.xaml +++ b/PCL.Core/App/Localization/Languages/en-US.xaml @@ -2907,6 +2907,7 @@ Warning Toast content Show test toast + Show a burst of toasts diff --git a/PCL.Core/App/Localization/Languages/zh-CN.xaml b/PCL.Core/App/Localization/Languages/zh-CN.xaml index 0b71a46af7..85f3ded1cf 100644 --- a/PCL.Core/App/Localization/Languages/zh-CN.xaml +++ b/PCL.Core/App/Localization/Languages/zh-CN.xaml @@ -2907,6 +2907,7 @@ 警告 弹窗内容 弹出测试弹窗 + 连续触发多条 diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml index 11c746834b..7b767e30ad 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml @@ -167,9 +167,14 @@ - + + + + diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml.cs b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml.cs index dcf6a087ea..a95aa0dbc5 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml.cs +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml.cs @@ -202,6 +202,22 @@ private void BtnToastTest_Click(object sender, MouseButtonEventArgs e) HintService.Hint(text, type); } + // 连续触发多条弹窗(调试选项) + private void BtnToastTestBatch_Click(object sender, MouseButtonEventArgs e) + { + const int batchCount = 6; + var baseText = TextToastTestContent.Text; + if (string.IsNullOrWhiteSpace(baseText)) + { + HintService.Hint("请输入要测试的弹窗内容。", HintType.Warning, false); + return; + } + + var types = new[] { HintType.Info, HintType.Success, HintType.Error, HintType.Warning }; + for (var i = 0; i < batchCount; i++) + HintService.Hint($"[{i + 1}/{batchCount}] {baseText}", types[i % types.Length], log: false); + } + private void CheckDebugMode_OnChange(object sender, bool user) { CheckBoxChange(sender, user); From 783588aba91db4f1503effc89ef80e579e32a63b Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 01:43:25 +0800 Subject: [PATCH 05/24] =?UTF-8?q?fix(toast):=20=E5=86=85=E5=AE=B9=E9=99=90?= =?UTF-8?q?=203=20=E8=A1=8C=E7=9C=81=E7=95=A5=EF=BC=8C=E6=94=B6=E7=AA=84?= =?UTF-8?q?=E6=9C=80=E5=A4=A7=E5=AE=BD=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2/Controls/MyToast.xaml | 2 +- Plain Craft Launcher 2/Controls/MyToast.xaml.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml b/Plain Craft Launcher 2/Controls/MyToast.xaml index 17852e1767..8441e10001 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml @@ -28,7 +28,7 @@ Date: Sat, 8 Aug 2026 01:50:23 +0800 Subject: [PATCH 06/24] =?UTF-8?q?feat(toast):=20=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E6=8C=89=20Z=20=E8=BD=B4=E5=8F=A0=E7=BD=AE=EF=BC=8C=E9=9C=B2?= =?UTF-8?q?=E5=87=BA=E4=B8=8A=E6=B2=BF=E5=8F=AF=E6=95=B0=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controls/MyToast.xaml.cs | 6 +++++ Plain Craft Launcher 2/FormMain.xaml | 8 +++--- .../Modules/UI/HintService.cs | 26 +++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index ef39b910d1..5fe409368e 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -159,7 +159,10 @@ private void StartHideAnimation(double delayMs) ModAnimation.AaCode(() => { if (Parent is Panel p) + { p.Children.Remove(this); + HintService.OnToastRemoved(this); + } }, after: true) }, $"Toast Hide {Uuid}"); } @@ -183,7 +186,10 @@ public void Dismiss() ModAnimation.AaCode(() => { if (Parent is Panel p) + { p.Children.Remove(this); + HintService.OnToastRemoved(this); + } }, after: true) }, $"Toast Dismiss {Uuid}"); } diff --git a/Plain Craft Launcher 2/FormMain.xaml b/Plain Craft Launcher 2/FormMain.xaml index eeef4f4325..190e442364 100644 --- a/Plain Craft Launcher 2/FormMain.xaml +++ b/Plain Craft Launcher 2/FormMain.xaml @@ -219,10 +219,10 @@ UseLayoutRounding="False" SnapsToDevicePixels="False" /> - + 叠置时旧弹窗露出的上沿高度(像素)。 + private const double ToastPeek = 10d; + /// 弹窗离容器底部的边距(像素)。 + private const double ToastBottomMargin = 4d; + private struct HintMessage { public string Text; @@ -85,6 +91,7 @@ internal static void Tick() ModMain.frmMain.PanHint.Children.Insert(0, toast); toast.Show(); + RearrangeToasts(); if (currentHint.Log) ModBase.Log("[UI] 弹出提示:" + currentHint.Text); @@ -96,9 +103,28 @@ internal static void Tick() } } + // 按叠置规则重排所有弹窗:最新在最前(Z 最高)且在最下,旧弹窗向上错开露出上沿 + private static void RearrangeToasts() + { + var toasts = ModMain.frmMain.PanHint.Children.OfType().Where(t => !t.IsDismissing).ToList(); + for (var i = 0; i < toasts.Count; i++) + { + var t = toasts[i]; // Children 顺序,索引 0 = 最新 + t.Margin = new Thickness(0, 0, 16, ToastBottomMargin + i * ToastPeek); + t.VerticalAlignment = VerticalAlignment.Bottom; // 底部锚定(Grid 内叠置的关键,防止 Stretch 居中断层) + Panel.SetZIndex(t, toasts.Count - 1 - i); // 最新 Z 最高 + } + } + public static void HideAll() { foreach (MyToast toast in ModMain.frmMain!.PanHint.Children.OfType().ToList()) toast.Dismiss(); } + + // 弹窗移除后回填错位(由 MyToast 移除自身后调用) + internal static void OnToastRemoved(MyToast toast) + { + RearrangeToasts(); + } } From d25a1bfc8baab1e4983d84544132e1378085f24a Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 02:09:37 +0800 Subject: [PATCH 07/24] =?UTF-8?q?feat(toast):=20=E5=85=A5=E5=9C=BA?= =?UTF-8?q?=E5=9B=9E=E5=BC=B9=E8=90=BD=E4=BD=8D=E4=B8=8E=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E6=8C=89=E5=8E=8B=EF=BC=8C=E5=8F=A0=E7=BD=AE=E6=A0=88=E8=A1=A5?= =?UTF-8?q?=E4=BD=8D=E6=94=B9=E4=B8=BA=E6=95=B4=E4=BD=93=E5=B1=82=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controls/MyToast.xaml.cs | 18 +++++++++++++----- .../Modules/UI/HintService.cs | 15 ++++++++++++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index 5fe409368e..0cd85f2091 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -65,6 +65,7 @@ public MyToast() ModAnimation.AniStop($"Toast Dismiss {Uuid}"); ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); + ModAnimation.AniStop($"Toast StackSettle {Uuid}"); ProgressBar.BeginAnimation(WidthProperty, null); }; } @@ -111,12 +112,18 @@ public void Show() RenderTransform = new TranslateTransform(60, 0); + // 图标“按入”微动效:0.8 → 1,略带过头回弹,呼应启动器按钮的按压语言 + ToastIcon.RenderTransformOrigin = new Point(0.5, 0.5); + ToastIcon.RenderTransform = new ScaleTransform(0.8, 0.8); + ModAnimation.AniStop($"Toast Drag Return {Uuid}"); var enterAnimations = new List { - ModAnimation.AaTranslateX(this, -60, 400, ease: new ModAnimation.AniEaseOutFluent()), + ModAnimation.AaTranslateX(this, -60, 380, ease: new ModAnimation.AniEaseOutCar()), ModAnimation.AaHeight(this, _targetHeight, 150, ease: new ModAnimation.AniEaseOutFluent()), - ModAnimation.AaOpacity(this, 1, 100) + ModAnimation.AaOpacity(this, 1, 100), + ModAnimation.AaScaleTransform(ToastIcon, 0.2, 260, 120, + ease: new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)) }; ModAnimation.AniStart(enterAnimations, $"Toast Show {Uuid}"); @@ -133,11 +140,11 @@ public void Emphasize() if (RenderTransform is TranslateTransform tt) tt.X = 0; Opacity = 1; Height = _targetHeight; + ToastIcon.RenderTransform = new ScaleTransform(1, 1); // 若入场动效尚未结束,先复位图标缩放 ModAnimation.AniStart(new List { - ModAnimation.AaTranslateX(this, -8, 70, ease: new ModAnimation.AniEaseOutFluent()), - ModAnimation.AaTranslateX(this, 16, 70, after: true), - ModAnimation.AaTranslateX(this, -8, 60, after: true, ease: new ModAnimation.AniEaseOutFluent()), + ModAnimation.AaTranslateX(this, -10, 90, ease: new ModAnimation.AniEaseOutFluent()), + ModAnimation.AaTranslateX(this, 10, 140, after: true, ease: new ModAnimation.AniEaseOutFluent()), ModAnimation.AaCode(RestartHideAnimation, after: true), }, $"Toast Emphasize {Uuid}"); } @@ -178,6 +185,7 @@ public void Dismiss() ModAnimation.AniStop($"Toast Hide {Uuid}"); ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); + ModAnimation.AniStop($"Toast StackSettle {Uuid}"); ProgressBar.BeginAnimation(WidthProperty, null); ModAnimation.AniStart(new List { diff --git a/Plain Craft Launcher 2/Modules/UI/HintService.cs b/Plain Craft Launcher 2/Modules/UI/HintService.cs index 9a2d990cca..83224141a3 100644 --- a/Plain Craft Launcher 2/Modules/UI/HintService.cs +++ b/Plain Craft Launcher 2/Modules/UI/HintService.cs @@ -1,5 +1,6 @@ using System.Windows; using System.Windows.Controls; +using System.Windows.Media; using PCL.Core.UI; namespace PCL; @@ -110,8 +111,20 @@ private static void RearrangeToasts() for (var i = 0; i < toasts.Count; i++) { var t = toasts[i]; // Children 顺序,索引 0 = 最新 - t.Margin = new Thickness(0, 0, 16, ToastBottomMargin + i * ToastPeek); + var oldBottom = t.Margin.Bottom; + var newBottom = ToastBottomMargin + i * ToastPeek; + var shift = oldBottom - newBottom; // >0 下落补位,<0 上抬让位 + t.Margin = new Thickness(0, 0, 16, newBottom); t.VerticalAlignment = VerticalAlignment.Bottom; // 底部锚定(Grid 内叠置的关键,防止 Stretch 居中断层) + if (Math.Abs(shift) > 0.5) + { + var tt = t.RenderTransform as TranslateTransform ?? new TranslateTransform(); + t.RenderTransform = tt; + tt.Y = -shift; // 视觉先停在旧位置,再动画归零,形成整体层动 + ModAnimation.AniStart( + ModAnimation.AaTranslateY(t, shift, 200, ease: new ModAnimation.AniEaseOutFluent()), + $"Toast StackSettle {t.Uuid}"); + } Panel.SetZIndex(t, toasts.Count - 1 - i); // 最新 Z 最高 } } From 0fd11930d1e1a07bbf683bd5d8124e00c7f3d407 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 02:10:15 +0800 Subject: [PATCH 08/24] =?UTF-8?q?feat(toast):=20=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E7=AE=B1=E8=B4=A8=E6=84=9F=E2=80=94=E2=80=94=E6=A5=94=E5=BD=A2?= =?UTF-8?q?=E8=89=B2=E6=A0=87=E3=80=81=E4=B8=8B=E6=B2=89=E8=BF=9B=E5=BA=A6?= =?UTF-8?q?=E8=BD=A8=E9=81=93=E3=80=81=E4=BF=A1=E6=81=AF=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E9=99=8D=E5=99=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2/Controls/MyToast.xaml | 28 +++++++++++++------ .../Controls/MyToast.xaml.cs | 10 +++++-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml b/Plain Craft Launcher 2/Controls/MyToast.xaml index 8441e10001..60f6b2e568 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml @@ -8,7 +8,7 @@ CornerRadius="8" BorderThickness="0" UseLayoutRounding="True" SnapsToDevicePixels="True"> - + @@ -16,7 +16,7 @@ - + @@ -35,12 +35,22 @@ Margin="8,0,0,0" SvgIcon="lucide/x" Theme="Custom" /> - - - - - + + + + + + + + + + + + diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index 0cd85f2091..65102d0695 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -224,6 +224,7 @@ private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e) private void UpdateColors() { + var isInfo = ToastType == HintType.Info; var baseHue = ToastType switch { HintType.Success => 145d, @@ -232,21 +233,26 @@ private void UpdateColors() _ => 210d }; var res = System.Windows.Application.Current.Resources; - var accent = new ModBase.MyColor().FromHSL2(baseHue, 75, 60); + var accent = new ModBase.MyColor().FromHSL2(baseHue, isInfo ? 38d : 75d, isInfo ? 56d : 60d); var bg = ThemeService.IsDarkMode ? new SolidColorBrush(LabColor.FromLch(0.35)) : (Brush)res["ColorBrushBackground"]; var text = (SolidColorBrush)res["ColorBrushGray1"]; var accentBrush = new SolidColorBrush(accent); + var track = new SolidColorBrush(ThemeService.IsDarkMode + ? Color.FromArgb(70, 255, 255, 255) // 暗色卡片上略亮的内嵌暗槽 + : Color.FromArgb(60, 0, 0, 0)); // 亮色卡片上略暗的内嵌暗槽 Root.Background = bg; Root.BorderBrush = bg; TitleText.Foreground = text; - ProgressBar.Fill = accentBrush; + ProgressTrack.Fill = track; + ProgressBar.Fill = isInfo ? text : accentBrush; // 信息类型淡化,其余类型带色 BtnClose.Foreground = text; ToastIcon.Icon = Icon; ToastIcon.IconBrush = accentBrush; ToastIcon.StrokeThickness = 0; + AccentBar.Fill = accentBrush; } #region 拖动关闭 From 0a0fa31292cd9500ec2ea88d6cd8c1e7e2af29e4 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 02:18:45 +0800 Subject: [PATCH 09/24] =?UTF-8?q?fix(toast):=20=E4=BF=AE=E5=A4=8D=E7=9F=AD?= =?UTF-8?q?=E6=96=87=E6=9C=AC=E5=BC=B9=E7=AA=97=E5=88=9D=E5=A7=8B=E8=BF=87?= =?UTF-8?q?=E5=AE=BD=E5=B9=B6=E9=80=90=E6=AD=A5=E6=94=B6=E7=AA=84=E7=9A=84?= =?UTF-8?q?=E5=AE=BD=E5=BA=A6=E5=9B=9E=E5=BD=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2/Controls/MyToast.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml b/Plain Craft Launcher 2/Controls/MyToast.xaml index 60f6b2e568..3228a8c6a5 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml @@ -39,7 +39,7 @@ From c7b021e660c3ec1e36821368d31d4e4e7b1b407e Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 02:39:37 +0800 Subject: [PATCH 10/24] =?UTF-8?q?feat(toast):=20=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E4=B8=8A=E9=99=90=E6=8F=90=E8=87=B3=206=20=E8=A1=8C=EF=BC=8C?= =?UTF-8?q?=E6=82=AC=E5=81=9C=E6=9A=82=E5=81=9C=E5=8A=A0=E8=BD=BD=E6=9D=A1?= =?UTF-8?q?=EF=BC=8C=E5=8A=A0=E5=BC=BA=E5=9B=9E=E5=BC=B9=E5=8A=A8=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TitleText MaxHeight 48→96,约 6 行 - 鼠标悬停 toast 时冻结进度条并停住隐藏倒计时,移出后按剩余时间续走;与拖拽互斥 - 重复触发强调动画改为三段回弹(-14/+18/-4),净位移归零 --- Plain Craft Launcher 2/Controls/MyToast.xaml | 2 +- .../Controls/MyToast.xaml.cs | 75 +++++++++++++++---- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml b/Plain Craft Launcher 2/Controls/MyToast.xaml index 3228a8c6a5..f0bb432f83 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml @@ -28,7 +28,7 @@ { UpdateColors(); @@ -67,6 +71,7 @@ public MyToast() ModAnimation.AniStop($"Toast Drag Return {Uuid}"); ModAnimation.AniStop($"Toast StackSettle {Uuid}"); ProgressBar.BeginAnimation(WidthProperty, null); + ResetHoverPause(); }; } @@ -83,7 +88,8 @@ public string Context private void ShowDetail() { if (IsDismissing) return; - ModMain.MyMsgBox(Context, Lang.Text("Main.Toast.Detail.Title"), Lang.Text("Common.Action.Confirm")); + ModMain.MyMsgBoxByHintType(Context, ToastType, Lang.Text("Main.Toast.Detail.Title"), + Lang.Text("Common.Action.Confirm")); } public string Icon { get; set; } = "lucide/info"; @@ -137,14 +143,16 @@ public void Emphasize() ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); ProgressBar.BeginAnimation(WidthProperty, null); + ResetHoverPause(); if (RenderTransform is TranslateTransform tt) tt.X = 0; Opacity = 1; Height = _targetHeight; ToastIcon.RenderTransform = new ScaleTransform(1, 1); // 若入场动效尚未结束,先复位图标缩放 ModAnimation.AniStart(new List { - ModAnimation.AaTranslateX(this, -10, 90, ease: new ModAnimation.AniEaseOutFluent()), - ModAnimation.AaTranslateX(this, 10, 140, after: true, ease: new ModAnimation.AniEaseOutFluent()), + ModAnimation.AaTranslateX(this, -14, 90, ease: new ModAnimation.AniEaseOutFluent()), + ModAnimation.AaTranslateX(this, 18, 100, after: true, ease: new ModAnimation.AniEaseOutFluent()), + ModAnimation.AaTranslateX(this, -4, 110, after: true, ease: new ModAnimation.AniEaseOutFluent()), ModAnimation.AaCode(RestartHideAnimation, after: true), }, $"Toast Emphasize {Uuid}"); } @@ -187,6 +195,7 @@ public void Dismiss() ModAnimation.AniStop($"Toast Drag Return {Uuid}"); ModAnimation.AniStop($"Toast StackSettle {Uuid}"); ProgressBar.BeginAnimation(WidthProperty, null); + ResetHoverPause(); ModAnimation.AniStart(new List { ModAnimation.AaTranslateX(this, 60, 150, ease: new ModAnimation.AniEaseInFluent()), @@ -225,20 +234,21 @@ private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e) private void UpdateColors() { var isInfo = ToastType == HintType.Info; - var baseHue = ToastType switch - { - HintType.Success => 145d, - HintType.Error => 355d, - HintType.Warning => 40d, - _ => 210d - }; var res = System.Windows.Application.Current.Resources; - var accent = new ModBase.MyColor().FromHSL2(baseHue, isInfo ? 38d : 75d, isInfo ? 56d : 60d); + // 信息类型整体用主题色,其余类型用饱和状态色 + var accentBrush = isInfo + ? (Brush)res["ColorBrush2"] + : new SolidColorBrush(ToastType switch + { + HintType.Success => new ModBase.MyColor().FromHSL2(145d, 75d, 60d), + HintType.Error => new ModBase.MyColor().FromHSL2(355d, 75d, 60d), + HintType.Warning => new ModBase.MyColor().FromHSL2(40d, 75d, 60d), + _ => new ModBase.MyColor().FromHSL2(210d, 75d, 60d) + }); var bg = ThemeService.IsDarkMode ? new SolidColorBrush(LabColor.FromLch(0.35)) : (Brush)res["ColorBrushBackground"]; var text = (SolidColorBrush)res["ColorBrushGray1"]; - var accentBrush = new SolidColorBrush(accent); var track = new SolidColorBrush(ThemeService.IsDarkMode ? Color.FromArgb(70, 255, 255, 255) // 暗色卡片上略亮的内嵌暗槽 : Color.FromArgb(60, 0, 0, 0)); // 亮色卡片上略暗的内嵌暗槽 @@ -247,7 +257,7 @@ private void UpdateColors() Root.BorderBrush = bg; TitleText.Foreground = text; ProgressTrack.Fill = track; - ProgressBar.Fill = isInfo ? text : accentBrush; // 信息类型淡化,其余类型带色 + ProgressBar.Fill = accentBrush; // Info 为主题色,其余类型为状态色 BtnClose.Foreground = text; ToastIcon.Icon = Icon; ToastIcon.IconBrush = accentBrush; @@ -456,5 +466,44 @@ private double GetProgressRemainingMs() return _progressTotalMs * (currentWidth / _progressStartWidth); } + private void Root_MouseEnter(object sender, MouseEventArgs e) + { + if (_isDragging || _pausedByHover) + return; + // 悬停暂停:停住隐藏倒计时并冻结进度条,移出后按剩余时间恢复 + ModAnimation.AniStop($"Toast Hide {Uuid}"); + var currentWidth = ProgressBar.Width; + ProgressBar.BeginAnimation(WidthProperty, null); + ProgressBar.Width = currentWidth; + _hoverRemainingMs = GetProgressRemainingMs(); + _pausedByHover = true; + ProgressBar.Opacity = 0.4; + } + + private void Root_MouseLeave(object sender, MouseEventArgs e) + { + if (_isDragging) + return; // 拖拽接管进度条,悬停恢复交由 ReturnFromDrag 处理 + if (!_pausedByHover) + return; + _pausedByHover = false; + ProgressBar.Opacity = 1; + if (_hoverRemainingMs <= 0 || ProgressBar.Width <= 0) + return; // 已结束或未启动,无需恢复动画 + StartHideAnimation(_hoverRemainingMs); + var currentWidth = ProgressBar.Width; + var anim = new DoubleAnimation(currentWidth, 0d, TimeSpan.FromMilliseconds(_hoverRemainingMs)); + ProgressBar.BeginAnimation(WidthProperty, anim); + } + + private void ResetHoverPause() + { + if (!_pausedByHover) + return; + _pausedByHover = false; + _hoverRemainingMs = 0; + ProgressBar.Opacity = 1; + } + #endregion } From 34031c968cbd7d6e1774c72569f9c103e2ffe318 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 02:39:42 +0800 Subject: [PATCH 11/24] =?UTF-8?q?feat(toast):=20=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E7=B1=BB=E5=BC=B9=E7=AA=97=E7=94=A8=E4=B8=BB=E9=A2=98=E8=89=B2?= =?UTF-8?q?=EF=BC=8C=E8=AF=A6=E6=83=85=E5=BC=B9=E7=AA=97=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E4=B8=8E=E7=B1=BB=E5=9E=8B=E8=81=94=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Info 类型图标、楔形条、底部加载条改用主题色 ColorBrush2(动态跟随主题切换) - MyMsgBoxConverter 新增 HintType 字段,新增 MyMsgBoxByHintType 入口 - 点击 toast 展开的详情弹窗按提示类型显示图标与配色(Info 主题色,Error 标题与按钮变红) --- .../Controls/MyMsg/MyMsgText.xaml | 6 +++- .../Controls/MyMsg/MyMsgText.xaml.cs | 19 +++++++++++- Plain Craft Launcher 2/Modules/ModMain.cs | 30 +++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml index 724dfd415a..8ca67e28d5 100644 --- a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml +++ b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml @@ -1,6 +1,7 @@ @@ -23,8 +24,11 @@ + "lucide/circle-check", + HintType.Error => "lucide/circle-minus", + HintType.Warning => "lucide/triangle-alert", + _ => "lucide/info" + }; + ToastTypeIcon.IconBrush = hintType switch + { + HintType.Success => new SolidColorBrush(new ModBase.MyColor().FromHSL2(145d, 75d, 60d)), + HintType.Error => new SolidColorBrush(new ModBase.MyColor().FromHSL2(355d, 75d, 60d)), + HintType.Warning => new SolidColorBrush(new ModBase.MyColor().FromHSL2(40d, 75d, 60d)), + _ => (Brush)System.Windows.Application.Current.Resources["ColorBrush2"] + }; } catch (Exception ex) diff --git a/Plain Craft Launcher 2/Modules/ModMain.cs b/Plain Craft Launcher 2/Modules/ModMain.cs index c1ecd882a1..410e1a6533 100644 --- a/Plain Craft Launcher 2/Modules/ModMain.cs +++ b/Plain Craft Launcher 2/Modules/ModMain.cs @@ -290,6 +290,11 @@ public class MyMsgBoxConverter public string Title; public MyMsgBoxType Type; + /// + /// 弹窗标题图标的提示类型。为 null 时按 Info 处理,不指定则不显示类型联动视觉。 + /// + public HintType? HintType; + /// /// 输入模式:输入验证规则。 /// @@ -408,6 +413,31 @@ public static int MyMsgBox(string caption, string? title = null, string? button1 return 1; } + /// + /// 显示一个带提示类型标题图标的弹窗,返回点击按钮的编号(从 1 开始)。 + /// 不阻塞当前线程,弹窗加入队列由主时钟显示。 + /// + /// 弹窗的内容。 + /// 提示类型,决定标题图标与配色。 + /// 弹窗的标题,默认为通用标题。 + /// 显示的第一个按钮,默认为“确定”。 + public static int MyMsgBoxByHintType(string caption, HintType type, string? title = null, string? button1 = null) + { + title ??= GetDefaultDialogTitle(); + button1 ??= GetDefaultConfirmText(); + // 将弹窗列入队列 + var converter = new MyMsgBoxConverter + { + Type = MyMsgBoxType.Text, Button1 = button1, Button2 = "", Button3 = "", Text = caption, Title = title, + HintType = type + }; + WaitingMyMsgBox.Add(converter); + if (ModBase.RunInUi()) + // 若为 UI 线程,立即执行弹窗刻,避免快速(连点器)点击时多次弹窗 + MyMsgBoxTick(); + return 1; + } + /// /// 显示弹窗,返回点击按钮的编号(从 1 开始)。 /// From c4ebabc7f38de9c5cbe521148e8e7a40708ea5e1 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 13:42:24 +0800 Subject: [PATCH 12/24] =?UTF-8?q?fix(toast):=20=E6=B6=88=E5=A4=B1=E6=8F=90?= =?UTF-8?q?=E9=80=9F=EF=BC=8C=E9=9A=90=E8=97=8F=E8=BF=87=E7=A8=8B=E4=B8=AD?= =?UTF-8?q?=E6=82=AC=E5=81=9C=E4=B8=8D=E5=86=8D=E5=8D=A1=E5=8D=8A=E9=80=8F?= =?UTF-8?q?=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 滑出 200→150ms、淡出 150→110ms,消失更利落 - 新增 _hideStartsAtTick 记录倒计时终点:进入滑出阶段后悬停不再暂停, 杜绝停掉半途隐藏导致卡半透明、永不消失;MouseLeave 异常路径直接放行消失 --- .../Controls/MyToast.xaml.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index 016e19107d..7f7bd45b1e 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -6,6 +6,7 @@ using PCL.Core.App; using PCL.Core.App.Localization; using PCL.Core.UI.Theme; +using PCL.Core.Utils; namespace PCL; @@ -44,6 +45,9 @@ public partial class MyToast private bool _pausedByHover; private double _hoverRemainingMs; + /// 当前隐藏动画倒计时结束、实际开始滑出/淡出的起始 tick(0 表示无待滑出的隐藏)。 + private long _hideStartsAtTick; + public MyToast() { InitializeComponent(); @@ -166,10 +170,11 @@ private void RestartHideAnimation() private void StartHideAnimation(double delayMs) { var delay = (int)Math.Round(delayMs); + _hideStartsAtTick = TimeUtils.GetTimeTick() + delay; ModAnimation.AniStart(new List { - ModAnimation.AaTranslateX(this, 60, 200, delay, new ModAnimation.AniEaseInFluent()), - ModAnimation.AaOpacity(this, -1, 150, delay), + ModAnimation.AaTranslateX(this, 60, 150, delay, new ModAnimation.AniEaseInFluent()), + ModAnimation.AaOpacity(this, -1, 110, delay), ModAnimation.AaHeight(this, -_targetHeight, 100, ease: new ModAnimation.AniEaseOutFluent(), after: true), ModAnimation.AaCode(() => { @@ -468,7 +473,10 @@ private double GetProgressRemainingMs() private void Root_MouseEnter(object sender, MouseEventArgs e) { - if (_isDragging || _pausedByHover) + if (_isDragging || _pausedByHover || IsDismissing) + return; + // 隐藏动画已进入滑出/淡出阶段:不悬停暂停,避免停掉半途的隐藏导致卡半透明、永不消失 + if (_hideStartsAtTick > 0 && TimeUtils.GetTimeTick() >= _hideStartsAtTick) return; // 悬停暂停:停住隐藏倒计时并冻结进度条,移出后按剩余时间恢复 ModAnimation.AniStop($"Toast Hide {Uuid}"); @@ -490,6 +498,12 @@ private void Root_MouseLeave(object sender, MouseEventArgs e) ProgressBar.Opacity = 1; if (_hoverRemainingMs <= 0 || ProgressBar.Width <= 0) return; // 已结束或未启动,无需恢复动画 + // 异常路径:暂停期间倒计时已走完,不再恢复倒计时,直接滑出消失 + if (_hideStartsAtTick > 0 && TimeUtils.GetTimeTick() >= _hideStartsAtTick) + { + StartHideAnimation(0); + return; + } StartHideAnimation(_hoverRemainingMs); var currentWidth = ProgressBar.Width; var anim = new DoubleAnimation(currentWidth, 0d, TimeSpan.FromMilliseconds(_hoverRemainingMs)); From 59f22f945a5414f3aa55f1b67f6512f05f650245 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 13:42:28 +0800 Subject: [PATCH 13/24] =?UTF-8?q?feat(toast):=20=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E6=A0=87=E9=A2=98=E4=B8=8E=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E5=90=8C=E8=89=B2=EF=BC=8C=E6=96=B0=E5=A2=9E=E5=A4=8D=E5=88=B6?= =?UTF-8?q?=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 标题与分割线颜色跟随图标(toast 类型色),仅 HintType 非空时生效;普通警告弹窗的红色标题不受影响 - 按钮区新增复制按钮,点击把正文写入剪贴板(复用 Common.Action.Copy) --- .../Controls/MyMsg/MyMsgText.xaml | 5 ++- .../Controls/MyMsg/MyMsgText.xaml.cs | 42 ++++++++++++------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml index 8ca67e28d5..756c3225d6 100644 --- a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml +++ b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml @@ -27,7 +27,7 @@ - @@ -41,6 +41,9 @@ + diff --git a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml.cs b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml.cs index 37813054b5..7895d58987 100644 --- a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml.cs @@ -28,22 +28,27 @@ public MyMsgText(ModMain.MyMsgBoxConverter converter) ConfigureSecondaryButton(Btn2, converter.Button2); ConfigureSecondaryButton(Btn3, converter.Button3); ShapeLine.StrokeThickness = ModBase.GetWPFSize(1d); - // 按提示类型设置标题图标与颜色(与 MyToast 的图标映射一致) - var hintType = converter.HintType ?? HintType.Info; - ToastTypeIcon.Icon = hintType switch + // 按提示类型设置标题图标与颜色(与 MyToast 的图标映射一致);仅 toast 联动弹窗(指定了 HintType)让标题跟随类型色 + if (converter.HintType is { } hintType) { - HintType.Success => "lucide/circle-check", - HintType.Error => "lucide/circle-minus", - HintType.Warning => "lucide/triangle-alert", - _ => "lucide/info" - }; - ToastTypeIcon.IconBrush = hintType switch - { - HintType.Success => new SolidColorBrush(new ModBase.MyColor().FromHSL2(145d, 75d, 60d)), - HintType.Error => new SolidColorBrush(new ModBase.MyColor().FromHSL2(355d, 75d, 60d)), - HintType.Warning => new SolidColorBrush(new ModBase.MyColor().FromHSL2(40d, 75d, 60d)), - _ => (Brush)System.Windows.Application.Current.Resources["ColorBrush2"] - }; + ToastTypeIcon.Icon = hintType switch + { + HintType.Success => "lucide/circle-check", + HintType.Error => "lucide/circle-minus", + HintType.Warning => "lucide/triangle-alert", + _ => "lucide/info" + }; + var hintBrush = hintType switch + { + HintType.Success => new SolidColorBrush(new ModBase.MyColor().FromHSL2(145d, 75d, 60d)), + HintType.Error => new SolidColorBrush(new ModBase.MyColor().FromHSL2(355d, 75d, 60d)), + HintType.Warning => new SolidColorBrush(new ModBase.MyColor().FromHSL2(40d, 75d, 60d)), + _ => (Brush)System.Windows.Application.Current.Resources["ColorBrush2"] + }; + ToastTypeIcon.IconBrush = hintBrush; + // 标题与分割线颜色跟随图标(类型色),与 MyToast 的类型映射一致;ShapeLine 的 binding 自动跟随 + LabTitle.Foreground = hintBrush; + } } catch (Exception ex) @@ -193,6 +198,13 @@ public void Btn3_Click(object sender, MouseButtonEventArgs e) } } + public void BtnCopy_Click(object? sender = null, MouseButtonEventArgs? e = null) + { + if (myConverter.IsExited) + return; + ModBase.ClipboardSet(myConverter.Text); + } + private void Drag(object sender, MouseButtonEventArgs e) { try From 6e7204f8d6d843bf7bf1509d598218f548617b65 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 14:03:44 +0800 Subject: [PATCH 14/24] =?UTF-8?q?feat(toast):=20=E5=8F=A0=E7=BD=AE?= =?UTF-8?q?=E6=97=B6=E6=97=A7=E5=BC=B9=E7=AA=97=E6=B7=A1=E5=8C=96=E5=A4=84?= =?UTF-8?q?=E7=90=86=EF=BC=8C=E6=9C=80=E6=96=B0=E5=85=A8=E4=BA=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 按叠置次序淡化:最新 1.0,旧弹窗递减至 55% 下限,200ms 过渡 - 入场中/拖拽中/隐藏滑出中的弹窗淡化系统不介入,避免 Opacity 动画冲突 - 入场结束经 NotifyToastShown 复位并重新分层;Emphasize/拖拽中断入场时复位 IsEntering,防止淡化被永久跳过 --- .../Controls/MyToast.xaml.cs | 20 +++++++++++++++- .../Modules/UI/HintService.cs | 23 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index 7f7bd45b1e..b571381f3f 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -74,6 +74,7 @@ public MyToast() ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); ModAnimation.AniStop($"Toast StackSettle {Uuid}"); + ModAnimation.AniStop($"Toast Dim {Uuid}"); ProgressBar.BeginAnimation(WidthProperty, null); ResetHoverPause(); }; @@ -104,6 +105,15 @@ private void ShowDetail() public bool IsDismissing { get; private set; } + /// 是否正处于入场动画期间(淡化系统应跳过,避免入场 Opacity 动画与淡化冲突)。 + internal bool IsEntering { get; set; } + + /// 是否正在被用户拖拽(拖拽期间透明度由拖拽逻辑接管,淡化系统应跳过)。 + internal bool IsDragging => _isDragging; + + /// 隐藏动画是否已进入实际滑出/淡出阶段(此时不再改写其 Opacity)。 + internal bool IsHiding => _hideStartsAtTick > 0 && TimeUtils.GetTimeTick() >= _hideStartsAtTick; + private double _targetHeight; public void Show() @@ -114,6 +124,7 @@ public void Show() MaxWidth = Math.Min(System.Windows.Application.Current.MainWindow.ActualWidth * 0.9, 360d); Margin = new Thickness(0, 0, 16, 4); Opacity = 0; + IsEntering = true; Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); Arrange(new Rect(0, 0, DesiredSize.Width, DesiredSize.Height)); @@ -133,7 +144,8 @@ public void Show() ModAnimation.AaHeight(this, _targetHeight, 150, ease: new ModAnimation.AniEaseOutFluent()), ModAnimation.AaOpacity(this, 1, 100), ModAnimation.AaScaleTransform(ToastIcon, 0.2, 260, 120, - ease: new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)) + ease: new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)), + ModAnimation.AaCode(() => HintService.NotifyToastShown(this), after: true) }; ModAnimation.AniStart(enterAnimations, $"Toast Show {Uuid}"); @@ -146,6 +158,8 @@ public void Emphasize() ModAnimation.AniStop($"Toast Hide {Uuid}"); ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); + ModAnimation.AniStop($"Toast Dim {Uuid}"); + IsEntering = false; // 入场动画被中断,入场末段的重排回调不会再执行,这里复位标记 ProgressBar.BeginAnimation(WidthProperty, null); ResetHoverPause(); if (RenderTransform is TranslateTransform tt) tt.X = 0; @@ -158,6 +172,7 @@ public void Emphasize() ModAnimation.AaTranslateX(this, 18, 100, after: true, ease: new ModAnimation.AniEaseOutFluent()), ModAnimation.AaTranslateX(this, -4, 110, after: true, ease: new ModAnimation.AniEaseOutFluent()), ModAnimation.AaCode(RestartHideAnimation, after: true), + ModAnimation.AaCode(() => HintService.RearrangeToasts(), after: true), }, $"Toast Emphasize {Uuid}"); } @@ -199,6 +214,7 @@ public void Dismiss() ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); ModAnimation.AniStop($"Toast StackSettle {Uuid}"); + ModAnimation.AniStop($"Toast Dim {Uuid}"); ProgressBar.BeginAnimation(WidthProperty, null); ResetHoverPause(); ModAnimation.AniStart(new List @@ -369,6 +385,8 @@ private void BeginDrag(double initialDelta) ModAnimation.AniStop($"Toast Hide {Uuid}"); ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); + ModAnimation.AniStop($"Toast Dim {Uuid}"); + IsEntering = false; // 入场动画被拖拽中断,入场末段的重排回调不会再执行,这里复位标记 PauseProgress(); diff --git a/Plain Craft Launcher 2/Modules/UI/HintService.cs b/Plain Craft Launcher 2/Modules/UI/HintService.cs index 83224141a3..786360e1d4 100644 --- a/Plain Craft Launcher 2/Modules/UI/HintService.cs +++ b/Plain Craft Launcher 2/Modules/UI/HintService.cs @@ -105,7 +105,8 @@ internal static void Tick() } // 按叠置规则重排所有弹窗:最新在最前(Z 最高)且在最下,旧弹窗向上错开露出上沿 - private static void RearrangeToasts() + // 同时按叠置次序对每个弹窗做淡化:最新全亮,旧弹窗递减至 55% 下限,形成清晰视觉层次 + internal static void RearrangeToasts() { var toasts = ModMain.frmMain.PanHint.Children.OfType().Where(t => !t.IsDismissing).ToList(); for (var i = 0; i < toasts.Count; i++) @@ -126,6 +127,19 @@ private static void RearrangeToasts() $"Toast StackSettle {t.Uuid}"); } Panel.SetZIndex(t, toasts.Count - 1 - i); // 最新 Z 最高 + + // 淡化:入场中/拖拽中/隐藏滑出中的弹窗,其 Opacity 分别由入场、拖拽、淡出动画接管,淡化系统不介入 + if (t.IsEntering || t.IsDragging || t.IsHiding || + ModAnimation.AniIsRun($"Toast Drag Return {t.Uuid}")) + continue; + var target = i == 0 ? 1d : Math.Max(1d - i * 0.15, 0.55d); + var delta = target - t.Opacity; + if (Math.Abs(delta) > 0.001) + { + ModAnimation.AniStart( + ModAnimation.AaOpacity(t, delta, 200, ease: new ModAnimation.AniEaseOutFluent()), + $"Toast Dim {t.Uuid}"); + } } } @@ -140,4 +154,11 @@ internal static void OnToastRemoved(MyToast toast) { RearrangeToasts(); } + + // 新弹窗入场动画结束后的回调:复位入场标记并重新分层(把已就位的弹窗淡化到各自档位) + internal static void NotifyToastShown(MyToast toast) + { + toast.IsEntering = false; + RearrangeToasts(); + } } From 2a6c8c74013e9a809276acd4f127d23e7ca94cf8 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 14:03:50 +0800 Subject: [PATCH 15/24] =?UTF-8?q?feat(toast):=20=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E5=A4=8D=E5=88=B6=E6=8C=89=E9=92=AE=E7=A7=BB?= =?UTF-8?q?=E8=87=B3=E5=BA=95=E9=83=A8=E5=B7=A6=E4=BE=A7=EF=BC=8C=E6=96=87?= =?UTF-8?q?=E6=9C=AC=E9=99=90=E9=AB=98=E6=94=B9=202=20=E8=A1=8C=EF=BC=8C?= =?UTF-8?q?=E6=94=BE=E5=A4=A7=E5=85=B3=E9=97=AD=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 复制按钮从底部右侧按钮组独立到左下,确认/取消按钮组保持右侧 - toast 文本 MaxHeight 96→32(2 行),长文本点击展开看详情 - 关闭按钮 20→26px,图标随按钮自适应放大 --- Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml | 8 +++++--- Plain Craft Launcher 2/Controls/MyToast.xaml | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml index 756c3225d6..4024ca3931 100644 --- a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml +++ b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml @@ -39,11 +39,13 @@ FontSize="15" LineHeight="18" Foreground="{DynamicResource ColorBrush1}" FontWeight="Normal" Padding="1" /> - + + + diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml b/Plain Craft Launcher 2/Controls/MyToast.xaml index f0bb432f83..660545c179 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml @@ -28,10 +28,10 @@ - From 3118088fa4234d72b55615ea90466db61f9a631a Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 14:13:05 +0800 Subject: [PATCH 16/24] =?UTF-8?q?feat(toast):=20=E6=B8=90=E9=9A=90?= =?UTF-8?q?=E6=97=A7=E5=BC=B9=E7=AA=97=E5=8A=A0=E6=AF=9B=E7=8E=BB=E7=92=83?= =?UTF-8?q?=E6=A8=A1=E7=B3=8A=EF=BC=8C=E8=B6=8A=E6=97=A7=E8=B6=8A=E6=A8=A1?= =?UTF-8?q?=E7=B3=8A=E8=B6=8A=E6=B7=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RootGrid 挂 BlurEffect 模糊卡片内容,背景迁移到 RootGrid 一起模糊(8px 圆角裁剪) - 模糊与淡化同组联动:最新清晰,旧弹窗 BlurRadius 按叠置次序 5+i*4.5 递增、封顶 14 - ResetDimBlur 统一复位时机:隐藏滑出/拖拽/强调前清零,防止残留模糊 --- .../Controls/MyToast.xaml.cs | 20 ++++++++++---- .../Modules/UI/HintService.cs | 26 +++++++++++++++---- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index b571381f3f..c8aa7a4520 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -3,6 +3,7 @@ using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; +using System.Windows.Media.Effects; using PCL.Core.App; using PCL.Core.App.Localization; using PCL.Core.UI.Theme; @@ -158,7 +159,7 @@ public void Emphasize() ModAnimation.AniStop($"Toast Hide {Uuid}"); ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); - ModAnimation.AniStop($"Toast Dim {Uuid}"); + ResetDimBlur(); // 停掉淡化组并复位模糊,随后三段回弹后由 RearrangeToasts 重新淡化+模糊 IsEntering = false; // 入场动画被中断,入场末段的重排回调不会再执行,这里复位标记 ProgressBar.BeginAnimation(WidthProperty, null); ResetHoverPause(); @@ -182,10 +183,19 @@ private void RestartHideAnimation() StartProgressAnimation(DisplayDuration); } + /// 停掉淡化动画组,并瞬时复位毛玻璃模糊为 0(隐藏/拖拽/强调前调用,防止残留模糊)。 + private void ResetDimBlur() + { + ModAnimation.AniStop($"Toast Dim {Uuid}"); + if (RootGrid.Effect is BlurEffect be) + be.Radius = 0; + } + private void StartHideAnimation(double delayMs) { var delay = (int)Math.Round(delayMs); _hideStartsAtTick = TimeUtils.GetTimeTick() + delay; + ResetDimBlur(); // 隐藏滑出阶段:停掉淡化组并瞬时复位模糊,避免向右滑+淡出时残留毛玻璃 ModAnimation.AniStart(new List { ModAnimation.AaTranslateX(this, 60, 150, delay, new ModAnimation.AniEaseInFluent()), @@ -214,7 +224,7 @@ public void Dismiss() ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); ModAnimation.AniStop($"Toast StackSettle {Uuid}"); - ModAnimation.AniStop($"Toast Dim {Uuid}"); + ResetDimBlur(); ProgressBar.BeginAnimation(WidthProperty, null); ResetHoverPause(); ModAnimation.AniStart(new List @@ -274,8 +284,8 @@ private void UpdateColors() ? Color.FromArgb(70, 255, 255, 255) // 暗色卡片上略亮的内嵌暗槽 : Color.FromArgb(60, 0, 0, 0)); // 亮色卡片上略暗的内嵌暗槽 - Root.Background = bg; - Root.BorderBrush = bg; + // 卡片背景移到 RootGrid:模糊 RootGrid 时背景一起模糊,并被 8px 圆角裁剪;阴影仍在 Root 上不受影响 + RootGrid.Background = bg; TitleText.Foreground = text; ProgressTrack.Fill = track; ProgressBar.Fill = accentBrush; // Info 为主题色,其余类型为状态色 @@ -385,7 +395,7 @@ private void BeginDrag(double initialDelta) ModAnimation.AniStop($"Toast Hide {Uuid}"); ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); - ModAnimation.AniStop($"Toast Dim {Uuid}"); + ResetDimBlur(); // 拖拽是用户主动操作,弹窗需清晰可读,模糊复位为 0 IsEntering = false; // 入场动画被拖拽中断,入场末段的重排回调不会再执行,这里复位标记 PauseProgress(); diff --git a/Plain Craft Launcher 2/Modules/UI/HintService.cs b/Plain Craft Launcher 2/Modules/UI/HintService.cs index 786360e1d4..dcf88a0e64 100644 --- a/Plain Craft Launcher 2/Modules/UI/HintService.cs +++ b/Plain Craft Launcher 2/Modules/UI/HintService.cs @@ -1,6 +1,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Media; +using System.Windows.Media.Effects; using PCL.Core.UI; namespace PCL; @@ -133,12 +134,27 @@ internal static void RearrangeToasts() ModAnimation.AniIsRun($"Toast Drag Return {t.Uuid}")) continue; var target = i == 0 ? 1d : Math.Max(1d - i * 0.15, 0.55d); - var delta = target - t.Opacity; - if (Math.Abs(delta) > 0.001) + var opacityDelta = target - t.Opacity; + + // 毛玻璃模糊:最新清晰,旧弹窗按叠置次序递增、封顶 14,与 Opacity 下限配套(越旧越模糊越淡) + var targetBlur = i == 0 ? 0d : Math.Min(5d + i * 4.5, 14d); + if (t.RootGrid.Effect is not BlurEffect blur) { - ModAnimation.AniStart( - ModAnimation.AaOpacity(t, delta, 200, ease: new ModAnimation.AniEaseOutFluent()), - $"Toast Dim {t.Uuid}"); + blur = new BlurEffect { Radius = 0, RenderingBias = RenderingBias.Performance }; + t.RootGrid.Effect = blur; + } + var blurDelta = targetBlur - blur.Radius; + + if (Math.Abs(opacityDelta) > 0.001 || Math.Abs(blurDelta) > 0.001) + { + // 与 Opacity 同组同组名,一起 AniStop / 一起重放 + var dimAnims = new List(); + if (Math.Abs(opacityDelta) > 0.001) + dimAnims.Add(ModAnimation.AaOpacity(t, opacityDelta, 200, ease: new ModAnimation.AniEaseOutFluent())); + if (Math.Abs(blurDelta) > 0.001) + dimAnims.Add(ModAnimation.AaDouble(v => blur.Radius += (double)v, blurDelta, 200, + ease: new ModAnimation.AniEaseOutFluent())); + ModAnimation.AniStart(dimAnims, $"Toast Dim {t.Uuid}"); } } } From 3529af87953b66e31d6f8a282b4e4928e217f4b0 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 14:13:09 +0800 Subject: [PATCH 17/24] =?UTF-8?q?feat(toast):=20=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E5=A4=8D=E5=88=B6=E6=8C=89=E9=92=AE=E7=A7=BB?= =?UTF-8?q?=E5=9B=9E=E5=8F=B3=E4=BE=A7=E6=8C=89=E9=92=AE=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml index 4024ca3931..756c3225d6 100644 --- a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml +++ b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml @@ -39,13 +39,11 @@ FontSize="15" LineHeight="18" Foreground="{DynamicResource ColorBrush1}" FontWeight="Normal" Padding="1" /> - - - + From 3594d8580c2f5030a6f970c2eac6b75d04e43b56 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sat, 8 Aug 2026 15:58:03 +0800 Subject: [PATCH 18/24] =?UTF-8?q?fix(toast):=20=E9=9A=90=E8=97=8F=E5=80=92?= =?UTF-8?q?=E8=AE=A1=E6=97=B6=E6=A0=87=E5=BF=97=E5=9C=A8=E5=8F=96=E6=B6=88?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E4=B8=8B=E5=A4=8D=E4=BD=8D=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8D=20IsHiding=20=E8=AF=AF=E5=88=A4=E9=98=BB=E5=A1=9E?= =?UTF-8?q?=E6=B7=A1=E5=8C=96/=E6=82=AC=E5=81=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2/Controls/MyToast.xaml.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index c8aa7a4520..811f3248b9 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -46,6 +46,9 @@ public partial class MyToast private bool _pausedByHover; private double _hoverRemainingMs; + /// 悬停暂停开始时刻(毫秒),配合 _hoverRemainingMs 判断暂停期间倒计时是否已走完。 + private long _pauseStartedAtTick; + /// 当前隐藏动画倒计时结束、实际开始滑出/淡出的起始 tick(0 表示无待滑出的隐藏)。 private long _hideStartsAtTick; @@ -160,6 +163,7 @@ public void Emphasize() ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); ResetDimBlur(); // 停掉淡化组并复位模糊,随后三段回弹后由 RearrangeToasts 重新淡化+模糊 + _hideStartsAtTick = 0; // 隐藏倒计时被取消,复位标志避免 IsHiding 误判 IsEntering = false; // 入场动画被中断,入场末段的重排回调不会再执行,这里复位标记 ProgressBar.BeginAnimation(WidthProperty, null); ResetHoverPause(); @@ -225,6 +229,7 @@ public void Dismiss() ModAnimation.AniStop($"Toast Drag Return {Uuid}"); ModAnimation.AniStop($"Toast StackSettle {Uuid}"); ResetDimBlur(); + _hideStartsAtTick = 0; // 隐藏动画被取消(改为滑动关闭),复位标志避免 IsHiding 误判 ProgressBar.BeginAnimation(WidthProperty, null); ResetHoverPause(); ModAnimation.AniStart(new List @@ -396,6 +401,7 @@ private void BeginDrag(double initialDelta) ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); ResetDimBlur(); // 拖拽是用户主动操作,弹窗需清晰可读,模糊复位为 0 + _hideStartsAtTick = 0; // 拖拽取消隐藏倒计时,复位标志避免 IsHiding 误判 IsEntering = false; // 入场动画被拖拽中断,入场末段的重排回调不会再执行,这里复位标记 PauseProgress(); @@ -512,6 +518,8 @@ private void Root_MouseEnter(object sender, MouseEventArgs e) ProgressBar.BeginAnimation(WidthProperty, null); ProgressBar.Width = currentWidth; _hoverRemainingMs = GetProgressRemainingMs(); + _pauseStartedAtTick = TimeUtils.GetTimeTick(); + _hideStartsAtTick = 0; // 悬停暂停即取消隐藏倒计时,结束判定改由 _pauseStartedAtTick + 剩余时长得出 _pausedByHover = true; ProgressBar.Opacity = 0.4; } @@ -527,7 +535,7 @@ private void Root_MouseLeave(object sender, MouseEventArgs e) if (_hoverRemainingMs <= 0 || ProgressBar.Width <= 0) return; // 已结束或未启动,无需恢复动画 // 异常路径:暂停期间倒计时已走完,不再恢复倒计时,直接滑出消失 - if (_hideStartsAtTick > 0 && TimeUtils.GetTimeTick() >= _hideStartsAtTick) + if (_pauseStartedAtTick > 0 && TimeUtils.GetTimeTick() - _pauseStartedAtTick >= _hoverRemainingMs) { StartHideAnimation(0); return; @@ -544,6 +552,7 @@ private void ResetHoverPause() return; _pausedByHover = false; _hoverRemainingMs = 0; + _pauseStartedAtTick = 0; ProgressBar.Opacity = 1; } From c1bd81cdd2aed92449fe528fcd398d951c3cbda5 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sun, 9 Aug 2026 13:35:40 +0800 Subject: [PATCH 19/24] =?UTF-8?q?refactor(toast):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E6=A8=A1=E7=B3=8A=E6=95=88=E6=9E=9C=EF=BC=8C=E7=AE=80=E5=8C=96?= =?UTF-8?q?=E6=B7=A1=E5=8C=96=E5=8A=A8=E7=94=BB=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controls/MyToast.xaml.cs | 14 +++++------ .../Modules/UI/HintService.cs | 24 ++++--------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index 811f3248b9..f91478f4eb 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -162,7 +162,7 @@ public void Emphasize() ModAnimation.AniStop($"Toast Hide {Uuid}"); ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); - ResetDimBlur(); // 停掉淡化组并复位模糊,随后三段回弹后由 RearrangeToasts 重新淡化+模糊 + ResetDim(); // 停掉淡化组,随后三段回弹后由 RearrangeToasts 重新淡化 _hideStartsAtTick = 0; // 隐藏倒计时被取消,复位标志避免 IsHiding 误判 IsEntering = false; // 入场动画被中断,入场末段的重排回调不会再执行,这里复位标记 ProgressBar.BeginAnimation(WidthProperty, null); @@ -187,19 +187,17 @@ private void RestartHideAnimation() StartProgressAnimation(DisplayDuration); } - /// 停掉淡化动画组,并瞬时复位毛玻璃模糊为 0(隐藏/拖拽/强调前调用,防止残留模糊)。 - private void ResetDimBlur() + /// 停掉淡化动画组(隐藏/拖拽/强调前调用)。 + private void ResetDim() { ModAnimation.AniStop($"Toast Dim {Uuid}"); - if (RootGrid.Effect is BlurEffect be) - be.Radius = 0; } private void StartHideAnimation(double delayMs) { var delay = (int)Math.Round(delayMs); _hideStartsAtTick = TimeUtils.GetTimeTick() + delay; - ResetDimBlur(); // 隐藏滑出阶段:停掉淡化组并瞬时复位模糊,避免向右滑+淡出时残留毛玻璃 + ResetDim(); // 隐藏滑出阶段:停掉淡化组,避免向右滑+淡出时残留淡化 ModAnimation.AniStart(new List { ModAnimation.AaTranslateX(this, 60, 150, delay, new ModAnimation.AniEaseInFluent()), @@ -228,7 +226,7 @@ public void Dismiss() ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); ModAnimation.AniStop($"Toast StackSettle {Uuid}"); - ResetDimBlur(); + ResetDim(); _hideStartsAtTick = 0; // 隐藏动画被取消(改为滑动关闭),复位标志避免 IsHiding 误判 ProgressBar.BeginAnimation(WidthProperty, null); ResetHoverPause(); @@ -400,7 +398,7 @@ private void BeginDrag(double initialDelta) ModAnimation.AniStop($"Toast Hide {Uuid}"); ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); - ResetDimBlur(); // 拖拽是用户主动操作,弹窗需清晰可读,模糊复位为 0 + ResetDim(); // 拖拽是用户主动操作,弹窗需清晰可读 _hideStartsAtTick = 0; // 拖拽取消隐藏倒计时,复位标志避免 IsHiding 误判 IsEntering = false; // 入场动画被拖拽中断,入场末段的重排回调不会再执行,这里复位标记 diff --git a/Plain Craft Launcher 2/Modules/UI/HintService.cs b/Plain Craft Launcher 2/Modules/UI/HintService.cs index dcf88a0e64..b7f8aeb0f6 100644 --- a/Plain Craft Launcher 2/Modules/UI/HintService.cs +++ b/Plain Craft Launcher 2/Modules/UI/HintService.cs @@ -136,26 +136,10 @@ internal static void RearrangeToasts() var target = i == 0 ? 1d : Math.Max(1d - i * 0.15, 0.55d); var opacityDelta = target - t.Opacity; - // 毛玻璃模糊:最新清晰,旧弹窗按叠置次序递增、封顶 14,与 Opacity 下限配套(越旧越模糊越淡) - var targetBlur = i == 0 ? 0d : Math.Min(5d + i * 4.5, 14d); - if (t.RootGrid.Effect is not BlurEffect blur) - { - blur = new BlurEffect { Radius = 0, RenderingBias = RenderingBias.Performance }; - t.RootGrid.Effect = blur; - } - var blurDelta = targetBlur - blur.Radius; - - if (Math.Abs(opacityDelta) > 0.001 || Math.Abs(blurDelta) > 0.001) - { - // 与 Opacity 同组同组名,一起 AniStop / 一起重放 - var dimAnims = new List(); - if (Math.Abs(opacityDelta) > 0.001) - dimAnims.Add(ModAnimation.AaOpacity(t, opacityDelta, 200, ease: new ModAnimation.AniEaseOutFluent())); - if (Math.Abs(blurDelta) > 0.001) - dimAnims.Add(ModAnimation.AaDouble(v => blur.Radius += (double)v, blurDelta, 200, - ease: new ModAnimation.AniEaseOutFluent())); - ModAnimation.AniStart(dimAnims, $"Toast Dim {t.Uuid}"); - } + if (Math.Abs(opacityDelta) > 0.001) + ModAnimation.AniStart( + ModAnimation.AaOpacity(t, opacityDelta, 200, ease: new ModAnimation.AniEaseOutFluent()), + $"Toast Dim {t.Uuid}"); } } From b6686fcdc41d87d5022e1069408b7cf5b4fc7b92 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sun, 9 Aug 2026 13:45:06 +0800 Subject: [PATCH 20/24] =?UTF-8?q?feat(toast):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E9=80=8F=E6=98=8E=E5=BA=A6=E7=AE=A1=E7=90=86?= =?UTF-8?q?=EF=BC=8C=E7=A1=AE=E4=BF=9D=E6=97=A7=E5=BC=B9=E7=AA=97=E9=80=90?= =?UTF-8?q?=E7=BA=A7=E6=B7=A1=E5=8C=96=EF=BC=8C=E6=8F=90=E5=8D=87=E8=A7=86?= =?UTF-8?q?=E8=A7=89=E5=B1=82=E6=AC=A1=E6=84=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2/Controls/MyToast.xaml.cs | 10 ++++++++-- Plain Craft Launcher 2/Modules/UI/HintService.cs | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index f91478f4eb..b5af1aaba2 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -436,11 +436,17 @@ private void ReturnFromDrag() ResumeProgress(remaining); + // 拖拽可能是旧弹窗:按叠置档位复位不透明度而非一律 1,避免与最新弹窗一样亮、叠置层次被破坏 + var targetOpacity = HintService.GetStackTargetOpacity(this); ModAnimation.AniStart(new List { ModAnimation.AaTranslateX(this, -currentX, ReturnAnimationMs, ease: new ModAnimation.AniEaseOutFluent()), - ModAnimation.AaOpacity(this, 1d - currentOpacity, ReturnAnimationMs), - ModAnimation.AaCode(() => StartHideAnimation(GetProgressRemainingMs()), after: true) + ModAnimation.AaOpacity(this, targetOpacity - currentOpacity, ReturnAnimationMs), + ModAnimation.AaCode(() => + { + HintService.RearrangeToasts(); // 复位后重算淡化,保证最新弹窗始终最不透明 + StartHideAnimation(GetProgressRemainingMs()); + }, after: true) }, $"Toast Drag Return {Uuid}"); } diff --git a/Plain Craft Launcher 2/Modules/UI/HintService.cs b/Plain Craft Launcher 2/Modules/UI/HintService.cs index b7f8aeb0f6..f08d28a780 100644 --- a/Plain Craft Launcher 2/Modules/UI/HintService.cs +++ b/Plain Craft Launcher 2/Modules/UI/HintService.cs @@ -12,6 +12,10 @@ public static class HintService private const double ToastPeek = 10d; /// 弹窗离容器底部的边距(像素)。 private const double ToastBottomMargin = 4d; + /// 叠置时每个旧弹窗逐级的淡出幅度(透明度逐级升高)。 + private const double ToastDimStep = 0.15d; + /// 叠置时旧弹窗不透明度的下限,保证始终可读。 + private const double ToastDimFloor = 0.55d; private struct HintMessage { @@ -105,6 +109,15 @@ internal static void Tick() } } + // 按叠置次序计算弹窗应保持的不透明度:最新弹窗最不透明(透明度最低),旧弹窗逐级递减 + // 弹窗的叠置次序由 PanHint.Children 决定,索引 0 = 最新 + internal static double GetStackTargetOpacity(MyToast toast) + { + var index = ModMain.frmMain.PanHint.Children.OfType() + .Where(t => !t.IsDismissing).ToList().IndexOf(toast); + return index <= 0 ? 1d : Math.Max(1d - index * ToastDimStep, ToastDimFloor); + } + // 按叠置规则重排所有弹窗:最新在最前(Z 最高)且在最下,旧弹窗向上错开露出上沿 // 同时按叠置次序对每个弹窗做淡化:最新全亮,旧弹窗递减至 55% 下限,形成清晰视觉层次 internal static void RearrangeToasts() @@ -133,7 +146,7 @@ internal static void RearrangeToasts() if (t.IsEntering || t.IsDragging || t.IsHiding || ModAnimation.AniIsRun($"Toast Drag Return {t.Uuid}")) continue; - var target = i == 0 ? 1d : Math.Max(1d - i * 0.15, 0.55d); + var target = GetStackTargetOpacity(t); var opacityDelta = target - t.Opacity; if (Math.Abs(opacityDelta) > 0.001) From d2b244e91bff4d79be1902f201347d4d7ca280e2 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sun, 9 Aug 2026 17:10:21 +0800 Subject: [PATCH 21/24] =?UTF-8?q?refactor(toast):=20=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E5=B8=83=E5=B1=80=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BC=B9=E7=AA=97=E9=97=B4=E8=B7=9D=E5=92=8C?= =?UTF-8?q?=E5=8F=AF=E8=A7=81=E6=80=A7=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2/Controls/MyToast.xaml | 8 +-- .../Controls/MyToast.xaml.cs | 40 ++---------- .../Modules/UI/HintService.cs | 65 ++++++------------- 3 files changed, 28 insertions(+), 85 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml b/Plain Craft Launcher 2/Controls/MyToast.xaml index 660545c179..649f11c235 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml @@ -16,7 +16,7 @@ - + @@ -46,11 +46,5 @@ - - - diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index b5af1aaba2..dabcfe9acc 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -3,7 +3,6 @@ using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; -using System.Windows.Media.Effects; using PCL.Core.App; using PCL.Core.App.Localization; using PCL.Core.UI.Theme; @@ -78,7 +77,6 @@ public MyToast() ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); ModAnimation.AniStop($"Toast StackSettle {Uuid}"); - ModAnimation.AniStop($"Toast Dim {Uuid}"); ProgressBar.BeginAnimation(WidthProperty, null); ResetHoverPause(); }; @@ -109,16 +107,8 @@ private void ShowDetail() public bool IsDismissing { get; private set; } - /// 是否正处于入场动画期间(淡化系统应跳过,避免入场 Opacity 动画与淡化冲突)。 - internal bool IsEntering { get; set; } - - /// 是否正在被用户拖拽(拖拽期间透明度由拖拽逻辑接管,淡化系统应跳过)。 - internal bool IsDragging => _isDragging; - - /// 隐藏动画是否已进入实际滑出/淡出阶段(此时不再改写其 Opacity)。 - internal bool IsHiding => _hideStartsAtTick > 0 && TimeUtils.GetTimeTick() >= _hideStartsAtTick; - - private double _targetHeight; + /// 弹窗完全展开后的目标高度,用于纵向排布的定位计算。 + internal double _targetHeight; public void Show() { @@ -128,7 +118,6 @@ public void Show() MaxWidth = Math.Min(System.Windows.Application.Current.MainWindow.ActualWidth * 0.9, 360d); Margin = new Thickness(0, 0, 16, 4); Opacity = 0; - IsEntering = true; Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); Arrange(new Rect(0, 0, DesiredSize.Width, DesiredSize.Height)); @@ -148,8 +137,7 @@ public void Show() ModAnimation.AaHeight(this, _targetHeight, 150, ease: new ModAnimation.AniEaseOutFluent()), ModAnimation.AaOpacity(this, 1, 100), ModAnimation.AaScaleTransform(ToastIcon, 0.2, 260, 120, - ease: new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)), - ModAnimation.AaCode(() => HintService.NotifyToastShown(this), after: true) + ease: new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)) }; ModAnimation.AniStart(enterAnimations, $"Toast Show {Uuid}"); @@ -162,9 +150,7 @@ public void Emphasize() ModAnimation.AniStop($"Toast Hide {Uuid}"); ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); - ResetDim(); // 停掉淡化组,随后三段回弹后由 RearrangeToasts 重新淡化 _hideStartsAtTick = 0; // 隐藏倒计时被取消,复位标志避免 IsHiding 误判 - IsEntering = false; // 入场动画被中断,入场末段的重排回调不会再执行,这里复位标记 ProgressBar.BeginAnimation(WidthProperty, null); ResetHoverPause(); if (RenderTransform is TranslateTransform tt) tt.X = 0; @@ -176,8 +162,7 @@ public void Emphasize() ModAnimation.AaTranslateX(this, -14, 90, ease: new ModAnimation.AniEaseOutFluent()), ModAnimation.AaTranslateX(this, 18, 100, after: true, ease: new ModAnimation.AniEaseOutFluent()), ModAnimation.AaTranslateX(this, -4, 110, after: true, ease: new ModAnimation.AniEaseOutFluent()), - ModAnimation.AaCode(RestartHideAnimation, after: true), - ModAnimation.AaCode(() => HintService.RearrangeToasts(), after: true), + ModAnimation.AaCode(RestartHideAnimation, after: true) }, $"Toast Emphasize {Uuid}"); } @@ -187,17 +172,10 @@ private void RestartHideAnimation() StartProgressAnimation(DisplayDuration); } - /// 停掉淡化动画组(隐藏/拖拽/强调前调用)。 - private void ResetDim() - { - ModAnimation.AniStop($"Toast Dim {Uuid}"); - } - private void StartHideAnimation(double delayMs) { var delay = (int)Math.Round(delayMs); _hideStartsAtTick = TimeUtils.GetTimeTick() + delay; - ResetDim(); // 隐藏滑出阶段:停掉淡化组,避免向右滑+淡出时残留淡化 ModAnimation.AniStart(new List { ModAnimation.AaTranslateX(this, 60, 150, delay, new ModAnimation.AniEaseInFluent()), @@ -226,7 +204,6 @@ public void Dismiss() ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); ModAnimation.AniStop($"Toast StackSettle {Uuid}"); - ResetDim(); _hideStartsAtTick = 0; // 隐藏动画被取消(改为滑动关闭),复位标志避免 IsHiding 误判 ProgressBar.BeginAnimation(WidthProperty, null); ResetHoverPause(); @@ -296,7 +273,6 @@ private void UpdateColors() ToastIcon.Icon = Icon; ToastIcon.IconBrush = accentBrush; ToastIcon.StrokeThickness = 0; - AccentBar.Fill = accentBrush; } #region 拖动关闭 @@ -398,9 +374,7 @@ private void BeginDrag(double initialDelta) ModAnimation.AniStop($"Toast Hide {Uuid}"); ModAnimation.AniStop($"Toast Emphasize {Uuid}"); ModAnimation.AniStop($"Toast Drag Return {Uuid}"); - ResetDim(); // 拖拽是用户主动操作,弹窗需清晰可读 _hideStartsAtTick = 0; // 拖拽取消隐藏倒计时,复位标志避免 IsHiding 误判 - IsEntering = false; // 入场动画被拖拽中断,入场末段的重排回调不会再执行,这里复位标记 PauseProgress(); @@ -436,15 +410,13 @@ private void ReturnFromDrag() ResumeProgress(remaining); - // 拖拽可能是旧弹窗:按叠置档位复位不透明度而非一律 1,避免与最新弹窗一样亮、叠置层次被破坏 - var targetOpacity = HintService.GetStackTargetOpacity(this); ModAnimation.AniStart(new List { ModAnimation.AaTranslateX(this, -currentX, ReturnAnimationMs, ease: new ModAnimation.AniEaseOutFluent()), - ModAnimation.AaOpacity(this, targetOpacity - currentOpacity, ReturnAnimationMs), + ModAnimation.AaOpacity(this, 1d - currentOpacity, ReturnAnimationMs), ModAnimation.AaCode(() => { - HintService.RearrangeToasts(); // 复位后重算淡化,保证最新弹窗始终最不透明 + HintService.RearrangeToasts(); // 复位后重排,让位/补位的旧弹窗回到正确位置 StartHideAnimation(GetProgressRemainingMs()); }, after: true) }, $"Toast Drag Return {Uuid}"); diff --git a/Plain Craft Launcher 2/Modules/UI/HintService.cs b/Plain Craft Launcher 2/Modules/UI/HintService.cs index f08d28a780..ef8497677f 100644 --- a/Plain Craft Launcher 2/Modules/UI/HintService.cs +++ b/Plain Craft Launcher 2/Modules/UI/HintService.cs @@ -1,21 +1,14 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Media; -using System.Windows.Media.Effects; using PCL.Core.UI; namespace PCL; public static class HintService { - /// 叠置时旧弹窗露出的上沿高度(像素)。 - private const double ToastPeek = 10d; - /// 弹窗离容器底部的边距(像素)。 - private const double ToastBottomMargin = 4d; - /// 叠置时每个旧弹窗逐级的淡出幅度(透明度逐级升高)。 - private const double ToastDimStep = 0.15d; - /// 叠置时旧弹窗不透明度的下限,保证始终可读。 - private const double ToastDimFloor = 0.55d; + /// 相邻弹窗之间的间距(像素)。 + private const double ToastGap = 4d; private struct HintMessage { @@ -109,28 +102,31 @@ internal static void Tick() } } - // 按叠置次序计算弹窗应保持的不透明度:最新弹窗最不透明(透明度最低),旧弹窗逐级递减 - // 弹窗的叠置次序由 PanHint.Children 决定,索引 0 = 最新 - internal static double GetStackTargetOpacity(MyToast toast) - { - var index = ModMain.frmMain.PanHint.Children.OfType() - .Where(t => !t.IsDismissing).ToList().IndexOf(toast); - return index <= 0 ? 1d : Math.Max(1d - index * ToastDimStep, ToastDimFloor); - } - - // 按叠置规则重排所有弹窗:最新在最前(Z 最高)且在最下,旧弹窗向上错开露出上沿 - // 同时按叠置次序对每个弹窗做淡化:最新全亮,旧弹窗递减至 55% 下限,形成清晰视觉层次 + // 非叠置布局:弹窗自底向上纵向排开,最新在最下,旧弹窗依次上移,彼此不重叠 internal static void RearrangeToasts() { var toasts = ModMain.frmMain.PanHint.Children.OfType().Where(t => !t.IsDismissing).ToList(); + if (toasts.Count == 0) + return; + var available = ModMain.frmMain.PanHint.ActualHeight; + if (available > 0) + { + var required = toasts.Sum(t => t._targetHeight + ToastGap) + ToastGap; + if (required > available) + { + toasts[^1].Dismiss(); // 超出可用高度时收掉最旧的弹窗,保证其余完整可见 + return; + } + } + var bottom = ToastGap; for (var i = 0; i < toasts.Count; i++) { var t = toasts[i]; // Children 顺序,索引 0 = 最新 var oldBottom = t.Margin.Bottom; - var newBottom = ToastBottomMargin + i * ToastPeek; - var shift = oldBottom - newBottom; // >0 下落补位,<0 上抬让位 - t.Margin = new Thickness(0, 0, 16, newBottom); - t.VerticalAlignment = VerticalAlignment.Bottom; // 底部锚定(Grid 内叠置的关键,防止 Stretch 居中断层) + var shift = oldBottom - bottom; // >0 下落补位,<0 上抬让位 + t.Margin = new Thickness(0, 0, 16, bottom); + t.VerticalAlignment = VerticalAlignment.Bottom; + Panel.SetZIndex(t, toasts.Count - 1 - i); if (Math.Abs(shift) > 0.5) { var tt = t.RenderTransform as TranslateTransform ?? new TranslateTransform(); @@ -140,19 +136,7 @@ internal static void RearrangeToasts() ModAnimation.AaTranslateY(t, shift, 200, ease: new ModAnimation.AniEaseOutFluent()), $"Toast StackSettle {t.Uuid}"); } - Panel.SetZIndex(t, toasts.Count - 1 - i); // 最新 Z 最高 - - // 淡化:入场中/拖拽中/隐藏滑出中的弹窗,其 Opacity 分别由入场、拖拽、淡出动画接管,淡化系统不介入 - if (t.IsEntering || t.IsDragging || t.IsHiding || - ModAnimation.AniIsRun($"Toast Drag Return {t.Uuid}")) - continue; - var target = GetStackTargetOpacity(t); - var opacityDelta = target - t.Opacity; - - if (Math.Abs(opacityDelta) > 0.001) - ModAnimation.AniStart( - ModAnimation.AaOpacity(t, opacityDelta, 200, ease: new ModAnimation.AniEaseOutFluent()), - $"Toast Dim {t.Uuid}"); + bottom += t._targetHeight + ToastGap; } } @@ -167,11 +151,4 @@ internal static void OnToastRemoved(MyToast toast) { RearrangeToasts(); } - - // 新弹窗入场动画结束后的回调:复位入场标记并重新分层(把已就位的弹窗淡化到各自档位) - internal static void NotifyToastShown(MyToast toast) - { - toast.IsEntering = false; - RearrangeToasts(); - } } From 2ab35e13f0b2cc1ec0914d2c05bbacc07f1121da Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Sun, 9 Aug 2026 23:18:22 +0800 Subject: [PATCH 22/24] =?UTF-8?q?revert(toast):=20=E6=92=A4=E9=94=80=20MyM?= =?UTF-8?q?sg=20=E6=94=B9=E5=8A=A8=EF=BC=8C=E5=B7=B2=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E8=87=B3=20feat/toast-detail-upstream?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controls/MyMsg/MyMsgText.xaml | 11 ++----- .../Controls/MyMsg/MyMsgText.xaml.cs | 31 +------------------ .../Controls/MyToast.xaml.cs | 3 +- Plain Craft Launcher 2/Modules/ModMain.cs | 30 ------------------ 4 files changed, 4 insertions(+), 71 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml index 756c3225d6..724dfd415a 100644 --- a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml +++ b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml @@ -1,7 +1,6 @@ @@ -24,11 +23,8 @@ - - - diff --git a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml.cs b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml.cs index 7895d58987..ec78916856 100644 --- a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml.cs @@ -2,7 +2,6 @@ using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interop; -using System.Windows.Media; using PCL.Core.UI.Controls; using PCL.Core.App.Localization; @@ -24,31 +23,10 @@ public MyMsgText(ModMain.MyMsgBoxConverter converter) myConverter = converter; LabTitle.Text = converter.Title; LabCaption.Text = converter.Text; - ConfigurePrimaryButton(converter.Button1, converter.IsWarn || converter.HintType == HintType.Error); + ConfigurePrimaryButton(converter.Button1, converter.IsWarn); ConfigureSecondaryButton(Btn2, converter.Button2); ConfigureSecondaryButton(Btn3, converter.Button3); ShapeLine.StrokeThickness = ModBase.GetWPFSize(1d); - // 按提示类型设置标题图标与颜色(与 MyToast 的图标映射一致);仅 toast 联动弹窗(指定了 HintType)让标题跟随类型色 - if (converter.HintType is { } hintType) - { - ToastTypeIcon.Icon = hintType switch - { - HintType.Success => "lucide/circle-check", - HintType.Error => "lucide/circle-minus", - HintType.Warning => "lucide/triangle-alert", - _ => "lucide/info" - }; - var hintBrush = hintType switch - { - HintType.Success => new SolidColorBrush(new ModBase.MyColor().FromHSL2(145d, 75d, 60d)), - HintType.Error => new SolidColorBrush(new ModBase.MyColor().FromHSL2(355d, 75d, 60d)), - HintType.Warning => new SolidColorBrush(new ModBase.MyColor().FromHSL2(40d, 75d, 60d)), - _ => (Brush)System.Windows.Application.Current.Resources["ColorBrush2"] - }; - ToastTypeIcon.IconBrush = hintBrush; - // 标题与分割线颜色跟随图标(类型色),与 MyToast 的类型映射一致;ShapeLine 的 binding 自动跟随 - LabTitle.Foreground = hintBrush; - } } catch (Exception ex) @@ -198,13 +176,6 @@ public void Btn3_Click(object sender, MouseButtonEventArgs e) } } - public void BtnCopy_Click(object? sender = null, MouseButtonEventArgs? e = null) - { - if (myConverter.IsExited) - return; - ModBase.ClipboardSet(myConverter.Text); - } - private void Drag(object sender, MouseButtonEventArgs e) { try diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index dabcfe9acc..2f66be30af 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -95,8 +95,7 @@ public string Context private void ShowDetail() { if (IsDismissing) return; - ModMain.MyMsgBoxByHintType(Context, ToastType, Lang.Text("Main.Toast.Detail.Title"), - Lang.Text("Common.Action.Confirm")); + ModMain.MyMsgBox(Context, Lang.Text("Main.Toast.Detail.Title"), Lang.Text("Common.Action.Confirm")); } public string Icon { get; set; } = "lucide/info"; diff --git a/Plain Craft Launcher 2/Modules/ModMain.cs b/Plain Craft Launcher 2/Modules/ModMain.cs index 410e1a6533..c1ecd882a1 100644 --- a/Plain Craft Launcher 2/Modules/ModMain.cs +++ b/Plain Craft Launcher 2/Modules/ModMain.cs @@ -290,11 +290,6 @@ public class MyMsgBoxConverter public string Title; public MyMsgBoxType Type; - /// - /// 弹窗标题图标的提示类型。为 null 时按 Info 处理,不指定则不显示类型联动视觉。 - /// - public HintType? HintType; - /// /// 输入模式:输入验证规则。 /// @@ -413,31 +408,6 @@ public static int MyMsgBox(string caption, string? title = null, string? button1 return 1; } - /// - /// 显示一个带提示类型标题图标的弹窗,返回点击按钮的编号(从 1 开始)。 - /// 不阻塞当前线程,弹窗加入队列由主时钟显示。 - /// - /// 弹窗的内容。 - /// 提示类型,决定标题图标与配色。 - /// 弹窗的标题,默认为通用标题。 - /// 显示的第一个按钮,默认为“确定”。 - public static int MyMsgBoxByHintType(string caption, HintType type, string? title = null, string? button1 = null) - { - title ??= GetDefaultDialogTitle(); - button1 ??= GetDefaultConfirmText(); - // 将弹窗列入队列 - var converter = new MyMsgBoxConverter - { - Type = MyMsgBoxType.Text, Button1 = button1, Button2 = "", Button3 = "", Text = caption, Title = title, - HintType = type - }; - WaitingMyMsgBox.Add(converter); - if (ModBase.RunInUi()) - // 若为 UI 线程,立即执行弹窗刻,避免快速(连点器)点击时多次弹窗 - MyMsgBoxTick(); - return 1; - } - /// /// 显示弹窗,返回点击按钮的编号(从 1 开始)。 /// From 67174cf7e1a0171623e07275448af6bd0f82ac4f Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Tue, 11 Aug 2026 21:17:51 +0800 Subject: [PATCH 23/24] =?UTF-8?q?feat(toast):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E6=97=A5=E5=BF=97=E4=BB=A5=E8=B7=9F=E8=B8=AA?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E6=98=BE=E7=A4=BA=E5=92=8C=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plain Craft Launcher 2/Controls/MyToast.xaml.cs | 4 ++++ Plain Craft Launcher 2/FormMain.xaml | 2 +- Plain Craft Launcher 2/Modules/UI/HintService.cs | 6 +++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index 2f66be30af..d34d08b9ef 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -184,6 +184,8 @@ private void StartHideAnimation(double delayMs) { if (Parent is Panel p) { + System.IO.File.AppendAllText(@"C:\Users\imagi\AppData\Local\Temp\opencode\toast-debug.log", + $"REMOVED toast={Uuid} tick={TimeUtils.GetTimeTick()}\n"); p.Children.Remove(this); HintService.OnToastRemoved(this); } @@ -194,6 +196,8 @@ private void StartHideAnimation(double delayMs) public void Dismiss() { if (IsDismissing) return; + System.IO.File.AppendAllText(@"C:\Users\imagi\AppData\Local\Temp\opencode\toast-debug.log", + $"DISMISS toast={Uuid} tick={TimeUtils.GetTimeTick()}\n"); IsDismissing = true; _isDragging = false; _dragPending = false; diff --git a/Plain Craft Launcher 2/FormMain.xaml b/Plain Craft Launcher 2/FormMain.xaml index 190e442364..c94578ec2f 100644 --- a/Plain Craft Launcher 2/FormMain.xaml +++ b/Plain Craft Launcher 2/FormMain.xaml @@ -220,7 +220,7 @@ diff --git a/Plain Craft Launcher 2/Modules/UI/HintService.cs b/Plain Craft Launcher 2/Modules/UI/HintService.cs index ef8497677f..bad5ff5558 100644 --- a/Plain Craft Launcher 2/Modules/UI/HintService.cs +++ b/Plain Craft Launcher 2/Modules/UI/HintService.cs @@ -47,7 +47,7 @@ internal static void Tick() try { ModMain.frmMain!.PanHint.HorizontalAlignment = HorizontalAlignment.Right; - ModMain.frmMain.PanHint.VerticalAlignment = VerticalAlignment.Bottom; + ModMain.frmMain.PanHint.VerticalAlignment = VerticalAlignment.Stretch; var extraHeight = ModMain.frmMain.PanExtraButtons.ActualHeight; ModMain.frmMain.PanHint.Margin = new Thickness(0, 0, 0, extraHeight > 0 ? extraHeight + 20 : 20); @@ -90,6 +90,8 @@ internal static void Tick() ModMain.frmMain.PanHint.Children.Insert(0, toast); toast.Show(); + System.IO.File.AppendAllText(@"C:\Users\imagi\AppData\Local\Temp\opencode\toast-debug.log", + $"SHOW uuid={toast.Uuid} dur={toast.DisplayDuration} active={activeCount} tick={PCL.Core.Utils.TimeUtils.GetTimeTick()}\n"); RearrangeToasts(); if (currentHint.Log) @@ -114,6 +116,8 @@ internal static void RearrangeToasts() var required = toasts.Sum(t => t._targetHeight + ToastGap) + ToastGap; if (required > available) { + System.IO.File.AppendAllText(@"C:\Users\imagi\AppData\Local\Temp\opencode\toast-debug.log", + $"OVERFLOW dismiss toast={toasts[^1].Uuid} avail={available:0.0} req={required:0.0} tick={PCL.Core.Utils.TimeUtils.GetTimeTick()}\n"); toasts[^1].Dismiss(); // 超出可用高度时收掉最旧的弹窗,保证其余完整可见 return; } From 2bef2828bf6ce6b8b924a1092462f3e293539031 Mon Sep 17 00:00:00 2001 From: Teror Fox Date: Wed, 12 Aug 2026 09:55:05 +0800 Subject: [PATCH 24/24] =?UTF-8?q?refactor(toast):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E5=BC=B9=E7=AA=97=E7=9B=B8=E5=85=B3=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E7=AE=80=E5=8C=96=E7=95=8C=E9=9D=A2=E5=92=8C?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App/Localization/Languages/en-US.xaml | 8 ----- .../App/Localization/Languages/zh-CN.xaml | 8 ----- .../Controls/MyToast.xaml.cs | 4 --- .../Modules/UI/HintService.cs | 4 --- .../PageSetup/PageSetupLauncherMisc.xaml | 31 ---------------- .../PageSetup/PageSetupLauncherMisc.xaml.cs | 36 ------------------- 6 files changed, 91 deletions(-) diff --git a/PCL.Core/App/Localization/Languages/en-US.xaml b/PCL.Core/App/Localization/Languages/en-US.xaml index 4f50a0ac34..84f0173cde 100644 --- a/PCL.Core/App/Localization/Languages/en-US.xaml +++ b/PCL.Core/App/Localization/Languages/en-US.xaml @@ -2900,14 +2900,6 @@ Do not copy files from other folders while downloading When downloading, do not copy identical files that already exist in other Minecraft folders; re-download them instead. Only recommended for testing download speed. Debug options - Toast type - Info - Success - Error - Warning - Toast content - Show test toast - Show a burst of toasts diff --git a/PCL.Core/App/Localization/Languages/zh-CN.xaml b/PCL.Core/App/Localization/Languages/zh-CN.xaml index 85f3ded1cf..f3d530e9f8 100644 --- a/PCL.Core/App/Localization/Languages/zh-CN.xaml +++ b/PCL.Core/App/Localization/Languages/zh-CN.xaml @@ -2900,14 +2900,6 @@ 禁止在下载时从其他文件夹复制文件 在下载时不直接复制其他 MC 文件夹中已经存在的相同文件,而是重新下载文件。 只建议在测试下载速度时开启。 调试选项 - 弹窗类型 - 信息 - 成功 - 错误 - 警告 - 弹窗内容 - 弹出测试弹窗 - 连续触发多条 diff --git a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs index d34d08b9ef..2f66be30af 100644 --- a/Plain Craft Launcher 2/Controls/MyToast.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyToast.xaml.cs @@ -184,8 +184,6 @@ private void StartHideAnimation(double delayMs) { if (Parent is Panel p) { - System.IO.File.AppendAllText(@"C:\Users\imagi\AppData\Local\Temp\opencode\toast-debug.log", - $"REMOVED toast={Uuid} tick={TimeUtils.GetTimeTick()}\n"); p.Children.Remove(this); HintService.OnToastRemoved(this); } @@ -196,8 +194,6 @@ private void StartHideAnimation(double delayMs) public void Dismiss() { if (IsDismissing) return; - System.IO.File.AppendAllText(@"C:\Users\imagi\AppData\Local\Temp\opencode\toast-debug.log", - $"DISMISS toast={Uuid} tick={TimeUtils.GetTimeTick()}\n"); IsDismissing = true; _isDragging = false; _dragPending = false; diff --git a/Plain Craft Launcher 2/Modules/UI/HintService.cs b/Plain Craft Launcher 2/Modules/UI/HintService.cs index bad5ff5558..bc856da8aa 100644 --- a/Plain Craft Launcher 2/Modules/UI/HintService.cs +++ b/Plain Craft Launcher 2/Modules/UI/HintService.cs @@ -90,8 +90,6 @@ internal static void Tick() ModMain.frmMain.PanHint.Children.Insert(0, toast); toast.Show(); - System.IO.File.AppendAllText(@"C:\Users\imagi\AppData\Local\Temp\opencode\toast-debug.log", - $"SHOW uuid={toast.Uuid} dur={toast.DisplayDuration} active={activeCount} tick={PCL.Core.Utils.TimeUtils.GetTimeTick()}\n"); RearrangeToasts(); if (currentHint.Log) @@ -116,8 +114,6 @@ internal static void RearrangeToasts() var required = toasts.Sum(t => t._targetHeight + ToastGap) + ToastGap; if (required > available) { - System.IO.File.AppendAllText(@"C:\Users\imagi\AppData\Local\Temp\opencode\toast-debug.log", - $"OVERFLOW dismiss toast={toasts[^1].Uuid} avail={available:0.0} req={required:0.0} tick={PCL.Core.Utils.TimeUtils.GetTimeTick()}\n"); toasts[^1].Dismiss(); // 超出可用高度时收掉最旧的弹窗,保证其余完整可见 return; } diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml index 7b767e30ad..7c31f83870 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml @@ -154,37 +154,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml.cs b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml.cs index a95aa0dbc5..ea201c281a 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml.cs +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLauncherMisc.xaml.cs @@ -182,42 +182,6 @@ private void ComboSystemActivity_SelectionChanged(object sender, SelectionChange 2) ComboSystemActivity.SelectedItem = e.RemovedItems[0]; } - // 测试弹窗(调试选项) - private void BtnToastTest_Click(object sender, MouseButtonEventArgs e) - { - var text = TextToastTestContent.Text; - if (string.IsNullOrWhiteSpace(text)) - { - HintService.Hint("请输入要测试的弹窗内容。", HintType.Warning, false); - return; - } - - var type = ComboToastTestType.SelectedIndex switch - { - 1 => HintType.Success, - 2 => HintType.Error, - 3 => HintType.Warning, - _ => HintType.Info - }; - HintService.Hint(text, type); - } - - // 连续触发多条弹窗(调试选项) - private void BtnToastTestBatch_Click(object sender, MouseButtonEventArgs e) - { - const int batchCount = 6; - var baseText = TextToastTestContent.Text; - if (string.IsNullOrWhiteSpace(baseText)) - { - HintService.Hint("请输入要测试的弹窗内容。", HintType.Warning, false); - return; - } - - var types = new[] { HintType.Info, HintType.Success, HintType.Error, HintType.Warning }; - for (var i = 0; i < batchCount; i++) - HintService.Hint($"[{i + 1}/{batchCount}] {baseText}", types[i % types.Length], log: false); - } - private void CheckDebugMode_OnChange(object sender, bool user) { CheckBoxChange(sender, user);