From c0a595f48e8601296ffbf667e0192dc1961d988e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 28 Jul 2019 18:32:37 +0200 Subject: [PATCH 01/15] Fix scrolling to selected item in TreeView. - Define a `PART_Header` in the `TreeViewItem` template which represents the header to be scrolled into view when a `TreeViewItem` is selected. Make this header only take up the minimum of required space. - Catch the `RequestBringIntoViewEventArgs` in `TreeViewItem` and update the target rect to that of `PART_Header` --- src/Avalonia.Controls/TreeViewItem.cs | 23 +++++++++++++++++++ src/Avalonia.Themes.Default/TreeViewItem.xaml | 4 +++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs index 4d229390cb..e185bc227b 100644 --- a/src/Avalonia.Controls/TreeViewItem.cs +++ b/src/Avalonia.Controls/TreeViewItem.cs @@ -42,6 +42,7 @@ namespace Avalonia.Controls new FuncTemplate(() => new StackPanel()); private TreeView _treeView; + private IControl _header; private bool _isExpanded; private int _level; @@ -53,6 +54,7 @@ namespace Avalonia.Controls SelectableMixin.Attach(IsSelectedProperty); FocusableProperty.OverrideDefaultValue(true); ItemsPanelProperty.OverrideDefaultValue(DefaultPanel); + RequestBringIntoViewEvent.AddClassHandler(x => x.OnRequestBringIntoView); } /// @@ -120,6 +122,21 @@ namespace Avalonia.Controls ItemContainerGenerator.Clear(); } + protected virtual void OnRequestBringIntoView(RequestBringIntoViewEventArgs e) + { + if (e.TargetObject == this && _header != null) + { + var m = _header.TransformToVisual(this); + + if (m.HasValue) + { + var bounds = new Rect(_header.Bounds.Size); + var rect = bounds.TransformToAABB(m.Value); + e.TargetRect = rect; + } + } + } + /// protected override void OnKeyDown(KeyEventArgs e) { @@ -146,6 +163,12 @@ namespace Avalonia.Controls // Don't call base.OnKeyDown - let events bubble up to containing TreeView. } + protected override void OnTemplateApplied(TemplateAppliedEventArgs e) + { + base.OnTemplateApplied(e); + _header = e.NameScope.Find("PART_Header"); + } + private static int CalculateDistanceFromLogicalParent(ILogical logical, int @default = -1) where T : class { var result = 0; diff --git a/src/Avalonia.Themes.Default/TreeViewItem.xaml b/src/Avalonia.Themes.Default/TreeViewItem.xaml index 5dd082cf7a..0d826806d0 100644 --- a/src/Avalonia.Themes.Default/TreeViewItem.xaml +++ b/src/Avalonia.Themes.Default/TreeViewItem.xaml @@ -16,7 +16,9 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" TemplatedControl.IsTemplateFocusTarget="True"> - Date: Wed, 21 Aug 2019 01:15:55 +0100 Subject: [PATCH 02/15] Fixed Binding.DoNothing for MultiBinding. --- .../Avalonia.Markup/Data/MultiBinding.cs | 12 +-- .../Converters/MultiValueConverterTests.cs | 76 +++++++++++++++++++ 2 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Converters/MultiValueConverterTests.cs diff --git a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs index 19f92149ec..29945e25c3 100644 --- a/src/Markup/Avalonia.Markup/Data/MultiBinding.cs +++ b/src/Markup/Avalonia.Markup/Data/MultiBinding.cs @@ -76,7 +76,12 @@ namespace Avalonia.Data } var children = Bindings.Select(x => x.Initiate(target, null)); - var input = children.Select(x => x.Observable).CombineLatest().Select(x => ConvertValue(x, targetType, converter)); + + var input = children.Select(x => x.Observable) + .CombineLatest() + .Select(x => ConvertValue(x, targetType, converter)) + .Where(x => x != BindingOperations.DoNothing); + var mode = Mode == BindingMode.Default ? targetProperty?.GetMetadata(target.GetType()).DefaultBindingMode : Mode; @@ -97,11 +102,6 @@ namespace Avalonia.Data var culture = CultureInfo.CurrentCulture; var converted = converter.Convert(values, targetType, ConverterParameter, culture); - if (converted == BindingOperations.DoNothing) - { - return converted; - } - if (converted == AvaloniaProperty.UnsetValue) { converted = FallbackValue; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/MultiValueConverterTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/MultiValueConverterTests.cs new file mode 100644 index 0000000000..a77723afe1 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/MultiValueConverterTests.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Data.Converters; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.Converters +{ + public class MultiValueConverterTests : XamlTestBase + { + [Fact] + public void MultiValueConverter_Special_Values_Work() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + window.ApplyTemplate(); + + window.DataContext = Tuple.Create(2, 2); + Assert.Equal("foo", textBlock.Text); + + window.DataContext = Tuple.Create(-3, 3); + Assert.Equal("foo", textBlock.Text); + + window.DataContext = Tuple.Create(0, 2); + Assert.Equal("bar", textBlock.Text); + } + } + } + + public class TestMultiValueConverter : IMultiValueConverter + { + public static readonly TestMultiValueConverter Instance = new TestMultiValueConverter(); + + public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) + { + if (values[0] is int i && values[1] is int j) + { + var p = i * j; + + if (p > 0) + { + return "foo"; + } + + if (p == 0) + { + return AvaloniaProperty.UnsetValue; + } + + return BindingOperations.DoNothing; + } + + return "(default)"; + } + } +} From b376313acff353872ce610d39997c9c553180ed4 Mon Sep 17 00:00:00 2001 From: ahopper Date: Wed, 28 Aug 2019 16:28:17 +0100 Subject: [PATCH 03/15] speed up GetValue and GetDefaultValue --- src/Avalonia.Base/AvaloniaObject.cs | 49 +++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 0e2f0feada..2fee277f21 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -208,20 +208,9 @@ namespace Avalonia { return ((IDirectPropertyAccessor)GetRegistered(property)).GetValue(this); } - else if (_values != null) - { - var result = Values.GetValue(property); - - if (result == AvaloniaProperty.UnsetValue) - { - result = GetDefaultValue(property); - } - - return result; - } else { - return GetDefaultValue(property); + return GetValueOrDefault(property); } } @@ -598,10 +587,44 @@ namespace Avalonia private object GetDefaultValue(AvaloniaProperty property) { if (property.Inherits && InheritanceParent is AvaloniaObject aobj) - return aobj.GetValue(property); + return aobj.GetValueOrDefault(property); return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType()); } + /// + /// Gets the value or default value for a property. + /// + /// The property. + /// The default value. + private object GetValueOrDefault(AvaloniaProperty property) + { + var aobj = this; + if (aobj.Values != null) + { + var result = aobj.Values.GetValue(property); + if (result != AvaloniaProperty.UnsetValue) + { + return result; + } + } + if (property.Inherits) + { + while(aobj.InheritanceParent is AvaloniaObject parent) + { + aobj = parent; + if (aobj.Values != null) + { + var result = aobj.Values.GetValue(property); + if (result != AvaloniaProperty.UnsetValue) + { + return result; + } + } + } + } + return ((IStyledPropertyAccessor)property).GetDefaultValue(GetType()); + } + /// /// Sets the value of a direct property. /// From ebccccb8e4c8a6138274eeb1e51769f93919c857 Mon Sep 17 00:00:00 2001 From: ahopper Date: Wed, 28 Aug 2019 17:37:01 +0100 Subject: [PATCH 04/15] add UnChecked suffix --- src/Avalonia.Base/AvaloniaObject.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 2fee277f21..5fa6dab29e 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -210,7 +210,7 @@ namespace Avalonia } else { - return GetValueOrDefault(property); + return GetValueOrDefaultUnChecked(property); } } @@ -596,7 +596,7 @@ namespace Avalonia /// /// The property. /// The default value. - private object GetValueOrDefault(AvaloniaProperty property) + private object GetValueOrDefaultUnChecked(AvaloniaProperty property) { var aobj = this; if (aobj.Values != null) From e8ba46160d3aad056a121b23b82f5290d8d360e6 Mon Sep 17 00:00:00 2001 From: ahopper Date: Wed, 28 Aug 2019 17:39:59 +0100 Subject: [PATCH 05/15] UnChecked suffix fixed --- src/Avalonia.Base/AvaloniaObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 5fa6dab29e..f8efb36562 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -587,7 +587,7 @@ namespace Avalonia private object GetDefaultValue(AvaloniaProperty property) { if (property.Inherits && InheritanceParent is AvaloniaObject aobj) - return aobj.GetValueOrDefault(property); + return aobj.GetValueOrDefaultUnChecked(property); return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType()); } From 77d9ae1cacfee35930c0543643c34a167ffe3b87 Mon Sep 17 00:00:00 2001 From: ahopper Date: Fri, 30 Aug 2019 10:18:30 +0100 Subject: [PATCH 06/15] change UnChecked suffix to Unchecked --- src/Avalonia.Base/AvaloniaObject.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index f8efb36562..48a222d5b4 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -210,7 +210,7 @@ namespace Avalonia } else { - return GetValueOrDefaultUnChecked(property); + return GetValueOrDefaultUnchecked(property); } } @@ -587,7 +587,7 @@ namespace Avalonia private object GetDefaultValue(AvaloniaProperty property) { if (property.Inherits && InheritanceParent is AvaloniaObject aobj) - return aobj.GetValueOrDefaultUnChecked(property); + return aobj.GetValueOrDefaultUnchecked(property); return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType()); } @@ -596,7 +596,7 @@ namespace Avalonia /// /// The property. /// The default value. - private object GetValueOrDefaultUnChecked(AvaloniaProperty property) + private object GetValueOrDefaultUnchecked(AvaloniaProperty property) { var aobj = this; if (aobj.Values != null) From 4d8973226d10ed188181367d5b522f0e2fd99bc7 Mon Sep 17 00:00:00 2001 From: ahopper Date: Fri, 30 Aug 2019 10:35:24 +0100 Subject: [PATCH 07/15] unbreak lazy initialization of Values --- src/Avalonia.Base/AvaloniaObject.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 48a222d5b4..035e0056e0 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -599,9 +599,9 @@ namespace Avalonia private object GetValueOrDefaultUnchecked(AvaloniaProperty property) { var aobj = this; - if (aobj.Values != null) + if (aobj._values != null) { - var result = aobj.Values.GetValue(property); + var result = aobj._values.GetValue(property); if (result != AvaloniaProperty.UnsetValue) { return result; @@ -612,9 +612,9 @@ namespace Avalonia while(aobj.InheritanceParent is AvaloniaObject parent) { aobj = parent; - if (aobj.Values != null) + if (aobj._values != null) { - var result = aobj.Values.GetValue(property); + var result = aobj._values.GetValue(property); if (result != AvaloniaProperty.UnsetValue) { return result; From 02ebded6a227481bde5347d5fc49ae1e2b6cde0d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 31 Aug 2019 18:25:23 +0200 Subject: [PATCH 08/15] Don't bring hidden controls into view. --- src/Avalonia.Controls/ControlExtensions.cs | 15 +++++++++------ .../Presenters/ScrollContentPresenter.cs | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/ControlExtensions.cs b/src/Avalonia.Controls/ControlExtensions.cs index 2fccb15acc..3bd222d132 100644 --- a/src/Avalonia.Controls/ControlExtensions.cs +++ b/src/Avalonia.Controls/ControlExtensions.cs @@ -34,14 +34,17 @@ namespace Avalonia.Controls { Contract.Requires(control != null); - var ev = new RequestBringIntoViewEventArgs + if (control.IsEffectivelyVisible) { - RoutedEvent = Control.RequestBringIntoViewEvent, - TargetObject = control, - TargetRect = rect, - }; + var ev = new RequestBringIntoViewEventArgs + { + RoutedEvent = Control.RequestBringIntoViewEvent, + TargetObject = control, + TargetRect = rect, + }; - control.RaiseEvent(ev); + control.RaiseEvent(ev); + } } /// diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index e7d8018a42..ec6a228421 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -141,7 +141,7 @@ namespace Avalonia.Controls.Presenters /// True if the scroll offset was changed; otherwise false. public bool BringDescendantIntoView(IVisual target, Rect targetRect) { - if (Child == null) + if (Child?.IsEffectivelyVisible != true) { return false; } From 26b1320971d3e7b8f75e0a7f5b4a2f84f71d9fb0 Mon Sep 17 00:00:00 2001 From: ahopper Date: Sun, 1 Sep 2019 19:39:18 +0100 Subject: [PATCH 09/15] reduce repeated field access --- src/Avalonia.Base/AvaloniaObject.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 035e0056e0..c619d80e23 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -599,9 +599,10 @@ namespace Avalonia private object GetValueOrDefaultUnchecked(AvaloniaProperty property) { var aobj = this; - if (aobj._values != null) + var valuestore = aobj._values; + if (valuestore != null) { - var result = aobj._values.GetValue(property); + var result = valuestore.GetValue(property); if (result != AvaloniaProperty.UnsetValue) { return result; @@ -612,9 +613,10 @@ namespace Avalonia while(aobj.InheritanceParent is AvaloniaObject parent) { aobj = parent; - if (aobj._values != null) + valuestore = aobj._values; + if (valuestore != null) { - var result = aobj._values.GetValue(property); + var result = valuestore.GetValue(property); if (result != AvaloniaProperty.UnsetValue) { return result; From bd354143caba5dca2ad0878989803edac2caa5c2 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 3 Sep 2019 18:04:48 +0200 Subject: [PATCH 10/15] Notify MouseDevice when TopLevel closed. So it can remove pointerover state. --- src/Avalonia.Controls/TopLevel.cs | 2 +- src/Avalonia.Input/IMouseDevice.cs | 2 ++ src/Avalonia.Input/MouseDevice.cs | 5 +++++ .../TopLevelTests.cs | 18 ++++++++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 87100ceeb0..e0acab1133 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -269,8 +269,8 @@ namespace Avalonia.Controls /// protected virtual void HandleClosed() { + (this as IInputRoot).MouseDevice?.TopLevelClosed(this); PlatformImpl = null; - Closed?.Invoke(this, EventArgs.Empty); Renderer?.Dispose(); Renderer = null; diff --git a/src/Avalonia.Input/IMouseDevice.cs b/src/Avalonia.Input/IMouseDevice.cs index 7e6bf657ae..a641544f7a 100644 --- a/src/Avalonia.Input/IMouseDevice.cs +++ b/src/Avalonia.Input/IMouseDevice.cs @@ -16,6 +16,8 @@ namespace Avalonia.Input [Obsolete("Use PointerEventArgs.GetPosition")] PixelPoint Position { get; } + void TopLevelClosed(IInputRoot root); + void SceneInvalidated(IInputRoot root, Rect rect); } } diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index d5152f58d5..0d5471f790 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -86,6 +86,11 @@ namespace Avalonia.Input ProcessRawEvent(margs); } + public void TopLevelClosed(IInputRoot root) + { + ClearPointerOver(this, 0, root, PointerPointProperties.None, KeyModifiers.None); + } + public void SceneInvalidated(IInputRoot root, Rect rect) { var clientPoint = root.PointToClient(Position); diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs index c744543f99..901d780f16 100644 --- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs @@ -224,6 +224,24 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Close_Should_Notify_MouseDevice() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var impl = new Mock(); + var mouseDevice = new Mock(); + impl.SetupAllProperties(); + impl.Setup(x => x.MouseDevice).Returns(mouseDevice.Object); + + var target = new TestTopLevel(impl.Object); + + impl.Object.Closed(); + + mouseDevice.Verify(x => x.TopLevelClosed(target)); + } + } + private FuncControlTemplate CreateTemplate() { return new FuncControlTemplate((x, scope) => From c619d1f0d4ceb92cb8bfe6d6bad2f4bceb8954a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Wed, 4 Sep 2019 13:55:33 +0200 Subject: [PATCH 11/15] Cache delegate used for hit testing. --- src/Avalonia.Input/InputExtensions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Input/InputExtensions.cs b/src/Avalonia.Input/InputExtensions.cs index c1d0729560..f83c41e266 100644 --- a/src/Avalonia.Input/InputExtensions.cs +++ b/src/Avalonia.Input/InputExtensions.cs @@ -13,6 +13,8 @@ namespace Avalonia.Input /// public static class InputExtensions { + private static readonly Func s_hitTestDelegate = IsHitTestVisible; + /// /// Returns the active input elements at a point on an . /// @@ -25,7 +27,7 @@ namespace Avalonia.Input { Contract.Requires(element != null); - return element.GetVisualsAt(p, IsHitTestVisible).Cast(); + return element.GetVisualsAt(p, s_hitTestDelegate).Cast(); } /// From 8cb5eedcda7e06f76bc0be81dd4cd17f154033a8 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 5 Sep 2019 16:14:13 +0300 Subject: [PATCH 12/15] Use [NSApp run] instead of a custom run loop --- native/Avalonia.Native/inc/avalonia-native.h | 2 +- native/Avalonia.Native/src/OSX/app.mm | 24 ++++++++ native/Avalonia.Native/src/OSX/common.h | 2 +- native/Avalonia.Native/src/OSX/main.mm | 3 +- .../src/OSX/platformthreading.mm | 59 +++++++++++-------- 5 files changed, 61 insertions(+), 29 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/app.mm diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index a35f4f3eeb..36e16c24d1 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -280,7 +280,7 @@ AVNCOM(IAvnPlatformThreadingInterface, 0b) : IUnknown virtual bool GetCurrentThreadIsLoopThread() = 0; virtual void SetSignaledCallback(IAvnSignaledCallback* cb) = 0; virtual IAvnLoopCancellation* CreateLoopCancellation() = 0; - virtual void RunLoop(IAvnLoopCancellation* cancel) = 0; + virtual HRESULT RunLoop(IAvnLoopCancellation* cancel) = 0; // Can't pass int* to sharpgentools for some reason virtual void Signal(int priority) = 0; virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback) = 0; diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm new file mode 100644 index 0000000000..7d45720a8b --- /dev/null +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -0,0 +1,24 @@ +#include "common.h" +@interface AvnAppDelegate : NSObject +@end + +@implementation AvnAppDelegate +- (void)applicationWillFinishLaunching:(NSNotification *)notification +{ + +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification +{ + [NSApp activateIgnoringOtherApps:true]; +} + +@end + +extern void InitializeAvnApp() +{ + NSApplication* app = [NSApplication sharedApplication]; + id delegate = [AvnAppDelegate new]; + [app setDelegate:delegate]; + +} diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index f748dd59ad..a716af1e2c 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -19,7 +19,7 @@ extern IAvnClipboard* CreateClipboard(); extern IAvnCursorFactory* CreateCursorFactory(); extern IAvnGlFeature* GetGlFeature(); extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view); - +extern void InitializeAvnApp(); extern NSPoint ToNSPoint (AvnPoint p); extern AvnPoint ToAvnPoint (NSPoint p); extern AvnPoint ConvertPointY (AvnPoint p); diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 5043246c53..fec5b9b662 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -64,8 +64,9 @@ public: { @autoreleasepool{ [[ThreadingInitializer new] do]; - return S_OK; } + InitializeAvnApp(); + return S_OK; }; virtual IAvnMacOptions* GetMacOptions() override diff --git a/native/Avalonia.Native/src/OSX/platformthreading.mm b/native/Avalonia.Native/src/OSX/platformthreading.mm index 0c814b2088..297097584a 100644 --- a/native/Avalonia.Native/src/OSX/platformthreading.mm +++ b/native/Avalonia.Native/src/OSX/platformthreading.mm @@ -57,16 +57,36 @@ class PlatformThreadingInterface : public ComSingleObject { public: FORWARD_IUNKNOWN() - bool Cancelled = 0; - virtual void Cancel() override + bool Running = false; + bool Cancelled = false; + virtual void Cancel() { - Cancelled = 1; + Cancelled = true; + if(Running) + { + Running = false; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSApplication sharedApplication] stop:nil]; + NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:nil + subtype:0 + data1:0 + data2:0]; + [NSApp postEvent:event atStart:YES]; + }); + } } + }; public: @@ -99,30 +119,17 @@ public: return new LoopCancellation(); } - virtual void RunLoop(IAvnLoopCancellation* cancel) override + virtual HRESULT RunLoop(IAvnLoopCancellation* cancel) override { - @autoreleasepool { - auto can = dynamic_cast(cancel); - [[NSApplication sharedApplication] activateIgnoringOtherApps:true]; - while(true) - { - @autoreleasepool - { - if(can != NULL && can->Cancelled) - return; - NSEvent* ev = [[NSApplication sharedApplication] - nextEventMatchingMask:NSEventMaskAny - untilDate: [NSDate dateWithTimeIntervalSinceNow:1] - inMode:NSDefaultRunLoopMode - dequeue:true]; - if(can != NULL && can->Cancelled) - return; - if(ev != NULL) - [[NSApplication sharedApplication] sendEvent:ev]; - } - } - NSDebugLog(@"RunLoop exited"); - } + auto can = dynamic_cast(cancel); + if(can->Cancelled) + return S_OK; + if(_wasRunningAtLeastOnce) + return E_FAIL; + can->Running = true; + _wasRunningAtLeastOnce = true; + [NSApp run]; + return S_OK; } virtual void Signal(int priority) override From 349ffa16002016e346f6a966b170314e78486353 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 5 Sep 2019 17:01:11 +0300 Subject: [PATCH 13/15] Set activation policy from applicationWillFinishLaunching --- .../OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj | 4 ++++ native/Avalonia.Native/src/OSX/app.mm | 4 ++-- native/Avalonia.Native/src/OSX/common.h | 2 +- native/Avalonia.Native/src/OSX/main.mm | 11 ++--------- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index cc74d5669f..1870ef7ab3 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; }; 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; }; 37DDA9B0219330F8002E132B /* AvnString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37DDA9AF219330F8002E132B /* AvnString.mm */; }; @@ -22,6 +23,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; 379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = ""; }; 37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = ""; }; 37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = ""; }; @@ -68,6 +70,7 @@ AB7A61E62147C814003C5833 = { isa = PBXGroup; children = ( + 1A002B9D232135EE00021753 /* app.mm */, 37DDA9B121933371002E132B /* AvnString.h */, 37DDA9AF219330F8002E132B /* AvnString.mm */, 37A4E71A2178846A00EACBCD /* headers */, @@ -164,6 +167,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1A002B9E232135EE00021753 /* app.mm in Sources */, 5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */, 5B21A982216530F500CEE36E /* cursor.mm in Sources */, 37DDA9B0219330F8002E132B /* AvnString.mm in Sources */, diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 7d45720a8b..81855995b7 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -1,11 +1,11 @@ #include "common.h" @interface AvnAppDelegate : NSObject @end - +extern NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivationPolicyRegular; @implementation AvnAppDelegate - (void)applicationWillFinishLaunching:(NSNotification *)notification { - + [[NSApplication sharedApplication] setActivationPolicy: AvnDesiredActivationPolicy]; } - (void)applicationDidFinishLaunching:(NSNotification *)notification diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index a716af1e2c..45ec40c361 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -20,11 +20,11 @@ extern IAvnCursorFactory* CreateCursorFactory(); extern IAvnGlFeature* GetGlFeature(); extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view); extern void InitializeAvnApp(); +extern NSApplicationActivationPolicy AvnDesiredActivationPolicy; extern NSPoint ToNSPoint (AvnPoint p); extern AvnPoint ToAvnPoint (NSPoint p); extern AvnPoint ConvertPointY (AvnPoint p); extern NSSize ToNSSize (AvnSize s); - #ifdef DEBUG #define NSDebugLog(...) NSLog(__VA_ARGS__) #else diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index fec5b9b662..70bd1e67f6 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -5,21 +5,14 @@ #define COM_GUIDS_MATERIALIZE #include "common.h" -static BOOL ShowInDock = 1; - -static void SetActivationPolicy() -{ - [[NSApplication sharedApplication] setActivationPolicy: (ShowInDock ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory)]; -} - class MacOptions : public ComSingleObject { public: FORWARD_IUNKNOWN() virtual HRESULT SetShowInDock(int show) override { - ShowInDock = show; - SetActivationPolicy(); + AvnDesiredActivationPolicy = show + ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory; return S_OK; } }; From 66d79c025da7bfe9046def4c72e1514804d4c601 Mon Sep 17 00:00:00 2001 From: danwalmsley Date: Sat, 7 Sep 2019 22:34:38 +0100 Subject: [PATCH 14/15] Update Default.xaml --- src/Avalonia.Controls.DataGrid/Themes/Default.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index eaa267ba66..7b6870fec3 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -217,12 +217,12 @@ - + - + From 3dddb569821d2235b923662b8e6e4c590b940e59 Mon Sep 17 00:00:00 2001 From: ahopper Date: Sun, 8 Sep 2019 06:58:09 +0100 Subject: [PATCH 15/15] add space after while --- src/Avalonia.Base/AvaloniaObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index c619d80e23..94558c4367 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -610,7 +610,7 @@ namespace Avalonia } if (property.Inherits) { - while(aobj.InheritanceParent is AvaloniaObject parent) + while (aobj.InheritanceParent is AvaloniaObject parent) { aobj = parent; valuestore = aobj._values;