diff --git a/Documentation/build.md b/Documentation/build.md
index a7d68eb599..9f5436e68e 100644
--- a/Documentation/build.md
+++ b/Documentation/build.md
@@ -6,6 +6,7 @@ Avalonia requires at least Visual Studio 2019 and .NET Core SDK 3.1 to build on
```
git clone https://github.com/AvaloniaUI/Avalonia.git
+cd Avalonia
git submodule update --init
```
diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets
index 45a7f1aa44..de3830ffea 100644
--- a/packages/Avalonia/AvaloniaBuildTasks.targets
+++ b/packages/Avalonia/AvaloniaBuildTasks.targets
@@ -42,12 +42,24 @@
- $(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources;ResolveReferences
+ $(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources;ResolveReferences;_GenerateAvaloniaResourcesDependencyCache
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs
index ab1aff9220..fea02dabf4 100644
--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs
@@ -3039,6 +3039,12 @@ namespace Avalonia.Controls
}
}
+ //TODO: Ensure right button is checked for
+ internal bool UpdateStateOnMouseRightButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit)
+ {
+ KeyboardHelper.GetMetaKeyState(pointerPressedEventArgs.KeyModifiers, out bool ctrl, out bool shift);
+ return UpdateStateOnMouseRightButtonDown(pointerPressedEventArgs, columnIndex, slot, allowEdit, shift, ctrl);
+ }
//TODO: Ensure left button is checked for
internal bool UpdateStateOnMouseLeftButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit)
{
@@ -4489,17 +4495,27 @@ namespace Avalonia.Controls
element = dataGridColumn.GenerateEditingElementInternal(dataGridCell, dataGridRow.DataContext);
if (element != null)
{
- // Subscribe to the new element's events
- element.Initialized += EditingElement_Initialized;
+
+ dataGridCell.Content = element;
+ if (element.IsInitialized)
+ {
+ PreparingCellForEditPrivate(element as Control);
+ }
+ else
+ {
+ // Subscribe to the new element's events
+ element.Initialized += EditingElement_Initialized;
+ }
}
}
else
{
// Generate Element and apply column style if available
element = dataGridColumn.GenerateElementInternal(dataGridCell, dataGridRow.DataContext);
+ dataGridCell.Content = element;
}
- dataGridCell.Content = element;
+
}
private void PreparingCellForEditPrivate(Control editingElement)
@@ -5711,6 +5727,35 @@ namespace Avalonia.Controls
VerticalScroll?.Invoke(sender, e);
}
+ //TODO: Ensure right button is checked for
+ private bool UpdateStateOnMouseRightButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit, bool shift, bool ctrl)
+ {
+ Debug.Assert(slot >= 0);
+
+ if (shift || ctrl)
+ {
+ return true;
+ }
+ if (IsSlotOutOfBounds(slot))
+ {
+ return true;
+ }
+ if (GetRowSelection(slot))
+ {
+ return true;
+ }
+ // Unselect everything except the row that was clicked on
+ try
+ {
+ UpdateSelectionAndCurrency(columnIndex, slot, DataGridSelectionAction.SelectCurrent, scrollIntoView: false);
+ }
+ finally
+ {
+ NoSelectionChangeCount--;
+ }
+ return true;
+ }
+
//TODO: Ensure left button is checked for
private bool UpdateStateOnMouseLeftButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit, bool shift, bool ctrl)
{
diff --git a/src/Avalonia.Controls.DataGrid/DataGridCell.cs b/src/Avalonia.Controls.DataGrid/DataGridCell.cs
index 7dda936317..e3f150f5c4 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridCell.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridCell.cs
@@ -161,29 +161,42 @@ namespace Avalonia.Controls
private void DataGridCell_PointerPressed(PointerPressedEventArgs e)
{
// OwningGrid is null for TopLeftHeaderCell and TopRightHeaderCell because they have no OwningRow
- if (OwningGrid != null)
+ if (OwningGrid == null)
{
- OwningGrid.OnCellPointerPressed(new DataGridCellPointerPressedEventArgs(this, OwningRow, OwningColumn, e));
- if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
+ return;
+ }
+ OwningGrid.OnCellPointerPressed(new DataGridCellPointerPressedEventArgs(this, OwningRow, OwningColumn, e));
+ if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
+ {
+ if (!e.Handled)
+ //if (!e.Handled && OwningGrid.IsTabStop)
+ {
+ OwningGrid.Focus();
+ }
+ if (OwningRow != null)
{
- if (!e.Handled)
- //if (!e.Handled && OwningGrid.IsTabStop)
+ var handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, ColumnIndex, OwningRow.Slot, !e.Handled);
+
+ // Do not handle PointerPressed with touch,
+ // so we can start scroll gesture on the same event.
+ if (e.Pointer.Type != PointerType.Touch)
{
- OwningGrid.Focus();
+ e.Handled = handled;
}
- if (OwningRow != null)
- {
- var handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, ColumnIndex, OwningRow.Slot, !e.Handled);
-
- // Do not handle PointerPressed with touch,
- // so we can start scroll gesture on the same event.
- if (e.Pointer.Type != PointerType.Touch)
- {
- e.Handled = handled;
- }
- OwningGrid.UpdatedStateOnMouseLeftButtonDown = true;
- }
+ OwningGrid.UpdatedStateOnMouseLeftButtonDown = true;
+ }
+ }
+ else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
+ {
+ if (!e.Handled)
+ //if (!e.Handled && OwningGrid.IsTabStop)
+ {
+ OwningGrid.Focus();
+ }
+ if (OwningRow != null)
+ {
+ e.Handled = OwningGrid.UpdateStateOnMouseRightButtonDown(e, ColumnIndex, OwningRow.Slot, !e.Handled);
}
}
}
diff --git a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs
index a94acdec57..fade597ca1 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs
@@ -233,7 +233,7 @@ namespace Avalonia.Controls
else
{
editableCollectionView.EditItem(dataItem);
- return editableCollectionView.IsEditingItem;
+ return editableCollectionView.IsEditingItem || editableCollectionView.IsAddingNew;
}
}
@@ -314,7 +314,14 @@ namespace Avalonia.Controls
CommittingEdit = true;
try
{
- editableCollectionView.CommitEdit();
+ if (editableCollectionView.IsAddingNew)
+ {
+ editableCollectionView.CommitNew();
+ }
+ else
+ {
+ editableCollectionView.CommitEdit();
+ }
}
finally
{
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRow.cs b/src/Avalonia.Controls.DataGrid/DataGridRow.cs
index 7546970498..1efce7c0b8 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRow.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRow.cs
@@ -378,13 +378,13 @@ namespace Avalonia.Controls
}
}
}
- }
+ }
internal Panel RootElement
{
get;
private set;
- }
+ }
internal int Slot
{
@@ -638,7 +638,7 @@ namespace Avalonia.Controls
PseudoClasses.Set(":editing", IsEditing);
PseudoClasses.Set(":invalid", !IsValid);
ApplyHeaderStatus();
- }
+ }
}
//TODO Animation
@@ -896,7 +896,7 @@ namespace Avalonia.Controls
_detailsElement.ContentHeight = _detailsDesiredHeight;
}
}
- }
+ }
// Makes sure the _detailsDesiredHeight is initialized. We need to measure it to know what
// height we want to animate to. Subsequently, we just update that height in response to SizeChanged
@@ -919,7 +919,7 @@ namespace Avalonia.Controls
//TODO Cleanup
double? _previousDetailsHeight = null;
-
+
//TODO Animation
private void DetailsContent_HeightChanged(double newValue)
{
@@ -1022,7 +1022,7 @@ namespace Avalonia.Controls
}
}
}
-
+
internal void ApplyDetailsTemplate(bool initializeDetailsPreferredHeight)
{
if (_detailsElement != null && AreDetailsVisible)
@@ -1066,7 +1066,7 @@ namespace Avalonia.Controls
.Subscribe(DetailsContent_MarginChanged);
}
-
+
_detailsElement.Children.Add(_detailsContent);
}
}
@@ -1090,6 +1090,28 @@ namespace Avalonia.Controls
}
}
}
+
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ if (change.Property == DataContextProperty)
+ {
+ var owner = OwningGrid;
+ if (owner != null && this.IsRecycled)
+ {
+ var columns = owner.ColumnsItemsInternal;
+ var nc = columns.Count;
+ for (int ci = 0; ci < nc; ci++)
+ {
+ if (columns[ci] is DataGridTemplateColumn column)
+ {
+ column.RefreshCellContent((Control)this.Cells[column.Index].Content, nameof(DataGridTemplateColumn.CellTemplate));
+ }
+ }
+ }
+ }
+ base.OnPropertyChanged(change);
+ }
}
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
index 1e03b134b1..49ca23d34c 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
@@ -283,7 +283,11 @@ namespace Avalonia.Controls
//TODO TabStop
private void DataGridRowGroupHeader_PointerPressed(PointerPressedEventArgs e)
{
- if (OwningGrid != null && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
+ if (OwningGrid == null)
+ {
+ return;
+ }
+ if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
if (OwningGrid.IsDoubleClickRecordsClickOnCall(this) && !e.Handled)
{
@@ -300,6 +304,15 @@ namespace Avalonia.Controls
e.Handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, OwningGrid.CurrentColumnIndex, RowGroupInfo.Slot, allowEdit: false);
}
}
+ else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
+ {
+ if (!e.Handled)
+ {
+ OwningGrid.Focus();
+ }
+ e.Handled = OwningGrid.UpdateStateOnMouseRightButtonDown(e, OwningGrid.CurrentColumnIndex, RowGroupInfo.Slot, allowEdit: false);
+ }
+
}
private void EnsureChildClip(Visual child, double frozenLeftEdge)
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
index 0cd3589a57..510072174f 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
@@ -179,12 +179,12 @@ namespace Avalonia.Controls.Primitives
//TODO TabStop
private void DataGridRowHeader_PointerPressed(object sender, PointerPressedEventArgs e)
{
- if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
+ if (OwningGrid == null)
{
return;
}
- if (OwningGrid != null)
+ if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
if (!e.Handled)
//if (!e.Handled && OwningGrid.IsTabStop)
@@ -199,6 +199,19 @@ namespace Avalonia.Controls.Primitives
OwningGrid.UpdatedStateOnMouseLeftButtonDown = true;
}
}
+ else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
+ {
+ if (!e.Handled)
+ {
+ OwningGrid.Focus();
+ }
+ if (OwningRow != null)
+ {
+ Debug.Assert(sender is DataGridRowHeader);
+ Debug.Assert(sender == this);
+ e.Handled = OwningGrid.UpdateStateOnMouseRightButtonDown(e, -1, Slot, false);
+ }
+ }
}
}
diff --git a/src/Avalonia.Controls/Design.cs b/src/Avalonia.Controls/Design.cs
index 0d05e19e53..07d2918a88 100644
--- a/src/Avalonia.Controls/Design.cs
+++ b/src/Avalonia.Controls/Design.cs
@@ -60,6 +60,19 @@ namespace Avalonia.Controls
return target.GetValue(PreviewWithProperty);
}
+ public static readonly AttachedProperty DesignStyleProperty = AvaloniaProperty
+ .RegisterAttached("DesignStyle", typeof(Design));
+
+ public static void SetDesignStyle(Control control, IStyle value)
+ {
+ control.SetValue(DesignStyleProperty, value);
+ }
+
+ public static IStyle GetDesignStyle(Control control)
+ {
+ return control.GetValue(DesignStyleProperty);
+ }
+
public static void ApplyDesignModeProperties(Control target, Control source)
{
if (source.IsSet(WidthProperty))
@@ -68,6 +81,8 @@ namespace Avalonia.Controls
target.Height = source.GetValue(HeightProperty);
if (source.IsSet(DataContextProperty))
target.DataContext = source.GetValue(DataContextProperty);
+ if (source.IsSet(DesignStyleProperty))
+ target.Styles.Add(source.GetValue(DesignStyleProperty));
}
}
}
diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
index 230b4954fe..4b903d056c 100644
--- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
+++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
@@ -215,11 +215,6 @@ namespace Avalonia.Controls.Primitives
}
}
- if (CancelOpening())
- {
- return false;
- }
-
if (Popup.Parent != null && Popup.Parent != placementTarget)
{
((ISetLogicalParent)Popup).SetParent(null);
@@ -236,6 +231,11 @@ namespace Avalonia.Controls.Primitives
Popup.Child = CreatePresenter();
}
+ if (CancelOpening())
+ {
+ return false;
+ }
+
PositionPopup(showAtPointer);
IsOpen = Popup.IsOpen = true;
OnOpened();
diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
index 209feb351c..e361e7b736 100644
--- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
+++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
@@ -275,7 +275,7 @@ namespace Avalonia.Controls.Platform
return;
}
- if (item.HasSubMenu)
+ if (item.HasSubMenu && item.IsEffectivelyEnabled)
{
Open(item, true);
}
@@ -303,7 +303,8 @@ namespace Avalonia.Controls.Platform
{
item.Parent.SelectedItem.Close();
SelectItemAndAncestors(item);
- Open(item, false);
+ if (item.HasSubMenu)
+ Open(item, false);
}
else
{
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index e804c4b4a9..a5cdeefb0e 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -53,6 +53,7 @@ namespace Avalonia.Controls.Primitives
AvaloniaProperty.Register(
nameof(PlacementConstraintAdjustment),
PopupPositionerConstraintAdjustment.FlipX | PopupPositionerConstraintAdjustment.FlipY |
+ PopupPositionerConstraintAdjustment.SlideX | PopupPositionerConstraintAdjustment.SlideY |
PopupPositionerConstraintAdjustment.ResizeX | PopupPositionerConstraintAdjustment.ResizeY);
///
@@ -145,7 +146,9 @@ namespace Avalonia.Controls.Primitives
{
IsHitTestVisibleProperty.OverrideDefaultValue(false);
ChildProperty.Changed.AddClassHandler((x, e) => x.ChildChanged(e));
- IsOpenProperty.Changed.AddClassHandler((x, e) => x.IsOpenChanged((AvaloniaPropertyChangedEventArgs)e));
+ IsOpenProperty.Changed.AddClassHandler((x, e) => x.IsOpenChanged((AvaloniaPropertyChangedEventArgs)e));
+ VerticalOffsetProperty.Changed.AddClassHandler((x, _) => x.HandlePositionChange());
+ HorizontalOffsetProperty.Changed.AddClassHandler((x, _) => x.HandlePositionChange());
}
///
@@ -519,6 +522,24 @@ namespace Avalonia.Controls.Primitives
base.OnDetachedFromLogicalTree(e);
Close();
}
+
+ private void HandlePositionChange()
+ {
+ if (_openState != null)
+ {
+ var placementTarget = PlacementTarget ?? this.FindLogicalAncestorOfType();
+ if (placementTarget == null)
+ return;
+ _openState.PopupHost.ConfigurePosition(
+ placementTarget,
+ PlacementMode,
+ new Point(HorizontalOffset, VerticalOffset),
+ PlacementAnchor,
+ PlacementGravity,
+ PlacementConstraintAdjustment,
+ PlacementRect);
+ }
+ }
private static IDisposable SubscribeToEventHandler(T target, TEventHandler handler, Action subscribe, Action unsubscribe)
{
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
index ea06c33e4d..73d867bf10 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
@@ -160,13 +160,19 @@ namespace Avalonia.Diagnostics.Views
return;
}
+ var root = Root;
+ if (root is null)
+ {
+ return;
+ }
+
switch (e.Modifiers)
{
case RawInputModifiers.Control | RawInputModifiers.Shift:
{
IControl? control = null;
- foreach (var popupRoot in GetPopupRoots(Root))
+ foreach (var popupRoot in GetPopupRoots(root))
{
control = GetHoveredControl(popupRoot);
@@ -176,7 +182,7 @@ namespace Avalonia.Diagnostics.Views
}
}
- control ??= GetHoveredControl(Root);
+ control ??= GetHoveredControl(root);
if (control != null)
{
@@ -190,7 +196,7 @@ namespace Avalonia.Diagnostics.Views
{
vm.FreezePopups = !vm.FreezePopups;
- foreach (var popupRoot in GetPopupRoots(Root))
+ foreach (var popupRoot in GetPopupRoots(root))
{
if (popupRoot.Parent is Popup popup)
{
diff --git a/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml.cs b/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml.cs
index 55e30396e1..5d7619d184 100644
--- a/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml.cs
+++ b/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml.cs
@@ -30,13 +30,13 @@ namespace Avalonia.Dialogs
}
else
{
- using (Process process = Process.Start(new ProcessStartInfo
+ using Process process = Process.Start(new ProcessStartInfo
{
FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? url : "open",
Arguments = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? $"{url}" : "",
CreateNoWindow = true,
UseShellExecute = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
- }));
+ });
}
}
diff --git a/src/Avalonia.Dialogs/ManagedFileChooser.cs b/src/Avalonia.Dialogs/ManagedFileChooser.cs
index f9f38ac474..9058c405a3 100644
--- a/src/Avalonia.Dialogs/ManagedFileChooser.cs
+++ b/src/Avalonia.Dialogs/ManagedFileChooser.cs
@@ -1,13 +1,11 @@
using System;
using System.Linq;
using System.Threading.Tasks;
-using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
-using Avalonia.Markup.Xaml;
namespace Avalonia.Dialogs
{
@@ -35,7 +33,9 @@ namespace Avalonia.Dialogs
if (_quickLinksRoot != null)
{
var isQuickLink = _quickLinksRoot.IsLogicalAncestorOf(e.Source as Control);
+#pragma warning disable CS0618 // Type or member is obsolete
if (e.ClickCount == 2 || isQuickLink)
+#pragma warning restore CS0618 // Type or member is obsolete
{
if (model.ItemType == ManagedFileChooserItemType.File)
{
diff --git a/src/Avalonia.Dialogs/ManagedFileChooserSources.cs b/src/Avalonia.Dialogs/ManagedFileChooserSources.cs
index 050d618ce1..a217a67bc6 100644
--- a/src/Avalonia.Dialogs/ManagedFileChooserSources.cs
+++ b/src/Avalonia.Dialogs/ManagedFileChooserSources.cs
@@ -67,7 +67,7 @@ namespace Avalonia.Dialogs
{
Directory.GetFiles(x.VolumePath);
}
- catch (Exception _)
+ catch (Exception)
{
return null;
}
diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
index 268171d467..63cbfb2dbe 100644
--- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
+++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
@@ -47,6 +47,8 @@ namespace Avalonia.Headless
}
public IStreamGeometryImpl CreateStreamGeometry() => new HeadlessStreamingGeometryStub();
+ public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => throw new NotImplementedException();
+ public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) => throw new NotImplementedException();
public IRenderTarget CreateRenderTarget(IEnumerable