Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into feature/load-writea…
Browse files Browse the repository at this point in the history
…ble-bitmap-from-file

# Conflicts:
#	src/Avalonia.Controls/ApiCompatBaseline.txt
#	src/Avalonia.Visuals/ApiCompatBaseline.txt
  • Loading branch information
danwalmsley committed May 1, 2021
2 parents 42b35e2 + fa20271 commit fd68976
Show file tree
Hide file tree
Showing 108 changed files with 2,484 additions and 926 deletions.
2 changes: 2 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
## Breaking changes
<!--- List any breaking changes here. When the PR is merged please add an entry to https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes -->

## Obsoletions / Deprecations
<!--- Obsolete and Deprecated attributes on APIs MUST only be included when discussed with Core team. @grokys, @kekekeks & @danwalmsley -->

## Fixed issues
<!--- If the pull request fixes issue(s) list them like this:
Expand Down
2 changes: 0 additions & 2 deletions Avalonia.sln
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.Vnc", "sr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.Events", "src\Avalonia.ReactiveUI.Events\Avalonia.ReactiveUI.Events.csproj", "{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sandbox", "samples\Sandbox\Sandbox.csproj", "{11BE52AF-E2DD-4CF0-B19A-05285ACAF571}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicroComGenerator", "src\tools\MicroComGenerator\MicroComGenerator.csproj", "{AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}"
Expand Down
37 changes: 0 additions & 37 deletions nukebuild/Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
using Nuke.Common.Tools.Npm;
using Nuke.Common.Utilities;
using Nuke.Common.Utilities.Collections;
using Pharmacist.Core;
using static Nuke.Common.EnvironmentInfo;
using static Nuke.Common.IO.FileSystemTasks;
using static Nuke.Common.IO.PathConstruction;
Expand Down Expand Up @@ -163,44 +162,8 @@ IReadOnlyCollection<Output> MsBuildCommon(
.AddProperty("PackageVersion", Parameters.Version)
.SetConfiguration(Parameters.Configuration)
);
await CompileReactiveEvents();
});

async Task CompileReactiveEvents()
{
var avaloniaBuildOutput = Path.Combine(RootDirectory, "packages", "Avalonia", "bin", Parameters.Configuration);
var avaloniaAssemblies = GlobFiles(avaloniaBuildOutput, "**/Avalonia*.dll")
.Where(file => !file.Contains("Avalonia.Build.Tasks") &&
!file.Contains("Avalonia.Remote.Protocol"));

var eventsDirectory = GlobDirectories($"{RootDirectory}/src/**/Avalonia.ReactiveUI.Events").First();
var eventsBuildFile = Path.Combine(eventsDirectory, "Events_Avalonia.cs");
if (File.Exists(eventsBuildFile))
File.Delete(eventsBuildFile);

using (var stream = File.Create(eventsBuildFile))
using (var writer = new StreamWriter(stream))
{
await ObservablesForEventGenerator.ExtractEventsFromAssemblies(
writer, avaloniaAssemblies, new string[0], "netstandard2.0"
);
}

var eventsProject = Path.Combine(eventsDirectory, "Avalonia.ReactiveUI.Events.csproj");
if (Parameters.IsRunningOnWindows)
MsBuildCommon(eventsProject, c => c
.SetProcessArgumentConfigurator(a => a.Add("/r"))
.AddTargets("Build")
);
else
DotNetBuild(c => c
.SetProjectFile(eventsProject)
.AddProperty("PackageVersion", Parameters.Version)
.SetConfiguration(Parameters.Configuration)
);
}

void RunCoreTest(string projectName)
{
Information($"Running tests from {projectName}");
Expand Down
1 change: 0 additions & 1 deletion nukebuild/_build.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
<PackageReference Include="ILRepack.NETStandard" Version="2.0.4" />
<!-- Keep in sync with Avalonia.Build.Tasks -->
<PackageReference Include="Mono.Cecil" Version="0.11.2" />
<PackageReference Include="Pharmacist.Core" Version="1.8.1" />
</ItemGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions samples/ControlCatalog/Pages/ContextMenuPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<CheckBox BorderThickness="0" IsHitTestVisible="False" IsChecked="True"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Menu Item that won't close on click" StaysOpenOnClick="True" />
</ContextMenu>
</Border.ContextMenu>
<TextBlock Text="Defined in XAML"/>
Expand Down
14 changes: 14 additions & 0 deletions src/Avalonia.Base/Data/Optional.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,18 @@ public TResult GetValueOrDefault<TResult>([AllowNull] TResult defaultValue)
/// </summary>
public static Optional<T> Empty => default;
}

public static class OptionalExtensions
{
/// <summary>
/// Casts the type of an <see cref="Optional{T}"/> using only the C# cast operator.
/// </summary>
/// <typeparam name="T">The target type.</typeparam>
/// <param name="value">The binding value.</param>
/// <returns>The cast value.</returns>
public static Optional<T> Cast<T>(this Optional<object> value)
{
return value.HasValue ? new Optional<T>((T)value.Value) : Optional<T>.Empty;
}
}
}
4 changes: 2 additions & 2 deletions src/Avalonia.Base/PropertyStore/BindingEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ public void RaiseValueChanged(
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,
(AvaloniaProperty<T>)property,
oldValue.GetValueOrDefault<T>(),
newValue.GetValueOrDefault<T>(),
oldValue.Cast<T>(),
newValue.Cast<T>(),
Priority));
}

Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ public void RaiseValueChanged(
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,
(AvaloniaProperty<T>)property,
oldValue.GetValueOrDefault<T>(),
newValue.GetValueOrDefault<T>(),
oldValue.Cast<T>(),
newValue.Cast<T>(),
Priority));
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public void RaiseValueChanged(
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,
(AvaloniaProperty<T>)property,
oldValue.GetValueOrDefault<T>(),
newValue.GetValueOrDefault<T>(),
oldValue.Cast<T>(),
newValue.Cast<T>(),
BindingPriority.LocalValue));
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/Avalonia.Base/PropertyStore/PriorityValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ public void RaiseValueChanged(
sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
owner,
(AvaloniaProperty<T>)property,
oldValue.GetValueOrDefault<T>(),
newValue.GetValueOrDefault<T>(),
oldValue.Cast<T>(),
newValue.Cast<T>(),
Priority));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,43 @@ public static DataGridSortDescription FromPath(string propertyPath, ListSortDire
{
return new DataGridPathSortDescription(propertyPath, direction, comparer, null);
}

public static DataGridSortDescription FromComparer(IComparer comparer, ListSortDirection direction = ListSortDirection.Ascending)
{
return new DataGridComparerSortDesctiption(comparer, direction);
}
}

public class DataGridComparerSortDesctiption : DataGridSortDescription
{
private readonly IComparer _innerComparer;
private readonly ListSortDirection _direction;
private readonly IComparer<object> _comparer;

public IComparer SourceComparer => _innerComparer;
public override IComparer<object> Comparer => _comparer;
public override ListSortDirection Direction => _direction;
public DataGridComparerSortDesctiption(IComparer comparer, ListSortDirection direction)
{
_innerComparer = comparer;
_direction = direction;
_comparer = Comparer<object>.Create((x, y) => Compare(x, y));
}

private int Compare(object x, object y)
{
int result = _innerComparer.Compare(x, y);

if (Direction == ListSortDirection.Descending)
return -result;
else
return result;
}
public override DataGridSortDescription SwitchSortDirection()
{
var newDirection = _direction == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
return new DataGridComparerSortDesctiption(_innerComparer, newDirection);
}
}

public class DataGridSortDescriptionCollection : AvaloniaList<DataGridSortDescription>
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Controls.DataGrid/DataGrid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5357,7 +5357,7 @@ private void ResetFocusedRow()
_focusedRow = null;
}

private void SelectAll()
public void SelectAll()
{
SetRowsSelection(0, SlotCount - 1);
}
Expand Down
16 changes: 16 additions & 0 deletions src/Avalonia.Controls.DataGrid/DataGridColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,14 @@ public string SortMemberPath
get;
set;
}
/// <summary>
/// Holds a Comparer to use for sorting, if not using the default.
/// </summary>
public System.Collections.IComparer CustomSortComparer
{
get;
set;
}

/// <summary>
/// We get the sort description from the data source. We don't worry whether we can modify sort -- perhaps the sort description
Expand All @@ -1020,6 +1028,14 @@ internal DataGridSortDescription GetSortDescription()
&& OwningGrid.DataConnection != null
&& OwningGrid.DataConnection.SortDescriptions != null)
{
if(CustomSortComparer != null)
{
return
OwningGrid.DataConnection.SortDescriptions
.OfType<DataGridComparerSortDesctiption>()
.FirstOrDefault(s => s.SourceComparer == CustomSortComparer);
}

string propertyName = GetSortPropertyName();

return OwningGrid.DataConnection.SortDescriptions.FirstOrDefault(s => s.HasPropertyPath && s.PropertyPath == propertyName);
Expand Down
6 changes: 6 additions & 0 deletions src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,12 @@ internal void ProcessSort(KeyModifiers keyModifiers)
owningGrid.DataConnection.SortDescriptions.Add(newSort);
}
}
else if (OwningColumn.CustomSortComparer != null)
{
newSort = DataGridSortDescription.FromComparer(OwningColumn.CustomSortComparer);

owningGrid.DataConnection.SortDescriptions.Add(newSort);
}
else
{
string propertyName = OwningColumn.GetSortPropertyName();
Expand Down
5 changes: 4 additions & 1 deletion src/Avalonia.Controls/ApiCompatBaseline.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
Compat issues with assembly Avalonia.Controls:
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.IMenuItem.StaysOpenOnClick' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.IMenuItem.StaysOpenOnClick.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.IMenuItem.StaysOpenOnClick.set(System.Boolean)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseClosed()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseOpening()' is present in the implementation but not in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
Expand All @@ -7,4 +10,4 @@ EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
Total Issues: 8
Total Issues: 11
2 changes: 1 addition & 1 deletion src/Avalonia.Controls/AutoCompleteBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ namespace Avalonia.Controls
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populated" />
/// event.
/// </summary>
[PseudoClasses(":dropdownopen")]
public class PopulatedEventArgs : EventArgs
{
/// <summary>
Expand Down Expand Up @@ -253,6 +252,7 @@ public enum AutoCompleteFilterMode
/// drop-down that contains possible matches based on the input in the text
/// box.
/// </summary>
[PseudoClasses(":dropdownopen")]
public class AutoCompleteBox : TemplatedControl
{
/// <summary>
Expand Down
2 changes: 0 additions & 2 deletions src/Avalonia.Controls/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ public class Control : InputElement, IControl, INamed, IVisualBrushInitialize, I
/// <summary>
/// Defines the <see cref="ContextMenu"/> property.
/// </summary>
[Obsolete("Prefer ContextFlyout")]
public static readonly StyledProperty<ContextMenu?> ContextMenuProperty =
AvaloniaProperty.Register<Control, ContextMenu?>(nameof(ContextMenu));

Expand Down Expand Up @@ -77,7 +76,6 @@ public ITemplate<IControl>? FocusAdorner
/// <summary>
/// Gets or sets a context menu to the control.
/// </summary>
[Obsolete("Prefer ContextFlyout")]
public ContextMenu? ContextMenu
{
get => GetValue(ContextMenuProperty);
Expand Down
6 changes: 6 additions & 0 deletions src/Avalonia.Controls/IMenuItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ public interface IMenuItem : IMenuElement
/// </summary>
bool IsSubMenuOpen { get; set; }

/// <summary>
/// Gets or sets a value that indicates the submenu that this <see cref="MenuItem"/> is
/// within should not close when this item is clicked.
/// </summary>
bool StaysOpenOnClick { get; set; }

/// <summary>
/// Gets a value that indicates whether the <see cref="MenuItem"/> is a top-level main menu item.
/// </summary>
Expand Down
25 changes: 17 additions & 8 deletions src/Avalonia.Controls/ItemsControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ static ItemsControl()
/// </summary>
public ItemsControl()
{
PseudoClasses.Add(":empty");
UpdatePseudoClasses(0);
SubscribeToItems(_items);
}

Expand Down Expand Up @@ -323,6 +323,16 @@ protected override void OnKeyDown(KeyEventArgs e)
base.OnKeyDown(e);
}

protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);

if (change.Property == ItemCountProperty)
{
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<int>());
}
}

/// <summary>
/// Called when the <see cref="Items"/> property changes.
/// </summary>
Expand Down Expand Up @@ -371,10 +381,6 @@ protected virtual void ItemsCollectionChanged(object sender, NotifyCollectionCha
}

Presenter?.ItemsChanged(e);

var collection = sender as ICollection;
PseudoClasses.Set(":empty", collection == null || collection.Count == 0);
PseudoClasses.Set(":singleitem", collection != null && collection.Count == 1);
}

/// <summary>
Expand Down Expand Up @@ -431,9 +437,6 @@ private void RemoveControlItemsFromLogicalChildren(IEnumerable items)
/// <param name="items">The items collection.</param>
private void SubscribeToItems(IEnumerable items)
{
PseudoClasses.Set(":empty", items == null || items.Count() == 0);
PseudoClasses.Set(":singleitem", items != null && items.Count() == 1);

if (items is INotifyCollectionChanged incc)
{
CollectionChangedEventManager.Instance.AddListener(incc, this);
Expand Down Expand Up @@ -469,6 +472,12 @@ private void UpdateItemCount()
}
}

private void UpdatePseudoClasses(int itemCount)
{
PseudoClasses.Set(":empty", itemCount == 0);
PseudoClasses.Set(":singleitem", itemCount == 1);
}

protected static IInputElement GetNextControl(
INavigableContainer container,
NavigationDirection direction,
Expand Down
16 changes: 16 additions & 0 deletions src/Avalonia.Controls/MenuItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable, I
public static readonly StyledProperty<bool> IsSubMenuOpenProperty =
AvaloniaProperty.Register<MenuItem, bool>(nameof(IsSubMenuOpen));

/// <summary>
/// Defines the <see cref="StaysOpenOnClick"/> property.
/// </summary>
public static readonly StyledProperty<bool> StaysOpenOnClickProperty =
AvaloniaProperty.Register<MenuItem, bool>(nameof(StaysOpenOnClick));

/// <summary>
/// Defines the <see cref="Click"/> event.
/// </summary>
Expand Down Expand Up @@ -265,6 +271,16 @@ public bool IsSubMenuOpen
set { SetValue(IsSubMenuOpenProperty, value); }
}

/// <summary>
/// Gets or sets a value that indicates the submenu that this <see cref="MenuItem"/> is
/// within should not close when this item is clicked.
/// </summary>
public bool StaysOpenOnClick
{
get { return GetValue(StaysOpenOnClickProperty); }
set { SetValue(StaysOpenOnClickProperty, value); }
}

/// <summary>
/// Gets or sets a value that indicates whether the <see cref="MenuItem"/> has a submenu.
/// </summary>
Expand Down
Loading

0 comments on commit fd68976

Please sign in to comment.