Skip to content

Commit

Permalink
Improve capture options edit
Browse files Browse the repository at this point in the history
  • Loading branch information
fgilde committed Nov 5, 2024
1 parent 92812c0 commit d6bac87
Show file tree
Hide file tree
Showing 20 changed files with 448 additions and 209 deletions.
152 changes: 97 additions & 55 deletions MudBlazor.Extensions/Components/MudExCaptureOptionsEdit.razor
Original file line number Diff line number Diff line change
@@ -1,67 +1,109 @@
@using MudBlazor.Extensions.Core
@using MudBlazor.Extensions.Components.ObjectEdit
@inherits MudExBaseComponent<MudExCaptureOptionsEdit>


<div class="@Class" style="@GetStyleStr()">
@if (Value != null)
{
<MudStack Style="margin: 10px;" StretchItems="StretchItems.All" Row="true">
<MudStack Style="width: 300px;">
<MudPaper Class="pa-3">Item 1</MudPaper>
<MudPaper Class="pa-3">Item 2</MudPaper>
<MudPaper Class="pa-3">Item 3</MudPaper>
</MudStack>
<MudStack>
<MudExGroupBox Text="@TryLocalize("Video")" Class="pa-3">
<MudAutocomplete T="string" @bind-Value="@Value.ContentType"
Label="@TryLocalize("VideoContentType")"
Dense="@Dense"
Disabled="@(string.IsNullOrEmpty(Value?.VideoDevice?.DeviceId))"
ReadOnly="@ReadOnly"
ResetValueOnEmptyText="true"
CoerceText="false"
CoerceValue="false"
Placeholder="@TryLocalize("VideoContentType")"
SearchFunc="@SearchVideoContentType"
Clearable="true" />

<p>@TryLocalize("Video device")</p>
<MudExSelect T="VideoDevice"
ItemCollection="@_videoDevices"
Value="@Value.VideoDevice"
ValueChanged="@(v => SetAndStateChange(o => o.VideoDevice = v))"
MultiSelection="false"
SearchBox="true"
SearchBoxVariant="Variant.Text"
Dense="@Dense"
SelectAll="false"></MudExSelect>
<MudExSplitPanel SplitterSize="1" UpdateSizesInPercentage="true" Splittable="false" ColumnLayout="true" Style="margin-bottom: 50px;">
<Left>
<MudExGroupBox Text="@TryLocalize("Screen Capture")" Class="pa-3" Style="width: 60%">

<MudSwitch Style="margin: 7px;" Color="Color.Primary" UncheckedColor="Color.Default" T="bool" @bind-Value="@_captureScreen" Label="@TryLocalize("Capture Screen")" />

@if (_captureScreen)
{
<MudPaper Class="ma-3 pa-3" Elevation="3" Style="cursor: pointer;" @onclick="@(SelectScreen)">
<MudGrid Class="mt-1 mb-2" Justify="Justify.Center">
@if (_track != null)
{
<MudFab Style="position: absolute; margin-left: 200px;" Color="Color.Error" OnClick="@StopPreviewTrack" StartIcon="@Icons.Material.Filled.Close" Size="Size.Small" />
}
<MudPaper Class="ma-3 pa-3" Style="width: 200px; height: 200px; border: 3px dotted gray">
<video @ref="_preview" style="@GetPreviewStyle()"></video>
</MudPaper>
</MudGrid>
<MudText Typo="Typo.subtitle1">@TryLocalize("Select Screen")</MudText>
<MudText Typo="Typo.body2">@TryLocalize("Click here to choose a screen to capture. If not selected, you will be prompted when starting the recording.")</MudText>
</MudPaper>

@if (!string.IsNullOrEmpty(Value?.VideoDevice?.DeviceId))
{
<MudExEnumSelect Class="ma-3" Variant="Variant.Outlined" @bind-Value="Value.OverlaySource" Label="@TryLocalize("Overlay Source")"></MudExEnumSelect>
<MudExEnumSelect Class="ml-3 mr-3 mb-1" Variant="Variant.Outlined" @bind-Value="Value.OverlayPosition" Label="@TryLocalize("Overlay Position")"></MudExEnumSelect>
}

<MudText Typo="Typo.body2" Color="Color.Warning" Class="pl-6" Style="font-size: 0.875em;">
@TryLocalize("Note: Following options can't changed after a screen is selected")
</MudText>
<MudCheckBox Disabled="@(_track != null)" @bind-Value="_recordSystemAudio" T="bool" Label="@TryLocalize("Record System Audio")" Class="mt-3 ml-2" />
<MudText Typo="Typo.body2" Color="Color.Secondary" Class="pl-6" Style="font-size: 0.875em;">
@TryLocalize("Note: System audio recording is supported only in certain browsers and typically for browser tabs only.")
</MudText>

<MudGrid Class="mt-6" Justify="Justify.FlexEnd">
<MudFab Disabled="@(_track != null)" StartIcon="@Icons.Material.Filled.ScreenShare" OnClick="@ChangeMediaOptions" Label="@TryLocalize("Change media options")" />
</MudGrid>
}

</MudExGroupBox>
<MudExGroupBox Text="@TryLocalize("Audio")" Class="pa-3">
<MudAutocomplete T="string" @bind-Value="@Value.AudioContentType"
Label="@TryLocalize("AudioContentType")"

</Left>
<Right>
<MudStack StretchItems="StretchItems.End" Style="width: 40%">
<MudExGroupBox Text="@TryLocalize("Additional Video")" Class="pa-3">
<MudAutocomplete T="string" @bind-Value="@Value.ContentType"
Label="@TryLocalize("VideoContentType")"
Dense="@Dense"
Disabled="@(string.IsNullOrEmpty(Value?.VideoDevice?.DeviceId))"
ReadOnly="@ReadOnly"
ResetValueOnEmptyText="true"
CoerceText="false"
CoerceValue="false"
Placeholder="@TryLocalize("VideoContentType")"
SearchFunc="@SearchVideoContentType"
Clearable="true" />

<p>@TryLocalize("Video device")</p>
<MudExSelect T="VideoDevice"
ItemCollection="@_videoDevices"
Value="@Value.VideoDevice"
ValueChanged="@(v => SetAndStateChange(o => o.VideoDevice = v))"
MultiSelection="false"
SearchBox="true"
SearchBoxVariant="Variant.Text"
Dense="@Dense"
Disabled="@((Value.AudioDevices?.Count ?? 0) <= 0)"
ReadOnly="@ReadOnly"
ResetValueOnEmptyText="true"
CoerceText="false"
CoerceValue="false"
Placeholder="@TryLocalize("AudioContentType")"
SearchFunc="@SearchAudioContentType"
Clearable="true"/>
SelectAll="false"></MudExSelect>
</MudExGroupBox>
<MudExGroupBox Text="@TryLocalize("Additional Audio")" Class="pa-3">
<MudAutocomplete T="string" @bind-Value="@Value.AudioContentType"
Label="@TryLocalize("AudioContentType")"
Dense="@Dense"
Disabled="@((Value.AudioDevices?.Count ?? 0) <= 0)"
ReadOnly="@ReadOnly"
ResetValueOnEmptyText="true"
CoerceText="false"
CoerceValue="false"
Placeholder="@TryLocalize("AudioContentType")"
SearchFunc="@SearchAudioContentType"
Clearable="true" />

<p>@TryLocalize("Audio devices")</p>
<MudExList T="AudioDevice" ItemCollection="@_audioDevices"
SelectedValues="@Value.AudioDevices"
MultiSelection="true"
SelectedItemsChanged="@AudioDevicesChanged"
SearchBox="true"
SearchBoxBackgroundColor="@("var(--mud-palette-surface)")"
SearchBoxVariant="Variant.Text"
Dense="@Dense"
SelectAll="false"
Style="max-height: 300px; overflow-y: auto;"></MudExList>
</MudExGroupBox>
</MudStack>
</MudStack>
<p>@TryLocalize("Audio devices")</p>
<MudExList T="AudioDevice" ItemCollection="@_audioDevices"
SelectedValues="@Value.AudioDevices"
Color="MudExColor.Inherit"
MultiSelection="true"
SelectedItemsChanged="@AudioDevicesChanged"
SearchBox="true"
SearchBoxBackgroundColor="@("var(--mud-palette-surface)")"
SearchBoxVariant="Variant.Text"
Dense="@Dense"
SelectAll="false"
Style="max-height: 400px; overflow-y: auto;"></MudExList>
</MudExGroupBox>
</MudStack>
</Right>
</MudExSplitPanel>
}
</div>
109 changes: 103 additions & 6 deletions MudBlazor.Extensions/Components/MudExCaptureOptionsEdit.razor.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using MudBlazor.Extensions.Components.ObjectEdit;
using MudBlazor.Extensions.Components.ObjectEdit.Options;
using MudBlazor.Extensions.Core;
using MudBlazor.Extensions.Helper;
using MudBlazor.Extensions.Options;
using Nextended.Core;
using Nextended.Core.Extensions;

Expand All @@ -14,13 +16,59 @@ namespace MudBlazor.Extensions.Components;
public partial class MudExCaptureOptionsEdit : IObjectEditorWithCustomPropertyRenderDataFor<CaptureOptions>
{
[Inject] private ICaptureService CaptureService { get; set; }
[Inject] private IDialogService DialogService { get; set; }

Check warning on line 19 in MudBlazor.Extensions/Components/MudExCaptureOptionsEdit.razor.cs

View workflow job for this annotation

GitHub Actions / build_and_deploy_preview_package

'MudExCaptureOptionsEdit.DialogService' hides inherited member 'MudExBaseComponent<MudExCaptureOptionsEdit>.DialogService'. Use the new keyword if hiding was intended.

private List<AudioDevice> _audioDevices = new();
private List<VideoDevice> _videoDevices = new();
private ElementReference _preview;
private CaptureOptions _value;
private DisplayMediaOptions _displayMediaOptions = new();
private MediaStreamTrack _track;
private bool _captureScreen;

private bool _recordSystemAudio
{
get => _displayMediaOptions?.SystemAudio == IncludeExclude.Include;
set
{
(_displayMediaOptions ??= DisplayMediaOptions.Default).SystemAudio = value ? IncludeExclude.Include : IncludeExclude.Exclude;
Value.ScreenCapture = _displayMediaOptions;
}
}

/// <inheritdoc />
[Parameter]
public CaptureOptions Value { get; set; }
public CaptureOptions Value
{
get => GetValue();
set
{
_value = value;
_displayMediaOptions = value.ScreenCapture is { IsT2: true, AsT2: not null } ? value.ScreenCapture.AsT2 : DisplayMediaOptions.Default;
}
}

private CaptureOptions GetValue()
{
_value.CaptureMediaOptions = null;
_value.ScreenSource = null;
_value.ScreenCapture = default;
if (_captureScreen)
{
if (_track is not null)
_value.ScreenSource = _track;
else if (_displayMediaOptions is not null)
_value.ScreenCapture = _displayMediaOptions;
else
_value.ScreenCapture = _captureScreen;
}
if(_value.VideoDevice?.DeviceId is null)
_value.VideoDevice = null;
if(_value.AudioDevices != null && _value.AudioDevices.All(d => d.DeviceId == null))
_value.AudioDevices = null;

return _value;
}

/// <inheritdoc />
[Parameter]
Expand All @@ -32,7 +80,9 @@ public partial class MudExCaptureOptionsEdit : IObjectEditorWithCustomPropertyRe
[Parameter]
public bool ReadOnly { get; set; }


/// <summary>
/// Dense
/// </summary>
[Parameter] public bool Dense { get; set; } = true;

/// <inheritdoc />
Expand All @@ -54,18 +104,18 @@ await Task.WhenAll(
);
}

private Task<IEnumerable<string>> SearchAudioContentType(string value, CancellationToken token)
private Task<IEnumerable<string>> SearchAudioContentType(string value, CancellationToken token)
=> Task.FromResult(MimeType.AudioTypes.Concat(new[] { "audio/webm" }).Distinct().Where(x => string.IsNullOrEmpty(value) || x.Contains(value, StringComparison.InvariantCultureIgnoreCase)));

private Task<IEnumerable<string>> SearchVideoContentType(string value, CancellationToken token)
private Task<IEnumerable<string>> SearchVideoContentType(string value, CancellationToken token)
=> Task.FromResult(MimeType.VideoTypes.Concat(new[] { "video/webm" }).Distinct().Where(x => string.IsNullOrEmpty(value) || x.Contains(value, StringComparison.InvariantCultureIgnoreCase)));

private void AudioDevicesChanged(IEnumerable<MudExListItem<AudioDevice>> obj)
private void AudioDevicesChanged(IEnumerable<MudExListItem<AudioDevice>> obj)
=> SetAndStateChange(o => o.AudioDevices = obj?.Select(x => x.Value).ToList());

private void SetAndStateChange(Action<CaptureOptions> action)
{
(Value ??=new CaptureOptions()).SetProperties(action);
(Value ??= new CaptureOptions()).SetProperties(action);
CallStateHasChanged();
}

Expand All @@ -77,4 +127,51 @@ private string GetStyleStr()
.WithHeight("100%")
.AddRaw(Style).Style;
}

private async Task ChangeMediaOptions(MouseEventArgs obj)
{
var dialogOptionsEx = DialogOptionsEx.DefaultDialogOptions.CloneOptions().SetProperties(o =>
{
o.MaxWidth = MaxWidth.Medium;
o.FullWidth = true;
o.MaxHeight = MaxHeight.Medium;
o.FullHeight = true;
o.Resizeable = true;
o.DragMode = MudDialogDragMode.Simple;
});

var dialogResult = await DialogService.EditObject(_displayMediaOptions, TryLocalize("Change media options"), Icons.Material.Filled.VideoCameraFront, dialogOptionsEx);
if (dialogResult.Cancelled)
return;
_recordSystemAudio = _displayMediaOptions.SystemAudio == IncludeExclude.Include;
}

private string GetPreviewStyle()
{
return MudExStyleBuilder.Default
.WithHeight("100%")
.WithWidth("100%")
.WithOpacity(0, _track == null)
.Style;
}

private async Task SelectScreen()
{
await StopPreviewTrack();
_track = await CaptureService.SelectCaptureSourceAsync(_displayMediaOptions, _preview);
Value.ScreenCapture = _track;
}

private async Task StopPreviewTrack()
{
if (_track is not null)
{
await CaptureService.StopCaptureAsync(_track);
_track = null;
if (_displayMediaOptions is not null)
Value.ScreenCapture = _displayMediaOptions;
else
Value.ScreenCapture = true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@using MudBlazor.Extensions.Helper
@using MudBlazor.Extensions.Options
@using Microsoft.AspNetCore.Components.Rendering
@using Nextended.Core.Extensions
@typeparam T
@inherits MudExBaseComponent<MudExObjectEdit<T>>
<style>
Expand Down Expand Up @@ -113,7 +114,8 @@

if (renderableProperties?.Any() == true)
{
<MudExpansionPanel @ref="@ExpansionPanel" Expanded="true" Disabled="@(!GroupsCollapsible)" Class="@($"mt-3 {(!string.IsNullOrWhiteSpace(groupName) ? CssClassName : "mud-ex-hidden-group")}")" Text="@(!string.IsNullOrWhiteSpace(groupName) ? TryLocalize(groupName) : groupName)">
var groupId = Guid.NewGuid().ToString("N");
<MudExpansionPanel @ref="@ExpansionPanel" Expanded="true" Tag="@groupId" ExpandedChanged="@(b => GroupExpandedChange(groupId, b))" Disabled="@(!GroupsCollapsible)" Class="@($"mt-3 {(!string.IsNullOrWhiteSpace(groupName) ? CssClassName : "mud-ex-hidden-group")}")" Text="@(!string.IsNullOrWhiteSpace(groupName) ? TryLocalize(groupName) : groupName)">
@if (PathDisplayMode == PathDisplayMode.DisplaySeparate && !string.IsNullOrEmpty(pathStr) && pathStr != groupName)
{
<p class="@(GroupingStyle == GroupingStyle.Flat ? "mud-ex-group-path-text-flat" : "mud-ex-group-path-text-default")">@pathStr</p>
Expand Down Expand Up @@ -179,4 +181,4 @@
PropertyMeta="@property">
</MudExPropertyEdit>;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ public partial class MudExObjectEdit<T>
/// </summary>
[Parameter] public bool? ReadOnlyOverwrite { get; set; }

/// <summary>
/// If this is true only one group can be expanded at a time other groups will be collapsed
/// </summary>
[Parameter] public bool SingleExpand { get; set; }

/// <summary>
/// Returns true if we have a registration for the current object that then uses the registered component
/// </summary>
Expand Down Expand Up @@ -865,10 +870,29 @@ private string ToolbarStyle()
return res;
}

private void GroupExpandedChange(string groupId, bool expanded)
{
if (expanded && SingleExpand)
_groups.Where(g => g.Tag?.ToString() != groupId).Apply(g => g.CollapseAsync());
}

//private bool GetIsExpanded(string groupId)
//{
// var existing = _groups.FirstOrDefault(g => g.Tag?.ToString() == groupId);
// if (existing != null)
// return existing.Expanded;
// if (SingleExpand && GroupsCollapsible && _groups.Any(p => p.Expanded))
// return false;
// return true;
//}

private Task ExpandCollapse()
{
var collapse = _groups[0].Expanded;
return Task.WhenAll(_groups.Select(g => collapse ? g.CollapseAsync() : g.ExpandAsync()));
_groups.ForEach(g =>
{
g.ToggleExpansionAsync();
});
return Task.CompletedTask;
}

private IDictionary<string, object> GetAttributesForPrimitive()
Expand Down
Loading

0 comments on commit d6bac87

Please sign in to comment.