Browse Source

Merge branch 'master' into dynamicresource-instead-of-styleresource

Conflicts:
	src/Avalonia.Themes.Default/ScrollBar.xaml
	src/Avalonia.Themes.Default/Slider.xaml
pull/1136/head
Steven Kirk 9 years ago
parent
commit
f19ec2248b
  1. 70
      src/Avalonia.Controls/Button.cs
  2. 24
      src/Avalonia.Controls/Primitives/RangeBase.cs
  3. 103
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  4. 4
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  5. 102
      src/Avalonia.Controls/Primitives/Track.cs
  6. 82
      src/Avalonia.Controls/RepeatButton.cs
  7. 4
      src/Avalonia.Controls/ScrollViewer.cs
  8. 55
      src/Avalonia.Controls/Slider.cs
  9. 1
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  10. 44
      src/Avalonia.Themes.Default/RepeatButton.xaml
  11. 158
      src/Avalonia.Themes.Default/ScrollBar.xaml
  12. 2
      src/Avalonia.Themes.Default/ScrollViewer.xaml
  13. 26
      src/Avalonia.Themes.Default/Slider.xaml
  14. 4
      src/Windows/Avalonia.Win32/WindowImpl.cs
  15. 5
      tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs
  16. 10
      tests/Avalonia.Controls.UnitTests/Primitives/TrackTests.cs
  17. 9
      tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs

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);
}

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="{DynamicResource 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="{DynamicResource 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="{DynamicResource 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="{DynamicResource 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="{DynamicResource 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="{DynamicResource 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="{DynamicResource 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

@ -517,7 +517,7 @@ namespace Avalonia.Win32
WindowsMouseDevice.Instance,
timestamp,
_owner,
ScreenToClient(DipFromLParam(lParam)),
PointToClient(PointFromLParam(lParam)),
new Vector(0, (ToInt32(wParam) >> 16) / wheelDelta), GetMouseModifiers(wParam));
break;
@ -526,7 +526,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);

Loading…
Cancel
Save