Skip to content

Commit

Permalink
Merge branch 'master' into fixes/OpacityMask
Browse files Browse the repository at this point in the history
  • Loading branch information
Gillibald authored Aug 24, 2021
2 parents 143b923 + 0f83ccb commit 1a63e3c
Show file tree
Hide file tree
Showing 14 changed files with 257 additions and 32 deletions.
32 changes: 29 additions & 3 deletions samples/ControlCatalog/Pages/ComboBoxPage.xaml
Original file line number Diff line number Diff line change
@@ -1,19 +1,45 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ComboBoxPage"
xmlns:sys="clr-namespace:System;assembly=netstandard">
xmlns:sys="using:System"
xmlns:col="using:System.Collections">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ComboBox</TextBlock>
<TextBlock Classes="h2">A drop-down list.</TextBlock>

<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Spacing="8">
<WrapPanel HorizontalAlignment="Center" Margin="0 16 0 0"
MaxWidth="750">
<WrapPanel.Styles>
<Style Selector="ComboBox">
<Setter Property="Width" Value="250" />
<Setter Property="Margin" Value="10" />
</Style>
</WrapPanel.Styles>
<ComboBox PlaceholderText="Pick an Item">
<ComboBoxItem>Inline Items</ComboBoxItem>
<ComboBoxItem>Inline Item 2</ComboBoxItem>
<ComboBoxItem>Inline Item 3</ComboBoxItem>
<ComboBoxItem>Inline Item 4</ComboBoxItem>
</ComboBox>

<ComboBox>
<ComboBox.Items>
<col:ArrayList>
<x:Null />
<sys:String>Hello</sys:String>
<sys:String>World</sys:String>
</col:ArrayList>
</ComboBox.Items>
<ComboBox.ItemTemplate>
<DataTemplate>
<Panel>
<TextBlock Text="{Binding}" />
<TextBlock Text="Null object" IsVisible="{Binding Converter={x:Static ObjectConverters.IsNull}}" />
</Panel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

<ComboBox SelectedIndex="0">
<ComboBoxItem>
<Panel>
Expand Down Expand Up @@ -46,7 +72,7 @@
<sys:Exception />
</DataValidationErrors.Error>
</ComboBox>
</StackPanel>
</WrapPanel>

</StackPanel>
</UserControl>
34 changes: 23 additions & 11 deletions src/Avalonia.Base/Utilities/TypeUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,25 @@ public static bool AcceptsNull(Type type)
return !type.IsValueType || IsNullableType(type);
}

/// <summary>
/// Returns a value indicating whether value can be casted to the specified type.
/// If value is null, checks if instances of that type can be null.
/// </summary>
/// <typeparam name="T">The type to cast to</typeparam>
/// <param name="value">The value to check if cast possible</param>
/// <returns>True if the cast is possible, otherwise false.</returns>
public static bool CanCast<T>(object value)
{
return value is T || (value is null && AcceptsNull(typeof(T)));
}

/// <summary>
/// Try to convert a value to a type by any means possible.
/// </summary>
/// <param name="to">The type to cast to.</param>
/// <param name="value">The value to cast.</param>
/// <param name="to">The type to convert to.</param>
/// <param name="value">The value to convert.</param>
/// <param name="culture">The culture to use.</param>
/// <param name="result">If successful, contains the cast value.</param>
/// <param name="result">If successful, contains the convert value.</param>
/// <returns>True if the cast was successful, otherwise false.</returns>
public static bool TryConvert(Type to, object value, CultureInfo culture, out object result)
{
Expand Down Expand Up @@ -216,10 +228,10 @@ public static bool TryConvert(Type to, object value, CultureInfo culture, out ob
/// Try to convert a value to a type using the implicit conversions allowed by the C#
/// language.
/// </summary>
/// <param name="to">The type to cast to.</param>
/// <param name="value">The value to cast.</param>
/// <param name="result">If successful, contains the cast value.</param>
/// <returns>True if the cast was successful, otherwise false.</returns>
/// <param name="to">The type to convert to.</param>
/// <param name="value">The value to convert.</param>
/// <param name="result">If successful, contains the converted value.</param>
/// <returns>True if the convert was successful, otherwise false.</returns>
public static bool TryConvertImplicit(Type to, object value, out object result)
{
if (value == null)
Expand Down Expand Up @@ -278,8 +290,8 @@ public static bool TryConvertImplicit(Type to, object value, out object result)
/// Convert a value to a type by any means possible, returning the default for that type
/// if the value could not be converted.
/// </summary>
/// <param name="value">The value to cast.</param>
/// <param name="type">The type to cast to..</param>
/// <param name="value">The value to convert.</param>
/// <param name="type">The type to convert to..</param>
/// <param name="culture">The culture to use.</param>
/// <returns>A value of <paramref name="type"/>.</returns>
public static object ConvertOrDefault(object value, Type type, CultureInfo culture)
Expand All @@ -291,8 +303,8 @@ public static object ConvertOrDefault(object value, Type type, CultureInfo cultu
/// Convert a value to a type using the implicit conversions allowed by the C# language or
/// return the default for the type if the value could not be converted.
/// </summary>
/// <param name="value">The value to cast.</param>
/// <param name="type">The type to cast to..</param>
/// <param name="value">The value to convert.</param>
/// <param name="type">The type to convert to.</param>
/// <returns>A value of <paramref name="type"/>.</returns>
public static object ConvertImplicitOrDefault(object value, Type type)
{
Expand Down
6 changes: 5 additions & 1 deletion src/Avalonia.Controls/Presenters/ContentPresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,11 @@ protected virtual IControl CreateChild()
var oldChild = Child;
var newChild = content as IControl;

if (content != null && newChild == null)
// We want to allow creating Child from the Template, if Content is null.
// But it's important to not use DataTemplates, otherwise we will break content presenters in many places,
// otherwise it will blow up every ContentPresenter without Content set.
if (newChild == null
&& (content != null || ContentTemplate != null))
{
var dataTemplate = this.FindDataTemplate(content, ContentTemplate) ??
(
Expand Down
6 changes: 4 additions & 2 deletions src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;

using Avalonia.Utilities;

namespace Avalonia.Controls.Templates
{
/// <summary>
Expand All @@ -16,7 +18,7 @@ public class FuncDataTemplate<T> : FuncDataTemplate
/// </param>
/// <param name="supportsRecycling">Whether the control can be recycled.</param>
public FuncDataTemplate(Func<T, INameScope, IControl> build, bool supportsRecycling = false)
: base(typeof(T), CastBuild(build), supportsRecycling)
: base(o => TypeUtilities.CanCast<T>(o), CastBuild(build), supportsRecycling)
{
}

Expand Down Expand Up @@ -63,7 +65,7 @@ public FuncDataTemplate(
/// <returns>The weakly typed function.</returns>
private static Func<object, bool> CastMatch(Func<T, bool> f)
{
return o => (o is T) && f((T)o);
return o => TypeUtilities.CanCast<T>(o) && f((T)o);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public ShapedTextCharacters(GlyphRun glyphRun, TextRunProperties properties)
/// <inheritdoc/>
public override void Draw(DrawingContext drawingContext, Point origin)
{
using (drawingContext.PushPostTransform(Matrix.CreateTranslation(origin)))
using (drawingContext.PushPreTransform(Matrix.CreateTranslation(origin)))
{
if (GlyphRun.GlyphIndices.Length == 0)
{
Expand Down
1 change: 0 additions & 1 deletion src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ public class DataTemplate : IRecyclingDataTemplate
{
public Type DataType { get; set; }

//we need content to be object otherwise portable.xaml is crashing
[Content]
[TemplateContent]
public object Content { get; set; }
Expand Down
15 changes: 2 additions & 13 deletions src/Skia/Avalonia.Skia/DrawingContextImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -534,11 +534,7 @@ public void PopBitmapBlendMode()
public void PushOpacityMask(IBrush mask, Rect bounds)
{
// TODO: This should be disposed
var paint = new SKPaint()
{
IsAntialias = true,
Style = SKPaintStyle.StrokeAndFill
};
var paint = new SKPaint();

Canvas.SaveLayer(paint);
_maskStack.Push(CreatePaint(paint, mask, bounds, true));
Expand All @@ -547,14 +543,7 @@ public void PushOpacityMask(IBrush mask, Rect bounds)
/// <inheritdoc />
public void PopOpacityMask()
{
using (var paint = new SKPaint
{
IsAntialias = true,
Style = SKPaintStyle.StrokeAndFill,
BlendMode = SKBlendMode.DstIn,
Color = new SKColor(0, 0, 0, 255),
ColorFilter = SKColorFilter.CreateLumaColor()
})
using (var paint = new SKPaint { BlendMode = SKBlendMode.DstIn })
{
Canvas.SaveLayer(paint);
using (var paintWrapper = _maskStack.Pop())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ public static void TryCreateAndRegister(EglPlatformOpenGlInterface angle)
try
{
TryCreateAndRegisterCore(angle);
return;
}
catch (Exception e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,5 +264,91 @@ public void Should_Reset_InheritanceParent_When_Child_Removed()
// InheritanceParent is exposed via StylingParent.
Assert.Same(logicalParent, ((IStyledElement)child).StylingParent);
}

[Fact]
public void Should_Create_Child_Even_With_Null_Content_When_ContentTemplate_Is_Set()
{
var target = new ContentPresenter
{
ContentTemplate = new FuncDataTemplate<object>(_ => true, (_, __) => new TextBlock
{
Text = "Hello World"
}),
Content = null
};

target.UpdateChild();

var textBlock = Assert.IsType<TextBlock>(target.Child);
Assert.Equal("Hello World", textBlock.Text);
}

[Fact]
public void Should_Not_Create_Child_Even_With_Null_Content_And_DataTemplates_InsteadOf_ContentTemplate()
{
var target = new ContentPresenter
{
DataTemplates =
{
new FuncDataTemplate<object>(_ => true, (_, __) => new TextBlock
{
Text = "Hello World"
})
},
Content = null
};

target.UpdateChild();

Assert.Null(target.Child);
}

[Fact]
public void Should_Not_Create_Child_When_Content_And_Template_Are_Null()
{
var target = new ContentPresenter
{
ContentTemplate = null,
Content = null
};

target.UpdateChild();

Assert.Null(target.Child);
}

[Fact]
public void Should_Not_Create_When_Child_Content_Is_Null_But_Expected_ValueType_With_FuncDataTemplate()
{
var target = new ContentPresenter
{
ContentTemplate = new FuncDataTemplate<int>(_ => true, (_, __) => new TextBlock
{
Text = "Hello World"
}),
Content = null
};

target.UpdateChild();

Assert.Null(target.Child);
}

[Fact]
public void Should_Create_Child_When_Content_Is_Null_And_Expected_NullableValueType_With_FuncDataTemplate()
{
var target = new ContentPresenter
{
ContentTemplate = new FuncDataTemplate<int?>(_ => true, (_, __) => new TextBlock
{
Text = "Hello World"
}),
Content = null
};

target.UpdateChild();

Assert.NotNull(target.Child);
}
}
}
Loading

0 comments on commit 1a63e3c

Please sign in to comment.