add:展台
This commit is contained in:
@@ -10558,6 +10558,22 @@
|
|||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
|
<ToggleButton
|
||||||
|
x:Name="BtnToggleVideoPresenterLiveOnCanvas"
|
||||||
|
Width="90"
|
||||||
|
Height="40"
|
||||||
|
Margin="3,0"
|
||||||
|
Background="{DynamicResource FloatBarBackground}"
|
||||||
|
BorderBrush="{DynamicResource FloatBarBorderBrush}"
|
||||||
|
BorderThickness="1"
|
||||||
|
Checked="BtnToggleVideoPresenterLiveOnCanvas_Checked"
|
||||||
|
Unchecked="BtnToggleVideoPresenterLiveOnCanvas_Unchecked">
|
||||||
|
<TextBlock
|
||||||
|
FontSize="12"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="{DynamicResource FloatBarForeground}"
|
||||||
|
Text="上屏" />
|
||||||
|
</ToggleButton>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
x:Name="CheckBoxEnablePhotoCorrection"
|
x:Name="CheckBoxEnablePhotoCorrection"
|
||||||
Width="110"
|
Width="110"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Ink_Canvas.Helpers;
|
using Ink_Canvas.Helpers;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -47,6 +47,10 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
if (child is Image || child is MediaElement)
|
if (child is Image || child is MediaElement)
|
||||||
{
|
{
|
||||||
|
if (child is Image img && img.Tag is string tag && tag == VideoPresenterLiveFrameTag)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!elementsInHistory.Contains(child))
|
if (!elementsInHistory.Contains(child))
|
||||||
{
|
{
|
||||||
timeMachine.CommitElementInsertHistory(child);
|
timeMachine.CommitElementInsertHistory(child);
|
||||||
@@ -288,12 +292,14 @@ namespace Ink_Canvas
|
|||||||
currentSelectedElement = null;
|
currentSelectedElement = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VideoPresenter_BeforePageLeave();
|
||||||
SaveStrokes();
|
SaveStrokes();
|
||||||
|
|
||||||
ClearStrokes(true);
|
ClearStrokes(true);
|
||||||
CurrentWhiteboardIndex--;
|
CurrentWhiteboardIndex--;
|
||||||
|
|
||||||
RestoreStrokes();
|
RestoreStrokes();
|
||||||
|
VideoPresenter_OnPageChanged();
|
||||||
|
|
||||||
UpdateIndexInfoDisplay();
|
UpdateIndexInfoDisplay();
|
||||||
}
|
}
|
||||||
@@ -322,12 +328,14 @@ namespace Ink_Canvas
|
|||||||
currentSelectedElement = null;
|
currentSelectedElement = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VideoPresenter_BeforePageLeave();
|
||||||
SaveStrokes();
|
SaveStrokes();
|
||||||
|
|
||||||
ClearStrokes(true);
|
ClearStrokes(true);
|
||||||
CurrentWhiteboardIndex++;
|
CurrentWhiteboardIndex++;
|
||||||
|
|
||||||
RestoreStrokes();
|
RestoreStrokes();
|
||||||
|
VideoPresenter_OnPageChanged();
|
||||||
|
|
||||||
UpdateIndexInfoDisplay();
|
UpdateIndexInfoDisplay();
|
||||||
}
|
}
|
||||||
@@ -349,6 +357,7 @@ namespace Ink_Canvas
|
|||||||
currentSelectedElement = null;
|
currentSelectedElement = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VideoPresenter_BeforePageLeave();
|
||||||
SaveStrokes();
|
SaveStrokes();
|
||||||
ClearStrokes(true);
|
ClearStrokes(true);
|
||||||
|
|
||||||
@@ -364,6 +373,7 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
// 恢复新页面(这会清空画布,因为历史记录为null)
|
// 恢复新页面(这会清空画布,因为历史记录为null)
|
||||||
RestoreStrokes();
|
RestoreStrokes();
|
||||||
|
VideoPresenter_OnPageChanged();
|
||||||
|
|
||||||
UpdateIndexInfoDisplay();
|
UpdateIndexInfoDisplay();
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,22 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
|
// 标记:用于在保存/恢复白板内容时排除“展台实时上屏”画面
|
||||||
|
private const string VideoPresenterLiveFrameTag = "__VideoPresenterLiveFrame";
|
||||||
|
|
||||||
private CameraService _cameraService;
|
private CameraService _cameraService;
|
||||||
private readonly object _videoPresenterFrameLock = new object();
|
private readonly object _videoPresenterFrameLock = new object();
|
||||||
private Bitmap _lastFrame;
|
private Bitmap _lastFrame;
|
||||||
|
|
||||||
private readonly List<CapturedImage> _capturedPhotos = new List<CapturedImage>();
|
private readonly List<CapturedImage> _capturedPhotos = new List<CapturedImage>();
|
||||||
|
|
||||||
|
// 按页绑定:每一页对应一个“实时画面”元素与布局/设备信息
|
||||||
|
private readonly Dictionary<int, System.Windows.Controls.Image> _liveFrameImageByPage = new Dictionary<int, System.Windows.Controls.Image>();
|
||||||
|
private readonly HashSet<int> _liveEnabledPages = new HashSet<int>();
|
||||||
|
private readonly Dictionary<int, int> _cameraIndexByPage = new Dictionary<int, int>();
|
||||||
|
private readonly Dictionary<int, (double left, double top, double width)> _liveFrameLayoutByPage =
|
||||||
|
new Dictionary<int, (double left, double top, double width)>();
|
||||||
|
|
||||||
private DateTime _lastCaptureTime = DateTime.MinValue;
|
private DateTime _lastCaptureTime = DateTime.MinValue;
|
||||||
private const int VideoPresenterCaptureCooldownMs = 1000;
|
private const int VideoPresenterCaptureCooldownMs = 1000;
|
||||||
|
|
||||||
@@ -53,6 +63,12 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
CheckBoxEnablePhotoCorrection.IsChecked = Settings?.Automation?.IsEnablePhotoCorrection ?? false;
|
CheckBoxEnablePhotoCorrection.IsChecked = Settings?.Automation?.IsEnablePhotoCorrection ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 同步“上屏”按钮状态(按页绑定)
|
||||||
|
if (BtnToggleVideoPresenterLiveOnCanvas != null)
|
||||||
|
{
|
||||||
|
BtnToggleVideoPresenterLiveOnCanvas.IsChecked = _liveEnabledPages.Contains(GetCurrentPageIndex());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BtnCloseVideoPresenter_Click(object sender, RoutedEventArgs e)
|
private void BtnCloseVideoPresenter_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -119,6 +135,9 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
BtnCapturePhoto.IsEnabled = true;
|
BtnCapturePhoto.IsEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 实时上屏:刷新当前页的画面元素
|
||||||
|
TryUpdateLiveFrameOnCanvas(preview);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
catch
|
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()
|
private void RefreshVideoPresenterDeviceList()
|
||||||
{
|
{
|
||||||
if (_cameraService == null) return;
|
if (_cameraService == null) return;
|
||||||
@@ -184,6 +265,7 @@ namespace Ink_Canvas
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
EnsureCameraService();
|
EnsureCameraService();
|
||||||
|
_cameraIndexByPage[GetCurrentPageIndex()] = cameraIndex;
|
||||||
if (_cameraService.StartPreview(cameraIndex))
|
if (_cameraService.StartPreview(cameraIndex))
|
||||||
{
|
{
|
||||||
if (BtnCapturePhoto != null) BtnCapturePhoto.IsEnabled = true;
|
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)
|
private void BtnCapturePhoto_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
Reference in New Issue
Block a user