Browse Source

Merge branch 'susloparovdenis-master'

pull/298/head
Steven Kirk 11 years ago
parent
commit
8e81a806cc
  1. 15
      samples/XamlTestApplicationPcl/Views/MainWindow.paml
  2. 1
      src/Markup/Perspex.Markup.Xaml/Context/PerspexObjectAssembler.cs
  3. 1
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
  4. 22
      src/Markup/Perspex.Markup.Xaml/Templates/ItemsPanelTemplate.cs
  5. 1
      src/Perspex.Controls/Perspex.Controls.csproj
  6. 249
      src/Perspex.Controls/WrapPanel.cs
  7. 4
      src/Perspex.Styling/Styling/Selector.cs
  8. 10
      src/Perspex.Themes.Default/TabStrip.paml
  9. 1
      tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj
  10. 96
      tests/Perspex.Controls.UnitTests/WrapPanelTests.cs

15
samples/XamlTestApplicationPcl/Views/MainWindow.paml

@ -174,6 +174,21 @@
<Rectangle Fill="#FFC107" Height="50" />
</StackPanel>
<TextBlock Text="WrapPanel" FontWeight="Medium" FontSize="20" Foreground="#212121" />
<TextBlock Text="Wraps children to new lines if no space is left:"
FontSize="13"
Foreground="#727272"
Margin="0, 0, 0, 10" />
<WrapPanel>
<Rectangle Fill="#FFC107" Height="50" Width="100" Margin="2.5" />
<Rectangle Fill="#FFC107" Height="100" Width="100" Margin="2.5" />
<Rectangle Fill="#FFC107" Height="50" Width="100" Margin="2.5" />
<Rectangle Fill="#FFC107" Height="50" Width="200" Margin="2.5" />
<Rectangle Fill="#FFC107" Height="100" Width="50" Margin="2.5" />
<Rectangle Fill="#FFC107" Height="50" Width="100" Margin="2.5" />
<Rectangle Fill="#FFC107" Height="50" Width="100" Margin="2.5" />
</WrapPanel>
<TextBlock Text="Canvas" FontWeight="Medium" FontSize="20" Foreground="#212121" />
<TextBlock Text="A panel which lays out its children by explicit coordinates."
FontSize="13"

1
src/Markup/Perspex.Markup.Xaml/Context/PerspexObjectAssembler.cs

@ -20,6 +20,7 @@ namespace Perspex.Markup.Xaml.Context
mapping.Map<DataTemplate>(x => x.Content, new TemplateLoader());
mapping.Map<FocusAdornerTemplate>(x => x.Content, new TemplateLoader());
mapping.Map<TreeDataTemplate>(x => x.Content, new TemplateLoader());
mapping.Map<ItemsPanelTemplate>(x => x.Content, new TemplateLoader());
var assembler = new ObjectAssembler(wiringContext, new TopDownValueContext(), objectAssemblerSettings);
_objectAssembler = new TemplateHostingObjectAssembler(assembler, mapping);

1
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@ -242,6 +242,7 @@
<Compile Include="Styling\StyleInclude.cs" />
<Compile Include="Templates\FocusAdornerTemplate.cs" />
<Compile Include="Templates\ControlTemplate.cs" />
<Compile Include="Templates\ItemsPanelTemplate.cs" />
<Compile Include="Templates\MemberSelector.cs" />
<Compile Include="Templates\TemplateLoader.cs" />
<Compile Include="Templates\Template.cs" />

22
src/Markup/Perspex.Markup.Xaml/Templates/ItemsPanelTemplate.cs

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Perspex.Controls;
using Perspex.Metadata;
namespace Perspex.Markup.Xaml.Templates
{
public class ItemsPanelTemplate : ITemplate<IPanel>
{
[Content]
public TemplateContent Content { get; set; }
public IPanel Build()
{
return (IPanel)Content.Load();
}
}
}

1
src/Perspex.Controls/Perspex.Controls.csproj

@ -162,6 +162,7 @@
<Compile Include="TreeView.cs" />
<Compile Include="TreeViewItem.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WrapPanel.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Reactive.Core">

249
src/Perspex.Controls/WrapPanel.cs

@ -0,0 +1,249 @@
// Copyright (c) The Perspex 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.Diagnostics;
using System.Linq;
using Perspex.Input;
using static System.Math;
namespace Perspex.Controls
{
/// <summary>
/// Positions child elements in sequential position from left to right,
/// breaking content to the next line at the edge of the containing box.
/// Subsequent ordering happens sequentially from top to bottom or from right to left,
/// depending on the value of the Orientation property.
/// </summary>
internal class WrapPanel : Panel, INavigableContainer
{
/// <summary>
/// Defines the <see cref="Orientation"/> property.
/// </summary>
public static readonly PerspexProperty<Orientation> OrientationProperty =
PerspexProperty.Register<WrapPanel, Orientation>(nameof(Orientation), defaultValue: Orientation.Horizontal);
/// <summary>
/// Initializes static members of the <see cref="WrapPanel"/> class.
/// </summary>
static WrapPanel()
{
AffectsMeasure(OrientationProperty);
}
/// <summary>
/// Gets or sets the orientation in which child controls will be layed out.
/// </summary>
public Orientation Orientation { get { return GetValue(OrientationProperty); } set { SetValue(OrientationProperty, value); } }
/// <summary>
/// Gets the next control in the specified direction.
/// </summary>
/// <param name="direction">The movement direction.</param>
/// <param name="from">The control from which movement begins.</param>
/// <returns>The control.</returns>
IInputElement INavigableContainer.GetControl(FocusNavigationDirection direction, IInputElement from)
{
var horiz = Orientation == Orientation.Horizontal;
int index = Children.IndexOf((IControl)from);
switch (direction)
{
case FocusNavigationDirection.First:
index = 0;
break;
case FocusNavigationDirection.Last:
index = Children.Count - 1;
break;
case FocusNavigationDirection.Next:
++index;
break;
case FocusNavigationDirection.Previous:
--index;
break;
case FocusNavigationDirection.Left:
index = horiz ? index - 1 : -1;
break;
case FocusNavigationDirection.Right:
index = horiz ? index + 1 : -1;
break;
case FocusNavigationDirection.Up:
index = horiz ? -1 : index - 1;
break;
case FocusNavigationDirection.Down:
index = horiz ? -1 : index + 1;
break;
}
if (index >= 0 && index < Children.Count)
{
return Children[index];
}
else
{
return null;
}
}
private UVSize CreateUVSize(Size size) => new UVSize(Orientation, size);
private UVSize CreateUVSize() => new UVSize(Orientation);
/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
var desiredSize = CreateUVSize();
var lineSize = CreateUVSize();
var uvAvailableSize = CreateUVSize(availableSize);
foreach (var child in Children)
{
child.Measure(availableSize);
var childSize = CreateUVSize(child.DesiredSize);
if (lineSize.U + childSize.U <= uvAvailableSize.U) // same line
{
lineSize.U += childSize.U;
lineSize.V = Max(lineSize.V, childSize.V);
}
else // moving to next line
{
desiredSize.U = Max(lineSize.U, uvAvailableSize.U);
desiredSize.V += lineSize.V;
lineSize = childSize;
}
}
// last element
desiredSize.U = Max(lineSize.U, uvAvailableSize.U);
desiredSize.V += lineSize.V;
return desiredSize.ToSize();
}
/// <inheritdoc/>
protected override Size ArrangeOverride(Size finalSize)
{
double accumulatedV = 0;
var uvFinalSize = CreateUVSize(finalSize);
var lineSize = CreateUVSize();
int firstChildInLineindex = 0;
for (int index = 0; index < Children.Count; index++)
{
var child = Children[index];
var childSize = CreateUVSize(child.DesiredSize);
if (lineSize.U + childSize.U <= uvFinalSize.U) // same line
{
lineSize.U += childSize.U;
lineSize.V = Max(lineSize.V, childSize.V);
}
else // moving to next line
{
var controlsInLine = GetContolsBetween(firstChildInLineindex, index);
ArrangeLine(accumulatedV, lineSize.V, controlsInLine);
accumulatedV += lineSize.V;
lineSize = childSize;
firstChildInLineindex = index;
}
}
if (firstChildInLineindex < Children.Count)
{
var controlsInLine = GetContolsBetween(firstChildInLineindex, Children.Count);
ArrangeLine(accumulatedV, lineSize.V, controlsInLine);
}
return finalSize;
}
private IEnumerable<IControl> GetContolsBetween(int first, int last)
{
return Children.Skip(first).Take(last - first);
}
private void ArrangeLine(double v, double lineV, IEnumerable<IControl> contols)
{
double u = 0;
bool isHorizontal = (Orientation == Orientation.Horizontal);
foreach (var child in contols)
{
var childSize = CreateUVSize(child.DesiredSize);
var x = isHorizontal ? u : v;
var y = isHorizontal ? v : u;
var width = isHorizontal ? childSize.U : lineV;
var height = isHorizontal ? lineV : childSize.U;
child.Arrange(new Rect(x, y, width, height));
u += childSize.U;
}
}
/// <summary>
/// Used to not not write sepearate code for horizontal and vertical orientation.
/// U is direction in line. (x if orientation is horizontal)
/// V is direction of lines. (y if orientation is horizonral)
/// </summary>
[DebuggerDisplay("U = {U} V = {V}")]
private struct UVSize
{
private readonly Orientation _orientation;
internal double U;
internal double V;
private UVSize(Orientation orientation, double width, double height)
{
U = V = 0d;
_orientation = orientation;
Width = width;
Height = height;
}
internal UVSize(Orientation orientation, Size size)
: this(orientation, size.Width, size.Height)
{
}
internal UVSize(Orientation orientation)
{
U = V = 0d;
_orientation = orientation;
}
private double Width
{
get { return (_orientation == Orientation.Horizontal ? U : V); }
set
{
if (_orientation == Orientation.Horizontal)
{
U = value;
}
else
{
V = value;
}
}
}
private double Height
{
get { return (_orientation == Orientation.Horizontal ? V : U); }
set
{
if (_orientation == Orientation.Horizontal)
{
V = value;
}
else
{
U = value;
}
}
}
public Size ToSize()
{
return new Size(Width, Height);
}
}
}
}

4
src/Perspex.Styling/Styling/Selector.cs

@ -10,7 +10,7 @@ namespace Perspex.Styling
/// A selector in a <see cref="Style"/>.
/// </summary>
/// <remarks>
/// Selectors represented in markup using a CSS-like syntax, e.g. "Button &lt; .dark" which
/// Selectors represented in markup using a CSS-like syntax, e.g. "Button &gt; .dark" which
/// means "A child of a Button with the 'dark' class applied. The preceeding example would be
/// stored in 3 <see cref="Selector"/> objects, linked by the <see cref="Previous"/> property:
/// <list type="number">
@ -21,7 +21,7 @@ namespace Perspex.Styling
/// </description>
/// </item>
/// <item>
/// <term>&lt;</term>
/// <term>&gt;</term>
/// <description>
/// A selector that selects a child of the previous selector.
/// </description>

10
src/Perspex.Themes.Default/TabStrip.paml

@ -6,9 +6,13 @@
ItemsPanel="{TemplateBinding ItemsPanel}"/>
</ControlTemplate>
</Setter>
<Setter Property="ItemsPanel">
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</Setter>
</Style>
<Style Selector="TabStrip /template/ StackPanel">
<Setter Property="Gap" Value="16"/>
<Setter Property="Orientation" Value="Horizontal"/>
<Style Selector="TabStrip > TabItem">
<Setter Property="Margin" Value="16"/>
</Style>
</Styles>

1
tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj

@ -120,6 +120,7 @@
<Compile Include="TestRoot.cs" />
<Compile Include="Utils\AncestorFinderTests.cs" />
<Compile Include="Utils\HotKeyManagerTests.cs" />
<Compile Include="WrapPanelTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Markup\Perspex.Markup.Xaml\Perspex.Markup.Xaml.csproj">

96
tests/Perspex.Controls.UnitTests/WrapPanelTests.cs

@ -0,0 +1,96 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Xunit;
namespace Perspex.Controls.UnitTests
{
public class WrapPanelTests
{
[Fact]
public void Lays_Out_Horizontally_On_Sepatate_Lines()
{
var target = new WrapPanel()
{
Width = 100,
Children = new Controls
{
new Border { Height = 50, Width = 100 },
new Border { Height = 50, Width = 100 },
}
};
target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Size(100, 100), target.Bounds.Size);
Assert.Equal(new Rect(0, 0, 100, 50), target.Children[0].Bounds);
Assert.Equal(new Rect(0, 50, 100, 50), target.Children[1].Bounds);
}
[Fact]
public void Lays_Out_Horizontally_On_A_Single_Line()
{
var target = new WrapPanel()
{
Width = 200,
Children = new Controls
{
new Border { Height = 50, Width = 100 },
new Border { Height = 50, Width = 100 },
}
};
target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Size(200, 50), target.Bounds.Size);
Assert.Equal(new Rect(0, 0, 100, 50), target.Children[0].Bounds);
Assert.Equal(new Rect(100, 0, 100, 50), target.Children[1].Bounds);
}
[Fact]
public void Lays_Out_Vertically_Children_On_A_Single_Line()
{
var target = new WrapPanel()
{
Orientation = Orientation.Vertical,
Height = 120,
Children = new Controls
{
new Border { Height = 50, Width = 100 },
new Border { Height = 50, Width = 100 },
}
};
target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Size(100, 120), target.Bounds.Size);
Assert.Equal(new Rect(0, 0, 100, 50), target.Children[0].Bounds);
Assert.Equal(new Rect(0, 50, 100, 50), target.Children[1].Bounds);
}
[Fact]
public void Lays_Out_Vertically_On_Sepatate_Lines()
{
var target = new WrapPanel()
{
Orientation = Orientation.Vertical,
Height = 60,
Children = new Controls
{
new Border { Height = 50, Width = 100 },
new Border { Height = 50, Width = 100 },
}
};
target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Size(200, 60), target.Bounds.Size);
Assert.Equal(new Rect(0, 0, 100, 50), target.Children[0].Bounds);
Assert.Equal(new Rect(100, 0, 100, 50), target.Children[1].Bounds);
}
}
}
Loading…
Cancel
Save