add:展台
This commit is contained in:
@@ -10558,6 +10558,22 @@
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
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
|
||||
x:Name="CheckBoxEnablePhotoCorrection"
|
||||
Width="110"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@@ -47,6 +47,10 @@ namespace Ink_Canvas
|
||||
{
|
||||
if (child is Image || child is MediaElement)
|
||||
{
|
||||
if (child is Image img && img.Tag is string tag && tag == VideoPresenterLiveFrameTag)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!elementsInHistory.Contains(child))
|
||||
{
|
||||
timeMachine.CommitElementInsertHistory(child);
|
||||
@@ -288,12 +292,14 @@ namespace Ink_Canvas
|
||||
currentSelectedElement = null;
|
||||
}
|
||||
|
||||
VideoPresenter_BeforePageLeave();
|
||||
SaveStrokes();
|
||||
|
||||
ClearStrokes(true);
|
||||
CurrentWhiteboardIndex--;
|
||||
|
||||
RestoreStrokes();
|
||||
VideoPresenter_OnPageChanged();
|
||||
|
||||
UpdateIndexInfoDisplay();
|
||||
}
|
||||
@@ -322,12 +328,14 @@ namespace Ink_Canvas
|
||||
currentSelectedElement = null;
|
||||
}
|
||||
|
||||
VideoPresenter_BeforePageLeave();
|
||||
SaveStrokes();
|
||||
|
||||
ClearStrokes(true);
|
||||
CurrentWhiteboardIndex++;
|
||||
|
||||
RestoreStrokes();
|
||||
VideoPresenter_OnPageChanged();
|
||||
|
||||
UpdateIndexInfoDisplay();
|
||||
}
|
||||
@@ -349,6 +357,7 @@ namespace Ink_Canvas
|
||||
currentSelectedElement = null;
|
||||
}
|
||||
|
||||
VideoPresenter_BeforePageLeave();
|
||||
SaveStrokes();
|
||||
ClearStrokes(true);
|
||||
|
||||
@@ -364,6 +373,7 @@ namespace Ink_Canvas
|
||||
|
||||
// 恢复新页面(这会清空画布,因为历史记录为null)
|
||||
RestoreStrokes();
|
||||
VideoPresenter_OnPageChanged();
|
||||
|
||||
UpdateIndexInfoDisplay();
|
||||
|
||||
|
||||
@@ -18,12 +18,22 @@ namespace Ink_Canvas
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
// 标记:用于在保存/恢复白板内容时排除“展台实时上屏”画面
|
||||
private const string VideoPresenterLiveFrameTag = "__VideoPresenterLiveFrame";
|
||||
|
||||
private CameraService _cameraService;
|
||||
private readonly object _videoPresenterFrameLock = new object();
|
||||
private Bitmap _lastFrame;
|
||||
|
||||
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 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
|
||||
|
||||
Reference in New Issue
Block a user