Browse Source

Implemented basic GridSplitter for DevTools.

pull/4/head
Steven Kirk 12 years ago
parent
commit
f361fe23ce
  1. 1
      Perspex.Base/Perspex.Base.csproj
  2. 106
      Perspex.Base/PerspexListExtensions.cs
  3. 25
      Perspex.Base/PerspexObject.cs
  4. 5
      Perspex.Controls/Button.cs
  5. 51
      Perspex.Controls/Grid.cs
  6. 34
      Perspex.Controls/GridSplitter.cs
  7. 2
      Perspex.Controls/Perspex.Controls.csproj
  8. 108
      Perspex.Controls/Primitives/Thumb.cs
  9. 8
      Perspex.Diagnostics/DevTools.cs
  10. 14
      Perspex.Input/InputElement.cs
  11. 15
      Perspex.Input/MouseDevice.cs
  12. 1
      Perspex.Input/Perspex.Input.csproj
  13. 16
      Perspex.Input/VectorEventArgs.cs
  14. 3
      Perspex.Interactivity/Interactive.cs
  15. 15
      Perspex.Interactivity/RoutedEventArgs.cs
  16. 1
      Perspex.Themes.Default/DefaultTheme.cs
  17. 42
      Perspex.Themes.Default/GridSplitterStyle.cs
  18. 1
      Perspex.Themes.Default/Perspex.Themes.Default.csproj

1
Perspex.Base/Perspex.Base.csproj

@ -37,6 +37,7 @@
<Compile Include="Binding.cs" />
<Compile Include="Contract.cs" />
<Compile Include="IDescription.cs" />
<Compile Include="PerspexListExtensions.cs" />
<Compile Include="IObservableDescription.cs" />
<Compile Include="IReadOnlyPerspexList.cs" />
<Compile Include="PerspexList.cs" />

106
Perspex.Base/PerspexListExtensions.cs

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Disposables;
using System.Text;
using System.Threading.Tasks;
namespace Perspex
{
public static class PerspexListExtensions
{
public static IDisposable ForEachItem<T>(
this IReadOnlyPerspexList<T> collection,
Action<T> added,
Action<T> removed)
{
NotifyCollectionChangedEventHandler handler = (_, e) =>
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (T i in e.NewItems)
{
added(i);
}
break;
case NotifyCollectionChangedAction.Replace:
foreach (T i in e.OldItems)
{
removed(i);
}
foreach (T i in e.NewItems)
{
added(i);
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (T i in e.OldItems)
{
removed(i);
}
break;
}
};
foreach (T i in collection)
{
added(i);
}
collection.CollectionChanged += handler;
System.Diagnostics.Debug.WriteLine("Tracked " + collection.GetHashCode());
return Disposable.Create(() => collection.CollectionChanged -= handler);
}
public static IDisposable TrackItemPropertyChanged<T>(
this IReadOnlyPerspexList<T> collection,
Action<Tuple<object, PropertyChangedEventArgs>> callback)
{
List<INotifyPropertyChanged> tracked = new List<INotifyPropertyChanged>();
PropertyChangedEventHandler handler = (s, e) =>
{
callback(Tuple.Create(s, e));
};
collection.ForEachItem(
x =>
{
var inpc = x as INotifyPropertyChanged;
if (inpc != null)
{
inpc.PropertyChanged += handler;
tracked.Add(inpc);
}
},
x =>
{
var inpc = x as INotifyPropertyChanged;
if (inpc != null)
{
inpc.PropertyChanged -= handler;
tracked.Remove(inpc);
}
});
return Disposable.Create(() =>
{
foreach (var i in tracked)
{
i.PropertyChanged -= handler;
}
});
}
}
}

25
Perspex.Base/PerspexObject.cs

@ -8,6 +8,7 @@ namespace Perspex
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Reflection;
@ -56,7 +57,7 @@ namespace Perspex
/// <remarks>
/// This class is analogous to DependencyObject in WPF.
/// </remarks>
public class PerspexObject : IEnableLogger
public class PerspexObject : INotifyPropertyChanged, IEnableLogger
{
/// <summary>
/// The registered properties by type.
@ -76,10 +77,24 @@ namespace Perspex
new Dictionary<PerspexProperty, PriorityValue>();
/// <summary>
/// Raised when a <see cref="PerspexProperty"/> value changes on this object/
/// Event handler for <see cref="INotifyPropertyChanged"/> implementation.
/// </summary>
private PropertyChangedEventHandler inpcChanged;
/// <summary>
/// Raised when a <see cref="PerspexProperty"/> value changes on this object.
/// </summary>
public event EventHandler<PerspexPropertyChangedEventArgs> PropertyChanged;
/// <summary>
/// Raised when a <see cref="PerspexProperty"/> value changes on this object.
/// </summary>
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add { this.inpcChanged += value; }
remove { this.inpcChanged -= value; }
}
/// <summary>
/// Gets or sets the parent object that inherited <see cref="PerspexProperty"/> values
/// are inherited from.
@ -646,6 +661,12 @@ namespace Perspex
property.NotifyChanged(e);
this.PropertyChanged(this, e);
}
if (this.inpcChanged != null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(property.Name);
this.inpcChanged(this, e);
}
}
}
}

5
Perspex.Controls/Button.cs

@ -34,7 +34,10 @@ namespace Perspex.Controls
if (this.Classes.Contains(":pointerover"))
{
RoutedEventArgs click = new RoutedEventArgs(ClickEvent, this);
RoutedEventArgs click = new RoutedEventArgs
{
RoutedEvent = ClickEvent,
};
this.RaiseEvent(click);
}
};

51
Perspex.Controls/Grid.cs

@ -27,25 +27,64 @@ namespace Perspex.Controls
public static readonly PerspexProperty<int> RowSpanProperty =
PerspexProperty.RegisterAttached<Grid, Control, int>("RowSpan", 1);
private ColumnDefinitions columnDefinitions;
private RowDefinitions rowDefinitions;
private Segment[,] rowMatrix;
private Segment[,] colMatrix;
public Grid()
{
this.ColumnDefinitions = new ColumnDefinitions();
this.RowDefinitions = new RowDefinitions();
}
public ColumnDefinitions ColumnDefinitions
{
get;
set;
get
{
if (this.columnDefinitions == null)
{
this.ColumnDefinitions = new ColumnDefinitions();
}
return this.columnDefinitions;
}
set
{
if (this.columnDefinitions != null)
{
throw new NotSupportedException("Reassigning ColumnDefinitions not yet implemented.");
}
this.columnDefinitions = value;
this.columnDefinitions.TrackItemPropertyChanged(_ => this.InvalidateMeasure());
}
}
public RowDefinitions RowDefinitions
{
get;
set;
get
{
if (this.rowDefinitions == null)
{
this.RowDefinitions = new RowDefinitions();
}
return this.rowDefinitions;
}
set
{
if (this.rowDefinitions != null)
{
throw new NotSupportedException("Reassigning RowDefinitions not yet implemented.");
}
this.rowDefinitions = value;
this.rowDefinitions.TrackItemPropertyChanged(_ => this.InvalidateMeasure());
}
}
public static int GetColumn(PerspexObject element)

34
Perspex.Controls/GridSplitter.cs

@ -0,0 +1,34 @@
// -----------------------------------------------------------------------
// <copyright file="GridSplitter.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls
{
using System;
using Perspex.Controls.Primitives;
using Perspex.Input;
public class GridSplitter : Thumb
{
private Grid grid;
protected override void OnDragDelta(VectorEventArgs e)
{
int col = this.GetValue(Grid.ColumnProperty);
if (grid != null && col > 0)
{
grid.ColumnDefinitions[col - 1].Width = new GridLength(
grid.ColumnDefinitions[col - 1].ActualWidth + e.Vector.X,
GridUnitType.Pixel);
}
}
protected override void OnVisualParentChanged(Visual oldParent)
{
this.grid = this.GetVisualParent<Grid>();
}
}
}

2
Perspex.Controls/Perspex.Controls.csproj

@ -40,6 +40,7 @@
<Compile Include="ColumnDefinition.cs" />
<Compile Include="ColumnDefinitions.cs" />
<Compile Include="ContentControl.cs" />
<Compile Include="GridSplitter.cs" />
<Compile Include="Presenters\ContentPresenter.cs" />
<Compile Include="Control.cs" />
<Compile Include="ControlExtensions.cs" />
@ -65,6 +66,7 @@
<Compile Include="ItemsPanelTemplate.cs" />
<Compile Include="Presenters\ItemsPresenter.cs" />
<Compile Include="Panel.cs" />
<Compile Include="Primitives\Thumb.cs" />
<Compile Include="RowDefinition.cs" />
<Compile Include="RowDefinitions.cs" />
<Compile Include="Primitives\SelectingItemsControl.cs" />

108
Perspex.Controls/Primitives/Thumb.cs

@ -0,0 +1,108 @@
// -----------------------------------------------------------------------
// <copyright file="Thumb.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Primitives
{
using System;
using Perspex.Input;
using Perspex.Interactivity;
public class Thumb : TemplatedControl
{
public static readonly RoutedEvent<VectorEventArgs> DragStartedEvent =
RoutedEvent.Register<Thumb, VectorEventArgs>("DragStarted", RoutingStrategy.Bubble);
public static readonly RoutedEvent<VectorEventArgs> DragDeltaEvent =
RoutedEvent.Register<Thumb, VectorEventArgs>("DragDelta", RoutingStrategy.Bubble);
public static readonly RoutedEvent<VectorEventArgs> DragCompletedEvent =
RoutedEvent.Register<Thumb, VectorEventArgs>("DragCompleted", RoutingStrategy.Bubble);
Point? lastPoint;
public Thumb()
{
this.DragStarted += (_, e) => this.OnDragStarted(e);
this.DragDelta += (_, e) => this.OnDragDelta(e);
this.DragCompleted += (_, e) => this.OnDragCompleted(e);
}
public event EventHandler<VectorEventArgs> DragStarted
{
add { this.AddHandler(DragStartedEvent, value); }
remove { this.RemoveHandler(DragStartedEvent, value); }
}
public event EventHandler<VectorEventArgs> DragDelta
{
add { this.AddHandler(DragDeltaEvent, value); }
remove { this.RemoveHandler(DragDeltaEvent, value); }
}
public event EventHandler<VectorEventArgs> DragCompleted
{
add { this.AddHandler(DragCompletedEvent, value); }
remove { this.RemoveHandler(DragCompletedEvent, value); }
}
protected virtual void OnDragStarted(VectorEventArgs e)
{
}
protected virtual void OnDragDelta(VectorEventArgs e)
{
}
protected virtual void OnDragCompleted(VectorEventArgs e)
{
}
protected override void OnPointerMoved(PointerEventArgs e)
{
if (this.lastPoint.HasValue)
{
var ev = new VectorEventArgs
{
RoutedEvent = DragDeltaEvent,
Vector = e.GetPosition(this) - this.lastPoint.Value,
};
this.RaiseEvent(ev);
}
}
protected override void OnPointerPressed(PointerEventArgs e)
{
e.Device.Capture(this);
this.lastPoint = e.GetPosition(this);
var ev = new VectorEventArgs
{
RoutedEvent = DragStartedEvent,
Vector = (Vector)this.lastPoint,
};
this.RaiseEvent(ev);
}
protected override void OnPointerReleased(PointerEventArgs e)
{
if (this.lastPoint.HasValue)
{
e.Device.Capture(null);
this.lastPoint = null;
var ev = new VectorEventArgs
{
RoutedEvent = DragCompletedEvent,
Vector = (Vector)e.GetPosition(this),
};
this.RaiseEvent(ev);
}
}
}
}

8
Perspex.Diagnostics/DevTools.cs

@ -46,7 +46,13 @@ namespace Perspex.Diagnostics
.Where(x => x != null)
.Cast<VisualTreeNode>()
.Select(x => new ControlDetails(x.Visual)),
[Grid.ColumnProperty] = 2,
};
var splitter = new GridSplitter
{
[Grid.ColumnProperty] = 1,
Width = 4,
};
this.Content = new Grid
@ -54,11 +60,13 @@ namespace Perspex.Diagnostics
ColumnDefinitions = new ColumnDefinitions
{
new ColumnDefinition(1, GridUnitType.Star),
new ColumnDefinition(4, GridUnitType.Pixel),
new ColumnDefinition(3, GridUnitType.Star),
},
Children = new Controls
{
treeView,
splitter,
detailsView,
}
};

14
Perspex.Input/InputElement.cs

@ -39,6 +39,9 @@ namespace Perspex.Input
public static readonly RoutedEvent<PointerEventArgs> PointerLeaveEvent =
RoutedEvent.Register<InputElement, PointerEventArgs>("PointerLeave", RoutingStrategy.Direct);
public static readonly RoutedEvent<PointerEventArgs> PointerMovedEvent =
RoutedEvent.Register<InputElement, PointerEventArgs>("PointerMove", RoutingStrategy.Bubble);
public static readonly RoutedEvent<PointerEventArgs> PointerPressedEvent =
RoutedEvent.Register<InputElement, PointerEventArgs>("PointerPressed", RoutingStrategy.Bubble);
@ -53,6 +56,7 @@ namespace Perspex.Input
this.PreviewKeyDown += (_, e) => this.OnPreviewKeyDown(e);
this.PointerEnter += (_, e) => this.OnPointerEnter(e);
this.PointerLeave += (_, e) => this.OnPointerLeave(e);
this.PointerMoved += (_, e) => this.OnPointerMoved(e);
this.PointerPressed += (_, e) => this.OnPointerPressed(e);
this.PointerReleased += (_, e) => this.OnPointerReleased(e);
}
@ -93,6 +97,12 @@ namespace Perspex.Input
remove { this.RemoveHandler(PointerLeaveEvent, value); }
}
public event EventHandler<PointerEventArgs> PointerMoved
{
add { this.AddHandler(PointerMovedEvent, value); }
remove { this.RemoveHandler(PointerMovedEvent, value); }
}
public event EventHandler<PointerEventArgs> PointerPressed
{
add { this.AddHandler(PointerPressedEvent, value); }
@ -156,6 +166,10 @@ namespace Perspex.Input
this.IsPointerOver = false;
}
protected virtual void OnPointerMoved(PointerEventArgs e)
{
}
protected virtual void OnPointerPressed(PointerEventArgs e)
{
}

15
Perspex.Input/MouseDevice.cs

@ -81,9 +81,12 @@ namespace Perspex.Input
private void MouseMove(IMouseDevice device, IVisual visual, Point p)
{
IInteractive source;
if (this.Captured == null)
{
this.InputManager.SetPointerOver(this, visual, p);
source = visual as IInteractive;
}
else
{
@ -95,6 +98,18 @@ namespace Perspex.Input
}
this.InputManager.SetPointerOver(this, this.Captured, p - offset);
source = this.Captured as IInteractive;
}
if (source != null)
{
source.RaiseEvent(new PointerEventArgs
{
Device = this,
RoutedEvent = InputElement.PointerMovedEvent,
OriginalSource = source,
Source = source,
});
}
}

1
Perspex.Input/Perspex.Input.csproj

@ -66,6 +66,7 @@
<Compile Include="IPointerDevice.cs" />
<Compile Include="Key.cs" />
<Compile Include="KeyboardDevice.cs" />
<Compile Include="VectorEventArgs.cs" />
<Compile Include="KeyEventArgs.cs" />
<Compile Include="MouseDevice.cs" />
<Compile Include="PointerEventArgs.cs" />

16
Perspex.Input/VectorEventArgs.cs

@ -0,0 +1,16 @@
// -----------------------------------------------------------------------
// <copyright file="VectorEventArgs.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Input
{
using System;
using Perspex.Interactivity;
public class VectorEventArgs : RoutedEventArgs
{
public Vector Vector { get; set; }
}
}

3
Perspex.Interactivity/Interactive.cs

@ -59,6 +59,9 @@ namespace Perspex.Interactivity
{
Contract.Requires<NullReferenceException>(e != null);
e.Source = e.Source ?? this;
e.OriginalSource = e.OriginalSource ?? this;
if (!e.Handled)
{
switch (e.RoutedEvent.RoutingStrategy)

15
Perspex.Interactivity/RoutedEventArgs.cs

@ -10,21 +10,6 @@ namespace Perspex.Interactivity
public class RoutedEventArgs : EventArgs
{
public RoutedEventArgs()
{
}
public RoutedEventArgs(RoutedEvent routedEvent)
{
this.RoutedEvent = routedEvent;
}
public RoutedEventArgs(RoutedEvent routedEvent, IInteractive source)
{
this.RoutedEvent = routedEvent;
this.Source = source;
}
public bool Handled { get; set; }
public IInteractive OriginalSource { get; set; }

1
Perspex.Themes.Default/DefaultTheme.cs

@ -15,6 +15,7 @@ namespace Perspex.Themes.Default
this.Add(new ButtonStyle());
this.Add(new CheckBoxStyle());
this.Add(new ContentControlStyle());
this.Add(new GridSplitterStyle());
this.Add(new ItemsControlStyle());
this.Add(new TabControlStyle());
this.Add(new TabItemStyle());

42
Perspex.Themes.Default/GridSplitterStyle.cs

@ -0,0 +1,42 @@
// -----------------------------------------------------------------------
// <copyright file="GridSplitterStyle.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Themes.Default
{
using System.Linq;
using Perspex.Controls;
using Perspex.Controls.Presenters;
using Perspex.Media;
using Perspex.Styling;
public class GridSplitterStyle : Styles
{
public GridSplitterStyle()
{
this.AddRange(new[]
{
new Style(x => x.OfType<GridSplitter>())
{
Setters = new[]
{
new Setter(GridSplitter.TemplateProperty, ControlTemplate.Create<GridSplitter>(this.Template)),
new Setter(GridSplitter.WidthProperty, 4),
},
},
});
}
private Control Template(GridSplitter control)
{
Border border = new Border
{
[~Border.BackgroundProperty] = control[~GridSplitter.BackgroundProperty],
};
return border;
}
}
}

1
Perspex.Themes.Default/Perspex.Themes.Default.csproj

@ -66,6 +66,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="GridSplitterStyle.cs" />
<Compile Include="ButtonStyle.cs" />
<Compile Include="CheckBoxStyle.cs" />
<Compile Include="ContentControlStyle.cs" />

Loading…
Cancel
Save