diff --git a/Ink Canvas/Helpers/PPTUIManager.cs b/Ink Canvas/Helpers/PPTUIManager.cs index aa91ab25..d500fd32 100644 --- a/Ink Canvas/Helpers/PPTUIManager.cs +++ b/Ink Canvas/Helpers/PPTUIManager.cs @@ -102,6 +102,7 @@ namespace Ink_Canvas.Helpers UpdateNavigationPanelsVisibility(); UpdateNavigationButtonStyles(); _mainWindow.UpdatePPTTimeCapsuleVisibility(); + _mainWindow.UpdatePPTQuickPanelVisibility(); if (MainWindow.Settings.Advanced.IsEnableAvoidFullScreenHelper) { // 设置为画板模式,允许全屏操作 @@ -122,6 +123,7 @@ namespace Ink_Canvas.Helpers _mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Collapsed; HideAllNavigationPanels(); _mainWindow.UpdatePPTTimeCapsuleVisibility(); + _mainWindow.UpdatePPTQuickPanelVisibility(); if (MainWindow.Settings.Advanced.IsEnableAvoidFullScreenHelper) { // 恢复为非画板模式,重新启用全屏限制 diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 0a8e1176..b8898875 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -10338,6 +10338,15 @@ Margin="0,20,20,0"> + + + + diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs index 0f0e9182..6ea57aa4 100644 --- a/Ink Canvas/MainWindow.xaml.cs +++ b/Ink Canvas/MainWindow.xaml.cs @@ -2710,10 +2710,11 @@ namespace Ink_Canvas Settings.PowerPointSettings.EnablePPTTimeCapsule = toggle != null && toggle.IsOn; SaveSettingsToFile(); - // 如果当前在PPT放映模式,需要立即更新时间胶囊的显示状态 + // 如果当前在PPT放映模式,需要立即更新时间胶囊和快捷面板的显示状态 if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) { UpdatePPTTimeCapsuleVisibility(); + UpdatePPTQuickPanelVisibility(); } LogHelper.WriteLogToFile($"PPT时间显示胶囊已{(Settings.PowerPointSettings.EnablePPTTimeCapsule ? "启用" : "禁用")}", LogHelper.LogType.Event); @@ -2799,6 +2800,33 @@ namespace Ink_Canvas } } + /// + /// 更新PPT快捷面板的显示状态 + /// + public void UpdatePPTQuickPanelVisibility() + { + try + { + if (PPTQuickPanelContainer == null || PPTQuickPanel == null) return; + + // 仅在PPT模式下显示 + if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + PPTQuickPanelContainer.Visibility = Visibility.Visible; + PPTQuickPanel?.UpdateVisibility(true); + } + else + { + PPTQuickPanelContainer.Visibility = Visibility.Collapsed; + PPTQuickPanel?.UpdateVisibility(false); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新PPT快捷面板显示状态时出错: {ex.Message}", LogHelper.LogType.Error); + } + } + /// /// 更新PPT时间胶囊的位置 /// diff --git a/Ink Canvas/Windows/PPTQuickPanel.xaml b/Ink Canvas/Windows/PPTQuickPanel.xaml new file mode 100644 index 00000000..3056766d --- /dev/null +++ b/Ink Canvas/Windows/PPTQuickPanel.xaml @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ink Canvas/Windows/PPTQuickPanel.xaml.cs b/Ink Canvas/Windows/PPTQuickPanel.xaml.cs new file mode 100644 index 00000000..1f5dc888 --- /dev/null +++ b/Ink Canvas/Windows/PPTQuickPanel.xaml.cs @@ -0,0 +1,826 @@ +using Ink_Canvas.Helpers; +using System; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Threading; +using Microsoft.Win32; +using SystemEvents = Microsoft.Win32.SystemEvents; + +namespace Ink_Canvas.Windows +{ + /// + /// PPT侧滑快捷面板 + /// + public partial class PPTQuickPanel : UserControl + { + // Windows Core Audio API + [ComImport] + [Guid("5CDF2C82-841E-4546-9722-0CF74078229A")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface IAudioEndpointVolume + { + int NotImpl1(); + int NotImpl2(); + int GetChannelCount(out int channelCount); + int SetMasterVolumeLevel(float level, Guid eventContext); + int SetMasterVolumeLevelScalar(float level, Guid eventContext); + int GetMasterVolumeLevel(out float level); + int GetMasterVolumeLevelScalar(out float level); + int SetChannelVolumeLevel(uint channelNumber, float level, Guid eventContext); + int SetChannelVolumeLevelScalar(uint channelNumber, float level, Guid eventContext); + int GetChannelVolumeLevel(uint channelNumber, out float level); + int GetChannelVolumeLevelScalar(uint channelNumber, out float level); + int SetMute([MarshalAs(UnmanagedType.Bool)] bool mute, Guid eventContext); + int GetMute(out bool mute); + } + + [ComImport] + [Guid("D666063F-1587-4E43-81F1-B948E807363F")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface IMMDevice + { + int Activate(ref Guid iid, int clsCtx, IntPtr activationParams, [MarshalAs(UnmanagedType.IUnknown)] out object interfacePointer); + int OpenPropertyStore(int stgmAccess, out IPropertyStore propertyStore); + int GetId([MarshalAs(UnmanagedType.LPWStr)] out string id); + int GetState(out int state); + } + + [ComImport] + [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface IMMDeviceEnumerator + { + int NotImpl1(); + int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice device); + } + + [ComImport] + [Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface IPropertyStore + { + int GetCount(out int propertyCount); + int GetAt(int propertyIndex, out Guid propertyKey); + int GetValue(ref Guid propertyKey, out object value); + int SetValue(ref Guid propertyKey, ref object value); + int Commit(); + } + + private static class MMDeviceEnumeratorFactory + { + [ComImport] + [Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] + private class MMDeviceEnumerator + { + } + + public static IMMDeviceEnumerator CreateInstance() + { + return new MMDeviceEnumerator() as IMMDeviceEnumerator; + } + } + + private const int DEVICE_STATE_ACTIVE = 1; + private const int eRender = 0; + private const int eConsole = 0; + + private IAudioEndpointVolume _audioEndpointVolume; + private bool _isExpanded = false; + private bool _isDragging = false; + private Point _dragStartPoint; + private double _panelWidth = 230; // 面板总宽度(30 + 200) + private double _collapsedOffset = 200; // 折叠时的偏移量(隐藏内容区域) + private MainWindow _mainWindow; + + public PPTQuickPanel() + { + InitializeComponent(); + InitializeAudio(); + ApplyTheme(); + + // 监听主题变化 + SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged; + + Loaded += PPTQuickPanel_Loaded; + Unloaded += PPTQuickPanel_Unloaded; + IsVisibleChanged += PPTQuickPanel_IsVisibleChanged; + } + + private void PPTQuickPanel_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if (Visibility == Visibility.Visible) + { + ApplyTheme(); + } + } + + private void PPTQuickPanel_Loaded(object sender, RoutedEventArgs e) + { + // 初始状态为折叠 + PanelTransform.X = _collapsedOffset; + UpdateArrowRotation(); + + // 获取MainWindow引用 + _mainWindow = Application.Current.MainWindow as MainWindow; + + // 延迟初始化音量显示,确保音频设备已初始化 + Dispatcher.BeginInvoke(new Action(() => + { + UpdateVolumeDisplay(); + }), DispatcherPriority.Loaded); + } + + private void PPTQuickPanel_Unloaded(object sender, RoutedEventArgs e) + { + SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged; + } + + private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + ApplyTheme(); + }), DispatcherPriority.Normal); + } + + #region 音频控制 + + private void InitializeAudio() + { + try + { + var deviceEnumerator = MMDeviceEnumeratorFactory.CreateInstance(); + IMMDevice device; + deviceEnumerator.GetDefaultAudioEndpoint(eRender, eConsole, out device); + + Guid IID_IAudioEndpointVolume = new Guid("5CDF2C82-841E-4546-9722-0CF74078229A"); + object interfacePointer; + device.Activate(ref IID_IAudioEndpointVolume, 0, IntPtr.Zero, out interfacePointer); + _audioEndpointVolume = interfacePointer as IAudioEndpointVolume; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"初始化音频控制失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private float GetVolume() + { + if (_audioEndpointVolume == null) return 0.5f; + try + { + float level; + _audioEndpointVolume.GetMasterVolumeLevelScalar(out level); + return level; + } + catch + { + return 0.5f; + } + } + + private void SetVolume(float volume) + { + if (_audioEndpointVolume == null) return; + try + { + volume = Math.Max(0f, Math.Min(1f, volume)); + _audioEndpointVolume.SetMasterVolumeLevelScalar(volume, Guid.Empty); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"设置音量失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private bool GetMute() + { + if (_audioEndpointVolume == null) return false; + try + { + bool mute; + _audioEndpointVolume.GetMute(out mute); + return mute; + } + catch + { + return false; + } + } + + private void SetMute(bool mute) + { + if (_audioEndpointVolume == null) return; + try + { + _audioEndpointVolume.SetMute(mute, Guid.Empty); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"设置静音失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private void UpdateVolumeDisplay() + { + if (_audioEndpointVolume == null) return; + + try + { + float volume = GetVolume(); + bool isMuted = GetMute(); + + // 更新滑块值(不触发事件) + VolumeSlider.ValueChanged -= VolumeSlider_ValueChanged; + VolumeSlider.Value = volume * 100; + VolumeSlider.ValueChanged += VolumeSlider_ValueChanged; + + // 更新文本显示 + VolumeValueText.Text = $"{(int)(volume * 100)}%"; + + // 更新图标 + UpdateVolumeIcon(isMuted, volume); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新音量显示失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private void UpdateVolumeIcon(bool isMuted, float volume) + { + // 清除描边 + VolumeIconPath.Stroke = null; + VolumeIconPath.StrokeThickness = 0; + VolumeIconPath2.Stroke = null; + VolumeIconPath2.StrokeThickness = 0; + + // 默认隐藏第二个Path + VolumeIconPath2.Visibility = Visibility.Collapsed; + + // 静音或音量为0%时显示静音图标 + if (isMuted || volume <= 0f) + { + // 静音图标:扬声器 + X + // 扬声器部分 + var speakerGeometry = Geometry.Parse("M 7,1.00772 C 6.70313,1.00381 6.42188,1.13272 6.23048,1.35928 L 3,4.99991 H 2 C 0.906251,4.99991 0,5.84366 0,6.99991 V 8.99991 C 0,10.0898 0.910157,10.9999 2,10.9999 H 3 L 6.23048,14.6405 C 6.44141,14.8944 6.72266,15.0038 7,14.9999 V 1.00772 Z"); + // X部分 + var xGeometry = Geometry.Parse("M 10,5.00012 C 9.73441,5.00012 9.4805,5.10559 9.293,5.29309 C 8.90237,5.68372 8.90237,6.31653 9.293,6.70715 L 10.586,8.00013 L 9.293,9.2931 C 8.90237,9.68372 8.90237,10.3165 9.293,10.7072 C 9.68362,11.0978 10.3164,11.0978 10.7071,10.7072 L 12,9.41419 L 13.293,10.7072 C 13.6836,11.0978 14.3164,11.0978 14.7071,10.7072 C 15.0977,10.3165 15.0977,9.68372 14.7071,9.2931 L 13.4141,8.00013 L 14.7071,6.70715 C 15.0977,6.31653 15.0977,5.68372 14.7071,5.29309 C 14.5196,5.10559 14.2657,5.00012 14,5.00012 C 13.7344,5.00012 13.4805,5.10559 13.293,5.29309 L 12,6.58606 L 10.7071,5.29309 C 10.5196,5.10559 10.2657,5.00012 10,5.00012 Z"); + + var group = new GeometryGroup(); + group.Children.Add(speakerGeometry); + group.Children.Add(xGeometry); + VolumeIconPath.Data = group; + VolumeIconPath2.Visibility = Visibility.Collapsed; + } + else if (volume >= 0.5f) + { + // 音量>=50%:扬声器 + 两条声波线 + // 扬声器部分 + var speakerGeometry = Geometry.Parse("M 7,0 C 6.70313,0 6.42188,0.125086 6.23048,0.35165 L 3,3.99228 H 2 C 0.906251,3.99228 0,4.83603 0,5.99228 V 7.99228 C 0,9.08213 0.910157,9.99228 2,9.99228 H 3 L 6.23048,13.6329 C 6.44141,13.8868 6.72266,13.9962 7,13.9923 V 0 Z"); + // 第一条声波线 + var wave1Geometry = Geometry.Parse("M 13.461,0.961025 C 13.2695,0.957119 13.0742,1.01571 12.9024,1.12899 C 12.4453,1.44149 12.3242,2.06259 12.6328,2.51962 C 14.457,5.22666 14.457,8.75791 12.6328,11.4649 C 12.3242,11.922 12.4453,12.5431 12.9024,12.8556 C 13.3594,13.1642 13.9805,13.0431 14.293,12.586 C 15.4297,10.8946 16,8.94541 16,6.99228 C 16,5.03915 15.4297,3.08993 14.293,1.39853 C 14.0977,1.11337 13.7813,0.961025 13.461,0.961025 Z"); + // 第二条声波线 + var wave2Geometry = Geometry.Parse("M 10.0391,2.98056 C 9.81642,2.97275 9.58595,3.03915 9.39454,3.18368 C 9.13282,3.3829 9.00001,3.68368 9.00001,3.98837 V 4.04697 C 9.01173,4.23837 9.07423,4.42197 9.19923,4.58212 C 10.2734,6.01181 10.2734,7.97275 9.19923,9.39853 C 9.07423,9.5626 9.01173,9.74619 9.00001,9.93369 V 9.99619 C 9.00001,10.3009 9.13282,10.6017 9.39454,10.8009 C 9.83595,11.1329 10.4609,11.0431 10.793,10.6017 C 11.5977,9.53525 12,8.26572 12,6.99228 C 12,5.71884 11.5977,4.44931 10.793,3.379 C 10.6094,3.1329 10.3281,2.99618 10.0391,2.98056 Z"); + + var group = new GeometryGroup(); + group.Children.Add(speakerGeometry); + group.Children.Add(wave1Geometry); + group.Children.Add(wave2Geometry); + VolumeIconPath.Data = group; + VolumeIconPath2.Visibility = Visibility.Collapsed; + } + else + { + // 音量<50%:扬声器 + 一条实线声波线 + // 扬声器部分 + var speakerGeometry = Geometry.Parse("M 7,1.00759 C 6.70313,1.00369 6.42188,1.13259 6.23048,1.35916 L 3,4.99979 H 2 C 0.906251,4.99979 0,5.84354 0,6.99979 V 8.99979 C 0,10.0896 0.910157,10.9998 2,10.9998 H 3 L 6.23048,14.6404 C 6.44141,14.8943 6.72266,15.0037 7,14.9998 V 1.00759 Z"); + // 声波线(实线,不透明度100%) + var wave1Geometry = Geometry.Parse("M 10.0391,3.98807 C 9.81642,3.98025 9.58595,4.04666 9.39454,4.19119 C 9.13282,4.39041 9.00001,4.69119 9.00001,4.99588 V 5.06229 C 9.01173,5.24979 9.07813,5.43338 9.19923,5.58963 C 10.2734,7.01932 10.2734,8.98026 9.19923,10.406 C 9.07813,10.5662 9.01173,10.7498 9.00001,10.9373 V 11.0037 C 9.00001,11.3084 9.13282,11.6092 9.39454,11.8084 C 9.83595,12.1404 10.4609,12.0506 10.793,11.6092 C 11.5977,10.5428 12,9.27323 12,7.99979 C 12,6.72635 11.5977,5.45682 10.793,4.3865 C 10.6094,4.14041 10.3281,4.00369 10.0391,3.98807 Z"); + + // 主Path:扬声器 + 声波线 + var group = new GeometryGroup(); + group.Children.Add(speakerGeometry); + group.Children.Add(wave1Geometry); + VolumeIconPath.Data = group; + + // 隐藏第二个Path + VolumeIconPath2.Visibility = Visibility.Collapsed; + } + } + + #endregion + + #region 展开/折叠动画 + + private void ExpandPanel() + { + if (_isExpanded) return; + + _isExpanded = true; + UpdateArrowRotation(); + + var animation = (Storyboard)Resources["ExpandAnimation"]; + var doubleAnimation = animation.Children[0] as DoubleAnimation; + doubleAnimation.From = PanelTransform.X; + doubleAnimation.To = 0; + animation.Begin(); + } + + private void CollapsePanel() + { + if (!_isExpanded) return; + + _isExpanded = false; + UpdateArrowRotation(); + + var animation = (Storyboard)Resources["CollapseAnimation"]; + var doubleAnimation = animation.Children[0] as DoubleAnimation; + doubleAnimation.From = PanelTransform.X; + doubleAnimation.To = _collapsedOffset; + animation.Begin(); + } + + private void UpdateArrowRotation() + { + if (_isExpanded) + { + // 展开时箭头指向左(折叠) + ArrowRotateTransform.Angle = 0; + } + else + { + // 折叠时箭头指向右(展开) + ArrowRotateTransform.Angle = 180; + } + } + + #endregion + + #region 箭头按钮事件 + + private void ArrowButton_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + e.Handled = true; + if (_isExpanded) + { + CollapsePanel(); + } + else + { + ExpandPanel(); + } + } + + private void ArrowButton_MouseEnter(object sender, MouseEventArgs e) + { + // 根据当前主题设置悬停颜色 + bool isDark = ArrowButtonBackgroundBrush.Color.R < 128; + if (isDark) + { + ArrowButtonBackgroundBrush.Color = Color.FromArgb(230, 32, 32, 32); + } + else + { + ArrowButtonBackgroundBrush.Color = Color.FromArgb(230, 255, 255, 255); + } + } + + private void ArrowButton_MouseLeave(object sender, MouseEventArgs e) + { + // 恢复主题颜色 + ApplyTheme(); + } + + #endregion + + #region 拖动手势 + + private void ContentBorder_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + if (e.OriginalSource is Slider) return; // 如果点击的是滑块,不处理拖动 + + _isDragging = true; + _dragStartPoint = e.GetPosition(MainCanvas); + ContentBorder.CaptureMouse(); + e.Handled = true; + } + + private void ContentBorder_MouseMove(object sender, MouseEventArgs e) + { + if (!_isDragging) return; + + Point currentPoint = e.GetPosition(MainCanvas); + double deltaX = currentPoint.X - _dragStartPoint.X; + + // 计算新位置 + double newX = PanelTransform.X + deltaX; + + // 限制拖动范围 + newX = Math.Max(0, Math.Min(_collapsedOffset, newX)); + + PanelTransform.X = newX; + _dragStartPoint = currentPoint; + } + + private void ContentBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) + { + if (!_isDragging) return; + + _isDragging = false; + ContentBorder.ReleaseMouseCapture(); + + // 判断是否超过一半 + if (PanelTransform.X < _collapsedOffset / 2) + { + ExpandPanel(); + } + else + { + CollapsePanel(); + } + } + + private void ContentBorder_TouchDown(object sender, TouchEventArgs e) + { + if (e.OriginalSource is Slider) return; + + _isDragging = true; + _dragStartPoint = e.GetTouchPoint(MainCanvas).Position; + e.Handled = true; + } + + private void ContentBorder_TouchMove(object sender, TouchEventArgs e) + { + if (!_isDragging) return; + + Point currentPoint = e.GetTouchPoint(MainCanvas).Position; + double deltaX = currentPoint.X - _dragStartPoint.X; + + double newX = PanelTransform.X + deltaX; + newX = Math.Max(0, Math.Min(_collapsedOffset, newX)); + + PanelTransform.X = newX; + _dragStartPoint = currentPoint; + } + + private void ContentBorder_TouchUp(object sender, TouchEventArgs e) + { + if (!_isDragging) return; + + _isDragging = false; + + if (PanelTransform.X < _collapsedOffset / 2) + { + ExpandPanel(); + } + else + { + CollapsePanel(); + } + } + + #endregion + + #region 音量控制事件 + + private void VolumeMuteButton_Click(object sender, RoutedEventArgs e) + { + try + { + bool currentMute = GetMute(); + float volume = GetVolume(); + SetMute(!currentMute); + UpdateVolumeIcon(!currentMute, volume); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"切换静音失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private void VolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) + { + if (!IsLoaded) return; + + try + { + float volume = (float)(e.NewValue / 100.0); + SetVolume(volume); + + // 更新文本显示 + VolumeValueText.Text = $"{(int)e.NewValue}%"; + + // 如果音量大于0,取消静音 + if (e.NewValue > 0) + { + bool isMuted = GetMute(); + if (isMuted) + { + SetMute(false); + } + } + + // 更新图标(根据当前音量和静音状态) + bool currentMute = GetMute(); + UpdateVolumeIcon(currentMute, volume); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"音量滑块值改变处理失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private void VolumeSlider_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) + { + // 确保精度为1% + double value = VolumeSlider.Value; + VolumeSlider.Value = Math.Round(value); + } + + private void VolumeSlider_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) + { + // 触摸操作完成时,确保精度为1% + double value = VolumeSlider.Value; + VolumeSlider.Value = Math.Round(value); + } + + #endregion + + #region 图片插入 + + private async void InsertImageButton_Click(object sender, RoutedEventArgs e) + { + if (_mainWindow == null) return; + + try + { + // 调用MainWindow的图片插入功能 + var dialog = new OpenFileDialog + { + Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif" + }; + + if (dialog.ShowDialog() == true) + { + string filePath = dialog.FileName; + + // 使用反射调用MainWindow的CreateAndCompressImageAsync方法 + var createImageMethod = _mainWindow.GetType().GetMethod("CreateAndCompressImageAsync", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + if (createImageMethod != null) + { + var imageTask = createImageMethod.Invoke(_mainWindow, new object[] { filePath }) as System.Threading.Tasks.Task; + if (imageTask != null) + { + var image = await imageTask; + if (image != null) + { + // 使用反射调用MainWindow的图片插入相关方法 + await InsertImageToMainWindow(image); + } + } + } + else + { + LogHelper.WriteLogToFile("无法找到CreateAndCompressImageAsync方法", LogHelper.LogType.Warning); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"插入图片失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private async System.Threading.Tasks.Task InsertImageToMainWindow(System.Windows.Controls.Image image) + { + if (_mainWindow == null || image == null) return; + + try + { + // 生成唯一名称 + string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff"); + image.Name = timestamp; + + // 初始化TransformGroup + if (image is FrameworkElement element) + { + var transformGroup = new TransformGroup(); + transformGroup.Children.Add(new ScaleTransform(1, 1)); + transformGroup.Children.Add(new TranslateTransform(0, 0)); + transformGroup.Children.Add(new RotateTransform(0)); + element.RenderTransform = transformGroup; + } + + // 使用反射调用CenterAndScaleElement + var centerMethod = _mainWindow.GetType().GetMethod("CenterAndScaleElement", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + centerMethod?.Invoke(_mainWindow, new object[] { image }); + + // 设置图片属性 + image.IsHitTestVisible = true; + image.Focusable = false; + + // 获取inkCanvas并设置 + var inkCanvasProperty = _mainWindow.GetType().GetProperty("inkCanvas", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var inkCanvas = inkCanvasProperty?.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas; + + if (inkCanvas != null) + { + inkCanvas.Select(new System.Windows.Ink.StrokeCollection()); + inkCanvas.EditingMode = System.Windows.Controls.InkCanvasEditingMode.None; + inkCanvas.Children.Add(image); + + // 绑定事件处理器 + if (image is FrameworkElement elementForEvents) + { + // 使用反射绑定事件 + var mouseDownMethod = _mainWindow.GetType().GetMethod("Element_MouseLeftButtonDown", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var mouseUpMethod = _mainWindow.GetType().GetMethod("Element_MouseLeftButtonUp", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var mouseMoveMethod = _mainWindow.GetType().GetMethod("Element_MouseMove", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var mouseWheelMethod = _mainWindow.GetType().GetMethod("Element_MouseWheel", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var touchDownMethod = _mainWindow.GetType().GetMethod("Element_TouchDown", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var touchUpMethod = _mainWindow.GetType().GetMethod("Element_TouchUp", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var manipulationDeltaMethod = _mainWindow.GetType().GetMethod("Element_ManipulationDelta", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var manipulationCompletedMethod = _mainWindow.GetType().GetMethod("Element_ManipulationCompleted", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + if (mouseDownMethod != null) + elementForEvents.MouseLeftButtonDown += (s, e) => mouseDownMethod.Invoke(_mainWindow, new object[] { s, e }); + if (mouseUpMethod != null) + elementForEvents.MouseLeftButtonUp += (s, e) => mouseUpMethod.Invoke(_mainWindow, new object[] { s, e }); + if (mouseMoveMethod != null) + elementForEvents.MouseMove += (s, e) => mouseMoveMethod.Invoke(_mainWindow, new object[] { s, e }); + if (mouseWheelMethod != null) + elementForEvents.MouseWheel += (s, e) => mouseWheelMethod.Invoke(_mainWindow, new object[] { s, e }); + if (touchDownMethod != null) + elementForEvents.TouchDown += (s, e) => touchDownMethod.Invoke(_mainWindow, new object[] { s, e }); + if (touchUpMethod != null) + elementForEvents.TouchUp += (s, e) => touchUpMethod.Invoke(_mainWindow, new object[] { s, e }); + + elementForEvents.IsManipulationEnabled = true; + if (manipulationDeltaMethod != null) + elementForEvents.ManipulationDelta += (s, e) => manipulationDeltaMethod.Invoke(_mainWindow, new object[] { s, e }); + if (manipulationCompletedMethod != null) + elementForEvents.ManipulationCompleted += (s, e) => manipulationCompletedMethod.Invoke(_mainWindow, new object[] { s, e }); + + elementForEvents.Cursor = Cursors.Hand; + } + } + + // 提交历史记录 + var timeMachineProperty = _mainWindow.GetType().GetProperty("timeMachine", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var timeMachine = timeMachineProperty?.GetValue(_mainWindow); + if (timeMachine != null) + { + var commitMethod = timeMachine.GetType().GetMethod("CommitElementInsertHistory"); + commitMethod?.Invoke(timeMachine, new object[] { image }); + } + + // 切换到选择模式 + var setModeMethod = _mainWindow.GetType().GetMethod("SetCurrentToolMode", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + setModeMethod?.Invoke(_mainWindow, new object[] { System.Windows.Controls.InkCanvasEditingMode.Select }); + + var updateModeMethod = _mainWindow.GetType().GetMethod("UpdateCurrentToolMode", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + updateModeMethod?.Invoke(_mainWindow, new object[] { "select" }); + + var hidePanelsMethod = _mainWindow.GetType().GetMethod("HideSubPanels", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + hidePanelsMethod?.Invoke(_mainWindow, new object[] { "select" }); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"插入图片到MainWindow失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + #endregion + + #region 主题适配 + + private void ApplyTheme() + { + try + { + if (MainWindow.Settings != null) + { + ApplyTheme(MainWindow.Settings); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"应用PPT快捷面板主题失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private void ApplyTheme(Settings settings) + { + try + { + bool isDarkTheme = false; + + if (settings.Appearance.Theme == 0) // 浅色主题 + { + isDarkTheme = false; + } + else if (settings.Appearance.Theme == 1) // 深色主题 + { + isDarkTheme = true; + } + else // 跟随系统主题 + { + bool isSystemLight = IsSystemThemeLight(); + isDarkTheme = !isSystemLight; + } + + if (isDarkTheme) + { + // 深色主题:使用80%不透明度的深色背景 + ArrowButtonBackgroundBrush.Color = Color.FromArgb(204, 32, 32, 32); // #CC202020 + ContentBackgroundBrush.Color = Color.FromArgb(204, 32, 32, 32); // #CC202020 + ArrowPathFillBrush.Color = Colors.White; + VolumeIconFillBrush.Color = Colors.White; + VolumeIconFillBrush2.Color = Colors.White; + VolumeValueForegroundBrush.Color = Color.FromArgb(200, 255, 255, 255); + MagnifierTitleForegroundBrush.Color = Colors.White; + MagnifierDescForegroundBrush.Color = Color.FromArgb(200, 255, 255, 255); + Separator1BackgroundBrush.Color = Color.FromArgb(128, 255, 255, 255); + } + else + { + // 浅色主题:使用80%不透明度的白色背景 + ArrowButtonBackgroundBrush.Color = Color.FromArgb(204, 255, 255, 255); // #CCFFFFFF + ContentBackgroundBrush.Color = Color.FromArgb(204, 255, 255, 255); // #CCFFFFFF + ArrowPathFillBrush.Color = Colors.Black; + VolumeIconFillBrush.Color = Colors.Black; + VolumeIconFillBrush2.Color = Colors.Black; + VolumeValueForegroundBrush.Color = Color.FromArgb(128, 0, 0, 0); + MagnifierTitleForegroundBrush.Color = Colors.Black; + MagnifierDescForegroundBrush.Color = Color.FromArgb(128, 0, 0, 0); + Separator1BackgroundBrush.Color = Color.FromArgb(255, 224, 224, 224); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"应用PPT快捷面板主题失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private bool IsSystemThemeLight() + { + var light = false; + try + { + var registryKey = Microsoft.Win32.Registry.CurrentUser; + var themeKey = registryKey.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"); + if (themeKey != null) + { + var value = themeKey.GetValue("AppsUseLightTheme"); + if (value != null) + { + light = (int)value == 1; + } + themeKey.Close(); + } + } + catch + { + // 如果读取注册表失败,默认为浅色主题 + light = true; + } + return light; + } + + #endregion + + #region 公开方法 + + /// + /// 设置面板的可见性(仅在PPT模式下显示) + /// + public void UpdateVisibility(bool isInPPTMode) + { + Visibility = isInPPTMode ? Visibility.Visible : Visibility.Collapsed; + } + + #endregion + } +} +