From fd13b9087b273cd2cafa21f7cc5ea6c303fb583c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Tue, 27 Jan 2026 11:50:59 +0800 Subject: [PATCH 01/16] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BE=A7=E8=BE=B9?= =?UTF-8?q?=E6=A0=8F=E5=92=8C=E5=88=97=E8=A1=A8=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BluePointLilac.Controls/MySideBar.cs | 401 +++++------------- ContextMenuManager/Controls/ShellItem.cs | 11 +- ContextMenuManager/Controls/ShellList.cs | 175 +++++++- ContextMenuManager/MainForm.cs | 11 +- 4 files changed, 287 insertions(+), 311 deletions(-) diff --git a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs index 1d59f94a..eac5013d 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs @@ -9,20 +9,11 @@ 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 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 = -1; private bool isAnimating = false; public Color SelectedGradientColor1 { get; set; } = Color.FromArgb(255, 195, 0); @@ -33,6 +24,19 @@ public sealed class MySideBar : Panel 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 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.Black; + public Color HoveredForeColor { get; set; } + + public event EventHandler SelectIndexChanged; + public event EventHandler HoverIndexChanged; public string[] ItemNames { @@ -42,33 +46,15 @@ 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 = 0; + foreach (var s in value) if (s != null) maxW = Math.Max(maxW, TextRenderer.MeasureText(s, Font).Width); + 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,325 +63,140 @@ 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); } } public int HoveredIndex { get => hoverIndex; - set - { - if (hoverIndex == value) return; - hoverIndex = value; - RefreshItem(PnlHovered, value); - HoverIndexChanged?.Invoke(this, EventArgs.Empty); - } + set { if (hoverIndex != value) { hoverIndex = value; Invalidate(); HoverIndexChanged?.Invoke(this, EventArgs.Empty); } } } public MySideBar() { - Dock = DockStyle.Left; - MinimumSize = new Size(1, 1); - BackgroundImageLayout = ImageLayout.None; - DoubleBuffered = true; + Dock = DockStyle.Left; MinimumSize = new Size(1, 1); BackgroundImageLayout = ImageLayout.None; DoubleBuffered = true; Font = new Font(SystemFonts.MenuFont.FontFamily, SystemFonts.MenuFont.Size + 1F); - 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 += (s, e) => + { + animProgress += 0.25f; + if (animProgress >= 1f) { isAnimating = false; animTimer.Stop(); SetSelectedIndexDirectly(animTarget); } + else + { + float t = 1 - (float)Math.Pow(1 - animProgress, 3); + float start = TopSpace + animCurrent * ItemHeight, target = TopSpace + animTarget * ItemHeight; + curSelTop = Math.Max(0, Math.Min(start + (target - start) * t, Height - ItemHeight)); + Invalidate(); + } + }; DarkModeHelper.ThemeChanged += OnThemeChanged; SelectedIndex = -1; } - private void StartAnimation(int fromIndex, int toIndex) - { - animationCurrentIndex = fromIndex; - animationTargetIndex = toIndex; - animationProgress = 0f; - isAnimating = true; - - if (!animationTimer.Enabled) - animationTimer.Start(); - } - - private void AnimationTimer_Tick(object sender, EventArgs e) - { - animationProgress += ANIMATION_SPEED; - - if (animationProgress >= 1f) - CompleteAnimation(); - else - UpdateAnimationFrame(); - } - - 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(); - } - - private void CompleteAnimation() - { - animationProgress = 1f; - isAnimating = false; - animationTimer.Stop(); - - SetSelectedIndexDirectly(animationTargetIndex); - PnlSelected.Top = GetItemTop(selectIndex); - - if (hoverIndex == selectIndex) - PnlHovered.Top = PnlSelected.Top; + private void OnThemeChanged(object sender, EventArgs e) { InitializeColors(); UpdateBackground(); } - Refresh(); - } - - private void SetSelectedIndexDirectly(int value) - { - selectIndex = value; - RefreshItem(PnlSelected, value); - HoveredIndex = value; - SelectIndexChanged?.Invoke(this, EventArgs.Empty); - } - - private int GetItemTop(int index) + private void InitializeColors() { - return TopSpace + index * ItemHeight; + BackColor = DarkModeHelper.SideBarBackground; ForeColor = DarkModeHelper.FormFore; + HoveredBackColor = DarkModeHelper.SideBarHovered; SeparatorColor = DarkModeHelper.SideBarSeparator; + BackgroundGradientColor1 = DarkModeHelper.ToolBarGradientTop; + BackgroundGradientColor2 = DarkModeHelper.ToolBarGradientMiddle; + BackgroundGradientColor3 = DarkModeHelper.ToolBarGradientBottom; } - 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(); + selectIndex = val; + curSelTop = (val >= 0 && ItemNames != null && val < ItemNames.Length) ? TopSpace + val * ItemHeight : -ItemHeight; + HoveredIndex = val; Invalidate(); SelectIndexChanged?.Invoke(this, EventArgs.Empty); } + 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() { ResumeLayout(true); UpdateBackground(); } - public int GetItemWidth(string str) - { - return TextRenderer.MeasureText(str, Font).Width + 2 * HorizontalSpace; - } - - public void StopAnimation() - { - if (isAnimating) - { - animationTimer.Stop(); - isAnimating = false; - SetSelectedIndexDirectly(animationTargetIndex); - } - } - - public void SmoothScrollTo(int index) - { - if (index >= 0 && index < ItemNames?.Length) - SelectedIndex = index; - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - DarkModeHelper.ThemeChanged -= OnThemeChanged; - animationTimer?.Stop(); - animationTimer?.Dispose(); - } - base.Dispose(disposing); - } - - private void InitializeColors() - { - 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; - } 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); - + int w = Math.Max(1, Width), h = ItemNames.Length == 0 ? Math.Max(1, Height) : Math.Max(Height, Math.Max(0, ItemHeight) * ItemNames.Length); try { - var oldBackground = BackgroundImage; - BackgroundImage = new Bitmap(bgWidth, bgHeight); - oldBackground?.Dispose(); - + var old = BackgroundImage; BackgroundImage = new Bitmap(w, h); old?.Dispose(); using var g = Graphics.FromImage(BackgroundImage); - DrawBackgroundGradient(g, bgWidth, bgHeight); - if (ItemNames.Length > 0 && ItemHeight > 0 && Width > 0 && Height > 0) + using (var b = new LinearGradientBrush(new Rectangle(0, 0, w, h), Color.Empty, Color.Empty, 0f) { InterpolationColors = new ColorBlend { Colors = new[] { BackgroundGradientColor1, BackgroundGradientColor2, BackgroundGradientColor3 }, Positions = new[] { 0f, 0.5f, 1f } } }) + g.FillRectangle(b, new Rectangle(0, 0, w, h)); + + using var tb = new SolidBrush(ForeColor); using var p = new Pen(SeparatorColor); + float vSpace = (ItemHeight - TextRenderer.MeasureText(" ", Font).Height) * 0.5f; + for (int i = 0; i < ItemNames.Length; i++) { - DrawTextItems(g); - DrawSeparators(g); + if (ItemNames[i] == null) g.DrawLine(p, HorizontalSpace, TopSpace + (i + 0.5f) * ItemHeight, Width - HorizontalSpace, TopSpace + (i + 0.5f) * ItemHeight); + else if (ItemNames[i].Length > 0) g.DrawString(ItemNames[i], Font, tb, HorizontalSpace, TopSpace + i * ItemHeight + vSpace); } } - 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)); - } - - private void DrawTextItems(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)); - } + catch { BackgroundImage?.Dispose(); BackgroundImage = null; } } - private void DrawSeparators(Graphics g) + protected override void OnPaint(PaintEventArgs e) { - using var pen = new Pen(SeparatorColor); - for (var 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); - } - } - - private int CalculateItemIndex(int yPos) - { - 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; - } - - private void RefreshItem(Panel panel, int index) - { - if (index < 0 || index >= ItemNames?.Length) - { - panel.Top = -ItemHeight; - panel.Text = null; - } - else - { - var actualTop = Math.Max(0, Math.Min(TopSpace + index * ItemHeight, Height - ItemHeight)); - panel.Top = actualTop; - panel.Text = ItemNames[index]; - } - panel.Height = ItemHeight; - panel.Refresh(); - } - - private void PaintHoveredItem(object sender, 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); - } - - private void PaintSelectedItem(object sender, PaintEventArgs e) - { - var ctr = (Control)sender; - if (string.IsNullOrEmpty(ctr.Text)) return; - - using (var brush = new LinearGradientBrush(new Rectangle(0, 0, ctr.Width, ctr.Height), Color.Empty, Color.Empty, 0f)) + base.OnPaint(e); + if (ItemNames == null) return; + float vSpace = (ItemHeight - TextRenderer.MeasureText(" ", Font).Height) * 0.5f; + void DrawItem(int idx, Color back, Color fore, float y) { - brush.InterpolationColors = new ColorBlend + if (idx < 0 || idx >= ItemNames.Length) return; + var r = new RectangleF(0, y, Width, ItemHeight); + 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(r, Color.Empty, Color.Empty, 0f) { InterpolationColors = new ColorBlend { Colors = new[] { SelectedGradientColor1, SelectedGradientColor2, SelectedGradientColor3 }, Positions = new[] { 0f, 0.5f, 1f } } }; + e.Graphics.FillRectangle(b, r); + } + else { using var b = new SolidBrush(back); e.Graphics.FillRectangle(b, r); } + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; + using var fb = new SolidBrush(fore == Color.Empty ? ForeColor : fore); + e.Graphics.DrawString(ItemNames[idx], Font, fb, HorizontalSpace, y + vSpace); } - DrawItemText(e, ctr, SelectedForeColor); - } - private void DrawItemText(PaintEventArgs e, Control ctr, Color textColor) - { - 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)); + if (hoverIndex >= 0 && hoverIndex != selectIndex) DrawItem(hoverIndex, HoveredBackColor, HoveredForeColor, TopSpace + hoverIndex * ItemHeight); + if (selectIndex >= 0) DrawItem(selectIndex, Color.Transparent, SelectedForeColor, curSelTop); + using (var p = new Pen(SeparatorColor)) e.Graphics.DrawLine(p, Width - 1, 0, Width - 1, Height); } - 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(); Font?.Dispose(); } 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..63332955 100644 --- a/ContextMenuManager/Controls/ShellList.cs +++ b/ContextMenuManager/Controls/ShellList.cs @@ -9,6 +9,8 @@ using System.Drawing; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using System.Windows.Forms; using System.Xml; @@ -35,6 +37,24 @@ public sealed class ShellList : MyList // 文件类型 Ink文件 uwp Ink exe文 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 + { + public string RegPath; + public string Text; + public Image Image; + public bool IsMultiItem; + } + + private static readonly Dictionary DefaultNameIndexs + = new(StringComparer.OrdinalIgnoreCase) + { + { "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" @@ -315,23 +335,160 @@ public void LoadItems() private void LoadItems(string scenePath) { if (scenePath == null) return; - RegTrustedInstaller.TakeRegKeyOwnerShip(scenePath); - LoadShellItems(GetShellPath(scenePath)); - LoadShellExItems(GetShellExPath(scenePath)); + + // Cancel previous task + cts?.Cancel(); + cts = new CancellationTokenSource(); + var token = cts.Token; + + // Start async loading + Task.Run(() => + { + if (token.IsCancellationRequested) return; + + RegTrustedInstaller.TakeRegKeyOwnerShip(scenePath); + + var shellPath = GetShellPath(scenePath); + var shellItemsData = GetShellItemsData(shellPath); + + if (token.IsCancellationRequested) return; + + Invoke(new Action(() => + { + if (token.IsCancellationRequested) return; + + // Add ShellItems from preloaded data + foreach (var data in shellItemsData) + { + AddItem(new ShellItem(data.RegPath, data.Text, data.Image, data.IsMultiItem)); + } + + // Continue with ShellEx items (sync for now, to keep it simple) + 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; + + // RegTrustedInstaller.TakeRegTreeOwnerShip(shellKey.Name); // Assuming this is fast enough or handled + foreach (var keyName in shellKey.GetSubKeyNames()) { - var item = new ShellItem($@"{shellPath}\{keyName}"); - AddItem(item); + var regPath = $@"{shellPath}\{keyName}"; + var isMultiItem = GetIsMultiItem(regPath); + var text = GetItemText(regPath, keyName, isMultiItem); + var image = GetItemIcon(regPath); + list.Add(new ShellItemData { RegPath = regPath, Text = text, Image = image, IsMultiItem = isMultiItem }); + } + return list; + } + + // Helper methods copied from ShellItem logic + private bool GetIsMultiItem(string regPath) + { + var value = Registry.GetValue(regPath, "SubCommands", null); + if (value != null) return true; + value = Registry.GetValue(regPath, "ExtendedSubCommandsKey", null); + if (!string.IsNullOrEmpty(value?.ToString())) return true; + return false; + } + + private string GetItemText(string regPath, string keyName, bool isMultiItem) + { + string name; + var valueNames = new List { "MUIVerb" }; + if (!isMultiItem) valueNames.Add(""); + foreach (var valueName in valueNames) + { + name = Registry.GetValue(regPath, valueName, null)?.ToString(); + name = ResourceString.GetDirectString(name); + if (!string.IsNullOrEmpty(name)) return name; + } + if (DefaultNameIndexs.TryGetValue(RegistryEx.GetKeyName(regPath), out var index)) + { + name = $"@windows.storage.dll,-{index}"; + name = ResourceString.GetDirectString(name); + if (!string.IsNullOrEmpty(name)) return name; + } + return RegistryEx.GetKeyName(regPath); + } + + private Image GetItemIcon(string regPath) + { + // Logic similar to ShellItem.ItemIcon but returns Image + var iconLocation = Registry.GetValue(regPath, "Icon", null)?.ToString(); + var hasLUAShield = Registry.GetValue(regPath, "HasLUAShield", null) != null; + + // We need ItemFilePath which is derived from Guid or Command + // ShellItem.ItemFilePath => GuidInfo.GetFilePath(Guid) ?? ObjectPath.ExtractFilePath(ItemCommand); + // This is complex to replicate exactly without creating ShellItem instance. + // But we can approximate. + + // Replicating Guid logic + string commandPath = $@"{regPath}\command"; + var keyValues = new Dictionary + { + { commandPath , "DelegateExecute" }, + { $@"{regPath}\DropTarget" , "CLSID" }, + { regPath , "ExplorerCommandHandler" }, + }; + Guid guid = Guid.Empty; + foreach (var item in keyValues) + { + var val = Registry.GetValue(item.Key, item.Value, null)?.ToString(); + if (GuidEx.TryParse(val, out var g)) { guid = g; break; } + } + + string itemCommand = null; + if (!isMultiItem(regPath)) // Helper needed + itemCommand = Registry.GetValue(commandPath, "", null)?.ToString(); + + string itemFilePath = GuidInfo.GetFilePath(guid) ?? ObjectPath.ExtractFilePath(itemCommand); + + Icon icon; + string iconPath; + int iconIndex; + + if (iconLocation != null) + { + icon = ResourceIcon.GetIcon(iconLocation, out iconPath, out iconIndex); + if (icon == null && Path.GetExtension(iconPath)?.ToLower() == ".exe") + icon = ResourceIcon.GetIcon(iconPath = "imageres.dll", iconIndex = -15); } + else if (hasLUAShield) + icon = ResourceIcon.GetIcon(iconPath = "imageres.dll", iconIndex = -78); + else + icon = ResourceIcon.GetIcon(iconPath = itemFilePath, iconIndex = 0); + + if (icon == null) + icon = ResourceIcon.GetExtensionIcon(iconPath = itemFilePath) ?? ResourceIcon.GetIcon(iconPath = "imageres.dll", iconIndex = -2); + + Image image = icon.ToBitmap(); + if (iconLocation == null && !hasLUAShield) + { + // ToTransparent logic if no icon/shield + // ShellItem: if (!HasIcon) Image = Image.ToTransparent(); + // HasIcon = !IconLocation.IsNullOrWhiteSpace() || HasLUAShield + image = image.ToTransparent(); + } + return image; } + private bool isMultiItem(string regPath) // helper + { + return GetIsMultiItem(regPath); + } + + // Original LoadShellItems removed or replaced + // private void LoadShellItems(string shellPath) { ... } // Deleted + private void LoadShellExItems(string shellExPath) { var names = new List(); @@ -1028,4 +1185,4 @@ public bool UseWin11ContextMenuStyle }; } } -} \ No newline at end of file +} diff --git a/ContextMenuManager/MainForm.cs b/ContextMenuManager/MainForm.cs index 287c9786..ae2a3518 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; @@ -57,6 +57,15 @@ public MainForm() AddContextMenus(); ResizeSideBar(); JumpItem(0, 0); + + shellList.ItemsLoaded += (sender, e) => + { + if (currentListControl == shellList) + { + SaveOriginalListItems(); + if (!string.IsNullOrEmpty(searchBox.Text)) FilterItems(searchBox.Text); + } + }; } private readonly MyToolBarButton[] ToolBarButtons = From dbbe2faaeea9424454d880275e2cb4f741c1a34f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Tue, 27 Jan 2026 16:27:57 +0800 Subject: [PATCH 02/16] Update ContextMenuManager/Controls/ShellList.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ContextMenuManager/Controls/ShellList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ContextMenuManager/Controls/ShellList.cs b/ContextMenuManager/Controls/ShellList.cs index 63332955..793de962 100644 --- a/ContextMenuManager/Controls/ShellList.cs +++ b/ContextMenuManager/Controls/ShellList.cs @@ -468,7 +468,7 @@ private Image GetItemIcon(string regPath) icon = ResourceIcon.GetIcon(iconPath = itemFilePath, iconIndex = 0); if (icon == null) - icon = ResourceIcon.GetExtensionIcon(iconPath = itemFilePath) ?? ResourceIcon.GetIcon(iconPath = "imageres.dll", iconIndex = -2); + icon = ResourceIcon.GetExtensionIcon(itemFilePath) ?? ResourceIcon.GetIcon(iconPath = "imageres.dll", iconIndex = -2); Image image = icon.ToBitmap(); if (iconLocation == null && !hasLUAShield) From fae118440a9e0a3482b266c992d9542e5fdd2c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Tue, 27 Jan 2026 16:28:29 +0800 Subject: [PATCH 03/16] Update ContextMenuManager/Controls/ShellList.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ContextMenuManager/Controls/ShellList.cs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/ContextMenuManager/Controls/ShellList.cs b/ContextMenuManager/Controls/ShellList.cs index 793de962..bd5d5a7f 100644 --- a/ContextMenuManager/Controls/ShellList.cs +++ b/ContextMenuManager/Controls/ShellList.cs @@ -454,22 +454,20 @@ private Image GetItemIcon(string regPath) Icon icon; string iconPath; - int iconIndex; - + if (iconLocation != null) { - icon = ResourceIcon.GetIcon(iconLocation, out iconPath, out iconIndex); + icon = ResourceIcon.GetIcon(iconLocation, out iconPath, out _); if (icon == null && Path.GetExtension(iconPath)?.ToLower() == ".exe") - icon = ResourceIcon.GetIcon(iconPath = "imageres.dll", iconIndex = -15); + icon = ResourceIcon.GetIcon("imageres.dll", -15); } else if (hasLUAShield) - icon = ResourceIcon.GetIcon(iconPath = "imageres.dll", iconIndex = -78); - else - icon = ResourceIcon.GetIcon(iconPath = itemFilePath, iconIndex = 0); - - if (icon == null) - icon = ResourceIcon.GetExtensionIcon(itemFilePath) ?? ResourceIcon.GetIcon(iconPath = "imageres.dll", iconIndex = -2); - + icon = ResourceIcon.GetIcon("imageres.dll", -78); + else + icon = ResourceIcon.GetIcon(itemFilePath, 0); + + if (icon == null) + icon = ResourceIcon.GetExtensionIcon(itemFilePath) ?? ResourceIcon.GetIcon("imageres.dll", -2); Image image = icon.ToBitmap(); if (iconLocation == null && !hasLUAShield) { From cc26bc48c06d9a97c66debbb0d675524570b4ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Tue, 27 Jan 2026 16:29:58 +0800 Subject: [PATCH 04/16] Update ContextMenuManager/Controls/ShellList.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ContextMenuManager/Controls/ShellList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ContextMenuManager/Controls/ShellList.cs b/ContextMenuManager/Controls/ShellList.cs index bd5d5a7f..a8f51b5f 100644 --- a/ContextMenuManager/Controls/ShellList.cs +++ b/ContextMenuManager/Controls/ShellList.cs @@ -479,7 +479,7 @@ private Image GetItemIcon(string regPath) return image; } - private bool isMultiItem(string regPath) // helper + private bool IsMultiItem(string regPath) // helper { return GetIsMultiItem(regPath); } From 66b5ed32d59a15e62ab7a11c3cefdb7ea2e89de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Tue, 27 Jan 2026 16:30:33 +0800 Subject: [PATCH 05/16] Update ContextMenuManager/BluePointLilac.Controls/MySideBar.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ContextMenuManager/BluePointLilac.Controls/MySideBar.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs index eac5013d..7e0ef485 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs @@ -123,7 +123,11 @@ private void SetSelectedIndexDirectly(int val) 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() { ResumeLayout(true); UpdateBackground(); } + public void EndUpdate() + { + ResumeLayout(true); + UpdateBackground(); + } private void UpdateBackground() { From d811f530e5fbbf12e1a0105351c4d1df129d59fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Tue, 3 Feb 2026 10:55:59 +0800 Subject: [PATCH 06/16] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E3=80=81=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E3=80=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=88=97=E8=A1=A8=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BluePointLilac.Controls/MyListBox.cs | 327 +++--- ContextMenuManager/Controls/ShellList.cs | 930 ++++++------------ 2 files changed, 405 insertions(+), 852 deletions(-) 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/Controls/ShellList.cs b/ContextMenuManager/Controls/ShellList.cs index a8f51b5f..5a520261 100644 --- a/ContextMenuManager/Controls/ShellList.cs +++ b/ContextMenuManager/Controls/ShellList.cs @@ -16,26 +16,24 @@ 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 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; @@ -48,22 +46,14 @@ private struct ShellItemData public bool IsMultiItem; } - private static readonly Dictionary DefaultNameIndexs - = new(StringComparer.OrdinalIgnoreCase) - { - { "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() + 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, @@ -75,27 +65,16 @@ private static readonly Dictionary DefaultNameIndexs 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, @@ -111,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); @@ -262,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)); @@ -335,37 +226,23 @@ public void LoadItems() private void LoadItems(string scenePath) { if (scenePath == null) return; - - // Cancel previous task cts?.Cancel(); cts = new CancellationTokenSource(); var token = cts.Token; - // Start async loading Task.Run(() => { if (token.IsCancellationRequested) return; - RegTrustedInstaller.TakeRegKeyOwnerShip(scenePath); - - var shellPath = GetShellPath(scenePath); - var shellItemsData = GetShellItemsData(shellPath); - + var shellItemsData = GetShellItemsData(GetShellPath(scenePath)); if (token.IsCancellationRequested) return; Invoke(new Action(() => { if (token.IsCancellationRequested) return; - - // Add ShellItems from preloaded data foreach (var data in shellItemsData) - { AddItem(new ShellItem(data.RegPath, data.Text, data.Image, data.IsMultiItem)); - } - - // Continue with ShellEx items (sync for now, to keep it simple) LoadShellExItems(GetShellExPath(scenePath)); - ItemsLoaded?.Invoke(this, EventArgs.Empty); })); }, token); @@ -376,45 +253,35 @@ private List GetShellItemsData(string shellPath) var list = new List(); using var shellKey = RegistryEx.GetRegistryKey(shellPath); if (shellKey == null) return list; - - // RegTrustedInstaller.TakeRegTreeOwnerShip(shellKey.Name); // Assuming this is fast enough or handled foreach (var keyName in shellKey.GetSubKeyNames()) { - var regPath = $@"{shellPath}\{keyName}"; - var isMultiItem = GetIsMultiItem(regPath); - var text = GetItemText(regPath, keyName, isMultiItem); - var image = GetItemIcon(regPath); - list.Add(new ShellItemData { RegPath = regPath, Text = text, Image = image, IsMultiItem = isMultiItem }); + var regPath = $"{shellPath}\\{keyName}"; + list.Add(new ShellItemData + { + RegPath = regPath, + Text = GetItemText(regPath, keyName), + Image = GetItemIcon(regPath), + IsMultiItem = GetIsMultiItem(regPath) + }); } return list; } - // Helper methods copied from ShellItem logic - private bool GetIsMultiItem(string regPath) - { - var value = Registry.GetValue(regPath, "SubCommands", null); - if (value != null) return true; - value = Registry.GetValue(regPath, "ExtendedSubCommandsKey", null); - if (!string.IsNullOrEmpty(value?.ToString())) return true; - return false; - } + 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, bool isMultiItem) + private string GetItemText(string regPath, string keyName) { - string name; - var valueNames = new List { "MUIVerb" }; - if (!isMultiItem) valueNames.Add(""); - foreach (var valueName in valueNames) + foreach (var valueName in new[] { "MUIVerb", "" }) { - name = Registry.GetValue(regPath, valueName, null)?.ToString(); - name = ResourceString.GetDirectString(name); + 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)) { - name = $"@windows.storage.dll,-{index}"; - name = ResourceString.GetDirectString(name); + var name = ResourceString.GetDirectString($"@windows.storage.dll,-{index}"); if (!string.IsNullOrEmpty(name)) return name; } return RegistryEx.GetKeyName(regPath); @@ -422,127 +289,70 @@ private string GetItemText(string regPath, string keyName, bool isMultiItem) private Image GetItemIcon(string regPath) { - // Logic similar to ShellItem.ItemIcon but returns Image var iconLocation = Registry.GetValue(regPath, "Icon", null)?.ToString(); var hasLUAShield = Registry.GetValue(regPath, "HasLUAShield", null) != null; - - // We need ItemFilePath which is derived from Guid or Command - // ShellItem.ItemFilePath => GuidInfo.GetFilePath(Guid) ?? ObjectPath.ExtractFilePath(ItemCommand); - // This is complex to replicate exactly without creating ShellItem instance. - // But we can approximate. - - // Replicating Guid logic - string commandPath = $@"{regPath}\command"; - var keyValues = new Dictionary - { - { commandPath , "DelegateExecute" }, - { $@"{regPath}\DropTarget" , "CLSID" }, - { regPath , "ExplorerCommandHandler" }, - }; - Guid guid = Guid.Empty; - foreach (var item in keyValues) - { - var val = Registry.GetValue(item.Key, item.Value, null)?.ToString(); - if (GuidEx.TryParse(val, out var g)) { guid = g; break; } - } - - string itemCommand = null; - if (!isMultiItem(regPath)) // Helper needed - itemCommand = Registry.GetValue(commandPath, "", null)?.ToString(); - - string itemFilePath = GuidInfo.GetFilePath(guid) ?? ObjectPath.ExtractFilePath(itemCommand); + var commandPath = $"{regPath}\\command"; - Icon icon; - string iconPath; + 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; } - if (iconLocation != null) - { - icon = ResourceIcon.GetIcon(iconLocation, out iconPath, out _); - if (icon == null && Path.GetExtension(iconPath)?.ToLower() == ".exe") - icon = ResourceIcon.GetIcon("imageres.dll", -15); - } - else if (hasLUAShield) - icon = ResourceIcon.GetIcon("imageres.dll", -78); - else - icon = ResourceIcon.GetIcon(itemFilePath, 0); + var itemCommand = !GetIsMultiItem(regPath) ? Registry.GetValue(commandPath, "", null)?.ToString() : null; + var itemFilePath = GuidInfo.GetFilePath(guid) ?? ObjectPath.ExtractFilePath(itemCommand); - if (icon == null) - icon = ResourceIcon.GetExtensionIcon(itemFilePath) ?? ResourceIcon.GetIcon("imageres.dll", -2); - Image image = icon.ToBitmap(); - if (iconLocation == null && !hasLUAShield) - { - // ToTransparent logic if no icon/shield - // ShellItem: if (!HasIcon) Image = Image.ToTransparent(); - // HasIcon = !IconLocation.IsNullOrWhiteSpace() || HasLUAShield - image = image.ToTransparent(); - } - return image; - } + 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); - private bool IsMultiItem(string regPath) // helper - { - return GetIsMultiItem(regPath); + icon ??= ResourceIcon.GetExtensionIcon(itemFilePath) ?? ResourceIcon.GetIcon("imageres.dll", -2); + var image = icon.ToBitmap(); + return iconLocation == null && !hasLUAShield ? image.ToTransparent() : image; } - // Original LoadShellItems removed or replaced - // private void LoadShellItems(string shellPath) { ... } // Deleted - 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 }; } @@ -552,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)); } } @@ -563,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)); } } } @@ -595,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)); } @@ -628,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); @@ -643,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; @@ -653,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; @@ -707,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() @@ -723,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; } @@ -855,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) @@ -959,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); @@ -979,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; } @@ -991,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)); } @@ -1012,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); @@ -1033,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; } } @@ -1056,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 @@ -1135,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; } @@ -1157,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() }; } } } From 0dd0a5ca78defc40bae2c81c0e75989f13478dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Tue, 17 Feb 2026 10:11:46 +0800 Subject: [PATCH 07/16] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../BluePointLilac.Controls/MySideBar.cs | 17 ++++++++++++----- ContextMenuManager/MainForm.cs | 17 ++++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs index 7e0ef485..0b050930 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs @@ -13,7 +13,7 @@ public sealed class MySideBar : Panel private string[] itemNames; private int itemHeight = 36, selectIndex = -1, hoverIndex = -1; private int animTarget = -1, animCurrent = -1; - private float animProgress = 0f, curSelTop = -1; + private float animProgress = 0f, curSelTop = -itemHeight; private bool isAnimating = false; public Color SelectedGradientColor1 { get; set; } = Color.FromArgb(255, 195, 0); @@ -75,7 +75,10 @@ public int HoveredIndex public MySideBar() { - Dock = DockStyle.Left; MinimumSize = new Size(1, 1); BackgroundImageLayout = ImageLayout.None; DoubleBuffered = true; + Dock = DockStyle.Left; + MinimumSize = new Size(1, 1); + BackgroundImageLayout = ImageLayout.None; + DoubleBuffered = true; Font = new Font(SystemFonts.MenuFont.FontFamily, SystemFonts.MenuFont.Size + 1F); InitializeColors(); SizeChanged += (s, e) => UpdateBackground(); @@ -86,7 +89,7 @@ public MySideBar() else { float t = 1 - (float)Math.Pow(1 - animProgress, 3); - float start = TopSpace + animCurrent * ItemHeight, target = TopSpace + animTarget * ItemHeight; + float start = TopSpace + animCurrent * (float)ItemHeight, target = TopSpace + animTarget * (float)ItemHeight; curSelTop = Math.Max(0, Math.Min(start + (target - start) * t, Height - ItemHeight)); Invalidate(); } @@ -148,7 +151,7 @@ private void UpdateBackground() else if (ItemNames[i].Length > 0) g.DrawString(ItemNames[i], Font, tb, HorizontalSpace, TopSpace + i * ItemHeight + vSpace); } } - catch { BackgroundImage?.Dispose(); BackgroundImage = null; } + catch (ArgumentException) { BackgroundImage?.Dispose(); BackgroundImage = null; } } protected override void OnPaint(PaintEventArgs e) @@ -171,7 +174,11 @@ void DrawItem(int idx, Color back, Color fore, float y) e.Graphics.DrawString(ItemNames[idx], Font, fb, HorizontalSpace, y + vSpace); } - if (hoverIndex >= 0 && hoverIndex != selectIndex) DrawItem(hoverIndex, HoveredBackColor, HoveredForeColor, TopSpace + hoverIndex * ItemHeight); + 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(SeparatorColor)) e.Graphics.DrawLine(p, Width - 1, 0, Width - 1, Height); } diff --git a/ContextMenuManager/MainForm.cs b/ContextMenuManager/MainForm.cs index ae2a3518..d90146be 100644 --- a/ContextMenuManager/MainForm.cs +++ b/ContextMenuManager/MainForm.cs @@ -58,14 +58,17 @@ public MainForm() ResizeSideBar(); JumpItem(0, 0); - shellList.ItemsLoaded += (sender, e) => + shellList.ItemsLoaded -= ShellList_ItemsLoaded; + shellList.ItemsLoaded += ShellList_ItemsLoaded; + } + + private void ShellList_ItemsLoaded(object sender, EventArgs e) + { + if (currentListControl == shellList) { - if (currentListControl == shellList) - { - SaveOriginalListItems(); - if (!string.IsNullOrEmpty(searchBox.Text)) FilterItems(searchBox.Text); - } - }; + SaveOriginalListItems(); + if (!string.IsNullOrEmpty(searchBox.Text)) FilterItems(searchBox.Text); + } } private readonly MyToolBarButton[] ToolBarButtons = From 5daaf52e04ac9966e36537286a32c2a6f80b6b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Tue, 17 Feb 2026 10:20:16 +0800 Subject: [PATCH 08/16] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20MySideBar.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ContextMenuManager/BluePointLilac.Controls/MySideBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs index 0b050930..a2b0b7b1 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs @@ -13,7 +13,7 @@ public sealed class MySideBar : Panel private string[] itemNames; private int itemHeight = 36, selectIndex = -1, hoverIndex = -1; private int animTarget = -1, animCurrent = -1; - private float animProgress = 0f, curSelTop = -itemHeight; + private float animProgress = 0f, curSelTop = -36; private bool isAnimating = false; public Color SelectedGradientColor1 { get; set; } = Color.FromArgb(255, 195, 0); From e88f7752bf54546917955136c6583e0f92f5f90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Tue, 17 Feb 2026 10:43:02 +0800 Subject: [PATCH 09/16] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20MySideBar.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BluePointLilac.Controls/MySideBar.cs | 135 ++++++++++++++---- 1 file changed, 107 insertions(+), 28 deletions(-) diff --git a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs index a2b0b7b1..d09d1ac6 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs @@ -3,18 +3,21 @@ 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 const float AnimationSpeed = 0.25f; private readonly Timer animTimer = new() { Interval = 16 }; private string[] itemNames; 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); @@ -46,8 +49,10 @@ public string[] ItemNames itemNames = value; if (value != null && !IsFixedWidth) { - var maxW = 0; - foreach (var s in value) if (s != null) maxW = Math.Max(maxW, TextRenderer.MeasureText(s, Font).Width); + var maxW = value.Where(s => s != null) + .Select(s => TextRenderer.MeasureText(s, Font).Width) + .DefaultIfEmpty(0) + .Max(); Width = maxW + 2 * HorizontalSpace; } UpdateBackground(); @@ -70,30 +75,36 @@ public int SelectedIndex public int HoveredIndex { get => hoverIndex; - set { if (hoverIndex != value) { hoverIndex = value; Invalidate(); HoverIndexChanged?.Invoke(this, EventArgs.Empty); } } + set + { + if (hoverIndex == value) return; + int oldIdx = hoverIndex; + hoverIndex = 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; InitializeColors(); SizeChanged += (s, e) => UpdateBackground(); - animTimer.Tick += (s, e) => - { - animProgress += 0.25f; - if (animProgress >= 1f) { isAnimating = false; animTimer.Stop(); SetSelectedIndexDirectly(animTarget); } - else - { - float t = 1 - (float)Math.Pow(1 - animProgress, 3); - float start = TopSpace + animCurrent * (float)ItemHeight, target = TopSpace + animTarget * (float)ItemHeight; - curSelTop = Math.Max(0, Math.Min(start + (target - start) * t, Height - ItemHeight)); - Invalidate(); - } - }; + animTimer.Tick += AnimationTimer_Tick; DarkModeHelper.ThemeChanged += OnThemeChanged; SelectedIndex = -1; } @@ -109,6 +120,44 @@ private void InitializeColors() BackgroundGradientColor3 = DarkModeHelper.ToolBarGradientBottom; } + private void AnimationTimer_Tick(object sender, EventArgs e) + { + animProgress += AnimationSpeed; + if (animProgress >= 1f) + CompleteAnimation(); + else + UpdateAnimationFrame(); + } + + private void UpdateAnimationFrame() + { + 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() + { + isAnimating = false; + animTimer.Stop(); + SetSelectedIndexDirectly(animTarget); + } + + private static float CalculateEasedProgress(float progress) + { + return 1 - (float)Math.Pow(1 - progress, 3); + } + + private void InvalidateAnimationRegion(float oldTop, float newTop) + { + 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 void StartAnimation(int from, int to) { animCurrent = from; animTarget = to; animProgress = 0f; isAnimating = true; @@ -117,9 +166,13 @@ private void StartAnimation(int from, int to) private void SetSelectedIndexDirectly(int val) { + int oldIdx = selectIndex; selectIndex = val; curSelTop = (val >= 0 && ItemNames != null && val < ItemNames.Length) ? TopSpace + val * ItemHeight : -ItemHeight; - HoveredIndex = val; Invalidate(); SelectIndexChanged?.Invoke(this, EventArgs.Empty); + InvalidateItem(oldIdx); + InvalidateItem(val); + HoveredIndex = val; + SelectIndexChanged?.Invoke(this, EventArgs.Empty); } public void StopAnimation() { if (isAnimating) { animTimer.Stop(); isAnimating = false; SetSelectedIndexDirectly(animTarget); } } @@ -140,18 +193,44 @@ private void UpdateBackground() { var old = BackgroundImage; BackgroundImage = new Bitmap(w, h); old?.Dispose(); using var g = Graphics.FromImage(BackgroundImage); - using (var b = new LinearGradientBrush(new Rectangle(0, 0, w, h), Color.Empty, Color.Empty, 0f) { InterpolationColors = new ColorBlend { Colors = new[] { BackgroundGradientColor1, BackgroundGradientColor2, BackgroundGradientColor3 }, Positions = new[] { 0f, 0.5f, 1f } } }) - g.FillRectangle(b, new Rectangle(0, 0, w, h)); - - using var tb = new SolidBrush(ForeColor); using var p = new Pen(SeparatorColor); - float vSpace = (ItemHeight - TextRenderer.MeasureText(" ", Font).Height) * 0.5f; - for (int i = 0; i < ItemNames.Length; i++) + DrawBackgroundGradient(g, w, h); + DrawTextItemsAndSeparators(g); + } + catch (ArgumentException) { BackgroundImage?.Dispose(); BackgroundImage = null; } + } + + private void DrawBackgroundGradient(Graphics g, int w, int h) + { + using var b = new LinearGradientBrush(new Rectangle(0, 0, w, h), Color.Empty, Color.Empty, 0f) + { + InterpolationColors = new ColorBlend { - if (ItemNames[i] == null) g.DrawLine(p, HorizontalSpace, TopSpace + (i + 0.5f) * ItemHeight, Width - HorizontalSpace, TopSpace + (i + 0.5f) * ItemHeight); - else if (ItemNames[i].Length > 0) g.DrawString(ItemNames[i], Font, tb, HorizontalSpace, TopSpace + i * ItemHeight + vSpace); + Colors = new[] { BackgroundGradientColor1, BackgroundGradientColor2, BackgroundGradientColor3 }, + Positions = new[] { 0f, 0.5f, 1f } } + }; + g.FillRectangle(b, new Rectangle(0, 0, w, h)); + } + + private void DrawTextItemsAndSeparators(Graphics g) + { + using var textBrush = new SolidBrush(ForeColor); + using var separatorPen = new Pen(SeparatorColor); + float vSpace = (ItemHeight - TextRenderer.MeasureText(" ", Font).Height) * 0.5f; + for (int i = 0; i < ItemNames.Length; i++) + { + 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); } - catch (ArgumentException) { BackgroundImage?.Dispose(); BackgroundImage = null; } + } + + private void DrawSeparator(Graphics g, Pen pen, int index) + { + float y = TopSpace + (index + 0.5f) * ItemHeight; + g.DrawLine(pen, HorizontalSpace, y, Width - HorizontalSpace, y); } protected override void OnPaint(PaintEventArgs e) @@ -161,7 +240,7 @@ protected override void OnPaint(PaintEventArgs e) float vSpace = (ItemHeight - TextRenderer.MeasureText(" ", Font).Height) * 0.5f; void DrawItem(int idx, Color back, Color fore, float y) { - if (idx < 0 || idx >= ItemNames.Length) return; + if (idx < 0 || idx >= ItemNames.Length || string.IsNullOrEmpty(ItemNames[idx])) return; var r = new RectangleF(0, y, Width, ItemHeight); if (back == Color.Transparent) { @@ -208,6 +287,6 @@ protected override void OnMouseDown(MouseEventArgs 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 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(); Font?.Dispose(); } base.Dispose(disposing); } + protected override void Dispose(bool disposing) { if (disposing) { DarkModeHelper.ThemeChanged -= OnThemeChanged; animTimer?.Dispose(); ownedFont?.Dispose(); } base.Dispose(disposing); } } } From 8dc6973e6668e81a307b2d68475951da2d533bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Fri, 27 Feb 2026 14:36:54 +0800 Subject: [PATCH 10/16] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BluePointLilac.Controls/LoadingDialog.cs | 340 +++----- .../BluePointLilac.Controls/RComboBox.cs | 635 ++++---------- ContextMenuManager/MainForm.cs | 774 +++++++----------- 3 files changed, 612 insertions(+), 1137 deletions(-) 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/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/MainForm.cs b/ContextMenuManager/MainForm.cs index d90146be..bcf8f225 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,70 +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); - - shellList.ItemsLoaded -= ShellList_ItemsLoaded; - shellList.ItemsLoaded += ShellList_ItemsLoaded; - } - - private void ShellList_ItemsLoaded(object sender, EventArgs e) - { - if (currentListControl == shellList) - { - SaveOriginalListItems(); - if (!string.IsNullOrEmpty(searchBox.Text)) FilterItems(searchBox.Text); - } - } + 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 = { @@ -80,25 +29,15 @@ private void ShellList_ItemsLoaded(object sender, EventArgs e) 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(); @@ -107,159 +46,149 @@ private void ShellList_ItemsLoaded(object sender, EventArgs e) 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.LoadAboutInfo(); 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(); @@ -279,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); @@ -373,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; } } @@ -405,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() @@ -415,203 +340,116 @@ 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; } } private void SwitchAboutItem() { - switch (SideBar.SelectedIndex) - { - case 0: - appSettingBox.LoadItems(); appSettingBox.Visible = true; - break; - case 1: - languagesBox.LoadLanguages(); languagesBox.Visible = true; - break; - case 2: - backupListBox.LoadItems(); backupListBox.Visible = true; - break; - case 3: - dictionariesBox.LoadText(); dictionariesBox.Visible = true; - break; - case 4: - aboutMeBox.LoadAboutInfo(); 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; - } - } - } - } - } + private static bool ContainsText(string text, string search, StringComparison comparison) + => !string.IsNullOrEmpty(text) && text.Contains(search, comparison); - if (matches) - { - itemsToShow.Add(item); - } - } - } - - // 清除当前列表并添加匹配的项 - 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; @@ -622,118 +460,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 +} From fd3a2fb88ea4b85d68ed26f5e3d32acd88df399f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Wed, 4 Mar 2026 11:44:13 +0800 Subject: [PATCH 11/16] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E6=A0=8F=E5=A4=96=E8=A7=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BluePointLilac.Controls/MyToolBar.cs | 202 ++++++++++++------ 1 file changed, 133 insertions(+), 69 deletions(-) diff --git a/ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs b/ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs index 8ddeedae..ffe4efa3 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; @@ -25,7 +24,6 @@ public MyToolBar() BackColor = DarkModeHelper.TitleArea; ForeColor = DarkModeHelper.FormFore; - // 创建按钮容器(左侧) buttonContainer = new FlowLayoutPanel { Dock = DockStyle.Left, @@ -36,7 +34,6 @@ public MyToolBar() BackColor = Color.Transparent }; - // 创建搜索框容器(右侧) searchBoxContainer = new Panel { Dock = DockStyle.Right, @@ -48,7 +45,6 @@ public MyToolBar() Controls.Add(buttonContainer); Controls.Add(searchBoxContainer); - // 监听主题变化 DarkModeHelper.ThemeChanged += OnThemeChanged; } @@ -61,16 +57,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 +83,6 @@ public int SelectedIndex null : (MyToolBarButton)buttonContainer.Controls[value]; } - // 添加一个方法来获取所有按钮(用于兼容旧代码) public Control.ControlCollection ButtonControls => buttonContainer.Controls; public void AddButton(MyToolBarButton button) @@ -104,8 +101,9 @@ public void AddButton(MyToolBarButton button) { if (button != SelectedButton) { - button.Opacity = HoveredOpacity; // 动画过渡到悬停状态 - button.UpdateTextColor(); // 更新文字颜色 + button.Opacity = HoveredOpacity; + button.UpdateTextColor(); + button.IsHovered = true; } }; @@ -113,8 +111,9 @@ public void AddButton(MyToolBarButton button) { if (button != SelectedButton) { - button.Opacity = UnSelctedOpacity; // 动画过渡到未选中状态 - button.UpdateTextColor(); // 更新文字颜色 + button.Opacity = UnSelctedOpacity; + button.UpdateTextColor(); + button.IsHovered = false; } }; @@ -128,13 +127,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 +142,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) { @@ -167,30 +161,28 @@ 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创建三色渐变 + 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; - e.Graphics.FillRectangle(brush, rect); + 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; @@ -212,9 +204,43 @@ 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) { @@ -225,16 +251,16 @@ public MyToolBarButton(Image image, string text) 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,7 +270,7 @@ 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 }; @@ -253,7 +279,7 @@ public MyToolBarButton(Image image, string text) { ForeColor = DarkModeHelper.FormFore, 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 +297,7 @@ public Image Image } public bool CanBeSelected = true; - private readonly float opacity; + public float Opacity { get => currentOpacity; @@ -284,40 +310,61 @@ 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 padding = 4; + var drawRect = new Rectangle( + padding, + padding, + Width - padding * 2, + Height - padding * 2); - // 深色模式使用白色,浅色模式使用黑色 - var baseColor = isDarkMode ? Color.White : Color.Black; + using var path = DarkModeHelper.CreateRoundedRectanglePath(drawRect, borderRadius); - // 减少两种模式的不透明度 - 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值在有效范围内 + 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); - var fillColor = Color.FromArgb(alpha, baseColor); + 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 (!isDarkMode && currentOpacity > 0.3f) { lblText.ForeColor = Color.White; } @@ -329,21 +376,39 @@ public void UpdateTextColor() 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 +416,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; } } From 3bdf74972398f38acfa350907d01a7f27538c078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Wed, 4 Mar 2026 13:04:13 +0800 Subject: [PATCH 12/16] =?UTF-8?q?=E6=92=A4=E9=94=80=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=85=B3=E4=BA=8E=E9=A1=B5=E9=9D=A2=E4=BB=A5=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E6=96=87=E5=AD=97=E4=B8=8D=E6=98=BE=E7=A4=BA=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BluePointLilac.Methods/IniReader.cs | 19 +- ContextMenuManager/MainForm.cs | 24 +- ContextMenuManager/Methods/AppConfig.cs | 16 +- ContextMenuManager/Methods/AppString.cs | 26 +- .../Properties/Resources.Designer.cs | 10 - ContextMenuManager/Properties/Resources.resx | 3 - .../Properties/Resources/Images/Logo.png | Bin 33920 -> 0 bytes .../Resources/Texts/AppLanguageDic.ini | 48 +- ...0\271\330\261\330\250\331\212\330\251.ini" | 30 +- languages/de-DE.ini | 7 - languages/en-US.ini | 29 +- languages/ja-JP.ini | 23 +- languages/ko-KR.ini | 52 +- languages/pt-BR.ini | 643 +++++++++--------- languages/ru-RU.ini | Bin 26923 -> 35446 bytes languages/zh-CN.ini | 7 - 16 files changed, 383 insertions(+), 554 deletions(-) delete mode 100644 ContextMenuManager/Properties/Resources/Images/Logo.png 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/MainForm.cs b/ContextMenuManager/MainForm.cs index bcf8f225..f4af2a04 100644 --- a/ContextMenuManager/MainForm.cs +++ b/ContextMenuManager/MainForm.cs @@ -41,7 +41,7 @@ internal sealed class MainForm : MyMainForm 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(); @@ -377,6 +377,28 @@ private void SwitchOtherRuleItem() private void SwitchAboutItem() { + switch (SideBar.SelectedIndex) + { + case 0: + appSettingBox.LoadItems(); appSettingBox.Visible = true; + break; + case 1: + languagesBox.LoadLanguages(); languagesBox.Visible = true; + break; + case 2: + backupListBox.LoadItems(); backupListBox.Visible = true; + break; + case 3: + dictionariesBox.LoadText(); dictionariesBox.Visible = true; + break; + case 4: + 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](); 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 d92495acd638af75f91a6f64803253ba7823fcc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33920 zcmb?@g;QH!v~`d|u%gA?wYU^_FHl;F6?cagD4O8KU5Z12Qi{71N^y$2yGw9OAYXpp zoA)2QJDJJ7Gnsqt*=OIq_F8+NMCoX$;9*l@0{{R#HC07j000GfiUI&)Aiq9+{$=~$ z)fZhAc|g?|^&#>C{foTX2O#nY09r=^0Q3MgMY#_?IVYXI9V|n7>kn~}pUy6Cm)g^- zO33^smyXgEHGWamwm6_0$OoKQI0q&=`%059{>2QH5ut6Ds^e#p#1)(xSTJZJtiv-P zHwN-0v&ah2SNxpybUJLjEivm1?9_Noc%|}MRM2fN5USVt+fOf0dp0mrl*!e0=kwS@ zu31iGSRt+ry8^|qPJ}?}|KVxfwBpJE2JUXwu=l!I{V0rT7%#mLrKPpR2chWwFvyeu z44R$QLz!@G?t`t}N#C7s{&lTi^z85N&++SY9k6*G9*%DA>N?An0e8Wov;%$^SI^^h zzt@&3!5QEWxAB1KxDruN24yQK4z>C0B&qZs9sTxMhpp*@mzUSP?dPjZFpqB@8A@gt zDsNnLhda}4P6_2tkROzvVX}EiFEq zSNRBdI0W_;;hGI9GRKBd4!=w-L|MpWWWU&hg6`U>pnmi1XB1Ra?Plr^Mk95mTwyJk z3!?URFZ#NEh=&fx@naVptPsWS@tvGH5K#Nhpc^Hvs+1M0_6Fo8Zl&* zAmyDjNI)I|v``tG0IY}&`95IM`pAOr|I)49O(@`ZPS;gHo4AjS!wH)4m;Tt2d|_Jr zeVH-4ej**qUn)5ctAoNIcWKoDyEstaaev!Gicn5d3-)=P2#k%#=t0m~=bh<8=6Jwf z;l;%Tv})FIou?##sbJ$M4W{h-JW3vG(8GtDf8yPqpPQWx>zwaA^D=uOAJMQ)s>Y`8TrqL`Oih#fk*FY6|CGxZR`P6xy5fe+=l<`n zV2&s;A@pT-r0qKy0c%Lt8(v&Ayhut2*y%R;x_YBeGvF)Z4$q=BXeQi5O@1w}8g@bO z6Ngn6hzAVQq30nWH4oOA#Ow~MeFuEMZLSGawn`*-j&+}CAt(OfG3kiS+jfrJwqOj^dXN!19V`?5sK~!@=uMpc-^Xi9UBE6bGaNIZD`QJ@Q$?#fb$+il!3t{=h(0%OYb_`)?daM#_j-j8*yL z#Kd~m9Rr+%7IR}A5u~^NDNK+rJ8 zO)}^6_|wYxtn_&>K0M8+@^)K3vb`?t_bUlAwssTrt3h{KB}_H46DfYpadtngLnXOI z4HC2NR}?`xTWrUMe|3JqTcI8q5G?*@?m};1>|6Rf#Z84Mh6VL%dGd&L`$i)KycBd) z3X)>Q`xapyBOnAYPRZFq3?{$_6K0NY@{@IGUw`~3{Yx**^b_%6uH2DussqD`N8*h^ znU!ijr_Y6d8p`17MR{_a7l?YBY<@r+qJ4g~QrKEjJ12nSGUb z`B6(~AqR)m7|*44x^;O<7@-~DsLOpFxw>oqRSL=JZo8cBVY4ZLBECU_imIyTdVKKx zU&KWQ_#q=OBEnn&xqfjhlYGL0f>&fJ%F55aX7slR z60q&N)vxROhuW_x)s-VK3XF6!U?=tCYhO+lYV(`9k0qMAh(k~E6c3e(NzC68aJ2p7 zE>DFp@6nM!W8~DqCCuhScU=<&jNF5j8RWs9?XS7~M=I|t*+JC2z3;0b{zRw*$tF^KuxuWJu>p{WquGo0x^jxsUR zk4(-K^toy^KV_{S*TYdiAVdlo(de-w7@bI>O+Y_-*o!UIrL#G>Oi9&ab>^_$#VF`!rkLCJ1kLO7t@(H$XM{g(5BF$fx`(v}TZJd;>FWY5gpWmm|KCw!|y;oPK zrmN+FXe}YqrUl&Gzf7~va7_)0id(!Zhq5r{J1!}$YoSb zJ}!{H${auOM}ih;tmzbjyBqf2kgf}t>|HL|J6#n%GgU@>{hx5d@C;|{Ew?v?c7mnG zspXU3e$$4<$%hGi<|~IY1oqP#{Gc*u*!5_j=IK;1B4gu^7HM8S1wSES=7DlFW6?2xnwj!aF-Aylqo&5m3kqQIWub3grw3i=<))fzeDwRl2TvxGlEYav&Hf`Z`V#_GUR z=sBkfTD9#$iC?^Hgsx$fL$^AMklEvxtM-y?K^9`qd4Ie$vuRk{^iGxku9v)|ipfa>yG zLvN_lErL8B@z$I^WVH zxT83g%@y)=JDxA`^(^XZ=dp?KxZ3Jsk?-!g~0+n ze4k3Dg&T523&QKmP*jmY`jE@)VHh8>F9pr~`3=L>!MBk-NN7#w>)FcP$!hyuM(|28 zwcarAJS2WMKW55H!^&S+_He}fS?|-OIt%Brsr7ENPeqq)dSm<6Cye;O1=YIMZ$otp z{(OVLoxfv+;rIg#e|YjGuO0aSs03eEJia~cq-Z~TcA6p_ zunV=zF26Y$aDhYWKIz1b`aV$)>2IEv#|D+bb?YjB;CFeRk14}=Qt(ncIXe~!RwMuP zm0b=TFP^r3S;3_oqz|ciJ0MzNSUmt_``TM~CT|tn+}8G(@^#f~p7(Ri6>4)E3PBKj zG5={X3GyI{*kf~EetxzL7ue0HB&%)XrO%Pt9bPNFe`VtL=Cd2|)Lmp6hi7Io?(b&9 z8J*b)#?Kt@F9o(my2+g%vOajxq|=AgNrw1HU8fuFqIY@mEiGfLhIc^`$8?W{bdSqs z&zBh~gb~d-ge_@!L4cWk>ATiWd>Qz;n=RX+YO(gi+n~m4Om@N$Vdbo3={{+$=a&7Z+ToVottQ^JqIyfV;cZ-l#&F%TI<68zP5@S6KTaP$UP-p_ z;AtF%!Y)h_{^WAQv7#yP&Q>5xgeKJuvTaSikEMt(ozS4;Li> z<(Ht{m}J9O=-Rj{M``q{Xrw1SQQl$czu6APl5xp{R+pV- zw|)ZW>w}jON(eo5e*CQd(jYr61m!>w7cCWX=}*Y=p&+D7!^lI*0zk0-Q;UJ9uzh5dfa!$TAKL*cCI zNLp=7if8>Mgin9YrMl*%3yo=27ahP{gul;g+HoIDIuE){a0=sE|p1DiD!Gn2F!aVSXW zF-*|k%M1do!TBbn3Hqs{VY+QKYjiKoaY1%bEc=!A!qV>F`D+BEq@MCbksf5W!zzl} z9D+WQ()Xh!p?hh4+4w=&BtI|j z`T743$tWZI@Rmii9XTjJ!fc`0Z5@npVi}%uSuGhp{mk0pPp@+DssnVp*}jEY%T3A~ zdSa8qgq(8ZZ zYFyfg;$?5$=cAc2CktIH?`bn#u-l z{!Hxhd0-_#`Bj?dcQm#=kO0;K^1rJ{eE&RKZTVF@)YBg__PWR{XwYx_ggkKv_E9Fh ztm5;sK#G&K;NoL^Zq&P&;!Qp9W7U%0S@{pmRF`V8dA#Psa#-cM!<)WCG~=E>(Bk=s#2JvemY;MXj|c;H$fe89}jMtL?IP z{8Mm?UM~l|zwLBf)=H1_3F*1hy{yKqdIhe3LE)2McgO66o}3@S9qoU9!i0vu33~>M zf8H5{1qEf(TbCNb%d*Ue|3)f7M6LQt`wf^n%o95=cP{r3MI*7)g=mcfu=ObXFwpVnoaK1Prrt-HMx=8DvqCSuZ>$V!9=`3_vkeSww zm8){ra_%u!uv#L9{Yeb3E(DzEk(bpqwC#r~Gib;UrV zkJ1f*>~{yFL56~cBzKNP-P5~(W9Vtlo^{kddsHUrDELVn>25X8oU>0K)L{>p6=oT* zk9GgxA240^a=&5an%3!H@zy*~Y4CPlow|Wx`ip;7LnU(2Q-k2sGEdX(S~+4D6by=0 z)iN1t%hKNj5pTj$Jh4+(PNbEydhW`faOu-I@&4#AazqW)->IfLx0BxSl%DKHzE#DU zhsdg|MkKk;h82y?%*+)1$4eVq>VWFu+G?N6g$7Ilp$N5jNROeMN`>k1a3SXh_+fEfeftC-LFR zz&1khyI@l#8nhCLBr9bY^h0CsxVX6JzEba3TjP8gc9YMkN1z_YlYzQ26`V7ick|AE z!60ZhTz59@XPdTAHE?IVTdm}?kHaZCMMZTrg6_YE1^&;?WQjTpuB80$&YotS<@iOH z+wSRV!p!Poay=a-_CV{_pTDg9edQ76y&b&LByX?*LB+o!*A2~c!F&)pB=od#HJ z1+1*Bv}c~;h|M9k{A&S;uFbj>P43AocS94*sqlzr z!!D{N?_xmii(8bH<|Upx25Eb`T}Ft!OP+`azVOaMNeuFOxZK5$OGrRmSzE0qYMyT9 zJR2~a(GBakR(Lj!T=w4KRR|)%=c(F%^%#_pN7zzWmm&|q8srT;)cBew=CP$8HE^2| zH_-I@StYfu>dO^UxBO!n1)%W;<}9m^gcHQZ#!5;G2wV=y!2ime^k%!UV=r(3>2aRG z<4vbuWm_*t`q(_upMe#Qzl2kHhG!*1BzCcu7%b)U!;_%@Sg_>s$PT>Q5syeklFkw{ z3{_(HR;@cLGxK3%VPQd~@YBP^p2zqTRhVGdvp&yH&qf(cR>CCX)K_>5=SRui2Hcu( zB?3Zq9)=gA$oK-;55}au4|H{BuOa?#7BcstGA&Q>5@+{b-IZApLvQY&i67T{WfWRb z)dbAmO3Fp|%fDwLVM6~*ZKky~q=x!NnO+%72`w&0x$vz>$-cm{wj}iS6|Sq+Z1NxW|>gsPm*6Nd$nPd*=4FaGlee%>Yq3vhK%<2A}fHyVD68GMTjp~!L5_B;A^2ppU&+A*-RM>2i&h@mtu zJs8sREzLEBZ>B6cTk*mw4$1-cUNjSZtU?KPF`hXVX>5ygp$gzFn08QD_+t?sk*Rbj ztZbt2_m;3FszVXj0Y zV@>AGn}-;^SG(~14Q@ET&ff)uIqxWq)v>eg zZ}`rp8pBTb-&ab@fOItZ7Uipz~{0=KL zQPwGuw1;9JuGt;4Kkx4RXEQK`dXZ^SX3AFk3r&fXCK2n(Nx!T?Y{fIy$eBMU)*mL7 z_yBaDRCw&q`BLBcW7%*k_3r;5tW3G+0{l_$Lk5Uz_=avcokb7iY}DxgeP3&dCRmGA zCj3C_yYmw|;Q{rGTcdHuv)>BxXC1Ob6^hvO+>>o&(f(# zu4_j39acypJuWbW-Uwg*WBRGvQM#-eu@KL=@nXyV@OOEoPGy+bLywRKJL%hZJ*%ZnN<5NB9g z)Y7W;h6=+#4prP!*KbwxEWi*S1geWr-u|>}qy1NLACT<)v{)mnt|5?tUv`8z$I!i{ z#`=rBV=_|;15CMHXa`%pnLyb}o#2o6TO4sG$?^UYq(LB)CNLHo3pwa>P5?WCsqrC|k; zctMf;-or1L!}9sjExM7G@O_w36E{gPDvFx6Cwin!v9enEI-Yy7CLC{5wonf9F-h=d zyM%Y`Z~D@fJ2`Rxgieu=Es$WCr{_&08eeBChJFsoA|>>%uQFtqffi{M8o{33MzJ!LQf#yY;( z*Jojl~f45>^Fv0t&GEwj8;hr zjbmyd>xGomPGMNEM^CZEPRI0e200=|9gu3+tU#-UBee9<1Ch=A5;r3#GXiQvtfW?3BD`6KQI$D9=ZU&bc4@fCbkw!fl; zQ!f$^QpGgO;%O|giVtf}uud#4K5|MQO z%jFD32z{@&(?TPq+{U!2@n5JXfB*TE23svp%xXK^p{L}>kLeDT)ze1}24+sRO-)(^ zVID0EN%EyME9qgiekm8V{L0xyEv_KlQPVps?TdQ9eLuIParQzuY6;u2v4xaq1tjFc<(pIfc-8XI24kt3mG^J>Jds`-- z=ig6+cqsyJI2)oh+9u45`J@y#d(m9W_;S)gY1IuNAH4vKq(=FEB1iSNwchTbU(~GN z_<`N#9Tm-=Iv4R{C(n7~19nkz23ZcAk`LF$m6Jv(<81U&5y>G&o&CM{V*cJxi{dvS zHinBCPCqfYcNw+GcO>n)-}ig+}IXLfDPe`%h!z0HIC`no#X7a?F7tDul#Rg zzwFbniTNxuC@U-L_R8)pEUsHCUC+kUrwP~$)xK|#%~>T+iE%NjTX5i8%__EGtsyaB zRLr9PLMvLp_0PUR2fY#A?m~=Knc8+$=@xkG27jzZJW9@s3E%rM0I+`jr;xi1MRdLQ z$`kqDUVt~xZC?zyczfI{5I_5v>4@e8C*jH14dkh zFcS9VXd=J4`SbQwNp<36<~Y9aBj#u<^*_D$z;F0;*2p6G%!;_~u;;&^$k5Lz?6v&B zB%gp3TYxPlt&)Uj(sNA3ov4H16s^?{ch(15@^ghhM$JRvyH+Q^o5lD&GCe#f9C}u~ zTJs_by5M%6thDM0M)c^rX!w%n^TGK$ln|p6Bc`C#2Lu%H1X=&DSOi9moO0+Id%_sT z?L^qJj6dSEH5PF%-A&)3i^qO<&*VGG#QbDUAls81ZZY_PlVMsT&h}F z1HqM7?b(Y=m^fscs99?KZb;W>fOqtf%tli8NAR%<5w^ViJGP2b-nWF_)cd_iqIu1m z4}N~IPMi>S9B51ngMZoE+moJh76Dgyc7P{-+@8oNw)~|4iEG>&cj7M!fWj( z;-JJ4C`L}H(UMJSs^Ylm$qP-P+hPXk;rkEpakl)etrCjVI+Qr5e}PxUz0$q8E>1B? zZ0twcT6l3g%Sp4TJ(jZ7q4s-{SyzGWR==)(6qyNO~`;-gtw$< z5%;IStH~6e%kJc%|93mD%z?GnrkHQg*}k_?KVCMj?lTONYoxTaRK|Th@a~Co!y|66 z+ld0Tcny)HLhFHSdN8ZXIk9@g0otYdU_=)>(+{ClYWxR}U-e$C+T};m%@jH_N8!y<%5T<)Khon++cml*>RK9N zbLhe8@1_F%15AF$1AOuZ<{=l<<5FPGM##G~+E@(aZuG6_}EmohKJX@V3FtWvg(Q12u9|OESQa4Km%kE-aph zgMyAXaQ@g?f0CY2Q!F=RpJ+|VVk3apY%Ctv?m%<@JSD&o_OLDUye-;sYy$NQw(kmZ zQ9+r{LMj_MNEjK>d(}G}GiV+tHUed3j&sKgTd0_YT8*v9&0~%bl?_1opMIQA^qZy( zW;j)5=4Ni5wBm$WG^80s$(0tHbnbmV-;jQ}<{gwYN%(1Ajw9`O0Gj`vK4I^Xq%#sD z{6H?-USY+Q&VV_eiXVJ9JZm3`7M6c7Oc28sm%k>gwP2zwf$2ud1d z>%)*a%>`k{ZQ-K|D-IojVKai>e)@3qSGql%WtO|_z#Qhc)<{LQwHe?M>YTMW8`824f#_~>AvDa+t z>B?VqKKb>XcY9w9Z@#rlV|LWILTvno|EQPP=N)?gJFUXh-pV&Kj;^V_3Z(t}GOTC8 z6KzXZr98twf*U+o=e1JeY3MNoeg6_W#zA485;SwhJZ-D4In4wNWF;S#Ui~gsRFB%e zUcwB~pJS;COkB>LZm}TKz~XkOA(;d!GU>(xn(*;}-G-p7vmJ#_m(1LR*jPB=Tsty3 z#P6wUnR%WFMOC&Fwh9I5BdeQ44*O$w+4kFnL zO*GBbVDVbh<+tm1Yj1WfK-IE|PHyt(jC{)5UQ$MMRvCS|_W2M56VHn-O;M9wtH5VcDvrA;X zkDY?feIJT=hC=q1Ay3N?^)NtFqsF-rBT|W|t*yQLpqHG1Ah!_yY--N#t7ZWq4P&39 zy8CR{ndeGgGQ}wjxZTfVnH>4W6dlfMn)UOT?Ia+QpW!CrFAZD#XZSrbfe>NRP51?K zadu8sF48KUw(#2`vO2~2g{_k#axJS^LQCvsB#}PI9xi+J z;^YR@KFu1;9u`N=ZmbtoEEAlRedH8q#CstboDH(t#@F-U`<}! zg$srtzz#mV8(&gOtuBclqvy5b!-K`1Syb_p6l+LluyfsvPmrNU$b>=CRylj#Tiw9- zKg_P%CZl07b4(dF%OY%A)JoZ3*s)X+X!bT-U}f`8Wta&q&ChHS7ngC$XagQdG_djZ zPM@57=h$eJe_6^0EjfDvjM;$q|O;TqgmZPljDI3j?Y4h8yg0wMO$x(^aAwok_ZsO6C*4F}onKlyep`%# zlAMYvCRX(W|7q1o518ibamjux1Fk}PM!KPiana(hFYw+dF$n$bEr+CPPr4=q!a*Kd zT)C*U5*!poD4!LvjFT(A8dUNsVS-}QCoq^EcS0QdPb`nXT;{^lU1Im_ub;EIO5Xbu zXJ;$7JJLBwwNfJ3Zpf;mTp4wq6Fu=i^_+UZV=akiP+E4bN6=X?tM!m2(DtoRe*XT6 zRnskL`VB3v02!36LL@}!n>=&T?vyJyg)%}>uC7Mx;(GCWr{+b>&9JKUfX71nS6&Sb zjelcf0{m|%Jka`!hg}6=r+Cn~;pIwFoR}D)Bd>Ux&fL90Rs21&p+0h@;$<)Gi|JTM zFL~;x_4^8X5#2nxx~?@#3y*MbQGi9^ulfpVWgGb3YvS=@5(w$rUy8)rGbOaYS}bcb zAX;CcOs&Uxs`ZNjZ0CpxLBR44v1KVzdIrP{6g~zGSVp#KtmTvJs4^6Jm_9Shw{>bd@L7XE@w2nSt`hD+XqYcI+8R~Mi2lC2XO5ETgNI5o z9*AOKIi7b0%{B^?lBH+n*^Yhe8XPSiN|h>7k`osu+Tj7xu9T; zU|vhdNBa?HLB8cni&T3~y$uZwt!!-4Zf-taSB1Eet>gb7Oq4I+rrI;Yw_r=GrIEd{}uj*dlzR#;Ix6A(mkEG=)vQ27gNrWzHCx%-Q}9?Q^Y zY->R7+U4JRsGK{v$hos(o|hApbmAm*M2`FA>Z+FTz^2W?#lXbS!q+DA33ewaESzR> z!vv)T7pk1gU1HZ+uNI$1cVWtPFD;34jc{?luC8gR=^ei0T&>DzYSu;v7A8_y6SexN z{ld$0t10=Dlq^eu$^c|GC{)IgV`n6xx3NaA65;JO4WtEM-jvv+q@+O6r4`l|P1A@> zh|$jf%el6Z9W$w>?vV=!{WlL8yT2L;@8{5QM!y*(4oobtb8X+0#7@6^`|ol<_q*b@ zx|B8rT)TPD6fqEy~={kUAP4vRpx*H1??P`+e=A$%>7FFem`o%Y2u} z{}YY}Tvf96CpGO;01J+b782f%nc8OK*E74u&_iM$i%SOC9*R4SB0;-!^hz&ye*{?u zB3s^>2P^B)!sy-%0=;614Fz<64zh+{E_jfyXPkp0b(D99k+ z=;)}&^U8~#rFMpHUcjE3b-MJjC^dvNT1kt}Pp^hKd!a&756h&;D^|-Y)(6EgLzCmsbs3!^Nq0f#;!g)UXn>$CB3<&JNf47wOjRxU%xkD1*p+UiyRHyHP2;>a9Y^ z(CQS15b5cohwTx5?brv_HT|+Fw!iPcC|oudz+0{KW&O>~J0KJSI(A+A%KH=w^^3j7 z-f#TRsEN0?SG>>ZMN?7fnNOvHyiH) z%7*Wgc5>vH1Tzo08R1_-g{x$$qlwjQN#bz0UOwjj}TKr{>7s_ zuz0xc`a+BIlcwRFiD9nXj_J}Ek;3Fa@7*Z6Gv8uiGpjL1-oBu;^jP8FZ`)CF^=fjH zvudU$=^7_$v2ld=!Q+S1c0PwC(-l=sKmI|kcl^lS9HO2+@#L6c{8CA^vbLCxdcWN@ z#U3_cZXK~W@3BBoJ+K<`z5E42s`jI4&-&BPwK5xm6=W}naqzg?{GX z+`YAV70W+3n^G+f&1WfZAVj}K*I;t9oNJ@v$?2HSU<8?b6nf+y;IwJG=Ufvq&TP#v zVZDjwgH*WEO^Tiqf5UFWT>q<6U&EKME%qPaJG8kIt+R!h$v>A!2Aw5EFOjEb==+?t zlC^p|P_E-+!)pf}{ElK%K&@MGs*6+hQz-|@YK$>d3_43o0zB}Um-!@pBoqKBk!J8dvksgC|DsZU40-%UZAP}?oMccgHQQ&<)_0lhO6+qHu26#R0_kS~p&CP`#B`gZ z#HT7eu8|S@$~T_p^uxMkmu#lezVoub;jD{x4d6<=>WU$_ur}tr65v~(>@1@g`?*|6?=)4nf^4N7*Xc)W|LVc^0w>C2%eOrqrb898zy?2!0 ziAT_l)YhIBLrxS}^v8p0wxn~6jXz&>Rn&_=0|dv8&lQ>8?xvO9@%n&>#%I>6J17oh z7Bp^;KKLT!dd;*hz8tHq4ib2_n~;)`iuoMyRUM;xJVW9m8_}2cV`cYz3g#FSaxHl`kMnz4T`^fo!bg3o5_ox`3wxpQmC|kIuHrr(W|5 z*c@(1)VQ%AgfGEmSySt637K+fiuaM&Z32C(7RIH0P^3`6X5X~r-3rcqaQnIPi`jt_Nx*{aB^40-%u9pBJAxl}97mqEpw?7_rfq4SKEV z={1D}bXeh*zGug;qMS{}0>{_vICf93$$QGN#j2Wy47b_vX(bJ48vvF!lD~|d?%gcM zEnRWe^DJHOzFw$5$puVCnijekvd?CBX$%5Ew~-LjE$s`XJmV;s=x>={*)_io46WV? z;W|*d<8HoBs1m;Va@A5bva^FuKlNzj)Z>%a#fQ5+^u|lvEq|`2rsiM$I4IY?Zj}oo)Qy7L!BU=& z5|3T*0Wf*9x@M>0ouNkUHx!r)cLJYQ$0g&VRSgn2b7As{h}qbf?}P?I zHxt0{?JxTN*X8_xA1$8Lr6efiR*9?O#!7eo!}wwBMn!i5QnxRcEV*-kNMtti-s~M# zNJ;IUjkdH4yXC7U;j7Noj}))jl1R5#U}OH4uCK2@S*y?X>r8((MMfU4Xw>B9YjeM6 z1E;?+ym^*>er*eFD~w;}Y^b=Ja8x7336ns>bv4|WBV~o$I1J2H=L@g$e)fyw8Be$u zSSYLt%v1oMB{(Uu>}`f*nq+Rrqn*czK9c{(ny6W`IK=!fjrUNRa8Oi#yzZ7pOYAQ^ zZ8c_mhW5b-ezS5B;|sh8OXZQv8NRd4Pev{DLLG|C*|+Awr(#^bDQn~Bdiy3~}emYMI46Zs%{ zg|)<}Z2Ft*%GgyZ3!tb>LjB~S`wz(Qqk(K~iMt6fSJnBI$$+Ai?9Rf&E%6bwETV%XVyPo0=5n0x?W_H9Re2uQ*z#MUyh_dKZ7*Wphvy7dn9=27O??+=U!T z?r!!RU}rl}uGu@Wwd`w+H64nBg`t`jSiqumki+)^ZI&C9iOI`!{<{ONY@-L`xO#5Y zKIx2O(A{ZS>)Aqe9kp=|phiFB+o1hJM#Rc{ID^4q#T6a7x8=VpVDSf##+5plp)d7V zJD)Jv6=to+J|g#~G4&>c(RxKfdle%4S6Il!ekr8Q=9h8rKS;mY;}ePly8QJP=pBl5 zlSWwcr!Etq^KxEsKA?kvL3R$HDgQ#~M!$JeXWooxyc)%YxiU#2GtVbXxKUIW{(Vzl zAQ&d4ZDxHv(gC8t{-u7;F@mDnl!2t=svReOq|X^?`_&O@^C9%+7N9)0)_q#;k)u2omRLX;PMtP@DgP~6yrs|xN$~2YI7)?OcXe9G=Tk~ zII({F7w^Ttp6z)mggkNdIv=<`s17BJs8PE?q%LfD#T4(`%FY_ zQ3K#QyV7S(fr9b2pH~e19th}R9R}%XxMe&|wi*!|>Ugs2EItU>x{Tff{o`>l&7NqK zb()OMThgzP(2<+@!nDwm54Krn|BhQZQ^8CnbV3m_9OD8{p+_Wl#vd+E+%S^@Y`p3N zWH(^{#>c-57O75EC`$h};5JXyr@)VRiv zb0@95te@-GK=WS<#zdh)8uXqhSBc>0CsK>06eeIicZ7Y<^b2FT6H{I^Nbjlp;VND7 zLF=%d+3ayE-asV!X@ps0!lfT*eavE`_OvyzZoS@Dod1J?9S5JUrVfda$;geUsZ0~% zn*M;gRS@fGLG(|Uy$$S|Q2adsnc#0t}iK3gTy7KKJ;pYBKEwv~kAlza`V&E4z zp3qou54(S5;c8jY1A?lrQ~^#sXVOvp8=!=1UgfotFCoY)0I(U4PW`MB1}Ix-GI>fS zq|vr)`+KvVc(^CHA?&)Hwz}R|7f4V>(p2)JN}5Fw&3{-5M6s#=X}v}&Tj$<7U&)ld zz4N3w6xdgBX*c3!G+x6eGv4(Ct4(E$tcN96{jnV8wZ<#ooZ=E}kjT9kRfKM=;U@D; zq119|DeI|oB&EmIo#tF>U==<4_P3W(cvy}@i4?&01`%y6?j=#*;QbzuC^L8xyUf$u zwx3&#ocUx{n#ou`dUY<$1r_QJXu4!Ip;rK+K{{WMlK&2RwWic?RwtQ_QsR2k>4%H= zEv;&UkXPgRmHK<5ux06c7TWAXN^Vf}4biw8Kqe22So4R*RU1C1t@J&hZcEAK+}HC# zs7uhDh&jL<0xO?tc(yFk2&df&xv54R3Iy`N|0Kr0e@*{MBY-rm6W>u!B(c}SEKd|y z5sJ*moHMSdLPVS{NETi13Jq;Lrk7nA)Gt1aBC0&X6&c5NoA`+zdRZ~(A|WMMPxNUT4M zJ(6{uhtJJYZ>cjVuCH<`06H;wBYf0&r(Ql5zn69b#`O)~JG~KoplO)#*}0*SD1{p` z>DT~?tP@Rhh?w3f5~_8M(ntSZ3%6VyCGcB>H85~ST0Bi%68Ybq9{0fx0l6m?-I>LO z2WcG<%@XbZ+Y3Nmd#1Aa@t)_n4v-!lLU}iI4z`?= zk6F;tdE%c*$X?|ZiM~}w543Myj*X8^^gMLPO@O_>dn-{w+qDh z^Q5^ zt;6Tx{IS`hPTCbyO7H z-=E#3LqNJ45s)qcVd;<#LFtkfQAC;r1nHKRl9G^+uBBT*I;2bK&RzJ;bI$wje|yfH z-I=-fe&gOx==sZYnotxibrw|B6ZcERxL8YyCfnvF%?s}>s|N&pd6y!4UfqC<+FT}p zi2pSj@HR+|_nnzv@nTri#P0j>UjF@uwQ>#Dzr0vc{S1$2E@(q#16gPPt`Z*yz~p-Q zw8Pi@7CfpJmGV)8oi9RF?+Nrep7zpp89j@*?%NF_Q66&#Sr&2rEE-4g3cnh&obTbk zWt(34Qh7rx}n+IqPTY?1)guhq*Gt$r+u(?|l`vhs4vsE)e*sQQ`4m;NVvNLBL3 zlg!VDoPL`CmPwqGtN?ZnVlPZOJ`nvPX|+gq)-`o6>$Rb0vJQ5yqoD5HtL`f?dih@* zgC~ht&lOdrbrmY^HHlJ$?Xbgi9%$b=mz9-Wg=iRy8ZS^ld3bm{zt|+!SZ<~5OH3N5 zo=An#Kv((eFTb`pA8;65x;ZTh$C~#g8s`7Jd{aF{MAh(w+g4k8dHN*j4_B(0uU$58 zLr;%I4bv__>1_KXOgk++s9Rr~+-Zm=p{ozHm2AMfxaJ$fF|%kb zbfZnEWRBAaH%Q?$v}k-dgjEps*}3o7e;ldh+GwpX_hqzicv$^6;QX?#oYLaWn;%UA zRpiaT6A-_;Pe%{^u^&CUhh{nIzIU2*6mga$LPthqYa9(Ed>p8DeAZkx|01ay?wtH9 zMK`ZjNiD7IQre4D>kEX&vlUM&Mj*7HhdqWtlIC7iKssN+Ea5suM!RN=*7N)e zNv`o@_GDY@Inlb^wT2IWdO0=AV{D%`s8z~%#=utb)u#yc2uT9i5(x*2X`@d{B~_EN zqe%S(d631zvSKfij9&`N$Q-(T`PKFirtRH6l)~p@y8wx*alM+-u=ZynjtCj3KN_E0 z_q?F0O@Cg#m7L&j%zQss_vIYRX6V#~PJM%%*(XRpVcmvM(TbpY&|Dbj<@c?}YwM(s zFHZ*XKuDQteY^oGvz7A*59`(bRqbM7w+fj&V)h9)`?of~psn* zZ;8JH3W{iGA+lvCt`PJKf?KDm6YhnM_#oI88+O-rYDtLtH0x%zvkHe})k$+u(-W4_ z3LA+m^Rov(f7$|2&y#^i%>)N>bJ}&$F!2vN`an&fcl8s$xctrr)&jrundeb~W9_d_ zsra;>X)dIxeOdmn7&w^CNlp-RCeI0I@CP7*{$GP*? zuj_4hlYX7oQ)}j^QI~ci6s}3-feniQfx8Mk7#6)`TLcwB+qQMz>0fp)lmn$jcrI6v z?lO1#eI7kAiKwBjqN#68@V<=&(l|27T=k7!iufd&Ch^DUk3x%T<}^nf4#%LXru=`1 z48c1V@8ccK*h~C`{rk}f;vjwk%GuDih<#zezMS%lZUG;bzyVv>>4H}SVGk8Qi<;P_ z&DTi1Lz~`KZ0>KUWZN%Xep9Lbf5mWuqk3L<2CjKeUO1A)MWYivWth>`eUG{Bm0T(s zBL2OqU+8f&$tU|{Dfr7U=;6twlnZPCBOS_zGkyu-NPF{gmMn z9Flwdoszp2?NZ$()J3eTD|o45t?xo%B9dv7@`X0$<5Tq$&jMHqMSs^ndGAVDw@$U- z2!OAd=QRe0u7}`^?z<=?36HvT|J;@_iAVRDWQ?BLl}j(_qg{i+*7y@c5EN!@8{WEC zgoFP140xF;Q~YF3g4RsS`mejBFio_u*CTYw1x6~_0lr$3c;ZIn*-BN2rg?I@r@xr| zl}OcBiDVb+$3470X{<{V>T^#DY2vrDopE~CQ<2nts2_xNN#D1%%&#V07f->nw%ikabZxbVY*zJeRfGJ-|P{W`0FFF5v#b`<{FxdD6L; zmOzclJyTZoDeiB>y*qAJ2sZWi=n*lUIevmF7fDf-V-d~YzF5J?yNY;J(*5xF!{-b&vSf zcDv@a^pKk8LqRn?7LyCSe&H)EJ^hpN{0!`d!MZo!Wy4Qy^G14(>wmsJjI}$d(uDz2 zZhmuu#xmDzOATetLMlwnW;W+Pvt$C%19et^vR$6PxQ^V8XZnW=z@l|;Mwrar^Z=Px<;bR7%AquDRFB#5DL zziBaq;TXI07is4EWnfSiCw^$35u-;;5RZ0BtIma-M1OyQD){Dp;r2wlu|#6|u+ez2 zWT#{)(OphOf|eOKBM_*595$p!5;vMNQtbPdGJ73+nUvah$PTOTx0qmzkkki-*9<`s zd3iVl4+uCd2_u+oSaEUxxsNZZHcdQt`6zi9>oZ!eS2R*y&@A!QeTFYP(dYKhd)Kec zs5azz=C=}CxBC+xK4@q$eHgB+FYWJG;yclgE;H{fsX}wY$>jZV|B;|bm&GjMxKKHL zVyH}!`ApZIbXSyoY)|g4K!7hYkGW9X$j-S>>XK<*{i-CuMAj+uO7xy55fx=Wl&A8G zKYR3Y6=MuAI^+Z-q@@1TgOUu@N=dq&1B!a6@AF~rN|4_;DrLMNcxw1o2@TS_v`9WR zi!3hH!&edFdnw4m26GCnZRY@v)oW)NCBfGfSek2xk)!z=bZ_s%ju=sKjLf;EUonf) zH~?sOlm&g81%<6ltKnx7q1-;`me_Fn(o9Y**tC zhPmW`i+{ZRPEx-$2#2MBSm@SEyaeJn80o~{<{Xy&9N}{5r!#mG88ftybJTjrz)ls- z*B93&MK?+M<;x>h9rg;PTD9BaZkFSEo8kU2!*xY-03k@qR1#oye_g(JmsF``V#5*! zkQw;#+ajX~x^OznY2YgSw)Npg!=Fe_i;l+KDUuCxiGvu*86#T_f4}zj57Ls8m54(P zP|5yD^-IW(pLzI1_;PQKlw7y;Ijj9-P%jza2eS8WpK~?F&O8?+MQ9pZ-b?Ltum}*a z&2;JN#xu75)sNhf)Y4HWH|Q&!wXz~HH*|TiT6}ut&0u3QQ<}U?j0^qqvy#4<6aD=t zQ?4rZtu{IF>@5?&6_)-YqOdQgZ!IiMHT4*SK!~~+jU_x~cuSs_BTwJ!c=D)*7PE#O z#oSu%mcx$4W`(1C;lLGGaupfF*+G!?NqzaFhe#DnARsuUQ`Dx6!!@?(mkTsBWuqd6I>Y36il_Ii;1csSOcdDsuJSqC=$Tr2N!pQ=Zks=Ys(}9ktBB2)P{U zD#SG!Q&KL&+(7Qg$m)SbS{>~_s`H{_s}bpMlUv!*2{MzwycP)XjRHm3%;dlS0@QW! z?d_?E0eV{6g}J%8$1WRnUqc_K^B733=-?SfT2DTQG1(@3wev5eKl%W_9(#4SdOEYd z%RM)h7>}9?yQlxXKH47okt~8bXzfE@c>U$Gt05Z-Y&2OmcEoFl4i!gmr6`8B0cMkB z`>6_lERye-T=AHi3f?RcVLKa?|C-hIVg6c2lb-<78$ci$DHAS#`c(= zUed$ar&7|E5iXAngB;!5+|*rOTqHB*lf7Bkaq!dQxpU=KB~y7m;0o)>E%mLVJ54{5+?t189gu?292YTsl?SlHtU*SZVF&gEU4ZhR<%Fx`)M94@tzz9Y?f zlU{Y9`%5Vwg5G@=r!& zseca4YlLvkUFsn823*@^9>sIs@`*c3YEr$o+!pvZs*g@EW?z|~VA?l4{`%80AAvwv z*`f%CyZmdqQ3F!O>OmBGvMgJYD1zuDL7B9#!&=4^fxXS&+Nf_pZnY1kF8~aBRr012 z018hTI05f_^d;kaQ?X$jG8vwFAHQ`M4l)@*y!Jc^p8C`sE2jZV68X!XV6ABHfo8L>Pg!1^=-zi7a@x~p8y{X4BZ zR=NitJc^6qR>2%*oI$_M&Ly=kL8^<9IN#5`_;YX`f7t7mxu_+BcE|d%f1low^IH>6 z1bj_Ry<%lb;nJMR(8ha3$SJ!gHVgw$=R0hP7AK^ZC%3gh@K#~^D8_S19h%R>FFALO z_1U^Bf|1%0g)mO@Cn=Tmt4+)bS7AEy{7ia}EutFlb2t91ZC*L) z!1uU0U$Ea)sm#UdS9&T_9Ym6J3qws$ADwBZZrCMWjgGJVk|fd*=I1|p7;$kQ1x2Xu z=Kw7!X5J2Zld5lSCC2bhGd0pxboTYhiDJHfc3EH-2#Bwu?lMj`&}9_y6V-l$rJzV% zn30zm_hlHm(IfK8GagUyp#n5X0P$=k*&K@(^C&5uR!lQv_qJhBZ;2TSANY5EXT~Z73CyvsU-I5{ z67I+0xmP6AybN{t4V~@F?F{@n%91$nX?O^RP#>RhqzT*f6!3i_Jms$v_^SfP*%z}> zP8tI%p3)DC{f{IrQxG)C`|Y#QOBA*Zr|5{Ydmllw^}=tfFN%;kdtu5GpI{nJ+}YueTjc6TG{VAqgi$<-i2LGL={gv zu`hjwG;B8CaxVH-{fZ7tm!5_C1RI{86HjK&7?#Dx)2jnzVc)rXhytmFsO~py0hUZhX2P2I5D0EnfFXvhZ zS+FKlH)ERDmgA4T4m`tm80#!`rQ&XelfoMOkKYrhW6($`_njMJJ{WNj!4qe_rqLCk z{4Op#sE?xdBS@M>4=-hxGXzwDH}hF#Z|>sqQiiadP)%OCL}?Rpbjtu7R`oI5`l-qv zSi9*Bt*~o3{0+7qVa@}(hpe*%g7lx`hlYk~bCSvR+N#RvsE@$mlYqi$PQp`d4Q_j3 za(Y(WH@`uJ`#?GAhIw>+}e9^hXoQH0K9Nx}4ozgUQl7{Gljk znZ%qP0ztGpJi}U3=0!{01f294`(g6T8mJ$+w9GmW5&uq97S91Jcmsc3>KE%~PFuu) zMvR(m^ZA_Y2cWvvU0+eVeDe&EDdxj)43I!=#4`6XJCCiU;YHTEID5FfAXD5mP9ZIZ z!dCPMs>Oo^9vf=T_&xYrIUO(73|~l{FQf!j%;tsu&hM29XF&Ri&j) z;d{{O$A@s|Wxqt!wJu9{y(Dg+42KCsy_Co;igmh*I5;?1-1mx~g2xDuQ)#Hg62iBs z6ST$>{m5i)9v=TK!QYd-nZ&ZgQP*U;P?t(*$7l>7BU4}otFZ4Y&R?}gG^## zP2@uOfw%uDx#>^hYR+8eRK*l`eIUiXvu2E4jkeBu2VDC;h@|A}ot>qMp?mtU*xgahESBAr?ws5iHOz_7L-%@szBBq`?#z3s+f)t7`)zocZo#w8_dcu$j>8j!J4& z-qn@w0lma0kgxmjgP}WgFb^h$(+>nq4Zw3>z0+AFYQZ}l8je$w#%IW33B)K{QshW{ z%&?thor(`fX)y~|(aDUmWRJqKqR+qj*-t3NN5F9Pj)l}crOnQ1P<;L|}%F4?3dfpEUDk<7ZAu{G{%a#f`oCqbU znhBWw>5bdotbl!bs6pwATqKf_m6g>JiwW^?D=OiH1t)Bnx0JL)k;1GDo50c5`Q<~s zNdbnd4M4U_J4gAk`yyvFoAzse@?9$Nh9B5Nb(Wj>oMd=LQwHtt-n;U*7~&cp{T7kw z3;4ly+u#56yfwaX6~r3{NQ#Q~<|Bs*Qfny2`uUlbai>-77V2DLlSBxlJ(_oYJzryY zir7dH?Q;Eh3@@zT9-|y*C`UDkeU(JN;bVo@Zc+A2Fz06 ztC7)i6_LZU$b4KjE3!h!Uz(invN1B34BNW7Pl^y&JIE+A|Le`q5OF$lk*LWX4X%eI zbqFFZwYM&|>%Gyz**B9#(&{+jkRZx*PVF)h`>udgD>1cjEC%}-R;Cg06E*D6cXuV# z|J-R77Z*Lz=QAKbeC^nCZm@Cn16zPlFY)?6omJsGs?+hI)Xa*C!d^owdupnFHvhkw z>oqjLXLK;+mWlW!BqFM+bjpkxA7&Ysuyf+29Ibm-y3mHmG0Ztrq=n=1XwdaI^p>jZ zijH&0c4DZT;$mG7YZNiE+MV02e#qu~w&k_75=uyWH~qmBUm7PY9pG2RW*VEN4(zr^ zV7D(I0|NeIUxdfR#5mbFzVRP8=f6ZfmzR(YBPziIG%yM1!z8K8#?5-$6=*|0qkG`S z#>Q11`>&jrTBy_1vB0H)Pq}y`#dn5^RR6V;cAew!N}<0j>FHeUby}zmsrSvLcGKo9 z$+_h4Qu5px3uo9cqisS`aGbg8UnMslxOT*V-<}j@-F}WXCsX#Qd@g2YmNX(iMRpux zt%_+*`5Y}0p&m6%&cgM_mN869N?7>pFhnl!<6ZbV@Ol!rUoRXM$30T0k>O8myY4-} zUfI0#r&5y`BL?2XDCf8XFP({k?Qm$vvyfupsL$q-T_~`V6TX6xZvo#34pN?5VMci0 zwFD;Hc5z*KAPxGhKWt-2{ApF8IXXpMt0NKrwoA@FQntb-_mUY{Kdl`1G2UaUP2Gpi z-bK+gNxX%#5H~QQRLdAAcr-&fm=`ks4fOsKI*b659B_RM+N z{5(sN(wD_wVXsxUI;BDU-PoYc3WP2f4sYP>oLoz}o8s^IO=X`q&Mt+x823}2aBjR) zDaI@lG?&L8U?zcCg;u0F;63>|YaNsDb8ocZ{1fqP9eVJQ%=G|$+~<25xoA;HUJX!R zQE?c!w6w&R{8i;K-sy6TLK&^vfcNJ_)`w@EGf*6YFP)RPul^8BQ}J02i}DNW>-*|{ zCrjovji{>=ZMoSg75CVqqTn?NGvavx#mEb9uVeYiPi#TZ^MZM6hHWFwT(p4lHscxN z%a}HM=+W>941t3ucxz5E&U4Jzz}o@~gFlCr<43pOzV5?m z>X&6Bh-k>g!1p3kzWQamb@|GB^@->6m!Q~CMeD`ix;q$H?;Hs1RK;Zr@;Frr zbj8mn_2LI?Dy{n-5X;4>a}->BEI@v1xz6iO7APGS;`2VWZN#o-vG$m&Dppa%wZK5r zi3=m@E=1iuJUZO|FrkXtzcJ9w1iH+Ds>;<^t>9C_bmI(km9K{&#eFCzPp%%Wm{%jC;x- zKZ=e2d(n`E~BYH|7k^w z8qu~9a(Wx}W%w({GWZ?+r{nYu$ZNSL6o3_WVYlmr3mr9XXWg#;cTCM~`#{#QCim@N zE&t_xF|!O^Iy536F;tV}Gx8nruY;~@2ilC@#}CdWD|xtWf)rQ+^|?Zx^EP6j(dgNx z58RvuMs?0Fi%Ix>viR~G+80p61Qvw|d#o#Tbk;;{8iXf`GYCJsj|s-Gq>z&oDjHBe z@O-#5duY>w3XAR$-~jdfk-hDT$#5?x9qTI{%PaKx%kfN{KK|XLnZ)_bkzU3y5ioKR z3ph%ljL&XCX&&Si^I(C${CfbAH<`5~;`wj%9bohu_zS25TY)fa27ZnAU-#tHUh~Zd z+kB(Fb+nKYMvknqzf2VhS-Cu*B_3ya| zHW6tWE^4(P<*1IkLP6pyRsmwSv62T&BalY;jh4E4`+(L8{qv=EM>LU2-{S-!v9=5m+@@}yE=oq<^TVXRg!|Njy!eSLdHQ~4+GOxd4pW3GVKNdi4fU)!10e|7$ zZsj+bNMGsCa=8dFK-ARKU_`#8F>3V|`7dPn-CkINlz*mA76S?F_6`n#g$}mrS~!o7 zFO=TfhtW5AWcvEH4Q%ivTkvNO_f>V0penGa)>X}_4~k$dc3EZ z0y4n`y_(%0hy=dEXM#PE^EUp#+hjlSglovkf0ItEf9j5NDqwsI{>=>Y{oZjMvKW&J zu%f4@AJ|{0mnkf9oLk#Y(k-4t+b>*Qgx)|o35f^rg(06=Tw!U@9t8%fdV({rqIDwW zmcX#)k+FJ5(1!$EKLDLWU31N^8He((@Z(RQ-q@)BCl81l%_%T(7s^>^2 z(&vR5@GzV3@GXX#5T31zCsT3KQ>smQi=Jv8=lL33P(7F9MVSdTKa%e{5{Q%MO;Iit z4ZP&NBlVsX8?(9@^8A^f-FR@04HUa=EMG(I)!UB1&U#_u-`TBB(p?J?Jn1g#_))o_ z7#AJ@#t&!ibSyRAl&;(5d;jsdIWOFRcUP-B<=Xi2W4-1Msr4f2qPUq_{x}hGxZ~Oe zJ+cW5y+Jlu)?V~}pMUmKWZ=Nk znZUZ1lQ&v^^nP%(ec#KDZOnD4<@yj*dOIlREI~(IUyn(Q<*2tStyU$?b)2|iJX!hk ze$>SW++b$ZGpMEIjK$%+mbYK_d>UpaQtTsroQ~Lq)IHa!3~V8^afxtd=pPgI*nDAi zx_Zr7OrI*|3XzbIh|?^JSLd)y=K%M+4FZ7v*FR^gtg$Z+7V*^{&(F_~{r(+zap6G~ zs52F2cMGN zuQK=hCUJzRT?*8LZ7lLFsX)C#!-8F|O0_h(x7HH2;Rqp4=1 z4-iG9axg%xkDULtq0SD`7>PZE+wh0F4Ek&$wxpy9J}+Pg-5+fsj}ZCO&U@1>3|USz zoy6k74pdsX#i@=73_ieX8IvJ`HNL3H_wzxvwmx%zm|lo$I`h2cPCWt)W)}a&N038` z7Xr1aqI&h}6*#WH=jS6lrOejEHwut{yVoy!24ujj0_3u#*kVL%VLJ(liiU!uzI9OX zF;yl&dPMrxnW`$4h^`JcRJS57ooZAU0*3zs&j*KQO@#PKiydlhM_&UpDgso5-MH`! zrD2?*XALe3&0t02`K!BIfkGNE;#3R3g63T^rdsuv#aAc_!*B*6h!6}yU<)++INx@f z`GL-^j?_`JWVjO0l|+uW^PbFH)|(M?oJqC2!G1CUK|vbvKgpvS9ri|_baKm|@+UoK zPCHOgR>lTrW1Lz6aco~#Wp1GZJF{H7-Jl8YXRs_JAR@AU`<58Ys-S#FSNsBap=ME6 zaqy2_6PcmbyV_6K+~4n|x)$o9!RzDWALi@kU2lSP{d#xu;mouoK|{$Gvgj8^y>rNMAvv z2?h;BaRgdT5vR;;Hp<(v)uBnC&PJ?N_!que4mCnEyq$G<7SXsZ-BvIfl5-@Ib1VZHH9yUK?zKhd7CkONUEdLuqQA!EyS9E zI!QsIjy_AD8a0z6L<*n+hQWl3M%oB^{TOD(0lx3ow@aPD zgPUJzwz#(AqhZp-{P&1W47i2f#v(@OKRIVw$DX#|qEQ>}Yzg1B3+()%X51F(fvJ>C z#$|d*2h1?62U2Pde_MvOPgfP#KM_+Y{hpo_E?2*e3%SjI&~G7}voHSE89OVZkNVk5 z6X5{K`LWLpnn6YZm2r>yP#SmGndD*}FK>}7v5b-)d%v@?z(TGcB?agw+&$Xx1*~4= z%+4h+cgj{BBJb}i7CqOaW#qdt*h#rvg9T8MoYUID&BkiZDB#vJ9Lj0Np9RNspTZ7K zeb7xG)bd5~&rdXlwomVEY?$wqU(hECp9nI2}V$w$9ml-odW{bsN#B>Vdf8P#R zyjg5IuV`*=u83?ZcDoX&X}d{t`1>Q%{>c0OJXuk8vrW%!JrCv9p{gX99XR|CJE`bqP>|3zUQa&BOd0 zKY7BR1QqTH{&DmElCr+-TWrYnY*d@Z03-2l3)3^4mO8O7jCc&={r&y&&z?OqSNeVY zNbue*{;vH@3kRC?F|*zQCqk^;D0vgqwD=l;1|{|WQ66m*ak7@KKj}aB#W|LberZ(0 zfTtG@8EdyxF<|`7iK&mLt%l-fDX>h=J(N6>?KoGdG)JsctTa%IFX5P65;nL&>lYD4 z9F{tyUdnw6vWmUq@t5jkEf_pB!T5y9yD<3HGxXhL zv{(bIg&V)~wtv%bGr7$3f1xAe!w;dzc^Du|Zg6aOa7>@w2GiGz{CWN9^g4Ioisfl( zn^FArvr$Qp(_dl%uoDvh3Bf~i_VkgNM~)B_U0@l%3|uw+NN7~*w2GYjMUn;+O+toz z-_T)5vX9@5w>j(aE6Abnh94F~L15!bFyy)TI|mXvl=b9tlVW+0E))++glAIH`7I*A z6aWI1aXIz1uZG@nb;F9cW4+n#ij|iowQ@UQ%nW3ncjRBz$ae6K(=t>)8YmX8m3E}! zavZBBUKecCmVZ9qQU-J-x7lli3}7Wep!Z@ztPM{v>k9zm z=#+w+pH81;X!lMhT#lm#FUaNO-df0iGpMswA4$zh8d;)k>*iQbArXL3!p6~``&(VYxmii+A*o@+xJ{>lPLiv!WcLyz}Ba&ka3%8o^17i1w+_Eh^ zQOCVMp3LHKL11-64~UcUCJCSCcsOM)mX{(5uT@pNsl3+#mMh6W+wfp)l4oVCtJf&k z%4I`LY;)YxM8}WM#0I3%0bRyM>76X%c8iRloJIC+UjVAlkCX8780KqJhg2cN4*cg| zAO%z+y{J25N#dfZgzKE=PQDx*9Q^H>D2}{v5o@P%`7jQpb^1QzwJ0{cb}8588zH;ce8t6`TAEnBb?!6-5|gWpIAO%VvRtGq2lEv)N^1I z4(GA7f7=s5{v=lB?#i#w;YSP#(}(P<@jbySkC~dqZgUDgUe zSQ#M^RE?*MG%di&YHg1Fd9p7@BFINL-Gm_UjFl!Ra8uuq0-Q6@EAJ`|Y9p{*^#ROU z{^mvguzDVKs_+;yi3)GuRTlMEp2_UZmP@B9+3(NV14aB_I!AvQUeNy1eV;pED}Czp z#DYx_x!Z&-a{@Y?olF^;e}#WvD3i?~p zNwJ3&-Kch9AwN}_*we!!;}>~?7-_CdbP_EN6NMR+d}h9^Ods)}aOVx&n*AGLoDW6Y zn(4yzs^MJ{hbc$%5hRW;MYpEQjgd|M=o`jw#TP^R_iDKCIA>bT@zwgiK${Ulw^aTG zqDRUNCxhI`XQHBY0uk4coH2h)r4)2_VS|(Ix~hOh>%X>=VHqhY)X($XnR|>)Js(3| zG&Q~0KO#CtQbfG^eyjvXIs`!YN^6;ei@d0!?Pe!ZL}NDrjZ;9TX(b}{4S(xzqR<;| z(IKA45qP_00Uy!dvKM#-<>XR7RmTUp`CVJ8gdsC`ILVGXlLO2LbuHrFx!Si zE>lxe&v{;NrXb~Yk|++0!e3fE8U+m0yL&}Nk0{cGAB7XoI^FZNV9vZX*c>+JB=cwk zjd-s2M!rgV6`6}{QMRJ{zQllw=pAv~R3wvjH4eq7;gVTiIBKJ;1|Ge=LE`=PdF?ypIbwl0VEP>xUU`hHP+ zvH%EY8AsajL@nv3)s9mo<)tsmy(PJ#9BYiHyCx(oW28kI8N?h860F)KPR&cz{6|lf z{#(CVV1C_q&GEG&a?>59et$La*AoU16GGP#*%Pc=z9Ycl7yCT@!?f8Nly*0KD93Wx zk;=FzcT=^XjDAh6(GYtPH#&ZV_NgaO&f4WY8xV#x4;~4Jd z#w+X3DUe|zd@%v;0S|&5`xWg9T;pQ=Y>(T1`2#E2whHTEk?PM+_AQjtb26A3Wdyq{ zsWR_D#GBuNHRJ6pe_@Ct`XMh1wSz zl0O2o(4_ji?ZXQhzXaENOy54)6FP*|kWnd$qhfrwC6`I2?uv({)AnQWjNJm}|0%!# zH<=R6X)NFf^oyi7s5%(mOUa6%tq%SqBo~t^Ernkm_>-T;k=J2%E+Wo- zhsD%mkq~ulIz!*E9wRAzzR4GGEt2&5I(${yO@uLR#$5F0Vc95*`1=duIP?=31P_Rn zzWEC`G@N<ng&4VDp6d{E$fSOIGM|7Nga2u z%H|~7A_Fx;!D!eJT z6aTe%TFX9%tuRjdSynZlxmW(+9*xo?GU7~op#6@+tN&>R z@~RV`FG^Orgm19R{K>D&PnAzu)3(J)pXW;uTu6Q8%6gI@;B?{8-6 z!4Fd7X1y~~tT$WsCK~z4ibx$jlJ4F~tdaG=>D)BuiCq$XqQE^Uk9+X4*<-)HdajJ{ zdfZgMn(z5td;w~t;S5`o4gdzd-`jo4BrLh^ z;q-IT+};XvuU00g`AF%=%wK6uY(;z|@IPNwF~1cUqbJ30QZIe0JJR`okH*6>0Qghu z(+^TiVO^iUWqyk4q&zM)N0;zr{E~L-z@*Ilw(I?|XM`9!V-oJWjh!P78+*6l)Evq2 zONxYi)g9}J6npD+c2*JvWLz(YGX3F?nZo*B zM{x=KQr8jDKwm$tB?5K3uoNxPc5-ovesr8`!3=xB*xFDFjKZVG7clW zhu(>e=dUY8;M#Tctz^CvvKTI%&-4lC13i*LkjTC2q12RY#(vhq^XWm94%1t?lDjBP zeL_S$K~9MSVC6RnAA2Pb(ZSKx$!?b ziC*fV*CaY(-`w+Hz?dg7F}|!rK9exMG-{RT@;~jR?8Ci1Uqb~M6l1dQr*8V-rzqA` z<%1xr^QrepjfefuvmkaIcB>xu)a@_rL(XT z?Fdg{kFWvY#Gfl+{R|Oy)NyK-XKxd?<46)fSr>h(_X$}`XP@*Ea7X6|nr3q>p6CCZJLKB`HKguDyc=7CV z;}Mk4i{sBU_o{~pA)i~axp9$FPe1rav|?{scKr^0mi4L3)&G)^`s~>H>le#d{>3`G$Ot(A8MKTit_Zi%HzFY>0`FDs=ize z=S-1!jFJzXF0FKCyIMR9bYu6_Mae~wD~j5dRDzern}5y@k2-iun3uDD=b zbH(Y)uQxdk)g+dnh5*Hk7*z77F0;!z^9X12R>zlBV zx!9T0Ts>^Z3tM~AQTUMar)00yde14DrBtM6f7P0QsP7R>keSJ|M|I7bG zGB>|t?{H5x>q?_XE0TJ-Utf4}rrfmiSb*~c6R>#4q+RL?x4Co7?M|q|`tSXZw5~;P z|8RA6MUwrgwoNo}lh1ol)9Jz4xUYR`oV)cAKH&1%im}*tsU`tBYVv@u4J*q_qNV%Q z*wrh~t-%}h+?_rl@Q5gO5lrkd|5+4PQ%KmKtw(=>$BgORM1D8dhjm?Bzx z5G>Bh^hP_=y7(!9s=IbAOyB7;avoChA&903K@%x!UDPQ=5lmAABv8-N_JAZdm*r~P zAwJi}&E$Z9tu#9hZQg&}J^Q6DihMHspHmsduOUDx4Nh1v z=C##g86@2QhAT68ZFGzV+?`75q<=Sbz%qOJSF`tQs?+;D7~AF`0DK?hIgtB7on_Q$ z!4q7rr^E)p&U~#CsX2PbkWYF&N(jWSEcuf#=ljNRRdw~@GQB>bNgB77mzURSyIPAx zqW`N_KZB8=SjVe&_C!#rLU9N7&Mteluqd483ur5JIktyAnKjAbmf`U4gnQ%y{GNU}1sC z_A5$vk3IRL>;O#d-Ew{>B9oO>di3NJ_7jW&gO{YX4*|tcOs-&A5f?ZXR&6Sw8u;=+ zpQ?)Trj;OkUegxOW3O-pD?;Gs4q>**uuRr~&0PcN8A%xcCPC*G(5Q+C)VcSeo-nk! zdL?w_iB(~+Nx6`fB)k9s#Y7la7q#{<_y_V+} zUq4I_f~{eBmYD0(y9wbG?uc>T*5B*hsH|=J^wiYZXCMab2CNF}WbRR?%?DtJh6U0y zZpM`s<|)%-J0_ldNj0k*oyfHCx43`;-->TZr~InS!tD+5iS@1C9gCOLl=f`2hftcO zHf^OlQT#A#_0|Dl4Qn6*PI17ddWt_PsnEeeV`4lauLg=^H&PsO4T);{A-UtVRBZcl zJ=r$ePB3ltn{w<0(SrvE;KYA^uLYWv27;uGjo^!^OxG`|1{_n|5czV<1Q&MY>@qVc zX(#(S7@&<5ZJ$wwAZE7|xUI6g?s#8TD}cFga(-c#078M=+6eM+d zfXH3P%gallfAl>H=lrY3ym5PxeiRNPDt=j8Pym>cg->%t{ImVQavH9w_C^)>ocj6wB~?e@TAz^he>>gCvy$R=g#Uus*}TMq!BHV{#^XoG;G znknLnH!~gmEN05L@AATw_Jkfj+pY~#5D6`(WGfM9^DLXbE!~_|)q0N$P-!U9T6|#n zImI1fJD@D+BI~hVveTMxZngYKzLq~$J>rFx$($F}%Qm`{ZNlUy1<0 zQ9)SQqy}et^Nr0@pTmmmr*iEV^c($Ur>9txBUH62elHi))Olb*YD_GeSh2jqmV4Ja@tA!Q1IR9fLVf*2 zdQ<32m8U8uQ(mEf#N1c=0doQ`n;i=S%cOVabxQmT+Vq_yi$LyDfj$wjLTv>WM)A8A z8m9icAGNNvbQLKtOB|qyw$5|sr2_u7Z3a#<+u*gE`a?qBDRH_*&-Z4mXS8~jD!w>= zxmxotA2xMCl0=z1WxUkV0Ky8Dpn;xcFAV$yqn0?Q%=2Hd1w6Yw&zi77IplBv`bA0* zNNL4q%UwvSBT|w)z3i)Ltlp3G$2z+o{ukE(DF36V$@mg#0zhq4>vO8cpkjS3?dt`B zdXCjYl^AF;(mCp!TA99DpC;38&QTKp>Z7RuLACmN&Y6m&uk{|LB30{Q)_R$~QvGDx z%~@&!z~pGEKv3zJ%x6&VxyOqp!=ROH`dZJeMoq@Ak(vN7HLBpM z4Ph$bxO$km$@q0r695`R^*+bR#^|Hk%=CM#C+Vh^HuW-clkq1>GyUPrHk!=yY=k*c oZ`*3e8_A&4skS#6e~R?~0ch5VgHJeL_5c6?07*qoM6N<$f);IW$^ZZW 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 e4614a335b2f5dfe17baa9db6d543eb351ae0369..e65633b58dd621cd2ae9900f3febf1c103fc3197 100644 GIT binary patch literal 35446 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 Date: Wed, 4 Mar 2026 13:07:17 +0800 Subject: [PATCH 13/16] =?UTF-8?q?=E5=88=A0=E9=99=A4=20AboutAppBox.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ContextMenuManager/Controls/AboutAppBox.cs | 212 --------------------- 1 file changed, 212 deletions(-) delete mode 100644 ContextMenuManager/Controls/AboutAppBox.cs 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 From 3c8af5aa4ff79d82e610367966e0e97e0bd332a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Wed, 4 Mar 2026 13:13:02 +0800 Subject: [PATCH 14/16] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20MainForm.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ContextMenuManager/MainForm.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ContextMenuManager/MainForm.cs b/ContextMenuManager/MainForm.cs index f4af2a04..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; @@ -173,7 +173,7 @@ public MainForm() aboutActions[1] = () => { languagesBox.LoadLanguages(); languagesBox.Visible = true; }; aboutActions[2] = () => { backupListBox.LoadItems(); backupListBox.Visible = true; }; aboutActions[3] = () => { dictionariesBox.LoadText(); dictionariesBox.Visible = true; }; - aboutActions[4] = () => { aboutMeBox.LoadAboutInfo(); aboutMeBox.Visible = true; }; + aboutActions[4] = () => { aboutMeBox.LoadIni(AppString.Other.AboutApp); aboutMeBox.Visible = true; }; aboutActions[5] = () => donateBox.Visible = true; HoveredToShowItemPath(); From 659de5872ce0fabc1fd315d74f2570f51ea64420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Wed, 4 Mar 2026 13:26:15 +0800 Subject: [PATCH 15/16] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BE=A7=E8=BE=B9?= =?UTF-8?q?=E6=A0=8F=E5=A4=96=E8=A7=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BluePointLilac.Controls/DarkModeHelper.cs | 30 ++++----- .../BluePointLilac.Controls/MySideBar.cs | 63 +++++++++++++++---- 2 files changed, 65 insertions(+), 28 deletions(-) diff --git a/ContextMenuManager/BluePointLilac.Controls/DarkModeHelper.cs b/ContextMenuManager/BluePointLilac.Controls/DarkModeHelper.cs index 2540c3f1..a6924602 100644 --- a/ContextMenuManager/BluePointLilac.Controls/DarkModeHelper.cs +++ b/ContextMenuManager/BluePointLilac.Controls/DarkModeHelper.cs @@ -143,12 +143,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 +165,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/MySideBar.cs b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs index d09d1ac6..602caa18 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs @@ -1,4 +1,4 @@ -using BluePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.ComponentModel; using System.Drawing; @@ -19,9 +19,9 @@ public sealed class MySideBar : Panel 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); @@ -30,13 +30,16 @@ public sealed class MySideBar : Panel 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.Black; + 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; @@ -115,6 +118,7 @@ private void InitializeColors() { BackColor = DarkModeHelper.SideBarBackground; ForeColor = DarkModeHelper.FormFore; HoveredBackColor = DarkModeHelper.SideBarHovered; SeparatorColor = DarkModeHelper.SideBarSeparator; + RightBorderColor = DarkModeHelper.SideBarSeparator; BackgroundGradientColor1 = DarkModeHelper.ToolBarGradientTop; BackgroundGradientColor2 = DarkModeHelper.ToolBarGradientMiddle; BackgroundGradientColor3 = DarkModeHelper.ToolBarGradientBottom; @@ -230,7 +234,9 @@ private void DrawTextItemsAndSeparators(Graphics g) private void DrawSeparator(Graphics g, Pen pen, int index) { float y = TopSpace + (index + 0.5f) * ItemHeight; - g.DrawLine(pen, HorizontalSpace, y, Width - HorizontalSpace, y); + int margin = ItemMargin + 8.DpiZoom(); + using var brush = new SolidBrush(SeparatorColor); + g.FillRectangle(brush, margin, y - 1, Width - 2 * margin, 2); } protected override void OnPaint(PaintEventArgs e) @@ -238,19 +244,36 @@ protected override void OnPaint(PaintEventArgs e) base.OnPaint(e); 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; + void DrawItem(int idx, Color back, Color fore, float y) { if (idx < 0 || idx >= ItemNames.Length || string.IsNullOrEmpty(ItemNames[idx])) return; - var r = new RectangleF(0, y, Width, ItemHeight); + 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) { - using var b = new LinearGradientBrush(r, Color.Empty, Color.Empty, 0f) { InterpolationColors = new ColorBlend { Colors = new[] { SelectedGradientColor1, SelectedGradientColor2, SelectedGradientColor3 }, Positions = new[] { 0f, 0.5f, 1f } } }; - e.Graphics.FillRectangle(b, r); + 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); } - else { using var b = new SolidBrush(back); e.Graphics.FillRectangle(b, r); } - e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; using var fb = new SolidBrush(fore == Color.Empty ? ForeColor : fore); - e.Graphics.DrawString(ItemNames[idx], Font, fb, HorizontalSpace, y + vSpace); + 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); } if (hoverIndex >= 0 && hoverIndex != selectIndex) @@ -259,7 +282,21 @@ void DrawItem(int idx, Color back, Color fore, float y) DrawItem(hoverIndex, HoveredBackColor, HoveredForeColor, hoverY); } if (selectIndex >= 0) DrawItem(selectIndex, Color.Transparent, SelectedForeColor, curSelTop); - using (var p = new Pen(SeparatorColor)) e.Graphics.DrawLine(p, Width - 1, 0, Width - 1, Height); + using var p = new Pen(RightBorderColor); + e.Graphics.DrawLine(p, Width - 1, 0, Width - 1, Height); + } + + private GraphicsPath GetRoundedRectPath(RectangleF rect, int radius) + { + 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; } protected override void OnMouseMove(MouseEventArgs e) From acc2a881ac3e87b118461f039b7a7405018976ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=9C=E8=8A=B8=E5=BC=9F=E5=BC=9F?= Date: Wed, 4 Mar 2026 19:52:38 +0800 Subject: [PATCH 16/16] =?UTF-8?q?=E5=B7=A5=E5=85=B7=E6=A0=8F=E3=80=81?= =?UTF-8?q?=E4=BE=A7=E8=BE=B9=E6=A0=8F=E3=80=81=E7=8A=B6=E6=80=81=E6=A0=8F?= =?UTF-8?q?=E4=BD=BF=E7=94=A8DWM=E8=83=8C=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BluePointLilac.Controls/DarkModeHelper.cs | 75 +++++++++++++++++++ .../BluePointLilac.Controls/MyMainForm.cs | 30 +++++++- .../BluePointLilac.Controls/MySideBar.cs | 66 ++++++++++++++-- .../BluePointLilac.Controls/MyStatusBar.cs | 62 +++++++++++---- .../BluePointLilac.Controls/MyToolBar.cs | 56 ++++++++------ 5 files changed, 247 insertions(+), 42 deletions(-) diff --git a/ContextMenuManager/BluePointLilac.Controls/DarkModeHelper.cs b/ContextMenuManager/BluePointLilac.Controls/DarkModeHelper.cs index a6924602..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; } 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 602caa18..d0785084 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs @@ -105,6 +105,7 @@ public MySideBar() DoubleBuffered = true; ownedFont = new Font(SystemFonts.MenuFont.FontFamily, SystemFonts.MenuFont.Size + 1F); Font = ownedFont; + SetStyle(ControlStyles.SupportsTransparentBackColor, true); InitializeColors(); SizeChanged += (s, e) => UpdateBackground(); animTimer.Tick += AnimationTimer_Tick; @@ -114,10 +115,15 @@ public MySideBar() private void OnThemeChanged(object sender, EventArgs e) { InitializeColors(); UpdateBackground(); } + public void UpdateThemeColors() { InitializeColors(); UpdateBackground(); Invalidate(); } + private void InitializeColors() { - BackColor = DarkModeHelper.SideBarBackground; ForeColor = DarkModeHelper.FormFore; - HoveredBackColor = DarkModeHelper.SideBarHovered; SeparatorColor = DarkModeHelper.SideBarSeparator; + 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; @@ -192,6 +198,14 @@ public void EndUpdate() private void UpdateBackground() { if (ItemNames == null) return; + + if (DarkModeHelper.IsDwmCompositionEnabled) + { + BackgroundImage?.Dispose(); + BackgroundImage = null; + return; + } + int w = Math.Max(1, Width), h = ItemNames.Length == 0 ? Math.Max(1, Height) : Math.Max(Height, Math.Max(0, ItemHeight) * ItemNames.Length); try { @@ -205,11 +219,21 @@ private void UpdateBackground() private void DrawBackgroundGradient(Graphics g, int w, int h) { + if (DarkModeHelper.IsDwmCompositionEnabled) + { + g.Clear(Color.Transparent); + return; + } + + var color1 = BackgroundGradientColor1; + var color2 = BackgroundGradientColor2; + var color3 = BackgroundGradientColor3; + using var b = new LinearGradientBrush(new Rectangle(0, 0, w, h), Color.Empty, Color.Empty, 0f) { InterpolationColors = new ColorBlend { - Colors = new[] { BackgroundGradientColor1, BackgroundGradientColor2, BackgroundGradientColor3 }, + Colors = new[] { color1, color2, color3 }, Positions = new[] { 0f, 0.5f, 1f } } }; @@ -239,14 +263,45 @@ private void DrawSeparator(Graphics g, Pen pen, int index) g.FillRectangle(brush, margin, y - 1, Width - 2 * margin, 2); } + protected override void OnPaintBackground(PaintEventArgs e) + { + if (DarkModeHelper.IsDwmCompositionEnabled) + { + e.Graphics.Clear(Color.Transparent); + } + else + { + base.OnPaintBackground(e); + } + } + protected override void OnPaint(PaintEventArgs e) { - base.OnPaint(e); 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; + 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); + } + } + } + void DrawItem(int idx, Color back, Color fore, float y) { if (idx < 0 || idx >= ItemNames.Length || string.IsNullOrEmpty(ItemNames[idx])) return; @@ -271,7 +326,8 @@ void DrawItem(int idx, Color back, Color fore, float y) using var b = new SolidBrush(back); e.Graphics.FillPath(b, path); } - using var fb = new SolidBrush(fore == Color.Empty ? ForeColor : fore); + // 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); } 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 ffe4efa3..a45bae39 100644 --- a/ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs @@ -21,9 +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, @@ -158,23 +160,28 @@ 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; - 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); + 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); @@ -185,7 +192,7 @@ protected override void OnPaintBackground(PaintEventArgs e) private void OnThemeChanged(object sender, EventArgs e) { - BackColor = DarkModeHelper.TitleArea; + BackColor = DarkModeHelper.IsDwmCompositionEnabled ? Color.Transparent : DarkModeHelper.TitleArea; ForeColor = DarkModeHelper.FormFore; Invalidate(); } @@ -246,7 +253,7 @@ 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(); @@ -277,7 +284,7 @@ public MyToolBarButton(Image image, string text) private readonly Label lblText = new() { - ForeColor = DarkModeHelper.FormFore, + ForeColor = Color.White, BackColor = Color.Transparent, Font = new Font(SystemFonts.MenuFont.FontFamily, SystemFonts.MenuFont.SizeInPoints, FontStyle.Regular, GraphicsUnit.Point), AutoSize = true, @@ -362,15 +369,22 @@ protected override void OnPaint(PaintEventArgs e) public void UpdateTextColor() { - var isDarkMode = DarkModeHelper.IsDarkTheme; - - if (!isDarkMode && currentOpacity > 0.3f) + 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; + } } }