diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 15392278c9..fbd8507193 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -51,7 +51,7 @@ jobs: inputs: actions: 'build' scheme: '' - sdk: 'macosx11.0' + sdk: 'macosx11.1' configuration: 'Release' xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace' xcodeVersion: '12' # Options: 8, 9, default, specifyPath diff --git a/build/SharedVersion.props b/build/SharedVersion.props index 43ec995ed9..9a268a21e7 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -21,6 +21,6 @@ - + diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index 00b6dab219..b2d4341bb9 100644 --- a/native/Avalonia.Native/src/OSX/rendertarget.mm +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -111,7 +111,11 @@ if(_renderbuffer != 0) glDeleteRenderbuffers(1, &_renderbuffer); } - CFRelease(surface); + + if(surface != nullptr) + { + CFRelease(surface); + } } @end @@ -145,6 +149,12 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta } - (void)resize:(AvnPixelSize)size withScale: (float) scale{ + + if(size.Height <= 0) + size.Height = 1; + if(size.Width <= 0) + size.Width = 1; + @synchronized (lock) { if(surface == nil || surface->size.Width != size.Width diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 5bb2763566..6687c59aa4 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -1,6 +1,6 @@ // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. -// All other rights reserved. +// All other rights reserved. using Avalonia.Collections; using Avalonia.Controls.Primitives; @@ -92,7 +92,7 @@ namespace Avalonia.Controls private ContentControl _topRightCornerHeader; private Control _frozenColumnScrollBarSpacer; - // the sum of the widths in pixels of the scrolling columns preceding + // the sum of the widths in pixels of the scrolling columns preceding // the first displayed scrolling column private double _horizontalOffset; @@ -143,7 +143,7 @@ namespace Avalonia.Controls private object _uneditedValue; // Represents the original current cell value at the time it enters editing mode. private ICellEditBinding _currentCellEditBinding; - // An approximation of the sum of the heights in pixels of the scrolling rows preceding + // An approximation of the sum of the heights in pixels of the scrolling rows preceding // the first displayed scrolling row. Since the scrolled off rows are discarded, the grid // does not know their actual height. The heights used for the approximation are the ones // set as the rows were scrolled off. @@ -162,7 +162,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(CanUserReorderColumns)); /// - /// Gets or sets a value that indicates whether the user can change + /// Gets or sets a value that indicates whether the user can change /// the column display order by dragging column headers with the mouse. /// public bool CanUserReorderColumns @@ -247,8 +247,8 @@ namespace Avalonia.Controls /// Gets or sets the that is used to paint the background of odd-numbered rows. /// /// - /// The brush that is used to paint the background of odd-numbered rows. The default is a - /// with a + /// The brush that is used to paint the background of odd-numbered rows. The default is a + /// with a /// value of white (ARGB value #00FFFFFF). /// public IBrush AlternatingRowBackground @@ -379,8 +379,8 @@ namespace Avalonia.Controls public bool IsValid { get { return _isValid; } - internal set - { + internal set + { SetAndRaise(IsValidProperty, ref _isValid, value); PseudoClasses.Set(":invalid", !value); } @@ -398,7 +398,7 @@ namespace Avalonia.Controls } /// - /// Gets or sets the maximum width of columns in the . + /// Gets or sets the maximum width of columns in the . /// public double MaxColumnWidth { @@ -418,7 +418,7 @@ namespace Avalonia.Controls } /// - /// Gets or sets the minimum width of columns in the . + /// Gets or sets the minimum width of columns in the . /// public double MinColumnWidth { @@ -496,7 +496,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(VerticalGridLinesBrush)); /// - /// Gets or sets the that is used to paint grid lines separating columns. + /// Gets or sets the that is used to paint grid lines separating columns. /// public IBrush VerticalGridLinesBrush { @@ -542,7 +542,7 @@ namespace Avalonia.Controls /// /// /// The index of the current selection, or -1 if the selection is empty. - /// + /// public int SelectedIndex { get { return _selectedIndex; } @@ -582,7 +582,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(AutoGenerateColumns)); /// - /// Gets or sets a value that indicates whether columns are created + /// Gets or sets a value that indicates whether columns are created /// automatically when the property is set. /// public bool AutoGenerateColumns @@ -626,7 +626,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(AreRowDetailsFrozen)); /// - /// Gets or sets a value that indicates whether the row details sections remain + /// Gets or sets a value that indicates whether the row details sections remain /// fixed at the width of the display area or can scroll horizontally. /// public bool AreRowDetailsFrozen @@ -881,7 +881,7 @@ namespace Avalonia.Controls { int index = (int)e.NewValue; - // GetDataItem returns null if index is >= Count, we do not check newValue + // GetDataItem returns null if index is >= Count, we do not check newValue // against Count here to avoid enumerating through an Enumerable twice // Setting SelectedItem coerces the finally value of the SelectedIndex object newSelectedItem = (index < 0) ? null : DataConnection.GetDataItem(index); @@ -1168,14 +1168,14 @@ namespace Avalonia.Controls } /// - /// Occurs one time for each public, non-static property in the bound data type when the - /// property is changed and the + /// Occurs one time for each public, non-static property in the bound data type when the + /// property is changed and the /// property is true. /// public event EventHandler AutoGeneratingColumn; /// - /// Occurs before a cell or row enters editing mode. + /// Occurs before a cell or row enters editing mode. /// public event EventHandler BeginningEdit; @@ -1195,7 +1195,7 @@ namespace Avalonia.Controls public event EventHandler CellPointerPressed; /// - /// Occurs when the + /// Occurs when the /// property of a column changes. /// public event EventHandler ColumnDisplayIndexChanged; @@ -1218,14 +1218,14 @@ namespace Avalonia.Controls public event EventHandler CurrentCellChanged; /// - /// Occurs after a + /// Occurs after a /// is instantiated, so that you can customize it before it is used. /// public event EventHandler LoadingRow; /// /// Occurs when a cell in a enters editing mode. - /// + /// /// public event EventHandler PreparingCellForEdit; @@ -1243,7 +1243,7 @@ namespace Avalonia.Controls RoutedEvent.Register(nameof(SelectionChanged), RoutingStrategies.Bubble); /// - /// Occurs when the or + /// Occurs when the or /// property value changes. /// public event EventHandler SelectionChanged @@ -1258,19 +1258,19 @@ namespace Avalonia.Controls public event EventHandler Sorting; /// - /// Occurs when a + /// Occurs when a /// object becomes available for reuse. /// public event EventHandler UnloadingRow; /// - /// Occurs when a new row details template is applied to a row, so that you can customize + /// Occurs when a new row details template is applied to a row, so that you can customize /// the details section before it is used. /// public event EventHandler LoadingRowDetails; /// - /// Occurs when the + /// Occurs when the /// property value changes. /// public event EventHandler RowDetailsVisibilityChanged; @@ -1282,7 +1282,7 @@ namespace Avalonia.Controls /// /// Gets a collection that contains all the columns in the control. - /// + /// public ObservableCollection Columns { get @@ -1456,7 +1456,7 @@ namespace Avalonia.Controls } // Height currently available for cells this value is smaller. This height is reduced by the existence of ColumnHeaders - // or a horizontal scrollbar. Layout is asynchronous so changes to the ColumnHeaders or the horizontal scrollbar are + // or a horizontal scrollbar. Layout is asynchronous so changes to the ColumnHeaders or the horizontal scrollbar are // not reflected immediately. internal double CellsHeight { @@ -1555,7 +1555,7 @@ namespace Avalonia.Controls internal static double HorizontalGridLinesThickness => DATAGRID_horizontalGridLinesThickness; - // the sum of the widths in pixels of the scrolling columns preceding + // the sum of the widths in pixels of the scrolling columns preceding // the first displayed scrolling column internal double HorizontalOffset { @@ -2083,20 +2083,20 @@ namespace Avalonia.Controls } /// - /// Measures the children of a to prepare for - /// arranging them during the - /// pass. + /// Measures the children of a to prepare for + /// arranging them during the + /// pass. /// /// /// The size that the determines it needs during layout, based on its calculations of child object allocated sizes. /// /// - /// The available size that this element can give to child elements. Indicates an upper limit that + /// The available size that this element can give to child elements. Indicates an upper limit that /// child elements should not exceed. /// protected override Size MeasureOverride(Size availableSize) { - // Delay layout until after the initial measure to avoid invalid calculations when the + // Delay layout until after the initial measure to avoid invalid calculations when the // DataGrid is not part of the visual tree if (!_measured) { @@ -3006,7 +3006,7 @@ namespace Avalonia.Controls /// If the editing element has focus, this method will set focus to the DataGrid itself /// in order to force the element to lose focus. It will then wait for the editing element's /// LostFocus event, at which point it will perform the specified action. - /// + /// /// NOTE: It is important to understand that the specified action will be performed when the editing /// element loses focus only if this method returns true. If it returns false, then the action /// will not be performed later on, and should instead be performed by the caller, if necessary. @@ -3065,7 +3065,7 @@ namespace Avalonia.Controls { if (!_scrollingByHeight) { - // Update layout when RowDetails are expanded or collapsed, just updating the vertical scroll bar is not enough + // Update layout when RowDetails are expanded or collapsed, just updating the vertical scroll bar is not enough // since rows could be added or removed InvalidateMeasure(); } @@ -3278,7 +3278,7 @@ namespace Avalonia.Controls { // Current cell was reset because the commit deleted row(s). // Since the user wants to change the current cell, we don't - // want to end up with no current cell. We pick the last row + // want to end up with no current cell. We pick the last row // in the grid which may be the 'new row'. int lastSlot = LastVisibleSlot; if (forCurrentCellChange && @@ -3336,7 +3336,7 @@ namespace Avalonia.Controls if (_ignoreNextScrollBarsLayout) { _ignoreNextScrollBarsLayout = false; - // + // } @@ -3393,7 +3393,7 @@ namespace Avalonia.Controls } // Now cellsWidth is the width potentially available for displaying data cells. - // Now cellsHeight is the height potentially available for displaying data cells. + // Now cellsHeight is the height potentially available for displaying data cells. bool needHorizScrollbar = false; bool needVertScrollbar = false; @@ -3418,7 +3418,7 @@ namespace Avalonia.Controls Debug.Assert(cellsHeight >= 0); needHorizScrollbarWithoutVertScrollbar = needHorizScrollbar = true; - if (vertScrollBarWidth > 0 && + if (vertScrollBarWidth > 0 && allowVertScrollbar && (MathUtilities.LessThanOrClose(totalVisibleWidth - cellsWidth, vertScrollBarWidth) || MathUtilities.LessThanOrClose(cellsWidth - totalVisibleFrozenWidth, vertScrollBarWidth))) { @@ -3458,7 +3458,7 @@ namespace Avalonia.Controls // we compute the number of visible columns only after we set up the vertical scroll bar. ComputeDisplayedColumns(); - if ((vertScrollBarWidth > 0 || horizScrollBarHeight > 0) && + if ((vertScrollBarWidth > 0 || horizScrollBarHeight > 0) && allowHorizScrollbar && needVertScrollbar && !needHorizScrollbar && MathUtilities.GreaterThan(totalVisibleWidth, cellsWidth) && @@ -3963,7 +3963,8 @@ namespace Avalonia.Controls { var errorList = binding.ValidationErrors - .SelectMany(ex => ValidationUtil.UnpackException(ex)) + .SelectMany(ValidationUtil.UnpackException) + .Select(ValidationUtil.UnpackDataValidationException) .ToList(); DataValidationErrors.SetErrors(editingElement, errorList); @@ -4124,7 +4125,7 @@ namespace Avalonia.Controls } /// - /// Exits editing mode without trying to commit or revert the editing, and + /// Exits editing mode without trying to commit or revert the editing, and /// without repopulating the edited row's cell. /// //TODO TabStop @@ -5103,9 +5104,9 @@ namespace Avalonia.Controls { if (ctrl || _editingColumnIndex == -1 || IsReadOnly) { - //Go to the next/previous control on the page when + //Go to the next/previous control on the page when // - Ctrl key is used - // - Potential current cell is not edited, or the datagrid is read-only. + // - Potential current cell is not edited, or the datagrid is read-only. return false; } @@ -5516,11 +5517,11 @@ namespace Avalonia.Controls // v---v //|<|_____|###|>| // ^ ^ - // min max + // min max // we want to make the relative size of the thumb reflect the relative size of the viewing area // viewportSize / (max + viewportSize) = cellsWidth / max - // -> viewportSize = max * cellsWidth / (max - cellsWidth) + // -> viewportSize = max * cellsWidth / (max - cellsWidth) // always zero _hScrollBar.Minimum = 0; @@ -5572,7 +5573,7 @@ namespace Avalonia.Controls _hScrollBar.Maximum = 0; if (_hScrollBar.IsVisible) { - // This will trigger a call to this method via Cells_SizeChanged for + // This will trigger a call to this method via Cells_SizeChanged for // which no processing is needed. _hScrollBar.IsVisible = false; _ignoreNextScrollBarsLayout = true; @@ -5591,14 +5592,14 @@ namespace Avalonia.Controls // v---v //|<|_____|###|>| // ^ ^ - // min max + // min max // we want to make the relative size of the thumb reflect the relative size of the viewing area // viewportSize / (max + viewportSize) = cellsWidth / max // -> viewportSize = max * cellsHeight / (totalVisibleHeight - cellsHeight) // -> = max * cellsHeight / (totalVisibleHeight - cellsHeight) // -> = max * cellsHeight / max - // -> = cellsHeight + // -> = cellsHeight // always zero _vScrollBar.Minimum = 0; @@ -5621,7 +5622,7 @@ namespace Avalonia.Controls if (!_vScrollBar.IsVisible) { - // This will trigger a call to this method via Cells_SizeChanged for + // This will trigger a call to this method via Cells_SizeChanged for // which no processing is needed. _vScrollBar.IsVisible = true; if (_vScrollBar.DesiredSize.Width == 0) @@ -5637,7 +5638,7 @@ namespace Avalonia.Controls _vScrollBar.Maximum = 0; if (_vScrollBar.IsVisible) { - // This will trigger a call to this method via Cells_SizeChanged for + // This will trigger a call to this method via Cells_SizeChanged for // which no processing is needed. _vScrollBar.IsVisible = false; _ignoreNextScrollBarsLayout = true; @@ -5660,8 +5661,8 @@ namespace Avalonia.Controls Debug.Assert(slot >= 0); // Before changing selection, check if the current cell needs to be committed, and - // check if the current row needs to be committed. If any of those two operations are required and fail, - // do not change selection, and do not change current cell. + // check if the current row needs to be committed. If any of those two operations are required and fail, + // do not change selection, and do not change current cell. bool wasInEdit = EditingColumnIndex != -1; diff --git a/src/Avalonia.Controls.DataGrid/Utils/ValidationUtil.cs b/src/Avalonia.Controls.DataGrid/Utils/ValidationUtil.cs index e1ecb2c562..c36d9708c1 100644 --- a/src/Avalonia.Controls.DataGrid/Utils/ValidationUtil.cs +++ b/src/Avalonia.Controls.DataGrid/Utils/ValidationUtil.cs @@ -80,19 +80,24 @@ namespace Avalonia.Controls.Utils { if (exception != null) { - var aggregate = exception as AggregateException; - var exceptions = aggregate == null ? - (IEnumerable)new[] { exception } : - aggregate.InnerExceptions; - var filtered = exceptions.Where(x => !(x is BindingChainException)).ToList(); + var exceptions = exception is AggregateException aggregate ? + aggregate.InnerExceptions : + (IEnumerable)new[] { exception }; - if (filtered.Count > 0) - { - return filtered; - } + return exceptions.Where(x => !(x is BindingChainException)).ToList(); } - return null; + return Array.Empty(); + } + + public static object UnpackDataValidationException(Exception exception) + { + if (exception is DataValidationException dataValidationException) + { + return dataValidationException.ErrorData; + } + + return exception; } /// diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 5e17182f3e..fb8080f0d4 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -246,7 +246,7 @@ namespace Avalonia.Controls /// /// Opens the menu. /// - public override void Open() => throw new NotSupportedException(); + public override void Open() => Open(null); /// /// Opens a context menu on the specified control. diff --git a/src/Avalonia.Layout/ElementManager.cs b/src/Avalonia.Layout/ElementManager.cs index 70805ba31c..bf5a45966b 100644 --- a/src/Avalonia.Layout/ElementManager.cs +++ b/src/Avalonia.Layout/ElementManager.cs @@ -129,7 +129,7 @@ namespace Avalonia.Layout { for (int i = 0; i < count; i++) { - // Clear from the edges so that ItemsRepeater can optimize on maintaining + // Clear from the edges so that ItemsRepeater can optimize on maintaining // realized indices without walking through all the children every time. int index = realizedIndex == 0 ? realizedIndex + i : (realizedIndex + count - 1) - i; var elementRef = _realizedElements[index]; @@ -212,7 +212,7 @@ namespace Avalonia.Layout public ILayoutable GetRealizedElement(int dataIndex) { return IsVirtualizingContext ? - GetAt(GetRealizedRangeIndexFromDataIndex(dataIndex)) : + GetAt(GetRealizedRangeIndexFromDataIndex(dataIndex)) : _context.GetOrCreateElementAt( dataIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle); @@ -252,7 +252,6 @@ namespace Avalonia.Layout (orientation == ScrollOrientation.Vertical ? ScrollOrientation.Horizontal : ScrollOrientation.Vertical) : orientation; - var windowStart = effectiveOrientation == ScrollOrientation.Vertical ? window.Y : window.X; var windowEnd = effectiveOrientation == ScrollOrientation.Vertical ? window.Y + window.Height : window.X + window.Width; var firstElementStart = effectiveOrientation == ScrollOrientation.Vertical ? firstElementBounds.Y : firstElementBounds.X; @@ -273,53 +272,53 @@ namespace Avalonia.Layout switch (args.Action) { case NotifyCollectionChangedAction.Add: - { - OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); - } - break; + { + OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); + } + break; case NotifyCollectionChangedAction.Replace: - { - int oldSize = args.OldItems.Count; - int newSize = args.NewItems.Count; - int oldStartIndex = args.OldStartingIndex; - int newStartIndex = args.NewStartingIndex; - - if (oldSize == newSize && - oldStartIndex == newStartIndex && - IsDataIndexRealized(oldStartIndex) && - IsDataIndexRealized(oldStartIndex + oldSize -1)) { - // Straight up replace of n items within the realization window. - // Removing and adding might causes us to lose the anchor causing us - // to throw away all containers and start from scratch. - // Instead, we can just clear those items and set the element to - // null (sentinel) and let the next measure get new containers for them. - var startRealizedIndex = GetRealizedRangeIndexFromDataIndex(oldStartIndex); - for (int realizedIndex = startRealizedIndex; realizedIndex < startRealizedIndex + oldSize; realizedIndex++) + int oldSize = args.OldItems.Count; + int newSize = args.NewItems.Count; + int oldStartIndex = args.OldStartingIndex; + int newStartIndex = args.NewStartingIndex; + + if (oldSize == newSize && + oldStartIndex == newStartIndex && + IsDataIndexRealized(oldStartIndex) && + IsDataIndexRealized(oldStartIndex + oldSize - 1)) { - var elementRef = _realizedElements[realizedIndex]; - - if (elementRef != null) + // Straight up replace of n items within the realization window. + // Removing and adding might causes us to lose the anchor causing us + // to throw away all containers and start from scratch. + // Instead, we can just clear those items and set the element to + // null (sentinel) and let the next measure get new containers for them. + var startRealizedIndex = GetRealizedRangeIndexFromDataIndex(oldStartIndex); + for (int realizedIndex = startRealizedIndex; realizedIndex < startRealizedIndex + oldSize; realizedIndex++) { - _context.RecycleElement(elementRef); - _realizedElements[realizedIndex] = null; + var elementRef = _realizedElements[realizedIndex]; + + if (elementRef != null) + { + _context.RecycleElement(elementRef); + _realizedElements[realizedIndex] = null; + } } } + else + { + OnItemsRemoved(oldStartIndex, oldSize); + OnItemsAdded(newStartIndex, newSize); + } } - else - { - OnItemsRemoved(oldStartIndex, oldSize); - OnItemsAdded(newStartIndex, newSize); - } - } - break; + break; case NotifyCollectionChangedAction.Remove: - { - OnItemsRemoved(args.OldStartingIndex, args.OldItems.Count); - } - break; + { + OnItemsRemoved(args.OldStartingIndex, args.OldItems.Count); + } + break; case NotifyCollectionChangedAction.Reset: ClearRealizedRange(); @@ -376,7 +375,7 @@ namespace Avalonia.Layout int backCutoffIndex = realizedRangeSize; for (int i = 0; - i _firstRealizedDataIndex && + if (newStartingIndex >= _firstRealizedDataIndex && newStartingIndex <= lastRealizedDataIndex) { // Inserted within the realized range int insertRangeStartIndex = newStartingIndex - _firstRealizedDataIndex; for (int i = 0; i < count; i++) { - // Insert null (sentinel) here instead of an element, that way we dont + // Insert null (sentinel) here instead of an element, that way we dont // end up creating a lot of elements only to be thrown out in the next layout. int insertRangeIndex = insertRangeStartIndex + i; int dataIndex = newStartingIndex + i; diff --git a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml index 97ca62c493..ad0fd18ea5 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml @@ -108,7 +108,7 @@