Skip to content

Commit cc2fc5e

Browse files
authored
Merge branch 'main' into feature/sl-1397-drawing-view-full-image
2 parents bedf54b + be32188 commit cc2fc5e

File tree

64 files changed

+585
-390
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+585
-390
lines changed

.github/workflows/benchmarks.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,16 @@ env:
1616
LATEST_NET_VERSION: '9.0.x'
1717
PathToCommunityToolkitAnalyzersBenchmarkCsproj: 'src/CommunityToolkit.Maui.Analyzers.Benchmarks/CommunityToolkit.Maui.Analyzers.Benchmarks.csproj'
1818

19+
concurrency:
20+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
21+
cancel-in-progress: true
22+
1923
jobs:
2024
run_benchmarks:
2125
name: Run Benchmarks
2226
runs-on: ${{ matrix.os }}
2327
strategy:
28+
fail-fast: false
2429
matrix:
2530
os: [windows-latest, macos-15]
2631

.github/workflows/dotnet-build.yml

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ env:
3232
PathToCommunityToolkitMediaElementCsproj: 'src/CommunityToolkit.Maui.MediaElement/CommunityToolkit.Maui.MediaElement.csproj'
3333
PathToCommunityToolkitMapsCsproj: 'src/CommunityToolkit.Maui.Maps/CommunityToolkit.Maui.Maps.csproj'
3434
PathToCommunityToolkitSampleCsproj: 'samples/CommunityToolkit.Maui.Sample/CommunityToolkit.Maui.Sample.csproj'
35-
PathToCommunityToolkitUnitTestCsproj: 'src/CommunityToolkit.Maui.UnitTests/CommunityToolkit.Maui.UnitTests.csproj'
35+
PathToCommunityToolkitUnitTestCsproj: 'src/CommunityToolkit.Maui.UnitTests'
3636
PathToCommunityToolkitAnalyzersCsproj: 'src/CommunityToolkit.Maui.Analyzers/CommunityToolkit.Maui.Analyzers.csproj'
3737
PathToCommunityToolkitCameraAnalyzersCsproj: 'src/CommunityToolkit.Maui.Camera.Analyzers/CommunityToolkit.Maui.Camera.Analyzers.csproj'
3838
PathToCommunityToolkitMediaElementAnalyzersCsproj: 'src/CommunityToolkit.Maui.MediaElement.Analyzers/CommunityToolkit.Maui.MediaElement.Analyzers.csproj'
@@ -41,16 +41,21 @@ env:
4141
PathToCommunityToolkitAnalyzersCodeFixCsproj: 'src/CommunityToolkit.Maui.Analyzers.CodeFixes/CommunityToolkit.Maui.Analyzers.CodeFixes.csproj'
4242
PathToCommunityToolkitCameraAnalyzersCodeFixCsproj: 'src/CommunityToolkit.Maui.Camera.Analyzers.CodeFixes/CommunityToolkit.Maui.Camera.Analyzers.CodeFixes.csproj'
4343
PathToCommunityToolkitMediaElementAnalyzersCodeFixCsproj: 'src/CommunityToolkit.Maui.MediaElement.Analyzers.CodeFixes/CommunityToolkit.Maui.MediaElement.Analyzers.CodeFixes.csproj'
44-
PathToCommunityToolkitAnalyzersUnitTestCsproj: 'src/CommunityToolkit.Maui.Analyzers.UnitTests/CommunityToolkit.Maui.Analyzers.UnitTests.csproj'
44+
PathToCommunityToolkitAnalyzersUnitTestCsproj: 'src/CommunityToolkit.Maui.Analyzers.UnitTests'
4545
PathToCommunityToolkitAnalyzersBenchmarkCsproj: 'src/CommunityToolkit.Maui.Analyzers.Benchmarks/CommunityToolkit.Maui.Analyzers.Benchmarks.csproj'
4646
CommunityToolkitSampleApp_Xcode_Version: '16.2'
4747
CommunityToolkitLibrary_Xcode_Version: '16.2'
4848

49+
concurrency:
50+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
51+
cancel-in-progress: true
52+
4953
jobs:
5054
build_sample:
5155
name: Build Sample App using Latest .NET SDK
5256
runs-on: ${{ matrix.os }}
5357
strategy:
58+
fail-fast: false
5459
matrix:
5560
os: [windows-latest, macos-15]
5661
steps:
@@ -90,9 +95,8 @@ jobs:
9095
build_library:
9196
name: Build Library
9297
runs-on: ${{ matrix.os }}
93-
env:
94-
VSTEST_TESTHOST_SHUTDOWN_TIMEOUT: 1100 # Fixes "The active test run was aborted. Reason: Test host process crashed"
9598
strategy:
99+
fail-fast: false
96100
matrix:
97101
os: [windows-latest, macos-15]
98102
steps:
@@ -189,16 +193,24 @@ jobs:
189193
- name: 'Build CommunityToolkit.Maui'
190194
run: dotnet build ${{ env.PathToLibrarySolution }} -c Release -p:PackageVersion=${{ env.NugetPackageVersion }} -p:Version=${{ env.NugetPackageVersion }}
191195

192-
- name: Run All Unit Tests
193-
run: dotnet test -c Release ${{ env.PathToLibrarySolution }} --settings ".runsettings" --collect "XPlat code coverage" --logger trx --results-directory ${{ runner.temp }} --logger GitHubActions
196+
- name: Run CommunityToolkit Analyzers UnitTests
197+
run: |
198+
cd ${{ env.PathToCommunityToolkitAnalyzersUnitTestCsproj }}
199+
dotnet run -c Release --results-directory "${{ runner.temp }}" --coverage --coverage-output "${{ runner.temp }}/ut-analyzers.cobertura.xml" --coverage-output-format cobertura --report-xunit
200+
201+
- name: Run CommunityToolkit UnitTests
202+
run: |
203+
cd ${{ env.PathToCommunityToolkitUnitTestCsproj }}
204+
dotnet run -c Release --results-directory "${{ runner.temp }}" --coverage --coverage-output "${{ runner.temp }}/ut.cobertura.xml" --coverage-output-format cobertura --report-xunit
194205
195206
- name: Publish Test Results
196-
if: runner.os == 'Windows'
207+
if: runner.os == 'Windows' && (${{ success() || failure() }})
197208
uses: actions/upload-artifact@v4
198209
with:
199-
name: Test Results
210+
name: Test Results ${{ github.run_number }} ${{ runner.os }}
200211
path: |
201-
${{ runner.temp }}/**/*.trx
212+
${{ runner.temp }}/*.xunit
213+
${{ runner.temp }}/*cobertura.xml
202214
203215
- name: Pack CommunityToolkit.Maui.Core NuGet
204216
run: dotnet pack -c Release ${{ env.PathToCommunityToolkitCoreCsproj }} -p:PackageVersion=${{ env.NugetPackageVersion }}

samples/CommunityToolkit.Maui.Sample.sln

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ EndProject
1212
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3ED2C978-9DDB-48FE-8C5A-521B254F18A3}"
1313
ProjectSection(SolutionItems) = preProject
1414
..\.editorconfig = ..\.editorconfig
15+
..\.github\workflows\benchmarks.yml = ..\.github\workflows\benchmarks.yml
1516
..\Directory.Build.props = ..\Directory.Build.props
1617
..\Directory.Build.targets = ..\Directory.Build.targets
18+
..\.github\workflows\dotnet-build.yml = ..\.github\workflows\dotnet-build.yml
1719
..\global.json = ..\global.json
1820
EndProjectSection
1921
EndProject

samples/CommunityToolkit.Maui.Sample/Pages/Alerts/SnackbarPage.xaml

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
<pages:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
33
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
44
xmlns:pages="clr-namespace:CommunityToolkit.Maui.Sample.Pages"
5+
xmlns:alertPages="clr-namespace:CommunityToolkit.Maui.Sample.Pages.Alerts"
56
xmlns:mct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
6-
x:Class="CommunityToolkit.Maui.Sample.Pages.Alerts.SnackbarPage"
77
xmlns:vm="clr-namespace:CommunityToolkit.Maui.Sample.ViewModels.Alerts"
8+
x:Class="CommunityToolkit.Maui.Sample.Pages.Alerts.SnackbarPage"
89
x:TypeArguments="vm:SnackbarViewModel"
910
x:DataType="vm:SnackbarViewModel">
1011

@@ -14,26 +15,34 @@
1415
</ResourceDictionary>
1516
</pages:BasePage.Resources>
1617

17-
<VerticalStackLayout Spacing="12">
18-
19-
<Label Text="The Snackbar is a timed alert that appears at the bottom of the screen by default. It is dismissed after a configurable duration of time. Snackbar is fully customizable and can be anchored to any IView."
20-
LineBreakMode = "WordWrap" />
18+
<Grid RowSpacing="12"
19+
RowDefinitions="70,20,40,40,40,20">
20+
<Label Grid.Row="0"
21+
Text="The Snackbar is a timed alert that appears at the bottom of the screen by default. It is dismissed after a configurable duration of time. Snackbar is fully customizable and can be anchored to any IView."
22+
HorizontalTextAlignment="Justify"
23+
LineBreakMode = "WordWrap" />
2124

22-
<Label Text="Windows uses toast notifications to display snackbar. Make sure you switched off Focus Assist."
25+
<Label Grid.Row="1"
26+
Text="NOTE: Windows uses toast notifications to display snackbar. Be sure you've switched off Focus Assist."
2327
IsVisible="{OnPlatform Default='false', WinUI='true'}"/>
2428

25-
<Button Clicked="DisplayDefaultSnackbarButtonClicked"
26-
Text="Display Default Snackbar"/>
29+
<Button Grid.Row="2"
30+
Clicked="DisplayDefaultSnackbarButtonClicked"
31+
Text = "Display Default Snackbar"/>
2732

28-
<Button x:Name="DisplayCustomSnackbarButton"
29-
Clicked="DisplayCustomSnackbarButtonClicked"
30-
TextColor="{Binding Source={RelativeSource Self}, Path=BackgroundColor, Converter={StaticResource ColorToColorForTextConverter}, x:DataType=Button}"/>
33+
<Button Grid.Row="3"
34+
x:Name="DisplayCustomSnackbarButtonAnchoredToButton"
35+
Clicked="DisplayCustomSnackbarAnchoredToButtonClicked"
36+
Text="{x:Static alertPages:SnackbarPage.DisplayCustomSnackbarText}"
37+
TextColor="{Binding Source={RelativeSource Self}, Path=BackgroundColor, Converter={StaticResource ColorToColorForTextConverter}, x:DataType=Button}"/>
3138

32-
<Button x:Name="DisplaySnackbarInModalButton"
39+
<Button Grid.Row="4"
40+
x:Name="DisplaySnackbarInModalButton"
3341
Text="Show Snackbar in Modal Page"
3442
Clicked="DisplaySnackbarInModalButtonClicked"/>
3543

36-
<Label x:Name="SnackbarShownStatus" />
37-
</VerticalStackLayout>
44+
<Label Grid.Row="5"
45+
x:Name="SnackbarShownStatus" />
46+
</Grid>
3847

3948
</pages:BasePage>

samples/CommunityToolkit.Maui.Sample/Pages/Alerts/SnackbarPage.xaml.cs

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,21 @@ namespace CommunityToolkit.Maui.Sample.Pages.Alerts;
1212

1313
public partial class SnackbarPage : BasePage<SnackbarViewModel>
1414
{
15-
const string displayCustomSnackbarText = "Display a Custom Snackbar, Anchored to this Button";
15+
public const string DisplayCustomSnackbarText = "Display Custom Snackbar";
1616
const string dismissCustomSnackbarText = "Dismiss Custom Snackbar";
17-
readonly IReadOnlyList<Color> colors = typeof(Colors)
17+
18+
readonly IReadOnlyList<Color> colors = [.. typeof(Colors)
1819
.GetFields(BindingFlags.Static | BindingFlags.Public)
1920
.ToDictionary(c => c.Name, c => (Color)(c.GetValue(null) ?? throw new InvalidOperationException()))
20-
.Values.ToList();
21+
.Values];
2122

2223
ISnackbar? customSnackbar;
2324

2425
public SnackbarPage(SnackbarViewModel snackbarViewModel) : base(snackbarViewModel)
2526
{
2627
InitializeComponent();
2728

28-
DisplayCustomSnackbarButton.Text = displayCustomSnackbarText;
29+
DisplayCustomSnackbarButtonAnchoredToButton.Text = DisplayCustomSnackbarText;
2930

3031
Snackbar.Shown += Snackbar_Shown;
3132
Snackbar.Dismissed += Snackbar_Dismissed;
@@ -34,9 +35,9 @@ public SnackbarPage(SnackbarViewModel snackbarViewModel) : base(snackbarViewMode
3435
async void DisplayDefaultSnackbarButtonClicked(object? sender, EventArgs args) =>
3536
await this.DisplaySnackbar("This is a Snackbar.\nIt will disappear in 3 seconds.\nOr click OK to dismiss immediately");
3637

37-
async void DisplayCustomSnackbarButtonClicked(object? sender, EventArgs args)
38+
async void DisplayCustomSnackbarAnchoredToButtonClicked(object? sender, EventArgs args)
3839
{
39-
if (DisplayCustomSnackbarButton.Text is displayCustomSnackbarText)
40+
if (DisplayCustomSnackbarButtonAnchoredToButton.Text is DisplayCustomSnackbarText)
4041
{
4142
var options = new SnackbarOptions
4243
{
@@ -52,20 +53,20 @@ async void DisplayCustomSnackbarButtonClicked(object? sender, EventArgs args)
5253
"This is a customized Snackbar",
5354
async () =>
5455
{
55-
await DisplayCustomSnackbarButton.BackgroundColorTo(colors[Random.Shared.Next(colors.Count)], length: 500);
56-
DisplayCustomSnackbarButton.Text = displayCustomSnackbarText;
56+
await DisplayCustomSnackbarButtonAnchoredToButton.BackgroundColorTo(colors[Random.Shared.Next(colors.Count)], length: 500);
57+
DisplayCustomSnackbarButtonAnchoredToButton.Text = DisplayCustomSnackbarText;
5758
},
5859
FontAwesomeIcons.Microsoft,
5960
TimeSpan.FromSeconds(30),
6061
options,
61-
DisplayCustomSnackbarButton);
62+
DisplayCustomSnackbarButtonAnchoredToButton);
6263

6364
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
6465
await customSnackbar.Show(cts.Token);
6566

66-
DisplayCustomSnackbarButton.Text = dismissCustomSnackbarText;
67+
DisplayCustomSnackbarButtonAnchoredToButton.Text = dismissCustomSnackbarText;
6768
}
68-
else if (DisplayCustomSnackbarButton.Text is dismissCustomSnackbarText)
69+
else if (DisplayCustomSnackbarButtonAnchoredToButton.Text is dismissCustomSnackbarText)
6970
{
7071
if (customSnackbar is not null)
7172
{
@@ -75,11 +76,11 @@ async void DisplayCustomSnackbarButtonClicked(object? sender, EventArgs args)
7576
customSnackbar.Dispose();
7677
}
7778

78-
DisplayCustomSnackbarButton.Text = displayCustomSnackbarText;
79+
DisplayCustomSnackbarButtonAnchoredToButton.Text = DisplayCustomSnackbarText;
7980
}
8081
else
8182
{
82-
throw new NotSupportedException($"{nameof(DisplayCustomSnackbarButton)}.{nameof(ITextButton.Text)} Not Recognized");
83+
throw new NotSupportedException($"{nameof(DisplayCustomSnackbarButtonAnchoredToButton)}.{nameof(ITextButton.Text)} Not Recognized");
8384
}
8485
}
8586

@@ -97,6 +98,20 @@ async void DisplaySnackbarInModalButtonClicked(object? sender, EventArgs e)
9798
{
9899
if (Application.Current?.Windows[0].Page is Page mainPage)
99100
{
101+
var button = new Button()
102+
.CenterHorizontal()
103+
.Text("Display Snackbar");
104+
button.Command = new AsyncRelayCommand(token => button.DisplaySnackbar(
105+
"This Snackbar is anchored to the button on the bottom to avoid clipping the Snackbar on the top of the Page.",
106+
() => { },
107+
"Close",
108+
TimeSpan.FromSeconds(5), token: token));
109+
110+
var backButton = new Button()
111+
.CenterHorizontal()
112+
.Text("Back to Snackbar MainPage");
113+
backButton.Command = new AsyncRelayCommand(mainPage.Navigation.PopModalAsync);
114+
100115
await mainPage.Navigation.PushModalAsync(new ContentPage
101116
{
102117
Content = new VerticalStackLayout
@@ -105,19 +120,11 @@ await mainPage.Navigation.PushModalAsync(new ContentPage
105120

106121
Children =
107122
{
108-
new Button { Command = new AsyncRelayCommand(static token => Snackbar.Make("Snackbar in a Modal MainPage").Show(token)) }
109-
.Top().CenterHorizontal()
110-
.Text("Display Snackbar"),
123+
button,
111124

112-
new Label()
113-
.Center().TextCenter()
114-
.Text("This is a Modal MainPage"),
115-
116-
new Button { Command = new AsyncRelayCommand(mainPage.Navigation.PopModalAsync) }
117-
.Bottom().CenterHorizontal()
118-
.Text("Back to Snackbar MainPage")
125+
backButton
119126
}
120-
}.Center()
127+
}
121128
}.Padding(12));
122129
}
123130
}

samples/CommunityToolkit.Maui.Sample/Pages/Base/BasePage.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
using System.Diagnostics;
22
using CommunityToolkit.Maui.Sample.ViewModels;
3+
using Microsoft.Maui.Controls.PlatformConfiguration;
4+
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
35

46
namespace CommunityToolkit.Maui.Sample.Pages;
57

6-
public abstract class BasePage<TViewModel>(TViewModel viewModel) : BasePage(viewModel)
8+
public abstract class BasePage<TViewModel>(TViewModel viewModel, bool shouldUseSafeArea = true) : BasePage(viewModel, shouldUseSafeArea)
79
where TViewModel : BaseViewModel
810
{
911
public new TViewModel BindingContext => (TViewModel)base.BindingContext;
1012
}
1113

1214
public abstract class BasePage : ContentPage
1315
{
14-
protected BasePage(object? viewModel = null)
16+
protected BasePage(object? viewModel = null, bool shouldUseSafeArea = true)
1517
{
1618
BindingContext = viewModel;
1719
Padding = 12;
1820

21+
On<iOS>().SetUseSafeArea(shouldUseSafeArea);
22+
1923
if (string.IsNullOrWhiteSpace(Title))
2024
{
2125
Title = GetType().Name;

src/CommunityToolkit.Maui.Analyzers.UnitTests/CommunityToolkit.Maui.Analyzers.UnitTests.csproj

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,35 @@
22

33
<PropertyGroup>
44
<TargetFramework>$(NetVersion)</TargetFramework>
5-
<IsPackable>false</IsPackable>
65
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
76
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GF</CompilerGeneratedFilesOutputPath>
87
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
98
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
9+
10+
<OutputType>Exe</OutputType>
11+
<RootNamespace>CommunityToolkit.Maui.Analyzers.UnitTests</RootNamespace>
12+
13+
<TestingPlatformDotnetTestSupport>false</TestingPlatformDotnetTestSupport>
14+
<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
1015
</PropertyGroup>
1116

1217
<ItemGroup>
13-
<PackageReference Include="FluentAssertions" Version="8.0.1" />
14-
<PackageReference Include="FluentAssertions.Analyzers" Version="0.34.1" />
15-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.2" />
16-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.2" />
17-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
18-
<PackageReference Include="xunit" Version="2.9.3" />
19-
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.1" PrivateAssets="All" />
20-
<PackageReference Include="coverlet.collector" Version="6.0.4" PrivateAssets="All" />
18+
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest"/>
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.2" />
23+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.2" />
24+
<PackageReference Include="xunit.v3" Version="1.0.1" />
25+
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" Version="17.13.1" />
2126
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0" />
22-
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiPackageVersion)" />
23-
<PackageReference Include="GitHubActionsTestLogger" Version="2.4.1" PrivateAssets="All" />
2427
</ItemGroup>
2528

2629
<ItemGroup>
2730
<!--Fix vulnerabilities-->
2831
<PackageReference Include="System.Formats.Asn1" Version="9.0.1" />
32+
<PackageReference Include="System.Net.Http" Version="4.3.4" />
33+
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
2934
</ItemGroup>
3035

3136
<ItemGroup>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json"
3+
}

src/CommunityToolkit.Maui.Camera/AppBuilderExtensions.shared.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ namespace CommunityToolkit.Maui;
1010
/// </summary>
1111
[SupportedOSPlatform("windows10.0.10240.0")]
1212
[SupportedOSPlatform("android21.0")]
13-
[SupportedOSPlatform("ios")]
14-
[SupportedOSPlatform("maccatalyst")]
15-
[SupportedOSPlatform("tizen")]
13+
[SupportedOSPlatform("ios15.0")]
14+
[SupportedOSPlatform("maccatalyst15.0")]
15+
[SupportedOSPlatform("tizen6.5")]
1616
public static class AppBuilderExtensions
1717
{
1818
/// <summary>

0 commit comments

Comments
 (0)