From e883107ca35a1965572acbb933977be307fffce7 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 26 May 2017 15:03:09 +0300 Subject: [PATCH 1/7] Make PlatformImpl nullable and always check for null value --- .../Direct3DInteropSample/MainWindow.cs | 2 +- .../Embedding/EmbeddableControlRoot.cs | 12 +++---- src/Avalonia.Controls/Primitives/PopupRoot.cs | 7 ++-- src/Avalonia.Controls/TopLevel.cs | 33 ++++++++++-------- src/Avalonia.Controls/Window.cs | 28 +++++++++------ src/Avalonia.Controls/WindowBase.cs | 34 +++++++++++-------- .../DesignerAssist.cs | 4 ++- .../Embedding/WinFormsAvaloniaControlHost.cs | 4 ++- 8 files changed, 71 insertions(+), 53 deletions(-) diff --git a/samples/interop/Direct3DInteropSample/MainWindow.cs b/samples/interop/Direct3DInteropSample/MainWindow.cs index 1ff1e1938b..ad40e81895 100644 --- a/samples/interop/Direct3DInteropSample/MainWindow.cs +++ b/samples/interop/Direct3DInteropSample/MainWindow.cs @@ -58,7 +58,7 @@ namespace Direct3DInteropSample new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height, new Rational(60, 1), Format.R8G8B8A8_UNorm), IsWindowed = true, - OutputHandle = PlatformImpl.Handle.Handle, + OutputHandle = PlatformImpl?.Handle.Handle ?? IntPtr.Zero, SampleDescription = new SampleDescription(1, 0), SwapEffect = SwapEffect.Discard, Usage = Usage.RenderTargetOutput diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs index 4688017187..b8d54fa67b 100644 --- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs +++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs @@ -4,6 +4,7 @@ using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; using Avalonia.Styling; +using JetBrains.Annotations; namespace Avalonia.Controls.Embedding { @@ -18,6 +19,7 @@ namespace Avalonia.Controls.Embedding { } + [CanBeNull] public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl; public void Prepare() @@ -39,8 +41,9 @@ namespace Avalonia.Controls.Embedding protected override Size MeasureOverride(Size availableSize) { - base.MeasureOverride(PlatformImpl.ClientSize); - return PlatformImpl.ClientSize; + var cs = PlatformImpl?.ClientSize ?? default(Size); + base.MeasureOverride(cs); + return cs; } private readonly NameScope _nameScope = new NameScope(); @@ -63,9 +66,6 @@ namespace Avalonia.Controls.Embedding public void Unregister(string name) => _nameScope.Unregister(name); Type IStyleable.StyleKey => typeof(EmbeddableControlRoot); - public void Dispose() - { - PlatformImpl.Dispose(); - } + public void Dispose() => PlatformImpl?.Dispose(); } } diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 86c1b47521..a999e4ae37 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -9,6 +9,7 @@ using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; using Avalonia.VisualTree; +using JetBrains.Annotations; namespace Avalonia.Controls.Primitives { @@ -49,6 +50,7 @@ namespace Avalonia.Controls.Primitives /// /// Gets the platform-specific window implementation. /// + [CanBeNull] public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl; /// @@ -65,10 +67,7 @@ namespace Avalonia.Controls.Primitives IVisual IHostedVisualTreeRoot.Host => Parent; /// - public void Dispose() - { - this.PlatformImpl.Dispose(); - } + public void Dispose() => PlatformImpl?.Dispose(); /// protected override void OnTemplateApplied(TemplateAppliedEventArgs e) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index f3a6ab92d0..53dd905eca 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -14,6 +14,7 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.VisualTree; +using JetBrains.Annotations; namespace Avalonia.Controls { @@ -92,13 +93,13 @@ namespace Avalonia.Controls var rendererFactory = TryGetService(dependencyResolver); Renderer = rendererFactory?.CreateRenderer(this, renderLoop); - PlatformImpl.SetInputRoot(this); + impl.SetInputRoot(this); - PlatformImpl.Closed = HandleClosed; - PlatformImpl.Input = HandleInput; - PlatformImpl.Paint = HandlePaint; - PlatformImpl.Resized = HandleResized; - PlatformImpl.ScalingChanged = HandleScalingChanged; + impl.Closed = HandleClosed; + impl.Input = HandleInput; + impl.Paint = HandlePaint; + impl.Resized = HandleResized; + impl.ScalingChanged = HandleScalingChanged; _keyboardNavigationHandler?.SetOwner(this); @@ -110,7 +111,7 @@ namespace Avalonia.Controls this.GetObservable(PointerOverElementProperty) .Select( x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty()) - .Switch().Subscribe(cursor => PlatformImpl.SetCursor(cursor?.PlatformCursor)); + .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor)); if (_applicationLifecycle != null) { @@ -135,10 +136,8 @@ namespace Avalonia.Controls /// /// Gets the platform-specific window implementation. /// - public ITopLevelImpl PlatformImpl - { - get; - } + [CanBeNull] + public ITopLevelImpl PlatformImpl { get; private set; } /// /// Gets the renderer for the window. @@ -177,7 +176,7 @@ namespace Avalonia.Controls Size ILayoutRoot.MaxClientSize => Size.Infinity; /// - double ILayoutRoot.LayoutScaling => PlatformImpl.Scaling; + double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1; IStyleHost IStyleHost.StylingParent { @@ -189,25 +188,27 @@ namespace Avalonia.Controls /// protected virtual IRenderTarget CreateRenderTarget() { + if(PlatformImpl == null) + throw new InvalidOperationException("PlatformImpl isn't available"); return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces); } /// void IRenderRoot.Invalidate(Rect rect) { - PlatformImpl.Invalidate(rect); + PlatformImpl?.Invalidate(rect); } /// Point IRenderRoot.PointToClient(Point p) { - return PlatformImpl.PointToClient(p); + return PlatformImpl?.PointToClient(p) ?? default(Point); } /// Point IRenderRoot.PointToScreen(Point p) { - return PlatformImpl.PointToScreen(p); + return PlatformImpl?.PointToScreen(p) ?? default(Point); } /// @@ -224,6 +225,8 @@ namespace Avalonia.Controls /// protected virtual void HandleClosed() { + PlatformImpl = null; + Closed?.Invoke(this, EventArgs.Empty); Renderer?.Dispose(); Renderer = null; diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 75587dcaec..32eaf499f0 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -12,6 +12,7 @@ using Avalonia.Platform; using Avalonia.Styling; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; namespace Avalonia.Controls { @@ -87,11 +88,11 @@ namespace Avalonia.Controls static Window() { BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); - TitleProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl.SetTitle((string)e.NewValue)); + TitleProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue)); HasSystemDecorationsProperty.Changed.AddClassHandler( - (s, e) => s.PlatformImpl.SetSystemDecorations((bool) e.NewValue)); + (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue)); - IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); + IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); } /// @@ -109,7 +110,7 @@ namespace Avalonia.Controls public Window(IWindowImpl impl) : base(impl) { - _maxPlatformClientSize = this.PlatformImpl.MaxClientSize; + _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size); } /// @@ -129,6 +130,7 @@ namespace Avalonia.Controls /// /// Gets the platform-specific window implementation. /// + [CanBeNull] public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl; /// @@ -164,8 +166,12 @@ namespace Avalonia.Controls /// public WindowState WindowState { - get { return this.PlatformImpl.WindowState; } - set { this.PlatformImpl.WindowState = value; } + get { return PlatformImpl?.WindowState ?? WindowState.Normal; } + set + { + if (PlatformImpl != null) + PlatformImpl.WindowState = value; + } } /// @@ -189,7 +195,7 @@ namespace Avalonia.Controls public void Close() { s_windows.Remove(this); - PlatformImpl.Dispose(); + PlatformImpl?.Dispose(); IsVisible = false; } @@ -221,7 +227,7 @@ namespace Avalonia.Controls { using (BeginAutoSizing()) { - PlatformImpl.Hide(); + PlatformImpl?.Hide(); } IsVisible = false; @@ -240,7 +246,7 @@ namespace Avalonia.Controls using (BeginAutoSizing()) { - PlatformImpl.Show(); + PlatformImpl?.Show(); } } @@ -278,7 +284,7 @@ namespace Avalonia.Controls var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault(); SetIsEnabled(affectedWindows, false); - var modal = PlatformImpl.ShowDialog(); + var modal = PlatformImpl?.ShowDialog(); var result = new TaskCompletionSource(); Observable.FromEventPattern( @@ -287,7 +293,7 @@ namespace Avalonia.Controls .Take(1) .Subscribe(_ => { - modal.Dispose(); + modal?.Dispose(); SetIsEnabled(affectedWindows, true); activated?.Activate(); result.SetResult((TResult)_dialogResult); diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 21c248db0c..1f484fd6cb 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -9,6 +9,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; +using JetBrains.Annotations; namespace Avalonia.Controls { @@ -43,10 +44,10 @@ namespace Avalonia.Controls public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver) { - PlatformImpl.Activated = HandleActivated; - PlatformImpl.Deactivated = HandleDeactivated; - PlatformImpl.PositionChanged = HandlePositionChanged; - this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl.Resize(x)); + impl.Activated = HandleActivated; + impl.Deactivated = HandleDeactivated; + impl.PositionChanged = HandlePositionChanged; + this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x)); } /// @@ -64,6 +65,7 @@ namespace Avalonia.Controls /// public event EventHandler PositionChanged; + [CanBeNull] public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl; /// @@ -80,8 +82,12 @@ namespace Avalonia.Controls /// public Point Position { - get { return PlatformImpl.Position; } - set { PlatformImpl.Position = value; } + get { return PlatformImpl?.Position ?? default(Point); } + set + { + if (PlatformImpl is IWindowBaseImpl impl) + impl.Position = value; + } } /// @@ -98,7 +104,7 @@ namespace Avalonia.Controls /// public void Activate() { - PlatformImpl.Activate(); + PlatformImpl?.Activate(); } /// @@ -110,7 +116,7 @@ namespace Avalonia.Controls try { - PlatformImpl.Hide(); + PlatformImpl?.Hide(); IsVisible = false; } finally @@ -131,7 +137,7 @@ namespace Avalonia.Controls EnsureInitialized(); IsVisible = true; LayoutManager.Instance.ExecuteInitialLayoutPass(this); - PlatformImpl.Show(); + PlatformImpl?.Show(); } finally { @@ -163,10 +169,10 @@ namespace Avalonia.Controls { using (BeginAutoSizing()) { - PlatformImpl.Resize(finalSize); + PlatformImpl?.Resize(finalSize); } - return base.ArrangeOverride(PlatformImpl.ClientSize); + return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size)); } /// @@ -174,7 +180,7 @@ namespace Avalonia.Controls /// protected void EnsureInitialized() { - if (!this.IsInitialized) + if (!IsInitialized) { var init = (ISupportInitialize)this; init.BeginInit(); @@ -268,12 +274,12 @@ namespace Avalonia.Controls /// /// Starts moving a window with left button being held. Should be called from left mouse button press event handler /// - public void BeginMoveDrag() => PlatformImpl.BeginMoveDrag(); + public void BeginMoveDrag() => PlatformImpl?.BeginMoveDrag(); /// /// Starts resizing a window. This function is used if an application has window resizing controls. /// Should be called from left mouse button press event handler /// - public void BeginResizeDrag(WindowEdge edge) => PlatformImpl.BeginResizeDrag(edge); + public void BeginResizeDrag(WindowEdge edge) => PlatformImpl?.BeginResizeDrag(edge); } } diff --git a/src/Avalonia.DesignerSupport/DesignerAssist.cs b/src/Avalonia.DesignerSupport/DesignerAssist.cs index c9ae89354c..8d30f3cf25 100644 --- a/src/Avalonia.DesignerSupport/DesignerAssist.cs +++ b/src/Avalonia.DesignerSupport/DesignerAssist.cs @@ -75,7 +75,7 @@ namespace Avalonia.DesignerSupport private static void SetScalingFactor(double factor) { PlatformManager.SetDesignerScalingFactor(factor); - s_currentWindow?.PlatformImpl.Resize(s_currentWindow.ClientSize); + s_currentWindow?.PlatformImpl?.Resize(s_currentWindow.ClientSize); } static Window s_currentWindow; @@ -149,6 +149,8 @@ namespace Avalonia.DesignerSupport s_currentWindow = window; window.Show(); Design.ApplyDesignerProperties(window, control); + // ReSharper disable once PossibleNullReferenceException + // Always not null at this point Api.OnWindowCreated?.Invoke(window.PlatformImpl.Handle.Handle); Api.OnResize?.Invoke(); } diff --git a/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs b/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs index bdee85c91e..a484d6c0d2 100644 --- a/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs +++ b/src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs @@ -15,7 +15,7 @@ namespace Avalonia.Win32.Embedding { private readonly EmbeddableControlRoot _root = new EmbeddableControlRoot(); - private IntPtr WindowHandle => ((WindowImpl) _root.PlatformImpl).Handle.Handle; + private IntPtr WindowHandle => ((WindowImpl) _root?.PlatformImpl)?.Handle?.Handle ?? IntPtr.Zero; public WinFormsAvaloniaControlHost() { @@ -25,6 +25,8 @@ namespace Avalonia.Win32.Embedding if (_root.IsFocused) FocusManager.Instance.Focus(null); _root.GotFocus += RootGotFocus; + // ReSharper disable once PossibleNullReferenceException + // Always non-null at this point _root.PlatformImpl.LostFocus += PlatformImpl_LostFocus; FixPosition(); } From 0b756d0b8a93a0c0073b226bd121e56d67fd96f2 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 26 May 2017 15:51:05 +0300 Subject: [PATCH 2/7] More null checks --- Avalonia.sln.DotSettings | 1 + src/Avalonia.Base/AvaloniaObject.cs | 11 +++----- .../Collections/AvaloniaDictionary.cs | 25 +++++++------------ src/Avalonia.Base/PriorityValue.cs | 2 +- src/Avalonia.Controls/Button.cs | 2 +- src/Avalonia.Controls/Control.cs | 2 +- src/Avalonia.Controls/ItemsControl.cs | 2 +- src/Avalonia.Controls/TextBox.cs | 7 ++++-- src/Avalonia.Controls/TopLevel.cs | 2 +- .../AppBuilder.cs | 8 +++++- src/Avalonia.Styling/Controls/NameScope.cs | 2 +- .../Rendering/ZIndexComparer.cs | 2 +- .../Avalonia.Cairo/Media/DrawingContext.cs | 1 + src/Gtk/Avalonia.Gtk3/SystemDialogs.cs | 1 + .../Parsers/SelectorParser.cs | 2 +- 15 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Avalonia.sln.DotSettings b/Avalonia.sln.DotSettings index ab21d6e50b..1fd6f8d092 100644 --- a/Avalonia.sln.DotSettings +++ b/Avalonia.sln.DotSettings @@ -1,4 +1,5 @@  + ExplicitlyExcluded ExplicitlyExcluded ExplicitlyExcluded HINT diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 1492c14c5f..1bdf1eb5e3 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -622,14 +622,9 @@ namespace Avalonia /// The default value. private object GetDefaultValue(AvaloniaProperty property) { - if (property.Inherits && _inheritanceParent != null) - { - return (_inheritanceParent as AvaloniaObject).GetValueInternal(property); - } - else - { - return ((IStyledPropertyAccessor)property).GetDefaultValue(GetType()); - } + if (property.Inherits && _inheritanceParent is AvaloniaObject aobj) + return aobj.GetValueInternal(property); + return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType()); } /// diff --git a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs index d63a5baf16..1aa239180c 100644 --- a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs +++ b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs @@ -103,11 +103,9 @@ namespace Avalonia.Collections _inner = new Dictionary(); - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs("Count")); - PropertyChanged(this, new PropertyChangedEventArgs($"Item[]")); - } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[]")); + if (CollectionChanged != null) { @@ -144,12 +142,9 @@ namespace Avalonia.Collections if (_inner.TryGetValue(key, out value)) { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs("Count")); - PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]")); - } - + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]")); + if (CollectionChanged != null) { var e = new NotifyCollectionChangedEventArgs( @@ -199,11 +194,9 @@ namespace Avalonia.Collections private void NotifyAdd(TKey key, TValue value) { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs("Count")); - PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]")); - } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]")); + if (CollectionChanged != null) { diff --git a/src/Avalonia.Base/PriorityValue.cs b/src/Avalonia.Base/PriorityValue.cs index 21467f3962..c33d50ff0e 100644 --- a/src/Avalonia.Base/PriorityValue.cs +++ b/src/Avalonia.Base/PriorityValue.cs @@ -285,7 +285,7 @@ namespace Avalonia Property.Name, _valueType, value, - value.GetType()); + value?.GetType()); } } } diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 24daa545ba..7ed1c7fd8c 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -275,7 +275,7 @@ namespace Avalonia.Controls { var button = e.Sender as Button; var isDefault = (bool)e.NewValue; - var inputRoot = button.VisualRoot as IInputElement; + var inputRoot = button?.VisualRoot as IInputElement; if (inputRoot != null) { diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 516da813ef..fa0d8fe7d6 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -645,7 +645,7 @@ namespace Avalonia.Controls if (_focusAdorner != null) { - var adornerLayer = _focusAdorner.Parent as Panel; + var adornerLayer = (Panel)_focusAdorner.Parent; adornerLayer.Children.Remove(_focusAdorner); _focusAdorner = null; } diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 5d12c9963f..aa209e0462 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -354,7 +354,7 @@ namespace Avalonia.Controls } var collection = sender as ICollection; - PseudoClasses.Set(":empty", collection.Count == 0); + PseudoClasses.Set(":empty", collection == null || collection.Count == 0); } /// diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 9748e5e772..d2e8085d8c 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -720,7 +720,7 @@ namespace Avalonia.Controls if (pos < text.Length) { --pos; - if (pos > 0 && Text[pos - 1] == '\r' && Text[pos] == '\n') + if (pos > 0 && text[pos - 1] == '\r' && text[pos] == '\n') { --pos; } @@ -771,6 +771,9 @@ namespace Avalonia.Controls private string GetSelection() { + var text = Text; + if (string.IsNullOrEmpty(text)) + return ""; var selectionStart = SelectionStart; var selectionEnd = SelectionEnd; var start = Math.Min(selectionStart, selectionEnd); @@ -779,7 +782,7 @@ namespace Avalonia.Controls { return ""; } - return Text.Substring(start, end - start); + return text.Substring(start, end - start); } private int GetLine(int caretIndex, IList lines) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 53dd905eca..7e0f4b7ac1 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -106,7 +106,7 @@ namespace Avalonia.Controls _accessKeyHandler?.SetOwner(this); styler?.ApplyStyles(this); - ClientSize = PlatformImpl.ClientSize; + ClientSize = impl.ClientSize; this.GetObservable(PointerOverElementProperty) .Select( diff --git a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs index fc7fdd2431..f8e8d4b094 100644 --- a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs +++ b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs @@ -82,7 +82,13 @@ namespace Avalonia private void LoadAssembliesInDirectory() { - foreach (var file in new FileInfo(Assembly.GetEntryAssembly().Location).Directory.EnumerateFiles("*.dll")) + var location = Assembly.GetEntryAssembly().Location; + if(location == null) + return; + var dir = new FileInfo(location).Directory; + if (dir == null) + return; + foreach (var file in dir.EnumerateFiles("*.dll")) { try { diff --git a/src/Avalonia.Styling/Controls/NameScope.cs b/src/Avalonia.Styling/Controls/NameScope.cs index 4c5875479e..8b5bd81d3c 100644 --- a/src/Avalonia.Styling/Controls/NameScope.cs +++ b/src/Avalonia.Styling/Controls/NameScope.cs @@ -50,7 +50,7 @@ namespace Avalonia.Controls return result; } - visual = (visual as ILogical).LogicalParent as Visual; + visual = (visual as ILogical)?.LogicalParent as Visual; } return null; diff --git a/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs b/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs index b9c43bcbc3..491541cd2e 100644 --- a/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs +++ b/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs @@ -8,6 +8,6 @@ namespace Avalonia.Rendering { public static readonly ZIndexComparer Instance = new ZIndexComparer(); - public int Compare(IVisual x, IVisual y) => x.ZIndex.CompareTo(y.ZIndex); + public int Compare(IVisual x, IVisual y) => (x?.ZIndex ?? 0).CompareTo(y?.ZIndex ?? 0); } } diff --git a/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs b/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs index 7d1776db0b..99b0a2ec73 100644 --- a/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs +++ b/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs @@ -9,6 +9,7 @@ using Avalonia.Cairo.Media.Imaging; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering; +// ReSharper disable PossibleNullReferenceException namespace Avalonia.Cairo.Media { diff --git a/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs b/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs index efdadc2379..f6232ac68e 100644 --- a/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs +++ b/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs @@ -28,6 +28,7 @@ namespace Avalonia.Gtk3 List disposables = null; Action dispose = () => { + // ReSharper disable once PossibleNullReferenceException foreach (var d in disposables) d.Dispose(); disposables.Clear(); diff --git a/src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs b/src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs index 8cf6f9794e..c4a7e188c6 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs @@ -68,7 +68,7 @@ namespace Avalonia.Markup.Xaml.Parsers } else if (property != null) { - var type = result.TargetType; + var type = result?.TargetType; if (type == null) { From 0ccd5fb3bead915f18a19fd3e8375e30bfda2444 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 26 May 2017 17:00:06 +0300 Subject: [PATCH 3/7] Speed limit enforced by aircraft --- build.cake | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/build.cake b/build.cake index 2f6bf5b0c1..00bc768b01 100644 --- a/build.cake +++ b/build.cake @@ -6,6 +6,7 @@ #addin "nuget:?package=NuGet.Core&version=2.12.0" #tool "nuget:https://dotnet.myget.org/F/nuget-build/?package=NuGet.CommandLine&version=4.3.0-preview1-3980&prerelease" #tool "nuget:?package=JetBrains.dotMemoryUnit&version=2.1.20150828.125449" +#tool "JetBrains.ReSharper.CommandLineTools" /////////////////////////////////////////////////////////////////////////////// // TOOLS /////////////////////////////////////////////////////////////////////////////// @@ -104,7 +105,7 @@ Task("Restore-NuGet-Packages") .Does(() => { var maxRetryCount = 5; - var toolTimeout = 1d; + var toolTimeout = 2d; Policy .Handle() .Retry(maxRetryCount, (exception, retryCount, context) => { @@ -287,6 +288,7 @@ Task("Zip-Files") Task("Create-NuGet-Packages") .IsDependentOn("Run-Unit-Tests") + .IsDependentOn("Inspect") .Does(() => { foreach(var nuspec in packages.NuspecNuGetSettings) @@ -363,6 +365,39 @@ Task("Publish-NuGet") Information("Publish-NuGet Task failed, but continuing with next Task..."); }); +Task("Inspect") + .WithCriteria(parameters.IsRunningOnWindows) + .IsDependentOn("Restore-NuGet-Packages") + .Does(() => + { + var badIssues = new []{"PossibleNullReferenceException"}; + var whitelist = new []{"tests\\", "src\\android", "src\\ios", + @"src\windows\avalonia.designer", @"src\avalonia.htmlrenderer\external"}; + Information("Running code inspections"); + + + StartProcess(@"tools\JetBrains.ReSharper.CommandLineTools\tools\inspectcode.exe", + new ProcessSettings{ Arguments = @"--output=artifacts\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln" }); + Information("Analyzing report"); + var doc = XDocument.Parse(System.IO.File.ReadAllText(@"artifacts\inspectcode.xml")); + var failBuild = false; + foreach(var xml in doc.Descendants("Issue")) + { + var typeId = xml.Attribute("TypeId").Value.ToString(); + if(badIssues.Contains(typeId)) + { + var file = xml.Attribute("File").Value.ToString().ToLower(); + if(whitelist.Any(wh => file.StartsWith(wh))) + continue; + var line = xml.Attribute("Line").Value.ToString(); + Error(typeId + " - " + file + " on line " + line); + failBuild = true; + } + } + if(failBuild) + throw new Exception("Issues found"); + }); + /////////////////////////////////////////////////////////////////////////////// // TARGETS /////////////////////////////////////////////////////////////////////////////// From 3d3786e7423c907db28ea94248c75beeb5085136 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 26 May 2017 17:36:53 +0300 Subject: [PATCH 4/7] Cake + Mono = HATE https://github.com/cake-build/cake/issues/1167 --- build.cake | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.cake b/build.cake index 00bc768b01..8cfdc3523a 100644 --- a/build.cake +++ b/build.cake @@ -371,15 +371,15 @@ Task("Inspect") .Does(() => { var badIssues = new []{"PossibleNullReferenceException"}; - var whitelist = new []{"tests\\", "src\\android", "src\\ios", - @"src\windows\avalonia.designer", @"src\avalonia.htmlrenderer\external"}; + var whitelist = new []{"tests", "src\\android", "src\\ios", + "src\\windows\\avalonia.designer", "src\\avalonia.htmlrenderer\\external"}; Information("Running code inspections"); - StartProcess(@"tools\JetBrains.ReSharper.CommandLineTools\tools\inspectcode.exe", - new ProcessSettings{ Arguments = @"--output=artifacts\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln" }); + StartProcess("tools\\JetBrains.ReSharper.CommandLineTools\\tools\\inspectcode.exe", + new ProcessSettings{ Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln" }); Information("Analyzing report"); - var doc = XDocument.Parse(System.IO.File.ReadAllText(@"artifacts\inspectcode.xml")); + var doc = XDocument.Parse(System.IO.File.ReadAllText("artifacts\\inspectcode.xml")); var failBuild = false; foreach(var xml in doc.Descendants("Issue")) { From b18a10383440bce406f1976ace26d42639038c20 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 27 May 2017 11:23:53 +0300 Subject: [PATCH 5/7] PR notes --- src/Avalonia.Controls/Control.cs | 2 +- src/Avalonia.Controls/TopLevel.cs | 2 +- src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index fa0d8fe7d6..27b046cf47 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -645,7 +645,7 @@ namespace Avalonia.Controls if (_focusAdorner != null) { - var adornerLayer = (Panel)_focusAdorner.Parent; + var adornerLayer = (IPanel)_focusAdorner.Parent; adornerLayer.Children.Remove(_focusAdorner); _focusAdorner = null; } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 7e0f4b7ac1..567ca80d6e 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -189,7 +189,7 @@ namespace Avalonia.Controls protected virtual IRenderTarget CreateRenderTarget() { if(PlatformImpl == null) - throw new InvalidOperationException("PlatformImpl isn't available"); + throw new InvalidOperationException("Cann't create render target, PlatformImpl is null (might be already disposed)"); return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces); } diff --git a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs index f8e8d4b094..9a54cdbab0 100644 --- a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs +++ b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs @@ -83,7 +83,7 @@ namespace Avalonia private void LoadAssembliesInDirectory() { var location = Assembly.GetEntryAssembly().Location; - if(location == null) + if (string.IsNullOrWhiteSpace(location)) return; var dir = new FileInfo(location).Directory; if (dir == null) From 470e64fbe0cc1c979434a433888b82dc4715557f Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 27 May 2017 11:24:09 +0300 Subject: [PATCH 6/7] Added inspectcode.xml to artifact list --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 4bf7f7f157..6b63176a89 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -35,6 +35,7 @@ test: off artifacts: - path: artifacts\nuget\*.nupkg - path: artifacts\zip\*.zip + - path: artifacts\inspectcode.xml cache: - gtk-sharp-2.12.26.msi - dotnet-1.0.1.exe From 25ba0c8207ba050086e8ae8875def58f7836a9a0 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 27 May 2017 12:48:51 +0300 Subject: [PATCH 7/7] Updated dotMemory --- build.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.cake b/build.cake index 8cfdc3523a..c5d4c9a43e 100644 --- a/build.cake +++ b/build.cake @@ -5,7 +5,7 @@ #addin "nuget:?package=Polly&version=4.2.0" #addin "nuget:?package=NuGet.Core&version=2.12.0" #tool "nuget:https://dotnet.myget.org/F/nuget-build/?package=NuGet.CommandLine&version=4.3.0-preview1-3980&prerelease" -#tool "nuget:?package=JetBrains.dotMemoryUnit&version=2.1.20150828.125449" +#tool "nuget:?package=JetBrains.dotMemoryUnit&version=2.3.20160517.113140" #tool "JetBrains.ReSharper.CommandLineTools" /////////////////////////////////////////////////////////////////////////////// // TOOLS