// ----------------------------------------------------------------------- // // Copyright 2014 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- namespace Perspex.Controls { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reactive.Linq; using Perspex.Input; using Perspex.Layout; using Perspex.Media; using Perspex.Styling; using Splat; public enum HorizontalAlignment { Stretch, Left, Center, Right, } public enum VerticalAlignment { Stretch, Top, Center, Bottom, } public class Control : Interactive, ILayoutable, IStyleable, IStyled { public static readonly PerspexProperty BackgroundProperty = PerspexProperty.Register("Background", inherits: true); public static readonly PerspexProperty BorderBrushProperty = PerspexProperty.Register("BorderBrush"); public static readonly PerspexProperty BorderThicknessProperty = PerspexProperty.Register("BorderThickness"); public static readonly PerspexProperty ForegroundProperty = PerspexProperty.Register("Foreground", new SolidColorBrush(0xff000000), true); public static readonly PerspexProperty IsMouseOverProperty = PerspexProperty.Register("IsMouseOver"); public static readonly PerspexProperty HorizontalAlignmentProperty = PerspexProperty.Register("HorizontalAlignment"); public static readonly PerspexProperty MarginProperty = PerspexProperty.Register("Margin"); public static readonly PerspexProperty VerticalAlignmentProperty = PerspexProperty.Register("VerticalAlignment"); public static readonly RoutedEvent MouseLeftButtonDownEvent = RoutedEvent.Register("MouseLeftButtonDown", RoutingStrategy.Bubble); public static readonly RoutedEvent MouseLeftButtonUpEvent = RoutedEvent.Register("MouseLeftButtonUp", RoutingStrategy.Bubble); private Classes classes; private string id; private Styles styles; public Control() { this.classes = new Classes(); this.classes.BeforeChanged.Subscribe(x => this.BeginDeferStyleChanges()); this.classes.AfterChanged.Subscribe(x => this.EndDeferStyleChanges()); this.GetObservable(IsMouseOverProperty).Subscribe(x => { if (x) { this.Classes.Add(":mouseover"); } else { this.Classes.Remove(":mouseover"); } }); // Hacky hack hack! this.GetObservable(BackgroundProperty).Skip(1).Subscribe(_ => this.InvalidateMeasure()); } public event EventHandler MouseLeftButtonDown { add { Contract.Requires(value != null); this.AddHandler(MouseLeftButtonDownEvent, value); } remove { Contract.Requires(value != null); this.RemoveHandler(MouseLeftButtonDownEvent, value); } } public event EventHandler MouseLeftButtonUp { add { Contract.Requires(value != null); this.AddHandler(MouseLeftButtonUpEvent, value); } remove { Contract.Requires(value != null); this.RemoveHandler(MouseLeftButtonUpEvent, value); } } public Brush Background { get { return this.GetValue(BackgroundProperty); } set { this.SetValue(BackgroundProperty, value); } } public Brush BorderBrush { get { return this.GetValue(BorderBrushProperty); } set { this.SetValue(BorderBrushProperty, value); } } public double BorderThickness { get { return this.GetValue(BorderThicknessProperty); } set { this.SetValue(BorderThicknessProperty, value); } } public Classes Classes { get { return this.classes; } set { if (this.classes != value) { this.BeginDeferStyleChanges(); this.classes.Clear(); this.classes.Add(value); this.EndDeferStyleChanges(); } } } public Size? DesiredSize { get; set; } public Brush Foreground { get { return this.GetValue(ForegroundProperty); } set { this.SetValue(ForegroundProperty, value); } } public string Id { get { return this.id; } set { if (this.id != null) { throw new InvalidOperationException("ID already set."); } if (((IVisual)this).VisualParent != null) { throw new InvalidOperationException("Cannot set ID : control already added to tree."); } this.id = value; } } public bool IsMouseOver { get { return this.GetValue(IsMouseOverProperty); } set { this.SetValue(IsMouseOverProperty, value); } } public HorizontalAlignment HorizontalAlignment { get { return this.GetValue(HorizontalAlignmentProperty); } set { this.SetValue(HorizontalAlignmentProperty, value); } } public Thickness Margin { get { return this.GetValue(MarginProperty); } set { this.SetValue(MarginProperty, value); } } public Styles Styles { get { if (this.styles == null) { this.styles = new Styles(); } return this.styles; } set { this.styles = value; } } public ITemplatedControl TemplatedParent { get; internal set; } public VerticalAlignment VerticalAlignment { get { return this.GetValue(VerticalAlignmentProperty); } set { this.SetValue(VerticalAlignmentProperty, value); } } public ILayoutRoot GetLayoutRoot() { Control c = this; while (c != null && !(c is ILayoutRoot)) { c = c.Parent; } return (ILayoutRoot)c; } public void Arrange(Rect rect) { this.Bounds = new Rect( rect.Position, this.ArrangeContent(rect.Size.Deflate(this.Margin).Constrain(rect.Size))); } public void Measure(Size availableSize) { availableSize = availableSize.Deflate(this.Margin); this.DesiredSize = this.MeasureContent(availableSize).Constrain(availableSize); } public void InvalidateArrange() { ILayoutRoot root = this.GetLayoutRoot(); if (root != null) { root.LayoutManager.InvalidateArrange(this); } } public void InvalidateMeasure() { ILayoutRoot root = this.GetLayoutRoot(); if (root != null) { root.LayoutManager.InvalidateMeasure(this); } } protected virtual Size ArrangeContent(Size finalSize) { return finalSize; } protected virtual Size MeasureContent(Size availableSize) { return new Size(); } protected override void AttachedToVisualTree() { IStyler styler = Locator.Current.GetService(); styler.ApplyStyles(this); base.AttachedToVisualTree(); } } }