add:新设置

增加搜索功能
This commit is contained in:
2025-12-28 10:40:13 +08:00
parent 7a4d33b7da
commit 3d4e1872f1
5 changed files with 605 additions and 1 deletions
+1
View File
@@ -154,6 +154,7 @@
<PackageReference Include="Microsoft.Office.Interop.PowerPoint" Version="15.0.4420.1018" />
<PackageReference Include="MicrosoftOfficeCore" Version="15.0.0" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageReference Include="Microsoft.International.Converters.PinYinConverter" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
@@ -0,0 +1,140 @@
<UserControl x:Class="Ink_Canvas.Windows.SettingsViews.SearchPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews"
mc:Ignorable="d"
d:DesignHeight="691" d:DesignWidth="910">
<Grid Background="#fafafa" Margin="250,0,0,0">
<!-- 搜索框区域 -->
<Grid Height="48" VerticalAlignment="Top" Margin="0,0,0,0">
<Border Height="48" CornerRadius="0,6,0,0" Background="#fafafa">
<Border.Effect>
<DropShadowEffect Direction="-90" ShadowDepth="3" BlurRadius="4" Color="#000000" Opacity="0.25"/>
</Border.Effect>
</Border>
<Grid Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 搜索输入框 -->
<Border Grid.Column="0" Margin="60,8,12,8" Background="White" CornerRadius="8" BorderBrush="#e6e6e6" BorderThickness="1">
<Grid>
<Image Width="16" Height="16" Margin="12,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V17 H16 V0 H0 Z">
<GeometryDrawing Brush="#FF222222" Geometry="F1 M16,17z M0,0z M6.9333,0.111572C3.11689,0.111572 -5.16259E-07,3.22999 -5.16259E-07,7.04827 -5.16259E-07,10.8665 3.11689,13.9829 6.9333,13.9829 8.45757,13.9829 9.86954,13.4831 11.0166,12.6427L14.1583,15.7858C15.1805,16.7869,16.6805,15.2528,15.6583,14.2518L12.5333,11.1252C13.3704,9.97933 13.8666,8.57003 13.8666,7.04827 13.8666,3.22999 10.7497,0.111572 6.9333,0.111572z M6.9333,2.24594C9.59676,2.24594 11.7333,4.38351 11.7333,7.04827 11.7333,9.71302 9.59676,11.8485 6.9333,11.8485 4.26985,11.8485 2.13332,9.71302 2.13332,7.04827 2.13332,4.38351 4.26985,2.24594 6.9333,2.24594z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBox Name="SearchTextBox"
FontSize="14"
Foreground="#2e3436"
Background="Transparent"
BorderThickness="0"
Padding="40,0,12,0"
VerticalContentAlignment="Center"
KeyDown="SearchTextBox_KeyDown"
TextChanged="SearchTextBox_TextChanged"
GotFocus="SearchTextBox_GotFocus"/>
</Grid>
</Border>
<!-- 关闭按钮 -->
<Border Grid.Column="1" CornerRadius="8" Background="#33ef4444" Width="34" Height="34" Margin="0,0,8,0" HorizontalAlignment="Right" VerticalAlignment="Center" MouseLeftButtonDown="CloseSearchButton_Click" Cursor="Hand">
<Image Width="12" Height="12">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V12 H12 V0 H0 Z">
<GeometryDrawing Brush="#991b1b" Geometry="F1 M12,12z M0,0z M0.999846,0C0.734646,4.07258E-05 0.480321,0.105424 0.292816,0.29297 0.105327,0.4805 0,0.734821 0,1 0,1.26518 0.105327,1.5195 0.292816,1.70703L4.58579,6 0.292816,10.293C0.105327,10.4805 0,10.7348 0,11 0,11.2652 0.105327,11.5195 0.292816,11.707 0.480347,11.8945 0.734668,11.9998 0.999846,11.9998 1.26503,11.9998 1.51935,11.8945 1.70688,11.707L5.99985,7.41406 10.2928,11.707C10.4803,11.8945 10.7347,11.9998 10.9998,11.9998 11.265,11.9998 11.5193,11.8945 11.7069,11.707 11.8944,11.5195 11.9997,11.2652 11.9997,11 11.9997,10.7348 11.8944,10.4805 11.7069,10.293L7.41391,6 11.7069,1.70703C11.8944,1.5195 11.9997,1.26518 11.9997,1 11.9997,0.734821 11.8944,0.4805 11.7069,0.29297 11.5194,0.105424 11.265,4.07258E-05 10.9998,0 10.7346,4.07258E-05 10.4803,0.105424 10.2928,0.29297L5.99985,4.58594 1.70688,0.29297C1.51937,0.105424,1.26505,4.07258E-05,0.999846,0z" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Border>
</Grid>
</Grid>
<!-- 搜索结果区域 -->
<ScrollViewer Margin="0,48,0,0" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="60,12,60,24" Name="SearchResultsPanel">
<!-- 精准和拼音匹配结果 -->
<StackPanel Name="ExactMatchPanel" Visibility="Collapsed">
<TextBlock Text="精准匹配" FontSize="13" FontWeight="Bold" Foreground="#2e3436" Margin="0,0,0,8"/>
<ItemsControl Name="ExactMatchItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="White" CornerRadius="6" Margin="0,0,0,6" Padding="16,12" MouseLeftButtonDown="SearchResultItem_Click" Cursor="Hand" Tag="{Binding}">
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Title}" FontSize="14" Foreground="#2e3436" FontWeight="Bold"/>
<TextBlock Text="{Binding Category}" FontSize="11" Foreground="#9a9996" Margin="0,4,0,0"/>
<TextBlock Text="{Binding Description}" FontSize="11" Foreground="#9a9996" Margin="0,2,0,0" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<!-- 模糊匹配结果 -->
<StackPanel Name="FuzzyMatchPanel" Visibility="Collapsed" Margin="0,16,0,0">
<TextBlock Text="模糊匹配" FontSize="13" FontWeight="Bold" Foreground="#2e3436" Margin="0,0,0,8"/>
<ItemsControl Name="FuzzyMatchItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="White" CornerRadius="6" Margin="0,0,0,6" Padding="16,12" MouseLeftButtonDown="SearchResultItem_Click" Cursor="Hand" Tag="{Binding}">
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Title}" FontSize="14" Foreground="#2e3436" FontWeight="Bold"/>
<TextBlock Text="{Binding Category}" FontSize="11" Foreground="#9a9996" Margin="0,4,0,0"/>
<TextBlock Text="{Binding Description}" FontSize="11" Foreground="#9a9996" Margin="0,2,0,0" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<!-- 相关项结果 -->
<StackPanel Name="RelatedItemsPanel" Visibility="Collapsed" Margin="0,16,0,0">
<TextBlock Text="相关项" FontSize="13" FontWeight="Bold" Foreground="#2e3436" Margin="0,0,0,8"/>
<ItemsControl Name="RelatedItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="White" CornerRadius="6" Margin="0,0,0,6" Padding="16,12" MouseLeftButtonDown="SearchResultItem_Click" Cursor="Hand" Tag="{Binding}">
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Title}" FontSize="14" Foreground="#2e3436" FontWeight="Bold"/>
<TextBlock Text="{Binding Category}" FontSize="11" Foreground="#9a9996" Margin="0,4,0,0"/>
<TextBlock Text="{Binding Description}" FontSize="11" Foreground="#9a9996" Margin="0,2,0,0" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<!-- 无结果提示 -->
<TextBlock Name="NoResultsText"
Text="未找到相关设置项"
FontSize="14"
Foreground="#9a9996"
HorizontalAlignment="Center"
Margin="0,40,0,0"
Visibility="Collapsed"/>
</StackPanel>
</ScrollViewer>
</Grid>
</UserControl>
@@ -0,0 +1,418 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.International.Converters.PinYinConverter;
namespace Ink_Canvas.Windows.SettingsViews
{
/// <summary>
/// SearchPanel.xaml 的交互逻辑
/// </summary>
public partial class SearchPanel : UserControl
{
public event EventHandler<string> NavigateToItem;
public event EventHandler CloseSearch;
private ObservableCollection<SearchResultItem> _exactMatches = new ObservableCollection<SearchResultItem>();
private ObservableCollection<SearchResultItem> _fuzzyMatches = new ObservableCollection<SearchResultItem>();
private ObservableCollection<SearchResultItem> _relatedItems = new ObservableCollection<SearchResultItem>();
private List<SettingItem> _allSettings = new List<SettingItem>();
public SearchPanel()
{
InitializeComponent();
InitializeSettings();
ExactMatchItemsControl.ItemsSource = _exactMatches;
FuzzyMatchItemsControl.ItemsSource = _fuzzyMatches;
RelatedItemsControl.ItemsSource = _relatedItems;
}
private void InitializeSettings()
{
// 初始化所有设置项数据
_allSettings = new List<SettingItem>
{
// 启动时行为
new SettingItem { Title = "启动时行为", Category = "启动时行为", ItemName = "StartupItem", Type = SettingItemType.Category },
// 画板和墨迹
new SettingItem { Title = "画板和墨迹", Category = "画板和墨迹", ItemName = "CanvasAndInkItem", Type = SettingItemType.Category },
// 手势操作
new SettingItem { Title = "手势操作", Category = "手势操作", ItemName = "GesturesItem", Type = SettingItemType.Category },
// 墨迹纠正
new SettingItem { Title = "墨迹纠正", Category = "墨迹纠正", ItemName = "InkRecognitionItem", Type = SettingItemType.Category },
// 个性化设置
new SettingItem { Title = "个性化设置", Category = "个性化设置", ItemName = "ThemeItem", Type = SettingItemType.Category },
// 快捷键设置
new SettingItem { Title = "快捷键设置", Category = "快捷键设置", ItemName = "ShortcutsItem", Type = SettingItemType.Category },
// 崩溃处理
new SettingItem { Title = "崩溃处理", Category = "崩溃处理", ItemName = "CrashActionItem", Type = SettingItemType.Category },
// PowerPoint 支持
new SettingItem { Title = "PowerPoint 支持", Category = "PowerPoint 支持", ItemName = "PowerPointItem", Type = SettingItemType.Category },
// 自动化行为
new SettingItem { Title = "自动化行为", Category = "自动化行为", ItemName = "AutomationItem", Type = SettingItemType.Category },
// 随机点名
new SettingItem { Title = "随机点名", Category = "随机点名", ItemName = "LuckyRandomItem", Type = SettingItemType.Category },
// 存储空间
new SettingItem { Title = "存储空间", Category = "存储空间", ItemName = "StorageItem", Type = SettingItemType.Category },
// 截图和屏幕捕捉
new SettingItem { Title = "截图和屏幕捕捉", Category = "截图和屏幕捕捉", ItemName = "SnapshotItem", Type = SettingItemType.Category },
// 高级选项
new SettingItem { Title = "高级选项", Category = "高级选项", ItemName = "AdvancedItem", Type = SettingItemType.Category },
// 关于
new SettingItem { Title = "关于 InkCanvasForClass", Category = "关于", ItemName = "AboutItem", Type = SettingItemType.Category },
};
}
public void PerformSearch(string searchText)
{
if (string.IsNullOrWhiteSpace(searchText))
{
ClearResults();
return;
}
_exactMatches.Clear();
_fuzzyMatches.Clear();
_relatedItems.Clear();
var searchLower = searchText.ToLower();
var exactMatchSet = new HashSet<string>();
var fuzzyMatchSet = new HashSet<string>();
// 精准匹配和拼音匹配
foreach (var setting in _allSettings)
{
var titleLower = setting.Title.ToLower();
var categoryLower = setting.Category.ToLower();
// 精准匹配
if (titleLower.Contains(searchLower) || categoryLower.Contains(searchLower))
{
if (!exactMatchSet.Contains(setting.ItemName))
{
_exactMatches.Add(new SearchResultItem
{
Title = setting.Title,
Category = setting.Category,
ItemName = setting.ItemName,
Type = setting.Type,
MatchType = MatchType.Exact
});
exactMatchSet.Add(setting.ItemName);
}
}
// 拼音匹配
else if (ContainsPinyinMatch(setting.Title, searchText) || ContainsPinyinMatch(setting.Category, searchText))
{
if (!exactMatchSet.Contains(setting.ItemName))
{
_exactMatches.Add(new SearchResultItem
{
Title = setting.Title,
Category = setting.Category,
ItemName = setting.ItemName,
Type = setting.Type,
MatchType = MatchType.Pinyin
});
exactMatchSet.Add(setting.ItemName);
}
}
}
// 模糊匹配
foreach (var setting in _allSettings)
{
if (exactMatchSet.Contains(setting.ItemName))
continue;
var searchableText = $"{setting.Title} {setting.Category} {setting.Description}".ToLower();
if (FuzzyMatch(searchableText, searchLower))
{
if (!fuzzyMatchSet.Contains(setting.ItemName))
{
_fuzzyMatches.Add(new SearchResultItem
{
Title = setting.Title,
Category = setting.Category,
ItemName = setting.ItemName,
Type = setting.Type,
MatchType = MatchType.Fuzzy
});
fuzzyMatchSet.Add(setting.ItemName);
}
}
}
// 相关项
var allMatched = new HashSet<string>(exactMatchSet.Concat(fuzzyMatchSet));
foreach (var setting in _allSettings)
{
if (!allMatched.Contains(setting.ItemName))
{
// 简单的相关性判断
if (IsRelated(setting, searchText))
{
_relatedItems.Add(new SearchResultItem
{
Title = setting.Title,
Category = setting.Category,
ItemName = setting.ItemName,
Type = setting.Type,
MatchType = MatchType.Related
});
}
}
}
UpdateResultsVisibility();
}
private bool ContainsPinyinMatch(string text, string search)
{
if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(search))
return false;
try
{
// 将搜索词转换为小写
var searchLower = search.ToLower();
// 获取文本的拼音首字母和全拼
var pinyinInitials = GetPinyinInitials(text);
var pinyinFull = GetPinyinFull(text);
// 检查搜索词是否匹配拼音首字母或全拼
if (pinyinInitials.ToLower().Contains(searchLower) ||
pinyinFull.ToLower().Contains(searchLower))
{
return true;
}
}
catch
{
// 如果拼音转换失败,返回false
return false;
}
return false;
}
private string GetPinyinInitials(string text)
{
var sb = new StringBuilder();
foreach (char c in text)
{
if (IsChinese(c))
{
try
{
var chineseChar = new ChineseChar(c);
if (chineseChar.PinyinCount > 0)
{
var pinyin = chineseChar.Pinyins[0];
if (!string.IsNullOrEmpty(pinyin) && pinyin.Length > 0)
{
// 获取首字母(移除音调数字后取第一个字母)
var firstChar = Regex.Replace(pinyin, @"\d", "")[0];
sb.Append(firstChar);
}
}
}
catch
{
sb.Append(c);
}
}
else
{
sb.Append(c);
}
}
return sb.ToString();
}
private string GetPinyinFull(string text)
{
var sb = new StringBuilder();
foreach (char c in text)
{
if (IsChinese(c))
{
try
{
var chineseChar = new ChineseChar(c);
if (chineseChar.PinyinCount > 0)
{
var pinyin = chineseChar.Pinyins[0];
// 移除音调数字
pinyin = Regex.Replace(pinyin, @"\d", "");
sb.Append(pinyin);
}
}
catch
{
sb.Append(c);
}
}
else
{
sb.Append(c);
}
}
return sb.ToString();
}
private bool IsChinese(char c)
{
return c >= 0x4e00 && c <= 0x9fbb;
}
private bool FuzzyMatch(string text, string search)
{
if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(search))
return false;
// 简单的模糊匹配:检查搜索词的字符是否按顺序出现在文本中
int searchIndex = 0;
foreach (char c in text)
{
if (searchIndex < search.Length && c == search[searchIndex])
{
searchIndex++;
if (searchIndex == search.Length)
return true;
}
}
return false;
}
private bool IsRelated(SettingItem setting, string search)
{
// 简单的相关性判断,可以根据需要改进
// 例如:检查是否有共同的关键词等
return false; // 暂时禁用相关项功能
}
private void UpdateResultsVisibility()
{
ExactMatchPanel.Visibility = _exactMatches.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
FuzzyMatchPanel.Visibility = _fuzzyMatches.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
RelatedItemsPanel.Visibility = _relatedItems.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
bool hasResults = _exactMatches.Count > 0 || _fuzzyMatches.Count > 0 || _relatedItems.Count > 0;
NoResultsText.Visibility = hasResults ? Visibility.Collapsed : Visibility.Visible;
}
private void ClearResults()
{
_exactMatches.Clear();
_fuzzyMatches.Clear();
_relatedItems.Clear();
UpdateResultsVisibility();
}
private void SearchTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
CloseSearch?.Invoke(this, EventArgs.Empty);
}
}
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var textBox = sender as TextBox;
if (textBox != null)
{
PerformSearch(textBox.Text);
}
}
private void SearchTextBox_GotFocus(object sender, RoutedEventArgs e)
{
var textBox = sender as TextBox;
textBox?.SelectAll();
}
private void CloseSearchButton_Click(object sender, MouseButtonEventArgs e)
{
CloseSearch?.Invoke(this, EventArgs.Empty);
}
private void SearchResultItem_Click(object sender, MouseButtonEventArgs e)
{
var border = sender as Border;
if (border?.Tag is SearchResultItem item)
{
NavigateToItem?.Invoke(this, item.ItemName);
}
}
public void FocusSearchBox()
{
SearchTextBox.Focus();
}
public void SetSearchText(string text)
{
SearchTextBox.Text = text;
PerformSearch(text);
}
}
public class SettingItem
{
public string Title { get; set; }
public string Category { get; set; }
public string ItemName { get; set; }
public string Description { get; set; } = "";
public SettingItemType Type { get; set; }
}
public class SearchResultItem
{
public string Title { get; set; }
public string Category { get; set; }
public string ItemName { get; set; }
public SettingItemType Type { get; set; }
public MatchType MatchType { get; set; }
public string Description { get; set; } = "";
}
public enum SettingItemType
{
Category,
Setting
}
public enum MatchType
{
Exact,
Pinyin,
Fuzzy,
Related
}
}
@@ -622,6 +622,11 @@
<settingsViews:AdvancedPanel x:Name="AdvancedPanel"/>
</Grid>
<!--SearchPanel-->
<Grid Margin="0,0,0,0" Visibility="Collapsed" Name="SearchPane" ZIndex="1000">
<settingsViews:SearchPanel x:Name="SearchPanelControl"/>
</Grid>
<!--Topbar-->
<Grid Height="48" VerticalAlignment="Top" Margin="250,0,0,0">
<Border Height="48" CornerRadius="0,6,0,0" Background="#fafafa">
@@ -18,6 +18,10 @@ namespace Ink_Canvas.Windows
public SettingsWindow()
{
InitializeComponent();
// 初始化搜索面板事件
SearchPanelControl.NavigateToItem += SearchPanel_NavigateToItem;
SearchPanelControl.CloseSearch += SearchPanel_CloseSearch;
// 初始化侧边栏项目
SidebarItemsControl.ItemsSource = SidebarItems;
@@ -445,7 +449,43 @@ namespace Ink_Canvas.Windows
private void SearchButton_Click(object sender, MouseButtonEventArgs e)
{
// 搜索功能 - 可以显示搜索框或搜索对话框
// 显示搜索界面
SearchPane.Visibility = Visibility.Visible;
SearchPanelControl.FocusSearchBox();
}
private void SearchPanel_NavigateToItem(object sender, string itemName)
{
// 隐藏搜索界面
SearchPane.Visibility = Visibility.Collapsed;
// 导航到对应的设置项
NavigateToSidebarItem(itemName);
}
private void SearchPanel_CloseSearch(object sender, EventArgs e)
{
// 隐藏搜索界面
SearchPane.Visibility = Visibility.Collapsed;
}
private void NavigateToSidebarItem(string itemName)
{
// 查找对应的侧边栏项并选中
foreach (var item in SidebarItems)
{
if (item.Name == itemName)
{
SelectSidebarItem(item);
break;
}
}
}
private void SelectSidebarItem(SidebarItem item)
{
_selectedSidebarItemName = item.Name;
UpdateSidebarItemsSelection();
}
private void MenuButton_Click(object sender, MouseButtonEventArgs e)