Browse Source

Merge pull request #801 from susloparovdenis/gridsplitter

Removed Orientation Property from GridSplitter.
pull/812/head
Jeremy Koritzinsky 10 years ago
committed by GitHub
parent
commit
7f043a5c46
  1. 126
      src/Avalonia.Controls/GridSplitter.cs
  2. 224
      tests/Avalonia.Controls.UnitTests/GridSplitterTests.cs

126
src/Avalonia.Controls/GridSplitter.cs

@ -11,62 +11,32 @@ using Avalonia.VisualTree;
namespace Avalonia.Controls
{
/// <summary>
/// Represents the control that redistributes space between columns or rows of a Grid control.
/// Represents the control that redistributes space between columns or rows of a Grid control.
/// </summary>
/// <remarks>
/// Unlike WPF GridSplitter, Avalonia GridSplitter has only one Behavior, GridResizeBehavior.PreviousAndNext.
/// Unlike WPF GridSplitter, Avalonia GridSplitter has only one Behavior, GridResizeBehavior.PreviousAndNext.
/// </remarks>
public class GridSplitter : Thumb
{
/// <summary>
/// Defines the <see cref="Orientation"/> property.
/// </summary>
public static readonly StyledProperty<Orientation> OrientationProperty =
AvaloniaProperty.Register<GridSplitter, Orientation>(nameof(Orientation));
protected Grid _grid;
private List<DefinitionBase> _definitions;
private DefinitionBase _prevDefinition;
private Grid _grid;
private DefinitionBase _nextDefinition;
private List<DefinitionBase> _definitions;
/// <summary>
/// Gets or sets the orientation of the GridsSlitter.
/// </summary>
/// <remarks>
/// if null, it's inferred from column/row definition (should be auto).
/// </remarks>
public Orientation Orientation {
get
{
return GetValue(OrientationProperty);
}
set
{
SetValue(OrientationProperty, value);
}
}
private Orientation _orientation;
/// <summary>
/// Initializes static members of the <see cref="GridSplitter"/> class.
/// </summary>
static GridSplitter()
{
PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Vertical, ":vertical");
PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Horizontal, ":horizontal");
}
private DefinitionBase _prevDefinition;
private void GetDeltaConstraints(out double min, out double max)
{
double prevDefinitionLen = GetActualLength(_prevDefinition);
double prevDefinitionMin = GetMinLength(_prevDefinition);
double prevDefinitionMax = GetMaxLength(_prevDefinition);
var prevDefinitionLen = GetActualLength(_prevDefinition);
var prevDefinitionMin = GetMinLength(_prevDefinition);
var prevDefinitionMax = GetMaxLength(_prevDefinition);
double nextDefinitionLen = GetActualLength(_nextDefinition);
double nextDefinitionMin = GetMinLength(_nextDefinition);
double nextDefinitionMax = GetMaxLength(_nextDefinition);
var nextDefinitionLen = GetActualLength(_nextDefinition);
var nextDefinitionMin = GetMinLength(_nextDefinition);
var nextDefinitionMax = GetMaxLength(_nextDefinition);
// Determine the minimum and maximum the columns can be resized
min = -Math.Min(prevDefinitionLen - prevDefinitionMin, nextDefinitionMax - nextDefinitionLen);
max = Math.Min(prevDefinitionMax - prevDefinitionLen, nextDefinitionLen - nextDefinitionMin);
@ -74,7 +44,7 @@ namespace Avalonia.Controls
protected override void OnDragDelta(VectorEventArgs e)
{
var delta = Orientation == Orientation.Vertical ? e.Vector.X : e.Vector.Y;
var delta = _orientation == Orientation.Vertical ? e.Vector.X : e.Vector.Y;
double max;
double min;
GetDeltaConstraints(out min, out max);
@ -98,26 +68,32 @@ namespace Avalonia.Controls
private double GetActualLength(DefinitionBase definition)
{
if (definition == null)
return 0;
var columnDefinition = definition as ColumnDefinition;
return columnDefinition?.ActualWidth ?? ((RowDefinition)definition).ActualHeight;
return columnDefinition?.ActualWidth ?? ((RowDefinition) definition).ActualHeight;
}
private double GetMinLength(DefinitionBase definition)
{
if (definition == null)
return 0;
var columnDefinition = definition as ColumnDefinition;
return columnDefinition?.MinWidth ?? ((RowDefinition)definition).MinHeight;
return columnDefinition?.MinWidth ?? ((RowDefinition) definition).MinHeight;
}
private double GetMaxLength(DefinitionBase definition)
{
if (definition == null)
return 0;
var columnDefinition = definition as ColumnDefinition;
return columnDefinition?.MaxWidth ?? ((RowDefinition)definition).MaxHeight;
return columnDefinition?.MaxWidth ?? ((RowDefinition) definition).MaxHeight;
}
private bool IsStar(DefinitionBase definition)
{
var columnDefinition = definition as ColumnDefinition;
return columnDefinition?.Width.IsStar ?? ((RowDefinition)definition).Height.IsStar;
return columnDefinition?.Width.IsStar ?? ((RowDefinition) definition).Height.IsStar;
}
private void SetLengthInStars(DefinitionBase definition, double value)
@ -129,7 +105,7 @@ namespace Avalonia.Controls
}
else
{
((RowDefinition)definition).Height = new GridLength(value, GridUnitType.Star);
((RowDefinition) definition).Height = new GridLength(value, GridUnitType.Star);
}
}
@ -137,24 +113,58 @@ namespace Avalonia.Controls
{
base.OnAttachedToVisualTree(e);
_grid = this.GetVisualParent<Grid>();
if (Orientation == Orientation.Vertical)
_orientation = DetectOrientation();
int defenitionIndex; //row or col
if (_orientation == Orientation.Vertical)
{
Cursor = new Cursor(StandardCursorType.SizeWestEast);
var col = GetValue(Grid.ColumnProperty);
_definitions = _grid.ColumnDefinitions.Cast<DefinitionBase>().ToList();
_prevDefinition = _definitions[col - 1];
_nextDefinition = _definitions[col + 1];
defenitionIndex = GetValue(Grid.ColumnProperty);
PseudoClasses.Add(":vertical");
}
else
{
Cursor = new Cursor(StandardCursorType.SizeNorthSouth);
var row = GetValue(Grid.RowProperty);
defenitionIndex = GetValue(Grid.RowProperty);
_definitions = _grid.RowDefinitions.Cast<DefinitionBase>().ToList();
_prevDefinition = _definitions[row - 1];
_nextDefinition = _definitions[row + 1];
PseudoClasses.Add(":horizontal");
}
if (defenitionIndex > 0)
_prevDefinition = _definitions[defenitionIndex - 1];
if (defenitionIndex < _definitions.Count - 1)
_nextDefinition = _definitions[defenitionIndex + 1];
}
}
}
private Orientation DetectOrientation()
{
if (!_grid.ColumnDefinitions.Any())
return Orientation.Horizontal;
if (!_grid.RowDefinitions.Any())
return Orientation.Vertical;
var col = GetValue(Grid.ColumnProperty);
var row = GetValue(Grid.RowProperty);
var width = _grid.ColumnDefinitions[col].Width;
var height = _grid.RowDefinitions[row].Height;
if (width.IsAuto && !height.IsAuto)
{
return Orientation.Vertical;
}
if (!width.IsAuto && height.IsAuto)
{
return Orientation.Horizontal;
}
if (_grid.Children.OfType<Control>() // Decision based on other controls in the same column
.Where(c => Grid.GetColumn(c) == col)
.Any(c => c.GetType() != typeof (GridSplitter)))
{
return Orientation.Horizontal;
}
return Orientation.Vertical;
}
}
}

224
tests/Avalonia.Controls.UnitTests/GridSplitterTests.cs

@ -1,116 +1,202 @@
using Moq;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Platform;
using Avalonia.UnitTests;
using Moq;
using Xunit;
namespace Avalonia.Controls.UnitTests
{
public class GridSplitterTests
{
[Fact]
public void Vertical_Stays_Within_Constraints()
public GridSplitterTests()
{
var cursorFactoryImpl = new Mock<IStandardCursorFactory>();
AvaloniaLocator.CurrentMutable.Bind<IStandardCursorFactory>().ToConstant(cursorFactoryImpl.Object);
}
var control1 = new Border { [Grid.ColumnProperty] = 0 };
var splitter = new GridSplitter
{
Orientation = Orientation.Vertical,
[Grid.ColumnProperty] = 1,
};
var control2 = new Border { [Grid.ColumnProperty] = 2 };
[Fact]
public void Detects_Horizontal_Orientation()
{
var grid = new Grid()
{
RowDefinitions = new RowDefinitions("*,Auto,*"),
ColumnDefinitions = new ColumnDefinitions("*,*"),
Children = new Controls()
{
new Border { [Grid.RowProperty] = 0 },
new GridSplitter { [Grid.RowProperty] = 1, Name = "splitter" },
new Border { [Grid.RowProperty] = 2 }
}
};
var columnDefinitions = new ColumnDefinitions()
{
new ColumnDefinition(1, GridUnitType.Star) {MinWidth = 10, MaxWidth = 190},
new ColumnDefinition(GridLength.Auto),
new ColumnDefinition(1, GridUnitType.Star) {MinWidth = 80, MaxWidth = 120},
};
var root = new TestRoot { Child = grid };
root.Measure(new Size(100, 300));
root.Arrange(new Rect(0, 0, 100, 300));
Assert.Contains(grid.FindControl<GridSplitter>("splitter").Classes, ":horizontal".Equals);
}
[Fact]
public void Detects_Vertical_Orientation()
{
var grid = new Grid()
{
ColumnDefinitions = columnDefinitions,
Children = new Controls()
{
control1, splitter, control2
}
};
{
ColumnDefinitions = new ColumnDefinitions("*,Auto,*"),
RowDefinitions = new RowDefinitions("*,*"),
Children = new Controls()
{
new Border { [Grid.ColumnProperty] = 0 },
new GridSplitter { [Grid.ColumnProperty] = 1, Name = "splitter" },
new Border { [Grid.ColumnProperty] = 2 },
}
};
var root = new TestRoot { Child = grid };
Assert.Equal(splitter.Orientation, Orientation.Vertical);
root.Measure(new Size(100, 300));
root.Arrange(new Rect(0, 0, 100, 300));
Assert.Contains(grid.FindControl<GridSplitter>("splitter").Classes, ":vertical".Equals);
}
root.Measure(new Size(200, 100));
root.Arrange(new Rect(0, 0, 200, 100));
[Fact]
public void Detects_With_Both_Auto()
{
var grid = new Grid()
{
ColumnDefinitions = new ColumnDefinitions("Auto,Auto,Auto"),
RowDefinitions = new RowDefinitions("Auto,Auto"),
Children = new Controls()
{
new Border { [Grid.ColumnProperty] = 0 },
new GridSplitter { [Grid.ColumnProperty] = 1, Name = "splitter" },
new Border { [Grid.ColumnProperty] = 2 },
}
};
splitter.RaiseEvent(new VectorEventArgs
{
RoutedEvent = Thumb.DragDeltaEvent,
Vector = new Vector(-100,0)
});
Assert.Equal(columnDefinitions[0].Width, new GridLength(80,GridUnitType.Star));
Assert.Equal(columnDefinitions[2].Width, new GridLength(120,GridUnitType.Star));
splitter.RaiseEvent(new VectorEventArgs
{
RoutedEvent = Thumb.DragDeltaEvent,
Vector = new Vector(100, 0)
});
Assert.Equal(columnDefinitions[0].Width, new GridLength(120, GridUnitType.Star));
Assert.Equal(columnDefinitions[2].Width, new GridLength(80, GridUnitType.Star));
var root = new TestRoot { Child = grid };
root.Measure(new Size(100, 300));
root.Arrange(new Rect(0, 0, 100, 300));
Assert.Contains(grid.FindControl<GridSplitter>("splitter").Classes, ":vertical".Equals);
}
[Fact]
public void Horizontal_Stays_Within_Constraints()
{
var cursorFactoryImpl = new Mock<IStandardCursorFactory>();
AvaloniaLocator.CurrentMutable.Bind<IStandardCursorFactory>().ToConstant(cursorFactoryImpl.Object);
var control1 = new Border { [Grid.RowProperty] = 0 };
var splitter = new GridSplitter
{
Orientation = Orientation.Horizontal,
[Grid.RowProperty] = 1,
};
{
[Grid.RowProperty] = 1,
};
var control2 = new Border { [Grid.RowProperty] = 2 };
var rowDefinitions = new RowDefinitions()
{
new RowDefinition(1, GridUnitType.Star) {MinHeight = 70, MaxHeight = 110},
new RowDefinition(GridLength.Auto),
new RowDefinition(1, GridUnitType.Star) { MinHeight = 10, MaxHeight = 140},
};
{
new RowDefinition(1, GridUnitType.Star) { MinHeight = 70, MaxHeight = 110 },
new RowDefinition(GridLength.Auto),
new RowDefinition(1, GridUnitType.Star) { MinHeight = 10, MaxHeight = 140 },
};
var grid = new Grid()
{
RowDefinitions = rowDefinitions,
Children = new Controls()
{
control1, splitter, control2
}
};
{
RowDefinitions = rowDefinitions,
Children = new Controls()
{
control1, splitter, control2
}
};
var root = new TestRoot { Child = grid };
Assert.Equal(splitter.Orientation, Orientation.Horizontal);
root.Measure(new Size(100, 200));
root.Arrange(new Rect(0, 0, 100, 200));
splitter.RaiseEvent(new VectorEventArgs
{
RoutedEvent = Thumb.DragDeltaEvent,
Vector = new Vector(0, -100)
});
{
RoutedEvent = Thumb.DragDeltaEvent,
Vector = new Vector(0, -100)
});
Assert.Equal(rowDefinitions[0].Height, new GridLength(70, GridUnitType.Star));
Assert.Equal(rowDefinitions[2].Height, new GridLength(130, GridUnitType.Star));
splitter.RaiseEvent(new VectorEventArgs
{
RoutedEvent = Thumb.DragDeltaEvent,
Vector = new Vector(0, 100)
});
{
RoutedEvent = Thumb.DragDeltaEvent,
Vector = new Vector(0, 100)
});
Assert.Equal(rowDefinitions[0].Height, new GridLength(110, GridUnitType.Star));
Assert.Equal(rowDefinitions[2].Height, new GridLength(90, GridUnitType.Star));
}
[Fact]
public void In_First_Position_Doesnt_Throw_Exception()
{
var grid = new Grid()
{
ColumnDefinitions = new ColumnDefinitions("Auto,*,*"),
RowDefinitions = new RowDefinitions("*,*"),
Children = new Controls()
{
new GridSplitter { [Grid.ColumnProperty] = 0, Name = "splitter" },
new Border { [Grid.ColumnProperty] = 1 },
new Border { [Grid.ColumnProperty] = 2 },
}
};
var root = new TestRoot { Child = grid };
root.Measure(new Size(100, 300));
root.Arrange(new Rect(0, 0, 100, 300));
var splitter = grid.FindControl<GridSplitter>("splitter");
splitter.RaiseEvent(new VectorEventArgs
{
RoutedEvent = Thumb.DragDeltaEvent,
Vector = new Vector(100, 1000)
});
}
[Fact]
public void Vertical_Stays_Within_Constraints()
{
var control1 = new Border { [Grid.ColumnProperty] = 0 };
var splitter = new GridSplitter
{
[Grid.ColumnProperty] = 1,
};
var control2 = new Border { [Grid.ColumnProperty] = 2 };
var columnDefinitions = new ColumnDefinitions()
{
new ColumnDefinition(1, GridUnitType.Star) { MinWidth = 10, MaxWidth = 190 },
new ColumnDefinition(GridLength.Auto),
new ColumnDefinition(1, GridUnitType.Star) { MinWidth = 80, MaxWidth = 120 },
};
var grid = new Grid()
{
ColumnDefinitions = columnDefinitions,
Children = new Controls()
{
control1, splitter, control2
}
};
var root = new TestRoot { Child = grid };
root.Measure(new Size(200, 100));
root.Arrange(new Rect(0, 0, 200, 100));
splitter.RaiseEvent(new VectorEventArgs
{
RoutedEvent = Thumb.DragDeltaEvent,
Vector = new Vector(-100, 0)
});
Assert.Equal(columnDefinitions[0].Width, new GridLength(80, GridUnitType.Star));
Assert.Equal(columnDefinitions[2].Width, new GridLength(120, GridUnitType.Star));
splitter.RaiseEvent(new VectorEventArgs
{
RoutedEvent = Thumb.DragDeltaEvent,
Vector = new Vector(100, 0)
});
Assert.Equal(columnDefinitions[0].Width, new GridLength(120, GridUnitType.Star));
Assert.Equal(columnDefinitions[2].Width, new GridLength(80, GridUnitType.Star));
}
}
}
Loading…
Cancel
Save