Browse Source

Merge branch 'master' into adorner-layer-clipping

pull/6235/head
Jumar Macato 5 years ago
committed by GitHub
parent
commit
998b15b12c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 36
      native/Avalonia.Native/src/OSX/window.mm
  2. 40
      src/Avalonia.Base/Collections/AvaloniaList.cs
  3. 24
      src/Avalonia.Controls.DataGrid/Utils/ReflectionHelper.cs
  4. 20
      src/Avalonia.Controls/Primitives/Thumb.cs
  5. 28
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
  6. 104
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

36
native/Avalonia.Native/src/OSX/window.mm

@ -29,10 +29,12 @@ public:
IAvnMenu* _mainMenu; IAvnMenu* _mainMenu;
bool _shown; bool _shown;
bool _inResize;
WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl) WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl)
{ {
_shown = false; _shown = false;
_inResize = false;
_mainMenu = nullptr; _mainMenu = nullptr;
BaseEvents = events; BaseEvents = events;
_glContext = gl; _glContext = gl;
@ -277,6 +279,13 @@ public:
virtual HRESULT Resize(double x, double y) override virtual HRESULT Resize(double x, double y) override
{ {
if(_inResize)
{
return S_OK;
}
_inResize = true;
START_COM_CALL; START_COM_CALL;
@autoreleasepool @autoreleasepool
@ -304,13 +313,19 @@ public:
y = maxSize.height; y = maxSize.height;
} }
if(!_shown) @try
{ {
BaseEvents->Resized(AvnSize{x,y}); if(!_shown)
{
BaseEvents->Resized(AvnSize{x,y});
}
[Window setContentSize:NSSize{x, y}];
}
@finally
{
_inResize = false;
} }
[StandardContainer setFrameSize:NSSize{x,y}];
[Window setContentSize:NSSize{x, y}];
return S_OK; return S_OK;
} }
@ -757,6 +772,7 @@ private:
} }
_lastWindowState = state; _lastWindowState = state;
_actualWindowState = state;
WindowEvents->WindowStateChanged(state); WindowEvents->WindowStateChanged(state);
} }
} }
@ -1276,6 +1292,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
[_blurBehind setWantsLayer:true]; [_blurBehind setWantsLayer:true];
_blurBehind.hidden = true; _blurBehind.hidden = true;
[_blurBehind setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[_content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[self addSubview:_blurBehind]; [self addSubview:_blurBehind];
[self addSubview:_content]; [self addSubview:_content];
@ -1311,9 +1330,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_settingSize = true; _settingSize = true;
[super setFrameSize:newSize]; [super setFrameSize:newSize];
[_blurBehind setFrameSize:newSize];
[_content setFrameSize:newSize];
auto window = objc_cast<AvnWindow>([self window]); auto window = objc_cast<AvnWindow>([self window]);
// TODO get actual titlebar size // TODO get actual titlebar size
@ -1329,6 +1345,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
[_titleBarMaterial setFrame:tbar]; [_titleBarMaterial setFrame:tbar];
tbar.size.height = height < 1 ? 0 : 1; tbar.size.height = height < 1 ? 0 : 1;
[_titleBarUnderline setFrame:tbar]; [_titleBarUnderline setFrame:tbar];
_settingSize = false; _settingSize = false;
} }
@ -2374,11 +2391,12 @@ protected:
virtual HRESULT Resize(double x, double y) override virtual HRESULT Resize(double x, double y) override
{ {
START_COM_CALL;
@autoreleasepool @autoreleasepool
{ {
if (Window != nullptr) if (Window != nullptr)
{ {
[StandardContainer setFrameSize:NSSize{x,y}];
[Window setContentSize:NSSize{x, y}]; [Window setContentSize:NSSize{x, y}];
[Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))];

40
src/Avalonia.Base/Collections/AvaloniaList.cs

@ -454,6 +454,28 @@ namespace Avalonia.Collections
} }
} }
/// <summary>
/// Ensures that the capacity of the list is at least <see cref="capacity"/>.
/// </summary>
/// <param name="capacity">The capacity.</param>
public void EnsureCapacity(int capacity)
{
// Adapted from List<T> implementation.
var currentCapacity = _inner.Capacity;
if (currentCapacity < capacity)
{
var newCapacity = currentCapacity == 0 ? 4 : currentCapacity * 2;
if (newCapacity < capacity)
{
newCapacity = capacity;
}
_inner.Capacity = newCapacity;
}
}
/// <summary> /// <summary>
/// Removes an item from the collection. /// Removes an item from the collection.
/// </summary> /// </summary>
@ -633,24 +655,6 @@ namespace Avalonia.Collections
/// <inheritdoc/> /// <inheritdoc/>
Delegate[] INotifyCollectionChangedDebug.GetCollectionChangedSubscribers() => _collectionChanged?.GetInvocationList(); Delegate[] INotifyCollectionChangedDebug.GetCollectionChangedSubscribers() => _collectionChanged?.GetInvocationList();
private void EnsureCapacity(int capacity)
{
// Adapted from List<T> implementation.
var currentCapacity = _inner.Capacity;
if (currentCapacity < capacity)
{
var newCapacity = currentCapacity == 0 ? 4 : currentCapacity * 2;
if (newCapacity < capacity)
{
newCapacity = capacity;
}
_inner.Capacity = newCapacity;
}
}
/// <summary> /// <summary>
/// Raises the <see cref="CollectionChanged"/> event with an add action. /// Raises the <see cref="CollectionChanged"/> event with an add action.
/// </summary> /// </summary>

24
src/Avalonia.Controls.DataGrid/Utils/ReflectionHelper.cs

@ -340,10 +340,30 @@ namespace Avalonia.Controls.Utils
internal static PropertyInfo GetPropertyOrIndexer(this Type type, string propertyPath, out object[] index) internal static PropertyInfo GetPropertyOrIndexer(this Type type, string propertyPath, out object[] index)
{ {
index = null; index = null;
// Return the default value of GetProperty if the first character is not an indexer token.
if (string.IsNullOrEmpty(propertyPath) || propertyPath[0] != LeftIndexerToken) if (string.IsNullOrEmpty(propertyPath) || propertyPath[0] != LeftIndexerToken)
{ {
// Return the default value of GetProperty if the first character is not an indexer token. var property = type.GetProperty(propertyPath);
return type.GetProperty(propertyPath); if (property != null)
{
return property;
}
// GetProperty does not return inherited interface properties,
// so we need to enumerate them manually.
if (type.IsInterface)
{
foreach (var typeInterface in type.GetInterfaces())
{
property = type.GetProperty(propertyPath);
if (property != null)
{
return property;
}
}
}
return null;
} }
if (propertyPath.Length < 2 || propertyPath[propertyPath.Length - 1] != RightIndexerToken) if (propertyPath.Length < 2 || propertyPath[propertyPath.Length - 1] != RightIndexerToken)

20
src/Avalonia.Controls/Primitives/Thumb.cs

@ -56,6 +56,26 @@ namespace Avalonia.Controls.Primitives
{ {
} }
protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
{
if (_lastPoint.HasValue)
{
var ev = new VectorEventArgs
{
RoutedEvent = DragCompletedEvent,
Vector = _lastPoint.Value,
};
_lastPoint = null;
RaiseEvent(ev);
}
PseudoClasses.Remove(":pressed");
base.OnPointerCaptureLost(e);
}
protected override void OnPointerMoved(PointerEventArgs e) protected override void OnPointerMoved(PointerEventArgs e)
{ {
if (_lastPoint.HasValue) if (_lastPoint.HasValue)

28
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs

@ -9,7 +9,6 @@ using XamlX.Ast;
using XamlX.Transform; using XamlX.Transform;
using XamlX.Transform.Transformers; using XamlX.Transform.Transformers;
using XamlX.TypeSystem; using XamlX.TypeSystem;
using XamlParseException = XamlX.XamlParseException; using XamlParseException = XamlX.XamlParseException;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
@ -21,6 +20,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
if (node is XamlAstObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension)) if (node is XamlAstObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension))
{ {
var convertedNode = ConvertLongFormPropertiesToBindingExpressionNode(context, binding); var convertedNode = ConvertLongFormPropertiesToBindingExpressionNode(context, binding);
var foundPath = false;
if (binding.Arguments.Count > 0 && binding.Arguments[0] is XamlAstTextNode bindingPathText) if (binding.Arguments.Count > 0 && binding.Arguments[0] is XamlAstTextNode bindingPathText)
{ {
@ -32,9 +32,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode); nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode);
} }
binding.Arguments[0] = new ParsedBindingPathNode(bindingPathText, context.GetAvaloniaTypes().CompiledBindingPath, nodes); if (nodes.Count == 1 && nodes[0] is BindingExpressionGrammar.EmptyExpressionNode)
{
binding.Arguments.RemoveAt(0);
}
else
{
binding.Arguments[0] = new ParsedBindingPathNode(bindingPathText, context.GetAvaloniaTypes().CompiledBindingPath, nodes);
foundPath = true;
}
} }
else
if (!foundPath)
{ {
var bindingPathAssignment = binding.Children.OfType<XamlAstXamlPropertyValueNode>() var bindingPathAssignment = binding.Children.OfType<XamlAstXamlPropertyValueNode>()
.FirstOrDefault(v => v.Property.GetClrProperty().Name == "Path"); .FirstOrDefault(v => v.Property.GetClrProperty().Name == "Path");
@ -44,12 +53,19 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var reader = new CharacterReader(pathValue.Text.AsSpan()); var reader = new CharacterReader(pathValue.Text.AsSpan());
var (nodes, _) = BindingExpressionGrammar.Parse(ref reader); var (nodes, _) = BindingExpressionGrammar.Parse(ref reader);
if (convertedNode != null) if (nodes.Count == 1 && nodes[0] is BindingExpressionGrammar.EmptyExpressionNode)
{ {
nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode); bindingPathAssignment.Values.RemoveAt(0);
} }
else
{
if (convertedNode != null)
{
nodes.Insert(nodes.TakeWhile(x => x is BindingExpressionGrammar.ITransformNode).Count(), convertedNode);
}
bindingPathAssignment.Values[0] = new ParsedBindingPathNode(pathValue, context.GetAvaloniaTypes().CompiledBindingPath, nodes); bindingPathAssignment.Values[0] = new ParsedBindingPathNode(pathValue, context.GetAvaloniaTypes().CompiledBindingPath, nodes);
}
} }
} }
} }

104
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@ -938,6 +938,110 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
} }
} }
[Fact]
public void SupportsEmptyPath()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
x:DataType='local:TestDataContext'>
<TextBlock Text='{CompiledBinding}' Name='textBlock' />
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
var dataContext = new TestDataContext
{
StringProperty = "foobar"
};
window.DataContext = dataContext;
Assert.Equal(typeof(TestDataContext).FullName, textBlock.Text);
}
}
[Fact]
public void SupportsEmptyPathWithStringFormat()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
x:DataType='local:TestDataContext'>
<TextBlock Text='{CompiledBinding StringFormat=bar-\{0\}}' Name='textBlock' />
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
var dataContext = new TestDataContext
{
StringProperty = "foobar"
};
window.DataContext = dataContext;
Assert.Equal("bar-" + typeof(TestDataContext).FullName, textBlock.Text);
}
}
[Fact]
public void SupportsDotPath()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
x:DataType='local:TestDataContext'>
<TextBlock Text='{CompiledBinding .}' Name='textBlock' />
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
var dataContext = new TestDataContext
{
StringProperty = "foobar"
};
window.DataContext = dataContext;
Assert.Equal(typeof(TestDataContext).FullName, textBlock.Text);
}
}
[Fact]
public void SupportsExplicitDotPathWithStringFormat()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
x:DataType='local:TestDataContext'>
<TextBlock Text='{CompiledBinding Path=., StringFormat=bar-\{0\}}' Name='textBlock' />
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
var dataContext = new TestDataContext
{
StringProperty = "foobar"
};
window.DataContext = dataContext;
Assert.Equal("bar-" + typeof(TestDataContext).FullName, textBlock.Text);
}
}
void Throws(string type, Action cb) void Throws(string type, Action cb)
{ {
try try

Loading…
Cancel
Save