From 024ebe8cc1339b0a62bfda47984253f9a00d5922 Mon Sep 17 00:00:00 2001 From: Wang Haoyu Date: Sat, 7 Mar 2026 23:48:04 +0800 Subject: [PATCH 01/10] =?UTF-8?q?fix:=20=E4=BD=BF=E7=94=A8=E5=86=85?= =?UTF-8?q?=E5=B5=8Cdrawer=E5=B9=B6=E8=99=9A=E6=8B=9F=E5=8C=96=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Services/FloatingWindowService.cs | 2 +- Settings/FloatingWindowTriggerSettings.cs | 205 ++++++++++++++++++++-- 2 files changed, 192 insertions(+), 15 deletions(-) diff --git a/Services/FloatingWindowService.cs b/Services/FloatingWindowService.cs index 8d2d0a0..17bb53a 100644 --- a/Services/FloatingWindowService.cs +++ b/Services/FloatingWindowService.cs @@ -198,7 +198,7 @@ private void RefreshWindowButtons() var nameBlock = new TextBlock { Text = string.IsNullOrWhiteSpace(entry.Name) ? "触发" : entry.Name, - FontSize = 10 * scale, + FontSize = 12 * scale, HorizontalAlignment = HorizontalAlignment.Center, TextAlignment = TextAlignment.Center, TextWrapping = TextWrapping.Wrap, diff --git a/Settings/FloatingWindowTriggerSettings.cs b/Settings/FloatingWindowTriggerSettings.cs index 9091952..e7a6d34 100644 --- a/Settings/FloatingWindowTriggerSettings.cs +++ b/Settings/FloatingWindowTriggerSettings.cs @@ -1,53 +1,226 @@ +using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.Templates; +using Avalonia.Layout; +using Avalonia.Media; using ClassIsland.Core.Abstractions.Controls; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; using SystemTools.Triggers; namespace SystemTools.Settings; public class FloatingWindowTriggerSettings : TriggerSettingsControlBase { + private const int IconCodeStart = 0xE000; + private const int IconCodeEnd = 0xF4D3; + private const int IconsPerRow = 10; + private readonly TextBox _iconTextBox; private readonly TextBox _nameTextBox; + private readonly Grid _rootGrid; + private readonly Border _iconDrawer; + private readonly ObservableCollection _iconRows = new(); public FloatingWindowTriggerSettings() { - var panel = new StackPanel { Spacing = 10, Margin = new(10) }; + _rootGrid = new Grid + { + ColumnDefinitions = new ColumnDefinitions("*,420"), + ColumnSpacing = 12 + }; + + var panel = new StackPanel { Spacing = 10, Margin = new Thickness(10) }; panel.Children.Add(new TextBlock { Text = "悬浮窗按钮图标(示例:/uE7C3)", - TextWrapping = Avalonia.Media.TextWrapping.Wrap + TextWrapping = TextWrapping.Wrap }); - _iconTextBox = new TextBox + _iconTextBox = new TextBox { Watermark = "/uE7C3", HorizontalAlignment = HorizontalAlignment.Stretch }; + _iconTextBox.TextChanged += (_, _) => { Settings.Icon = _iconTextBox.Text ?? string.Empty; }; + + var iconRow = new Grid { - Watermark = "/uE7C3" + ColumnDefinitions = new ColumnDefinitions("*,Auto"), + ColumnSpacing = 8 }; - _iconTextBox.TextChanged += (_, _) => { Settings.Icon = _iconTextBox.Text ?? string.Empty; }; + iconRow.Children.Add(_iconTextBox); - panel.Children.Add(_iconTextBox); + var pickIconButton = new Button + { + Content = "选择图标", + VerticalAlignment = VerticalAlignment.Center + }; + pickIconButton.Click += (_, _) => OpenIconDrawer(); + Grid.SetColumn(pickIconButton, 1); + iconRow.Children.Add(pickIconButton); + panel.Children.Add(iconRow); panel.Children.Add(new TextBlock { Text = "悬浮窗按钮名称(显示在图标下方)", - TextWrapping = Avalonia.Media.TextWrapping.Wrap + TextWrapping = TextWrapping.Wrap }); - _nameTextBox = new TextBox - { - Watermark = "例如:快捷抽取" - }; + _nameTextBox = new TextBox { Watermark = "例如:快捷抽取" }; _nameTextBox.TextChanged += (_, _) => { Settings.ButtonName = _nameTextBox.Text ?? string.Empty; }; panel.Children.Add(_nameTextBox); panel.Children.Add(new TextBlock { Text = "每个“从悬浮窗触发”触发器会在浮窗里生成一个按钮。", - TextWrapping = Avalonia.Media.TextWrapping.Wrap, - Foreground = Avalonia.Media.Brushes.Gray + TextWrapping = TextWrapping.Wrap, + Foreground = Brushes.Gray }); - Content = panel; + _rootGrid.Children.Add(panel); + + _iconDrawer = BuildIconDrawer(); + Grid.SetColumn(_iconDrawer, 1); + _rootGrid.Children.Add(_iconDrawer); + + Content = _rootGrid; + } + + private Border BuildIconDrawer() + { + var title = new TextBlock + { + Text = "选择悬浮窗图标", + FontSize = 14, + FontWeight = FontWeight.SemiBold, + VerticalAlignment = VerticalAlignment.Center + }; + + var closeButton = new Button + { + Content = "关闭", + HorizontalAlignment = HorizontalAlignment.Right + }; + closeButton.Click += (_, _) => CloseIconDrawer(); + + var header = new Grid + { + ColumnDefinitions = new ColumnDefinitions("*,Auto"), + Margin = new Thickness(0, 0, 0, 8) + }; + header.Children.Add(title); + Grid.SetColumn(closeButton, 1); + header.Children.Add(closeButton); + + var listBox = new ListBox + { + ItemsSource = _iconRows, + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Stretch + }; + + listBox.ItemsPanel = new FuncTemplate(() => new VirtualizingStackPanel()); + listBox.ItemTemplate = new FuncDataTemplate((row, _) => BuildRowPanel(row)); + + var drawerContent = new StackPanel + { + Children = + { + header, + new Border + { + BorderBrush = new SolidColorBrush(Color.Parse("#22000000")), + BorderThickness = new Thickness(1), + CornerRadius = new CornerRadius(6), + Padding = new Thickness(8), + Child = listBox + } + } + }; + + return new Border + { + IsVisible = false, + Background = new SolidColorBrush(Color.Parse("#111111")), + BorderBrush = new SolidColorBrush(Color.Parse("#33444444")), + BorderThickness = new Thickness(1), + CornerRadius = new CornerRadius(8), + Padding = new Thickness(10), + Child = drawerContent + }; + } + + private Control BuildRowPanel(IconRow row) + { + var wrapPanel = new WrapPanel + { + Orientation = Orientation.Horizontal, + ItemWidth = 36, + ItemHeight = 36, + HorizontalAlignment = HorizontalAlignment.Stretch + }; + + foreach (var icon in row.Icons) + { + var iconButton = new Button + { + Width = 34, + Height = 34, + Margin = new Thickness(1), + ToolTip = icon.Token, + Padding = new Thickness(0), + Content = new TextBlock + { + Text = icon.Glyph, + FontFamily = new FontFamily("Segoe Fluent Icons, Segoe MDL2 Assets"), + FontSize = 16, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + TextAlignment = TextAlignment.Center + } + }; + + iconButton.Click += (_, _) => SelectIcon(icon.Token); + wrapPanel.Children.Add(iconButton); + } + + return wrapPanel; + } + + private void OpenIconDrawer() + { + if (_iconRows.Count == 0) + { + LoadIconRows(); + } + + _iconDrawer.IsVisible = true; + } + + private void CloseIconDrawer() + { + _iconDrawer.IsVisible = false; + } + + private void SelectIcon(string token) + { + Settings.Icon = token; + _iconTextBox.Text = token; + CloseIconDrawer(); + } + + private void LoadIconRows() + { + var allIcons = new List(IconCodeEnd - IconCodeStart + 1); + for (var code = IconCodeStart; code <= IconCodeEnd; code++) + { + allIcons.Add(new IconItem($"/u{code:X4}", char.ConvertFromUtf32(code))); + } + + foreach (var chunk in allIcons.Chunk(IconsPerRow)) + { + _iconRows.Add(new IconRow(chunk.ToList())); + } } protected override void OnInitialized() @@ -56,4 +229,8 @@ protected override void OnInitialized() _iconTextBox.Text = Settings.Icon; _nameTextBox.Text = Settings.ButtonName; } + + private sealed record IconItem(string Token, string Glyph); + + private sealed record IconRow(IReadOnlyList Icons); } From 226d00cfbccfcb133599de9d600a47865fdc2ae8 Mon Sep 17 00:00:00 2001 From: Wang Haoyu Date: Sun, 8 Mar 2026 21:37:31 +0800 Subject: [PATCH 02/10] =?UTF-8?q?fix:=20=E9=98=B2=E5=BE=A1=E6=80=A7?= =?UTF-8?q?=E5=A4=84=E7=90=86=E5=9B=BE=E6=A0=87=E8=A1=8C=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E7=A9=BA=E6=95=B0=E6=8D=AE=E5=AF=BC=E8=87=B4=E7=9A=84NRE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Settings/FloatingWindowTriggerSettings.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Settings/FloatingWindowTriggerSettings.cs b/Settings/FloatingWindowTriggerSettings.cs index e7a6d34..2950ac2 100644 --- a/Settings/FloatingWindowTriggerSettings.cs +++ b/Settings/FloatingWindowTriggerSettings.cs @@ -120,7 +120,7 @@ private Border BuildIconDrawer() }; listBox.ItemsPanel = new FuncTemplate(() => new VirtualizingStackPanel()); - listBox.ItemTemplate = new FuncDataTemplate((row, _) => BuildRowPanel(row)); + listBox.ItemTemplate = new FuncDataTemplate((row, _) => BuildRowPanel(row)); var drawerContent = new StackPanel { @@ -150,7 +150,7 @@ private Border BuildIconDrawer() }; } - private Control BuildRowPanel(IconRow row) + private Control BuildRowPanel(IconRow? row) { var wrapPanel = new WrapPanel { @@ -160,8 +160,17 @@ private Control BuildRowPanel(IconRow row) HorizontalAlignment = HorizontalAlignment.Stretch }; + if (row?.Icons == null || row.Icons.Count == 0) + { + return wrapPanel; + } + foreach (var icon in row.Icons) { + if (icon == null || string.IsNullOrWhiteSpace(icon.Token) || string.IsNullOrEmpty(icon.Glyph)) + { + continue; + } var iconButton = new Button { Width = 34, From 8777afb631253c1b06c583a52d2a71bfbdd7d6c8 Mon Sep 17 00:00:00 2001 From: Wang Haoyu Date: Sun, 8 Mar 2026 21:45:54 +0800 Subject: [PATCH 03/10] =?UTF-8?q?refactor:=20=E5=9B=BE=E6=A0=87=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E5=99=A8=E6=94=B9=E7=94=A8ClassIsland=20FluentIcon?= =?UTF-8?q?=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Settings/FloatingWindowTriggerSettings.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Settings/FloatingWindowTriggerSettings.cs b/Settings/FloatingWindowTriggerSettings.cs index 2950ac2..8496f7f 100644 --- a/Settings/FloatingWindowTriggerSettings.cs +++ b/Settings/FloatingWindowTriggerSettings.cs @@ -4,6 +4,7 @@ using Avalonia.Layout; using Avalonia.Media; using ClassIsland.Core.Abstractions.Controls; +using ClassIsland.Core.Controls; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -178,14 +179,12 @@ private Control BuildRowPanel(IconRow? row) Margin = new Thickness(1), ToolTip = icon.Token, Padding = new Thickness(0), - Content = new TextBlock + Content = new FluentIcon { - Text = icon.Glyph, - FontFamily = new FontFamily("Segoe Fluent Icons, Segoe MDL2 Assets"), + Glyph = icon.Glyph, FontSize = 16, HorizontalAlignment = HorizontalAlignment.Center, - VerticalAlignment = VerticalAlignment.Center, - TextAlignment = TextAlignment.Center + VerticalAlignment = VerticalAlignment.Center } }; From 53387b4dc215c2f77e90c53ab3aad4b1963e7027 Mon Sep 17 00:00:00 2001 From: Wang Haoyu Date: Sun, 8 Mar 2026 21:59:47 +0800 Subject: [PATCH 04/10] =?UTF-8?q?fix:=20=E7=BB=9F=E4=B8=80=E4=BD=BF?= =?UTF-8?q?=E7=94=A8ClassIsland=E5=9B=BE=E6=A0=87=E5=B9=B6=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=BC=B9=E7=AA=97=E9=80=89=E6=8B=A9=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Services/FloatingWindowService.cs | 9 ++- Settings/FloatingWindowTriggerSettings.cs | 76 ++++++++++++----------- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/Services/FloatingWindowService.cs b/Services/FloatingWindowService.cs index 17bb53a..1a4637c 100644 --- a/Services/FloatingWindowService.cs +++ b/Services/FloatingWindowService.cs @@ -5,6 +5,7 @@ using Avalonia.Layout; using Avalonia.Media; using Avalonia.Threading; +using ClassIsland.Core.Controls; using System; using System.Collections.Generic; using System.Linq; @@ -185,14 +186,12 @@ private void RefreshWindowButtons() foreach (var entry in GetOrderedEntries()) { - var iconBlock = new TextBlock + var iconBlock = new FluentIcon { - Text = ConvertIcon(entry.Icon), + Glyph = ConvertIcon(entry.Icon), FontSize = 18 * scale, - FontFamily = new FontFamily("Segoe Fluent Icons, Segoe MDL2 Assets"), HorizontalAlignment = HorizontalAlignment.Center, - VerticalAlignment = VerticalAlignment.Center, - TextAlignment = TextAlignment.Center + VerticalAlignment = VerticalAlignment.Center }; var nameBlock = new TextBlock diff --git a/Settings/FloatingWindowTriggerSettings.cs b/Settings/FloatingWindowTriggerSettings.cs index 8496f7f..bea17b8 100644 --- a/Settings/FloatingWindowTriggerSettings.cs +++ b/Settings/FloatingWindowTriggerSettings.cs @@ -21,18 +21,11 @@ public class FloatingWindowTriggerSettings : TriggerSettingsControlBase _iconRows = new(); + private Window? _iconPickerWindow; public FloatingWindowTriggerSettings() { - _rootGrid = new Grid - { - ColumnDefinitions = new ColumnDefinitions("*,420"), - ColumnSpacing = 12 - }; - var panel = new StackPanel { Spacing = 10, Margin = new Thickness(10) }; panel.Children.Add(new TextBlock @@ -56,7 +49,7 @@ public FloatingWindowTriggerSettings() Content = "选择图标", VerticalAlignment = VerticalAlignment.Center }; - pickIconButton.Click += (_, _) => OpenIconDrawer(); + pickIconButton.Click += (_, _) => OpenIconPickerWindow(); Grid.SetColumn(pickIconButton, 1); iconRow.Children.Add(pickIconButton); panel.Children.Add(iconRow); @@ -78,16 +71,10 @@ public FloatingWindowTriggerSettings() Foreground = Brushes.Gray }); - _rootGrid.Children.Add(panel); - - _iconDrawer = BuildIconDrawer(); - Grid.SetColumn(_iconDrawer, 1); - _rootGrid.Children.Add(_iconDrawer); - - Content = _rootGrid; + Content = panel; } - private Border BuildIconDrawer() + private Control BuildIconPickerContent() { var title = new TextBlock { @@ -102,7 +89,7 @@ private Border BuildIconDrawer() Content = "关闭", HorizontalAlignment = HorizontalAlignment.Right }; - closeButton.Click += (_, _) => CloseIconDrawer(); + closeButton.Click += (_, _) => _iconPickerWindow?.Close(); var header = new Grid { @@ -123,7 +110,7 @@ private Border BuildIconDrawer() listBox.ItemsPanel = new FuncTemplate(() => new VirtualizingStackPanel()); listBox.ItemTemplate = new FuncDataTemplate((row, _) => BuildRowPanel(row)); - var drawerContent = new StackPanel + return new StackPanel { Children = { @@ -138,17 +125,6 @@ private Border BuildIconDrawer() } } }; - - return new Border - { - IsVisible = false, - Background = new SolidColorBrush(Color.Parse("#111111")), - BorderBrush = new SolidColorBrush(Color.Parse("#33444444")), - BorderThickness = new Thickness(1), - CornerRadius = new CornerRadius(8), - Padding = new Thickness(10), - Child = drawerContent - }; } private Control BuildRowPanel(IconRow? row) @@ -172,6 +148,7 @@ private Control BuildRowPanel(IconRow? row) { continue; } + var iconButton = new Button { Width = 34, @@ -195,26 +172,51 @@ private Control BuildRowPanel(IconRow? row) return wrapPanel; } - private void OpenIconDrawer() + private void OpenIconPickerWindow() { if (_iconRows.Count == 0) { LoadIconRows(); } - _iconDrawer.IsVisible = true; - } + if (_iconPickerWindow?.IsVisible == true) + { + _iconPickerWindow.Activate(); + return; + } - private void CloseIconDrawer() - { - _iconDrawer.IsVisible = false; + _iconPickerWindow = new Window + { + Width = 480, + Height = 620, + MinWidth = 420, + MinHeight = 500, + ShowInTaskbar = false, + Title = "选择悬浮窗图标", + Content = new Border + { + Padding = new Thickness(10), + Child = BuildIconPickerContent() + } + }; + + _iconPickerWindow.Closed += (_, _) => _iconPickerWindow = null; + + if (TopLevel.GetTopLevel(this) is Window owner) + { + _iconPickerWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner; + _iconPickerWindow.Show(owner); + return; + } + + _iconPickerWindow.Show(); } private void SelectIcon(string token) { Settings.Icon = token; _iconTextBox.Text = token; - CloseIconDrawer(); + _iconPickerWindow?.Close(); } private void LoadIconRows() From 449585b69529ce463fa0d61a7884b0ea93dd76df Mon Sep 17 00:00:00 2001 From: Wang Haoyu Date: Sun, 8 Mar 2026 22:31:19 +0800 Subject: [PATCH 05/10] =?UTF-8?q?fix:=20=E5=9B=BE=E6=A0=87=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E6=94=B9=E4=B8=BA=E5=8F=AF=E6=BB=9A=E5=8A=A8ContentDi?= =?UTF-8?q?alog=E5=B9=B6=E5=8F=96=E6=B6=88=E5=9B=BA=E5=AE=9A=E6=8E=92?= =?UTF-8?q?=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Settings/FloatingWindowTriggerSettings.cs | 179 ++++++---------------- 1 file changed, 47 insertions(+), 132 deletions(-) diff --git a/Settings/FloatingWindowTriggerSettings.cs b/Settings/FloatingWindowTriggerSettings.cs index bea17b8..2f00db8 100644 --- a/Settings/FloatingWindowTriggerSettings.cs +++ b/Settings/FloatingWindowTriggerSettings.cs @@ -1,14 +1,12 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Controls.Templates; using Avalonia.Layout; using Avalonia.Media; using ClassIsland.Core.Abstractions.Controls; using ClassIsland.Core.Controls; +using FluentAvalonia.UI.Controls; using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; +using System.Threading.Tasks; using SystemTools.Triggers; namespace SystemTools.Settings; @@ -17,12 +15,21 @@ public class FloatingWindowTriggerSettings : TriggerSettingsControlBase _iconRows = new(); - private Window? _iconPickerWindow; + + private readonly WrapPanel _iconWrapPanel = new() + { + Orientation = Orientation.Horizontal, + ItemWidth = 36, + ItemHeight = 36, + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Top + }; + + private bool _iconsLoaded; + private ContentDialog? _iconPickerDialog; public FloatingWindowTriggerSettings() { @@ -49,7 +56,7 @@ public FloatingWindowTriggerSettings() Content = "选择图标", VerticalAlignment = VerticalAlignment.Center }; - pickIconButton.Click += (_, _) => OpenIconPickerWindow(); + pickIconButton.Click += async (_, _) => await OpenIconPickerDialogAsync(); Grid.SetColumn(pickIconButton, 1); iconRow.Children.Add(pickIconButton); panel.Children.Add(iconRow); @@ -74,163 +81,75 @@ public FloatingWindowTriggerSettings() Content = panel; } - private Control BuildIconPickerContent() + private async Task OpenIconPickerDialogAsync() { - var title = new TextBlock - { - Text = "选择悬浮窗图标", - FontSize = 14, - FontWeight = FontWeight.SemiBold, - VerticalAlignment = VerticalAlignment.Center - }; - - var closeButton = new Button - { - Content = "关闭", - HorizontalAlignment = HorizontalAlignment.Right - }; - closeButton.Click += (_, _) => _iconPickerWindow?.Close(); - - var header = new Grid + var topLevel = TopLevel.GetTopLevel(this); + if (topLevel == null) { - ColumnDefinitions = new ColumnDefinitions("*,Auto"), - Margin = new Thickness(0, 0, 0, 8) - }; - header.Children.Add(title); - Grid.SetColumn(closeButton, 1); - header.Children.Add(closeButton); + return; + } - var listBox = new ListBox + if (!_iconsLoaded) { - ItemsSource = _iconRows, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch - }; - - listBox.ItemsPanel = new FuncTemplate(() => new VirtualizingStackPanel()); - listBox.ItemTemplate = new FuncDataTemplate((row, _) => BuildRowPanel(row)); + LoadIcons(); + _iconsLoaded = true; + } - return new StackPanel + _iconPickerDialog = new ContentDialog { - Children = + Title = "选择悬浮窗图标", + PrimaryButtonText = "关闭", + DefaultButton = ContentDialogButton.Primary, + Content = new Border { - header, - new Border + Padding = new Thickness(8), + Child = new ScrollViewer { - BorderBrush = new SolidColorBrush(Color.Parse("#22000000")), - BorderThickness = new Thickness(1), - CornerRadius = new CornerRadius(6), - Padding = new Thickness(8), - Child = listBox + Height = 520, + VerticalScrollBarVisibility = ScrollBarVisibility.Auto, + HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled, + Content = _iconWrapPanel } } }; + + await _iconPickerDialog.ShowAsync(topLevel); + _iconPickerDialog = null; } - private Control BuildRowPanel(IconRow? row) + private void LoadIcons() { - var wrapPanel = new WrapPanel - { - Orientation = Orientation.Horizontal, - ItemWidth = 36, - ItemHeight = 36, - HorizontalAlignment = HorizontalAlignment.Stretch - }; - - if (row?.Icons == null || row.Icons.Count == 0) - { - return wrapPanel; - } - - foreach (var icon in row.Icons) + for (var code = IconCodeStart; code <= IconCodeEnd; code++) { - if (icon == null || string.IsNullOrWhiteSpace(icon.Token) || string.IsNullOrEmpty(icon.Glyph)) - { - continue; - } + var token = $"/u{code:X4}"; + var glyph = char.ConvertFromUtf32(code); var iconButton = new Button { Width = 34, Height = 34, Margin = new Thickness(1), - ToolTip = icon.Token, + ToolTip = token, Padding = new Thickness(0), Content = new FluentIcon { - Glyph = icon.Glyph, + Glyph = glyph, FontSize = 16, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center } }; - iconButton.Click += (_, _) => SelectIcon(icon.Token); - wrapPanel.Children.Add(iconButton); + iconButton.Click += (_, _) => SelectIcon(token); + _iconWrapPanel.Children.Add(iconButton); } - - return wrapPanel; - } - - private void OpenIconPickerWindow() - { - if (_iconRows.Count == 0) - { - LoadIconRows(); - } - - if (_iconPickerWindow?.IsVisible == true) - { - _iconPickerWindow.Activate(); - return; - } - - _iconPickerWindow = new Window - { - Width = 480, - Height = 620, - MinWidth = 420, - MinHeight = 500, - ShowInTaskbar = false, - Title = "选择悬浮窗图标", - Content = new Border - { - Padding = new Thickness(10), - Child = BuildIconPickerContent() - } - }; - - _iconPickerWindow.Closed += (_, _) => _iconPickerWindow = null; - - if (TopLevel.GetTopLevel(this) is Window owner) - { - _iconPickerWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner; - _iconPickerWindow.Show(owner); - return; - } - - _iconPickerWindow.Show(); } private void SelectIcon(string token) { Settings.Icon = token; _iconTextBox.Text = token; - _iconPickerWindow?.Close(); - } - - private void LoadIconRows() - { - var allIcons = new List(IconCodeEnd - IconCodeStart + 1); - for (var code = IconCodeStart; code <= IconCodeEnd; code++) - { - allIcons.Add(new IconItem($"/u{code:X4}", char.ConvertFromUtf32(code))); - } - - foreach (var chunk in allIcons.Chunk(IconsPerRow)) - { - _iconRows.Add(new IconRow(chunk.ToList())); - } + _iconPickerDialog?.Hide(); } protected override void OnInitialized() @@ -239,8 +158,4 @@ protected override void OnInitialized() _iconTextBox.Text = Settings.Icon; _nameTextBox.Text = Settings.ButtonName; } - - private sealed record IconItem(string Token, string Glyph); - - private sealed record IconRow(IReadOnlyList Icons); } From 1c54677e4d08af830193503b3651b919cc5ad760 Mon Sep 17 00:00:00 2001 From: Wang Haoyu Date: Sun, 8 Mar 2026 22:44:03 +0800 Subject: [PATCH 06/10] =?UTF-8?q?fix:=20=E9=81=BF=E5=85=8D=E5=A4=8D?= =?UTF-8?q?=E7=94=A8WrapPanel=E5=AF=BC=E8=87=B4=E5=BC=B9=E7=AA=97=E8=A7=86?= =?UTF-8?q?=E8=A7=89=E6=A0=91=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Settings/FloatingWindowTriggerSettings.cs | 129 ++++++++++++++-------- 1 file changed, 82 insertions(+), 47 deletions(-) diff --git a/Settings/FloatingWindowTriggerSettings.cs b/Settings/FloatingWindowTriggerSettings.cs index 2f00db8..5627696 100644 --- a/Settings/FloatingWindowTriggerSettings.cs +++ b/Settings/FloatingWindowTriggerSettings.cs @@ -1,11 +1,13 @@ using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.Templates; using Avalonia.Layout; using Avalonia.Media; using ClassIsland.Core.Abstractions.Controls; using ClassIsland.Core.Controls; using FluentAvalonia.UI.Controls; using System; +using System.Collections.Generic; using System.Threading.Tasks; using SystemTools.Triggers; @@ -18,17 +20,8 @@ public class FloatingWindowTriggerSettings : TriggerSettingsControlBase _iconTokens = new(); - private readonly WrapPanel _iconWrapPanel = new() - { - Orientation = Orientation.Horizontal, - ItemWidth = 36, - ItemHeight = 36, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Top - }; - - private bool _iconsLoaded; private ContentDialog? _iconPickerDialog; public FloatingWindowTriggerSettings() @@ -89,59 +82,101 @@ private async Task OpenIconPickerDialogAsync() return; } - if (!_iconsLoaded) - { - LoadIcons(); - _iconsLoaded = true; - } + EnsureIconsLoaded(); _iconPickerDialog = new ContentDialog { Title = "选择悬浮窗图标", PrimaryButtonText = "关闭", DefaultButton = ContentDialogButton.Primary, - Content = new Border - { - Padding = new Thickness(8), - Child = new ScrollViewer - { - Height = 520, - VerticalScrollBarVisibility = ScrollBarVisibility.Auto, - HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled, - Content = _iconWrapPanel - } - } + Content = BuildIconPickerContent() }; await _iconPickerDialog.ShowAsync(topLevel); _iconPickerDialog = null; } - private void LoadIcons() + private Control BuildIconPickerContent() { - for (var code = IconCodeStart; code <= IconCodeEnd; code++) + var itemsControl = new ItemsControl + { + ItemsSource = _iconTokens + }; + + itemsControl.ItemsPanel = new FuncTemplate(() => new WrapPanel + { + Orientation = Orientation.Horizontal, + ItemWidth = 36, + ItemHeight = 36, + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Top + }); + + itemsControl.ItemTemplate = new FuncDataTemplate((token, _) => BuildIconButton(token)); + + return new Border + { + Padding = new Thickness(8), + Child = new ScrollViewer + { + Height = 520, + VerticalScrollBarVisibility = ScrollBarVisibility.Auto, + HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled, + Content = itemsControl + } + }; + } + + private Control BuildIconButton(string? token) + { + if (string.IsNullOrWhiteSpace(token)) + { + return new Border { Width = 34, Height = 34 }; + } + + var iconButton = new Button { - var token = $"/u{code:X4}"; - var glyph = char.ConvertFromUtf32(code); + Width = 34, + Height = 34, + Margin = new Thickness(1), + ToolTip = token, + Padding = new Thickness(0), + Content = new FluentIcon + { + Glyph = ToGlyph(token), + FontSize = 16, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center + } + }; - var iconButton = new Button + iconButton.Click += (_, _) => SelectIcon(token); + return iconButton; + } + + private static string ToGlyph(string token) + { + if (token.Length > 2 && (token.StartsWith("/u", StringComparison.OrdinalIgnoreCase) || token.StartsWith("\\u", StringComparison.OrdinalIgnoreCase))) + { + if (int.TryParse(token[2..], System.Globalization.NumberStyles.HexNumber, null, out var code)) { - Width = 34, - Height = 34, - Margin = new Thickness(1), - ToolTip = token, - Padding = new Thickness(0), - Content = new FluentIcon - { - Glyph = glyph, - FontSize = 16, - HorizontalAlignment = HorizontalAlignment.Center, - VerticalAlignment = VerticalAlignment.Center - } - }; - - iconButton.Click += (_, _) => SelectIcon(token); - _iconWrapPanel.Children.Add(iconButton); + return char.ConvertFromUtf32(code); + } + } + + return token; + } + + private void EnsureIconsLoaded() + { + if (_iconTokens.Count > 0) + { + return; + } + + for (var code = IconCodeStart; code <= IconCodeEnd; code++) + { + _iconTokens.Add($"/u{code:X4}"); } } From 772342714bfe0536e25260ab035fec49a5761098 Mon Sep 17 00:00:00 2001 From: Wang Haoyu Date: Sun, 8 Mar 2026 22:57:13 +0800 Subject: [PATCH 07/10] =?UTF-8?q?perf:=20=E5=9B=BE=E6=A0=87=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=E6=94=B9=E4=B8=BA=E8=A1=8C=E7=BA=A7=E8=99=9A=E6=8B=9F?= =?UTF-8?q?=E5=8C=96=E5=87=8F=E5=B0=91=E6=89=93=E5=BC=80=E5=8D=A1=E9=A1=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Settings/FloatingWindowTriggerSettings.cs | 68 +++++++++++++++++------ 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/Settings/FloatingWindowTriggerSettings.cs b/Settings/FloatingWindowTriggerSettings.cs index 5627696..90c5509 100644 --- a/Settings/FloatingWindowTriggerSettings.cs +++ b/Settings/FloatingWindowTriggerSettings.cs @@ -8,6 +8,8 @@ using FluentAvalonia.UI.Controls; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; using System.Threading.Tasks; using SystemTools.Triggers; @@ -83,48 +85,65 @@ private async Task OpenIconPickerDialogAsync() } EnsureIconsLoaded(); + var rows = BuildVirtualizedRows(460); _iconPickerDialog = new ContentDialog { Title = "选择悬浮窗图标", PrimaryButtonText = "关闭", DefaultButton = ContentDialogButton.Primary, - Content = BuildIconPickerContent() + Content = BuildIconPickerContent(rows) }; await _iconPickerDialog.ShowAsync(topLevel); _iconPickerDialog = null; } - private Control BuildIconPickerContent() + private Control BuildIconPickerContent(ObservableCollection rows) { - var itemsControl = new ItemsControl + var listBox = new ListBox { - ItemsSource = _iconTokens + ItemsSource = rows, + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Stretch }; - itemsControl.ItemsPanel = new FuncTemplate(() => new WrapPanel + listBox.ItemsPanel = new FuncTemplate(() => new VirtualizingStackPanel()); + listBox.ItemTemplate = new FuncDataTemplate((row, _) => BuildIconRow(row)); + + listBox.Height = 520; + listBox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; + listBox.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled; + + return new Border + { + Padding = new Thickness(8), + Child = listBox + }; + } + + private Control BuildIconRow(IconRow? row) + { + var panel = new WrapPanel { Orientation = Orientation.Horizontal, ItemWidth = 36, ItemHeight = 36, HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Top - }); + }; - itemsControl.ItemTemplate = new FuncDataTemplate((token, _) => BuildIconButton(token)); + if (row?.Tokens == null || row.Tokens.Count == 0) + { + return panel; + } - return new Border + foreach (var token in row.Tokens) { - Padding = new Thickness(8), - Child = new ScrollViewer - { - Height = 520, - VerticalScrollBarVisibility = ScrollBarVisibility.Auto, - HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled, - Content = itemsControl - } - }; + panel.Children.Add(BuildIconButton(token)); + } + + return panel; } private Control BuildIconButton(string? token) @@ -154,6 +173,19 @@ private Control BuildIconButton(string? token) return iconButton; } + private ObservableCollection BuildVirtualizedRows(double dialogWidth) + { + var columns = Math.Max(6, (int)((dialogWidth - 32) / 38)); + var rows = new ObservableCollection(); + + foreach (var chunk in _iconTokens.Chunk(columns)) + { + rows.Add(new IconRow(chunk.ToList())); + } + + return rows; + } + private static string ToGlyph(string token) { if (token.Length > 2 && (token.StartsWith("/u", StringComparison.OrdinalIgnoreCase) || token.StartsWith("\\u", StringComparison.OrdinalIgnoreCase))) @@ -193,4 +225,6 @@ protected override void OnInitialized() _iconTextBox.Text = Settings.Icon; _nameTextBox.Text = Settings.ButtonName; } + + private sealed record IconRow(IReadOnlyList Tokens); } From 8c045c85cf1ade11970210ac09b8d5de028ae45d Mon Sep 17 00:00:00 2001 From: Wang Haoyu Date: Sun, 8 Mar 2026 22:59:46 +0800 Subject: [PATCH 08/10] =?UTF-8?q?fix:=20=E4=BD=BF=E7=94=A8ScrollViewer?= =?UTF-8?q?=E9=99=84=E5=8A=A0=E5=B1=9E=E6=80=A7=E8=AE=BE=E7=BD=AEListBox?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Settings/FloatingWindowTriggerSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Settings/FloatingWindowTriggerSettings.cs b/Settings/FloatingWindowTriggerSettings.cs index 90c5509..313acfc 100644 --- a/Settings/FloatingWindowTriggerSettings.cs +++ b/Settings/FloatingWindowTriggerSettings.cs @@ -112,8 +112,8 @@ private Control BuildIconPickerContent(ObservableCollection rows) listBox.ItemTemplate = new FuncDataTemplate((row, _) => BuildIconRow(row)); listBox.Height = 520; - listBox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; - listBox.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled; + ScrollViewer.SetVerticalScrollBarVisibility(listBox, ScrollBarVisibility.Auto); + ScrollViewer.SetHorizontalScrollBarVisibility(listBox, ScrollBarVisibility.Disabled); return new Border { From ff7bd8b3fa1a390e52e5021656fafce3c64a21ac Mon Sep 17 00:00:00 2001 From: Wang Haoyu Date: Sun, 8 Mar 2026 23:08:36 +0800 Subject: [PATCH 09/10] =?UTF-8?q?style:=20=E6=94=BE=E5=A4=A7=E6=82=AC?= =?UTF-8?q?=E6=B5=AE=E7=AA=97=E4=B8=8E=E8=AE=BE=E7=BD=AE=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=B0=BA=E5=AF=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Services/FloatingWindowService.cs | 2 +- Settings/FloatingWindowTriggerSettings.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Services/FloatingWindowService.cs b/Services/FloatingWindowService.cs index 1a4637c..9ebaaf9 100644 --- a/Services/FloatingWindowService.cs +++ b/Services/FloatingWindowService.cs @@ -189,7 +189,7 @@ private void RefreshWindowButtons() var iconBlock = new FluentIcon { Glyph = ConvertIcon(entry.Icon), - FontSize = 18 * scale, + FontSize = 20 * scale, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center }; diff --git a/Settings/FloatingWindowTriggerSettings.cs b/Settings/FloatingWindowTriggerSettings.cs index 313acfc..2dfc0fd 100644 --- a/Settings/FloatingWindowTriggerSettings.cs +++ b/Settings/FloatingWindowTriggerSettings.cs @@ -163,7 +163,7 @@ private Control BuildIconButton(string? token) Content = new FluentIcon { Glyph = ToGlyph(token), - FontSize = 16, + FontSize = 18, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center } From 9f25125d39b461f243ae9f11cd7f0f57aa7cdbed Mon Sep 17 00:00:00 2001 From: Programmer_MrWang Date: Sun, 8 Mar 2026 23:17:09 +0800 Subject: [PATCH 10/10] =?UTF-8?q?fix:=20=E6=9B=B4=E6=94=B9=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Services/FloatingWindowService.cs | 2 +- Settings/FloatingWindowTriggerSettings.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Services/FloatingWindowService.cs b/Services/FloatingWindowService.cs index 9ebaaf9..ac084de 100644 --- a/Services/FloatingWindowService.cs +++ b/Services/FloatingWindowService.cs @@ -189,7 +189,7 @@ private void RefreshWindowButtons() var iconBlock = new FluentIcon { Glyph = ConvertIcon(entry.Icon), - FontSize = 20 * scale, + FontSize = 22 * scale, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center }; diff --git a/Settings/FloatingWindowTriggerSettings.cs b/Settings/FloatingWindowTriggerSettings.cs index 2dfc0fd..8fb96f0 100644 --- a/Settings/FloatingWindowTriggerSettings.cs +++ b/Settings/FloatingWindowTriggerSettings.cs @@ -11,6 +11,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; +using Avalonia.Controls.Primitives; using SystemTools.Triggers; namespace SystemTools.Settings; @@ -158,12 +159,12 @@ private Control BuildIconButton(string? token) Width = 34, Height = 34, Margin = new Thickness(1), - ToolTip = token, + //ToolTip = token, Padding = new Thickness(0), Content = new FluentIcon { Glyph = ToGlyph(token), - FontSize = 18, + FontSize = 21, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center }