Skip to content

Commit

Permalink
Merge branch 'main' into dev/gif-in-release
Browse files Browse the repository at this point in the history
  • Loading branch information
mattleibow committed Jun 8, 2024
2 parents 595ff03 + e94b364 commit 4b3c01f
Show file tree
Hide file tree
Showing 103 changed files with 1,310 additions and 641 deletions.
48 changes: 2 additions & 46 deletions docs/ReleasePlanning.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,3 @@
Throughout the year we add issues to the `Backlog` milestone as is pointed out in our [Triage Process](TriageProcess.md).
We review all the issues in that milestone once a year, after the work on an upcoming major release is complete.
Given the large number of issues, it takes multiple sessions for teams to review and identify candidates for consideration for the next major release.
This document details the process we use for identifying candidate issues for the next release.
# .NET MAUI Roadmap

## Phases
The process for identifying candidates for the next major release consists of multiple phases. In each phase, we filter issues out of the release by either moving them to the `Backlog`, or closing the issue.
- Filtering & Individual prioritization
- Rough costing
- Team review & Priority adjustment
- Capacity planning
- Define the cut line

### Filtering
At this stage all the issues are distributed to engineers by feature areas. Each engineer reviews all the issues within their feature area, and returns to the next meeting with individual priority labels assigned - fl-p1, fl-p2, fl-p3, where `fl` are their initials.

All the issues which the engineer believes are lower than `Priority-3` - remain in the backlog. We also agree to approximately balance the distribution of the 3 priority labels on the issues that will be brought back by each engineer, so that it forces real prioritization exercise.
The issues which engineers think are good candidates and fit in the above listed requirements are moved to the `.NET V Planning` milestone, where `V` is the upcoming version number.

### Rough costing
At this phase engineers apply rough cost estimates to the final list of issues that they have moved to the `.NET V Planning` milestone, by applying one of the `Cost: X` labels below, where `X` is the size:

| **Label** | **Description** |
|--------------|---------------------------------------------------|
| **Cost: S** | Work that requires one engineer up to 1 week |
| **Cost: M** | Work that requires one engineer up to 2 weeks |
| **Cost: L** | Work that requires one engineer up to 4 weeks |
| **Cost: XL** | Work that requires one engineer more than 4 weeks |

This will be used later during the planning process.

For issues which don't have a clear description of the associated work, it's important to drop a comment summarizing the work involved. This will help at a later time, in case a question about the cost will be raised.

**Note**: while costing issues, it's important to reevaluate costs for those, which already have cost labels applied. Those are most probably from the past and may be outdated, not properly representing the cost any more.

### Team Review & Priority adjustment
Now, that all the issues are in the `.NET V planning` milestone, the team reviews each issue one at a time starting from the highest priority ones (Priority: 1).
We discuss the issues and agree on the priority at this point. Sometimes we make adjustments to the suggested individual priorities. After discussing each issue the `Priority: X` label is applied to each issue.
Each `Priority: 1` issue is then moved to the project board, which will be used by each team for tracking the work for the upcoming release throughout the year. The issues start off in the `Triage` column. At this point we bring only the top priority issues to the board.

### Capacity Planning
We usually reserve only 50% of the team capacity for this work. The reason is that we will be getting a lot of incoming feedback throughout the year and we need to allocate time for handling this feedback throughout the year.
So we calculate the capacity of the team in weeks for the upcoming year and use half of the final number later in this process.

### Define the cut line
At this point we have all the candidate issues that we think are worth considering for the upcoming release. This number is quite large, so the teams usually won't have enough capacity to handle all this.
We start stack ranking issues so the most important work remains on the top of the list. We then draw the cut line and that defines the rough list of things the team will work on during the upcoming release.
[Roadmap](https://github.com/dotnet/maui/wiki/Roadmap)
17 changes: 1 addition & 16 deletions docs/ReleaseSchedule.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
# .NET MAUI Release Schedule Information

Versions of .NET MAUI are being released in sync with new .NET versions. More information on the .NET release policy can be found [here](https://dotnet.microsoft.com/platform/support/policy/dotnet-core).

## Past .NET MAUI Releases

Below you can find a list of all the previous releases of .NET MAUI, excluding pre-releases.
For a full list, including release notes, please refer to our [Releases page](https://github.com/dotnet/maui/releases).

| Version | Release Date |
|---------|--------------|
| [6.0.536 (Service Release 4.1)](https://github.com/dotnet/maui/releases/tag/6.0.536) | 2022/09/14 |
| [6.0.486 (Service Release 4)](https://github.com/dotnet/maui/releases/tag/6.0.486) | 2022/08/09 |
| [6.0.424 (Service Release 3.1)](https://github.com/dotnet/maui/releases/tag/6.0.424) | 2022/08/01 |
| [6.0.419 (Service Release 3)](https://github.com/dotnet/maui/releases/tag/6.0.419) | 2022/07/20 |
| [6.0.408 (Service Release 2)](https://github.com/dotnet/maui/releases/tag/6.0.408) | 2022/07/12 |
| [6.0.400](https://github.com/dotnet/maui/releases/tag/6.0.400) | 2022/06/14 |
| [6.0.312](https://github.com/dotnet/maui/releases/tag/6.0.312) | 2022/05/23 |
[Release Versions](https://github.com/dotnet/maui/wiki/Release-Versions)
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<!-- The .NET product branding version -->
<ProductVersion>8.0.60</ProductVersion>
<ProductVersion>8.0.70</ProductVersion>
<MajorVersion>8</MajorVersion>
<MinorVersion>0</MinorVersion>
<PatchVersion>60</PatchVersion>
Expand Down
14 changes: 8 additions & 6 deletions eng/scripts/appium-install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,15 @@ if (!(Test-Path $logsDir -PathType Container)) {
$AppiumHome = $env:APPIUM_HOME
Write-Output "APPIUM_HOME: $AppiumHome"

if (Test-Path $AppiumHome) {
Write-Output "Removing existing APPIUM_HOME Cache..."
Remove-Item -Path $AppiumHome -Recurse -Force
}
if ($AppiumHome) {
if (Test-Path $AppiumHome) {
Write-Output "Removing existing APPIUM_HOME Cache..."
Remove-Item -Path $AppiumHome -Recurse -Force
}

# Create the directory for appium home
New-Item -ItemType Directory -Path $AppiumHome
# Create the directory for appium home
New-Item -ItemType Directory -Path $AppiumHome
}

# Check for an existing appium install version
$appiumCurrentVersion = ""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable disable
using System;
using System.ComponentModel;
using CoreGraphics;
using ObjCRuntime;
Expand Down Expand Up @@ -26,20 +27,20 @@ public static UIBarButtonItem ToUIBarButtonItem(this ToolbarItem item, bool forc
sealed class PrimaryToolbarItem : UIBarButtonItem
{
readonly bool _forceName;
readonly ToolbarItem _item;
readonly WeakReference<ToolbarItem> _item;

public PrimaryToolbarItem(ToolbarItem item, bool forceName)
{
_forceName = forceName;
_item = item;
_item = new(item);

if (item.IconImageSource != null && !item.IconImageSource.IsEmpty && !forceName)
UpdateIconAndStyle();
UpdateIconAndStyle(item);
else
UpdateTextAndStyle();
UpdateIsEnabled();
UpdateTextAndStyle(item);
UpdateIsEnabled(item);

Clicked += (sender, e) => ((IMenuItemController)_item).Activate();
Clicked += OnClicked;
item.PropertyChanged += OnPropertyChanged;

if (item != null && !string.IsNullOrEmpty(item.AutomationId))
Expand All @@ -49,65 +50,76 @@ public PrimaryToolbarItem(ToolbarItem item, bool forceName)
this.SetAccessibilityLabel(item);
}

void OnClicked (object sender, EventArgs e)
{
if (_item.TryGetTarget(out var item))
{
((IMenuItemController)item).Activate();
}
}

protected override void Dispose(bool disposing)
{
if (disposing)
_item.PropertyChanged -= OnPropertyChanged;
if (disposing && _item.TryGetTarget(out var item))
item.PropertyChanged -= OnPropertyChanged;
base.Dispose(disposing);
}

void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!_item.TryGetTarget(out var item))
return;

if (e.PropertyName == MenuItem.IsEnabledProperty.PropertyName)
UpdateIsEnabled();
UpdateIsEnabled(item);
else if (e.PropertyName == MenuItem.TextProperty.PropertyName)
{
if (_item.IconImageSource == null || _item.IconImageSource.IsEmpty || _forceName)
UpdateTextAndStyle();
if (item.IconImageSource == null || item.IconImageSource.IsEmpty || _forceName)
UpdateTextAndStyle(item);
}
else if (e.PropertyName == MenuItem.IconImageSourceProperty.PropertyName)
{
if (!_forceName)
{
if (_item.IconImageSource != null && !_item.IconImageSource.IsEmpty)
UpdateIconAndStyle();
if (item.IconImageSource != null && !item.IconImageSource.IsEmpty)
UpdateIconAndStyle(item);
else
UpdateTextAndStyle();
UpdateTextAndStyle(item);
}
}
#pragma warning disable CS0618 // Type or member is obsolete
else if (e.PropertyName == AutomationProperties.HelpTextProperty.PropertyName)
this.SetAccessibilityHint(_item);
this.SetAccessibilityHint(item);
else if (e.PropertyName == AutomationProperties.NameProperty.PropertyName)
this.SetAccessibilityLabel(_item);
this.SetAccessibilityLabel(item);
#pragma warning restore CS0618 // Type or member is obsolete
}

void UpdateIconAndStyle()
void UpdateIconAndStyle(ToolbarItem item)
{
if (_item?.IconImageSource == null)
if (item?.IconImageSource == null)
{
Image = null;
Style = UIBarButtonItemStyle.Plain;
}
else
{
_item.IconImageSource.LoadImage(_item.FindMauiContext(), result =>
item.IconImageSource.LoadImage(item.FindMauiContext(), result =>
{
Image = result?.Value;
Style = UIBarButtonItemStyle.Plain;
});
}
}

void UpdateIsEnabled()
void UpdateIsEnabled(ToolbarItem item)
{
Enabled = _item.IsEnabled;
Enabled = item.IsEnabled;
}

void UpdateTextAndStyle()
void UpdateTextAndStyle(ToolbarItem item)
{
Title = _item.Text;
Title = item.Text;
#pragma warning disable CA1416, CA1422 // TODO: [UnsupportedOSPlatform("ios8.0")]
Style = UIBarButtonItemStyle.Bordered;
#pragma warning restore CA1416, CA1422
Expand All @@ -117,16 +129,16 @@ void UpdateTextAndStyle()

sealed class SecondaryToolbarItem : UIBarButtonItem
{
readonly ToolbarItem _item;
readonly WeakReference<ToolbarItem> _item;

public SecondaryToolbarItem(ToolbarItem item) : base(new SecondaryToolbarItemContent())
{
_item = item;
UpdateText();
UpdateIcon();
UpdateIsEnabled();
_item = new(item);
UpdateText(item);
UpdateIcon(item);
UpdateIsEnabled(item);

((SecondaryToolbarItemContent)CustomView).TouchUpInside += (sender, e) => ((IMenuItemController)_item).Activate();
((SecondaryToolbarItemContent)CustomView).TouchUpInside += OnClicked;
item.PropertyChanged += OnPropertyChanged;

if (item != null && !string.IsNullOrEmpty(item.AutomationId))
Expand All @@ -136,34 +148,45 @@ public SecondaryToolbarItem(ToolbarItem item) : base(new SecondaryToolbarItemCon
this.SetAccessibilityLabel(item);
}

void OnClicked (object sender, EventArgs e)
{
if (_item.TryGetTarget(out var item))
{
((IMenuItemController)item).Activate();
}
}

protected override void Dispose(bool disposing)
{
if (disposing)
_item.PropertyChanged -= OnPropertyChanged;
if (disposing && _item.TryGetTarget(out var item))
item.PropertyChanged -= OnPropertyChanged;
base.Dispose(disposing);
}

void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!_item.TryGetTarget(out var item))
return;

if (e.PropertyName == MenuItem.TextProperty.PropertyName)
UpdateText();
UpdateText(item);
else if (e.PropertyName == MenuItem.IconImageSourceProperty.PropertyName)
UpdateIcon();
UpdateIcon(item);
else if (e.PropertyName == MenuItem.IsEnabledProperty.PropertyName)
UpdateIsEnabled();
UpdateIsEnabled(item);
#pragma warning disable CS0618 // Type or member is obsolete
else if (e.PropertyName == AutomationProperties.HelpTextProperty.PropertyName)
this.SetAccessibilityHint(_item);
this.SetAccessibilityHint(item);
else if (e.PropertyName == AutomationProperties.NameProperty.PropertyName)
#pragma warning restore CS0618 // Type or member is obsolete
this.SetAccessibilityLabel(_item);
this.SetAccessibilityLabel(item);
}

void UpdateIcon()
void UpdateIcon(ToolbarItem item)
{
if (_item.IconImageSource != null && !_item.IconImageSource.IsEmpty)
if (item.IconImageSource != null && !item.IconImageSource.IsEmpty)
{
_item.IconImageSource.LoadImage(_item.FindMauiContext(), result =>
item.IconImageSource.LoadImage(item.FindMauiContext(), result =>
{
((SecondaryToolbarItemContent)CustomView).Image = result?.Value;
});
Expand All @@ -174,14 +197,14 @@ void UpdateIcon()
}
}

void UpdateIsEnabled()
void UpdateIsEnabled(ToolbarItem item)
{
((UIControl)CustomView).Enabled = _item.IsEnabled;
((UIControl)CustomView).Enabled = item.IsEnabled;
}

void UpdateText()
void UpdateText(ToolbarItem item)
{
((SecondaryToolbarItemContent)CustomView).Text = _item.Text;
((SecondaryToolbarItemContent)CustomView).Text = item.Text;
}

sealed class SecondaryToolbarItemContent : UIControl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,12 @@ protected override void BindTemplatedItemViewHolder(TemplatedItemViewHolder temp

void UpdateHasHeader()
{
ItemsSource.HasHeader = ItemsView.Header != null;
ItemsSource.HasHeader = (ItemsView.Header ?? ItemsView.HeaderTemplate) is not null;
}

void UpdateHasFooter()
{
ItemsSource.HasFooter = ItemsView.Footer != null;
ItemsSource.HasFooter = (ItemsView.Footer ?? ItemsView.FooterTemplate) is not null;
}

bool IsHeader(int position)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ public static void MapCurrentItem(CarouselViewHandler handler, CarouselView caro

public static void MapPosition(CarouselViewHandler handler, CarouselView carouselView)
{
(handler.Controller as CarouselViewController)?.UpdateFromPosition();
// If the initial position hasn't been set, we have a UpdateInitialPosition call on CarouselViewController
// that will handle this so we want to skip this mapper call. We need to wait for the CollectionView to be ready
if(handler.Controller is CarouselViewController carouselViewController && carouselViewController.InitialPositionSet)
{
carouselViewController.UpdateFromPosition();
}
}

public static void MapLoop(CarouselViewHandler handler, CarouselView carouselView)
Expand Down
Loading

0 comments on commit 4b3c01f

Please sign in to comment.