73 changed files with 811 additions and 139 deletions
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 27 KiB |
@ -0,0 +1,147 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Avalonia.Controls; |
||||
|
|
||||
|
namespace Avalonia.Diagnostics.Views |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A simple grid control that lays out columns with a equal width and rows to their desired
|
||||
|
/// size.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// This is used in the devtools because our <see cref="Grid"/> performance sucks.
|
||||
|
/// </remarks>
|
||||
|
public class SimpleGrid : Panel |
||||
|
{ |
||||
|
private List<double> _columnWidths = new List<double>(); |
||||
|
private List<double> _rowHeights = new List<double>(); |
||||
|
private double _totalWidth; |
||||
|
private double _totalHeight; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the Column attached property.
|
||||
|
/// </summary>
|
||||
|
public static readonly AttachedProperty<int> ColumnProperty = |
||||
|
AvaloniaProperty.RegisterAttached<SimpleGrid, Control, int>("Column"); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the Row attached property.
|
||||
|
/// </summary>
|
||||
|
public static readonly AttachedProperty<int> RowProperty = |
||||
|
AvaloniaProperty.RegisterAttached<SimpleGrid, Control, int>("Row"); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the value of the Column attached property for a control.
|
||||
|
/// </summary>
|
||||
|
/// <param name="control">The control.</param>
|
||||
|
/// <returns>The control's column.</returns>
|
||||
|
public static int GetColumn(IControl control) |
||||
|
{ |
||||
|
return control.GetValue(ColumnProperty); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the value of the Row attached property for a control.
|
||||
|
/// </summary>
|
||||
|
/// <param name="control">The control.</param>
|
||||
|
/// <returns>The control's row.</returns>
|
||||
|
public static int GetRow(IControl control) |
||||
|
{ |
||||
|
return control.GetValue(RowProperty); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets the value of the Column attached property for a control.
|
||||
|
/// </summary>
|
||||
|
/// <param name="control">The control.</param>
|
||||
|
/// <param name="value">The column value.</param>
|
||||
|
public static void SetColumn(IControl control, int value) |
||||
|
{ |
||||
|
control.SetValue(ColumnProperty, value); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets the value of the Row attached property for a control.
|
||||
|
/// </summary>
|
||||
|
/// <param name="control">The control.</param>
|
||||
|
/// <param name="value">The row value.</param>
|
||||
|
public static void SetRow(IControl control, int value) |
||||
|
{ |
||||
|
control.SetValue(RowProperty, value); |
||||
|
} |
||||
|
|
||||
|
protected override Size MeasureOverride(Size availableSize) |
||||
|
{ |
||||
|
_columnWidths.Clear(); |
||||
|
_rowHeights.Clear(); |
||||
|
_totalWidth = 0; |
||||
|
_totalHeight = 0; |
||||
|
|
||||
|
foreach (var child in Children) |
||||
|
{ |
||||
|
var column = GetColumn(child); |
||||
|
var row = GetRow(child); |
||||
|
|
||||
|
child.Measure(availableSize); |
||||
|
|
||||
|
var desired = child.DesiredSize; |
||||
|
UpdateCell(_columnWidths, column, desired.Width, ref _totalWidth); |
||||
|
UpdateCell(_rowHeights, row, desired.Height, ref _totalHeight); |
||||
|
} |
||||
|
|
||||
|
return new Size(_totalWidth, _totalHeight); |
||||
|
} |
||||
|
|
||||
|
protected override Size ArrangeOverride(Size finalSize) |
||||
|
{ |
||||
|
var columnWidth = finalSize.Width / _columnWidths.Count; |
||||
|
|
||||
|
foreach (var child in Children) |
||||
|
{ |
||||
|
var column = GetColumn(child); |
||||
|
var row = GetRow(child); |
||||
|
var rect = new Rect(column * columnWidth, GetRowTop(row), columnWidth, _rowHeights[row]); |
||||
|
child.Arrange(rect); |
||||
|
} |
||||
|
|
||||
|
return new Size(finalSize.Width, _totalHeight); |
||||
|
} |
||||
|
|
||||
|
private double UpdateCell(IList<double> cells, int cell, double value, ref double total) |
||||
|
{ |
||||
|
while (cells.Count < cell + 1) |
||||
|
{ |
||||
|
cells.Add(0); |
||||
|
} |
||||
|
|
||||
|
var existing = cells[cell]; |
||||
|
|
||||
|
if (value > existing) |
||||
|
{ |
||||
|
cells[cell] = value; |
||||
|
total += value - existing; |
||||
|
return value; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return existing; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private double GetRowTop(int row) |
||||
|
{ |
||||
|
var result = 0.0; |
||||
|
|
||||
|
for (var i = 0; i < row; ++i) |
||||
|
{ |
||||
|
result += _rowHeights[i]; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,10 +1,10 @@ |
|||||
// Copyright (c) The Avalonia Project. All rights reserved.
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
namespace Avalonia.Controls |
namespace Avalonia.Media |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Controls the wrapping mode in a <see cref="TextBlock"/>.
|
/// Controls the wrapping mode of text.
|
||||
/// </summary>
|
/// </summary>
|
||||
public enum TextWrapping |
public enum TextWrapping |
||||
{ |
{ |
||||
@ -1,20 +1,66 @@ |
|||||
// Copyright (c) The Avalonia Project. All rights reserved.
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
using System.Reflection; |
|
||||
using Avalonia.Controls.Templates; |
using Avalonia.Controls.Templates; |
||||
|
using Avalonia.Data; |
||||
|
using Avalonia.Markup.Data; |
||||
|
using System; |
||||
|
|
||||
namespace Avalonia.Markup.Xaml.Templates |
namespace Avalonia.Markup.Xaml.Templates |
||||
{ |
{ |
||||
public class MemberSelector : IMemberSelector |
public class MemberSelector : IMemberSelector |
||||
{ |
{ |
||||
public string MemberName { get; set; } |
private ExpressionNode _expressionNode; |
||||
|
private string _memberName; |
||||
|
private ExpressionNode _memberValueNode; |
||||
|
|
||||
|
public string MemberName |
||||
|
{ |
||||
|
get { return _memberName; } |
||||
|
set |
||||
|
{ |
||||
|
if (_memberName != value) |
||||
|
{ |
||||
|
_memberName = value; |
||||
|
_expressionNode = null; |
||||
|
_memberValueNode = null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
public object Select(object o) |
public object Select(object o) |
||||
{ |
{ |
||||
// TODO: Handle nested property paths, changing values etc.
|
if (string.IsNullOrEmpty(MemberName)) |
||||
var property = o.GetType().GetRuntimeProperty(MemberName); |
{ |
||||
return property?.GetValue(o); |
return o; |
||||
|
} |
||||
|
|
||||
|
if (_expressionNode == null) |
||||
|
{ |
||||
|
_expressionNode = ExpressionNodeBuilder.Build(MemberName); |
||||
|
|
||||
|
_memberValueNode = _expressionNode; |
||||
|
|
||||
|
while (_memberValueNode.Next != null) |
||||
|
{ |
||||
|
_memberValueNode = _memberValueNode.Next; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
_expressionNode.Target = new WeakReference(o); |
||||
|
|
||||
|
object result = _memberValueNode.CurrentValue.Target; |
||||
|
|
||||
|
if (result == AvaloniaProperty.UnsetValue) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
else if (result is BindingError) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,168 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using Avalonia.Markup.Xaml.Templates; |
||||
|
using System; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.Markup.Xaml.UnitTests.Templates |
||||
|
{ |
||||
|
public class MemberSelectorTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Should_Not_Hold_Reference_To_Object() |
||||
|
{ |
||||
|
WeakReference dataRef = null; |
||||
|
|
||||
|
var selector = new MemberSelector() { MemberName = "Child.StringValue" }; |
||||
|
|
||||
|
Action run = () => |
||||
|
{ |
||||
|
var data = new Item() |
||||
|
{ |
||||
|
Child = new Item() { StringValue = "Value1" } |
||||
|
}; |
||||
|
|
||||
|
Assert.Same("Value1", selector.Select(data)); |
||||
|
|
||||
|
dataRef = new WeakReference(data); |
||||
|
}; |
||||
|
|
||||
|
run(); |
||||
|
|
||||
|
GC.Collect(); |
||||
|
|
||||
|
Assert.False(dataRef.IsAlive); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Select_Child_Property_Value() |
||||
|
{ |
||||
|
var selector = new MemberSelector() { MemberName = "Child.StringValue" }; |
||||
|
|
||||
|
var data = new Item() |
||||
|
{ |
||||
|
Child = new Item() { StringValue = "Value1" } |
||||
|
}; |
||||
|
|
||||
|
Assert.Same("Value1", selector.Select(data)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Select_Child_Property_Value_In_Multiple_Items() |
||||
|
{ |
||||
|
var selector = new MemberSelector() { MemberName = "Child.StringValue" }; |
||||
|
|
||||
|
var data = new Item[] |
||||
|
{ |
||||
|
new Item() { Child = new Item() { StringValue = "Value1" } }, |
||||
|
new Item() { Child = new Item() { StringValue = "Value2" } }, |
||||
|
new Item() { Child = new Item() { StringValue = "Value3" } } |
||||
|
}; |
||||
|
|
||||
|
Assert.Same("Value1", selector.Select(data[0])); |
||||
|
Assert.Same("Value2", selector.Select(data[1])); |
||||
|
Assert.Same("Value3", selector.Select(data[2])); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Select_MoreComplex_Property_Value() |
||||
|
{ |
||||
|
var selector = new MemberSelector() { MemberName = "Child.Child.Child.StringValue" }; |
||||
|
|
||||
|
var data = new Item() |
||||
|
{ |
||||
|
Child = new Item() |
||||
|
{ |
||||
|
Child = new Item() |
||||
|
{ |
||||
|
Child = new Item() { StringValue = "Value1" } |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
Assert.Same("Value1", selector.Select(data)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Select_Null_Value_On_Null_Object() |
||||
|
{ |
||||
|
var selector = new MemberSelector() { MemberName = "StringValue" }; |
||||
|
|
||||
|
Assert.Equal(null, selector.Select(null)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Select_Null_Value_On_Wrong_MemberName() |
||||
|
{ |
||||
|
var selector = new MemberSelector() { MemberName = "WrongProperty" }; |
||||
|
|
||||
|
var data = new Item() { StringValue = "Value1" }; |
||||
|
|
||||
|
Assert.Same(null, selector.Select(data)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Select_Simple_Property_Value() |
||||
|
{ |
||||
|
var selector = new MemberSelector() { MemberName = "StringValue" }; |
||||
|
|
||||
|
var data = new Item() { StringValue = "Value1" }; |
||||
|
|
||||
|
Assert.Same("Value1", selector.Select(data)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Select_Simple_Property_Value_In_Multiple_Items() |
||||
|
{ |
||||
|
var selector = new MemberSelector() { MemberName = "StringValue" }; |
||||
|
|
||||
|
var data = new Item[] |
||||
|
{ |
||||
|
new Item() { StringValue = "Value1" }, |
||||
|
new Item() { StringValue = "Value2" }, |
||||
|
new Item() { StringValue = "Value3" } |
||||
|
}; |
||||
|
|
||||
|
Assert.Same("Value1", selector.Select(data[0])); |
||||
|
Assert.Same("Value2", selector.Select(data[1])); |
||||
|
Assert.Same("Value3", selector.Select(data[2])); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Select_Target_On_Empty_MemberName() |
||||
|
{ |
||||
|
var selector = new MemberSelector(); |
||||
|
|
||||
|
var data = new Item() { StringValue = "Value1" }; |
||||
|
|
||||
|
Assert.Same(data, selector.Select(data)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Support_Change_Of_MemberName() |
||||
|
{ |
||||
|
var selector = new MemberSelector() { MemberName = "StringValue" }; |
||||
|
|
||||
|
var data = new Item() |
||||
|
{ |
||||
|
StringValue = "Value1", |
||||
|
IntValue = 1 |
||||
|
}; |
||||
|
|
||||
|
Assert.Same("Value1", selector.Select(data)); |
||||
|
|
||||
|
selector.MemberName = "IntValue"; |
||||
|
|
||||
|
Assert.Equal(1, selector.Select(data)); |
||||
|
} |
||||
|
|
||||
|
private class Item |
||||
|
{ |
||||
|
public Item Child { get; set; } |
||||
|
public int IntValue { get; set; } |
||||
|
|
||||
|
public string StringValue { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Layout; |
||||
|
using Avalonia.Media; |
||||
|
using Xunit; |
||||
|
|
||||
|
#if AVALONIA_CAIRO
|
||||
|
namespace Avalonia.Cairo.RenderTests.Controls |
||||
|
#elif AVALONIA_SKIA
|
||||
|
namespace Avalonia.Skia.RenderTests |
||||
|
#else
|
||||
|
namespace Avalonia.Direct2D1.RenderTests.Controls |
||||
|
#endif
|
||||
|
{ |
||||
|
public class TextBlockTests : TestBase |
||||
|
{ |
||||
|
public TextBlockTests() |
||||
|
: base(@"Controls\TextBlock") |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Wrapping_NoWrap() |
||||
|
{ |
||||
|
Decorator target = new Decorator |
||||
|
{ |
||||
|
Padding = new Thickness(8), |
||||
|
Width = 200, |
||||
|
Height = 200, |
||||
|
Child = new TextBlock |
||||
|
{ |
||||
|
Background = Brushes.Red, |
||||
|
FontSize = 12, |
||||
|
Foreground = Brushes.Black, |
||||
|
Text = "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit", |
||||
|
VerticalAlignment = VerticalAlignment.Top, |
||||
|
TextWrapping = TextWrapping.NoWrap, |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
RenderToFile(target); |
||||
|
CompareImages(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 1.2 KiB |
Loading…
Reference in new issue