Browse Source

Added StackPanel.

pull/4/head
Steven Kirk 12 years ago
parent
commit
d90133677b
  1. 18
      Perspex/Controls/ContentPresenter.cs
  2. 56
      Perspex/Controls/Control.cs
  3. 17
      Perspex/Controls/LogicalChildren.cs
  4. 37
      Perspex/Controls/Panel.cs
  5. 154
      Perspex/Controls/StackPanel.cs
  6. 6
      Perspex/Controls/TemplatedControl.cs
  7. 1
      Perspex/Controls/TextBlock.cs
  8. 3
      Perspex/Perspex.csproj
  9. 8
      Perspex/Rect.cs
  10. 20
      Perspex/Size.cs
  11. 15
      Perspex/Styling/StyleActivator.cs
  12. 8
      Perspex/Visual.cs
  13. 32
      Perspex/VisualExtensions.cs
  14. 27
      TestApplication/Program.cs

18
Perspex/Controls/ContentPresenter.cs

@ -21,6 +21,24 @@ namespace Perspex.Controls
private Visual visualChild;
public ContentPresenter()
{
this.GetObservableWithHistory(ContentProperty).Subscribe(x =>
{
if (x.Item1 is Control)
{
((IVisual)x.Item1).VisualParent = null;
((ILogical)x.Item1).LogicalParent = null;
}
if (x.Item2 is Control)
{
((IVisual)x.Item2).VisualParent = this;
((ILogical)x.Item2).LogicalParent = this;
}
});
}
public object Content
{
get { return this.GetValue(ContentProperty); }

56
Perspex/Controls/Control.cs

@ -47,8 +47,11 @@ namespace Perspex.Controls
public static readonly PerspexProperty<Brush> ForegroundProperty =
PerspexProperty.Register<Control, Brush>("Foreground", new SolidColorBrush(0xff000000), true);
public static readonly PerspexProperty<double> HeightProperty =
PerspexProperty.Register<Control, double>("Height", double.NaN);
public static readonly PerspexProperty<bool> IsMouseOverProperty =
PerspexProperty.Register<Visual, bool>("IsMouseOver");
PerspexProperty.Register<Control, bool>("IsMouseOver");
public static readonly PerspexProperty<HorizontalAlignment> HorizontalAlignmentProperty =
PerspexProperty.Register<Control, HorizontalAlignment>("HorizontalAlignment");
@ -56,12 +59,27 @@ namespace Perspex.Controls
public static readonly PerspexProperty<Thickness> MarginProperty =
PerspexProperty.Register<Control, Thickness>("Margin");
public static readonly PerspexProperty<double> MaxHeightProperty =
PerspexProperty.Register<Control, double>("MaxHeight", double.PositiveInfinity);
public static readonly PerspexProperty<double> MaxWidthProperty =
PerspexProperty.Register<Control, double>("MaxWidth", double.PositiveInfinity);
public static readonly PerspexProperty<double> MinHeightProperty =
PerspexProperty.Register<Control, double>("MinHeight");
public static readonly PerspexProperty<double> MinWidthProperty =
PerspexProperty.Register<Control, double>("MinWidth");
public static readonly ReadOnlyPerspexProperty<Control> ParentProperty =
new ReadOnlyPerspexProperty<Control>(ParentPropertyRW);
public static readonly PerspexProperty<VerticalAlignment> VerticalAlignmentProperty =
PerspexProperty.Register<Control, VerticalAlignment>("VerticalAlignment");
public static readonly PerspexProperty<double> WidthProperty =
PerspexProperty.Register<Control, double>("Width", double.NaN);
public static readonly RoutedEvent<MouseEventArgs> MouseLeftButtonDownEvent =
RoutedEvent.Register<Control, MouseEventArgs>("MouseLeftButtonDown", RoutingStrategy.Bubble);
@ -196,6 +214,12 @@ namespace Perspex.Controls
}
}
public double Height
{
get { return this.GetValue(HeightProperty); }
set { this.SetValue(HeightProperty, value); }
}
public bool IsMouseOver
{
get { return this.GetValue(IsMouseOverProperty); }
@ -214,6 +238,30 @@ namespace Perspex.Controls
set { this.SetValue(MarginProperty, value); }
}
public double MaxHeight
{
get { return this.GetValue(MaxHeightProperty); }
set { this.SetValue(MaxHeightProperty, value); }
}
public double MaxWidth
{
get { return this.GetValue(MaxWidthProperty); }
set { this.SetValue(MaxWidthProperty, value); }
}
public double MinHeight
{
get { return this.GetValue(MinHeightProperty); }
set { this.SetValue(MinHeightProperty, value); }
}
public double MinWidth
{
get { return this.GetValue(MinWidthProperty); }
set { this.SetValue(MinWidthProperty, value); }
}
public Control Parent
{
get { return this.GetValue(ParentPropertyRW); }
@ -250,6 +298,12 @@ namespace Perspex.Controls
set { this.SetValue(VerticalAlignmentProperty, value); }
}
public double Width
{
get { return this.GetValue(WidthProperty); }
set { this.SetValue(WidthProperty, value); }
}
ILogical ILogical.LogicalParent
{
get { return this.Parent; }

17
Perspex/Controls/LogicalChildren.cs

@ -26,12 +26,25 @@ namespace Perspex.Controls
{
private T parent;
private PerspexList<T> childrenCollection;
private List<T> inner = new List<T>();
public LogicalChildren(T parent, INotifyCollectionChanged childrenCollection)
public LogicalChildren(T parent, PerspexList<T> childrenCollection)
{
this.parent = parent;
childrenCollection.CollectionChanged += CollectionChanged;
this.childrenCollection = childrenCollection;
this.Add(childrenCollection);
childrenCollection.CollectionChanged += this.CollectionChanged;
}
public void Change(PerspexList<T> childrenCollection)
{
this.childrenCollection.CollectionChanged -= this.CollectionChanged;
this.Remove(inner.ToList());
this.childrenCollection = childrenCollection;
this.Add(childrenCollection);
childrenCollection.CollectionChanged += this.CollectionChanged;
}
private void Add(IEnumerable<T> items)

37
Perspex/Controls/Panel.cs

@ -17,20 +17,43 @@ namespace Perspex.Controls
/// <summary>
/// Base class for controls that can contain multiple children.
/// </summary>
public class Panel : Control
public class Panel : Control, IVisual
{
private PerspexList<Control> children;
private LogicalChildren<Control> logicalChildren;
public Panel()
public PerspexList<Control> Children
{
this.Children = new ObservableCollection<Control>();
this.logicalChildren = new LogicalChildren<Control>(this, this.Children);
get
{
if (this.children == null)
{
this.children = new PerspexList<Control>();
this.logicalChildren = new LogicalChildren<Control>(this, this.children);
}
return this.children;
}
set
{
this.children = value;
if (this.logicalChildren != null)
{
this.logicalChildren.Change(this.children);
}
else
{
this.logicalChildren = new LogicalChildren<Control>(this, this.children);
}
}
}
public ObservableCollection<Control> Children
IEnumerable<IVisual> IVisual.VisualChildren
{
get;
private set;
get { return this.children; }
}
}
}

154
Perspex/Controls/StackPanel.cs

@ -0,0 +1,154 @@
// -----------------------------------------------------------------------
// <copyright file="StackPanel.cs" company="Tricycle">
// Copyright 2014 Tricycle. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public enum Orientation
{
Horizontal,
Vertical,
}
public class StackPanel : Panel
{
public static readonly PerspexProperty<Orientation> OrientationProperty =
PerspexProperty.Register<StackPanel, Orientation>("Orientation");
public Orientation Orientation
{
get { return this.GetValue(OrientationProperty); }
set { this.SetValue(OrientationProperty, value); }
}
protected override Size MeasureContent(Size availableSize)
{
double childAvailableWidth = double.PositiveInfinity;
double childAvailableHeight = double.PositiveInfinity;
if (this.Orientation == Orientation.Vertical)
{
childAvailableWidth = availableSize.Width;
if (!double.IsNaN(this.Width))
{
childAvailableWidth = this.Width;
}
childAvailableWidth = Math.Min(childAvailableWidth, this.MaxWidth);
childAvailableWidth = Math.Max(childAvailableWidth, this.MinWidth);
}
else
{
childAvailableHeight = availableSize.Height;
if (!double.IsNaN(this.Height))
{
childAvailableHeight = this.Height;
}
childAvailableHeight = Math.Min(childAvailableHeight, this.MaxHeight);
childAvailableHeight = Math.Max(childAvailableHeight, this.MinHeight);
}
double measuredWidth = 0;
double measuredHeight = 0;
foreach (Control child in this.Children)
{
child.Measure(new Size(childAvailableWidth, childAvailableHeight));
Size size = child.DesiredSize.Value;
if (Orientation == Orientation.Vertical)
{
measuredHeight += size.Height;
measuredWidth = Math.Max(measuredWidth, size.Width);
}
else
{
measuredWidth += size.Width;
measuredHeight = Math.Max(measuredHeight, size.Height);
}
}
return new Size(measuredWidth, measuredHeight);
}
protected override Size ArrangeContent(Size finalSize)
{
double arrangedWidth = finalSize.Width;
double arrangedHeight = finalSize.Height;
if (Orientation == Orientation.Vertical)
{
arrangedHeight = 0;
}
else
{
arrangedWidth = 0;
}
foreach (Control child in this.Children)
{
double childWidth = child.DesiredSize.Value.Width;
double childHeight = child.DesiredSize.Value.Height;
if (Orientation == Orientation.Vertical)
{
childWidth = finalSize.Width;
Rect childFinal = new Rect(0, arrangedHeight, childWidth, childHeight);
if (childFinal.IsEmpty)
{
child.Arrange(new Rect());
}
else
{
child.Arrange(childFinal);
}
arrangedWidth = Math.Max(arrangedWidth, childWidth);
arrangedHeight += childHeight;
}
else
{
childHeight = finalSize.Height;
Rect childFinal = new Rect(arrangedWidth, 0, childWidth, childHeight);
if (childFinal.IsEmpty)
{
child.Arrange(new Rect());
}
else
{
child.Arrange(childFinal);
}
arrangedWidth += childWidth;
arrangedHeight = Math.Max(arrangedHeight, childHeight);
}
}
if (Orientation == Orientation.Vertical)
{
arrangedHeight = Math.Max(arrangedHeight, finalSize.Height);
}
else
{
arrangedWidth = Math.Max(arrangedWidth, finalSize.Width);
}
return new Size(arrangedWidth, arrangedHeight);
}
}
}

6
Perspex/Controls/TemplatedControl.cs

@ -10,6 +10,7 @@ namespace Perspex.Controls
using System.Collections.Generic;
using System.Linq;
using Perspex.Media;
using Splat;
public class TemplatedControl : Control, IVisual, ITemplatedControl
{
@ -37,6 +38,11 @@ namespace Perspex.Controls
if (this.visualChild == null && template != null)
{
this.Log().Debug(string.Format(
"Creating template for {0} (#{1:x8})",
this.GetType().Name,
this.GetHashCode()));
this.visualChild = template.Build(this);
this.visualChild.VisualParent = this;
}

1
Perspex/Controls/TextBlock.cs

@ -14,6 +14,7 @@ namespace Perspex.Controls
public static readonly PerspexProperty<double> FontSizeProperty =
PerspexProperty.Register<TextBlock, double>(
"FontSize",
defaultValue: 12.0,
inherits: true);
public static readonly PerspexProperty<string> TextProperty =

3
Perspex/Perspex.csproj

@ -70,6 +70,9 @@
<ItemGroup>
<Compile Include="Application.cs" />
<Compile Include="BindingExtensions.cs" />
<Compile Include="Controls\LogicalChildren.cs" />
<Compile Include="Controls\Panel.cs" />
<Compile Include="Controls\StackPanel.cs" />
<Compile Include="Styling\StyleBinding.cs" />
<Compile Include="Classes.cs" />
<Compile Include="Contract.cs" />

8
Perspex/Rect.cs

@ -122,6 +122,14 @@ namespace Perspex
get { return new Size(this.width, this.height); }
}
/// <summary>
/// Gets a value that indicates whether the rectangle is empty.
/// </summary>
public bool IsEmpty
{
get { return this.width == 0 && this.height == 0; }
}
/// <summary>
/// Determines whether a points in in the bounds of the rectangle.
/// </summary>

20
Perspex/Size.cs

@ -88,6 +88,26 @@ namespace Perspex
this.height + thickness.Top + thickness.Bottom);
}
/// <summary>
/// Returns a new <see cref="Size"/> with the same height and the specified width.
/// </summary>
/// <param name="width">The width.</param>
/// <returns>The new <see cref="Size"/>.</returns>
public Size WithWidth(double width)
{
return new Size(width, this.height);
}
/// <summary>
/// Returns a new <see cref="Size"/> with the same width and the specified height.
/// </summary>
/// <param name="height">The height.</param>
/// <returns>The new <see cref="Size"/>.</returns>
public Size WithHeight(double height)
{
return new Size(this.width, height);
}
/// <summary>
/// Returns the string representation of the size.
/// </summary>

15
Perspex/Styling/StyleActivator.cs

@ -17,7 +17,7 @@ namespace Perspex.Styling
Or,
}
public class StyleActivator : IObservable<bool>
public class StyleActivator : IObservable<bool>, IDisposable
{
ActivatorMode mode;
@ -62,6 +62,19 @@ namespace Perspex.Styling
private set;
}
public void Dispose()
{
foreach (IObserver<bool> observer in this.observers)
{
observer.OnCompleted();
}
foreach (IDisposable subscription in this.subscriptions)
{
subscription.Dispose();
}
}
public IDisposable Subscribe(IObserver<bool> observer)
{
Contract.Requires<ArgumentNullException>(observer != null);

8
Perspex/Visual.cs

@ -12,6 +12,7 @@ namespace Perspex
using Perspex.Controls;
using Perspex.Layout;
using Perspex.Media;
using Splat;
public abstract class Visual : PerspexObject, IVisual
{
@ -25,7 +26,7 @@ namespace Perspex
IEnumerable<IVisual> IVisual.ExistingVisualChildren
{
get { return Enumerable.Empty<Visual>(); }
get { return ((IVisual)this).VisualChildren; }
}
IEnumerable<IVisual> IVisual.VisualChildren
@ -51,6 +52,11 @@ namespace Perspex
if (this.GetVisualAncestor<ILayoutRoot>() != null)
{
this.Log().Debug(string.Format(
"Attached {0} (#{1:x8}) to visual tree",
this.GetType().Name,
this.GetHashCode()));
this.AttachedToVisualTree();
}
}

32
Perspex/VisualExtensions.cs

@ -7,10 +7,29 @@
namespace Perspex
{
using System;
using System.Collections.Generic;
using System.Linq;
using Perspex.Styling;
public static class VisualExtensions
{
public static IEnumerable<IVisual> GetVisual(this IVisual visual, Func<Selector, Selector> selector)
{
Selector sel = selector(new Selector());
IEnumerable<IVisual> visuals = Enumerable.Repeat(visual, 1).Concat(visual.GetVisualDescendents());
foreach (IStyleable v in visuals.OfType<IStyleable>())
{
using (StyleActivator activator = sel.GetActivator(v))
{
if (activator.CurrentValue)
{
yield return (IVisual)v;
}
}
}
}
public static T GetVisualAncestor<T>(this IVisual visual) where T : class
{
Contract.Requires<NullReferenceException>(visual != null);
@ -67,5 +86,18 @@ namespace Perspex
return null;
}
public static IEnumerable<IVisual> GetVisualDescendents(this IVisual visual)
{
foreach (IVisual child in visual.VisualChildren)
{
yield return child;
foreach (IVisual descendent in child.GetVisualDescendents())
{
yield return descendent;
}
}
}
}
}

27
TestApplication/Program.cs

@ -36,7 +36,9 @@ namespace TestApplication
{
static void Main(string[] args)
{
Locator.CurrentMutable.Register(() => new TextService(new SharpDX.DirectWrite.Factory()), typeof(ITextService));
TextService textService = new TextService(new SharpDX.DirectWrite.Factory());
Locator.CurrentMutable.Register(() => textService, typeof(ITextService));
Locator.CurrentMutable.Register(() => new Styler(), typeof(IStyler));
Locator.CurrentMutable.Register(() => new TestLogger(), typeof(ILogger));
@ -50,15 +52,28 @@ namespace TestApplication
Window window = new Window
{
Content = new Button
{
Background = new SolidColorBrush(0xff0000ff),
Content = "Hello World",
Content = new StackPanel
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
},
Orientation = Orientation.Vertical,
Children = new PerspexList<Control>
{
new Button
{
Margin = new Thickness(2),
},
new Button
{
Content = "Explict Background",
Background = new SolidColorBrush(0xffa0a0ff),
}
}
}
};
var m = window.GetVisual(x => x.OfType<Button>().Template().Id("border"));
window.Show();
Dispatcher.Run();
}

Loading…
Cancel
Save