ef5377f85c
重构主题和语言设置功能,将相关代码从主窗口迁移至新增的个性化设置页面 优化浮动工具栏图标选择逻辑,移除冗余代码 统一设置页面中开关控件的样式和行为 修复设置页面导航项的选择状态问题
495 lines
18 KiB
C#
495 lines
18 KiB
C#
using Ink_Canvas.Windows.SettingsViews.Pages;
|
||
using iNKORE.UI.WPF.Modern.Controls;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Windows;
|
||
using System.Windows.Interop;
|
||
using System.Windows.Navigation;
|
||
using System.Windows.Media;
|
||
using MessageBox = System.Windows.MessageBox;
|
||
using Screen = System.Windows.Forms.Screen;
|
||
|
||
namespace Ink_Canvas.Windows.SettingsViews
|
||
{
|
||
public partial class SettingsWindow : Window
|
||
{
|
||
private Dictionary<string, Type> _pageTypes;
|
||
private readonly Dictionary<string, object> _pages = new Dictionary<string, object>();
|
||
private readonly Dictionary<string, Ink_Canvas.Plugins.PluginInfo> _pluginPages = new Dictionary<string, Ink_Canvas.Plugins.PluginInfo>();
|
||
|
||
// 保存窗口原始位置和大小
|
||
private double _originalLeft;
|
||
private double _originalTop;
|
||
private double _originalWidth;
|
||
private double _originalHeight;
|
||
|
||
// 标记窗口是否曾经最大化过
|
||
private bool _wasMaximized = false;
|
||
|
||
private bool _isNavigating = false;
|
||
|
||
public SettingsWindow()
|
||
{
|
||
InitializeComponent();
|
||
|
||
// 初始化内置页面映射
|
||
_pageTypes = new Dictionary<string, Type>
|
||
{
|
||
{ "HomePage", typeof(HomePage) },
|
||
{ "StartupPage", typeof(StartupPage) },
|
||
{ "PrivacyPage", typeof(PrivacyPage) },
|
||
{ "WindowPage", typeof(WindowPage) },
|
||
{ "AppearancePage", typeof(AppearancePage) },
|
||
{ "UpdatePage", typeof(UpdatePage) },
|
||
{ "ExperimentalPage", typeof(ExperimentalPage) },
|
||
{ "CanvasPage", typeof(CanvasPage) },
|
||
{ "InkRecognitionPage", typeof(InkRecognitionPage) },
|
||
{ "DebugPage", typeof(IconographyPage) },
|
||
{ "AboutPage", typeof(AboutPage) },
|
||
{ "Settings", typeof(SettingsPage) },
|
||
{ "PluginPage", typeof(PluginPage) },
|
||
{ "PluginSettingsPage", typeof(PluginSettingsPage) }
|
||
};
|
||
|
||
// 默认选中首页
|
||
if (NavigationViewControl.MenuItems.Count > 0)
|
||
{
|
||
NavigateToPage("HomePage");
|
||
NavigationViewControl.SelectedItem = NavigationViewControl.MenuItems[0];
|
||
NavigationViewControl.Header = "首页";
|
||
}
|
||
|
||
UpdateAppTitleBarMargin();
|
||
|
||
// 窗口生命周期事件
|
||
this.Loaded += (sender, e) =>
|
||
{
|
||
SetMaxSizeAndCenter();
|
||
RegisterDpiChangedListener();
|
||
LoadPluginSettingsPages();
|
||
};
|
||
|
||
this.Closed += (sender, e) =>
|
||
{
|
||
UnregisterDpiChangedListener();
|
||
_pages.Clear();
|
||
_pageTypes.Clear();
|
||
};
|
||
|
||
// 修复触摸屏操作后鼠标指针消失的问题
|
||
this.TouchUp += (s, e) => ShowCursor(true);
|
||
this.MouseEnter += (s, e) => ShowCursor(true);
|
||
this.Activated += (s, e) => ShowCursor(true);
|
||
|
||
// 窗口状态改变时调整大小限制
|
||
this.StateChanged += (sender, e) =>
|
||
{
|
||
if (this.WindowState == WindowState.Maximized)
|
||
{
|
||
_originalLeft = this.Left;
|
||
_originalTop = this.Top;
|
||
_originalWidth = this.Width;
|
||
_originalHeight = this.Height;
|
||
_wasMaximized = true;
|
||
this.MaxWidth = double.PositiveInfinity;
|
||
this.MaxHeight = double.PositiveInfinity;
|
||
}
|
||
else if (this.WindowState == WindowState.Normal && _wasMaximized)
|
||
{
|
||
this.Left = _originalLeft;
|
||
this.Top = _originalTop;
|
||
this.Width = _originalWidth;
|
||
this.Height = _originalHeight;
|
||
_wasMaximized = false;
|
||
SetMaxSizeOnly();
|
||
}
|
||
else if (this.WindowState == WindowState.Normal)
|
||
{
|
||
SetMaxSizeOnly();
|
||
}
|
||
UpdateAppTitleBarMargin();
|
||
};
|
||
|
||
this.SizeChanged += (sender, e) =>
|
||
{
|
||
if (NavigationViewControl.DisplayMode == NavigationViewDisplayMode.Minimal)
|
||
{
|
||
UpdateAppTitleBarMargin();
|
||
}
|
||
};
|
||
}
|
||
|
||
#region 修复触摸屏鼠标指针消失问题
|
||
|
||
[System.Runtime.InteropServices.DllImport("user32.dll")]
|
||
private static extern int ShowCursor(bool bShow);
|
||
#endregion
|
||
|
||
#region 高DPI/多屏自适应窗口控制
|
||
|
||
/// <summary>
|
||
/// 获取当前窗口所在屏幕的工作区尺寸(DIP单位)
|
||
/// </summary>
|
||
private void GetWorkAreaSize(out double workAreaWidthDip, out double workAreaHeightDip, out double screenLeftDip, out double screenTopDip)
|
||
{
|
||
// 1. 获取窗口当前所在屏幕
|
||
var windowHandle = new WindowInteropHelper(this).Handle;
|
||
var currentScreen = Screen.FromHandle(windowHandle);
|
||
var workingArea = currentScreen.WorkingArea;
|
||
var screenBounds = currentScreen.Bounds;
|
||
|
||
// 2. 获取当前窗口的DPI缩放因子
|
||
var source = PresentationSource.FromVisual(this);
|
||
double dpiScaleX = 1.0;
|
||
double dpiScaleY = 1.0;
|
||
|
||
if (source?.CompositionTarget != null)
|
||
{
|
||
dpiScaleX = source.CompositionTarget.TransformToDevice.M11;
|
||
dpiScaleY = source.CompositionTarget.TransformToDevice.M22;
|
||
}
|
||
|
||
// 3. 物理像素 → WPF设备无关像素(DIP)转换
|
||
workAreaWidthDip = workingArea.Width / dpiScaleX;
|
||
workAreaHeightDip = workingArea.Height / dpiScaleY;
|
||
screenLeftDip = screenBounds.Left / dpiScaleX;
|
||
screenTopDip = screenBounds.Top / dpiScaleY;
|
||
}
|
||
|
||
private void SetMaxSizeAndCenter()
|
||
{
|
||
if (!this.IsLoaded) return;
|
||
|
||
GetWorkAreaSize(out double workAreaWidthDip, out double workAreaHeightDip, out double screenLeftDip, out double screenTopDip);
|
||
|
||
// 设置窗口最大尺寸
|
||
this.MaxWidth = workAreaWidthDip;
|
||
this.MaxHeight = workAreaHeightDip;
|
||
|
||
// 窗口在当前屏幕居中(解决副屏居中跑偏问题)
|
||
this.Left = screenLeftDip + (workAreaWidthDip - this.ActualWidth) / 2;
|
||
this.Top = screenTopDip + (workAreaHeightDip - this.ActualHeight) / 2;
|
||
}
|
||
|
||
private void SetMaxSizeOnly()
|
||
{
|
||
if (!this.IsLoaded) return;
|
||
|
||
GetWorkAreaSize(out double workAreaWidthDip, out double workAreaHeightDip, out _, out _);
|
||
|
||
// 只设置窗口最大尺寸,不改变窗口位置
|
||
this.MaxWidth = workAreaWidthDip;
|
||
this.MaxHeight = workAreaHeightDip;
|
||
}
|
||
|
||
#region DPI/系统缩放变化监听
|
||
private HwndSource _hwndSource;
|
||
private void RegisterDpiChangedListener()
|
||
{
|
||
_hwndSource = PresentationSource.FromVisual(this) as HwndSource;
|
||
_hwndSource?.AddHook(DpiChangedWndProc);
|
||
}
|
||
|
||
private void UnregisterDpiChangedListener()
|
||
{
|
||
_hwndSource?.RemoveHook(DpiChangedWndProc);
|
||
_hwndSource = null;
|
||
}
|
||
|
||
private IntPtr DpiChangedWndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||
{
|
||
const int WM_DPICHANGED = 0x02E0;
|
||
// 系统DPI/缩放变化时自动重新计算窗口参数
|
||
if (msg == WM_DPICHANGED)
|
||
{
|
||
SetMaxSizeAndCenter();
|
||
handled = true;
|
||
}
|
||
return IntPtr.Zero;
|
||
}
|
||
#endregion
|
||
#endregion
|
||
|
||
#region 导航逻辑优化(含页面缓存)
|
||
private void OnNavigationViewSelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
|
||
{
|
||
if (_isNavigating)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (args.IsSettingsSelected)
|
||
{
|
||
NavigateToPage("Settings");
|
||
NavigationViewControl.Header = "设置";
|
||
return;
|
||
}
|
||
|
||
// 处理普通导航项
|
||
if (args.SelectedItem is NavigationViewItem selectedItem)
|
||
{
|
||
string tag = selectedItem.Tag as string;
|
||
if (!string.IsNullOrEmpty(tag) && _pageTypes.ContainsKey(tag))
|
||
{
|
||
// 如果是插件设置页面,设置当前插件
|
||
if (_pluginPages.TryGetValue(tag, out var plugin))
|
||
{
|
||
PluginSettingsPage.CurrentPlugin = plugin;
|
||
}
|
||
|
||
// 避免重复导航到当前页面
|
||
if (rootFrame.SourcePageType != _pageTypes[tag])
|
||
{
|
||
NavigateToPage(tag);
|
||
}
|
||
NavigationViewControl.Header = selectedItem.Content;
|
||
}
|
||
}
|
||
}
|
||
|
||
public void NavigateToPage(string pageTag)
|
||
{
|
||
if (!_pageTypes.TryGetValue(pageTag, out Type pageType)) return;
|
||
|
||
try
|
||
{
|
||
_isNavigating = true;
|
||
|
||
if (!_pages.TryGetValue(pageTag, out var cachedPage))
|
||
{
|
||
cachedPage = Activator.CreateInstance(pageType);
|
||
_pages.Add(pageTag, cachedPage);
|
||
}
|
||
|
||
rootFrame.Navigate(cachedPage.GetType(), cachedPage);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"导航到页面时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
finally
|
||
{
|
||
_isNavigating = false;
|
||
}
|
||
}
|
||
|
||
private void OnNavigationViewBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args)
|
||
{
|
||
if (rootFrame.CanGoBack) rootFrame.GoBack();
|
||
}
|
||
|
||
private void OnRootFrameNavigated(object sender, NavigationEventArgs e)
|
||
{
|
||
if (_isNavigating)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Type currentPageType = rootFrame.SourcePageType;
|
||
|
||
// 处理设置项的选中状态
|
||
if (currentPageType == typeof(SettingsPage))
|
||
{
|
||
NavigationViewControl.SelectedItem = NavigationViewControl.SettingsItem;
|
||
NavigationViewControl.Header = "设置";
|
||
return;
|
||
}
|
||
|
||
// 同步其他页面的选中状态
|
||
foreach (var kvp in _pageTypes)
|
||
{
|
||
if (kvp.Value == currentPageType)
|
||
{
|
||
var targetItem = FindNavigationViewItemByTag(kvp.Key);
|
||
if (targetItem != null && NavigationViewControl.SelectedItem != targetItem)
|
||
{
|
||
NavigationViewControl.SelectedItem = targetItem;
|
||
NavigationViewControl.Header = targetItem.Content;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void NavigationViewControl_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
|
||
{
|
||
UpdateAppTitleBarMargin(sender);
|
||
}
|
||
|
||
private void UpdateAppTitleBarMargin()
|
||
{
|
||
UpdateAppTitleBarMargin(NavigationViewControl);
|
||
}
|
||
|
||
private void UpdateAppTitleBarMargin(NavigationView sender)
|
||
{
|
||
Thickness currMargin = AppTitleBar.Margin;
|
||
if (sender.DisplayMode == NavigationViewDisplayMode.Minimal)
|
||
{
|
||
AppTitleBar.Margin = new Thickness((sender.CompactPaneLength * 2), currMargin.Top, currMargin.Right, currMargin.Bottom);
|
||
|
||
// 当窗口宽度非常小时,隐藏图标和应用设置文字
|
||
if (this.ActualWidth < 400)
|
||
{
|
||
AppTitle.Visibility = Visibility.Collapsed;
|
||
}
|
||
else
|
||
{
|
||
AppTitle.Visibility = Visibility.Visible;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AppTitleBar.Margin = new Thickness(sender.CompactPaneLength, currMargin.Top, currMargin.Right, currMargin.Bottom);
|
||
AppTitle.Visibility = Visibility.Visible;
|
||
}
|
||
AppTitleBar.Visibility = sender.PaneDisplayMode == NavigationViewPaneDisplayMode.Top ? Visibility.Collapsed : Visibility.Visible;
|
||
}
|
||
|
||
private NavigationViewItem FindNavigationViewItemByTag(string tag)
|
||
{
|
||
// 遍历主菜单
|
||
foreach (var item in NavigationViewControl.MenuItems)
|
||
{
|
||
if (item is NavigationViewItem navItem)
|
||
{
|
||
if (navItem.Tag as string == tag)
|
||
return navItem;
|
||
|
||
// 遍历子菜单,自动展开父项
|
||
foreach (var childItem in navItem.MenuItems)
|
||
{
|
||
if (childItem is NavigationViewItem childNavItem && childNavItem.Tag as string == tag)
|
||
{
|
||
navItem.IsExpanded = true;
|
||
return childNavItem;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 遍历底部菜单
|
||
foreach (var item in NavigationViewControl.FooterMenuItems)
|
||
{
|
||
if (item is NavigationViewItem navItem && navItem.Tag as string == tag)
|
||
{
|
||
return navItem;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
#endregion
|
||
|
||
#region 搜索框逻辑优化
|
||
private void OnControlsSearchBoxQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(args.QueryText)) return;
|
||
|
||
string query = args.QueryText.Trim().ToLower();
|
||
var allNavItems = GetAllNavigationItems();
|
||
|
||
var targetItem = allNavItems.FirstOrDefault(item =>
|
||
item.Content?.ToString().ToLower().Contains(query) == true);
|
||
|
||
if (targetItem != null)
|
||
{
|
||
NavigationViewControl.SelectedItem = targetItem;
|
||
}
|
||
}
|
||
|
||
private void OnControlsSearchBoxTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
||
{
|
||
if (args.Reason != AutoSuggestionBoxTextChangeReason.UserInput) return;
|
||
|
||
string query = sender.Text.Trim().ToLower();
|
||
var suggestions = new List<string>();
|
||
|
||
if (!string.IsNullOrEmpty(query))
|
||
{
|
||
var allNavItems = GetAllNavigationItems();
|
||
suggestions = allNavItems
|
||
.Where(item => item.Content?.ToString().ToLower().Contains(query) == true)
|
||
.Select(item => item.Content.ToString())
|
||
.ToList();
|
||
}
|
||
|
||
sender.ItemsSource = suggestions;
|
||
}
|
||
|
||
// 统一获取所有导航项(主菜单+子菜单+底部菜单)
|
||
private List<NavigationViewItem> GetAllNavigationItems()
|
||
{
|
||
var items = new List<NavigationViewItem>();
|
||
|
||
// 主菜单+子菜单
|
||
foreach (var item in NavigationViewControl.MenuItems)
|
||
{
|
||
if (item is NavigationViewItem navItem)
|
||
{
|
||
items.Add(navItem);
|
||
foreach (var child in navItem.MenuItems)
|
||
{
|
||
if (child is NavigationViewItem childNavItem)
|
||
items.Add(childNavItem);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 底部菜单
|
||
foreach (var item in NavigationViewControl.FooterMenuItems)
|
||
{
|
||
if (item is NavigationViewItem navItem)
|
||
items.Add(navItem);
|
||
}
|
||
|
||
return items;
|
||
}
|
||
|
||
private void LoadPluginSettingsPages()
|
||
{
|
||
try
|
||
{
|
||
var pluginManager = Ink_Canvas.Plugins.PluginManager.Instance;
|
||
var plugins = pluginManager.Plugins;
|
||
|
||
foreach (var plugin in plugins)
|
||
{
|
||
var settingsView = plugin.Instance.GetSettingsView();
|
||
if (settingsView != null)
|
||
{
|
||
var pageTag = string.Format("PluginSettings_{0}", plugin.Id);
|
||
|
||
_pageTypes[pageTag] = typeof(PluginSettingsPage);
|
||
_pluginPages[pageTag] = plugin;
|
||
|
||
var navItem = new NavigationViewItem
|
||
{
|
||
Content = string.Format("{0} 设置", plugin.Name),
|
||
Tag = pageTag
|
||
};
|
||
|
||
navItem.Icon = new FontIcon
|
||
{
|
||
Glyph = "\uE713"
|
||
};
|
||
|
||
NavigationViewControl.MenuItems.Add(navItem);
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine(string.Format("加载插件设置页面时出错: {0}", ex.Message));
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
public NavigationView GetNavigationView()
|
||
{
|
||
return NavigationViewControl;
|
||
}
|
||
}
|
||
}
|