diff --git a/ContextMenuManager/BluePointLilac.Controls/DarkModeHelper.cs b/ContextMenuManager/BluePointLilac.Controls/DarkModeHelper.cs index 2540c3f1..e2045787 100644 --- a/ContextMenuManager/BluePointLilac.Controls/DarkModeHelper.cs +++ b/ContextMenuManager/BluePointLilac.Controls/DarkModeHelper.cs @@ -17,10 +17,85 @@ public static class DarkModeHelper [DllImport("DwmApi")] private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, int[] attrValue, int attrSize); + [DllImport("DwmApi")] + private static extern int DwmIsCompositionEnabled(out bool pfEnabled); + + [DllImport("DwmApi")] + private static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS pMarInset); + + [DllImport("DwmApi")] + private static extern int DwmGetColorizationColor(out uint pcrColorization, out bool pfOpaqueBlend); + + [StructLayout(LayoutKind.Sequential)] + private struct MARGINS + { + public int cxLeftWidth; + public int cxRightWidth; + public int cyTopHeight; + public int cyBottomHeight; + } + public static event EventHandler ThemeChanged; private static SynchronizationContext uiContext; public static Color MainColor = Color.FromArgb(255, 143, 31); + public static bool IsDwmCompositionEnabled + { + get + { + try + { + DwmIsCompositionEnabled(out bool enabled); + return enabled; + } + catch + { + return false; + } + } + } + + public static Color GetDwmColorizationColor() + { + try + { + if (!IsDwmCompositionEnabled) return Color.Empty; + DwmGetColorizationColor(out uint color, out _); + byte a = (byte)((color >> 24) & 0xFF); + byte b = (byte)((color >> 16) & 0xFF); + byte g = (byte)((color >> 8) & 0xFF); + byte r = (byte)(color & 0xFF); + return Color.FromArgb(a, r, g, b); + } + catch + { + return Color.Empty; + } + } + + public static void ExtendFrameIntoClientArea(IntPtr hwnd, int left, int right, int top, int bottom) + { + if (!IsDwmCompositionEnabled) return; + try + { + var margins = new MARGINS + { + cxLeftWidth = left, + cxRightWidth = right, + cyTopHeight = top, + cyBottomHeight = bottom + }; + DwmExtendFrameIntoClientArea(hwnd, ref margins); + } + catch { } + } + + public static void ExtendFrameIntoClientArea(IntPtr hwnd, bool extendAll) + { + if (extendAll) + ExtendFrameIntoClientArea(hwnd, -1, -1, -1, -1); + } + // 颜色属性 public static Color TitleArea { get; private set; } public static Color FormBack { get; private set; } @@ -143,12 +218,12 @@ private static void SetDarkModeColors() FormBorder = Color.FromArgb(255, 50, 50, 50); ButtonMain = Color.FromArgb(255, 55, 55, 55); ButtonSecond = Color.FromArgb(255, 38, 38, 38); - SideBarBackground = Color.FromArgb(255, 26, 26, 26); - SideBarSeparator = Color.FromArgb(255, 64, 64, 64); - SideBarHovered = Color.FromArgb(255, 51, 51, 51); - ToolBarGradientTop = Color.FromArgb(255, 128, 128, 128); - ToolBarGradientMiddle = Color.FromArgb(255, 56, 56, 56); - ToolBarGradientBottom = Color.FromArgb(255, 128, 128, 128); + SideBarBackground = Color.FromArgb(255, 30, 30, 30); + SideBarSeparator = Color.FromArgb(255, 60, 60, 60); + SideBarHovered = Color.FromArgb(255, 50, 50, 52); + ToolBarGradientTop = Color.FromArgb(255, 45, 45, 45); + ToolBarGradientMiddle = Color.FromArgb(255, 35, 35, 35); + ToolBarGradientBottom = Color.FromArgb(255, 45, 45, 45); StatusBarGradientTop = Color.FromArgb(255, 128, 128, 128); StatusBarGradientMiddle = Color.FromArgb(255, 56, 56, 56); StatusBarGradientBottom = Color.FromArgb(255, 128, 128, 128); @@ -165,18 +240,18 @@ private static void SetDarkModeColors() private static void SetLightModeColors() { - TitleArea = Color.FromArgb(255, 243, 243, 243); - FormBack = SystemColors.Control; + TitleArea = Color.FromArgb(255, 248, 248, 248); + FormBack = Color.FromArgb(255, 245, 245, 245); FormFore = SystemColors.ControlText; - FormBorder = Color.LightGray; + FormBorder = Color.FromArgb(255, 220, 220, 220); ButtonMain = SystemColors.ControlLightLight; ButtonSecond = SystemColors.ControlLight; - SideBarBackground = SystemColors.Control; - SideBarSeparator = Color.FromArgb(255, 200, 200, 200); - SideBarHovered = Color.FromArgb(255, 230, 230, 230); - ToolBarGradientTop = Color.FromArgb(255, 255, 255, 255); - ToolBarGradientMiddle = Color.FromArgb(255, 230, 230, 230); - ToolBarGradientBottom = Color.FromArgb(255, 255, 255, 255); + SideBarBackground = Color.FromArgb(255, 250, 250, 250); + SideBarSeparator = Color.FromArgb(255, 230, 230, 230); + SideBarHovered = Color.FromArgb(255, 240, 240, 242); + ToolBarGradientTop = Color.FromArgb(255, 252, 252, 252); + ToolBarGradientMiddle = Color.FromArgb(255, 245, 245, 245); + ToolBarGradientBottom = Color.FromArgb(255, 252, 252, 252); StatusBarGradientTop = Color.FromArgb(255, 255, 255, 255); StatusBarGradientMiddle = Color.FromArgb(255, 230, 230, 230); StatusBarGradientBottom = Color.FromArgb(255, 255, 255, 255); diff --git a/ContextMenuManager/BluePointLilac.Controls/LoadingDialog.cs b/ContextMenuManager/BluePointLilac.Controls/LoadingDialog.cs index 55d044d7..5fbfedf9 100644 --- a/ContextMenuManager/BluePointLilac.Controls/LoadingDialog.cs +++ b/ContextMenuManager/BluePointLilac.Controls/LoadingDialog.cs @@ -9,14 +9,16 @@ Apache License Version 2.0 using System.Drawing.Drawing2D; using System.Threading; using System.Windows.Forms; -using Timer = System.Timers.Timer; namespace BluePointLilac.Controls { public sealed partial class LoadingDialog : Form { - private readonly Thread _workThread; + private const int CsDropShadow = 0x20000, DefaultWidth = 408, DefaultHeight = 45; + private const int ProgressBarWidth = 391, ProgressBarHeight = 25, PaddingH = 8, PaddingV = 10; + private readonly LoadingDialogInterface _controller; + private readonly Action _action; private Point _offset; private ContentAlignment _ownerAlignment; private bool _startAutomatically; @@ -30,21 +32,14 @@ internal LoadingDialog(string title, Action action) ForeColor = DarkModeHelper.FormFore; BackColor = DarkModeHelper.FormBack; UseWaitCursor = true; - + _action = action; _controller = new LoadingDialogInterface(this); - _workThread = new Thread(() => ExecuteAction(action)) { Name = "LoadingDialogThread - " + title }; - DarkModeHelper.ThemeChanged += OnThemeChanged; } protected override CreateParams CreateParams { - get - { - var cp = base.CreateParams; - cp.ClassStyle |= 0x20000; // csDropshadow - return cp; - } + get { var cp = base.CreateParams; cp.ClassStyle |= CsDropShadow; return cp; } } public static Form DefaultOwner { get; set; } @@ -54,6 +49,7 @@ protected override CreateParams CreateParams protected override void OnShown(EventArgs e) { base.OnShown(e); + _controller.SignalDialogReady(); if (Owner != null) { Owner.Move += OwnerOnMove; @@ -65,47 +61,37 @@ protected override void OnShown(EventArgs e) protected override void OnClosing(CancelEventArgs e) { - if (Owner != null) - { - Owner.Move -= OwnerOnMove; - Owner.Resize -= OwnerOnMove; - } + if (Owner != null) { Owner.Move -= OwnerOnMove; Owner.Resize -= OwnerOnMove; } base.OnClosing(e); } - protected override void OnFormClosed(FormClosedEventArgs e) - { - _controller.Abort = true; - base.OnFormClosed(e); - } + protected override void OnFormClosed(FormClosedEventArgs e) { _controller.Abort = true; base.OnFormClosed(e); } private void OwnerOnMove(object sender, EventArgs e) { if (Owner == null) return; - var newPos = CalculatePosition(); - newPos.X += _offset.X; - newPos.Y += _offset.Y; - Location = newPos; + var pos = CalculatePosition(); + Location = new Point(pos.X + _offset.X, pos.Y + _offset.Y); } private Point CalculatePosition() { - var pos = Point.Empty; - var ownerAlignment = _ownerAlignment.ToString(); - - if (ownerAlignment.Contains("Middle")) - pos.Y = Owner.Location.Y + Owner.Size.Height / 2 - Size.Height / 2; - else if (ownerAlignment.Contains("Bottom")) - pos.Y = Owner.Location.Y + Owner.Size.Height - Size.Height; - - if (ownerAlignment.Contains("Center")) - pos.X = Owner.Location.X + Owner.Size.Width / 2 - Size.Width / 2; - else if (ownerAlignment.Contains("Right")) - pos.X = Owner.Location.X + Owner.Size.Width - Size.Width; - - return pos; + var r = Owner.Bounds; + var s = Size; + var (h, v) = GetAlignment(_ownerAlignment); + return new Point( + h == 0 ? r.X : h == 1 ? r.X + (r.Width - s.Width) / 2 : r.Right - s.Width, + v == 0 ? r.Y : v == 1 ? r.Y + (r.Height - s.Height) / 2 : r.Bottom - s.Height); } + private static (int, int) GetAlignment(ContentAlignment a) => a switch + { + ContentAlignment.TopLeft => (0, 0), ContentAlignment.TopCenter => (1, 0), ContentAlignment.TopRight => (2, 0), + ContentAlignment.MiddleLeft => (0, 1), ContentAlignment.MiddleCenter => (1, 1), ContentAlignment.MiddleRight => (2, 1), + ContentAlignment.BottomLeft => (0, 2), ContentAlignment.BottomCenter => (1, 2), ContentAlignment.BottomRight => (2, 2), + _ => (1, 1) + }; + public static LoadingDialog Show(Form owner, string title, Action action, Point offset = default, ContentAlignment ownerAlignment = ContentAlignment.MiddleCenter) { @@ -126,40 +112,22 @@ public static Exception ShowDialog(Form owner, string title, Action action, Point offset, ContentAlignment alignment) - { - return new LoadingDialog(title, action) - { - _offset = offset, - _ownerAlignment = alignment, - Owner = owner, - StartPosition = FormStartPosition.Manual - }; - } + => new(title, action) { _offset = offset, _ownerAlignment = alignment, Owner = owner, StartPosition = FormStartPosition.Manual }; private static Form GetTopmostOwner(Form owner) { - if (owner == null) owner = DefaultOwner; - while (owner != null && owner.OwnedForms.Length > 0) - owner = owner.OwnedForms[0]; + owner ??= DefaultOwner; + while (owner?.OwnedForms.Length > 0) owner = owner.OwnedForms[0]; return owner; } - public void StartWork() - { - _workThread.Start(); - } - - private void panel1_Resize(object sender, EventArgs e) - { - Size = panel1.Size; - } + public void StartWork() => ThreadPool.QueueUserWorkItem(static state => ((LoadingDialog)state!).ExecuteAction(), this); + private void panel1_Resize(object sender, EventArgs e) => Size = panel1.Size; - private void ExecuteAction(Action action) + private void ExecuteAction() { _controller.WaitTillDialogIsReady(); - try { action(_controller); } - catch (Exception ex) { Error = ex; } - _controller.CloseDialog(); + try { _action(_controller); } catch (Exception ex) { Error = ex; } finally { _controller.CloseDialog(); } } private void OnThemeChanged(object sender, EventArgs e) @@ -178,8 +146,8 @@ private void InitializeComponent() panel1.SuspendLayout(); SuspendLayout(); - progressBar.Location = new Point(8, 10); - progressBar.Size = new Size(391, 25); + progressBar.Location = new Point(PaddingH, PaddingV); + progressBar.Size = new Size(ProgressBarWidth, ProgressBarHeight); progressBar.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; panel1.AutoSize = true; @@ -187,14 +155,14 @@ private void InitializeComponent() panel1.BorderStyle = BorderStyle.FixedSingle; panel1.Controls.Add(progressBar); panel1.Dock = DockStyle.Fill; - panel1.MinimumSize = new Size(408, 45); - panel1.Padding = new Padding(8, 10, 8, 10); - panel1.Size = new Size(408, 45); + panel1.MinimumSize = new Size(DefaultWidth, DefaultHeight); + panel1.Padding = new Padding(PaddingH, PaddingV, PaddingH, PaddingV); + panel1.Size = new Size(DefaultWidth, DefaultHeight); panel1.Resize += panel1_Resize; AutoScaleDimensions = new SizeF(7F, 17F); AutoScaleMode = AutoScaleMode.Font; - ClientSize = new Size(408, 45); + ClientSize = new Size(DefaultWidth, DefaultHeight); Controls.Add(panel1); FormBorderStyle = FormBorderStyle.None; ShowInTaskbar = false; @@ -209,6 +177,7 @@ protected override void Dispose(bool disposing) if (disposing) { DarkModeHelper.ThemeChanged -= OnThemeChanged; + _controller?.Dispose(); progressBar?.Dispose(); panel1?.Dispose(); } @@ -216,205 +185,168 @@ protected override void Dispose(bool disposing) } } - public sealed class LoadingDialogInterface + public sealed class LoadingDialogInterface : IDisposable { - private readonly Timer _updateTimer = new() { Interval = 35 }; - private Action _lastProgressUpdate; + private const int UpdateInterval = 35; + private const byte OpNone = 0, OpProgress = 1, OpMaximum = 2, OpMinimum = 3; + + private readonly System.Windows.Forms.Timer _updateTimer; + private int _pendingValue, _pendingMinOrMax; + private byte _pendingOperation; + private readonly ManualResetEventSlim _dialogReadyEvent = new(false); + private bool _disposed; internal LoadingDialogInterface(LoadingDialog dialog) { Dialog = dialog; - _updateTimer.SynchronizingObject = Dialog; - _updateTimer.Elapsed += (s, e) => - Interlocked.Exchange(ref _lastProgressUpdate, null)?.Invoke(); + _updateTimer = new System.Windows.Forms.Timer { Interval = UpdateInterval }; + _updateTimer.Tick += OnTimerTick; _updateTimer.Start(); - dialog.Disposed += (s, e) => _updateTimer.Dispose(); } - public bool Abort { get; internal set; } + public volatile bool Abort; private LoadingDialog Dialog { get; } + internal void SignalDialogReady() => _dialogReadyEvent.Set(); + public void CloseDialog() { - _updateTimer.Dispose(); + _updateTimer.Stop(); SafeInvoke(() => { if (!Dialog.IsDisposed) Dialog.Close(); }); } - public void SetMaximum(int value) + public void SetMaximum(int value) { _pendingMinOrMax = value; Volatile.Write(ref _pendingOperation, OpMaximum); } + public void SetMinimum(int value) { _pendingMinOrMax = value; Volatile.Write(ref _pendingOperation, OpMinimum); } + public void SetProgress(int value, bool forceNoAnimation = false) { - SafeInvoke(() => UpdateProgressBar(pb => pb.Maximum = value)); + _pendingValue = (value << 1) | (forceNoAnimation ? 1 : 0); + Volatile.Write(ref _pendingOperation, OpProgress); } + public void SetTitle(string newTitle) => SafeInvoke(() => Dialog.Text = newTitle); - public void SetMinimum(int value) - { - SafeInvoke(() => UpdateProgressBar(pb => pb.Minimum = value)); - } + internal void WaitTillDialogIsReady() => _dialogReadyEvent.Wait(); - public void SetProgress(int value, string description = null, bool forceNoAnimation = false) + private void OnTimerTick(object sender, EventArgs e) { - _lastProgressUpdate = () => UpdateProgressBar(pb => ApplyProgressValue(pb, value, forceNoAnimation)); - } + var op = Volatile.Read(ref _pendingOperation); + if (op == OpNone) return; + Volatile.Write(ref _pendingOperation, OpNone); - public void SetTitle(string newTitle) - { - SafeInvoke(() => Dialog.Text = newTitle); - } + var pb = Dialog.ProgressBar; + if (pb == null || pb.IsDisposed) return; - internal void WaitTillDialogIsReady() - { - var notReady = true; - while (notReady) + if (op == OpProgress) { - SafeInvoke(() => notReady = !Dialog.Visible); - Thread.Sleep(10); + var packed = _pendingValue; + ApplyProgressValue(pb, packed >> 1, (packed & 1) == 1); } + else if (op == OpMaximum) pb.Maximum = _pendingMinOrMax; + else if (op == OpMinimum) pb.Minimum = _pendingMinOrMax; } - private void ApplyProgressValue(ProgressBar pb, int value, bool forceNoAnimation) + private static void ApplyProgressValue(ProgressBar pb, int value, bool forceNoAnimation) { try { if (pb.Value == value) return; - if (value < pb.Minimum || value > pb.Maximum) - pb.Style = ProgressBarStyle.Marquee; - else - { - pb.Style = ProgressBarStyle.Blocks; - if (forceNoAnimation && value < pb.Maximum) - pb.Value = value + 1; - pb.Value = value; - } + if (value < pb.Minimum || value > pb.Maximum) { pb.Style = ProgressBarStyle.Marquee; return; } + pb.Style = ProgressBarStyle.Blocks; + if (forceNoAnimation && value < pb.Maximum) pb.Value = value + 1; + pb.Value = value; } catch { pb.Style = ProgressBarStyle.Marquee; } } - private void UpdateProgressBar(Action action) - { - var pb = Dialog.ProgressBar; - if (pb != null && !pb.IsDisposed) action(pb); - } - private void SafeInvoke(Action action) { if (Dialog.IsDisposed || Dialog.Disposing) return; - if (Dialog.InvokeRequired) - { - try { Dialog.Invoke(action); } - catch { /* 忽略调用异常 */ } - } + if (Dialog.InvokeRequired) { try { Dialog.Invoke(action); } catch { } } else action(); } + + public void Dispose() + { + if (_disposed) return; + _disposed = true; + _updateTimer?.Dispose(); + _dialogReadyEvent?.Dispose(); + } } public class NewProgressBar : ProgressBar { + private const int Inset = 1, CornerRadius = 6; + + private static readonly Color Fg1 = Color.FromArgb(255, 195, 0), Fg2 = Color.FromArgb(255, 140, 26), Fg3 = Color.FromArgb(255, 195, 0); + private static readonly Color[] FgColors = { Fg1, Fg2, Fg3 }; + private static readonly Color[] DarkBg = { Color.FromArgb(80, 80, 80), Color.FromArgb(60, 60, 60), Color.FromArgb(80, 80, 80) }; + private static readonly Color[] LightBg = { Color.FromArgb(220, 220, 220), Color.FromArgb(180, 180, 180), Color.FromArgb(220, 220, 220) }; + private static readonly float[] Positions = { 0f, 0.5f, 1f }; + private static readonly ColorBlend FgBlend = new() { Colors = FgColors, Positions = Positions }; + private static readonly ColorBlend DarkBlend = new() { Colors = DarkBg, Positions = Positions }; + private static readonly ColorBlend LightBlend = new() { Colors = LightBg, Positions = Positions }; + private static readonly Point Origin = new(0, 0); + + private ColorBlend _bgBlend; + private Color _backColor; + public NewProgressBar() { - SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | - ControlStyles.OptimizedDoubleBuffer, true); - + SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true); + UpdateColors(); DarkModeHelper.ThemeChanged += OnThemeChanged; } - protected override void OnPaintBackground(PaintEventArgs pevent) + private void UpdateColors() { - using var brush = new SolidBrush(DarkModeHelper.FormBack); - pevent.Graphics.FillRectangle(brush, pevent.ClipRectangle); + _bgBlend = DarkModeHelper.IsDarkTheme ? DarkBlend : LightBlend; + _backColor = DarkModeHelper.FormBack; } + protected override void OnPaintBackground(PaintEventArgs pevent) { } + protected override void OnPaint(PaintEventArgs e) { - const int inset = 1; - const int cornerRadius = 6; - e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; - e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - - using (var bgClearBrush = new SolidBrush(DarkModeHelper.FormBack)) - e.Graphics.FillRectangle(bgClearBrush, ClientRectangle); + var g = e.Graphics; + g.SmoothingMode = SmoothingMode.AntiAlias; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; - var rect = new Rectangle(0, 0, Width, Height); + using (var b = new SolidBrush(_backColor)) g.FillRectangle(b, ClientRectangle); - using (var bgPath = CreateRoundedRectanglePath(rect, cornerRadius)) + using (var p = CreateRoundedRect(new Rectangle(0, 0, Width, Height), CornerRadius)) + using (var b = new LinearGradientBrush(Origin, new Point(0, Height), _bgBlend.Colors[0], _bgBlend.Colors[2])) { - Color bgColor1, bgColor2, bgColor3; - - if (DarkModeHelper.IsDarkTheme) - { - bgColor1 = Color.FromArgb(80, 80, 80); - bgColor2 = Color.FromArgb(60, 60, 60); - bgColor3 = Color.FromArgb(80, 80, 80); - } - else - { - bgColor1 = Color.FromArgb(220, 220, 220); - bgColor2 = Color.FromArgb(180, 180, 180); - bgColor3 = Color.FromArgb(220, 220, 220); - } - - using var bgBrush = new LinearGradientBrush( - new Point(0, rect.Top), new Point(0, rect.Bottom), bgColor1, bgColor3); - bgBrush.InterpolationColors = new ColorBlend - { - Colors = new[] { bgColor1, bgColor2, bgColor3 }, - Positions = new[] { 0f, 0.5f, 1f } - }; - e.Graphics.FillPath(bgBrush, bgPath); + b.InterpolationColors = _bgBlend; + g.FillPath(b, p); } - var progressWidth = (int)((Width - 2 * inset) * ((double)Value / Maximum)); + if (Maximum <= 0) return; + var w = (int)((Width - 2 * Inset) * ((double)Value / Maximum)); + if (w <= 0) return; - if (progressWidth > 0) - { - var progressRect = new Rectangle(inset, inset, progressWidth, Height - 2 * inset); - if (progressWidth < cornerRadius * 2) - progressRect.Width = Math.Max(progressWidth, cornerRadius); - - var fgColor1 = Color.FromArgb(255, 195, 0); - var fgColor2 = Color.FromArgb(255, 140, 26); - var fgColor3 = Color.FromArgb(255, 195, 0); - - using var fgBrush = new LinearGradientBrush( - new Point(0, progressRect.Top), new Point(0, progressRect.Bottom), fgColor1, fgColor3); - fgBrush.InterpolationColors = new ColorBlend - { - Colors = new[] { fgColor1, fgColor2, fgColor3 }, - Positions = new[] { 0f, 0.5f, 1f } - }; - - using var progressPath = CreateRoundedRectanglePath(progressRect, cornerRadius - 1); - e.Graphics.FillPath(fgBrush, progressPath); - } + var rect = new Rectangle(Inset, Inset, Math.Max(w, CornerRadius), Height - 2 * Inset); + using var fp = CreateRoundedRect(rect, CornerRadius - 1); + using var fb = new LinearGradientBrush(new Point(0, rect.Top), new Point(0, rect.Bottom), Fg1, Fg3); + fb.InterpolationColors = FgBlend; + g.FillPath(fb, fp); } - private GraphicsPath CreateRoundedRectanglePath(Rectangle rect, int radius) + private static GraphicsPath CreateRoundedRect(Rectangle r, int rad) { var path = new GraphicsPath(); - if (radius <= 0) - { - path.AddRectangle(rect); - return path; - } - - radius = Math.Min(radius, Math.Min(rect.Width, rect.Height) / 2); - var diameter = radius * 2; - - path.AddArc(rect.X, rect.Y, diameter, diameter, 180, 90); - path.AddArc(rect.Right - diameter, rect.Y, diameter, diameter, 270, 90); - path.AddArc(rect.Right - diameter, rect.Bottom - diameter, diameter, diameter, 0, 90); - path.AddArc(rect.X, rect.Bottom - diameter, diameter, diameter, 90, 90); + if (rad <= 0) { path.AddRectangle(r); return path; } + + rad = Math.Min(rad, Math.Min(r.Width, r.Height) / 2); + var d = rad * 2; + path.AddArc(r.X, r.Y, d, d, 180, 90); + path.AddArc(r.Right - d, r.Y, d, d, 270, 90); + path.AddArc(r.Right - d, r.Bottom - d, d, d, 0, 90); + path.AddArc(r.X, r.Bottom - d, d, d, 90, 90); path.CloseFigure(); return path; } - private void OnThemeChanged(object sender, EventArgs e) - { - Invalidate(); - } - - protected override void Dispose(bool disposing) - { - if (disposing) DarkModeHelper.ThemeChanged -= OnThemeChanged; - base.Dispose(disposing); - } + private void OnThemeChanged(object sender, EventArgs e) { UpdateColors(); Invalidate(); } + protected override void Dispose(bool disposing) { if (disposing) DarkModeHelper.ThemeChanged -= OnThemeChanged; base.Dispose(disposing); } } -} \ No newline at end of file +} diff --git a/ContextMenuManager/BluePointLilac.Controls/MyListBox.cs b/ContextMenuManager/BluePointLilac.Controls/MyListBox.cs index c0f7d1ae..9e20de38 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MyListBox.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MyListBox.cs @@ -12,20 +12,12 @@ public MyListBox() { AutoScroll = true; UpdateColors(); - - // 订阅主题变化事件 DarkModeHelper.ThemeChanged += OnThemeChanged; } - protected override void OnMouseWheel(MouseEventArgs e) - { - // 使滚动幅度与MyListItem的高度相配合,防止滚动过快导致来不及重绘界面变花 + protected override void OnMouseWheel(MouseEventArgs e) => base.OnMouseWheel(new MouseEventArgs(e.Button, e.Clicks, e.X, e.Y, Math.Sign(e.Delta) * 50.DpiZoom())); - } - /// - /// 主题变化事件处理 - /// private void OnThemeChanged(object sender, EventArgs e) { if (IsHandleCreated && !IsDisposed) @@ -35,40 +27,33 @@ private void OnThemeChanged(object sender, EventArgs e) } } - /// - /// 更新控件颜色 - /// public void UpdateColors() { BackColor = DarkModeHelper.FormBack; ForeColor = DarkModeHelper.FormFore; } - /// - /// 清理资源 - /// protected override void Dispose(bool disposing) { - if (disposing) - { - DarkModeHelper.ThemeChanged -= OnThemeChanged; - } + if (disposing) DarkModeHelper.ThemeChanged -= OnThemeChanged; base.Dispose(disposing); } } public class MyList : FlowLayoutPanel { + private MyListItem hoveredItem; + private static readonly FontStyle RegularStyle = FontStyle.Regular; + private static readonly FontStyle BoldStyle = FontStyle.Bold; + private readonly Dictionary _resizeHandlers = new(); + public MyListBox Owner { get => (MyListBox)Parent; set => Parent = value; } - public MyList(MyListBox owner) : this() - { - Owner = owner; - } + public MyList(MyListBox owner) : this() => Owner = owner; public MyList() { @@ -77,12 +62,9 @@ public MyList() Dock = DockStyle.Top; DoubleBuffered = true; AutoSizeMode = AutoSizeMode.GrowAndShrink; - - // 订阅主题变化事件 DarkModeHelper.ThemeChanged += OnThemeChanged; } - private MyListItem hoveredItem; public MyListItem HoveredItem { get => hoveredItem; @@ -92,13 +74,15 @@ public MyListItem HoveredItem if (hoveredItem != null) { hoveredItem.ForeColor = DarkModeHelper.FormFore; - hoveredItem.Font = new Font(hoveredItem.Font, FontStyle.Regular); + if (hoveredItem.Font.Style != RegularStyle) + hoveredItem.Font = new Font(hoveredItem.Font.FontFamily, hoveredItem.Font.Size, RegularStyle); } hoveredItem = value; if (hoveredItem != null) { value.ForeColor = DarkModeHelper.MainColor; - value.Font = new Font(hoveredItem.Font, FontStyle.Bold); + if (value.Font.Style != BoldStyle) + value.Font = new Font(value.Font.FontFamily, value.Font.Size, BoldStyle); value.Focus(); } HoveredItemChanged?.Invoke(this, null); @@ -111,36 +95,57 @@ public void AddItem(MyListItem item) { SuspendLayout(); item.Parent = this; - item.MouseEnter += (sender, e) => HoveredItem = item; - MouseWheel += (sender, e) => item.ContextMenuStrip?.Close(); - void ResizeItem() - { - item.Width = Owner.Width - item.Margin.Horizontal; - } + item.MouseEnter += OnItemMouseEnter; + MouseWheel += OnItemMouseWheel; - Owner.Resize += (sender, e) => ResizeItem(); - ResizeItem(); + EventHandler handler = (s, e) => item.Width = Owner.Width - item.Margin.Horizontal; + _resizeHandlers[item] = handler; + Owner.Resize += handler; + item.Width = Owner.Width - item.Margin.Horizontal; ResumeLayout(); } - public void AddItems(MyListItem[] items) + private void OnItemMouseEnter(object sender, EventArgs e) { - Array.ForEach(items, item => AddItem(item)); + if (sender is MyListItem item) HoveredItem = item; } - public void AddItems(List items) + private void OnItemMouseWheel(object sender, MouseEventArgs e) { - items.ForEach(item => AddItem(item)); + if (HoveredItem?.ContextMenuStrip != null) HoveredItem.ContextMenuStrip.Close(); } - public void SetItemIndex(MyListItem item, int newIndex) + public void AddItems(MyListItem[] items) => AddItemsCore(items); + public void AddItems(List items) => AddItemsCore(items); + + private void AddItemsCore(IEnumerable items) { - Controls.SetChildIndex(item, newIndex); + Owner?.SuspendLayout(); + SuspendLayout(); + try { foreach (var item in items) AddItem(item); } + finally { ResumeLayout(); Owner?.ResumeLayout(); } } - public int GetItemIndex(MyListItem item) + public void SetItemIndex(MyListItem item, int newIndex) => Controls.SetChildIndex(item, newIndex); + public int GetItemIndex(MyListItem item) => Controls.GetChildIndex(item); + + public void RemoveItem(MyListItem item) { - return Controls.GetChildIndex(item); + if (item == null || !Controls.Contains(item)) return; + SuspendLayout(); + try + { + item.MouseEnter -= OnItemMouseEnter; + if (_resizeHandlers.TryGetValue(item, out var handler)) + { + Owner.Resize -= handler; + _resizeHandlers.Remove(item); + } + if (hoveredItem == item) hoveredItem = null; + Controls.Remove(item); + item.Dispose(); + } + finally { ResumeLayout(); } } public void InsertItem(MyListItem item, int index) @@ -153,14 +158,27 @@ public void InsertItem(MyListItem item, int index) public virtual void ClearItems() { if (Controls.Count == 0) return; + Owner?.SuspendLayout(); SuspendLayout(); - for (var i = Controls.Count - 1; i >= 0; i--) + try { - var ctr = Controls[i]; - Controls.Remove(ctr); - ctr.Dispose(); + foreach (MyListItem item in Controls) + { + item.MouseEnter -= OnItemMouseEnter; + if (_resizeHandlers.TryGetValue(item, out var handler)) + { + Owner.Resize -= handler; + _resizeHandlers.Remove(item); + } + } + while (Controls.Count > 0) + { + var ctr = Controls[0]; + Controls.RemoveAt(0); + ctr.Dispose(); + } } - ResumeLayout(); + finally { ResumeLayout(); Owner?.ResumeLayout(); } } public void SortItemByText() @@ -168,45 +186,24 @@ public void SortItemByText() var items = new List(); foreach (MyListItem item in Controls) items.Add(item); Controls.Clear(); - items.Sort(new TextComparer()); - items.ForEach(item => AddItem(item)); - } - - public class TextComparer : IComparer - { - public int Compare(MyListItem x, MyListItem y) - { - if (x.Equals(y)) return 0; - string[] strs = { x.Text, y.Text }; - Array.Sort(strs); - if (strs[0] == x.Text) return -1; - else return 1; - } + items.Sort((x, y) => string.Compare(x.Text, y.Text, StringComparison.CurrentCulture)); + items.ForEach(AddItem); } - /// - /// 主题变化事件处理 - /// private void OnThemeChanged(object sender, EventArgs e) { - if (IsHandleCreated && !IsDisposed) - { - // 更新悬停项的颜色 - if (hoveredItem != null && hoveredItem.IsHandleCreated && !hoveredItem.IsDisposed) - { - hoveredItem.ForeColor = DarkModeHelper.MainColor; - } - } + if (IsHandleCreated && !IsDisposed && hoveredItem?.IsHandleCreated == true && !hoveredItem.IsDisposed) + hoveredItem.ForeColor = DarkModeHelper.MainColor; } - /// - /// 清理资源 - /// protected override void Dispose(bool disposing) { if (disposing) { DarkModeHelper.ThemeChanged -= OnThemeChanged; + foreach (var kvp in _resizeHandlers) Owner.Resize -= kvp.Value; + _resizeHandlers.Clear(); + MouseWheel -= OnItemMouseWheel; } base.Dispose(disposing); } @@ -214,65 +211,67 @@ protected override void Dispose(bool disposing) public class MyListItem : Panel { - private string subText; // 添加SubText字段 + private string subText; + private bool hasImage = true; + + private readonly Label lblText = new() { AutoSize = true, Left = 60.DpiZoom(), Name = "Text" }; + private readonly PictureBox picImage = new() + { + SizeMode = PictureBoxSizeMode.AutoSize, + Left = 20.DpiZoom(), + Enabled = false, + Name = "Image" + }; + private readonly FlowLayoutPanel flpControls = new() + { + AutoSizeMode = AutoSizeMode.GrowAndShrink, + FlowDirection = FlowDirection.RightToLeft, + Anchor = AnchorStyles.Right, + AutoSize = true, + Name = "Controls" + }; + private readonly Label lblSeparator = new() + { + BackColor = DarkModeHelper.FormFore, + Dock = DockStyle.Bottom, + Name = "Separator", + Height = 1 + }; + private readonly Panel pnlScrollbar = new() + { + Width = SystemInformation.VerticalScrollBarWidth, + Enabled = false + }; public MyListItem() { SuspendLayout(); - HasImage = true; DoubleBuffered = true; Height = 50.DpiZoom(); Margin = new Padding(0); Font = SystemFonts.IconTitleFont; - - // 初始化颜色 UpdateColors(); - Controls.AddRange(new Control[] { lblSeparator, flpControls, lblText, picImage }); - Resize += (Sender, e) => pnlScrollbar.Height = ClientSize.Height; - flpControls.MouseClick += (sender, e) => OnMouseClick(e); - flpControls.MouseEnter += (sender, e) => OnMouseEnter(e); - flpControls.MouseDown += (sender, e) => OnMouseDown(e); + Controls.AddRange(new Control[] { lblSeparator, flpControls, picImage, lblText }); + Resize += (s, e) => pnlScrollbar.Height = ClientSize.Height; + flpControls.MouseClick += (s, e) => OnMouseClick(e); + flpControls.MouseEnter += (s, e) => OnMouseEnter(e); + flpControls.MouseDown += (s, e) => OnMouseDown(e); lblSeparator.SetEnabled(false); lblText.SetEnabled(false); CenterControl(lblText); CenterControl(picImage); AddCtr(pnlScrollbar, 0); - - // 订阅主题变化事件 DarkModeHelper.ThemeChanged += OnThemeChanged; - ResumeLayout(); } - public Image Image - { - get => picImage.Image; - set => picImage.Image = value; - } - public new string Text - { - get => lblText.Text; - set => lblText.Text = value; - } - public new Font Font - { - get => lblText.Font; - set => lblText.Font = value; - } - public new Color ForeColor - { - get => lblText.ForeColor; - set => lblText.ForeColor = value; - } - - // 添加SubText属性 - public string SubText - { - get => subText; set => subText = value;// 如果需要显示副文本,可以在这里添加UI更新逻辑 - } + public Image Image { get => picImage.Image; set => picImage.Image = value; } + public new string Text { get => lblText.Text; set => lblText.Text = value; } + public new Font Font { get => lblText.Font; set => lblText.Font = value; } + public new Color ForeColor { get => lblText.ForeColor; set => lblText.ForeColor = value; } + public string SubText { get => subText; set => subText = value; } - private bool hasImage; public bool HasImage { get => hasImage; @@ -284,100 +283,45 @@ public bool HasImage } } - private readonly Label lblText = new() - { - AutoSize = true, - Name = "Text" - }; - private readonly PictureBox picImage = new() - { - SizeMode = PictureBoxSizeMode.AutoSize, - Left = 20.DpiZoom(), - Enabled = false, - Name = "Image" - }; - private readonly FlowLayoutPanel flpControls = new() - { - AutoSizeMode = AutoSizeMode.GrowAndShrink, - FlowDirection = FlowDirection.RightToLeft, - Anchor = AnchorStyles.Right, - AutoSize = true, - Name = "Controls" - }; - private readonly Label lblSeparator = new() - { - BackColor = DarkModeHelper.FormFore, - Dock = DockStyle.Bottom, - Name = "Separator", - Height = 1 - };//分割线 - private readonly Panel pnlScrollbar = new() - { - Width = SystemInformation.VerticalScrollBarWidth, - Enabled = false - };//预留滚动条宽度 - protected override void OnMouseDown(MouseEventArgs e) { - base.OnMouseDown(e); OnMouseEnter(null); + base.OnMouseDown(e); + OnMouseEnter(null); } private void CenterControl(Control ctr) { - void reSize() + void Resize() { if (ctr.Parent == null) return; var top = (ClientSize.Height - ctr.Height) / 2; ctr.Top = top; if (ctr.Parent == flpControls) - { ctr.Margin = new Padding(0, top, ctr.Margin.Right, top); - } } - ctr.Parent.Resize += (sender, e) => reSize(); - ctr.Resize += (sender, e) => reSize(); - reSize(); + ctr.Parent.Resize += (s, e) => Resize(); + ctr.Resize += (s, e) => Resize(); + Resize(); } - public void AddCtr(Control ctr) - { - AddCtr(ctr, 20.DpiZoom()); - } + public void AddCtr(Control ctr) => AddCtr(ctr, 20.DpiZoom()); public void AddCtr(Control ctr, int space) { SuspendLayout(); ctr.Parent = flpControls; ctr.Margin = new Padding(0, 0, space, 0); - ctr.MouseEnter += (sender, e) => OnMouseEnter(e); - ctr.MouseDown += (sender, e) => OnMouseEnter(e); + ctr.MouseEnter += (s, e) => OnMouseEnter(e); + ctr.MouseDown += (s, e) => OnMouseEnter(e); CenterControl(ctr); ResumeLayout(); } - public void AddCtrs(Control[] ctrs) - { - Array.ForEach(ctrs, ctr => AddCtr(ctr)); - } - - public void RemoveCtrAt(int index) - { - if (flpControls.Controls.Count > index) flpControls.Controls.RemoveAt(index + 1); - } - - public int GetCtrIndex(Control ctr) - { - return flpControls.Controls.GetChildIndex(ctr, true) - 1; - } + public void AddCtrs(Control[] ctrs) => Array.ForEach(ctrs, AddCtr); + public void RemoveCtrAt(int index) { if (flpControls.Controls.Count > index) flpControls.Controls.RemoveAt(index + 1); } + public int GetCtrIndex(Control ctr) => flpControls.Controls.GetChildIndex(ctr, true) - 1; + public void SetCtrIndex(Control ctr, int newIndex) => flpControls.Controls.SetChildIndex(ctr, newIndex + 1); - public void SetCtrIndex(Control ctr, int newIndex) - { - flpControls.Controls.SetChildIndex(ctr, newIndex + 1); - } - - /// - /// 主题变化事件处理 - /// private void OnThemeChanged(object sender, EventArgs e) { if (IsHandleCreated && !IsDisposed) @@ -387,29 +331,18 @@ private void OnThemeChanged(object sender, EventArgs e) } } - /// - /// 更新控件颜色 - /// public void UpdateColors() { BackColor = DarkModeHelper.FormBack; ForeColor = DarkModeHelper.FormFore; - - // 更新子控件颜色 lblSeparator.BackColor = DarkModeHelper.FormFore; lblText.ForeColor = DarkModeHelper.FormFore; } - /// - /// 清理资源 - /// protected override void Dispose(bool disposing) { - if (disposing) - { - DarkModeHelper.ThemeChanged -= OnThemeChanged; - } + if (disposing) DarkModeHelper.ThemeChanged -= OnThemeChanged; base.Dispose(disposing); } } -} \ No newline at end of file +} diff --git a/ContextMenuManager/BluePointLilac.Controls/MyMainForm.cs b/ContextMenuManager/BluePointLilac.Controls/MyMainForm.cs index 76b97b0b..372fc8dd 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MyMainForm.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MyMainForm.cs @@ -1,4 +1,4 @@ -using BluePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.Drawing; using System.Windows.Forms; @@ -27,11 +27,38 @@ public MyMainForm() DarkModeHelper.Initialize(); DarkModeHelper.ThemeChanged += OnThemeChanged; DarkModeHelper.ApplyDarkModeToForm(this); + ApplyDwmEffect(); + + // 更新控件颜色(因为控件在DarkModeHelper.Initialize之前创建) + SideBar.UpdateThemeColors(); + StatusBar.UpdateThemeColors(); + Adjust(); ResumeLayout(); } + private void ApplyDwmEffect() + { + if (!DarkModeHelper.IsDwmCompositionEnabled) return; + if (!IsHandleCreated) return; + + try + { + // 扩展玻璃效果到工具栏、侧边栏和状态栏区域 + // 侧边栏宽度减去8像素,减小扩展范围 + DarkModeHelper.ExtendFrameIntoClientArea(Handle, + Math.Max(0, SideBar.Width - 8), 0, ToolBar.Height, StatusBar.Height); + } + catch { } + } + + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + ApplyDwmEffect(); + } + public readonly MyToolBar ToolBar = new(); public readonly MySideBar SideBar = new(); public readonly MyStatusBar StatusBar = new(); @@ -114,6 +141,7 @@ protected override CreateParams CreateParams private void OnThemeChanged(object sender, EventArgs e) { DarkModeHelper.ApplyDarkModeToForm(this); + ApplyDwmEffect(); Adjust(); Invalidate(); } diff --git a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs index 1d59f94a..d0785084 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs @@ -1,38 +1,48 @@ -using BluePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; +using System.Linq; using System.Windows.Forms; namespace BluePointLilac.Controls { public sealed class MySideBar : Panel { - private readonly Panel PnlSelected = new() { Enabled = false, BackColor = Color.Transparent }; - private readonly Panel PnlHovered = new() { Enabled = false, BackColor = Color.Transparent }; - private readonly Label LblSeparator = new() { Dock = DockStyle.Right, Width = 1 }; - private readonly Timer animationTimer = new() { Interval = 16 }; - + private const float AnimationSpeed = 0.25f; + private readonly Timer animTimer = new() { Interval = 16 }; private string[] itemNames; - private int itemHeight = 36; - private int selectIndex = -1; - private int hoverIndex = -1; - - private int animationTargetIndex = -1; - private int animationCurrentIndex = -1; - private float animationProgress = 0f; - private const float ANIMATION_SPEED = 0.25f; + private int itemHeight = 36, selectIndex = -1, hoverIndex = -1; + private int animTarget = -1, animCurrent = -1; + private float animProgress = 0f, curSelTop = -36; private bool isAnimating = false; + private Font ownedFont; - public Color SelectedGradientColor1 { get; set; } = Color.FromArgb(255, 195, 0); - public Color SelectedGradientColor2 { get; set; } = Color.FromArgb(255, 141, 26); - public Color SelectedGradientColor3 { get; set; } = Color.FromArgb(255, 195, 0); + public Color SelectedGradientColor1 { get; set; } = Color.FromArgb(255, 255, 160, 60); + public Color SelectedGradientColor2 { get; set; } = Color.FromArgb(255, 255, 120, 40); + public Color SelectedGradientColor3 { get; set; } = Color.FromArgb(255, 255, 160, 60); public Color BackgroundGradientColor1 { get; set; } = Color.FromArgb(240, 240, 240); public Color BackgroundGradientColor2 { get; set; } = Color.FromArgb(220, 220, 220); public Color BackgroundGradientColor3 { get; set; } = Color.FromArgb(200, 200, 200); [Browsable(false)] public bool EnableAnimation { get; set; } = true; + public int ItemHeight { get => itemHeight; set => itemHeight = Math.Max(1, value); } + public int TopSpace { get; set; } = 4.DpiZoom(); + public int HorizontalSpace { get; set; } = 20.DpiZoom(); + public int ItemMargin { get; set; } = 4.DpiZoom(); + public int CornerRadius { get; set; } = 6.DpiZoom(); + public bool IsFixedWidth { get; set; } = true; + + public Color SeparatorColor { get; set; } + public Color SelectedBackColor { get; set; } = Color.Transparent; + public Color HoveredBackColor { get; set; } + public Color SelectedForeColor { get; set; } = Color.White; + public Color HoveredForeColor { get; set; } + public Color RightBorderColor { get; set; } + + public event EventHandler SelectIndexChanged; + public event EventHandler HoverIndexChanged; public string[] ItemNames { @@ -42,33 +52,17 @@ public string[] ItemNames itemNames = value; if (value != null && !IsFixedWidth) { - var maxWidth = 0; - foreach (var str in value) - maxWidth = Math.Max(maxWidth, GetItemWidth(str)); - Width = maxWidth + 2 * HorizontalSpace; + var maxW = value.Where(s => s != null) + .Select(s => TextRenderer.MeasureText(s, Font).Width) + .DefaultIfEmpty(0) + .Max(); + Width = maxW + 2 * HorizontalSpace; } - PnlHovered.Width = PnlSelected.Width = Width; UpdateBackground(); SelectedIndex = -1; } } - public int ItemHeight { get => itemHeight; set => itemHeight = Math.Max(1, value); } - public int TopSpace { get; set; } = 4.DpiZoom(); - public int HorizontalSpace { get; set; } = 20.DpiZoom(); - public bool IsFixedWidth { get; set; } = true; - - public Color SeparatorColor { get => LblSeparator.BackColor; set => LblSeparator.BackColor = value; } - public Color SelectedBackColor { get => PnlSelected.BackColor; set => PnlSelected.BackColor = value; } - public Color HoveredBackColor { get => PnlHovered.BackColor; set => PnlHovered.BackColor = value; } - public Color SelectedForeColor { get; set; } = Color.Black; - public Color HoveredForeColor { get; set; } - - private float VerticalSpace => (itemHeight - TextRenderer.MeasureText(" ", Font).Height) * 0.5F; - - public event EventHandler SelectIndexChanged; - public event EventHandler HoverIndexChanged; - public int SelectedIndex { get => selectIndex; @@ -77,8 +71,7 @@ public int SelectedIndex if (selectIndex == value) return; if (EnableAnimation && value >= 0 && value < ItemNames?.Length && selectIndex >= 0 && !isAnimating) StartAnimation(selectIndex, value); - else - SetSelectedIndexDirectly(value); + else SetSelectedIndexDirectly(value); } } @@ -88,48 +81,59 @@ public int HoveredIndex set { if (hoverIndex == value) return; + int oldIdx = hoverIndex; hoverIndex = value; - RefreshItem(PnlHovered, value); + InvalidateItem(oldIdx); + InvalidateItem(hoverIndex); HoverIndexChanged?.Invoke(this, EventArgs.Empty); } } + private Rectangle GetItemRect(int idx) + { + if (idx < 0 || ItemNames == null || idx >= ItemNames.Length) return Rectangle.Empty; + return new Rectangle(0, TopSpace + idx * ItemHeight, Width, ItemHeight); + } + + private void InvalidateItem(int idx) => Invalidate(GetItemRect(idx)); + public MySideBar() { Dock = DockStyle.Left; MinimumSize = new Size(1, 1); BackgroundImageLayout = ImageLayout.None; DoubleBuffered = true; - Font = new Font(SystemFonts.MenuFont.FontFamily, SystemFonts.MenuFont.Size + 1F); - + ownedFont = new Font(SystemFonts.MenuFont.FontFamily, SystemFonts.MenuFont.Size + 1F); + Font = ownedFont; + SetStyle(ControlStyles.SupportsTransparentBackColor, true); InitializeColors(); - Controls.AddRange(new Control[] { LblSeparator, PnlSelected, PnlHovered }); - - SizeChanged += (sender, e) => UpdateBackground(); - PnlHovered.Paint += PaintHoveredItem; - PnlSelected.Paint += PaintSelectedItem; - animationTimer.Tick += AnimationTimer_Tick; - + SizeChanged += (s, e) => UpdateBackground(); + animTimer.Tick += AnimationTimer_Tick; DarkModeHelper.ThemeChanged += OnThemeChanged; SelectedIndex = -1; } - private void StartAnimation(int fromIndex, int toIndex) - { - animationCurrentIndex = fromIndex; - animationTargetIndex = toIndex; - animationProgress = 0f; - isAnimating = true; + private void OnThemeChanged(object sender, EventArgs e) { InitializeColors(); UpdateBackground(); } - if (!animationTimer.Enabled) - animationTimer.Start(); + public void UpdateThemeColors() { InitializeColors(); UpdateBackground(); Invalidate(); } + + private void InitializeColors() + { + BackColor = DarkModeHelper.IsDwmCompositionEnabled ? Color.Transparent : DarkModeHelper.SideBarBackground; + ForeColor = Color.White; + HoveredBackColor = DarkModeHelper.SideBarHovered; + HoveredForeColor = Color.White; + SeparatorColor = DarkModeHelper.SideBarSeparator; + RightBorderColor = DarkModeHelper.SideBarSeparator; + BackgroundGradientColor1 = DarkModeHelper.ToolBarGradientTop; + BackgroundGradientColor2 = DarkModeHelper.ToolBarGradientMiddle; + BackgroundGradientColor3 = DarkModeHelper.ToolBarGradientBottom; } private void AnimationTimer_Tick(object sender, EventArgs e) { - animationProgress += ANIMATION_SPEED; - - if (animationProgress >= 1f) + animProgress += AnimationSpeed; + if (animProgress >= 1f) CompleteAnimation(); else UpdateAnimationFrame(); @@ -137,265 +141,245 @@ private void AnimationTimer_Tick(object sender, EventArgs e) private void UpdateAnimationFrame() { - if (animationCurrentIndex < 0 || animationTargetIndex < 0) return; - - var easedProgress = EaseOutCubic(animationProgress); - var startTop = GetItemTop(animationCurrentIndex); - var targetTop = GetItemTop(animationTargetIndex); - var currentTop = Math.Max(0, Math.Min( - (int)(startTop + (targetTop - startTop) * easedProgress), Height - ItemHeight)); - - PnlSelected.Top = currentTop; - PnlSelected.Height = ItemHeight; - - if (hoverIndex == selectIndex) - { - PnlHovered.Top = PnlSelected.Top; - PnlHovered.Height = ItemHeight; - } - - Invalidate(); - Update(); + float easedProgress = CalculateEasedProgress(animProgress); + float startY = TopSpace + animCurrent * (float)ItemHeight; + float targetY = TopSpace + animTarget * (float)ItemHeight; + float oldTop = curSelTop; + curSelTop = Math.Max(0, Math.Min(startY + (targetY - startY) * easedProgress, Height - ItemHeight)); + InvalidateAnimationRegion(oldTop, curSelTop); } private void CompleteAnimation() { - animationProgress = 1f; isAnimating = false; - animationTimer.Stop(); - - SetSelectedIndexDirectly(animationTargetIndex); - PnlSelected.Top = GetItemTop(selectIndex); - - if (hoverIndex == selectIndex) - PnlHovered.Top = PnlSelected.Top; - - Refresh(); + animTimer.Stop(); + SetSelectedIndexDirectly(animTarget); } - private void SetSelectedIndexDirectly(int value) + private static float CalculateEasedProgress(float progress) { - selectIndex = value; - RefreshItem(PnlSelected, value); - HoveredIndex = value; - SelectIndexChanged?.Invoke(this, EventArgs.Empty); + return 1 - (float)Math.Pow(1 - progress, 3); } - private int GetItemTop(int index) + private void InvalidateAnimationRegion(float oldTop, float newTop) { - return TopSpace + index * ItemHeight; + int minY = Math.Max(0, (int)Math.Min(oldTop, newTop)); + int maxY = (int)Math.Max(oldTop, newTop) + ItemHeight; + Invalidate(new Rectangle(0, minY, Width, maxY - minY)); } - private float EaseOutCubic(float t) + private void StartAnimation(int from, int to) { - return 1 - (float)Math.Pow(1 - t, 3); + animCurrent = from; animTarget = to; animProgress = 0f; isAnimating = true; + if (!animTimer.Enabled) animTimer.Start(); } - public void BeginUpdate() + private void SetSelectedIndexDirectly(int val) { - SuspendLayout(); + int oldIdx = selectIndex; + selectIndex = val; + curSelTop = (val >= 0 && ItemNames != null && val < ItemNames.Length) ? TopSpace + val * ItemHeight : -ItemHeight; + InvalidateItem(oldIdx); + InvalidateItem(val); + HoveredIndex = val; + SelectIndexChanged?.Invoke(this, EventArgs.Empty); } - public void EndUpdate() { ResumeLayout(true); UpdateBackground(); } - public int GetItemWidth(string str) + public void StopAnimation() { if (isAnimating) { animTimer.Stop(); isAnimating = false; SetSelectedIndexDirectly(animTarget); } } + public void SmoothScrollTo(int idx) { if (idx >= 0 && idx < ItemNames?.Length) SelectedIndex = idx; } + public int GetItemWidth(string str) => TextRenderer.MeasureText(str, Font).Width + 2 * HorizontalSpace; + public void BeginUpdate() => SuspendLayout(); + public void EndUpdate() { - return TextRenderer.MeasureText(str, Font).Width + 2 * HorizontalSpace; + ResumeLayout(true); + UpdateBackground(); } - public void StopAnimation() + private void UpdateBackground() { - if (isAnimating) + if (ItemNames == null) return; + + if (DarkModeHelper.IsDwmCompositionEnabled) { - animationTimer.Stop(); - isAnimating = false; - SetSelectedIndexDirectly(animationTargetIndex); + BackgroundImage?.Dispose(); + BackgroundImage = null; + return; } - } - - public void SmoothScrollTo(int index) - { - if (index >= 0 && index < ItemNames?.Length) - SelectedIndex = index; - } - protected override void Dispose(bool disposing) - { - if (disposing) + int w = Math.Max(1, Width), h = ItemNames.Length == 0 ? Math.Max(1, Height) : Math.Max(Height, Math.Max(0, ItemHeight) * ItemNames.Length); + try { - DarkModeHelper.ThemeChanged -= OnThemeChanged; - animationTimer?.Stop(); - animationTimer?.Dispose(); + var old = BackgroundImage; BackgroundImage = new Bitmap(w, h); old?.Dispose(); + using var g = Graphics.FromImage(BackgroundImage); + DrawBackgroundGradient(g, w, h); + DrawTextItemsAndSeparators(g); } - base.Dispose(disposing); + catch (ArgumentException) { BackgroundImage?.Dispose(); BackgroundImage = null; } } - private void InitializeColors() + private void DrawBackgroundGradient(Graphics g, int w, int h) { - BackColor = DarkModeHelper.SideBarBackground; - ForeColor = DarkModeHelper.FormFore; - HoveredBackColor = DarkModeHelper.SideBarHovered; - SeparatorColor = DarkModeHelper.SideBarSeparator; - PnlSelected.BackColor = Color.Transparent; - - BackgroundGradientColor1 = DarkModeHelper.ToolBarGradientTop; - BackgroundGradientColor2 = DarkModeHelper.ToolBarGradientMiddle; - BackgroundGradientColor3 = DarkModeHelper.ToolBarGradientBottom; - } + if (DarkModeHelper.IsDwmCompositionEnabled) + { + g.Clear(Color.Transparent); + return; + } - private void UpdateBackground() - { - if (ItemNames == null) return; - var bgWidth = Math.Max(1, Width); - var bgHeight = ItemNames.Length == 0 ? Math.Max(1, Height) : - Math.Max(Height, Math.Max(0, ItemHeight) * ItemNames.Length); + var color1 = BackgroundGradientColor1; + var color2 = BackgroundGradientColor2; + var color3 = BackgroundGradientColor3; - try + using var b = new LinearGradientBrush(new Rectangle(0, 0, w, h), Color.Empty, Color.Empty, 0f) { - var oldBackground = BackgroundImage; - BackgroundImage = new Bitmap(bgWidth, bgHeight); - oldBackground?.Dispose(); - - using var g = Graphics.FromImage(BackgroundImage); - DrawBackgroundGradient(g, bgWidth, bgHeight); - if (ItemNames.Length > 0 && ItemHeight > 0 && Width > 0 && Height > 0) + InterpolationColors = new ColorBlend { - DrawTextItems(g); - DrawSeparators(g); + Colors = new[] { color1, color2, color3 }, + Positions = new[] { 0f, 0.5f, 1f } } - } - catch (ArgumentException) - { - BackgroundImage?.Dispose(); - BackgroundImage = null; - } - } - - private void DrawBackgroundGradient(Graphics g, int width, int height) - { - using var brush = new LinearGradientBrush(new Rectangle(0, 0, width, height), Color.Empty, Color.Empty, 0f); - brush.InterpolationColors = new ColorBlend - { - Colors = new[] { BackgroundGradientColor1, BackgroundGradientColor2, BackgroundGradientColor3 }, - Positions = new[] { 0f, 0.5f, 1f } }; - g.FillRectangle(brush, new Rectangle(0, 0, width, height)); + g.FillRectangle(b, new Rectangle(0, 0, w, h)); } - private void DrawTextItems(Graphics g) + private void DrawTextItemsAndSeparators(Graphics g) { - if (ItemNames == null || ItemNames.Length == 0) return; using var textBrush = new SolidBrush(ForeColor); - for (var i = 0; i < ItemNames.Length; i++) - { - var item = ItemNames[i]; - if (string.IsNullOrEmpty(item)) continue; - var yPos = TopSpace + i * ItemHeight + VerticalSpace; - g.DrawString(item, Font, textBrush, new PointF(HorizontalSpace, yPos)); - } - } - - private void DrawSeparators(Graphics g) - { - using var pen = new Pen(SeparatorColor); - for (var i = 0; i < ItemNames.Length; i++) + using var separatorPen = new Pen(SeparatorColor); + float vSpace = (ItemHeight - TextRenderer.MeasureText(" ", Font).Height) * 0.5f; + for (int i = 0; i < ItemNames.Length; i++) { - if (ItemNames[i] != null) continue; - var yPos = TopSpace + (i + 0.5F) * ItemHeight; - g.DrawLine(pen, HorizontalSpace, yPos, Width - HorizontalSpace, yPos); + float y = TopSpace + i * ItemHeight; + if (ItemNames[i] == null) + DrawSeparator(g, separatorPen, i); + else if (ItemNames[i].Length > 0) + g.DrawString(ItemNames[i], Font, textBrush, HorizontalSpace, y + vSpace); } } - private int CalculateItemIndex(int yPos) + private void DrawSeparator(Graphics g, Pen pen, int index) { - if (ItemNames == null || ItemHeight <= 0) return -1; - var index = (yPos - TopSpace) / ItemHeight; - if (index < 0 || index >= ItemNames.Length) return -1; - if (string.IsNullOrEmpty(ItemNames[index])) return -1; - return index; + float y = TopSpace + (index + 0.5f) * ItemHeight; + int margin = ItemMargin + 8.DpiZoom(); + using var brush = new SolidBrush(SeparatorColor); + g.FillRectangle(brush, margin, y - 1, Width - 2 * margin, 2); } - private void RefreshItem(Panel panel, int index) + protected override void OnPaintBackground(PaintEventArgs e) { - if (index < 0 || index >= ItemNames?.Length) + if (DarkModeHelper.IsDwmCompositionEnabled) { - panel.Top = -ItemHeight; - panel.Text = null; + e.Graphics.Clear(Color.Transparent); } else { - var actualTop = Math.Max(0, Math.Min(TopSpace + index * ItemHeight, Height - ItemHeight)); - panel.Top = actualTop; - panel.Text = ItemNames[index]; + base.OnPaintBackground(e); } - panel.Height = ItemHeight; - panel.Refresh(); } - private void PaintHoveredItem(object sender, PaintEventArgs e) + protected override void OnPaint(PaintEventArgs e) { - var ctr = (Control)sender; - if (string.IsNullOrEmpty(ctr.Text)) return; - e.Graphics.FillRectangle(new SolidBrush(HoveredBackColor), new Rectangle(0, 0, ctr.Width, ctr.Height)); - DrawItemText(e, ctr, HoveredForeColor); - } + if (ItemNames == null) return; + float vSpace = (ItemHeight - TextRenderer.MeasureText(" ", Font).Height) * 0.5f; + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; - private void PaintSelectedItem(object sender, PaintEventArgs e) - { - var ctr = (Control)sender; - if (string.IsNullOrEmpty(ctr.Text)) return; + if (DarkModeHelper.IsDwmCompositionEnabled) + { + // DWM模式下强制使用白色文字 + using var textBrush = new SolidBrush(Color.White); + for (int i = 0; i < ItemNames.Length; i++) + { + float y = TopSpace + i * ItemHeight; + if (ItemNames[i] == null) + { + int margin = ItemMargin + 8.DpiZoom(); + using var brush = new SolidBrush(SeparatorColor); + e.Graphics.FillRectangle(brush, margin, y + ItemHeight / 2 - 1, Width - 2 * margin, 2); + } + else if (ItemNames[i].Length > 0) + { + e.Graphics.DrawString(ItemNames[i], Font, textBrush, HorizontalSpace, y + vSpace); + } + } + } - using (var brush = new LinearGradientBrush(new Rectangle(0, 0, ctr.Width, ctr.Height), Color.Empty, Color.Empty, 0f)) + void DrawItem(int idx, Color back, Color fore, float y) { - brush.InterpolationColors = new ColorBlend + if (idx < 0 || idx >= ItemNames.Length || string.IsNullOrEmpty(ItemNames[idx])) return; + var itemRect = new RectangleF(ItemMargin, y + 2, Width - 2 * ItemMargin, ItemHeight - 4); + if (itemRect.Width <= 0 || itemRect.Height <= 0) return; + + using var path = GetRoundedRectPath(itemRect, CornerRadius); + if (back == Color.Transparent) { - Colors = new[] { SelectedGradientColor1, SelectedGradientColor2, SelectedGradientColor3 }, - Positions = new[] { 0f, 0.5f, 1f } - }; - e.Graphics.FillRectangle(brush, new Rectangle(0, 0, ctr.Width, ctr.Height)); + using var b = new LinearGradientBrush(itemRect, Color.Empty, Color.Empty, 90f) + { + InterpolationColors = new ColorBlend + { + Colors = new[] { SelectedGradientColor1, SelectedGradientColor2, SelectedGradientColor3 }, + Positions = new[] { 0f, 0.5f, 1f } + } + }; + e.Graphics.FillPath(b, path); + } + else + { + using var b = new SolidBrush(back); + e.Graphics.FillPath(b, path); + } + // DWM模式下强制使用白色文字 + using var fb = new SolidBrush(DarkModeHelper.IsDwmCompositionEnabled ? Color.White : (fore == Color.Empty ? ForeColor : fore)); + var textRect = new RectangleF(ItemMargin + HorizontalSpace - ItemMargin, y + 2 + vSpace, Width - 2 * HorizontalSpace, ItemHeight - 4 - 2 * vSpace); + e.Graphics.DrawString(ItemNames[idx], Font, fb, HorizontalSpace, y + 2 + vSpace); } - DrawItemText(e, ctr, SelectedForeColor); + + if (hoverIndex >= 0 && hoverIndex != selectIndex) + { + float hoverY = TopSpace + (float)hoverIndex * ItemHeight; + DrawItem(hoverIndex, HoveredBackColor, HoveredForeColor, hoverY); + } + if (selectIndex >= 0) DrawItem(selectIndex, Color.Transparent, SelectedForeColor, curSelTop); + using var p = new Pen(RightBorderColor); + e.Graphics.DrawLine(p, Width - 1, 0, Width - 1, Height); } - private void DrawItemText(PaintEventArgs e, Control ctr, Color textColor) + private GraphicsPath GetRoundedRectPath(RectangleF rect, int radius) { - e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; - e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; - using var brush = new SolidBrush(textColor == Color.Empty ? ForeColor : textColor); - e.Graphics.DrawString(ctr.Text, Font, brush, new PointF(HorizontalSpace, VerticalSpace)); + var path = new GraphicsPath(); + if (radius <= 0) { path.AddRectangle(rect); return path; } + float r = radius; + path.AddArc(rect.X, rect.Y, r * 2, r * 2, 180, 90); + path.AddArc(rect.Right - r * 2, rect.Y, r * 2, r * 2, 270, 90); + path.AddArc(rect.Right - r * 2, rect.Bottom - r * 2, r * 2, r * 2, 0, 90); + path.AddArc(rect.X, rect.Bottom - r * 2, r * 2, r * 2, 90, 90); + path.CloseFigure(); + return path; } - private void ShowItem(Panel panel, MouseEventArgs e) + protected override void OnMouseMove(MouseEventArgs e) { + base.OnMouseMove(e); if (ItemNames == null) return; - var index = CalculateItemIndex(e.Y); - var isValid = index != -1 && index != SelectedIndex; - Cursor = isValid ? Cursors.Hand : Cursors.Default; + int idx = (e.Y - TopSpace) / ItemHeight; + bool valid = idx >= 0 && idx < ItemNames.Length && !string.IsNullOrEmpty(ItemNames[idx]) && idx != SelectedIndex; + Cursor = valid ? Cursors.Hand : Cursors.Default; + HoveredIndex = valid ? idx : SelectedIndex; + } - if (isValid) + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + if (e.Button != MouseButtons.Left || ItemNames == null) return; + int idx = (e.Y - TopSpace) / ItemHeight; + if (idx >= 0 && idx < ItemNames.Length && !string.IsNullOrEmpty(ItemNames[idx]) && idx != SelectedIndex) { - if (panel == PnlSelected) - { - if (isAnimating) StopAnimation(); - SelectedIndex = index; - } - else HoveredIndex = index; + if (isAnimating) StopAnimation(); + SelectedIndex = idx; } - else HoveredIndex = SelectedIndex; } - protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); ShowItem(PnlHovered, e); } - protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (e.Button == MouseButtons.Left) ShowItem(PnlSelected, e); } protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); HoveredIndex = SelectedIndex; } protected override void OnBackColorChanged(EventArgs e) { base.OnBackColorChanged(e); InitializeColors(); UpdateBackground(); } - protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) - { - base.SetBoundsCore(x, y, Math.Max(1, width), Math.Max(1, height), specified); - } - - private void OnThemeChanged(object sender, EventArgs e) - { - InitializeColors(); - UpdateBackground(); - } + protected override void SetBoundsCore(int x, int y, int w, int h, BoundsSpecified s) => base.SetBoundsCore(x, y, Math.Max(1, w), Math.Max(1, h), s); + protected override void Dispose(bool disposing) { if (disposing) { DarkModeHelper.ThemeChanged -= OnThemeChanged; animTimer?.Dispose(); ownedFont?.Dispose(); } base.Dispose(disposing); } } -} \ No newline at end of file +} diff --git a/ContextMenuManager/BluePointLilac.Controls/MyStatusBar.cs b/ContextMenuManager/BluePointLilac.Controls/MyStatusBar.cs index 0148896a..cc1def87 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MyStatusBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MyStatusBar.cs @@ -26,6 +26,8 @@ public MyStatusBar() Dock = DockStyle.Bottom; Font = SystemFonts.StatusFont; + SetStyle(ControlStyles.SupportsTransparentBackColor, true); + // 初始化系统主题 CheckSystemTheme(); @@ -40,17 +42,32 @@ private void OnThemeChanged(object sender, EventArgs e) Refresh(); } + public void UpdateThemeColors() + { + CheckSystemTheme(); + Refresh(); + } + // 检查系统主题 private void CheckSystemTheme() { // 使用DarkModeHelper统一管理主题 isDarkMode = DarkModeHelper.IsDarkTheme; - if (isDarkMode) + if (DarkModeHelper.IsDwmCompositionEnabled) + { + // DWM启用时使用透明背景和白色文字 + BackColor = Color.Transparent; + ForeColor = Color.White; + topColor = Color.Transparent; + middleColor = Color.Transparent; + bottomColor = Color.Transparent; + } + else if (isDarkMode) { // 深色模式颜色方案 - 使用渐变色 BackColor = Color.FromArgb(40, 40, 40); // 备用背景色 - ForeColor = Color.LightGray; + ForeColor = Color.White; // 使用DarkModeHelper中的颜色 topColor = DarkModeHelper.StatusBarGradientTop; @@ -61,7 +78,7 @@ private void CheckSystemTheme() { // 浅色模式颜色方案 BackColor = DarkModeHelper.ButtonMain; - ForeColor = DarkModeHelper.FormFore; + ForeColor = Color.White; // 使用DarkModeHelper中的颜色 topColor = DarkModeHelper.StatusBarGradientTop; @@ -95,22 +112,37 @@ public Color BottomColor set { bottomColor = value; Refresh(); } } + protected override void OnPaintBackground(PaintEventArgs e) + { + if (DarkModeHelper.IsDwmCompositionEnabled) + { + e.Graphics.Clear(Color.Transparent); + } + else + { + base.OnPaintBackground(e); + } + } + protected override void OnPaint(PaintEventArgs e) { - // 绘制渐变色背景 - using (var brush = new LinearGradientBrush( - ClientRectangle, - Color.Empty, - Color.Empty, - LinearGradientMode.Vertical)) + if (!DarkModeHelper.IsDwmCompositionEnabled) { - // 设置渐变色 - var colorBlend = new ColorBlend(3); - colorBlend.Colors = new Color[] { TopColor, MiddleColor, BottomColor }; - colorBlend.Positions = new float[] { 0f, 0.5f, 1f }; - brush.InterpolationColors = colorBlend; + // 绘制渐变色背景 + using (var brush = new LinearGradientBrush( + ClientRectangle, + Color.Empty, + Color.Empty, + LinearGradientMode.Vertical)) + { + // 设置渐变色 + var colorBlend = new ColorBlend(3); + colorBlend.Colors = new Color[] { TopColor, MiddleColor, BottomColor }; + colorBlend.Positions = new float[] { 0f, 0.5f, 1f }; + brush.InterpolationColors = colorBlend; - e.Graphics.FillRectangle(brush, ClientRectangle); + e.Graphics.FillRectangle(brush, ClientRectangle); + } } // 绘制文本(带有省略号处理) diff --git a/ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs b/ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs index 8ddeedae..a45bae39 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs @@ -9,9 +9,8 @@ namespace BluePointLilac.Controls { public sealed class MyToolBar : Panel { - // 提高不透明度值,使白色背景更加明显 - public const float SelctedOpacity = 0.8F; - public const float HoveredOpacity = 0.4F; + public const float SelctedOpacity = 1.0F; + public const float HoveredOpacity = 0.6F; public const float UnSelctedOpacity = 0; private readonly FlowLayoutPanel buttonContainer; @@ -22,10 +21,11 @@ public MyToolBar() Height = 80.DpiZoom(); Dock = DockStyle.Top; DoubleBuffered = true; - BackColor = DarkModeHelper.TitleArea; + BackColor = DarkModeHelper.IsDwmCompositionEnabled ? Color.Transparent : DarkModeHelper.TitleArea; ForeColor = DarkModeHelper.FormFore; - // 创建按钮容器(左侧) + SetStyle(ControlStyles.SupportsTransparentBackColor, true); + buttonContainer = new FlowLayoutPanel { Dock = DockStyle.Left, @@ -36,7 +36,6 @@ public MyToolBar() BackColor = Color.Transparent }; - // 创建搜索框容器(右侧) searchBoxContainer = new Panel { Dock = DockStyle.Right, @@ -48,7 +47,6 @@ public MyToolBar() Controls.Add(buttonContainer); Controls.Add(searchBoxContainer); - // 监听主题变化 DarkModeHelper.ThemeChanged += OnThemeChanged; } @@ -61,16 +59,18 @@ public MyToolBarButton SelectedButton if (selectedButton == value) return; if (selectedButton != null) { - selectedButton.Opacity = UnSelctedOpacity; // 动画过渡到未选中状态 + selectedButton.Opacity = UnSelctedOpacity; selectedButton.Cursor = Cursors.Hand; - selectedButton.UpdateTextColor(); // 更新文字颜色 + selectedButton.UpdateTextColor(); + selectedButton.IsSelected = false; } selectedButton = value; if (selectedButton != null) { - selectedButton.Opacity = SelctedOpacity; // 动画过渡到选中状态 + selectedButton.Opacity = SelctedOpacity; selectedButton.Cursor = Cursors.Default; - selectedButton.UpdateTextColor(); // 更新文字颜色 + selectedButton.UpdateTextColor(); + selectedButton.IsSelected = true; } SelectedButtonChanged?.Invoke(this, null); } @@ -85,7 +85,6 @@ public int SelectedIndex null : (MyToolBarButton)buttonContainer.Controls[value]; } - // 添加一个方法来获取所有按钮(用于兼容旧代码) public Control.ControlCollection ButtonControls => buttonContainer.Controls; public void AddButton(MyToolBarButton button) @@ -104,8 +103,9 @@ public void AddButton(MyToolBarButton button) { if (button != SelectedButton) { - button.Opacity = HoveredOpacity; // 动画过渡到悬停状态 - button.UpdateTextColor(); // 更新文字颜色 + button.Opacity = HoveredOpacity; + button.UpdateTextColor(); + button.IsHovered = true; } }; @@ -113,8 +113,9 @@ public void AddButton(MyToolBarButton button) { if (button != SelectedButton) { - button.Opacity = UnSelctedOpacity; // 动画过渡到未选中状态 - button.UpdateTextColor(); // 更新文字颜色 + button.Opacity = UnSelctedOpacity; + button.UpdateTextColor(); + button.IsHovered = false; } }; @@ -128,13 +129,10 @@ public void AddButtons(MyToolBarButton[] buttons) Array.ForEach(buttons, button => { button.Width = maxWidth; AddButton(button); }); } - // 添加搜索框到工具栏右侧 public void AddSearchBox(SearchBox searchBox) { - // 清除现有搜索框 searchBoxContainer.Controls.Clear(); - // 设置搜索框 searchBox.Parent = searchBoxContainer; searchBox.Width = searchBoxContainer.Width - 40.DpiZoom(); searchBox.Height = 36.DpiZoom(); @@ -146,13 +144,11 @@ protected override void OnResize(EventArgs e) { base.OnResize(e); - // 调整搜索框容器的宽度 if (searchBoxContainer != null) { searchBoxContainer.Width = 240.DpiZoom(); searchBoxContainer.Height = Height; - // 确保搜索框在容器中垂直居中 var searchBox = searchBoxContainer.Controls.OfType().FirstOrDefault(); if (searchBox != null) { @@ -164,36 +160,39 @@ protected override void OnResize(EventArgs e) protected override void OnPaintBackground(PaintEventArgs e) { - base.OnPaintBackground(e); - var rect = ClientRectangle; + var g = e.Graphics; + g.SmoothingMode = SmoothingMode.AntiAlias; - // 使用DarkModeHelper中的颜色 - var color1 = DarkModeHelper.ToolBarGradientTop; - var color2 = DarkModeHelper.ToolBarGradientMiddle; - var color3 = DarkModeHelper.ToolBarGradientBottom; - - // 创建三色渐变 - using var brush = new LinearGradientBrush( - rect, Color.Empty, Color.Empty, LinearGradientMode.Vertical); - // 使用ColorBlend创建三色渐变 - var colorBlend = new ColorBlend(3); - colorBlend.Colors = new Color[] { color1, color2, color3 }; - colorBlend.Positions = new float[] { 0f, 0.5f, 1f }; - brush.InterpolationColors = colorBlend; - - e.Graphics.FillRectangle(brush, rect); + if (DarkModeHelper.IsDwmCompositionEnabled) + { + g.Clear(Color.Transparent); + } + else + { + base.OnPaintBackground(e); + var color1 = DarkModeHelper.ToolBarGradientTop; + var color2 = DarkModeHelper.ToolBarGradientMiddle; + var color3 = DarkModeHelper.ToolBarGradientBottom; + + using var brush = new LinearGradientBrush(rect, Color.Empty, Color.Empty, LinearGradientMode.Vertical); + var colorBlend = new ColorBlend(3); + colorBlend.Colors = new Color[] { color1, color2, color3 }; + colorBlend.Positions = new float[] { 0f, 0.5f, 1f }; + brush.InterpolationColors = colorBlend; + g.FillRectangle(brush, rect); + } + + using var borderPen = new Pen(DarkModeHelper.IsDarkTheme ? + Color.FromArgb(60, 60, 60) : Color.FromArgb(220, 220, 220), 1); + g.DrawLine(borderPen, 0, rect.Bottom - 1, rect.Right, rect.Bottom - 1); } - // 重写Controls属性以保持向后兼容(注意:这可能会影响一些代码) - public new Control.ControlCollection Controls => - // 返回包含所有子控件的集合 - base.Controls; + public new Control.ControlCollection Controls => base.Controls; - // 主题变化事件处理 private void OnThemeChanged(object sender, EventArgs e) { - BackColor = DarkModeHelper.TitleArea; + BackColor = DarkModeHelper.IsDwmCompositionEnabled ? Color.Transparent : DarkModeHelper.TitleArea; ForeColor = DarkModeHelper.FormFore; Invalidate(); } @@ -212,29 +211,63 @@ public sealed class MyToolBarButton : Panel { private float targetOpacity; private float currentOpacity; + private float currentScale = 1.0f; + private float targetScale = 1.0f; private readonly Timer animationTimer = new() { Interval = 16 }; - private const float AnimationSpeed = 0.15f; - private readonly int borderRadius = 10; // 圆角半径 + private const float AnimationSpeed = 0.12f; + private const float ScaleAnimationSpeed = 0.15f; + private readonly int borderRadius = 12; + + private bool isSelected = false; + private bool isHovered = false; + + public bool IsSelected + { + get => isSelected; + set + { + if (isSelected != value) + { + isSelected = value; + targetScale = value ? 1.02f : 1.0f; + if (!animationTimer.Enabled) animationTimer.Start(); + } + } + } + + public bool IsHovered + { + get => isHovered; + set + { + if (isHovered != value && !isSelected) + { + isHovered = value; + targetScale = value ? 1.01f : 1.0f; + if (!animationTimer.Enabled) animationTimer.Start(); + } + } + } public MyToolBarButton(Image image, string text) { SuspendLayout(); DoubleBuffered = true; - ForeColor = DarkModeHelper.FormFore; + ForeColor = DarkModeHelper.IsDwmCompositionEnabled ? Color.White : DarkModeHelper.FormFore; BackColor = Color.Transparent; Cursor = Cursors.Hand; Size = new Size(72, 72).DpiZoom(); - // 启用透明背景 SetStyle(ControlStyles.SupportsTransparentBackColor, true); SetStyle(ControlStyles.Opaque, false); + SetStyle(ControlStyles.ResizeRedraw, true); animationTimer.Tick += (s, e) => UpdateAnimation(); Controls.AddRange(new Control[] { picImage, lblText }); lblText.Resize += (sender, e) => OnResize(null); - picImage.Top = 6.DpiZoom(); - lblText.Top = 52.DpiZoom(); + picImage.Top = 8.DpiZoom(); + lblText.Top = 50.DpiZoom(); lblText.SetEnabled(false); Image = image; Text = text; @@ -244,16 +277,16 @@ public MyToolBarButton(Image image, string text) private readonly PictureBox picImage = new() { SizeMode = PictureBoxSizeMode.StretchImage, - Size = new Size(40, 40).DpiZoom(), + Size = new Size(36, 36).DpiZoom(), BackColor = Color.Transparent, Enabled = false }; private readonly Label lblText = new() { - ForeColor = DarkModeHelper.FormFore, + ForeColor = Color.White, BackColor = Color.Transparent, - Font = SystemFonts.MenuFont, + Font = new Font(SystemFonts.MenuFont.FontFamily, SystemFonts.MenuFont.SizeInPoints, FontStyle.Regular, GraphicsUnit.Point), AutoSize = true, Anchor = AnchorStyles.Left | AnchorStyles.Right }; @@ -271,7 +304,7 @@ public Image Image } public bool CanBeSelected = true; - private readonly float opacity; + public float Opacity { get => currentOpacity; @@ -284,66 +317,112 @@ public float Opacity } } - // 重写OnPaint方法以实现圆角效果 protected override void OnPaint(PaintEventArgs e) { - // 调用基类绘制以确保子控件正确显示 base.OnPaint(e); - // 创建圆角矩形路径 - using var path = DarkModeHelper.CreateRoundedRectanglePath(ClientRectangle, borderRadius); - // 根据当前模式选择颜色 + var g = e.Graphics; + g.SmoothingMode = SmoothingMode.AntiAlias; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + var isDarkMode = DarkModeHelper.IsDarkTheme; + var mainColor = DarkModeHelper.MainColor; - // 深色模式使用白色,浅色模式使用黑色 - var baseColor = isDarkMode ? Color.White : Color.Black; + var padding = 4; + var drawRect = new Rectangle( + padding, + padding, + Width - padding * 2, + Height - padding * 2); - // 减少两种模式的不透明度 - var opacityFactor = isDarkMode ? 0.4f : 0.6f; // 深色模式减少更多(0.4),浅色模式减少较少(0.6) - var alpha = (int)(currentOpacity * 255 * opacityFactor); - alpha = Math.Max(0, Math.Min(255, alpha)); // 确保alpha值在有效范围内 + using var path = DarkModeHelper.CreateRoundedRectanglePath(drawRect, borderRadius); - var fillColor = Color.FromArgb(alpha, baseColor); + if (currentOpacity > 0.01f) + { + var baseColor = isDarkMode ? Color.White : mainColor; + var opacityFactor = isDarkMode ? 0.35f : 0.5f; + var alpha = (int)(currentOpacity * 255 * opacityFactor); + alpha = Math.Max(0, Math.Min(255, alpha)); + var fillColor = Color.FromArgb(alpha, baseColor); + + using var brush = new SolidBrush(fillColor); + g.FillPath(brush, path); + + if (isSelected && currentOpacity > 0.5f) + { + var glowAlpha = (int)(currentOpacity * 60); + var glowColor = Color.FromArgb(glowAlpha, mainColor); + using var glowPath = DarkModeHelper.CreateRoundedRectanglePath( + new Rectangle(drawRect.X - 2, drawRect.Y - 2, drawRect.Width + 4, drawRect.Height + 4), + borderRadius + 2); + using var glowPen = new Pen(glowColor, 2); + g.DrawPath(glowPen, glowPath); + } - // 使用计算出的颜色填充圆角矩形 - using var brush = new SolidBrush(fillColor); - e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; - e.Graphics.FillPath(brush, path); + var borderColor = isSelected ? + Color.FromArgb(180, mainColor) : + Color.FromArgb(100, isDarkMode ? Color.White : mainColor); + using var borderPen = new Pen(borderColor, 1); + g.DrawPath(borderPen, path); + } } - // 更新文字颜色的方法 public void UpdateTextColor() { - var isDarkMode = DarkModeHelper.IsDarkTheme; - - // 浅色模式下,当按钮被选中或悬停时,文字颜色改为白色 - if (!isDarkMode && currentOpacity > 0.1f) + if (DarkModeHelper.IsDwmCompositionEnabled) { lblText.ForeColor = Color.White; } else { - lblText.ForeColor = DarkModeHelper.FormFore; + var isDarkMode = DarkModeHelper.IsDarkTheme; + + if (!isDarkMode && currentOpacity > 0.3f) + { + lblText.ForeColor = Color.White; + } + else + { + lblText.ForeColor = DarkModeHelper.FormFore; + } } } private void UpdateAnimation() { - currentOpacity += (targetOpacity - currentOpacity) * AnimationSpeed; - var difference = Math.Abs(currentOpacity - targetOpacity); + var needsUpdate = false; - if (difference < 0.01f) + var opacityDiff = targetOpacity - currentOpacity; + if (Math.Abs(opacityDiff) > 0.001f) + { + currentOpacity += opacityDiff * AnimationSpeed; + needsUpdate = true; + } + else { currentOpacity = targetOpacity; - animationTimer.Stop(); } - // 更新文字颜色 + var scaleDiff = targetScale - currentScale; + if (Math.Abs(scaleDiff) > 0.001f) + { + currentScale += scaleDiff * ScaleAnimationSpeed; + needsUpdate = true; + } + else + { + currentScale = targetScale; + } + UpdateTextColor(); - // 强制重绘 - this.Invalidate(); - this.Update(); + Invalidate(); + Update(); + + if (!needsUpdate) + { + animationTimer.Stop(); + } } protected override void OnResize(EventArgs e) @@ -351,16 +430,15 @@ protected override void OnResize(EventArgs e) base.OnResize(e); lblText.Left = (Width - lblText.Width) / 2; picImage.Left = (Width - picImage.Width) / 2; - this.Invalidate(); // 重绘以确保圆角正确显示 + Invalidate(); } - // 确保透明背景正确渲染 protected override CreateParams CreateParams { get { var cp = base.CreateParams; - cp.ExStyle |= 0x20; // 添加 WS_EX_TRANSPARENT 样式 + cp.ExStyle |= 0x20; return cp; } } diff --git a/ContextMenuManager/BluePointLilac.Controls/RComboBox.cs b/ContextMenuManager/BluePointLilac.Controls/RComboBox.cs index 0ec2c308..b5dbc0ff 100644 --- a/ContextMenuManager/BluePointLilac.Controls/RComboBox.cs +++ b/ContextMenuManager/BluePointLilac.Controls/RComboBox.cs @@ -9,491 +9,243 @@ namespace ContextMenuManager.BluePointLilac.Controls { - // 一个支持圆角、动画和深色模式的 ComboBox 控件。 - public unsafe partial class RComboBox : ComboBox + public sealed partial class RComboBox : ComboBox { #region Fields - - // State - private int originalSelectedIndex = -1; - private bool mouseOverDropDown = false; - private bool focused = false; - - // Animation - private readonly Timer animTimer; - private float borderWidth = 1.2f; - private float targetWidth = 1.2f; - private Color currentBorder; - private Color targetBorder; - private int animatedIndex = -1; - private int previousAnimatedIndex = -1; - private float hoverProgress = 0f; - - // Style - [DefaultValue(8)] - [Category("Style")] - public int BorderRadius { get; set; } = 8; - - // Win32 - private IntPtr dropDownHwnd = IntPtr.Zero; - + private int _originalSelectedIndex = -1; + private bool _mouseOverDropDown, _focused; + private readonly Timer _animTimer; + private float _borderWidth = 1.2f, _targetWidth = 1.2f, _hoverProgress; + private Color _currentBorder, _targetBorder; + private int _animatedIndex = -1, _previousAnimatedIndex = -1; + private IntPtr _dropDownHwnd; #endregion #region Properties + [DefaultValue(8), Category("Style")] + public int BorderRadius { get; set; } = 8; - private Color hoverColor = Color.FromArgb(255, 145, 60); - // 获取或设置鼠标悬停时控件的边框颜色。 - [DefaultValue(typeof(Color), "255, 145, 60")] + private Color _hoverColor = Color.FromArgb(255, 145, 60); + [DefaultValue(typeof(Color), "255, 145, 60"), Category("Style")] public Color HoverColor { - get => hoverColor; - set - { - hoverColor = value; - DropDownHoverColor = value; - } + get => _hoverColor; + set { _hoverColor = value; DropDownHoverColor = value; } } - private Color focusColor = Color.FromArgb(255, 107, 0); - // 获取或设置控件获得焦点时的边框颜色。 - [DefaultValue(typeof(Color), "255, 107, 0")] + private Color _focusColor = Color.FromArgb(255, 107, 0); + [DefaultValue(typeof(Color), "255, 107, 0"), Category("Style")] public Color FocusColor { - get => focusColor; - set - { - focusColor = value; - DropDownSelectedColor = value; - } + get => _focusColor; + set { _focusColor = value; DropDownSelectedColor = value; } } - // 获取或设置下拉箭头的颜色。 - [DefaultValue(typeof(Color), "100, 100, 100")] + [DefaultValue(typeof(Color), "100, 100, 100"), Category("Style")] public Color ArrowColor { get; set; } = Color.FromArgb(100, 100, 100); - // 获取或设置是否根据内容自动调整控件宽度。 [DefaultValue(true)] public new bool AutoSize { get; set; } = true; - // 获取或设置控件的最小宽度。 - [DefaultValue(120)] - private int minWidth = 120; - public int MinWidth - { - get => minWidth.DpiZoom(); - set => minWidth = value; - } + private int _minWidth = 120; + [DefaultValue(120), Category("Style")] + public int MinWidth { get => _minWidth.DpiZoom(); set => _minWidth = value; } - // 获取或设置控件的最大宽度。 - [DefaultValue(400)] - private int maxWidth = 400; - public int MaxWidth - { - get => maxWidth.DpiZoom(); - set => maxWidth = value; - } - - // 获取或设置文本与控件边缘的内边距。 - [DefaultValue(50)] - private int textPadding = 5; - public int TextPadding - { - get => textPadding.DpiZoom(); - set => textPadding = value; - } + private int _maxWidth = 400; + [DefaultValue(400), Category("Style")] + public int MaxWidth { get => _maxWidth.DpiZoom(); set => _maxWidth = value; } - #endregion - - #region DropDown Properties + private int _textPadding = 5; + [DefaultValue(5), Category("Style")] + public int TextPadding { get => _textPadding.DpiZoom(); set => _textPadding = value; } - // 获取或设置下拉列表中各项的高度。 - [Category("DropDown"), Description("下拉列表中各项的高度")] - [DefaultValue(32)] + [Category("DropDown"), DefaultValue(32)] public int DropDownItemHeight { get; set; } = 32; - // 获取或设置下拉列表各项的字体。 - [Category("DropDown"), Description("下拉列表各项的字体")] - [DefaultValue(null)] + [Category("DropDown")] public Font DropDownFont { get; set; } - // 获取或设置下拉列表鼠标悬停项的背景色。 - [Category("DropDown"), Description("下拉列表鼠标悬停项的背景色")] + [Category("DropDown")] public Color DropDownHoverColor { get; set; } - // 获取或设置下拉列表鼠标悬停项的文字色。 - [Category("DropDown"), Description("下拉列表鼠标悬停项的文字色")] - [DefaultValue(typeof(Color), "White")] + [Category("DropDown"), DefaultValue(typeof(Color), "White")] public Color DropDownHoverForeColor { get; set; } = Color.White; - // 获取或设置下拉列表选中项的背景色。 - [Category("DropDown"), Description("下拉列表选中项的背景色")] + [Category("DropDown")] public Color DropDownSelectedColor { get; set; } - // 获取或设置下拉列表选中项的文字色。 - [Category("DropDown"), Description("下拉列表选中项的文字色")] - [DefaultValue(typeof(Color), "White")] + [Category("DropDown"), DefaultValue(typeof(Color), "White")] public Color DropDownSelectedForeColor { get; set; } = Color.White; - // 获取或设置下拉列表各项的文字色。 - [Category("DropDown"), Description("下拉列表各项的文字色")] + [Category("DropDown")] public Color DropDownForeColor { get; set; } - #endregion - public void AutosizeDropDownWidth() - { - var maxWidth = 0; - foreach (var item in Items) - { - var width = TextRenderer.MeasureText(item.ToString(), Font).Width; - if (width > maxWidth) - { - maxWidth = width; - } - } - DropDownWidth = maxWidth + SystemInformation.VerticalScrollBarWidth; - } - - // 处理控件的构造和样式设置。 public RComboBox() { - SetStyle(ControlStyles.AllPaintingInWmPaint | - ControlStyles.UserPaint | - ControlStyles.ResizeRedraw | - ControlStyles.OptimizedDoubleBuffer, true); - + SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | + ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true); DrawMode = DrawMode.OwnerDrawVariable; DropDownStyle = ComboBoxStyle.DropDownList; FlatStyle = FlatStyle.Flat; Height = 32.DpiZoom(); - - animTimer = new Timer { Interval = 16 }; + _animTimer = new Timer { Interval = 16 }; InitEvents(); } - // 初始化所有事件订阅。 private void InitEvents() { DarkModeHelper.ThemeChanged += OnThemeChanged; - - GotFocus += (s, e) => { focused = true; UpdateState(); }; - LostFocus += (s, e) => { focused = false; UpdateState(); }; + GotFocus += (s, e) => { _focused = true; UpdateState(); }; + LostFocus += (s, e) => { _focused = false; UpdateState(); }; MouseEnter += (s, e) => UpdateState(); - MouseLeave += (s, e) => { mouseOverDropDown = false; UpdateState(); }; + MouseLeave += (s, e) => { _mouseOverDropDown = false; UpdateState(); }; MouseMove += (s, e) => UpdateDropDownHoverState(e.Location); - MouseDown += RComboBox_MouseDown; - + MouseDown += (s, e) => { if (e.Button == MouseButtons.Left && !DroppedDown) { if (!Focused) Focus(); DroppedDown = true; } }; SelectedIndexChanged += (s, e) => { if (AutoSize) AdjustWidth(); }; TextChanged += (s, e) => { if (AutoSize) AdjustWidth(); }; + DropDown += OnDropDown; + DropDownClosed += (s, e) => { _originalSelectedIndex = _animatedIndex = _previousAnimatedIndex = -1; _dropDownHwnd = IntPtr.Zero; }; + _animTimer.Tick += OnAnimTick; + _animTimer.Start(); + } - DropDown += RComboBox_DropDown; - DropDownClosed += (s, e) => { - originalSelectedIndex = animatedIndex = previousAnimatedIndex = -1; - dropDownHwnd = IntPtr.Zero; - }; - - animTimer.Tick += AnimTimer_Tick; - animTimer.Start(); + private void OnAnimTick(object s, EventArgs e) + { + if (IsDisposed) { _animTimer.Stop(); return; } + bool redraw = false; + if (Math.Abs(_borderWidth - _targetWidth) > 0.01f) { _borderWidth += (_targetWidth - _borderWidth) * 0.3f; redraw = true; } + if (_currentBorder != _targetBorder) { _currentBorder = ColorLerp(_currentBorder, _targetBorder, 0.25f); redraw = true; } + if (redraw) Invalidate(); + UpdateDropDownAnimation(); } - // 处理鼠标按下事件以打开下拉列表。 - private void RComboBox_MouseDown(object sender, MouseEventArgs e) + private void OnDropDown(object s, EventArgs e) { - if (e.Button == MouseButtons.Left && !DroppedDown) + _originalSelectedIndex = SelectedIndex; + DropDownHeight = Items.Count * DropDownItemHeight.DpiZoom() + 2 * SystemInformation.BorderSize.Height; + if (Parent?.FindForm() is not Form form) return; + form.BeginInvoke(() => { - // 确保控件获得焦点 - if (!Focused) + try { - Focus(); + if (IsDisposed || !IsHandleCreated) return; + var cbi = new COMBOBOXINFO { cbSize = Marshal.SizeOf() }; + if (!GetComboBoxInfo(Handle, ref cbi) || cbi.hwndList == IntPtr.Zero) return; + _dropDownHwnd = cbi.hwndList; + if (!GetWindowRect(cbi.hwndList, out RECT rect)) return; + var hrgn = CreateRoundRectRgn(0, 0, rect.Right - rect.Left, rect.Bottom - rect.Top, BorderRadius.DpiZoom(), BorderRadius.DpiZoom()); + if (hrgn != IntPtr.Zero && SetWindowRgn(cbi.hwndList, hrgn, true) == 0) DeleteObject(hrgn); } - // 打开下拉列表 - DroppedDown = true; - } + catch { } + }); } - // 动画计时器的 Tick 事件处理程序,用于更新边框和下拉项的动画效果。 - private void AnimTimer_Tick(object sender, EventArgs e) + private void UpdateDropDownAnimation() { - if (IsDisposed) - { - animTimer.Stop(); - return; - } - - var needsRedraw = false; - if (Math.Abs(borderWidth - targetWidth) > 0.01f) + if (DroppedDown && _dropDownHwnd != IntPtr.Zero) UpdateHoverIndex(); + else if (_animatedIndex != -1) { _previousAnimatedIndex = _animatedIndex; _animatedIndex = -1; _hoverProgress = 0; } + if (_hoverProgress < 1f) { - borderWidth += (targetWidth - borderWidth) * 0.3f; - needsRedraw = true; + _hoverProgress = Math.Min(1f, _hoverProgress + 0.1f); + if (_dropDownHwnd != IntPtr.Zero) InvalidateRect(_dropDownHwnd, IntPtr.Zero, true); } - if (currentBorder != targetBorder) - { - currentBorder = ColorLerp(currentBorder, targetBorder, 0.25f); - needsRedraw = true; - } - if (needsRedraw) Invalidate(); - - UpdateDropDownAnimation(); + else _previousAnimatedIndex = -1; } - // 更新下拉列表项的悬停动画状态。 - private void UpdateDropDownAnimation() + private void UpdateHoverIndex() { - if (DroppedDown && dropDownHwnd != IntPtr.Zero) - { - GetCursorPos(out var p); - var r = new RECT(); - GetWindowRect(dropDownHwnd, ref r); - var dropDownRect = new Rectangle(r.Left, r.Top, r.Right - r.Left, r.Bottom - r.Top); - - var hoverIndex = -1; - if (dropDownRect.Contains(p.X, p.Y)) - { - hoverIndex = (p.Y - dropDownRect.Top) / DropDownItemHeight.DpiZoom(); - if (hoverIndex < 0 || hoverIndex >= Items.Count || hoverIndex == originalSelectedIndex) - { - hoverIndex = -1; - } - } - - if (animatedIndex != hoverIndex) - { - previousAnimatedIndex = animatedIndex; - animatedIndex = hoverIndex; - hoverProgress = 0f; - } - } - else - { - if (animatedIndex != -1) - { - previousAnimatedIndex = animatedIndex; - animatedIndex = -1; - hoverProgress = 0f; - } - } - - if (hoverProgress < 1f) - { - hoverProgress += 0.1f; - if (hoverProgress > 1f) hoverProgress = 1f; - - if (dropDownHwnd != IntPtr.Zero) - { - InvalidateRect(dropDownHwnd, IntPtr.Zero, true); - } - } - else + GetCursorPos(out POINT pt); + GetWindowRect(_dropDownHwnd, out RECT rc); + var rect = new Rectangle(rc.Left, rc.Top, rc.Right - rc.Left, rc.Bottom - rc.Top); + int idx = -1; + if (rect.Contains(pt.X, pt.Y)) { - previousAnimatedIndex = -1; + idx = (pt.Y - rect.Top) / DropDownItemHeight.DpiZoom(); + if (idx < 0 || idx >= Items.Count || idx == _originalSelectedIndex) idx = -1; } + if (_animatedIndex != idx) { _previousAnimatedIndex = _animatedIndex; _animatedIndex = idx; _hoverProgress = 0; } } - // 测量下拉列表中项的大小。 protected override void OnMeasureItem(MeasureItemEventArgs e) { base.OnMeasureItem(e); e.ItemHeight = DropDownItemHeight.DpiZoom(); } - // 在下拉列表打开时触发,设置下拉列表的高度和圆角。 - private void RComboBox_DropDown(object sender, EventArgs e) - { - originalSelectedIndex = SelectedIndex; - DropDownHeight = Items.Count * DropDownItemHeight.DpiZoom() + 2 * SystemInformation.BorderSize.Height; - - if (Parent.FindForm() is Form form) - form.BeginInvoke(() => - { - try - { - // 验证控件句柄是否有效 - if (IsDisposed || !IsHandleCreated) return; - - var cbi = new COMBOBOXINFO { cbSize = Marshal.SizeOf() }; - if (!GetComboBoxInfo(Handle, ref cbi)) return; - - // 验证下拉窗口句柄是否有效 - if (cbi.hwndList == IntPtr.Zero) return; - - dropDownHwnd = cbi.hwndList; - var r = new RECT(); - if (!GetWindowRect(cbi.hwndList, ref r)) return; - - var h = CreateRoundRectRgn(0, 0, r.Right - r.Left, r.Bottom - r.Top, BorderRadius.DpiZoom(), BorderRadius.DpiZoom()); - if (h == IntPtr.Zero) return; - - // SetWindowRgn 在成功时会接管 region 句柄的所有权,失败时需要手动删除 - if (SetWindowRgn(cbi.hwndList, h, true) == 0) - { - DeleteObject(h); - } - } - catch (Exception ex) when (ex is Win32Exception or ObjectDisposedException or InvalidOperationException) - { - // 静默捕获预期的异常,防止 ExecutionEngineException 导致程序崩溃 - // 如果设置圆角失败,下拉列表将使用默认的矩形样式 - } - }); - } - - // 自定义绘制下拉列表中的每个项。 protected override void OnDrawItem(DrawItemEventArgs e) { if (e.Index < 0) return; e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; - - var bounds = e.Bounds; - bool isActuallySelected = (e.State & DrawItemState.Selected) == DrawItemState.Selected || - (DroppedDown && animatedIndex == -1 && e.Index == originalSelectedIndex); - - var textFont = DropDownFont ?? Font; - var textColor = DropDownForeColor; - - var backColor = BackColor; - if (e.Index == animatedIndex) - { - backColor = ColorLerp(BackColor, DropDownHoverColor, hoverProgress); - textColor = ColorLerp(DropDownForeColor, DropDownHoverForeColor, hoverProgress); - } - else if (e.Index == previousAnimatedIndex) - { - backColor = ColorLerp(DropDownHoverColor, BackColor, hoverProgress); - textColor = ColorLerp(DropDownHoverForeColor, DropDownForeColor, hoverProgress); - } - else if (isActuallySelected) - { - backColor = DropDownSelectedColor; - textColor = DropDownSelectedForeColor; - } - - using (var backBrush = new SolidBrush(backColor)) - { - Rectangle highlightBounds = new(bounds.X + 2, bounds.Y + 2, bounds.Width - 4, bounds.Height - 4); - using var path = DarkModeHelper.CreateRoundedRectanglePath(highlightBounds, 4); - e.Graphics.FillPath(backBrush, path); - } - - var text = GetItemText(Items[e.Index]); - Rectangle textBounds = new(bounds.Left + TextPadding, bounds.Top, bounds.Width - TextPadding * 2, bounds.Height); - TextRenderer.DrawText(e.Graphics, text, textFont, textBounds, textColor, - TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis | TextFormatFlags.NoPadding); - } - - #region Win32 - - // 检索光标在屏幕坐标中的位置。 - // lpPoint: 指向 POINT 结构的指针,该结构接收光标的屏幕坐标。 - // returns: 如果函数成功,则返回非零值。 - [LibraryImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool GetCursorPos(out POINT lpPoint); - - // 定义一个点的 x 和 y 坐标。 - [StructLayout(LayoutKind.Sequential)] - public struct POINT - { - public int X; - public int Y; - } - - // 定义一个矩形的左上角和右下角的坐标。 - [StructLayout(LayoutKind.Sequential)] - internal struct RECT - { - public int Left; - public int Top; - public int Right; - public int Bottom; + bool selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected || + (DroppedDown && _animatedIndex == -1 && e.Index == _originalSelectedIndex); + var (back, fore) = GetItemColors(e.Index, selected); + using (var brush = new SolidBrush(back)) + using (var path = DarkModeHelper.CreateRoundedRectanglePath(new Rectangle(e.Bounds.X + 2, e.Bounds.Y + 2, e.Bounds.Width - 4, e.Bounds.Height - 4), 4)) + e.Graphics.FillPath(brush, path); + TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), DropDownFont ?? Font, + new Rectangle(e.Bounds.Left + TextPadding, e.Bounds.Top, e.Bounds.Width - TextPadding * 2, e.Bounds.Height), + fore, TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis); } - // 包含有关组合框的信息。 - [StructLayout(LayoutKind.Sequential)] - internal struct COMBOBOXINFO + private (Color Back, Color Fore) GetItemColors(int index, bool selected) { - public int cbSize; - public RECT rcItem; - public RECT rcButton; - public int stateButton; - public IntPtr hwndCombo; - public IntPtr hwndItem; - public IntPtr hwndList; + if (index == _animatedIndex) return (ColorLerp(BackColor, DropDownHoverColor, _hoverProgress), ColorLerp(DropDownForeColor, DropDownHoverForeColor, _hoverProgress)); + if (index == _previousAnimatedIndex) return (ColorLerp(DropDownHoverColor, BackColor, _hoverProgress), ColorLerp(DropDownHoverForeColor, DropDownForeColor, _hoverProgress)); + if (selected) return (DropDownSelectedColor, DropDownSelectedForeColor); + return (BackColor, DropDownForeColor); } - #endregion - - - - - private static Color ColorLerp(Color c1, Color c2, float t) => Color.FromArgb( - (int)(c1.A + (c2.A - c1.A) * t), (int)(c1.R + (c2.R - c1.R) * t), - (int)(c1.G + (c2.G - c1.G) * t), (int)(c1.B + (c2.B - c1.B) * t)); - - // 当系统主题(深色/浅色模式)更改时,更新控件的颜色。 - public void UpdateColors() + protected override void OnPaint(PaintEventArgs e) { - if (IsDisposed) return; - - SafeInvoke(() => + base.OnPaint(e); + var g = e.Graphics; + g.SmoothingMode = SmoothingMode.AntiAlias; + g.Clear(Parent?.BackColor ?? SystemColors.Control); + var rect = new Rectangle(0, 0, Width - 1, Height - 1); + using (var path = DarkModeHelper.CreateRoundedRectanglePath(rect, BorderRadius.DpiZoom())) { - BackColor = DarkModeHelper.ComboBoxBack; - ForeColor = DarkModeHelper.ComboBoxFore; - DropDownForeColor = DarkModeHelper.IsDarkTheme ? Color.White : Color.Black; - currentBorder = targetBorder = DarkModeHelper.ComboBoxBorder; - ArrowColor = DarkModeHelper.ComboBoxArrow; - DropDownHoverColor = DarkModeHelper.ComboBoxHover; - DropDownSelectedColor = DarkModeHelper.ComboBoxSelected; - HoverColor = DarkModeHelper.MainColor; - FocusColor = DarkModeHelper.MainColor; - }); + using (var brush = new SolidBrush(BackColor)) g.FillPath(brush, path); + using (var pen = new Pen(_currentBorder, _borderWidth)) g.DrawPath(pen, path); + } + var btnRect = GetDropDownButtonRect(); + var text = string.IsNullOrEmpty(Text) && SelectedItem != null ? GetItemText(SelectedItem) : Text; + TextRenderer.DrawText(g, text, Font, new Rectangle(TextPadding, 0, Width - btnRect.Width - TextPadding * 2, Height), + ForeColor, TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis); + var c = new Point(btnRect.Left + btnRect.Width / 2, btnRect.Top + btnRect.Height / 2); + int s = 6.DpiZoom(); + using (var brush = new SolidBrush(_mouseOverDropDown || _focused ? FocusColor : ArrowColor)) + g.FillPolygon(brush, new[] { new Point(c.X - s, c.Y - s / 2), new Point(c.X + s, c.Y - s / 2), new Point(c.X, c.Y + s / 2) }); } - // 根据控件的焦点和鼠标悬停状态更新边框的外观。 private void UpdateState() { - bool hover = mouseOverDropDown || ClientRectangle.Contains(PointToClient(MousePosition)); - targetBorder = focused ? FocusColor : (hover ? HoverColor : DarkModeHelper.ComboBoxBorder); - targetWidth = focused || hover ? 2f : 1.2f; + bool hover = _mouseOverDropDown || ClientRectangle.Contains(PointToClient(MousePosition)); + _targetBorder = _focused ? FocusColor : (hover ? HoverColor : DarkModeHelper.ComboBoxBorder); + _targetWidth = _focused || hover ? 2f : 1.2f; Invalidate(); } - // 更新下拉按钮的悬停状态,并相应地更改光标。 - private void UpdateDropDownHoverState(Point location) + private void UpdateDropDownHoverState(Point loc) { - bool hover = GetDropDownButtonRect().Contains(location); - if (mouseOverDropDown == hover) return; - mouseOverDropDown = hover; + bool hover = GetDropDownButtonRect().Contains(loc); + if (_mouseOverDropDown == hover) return; + _mouseOverDropDown = hover; Cursor = hover ? Cursors.Hand : Cursors.Default; UpdateState(); } - // 获取下拉按钮的矩形区域。 - private Rectangle GetDropDownButtonRect() - { - int w = SystemInformation.HorizontalScrollBarArrowWidth + 8.DpiZoom(); - return new Rectangle(ClientRectangle.Right - w, ClientRectangle.Top, w, ClientRectangle.Height); - } + private Rectangle GetDropDownButtonRect() => new(ClientRectangle.Right - (SystemInformation.HorizontalScrollBarArrowWidth + 8.DpiZoom()), + ClientRectangle.Top, SystemInformation.HorizontalScrollBarArrowWidth + 8.DpiZoom(), ClientRectangle.Height); - // 根据当前选择的项或文本内容调整控件的宽度。 private void AdjustWidth() { if (!AutoSize) return; - - int newWidth; - if (Items.Count == 0 && string.IsNullOrEmpty(Text)) - { - newWidth = MinWidth; - } - else - { - var textToMeasure = string.IsNullOrEmpty(Text) ? GetItemText(SelectedItem) : Text; - newWidth = TextRenderer.MeasureText(textToMeasure, Font).Width + TextPadding + GetDropDownButtonRect().Width; - } - - Width = Math.Max(MinWidth, Math.Min(MaxWidth, newWidth)); + int w = Items.Count == 0 && string.IsNullOrEmpty(Text) ? MinWidth : + TextRenderer.MeasureText(string.IsNullOrEmpty(Text) ? GetItemText(SelectedItem) : Text, Font).Width + TextPadding * 2 + GetDropDownButtonRect().Width; + Width = Math.Max(MinWidth, Math.Min(MaxWidth, w)); } - // 在控件首次创建时调整宽度。 protected override void OnCreateControl() { base.OnCreateControl(); @@ -501,106 +253,67 @@ protected override void OnCreateControl() UpdateColors(); } - // 当字体更改时调整宽度。 protected override void OnFontChanged(EventArgs e) { base.OnFontChanged(e); if (AutoSize) AdjustWidth(); } - // 处理主题更改事件,更新控件颜色。 - private void OnThemeChanged(object sender, EventArgs e) => UpdateColors(); - - // 释放资源。 - protected override void Dispose(bool disposing) + public void UpdateColors() { - if (disposing) + if (IsDisposed) return; + SafeInvoke(() => { - DarkModeHelper.ThemeChanged -= OnThemeChanged; - animTimer?.Dispose(); - } - base.Dispose(disposing); + BackColor = DarkModeHelper.ComboBoxBack; + ForeColor = DarkModeHelper.ComboBoxFore; + DropDownForeColor = DarkModeHelper.IsDarkTheme ? Color.White : Color.Black; + _currentBorder = _targetBorder = DarkModeHelper.ComboBoxBorder; + ArrowColor = DarkModeHelper.ComboBoxArrow; + DropDownHoverColor = DarkModeHelper.ComboBoxHover; + DropDownSelectedColor = DarkModeHelper.ComboBoxSelected; + HoverColor = FocusColor = DarkModeHelper.MainColor; + }); } - // 自定义绘制控件。 - protected override void OnPaint(PaintEventArgs e) - { - base.OnPaint(e); - e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; - - using var pen = new Pen(currentBorder, borderWidth); - var rect = new Rectangle(0, 0, Width - 1, Height - 1); - using var path = DarkModeHelper.CreateRoundedRectanglePath(rect, BorderRadius.DpiZoom()); - e.Graphics.Clear(Parent.BackColor); - - using (var brush = new SolidBrush(BackColor)) - { - e.Graphics.FillPath(brush, path); - } + private void OnThemeChanged(object s, EventArgs e) => UpdateColors(); - // 绘制边框 - e.Graphics.DrawPath(pen, path); + private void SafeInvoke(Action action) + { + if (!IsHandleCreated) return; + if (InvokeRequired) Invoke(action); else action(); + } - // 绘制文本 - var text = Text; - if (string.IsNullOrEmpty(text) && SelectedItem != null) - { - text = GetItemText(SelectedItem); - } - Rectangle textRect = new(TextPadding, 0, Width - GetDropDownButtonRect().Width - TextPadding, Height); - TextRenderer.DrawText(e.Graphics, text, Font, textRect, ForeColor, - TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis | TextFormatFlags.NoPadding); + protected override void Dispose(bool disposing) + { + if (disposing) { DarkModeHelper.ThemeChanged -= OnThemeChanged; _animTimer?.Dispose(); } + base.Dispose(disposing); + } - // 绘制箭头 - var btnRect = GetDropDownButtonRect(); - var center = new Point(btnRect.Left + btnRect.Width / 2, btnRect.Top + btnRect.Height / 2); - int s = 6.DpiZoom(); - using var arrowBrush = new SolidBrush(mouseOverDropDown || focused ? FocusColor : ArrowColor); - e.Graphics.FillPolygon(arrowBrush, new Point[] { - new(center.X - s, center.Y - s / 2), - new(center.X + s, center.Y - s / 2), - new(center.X, center.Y + s / 2) - }); + private static Color ColorLerp(Color c1, Color c2, float t) + { + t = Math.Max(0f, Math.Min(1f, t)); + return Color.FromArgb((int)(c1.A + (c2.A - c1.A) * t), (int)(c1.R + (c2.R - c1.R) * t), + (int)(c1.G + (c2.G - c1.G) * t), (int)(c1.B + (c2.B - c1.B) * t)); } - // 在 UI 线程上安全地执行操作。 - private void SafeInvoke(Action action) + public void AutosizeDropDownWidth() { - if (IsHandleCreated) - if (InvokeRequired) Invoke(action); - else action(); + int max = 0; + foreach (var item in Items) max = Math.Max(max, TextRenderer.MeasureText(item?.ToString() ?? string.Empty, Font).Width); + DropDownWidth = max + SystemInformation.VerticalScrollBarWidth; } - } - // 包含 P/Invoke 方法的 RComboBox 类的部分。 - public partial class RComboBox - { - // 使指定窗口的整个工作区无效。窗口将在下一次 `WM_PAINT` 消息时重绘。 - [LibraryImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool InvalidateRect(IntPtr hWnd, IntPtr lpRect, [MarshalAs(UnmanagedType.Bool)] bool bErase); - - // 获取有关指定组合框的信息。 - [LibraryImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool GetComboBoxInfo(IntPtr hwnd, ref COMBOBOXINFO pcbi); - - // 检索指定窗口的边框矩形的尺寸。 - [LibraryImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool GetWindowRect(IntPtr hwnd, ref RECT lpRect); - - // 创建一个具有圆角的矩形区域。 - [LibraryImport("gdi32.dll")] - internal static partial IntPtr CreateRoundRectRgn(int x1, int y1, int x2, int y2, int w, int h); - - // 将窗口的窗口区域设置为特定区域。 - [LibraryImport("user32.dll")] - internal static partial int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, [MarshalAs(UnmanagedType.Bool)] bool bRedraw); - - // 删除 GDI 对象,释放系统资源。 - [LibraryImport("gdi32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool DeleteObject(IntPtr hObject); + #region Win32 + [DllImport("user32.dll")] private static extern bool GetCursorPos(out POINT lpPoint); + [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect); + [DllImport("user32.dll")] private static extern bool InvalidateRect(IntPtr hWnd, IntPtr lpRect, bool bErase); + [DllImport("user32.dll")] private static extern bool GetComboBoxInfo(IntPtr hwnd, ref COMBOBOXINFO pcbi); + [DllImport("gdi32.dll")] private static extern IntPtr CreateRoundRectRgn(int x1, int y1, int x2, int y2, int w, int h); + [DllImport("user32.dll")] private static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw); + [DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr hObject); + [StructLayout(LayoutKind.Sequential)] private struct POINT { public int X, Y; } + [StructLayout(LayoutKind.Sequential)] private struct RECT { public int Left, Top, Right, Bottom; } + [StructLayout(LayoutKind.Sequential)] private struct COMBOBOXINFO { public int cbSize; public RECT rcItem, rcButton; public int stateButton; public IntPtr hwndCombo, hwndItem, hwndList; } + #endregion } } diff --git a/ContextMenuManager/BluePointLilac.Methods/IniReader.cs b/ContextMenuManager/BluePointLilac.Methods/IniReader.cs index d60d2777..ed2e6826 100644 --- a/ContextMenuManager/BluePointLilac.Methods/IniReader.cs +++ b/ContextMenuManager/BluePointLilac.Methods/IniReader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -31,10 +31,7 @@ public void LoadStringBuilder(StringBuilder sb) if (sb.ToString().IsNullOrWhiteSpace()) return; var lines = sb.ToString().Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries).ToList();//拆分为行 - for (int i = 0; i < lines.Count; i++) - { - lines[i] = lines[i].Trim(); - } + lines.ForEach(line => line.Trim()); ReadLines(lines); } @@ -72,10 +69,9 @@ private void ReadLines(List lines) for (var i = 0; i < indexs.Count - 1; i++) { var section = lines[indexs[i]]; - var startIndex = section.IndexOf('[') + 1; - var endIndex = section.IndexOf(']'); - if (endIndex <= startIndex) continue; - section = section.Substring(startIndex, endIndex - startIndex).Trim();// 修剪section名称 + var m = section.IndexOf(']') - 1; + if (m < 0) continue; + section = section.Substring(1, m); if (RootDic.ContainsKey(section)) continue; var keyValues = new Dictionary(StringComparer.OrdinalIgnoreCase); RootDic.Add(section, keyValues); @@ -83,9 +79,8 @@ private void ReadLines(List lines) for (var j = indexs[i] + 1; j < indexs[i + 1]; j++) { var k = lines[j].IndexOf('='); - if (k <= 0) continue; - var key = lines[j][..k].Trim();// 修剪key名称 - var value = lines[j][(k + 1)..].Trim();// 修剪value值 + var key = lines[j][..k].TrimEnd(); + var value = lines[j][(k + 1)..].TrimStart(); if (keyValues.ContainsKey(key)) continue; keyValues.Add(key, value); } diff --git a/ContextMenuManager/Controls/AboutAppBox.cs b/ContextMenuManager/Controls/AboutAppBox.cs deleted file mode 100644 index 276356fb..00000000 --- a/ContextMenuManager/Controls/AboutAppBox.cs +++ /dev/null @@ -1,212 +0,0 @@ -using BluePointLilac.Controls; -using BluePointLilac.Methods; -using ContextMenuManager.Methods; -using ContextMenuManager.Properties; -using System; -using System.Drawing; -using System.IO; -using System.Windows.Forms; - -namespace ContextMenuManager.Controls -{ - internal sealed class AboutAppBox : Panel - { - public AboutAppBox() - { - SuspendLayout(); - Dock = DockStyle.Fill; - BackColor = DarkModeHelper.FormBack; - Font = SystemFonts.MenuFont; - Font = new Font(Font.FontFamily, Font.Size + 1F); - - // 重置所有控件属性,使用最简单可靠的设置 - - // 设置logo图片 - pbLogo.Image = Resources.Logo; - pbLogo.SizeMode = PictureBoxSizeMode.Zoom; - pbLogo.Size = new Size(100, 100); - pbLogo.Visible = true; - - // 设置标题标签 - lblTitle.Text = AppString.General.AppName; - lblTitle.Font = new Font(Font.FontFamily, Font.Size + 3F, FontStyle.Bold); - lblTitle.ForeColor = Color.Orange; - lblTitle.TextAlign = ContentAlignment.MiddleCenter; - lblTitle.Size = new Size(400, 30); - lblTitle.Visible = true; - - // 设置描述标签 - 重写为更简单可靠的设置 - lblDescription.Text = AppString.About.Description; // 使用多语言文本 - lblDescription.TextAlign = ContentAlignment.MiddleCenter; - lblDescription.Size = new Size(300, 30); // 调整高度为30像素 - lblDescription.Visible = true; - lblDescription.ForeColor = DarkModeHelper.FormFore; - lblDescription.BackColor = Color.Transparent; - lblDescription.BorderStyle = BorderStyle.None; - - // 设置GitHub标签 - lblGitHub.Text = $"{AppString.About.GitHub}: https://github.com/Jack251970/ContextMenuManager"; // 使用多语言文本 - lblGitHub.TextAlign = ContentAlignment.MiddleCenter; - lblGitHub.ForeColor = Color.Orange; - lblGitHub.Cursor = Cursors.Hand; - lblGitHub.Size = new Size(400, 30); - lblGitHub.Visible = true; - lblGitHub.MouseDown += (sender, e) => ExternalProgram.OpenWebUrl("https://github.com/Jack251970/ContextMenuManager"); - - // 设置Gitee标签 - lblGitee.Text = $"{AppString.About.Gitee}: https://gitee.com/Jack251970/ContextMenuManager"; // 使用多语言文本 - lblGitee.TextAlign = ContentAlignment.MiddleCenter; - lblGitee.ForeColor = Color.Orange; - lblGitee.Cursor = Cursors.Hand; - lblGitee.Size = new Size(400, 30); - lblGitee.Visible = true; - lblGitee.MouseDown += (sender, e) => ExternalProgram.OpenWebUrl("https://gitee.com/Jack251970/ContextMenuManager"); - - // 设置许可证标签 - lblLicense.Text = $"{AppString.About.License}: GPL License"; // 使用多语言文本 - lblLicense.TextAlign = ContentAlignment.MiddleCenter; - lblLicense.Size = new Size(400, 30); - lblLicense.Visible = true; - - // 设置检查更新按钮 - btnCheckUpdate.Text = AppString.About.CheckUpdate; // 使用多语言文本 - btnCheckUpdate.Size = new Size(210, 40); - btnCheckUpdate.BackColor = Color.Orange; - btnCheckUpdate.ForeColor = Color.White; - btnCheckUpdate.FlatStyle = FlatStyle.Flat; - btnCheckUpdate.FlatAppearance.BorderSize = 0; - btnCheckUpdate.Cursor = Cursors.Hand; - btnCheckUpdate.Visible = true; - btnCheckUpdate.Click += (sender, e) => Updater.Update(true); - - // 直接在面板上添加控件 - Controls.AddRange(new Control[] { pbLogo, lblTitle, lblDescription, lblGitHub, lblGitee, lblLicense, btnCheckUpdate }); - - // 监听主题变化事件 - DarkModeHelper.ThemeChanged += OnThemeChanged; - - // 设置控件初始颜色 - UpdateControlColors(); - - // 监听大小变化,调整控件位置和宽度(实现垂直居中) - Resize += (sender, e) => { - // 计算各个控件之间的间距 - const int spacingLogoTitle = 20; - const int spacingTitleDesc = 10; - const int spacingDescGitHub = 15; - const int spacingGitHubGitee = 10; - const int spacingGiteeLicense = 10; - const int spacingLicenseBtn = 30; - - // 计算总高度 - int totalHeight = pbLogo.Height + spacingLogoTitle + - lblTitle.Height + spacingTitleDesc + - lblDescription.Height + spacingDescGitHub + - lblGitHub.Height + spacingGitHubGitee + - lblGitee.Height + spacingGiteeLicense + - lblLicense.Height + spacingLicenseBtn + - btnCheckUpdate.Height; - - // 计算起始Y坐标,实现垂直居中 - int startY = (Height - totalHeight) / 2; - - // 设置各个控件的位置 - pbLogo.Location = new Point((Width - pbLogo.Width) / 2, startY); - - lblTitle.Location = new Point(0, pbLogo.Bottom + spacingLogoTitle); - lblTitle.Width = Width; - - lblDescription.Location = new Point(50, lblTitle.Bottom + spacingTitleDesc); - lblDescription.Width = Width - 100; - - lblGitHub.Location = new Point(0, lblDescription.Bottom + spacingDescGitHub); - lblGitHub.Width = Width; - - lblGitee.Location = new Point(0, lblGitHub.Bottom + spacingGitHubGitee); - lblGitee.Width = Width; - - lblLicense.Location = new Point(0, lblGitee.Bottom + spacingGiteeLicense); - lblLicense.Width = Width; - - btnCheckUpdate.Location = new Point((Width - btnCheckUpdate.Width) / 2, lblLicense.Bottom + spacingLicenseBtn); - }; - - // 初始布局 - OnResize(null); - - ResumeLayout(); - } - - private readonly PictureBox pbLogo = new(); - private readonly Label lblTitle = new(); - private readonly Label lblGitHub = new(); - private readonly Label lblGitee = new(); // 添加Gitee标签 - private readonly Label lblLicense = new(); - private readonly Label lblDescription = new(); - private readonly Button btnCheckUpdate = new(); - - public void LoadAboutInfo() - { - // 恢复使用多语言文本 - lblTitle.Text = AppString.General.AppName; - - // 手动加载About类的文本,确保它们被正确初始化 - string description = AppString.About.Description; - string gitHub = AppString.About.GitHub; - string gitee = AppString.About.Gitee; - string license = AppString.About.License; - string checkUpdate = AppString.About.CheckUpdate; - - // 添加默认值支持,确保即使语言文件加载失败也能正常显示 - if (string.IsNullOrEmpty(description)) description = "一个纯粹的Windows右键菜单管理器"; - if (string.IsNullOrEmpty(gitHub)) gitHub = "GitHub"; - if (string.IsNullOrEmpty(gitee)) gitee = "Gitee"; - if (string.IsNullOrEmpty(license)) license = "许可证"; - if (string.IsNullOrEmpty(checkUpdate)) checkUpdate = "检查更新"; - - // 设置控件文本 - lblDescription.Text = description; - lblGitHub.Text = $"{gitHub}: https://github.com/Jack251970/ContextMenuManager"; - lblGitee.Text = $"{gitee}: https://gitee.com/Jack251970/ContextMenuManager"; - lblLicense.Text = $"{license}: GPL License"; - btnCheckUpdate.Text = checkUpdate; - - // 确保控件可见 - Visible = true; - } - - // 主题变化事件处理程序 - private void OnThemeChanged(object sender, EventArgs e) - { - UpdateControlColors(); - } - - // 更新控件颜色以适应主题变化 - private void UpdateControlColors() - { - // 更新面板背景色 - BackColor = DarkModeHelper.FormBack; - - // 更新标签文字颜色 - lblTitle.ForeColor = Color.Orange; // 保持橘色 - lblDescription.ForeColor = DarkModeHelper.FormFore; - lblGitHub.ForeColor = Color.Orange; // 保持橘色 - lblGitee.ForeColor = Color.Orange; // 保持橘色 - lblLicense.ForeColor = DarkModeHelper.FormFore; - - // 更新按钮颜色 - btnCheckUpdate.BackColor = Color.Orange; // 保持橘色 - btnCheckUpdate.ForeColor = Color.White; // 保持白色文字 - } - - // 重写Dispose方法,取消订阅事件 - protected override void Dispose(bool disposing) - { - if (disposing) - { - DarkModeHelper.ThemeChanged -= OnThemeChanged; - } - base.Dispose(disposing); - } - } -} \ No newline at end of file diff --git a/ContextMenuManager/Controls/ShellItem.cs b/ContextMenuManager/Controls/ShellItem.cs index 68808c29..84efbdbc 100644 --- a/ContextMenuManager/Controls/ShellItem.cs +++ b/ContextMenuManager/Controls/ShellItem.cs @@ -1,4 +1,4 @@ -using BluePointLilac.Controls; +using BluePointLilac.Controls; using BluePointLilac.Methods; using ContextMenuManager.Controls.Interfaces; using ContextMenuManager.Methods; @@ -38,6 +38,15 @@ public ShellItem(string regPath) RegPath = regPath; } + public ShellItem(string regPath, string text, Image image, bool isMultiItem) + { + InitializeComponents(); + this.regPath = regPath; + this.Text = text; + this.Image = image; + BtnSubItems.Visible = isMultiItem; + } + private string regPath; public string RegPath { diff --git a/ContextMenuManager/Controls/ShellList.cs b/ContextMenuManager/Controls/ShellList.cs index 0a180ee9..5a520261 100644 --- a/ContextMenuManager/Controls/ShellList.cs +++ b/ContextMenuManager/Controls/ShellList.cs @@ -9,41 +9,51 @@ using System.Drawing; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using System.Windows.Forms; using System.Xml; namespace ContextMenuManager.Controls { - public sealed class ShellList : MyList // 文件类型 Ink文件 uwp Ink exe文件 未知格式 // 主页 第一栏 + public sealed class ShellList : MyList { - public const string MENUPATH_FILE = @"HKEY_CLASSES_ROOT\*";//文件 - public const string MENUPATH_FOLDER = @"HKEY_CLASSES_ROOT\Folder";//文件夹 - public const string MENUPATH_DIRECTORY = @"HKEY_CLASSES_ROOT\Directory";//目录 - public const string MENUPATH_BACKGROUND = @"HKEY_CLASSES_ROOT\Directory\Background";//目录背景 - public const string MENUPATH_DESKTOP = @"HKEY_CLASSES_ROOT\DesktopBackground";//桌面背景 - public const string MENUPATH_DRIVE = @"HKEY_CLASSES_ROOT\Drive";//磁盘分区 - public const string MENUPATH_ALLOBJECTS = @"HKEY_CLASSES_ROOT\AllFilesystemObjects";//所有对象 - public const string MENUPATH_COMPUTER = @"HKEY_CLASSES_ROOT\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}";//此电脑 - public const string MENUPATH_RECYCLEBIN = @"HKEY_CLASSES_ROOT\CLSID\{645FF040-5081-101B-9F08-00AA002F954E}";//回收站 - - public const string MENUPATH_LIBRARY = @"HKEY_CLASSES_ROOT\LibraryFolder";//库 - public const string MENUPATH_LIBRARY_BACKGROUND = @"HKEY_CLASSES_ROOT\LibraryFolder\Background";//库背景 - public const string MENUPATH_LIBRARY_USER = @"HKEY_CLASSES_ROOT\UserLibraryFolder";//用户库 - - public const string MENUPATH_UWPLNK = @"HKEY_CLASSES_ROOT\Launcher.ImmersiveApplication";//UWP快捷方式 - public const string MENUPATH_UNKNOWN = @"HKEY_CLASSES_ROOT\Unknown";//未知格式 - public const string SYSFILEASSPATH = @"HKEY_CLASSES_ROOT\SystemFileAssociations";//系统扩展名注册表父项路径 - private const string LASTKEYPATH = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit";//上次打开的注册表项路径记录 - - public static readonly List DirectoryTypes = new() + public const string MENUPATH_FILE = @"HKEY_CLASSES_ROOT\*"; + public const string MENUPATH_FOLDER = @"HKEY_CLASSES_ROOT\Folder"; + public const string MENUPATH_DIRECTORY = @"HKEY_CLASSES_ROOT\Directory"; + public const string MENUPATH_BACKGROUND = @"HKEY_CLASSES_ROOT\Directory\Background"; + public const string MENUPATH_DESKTOP = @"HKEY_CLASSES_ROOT\DesktopBackground"; + public const string MENUPATH_DRIVE = @"HKEY_CLASSES_ROOT\Drive"; + public const string MENUPATH_ALLOBJECTS = @"HKEY_CLASSES_ROOT\AllFilesystemObjects"; + public const string MENUPATH_COMPUTER = @"HKEY_CLASSES_ROOT\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}"; + public const string MENUPATH_RECYCLEBIN = @"HKEY_CLASSES_ROOT\CLSID\{645FF040-5081-101B-9F08-00AA002F954E}"; + public const string MENUPATH_LIBRARY = @"HKEY_CLASSES_ROOT\LibraryFolder"; + public const string MENUPATH_LIBRARY_BACKGROUND = @"HKEY_CLASSES_ROOT\LibraryFolder\Background"; + public const string MENUPATH_LIBRARY_USER = @"HKEY_CLASSES_ROOT\UserLibraryFolder"; + public const string MENUPATH_UWPLNK = @"HKEY_CLASSES_ROOT\Launcher.ImmersiveApplication"; + public const string MENUPATH_UNKNOWN = @"HKEY_CLASSES_ROOT\Unknown"; + public const string SYSFILEASSPATH = @"HKEY_CLASSES_ROOT\SystemFileAssociations"; + private const string LASTKEYPATH = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit"; + + public event EventHandler ItemsLoaded; + private CancellationTokenSource cts; + + private struct ShellItemData { - "Document", "Image", "Video", "Audio" - }; - public static readonly List PerceivedTypes = new() + public string RegPath; + public string Text; + public Image Image; + public bool IsMultiItem; + } + + private static readonly Dictionary DefaultNameIndexs = new(StringComparer.OrdinalIgnoreCase) { - null, "Text", "Document", "Image", - "Video", "Audio", "Compressed", "System" + { "open", 8496 }, { "edit", 8516 }, { "print", 8497 }, { "find", 8503 }, + { "play", 8498 }, { "runas", 8505 }, { "explore", 8502 }, { "preview", 8499 } }; + + public static readonly List DirectoryTypes = new() { "Document", "Image", "Video", "Audio" }; + public static readonly List PerceivedTypes = new() { null, "Text", "Document", "Image", "Video", "Audio", "Compressed", "System" }; private static readonly string[] PerceivedTypeNames = { AppString.Dialog.NoPerceivedType, AppString.Dialog.TextFile, AppString.Dialog.DocumentFile, AppString.Dialog.ImageFile, @@ -55,27 +65,16 @@ public sealed class ShellList : MyList // 文件类型 Ink文件 uwp Ink exe文 AppString.Dialog.VideoDirectory, AppString.Dialog.AudioDirectory }; - private static string GetDirectoryTypeName(string directoryType) - { - if (directoryType == null) return null; - var index = DirectoryTypes.FindIndex(type => directoryType.Equals(type, StringComparison.OrdinalIgnoreCase)); - if (index >= 0) return DirectoryTypeNames[index]; - else return null; - } + private static string GetDirectoryTypeName(string directoryType) => + directoryType == null ? null : DirectoryTypes.FindIndex(type => directoryType.Equals(type, StringComparison.OrdinalIgnoreCase)) is var idx && idx >= 0 ? DirectoryTypeNames[idx] : null; private static string GetPerceivedTypeName(string perceivedType) { - var index = 0; - if (perceivedType != null) index = PerceivedTypes.FindIndex(type => perceivedType.Equals(type, StringComparison.OrdinalIgnoreCase)); - if (index == -1) index = 0; - return PerceivedTypeNames[index]; + var index = perceivedType != null ? PerceivedTypes.FindIndex(type => perceivedType.Equals(type, StringComparison.OrdinalIgnoreCase)) : 0; + return PerceivedTypeNames[index == -1 ? 0 : index]; } - private static readonly string[] DropEffectPaths = - { - MENUPATH_FILE, MENUPATH_ALLOBJECTS, - MENUPATH_FOLDER, MENUPATH_DIRECTORY - }; + private static readonly string[] DropEffectPaths = { MENUPATH_FILE, MENUPATH_ALLOBJECTS, MENUPATH_FOLDER, MENUPATH_DIRECTORY }; private static readonly string[] DropEffectNames = { AppString.Dialog.DefaultDropEffect, AppString.Dialog.CopyDropEffect, @@ -91,150 +90,96 @@ public static DropEffect DefaultDropEffect foreach (var path in DropEffectPaths) { var value = Registry.GetValue(path, "DefaultDropEffect", null); - if (value != null) - { - switch (value) - { - case 1: - return DropEffect.Copy; - case 2: - return DropEffect.Move; - case 4: - return DropEffect.CreateLink; - } - } + if (value is int i && i is >= 1 and <= 4) + return (DropEffect)i; } return DropEffect.Default; } set { - var data = value switch - { - DropEffect.Copy => 1, - DropEffect.Move => 2, - DropEffect.CreateLink => 4, - _ => (object)0, - }; + var data = value switch { DropEffect.Copy => 1, DropEffect.Move => 2, DropEffect.CreateLink => 4, _ => 0 }; foreach (var path in DropEffectPaths) - { Registry.SetValue(path, "DefaultDropEffect", data, RegistryValueKind.DWord); - } } } - private static string GetDropEffectName() - { - return DefaultDropEffect switch - { - DropEffect.Copy => DropEffectNames[1], - DropEffect.Move => DropEffectNames[2], - DropEffect.CreateLink => DropEffectNames[3], - _ => DropEffectNames[0], - }; - } + private static string GetDropEffectName() => DropEffectNames[(int)DefaultDropEffect]; - private static string CurrentExtension = null; - private static string CurrentDirectoryType = null; - private static string CurrentPerceivedType = null; - public static string CurrentCustomRegPath = null; - public static string CurrentFileObjectPath = null; + private static string CurrentExtension, CurrentDirectoryType, CurrentPerceivedType; + public static string CurrentCustomRegPath, CurrentFileObjectPath; private static string CurrentExtensionPerceivedType { get => GetPerceivedType(CurrentExtension); set { - var path = $@"{RegistryEx.CLASSES_ROOT}\{CurrentExtension}"; + var path = $"{RegistryEx.CLASSES_ROOT}\\{CurrentExtension}"; if (value == null) RegistryEx.DeleteValue(path, "PerceivedType"); else Registry.SetValue(path, "PerceivedType", value, RegistryValueKind.String); } } - public static string GetShellPath(string scenePath) - { - return $@"{scenePath}\shell"; - } + public static string GetShellPath(string scenePath) => $"{scenePath}\\shell"; + public static string GetShellExPath(string scenePath) => $"{scenePath}\\ShellEx"; + public static string GetSysAssExtPath(string typeName) => typeName != null ? $"{SYSFILEASSPATH}\\{typeName}" : null; + private static string GetOpenMode(string extension) => FileExtension.GetOpenMode(extension); + public static string GetOpenModePath(string extension) => extension != null ? $"{RegistryEx.CLASSES_ROOT}\\{GetOpenMode(extension)}" : null; + private static string GetPerceivedType(string extension) => Registry.GetValue($"{RegistryEx.CLASSES_ROOT}\\{extension}", "PerceivedType", null)?.ToString(); - public static string GetShellExPath(string scenePath) - { - return $@"{scenePath}\ShellEx"; - } + public Scenes Scene { get; set; } - public static string GetSysAssExtPath(string typeName) + public void LoadItems() { - return typeName != null ? $@"{SYSFILEASSPATH}\{typeName}" : null; - } + var scenePath = GetScenePath(Scene); + if (scenePath == string.Empty) return; + if (scenePath == "SPECIAL") + { + HandleSpecialScenes(); + return; + } - private static string GetOpenMode(string extension) - { - return FileExtension.GetOpenMode(extension); - } + if (WinOsVersion.Current >= WinOsVersion.Win11 && Scene is Scenes.File or Scenes.Folder or Scenes.Directory or Scenes.Background or Scenes.Desktop or Scenes.Drive or Scenes.AllObjects or Scenes.Computer or Scenes.RecycleBin or Scenes.Library) + AddItem(new SwitchContextMenuStyleItem()); - public static string GetOpenModePath(string extension) - { - return extension != null ? $@"{RegistryEx.CLASSES_ROOT}\{GetOpenMode(extension)}" : null; - } + AddNewItem(scenePath); + LoadItems(scenePath); - private static string GetPerceivedType(string extension) - { - return Registry.GetValue($@"{RegistryEx.CLASSES_ROOT}\{extension}", "PerceivedType", null)?.ToString(); + if (WinOsVersion.Current >= WinOsVersion.Win10) LoadUwpModeItem(); + + AddSceneSpecificItems(scenePath); } - public Scenes Scene { get; set; } + private string GetScenePath(Scenes scene) => scene switch + { + Scenes.File => MENUPATH_FILE, + Scenes.Folder => MENUPATH_FOLDER, + Scenes.Directory => MENUPATH_DIRECTORY, + Scenes.Background => MENUPATH_BACKGROUND, + Scenes.Desktop => WinOsVersion.Current == WinOsVersion.Vista ? string.Empty : MENUPATH_DESKTOP, + Scenes.Drive => MENUPATH_DRIVE, + Scenes.AllObjects => MENUPATH_ALLOBJECTS, + Scenes.Computer => MENUPATH_COMPUTER, + Scenes.RecycleBin => MENUPATH_RECYCLEBIN, + Scenes.Library => WinOsVersion.Current == WinOsVersion.Vista ? string.Empty : MENUPATH_LIBRARY, + Scenes.LnkFile => GetOpenModePath(".lnk"), + Scenes.UwpLnk => WinOsVersion.Current < WinOsVersion.Win8 ? string.Empty : MENUPATH_UWPLNK, + Scenes.ExeFile => GetSysAssExtPath(".exe"), + Scenes.UnknownType => MENUPATH_UNKNOWN, + Scenes.CustomExtension => CurrentExtension?.ToLower() == ".lnk" ? GetOpenModePath(".lnk") : GetSysAssExtPath(CurrentExtension), + Scenes.PerceivedType => GetSysAssExtPath(CurrentPerceivedType), + Scenes.DirectoryType => CurrentDirectoryType == null ? null : GetSysAssExtPath($"Directory.{CurrentDirectoryType}"), + Scenes.MenuAnalysis or Scenes.DragDrop or Scenes.PublicReferences or Scenes.CustomRegPath => "SPECIAL", + _ => null + }; - public void LoadItems() + private void HandleSpecialScenes() { - string scenePath = null; switch (Scene) { - case Scenes.File: - scenePath = MENUPATH_FILE; break; - case Scenes.Folder: - scenePath = MENUPATH_FOLDER; break; - case Scenes.Directory: - scenePath = MENUPATH_DIRECTORY; break; - case Scenes.Background: - scenePath = MENUPATH_BACKGROUND; break; - case Scenes.Desktop: - //Vista系统没有这一项 - if (WinOsVersion.Current == WinOsVersion.Vista) return; - scenePath = MENUPATH_DESKTOP; break; - case Scenes.Drive: - scenePath = MENUPATH_DRIVE; break; - case Scenes.AllObjects: - scenePath = MENUPATH_ALLOBJECTS; break; - case Scenes.Computer: - scenePath = MENUPATH_COMPUTER; break; - case Scenes.RecycleBin: - scenePath = MENUPATH_RECYCLEBIN; break; - case Scenes.Library: - //Vista系统没有这一项 - if (WinOsVersion.Current == WinOsVersion.Vista) return; - scenePath = MENUPATH_LIBRARY; break; - case Scenes.LnkFile: - scenePath = GetOpenModePath(".lnk"); break; - case Scenes.UwpLnk: - //Win8之前没有Uwp - if (WinOsVersion.Current < WinOsVersion.Win8) return; - scenePath = MENUPATH_UWPLNK; break; - case Scenes.ExeFile: - scenePath = GetSysAssExtPath(".exe"); break; - case Scenes.UnknownType: - scenePath = MENUPATH_UNKNOWN; break; - case Scenes.CustomExtension: - var isLnk = CurrentExtension?.ToLower() == ".lnk"; - if (isLnk) scenePath = GetOpenModePath(".lnk"); - else scenePath = GetSysAssExtPath(CurrentExtension); - break; - case Scenes.PerceivedType: - scenePath = GetSysAssExtPath(CurrentPerceivedType); break; - case Scenes.DirectoryType: - if (CurrentDirectoryType == null) scenePath = null; - else scenePath = GetSysAssExtPath($"Directory.{CurrentDirectoryType}"); break; case Scenes.MenuAnalysis: AddItem(new SelectItem(Scene)); LoadAnalysisItems(); - return; + break; case Scenes.DragDrop: AddItem(new SelectItem(Scene)); AddNewItem(MENUPATH_FOLDER); @@ -242,67 +187,33 @@ public void LoadItems() LoadShellExItems(GetShellExPath(MENUPATH_DIRECTORY)); LoadShellExItems(GetShellExPath(MENUPATH_DRIVE)); LoadShellExItems(GetShellExPath(MENUPATH_ALLOBJECTS)); - return; + break; case Scenes.PublicReferences: - //Vista系统没有这一项 if (WinOsVersion.Current == WinOsVersion.Vista) return; AddNewItem(RegistryEx.GetParentPath(ShellItem.CommandStorePath)); LoadStoreItems(); - return; + break; case Scenes.CustomRegPath: - scenePath = CurrentCustomRegPath; break; - } - //Win11系统切换旧版菜单 - if (WinOsVersion.Current >= WinOsVersion.Win11) - { - switch (Scene) - { - case Scenes.File: - case Scenes.Folder: - case Scenes.Directory: - case Scenes.Background: - case Scenes.Desktop: - case Scenes.Drive: - case Scenes.AllObjects: - case Scenes.Computer: - case Scenes.RecycleBin: - case Scenes.Library: - AddItem(new SwitchContextMenuStyleItem()); break; - } - } - AddNewItem(scenePath); // 新建一个菜单项目 - LoadItems(scenePath); - if (WinOsVersion.Current >= WinOsVersion.Win10) - { - LoadUwpModeItem(); + AddNewItem(CurrentCustomRegPath); + LoadItems(CurrentCustomRegPath); + break; } + } + + private void AddSceneSpecificItems(string scenePath) + { switch (Scene) { - case Scenes.Background: - var item = new VisibleRegRuleItem(VisibleRegRuleItem.CustomFolder); - AddItem(item); - break; - case Scenes.Computer: - item = new VisibleRegRuleItem(VisibleRegRuleItem.NetworkDrive); - AddItem(item); - break; - case Scenes.RecycleBin: - item = new VisibleRegRuleItem(VisibleRegRuleItem.RecycleBinProperties); - AddItem(item); - break; + case Scenes.Background: AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.CustomFolder)); break; + case Scenes.Computer: AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.NetworkDrive)); break; + case Scenes.RecycleBin: AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.RecycleBinProperties)); break; case Scenes.Library: LoadItems(MENUPATH_LIBRARY_BACKGROUND); LoadItems(MENUPATH_LIBRARY_USER); break; - case Scenes.ExeFile: - LoadItems(GetOpenModePath(".exe")); - break; - case Scenes.CustomExtension: - case Scenes.PerceivedType: - case Scenes.DirectoryType: - case Scenes.CustomRegPath: - InsertItem(new SelectItem(Scene), 0); // 请选择一个文件扩展名项目 - // 自选文件扩展名后加载对应的右键菜单 + case Scenes.ExeFile: LoadItems(GetOpenModePath(".exe")); break; + case Scenes.CustomExtension or Scenes.PerceivedType or Scenes.DirectoryType or Scenes.CustomRegPath: + InsertItem(new SelectItem(Scene), 0); if (Scene == Scenes.CustomExtension && CurrentExtension != null) { LoadItems(GetOpenModePath(CurrentExtension)); @@ -315,79 +226,133 @@ public void LoadItems() private void LoadItems(string scenePath) { if (scenePath == null) return; - RegTrustedInstaller.TakeRegKeyOwnerShip(scenePath); - LoadShellItems(GetShellPath(scenePath)); - LoadShellExItems(GetShellExPath(scenePath)); + cts?.Cancel(); + cts = new CancellationTokenSource(); + var token = cts.Token; + + Task.Run(() => + { + if (token.IsCancellationRequested) return; + RegTrustedInstaller.TakeRegKeyOwnerShip(scenePath); + var shellItemsData = GetShellItemsData(GetShellPath(scenePath)); + if (token.IsCancellationRequested) return; + + Invoke(new Action(() => + { + if (token.IsCancellationRequested) return; + foreach (var data in shellItemsData) + AddItem(new ShellItem(data.RegPath, data.Text, data.Image, data.IsMultiItem)); + LoadShellExItems(GetShellExPath(scenePath)); + ItemsLoaded?.Invoke(this, EventArgs.Empty); + })); + }, token); } - private void LoadShellItems(string shellPath) + private List GetShellItemsData(string shellPath) { + var list = new List(); using var shellKey = RegistryEx.GetRegistryKey(shellPath); - if (shellKey == null) return; - RegTrustedInstaller.TakeRegTreeOwnerShip(shellKey.Name); + if (shellKey == null) return list; + foreach (var keyName in shellKey.GetSubKeyNames()) { - var item = new ShellItem($@"{shellPath}\{keyName}"); - AddItem(item); + var regPath = $"{shellPath}\\{keyName}"; + list.Add(new ShellItemData + { + RegPath = regPath, + Text = GetItemText(regPath, keyName), + Image = GetItemIcon(regPath), + IsMultiItem = GetIsMultiItem(regPath) + }); } + return list; + } + + private bool GetIsMultiItem(string regPath) => + Registry.GetValue(regPath, "SubCommands", null) != null || + !string.IsNullOrEmpty(Registry.GetValue(regPath, "ExtendedSubCommandsKey", null)?.ToString()); + + private string GetItemText(string regPath, string keyName) + { + foreach (var valueName in new[] { "MUIVerb", "" }) + { + var name = ResourceString.GetDirectString(Registry.GetValue(regPath, valueName, null)?.ToString()); + if (!string.IsNullOrEmpty(name)) return name; + } + if (DefaultNameIndexs.TryGetValue(RegistryEx.GetKeyName(regPath), out var index)) + { + var name = ResourceString.GetDirectString($"@windows.storage.dll,-{index}"); + if (!string.IsNullOrEmpty(name)) return name; + } + return RegistryEx.GetKeyName(regPath); + } + + private Image GetItemIcon(string regPath) + { + var iconLocation = Registry.GetValue(regPath, "Icon", null)?.ToString(); + var hasLUAShield = Registry.GetValue(regPath, "HasLUAShield", null) != null; + var commandPath = $"{regPath}\\command"; + + Guid guid = Guid.Empty; + foreach (var item in new Dictionary { { commandPath, "DelegateExecute" }, { $"{regPath}\\DropTarget", "CLSID" }, { regPath, "ExplorerCommandHandler" } }) + if (GuidEx.TryParse(Registry.GetValue(item.Key, item.Value, null)?.ToString(), out var g)) { guid = g; break; } + + var itemCommand = !GetIsMultiItem(regPath) ? Registry.GetValue(commandPath, "", null)?.ToString() : null; + var itemFilePath = GuidInfo.GetFilePath(guid) ?? ObjectPath.ExtractFilePath(itemCommand); + + Icon icon = iconLocation != null ? ResourceIcon.GetIcon(iconLocation, out var iconPath, out _) ?? (Path.GetExtension(iconPath)?.ToLower() == ".exe" ? ResourceIcon.GetIcon("imageres.dll", -15) : null) + : hasLUAShield ? ResourceIcon.GetIcon("imageres.dll", -78) + : ResourceIcon.GetIcon(itemFilePath, 0); + + icon ??= ResourceIcon.GetExtensionIcon(itemFilePath) ?? ResourceIcon.GetIcon("imageres.dll", -2); + var image = icon.ToBitmap(); + return iconLocation == null && !hasLUAShield ? image.ToTransparent() : image; } private void LoadShellExItems(string shellExPath) { - var names = new List(); using var shellExKey = RegistryEx.GetRegistryKey(shellExPath); if (shellExKey == null) return; + var isDragDrop = Scene == Scenes.DragDrop; RegTrustedInstaller.TakeRegTreeOwnerShip(shellExKey.Name); var dic = ShellExItem.GetPathAndGuids(shellExPath, isDragDrop); + var names = new List(); FoldGroupItem groupItem = null; + if (isDragDrop) { groupItem = GetDragDropGroupItem(shellExPath); AddItem(groupItem); } + foreach (var path in dic.Keys) { var keyName = RegistryEx.GetKeyName(path); - if (!names.Contains(keyName)) + if (names.Contains(keyName)) continue; + var item = new ShellExItem(dic[path], path); + if (groupItem != null) { - var item = new ShellExItem(dic[path], path); - if (groupItem != null) - { - item.FoldGroupItem = groupItem; - item.Indent(); - } - AddItem(item); - names.Add(keyName); + item.FoldGroupItem = groupItem; + item.Indent(); } + AddItem(item); + names.Add(keyName); } groupItem?.SetVisibleWithSubItemCount(); } private FoldGroupItem GetDragDropGroupItem(string shellExPath) { - string text = null; - Image image = null; var path = shellExPath[..shellExPath.LastIndexOf('\\')]; - switch (path) + var (text, image) = path switch { - case MENUPATH_FOLDER: - text = AppString.SideBar.Folder; - image = AppImage.Folder; - break; - case MENUPATH_DIRECTORY: - text = AppString.SideBar.Directory; - image = AppImage.Directory; - break; - case MENUPATH_DRIVE: - text = AppString.SideBar.Drive; - image = AppImage.Drive; - break; - case MENUPATH_ALLOBJECTS: - text = AppString.SideBar.AllObjects; - image = AppImage.AllObjects; - break; - } + MENUPATH_FOLDER => (AppString.SideBar.Folder, AppImage.Folder), + MENUPATH_DIRECTORY => (AppString.SideBar.Directory, AppImage.Directory), + MENUPATH_DRIVE => (AppString.SideBar.Drive, AppImage.Drive), + MENUPATH_ALLOBJECTS => (AppString.SideBar.AllObjects, AppImage.AllObjects), + _ => (null, null) + }; return new FoldGroupItem(shellExPath, ObjectPath.PathType.Registry) { Text = text, Image = image }; } @@ -397,7 +362,7 @@ private void LoadStoreItems() foreach (var itemName in shellKey.GetSubKeyNames()) { if (AppConfig.HideSysStoreItems && itemName.StartsWith("Windows.", StringComparison.OrdinalIgnoreCase)) continue; - AddItem(new StoreShellItem($@"{ShellItem.CommandStorePath}\{itemName}", true, false)); + AddItem(new StoreShellItem($"{ShellItem.CommandStorePath}\\{itemName}", true, false)); } } @@ -408,24 +373,13 @@ private void LoadUwpModeItem() if (doc?.DocumentElement == null) continue; foreach (XmlNode sceneXN in doc.DocumentElement.ChildNodes) { - if (sceneXN.Name == Scene.ToString()) + if (sceneXN.Name != Scene.ToString()) continue; + foreach (XmlElement itemXE in sceneXN.ChildNodes) { - foreach (XmlElement itemXE in sceneXN.ChildNodes) - { - if (GuidEx.TryParse(itemXE.GetAttribute("Guid"), out var guid)) - { - var isAdded = false; - foreach (Control ctr in Controls) - { - if (ctr is UwpModeItem item && item.Guid == guid) { isAdded = true; break; } - } - if (isAdded) continue; - if (GuidInfo.GetFilePath(guid) == null) continue; - var uwpName = GuidInfo.GetUwpName(guid); - var uwpItem = new UwpModeItem(uwpName, guid); - AddItem(uwpItem); - } - } + if (!GuidEx.TryParse(itemXE.GetAttribute("Guid"), out var guid)) continue; + if (Controls.Cast().Any(ctr => ctr is UwpModeItem item && item.Guid == guid)) continue; + if (GuidInfo.GetFilePath(guid) == null) continue; + AddItem(new UwpModeItem(GuidInfo.GetUwpName(guid), guid)); } } } @@ -440,14 +394,12 @@ void AddFileItems(string filePath) var extension = Path.GetExtension(filePath).ToLower(); if (extension == string.Empty) extension = "."; var perceivedType = GetPerceivedType(extension); - var perceivedTypeName = GetPerceivedTypeName(perceivedType); JumpItem.TargetPath = filePath; JumpItem.Extension = extension; JumpItem.PerceivedType = perceivedType; AddItem(new JumpItem(Scenes.File)); AddItem(new JumpItem(Scenes.AllObjects)); - if (extension == ".exe") AddItem(new JumpItem(Scenes.ExeFile)); - else AddItem(new JumpItem(Scenes.CustomExtension)); + AddItem(new JumpItem(extension == ".exe" ? Scenes.ExeFile : Scenes.CustomExtension)); if (GetOpenMode(extension) == null) AddItem(new JumpItem(Scenes.UnknownType)); if (perceivedType != null) AddItem(new JumpItem(Scenes.PerceivedType)); } @@ -473,12 +425,10 @@ void AddDirItems(string dirPath) var extension = Path.GetExtension(CurrentFileObjectPath).ToLower(); if (extension == ".lnk") { - using (var shellLink = new ShellLink(CurrentFileObjectPath)) - { - var targetPath = shellLink.TargetPath; - if (File.Exists(targetPath)) AddFileItems(targetPath); - else if (Directory.Exists(targetPath)) AddDirItems(targetPath); - } + using var shellLink = new ShellLink(CurrentFileObjectPath); + var targetPath = shellLink.TargetPath; + if (File.Exists(targetPath)) AddFileItems(targetPath); + else if (Directory.Exists(targetPath)) AddDirItems(targetPath); AddItem(new JumpItem(Scenes.LnkFile)); } else AddFileItems(CurrentFileObjectPath); @@ -488,6 +438,10 @@ void AddDirItems(string dirPath) public class SelectItem : MyListItem { + public Scenes Scene { get; private set; } + public string SelectedPath { get; set; } + private readonly PictureButton BtnSelect = new(AppImage.Select); + public SelectItem(Scenes scene) { Scene = scene; @@ -498,53 +452,46 @@ public SelectItem(Scenes scene) MouseDoubleClick += (sender, e) => ShowSelectDialog(); } - private readonly PictureButton BtnSelect = new(AppImage.Select); - - public Scenes Scene { get; private set; } - public string SelectedPath { get; set; } - private void SetTextAndTip() { - var tip = ""; - var text = ""; + string tip, text; switch (Scene) { case Scenes.CustomExtension: tip = AppString.Dialog.SelectExtension; - if (CurrentExtension == null) text = tip; - else text = AppString.Other.CurrentExtension.Replace("%s", CurrentExtension); + text = CurrentExtension == null ? AppString.Dialog.SelectExtension : AppString.Other.CurrentExtension.Replace("%s", CurrentExtension); break; case Scenes.PerceivedType: tip = AppString.Dialog.SelectPerceivedType; - if (CurrentPerceivedType == null) text = tip; - else text = AppString.Other.CurrentPerceivedType.Replace("%s", GetPerceivedTypeName(CurrentPerceivedType)); + text = CurrentPerceivedType == null ? AppString.Dialog.SelectPerceivedType : AppString.Other.CurrentPerceivedType.Replace("%s", GetPerceivedTypeName(CurrentPerceivedType)); break; case Scenes.DirectoryType: tip = AppString.Dialog.SelectDirectoryType; - if (CurrentDirectoryType == null) text = tip; - else text = AppString.Other.CurrentDirectoryType.Replace("%s", GetDirectoryTypeName(CurrentDirectoryType)); + text = CurrentDirectoryType == null ? AppString.Dialog.SelectDirectoryType : AppString.Other.CurrentDirectoryType.Replace("%s", GetDirectoryTypeName(CurrentDirectoryType)); break; case Scenes.CustomRegPath: - SelectedPath = CurrentCustomRegPath; tip = AppString.Other.SelectRegPath; - if (SelectedPath == null) text = tip; - else text = AppString.Other.CurrentRegPath + "\n" + SelectedPath; + SelectedPath = CurrentCustomRegPath; + text = SelectedPath == null ? AppString.Other.SelectRegPath : AppString.Other.CurrentRegPath + "\n" + SelectedPath; break; case Scenes.MenuAnalysis: - SelectedPath = CurrentFileObjectPath; tip = AppString.Tip.DropOrSelectObject; - if (SelectedPath == null) text = tip; - else text = AppString.Other.CurrentFilePath + "\n" + SelectedPath; + SelectedPath = CurrentFileObjectPath; + text = SelectedPath == null ? AppString.Tip.DropOrSelectObject : AppString.Other.CurrentFilePath + "\n" + SelectedPath; break; case Scenes.DragDrop: - SelectedPath = GetDropEffectName(); tip = AppString.Dialog.SelectDropEffect; + SelectedPath = GetDropEffectName(); text = AppString.Other.SetDefaultDropEffect + " " + SelectedPath; break; case Scenes.CustomExtensionPerceivedType: tip = AppString.Dialog.SelectPerceivedType; text = AppString.Other.SetPerceivedType.Replace("%s", CurrentExtension) + " " + GetPerceivedTypeName(CurrentExtensionPerceivedType); break; + default: + tip = ""; + text = ""; + break; } ToolTipBox.SetToolTip(BtnSelect, tip); Text = text; @@ -552,14 +499,10 @@ private void SetTextAndTip() private void SetImage() { - switch (Scene) - { - case Scenes.CustomExtensionPerceivedType: - using (var icon = ResourceIcon.GetExtensionIcon(CurrentExtension)) - Image = icon?.ToBitmap(); - break; - } - if (Image == null) Image = AppImage.Custom; + if (Scene == Scenes.CustomExtensionPerceivedType) + using (var icon = ResourceIcon.GetExtensionIcon(CurrentExtension)) + Image = icon?.ToBitmap(); + Image ??= AppImage.Custom; } private void ShowSelectDialog() @@ -568,114 +511,57 @@ private void ShowSelectDialog() switch (Scene) { case Scenes.CustomExtension: - dlg = new FileExtensionDialog - { - Selected = CurrentExtension?[1..] - }; + dlg = new FileExtensionDialog { Selected = CurrentExtension?[1..] }; break; case Scenes.PerceivedType: - dlg = new SelectDialog - { - Items = PerceivedTypeNames, - Title = AppString.Dialog.SelectPerceivedType, - Selected = GetPerceivedTypeName(CurrentPerceivedType) - }; + dlg = new SelectDialog { Items = PerceivedTypeNames, Title = AppString.Dialog.SelectPerceivedType, Selected = GetPerceivedTypeName(CurrentPerceivedType) }; break; case Scenes.DirectoryType: - dlg = new SelectDialog - { - Items = DirectoryTypeNames, - Title = AppString.Dialog.SelectDirectoryType, - Selected = GetDirectoryTypeName(CurrentDirectoryType) - }; + dlg = new SelectDialog { Items = DirectoryTypeNames, Title = AppString.Dialog.SelectDirectoryType, Selected = GetDirectoryTypeName(CurrentDirectoryType) }; break; case Scenes.CustomExtensionPerceivedType: - dlg = new SelectDialog - { - Items = PerceivedTypeNames, - Title = AppString.Dialog.SelectPerceivedType, - Selected = GetPerceivedTypeName(CurrentExtensionPerceivedType) - }; + dlg = new SelectDialog { Items = PerceivedTypeNames, Title = AppString.Dialog.SelectPerceivedType, Selected = GetPerceivedTypeName(CurrentExtensionPerceivedType) }; break; case Scenes.DragDrop: - dlg = new SelectDialog - { - Items = DropEffectNames, - Title = AppString.Dialog.SelectDropEffect, - Selected = GetDropEffectName() - }; + dlg = new SelectDialog { Items = DropEffectNames, Title = AppString.Dialog.SelectDropEffect, Selected = GetDropEffectName() }; break; case Scenes.MenuAnalysis: - dlg = new SelectDialog - { - Items = new[] { AppString.SideBar.File, AppString.SideBar.Directory }, - Title = AppString.Dialog.SelectObjectType, - }; + dlg = new SelectDialog { Items = new[] { AppString.SideBar.File, AppString.SideBar.Directory }, Title = AppString.Dialog.SelectObjectType }; break; case Scenes.CustomRegPath: - if (AppMessageBox.Show(AppString.Message.SelectRegPath, - MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) return; + if (AppMessageBox.Show(AppString.Message.SelectRegPath, MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) return; var frm = FindForm(); frm.Hide(); - using (var process = Process.Start("regedit.exe", "-m")) - { - process.WaitForExit(); - } + using (var process = Process.Start("regedit.exe", "-m")) process.WaitForExit(); var path = Registry.GetValue(LASTKEYPATH, "LastKey", "").ToString(); var index = path.IndexOf('\\'); if (index == -1) return; - path = path[(index + 1)..]; - CurrentCustomRegPath = path; + CurrentCustomRegPath = path[(index + 1)..]; RefreshList(); frm.Show(); frm.Activate(); - break; - } - switch (Scene) - { - case Scenes.CustomExtension: - case Scenes.PerceivedType: - case Scenes.DirectoryType: - case Scenes.MenuAnalysis: - case Scenes.DragDrop: - case Scenes.CustomExtensionPerceivedType: - if (dlg.ShowDialog() != DialogResult.OK) return; - break; + return; } + + if (dlg?.ShowDialog() != DialogResult.OK) return; + switch (Scene) { - case Scenes.CustomExtension: - CurrentExtension = dlg.Selected; - RefreshList(); - break; - case Scenes.PerceivedType: - CurrentPerceivedType = PerceivedTypes[dlg.SelectedIndex]; - RefreshList(); - break; - case Scenes.DirectoryType: - CurrentDirectoryType = DirectoryTypes[dlg.SelectedIndex]; - RefreshList(); - break; + case Scenes.CustomExtension: CurrentExtension = dlg.Selected; RefreshList(); break; + case Scenes.PerceivedType: CurrentPerceivedType = PerceivedTypes[dlg.SelectedIndex]; RefreshList(); break; + case Scenes.DirectoryType: CurrentDirectoryType = DirectoryTypes[dlg.SelectedIndex]; RefreshList(); break; case Scenes.CustomExtensionPerceivedType: - var selected = PerceivedTypes[dlg.SelectedIndex]; - CurrentExtensionPerceivedType = selected; - Text = AppString.Other.SetPerceivedType.Replace("%s", CurrentExtension) + " " + GetPerceivedTypeName(selected); + CurrentExtensionPerceivedType = PerceivedTypes[dlg.SelectedIndex]; + Text = AppString.Other.SetPerceivedType.Replace("%s", CurrentExtension) + " " + GetPerceivedTypeName(CurrentExtensionPerceivedType); break; case Scenes.DragDrop: - switch (dlg.SelectedIndex) - { - case 0: DefaultDropEffect = DropEffect.Default; break; - case 1: DefaultDropEffect = DropEffect.Copy; break; - case 2: DefaultDropEffect = DropEffect.Move; break; - case 3: DefaultDropEffect = DropEffect.CreateLink; break; - } + DefaultDropEffect = (DropEffect)(dlg.SelectedIndex is >= 0 and <= 3 ? dlg.SelectedIndex : 0); Text = AppString.Other.SetDefaultDropEffect + " " + GetDropEffectName(); break; case Scenes.MenuAnalysis: if (dlg.SelectedIndex == 0) { - using var dlg1 = new System.Windows.Forms.OpenFileDialog(); - dlg1.DereferenceLinks = false; + using var dlg1 = new System.Windows.Forms.OpenFileDialog { DereferenceLinks = false }; if (dlg1.ShowDialog() != DialogResult.OK) return; CurrentFileObjectPath = dlg1.FileName; } @@ -700,99 +586,44 @@ private void RefreshList() private sealed class JumpItem : MyListItem { + public static string Extension, PerceivedType, TargetPath; + private readonly PictureButton btnJump = new(AppImage.Jump); + public JumpItem(Scenes scene) { AddCtr(btnJump); - Image image = null; - var index1 = 0; - var index2 = 0; - string[] txts = null; - switch (scene) + var (txts, image, index1, index2) = scene switch { - case Scenes.File: - txts = new[] { AppString.ToolBar.Home, AppString.SideBar.File }; - image = AppImage.File; - break; - case Scenes.Folder: - txts = new[] { AppString.ToolBar.Home, AppString.SideBar.Folder }; - image = AppImage.Folder; - index2 = 1; - break; - case Scenes.Directory: - txts = new[] { AppString.ToolBar.Home, AppString.SideBar.Directory }; - image = AppImage.Directory; - index2 = 2; - break; - case Scenes.Drive: - txts = new[] { AppString.ToolBar.Home, AppString.SideBar.Drive }; - image = AppImage.Drive; - index2 = 5; - break; - case Scenes.AllObjects: - txts = new[] { AppString.ToolBar.Home, AppString.SideBar.AllObjects }; - image = AppImage.AllObjects; - index2 = 6; - break; - case Scenes.LnkFile: - txts = new[] { AppString.ToolBar.Type, AppString.SideBar.LnkFile }; - image = AppImage.LnkFile; - index1 = 1; - index2 = 0; //MainForm.TypeShellScenes - break; - case Scenes.ExeFile: - txts = new[] { AppString.ToolBar.Type, AppString.SideBar.ExeFile }; - using (var icon = ResourceIcon.GetExtensionIcon(TargetPath)) image = icon.ToBitmap(); - index1 = 1; - index2 = 2; //MainForm.TypeShellScenes - break; - case Scenes.UnknownType: - txts = new[] { AppString.ToolBar.Type, AppString.SideBar.UnknownType }; - image = AppImage.NotFound; - index1 = 1; - index2 = 8; //MainForm.TypeShellScenes - break; - case Scenes.CustomExtension: - txts = new[] { AppString.ToolBar.Type, AppString.SideBar.CustomExtension, Extension }; - using (var icon = ResourceIcon.GetExtensionIcon(Extension)) image = icon.ToBitmap(); - index1 = 1; - index2 = 5; //MainForm.TypeShellScenes - break; - case Scenes.PerceivedType: - txts = new[] { AppString.ToolBar.Type, AppString.SideBar.PerceivedType, GetPerceivedTypeName(PerceivedType) }; - image = AppImage.File; - index1 = 1; - index2 = 6; //MainForm.TypeShellScenes - break; - case Scenes.DirectoryType: - txts = new[] { AppString.ToolBar.Type, AppString.SideBar.DirectoryType }; - image = AppImage.Directory; - index1 = 1; - index2 = 7; //MainForm.TypeShellScenes - break; - } + Scenes.File => (new[] { AppString.ToolBar.Home, AppString.SideBar.File }, AppImage.File, 0, 0), + Scenes.Folder => (new[] { AppString.ToolBar.Home, AppString.SideBar.Folder }, AppImage.Folder, 0, 1), + Scenes.Directory => (new[] { AppString.ToolBar.Home, AppString.SideBar.Directory }, AppImage.Directory, 0, 2), + Scenes.Drive => (new[] { AppString.ToolBar.Home, AppString.SideBar.Drive }, AppImage.Drive, 0, 5), + Scenes.AllObjects => (new[] { AppString.ToolBar.Home, AppString.SideBar.AllObjects }, AppImage.AllObjects, 0, 6), + Scenes.LnkFile => (new[] { AppString.ToolBar.Type, AppString.SideBar.LnkFile }, AppImage.LnkFile, 1, 0), + Scenes.ExeFile => (new[] { AppString.ToolBar.Type, AppString.SideBar.ExeFile }, GetIcon(), 1, 2), + Scenes.UnknownType => (new[] { AppString.ToolBar.Type, AppString.SideBar.UnknownType }, AppImage.NotFound, 1, 8), + Scenes.CustomExtension => (new[] { AppString.ToolBar.Type, AppString.SideBar.CustomExtension, Extension }, GetIcon(Extension), 1, 5), + Scenes.PerceivedType => (new[] { AppString.ToolBar.Type, AppString.SideBar.PerceivedType, GetPerceivedTypeName(PerceivedType) }, AppImage.File, 1, 6), + Scenes.DirectoryType => (new[] { AppString.ToolBar.Type, AppString.SideBar.DirectoryType }, AppImage.Directory, 1, 7), + _ => (null, null, 0, 0) + }; Text = "[ " + string.Join(" ] ▶ [ ", txts) + " ]"; Image = image; void SwitchTab() { - switch (scene) - { - case Scenes.CustomExtension: - CurrentExtension = Extension; break; - case Scenes.PerceivedType: - CurrentPerceivedType = PerceivedType; break; - } - ((MainForm)FindForm()).JumpItem(index1, index2);// + if (scene == Scenes.CustomExtension) CurrentExtension = Extension; + else if (scene == Scenes.PerceivedType) CurrentPerceivedType = PerceivedType; + ((MainForm)FindForm()).JumpItem(index1, index2); } - ; btnJump.MouseDown += (sender, e) => SwitchTab(); DoubleClick += (sender, e) => SwitchTab(); - } - - private readonly PictureButton btnJump = new(AppImage.Jump); - public static string Extension = null; - public static string PerceivedType = null; - public static string TargetPath = null; + static Image GetIcon(string ext = null) + { + using var icon = ResourceIcon.GetExtensionIcon(ext ?? TargetPath); + return icon?.ToBitmap(); + } + } } private void AddNewItem(string scenePath) @@ -804,8 +635,9 @@ private void AddNewItem(string scenePath) var btnEnhanceMenu = new PictureButton(AppImage.Enhance); ToolTipBox.SetToolTip(btnAddExisting, AppString.Tip.AddFromPublic); ToolTipBox.SetToolTip(btnEnhanceMenu, AppString.StatusBar.EnhanceMenu); - if (Scene == Scenes.DragDrop || ShellItem.CommandStorePath.Equals(shellPath, - StringComparison.OrdinalIgnoreCase)) btnAddExisting.Visible = false; + + if (Scene == Scenes.DragDrop || ShellItem.CommandStorePath.Equals(shellPath, StringComparison.OrdinalIgnoreCase)) + btnAddExisting.Visible = false; else { using var key = RegistryEx.GetRegistryKey(ShellItem.CommandStorePath); @@ -824,9 +656,7 @@ private void AddNewItem(string scenePath) else if (Scene == Scenes.DragDrop) isShell = false; else { - using var dlg = new SelectDialog(); - dlg.Items = new[] { "Shell", "ShellEx" }; - dlg.Title = AppString.Dialog.SelectNewItemType; + using var dlg = new SelectDialog { Items = new[] { "Shell", "ShellEx" }, Title = AppString.Dialog.SelectNewItemType }; if (dlg.ShowDialog() != DialogResult.OK) return; isShell = dlg.SelectedIndex == 0; } @@ -836,17 +666,12 @@ private void AddNewItem(string scenePath) btnAddExisting.MouseDown += (sender, e) => { - using var dlg = new ShellStoreDialog(); - dlg.IsReference = false; - dlg.ShellPath = ShellItem.CommandStorePath; - dlg.Filter = new Func(itemName => !(AppConfig.HideSysStoreItems - && itemName.StartsWith("Windows.", StringComparison.OrdinalIgnoreCase))); + using var dlg = new ShellStoreDialog { IsReference = false, ShellPath = ShellItem.CommandStorePath, Filter = itemName => !(AppConfig.HideSysStoreItems && itemName.StartsWith("Windows.", StringComparison.OrdinalIgnoreCase)) }; if (dlg.ShowDialog() != DialogResult.OK) return; foreach (var keyName in dlg.SelectedKeyNames) { - var srcPath = $@"{dlg.ShellPath}\{keyName}"; - var dstPath = ObjectPath.GetNewPathWithIndex($@"{shellPath}\{keyName}", ObjectPath.PathType.Registry); - + var srcPath = $"{dlg.ShellPath}\\{keyName}"; + var dstPath = ObjectPath.GetNewPathWithIndex($"{shellPath}\\{keyName}", ObjectPath.PathType.Registry); RegistryEx.CopyTo(srcPath, dstPath); AddItem(new ShellItem(dstPath)); } @@ -857,11 +682,7 @@ private void AddNewItem(string scenePath) var tempPath1 = Path.GetTempFileName(); var tempPath2 = Path.GetTempFileName(); ExternalProgram.ExportRegistry(scenePath, tempPath1); - using (var dlg = new EnhanceMenusDialog()) - { - dlg.ScenePath = scenePath; - dlg.ShowDialog(); - } + using (var dlg = new EnhanceMenusDialog { ScenePath = scenePath }) dlg.ShowDialog(); ExternalProgram.ExportRegistry(scenePath, tempPath2); var str1 = File.ReadAllText(tempPath1); var str2 = File.ReadAllText(tempPath2); @@ -878,20 +699,14 @@ private void AddNewItem(string scenePath) private void AddNewShellItem(string scenePath) { var shellPath = GetShellPath(scenePath); - using var dlg = new NewShellDialog(); - dlg.ScenePath = scenePath; - dlg.ShellPath = shellPath; + using var dlg = new NewShellDialog { ScenePath = scenePath, ShellPath = shellPath }; if (dlg.ShowDialog() != DialogResult.OK) return; for (var i = 0; i < Controls.Count; i++) { - if (Controls[i] is NewItem) - { - ShellItem item; - if (Scene != Scenes.PublicReferences) item = new ShellItem(dlg.NewItemRegPath); - else item = new StoreShellItem(dlg.NewItemRegPath, true, false); - InsertItem(item, i + 1); - break; - } + if (Controls[i] is not NewItem) continue; + var item = Scene != Scenes.PublicReferences ? new ShellItem(dlg.NewItemRegPath) : new StoreShellItem(dlg.NewItemRegPath, true, false); + InsertItem(item, i + 1); + break; } } @@ -901,70 +716,48 @@ private void AddNewShellExItem(string scenePath) using var dlg1 = new InputDialog { Title = AppString.Dialog.InputGuid }; if (GuidEx.TryParse(Clipboard.GetText(), out var guid)) dlg1.Text = guid.ToString(); if (dlg1.ShowDialog() != DialogResult.OK) return; - if (GuidEx.TryParse(dlg1.Text, out guid)) + if (!GuidEx.TryParse(dlg1.Text, out guid)) { - if (isDragDrop) - { - using var dlg2 = new SelectDialog(); - dlg2.Title = AppString.Dialog.SelectGroup; - dlg2.Items = new[] { AppString.SideBar.Folder, AppString.SideBar.Directory, - AppString.SideBar.Drive, AppString.SideBar.AllObjects }; - if (dlg2.ShowDialog() != DialogResult.OK) return; - switch (dlg2.SelectedIndex) - { - case 0: - scenePath = MENUPATH_FOLDER; break; - case 1: - scenePath = MENUPATH_DIRECTORY; break; - case 2: - scenePath = MENUPATH_DRIVE; break; - case 3: - scenePath = MENUPATH_ALLOBJECTS; break; - } - } - var shellExPath = GetShellExPath(scenePath); - if (ShellExItem.GetPathAndGuids(shellExPath, isDragDrop).Values.Contains(guid)) + AppMessageBox.Show(AppString.Message.MalformedGuid); + return; + } + + if (isDragDrop) + { + using var dlg2 = new SelectDialog { Title = AppString.Dialog.SelectGroup, Items = new[] { AppString.SideBar.Folder, AppString.SideBar.Directory, AppString.SideBar.Drive, AppString.SideBar.AllObjects } }; + if (dlg2.ShowDialog() != DialogResult.OK) return; + scenePath = dlg2.SelectedIndex switch { 0 => MENUPATH_FOLDER, 1 => MENUPATH_DIRECTORY, 2 => MENUPATH_DRIVE, 3 => MENUPATH_ALLOBJECTS, _ => scenePath }; + } + + var shellExPath = GetShellExPath(scenePath); + if (ShellExItem.GetPathAndGuids(shellExPath, isDragDrop).Values.Contains(guid)) + { + AppMessageBox.Show(AppString.Message.HasBeenAdded); + return; + } + + var part = isDragDrop ? ShellExItem.DdhParts[0] : ShellExItem.CmhParts[0]; + var regPath = $"{shellExPath}\\{part}\\{guid:B}"; + Registry.SetValue(regPath, "", guid.ToString("B")); + var item = new ShellExItem(guid, regPath); + + for (var i = 0; i < Controls.Count; i++) + { + if (isDragDrop && Controls[i] is FoldGroupItem groupItem && groupItem.GroupPath.Equals(shellExPath, StringComparison.OrdinalIgnoreCase)) { - AppMessageBox.Show(AppString.Message.HasBeenAdded); + InsertItem(item, i + 1); + item.FoldGroupItem = groupItem; + groupItem.SetVisibleWithSubItemCount(); + item.Visible = !groupItem.IsFold; + item.Indent(); + break; } - else + else if (!isDragDrop && Controls[i] is NewItem) { - var part = isDragDrop ? ShellExItem.DdhParts[0] : ShellExItem.CmhParts[0]; - var regPath = $@"{shellExPath}\{part}\{guid:B}"; - Registry.SetValue(regPath, "", guid.ToString("B")); - var item = new ShellExItem(guid, regPath); - for (var i = 0; i < Controls.Count; i++) - { - if (isDragDrop) - { - if (Controls[i] is FoldGroupItem groupItem) - { - if (groupItem.GroupPath.Equals(shellExPath, StringComparison.OrdinalIgnoreCase)) - { - InsertItem(item, i + 1); - item.FoldGroupItem = groupItem; - groupItem.SetVisibleWithSubItemCount(); - item.Visible = !groupItem.IsFold; - item.Indent(); - break; - } - } - } - else - { - if (Controls[i] is NewItem) - { - InsertItem(item, i + 1); - break; - } - } - } + InsertItem(item, i + 1); + break; } } - else - { - AppMessageBox.Show(AppString.Message.MalformedGuid); - } } private sealed class SwitchContextMenuStyleItem : MyListItem @@ -980,14 +773,8 @@ public SwitchContextMenuStyleItem() cmbDic.AutosizeDropDownWidth(); cmbDic.Font = new Font(Font.FontFamily, Font.Size + 1F); cmbDic.Items.AddRange(new[] { AppString.Menu.Win11DefaultContextMenuStyle, AppString.Menu.Win10ClassicContextMenuStyle }); - cmbDic.SelectionChangeCommitted += (sender, e) => - { - Focus(); - UseWin11ContextMenuStyle = cmbDic.SelectedIndex == 0; - }; - // 判断注册表中是否存在registryKeyPath项:存在则为Win10经典右键菜单,不存在则为Win11默认右键菜单 - var registryKey = Registry.CurrentUser.OpenSubKey(registryKeyPath); - useWin11ContextMenuStyle = registryKey == null; + cmbDic.SelectionChangeCommitted += (sender, e) => { Focus(); UseWin11ContextMenuStyle = cmbDic.SelectedIndex == 0; }; + useWin11ContextMenuStyle = Registry.CurrentUser.OpenSubKey(registryKeyPath) == null; cmbDic.SelectedIndex = useWin11ContextMenuStyle ? 0 : 1; } @@ -1002,30 +789,18 @@ public bool UseWin11ContextMenuStyle useWin11ContextMenuStyle = value; if (value) { - // 切换Win11默认右键菜单样式 - // 删除注册表项目:HKEY_CURRENT_USER\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2} var registryKey = Registry.CurrentUser.OpenSubKey(registryKeyPath, true); - if (registryKey != null) - { - Registry.CurrentUser.DeleteSubKeyTree(registryKeyPath); - } + if (registryKey != null) Registry.CurrentUser.DeleteSubKeyTree(registryKeyPath); } else { - // 切换Win10经典右键菜单样式 - // 添加注册表项目:HKEY_CURRENT_USER\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32 - var registryKey = Registry.CurrentUser.CreateSubKey(registrySubKeyPath); - registryKey?.SetValue(null, "", RegistryValueKind.String); + Registry.CurrentUser.CreateSubKey(registrySubKeyPath)?.SetValue(null, "", RegistryValueKind.String); } ExplorerRestarter.Show(); } } - private readonly RComboBox cmbDic = new() - { - DropDownStyle = ComboBoxStyle.DropDownList, - Width = 180.DpiZoom() - }; + private readonly RComboBox cmbDic = new() { DropDownStyle = ComboBoxStyle.DropDownList, Width = 180.DpiZoom() }; } } -} \ No newline at end of file +} diff --git a/ContextMenuManager/MainForm.cs b/ContextMenuManager/MainForm.cs index 287c9786..927023ed 100644 --- a/ContextMenuManager/MainForm.cs +++ b/ContextMenuManager/MainForm.cs @@ -1,4 +1,4 @@ -using BluePointLilac.Controls; +using BluePointLilac.Controls; using BluePointLilac.Methods; using ContextMenuManager.Controls; using ContextMenuManager.Methods; @@ -6,58 +6,19 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Reflection; using System.Windows.Forms; namespace ContextMenuManager { internal sealed class MainForm : MyMainForm { - private readonly SearchBox searchBox; // 添加搜索框成员变量 - private Control currentListControl; // 当前显示的列表控件 - private readonly List originalListItems = new(); // 保存原始列表项 - - public MainForm() - { - TopMost = AppConfig.TopMost; - StartPosition = FormStartPosition.CenterScreen; - Size = AppConfig.MainFormSize; - Text = AppString.General.AppName; - Controls.Add(explorerRestarter); - ToolBar.AddButtons(ToolBarButtons); - - // 创建并添加搜索框到工具栏 - searchBox = new SearchBox(); - searchBox.PlaceholderText = AppString.General.Search ?? "搜索..."; - ToolBar.AddSearchBox(searchBox); - - MainBody.Controls.AddRange(MainControls); - ToolBarButtons[3].CanBeSelected = false; - ToolBarButtons[3].MouseDown += (sender, e) => RefreshApp(); - ToolBar.SelectedButtonChanged += (sender, e) => - { - searchBox.Clear(); // 切换标签页时清空搜索框 - originalListItems.Clear(); // 清除保存的原始列表项 - SwitchTab(); - }; - SideBar.HoverIndexChanged += (sender, e) => ShowItemInfo(); - SideBar.SelectIndexChanged += (sender, e) => - { - searchBox.Clear(); // 切换侧边栏项时清空搜索框 - originalListItems.Clear(); // 清除保存的原始列表项 - SwitchItem(); - }; - Shown += (sender, e) => FirstRunDownloadLanguage(); - FormClosing += (sender, e) => CloseMainForm(); - - // 监听搜索框文本变化事件 - searchBox.TextChanged += (sender, e) => FilterItems(searchBox.Text); - - HoveredToShowItemPath(); - DragDropToAnalysis(); - AddContextMenus(); - ResizeSideBar(); - JumpItem(0, 0); - } + private readonly SearchBox searchBox; + private Control currentListControl; + private readonly List originalListItems = new(); + private static readonly Dictionary TextPropertiesCache = new(); + private static readonly string[] PathPropertyNames = { "ItemFilePath", "RegPath", "GroupPath", "SelectedPath" }; + private static readonly Dictionary PathPropertiesCache = new(); private readonly MyToolBarButton[] ToolBarButtons = { @@ -68,186 +29,166 @@ public MainForm() new(AppImage.About, AppString.ToolBar.About) }; - private Control[] MainControls => new Control[] - { - shellList, shellNewList, sendToList, openWithList, winXList, - enhanceMenusList, detailedEditList, guidBlockedList, iEList, - appSettingBox, languagesBox, dictionariesBox, aboutMeBox, - donateBox, backupListBox - }; - private readonly ShellList shellList = new(); private readonly ShellNewList shellNewList = new(); private readonly SendToList sendToList = new(); private readonly OpenWithList openWithList = new(); private readonly WinXList winXList = new(); - private readonly EnhanceMenuList enhanceMenusList = new(); private readonly DetailedEditList detailedEditList = new(); private readonly GuidBlockedList guidBlockedList = new(); private readonly IEList iEList = new(); - private readonly AppSettingBox appSettingBox = new(); private readonly LanguagesBox languagesBox = new(); private readonly DictionariesBox dictionariesBox = new(); - private readonly AboutAppBox aboutMeBox = new(); + private readonly ReadOnlyRichTextBox aboutMeBox = new(); private readonly DonateBox donateBox = new(); private readonly BackupListBox backupListBox = new(); private readonly ExplorerRestarter explorerRestarter = new(); - // 主页 + private readonly Control[] mainControls; + private static readonly string[] GeneralItems = { - AppString.SideBar.File, - AppString.SideBar.Folder, - AppString.SideBar.Directory, - AppString.SideBar.Background, - AppString.SideBar.Desktop, - AppString.SideBar.Drive, - AppString.SideBar.AllObjects, - AppString.SideBar.Computer, - AppString.SideBar.RecycleBin, - AppString.SideBar.Library, - null, - AppString.SideBar.New, - AppString.SideBar.SendTo, - AppString.SideBar.OpenWith, - null, - AppString.SideBar.WinX + AppString.SideBar.File, AppString.SideBar.Folder, AppString.SideBar.Directory, + AppString.SideBar.Background, AppString.SideBar.Desktop, AppString.SideBar.Drive, + AppString.SideBar.AllObjects, AppString.SideBar.Computer, AppString.SideBar.RecycleBin, + AppString.SideBar.Library, null, AppString.SideBar.New, AppString.SideBar.SendTo, + AppString.SideBar.OpenWith, null, AppString.SideBar.WinX }; + private static readonly string[] GeneralItemInfos = { - AppString.StatusBar.File, - AppString.StatusBar.Folder, - AppString.StatusBar.Directory, - AppString.StatusBar.Background, - AppString.StatusBar.Desktop, - AppString.StatusBar.Drive, - AppString.StatusBar.AllObjects, - AppString.StatusBar.Computer, - AppString.StatusBar.RecycleBin, - AppString.StatusBar.Library, - null, - AppString.StatusBar.New, - AppString.StatusBar.SendTo, - AppString.StatusBar.OpenWith, - null, - AppString.StatusBar.WinX + AppString.StatusBar.File, AppString.StatusBar.Folder, AppString.StatusBar.Directory, + AppString.StatusBar.Background, AppString.StatusBar.Desktop, AppString.StatusBar.Drive, + AppString.StatusBar.AllObjects, AppString.StatusBar.Computer, AppString.StatusBar.RecycleBin, + AppString.StatusBar.Library, null, AppString.StatusBar.New, AppString.StatusBar.SendTo, + AppString.StatusBar.OpenWith, null, AppString.StatusBar.WinX }; - // 文件类型 private static readonly string[] TypeItems = { - AppString.SideBar.LnkFile, - AppString.SideBar.UwpLnk, - AppString.SideBar.ExeFile, - AppString.SideBar.UnknownType, - null, - AppString.SideBar.CustomExtension, - AppString.SideBar.PerceivedType, - AppString.SideBar.DirectoryType, - null, - AppString.SideBar.MenuAnalysis + AppString.SideBar.LnkFile, AppString.SideBar.UwpLnk, AppString.SideBar.ExeFile, + AppString.SideBar.UnknownType, null, AppString.SideBar.CustomExtension, + AppString.SideBar.PerceivedType, AppString.SideBar.DirectoryType, null, AppString.SideBar.MenuAnalysis }; + private static readonly string[] TypeItemInfos = { - AppString.StatusBar.LnkFile, - AppString.StatusBar.UwpLnk, - AppString.StatusBar.ExeFile, - AppString.StatusBar.UnknownType, - null, - AppString.StatusBar.CustomExtension, - AppString.StatusBar.PerceivedType, - AppString.StatusBar.DirectoryType, - null, - AppString.StatusBar.MenuAnalysis + AppString.StatusBar.LnkFile, AppString.StatusBar.UwpLnk, AppString.StatusBar.ExeFile, + AppString.StatusBar.UnknownType, null, AppString.StatusBar.CustomExtension, + AppString.StatusBar.PerceivedType, AppString.StatusBar.DirectoryType, null, AppString.StatusBar.MenuAnalysis }; - // 其他规则 private static readonly string[] OtherRuleItems = { - AppString.SideBar.EnhanceMenu, - AppString.SideBar.DetailedEdit, - null, - AppString.SideBar.DragDrop, - AppString.SideBar.PublicReferences, - AppString.SideBar.IEMenu, - null, - AppString.SideBar.GuidBlocked, - AppString.SideBar.CustomRegPath, + AppString.SideBar.EnhanceMenu, AppString.SideBar.DetailedEdit, null, + AppString.SideBar.DragDrop, AppString.SideBar.PublicReferences, AppString.SideBar.IEMenu, + null, AppString.SideBar.GuidBlocked, AppString.SideBar.CustomRegPath }; + private static readonly string[] OtherRuleItemInfos = { - AppString.StatusBar.EnhanceMenu, - AppString.StatusBar.DetailedEdit, - null, - AppString.StatusBar.DragDrop, - AppString.StatusBar.PublicReferences, - AppString.StatusBar.IEMenu, - null, - AppString.StatusBar.GuidBlocked, - AppString.StatusBar.CustomRegPath, + AppString.StatusBar.EnhanceMenu, AppString.StatusBar.DetailedEdit, null, + AppString.StatusBar.DragDrop, AppString.StatusBar.PublicReferences, AppString.StatusBar.IEMenu, + null, AppString.StatusBar.GuidBlocked, AppString.StatusBar.CustomRegPath }; - // 关于 private static readonly string[] AboutItems = { - AppString.SideBar.AppSetting, - AppString.SideBar.AppLanguage, - AppString.SideBar.BackupRestore, - AppString.SideBar.Dictionaries, - AppString.SideBar.AboutApp, - AppString.SideBar.Donate, + AppString.SideBar.AppSetting, AppString.SideBar.AppLanguage, AppString.SideBar.BackupRestore, + AppString.SideBar.Dictionaries, AppString.SideBar.AboutApp, AppString.SideBar.Donate }; private static readonly string[] SettingItems = { - AppString.Other.TopMost, - null, - AppString.Other.ShowFilePath, - AppString.Other.HideDisabledItems, - null, - AppString.Other.OpenMoreRegedit, - AppString.Other.OpenMoreExplorer, + AppString.Other.TopMost, null, AppString.Other.ShowFilePath, + AppString.Other.HideDisabledItems, null, AppString.Other.OpenMoreRegedit, AppString.Other.OpenMoreExplorer }; private static readonly Scenes[] GeneralShellScenes = { - Scenes.File, - Scenes.Folder, - Scenes.Directory, - Scenes.Background, - Scenes.Desktop, - Scenes.Drive, - Scenes.AllObjects, - Scenes.Computer, - Scenes.RecycleBin, - Scenes.Library + Scenes.File, Scenes.Folder, Scenes.Directory, Scenes.Background, Scenes.Desktop, + Scenes.Drive, Scenes.AllObjects, Scenes.Computer, Scenes.RecycleBin, Scenes.Library }; private static readonly Scenes?[] TypeShellScenes = { - Scenes.LnkFile, - Scenes.UwpLnk, - Scenes.ExeFile, - Scenes.UnknownType, - null, - Scenes.CustomExtension, - Scenes.PerceivedType, - Scenes.DirectoryType, - null, - Scenes.MenuAnalysis + Scenes.LnkFile, Scenes.UwpLnk, Scenes.ExeFile, Scenes.UnknownType, null, + Scenes.CustomExtension, Scenes.PerceivedType, Scenes.DirectoryType, null, Scenes.MenuAnalysis }; private readonly int[] lastItemIndex = new int[5]; + private readonly Dictionary GetState, Action SetState)> settingConfigs; + + private readonly Action[] aboutActions = new Action[6]; + + public MainForm() + { + TopMost = AppConfig.TopMost; + StartPosition = FormStartPosition.CenterScreen; + Size = AppConfig.MainFormSize; + Text = AppString.General.AppName; + Controls.Add(explorerRestarter); + ToolBar.AddButtons(ToolBarButtons); + + searchBox = new SearchBox { PlaceholderText = AppString.General.Search ?? "搜索..." }; + ToolBar.AddSearchBox(searchBox); + + mainControls = new Control[] { shellList, shellNewList, sendToList, openWithList, winXList, + enhanceMenusList, detailedEditList, guidBlockedList, iEList, appSettingBox, languagesBox, + dictionariesBox, aboutMeBox, donateBox, backupListBox }; + + MainBody.Controls.AddRange(mainControls); + ToolBarButtons[3].CanBeSelected = false; + ToolBarButtons[3].MouseDown += (_, _) => RefreshApp(); + ToolBar.SelectedButtonChanged += (_, _) => + { + searchBox.Clear(); + originalListItems.Clear(); + SwitchTab(); + }; + SideBar.HoverIndexChanged += (_, _) => ShowItemInfo(); + SideBar.SelectIndexChanged += (_, _) => + { + searchBox.Clear(); + originalListItems.Clear(); + SwitchItem(); + }; + Shown += (_, _) => FirstRunDownloadLanguage(); + FormClosing += (_, _) => CloseMainForm(); + searchBox.TextChanged += (_, _) => FilterItems(searchBox.Text); + + settingConfigs = new Dictionary GetState, Action SetState)> + { + { 0, (() => TopMost, v => TopMost = AppConfig.TopMost = v) }, + { 2, (() => AppConfig.ShowFilePath, v => AppConfig.ShowFilePath = v) }, + { 3, (() => AppConfig.HideDisabledItems, v => AppConfig.HideDisabledItems = v) }, + { 5, (() => AppConfig.OpenMoreRegedit, v => AppConfig.OpenMoreRegedit = v) }, + { 6, (() => AppConfig.OpenMoreExplorer, v => AppConfig.OpenMoreExplorer = v) } + }; + + aboutActions[0] = () => { appSettingBox.LoadItems(); appSettingBox.Visible = true; }; + aboutActions[1] = () => { languagesBox.LoadLanguages(); languagesBox.Visible = true; }; + aboutActions[2] = () => { backupListBox.LoadItems(); backupListBox.Visible = true; }; + aboutActions[3] = () => { dictionariesBox.LoadText(); dictionariesBox.Visible = true; }; + aboutActions[4] = () => { aboutMeBox.LoadIni(AppString.Other.AboutApp); aboutMeBox.Visible = true; }; + aboutActions[5] = () => donateBox.Visible = true; + + HoveredToShowItemPath(); + DragDropToAnalysis(); + AddContextMenus(); + ResizeSideBar(); + JumpItem(0, 0); + } + public void JumpItem(int toolBarIndex, int sideBarIndex) { - var flag1 = ToolBar.SelectedIndex == toolBarIndex; - var flag2 = SideBar.SelectedIndex == sideBarIndex; lastItemIndex[toolBarIndex] = sideBarIndex; + var needSwitch = ToolBar.SelectedIndex != toolBarIndex || SideBar.SelectedIndex != sideBarIndex; ToolBar.SelectedIndex = toolBarIndex; - if (flag1 || flag2) + if (needSwitch) { SideBar.SelectedIndex = sideBarIndex; SwitchItem(); @@ -267,89 +208,92 @@ private void RefreshApp() private void SwitchTab() { - switch (ToolBar.SelectedIndex) + SideBar.ItemNames = ToolBar.SelectedIndex switch { - case 0: - SideBar.ItemNames = GeneralItems; break; - case 1: - SideBar.ItemNames = TypeItems; break; - case 2: - SideBar.ItemNames = OtherRuleItems; break; - case 4: - SideBar.ItemNames = AboutItems; break; - } + 0 => GeneralItems, + 1 => TypeItems, + 2 => OtherRuleItems, + 4 => AboutItems, + _ => SideBar.ItemNames + }; SideBar.SelectedIndex = lastItemIndex[ToolBar.SelectedIndex]; } private void SwitchItem() { - // 清空原始列表项缓存 originalListItems.Clear(); - foreach (var ctr in MainControls) + Array.ForEach(mainControls, ctr => { ctr.Visible = false; if (ctr is MyList list) list.ClearItems(); - } + }); + if (SideBar.SelectedIndex == -1) return; - switch (ToolBar.SelectedIndex) + + (ToolBar.SelectedIndex switch { - case 0: - SwitchGeneralItem(); break; - case 1: - SwitchTypeItem(); break; - case 2: - SwitchOtherRuleItem(); break; - case 4: - SwitchAboutItem(); break; - } + 0 => SwitchGeneralItem, + 1 => SwitchTypeItem, + 2 => SwitchOtherRuleItem, + 4 => SwitchAboutItem, + _ => (Action)(() => { }) + })(); + lastItemIndex[ToolBar.SelectedIndex] = SideBar.SelectedIndex; - SuspendMainBodyWhenMove = MainControls.ToList().Any(ctr => ctr.Controls.Count > 50); + SuspendMainBodyWhenMove = mainControls.Any(ctr => ctr.Controls.Count > 50); } private void ShowItemInfo() { - if (SideBar.HoveredIndex >= 0) - { - var i = SideBar.HoveredIndex; - switch (ToolBar.SelectedIndex) + StatusBar.Text = SideBar.HoveredIndex < 0 ? MyStatusBar.DefaultText : + (ToolBar.SelectedIndex switch { - case 0: - StatusBar.Text = GeneralItemInfos[i]; return; - case 1: - StatusBar.Text = TypeItemInfos[i]; return; - case 2: - StatusBar.Text = OtherRuleItemInfos[i]; return; - } - } - StatusBar.Text = MyStatusBar.DefaultText; + 0 => GeneralItemInfos, + 1 => TypeItemInfos, + 2 => OtherRuleItemInfos, + _ => null + })?[SideBar.HoveredIndex] ?? MyStatusBar.DefaultText; } private void HoveredToShowItemPath() { foreach (Control ctr in MainBody.Controls) { - if (ctr is MyList list && list != appSettingBox) + if (ctr is not MyList list || list == appSettingBox) continue; + + list.HoveredItemChanged += (_, _) => { - list.HoveredItemChanged += (sender, e) => - { - if (!AppConfig.ShowFilePath) return; - var item = list.HoveredItem; - foreach (var prop in new[] { "ItemFilePath", "RegPath", "GroupPath", "SelectedPath" }) - { - var path = item.GetType().GetProperty(prop)?.GetValue(item, null)?.ToString(); - if (!path.IsNullOrWhiteSpace()) { StatusBar.Text = path; return; } - } - StatusBar.Text = item.Text; - }; - } + if (!AppConfig.ShowFilePath) return; + var item = list.HoveredItem; + var path = GetFirstValidPath(item); + StatusBar.Text = path ?? item.Text; + }; + } + } + + private static string GetFirstValidPath(object item) + { + var type = item.GetType(); + + if (!PathPropertiesCache.TryGetValue(type, out var properties)) + { + properties = PathPropertyNames + .Select(name => type.GetProperty(name)) + .Where(p => p != null) + .ToArray()!; + PathPropertiesCache[type] = properties; } + + return properties + .Select(prop => prop.GetValue(item, null)?.ToString()) + .FirstOrDefault(path => !path.IsNullOrWhiteSpace()); } private void DragDropToAnalysis() { var droper = new ElevatedFileDroper(this); - droper.DragDrop += (sender, e) => + droper.DragDrop += (_, _) => { ShellList.CurrentFileObjectPath = droper.DropFilePaths[0]; JumpItem(1, 9); @@ -361,30 +305,25 @@ private void SwitchGeneralItem() switch (SideBar.SelectedIndex) { case 11: - shellNewList.LoadItems(); shellNewList.Visible = true; - currentListControl = shellNewList; - SaveOriginalListItems(); + shellNewList.LoadItems(); + ShowListAndSave(shellNewList); break; case 12: - sendToList.LoadItems(); sendToList.Visible = true; - currentListControl = sendToList; - SaveOriginalListItems(); + sendToList.LoadItems(); + ShowListAndSave(sendToList); break; case 13: - openWithList.LoadItems(); openWithList.Visible = true; - currentListControl = openWithList; - SaveOriginalListItems(); + openWithList.LoadItems(); + ShowListAndSave(openWithList); break; case 15: - winXList.LoadItems(); winXList.Visible = true; - currentListControl = winXList; - SaveOriginalListItems(); + winXList.LoadItems(); + ShowListAndSave(winXList); break; default: shellList.Scene = GeneralShellScenes[SideBar.SelectedIndex]; - shellList.LoadItems(); shellList.Visible = true; - currentListControl = shellList; - SaveOriginalListItems(); + shellList.LoadItems(); + ShowListAndSave(shellList); break; } } @@ -393,9 +332,7 @@ private void SwitchTypeItem() { shellList.Scene = (Scenes)TypeShellScenes[SideBar.SelectedIndex]; shellList.LoadItems(); - shellList.Visible = true; - currentListControl = shellList; - SaveOriginalListItems(); + ShowListAndSave(shellList); } private void SwitchOtherRuleItem() @@ -403,39 +340,37 @@ private void SwitchOtherRuleItem() switch (SideBar.SelectedIndex) { case 0: - enhanceMenusList.ScenePath = null; enhanceMenusList.LoadItems(); enhanceMenusList.Visible = true; - currentListControl = enhanceMenusList; - SaveOriginalListItems(); + enhanceMenusList.ScenePath = null; + enhanceMenusList.LoadItems(); + ShowListAndSave(enhanceMenusList); break; case 1: - detailedEditList.GroupGuid = Guid.Empty; detailedEditList.LoadItems(); detailedEditList.Visible = true; - currentListControl = detailedEditList; - SaveOriginalListItems(); + detailedEditList.GroupGuid = Guid.Empty; + detailedEditList.LoadItems(); + ShowListAndSave(detailedEditList); break; case 3: - shellList.Scene = Scenes.DragDrop; shellList.LoadItems(); shellList.Visible = true; - currentListControl = shellList; - SaveOriginalListItems(); + shellList.Scene = Scenes.DragDrop; + shellList.LoadItems(); + ShowListAndSave(shellList); break; case 4: - shellList.Scene = Scenes.PublicReferences; shellList.LoadItems(); shellList.Visible = true; - currentListControl = shellList; - SaveOriginalListItems(); + shellList.Scene = Scenes.PublicReferences; + shellList.LoadItems(); + ShowListAndSave(shellList); break; case 5: - iEList.LoadItems(); iEList.Visible = true; - currentListControl = iEList; - SaveOriginalListItems(); + iEList.LoadItems(); + ShowListAndSave(iEList); break; case 7: - guidBlockedList.LoadItems(); guidBlockedList.Visible = true; - currentListControl = guidBlockedList; - SaveOriginalListItems(); + guidBlockedList.LoadItems(); + ShowListAndSave(guidBlockedList); break; case 8: - shellList.Scene = Scenes.CustomRegPath; shellList.LoadItems(); shellList.Visible = true; - currentListControl = shellList; - SaveOriginalListItems(); + shellList.Scene = Scenes.CustomRegPath; + shellList.LoadItems(); + ShowListAndSave(shellList); break; } } @@ -457,149 +392,86 @@ private void SwitchAboutItem() dictionariesBox.LoadText(); dictionariesBox.Visible = true; break; case 4: - aboutMeBox.LoadAboutInfo(); aboutMeBox.Visible = true; + if (aboutMeBox.TextLength == 0) aboutMeBox.LoadIni(AppString.Other.AboutApp); + aboutMeBox.Visible = true; break; case 5: donateBox.Visible = true; break; } currentListControl = null; + if (SideBar.SelectedIndex is >= 0 and <= 5) + aboutActions[SideBar.SelectedIndex](); } - // 保存原始列表项 - private void SaveOriginalListItems() + private void ShowListAndSave(Control list) { + list.Visible = true; + currentListControl = list; originalListItems.Clear(); - - if (currentListControl is not null and MyList myList) - { - foreach (Control control in myList.Controls) - { - originalListItems.Add(control); - } - } + if (list is MyList myList) + originalListItems.AddRange(myList.Controls.Cast()); } - // 过滤项目 private void FilterItems(string filterText) { + if (currentListControl is not MyList myList) return; + if (string.IsNullOrWhiteSpace(filterText)) { - // 如果搜索框为空,恢复显示所有原始项 - RestoreOriginalListItems(); - return; + myList.Controls.Clear(); + myList.Controls.AddRange(originalListItems.ToArray()); + StatusBar.Text = MyStatusBar.DefaultText; } - - var searchText = filterText.ToLower(); - - // 根据当前列表控件进行过滤 - if (currentListControl is not null and MyList myList) + else { - FilterListItems(myList, searchText); - } - } + var searchComparison = StringComparison.OrdinalIgnoreCase; + var itemsToShow = myList.Controls + .OfType() + .Where(item => ContainsText(item.Text, filterText, searchComparison) || + ContainsText(item.SubText, filterText, searchComparison) || + ContainsTextInProperties(item, filterText, searchComparison)) + .Cast() + .ToArray(); - // 恢复原始列表项 - private void RestoreOriginalListItems() - { - if (currentListControl is not null and MyList myList) - { - // 先清空当前列表 myList.Controls.Clear(); + myList.Controls.AddRange(itemsToShow); - // 恢复所有原始项 - foreach (var item in originalListItems) + StatusBar.Text = itemsToShow.Length switch { - myList.Controls.Add(item); - } - - // 恢复状态栏文本 - StatusBar.Text = MyStatusBar.DefaultText; + 0 => $"{AppString.General.NoResultsFor ?? "没有找到匹配"} \"{filterText}\"", + > 0 => $"找到 {itemsToShow.Length} 个匹配项", + _ => StatusBar.Text + }; } } - private void FilterListItems(MyList listControl, string searchText) - { - // 遍历列表项并过滤 - var itemsToShow = new List(); - - foreach (Control control in listControl.Controls) - { - if (control is MyListItem item) - { - var matches = false; - - // 检查主文本 - if (item.Text != null && item.Text.ToLower().Contains(searchText)) - { - matches = true; - } - - // 检查SubText - if (!matches && !string.IsNullOrEmpty(item.SubText) && item.SubText.ToLower().Contains(searchText)) - { - matches = true; - } - - // 检查其他可能包含文本的属性 - if (!matches) - { - // 可以通过反射检查其他文本属性 - var properties = item.GetType().GetProperties(); - foreach (var prop in properties) - { - if (prop.PropertyType == typeof(string)) - { - // 排除 Text 属性,因为已经检查过了 - if (prop.Name is not "Text" and not "SubText") - { - if (prop.GetValue(item) is string value && value.ToLower().Contains(searchText)) - { - matches = true; - break; - } - } - } - } - } - - if (matches) - { - itemsToShow.Add(item); - } - } - } + private static bool ContainsText(string text, string search, StringComparison comparison) + => !string.IsNullOrEmpty(text) && text.Contains(search, comparison); - // 清除当前列表并添加匹配的项 - listControl.Controls.Clear(); - foreach (var item in itemsToShow) + private static bool ContainsTextInProperties(MyListItem item, string search, StringComparison comparison) + { + var type = item.GetType(); + if (!TextPropertiesCache.TryGetValue(type, out var properties)) { - listControl.Controls.Add(item); + properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => p.PropertyType == typeof(string) && p.Name is not "Text" and not "SubText") + .ToArray(); + TextPropertiesCache[type] = properties; } - // 如果没有匹配项,显示提示 - if (itemsToShow.Count == 0 && !string.IsNullOrWhiteSpace(searchText)) - { - StatusBar.Text = $"{AppString.General.NoResultsFor ?? "没有找到匹配"} \"{searchText}\""; - } - else if (itemsToShow.Count > 0) - { - StatusBar.Text = $"找到 {itemsToShow.Count} 个匹配项"; - } + return properties.Any(prop => prop.GetValue(item) is string value && value.Contains(search, comparison)); } - // 添加快捷键支持 protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { - // 全局搜索快捷键 Ctrl+F - if (keyData == (Keys.Control | Keys.F)) + if (keyData == (Keys.Control | Keys.F) && searchBox != null) { - searchBox?.FocusTextBox(); + searchBox.FocusTextBox(); return true; } - // ESC 清除搜索框 - if (keyData == Keys.Escape && searchBox != null && !string.IsNullOrEmpty(searchBox.Text)) + if (keyData == Keys.Escape && searchBox is { Text: { Length: > 0 } }) { searchBox.Clear(); return true; @@ -610,118 +482,110 @@ protected override bool ProcessCmdKey(ref Message msg, Keys keyData) private void ResizeSideBar() { - SideBar.Width = 0; - var strs = GeneralItems.Concat(TypeItems).Concat(OtherRuleItems).Concat(AboutItems).ToArray(); - Array.ForEach(strs, str => SideBar.Width = Math.Max(SideBar.Width, SideBar.GetItemWidth(str))); + SideBar.Width = new[] { GeneralItems, TypeItems, OtherRuleItems, AboutItems } + .SelectMany(items => items.Where(str => str != null)) + .Max(str => SideBar.GetItemWidth(str!)); } private void AddContextMenus() { - var dic = new Dictionary + var menuConfigs = new (MyToolBarButton Button, string[] Items)[] { - { ToolBarButtons[0], GeneralItems }, - { ToolBarButtons[1], TypeItems }, - { ToolBarButtons[2], OtherRuleItems }, - { ToolBarButtons[4], SettingItems } + (ToolBarButtons[0], GeneralItems), + (ToolBarButtons[1], TypeItems), + (ToolBarButtons[2], OtherRuleItems), + (ToolBarButtons[4], SettingItems) }; - foreach (var item in dic) + foreach (var (button, items) in menuConfigs) { var cms = new ContextMenuStrip(); - cms.MouseEnter += (sender, e) => + var capturedButton = button; + + cms.MouseEnter += (_, _) => { - if (item.Key != ToolBar.SelectedButton) item.Key.Opacity = MyToolBar.HoveredOpacity; + if (capturedButton != ToolBar.SelectedButton) + capturedButton.Opacity = MyToolBar.HoveredOpacity; }; - cms.Closed += (sender, e) => + cms.Closed += (_, _) => { - if (item.Key != ToolBar.SelectedButton) item.Key.Opacity = MyToolBar.UnSelctedOpacity; + if (capturedButton != ToolBar.SelectedButton) + capturedButton.Opacity = MyToolBar.UnSelctedOpacity; }; - item.Key.MouseDown += (sender, e) => + button.MouseDown += (sender, e) => { - if (e.Button != MouseButtons.Right) return; - if (sender == ToolBar.SelectedButton) return; - cms.Show(item.Key, e.Location); + if (e.Button != MouseButtons.Right || sender == ToolBar.SelectedButton) return; + cms.Show(button, e.Location); }; - for (var i = 0; i < item.Value.Length; i++) + + for (var i = 0; i < items.Length; i++) { - if (item.Value[i] == null) cms.Items.Add(new RToolStripSeparator()); + if (items[i] == null) + { + cms.Items.Add(new RToolStripSeparator()); + continue; + } + + var tsi = new RToolStripMenuItem(items[i]); + cms.Items.Add(tsi); + var toolBarIndex = ToolBar.ButtonControls.GetChildIndex(button); + var index = i; + + if (toolBarIndex != 4) + { + tsi.Click += (_, _) => JumpItem(toolBarIndex, index); + cms.Opening += (_, _) => tsi.Checked = lastItemIndex[toolBarIndex] == index; + } else { - var tsi = new RToolStripMenuItem(item.Value[i]); - cms.Items.Add(tsi); - - // 修复:使用 ButtonControls 而不是 Controls - var toolBarIndex = ToolBar.ButtonControls.GetChildIndex(item.Key); - var index = i; - - if (toolBarIndex != 4) - { - tsi.Click += (sender, e) => JumpItem(toolBarIndex, index); - cms.Opening += (sender, e) => tsi.Checked = lastItemIndex[toolBarIndex] == index; - } - else - { - tsi.Click += (sender, e) => - { - switch (index) - { - case 0: - AppConfig.TopMost = TopMost = !tsi.Checked; break; - case 2: - AppConfig.ShowFilePath = !tsi.Checked; break; - case 3: - AppConfig.HideDisabledItems = !tsi.Checked; SwitchItem(); break; - case 5: - AppConfig.OpenMoreRegedit = !tsi.Checked; break; - case 6: - AppConfig.OpenMoreExplorer = !tsi.Checked; break; - } - }; - cms.Opening += (sender, e) => - { - switch (index) - { - case 0: - tsi.Checked = TopMost; break; - case 2: - tsi.Checked = AppConfig.ShowFilePath; break; - case 3: - tsi.Checked = AppConfig.HideDisabledItems; break; - case 5: - tsi.Checked = AppConfig.OpenMoreRegedit; break; - case 6: - tsi.Checked = AppConfig.OpenMoreExplorer; break; - } - }; - } + SetupSettingMenuItem(tsi, index, cms); } } } } - private void FirstRunDownloadLanguage() + private void SetupSettingMenuItem(RToolStripMenuItem tsi, int index, ContextMenuStrip cms) { - if (AppConfig.IsFirstRun && CultureInfo.CurrentUICulture.Name != "zh-CN") + tsi.Click += (_, _) => { - if (AppMessageBox.Show("It is detected that you may be running this program for the first time,\n" + - "and your system display language is not simplified Chinese (zh-CN),\n" + - "do you need to download another language?", - MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) + if (settingConfigs.TryGetValue(index, out var setting)) { - JumpItem(4, 1); - languagesBox.ShowLanguageDialog(); + setting.SetState(!tsi.Checked); + if (index == 3) SwitchItem(); } + }; + cms.Opening += (_, _) => + { + tsi.Checked = settingConfigs.TryGetValue(index, out var setting) && setting.GetState(); + }; + } + + private void FirstRunDownloadLanguage() + { + if (!AppConfig.IsFirstRun || CultureInfo.CurrentUICulture.Name == "zh-CN") return; + + if (AppMessageBox.Show( + "It is detected that you may be running this program for the first time,\n" + + "and your system display language is not simplified Chinese (zh-CN),\n" + + "do you need to download another language?", + MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) + { + JumpItem(4, 1); + languagesBox.ShowLanguageDialog(); } } private void CloseMainForm() { - if (explorerRestarter.Visible && AppMessageBox.Show(explorerRestarter.Text, - MessageBoxButtons.OKCancel) == DialogResult.OK) ExternalProgram.RestartExplorer(); + if (explorerRestarter.Visible && + AppMessageBox.Show(explorerRestarter.Text, MessageBoxButtons.OKCancel) == DialogResult.OK) + { + ExternalProgram.RestartExplorer(); + } Opacity = 0; WindowState = FormWindowState.Normal; explorerRestarter.Visible = false; AppConfig.MainFormSize = Size; } } -} \ No newline at end of file +} diff --git a/ContextMenuManager/Methods/AppConfig.cs b/ContextMenuManager/Methods/AppConfig.cs index f1d09749..ff2d5410 100644 --- a/ContextMenuManager/Methods/AppConfig.cs +++ b/ContextMenuManager/Methods/AppConfig.cs @@ -1,4 +1,4 @@ -using BluePointLilac.Methods; +using BluePointLilac.Methods; using ContextMenuManager.Controls; using System; using System.Collections.Generic; @@ -171,21 +171,7 @@ private static void LoadLanguage() return; } if (language == "") language = CultureInfo.CurrentUICulture.Name; - -#if DEBUG - // 在开发环境中使用项目根目录下的languages目录 - string devLangsDir = $@"{Application.StartupPath}\..\languages"; - LanguageIniPath = $@"{devLangsDir}\{language}.ini"; - if (!File.Exists(LanguageIniPath)) - { - // 如果开发目录中的语言文件不存在,回退到Config\Languages目录 - LanguageIniPath = $@"{LangsDir}\{language}.ini"; - } -#else - // 在发布环境中使用Config\Languages目录 LanguageIniPath = $@"{LangsDir}\{language}.ini"; -#endif - if (!File.Exists(LanguageIniPath)) { LanguageIniPath = ""; diff --git a/ContextMenuManager/Methods/AppString.cs b/ContextMenuManager/Methods/AppString.cs index 9745a96f..00d5e301 100644 --- a/ContextMenuManager/Methods/AppString.cs +++ b/ContextMenuManager/Methods/AppString.cs @@ -5,19 +5,7 @@ namespace ContextMenuManager.Methods { internal static class AppString { - private static IniReader _userLangReader; - private static IniReader UserLangReader - { - get - { - if (_userLangReader == null) - { - // 延迟初始化,确保AppConfig.LanguageIniPath已经被正确设置 - _userLangReader = new IniReader(AppConfig.LanguageIniPath); - } - return _userLangReader; - } - } + private static readonly IniReader UserLangReader = new(AppConfig.LanguageIniPath); public static readonly IniReader DefLangReader = new(new StringBuilder(Properties.Resources.AppLanguageDic)); private static string GetValue(string section, string key) @@ -30,8 +18,6 @@ private static string GetValue(string section, string key) /// 加载语言 public static void LoadStrings() { - // 重置UserLangReader,确保使用最新的LanguageIniPath - _userLangReader = new IniReader(AppConfig.LanguageIniPath); foreach (var type in typeof(AppString).GetNestedTypes()) { foreach (var pi in type.GetProperties()) @@ -325,16 +311,6 @@ public static class Tip public static string ImmediatelyCheck { get; set; } } - /// 关于页面 - public static class About - { - public static string Description { get; set; } - public static string CheckUpdate { get; set; } - public static string GitHub { get; set; } - public static string Gitee { get; set; } - public static string License { get; set; } - } - /// 其他文本 public static class Other { diff --git a/ContextMenuManager/Properties/Resources.Designer.cs b/ContextMenuManager/Properties/Resources.Designer.cs index d91a5684..3913ca6d 100644 --- a/ContextMenuManager/Properties/Resources.Designer.cs +++ b/ContextMenuManager/Properties/Resources.Designer.cs @@ -279,16 +279,6 @@ internal static System.Drawing.Bitmap Jump { } } - /// - /// 查找 System.Drawing.Bitmap 类型的本地化资源。 - /// - internal static System.Drawing.Bitmap Logo { - get { - object obj = ResourceManager.GetObject("Logo", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// diff --git a/ContextMenuManager/Properties/Resources.resx b/ContextMenuManager/Properties/Resources.resx index 1ec99984..ae782628 100644 --- a/ContextMenuManager/Properties/Resources.resx +++ b/ContextMenuManager/Properties/Resources.resx @@ -172,9 +172,6 @@ resources\images\jump.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - resources\images\logo.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - resources\images\microsoftstore.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/ContextMenuManager/Properties/Resources/Images/Logo.png b/ContextMenuManager/Properties/Resources/Images/Logo.png deleted file mode 100644 index d92495ac..00000000 Binary files a/ContextMenuManager/Properties/Resources/Images/Logo.png and /dev/null differ diff --git a/ContextMenuManager/Properties/Resources/Texts/AppLanguageDic.ini b/ContextMenuManager/Properties/Resources/Texts/AppLanguageDic.ini index 8705f823..da4cddfc 100644 --- a/ContextMenuManager/Properties/Resources/Texts/AppLanguageDic.ini +++ b/ContextMenuManager/Properties/Resources/Texts/AppLanguageDic.ini @@ -273,13 +273,6 @@ CommandFiles = 此命令依赖配置文件,移动配置文件位置\r\n会导 CreateGroup = 新建一个分组 ImmediatelyCheck = 立即检查 -[About] -Description = 一个纯粹的Windows右键菜单管理器 -CheckUpdate = 检查更新 -GitHub = GitHub -Gitee = Gitee -License = 许可证 - [Other] CustomFolder = 自定义文件夹(&F)... BuildSendtoMenu = 快速构建发送到子菜单 @@ -327,46 +320,11 @@ SetPerceivedType = 设置扩展名为 %s 的文件感知类型为 SetDefaultDropEffect = 设置文件对象默认拖拽命令为 TopMost = 使窗口始终在屏幕最上方 -AboutApp = [兼容性能] - 1 = 适用于Win11、10、8.1、8、7、Vista - 2 = 适用于 64bit、32bit CPU 操作系统 - 3 = 适配高分屏,最佳缩放比为150% - -[代码开源] - 1 = 代码语言:C Sharp,Winform 程序,MIT 开源协议 - 2 = Github 仓库:https://github.com/Jack251970/ContextMenuManager - 3 = Gitee 仓库:https://gitee.com/Jack251970/ContextMenuManager - -[温馨提示] - 1 = 程序需要对大量的注册表项和文件进行读写删改操作,这些行为比较敏感, - 可能会被Windows Defender等误报为病毒,如发生此情况请自行添加进白名单。 - - 2 = 一些特殊菜单项可能会受到其他因素影响导致不会直接显示在右键菜单中, - 但是按照程序使用的通用规则在此程序中仍会显示为启用状态,这是正常的现象。 - - 3 = 每个右键管理程序禁用菜单方法可能不同,建议不要同时使用多个右键菜单管理程序, - 大部分程序使用简单暴力的备份-删除法,此程序尽可能使用了系统提供的键值进行隐藏, - 通过其他程序禁用的菜单项目,请先使用对应程序还原,不然可能无法在此程序中看到它。 - - 4 = 此程序不用于清理未卸载干净的程序,但可帮助你定位菜单项相关注册表和文件位置, - 你可根据相关内容进行你的操作,如果你是一个电脑小白,建议只碰启用\禁用开关。 - -Dictionaries = [字典说明] - 此程序拥有几个字典文件,每份字典又有用户字典(User目录)和网络字典(Web目录) - 如果想为此程序添加字典可右键保存文件至User目录,并按照文件内说明进行添加 - 你可以将你的字典发送到我的邮箱或者提交合并到Github为此项目做出你的贡献 - 右侧选项卡中为原始字典内容,你可以切换选项卡进行查看和右键编辑、保存操作 +AboutApp = [兼容性能]\r\n 1 = 适用于Win11、10、8.1、8、7、Vista \r\n 2 = 适用于 64bit、32bit CPU 操作系统\r\n 3 = 适配高分屏,最佳缩放比为150%\r\n\r\n[代码开源]\r\n 1 = 代码语言:C Sharp,Winform 程序,MIT 开源协议\r\n 2 = Github 仓库:https://github.com/Jack251970/ContextMenuManager \r\n 3 = Gitee 仓库:https://gitee.com/Jack251970/ContextMenuManager \r\n\r\n[温馨提示]\r\n 1 = 程序需要对大量的注册表项和文件进行读写删改操作,这些行为比较敏感,\r\n 可能会被Windows Defender等误报为病毒,如发生此情况请自行添加进白名单。\r\n\r\n 2 = 一些特殊菜单项可能会受到其他因素影响导致不会直接显示在右键菜单中,\r\n 但是按照程序使用的通用规则在此程序中仍会显示为启用状态,这是正常的现象。\r\n\r\n 3 = 每个右键管理程序禁用菜单方法可能不同,建议不要同时使用多个右键菜单管理程序,\r\n 大部分程序使用简单暴力的备份-删除法,此程序尽可能使用了系统提供的键值进行隐藏,\r\n 通过其他程序禁用的菜单项目,请先使用对应程序还原,不然可能无法在此程序中看到它。\r\n\r\n 4 = 此程序不用于清理未卸载干净的程序,但可帮助你定位菜单项相关注册表和文件位置,\r\n 你可根据相关内容进行你的操作,如果你是一个电脑小白,建议只碰启用\禁用开关。 -[字典内容] - 1 = 程序显示文本语言字典 (Languages目录) - 2 = ShellEx菜单项GUID文本图标字典 (GuidInfosDic.ini) - 3 = 系统和其他程序内部部分菜单详细设置规则字典 (DetailedEditDic.xml) - 4 = 增强菜单项目字典 (EnhanceMenusDic.xml) - 5 = UWP新模块字典 (UWPModeItemsDic.xml) +Dictionaries = [字典说明]\r\n 此程序拥有几个字典文件,每份字典又有用户字典(User目录)和网络字典(Web目录)\r\n 如果想为此程序添加字典可右键保存文件至User目录,并按照文件内说明进行添加\r\n 你可以将你的字典发送到我的邮箱或者提交合并到Github为此项目做出你的贡献\n 右侧选项卡中为原始字典内容,你可以切换选项卡进行查看和右键编辑、保存操作\r\n\r\n[字典内容]\r\n 1 = 程序显示文本语言字典 (Languages目录)\r\n 2 = ShellEx菜单项GUID文本图标字典 (GuidInfosDic.ini)\r\n 3 = 系统和其他程序内部部分菜单详细设置规则字典 (DetailedEditDic.xml)\r\n 4 = 增强菜单项目字典 (EnhanceMenusDic.xml)\r\n 5 = UWP新模块字典 (UWPModeItemsDic.xml) -Donate = 此程序完全免费,如果你觉得这个软件对你有所帮助,你可以通过扫描 -下方二维码(微信、支付宝、腾讯QQ)进行捐赠,金额请随意,谢谢支持! -也期待你在Github或者Gitee上为此程序项目点亮Star (这对我很重要!) +Donate = 此程序完全免费,如果你觉得这个软件对你有所帮助,你可以通过扫描\r\n下方二维码(微信、支付宝、腾讯QQ)进行捐赠,金额请随意,谢谢支持!\r\n也期待你在Github或者Gitee上为此程序项目点亮Star (这对我很重要!) Unknown = 未知 RestoreItemText = 备份(源计算机: %device ;创建于: %time ) \ No newline at end of file diff --git "a/languages/ar-DZ \330\271\330\261\330\250\331\212\330\251.ini" "b/languages/ar-DZ \330\271\330\261\330\250\331\212\330\251.ini" index a1aef6a9..436496d8 100644 --- "a/languages/ar-DZ \330\271\330\261\330\250\331\212\330\251.ini" +++ "b/languages/ar-DZ \330\271\330\261\330\250\331\212\330\251.ini" @@ -478,15 +478,13 @@ DeleteGroup = هل ترغب حقًا في حذف هذه المجموعة نها [Tip] -RestartExplorer = عند إعادة تشغيل المستكشف، سيومض سطح المكتب. هذا أمر طبيعي. - كما ستدخل التغييرات حيز التنفيذ أيضًا عند إعادة تشغيل الكمبيوتر وتسجيل الخروج والدخول للنظام. +RestartExplorer = عند إعادة تشغيل المستكشف، سيومض سطح المكتب. هذا أمر طبيعي.\r\n كما ستدخل التغييرات حيز التنفيذ أيضًا عند إعادة تشغيل الكمبيوتر وتسجيل الخروج والدخول للنظام. CustomFolder = إيقاف تشغيل هذا الخيار سيقوم أيضًا بإيقاف تشغيل علامة التبويب المخصصة في لوحة خصائص كائن نظام الملفات. -SendToDrive = يعمل فقط في حالة توصيل قرص قابل للإزالة. - يعرض كل أقسام القرص القابل للإزالة. +SendToDrive = يعمل فقط في حالة توصيل قرص قابل للإزالة.\r\n يعرض كل أقسام القرص القابل للإزالة. BuildSendtoMenu = إيقاف تشغيل هذا الخيار سيسرع ظهور القائمة الرئيسية، ولكن سيبطئ ظهور قائمة "إرسال" الفرعية. @@ -519,8 +517,7 @@ LockNewMenu = إذا تم تمكينه، فلن تتمكن البرامج الط DropOrSelectObject = إسقاط أو تحديد الكائن -ConfigPath = بعد تغيير مسار حفظ ملف التكوين والبيانات، قد تصبح بعض القوائم المحسنة التي تم تمكينها غير صالحة. - يمكن إعادة تمكينها في القائمة المحسنة. +ConfigPath = بعد تغيير مسار حفظ ملف التكوين والبيانات، قد تصبح بعض القوائم المحسنة التي تم تمكينها غير صالحة.\r\n يمكن إعادة تمكينها في القائمة المحسنة. CommandFiles = هذا الأمر يعتمد على ملف التكوين. عند نقل ملف التكوين، سيصبح هذا العنصر من القائمة غير صالح. يجب تمكينه مرة أخرى. @@ -533,27 +530,6 @@ ImmediatelyCheck = التحقق على الفور - -[About] - - -Description = أداة لإدارة قوائم السياق في ويندوز - - -CheckUpdate = التحقق من التحديثات - - -GitHub = GitHub - - -Gitee = Gitee - - -License = الرخصة - - - - [Other] diff --git a/languages/de-DE.ini b/languages/de-DE.ini index 9bbb5331..a9de4e04 100644 --- a/languages/de-DE.ini +++ b/languages/de-DE.ini @@ -267,13 +267,6 @@ CommandFiles = Dieser Befehl hängt von der Konfigurationsdatei ab, verschieben CreateGroup = eine neue Gruppe erstellen ImmediatelyCheck = Jetzt prüfen -[About] -Description = Ein Werkzeug zum Verwalten von Windows-Kontextmenüs -CheckUpdate = Auf Aktualisierungen prüfen -GitHub = GitHub -Gitee = Gitee -License = Lizenz - [Other] CustomFolder = benutzerdefinierter Ordner(&F)... BuildSendtoMenu = Schneller Aufbau Zum Untermenü senden diff --git a/languages/en-US.ini b/languages/en-US.ini index 0c1426ec..eb2b35bd 100644 --- a/languages/en-US.ini +++ b/languages/en-US.ini @@ -251,39 +251,24 @@ NotChooseAnyBackup = You didn't select any backup items! NotChooseAnyRestore = You didn't select any restore items! [Tip] -RestartExplorer = The desktop will flicker for a while after restarting Explorer. This is normal. - Restarting or logging in and out of your PC will also make the changes take effect. -CustomFolder = Disabling this option will also disable the custom tab - in the file system object properties panel. -SendToDrive = Only works if a removable disk is connected. - Displays all partitions of the removable disk. -BuildSendtoMenu = Disabling this option will speed up the pop-up speed of the main menu - but will slow down the pop-up speed of the "Send to" sub-menu -InvalidItem = If a menu item is invalid, all menu items - under this item will be hidden (deletion recommended). +RestartExplorer = The desktop will flicker for a while after restarting Explorer. This is normal.\r\n Restarting or logging in and out of your PC will also make the changes take effect. +CustomFolder = Disabling this option will also disable the custom tab \r\n in the file system object properties panel. +SendToDrive = Only works if a removable disk is connected.\r\n Displays all partitions of the removable disk. +BuildSendtoMenu = Disabling this option will speed up the pop-up speed of the main menu \r\n but will slow down the pop-up speed of the "Send to" sub-menu +InvalidItem = If a menu item is invalid, all menu items \r\n under this item will be hidden (deletion recommended). EditSubItems = Edit sub-menu Items AddReference = Add reference from public reference project AddFromPublic = Copy item from public AddFromParentMenu = Copy item from parent menu AddSeparator = Add a seperator DeleteGuidDic = Delete the local GUID dictionary added by the user -LockNewMenu = Once enabled, it can prevent third-party programs from adding items - and can sort existing items (close and restore) +LockNewMenu = Once enabled, it can prevent third-party programs from adding items \r\n and can sort existing items (close and restore) DropOrSelectObject = Drop or select an object -ConfigPath = After changing the configuration and data file save path, - some of the enhanced menus that have been enabled will become invalid. - They can be re-enabled in the enhanced menu +ConfigPath = After changing the configuration and data file save path, \r\n some of the enhanced menus that have been enabled will become invalid.\r\n They can be re-enabled in the enhanced menu CommandFiles = This command depends on the configuration file. Moving the configuration file location will cause this menu item to become invalid. Re-enable it. CreateGroup = Create a new group ImmediatelyCheck = Check now -[About] -Description = A tool for managing Windows context menus -CheckUpdate = Check for Updates -GitHub = GitHub -Gitee = Gitee -License = License - [Other] CustomFolder = Customize this &folder... BuildSendtoMenu = Create "Send to" submenu diff --git a/languages/ja-JP.ini b/languages/ja-JP.ini index e769deac..e5604710 100644 --- a/languages/ja-JP.ini +++ b/languages/ja-JP.ini @@ -220,13 +220,10 @@ RestoreDefault = デフォルトのメニュー項目に戻すことを確認し DeleteGroup = このグループとそのすべてのメニュー項目を完全に削除してもよろしいですか? [Tip] -RestartExplorer = Explorerを再起動すると、デスクトップがしばらくちらつきます。通常の現象を心配する必要はありません、 -後でコンピュータを再起動またはログオフして、操作を有効にすることもできます。 +RestartExplorer = Explorerを再起動すると、デスクトップがしばらくちらつきます。通常の現象を心配する必要はありません、\r\n後でコンピュータを再起動またはログオフして、操作を有効にすることもできます。 CustomFolder = このオプションを無効にすると、ファイルシステムオブジェクトのプロパティパネルのカスタムタブも無効になります -SendToDrive = リムーバブルディスクが挿入されている場合にのみ機能します、 -リムーバブルディスクのすべてのパーティションを表示する -BuildSendtoMenu = このオプションを無効にすると、メインメニューのポップアップ表示速度が速くなります、 -ただし、サブメニューポップアップの表示速度が遅くなります +SendToDrive = リムーバブルディスクが挿入されている場合にのみ機能します、\r\nリムーバブルディスクのすべてのパーティションを表示する +BuildSendtoMenu = このオプションを無効にすると、メインメニューのポップアップ表示速度が速くなります、\r\nただし、サブメニューポップアップの表示速度が遅くなります InvalidItem = メニュー項目が無効な場合、この項目の下にあるすべてのメニュー項目が非表示になります(削除することをお勧めします) EditSubItems = サブメニュー項目の編集 AddReference = パブリックリファレンスプロジェクトからのリファレンスを追加する @@ -234,23 +231,13 @@ AddFromPublic = パブリックからアイテムをコピー AddFromParentMenu = 親メニューからアイテムをコピー AddSeparator = 分割線を追加 DeleteGuidDic = ユーザーが追加したローカルGUID辞書を削除します -LockNewMenu = 有効にすると、サードパーティプログラムがアイテムを追加するのを防ぐことができます -既存のアイテムを並べ替えることができます(閉じて復元) +LockNewMenu = 有効にすると、サードパーティプログラムがアイテムを追加するのを防ぐことができます\r\n既存のアイテムを並べ替えることができます(閉じて復元) DropOrSelectObject = オブジェクトをドロップまたは選択します -ConfigPath = 構成とデータファイルの保存パスを変更すると、 -有効になっている一部の拡張メニューが無効になります。 -拡張メニューで再度有効にできます +ConfigPath = 構成とデータファイルの保存パスを変更すると、\r\n有効になっている一部の拡張メニューが無効になります。\r\n拡張メニューで再度有効にできます CommandFiles = このコマンドは、構成ファイルによって異なります。構成ファイルの場所を移動すると、このメニュー項目が無効になります。再度有効にします。 CreateGroup = 新しいグループを作成します ImmediatelyCheck = 今すぐチェック -[About] -Description = Windows コンテキストメニューを管理するツール -CheckUpdate = 更新を確認 -GitHub = GitHub -Gitee = Gitee -License = ライセンス - [Other] CustomFolder = このフォルダーのカスタマイズ(&F)... BuildSendtoMenu = 「送る」サブメニューを作成 diff --git a/languages/ko-KR.ini b/languages/ko-KR.ini index 548740ef..a6ec025c 100644 --- a/languages/ko-KR.ini +++ b/languages/ko-KR.ini @@ -219,39 +219,25 @@ WinXSorted = 정렬 기능을 최적화하기 위해 일부 항목의 번호가 RestoreDefault = 기본 메뉴 항목으로 복원할지 확인합니다. DeleteGroup = 이 그룹과 모든 메뉴 항목을 완전히 삭제하시겠습니까? -[Tip] -RestartExplorer = 탐색기를 다시 시작한 후 바탕화면이 잠시 깜박입니다. 이것은 정상입니다. - PC를 다시 시작하거나 로그인하거나 로그아웃해도 변경 사항이 적용됩니다. -CustomFolder = 이 옵션을 사용하지 않도록 설정하면 파일 시스템 개체 속성 - 패널의 사용자 지정 탭도 사용할 수 없게 됩니다. -SendToDrive = 이동식 디스크가 연결된 경우에만 작동합니다. - 이동식 디스크의 모든 파티션을 표시합니다. -BuildSendtoMenu = 이 옵션을 비활성화하면 기본 메뉴의 팝업 속도는 빨라지지만 - "보네기" 하위 메뉴의 팝업 속도는 느려집니다. -InvalidItem = 메뉴 항목이 잘못된 경우 이 항목 아래의 - 모든 메뉴 항목이 숨겨집니다 (삭제 권장). -EditSubItems = 하위 메뉴 항목 편집 -AddReference = 공개 참조 프로젝트에서 참조 추가 -AddFromPublic = 공용에서 항목 복사 -AddFromParentMenu = 상위 메뉴에서 항목 복사 -AddSeparator = 구분 기호 추가 -DeleteGuidDic = 사용자가 추가한 로컬 GUID 사전 삭제 -LockNewMenu =활성화되면 타사 프로그램이 항목을 추가하지 못하도록 할 수 있으며 기존 항목을 정렬할 수 있습니다 (닫기 및 복원). -DropOrSelectObject = 개체 삭제 또는 선택 -ConfigPath = 구성 및 데이터 파일 저장 경로를 변경하면 활성화된, - 고급 메뉴 중 일부가 유효하지 않게 됩니다. - 향상된 메뉴에서 다시 활성화할 수 있습니다 -CommandFiles = 이 명령은 구성 파일에 따라 다릅니다. 구성 파일 위치를 이동하면 이 메뉴 항목이 유효하지 않게 됩니다. 다시 활성화하십시오. -CreateGroup = 새 그룹 만들기 -ImmediatelyCheck = 지금 확인 - -[About] -Description = Windows 상황에 맞는 메뉴를 관리하기 위한 도구 -CheckUpdate = 업데이트 확인 -GitHub = GitHub -Gitee = Gitee -License = 라이선스 - +[Tip] +RestartExplorer = 탐색기를 다시 시작한 후 바탕화면이 잠시 깜박입니다. 이것은 정상입니다.\r\n PC를 다시 시작하거나 로그인하거나 로그아웃해도 변경 사항이 적용됩니다. +CustomFolder = 이 옵션을 사용하지 않도록 설정하면 파일 시스템 개체 속성 \r\n 패널의 사용자 지정 탭도 사용할 수 없게 됩니다. +SendToDrive = 이동식 디스크가 연결된 경우에만 작동합니다.\r\n 이동식 디스크의 모든 파티션을 표시합니다. +BuildSendtoMenu = 이 옵션을 비활성화하면 기본 메뉴의 팝업 속도는 빨라지지만 \r\n "보네기" 하위 메뉴의 팝업 속도는 느려집니다. +InvalidItem = 메뉴 항목이 잘못된 경우 이 항목 아래의 \r\n 모든 메뉴 항목이 숨겨집니다 (삭제 권장). +EditSubItems = 하위 메뉴 항목 편집 +AddReference = 공개 참조 프로젝트에서 참조 추가 +AddFromPublic = 공용에서 항목 복사 +AddFromParentMenu = 상위 메뉴에서 항목 복사 +AddSeparator = 구분 기호 추가 +DeleteGuidDic = 사용자가 추가한 로컬 GUID 사전 삭제 +LockNewMenu =활성화되면 타사 프로그램이 항목을 추가하지 못하도록 할 수 있으며 기존 항목을 정렬할 수 있습니다 (닫기 및 복원). +DropOrSelectObject = 개체 삭제 또는 선택 +ConfigPath = 구성 및 데이터 파일 저장 경로를 변경하면 활성화된, \r\n 고급 메뉴 중 일부가 유효하지 않게 됩니다.\r\n 향상된 메뉴에서 다시 활성화할 수 있습니다 +CommandFiles = 이 명령은 구성 파일에 따라 다릅니다. 구성 파일 위치를 이동하면 이 메뉴 항목이 유효하지 않게 됩니다. 다시 활성화하십시오. +CreateGroup = 새 그룹 만들기 +ImmediatelyCheck = 지금 확인 + [Other] CustomFolder = 이 폴더 사용자 지정(&F)... BuildSendtoMenu = "보내기" 하위 메뉴 만들기 diff --git a/languages/pt-BR.ini b/languages/pt-BR.ini index 21169a9c..d225d282 100644 --- a/languages/pt-BR.ini +++ b/languages/pt-BR.ini @@ -1,342 +1,327 @@ -; Este arquivo é um dicionário de texto para o programa ContextMenuManager, Gerenciador de Menu de Contexto do Windows. -; Pode ajudar o autor a fornecer traduções para este programa e enviá-las para o Github. Substitua o conteúdo à direita do sinal de igual pelo texto traduzido. -; General - Translator são os contribuidores da tradução, General - Language é o nome do idioma, como pt-BR Português do Brasil, -; General - TranslatorUrl é o URL do contribuidor da tradução (qualquer link URL que possa ser aberto com o comando Win+R) -; Instruções de tradução: Valores que temporariamente não devem ser traduzidos devem ser mantidos vazios. Use \r\n ou \n para quebras de linha nos valores atribuídos no dicionário. -; Se houver múltiplos contribuidores para a tradução, use \r\n ou \n para separar os valores de Translator e os valores correspondentes de TranslatorUrl, -; Atribua null para URLs inexistentes, fazendo com que os contribuidores e links correspondam um a um. Ex: Translator = Bob \r\n João \r\n Andi, -; TranslatorUrl = https://github.com/BluePointLilac \r\n null \r\n https://gitee.com/BluePointLilac - -[General] -AppName = Gerenciador de Menu de Contexto do Windows -Language = pt-BR Português do Brasil -Translator = Jukmisael -TranslatorUrl = https://github.com/jukmisael - -[ToolBar] -Home = Início -Type = Tipos de Arquivos -Rule = Outras Regras -Refresh = Atualizar -About = Config - -[SideBar] -File = Arquivo -Folder = Pasta -Directory = Diretório -Background = Plano de Fundo do Diretório -Desktop = Plano de Fundo da Área de Trabalho -Drive = Partição de Disco -AllObjects = Todos os Objetos -Computer = Este Computador -RecycleBin = Lixeira -Library = Biblioteca -New = Novo -SendTo = Enviar para -OpenWith = Abrir com -WinX = Win+X - -LnkFile = Arquivos .lnk -UwpLnk = UWP .lnk -ExeFile = Arquivos .exe -CustomExtension = Formatos Personalizados -PerceivedType = Tipos Conhecidos -DirectoryType = Tipos de Diretórios -UnknownType = Formatos Desconhecidos -MenuAnalysis = Análise - -EnhanceMenu = Menu Aprimorado -DetailedEdit = Edição Detalhada -DragDrop = Arrastar e Soltar com Botão Direito -PublicReferences = Referências Públicas -CustomRegPath = Caminho Personalizado -GUIDBlocked = Bloqueio GUID -IEMenu = Navegador IE - -AppSetting = Configurações do Programa -AppLanguage = Idioma do Programa -CheckUpdate = Verificar Atualizações -Dictionaries = Dicionários do Programa -AboutApp = Sobre o Programa -Donate = Doar ao Autor -BackupRestore = Backup e Restauração - -[StatusBar] -File = Menu de contexto para todos os tipos de arquivos -Folder = Menu de contexto para todas as pastas -Directory = Menu de contexto para todos os diretórios -Background = Menu de contexto para todos os planos de fundo de diretórios e da área de trabalho -Desktop = Menu de contexto do plano de fundo da área de trabalho -Drive = Menu de contexto para todas as unidades de disco -AllObjects = Menu de contexto para todos os objetos do sistema de arquivos (incluindo arquivos, pastas) -Computer = Menu de contexto do ícone Este Computador -RecycleBin = Menu de contexto do ícone Lixeira -Library = Menu de contexto para todas as bibliotecas e planos de fundo de bibliotecas -New = Itens do menu "Novo" no botão direito do plano de fundo de todos os diretórios e da área de trabalho -SendTo = Itens do menu "Enviar para" no botão direito de todos os objetos do sistema de arquivos -OpenWith = Itens do menu "Abrir com" no botão direito de todos os arquivos -WinX = Itens do menu Win+X no botão direito do botão "Iniciar" do Win8~Win11 - -LnkFile = Menu de contexto para todos os atalhos LNK -UwpLnk = Menu de contexto para atalhos de aplicativos UWP do Win8~Win11 -ExeFile = Menu de contexto para todos os arquivos executáveis EXE -CustomExtension = Menu de contexto para arquivos de formato especificado personalizado -PerceivedType = Menu de contexto para tipo percebido de arquivo especificado personalizado -DirectoryType = Menu de contexto para tipo percebido de diretório especificado personalizado -UnknownType = Menu de contexto para todos os arquivos de formato não associados a um método de abertura -MenuAnalysis = Analisa a localização de todos os menus de contexto para um objeto de arquivo especificado - -EnhanceMenu = Adiciona alguns itens de menu poderosos e convenientes -DetailedEdit = Regras de configuração detalhadas para menus internos do sistema e de outros programas -DragDrop = Itens de menu ao arrastar e soltar arquivos com o botão direito -PublicReferences = Edita itens de submenu do tipo Shell de referências públicas adicionadas pelo usuário -CustomRegPath = Edita itens de menu de contexto para caminhos de registro personalizados -GUIDBlocked = Aplicável para ocultar alguns itens persistentes do tipo ShellEx que dependem de GUID -IEMenu = Menu de contexto da página da web do Internet Explorer - -[Menu] -ChangeText = Alterar Texto -ItemIcon = Ícone do Menu -ChangeIcon = Alterar Ícone -ShieldIcon = Ícone de Escudo -AddIcon = Adicionar Ícone -DeleteIcon = Excluir Ícone -ItemPosition = Posição do Menu -SetDefault = Padrão -SetTop = Topo -SetBottom = Fundo -OtherAttributes = Outros Atributos -OnlyWithShift = Mostrar apenas ao pressionar Shift -OnlyInExplorer = Mostrar apenas na janela do Explorer -NoWorkingDirectory = Não usar informações do diretório onde o botão direito foi clicado -NeverDefault = Nunca usar como comando padrão do botão esquerdo -ShowAsDisabledIfHidden = Mostrar cinza quando desativado, não ocultar -Details = Detalhes -WebSearch = Pesquisa na Web -ChangeCommand = Alterar Comando -RunAsAdministrator = Executar como Administrador -FileProperties = Propriedades do Arquivo -FileLocation = Localização do Arquivo -RegistryLocation = Localização do Registro -ExportRegistry = Exportar Registro -Delete = Excluir este item -DeleteReference = Excluir Referência -HandleGUID = Processar GUID -CopyGUID = Copiar GUID -BlockGUID = Bloquear GUID -ClsidLocation = Caminho CLSID -AddGUIDDic = Adicionar ao Dicionário -InitialData = Editar Dados Iniciais do Arquivo -BeforeSeparator = Mostrar acima da linha separadora -ChangeGroup = Alterar Grupo -RestoreDefault = Restaurar Padrão -Edit = Editar -Save = Salvar -FoldAll = Recolher Tudo -UnfoldAll = Expandir Tudo -RestoreBackup = Restaurar Backup -DeleteBackup = Excluir Backup -SwitchUserContextMenuStyle = Alternar estilo de menu de contexto do usuário atual -Win11DefaultContextMenuStyle = Estilo padrão de menu de contexto do Win11 -Win10ClassicContextMenuStyle = Estilo clássico de menu de contexto do Win10 - -[Dialog] -OK = OK -Cancel = Cancelar -Browse = Procurar -Program = Programa -AllFiles = Todos os Arquivos -RegistryFile = Arquivo de Registro -ItemText = Texto do Menu -ItemCommand = Comando do Menu -CommandArguments = Argumentos do Comando -SingleMenu = Nível Único -MultiMenu = Multinível -Public = Público -Private = Privado -SelectAll = Selecionar Tudo -InputGUID = Inserir GUID -AddGUIDDic = Adicionar GUID ao Dicionário Local -DeleteGUIDDic = Excluir -NoPerceivedType = Nenhum Tipo Percebido -TextFile = Arquivo de Texto -DocumentFile = Arquivo de Documento -ImageFile = Arquivo de Imagem -VideoFile = Arquivo de Vídeo -AudioFile = Arquivo de Áudio -CompressedFile = Arquivo Compactado -SystemFile = Arquivo de Sistema -DocumentDirectory = Diretório de Documentos -ImageDirectory = Diretório de Imagens -VideoDirectory = Diretório de Vídeos -AudioDirectory = Diretório de Áudio -EditSubItems = Editar itens do submenu "%s" -DetailedEdit = Edição detalhada do item de menu "%s" -CheckReference = Marque os itens de menu que deseja referenciar -CheckCopy = Marque os itens de menu que deseja copiar -SelectExtension = Selecione uma extensão de arquivo -SelectPerceivedType = Selecione um tipo percebido de arquivo -SelectDirectoryType = Selecione um tipo percebido de diretório -SelectGroup = Selecione o grupo para salvar -SelectNewItemType = Selecione o tipo de item Novo -SelectObjectType = Selecione o tipo de objeto a ser analisado -SelectDropEffect = Selecione o comando padrão para arrastar arquivos -DefaultDropEffect = Padrão (Mover no mesmo disco, Copiar entre discos) -CopyDropEffect = Copiar (Ctrl) -MoveDropEffect = Mover (Shift) -CreateLinkDropEffect = Criar Atalho (Alt) -DownloadLanguages = Baixar Arquivos de Idioma -TranslateTool = Ferramenta de Tradução -DefaultText = Texto Padrão -OldTranslation = Tradução Antiga -NewTranslation = Nova Tradução -DonateInfo = Esta lista é atualizada periodicamente, última atualização: %date \r\n\r\nValor total acumulado: %money yuan, total de pessoas: %count -NewBackupItem = Criar um novo backup -BackupContent = Conteúdo do backup: -BackupMode = Modo de backup: -RestoreBackupItem = Restaurar um backup -RestoreContent = Conteúdo da restauração: -RestoreMode = Modo de restauração: -BackupMode1 = Fazer backup de todos os itens de menu -BackupMode2 = Fazer backup apenas dos itens de menu ativados -BackupMode3 = Fazer backup apenas dos itens de menu desativados -RestoreMode1 = Não processar itens de menu não listados no backup -RestoreMode2 = Desativar itens de menu não listados no backup -RestoreMode3 = Ativar itens de menu não listados no backup -RestoreDetails = Detalhes da Restauração -ItemLocation = Localização do Item -RestoredValue = Valor Restaurado -Enabled = Ativado -Disabled = Desativado - -[Message] -TextCannotBeEmpty = O texto do menu não pode estar vazio! -CommandCannotBeEmpty = O comando do menu não pode estar vazio! -StringParsingFailed = Falha na análise da string de localização! -TextLengthCannotExceed80 = Texto do menu muito longo, o comprimento não pode exceder 80! -ConfirmDeletePermanently = Confirmar exclusão permanente deste item?\r\nEsta operação não pode ser desfeita, proceda com cautela! -DeleteButCanRestore = Confirmar exclusão do item de registro deste menu?\r\nComo o backup automático está ativado (ativado por padrão),\r\npode ser restaurado na pasta de backup após a exclusão. -ConfirmDeleteReference = Confirmar remoção da referência a este item? -ConfirmDelete = Confirmar exclusão deste item? -ConfirmDeleteReferenced = Confirmar exclusão deste item?\r\nTodos os itens que referenciam este item ficarão invalidados, proceda com cautela! -CannotAddNewItem = O sistema limita o número de submenus a um máximo de 16,\r\nnão é possível adicionar mais itens de submenu! -VistaUnsupportedMulti = O sistema Vista não suporta menus multinível! -CannotHideSubItem = Sua versão do sistema é muito baixa, não suporta ocultar submenus! -UnsupportedFilename = Nome de arquivo não suportado,\r\npode já existir um item de menu com o mesmo nome! -NoOpenModeExtension = Esta extensão não tem um método de abertura padrão associado,\r\nassocie primeiro um método de abertura para este tipo de arquivo! -CannotChangePath = Não é permitido alterar o caminho do arquivo! -CopiedToClipboard = Copiado para a área de transferência: -MalformedGUID = GUID malformado -HasBeenAdded = Este item já foi adicionado! -SelectSubMenuMode = Este menu multinível tem 0 itens de submenu, você tem duas opções:\r\n① Todos os itens de submenu deste menu multinível são privados (recomendado),\r\n② Este menu multinível pode referenciar os mesmos subitens que outros menus multinível,\r\npor favor, faça sua escolha... -EditInitialData = Este programa atualmente suporta apenas a edição de dados iniciais de arquivos de texto puro,\r\npara outros tipos de arquivo, edite manualmente o valor da chave "Data" no registro,\r\nconfirma continuar sua operação? -PromptIsOpenItem = Este item é o menu "Abrir" para arquivos ou pastas,\noperações cegas podem impossibilitar a abertura de arquivos ou pastas,\nconfirma continuar sua operação? (não recomendado) -SelectRegPath = Passos da operação:\r\n① Abrir o editor do registro (automático)\r\n② Navegar até o caminho de registro alvo\r\n③ Fechar a janela do editor do registro\nConfirma continuar? -RestartApp = O programa será reiniciado! -FileNotExists = O arquivo não existe! -FolderNotExists = A pasta não existe! -UpdateInfo = 【Verificar atualizações】\r\nVersão atual: %v1\r\nÚltima versão: %v2\r\nBaixar atualização agora? -UpdateSucceeded = Atualização do programa bem-sucedida! -DicUpdateSucceeded = Atualização de dicionários e arquivos de idioma bem-sucedida! -VersionIsLatest = Versão atual é a mais recente! -WebDataReadFailed = Falha na leitura de dados da web! -OpenWebUrl = Abrir a página web relacionada? -AuthorityProtection = Este item de menu do registro pode estar protegido por software de segurança,\r\nnão é possível desativar, excluir ou fazer outras modificações personalizadas. -WinXSorted = Alguns itens foram renumerados para otimizar a funcionalidade de classificação,\r\né necessário reiniciar o Explorador de Arquivos para aplicar o efeito -RestoreDefault = Confirmar restauração para o item de menu padrão? -DeleteGroup = Confirmar exclusão permanente deste grupo e todos os itens de menu dentro dele? -OldBackupVersion = Este backup é de dados de backup de uma versão antiga, os dados de backup podem não ser totalmente restaurados! -BackupSucceeded = Backup concluído! Total de %s itens de menu processados! -NoNeedRestore = Você não tem itens para restaurar! -RestoreSucceeded = Restauração concluída! Total de %s itens de menu processados! -ConfirmDeleteBackupPermanently = Confirmar exclusão permanente deste backup?\r\nEsta operação não pode ser desfeita, proceda com cautela! -DeprecatedBackupVersion = Este backup é de dados de backup obsoletos e não pode ser restaurado! -NotChooseAnyBackup = Você não selecionou nenhum item de backup! -NotChooseAnyRestore = Você não selecionou nenhum item para restauração! - +; Este arquivo é um dicionário de texto para o programa ContextMenuManager, Gerenciador de Menu de Contexto do Windows. +; Pode ajudar o autor a fornecer traduções para este programa e enviá-las para o Github. Substitua o conteúdo à direita do sinal de igual pelo texto traduzido. +; General - Translator são os contribuidores da tradução, General - Language é o nome do idioma, como pt-BR Português do Brasil, +; General - TranslatorUrl é o URL do contribuidor da tradução (qualquer link URL que possa ser aberto com o comando Win+R) +; Instruções de tradução: Valores que temporariamente não devem ser traduzidos devem ser mantidos vazios. Use \r\n ou \n para quebras de linha nos valores atribuídos no dicionário. +; Se houver múltiplos contribuidores para a tradução, use \r\n ou \n para separar os valores de Translator e os valores correspondentes de TranslatorUrl, +; Atribua null para URLs inexistentes, fazendo com que os contribuidores e links correspondam um a um. Ex: Translator = Bob \r\n João \r\n Andi, +; TranslatorUrl = https://github.com/BluePointLilac \r\n null \r\n https://gitee.com/BluePointLilac + +[General] +AppName = Gerenciador de Menu de Contexto do Windows +Language = pt-BR Português do Brasil +Translator = Jukmisael +TranslatorUrl = https://github.com/jukmisael + +[ToolBar] +Home = Início +Type = Tipos de Arquivos +Rule = Outras Regras +Refresh = Atualizar +About = Config + +[SideBar] +File = Arquivo +Folder = Pasta +Directory = Diretório +Background = Plano de Fundo do Diretório +Desktop = Plano de Fundo da Área de Trabalho +Drive = Partição de Disco +AllObjects = Todos os Objetos +Computer = Este Computador +RecycleBin = Lixeira +Library = Biblioteca +New = Novo +SendTo = Enviar para +OpenWith = Abrir com +WinX = Win+X + +LnkFile = Arquivos .lnk +UwpLnk = UWP .lnk +ExeFile = Arquivos .exe +CustomExtension = Formatos Personalizados +PerceivedType = Tipos Conhecidos +DirectoryType = Tipos de Diretórios +UnknownType = Formatos Desconhecidos +MenuAnalysis = Análise + +EnhanceMenu = Menu Aprimorado +DetailedEdit = Edição Detalhada +DragDrop = Arrastar e Soltar com Botão Direito +PublicReferences = Referências Públicas +CustomRegPath = Caminho Personalizado +GUIDBlocked = Bloqueio GUID +IEMenu = Navegador IE + +AppSetting = Configurações do Programa +AppLanguage = Idioma do Programa +CheckUpdate = Verificar Atualizações +Dictionaries = Dicionários do Programa +AboutApp = Sobre o Programa +Donate = Doar ao Autor +BackupRestore = Backup e Restauração + +[StatusBar] +File = Menu de contexto para todos os tipos de arquivos +Folder = Menu de contexto para todas as pastas +Directory = Menu de contexto para todos os diretórios +Background = Menu de contexto para todos os planos de fundo de diretórios e da área de trabalho +Desktop = Menu de contexto do plano de fundo da área de trabalho +Drive = Menu de contexto para todas as unidades de disco +AllObjects = Menu de contexto para todos os objetos do sistema de arquivos (incluindo arquivos, pastas) +Computer = Menu de contexto do ícone Este Computador +RecycleBin = Menu de contexto do ícone Lixeira +Library = Menu de contexto para todas as bibliotecas e planos de fundo de bibliotecas +New = Itens do menu "Novo" no botão direito do plano de fundo de todos os diretórios e da área de trabalho +SendTo = Itens do menu "Enviar para" no botão direito de todos os objetos do sistema de arquivos +OpenWith = Itens do menu "Abrir com" no botão direito de todos os arquivos +WinX = Itens do menu Win+X no botão direito do botão "Iniciar" do Win8~Win11 + +LnkFile = Menu de contexto para todos os atalhos LNK +UwpLnk = Menu de contexto para atalhos de aplicativos UWP do Win8~Win11 +ExeFile = Menu de contexto para todos os arquivos executáveis EXE +CustomExtension = Menu de contexto para arquivos de formato especificado personalizado +PerceivedType = Menu de contexto para tipo percebido de arquivo especificado personalizado +DirectoryType = Menu de contexto para tipo percebido de diretório especificado personalizado +UnknownType = Menu de contexto para todos os arquivos de formato não associados a um método de abertura +MenuAnalysis = Analisa a localização de todos os menus de contexto para um objeto de arquivo especificado + +EnhanceMenu = Adiciona alguns itens de menu poderosos e convenientes +DetailedEdit = Regras de configuração detalhadas para menus internos do sistema e de outros programas +DragDrop = Itens de menu ao arrastar e soltar arquivos com o botão direito +PublicReferences = Edita itens de submenu do tipo Shell de referências públicas adicionadas pelo usuário +CustomRegPath = Edita itens de menu de contexto para caminhos de registro personalizados +GUIDBlocked = Aplicável para ocultar alguns itens persistentes do tipo ShellEx que dependem de GUID +IEMenu = Menu de contexto da página da web do Internet Explorer + +[Menu] +ChangeText = Alterar Texto +ItemIcon = Ícone do Menu +ChangeIcon = Alterar Ícone +ShieldIcon = Ícone de Escudo +AddIcon = Adicionar Ícone +DeleteIcon = Excluir Ícone +ItemPosition = Posição do Menu +SetDefault = Padrão +SetTop = Topo +SetBottom = Fundo +OtherAttributes = Outros Atributos +OnlyWithShift = Mostrar apenas ao pressionar Shift +OnlyInExplorer = Mostrar apenas na janela do Explorer +NoWorkingDirectory = Não usar informações do diretório onde o botão direito foi clicado +NeverDefault = Nunca usar como comando padrão do botão esquerdo +ShowAsDisabledIfHidden = Mostrar cinza quando desativado, não ocultar +Details = Detalhes +WebSearch = Pesquisa na Web +ChangeCommand = Alterar Comando +RunAsAdministrator = Executar como Administrador +FileProperties = Propriedades do Arquivo +FileLocation = Localização do Arquivo +RegistryLocation = Localização do Registro +ExportRegistry = Exportar Registro +Delete = Excluir este item +DeleteReference = Excluir Referência +HandleGUID = Processar GUID +CopyGUID = Copiar GUID +BlockGUID = Bloquear GUID +ClsidLocation = Caminho CLSID +AddGUIDDic = Adicionar ao Dicionário +InitialData = Editar Dados Iniciais do Arquivo +BeforeSeparator = Mostrar acima da linha separadora +ChangeGroup = Alterar Grupo +RestoreDefault = Restaurar Padrão +Edit = Editar +Save = Salvar +FoldAll = Recolher Tudo +UnfoldAll = Expandir Tudo +RestoreBackup = Restaurar Backup +DeleteBackup = Excluir Backup +SwitchUserContextMenuStyle = Alternar estilo de menu de contexto do usuário atual +Win11DefaultContextMenuStyle = Estilo padrão de menu de contexto do Win11 +Win10ClassicContextMenuStyle = Estilo clássico de menu de contexto do Win10 + +[Dialog] +OK = OK +Cancel = Cancelar +Browse = Procurar +Program = Programa +AllFiles = Todos os Arquivos +RegistryFile = Arquivo de Registro +ItemText = Texto do Menu +ItemCommand = Comando do Menu +CommandArguments = Argumentos do Comando +SingleMenu = Nível Único +MultiMenu = Multinível +Public = Público +Private = Privado +SelectAll = Selecionar Tudo +InputGUID = Inserir GUID +AddGUIDDic = Adicionar GUID ao Dicionário Local +DeleteGUIDDic = Excluir +NoPerceivedType = Nenhum Tipo Percebido +TextFile = Arquivo de Texto +DocumentFile = Arquivo de Documento +ImageFile = Arquivo de Imagem +VideoFile = Arquivo de Vídeo +AudioFile = Arquivo de Áudio +CompressedFile = Arquivo Compactado +SystemFile = Arquivo de Sistema +DocumentDirectory = Diretório de Documentos +ImageDirectory = Diretório de Imagens +VideoDirectory = Diretório de Vídeos +AudioDirectory = Diretório de Áudio +EditSubItems = Editar itens do submenu "%s" +DetailedEdit = Edição detalhada do item de menu "%s" +CheckReference = Marque os itens de menu que deseja referenciar +CheckCopy = Marque os itens de menu que deseja copiar +SelectExtension = Selecione uma extensão de arquivo +SelectPerceivedType = Selecione um tipo percebido de arquivo +SelectDirectoryType = Selecione um tipo percebido de diretório +SelectGroup = Selecione o grupo para salvar +SelectNewItemType = Selecione o tipo de item Novo +SelectObjectType = Selecione o tipo de objeto a ser analisado +SelectDropEffect = Selecione o comando padrão para arrastar arquivos +DefaultDropEffect = Padrão (Mover no mesmo disco, Copiar entre discos) +CopyDropEffect = Copiar (Ctrl) +MoveDropEffect = Mover (Shift) +CreateLinkDropEffect = Criar Atalho (Alt) +DownloadLanguages = Baixar Arquivos de Idioma +TranslateTool = Ferramenta de Tradução +DefaultText = Texto Padrão +OldTranslation = Tradução Antiga +NewTranslation = Nova Tradução +DonateInfo = Esta lista é atualizada periodicamente, última atualização: %date \r\n\r\nValor total acumulado: %money yuan, total de pessoas: %count +NewBackupItem = Criar um novo backup +BackupContent = Conteúdo do backup: +BackupMode = Modo de backup: +RestoreBackupItem = Restaurar um backup +RestoreContent = Conteúdo da restauração: +RestoreMode = Modo de restauração: +BackupMode1 = Fazer backup de todos os itens de menu +BackupMode2 = Fazer backup apenas dos itens de menu ativados +BackupMode3 = Fazer backup apenas dos itens de menu desativados +RestoreMode1 = Não processar itens de menu não listados no backup +RestoreMode2 = Desativar itens de menu não listados no backup +RestoreMode3 = Ativar itens de menu não listados no backup +RestoreDetails = Detalhes da Restauração +ItemLocation = Localização do Item +RestoredValue = Valor Restaurado +Enabled = Ativado +Disabled = Desativado + +[Message] +TextCannotBeEmpty = O texto do menu não pode estar vazio! +CommandCannotBeEmpty = O comando do menu não pode estar vazio! +StringParsingFailed = Falha na análise da string de localização! +TextLengthCannotExceed80 = Texto do menu muito longo, o comprimento não pode exceder 80! +ConfirmDeletePermanently = Confirmar exclusão permanente deste item?\r\nEsta operação não pode ser desfeita, proceda com cautela! +DeleteButCanRestore = Confirmar exclusão do item de registro deste menu?\r\nComo o backup automático está ativado (ativado por padrão),\r\npode ser restaurado na pasta de backup após a exclusão. +ConfirmDeleteReference = Confirmar remoção da referência a este item? +ConfirmDelete = Confirmar exclusão deste item? +ConfirmDeleteReferenced = Confirmar exclusão deste item?\r\nTodos os itens que referenciam este item ficarão invalidados, proceda com cautela! +CannotAddNewItem = O sistema limita o número de submenus a um máximo de 16,\r\nnão é possível adicionar mais itens de submenu! +VistaUnsupportedMulti = O sistema Vista não suporta menus multinível! +CannotHideSubItem = Sua versão do sistema é muito baixa, não suporta ocultar submenus! +UnsupportedFilename = Nome de arquivo não suportado,\r\npode já existir um item de menu com o mesmo nome! +NoOpenModeExtension = Esta extensão não tem um método de abertura padrão associado,\r\nassocie primeiro um método de abertura para este tipo de arquivo! +CannotChangePath = Não é permitido alterar o caminho do arquivo! +CopiedToClipboard = Copiado para a área de transferência: +MalformedGUID = GUID malformado +HasBeenAdded = Este item já foi adicionado! +SelectSubMenuMode = Este menu multinível tem 0 itens de submenu, você tem duas opções:\r\n① Todos os itens de submenu deste menu multinível são privados (recomendado),\r\n② Este menu multinível pode referenciar os mesmos subitens que outros menus multinível,\r\npor favor, faça sua escolha... +EditInitialData = Este programa atualmente suporta apenas a edição de dados iniciais de arquivos de texto puro,\r\npara outros tipos de arquivo, edite manualmente o valor da chave "Data" no registro,\r\nconfirma continuar sua operação? +PromptIsOpenItem = Este item é o menu "Abrir" para arquivos ou pastas,\noperações cegas podem impossibilitar a abertura de arquivos ou pastas,\nconfirma continuar sua operação? (não recomendado) +SelectRegPath = Passos da operação:\r\n① Abrir o editor do registro (automático)\r\n② Navegar até o caminho de registro alvo\r\n③ Fechar a janela do editor do registro\nConfirma continuar? +RestartApp = O programa será reiniciado! +FileNotExists = O arquivo não existe! +FolderNotExists = A pasta não existe! +UpdateInfo = 【Verificar atualizações】\r\nVersão atual: %v1\r\nÚltima versão: %v2\r\nBaixar atualização agora? +UpdateSucceeded = Atualização do programa bem-sucedida! +DicUpdateSucceeded = Atualização de dicionários e arquivos de idioma bem-sucedida! +VersionIsLatest = Versão atual é a mais recente! +WebDataReadFailed = Falha na leitura de dados da web! +OpenWebUrl = Abrir a página web relacionada? +AuthorityProtection = Este item de menu do registro pode estar protegido por software de segurança,\r\nnão é possível desativar, excluir ou fazer outras modificações personalizadas. +WinXSorted = Alguns itens foram renumerados para otimizar a funcionalidade de classificação,\r\né necessário reiniciar o Explorador de Arquivos para aplicar o efeito +RestoreDefault = Confirmar restauração para o item de menu padrão? +DeleteGroup = Confirmar exclusão permanente deste grupo e todos os itens de menu dentro dele? +OldBackupVersion = Este backup é de dados de backup de uma versão antiga, os dados de backup podem não ser totalmente restaurados! +BackupSucceeded = Backup concluído! Total de %s itens de menu processados! +NoNeedRestore = Você não tem itens para restaurar! +RestoreSucceeded = Restauração concluída! Total de %s itens de menu processados! +ConfirmDeleteBackupPermanently = Confirmar exclusão permanente deste backup?\r\nEsta operação não pode ser desfeita, proceda com cautela! +DeprecatedBackupVersion = Este backup é de dados de backup obsoletos e não pode ser restaurado! +NotChooseAnyBackup = Você não selecionou nenhum item de backup! +NotChooseAnyRestore = Você não selecionou nenhum item para restauração! + [Tip] -RestartExplorer = Reiniciar o Explorer fará a área de trabalho piscar momentaneamente, é normal, não se preocupe, -ou você pode reiniciar ou fazer logoff mais tarde para que suas alterações entrem em vigor -CustomFolder = Desativar este item também desativará a guia personalizada -no painel de propriedades do objeto do sistema de arquivos -SendToDrive = Só tem efeito quando um disco removível é inserido, -exibe todas as partições desse disco removível -BuildSendtoMenu = Desativar este item acelerará a abertura do menu principal -mas retardará a abertura do submenu Enviar para -InvalidItem = Itens de menu inválidos farão com que todos os itens de menu -abaixo deste fiquem invisíveis (recomenda-se excluir) +RestartExplorer = Reiniciar o Explorer fará a área de trabalho piscar momentaneamente, é normal, não se preocupe,\r\nou você pode reiniciar ou fazer logoff mais tarde para que suas alterações entrem em vigor +CustomFolder = Desativar este item também desativará a guia personalizada\r\nno painel de propriedades do objeto do sistema de arquivos +SendToDrive = Só tem efeito quando um disco removível é inserido,\r\nexibe todas as partições desse disco removível +BuildSendtoMenu = Desativar este item acelerará a abertura do menu principal\r\nmas retardará a abertura do submenu Enviar para +InvalidItem = Itens de menu inválidos farão com que todos os itens de menu\r\nabaixo deste fiquem invisíveis (recomenda-se excluir) EditSubItems = Editar itens de submenu AddReference = Adicionar referência a partir de itens de referência pública AddFromPublic = Copiar item de menu de referências públicas AddFromParentMenu = Copiar item do menu principal AddSeparator = Adicionar linha separadora DeleteGUIDDic = Excluir o dicionário local GUID deste item adicionado pelo usuário -LockNewMenu = Quando ativado, impede que programas de terceiros adicionem itens e permite ordenar itens existentes (restaurado quando desativado) +LockNewMenu = Quando ativado, impede que programas de terceiros adicionem itens\re permite ordenar itens existentes (restaurado quando desativado) DropOrSelectObject = Arraste ou selecione um arquivo ou diretório através do botão -ConfigPath = Alterar o caminho de salvamento de arquivos de configuração e dados -pode invalidar alguns menus aprimorados ativados, -reative-os no menu Aprimorado -CommandFiles = Este comando depende de arquivos de configuração, mover a localização dos arquivos de configuração -invalidará este item de menu, reative-o +ConfigPath = Alterar o caminho de salvamento de arquivos de configuração e dados\r\npode invalidar alguns menus aprimorados ativados,\r\nreative-os no menu Aprimorado +CommandFiles = Este comando depende de arquivos de configuração, mover a localização dos arquivos de configuração\r\ninvalidará este item de menu, reative-o CreateGroup = Criar um novo grupo ImmediatelyCheck = Verificar imediatamente -[About] -Description = Uma ferramenta para gerenciar menus de contexto do Windows -CheckUpdate = Verificar Atualizações -GitHub = GitHub -Gitee = Gitee -License = Licença +[Other] +CustomFolder = Personalizar pasta(&F)... +BuildSendtoMenu = Submenu 'Enviar para' +NewItem = Criar um novo item de menu +AddGUIDBlockedItem = Adicionar item de bloqueio GUID +LockNewMenu = Bloquear menu Novo e ativar funcionalidade de ordenação +InvalidItem = Item de menu inválido: +Separator = >>>>>> Linha Separadora <<<<<< +SelectRegPath = Selecione a chave do registro +CurrentExtension = A extensão de arquivo selecionada atualmente é %s +CurrentPerceivedType = O tipo percebido de arquivo selecionado atualmente é %s +CurrentDirectoryType = O tipo percebido de diretório selecionado atualmente é %s +CurrentFilePath = O caminho do objeto de arquivo selecionado atualmente é +CurrentRegPath = O caminho do registro selecionado atualmente é +WinXSortable = Ativar funcionalidade de ordenação do menu WinX +ShowFilePath = Mostrar caminho do arquivo em tempo real na barra de status +OpenMoreRegedit = Abrir múltiplas instâncias do Editor do Registro +OpenMoreExplorer = Abrir múltiplas instâncias do Explorador de Arquivos +RestartExplorer = Algumas operações atuais requerem reinicialização do Explorador de Arquivos para entrar em vigor +SwitchDictionaries = Alternar dicionários +WebDictionaries = Dicionários da Web +UserDictionaries = Dicionários do Usuário +DictionaryDescription = Descrição do Dicionário +GUIDInfosDictionary = Informações GUID +UwpMode = Módulo UWP +Translators = Contribuidores da Tradução +DonationList = Lista de Doações +ConfigPath = Local de salvamento de arquivos de configuração e dados +AppDataDir = AppData +AppDir = Junto ao Executável +AutoBackup = Backup automático do registro ao excluir menu +SetUpdateFrequency = Definir frequência de verificação automática de atualizações do programa +OnceAWeek = Uma vez por semana +OnceAMonth = Uma vez por mês +OnceASeason = Uma vez por trimestre +NeverCheck = Nunca verificar +SetRequestRepo = Definir site do repositório de acesso a dados de rede +ProtectOpenItem = Proteger itens de menu nomeados "Abrir" +WebSearchEngine = Definir motor de busca usado na pesquisa web +CustomEngine = Personalizado +SetCustomEngine = Definir motor de busca (use %s para substituir a palavra-chave de pesquisa) +HideDisabledItems = Ocultar itens de menu desativados +HideSysStoreItems = Ocultar menus do sistema em referências públicas +SetPerceivedType = Definir o tipo percebido para arquivos com extensão %s como +SetDefaultDropEffect = Definir o comando padrão de arrastar para objetos de arquivo como +TopMost = Manter janela sempre no topo + +AboutApp = [Compatibilidade e Desempenho]\r\n 1 = Compatível com Win11, 10, 8.1, 8, 7, Vista \r\n 2 = Compatível com sistemas operacionais de 64bit e 32bit\r\n 3 = Adaptado para telas de alta resolução, melhor escala 150%\r\n\r\n[Código Aberto]\r\n 1 = Linguagem de código: C Sharp, programa Winform, licença MIT\r\n 2 = Repositório Github: https://github.com/Jack251970/ContextMenuManager \r\n 3 = Repositório Gitee: https://gitee.com/Jack251970/ContextMenuManager \r\n\r\n[Lembretes]\r\n 1 = O programa precisa ler, gravar e modificar uma grande quantidade de chaves de registro e arquivos, esses comportamentos são sensíveis,\r\n podem ser erroneamente relatados como vírus pelo Windows Defender, etc. Se isso acontecer, adicione manualmente à lista de permissões.\r\n\r\n 2 = Alguns itens de menu especiais podem ser afetados por outros fatores e não aparecer diretamente no menu de contexto,\r\n mas de acordo com as regras gerais usadas pelo programa, ainda aparecerão como ativados, o que é normal.\r\n\r\n 3 = Cada gerenciador de menu de contexto pode usar métodos diferentes para desativar menus, é recomendável não usar vários gerenciadores simultaneamente,\r\n a maioria usa o método simples e brutal de backup-exclusão. Este programa tenta usar valores fornecidos pelo sistema para ocultar.\r\n Itens de menu desativados por outros programas devem ser restaurados pelo programa correspondente, caso contrário, podem não ser vistos aqui.\r\n\r\n 4 = Este programa não é para limpar programas não desinstalados completamente, mas pode ajudar a localizar registros e arquivos relacionados aos itens de menu,\r\n você pode operar com base nisso. Se você é iniciante, recomenda-se mexer apenas nas opções ativar/desativar. + +Dictionaries = [Descrição do Dicionário]\r\n Este programa possui vários arquivos de dicionário, cada dicionário tem um dicionário do usuário (diretório User) e um da web (diretório Web)\r\n Se quiser adicionar um dicionário a este programa, clique com o botão direito para salvar o arquivo no diretório User e siga as instruções no arquivo para adicionar\r\n Você pode enviar seu dicionário para meu e-mail ou enviar um merge para o Github para contribuir com o projeto\n As abas à direita mostram o conteúdo original do dicionário, você pode alternar para visualizar e editar/salvar com o botão direito\r\n\r\n[Conteúdo do Dicionário]\r\n 1 = Dicionário de idioma para texto exibido no programa (diretório Languages)\r\n 2 = Dicionário de texto e ícone GUID para itens de menu ShellEx (GUIDInfosDic.ini)\r\n 3 = Dicionário de regras de configuração detalhada para menus internos do sistema e outros programas (DetailedEditDic.xml)\r\n 4 = Dicionário de itens de menu aprimorados (EnhanceMenusDic.xml)\r\n 5 = Dicionário do novo módulo UWP (UWPModeItemsDic.xml) -[Other] -CustomFolder = Personalizar pasta(&F)... -BuildSendtoMenu = Submenu 'Enviar para' -NewItem = Criar um novo item de menu -AddGUIDBlockedItem = Adicionar item de bloqueio GUID -LockNewMenu = Bloquear menu Novo e ativar funcionalidade de ordenação -InvalidItem = Item de menu inválido: -Separator = >>>>>> Linha Separadora <<<<<< -SelectRegPath = Selecione a chave do registro -CurrentExtension = A extensão de arquivo selecionada atualmente é %s -CurrentPerceivedType = O tipo percebido de arquivo selecionado atualmente é %s -CurrentDirectoryType = O tipo percebido de diretório selecionado atualmente é %s -CurrentFilePath = O caminho do objeto de arquivo selecionado atualmente é -CurrentRegPath = O caminho do registro selecionado atualmente é -WinXSortable = Ativar funcionalidade de ordenação do menu WinX -ShowFilePath = Mostrar caminho do arquivo em tempo real na barra de status -OpenMoreRegedit = Abrir múltiplas instâncias do Editor do Registro -OpenMoreExplorer = Abrir múltiplas instâncias do Explorador de Arquivos -RestartExplorer = Algumas operações atuais requerem reinicialização do Explorador de Arquivos para entrar em vigor -SwitchDictionaries = Alternar dicionários -WebDictionaries = Dicionários da Web -UserDictionaries = Dicionários do Usuário -DictionaryDescription = Descrição do Dicionário -GUIDInfosDictionary = Informações GUID -UwpMode = Módulo UWP -Translators = Contribuidores da Tradução -DonationList = Lista de Doações -ConfigPath = Local de salvamento de arquivos de configuração e dados -AppDataDir = AppData -AppDir = Junto ao Executável -AutoBackup = Backup automático do registro ao excluir menu -SetUpdateFrequency = Definir frequência de verificação automática de atualizações do programa -OnceAWeek = Uma vez por semana -OnceAMonth = Uma vez por mês -OnceASeason = Uma vez por trimestre -NeverCheck = Nunca verificar -SetRequestRepo = Definir site do repositório de acesso a dados de rede -ProtectOpenItem = Proteger itens de menu nomeados "Abrir" -WebSearchEngine = Definir motor de busca usado na pesquisa web -CustomEngine = Personalizado -SetCustomEngine = Definir motor de busca (use %s para substituir a palavra-chave de pesquisa) -HideDisabledItems = Ocultar itens de menu desativados -HideSysStoreItems = Ocultar menus do sistema em referências públicas -SetPerceivedType = Definir o tipo percebido para arquivos com extensão %s como -SetDefaultDropEffect = Definir o comando padrão de arrastar para objetos de arquivo como -TopMost = Manter janela sempre no topo - -AboutApp = [Compatibilidade e Desempenho]\r\n 1 = Compatível com Win11, 10, 8.1, 8, 7, Vista \r\n 2 = Compatível com sistemas operacionais de 64bit e 32bit\r\n 3 = Adaptado para telas de alta resolução, melhor escala 150%\r\n\r\n[Código Aberto]\r\n 1 = Linguagem de código: C Sharp, programa Winform, licença MIT\r\n 2 = Repositório Github: https://github.com/Jack251970/ContextMenuManager \r\n 3 = Repositório Gitee: https://gitee.com/Jack251970/ContextMenuManager \r\n\r\n[Lembretes]\r\n 1 = O programa precisa ler, gravar e modificar uma grande quantidade de chaves de registro e arquivos, esses comportamentos são sensíveis,\r\n podem ser erroneamente relatados como vírus pelo Windows Defender, etc. Se isso acontecer, adicione manualmente à lista de permissões.\r\n\r\n 2 = Alguns itens de menu especiais podem ser afetados por outros fatores e não aparecer diretamente no menu de contexto,\r\n mas de acordo com as regras gerais usadas pelo programa, ainda aparecerão como ativados, o que é normal.\r\n\r\n 3 = Cada gerenciador de menu de contexto pode usar métodos diferentes para desativar menus, é recomendável não usar vários gerenciadores simultaneamente,\r\n a maioria usa o método simples e brutal de backup-exclusão. Este programa tenta usar valores fornecidos pelo sistema para ocultar.\r\n Itens de menu desativados por outros programas devem ser restaurados pelo programa correspondente, caso contrário, podem não ser vistos aqui.\r\n\r\n 4 = Este programa não é para limpar programas não desinstalados completamente, mas pode ajudar a localizar registros e arquivos relacionados aos itens de menu,\r\n você pode operar com base nisso. Se você é iniciante, recomenda-se mexer apenas nas opções ativar/desativar. - -Dictionaries = [Descrição do Dicionário]\r\n Este programa possui vários arquivos de dicionário, cada dicionário tem um dicionário do usuário (diretório User) e um da web (diretório Web)\r\n Se quiser adicionar um dicionário a este programa, clique com o botão direito para salvar o arquivo no diretório User e siga as instruções no arquivo para adicionar\r\n Você pode enviar seu dicionário para meu e-mail ou enviar um merge para o Github para contribuir com o projeto\n As abas à direita mostram o conteúdo original do dicionário, você pode alternar para visualizar e editar/salvar com o botão direito\r\n\r\n[Conteúdo do Dicionário]\r\n 1 = Dicionário de idioma para texto exibido no programa (diretório Languages)\r\n 2 = Dicionário de texto e ícone GUID para itens de menu ShellEx (GUIDInfosDic.ini)\r\n 3 = Dicionário de regras de configuração detalhada para menus internos do sistema e outros programas (DetailedEditDic.xml)\r\n 4 = Dicionário de itens de menu aprimorados (EnhanceMenusDic.xml)\r\n 5 = Dicionário do novo módulo UWP (UWPModeItemsDic.xml) - Donate = Este programa é totalmente gratuito. Se achar que ele foi útil, você pode doar escaneando \ No newline at end of file diff --git a/languages/ru-RU.ini b/languages/ru-RU.ini index e4614a33..e65633b5 100644 Binary files a/languages/ru-RU.ini and b/languages/ru-RU.ini differ diff --git a/languages/zh-CN.ini b/languages/zh-CN.ini index c66ec4c1..fe65c96d 100644 --- a/languages/zh-CN.ini +++ b/languages/zh-CN.ini @@ -273,13 +273,6 @@ CommandFiles = 此命令依赖配置文件,移动配置文件位置\r\n会导 CreateGroup = 新建一个分组 ImmediatelyCheck = 立即检查 -[About] -Description = 一个用于管理Windows右键菜单的工具 -CheckUpdate = 检查更新 -GitHub = GitHub -Gitee = Gitee -License = 许可证 - [Other] CustomFolder = 自定义文件夹(&F)... BuildSendtoMenu = 快速构建发送到子菜单