Files
community/Ink Canvas/Windows/SettingsViews/SettingsWindow.xaml.cs
T

570 lines
20 KiB
C#
Raw Normal View History

2026-04-05 21:50:53 +08:00
using Ink_Canvas.Windows.SettingsViews.Pages;
2026-03-29 12:38:46 +08:00
using iNKORE.UI.WPF.Modern.Controls;
using System;
using System.Collections.Generic;
2026-04-08 00:00:32 +08:00
using System.Linq;
2026-03-29 12:38:46 +08:00
using System.Windows;
using System.Windows.Interop;
2026-04-08 00:00:32 +08:00
using System.Windows.Navigation;
2026-04-30 14:38:23 +08:00
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
using Screen = System.Windows.Forms.Screen;
2026-03-29 12:38:46 +08:00
2026-04-05 21:50:53 +08:00
namespace Ink_Canvas.Windows.SettingsViews
2026-03-29 12:38:46 +08:00
{
2026-04-05 21:50:53 +08:00
public partial class SettingsWindow : Window
2026-03-29 12:38:46 +08:00
{
2026-04-10 19:49:22 +08:00
private Dictionary<string, Type> _pageTypes;
private readonly Dictionary<string, object> _pages = new Dictionary<string, object>();
2026-04-10 01:24:57 +08:00
private readonly Dictionary<string, Ink_Canvas.Plugins.PluginInfo> _pluginPages = new Dictionary<string, Ink_Canvas.Plugins.PluginInfo>();
2026-04-08 00:00:32 +08:00
2026-04-04 00:54:04 +08:00
// 保存窗口原始位置和大小
private double _originalLeft;
private double _originalTop;
private double _originalWidth;
private double _originalHeight;
2026-04-08 00:00:32 +08:00
2026-04-04 11:33:47 +08:00
// 标记窗口是否曾经最大化过
private bool _wasMaximized = false;
2026-03-29 12:38:46 +08:00
private bool _isNavigating = false;
2026-05-01 20:21:29 +08:00
private bool _updateBadgeDismissed = false;
2026-04-05 21:50:53 +08:00
public SettingsWindow()
2026-03-29 12:38:46 +08:00
{
InitializeComponent();
ApplyCurrentTheme();
// 初始化内置页面映射
2026-03-29 12:38:46 +08:00
_pageTypes = new Dictionary<string, Type>
{
2026-04-04 18:45:59 +08:00
{ "HomePage", typeof(HomePage) },
2026-04-05 17:27:00 +08:00
{ "StartupPage", typeof(StartupPage) },
2026-04-24 10:24:20 +08:00
{ "PrivacyPage", typeof(PrivacyPage) },
2026-05-01 01:22:35 +08:00
{ "SecurityPage", typeof(SecurityPage) },
2026-04-24 10:24:20 +08:00
{ "WindowPage", typeof(WindowPage) },
{ "AppearancePage", typeof(AppearancePage) },
2026-05-01 17:36:53 +08:00
{ "HotkeyPage", typeof(HotkeyPage) },
2026-04-19 14:42:11 +08:00
{ "UpdatePage", typeof(UpdatePage) },
{ "ExperimentalPage", typeof(ExperimentalPage) },
{ "AdvancedPage", typeof(AdvancedPage) },
2026-05-01 01:00:12 +08:00
{ "StoragePage", typeof(StoragePage) },
2026-04-26 09:12:26 +08:00
{ "AutomationPage", typeof(AutomationPage) },
{ "PowerPointPage", typeof(PowerPointPage) },
{ "RandomDrawPage", typeof(RandomDrawPage) },
2026-04-19 14:42:11 +08:00
{ "CanvasPage", typeof(CanvasPage) },
{ "InkRecognitionPage", typeof(InkRecognitionPage) },
2026-05-01 00:27:29 +08:00
{ "DebugPage", typeof(DebugPage) },
2026-04-04 18:45:59 +08:00
{ "AboutPage", typeof(AboutPage) },
2026-04-10 01:24:57 +08:00
{ "Settings", typeof(SettingsPage) },
{ "PluginPage", typeof(PluginPage) },
{ "PluginSettingsPage", typeof(PluginSettingsPage) }
2026-03-29 12:38:46 +08:00
};
2026-04-04 18:45:59 +08:00
// 默认选中首页
2026-03-29 12:38:46 +08:00
if (NavigationViewControl.MenuItems.Count > 0)
{
2026-04-04 18:45:59 +08:00
NavigateToPage("HomePage");
2026-03-29 12:38:46 +08:00
NavigationViewControl.SelectedItem = NavigationViewControl.MenuItems[0];
2026-04-04 18:45:59 +08:00
NavigationViewControl.Header = "首页";
2026-03-29 12:38:46 +08:00
}
2026-04-01 13:56:46 +08:00
2026-04-04 18:45:59 +08:00
UpdateAppTitleBarMargin();
2026-04-10 19:49:22 +08:00
// 窗口生命周期事件
2026-04-01 13:56:46 +08:00
this.Loaded += (sender, e) =>
{
SetMaxSizeAndCenter();
RegisterDpiChangedListener();
2026-04-10 01:24:57 +08:00
LoadPluginSettingsPages();
2026-05-01 20:21:29 +08:00
UpdateUpdateBadgeVisibility();
};
this.Closed += (sender, e) =>
{
UnregisterDpiChangedListener();
_pages.Clear();
_pageTypes.Clear();
};
// 修复触摸屏操作后鼠标指针消失的问题
2026-04-10 19:49:22 +08:00
this.TouchUp += (s, e) => ShowCursor(true);
this.MouseEnter += (s, e) => ShowCursor(true);
this.Activated += (s, e) => ShowCursor(true);
2026-04-04 00:54:04 +08:00
// 窗口状态改变时调整大小限制
this.StateChanged += (sender, e) =>
{
if (this.WindowState == WindowState.Maximized)
{
_originalLeft = this.Left;
_originalTop = this.Top;
_originalWidth = this.Width;
_originalHeight = this.Height;
2026-04-04 11:33:47 +08:00
_wasMaximized = true;
2026-04-04 00:54:04 +08:00
this.MaxWidth = double.PositiveInfinity;
this.MaxHeight = double.PositiveInfinity;
}
2026-04-04 11:33:47 +08:00
else if (this.WindowState == WindowState.Normal && _wasMaximized)
2026-04-04 00:54:04 +08:00
{
this.Left = _originalLeft;
this.Top = _originalTop;
this.Width = _originalWidth;
this.Height = _originalHeight;
2026-04-04 11:33:47 +08:00
_wasMaximized = false;
2026-04-04 00:54:04 +08:00
SetMaxSizeOnly();
}
2026-04-04 11:33:47 +08:00
else if (this.WindowState == WindowState.Normal)
{
SetMaxSizeOnly();
}
2026-04-04 18:45:59 +08:00
UpdateAppTitleBarMargin();
};
2026-04-08 00:00:32 +08:00
2026-04-04 18:45:59 +08:00
this.SizeChanged += (sender, e) =>
{
if (NavigationViewControl.DisplayMode == NavigationViewDisplayMode.Minimal)
{
UpdateAppTitleBarMargin();
}
2026-04-04 00:54:04 +08:00
};
}
public void RefreshTheme()
{
ApplyCurrentTheme();
}
private void ApplyCurrentTheme()
{
try
{
int themeIndex = Helpers.SettingsManager.Settings.Appearance.Theme;
var elementTheme = themeIndex switch
{
0 => iNKORE.UI.WPF.Modern.ElementTheme.Light,
1 => iNKORE.UI.WPF.Modern.ElementTheme.Dark,
2026-05-01 09:31:10 +08:00
_ => IsSystemThemeLight() ? iNKORE.UI.WPF.Modern.ElementTheme.Light : iNKORE.UI.WPF.Modern.ElementTheme.Dark,
};
iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, elementTheme);
}
catch { }
}
2026-05-01 09:31:10 +08:00
private static bool IsSystemThemeLight()
{
try
{
using (var themeKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(
@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"))
{
if (themeKey?.GetValue("SystemUsesLightTheme") is int v) return v == 1;
}
}
catch { }
return false;
}
#region
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int ShowCursor(bool bShow);
#endregion
#region DPI/
2026-04-08 00:00:32 +08:00
2026-04-04 00:54:04 +08:00
/// <summary>
/// 获取当前窗口所在屏幕的工作区尺寸(DIP单位)
/// </summary>
private void GetWorkAreaSize(out double workAreaWidthDip, out double workAreaHeightDip, out double screenLeftDip, out double screenTopDip)
2026-04-01 13:56:46 +08:00
{
2026-04-04 00:54:04 +08:00
// 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);
2026-04-01 13:56:46 +08:00
double dpiScaleX = 1.0;
double dpiScaleY = 1.0;
if (source?.CompositionTarget != null)
2026-04-01 13:56:46 +08:00
{
dpiScaleX = source.CompositionTarget.TransformToDevice.M11;
dpiScaleY = source.CompositionTarget.TransformToDevice.M22;
}
// 3. 物理像素 → WPF设备无关像素(DIP)转换
2026-04-04 00:54:04 +08:00
workAreaWidthDip = workingArea.Width / dpiScaleX;
workAreaHeightDip = workingArea.Height / dpiScaleY;
screenLeftDip = screenBounds.Left / dpiScaleX;
screenTopDip = screenBounds.Top / dpiScaleY;
}
2026-04-04 00:54:04 +08:00
private void SetMaxSizeAndCenter()
{
if (!this.IsLoaded) return;
GetWorkAreaSize(out double workAreaWidthDip, out double workAreaHeightDip, out double screenLeftDip, out double screenTopDip);
// 设置窗口最大尺寸
2026-04-03 19:30:44 +08:00
this.MaxWidth = workAreaWidthDip;
this.MaxHeight = workAreaHeightDip;
2026-04-04 00:54:04 +08:00
// 窗口在当前屏幕居中(解决副屏居中跑偏问题)
this.Left = screenLeftDip + (workAreaWidthDip - this.ActualWidth) / 2;
this.Top = screenTopDip + (workAreaHeightDip - this.ActualHeight) / 2;
}
2026-04-04 00:54:04 +08:00
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;
2026-03-29 12:38:46 +08:00
}
#endregion
#endregion
2026-03-29 12:38:46 +08:00
#region
2026-03-29 12:38:46 +08:00
private void OnNavigationViewSelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
if (_isNavigating)
{
return;
}
2026-03-29 12:38:46 +08:00
if (args.IsSettingsSelected)
{
2026-03-29 14:21:27 +08:00
NavigateToPage("Settings");
NavigationViewControl.Header = "设置";
return;
2026-03-29 12:38:46 +08:00
}
// 处理普通导航项
if (args.SelectedItem is NavigationViewItem selectedItem)
2026-03-29 12:38:46 +08:00
{
string tag = selectedItem.Tag as string;
if (!string.IsNullOrEmpty(tag) && _pageTypes.ContainsKey(tag))
2026-03-29 12:38:46 +08:00
{
Ink_Canvas.Plugins.PluginInfo pluginInfo = null;
_pluginPages.TryGetValue(tag, out pluginInfo);
object cachedPage = null;
_pages.TryGetValue(tag, out cachedPage);
if (cachedPage == null || rootFrame.Content != cachedPage)
2026-04-10 01:24:57 +08:00
{
NavigateToPage(tag, pluginInfo);
2026-04-10 01:24:57 +08:00
}
else if (cachedPage is PluginSettingsPage pluginSettingsPage && pluginInfo != null)
2026-04-01 13:56:46 +08:00
{
pluginSettingsPage.CurrentPlugin = pluginInfo;
2026-04-01 13:56:46 +08:00
}
NavigationViewControl.Header = selectedItem.Content;
2026-05-01 20:21:29 +08:00
if (tag == "UpdatePage")
{
_updateBadgeDismissed = true;
UpdateUpdateBadgeVisibility();
}
2026-03-29 12:38:46 +08:00
}
}
}
public void NavigateToPage(string pageTag, Ink_Canvas.Plugins.PluginInfo pluginInfo = null)
2026-03-29 12:38:46 +08:00
{
if (!_pageTypes.TryGetValue(pageTag, out Type pageType)) return;
try
2026-03-29 12:38:46 +08:00
{
_isNavigating = true;
if (!_pages.TryGetValue(pageTag, out var cachedPage))
{
cachedPage = Activator.CreateInstance(pageType);
_pages.Add(pageTag, cachedPage);
2026-03-29 12:38:46 +08:00
}
if (cachedPage is PluginSettingsPage pluginSettingsPage && pluginInfo != null)
{
pluginSettingsPage.CurrentPlugin = pluginInfo;
}
rootFrame.Navigate(cachedPage);
}
catch (Exception ex)
{
MessageBox.Show($"导航到页面时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
2026-03-29 12:38:46 +08:00
}
finally
{
_isNavigating = false;
}
2026-03-29 12:38:46 +08:00
}
private void OnNavigationViewBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args)
{
if (rootFrame.CanGoBack) rootFrame.GoBack();
}
private void OnRootFrameNavigated(object sender, NavigationEventArgs e)
2026-03-29 12:38:46 +08:00
{
if (_isNavigating)
{
return;
}
Type currentPageType = rootFrame.SourcePageType;
// 处理设置项的选中状态
if (currentPageType == typeof(SettingsPage))
{
NavigationViewControl.SelectedItem = NavigationViewControl.SettingsItem;
NavigationViewControl.Header = "设置";
return;
}
2026-03-29 12:38:46 +08:00
// 同步其他页面的选中状态
foreach (var kvp in _pageTypes)
2026-03-29 12:38:46 +08:00
{
if (kvp.Value == currentPageType)
2026-03-29 12:38:46 +08:00
{
var targetItem = FindNavigationViewItemByTag(kvp.Key);
if (targetItem != null && NavigationViewControl.SelectedItem != targetItem)
2026-03-29 12:38:46 +08:00
{
NavigationViewControl.SelectedItem = targetItem;
NavigationViewControl.Header = targetItem.Content;
2026-03-29 12:38:46 +08:00
}
break;
2026-03-29 12:38:46 +08:00
}
}
}
2026-04-04 18:45:59 +08:00
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);
2026-04-08 00:00:32 +08:00
2026-04-04 18:45:59 +08:00
// 当窗口宽度非常小时,隐藏图标和应用设置文字
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)
2026-03-29 12:38:46 +08:00
{
// 遍历主菜单
foreach (var item in NavigationViewControl.MenuItems)
2026-03-29 12:38:46 +08:00
{
if (item is NavigationViewItem navItem)
{
if (navItem.Tag as string == tag)
return navItem;
// 遍历子菜单,自动展开父项
foreach (var childItem in navItem.MenuItems)
2026-03-29 12:38:46 +08:00
{
if (childItem is NavigationViewItem childNavItem && childNavItem.Tag as string == tag)
2026-03-29 12:38:46 +08:00
{
navItem.IsExpanded = true;
return childNavItem;
2026-03-29 12:38:46 +08:00
}
}
}
}
2026-03-29 12:38:46 +08:00
// 遍历底部菜单
foreach (var item in NavigationViewControl.FooterMenuItems)
{
if (item is NavigationViewItem navItem && navItem.Tag as string == tag)
{
return navItem;
}
2026-03-29 12:38:46 +08:00
}
return null;
2026-03-29 12:38:46 +08:00
}
#endregion
2026-03-29 13:47:19 +08:00
#region
private void OnControlsSearchBoxQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
2026-03-29 13:47:19 +08:00
{
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)
2026-03-29 13:47:19 +08:00
{
NavigationViewControl.SelectedItem = targetItem;
2026-03-29 13:47:19 +08:00
}
}
private void OnControlsSearchBoxTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
2026-03-29 13:47:19 +08:00
{
if (args.Reason != AutoSuggestionBoxTextChangeReason.UserInput) return;
string query = sender.Text.Trim().ToLower();
var suggestions = new List<string>();
if (!string.IsNullOrEmpty(query))
2026-03-29 13:47:19 +08:00
{
var allNavItems = GetAllNavigationItems();
suggestions = allNavItems
.Where(item => item.Content?.ToString().ToLower().Contains(query) == true)
.Select(item => item.Content.ToString())
.ToList();
2026-03-29 13:47:19 +08:00
}
sender.ItemsSource = suggestions;
2026-03-29 13:47:19 +08:00
}
2026-04-05 21:35:40 +08:00
// 统一获取所有导航项(主菜单+子菜单+底部菜单)
private List<NavigationViewItem> GetAllNavigationItems()
2026-03-29 13:47:19 +08:00
{
var items = new List<NavigationViewItem>();
// 主菜单+子菜单
foreach (var item in NavigationViewControl.MenuItems)
2026-03-29 13:47:19 +08:00
{
if (item is NavigationViewItem navItem)
{
items.Add(navItem);
foreach (var child in navItem.MenuItems)
2026-03-29 13:47:19 +08:00
{
if (child is NavigationViewItem childNavItem)
items.Add(childNavItem);
2026-03-29 13:47:19 +08:00
}
}
}
// 底部菜单
foreach (var item in NavigationViewControl.FooterMenuItems)
2026-03-29 14:21:27 +08:00
{
if (item is NavigationViewItem navItem)
items.Add(navItem);
2026-03-29 14:21:27 +08:00
}
return items;
2026-03-29 13:47:19 +08:00
}
2026-04-10 01:24:57 +08:00
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);
2026-04-30 14:29:06 +08:00
2026-04-10 01:24:57 +08:00
_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
2026-04-24 07:13:27 +08:00
public NavigationView GetNavigationView()
{
return NavigationViewControl;
}
2026-05-01 20:21:29 +08:00
public void UpdateUpdateBadgeVisibility()
{
try
{
var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
bool hasUpdate = mainWindow != null && !string.IsNullOrEmpty(mainWindow.AvailableLatestVersion);
var item = FindNavigationViewItemByTag("UpdatePage");
var badge = item?.InfoBadge;
if (badge != null)
{
badge.Visibility = (hasUpdate && !_updateBadgeDismissed) ? Visibility.Visible : Visibility.Collapsed;
}
}
catch { }
}
2026-03-29 12:38:46 +08:00
}
}