@@ -6766,7 +6766,198 @@
|
||||
</Border>
|
||||
</Viewbox>
|
||||
|
||||
<!-- 视频展台侧栏 -->
|
||||
<Border
|
||||
x:Name="VideoPresenterSidebar"
|
||||
Width="350"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{DynamicResource FloatBarBackground}"
|
||||
BorderBrush="{DynamicResource FloatBarBorderBrush}"
|
||||
BorderThickness="0,0,1,0"
|
||||
Visibility="Collapsed"
|
||||
Panel.ZIndex="996">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="50" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="110" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 顶部标题栏 -->
|
||||
<Grid Grid.Row="0" Height="50">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="50" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="15,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
FontWeight="Bold"
|
||||
Foreground="{DynamicResource FloatBarForeground}"
|
||||
Text="{i18n:I18n Key=Booth_Title}" />
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Width="50"
|
||||
Height="50"
|
||||
Padding="0"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
Click="BtnCloseVideoPresenter_Click">
|
||||
<Path
|
||||
Stroke="{DynamicResource IconForeground}"
|
||||
StrokeThickness="2"
|
||||
StrokeStartLineCap="Round"
|
||||
StrokeEndLineCap="Round"
|
||||
Width="16"
|
||||
Height="16"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Stretch="Uniform"
|
||||
Data="M4.5 4.5L11.5 11.5M11.5 4.5L4.5 11.5" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<!-- 预览区域 -->
|
||||
<Border Grid.Row="1" Margin="10,10,10,2" Padding="10" CornerRadius="6" Background="#1f2328">
|
||||
<Image x:Name="VideoPresenterPreviewImage" Stretch="Uniform" Height="180" />
|
||||
</Border>
|
||||
|
||||
<!-- 照片显示区域 -->
|
||||
<Border Grid.Row="2" Margin="10,2,10,2" Padding="10" CornerRadius="6" Background="{DynamicResource FloatBarBackground}">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Margin="0,0,0,8"
|
||||
HorizontalAlignment="Left"
|
||||
FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource FloatBarForeground}"
|
||||
Text="{i18n:I18n Key=Booth_CapturedPhotos}" />
|
||||
<ScrollViewer
|
||||
x:Name="CapturedPhotosScrollViewer"
|
||||
MaxHeight="280"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
PanningMode="VerticalFirst">
|
||||
<StackPanel x:Name="CapturedPhotosStackPanel" Margin="0,0,0,5" />
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 设备选择区域 -->
|
||||
<Border Grid.Row="3" Margin="10,2,10,5" Padding="10" CornerRadius="6" Background="{DynamicResource FloatBarBackground}">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Margin="0,0,0,8"
|
||||
HorizontalAlignment="Left"
|
||||
FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource FloatBarForeground}"
|
||||
Text="{i18n:I18n Key=Booth_CameraDevices}" />
|
||||
<ScrollViewer
|
||||
x:Name="CameraDevicesScrollViewer"
|
||||
MaxHeight="120"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
PanningMode="VerticalFirst">
|
||||
<StackPanel x:Name="CameraDevicesStackPanel" />
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 底部按钮区域 -->
|
||||
<Grid Grid.Row="4" Margin="0,0,0,6">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center" Margin="10,4,10,2">
|
||||
<ToggleButton
|
||||
x:Name="BtnToggleVideoPresenterLiveOnCanvas"
|
||||
Width="70"
|
||||
Height="40"
|
||||
Margin="2,0"
|
||||
Background="{DynamicResource FloatBarBackground}"
|
||||
BorderBrush="{DynamicResource FloatBarBorderBrush}"
|
||||
BorderThickness="1"
|
||||
Checked="BtnToggleVideoPresenterLiveOnCanvas_Checked"
|
||||
Unchecked="BtnToggleVideoPresenterLiveOnCanvas_Unchecked">
|
||||
<TextBlock FontSize="12" FontWeight="SemiBold" Foreground="{DynamicResource FloatBarForeground}" Text="{i18n:I18n Key=Booth_Present}" />
|
||||
</ToggleButton>
|
||||
<ToggleButton
|
||||
x:Name="ToggleBtnPhotoCorrection"
|
||||
Width="70"
|
||||
Height="40"
|
||||
Margin="2,0"
|
||||
Background="{DynamicResource FloatBarBackground}"
|
||||
BorderBrush="{DynamicResource FloatBarBorderBrush}"
|
||||
BorderThickness="1"
|
||||
Checked="ToggleBtnPhotoCorrection_Checked"
|
||||
Unchecked="ToggleBtnPhotoCorrection_Unchecked">
|
||||
<TextBlock FontSize="12" FontWeight="SemiBold" Foreground="{DynamicResource FloatBarForeground}" Text="{i18n:I18n Key=Booth_Correction}" />
|
||||
</ToggleButton>
|
||||
<Button
|
||||
x:Name="BtnCapturePhoto"
|
||||
Width="70"
|
||||
Height="40"
|
||||
Margin="2,0"
|
||||
Background="{DynamicResource FloatBarBackground}"
|
||||
BorderBrush="{DynamicResource FloatBarBorderBrush}"
|
||||
BorderThickness="1"
|
||||
Click="BtnCapturePhoto_Click">
|
||||
<TextBlock FontSize="12" FontWeight="SemiBold" Foreground="{DynamicResource FloatBarForeground}" Text="{i18n:I18n Key=Booth_Capture}" />
|
||||
</Button>
|
||||
<Button
|
||||
x:Name="BtnRotateImage"
|
||||
Width="70"
|
||||
Height="40"
|
||||
Margin="2,0"
|
||||
Background="{DynamicResource FloatBarBackground}"
|
||||
BorderBrush="{DynamicResource FloatBarBorderBrush}"
|
||||
BorderThickness="1"
|
||||
Click="BtnRotateImage_Click">
|
||||
<TextBlock FontSize="12" FontWeight="SemiBold" Foreground="{DynamicResource FloatBarForeground}" Text="{i18n:I18n Key=Booth_Rotate}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<!-- 分辨率 Tab -->
|
||||
<Grid Grid.Row="1" HorizontalAlignment="Center" Margin="10,6,10,4">
|
||||
<Border Background="{DynamicResource FloatBarBackground}"
|
||||
CornerRadius="8" BorderThickness="1" BorderBrush="#616161"
|
||||
Width="282" Height="40" ToolTip="{x:Static props:Strings.Booth_Resolution_Tooltip}">
|
||||
<Grid>
|
||||
<Border x:Name="BoothResolutionTabIndicator"
|
||||
Background="#66CCFF"
|
||||
CornerRadius="6"
|
||||
Width="70" Height="38"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Center"
|
||||
Margin="0,0,0,0"/>
|
||||
<Grid>
|
||||
<Button x:Name="BtnBoothResolution720" Width="70" Height="40" Background="Transparent" BorderThickness="0"
|
||||
Click="BoothResolutionTab_Click" Tag="1280,720" Cursor="Hand" HorizontalAlignment="Left">
|
||||
<TextBlock Text="720p" FontSize="12" FontWeight="SemiBold" Foreground="{DynamicResource FloatBarForeground}" Opacity="0.7"/>
|
||||
</Button>
|
||||
<Button x:Name="BtnBoothResolution1080" Width="70" Height="40" Background="Transparent" BorderThickness="0"
|
||||
Click="BoothResolutionTab_Click" Tag="1920,1080" Cursor="Hand" HorizontalAlignment="Left" Margin="70,0,0,0">
|
||||
<TextBlock x:Name="TbBoothResolution1080" Text="1080p" FontSize="12" FontWeight="Bold" Foreground="White"/>
|
||||
</Button>
|
||||
<Button x:Name="BtnBoothResolution2K" Width="70" Height="40" Background="Transparent" BorderThickness="0"
|
||||
Click="BoothResolutionTab_Click" Tag="2560,1440" Cursor="Hand" HorizontalAlignment="Left" Margin="140,0,0,0">
|
||||
<TextBlock Text="2K" FontSize="12" FontWeight="SemiBold" Foreground="{DynamicResource FloatBarForeground}" Opacity="0.7"/>
|
||||
</Button>
|
||||
<Button x:Name="BtnBoothResolution4K" Width="70" Height="40" Background="Transparent" BorderThickness="0"
|
||||
Click="BoothResolutionTab_Click" Tag="3840,2160" Cursor="Hand" HorizontalAlignment="Left" Margin="210,0,0,0">
|
||||
<TextBlock Text="4K" FontSize="12" FontWeight="SemiBold" Foreground="{DynamicResource FloatBarForeground}" Opacity="0.7"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Border x:Name="TimerContainer"
|
||||
Visibility="Collapsed"
|
||||
|
||||
@@ -73,7 +73,10 @@ namespace Ink_Canvas
|
||||
// 全屏处理状态标志
|
||||
public bool isFullScreenApplied = false;
|
||||
|
||||
|
||||
private int _boothResolutionWidth = 1920;
|
||||
private int _boothResolutionHeight = 1080;
|
||||
public int BoothResolutionWidth => _boothResolutionWidth;
|
||||
public int BoothResolutionHeight => _boothResolutionHeight;
|
||||
|
||||
private static Cursor _cachedPenCursor = null;
|
||||
private static readonly object _cursorLock = new object();
|
||||
@@ -1763,6 +1766,24 @@ namespace Ink_Canvas
|
||||
{
|
||||
SystemEvents.DisplaySettingsChanged -= SystemEventsOnDisplaySettingsChanged;
|
||||
|
||||
try
|
||||
{
|
||||
// 清理视频展台资源
|
||||
if (_cameraService != null)
|
||||
{
|
||||
_cameraService.FrameReceived -= CameraService_FrameReceived;
|
||||
_cameraService.ErrorOccurred -= CameraService_ErrorOccurred;
|
||||
_cameraService.Dispose();
|
||||
_cameraService = null;
|
||||
}
|
||||
lock (_videoPresenterFrameLock)
|
||||
{
|
||||
_lastFrame?.Dispose();
|
||||
_lastFrame = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
|
||||
|
||||
// 释放PPT管理器资源
|
||||
DisposePPTManagers();
|
||||
|
||||
@@ -2997,6 +3018,62 @@ namespace Ink_Canvas
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 展台/白板分辨率切换
|
||||
private const int BoothResolutionTabCount = 4;
|
||||
private static readonly (int w, int h)[] BoothResolutionValues = { (1280, 720), (1920, 1080), (2560, 1440), (3840, 2160) };
|
||||
|
||||
private void BoothResolutionTab_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button btn && btn.Tag is string tag)
|
||||
{
|
||||
var parts = tag.Split(',');
|
||||
if (parts.Length == 2 && int.TryParse(parts[0].Trim(), out int w) && int.TryParse(parts[1].Trim(), out int h) && w > 0 && h > 0)
|
||||
{
|
||||
_boothResolutionWidth = w;
|
||||
_boothResolutionHeight = h;
|
||||
UpdateBoothResolutionTabState();
|
||||
SyncBoothResolutionToCameraService();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBoothResolutionTabState()
|
||||
{
|
||||
int index = 0;
|
||||
for (int i = 0; i < BoothResolutionValues.Length; i++)
|
||||
{
|
||||
if (BoothResolutionValues[i].w == _boothResolutionWidth && BoothResolutionValues[i].h == _boothResolutionHeight)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (BoothResolutionTabIndicator != null)
|
||||
{
|
||||
BoothResolutionTabIndicator.Margin = new Thickness(index * 70, 0, 0, 0);
|
||||
}
|
||||
|
||||
var texts = new[] { BtnBoothResolution720?.Content as TextBlock, BtnBoothResolution1080?.Content as TextBlock, BtnBoothResolution2K?.Content as TextBlock, BtnBoothResolution4K?.Content as TextBlock };
|
||||
for (int i = 0; i < texts.Length && i < 4; i++)
|
||||
{
|
||||
if (texts[i] == null) continue;
|
||||
if (i == index)
|
||||
{
|
||||
texts[i].FontWeight = FontWeights.Bold;
|
||||
texts[i].Foreground = new SolidColorBrush(Colors.White);
|
||||
texts[i].Opacity = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
texts[i].FontWeight = FontWeights.SemiBold;
|
||||
texts[i].SetResourceReference(TextBlock.ForegroundProperty, "FloatBarForeground");
|
||||
texts[i].Opacity = 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
private void ToggleSwitchEnableInkToShape_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Ink_Canvas
|
||||
/// </summary>
|
||||
/// <param name="isBackupMain">为 true 时将导出结果保存到主备份槽(索引 0);为 false 时保存到当前白板索引。</param>
|
||||
/// <remarks>
|
||||
/// - 会提交画布上缺失于历史记录的 Image/MediaElement 和缺失的墨迹;
|
||||
/// - 会提交画布上缺失于历史记录的 Image/MediaElement(但跳过 Tag 等于 VideoPresenterLiveFrameTag 的 Image)和缺失的墨迹;
|
||||
/// - 导出后把结果存入 TimeMachineHistories 的相应索引,并保存当前多指书写模式到 savedMultiTouchModeStates;
|
||||
/// - 导出后会清除时间机器的临时墨迹历史以释放内存。
|
||||
/// - 此方法有副作用:修改 TimeMachineHistories、savedMultiTouchModeStates,并通过 timeMachine 的提交方法改变其内部历史状态。
|
||||
@@ -85,6 +85,10 @@ namespace Ink_Canvas
|
||||
{
|
||||
if (child is Image || child is MediaElement || child is PdfEmbeddedView)
|
||||
{
|
||||
if (child is Image img && img.Tag is string tag && tag == VideoPresenterLiveFrameTag)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!elementsInHistory.Contains(child))
|
||||
{
|
||||
timeMachine.CommitElementInsertHistory(child);
|
||||
@@ -441,12 +445,14 @@ namespace Ink_Canvas
|
||||
currentSelectedElement = null;
|
||||
}
|
||||
|
||||
VideoPresenter_BeforePageLeave();
|
||||
SaveStrokes();
|
||||
|
||||
ClearStrokes(true);
|
||||
CurrentWhiteboardIndex--;
|
||||
|
||||
RestoreStrokes();
|
||||
VideoPresenter_OnPageChanged();
|
||||
|
||||
UpdateIndexInfoDisplay();
|
||||
}
|
||||
@@ -484,12 +490,14 @@ namespace Ink_Canvas
|
||||
currentSelectedElement = null;
|
||||
}
|
||||
|
||||
VideoPresenter_BeforePageLeave();
|
||||
SaveStrokes();
|
||||
|
||||
ClearStrokes(true);
|
||||
CurrentWhiteboardIndex++;
|
||||
|
||||
RestoreStrokes();
|
||||
VideoPresenter_OnPageChanged();
|
||||
|
||||
UpdateIndexInfoDisplay();
|
||||
}
|
||||
@@ -525,6 +533,7 @@ namespace Ink_Canvas
|
||||
currentSelectedElement = null;
|
||||
}
|
||||
|
||||
VideoPresenter_BeforePageLeave();
|
||||
SaveStrokes();
|
||||
ClearStrokes(true);
|
||||
|
||||
@@ -540,9 +549,12 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
// 确保新页面的历史记录为空
|
||||
TimeMachineHistories[CurrentWhiteboardIndex] = null;
|
||||
|
||||
// 恢复新页面(这会清空画布,因为历史记录为null)
|
||||
RestoreStrokes();
|
||||
VideoPresenter_OnPageChanged();
|
||||
|
||||
UpdateIndexInfoDisplay();
|
||||
|
||||
|
||||
@@ -3391,6 +3391,7 @@ namespace Ink_Canvas
|
||||
switch (++currentMode % 2)
|
||||
{
|
||||
case 0: //屏幕模式
|
||||
VideoPresenter_OnExitWhiteboardMode();
|
||||
currentMode = 0;
|
||||
GridBackgroundCover.Visibility = Visibility.Collapsed;
|
||||
AnimationsHelper.HideWithSlideAndFade(BlackboardLeftSide);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -151,6 +151,10 @@ namespace Ink_Canvas
|
||||
[JsonProperty("enableVelocityBrushTip")]
|
||||
public bool EnableVelocityBrushTip { get; set; }
|
||||
|
||||
/// <summary>为 true 时,白板工具栏「展台」按钮启动希沃视频展台(sweclauncher),否则使用内置展台。</summary>
|
||||
[JsonProperty("launchSeewoVideoShowcaseForWhiteboardBooth")]
|
||||
public bool LaunchSeewoVideoShowcaseForWhiteboardBooth { get; set; } = false;
|
||||
|
||||
}
|
||||
|
||||
public enum OptionalOperation
|
||||
|
||||
@@ -96,6 +96,13 @@
|
||||
SwitchName="ToggleSwitchCompressPicturesUploaded"
|
||||
Toggled="ToggleSwitchCompressPicturesUploaded_Toggled" />
|
||||
|
||||
<controls:LabeledSettingsCard x:Name="CardLaunchSeewoVideoShowcaseForWhiteboardBooth"
|
||||
Header="{i18n:I18n Key=Canvas_LaunchSeewoVideoShowcaseForWhiteboardBooth}"
|
||||
Description="{i18n:I18n Key=Canvas_LaunchSeewoVideoShowcaseForWhiteboardBoothHint}"
|
||||
Icon="{x:Static ui:SegoeFluentIcons.Video}"
|
||||
SwitchName="ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth"
|
||||
Toggled="ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth_Toggled" />
|
||||
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Canvas_KeepHyperbolaAsymptote}"
|
||||
Description="{i18n:I18n Key=Canvas_HyperbolaAsymptoteHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
CardClearCanvasAndClearTimeMachine.IsOn = settings.Canvas.ClearCanvasAndClearTimeMachine;
|
||||
CardClearCanvasAlsoClearImages.IsOn = settings.Canvas.ClearCanvasAlsoClearImages;
|
||||
CardCompressPicturesUploaded.IsOn = settings.Canvas.IsCompressPicturesUploaded;
|
||||
CardLaunchSeewoVideoShowcaseForWhiteboardBooth.IsOn = settings.Canvas.LaunchSeewoVideoShowcaseForWhiteboardBooth;
|
||||
ComboBoxHyperbolaAsymptoteOption.SelectedIndex = (int)settings.Canvas.HyperbolaAsymptoteOption;
|
||||
CardShowCircleCenter.IsOn = settings.Canvas.ShowCircleCenter;
|
||||
int curveMode = 0;
|
||||
@@ -175,6 +176,13 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
SettingsManager.SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
SettingsManager.Settings.Canvas.LaunchSeewoVideoShowcaseForWhiteboardBooth = CardLaunchSeewoVideoShowcaseForWhiteboardBooth.IsOn;
|
||||
SettingsManager.SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ComboBoxHyperbolaAsymptoteOption_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (!_isLoaded) return;
|
||||
|
||||
Reference in New Issue
Block a user