Files
voice-cat/clients/apple/VoiceCat.Mac/ScreenAudioPicker.cs
Talon 08e6c5930a
Build and test / test (macos-latest) (push) Canceled after 0s
Build and test / test (ubuntu-24.04) (push) Canceled after 0s
Build and test / test (windows-latest) (push) Canceled after 0s
Build and test / apple-client (push) Canceled after 0s
Retire legacy implementations and flatten managed layout
2026-09-21 00:11:32 +02:00

50 lines
3.3 KiB
C#

using AppKit;
using CoreGraphics;
using Foundation;
using ScreenCaptureKit;
namespace VoiceCat.Mac;
internal sealed record ScreenApplication(string Name, string BundleIdentifier);
internal static class ScreenAudioPicker
{
internal static async Task<ScreenAudioSelection?> ChooseAsync(ScreenAudioSelection initial, CancellationToken cancellationToken = default)
{
IReadOnlyList<ScreenApplication> applications = await ScreenAudioCapture.GetApplicationsAsync(cancellationToken);
var mode = new NSPopUpButton(new CGRect(0, 270, 390, 28), false);
mode.AddItems(["Everything", "Only selected applications", "All except selected applications"]);
mode.SelectItem((int)initial.Scope);
((INSAccessibility)mode).AccessibilityLabel = "Screen audio sharing mode";
var source = new ApplicationSource(applications);
var table = new NSTableView(new CGRect(0, 0, 390, 225)) { DataSource = source, Delegate = source, AllowsMultipleSelection = true, HeaderView = null };
table.AddColumn(new NSTableColumn("application") { Title = "Application", Width = 370 });
((INSAccessibility)table).AccessibilityLabel = "Applications to include or exclude";
uint[] selected = applications.Select((application, index) => initial.BundleIdentifiers.Contains(application.BundleIdentifier) ? (uint?)index : null)
.Where(index => index.HasValue).Select(index => index!.Value).ToArray();
if (selected.Length > 0) table.SelectRows(NSIndexSet.FromArray(selected), false);
var scroll = new NSScrollView(new CGRect(0, 38, 390, 225)) { DocumentView = table, HasVerticalScroller = true, BorderType = NSBorderType.BezelBorder };
var exclude = NSButton.CreateCheckbox("Exclude VoiceOver and speech-synthesis audio", () => { });
exclude.Frame = new CGRect(0, 5, 390, 24); exclude.State = initial.ExcludeScreenReader ? NSCellStateValue.On : NSCellStateValue.Off;
((INSAccessibility)exclude).AccessibilityLabel = "Exclude screen reader audio";
var accessory = new NSView(new CGRect(0, 0, 390, 305)); accessory.AddSubview(mode); accessory.AddSubview(scroll); accessory.AddSubview(exclude);
var alert = new NSAlert { MessageText = "Choose screen audio", InformativeText = "Select what other people will hear.", AccessoryView = accessory };
alert.AddButton("Share"); alert.AddButton("Cancel");
if (alert.RunModal() != 1000) return null;
var bundleIdentifiers = table.SelectedRows.ToArray().Select(index => applications[checked((int)index)].BundleIdentifier).ToHashSet(StringComparer.Ordinal);
return new((ScreenAudioScope)(int)mode.IndexOfSelectedItem, bundleIdentifiers, exclude.State == NSCellStateValue.On);
}
private sealed class ApplicationSource(IReadOnlyList<ScreenApplication> applications) : NSTableViewDataSource, INSTableViewDelegate
{
public override nint GetRowCount(NSTableView tableView) => applications.Count;
[Export("tableView:viewForTableColumn:row:")]
public NSView GetViewForItem(NSTableView tableView, NSTableColumn? tableColumn, nint row)
{
var value = NSTextField.CreateLabel(applications[checked((int)row)].Name);
value.LineBreakMode = AppKit.NSLineBreakMode.TruncatingTail; return value;
}
}
}