From d2abadd69b421a5569c3f30f054dd86ab18ef614 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Thu, 19 Feb 2026 18:24:55 +0800 Subject: [PATCH] =?UTF-8?q?add:=E5=B1=95=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/MainWindow.xaml | 16 ++ Ink Canvas/MainWindow_cs/MW_BoardControls.cs | 12 +- Ink Canvas/MainWindow_cs/MW_VideoPresenter.cs | 177 ++++++++++++++++++ 3 files changed, 204 insertions(+), 1 deletion(-) diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 13c5e9fd..116ebe99 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -10558,6 +10558,22 @@ HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal"> + + + _capturedPhotos = new List(); + // 按页绑定:每一页对应一个“实时画面”元素与布局/设备信息 + private readonly Dictionary _liveFrameImageByPage = new Dictionary(); + private readonly HashSet _liveEnabledPages = new HashSet(); + private readonly Dictionary _cameraIndexByPage = new Dictionary(); + private readonly Dictionary _liveFrameLayoutByPage = + new Dictionary(); + private DateTime _lastCaptureTime = DateTime.MinValue; private const int VideoPresenterCaptureCooldownMs = 1000; @@ -53,6 +63,12 @@ namespace Ink_Canvas { CheckBoxEnablePhotoCorrection.IsChecked = Settings?.Automation?.IsEnablePhotoCorrection ?? false; } + + // 同步“上屏”按钮状态(按页绑定) + if (BtnToggleVideoPresenterLiveOnCanvas != null) + { + BtnToggleVideoPresenterLiveOnCanvas.IsChecked = _liveEnabledPages.Contains(GetCurrentPageIndex()); + } } private void BtnCloseVideoPresenter_Click(object sender, RoutedEventArgs e) @@ -119,6 +135,9 @@ namespace Ink_Canvas { BtnCapturePhoto.IsEnabled = true; } + + // 实时上屏:刷新当前页的画面元素 + TryUpdateLiveFrameOnCanvas(preview); })); } catch @@ -127,6 +146,68 @@ namespace Ink_Canvas } } + private int GetCurrentPageIndex() + { + return Math.Max(1, CurrentWhiteboardIndex); + } + + private void TryUpdateLiveFrameOnCanvas(BitmapImage preview) + { + try + { + int page = GetCurrentPageIndex(); + if (!_liveEnabledPages.Contains(page)) return; + if (inkCanvas == null) return; + if (!_liveFrameImageByPage.TryGetValue(page, out var img) || img == null) return; + + if (!inkCanvas.Children.Contains(img)) + { + inkCanvas.Children.Add(img); + } + + img.Source = preview; + img.Visibility = Visibility.Visible; + } + catch { } + } + + private System.Windows.Controls.Image EnsureLiveFrameElementForPage(int page) + { + if (_liveFrameImageByPage.TryGetValue(page, out var existing) && existing != null) return existing; + + var img = new System.Windows.Controls.Image + { + Tag = VideoPresenterLiveFrameTag, + Stretch = System.Windows.Media.Stretch.Uniform, + Width = 520, + Visibility = Visibility.Visible, + Opacity = 1.0 + }; + _liveFrameImageByPage[page] = img; + return img; + } + + private void ApplyLiveFrameLayoutForPage(int page, System.Windows.Controls.Image img) + { + if (img == null) return; + + if (_liveFrameLayoutByPage.TryGetValue(page, out var layout)) + { + if (!double.IsNaN(layout.width) && layout.width > 10) img.Width = layout.width; + InkCanvas.SetLeft(img, Math.Max(0, layout.left)); + InkCanvas.SetTop(img, Math.Max(0, layout.top)); + return; + } + + // 默认位置:居中偏上 + double x = (inkCanvas?.ActualWidth ?? 0) / 2 - img.Width / 2; + double y = (inkCanvas?.ActualHeight ?? 0) / 2 - 200; + if (double.IsNaN(x) || double.IsInfinity(x)) x = 100; + if (double.IsNaN(y) || double.IsInfinity(y)) y = 100; + InkCanvas.SetLeft(img, Math.Max(0, x)); + InkCanvas.SetTop(img, Math.Max(0, y)); + } + private void RefreshVideoPresenterDeviceList() { if (_cameraService == null) return; @@ -184,6 +265,7 @@ namespace Ink_Canvas try { EnsureCameraService(); + _cameraIndexByPage[GetCurrentPageIndex()] = cameraIndex; if (_cameraService.StartPreview(cameraIndex)) { if (BtnCapturePhoto != null) BtnCapturePhoto.IsEnabled = true; @@ -195,6 +277,101 @@ namespace Ink_Canvas } } + private void BtnToggleVideoPresenterLiveOnCanvas_Checked(object sender, RoutedEventArgs e) + { + int page = GetCurrentPageIndex(); + _liveEnabledPages.Add(page); + + var img = EnsureLiveFrameElementForPage(page); + ApplyLiveFrameLayoutForPage(page, img); + + if (inkCanvas != null && !inkCanvas.Children.Contains(img)) + { + inkCanvas.Children.Add(img); + } + + // 立即用侧栏预览刷新一次 + if (VideoPresenterPreviewImage?.Source is BitmapImage bi) + { + img.Source = bi; + } + } + + private void BtnToggleVideoPresenterLiveOnCanvas_Unchecked(object sender, RoutedEventArgs e) + { + int page = GetCurrentPageIndex(); + _liveEnabledPages.Remove(page); + + if (_liveFrameImageByPage.TryGetValue(page, out var img) && img != null) + { + try + { + if (inkCanvas != null && inkCanvas.Children.Contains(img)) + { + inkCanvas.Children.Remove(img); + } + } + catch { } + } + } + + // 翻页前调用:保存当前页实时画面的位置/大小 + private void VideoPresenter_BeforePageLeave() + { + try + { + int page = GetCurrentPageIndex(); + if (!_liveFrameImageByPage.TryGetValue(page, out var img) || img == null) return; + + double left = InkCanvas.GetLeft(img); + double top = InkCanvas.GetTop(img); + if (double.IsNaN(left)) left = 0; + if (double.IsNaN(top)) top = 0; + + _liveFrameLayoutByPage[page] = (left, top, img.Width); + } + catch { } + } + + // 翻页后调用:根据该页状态恢复实时画面,并同步设备选择 + private void VideoPresenter_OnPageChanged() + { + try + { + int page = GetCurrentPageIndex(); + + // 同步“上屏”按钮状态 + if (BtnToggleVideoPresenterLiveOnCanvas != null) + { + BtnToggleVideoPresenterLiveOnCanvas.IsChecked = _liveEnabledPages.Contains(page); + } + + // 若该页上屏,恢复画面元素(RestoreStrokes 会清空 inkCanvas.Children) + if (_liveEnabledPages.Contains(page)) + { + var img = EnsureLiveFrameElementForPage(page); + ApplyLiveFrameLayoutForPage(page, img); + if (inkCanvas != null && !inkCanvas.Children.Contains(img)) + { + inkCanvas.Children.Add(img); + } + + if (VideoPresenterPreviewImage?.Source is BitmapImage bi) + { + img.Source = bi; + } + } + + // 按页摄像头索引:切页后自动切回该页的摄像头 + if (_cameraIndexByPage.TryGetValue(page, out int idx)) + { + EnsureCameraService(); + _cameraService?.StartPreview(idx); + } + } + catch { } + } + private void BtnCapturePhoto_Click(object sender, RoutedEventArgs e) { try