Browse Source

Implement auto hide and sync scrollviewer this scrollbar visibility.

pull/4119/head
Dariusz Komosinski 6 years ago
parent
commit
52606fa56d
  1. 93
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  2. 73
      src/Avalonia.Controls/ScrollViewer.cs
  3. 2
      src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml
  4. 12
      src/Avalonia.Themes.Fluent/ScrollBar.xaml
  5. 21
      src/Avalonia.Themes.Fluent/ScrollViewer.xaml

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

@ -41,11 +41,20 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<Orientation> OrientationProperty =
AvaloniaProperty.Register<ScrollBar, Orientation>(nameof(Orientation), Orientation.Vertical);
public static readonly StyledProperty<bool> AllowAutoHideProperty =
AvaloniaProperty.Register<ScrollBar, bool>(nameof(AllowAutoHide), true);
public static readonly DirectProperty<ScrollBar, bool> IsExpandedProperty =
AvaloniaProperty.RegisterDirect<ScrollBar, bool>(
nameof(IsExpanded),
o => o.IsExpanded);
private Button _lineUpButton;
private Button _lineDownButton;
private Button _pageUpButton;
private Button _pageDownButton;
private DispatcherTimer _collapseTimer;
private DispatcherTimer _timer;
private bool _isExpanded;
/// <summary>
/// Initializes static members of the <see cref="ScrollBar"/> class.
@ -64,6 +73,18 @@ namespace Avalonia.Controls.Primitives
UpdatePseudoClasses(Orientation);
}
public bool IsExpanded
{
get => _isExpanded;
private set => SetAndRaise(IsExpandedProperty, ref _isExpanded, value);
}
public bool AllowAutoHide
{
get { return GetValue(AllowAutoHideProperty); }
set { SetValue(AllowAutoHideProperty, value); }
}
/// <summary>
/// Gets or sets the amount of the scrollable content that is currently visible.
/// </summary>
@ -133,6 +154,10 @@ namespace Avalonia.Controls.Primitives
{
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<Orientation>());
}
else if (change.Property == AllowAutoHideProperty)
{
UpdateIsExpandedState();
}
else
{
if (change.Property == MinimumProperty ||
@ -149,16 +174,20 @@ namespace Avalonia.Controls.Primitives
{
base.OnPointerEnter(e);
ResetCollapseTimer();
Expand();
if (AllowAutoHide)
{
ExpandAfterDelay();
}
}
protected override void OnPointerLeave(PointerEventArgs e)
{
base.OnPointerLeave(e);
CollapseAfterDelay();
if (AllowAutoHide)
{
CollapseAfterDelay();
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
@ -188,8 +217,6 @@ namespace Avalonia.Controls.Primitives
_pageUpButton = e.NameScope.Find<Button>("PART_PageUpButton");
_pageDownButton = e.NameScope.Find<Button>("PART_PageDownButton");
if (_lineUpButton != null)
{
_lineUpButton.Click += LineUpClick;
@ -211,48 +238,66 @@ namespace Avalonia.Controls.Primitives
}
}
private void CollapseAfterDelay()
private void InvokeAfterDelay(Action handler, TimeSpan delay)
{
if (_collapseTimer != null)
if (_timer != null)
{
_collapseTimer.Stop();
_timer.Stop();
}
else
{
_collapseTimer = new DispatcherTimer(TimeSpan.FromSeconds(2), DispatcherPriority.Normal, OnCollapseTimerTick);
_timer = new DispatcherTimer(DispatcherPriority.Normal);
_timer.Tick += (sender, args) =>
{
var senderTimer = (DispatcherTimer)sender;
if (senderTimer.Tag is Action action)
{
action();
}
senderTimer.Stop();
};
}
_collapseTimer.Start();
_timer.Tag = handler;
_timer.Interval = delay;
_timer.Start();
}
private void ResetCollapseTimer(bool restart = false)
private void UpdateIsExpandedState()
{
if (_collapseTimer != null && _collapseTimer.IsEnabled)
if (!AllowAutoHide)
{
_collapseTimer.Stop();
_timer?.Stop();
if (restart)
{
_collapseTimer.Start();
}
IsExpanded = true;
}
else
{
IsExpanded = IsPointerOver;
}
}
private void OnCollapseTimerTick(object sender, EventArgs e)
private void CollapseAfterDelay()
{
ResetCollapseTimer();
InvokeAfterDelay(Collapse, TimeSpan.FromSeconds(2));
}
Collapse();
private void ExpandAfterDelay()
{
InvokeAfterDelay(Expand, TimeSpan.FromMilliseconds(400));
}
private void Collapse()
{
PseudoClasses.Set(":expanded", false);
IsExpanded = false;
}
private void Expand()
{
PseudoClasses.Set(":expanded", true);
IsExpanded = true;
}
private void LineUpClick(object sender, RoutedEventArgs e)

73
src/Avalonia.Controls/ScrollViewer.cs

@ -1,4 +1,5 @@
using System;
using System.Reactive.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
@ -158,6 +159,9 @@ namespace Avalonia.Controls
nameof(VerticalScrollBarViewportSize),
o => o.VerticalScrollBarViewportSize);
public static readonly DirectProperty<ScrollViewer, bool> IsExpandedProperty =
ScrollBar.IsExpandedProperty.AddOwner<ScrollViewer>(o => o.IsExpanded);
/// <summary>
/// Defines the <see cref="VerticalScrollBarVisibility"/> property.
/// </summary>
@ -166,6 +170,9 @@ namespace Avalonia.Controls
nameof(VerticalScrollBarVisibility),
ScrollBarVisibility.Auto);
public static readonly StyledProperty<bool> AllowAutoHideProperty =
ScrollBar.AllowAutoHideProperty.AddOwner<ScrollViewer>();
/// <summary>
/// Defines the <see cref="ScrollChanged"/> event.
/// </summary>
@ -186,6 +193,8 @@ namespace Avalonia.Controls
private Size _oldViewport;
private Size _largeChange;
private Size _smallChange = new Size(DefaultSmallChange, DefaultSmallChange);
private bool _isExpanded;
private IDisposable _scrollBarExpandSubscription;
/// <summary>
/// Initializes static members of the <see cref="ScrollViewer"/> class.
@ -213,6 +222,18 @@ namespace Avalonia.Controls
remove => RemoveHandler(ScrollChangedEvent, value);
}
public bool IsExpanded
{
get => _isExpanded;
private set => SetAndRaise(ScrollBar.IsExpandedProperty, ref _isExpanded, value);
}
public bool AllowAutoHide
{
get { return GetValue(AllowAutoHideProperty); }
set { SetValue(AllowAutoHideProperty, value); }
}
/// <summary>
/// Gets the extent of the scrollable content.
/// </summary>
@ -630,6 +651,58 @@ namespace Avalonia.Controls
RaiseEvent(e);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_scrollBarExpandSubscription?.Dispose();
var horizontalScrollBar = e.NameScope.Find<ScrollBar>("PART_HorizontalScrollBar");
var verticalScrollBar = e.NameScope.Find<ScrollBar>("PART_VerticalScrollBar");
IObservable<bool> horizontalExpanded = null, verticalExpanded = null;
if (horizontalScrollBar != null)
{
horizontalExpanded = horizontalScrollBar
.GetObservable(ScrollBar.IsExpandedProperty);
}
if (verticalScrollBar != null)
{
verticalExpanded = verticalScrollBar
.GetObservable(ScrollBar.IsExpandedProperty);
}
IObservable<bool> finalExpanded = null;
if (horizontalExpanded != null && verticalExpanded != null)
{
finalExpanded = horizontalExpanded.CombineLatest(verticalExpanded, (h, v) => h || v);
}
else
{
if (horizontalExpanded != null)
{
finalExpanded = horizontalExpanded;
}
else if (verticalExpanded != null)
{
finalExpanded = verticalExpanded;
}
}
if (finalExpanded != null)
{
_scrollBarExpandSubscription = finalExpanded.Subscribe(OnScrollBarExpandedChanged);
}
}
private void OnScrollBarExpandedChanged(bool isExpanded)
{
IsExpanded = isExpanded;
}
private void OnLayoutUpdated(object sender, EventArgs e) => RaiseScrollChanged();
private void RaiseScrollChanged()

2
src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml

@ -437,7 +437,7 @@
<StaticResource x:Key="ScrollBarPanningThumbBackgroundColor" ResourceKey="SystemChromeDisabledLowColor" />
<SolidColorBrush x:Key="ScrollBarThumbBackground" Color="{StaticResource ScrollBarThumbBackgroundColor}" />
<SolidColorBrush x:Key="ScrollBarPanningThumbBackground" Color="{StaticResource ScrollBarPanningThumbBackgroundColor}" />
<x:String x:Key="ScrollBarExpandDuration">00:00:00.1</x:String>
<x:TimeSpan x:Key="ScrollBarExpandDuration">00:00:00.1</x:TimeSpan>
<x:String x:Key="ScrollBarExpandBeginTime">00:00:00.40</x:String>
<x:String x:Key="ScrollBarContractBeginTime">00:00:02.00</x:String>
<x:String x:Key="ScrollBarContractDelay">00:00:02</x:String>

12
src/Avalonia.Themes.Fluent/ScrollBar.xaml

@ -127,7 +127,7 @@
</Setter>
</Style>
<Style Selector="ScrollBar:expanded /template/ Grid#Root">
<Style Selector="ScrollBar[IsExpanded=true] /template/ Grid#Root">
<Setter Property="Background" Value="{DynamicResource ScrollBarBackgroundPointerOver}" />
</Style>
@ -155,7 +155,7 @@
<Setter Property="RenderTransform" Value="{DynamicResource HorizontalSmallScrollThumbScaleTransform}" />
</Style>
<Style Selector="ScrollBar:expanded /template/ Thumb.thumb">
<Style Selector="ScrollBar[IsExpanded=true] /template/ Thumb.thumb">
<Setter Property="RenderTransform" Value="none" />
<Setter Property="Background" Value="{DynamicResource ScrollBarThumbBackgroundColor}" />
</Style>
@ -169,7 +169,7 @@
</Setter>
</Style>
<Style Selector="ScrollBar:expanded /template/ Thumb.thumb /template/ Border#ThumbVisual">
<Style Selector="ScrollBar[IsExpanded=true] /template/ Thumb.thumb /template/ Border#ThumbVisual">
<Setter Property="CornerRadius" Value="0" />
</Style>
@ -243,7 +243,7 @@
<Setter Property="Fill" Value="{DynamicResource ScrollBarButtonArrowForegroundDisabled}" />
</Style>
<Style Selector="ScrollBar:expanded /template/ RepeatButton.line">
<Style Selector="ScrollBar[IsExpanded=true] /template/ RepeatButton.line">
<Setter Property="Opacity" Value="1" />
</Style>
@ -277,7 +277,7 @@
</Setter>
</Style>
<Style Selector="ScrollBar:expanded /template/ Rectangle#TrackRect">
<Style Selector="ScrollBar[IsExpanded=true] /template/ Rectangle#TrackRect">
<Setter Property="Fill" Value="{DynamicResource ScrollBarTrackFillPointerOver}" />
<Setter Property="Stroke" Value="{DynamicResource ScrollBarTrackStrokePointerOver}" />
<Setter Property="Opacity" Value="1" />
@ -295,7 +295,7 @@
<Setter Property="Opacity" Value="0" />
</Style>
<Style Selector="ScrollBar:expanded /template/ RepeatButton.largeIncrease">
<Style Selector="ScrollBar[IsExpanded=true] /template/ RepeatButton.largeIncrease">
<Setter Property="Opacity" Value="1" />
</Style>

21
src/Avalonia.Themes.Fluent/ScrollViewer.xaml

@ -25,7 +25,8 @@
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" />
</ScrollContentPresenter.GestureRecognizers>
</ScrollContentPresenter>
<ScrollBar Name="horizontalScrollBar"
<ScrollBar Name="PART_HorizontalScrollBar"
AllowAutoHide="{TemplateBinding AllowAutoHide}"
Orientation="Horizontal"
LargeChange="{Binding LargeChange.Width, RelativeSource={RelativeSource TemplatedParent}}"
SmallChange="{Binding SmallChange.Width, RelativeSource={RelativeSource TemplatedParent}}"
@ -35,7 +36,8 @@
Visibility="{TemplateBinding HorizontalScrollBarVisibility}"
Grid.Row="1"
Focusable="False" />
<ScrollBar Name="verticalScrollBar"
<ScrollBar Name="PART_VerticalScrollBar"
AllowAutoHide="{TemplateBinding AllowAutoHide}"
Orientation="Vertical"
LargeChange="{Binding LargeChange.Height, RelativeSource={RelativeSource TemplatedParent}}"
SmallChange="{Binding SmallChange.Height, RelativeSource={RelativeSource TemplatedParent}}"
@ -45,11 +47,24 @@
Visibility="{TemplateBinding VerticalScrollBarVisibility}"
Grid.Column="1"
Focusable="False" />
<Panel x:Name="PART_ScrollBarsSeparator" Grid.Row="1" Grid.Column="1" Background="{DynamicResource ScrollViewerScrollBarsSeparatorBackground}" Opacity="0" />
<Panel x:Name="PART_ScrollBarsSeparator" Grid.Row="1" Grid.Column="1" Background="{DynamicResource ScrollViewerScrollBarsSeparatorBackground}" />
</Grid>
</ControlTemplate>
</Setter>
<Setter Property="AllowAutoHide" Value="False" />
</Style>
<Style Selector="ScrollViewer /template/ Panel#PART_ScrollBarsSeparator">
<Setter Property="Opacity" Value="0" />
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.1" />
</Transitions>
</Setter>
</Style>
<Style Selector="ScrollViewer[IsExpanded=true] /template/ Panel#PART_ScrollBarsSeparator">
<Setter Property="Opacity" Value="1" />
</Style>
</Styles>

Loading…
Cancel
Save