Browse Source

Merge branch 'master' into monomac

pull/1005/head
Nikita Tsukanov 9 years ago
committed by GitHub
parent
commit
4631b50e1c
  1. 4
      .gitmodules
  2. 1
      Avalonia.sln
  3. 5
      build/ReactiveUI.props
  4. 13
      build/Rx.props
  5. 22
      packages.cake
  6. 1
      samples/BindingTest/App.xaml.cs
  7. 2
      samples/BindingTest/BindingTest.csproj
  8. 10
      samples/BindingTest/ViewModels/MainWindowViewModel.cs
  9. 2
      samples/BindingTest/ViewModels/NestedCommandViewModel.cs
  10. 1
      samples/ControlCatalog.NetCore/Program.cs
  11. 1
      samples/RenderTest/Program.cs
  12. 1
      samples/RenderTest/RenderTest.csproj
  13. 10
      samples/RenderTest/ViewModels/MainWindowViewModel.cs
  14. 1
      samples/VirtualizationTest/Program.cs
  15. 25
      samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs
  16. 1
      samples/VirtualizationTest/VirtualizationTest.csproj
  17. 70
      src/Avalonia.Controls/Button.cs
  18. 24
      src/Avalonia.Controls/Primitives/RangeBase.cs
  19. 103
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  20. 4
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  21. 102
      src/Avalonia.Controls/Primitives/Track.cs
  22. 82
      src/Avalonia.Controls/RepeatButton.cs
  23. 4
      src/Avalonia.Controls/ScrollViewer.cs
  24. 55
      src/Avalonia.Controls/Slider.cs
  25. 24
      src/Avalonia.ReactiveUI/AppBuilderExtensions.cs
  26. 74
      src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
  27. 3
      src/Avalonia.ReactiveUI/Properties/AssemblyInfo.cs
  28. 23
      src/Avalonia.ReactiveUI/Registrations.cs
  29. 34
      src/Avalonia.ReactiveUI/Shims.cs
  30. 1
      src/Avalonia.ReactiveUI/src
  31. 1
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  32. 44
      src/Avalonia.Themes.Default/RepeatButton.xaml
  33. 158
      src/Avalonia.Themes.Default/ScrollBar.xaml
  34. 2
      src/Avalonia.Themes.Default/ScrollViewer.xaml
  35. 26
      src/Avalonia.Themes.Default/Slider.xaml
  36. 4
      src/Windows/Avalonia.Win32/WindowImpl.cs
  37. 5
      tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs
  38. 10
      tests/Avalonia.Controls.UnitTests/Primitives/TrackTests.cs
  39. 9
      tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
  40. 1
      tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
  41. 18
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs
  42. 20
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Source.cs

4
.gitmodules

@ -1,7 +1,3 @@
[submodule "src/Avalonia.ReactiveUI/src"]
path = src/Avalonia.ReactiveUI/src
url = https://github.com/AvaloniaUI/ReactiveUI.git
branch = avalonia-snapshot
[submodule "src/Avalonia.HtmlRenderer/external"]
path = src/Avalonia.HtmlRenderer/external
url = https://github.com/AvaloniaUI/HTML-Renderer.git

1
Avalonia.sln

@ -159,6 +159,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props
build\Moq.props = build\Moq.props
build\NetCore.props = build\NetCore.props
build\ReactiveUI.props = build\ReactiveUI.props
build\Rx.props = build\Rx.props
build\Serilog.props = build\Serilog.props
build\Serilog.Sinks.Trace.props = build\Serilog.Sinks.Trace.props

5
build/ReactiveUI.props

@ -0,0 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="reactiveui" Version="8.0.0-alpha0073" />
</ItemGroup>
</Project>

13
build/Rx.props

@ -1,11 +1,10 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="System.Reactive" Version="3.0.0" />
<PackageReference Include="System.Reactive.Core" Version="3.0.0" />
<PackageReference Include="System.Reactive.Interfaces" Version="3.0.0" />
<PackageReference Include="System.Reactive.Linq" Version="3.0.0" />
<PackageReference Include="System.Reactive.PlatformServices" Version="3.0.0" />
<PackageReference Condition="'$(TargetFramework)' == 'net45'" Include="System.Reactive.Windows.Threading" Version="3.0.0" />
<PackageReference Condition="'$(TargetFramework)' == 'net461'" Include="System.Reactive.Windows.Threading" Version="3.0.0" />
<PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="System.Reactive.Core" Version="3.1.1" />
<PackageReference Include="System.Reactive.Interfaces" Version="3.1.1" />
<PackageReference Include="System.Reactive.Linq" Version="3.1.1" />
<PackageReference Include="System.Reactive.PlatformServices" Version="3.1.1" />
<PackageReference Condition="$(TargetFramework.StartsWith('net4'))" Include="System.Reactive.Windows.Threading" Version="3.1.1" />
</ItemGroup>
</Project>

22
packages.cake

@ -110,6 +110,7 @@ public class Packages
var SplatVersion = packageVersions["Splat"].FirstOrDefault().Item1;
var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1;
var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1;
var ReactiveUIVersion = packageVersions["reactiveui"].FirstOrDefault().Item1;
var SystemValueTupleVersion = packageVersions["System.ValueTuple"].FirstOrDefault().Item1;
SkiaSharpVersion = packageVersions["SkiaSharp"].FirstOrDefault().Item1;
SkiaSharpLinuxVersion = packageVersions["Avalonia.Skia.Linux.Natives"].FirstOrDefault().Item1;
@ -123,6 +124,7 @@ public class Packages
context.Information("Package: Splat, version: {0}", SplatVersion);
context.Information("Package: Sprache, version: {0}", SpracheVersion);
context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion);
context.Information("Package: reactiveui, version: {0}", ReactiveUIVersion);
context.Information("Package: System.ValueTuple, version: {0}", SystemValueTupleVersion);
context.Information("Package: SkiaSharp, version: {0}", SkiaSharpVersion);
context.Information("Package: Avalonia.Skia.Linux.Natives, version: {0}", SkiaSharpLinuxVersion);
@ -175,7 +177,6 @@ public class Packages
new [] { "./src/", "Avalonia.Visuals", ".xml" },
new [] { "./src/", "Avalonia.Styling", ".dll" },
new [] { "./src/", "Avalonia.Styling", ".xml" },
new [] { "./src/", "Avalonia.ReactiveUI", ".dll" },
new [] { "./src/", "Avalonia.Themes.Default", ".dll" },
new [] { "./src/", "Avalonia.Themes.Default", ".xml" },
new [] { "./src/Markup/", "Avalonia.Markup", ".dll" },
@ -272,7 +273,24 @@ public class Packages
},
BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard2.0"),
OutputDirectory = parameters.NugetRoot
}
},
///////////////////////////////////////////////////////////////////////////////
// Avalonia.ReactiveUI
///////////////////////////////////////////////////////////////////////////////
new NuGetPackSettings()
{
Id = "Avalonia.ReactiveUI",
Dependencies = new DependencyBuilder(this)
{
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version },
}.Deps(new string[] {null}, "reactiveui"),
Files = new []
{
new NuSpecContent { Source = "Avalonia.ReactiveUI.dll", Target = "lib/netstandard2.0" }
},
BasePath = context.Directory("./src/Avalonia.ReactiveUI/bin/" + parameters.DirSuffix + "/netstandard2.0"),
OutputDirectory = parameters.NugetRoot
},
};
var nuspecNuGetSettingsMobile = new []

1
samples/BindingTest/App.xaml.cs

@ -20,6 +20,7 @@ namespace BindingTest
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.Start<MainWindow>();
}

2
samples/BindingTest/BindingTest.csproj

@ -45,7 +45,6 @@
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
@ -163,4 +162,5 @@
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="..\..\build\Splat.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
</Project>

10
samples/BindingTest/ViewModels/MainWindowViewModel.cs

@ -28,15 +28,13 @@ namespace BindingTest.ViewModels
SelectedItems = new ObservableCollection<TestItem>();
ShuffleItems = ReactiveCommand.Create();
ShuffleItems.Subscribe(_ =>
ShuffleItems = ReactiveCommand.Create(() =>
{
var r = new Random();
Items.Move(r.Next(Items.Count), 1);
});
StringValueCommand = ReactiveCommand.Create();
StringValueCommand.Subscribe(param =>
StringValueCommand = ReactiveCommand.Create<object>(param =>
{
BooleanFlag = !BooleanFlag;
StringValue = param.ToString();
@ -58,7 +56,7 @@ namespace BindingTest.ViewModels
public ObservableCollection<TestItem> Items { get; }
public ObservableCollection<TestItem> SelectedItems { get; }
public ReactiveCommand<object> ShuffleItems { get; }
public ReactiveCommand ShuffleItems { get; }
public string BooleanString
{
@ -91,7 +89,7 @@ namespace BindingTest.ViewModels
}
public IObservable<string> CurrentTimeObservable { get; }
public ReactiveCommand<object> StringValueCommand { get; }
public ReactiveCommand StringValueCommand { get; }
public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel();
public ExceptionErrorViewModel ExceptionDataValidation { get; } = new ExceptionErrorViewModel();

2
samples/BindingTest/ViewModels/NestedCommandViewModel.cs

@ -12,7 +12,7 @@ namespace BindingTest.ViewModels
{
public NestedCommandViewModel()
{
Command = ReactiveCommand.Create();
Command = ReactiveCommand.Create(() => { });
}
public ICommand Command { get; }

1
samples/ControlCatalog.NetCore/Program.cs

@ -30,6 +30,7 @@ namespace ControlCatalog.NetCore
else
AppBuilder.Configure<App>()
.CustomPlatformDetect()
.UseReactiveUI()
.Start<MainWindow>();
}

1
samples/RenderTest/Program.cs

@ -18,6 +18,7 @@ namespace RenderTest
// again.
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.Start<MainWindow>();
}

1
samples/RenderTest/RenderTest.csproj

@ -191,4 +191,5 @@
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="..\..\build\Splat.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
</Project>

10
samples/RenderTest/ViewModels/MainWindowViewModel.cs

@ -10,10 +10,8 @@ namespace RenderTest.ViewModels
public MainWindowViewModel()
{
ToggleDrawDirtyRects = ReactiveCommand.Create();
ToggleDrawDirtyRects.Subscribe(_ => DrawDirtyRects = !DrawDirtyRects);
ToggleDrawFps = ReactiveCommand.Create();
ToggleDrawFps.Subscribe(_ => DrawFps = !DrawFps);
ToggleDrawDirtyRects = ReactiveCommand.Create(() => DrawDirtyRects = !DrawDirtyRects);
ToggleDrawFps = ReactiveCommand.Create(() => DrawFps = !DrawFps);
}
public bool DrawDirtyRects
@ -28,7 +26,7 @@ namespace RenderTest.ViewModels
set { this.RaiseAndSetIfChanged(ref drawFps, value); }
}
public ReactiveCommand<object> ToggleDrawDirtyRects { get; }
public ReactiveCommand<object> ToggleDrawFps { get; }
public ReactiveCommand ToggleDrawDirtyRects { get; }
public ReactiveCommand ToggleDrawFps { get; }
}
}

1
samples/VirtualizationTest/Program.cs

@ -17,6 +17,7 @@ namespace VirtualizationTest
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.Start<MainWindow>();
}

25
samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs

@ -23,20 +23,15 @@ namespace VirtualizationTest.ViewModels
public MainWindowViewModel()
{
this.WhenAnyValue(x => x.ItemCount).Subscribe(ResizeItems);
RecreateCommand = ReactiveCommand.Create();
RecreateCommand.Subscribe(_ => Recreate());
RecreateCommand = ReactiveCommand.Create(() => Recreate());
AddItemCommand = ReactiveCommand.Create();
AddItemCommand.Subscribe(_ => AddItem());
AddItemCommand = ReactiveCommand.Create(() => AddItem());
RemoveItemCommand = ReactiveCommand.Create();
RemoveItemCommand.Subscribe(_ => Remove());
RemoveItemCommand = ReactiveCommand.Create(() => Remove());
SelectFirstCommand = ReactiveCommand.Create();
SelectFirstCommand.Subscribe(_ => SelectItem(0));
SelectFirstCommand = ReactiveCommand.Create(() => SelectItem(0));
SelectLastCommand = ReactiveCommand.Create();
SelectLastCommand.Subscribe(_ => SelectItem(Items.Count - 1));
SelectLastCommand = ReactiveCommand.Create(() => SelectItem(Items.Count - 1));
}
public string NewItemString
@ -78,11 +73,11 @@ namespace VirtualizationTest.ViewModels
public IEnumerable<ItemVirtualizationMode> VirtualizationModes =>
Enum.GetValues(typeof(ItemVirtualizationMode)).Cast<ItemVirtualizationMode>();
public ReactiveCommand<object> AddItemCommand { get; private set; }
public ReactiveCommand<object> RecreateCommand { get; private set; }
public ReactiveCommand<object> RemoveItemCommand { get; private set; }
public ReactiveCommand<object> SelectFirstCommand { get; private set; }
public ReactiveCommand<object> SelectLastCommand { get; private set; }
public ReactiveCommand AddItemCommand { get; private set; }
public ReactiveCommand RecreateCommand { get; private set; }
public ReactiveCommand RemoveItemCommand { get; private set; }
public ReactiveCommand SelectFirstCommand { get; private set; }
public ReactiveCommand SelectLastCommand { get; private set; }
private void ResizeItems(int count)
{

1
samples/VirtualizationTest/VirtualizationTest.csproj

@ -158,4 +158,5 @@
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="..\..\build\Splat.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
</Project>

70
src/Avalonia.Controls/Button.cs

@ -2,13 +2,10 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using System.Windows.Input;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Rendering;
using Avalonia.VisualTree;
namespace Avalonia.Controls
{
@ -68,19 +65,22 @@ namespace Avalonia.Controls
/// Defines the <see cref="Click"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> ClickEvent =
RoutedEvent.Register<Button, RoutedEventArgs>("Click", RoutingStrategies.Bubble);
RoutedEvent.Register<Button, RoutedEventArgs>(nameof(Click), RoutingStrategies.Bubble);
private ICommand _command;
public static readonly StyledProperty<bool> IsPressedProperty =
AvaloniaProperty.Register<Button, bool>(nameof(IsPressed));
/// <summary>
/// Initializes static members of the <see cref="Button"/> class.
/// </summary>
static Button()
{
FocusableProperty.OverrideDefaultValue(typeof(Button), true);
ClickEvent.AddClassHandler<Button>(x => x.OnClick);
CommandProperty.Changed.Subscribe(CommandChanged);
IsDefaultProperty.Changed.Subscribe(IsDefaultChanged);
PseudoClass(IsPressedProperty, ":pressed");
}
/// <summary>
@ -138,6 +138,12 @@ namespace Avalonia.Controls
set { SetValue(IsDefaultProperty, value); }
}
public bool IsPressed
{
get { return GetValue(IsPressedProperty); }
private set { SetValue(IsPressedProperty, value); }
}
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
@ -145,9 +151,7 @@ namespace Avalonia.Controls
if (IsDefault)
{
var inputElement = e.Root as IInputElement;
if (inputElement != null)
if (e.Root is IInputElement inputElement)
{
ListenForDefault(inputElement);
}
@ -159,16 +163,16 @@ namespace Avalonia.Controls
{
if (e.Key == Key.Enter)
{
RaiseClickEvent();
OnClick();
e.Handled = true;
}
else if (e.Key == Key.Space)
{
if (ClickMode == ClickMode.Press)
{
RaiseClickEvent();
OnClick();
}
IsPressed = true;
e.Handled = true;
}
@ -182,9 +186,9 @@ namespace Avalonia.Controls
{
if (ClickMode == ClickMode.Release)
{
RaiseClickEvent();
OnClick();
}
IsPressed = false;
e.Handled = true;
}
}
@ -196,9 +200,7 @@ namespace Avalonia.Controls
if (IsDefault)
{
var inputElement = e.Root as IInputElement;
if (inputElement != null)
if (e.Root is IInputElement inputElement)
{
StopListeningForDefault(inputElement);
}
@ -208,9 +210,11 @@ namespace Avalonia.Controls
/// <summary>
/// Invokes the <see cref="Click"/> event.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void OnClick(RoutedEventArgs e)
protected virtual void OnClick()
{
var e = new RoutedEventArgs(ClickEvent);
RaiseEvent(e);
if (Command != null)
{
Command.Execute(CommandParameter);
@ -225,13 +229,13 @@ namespace Avalonia.Controls
if (e.MouseButton == MouseButton.Left)
{
PseudoClasses.Add(":pressed");
e.Device.Capture(this);
IsPressed = true;
e.Handled = true;
if (ClickMode == ClickMode.Press)
{
RaiseClickEvent();
OnClick();
}
}
}
@ -244,12 +248,12 @@ namespace Avalonia.Controls
if (e.MouseButton == MouseButton.Left)
{
e.Device.Capture(null);
PseudoClasses.Remove(":pressed");
IsPressed = false;
e.Handled = true;
if (ClickMode == ClickMode.Release && new Rect(Bounds.Size).Contains(e.GetPosition(this)))
{
RaiseClickEvent();
OnClick();
}
}
}
@ -272,9 +276,7 @@ namespace Avalonia.Controls
/// <param name="e">The event args.</param>
private static void CommandChanged(AvaloniaPropertyChangedEventArgs e)
{
var button = e.Sender as Button;
if (button != null)
if (e.Sender is Button button)
{
var oldCommand = e.OldValue as ICommand;
var newCommand = e.NewValue as ICommand;
@ -301,9 +303,8 @@ namespace Avalonia.Controls
{
var button = e.Sender as Button;
var isDefault = (bool)e.NewValue;
var inputRoot = button?.VisualRoot as IInputElement;
if (inputRoot != null)
if (button?.VisualRoot is IInputElement inputRoot)
{
if (isDefault)
{
@ -346,19 +347,6 @@ namespace Avalonia.Controls
root.RemoveHandler(KeyDownEvent, RootKeyDown);
}
/// <summary>
/// Raises the <see cref="Click"/> event.
/// </summary>
private void RaiseClickEvent()
{
RoutedEventArgs click = new RoutedEventArgs
{
RoutedEvent = ClickEvent,
};
RaiseEvent(click);
}
/// <summary>
/// Called when a key is pressed on the input root and the button <see cref="IsDefault"/>.
/// </summary>
@ -368,7 +356,7 @@ namespace Avalonia.Controls
{
if (e.Key == Key.Enter && IsVisible && IsEnabled)
{
RaiseClickEvent();
OnClick();
}
}
}

24
src/Avalonia.Controls/Primitives/RangeBase.cs

@ -38,6 +38,18 @@ namespace Avalonia.Controls.Primitives
o => o.Value,
(o, v) => o.Value = v);
/// <summary>
/// Defines the <see cref="SmallChange"/> property.
/// </summary>
public static readonly StyledProperty<double> SmallChangeProperty =
AvaloniaProperty.Register<RangeBase, double>(nameof(SmallChange), 0.1);
/// <summary>
/// Defines the <see cref="LargeChange"/> property.
/// </summary>
public static readonly StyledProperty<double> LargeChangeProperty =
AvaloniaProperty.Register<RangeBase, double>(nameof(LargeChange), 1);
private double _minimum;
private double _maximum = 100.0;
private double _value;
@ -103,6 +115,18 @@ namespace Avalonia.Controls.Primitives
}
}
public double SmallChange
{
get => GetValue(SmallChangeProperty);
set => SetValue(SmallChangeProperty, value);
}
public double LargeChange
{
get => GetValue(LargeChangeProperty);
set => SetValue(LargeChangeProperty, value);
}
/// <summary>
/// Throws an exception if the double valus is NaN or Inf.
/// </summary>

103
src/Avalonia.Controls/Primitives/ScrollBar.cs

@ -5,6 +5,7 @@ using System;
using System.Reactive;
using System.Reactive.Linq;
using Avalonia.Data;
using Avalonia.Interactivity;
namespace Avalonia.Controls.Primitives
{
@ -31,13 +32,18 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<Orientation> OrientationProperty =
AvaloniaProperty.Register<ScrollBar, Orientation>(nameof(Orientation));
private Button _lineUpButton;
private Button _lineDownButton;
private Button _pageUpButton;
private Button _pageDownButton;
/// <summary>
/// Initializes static members of the <see cref="ScrollBar"/> class.
/// </summary>
static ScrollBar()
{
PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Vertical, ":vertical");
PseudoClass(OrientationProperty, o => o == Avalonia.Controls.Orientation.Horizontal, ":horizontal");
PseudoClass(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
}
/// <summary>
@ -97,12 +103,101 @@ namespace Avalonia.Controls.Primitives
return false;
case ScrollBarVisibility.Auto:
var viewportSize = ViewportSize;
return double.IsNaN(viewportSize) || viewportSize < Maximum - Minimum;
return double.IsNaN(ViewportSize) || Maximum > 0;
default:
throw new InvalidOperationException("Invalid value for ScrollBar.Visibility.");
}
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
if (_lineUpButton != null)
{
_lineUpButton.Click -= LineUpClick;
}
if (_lineDownButton != null)
{
_lineDownButton.Click -= LineDownClick;
}
if (_pageUpButton != null)
{
_pageUpButton.Click -= PageUpClick;
}
if (_pageDownButton != null)
{
_pageDownButton.Click -= PageDownClick;
}
_lineUpButton = e.NameScope.Find<Button>("PART_LineUpButton");
_lineDownButton = e.NameScope.Find<Button>("PART_LineDownButton");
_pageUpButton = e.NameScope.Find<Button>("PART_PageUpButton");
_pageDownButton = e.NameScope.Find<Button>("PART_PageDownButton");
if (_lineUpButton != null)
{
_lineUpButton.Click += LineUpClick;
}
if (_lineDownButton != null)
{
_lineDownButton.Click += LineDownClick;
}
if (_pageUpButton != null)
{
_pageUpButton.Click += PageUpClick;
}
if (_pageDownButton != null)
{
_pageDownButton.Click += PageDownClick;
}
}
private void LineUpClick(object sender, RoutedEventArgs e)
{
SmallDecrement();
}
private void LineDownClick(object sender, RoutedEventArgs e)
{
SmallIncrement();
}
private void PageUpClick(object sender, RoutedEventArgs e)
{
LargeDecrement();
}
private void PageDownClick(object sender, RoutedEventArgs e)
{
LargeIncrement();
}
private void SmallDecrement()
{
Value = Math.Max(Value - SmallChange * ViewportSize, Minimum);
}
private void SmallIncrement()
{
Value = Math.Min(Value + SmallChange * ViewportSize, Maximum);
}
private void LargeDecrement()
{
Value = Math.Max(Value - LargeChange * ViewportSize, Minimum);
}
private void LargeIncrement()
{
Value = Math.Min(Value + LargeChange * ViewportSize, Maximum);
}
}
}

4
src/Avalonia.Controls/Primitives/ToggleButton.cs

@ -29,10 +29,10 @@ namespace Avalonia.Controls.Primitives
set { SetAndRaise(IsCheckedProperty, ref _isChecked, value); }
}
protected override void OnClick(RoutedEventArgs e)
protected override void OnClick()
{
Toggle();
base.OnClick(e);
base.OnClick();
}
protected virtual void Toggle()

102
src/Avalonia.Controls/Primitives/Track.cs

@ -10,7 +10,7 @@ namespace Avalonia.Controls.Primitives
public class Track : Control
{
public static readonly DirectProperty<Track, double> MinimumProperty =
RangeBase.MinimumProperty.AddOwner<Track>(o => o.Minimum, (o,v) => o.Minimum = v);
RangeBase.MinimumProperty.AddOwner<Track>(o => o.Minimum, (o, v) => o.Minimum = v);
public static readonly DirectProperty<Track, double> MaximumProperty =
RangeBase.MaximumProperty.AddOwner<Track>(o => o.Maximum, (o, v) => o.Maximum = v);
@ -25,7 +25,13 @@ namespace Avalonia.Controls.Primitives
ScrollBar.OrientationProperty.AddOwner<Track>();
public static readonly StyledProperty<Thumb> ThumbProperty =
AvaloniaProperty.Register<Track, Thumb>("Thumb");
AvaloniaProperty.Register<Track, Thumb>(nameof(Thumb));
public static readonly StyledProperty<Button> IncreaseButtonProperty =
AvaloniaProperty.Register<Track, Button>(nameof(IncreaseButton));
public static readonly StyledProperty<Button> DecreaseButtonProperty =
AvaloniaProperty.Register<Track, Button>(nameof(DecreaseButton));
private double _minimum;
private double _maximum = 100.0;
@ -34,6 +40,8 @@ namespace Avalonia.Controls.Primitives
static Track()
{
ThumbProperty.Changed.AddClassHandler<Track>(x => x.ThumbChanged);
IncreaseButtonProperty.Changed.AddClassHandler<Track>(x => x.ButtonChanged);
DecreaseButtonProperty.Changed.AddClassHandler<Track>(x => x.ButtonChanged);
AffectsArrange(MinimumProperty, MaximumProperty, ValueProperty, OrientationProperty);
}
@ -74,6 +82,18 @@ namespace Avalonia.Controls.Primitives
set { SetValue(ThumbProperty, value); }
}
public Button IncreaseButton
{
get { return GetValue(IncreaseButtonProperty); }
set { SetValue(IncreaseButtonProperty, value); }
}
public Button DecreaseButton
{
get { return GetValue(DecreaseButtonProperty); }
set { SetValue(DecreaseButtonProperty, value); }
}
protected override Size MeasureOverride(Size availableSize)
{
var thumb = Thumb;
@ -98,34 +118,54 @@ namespace Avalonia.Controls.Primitives
protected override Size ArrangeOverride(Size finalSize)
{
var thumb = Thumb;
var increaseButton = IncreaseButton;
var decreaseButton = DecreaseButton;
if (thumb != null)
var range = Maximum - Minimum;
var offset = Math.Min(Value - Minimum, range);
var viewportSize = ViewportSize;
var extent = range + viewportSize;
if (Orientation == Orientation.Horizontal)
{
var range = Maximum - Minimum;
var thumbFraction = ViewportSize / range;
var valueFraction = (Value - Minimum) / range;
var thumbWidth = double.IsNaN(viewportSize) ? thumb?.DesiredSize.Width ?? 0 : finalSize.Width * viewportSize / extent;
var remaining = finalSize.Width - thumbWidth;
var firstWidth = range <= 0 ? 0 : remaining * offset / range;
if (double.IsNaN(valueFraction) || double.IsInfinity(valueFraction))
if (decreaseButton != null)
{
valueFraction = 0;
thumbFraction = 1;
decreaseButton.Arrange(new Rect(0, 0, firstWidth, finalSize.Height));
}
else if (double.IsNaN(thumbFraction) || double.IsInfinity(thumbFraction))
if (thumb != null)
{
thumbFraction = 0;
thumb.Arrange(new Rect(firstWidth, 0, thumbWidth, finalSize.Height));
}
if (Orientation == Orientation.Horizontal)
if (increaseButton != null)
{
var width = Math.Max(finalSize.Width * thumbFraction, thumb.MinWidth);
var x = (finalSize.Width - width) * valueFraction;
thumb.Arrange(new Rect(x, 0, width, finalSize.Height));
increaseButton.Arrange(new Rect(firstWidth + thumbWidth, 0, remaining - firstWidth, finalSize.Height));
}
else
}
else
{
var thumbHeight = double.IsNaN(viewportSize) ? thumb?.DesiredSize.Height ?? 0 : finalSize.Height * viewportSize / extent;
var remaining = finalSize.Height - thumbHeight;
var firstHeight = range <= 0 ? 0 : remaining * offset / range;
if (decreaseButton != null)
{
var height = Math.Max(finalSize.Height * thumbFraction, thumb.MinHeight);
var y = (finalSize.Height - height) * valueFraction;
thumb.Arrange(new Rect(0, y, finalSize.Width, height));
decreaseButton.Arrange(new Rect(0, 0, finalSize.Width, firstHeight));
}
if (thumb != null)
{
thumb.Arrange(new Rect(0, firstHeight, finalSize.Width, thumbHeight));
}
if (increaseButton != null)
{
increaseButton.Arrange(new Rect(0, firstHeight + thumbHeight, finalSize.Width, remaining - firstHeight));
}
}
@ -140,10 +180,10 @@ namespace Avalonia.Controls.Primitives
if (oldThumb != null)
{
oldThumb.DragDelta -= ThumbDragged;
}
LogicalChildren.Clear();
VisualChildren.Clear();
LogicalChildren.Remove(oldThumb);
VisualChildren.Remove(oldThumb);
}
if (newThumb != null)
{
@ -153,6 +193,24 @@ namespace Avalonia.Controls.Primitives
}
}
private void ButtonChanged(AvaloniaPropertyChangedEventArgs e)
{
var oldButton = (Button)e.OldValue;
var newButton = (Button)e.NewValue;
if (oldButton != null)
{
LogicalChildren.Remove(oldButton);
VisualChildren.Remove(oldButton);
}
if (newButton != null)
{
LogicalChildren.Add(newButton);
VisualChildren.Add(newButton);
}
}
private void ThumbDragged(object sender, VectorEventArgs e)
{
double range = Maximum - Minimum;

82
src/Avalonia.Controls/RepeatButton.cs

@ -0,0 +1,82 @@
using System;
using Avalonia.Input;
using Avalonia.Threading;
namespace Avalonia.Controls
{
public class RepeatButton : Button
{
/// <summary>
/// Defines the <see cref="Delay"/> property.
/// </summary>
public static readonly StyledProperty<int> DelayProperty =
AvaloniaProperty.Register<Button, int>(nameof(Delay), 100);
private DispatcherTimer _repeatTimer;
/// <summary>
/// Gets or sets the amount of time, in milliseconds, of repeating clicks.
/// </summary>
public int Delay
{
get { return GetValue(DelayProperty); }
set { SetValue(DelayProperty, value); }
}
private void StartTimer()
{
if (_repeatTimer == null)
{
_repeatTimer = new DispatcherTimer();
_repeatTimer.Tick += (o, e) => OnClick();
}
if (_repeatTimer.IsEnabled) return;
_repeatTimer.Interval = TimeSpan.FromMilliseconds(Delay);
_repeatTimer.Start();
}
private void StopTimer()
{
_repeatTimer?.Stop();
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.Space)
{
StartTimer();
}
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
StopTimer();
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
if (e.MouseButton == MouseButton.Left)
{
StartTimer();
}
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
if (e.MouseButton == MouseButton.Left)
{
StopTimer();
}
}
}
}

4
src/Avalonia.Controls/ScrollViewer.cs

@ -275,7 +275,7 @@ namespace Avalonia.Controls
/// </summary>
protected double HorizontalScrollBarViewportSize
{
get { return Max((_viewport.Width / _extent.Width) * (_extent.Width - _viewport.Width), 0); }
get { return _viewport.Width; }
}
/// <summary>
@ -308,7 +308,7 @@ namespace Avalonia.Controls
/// </summary>
protected double VerticalScrollBarViewportSize
{
get { return Max((_viewport.Height / _extent.Height) * (_extent.Height - _viewport.Height), 0); }
get { return _viewport.Height; }
}
/// <summary>

55
src/Avalonia.Controls/Slider.cs

@ -33,6 +33,8 @@ namespace Avalonia.Controls
// Slider required parts
private Track _track;
private Button _decreaseButton;
private Button _increaseButton;
/// <summary>
/// Initializes static members of the <see cref="Slider"/> class.
@ -44,6 +46,8 @@ namespace Avalonia.Controls
Thumb.DragStartedEvent.AddClassHandler<Slider>(x => x.OnThumbDragStarted, RoutingStrategies.Bubble);
Thumb.DragDeltaEvent.AddClassHandler<Slider>(x => x.OnThumbDragDelta, RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>(x => x.OnThumbDragCompleted, RoutingStrategies.Bubble);
SmallChangeProperty.OverrideDefaultValue<Slider>(1);
LargeChangeProperty.OverrideDefaultValue<Slider>(10);
}
/// <summary>
@ -83,7 +87,54 @@ namespace Avalonia.Controls
/// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
_track = e.NameScope.Get<Track>("PART_Track");
if (_decreaseButton != null)
{
_decreaseButton.Click -= DecreaseClick;
}
if (_increaseButton != null)
{
_increaseButton.Click -= IncreaseClick;
}
_decreaseButton = e.NameScope.Find<Button>("PART_DecreaseButton");
_track = e.NameScope.Find<Track>("PART_Track");
_increaseButton = e.NameScope.Find<Button>("PART_IncreaseButton");
if (_decreaseButton != null)
{
_decreaseButton.Click += DecreaseClick;
}
if (_increaseButton != null)
{
_increaseButton.Click += IncreaseClick;
}
}
private void DecreaseClick(object sender, RoutedEventArgs e)
{
ChangeValueBy(-LargeChange);
}
private void IncreaseClick(object sender, RoutedEventArgs e)
{
ChangeValueBy(LargeChange);
}
private void ChangeValueBy(double by)
{
if (IsSnapToTickEnabled)
{
by = by < 0 ? Math.Min(-TickFrequency, by) : Math.Max(TickFrequency, by);
}
var value = Value;
var next = SnapToTick(Math.Max(Math.Min(value + by, Maximum), Minimum));
if (next != value)
{
Value = next;
}
}
/// <summary>
@ -101,7 +152,7 @@ namespace Avalonia.Controls
protected virtual void OnThumbDragDelta(VectorEventArgs e)
{
Thumb thumb = e.Source as Thumb;
if (thumb != null && _track.Thumb == thumb)
if (thumb != null && _track?.Thumb == thumb)
{
MoveToNextTick(_track.Value);
}

24
src/Avalonia.ReactiveUI/AppBuilderExtensions.cs

@ -0,0 +1,24 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Controls;
using Avalonia.Threading;
using ReactiveUI;
using System;
using System.Reactive.Concurrency;
using System.Threading;
namespace Avalonia
{
public static class AppBuilderExtensions
{
public static TAppBuilder UseReactiveUI<TAppBuilder>(this TAppBuilder builder)
where TAppBuilder : AppBuilderBase<TAppBuilder>, new()
{
return builder.AfterSetup(_ =>
{
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
});
}
}
}

74
src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj

@ -1,83 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;MONO PORTABLE;NETSTANDARD1_1</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;MONO PORTABLE;RELEASE;NETSTANDARD1_1</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<None Remove="Shims.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Shared\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Registrations.cs" />
<Compile Include="Shims.cs" />
<Compile Include="src\ReactiveUI\ExpressionMixins.cs" />
<Compile Include="src\ReactiveUI\ExpressionRewriter.cs" />
<Compile Include="src\ReactiveUI\Activation.cs" />
<Compile Include="src\ReactiveUI\ReactiveBinding.cs" />
<Compile Include="src\ReactiveUI\AutoPersistHelper.cs" />
<Compile Include="src\ReactiveUI\BindingTypeConverters.cs" />
<Compile Include="src\ReactiveUI\CollectionDebugView.cs" />
<Compile Include="src\ReactiveUI\CommandBinding.cs" />
<Compile Include="src\ReactiveUI\CompatMixins.cs" />
<Compile Include="src\ReactiveUI\ContractStubs.cs" />
<Compile Include="src\ReactiveUI\CreatesCommandBinding.cs" />
<Compile Include="src\ReactiveUI\DefaultPropertyBinding.cs" />
<Compile Include="src\ReactiveUI\Errors.cs" />
<Compile Include="src\ReactiveUI\IDependencyResolver.cs" />
<Compile Include="src\ReactiveUI\INPCObservableForProperty.cs" />
<Compile Include="src\ReactiveUI\Interfaces.cs" />
<Compile Include="src\ReactiveUI\IROObservableForProperty.cs" />
<Compile Include="src\ReactiveUI\MessageBus.cs" />
<Compile Include="src\ReactiveUI\MobileLifecycle.cs" />
<Compile Include="src\ReactiveUI\NullDefaultPropertyBindingProvider.cs" />
<Compile Include="src\ReactiveUI\ObservableAsPropertyHelper.cs" />
<Compile Include="src\ReactiveUI\ObservedChangedMixin.cs" />
<Compile Include="src\ReactiveUI\OrderedComparer.cs" />
<Compile Include="src\ReactiveUI\POCOObservableForProperty.cs" />
<Compile Include="src\ReactiveUI\PropertyBinding.cs" />
<Compile Include="src\ReactiveUI\ReactiveList.cs" />
<Compile Include="src\ReactiveUI\ReactiveCollectionMixins.cs" />
<Compile Include="src\ReactiveUI\ReactiveCommand.cs" />
<Compile Include="src\ReactiveUI\ReactiveNotifyPropertyChangedMixin.cs" />
<Compile Include="src\ReactiveUI\ReactiveObject.cs" />
<Compile Include="src\ReactiveUI\RefcountDisposeWrapper.cs" />
<Compile Include="src\ReactiveUI\Reflection.cs" />
<Compile Include="src\ReactiveUI\RegisterableInterfaces.cs" />
<Compile Include="src\ReactiveUI\Registrations.cs" />
<Compile Include="src\ReactiveUI\RoutableViewModelMixin.cs" />
<Compile Include="src\ReactiveUI\RoutingState.cs" />
<Compile Include="src\ReactiveUI\RxApp.cs" />
<Compile Include="src\ReactiveUI\ViewLocator.cs" />
<Compile Include="src\ReactiveUI\ScheduledSubject.cs" />
<Compile Include="src\ReactiveUI\VariadicTemplates.cs" />
<Compile Include="src\ReactiveUI\WaitForDispatcherScheduler.cs" />
<Compile Include="src\ReactiveUI\LoggingMixins.cs" />
<Compile Include="src\ReactiveUI\IReactiveObject.cs" />
<Compile Include="src\ReactiveUI\WeakEventManager.cs" />
<Compile Include="src\ReactiveUI\Legacy\ReactiveCommand.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
@ -88,5 +15,6 @@
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
<Import Project="..\..\build\Splat.props" />
</Project>

3
src/Avalonia.ReactiveUI/Properties/AssemblyInfo.cs

@ -3,7 +3,4 @@
using System.Reflection;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.ReactiveUI")]

23
src/Avalonia.ReactiveUI/Registrations.cs

@ -1,23 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reactive.Concurrency;
using System.Threading;
namespace ReactiveUI
{
/// <summary>
/// Ignore me. This class is a secret handshake between RxUI and RxUI.Xaml
/// in order to register certain classes on startup that would be difficult
/// to register otherwise.
/// </summary>
public class PlatformRegistrations : IWantsToRegisterStuff
{
public void Register(Action<Func<object>, Type> registerFunction)
{
RxApp.MainThreadScheduler = new SynchronizationContextScheduler(SynchronizationContext.Current);
}
}
}

34
src/Avalonia.ReactiveUI/Shims.cs

@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace System.Runtime.Serialization
{
class IgnoreDataMemberAttribute : Attribute
{
}
class DataMemberAttribute : Attribute
{
}
class OnDeserializedAttribute : Attribute
{
}
class DataContractAttribute : Attribute
{
}
class StreamingContext { }
}
namespace System.Diagnostics.Contracts
{
static class Contract
{
public static void Requires(bool condition)
{
}
}
}

1
src/Avalonia.ReactiveUI/src

@ -1 +0,0 @@
Subproject commit 3f725c808b1d4c8457f0d3204e0a071aa462cd75

1
src/Avalonia.Themes.Default/DefaultTheme.xaml

@ -20,6 +20,7 @@
<StyleInclude Source="resm:Avalonia.Themes.Default.PopupRoot.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ProgressBar.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.RadioButton.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.RepeatButton.xaml?assembly=Avalonia.Themes.Default" />
<StyleInclude Source="resm:Avalonia.Themes.Default.Separator.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Slider.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ScrollBar.xaml?assembly=Avalonia.Themes.Default"/>

44
src/Avalonia.Themes.Default/RepeatButton.xaml

@ -0,0 +1,44 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="RepeatButton">
<Setter Property="Background"
Value="{StyleResource ThemeControlMidBrush}" />
<Setter Property="BorderBrush"
Value="{StyleResource ThemeBorderLightBrush}" />
<Setter Property="BorderThickness"
Value="{StyleResource ThemeBorderThickness}" />
<Setter Property="Foreground"
Value="{StyleResource ThemeForegroundBrush}" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="Padding"
Value="4" />
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</ControlTemplate>
</Setter>
</Style>
<Style Selector="RepeatButton:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush"
Value="{StyleResource ThemeBorderMidBrush}" />
</Style>
<Style Selector="RepeatButton:pressed /template/ ContentPresenter">
<Setter Property="Background"
Value="{StyleResource ThemeControlDarkBrush}" />
</Style>
<Style Selector="RepeatButton:disabled">
<Setter Property="Opacity"
Value="{StyleResource ThemeDisabledOpacity}" />
</Style>
</Styles>

158
src/Avalonia.Themes.Default/ScrollBar.xaml

@ -1,35 +1,127 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="ScrollBar">
<Setter Property="Template">
<ControlTemplate>
<Border Background="{StyleResource ThemeControlMidBrush}">
<Track Minimum="{TemplateBinding Minimum}"
Maximum="{TemplateBinding Maximum}"
Value="{TemplateBinding Path=Value, Mode=TwoWay}"
ViewportSize="{TemplateBinding ViewportSize}"
Orientation="{TemplateBinding Orientation}">
<Thumb Name="thumb">
<Thumb.Template>
<ControlTemplate>
<Border Background="{StyleResource ThemeControlDarkBrush}"/>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ScrollBar:horizontal">
<Setter Property="Height" Value="10"/>
</Style>
<Style Selector="ScrollBar:horizontal /template/ Thumb#thumb">
<Setter Property="MinWidth" Value="10"/>
</Style>
<Style Selector="ScrollBar:vertical">
<Setter Property="Width" Value="10"/>
</Style>
<Style Selector="ScrollBar:vertical /template/ Thumb#thumb">
<Setter Property="MinHeight" Value="10"/>
</Style>
<Style Selector="ScrollBar">
<Setter Property="Template">
<ControlTemplate>
<Border Background="{StyleResource ThemeControlMidBrush}">
<Grid RowDefinitions="10,*,10">
<RepeatButton Name="PART_LineUpButton"
Classes="repeat"
Grid.Row="0"
Grid.Column="0">
<Path Data="M 0,4 C0,4 0,6 0,6 0,6 3.5,2.5 3.5,2.5 3.5,2.5 7,6 7,6 7,6 7,4 7,4 7,4 3.5,0.5 3.5,0.5 3.5,0.5 0,4 0,4 z"
Stretch="Uniform"
Fill="Gray" />
</RepeatButton>
<Track Grid.Row="1"
Grid.Column="1"
Minimum="{TemplateBinding Minimum}"
Maximum="{TemplateBinding Maximum}"
Value="{TemplateBinding Path=Value, Mode=TwoWay}"
ViewportSize="{TemplateBinding ViewportSize}"
Orientation="{TemplateBinding Orientation}">
<Track.DecreaseButton>
<RepeatButton Name="PART_PageUpButton"
Classes="repeattrack" />
</Track.DecreaseButton>
<Track.IncreaseButton>
<RepeatButton Name="PART_PageDownButton"
Classes="repeattrack" />
</Track.IncreaseButton>
<Thumb Name="thumb">
<Thumb.Template>
<ControlTemplate>
<Border Background="{StyleResource ThemeControlDarkBrush}" />
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track>
<RepeatButton Name="PART_LineDownButton"
Classes="repeat"
Grid.Row="2"
Grid.Column="2">
<Path Data="M 0,2.5 C0,2.5 0,0.5 0,0.5 0,0.5 3.5,4 3.5,4 3.5,4 7,0.5 7,0.5 7,0.5 7,2.5 7,2.5 7,2.5 3.5,6 3.5,6 3.5,6 0,2.5 0,2.5 z"
Stretch="Uniform"
Fill="Gray" />
</RepeatButton>
</Grid>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ScrollBar:horizontal">
<Setter Property="Height"
Value="10" />
<Setter Property="Template">
<ControlTemplate>
<Border Background="{StyleResource ThemeControlMidBrush}">
<Grid ColumnDefinitions="10,*,10">
<RepeatButton Name="PART_LineUpButton"
Classes="repeat"
Grid.Row="0"
Grid.Column="0">
<Path Data="M 3.18,7 C3.18,7 5,7 5,7 5,7 1.81,3.5 1.81,3.5 1.81,3.5 5,0 5,0 5,0 3.18,0 3.18,0 3.18,0 0,3.5 0,3.5 0,3.5 3.18,7 3.18,7 z"
Stretch="Uniform"
Fill="Gray" />
</RepeatButton>
<Track Grid.Row="1"
Grid.Column="1"
Minimum="{TemplateBinding Minimum}"
Maximum="{TemplateBinding Maximum}"
Value="{TemplateBinding Path=Value, Mode=TwoWay}"
ViewportSize="{TemplateBinding ViewportSize}"
Orientation="{TemplateBinding Orientation}">
<Track.DecreaseButton>
<RepeatButton Name="PART_PageUpButton"
Classes="repeattrack" />
</Track.DecreaseButton>
<Track.IncreaseButton>
<RepeatButton Name="PART_PageDownButton"
Classes="repeattrack" />
</Track.IncreaseButton>
<Thumb Name="thumb">
<Thumb.Template>
<ControlTemplate>
<Border Background="{StyleResource ThemeControlDarkBrush}" />
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track>
<RepeatButton Name="PART_LineDownButton"
Classes="repeat"
Grid.Row="2"
Grid.Column="2">
<Path Data="M 1.81,7 C1.81,7 0,7 0,7 0,7 3.18,3.5 3.18,3.5 3.18,3.5 0,0 0,0 0,0 1.81,0 1.81,0 1.81,0 5,3.5 5,3.5 5,3.5 1.81,7 1.81,7 z"
Stretch="Uniform"
Fill="Gray" />
</RepeatButton>
</Grid>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ScrollBar:horizontal /template/ Thumb#thumb">
<Setter Property="MinWidth"
Value="10" />
</Style>
<Style Selector="ScrollBar:vertical">
<Setter Property="Width"
Value="10" />
</Style>
<Style Selector="ScrollBar:vertical /template/ Thumb#thumb">
<Setter Property="MinHeight"
Value="10" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.repeat">
<Setter Property="Padding"
Value="2" />
<Setter Property="BorderThickness"
Value="0" />
</Style>
<Style Selector="ScrollBar /template/ RepeatButton.repeattrack">
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}" />
</ControlTemplate>
</Setter>
</Style>
</Styles>

2
src/Avalonia.Themes.Default/ScrollViewer.xaml

@ -1,4 +1,6 @@
<Style xmlns="https://github.com/avaloniaui" Selector="ScrollViewer">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Template">
<ControlTemplate>
<Grid ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">

26
src/Avalonia.Themes.Default/Slider.xaml

@ -12,6 +12,14 @@
</Grid.RowDefinitions>
<Border Name="TrackBackground" Grid.Row="1" Height="4" Margin="6,0" VerticalAlignment="Center"/>
<Track Name="PART_Track" Grid.Row="1">
<Track.DecreaseButton>
<RepeatButton Name="PART_DecreaseButton"
Classes="repeattrack" />
</Track.DecreaseButton>
<Track.IncreaseButton>
<RepeatButton Name="PART_IncreaseButton"
Classes="repeattrack" />
</Track.IncreaseButton>
<Thumb MinWidth="20" MinHeight="20">
<Thumb.Template>
<ControlTemplate>
@ -39,6 +47,14 @@
</Grid.ColumnDefinitions>
<Border Name="TrackBackground" Grid.Column="1" Width="4" Margin="0,6" HorizontalAlignment="Center"/>
<Track Name="PART_Track" Grid.Column="1">
<Track.DecreaseButton>
<RepeatButton Name="PART_DecreaseButton"
Classes="repeattrack" />
</Track.DecreaseButton>
<Track.IncreaseButton>
<RepeatButton Name="PART_IncreaseButton"
Classes="repeattrack" />
</Track.IncreaseButton>
<Thumb MinWidth="20" MinHeight="20">
<Thumb.Template>
<ControlTemplate>
@ -62,5 +78,13 @@
<Style Selector="Slider /template/ Border#TrackBackground">
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderLightBrush}"/>
</Style>
</Style>
<Style Selector="Slider /template/ RepeatButton.repeattrack">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}" />
</ControlTemplate>
</Setter>
</Style>
</Styles>

4
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -533,7 +533,7 @@ namespace Avalonia.Win32
WindowsMouseDevice.Instance,
timestamp,
_owner,
ScreenToClient(DipFromLParam(lParam)),
PointToClient(PointFromLParam(lParam)),
new Vector(0, (ToInt32(wParam) >> 16) / wheelDelta), GetMouseModifiers(wParam));
break;
@ -542,7 +542,7 @@ namespace Avalonia.Win32
WindowsMouseDevice.Instance,
timestamp,
_owner,
ScreenToClient(DipFromLParam(lParam)),
PointToClient(PointFromLParam(lParam)),
new Vector(-(ToInt32(wParam) >> 16) / wheelDelta, 0), GetMouseModifiers(wParam));
break;

5
tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs

@ -65,9 +65,8 @@ namespace Avalonia.Controls.UnitTests.Primitives
var target = new ScrollBar();
target.Visibility = ScrollBarVisibility.Auto;
target.Minimum = 0;
target.Maximum = 100;
target.ViewportSize = 100;
target.ViewportSize = 1;
target.Maximum = 0;
Assert.False(target.IsVisible);
}

10
tests/Avalonia.Controls.UnitTests/Primitives/TrackTests.cs

@ -69,7 +69,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
Assert.Equal(new Rect(25, 0, 50, 12), thumb.Bounds);
Assert.Equal(new Rect(33, 0, 34, 12), thumb.Bounds);
}
[Fact]
@ -85,7 +85,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
Thumb = thumb,
Orientation = Orientation.Vertical,
Minimum = 100,
Maximum = 300,
Maximum = 200,
Value = 150,
ViewportSize = 50,
Width = 12,
@ -94,11 +94,11 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
Assert.Equal(new Rect(0, 18, 12, 25), thumb.Bounds);
Assert.Equal(new Rect(0, 33, 12, 34), thumb.Bounds);
}
[Fact]
public void Thumb_Should_Fill_Track_When_Minimum_Equals_Maximum()
public void Thumb_Should_Have_Zero_Width_When_Minimum_Equals_Maximum()
{
var thumb = new Thumb
{
@ -117,7 +117,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
Assert.Equal(new Rect(0, 0, 100, 12), thumb.Bounds);
Assert.Equal(new Rect(0, 0, 0, 12), thumb.Bounds);
}
[Fact]

9
tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs

@ -177,6 +177,15 @@ namespace Avalonia.Layout.UnitTests
It.IsAny<IReadOnlyList<FormattedTextStyleSpan>>()))
.Returns(new FormattedTextMock("TEST"));
var streamGeometry = new Mock<IStreamGeometryImpl>();
streamGeometry.Setup(x =>
x.Open())
.Returns(new Mock<IStreamGeometryContextImpl>().Object);
renderInterface.Setup(x =>
x.CreateStreamGeometry())
.Returns(streamGeometry.Object);
var windowImpl = new Mock<IWindowImpl>();
Size clientSize = default(Size);

1
tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj

@ -19,7 +19,6 @@
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj" />
<ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />

18
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs

@ -10,8 +10,9 @@ using Avalonia.Data;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.Data;
using Moq;
using ReactiveUI;
using Xunit;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Avalonia.Markup.Xaml.UnitTests.Data
{
@ -350,14 +351,25 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
}
}
public class Source : ReactiveObject
public class Source : INotifyPropertyChanged
{
private string _foo;
public string Foo
{
get { return _foo; }
set { this.RaiseAndSetIfChanged(ref _foo, value); }
set
{
_foo = value;
RaisePropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
}

20
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Source.cs

@ -6,8 +6,9 @@ using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.Data;
using ReactiveUI;
using Xunit;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Avalonia.Markup.Xaml.UnitTests.Data
{
@ -24,15 +25,26 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
Assert.Equal(target.Text, "foo");
}
public class Source : ReactiveObject
public class Source : INotifyPropertyChanged
{
private string _foo;
public string Foo
{
get { return _foo; }
set { this.RaiseAndSetIfChanged(ref _foo, value); }
set
{
_foo = value;
RaisePropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
}
}

Loading…
Cancel
Save