|
|
|
@ -1,14 +1,18 @@ |
|
|
|
using System; |
|
|
|
using System.Linq; |
|
|
|
using System.Windows.Input; |
|
|
|
using Avalonia.Automation; |
|
|
|
using Avalonia.Automation.Peers; |
|
|
|
using Avalonia.Controls.Metadata; |
|
|
|
using Avalonia.Controls.Templates; |
|
|
|
using Avalonia.Data; |
|
|
|
using Avalonia.Interactivity; |
|
|
|
using Avalonia.Input; |
|
|
|
using Avalonia.Interactivity; |
|
|
|
using Avalonia.Layout; |
|
|
|
using Avalonia.Media; |
|
|
|
using Avalonia.Reactive; |
|
|
|
using Avalonia.Threading; |
|
|
|
using Avalonia.Controls.Metadata; |
|
|
|
using Avalonia.Automation.Peers; |
|
|
|
using Avalonia.VisualTree; |
|
|
|
using Avalonia.Reactive; |
|
|
|
using System.Linq; |
|
|
|
|
|
|
|
namespace Avalonia.Controls.Primitives |
|
|
|
{ |
|
|
|
@ -33,6 +37,7 @@ namespace Avalonia.Controls.Primitives |
|
|
|
[PseudoClasses(":vertical", ":horizontal")] |
|
|
|
public class ScrollBar : RangeBase |
|
|
|
{ |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Defines the <see cref="ViewportSize"/> property.
|
|
|
|
/// </summary>
|
|
|
|
@ -85,6 +90,7 @@ namespace Avalonia.Controls.Primitives |
|
|
|
private bool _isExpanded; |
|
|
|
private CompositeDisposable? _ownerSubscriptions; |
|
|
|
private ScrollViewer? _owner; |
|
|
|
private Point _lastRightClickPosition; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Initializes static members of the <see cref="ScrollBar"/> class.
|
|
|
|
@ -103,6 +109,7 @@ namespace Avalonia.Controls.Primitives |
|
|
|
public ScrollBar() |
|
|
|
{ |
|
|
|
UpdatePseudoClasses(Orientation); |
|
|
|
ContextRequested += OnContextRequested; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -506,5 +513,200 @@ namespace Avalonia.Controls.Primitives |
|
|
|
PseudoClasses.Set(":vertical", o == Orientation.Vertical); |
|
|
|
PseudoClasses.Set(":horizontal", o == Orientation.Horizontal); |
|
|
|
} |
|
|
|
|
|
|
|
protected override void OnPointerPressed(PointerPressedEventArgs e) |
|
|
|
{ |
|
|
|
base.OnPointerPressed(e); |
|
|
|
|
|
|
|
if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed) |
|
|
|
{ |
|
|
|
_lastRightClickPosition = e.GetPosition(this); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void OnContextRequested(object? sender, ContextRequestedEventArgs e) |
|
|
|
{ |
|
|
|
if (e.TryGetPosition(this, out var position)) |
|
|
|
{ |
|
|
|
_lastRightClickPosition = position; |
|
|
|
} |
|
|
|
|
|
|
|
if (Orientation == Orientation.Vertical) |
|
|
|
{ |
|
|
|
SetCurrentValue(ContextMenuProperty, CreateVerticalContextMenu()); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
var visualRoot = this.GetVisualRoot() as Visual; |
|
|
|
var flowDirection = visualRoot?.FlowDirection ?? FlowDirection.LeftToRight; |
|
|
|
SetCurrentValue(ContextMenuProperty, flowDirection == FlowDirection.LeftToRight |
|
|
|
? CreateHorizontalContextMenuLTR() |
|
|
|
: CreateHorizontalContextMenuRTL()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private ContextMenu CreateVerticalContextMenu() |
|
|
|
{ |
|
|
|
var contextMenu = new ContextMenu(); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Scroll Here", "ScrollHere", () => ScrollHereAction(this))); |
|
|
|
contextMenu.Items.Add(new Separator()); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Top", "Top", () => ScrollToTopAction(this))); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Bottom", "Bottom", () => ScrollToBottomAction(this))); |
|
|
|
contextMenu.Items.Add(new Separator()); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Page Up", "PageUp", () => PageUpAction(this))); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Page Down", "PageDown", () => PageDownAction(this))); |
|
|
|
contextMenu.Items.Add(new Separator()); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Scroll Up", "ScrollUp", () => LineUpAction(this))); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Scroll Down", "ScrollDown", () => LineDownAction(this))); |
|
|
|
return contextMenu; |
|
|
|
} |
|
|
|
|
|
|
|
private ContextMenu CreateHorizontalContextMenuLTR() |
|
|
|
{ |
|
|
|
var contextMenu = new ContextMenu(); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Scroll Here", "ScrollHere", () => ScrollHereAction(this))); |
|
|
|
contextMenu.Items.Add(new Separator()); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Left Edge", "LeftEdge", () => ScrollToLeftEndAction(this))); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Right Edge", "RightEdge", () => ScrollToRightEndAction(this))); |
|
|
|
contextMenu.Items.Add(new Separator()); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Page Left", "PageLeft", () => PageLeftAction(this))); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Page Right", "PageRight", () => PageRightAction(this))); |
|
|
|
contextMenu.Items.Add(new Separator()); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Scroll Left", "ScrollLeft", () => LineLeftAction(this))); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Scroll Right", "ScrollRight", () => LineRightAction(this))); |
|
|
|
return contextMenu; |
|
|
|
} |
|
|
|
|
|
|
|
private ContextMenu CreateHorizontalContextMenuRTL() |
|
|
|
{ |
|
|
|
var contextMenu = new ContextMenu(); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Scroll Here", "ScrollHere", () => ScrollHereAction(this))); |
|
|
|
contextMenu.Items.Add(new Separator()); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Right Edge", "RightEdge", () => ScrollToRightEndAction(this))); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Left Edge", "LeftEdge", () => ScrollToLeftEndAction(this))); |
|
|
|
contextMenu.Items.Add(new Separator()); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Page Right", "PageRight", () => PageRightAction(this))); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Page Left", "PageLeft", () => PageLeftAction(this))); |
|
|
|
contextMenu.Items.Add(new Separator()); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Scroll Right", "ScrollRight", () => LineRightAction(this))); |
|
|
|
contextMenu.Items.Add(CreateMenuItem("Scroll Left", "ScrollLeft", () => LineLeftAction(this))); |
|
|
|
return contextMenu; |
|
|
|
} |
|
|
|
|
|
|
|
private static MenuItem CreateMenuItem(string header, string automationId, Action action) |
|
|
|
{ |
|
|
|
var menuItem = new MenuItem |
|
|
|
{ |
|
|
|
Header = header, |
|
|
|
Command = new SimpleCommand(action) |
|
|
|
}; |
|
|
|
AutomationProperties.SetAutomationId(menuItem, automationId); |
|
|
|
return menuItem; |
|
|
|
} |
|
|
|
|
|
|
|
private static void ScrollHereAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
if (scrollBar.Track != null) |
|
|
|
{ |
|
|
|
var track = scrollBar.Track; |
|
|
|
var trackLength = scrollBar.Orientation == Orientation.Vertical ? track.Bounds.Height : track.Bounds.Width; |
|
|
|
var thumbLength = scrollBar.Orientation == Orientation.Vertical ? track.Thumb?.Bounds.Height ?? 0 : track.Thumb?.Bounds.Width ?? 0; |
|
|
|
var clickPosition = scrollBar.Orientation == Orientation.Vertical ? scrollBar._lastRightClickPosition.Y : scrollBar._lastRightClickPosition.X; |
|
|
|
|
|
|
|
if (trackLength > thumbLength) |
|
|
|
{ |
|
|
|
var ratio = clickPosition / trackLength; |
|
|
|
var range = scrollBar.Maximum - scrollBar.Minimum; |
|
|
|
var value = scrollBar.Minimum + (ratio * range); |
|
|
|
scrollBar.SetCurrentValue(ValueProperty, Math.Max(scrollBar.Minimum, Math.Min(scrollBar.Maximum, value))); |
|
|
|
scrollBar.OnScroll(ScrollEventType.ThumbTrack); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private static void ScrollToTopAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
scrollBar.SetCurrentValue(ValueProperty, scrollBar.Minimum); |
|
|
|
scrollBar.OnScroll(ScrollEventType.SmallDecrement); |
|
|
|
} |
|
|
|
|
|
|
|
private static void ScrollToBottomAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
scrollBar.SetCurrentValue(ValueProperty, scrollBar.Maximum); |
|
|
|
scrollBar.OnScroll(ScrollEventType.SmallIncrement); |
|
|
|
} |
|
|
|
|
|
|
|
private static void ScrollToLeftEndAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
scrollBar.SetCurrentValue(ValueProperty, scrollBar.Minimum); |
|
|
|
scrollBar.OnScroll(ScrollEventType.SmallDecrement); |
|
|
|
} |
|
|
|
|
|
|
|
private static void ScrollToRightEndAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
scrollBar.SetCurrentValue(ValueProperty, scrollBar.Maximum); |
|
|
|
scrollBar.OnScroll(ScrollEventType.SmallIncrement); |
|
|
|
} |
|
|
|
|
|
|
|
private static void PageUpAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
scrollBar.LargeDecrement(); |
|
|
|
} |
|
|
|
|
|
|
|
private static void PageDownAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
scrollBar.LargeIncrement(); |
|
|
|
} |
|
|
|
|
|
|
|
private static void PageLeftAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
scrollBar.LargeDecrement(); |
|
|
|
} |
|
|
|
|
|
|
|
private static void PageRightAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
scrollBar.LargeIncrement(); |
|
|
|
} |
|
|
|
|
|
|
|
private static void LineUpAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
scrollBar.SmallDecrement(); |
|
|
|
} |
|
|
|
|
|
|
|
private static void LineDownAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
scrollBar.SmallIncrement(); |
|
|
|
} |
|
|
|
|
|
|
|
private static void LineLeftAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
scrollBar.SmallDecrement(); |
|
|
|
} |
|
|
|
|
|
|
|
private static void LineRightAction(ScrollBar scrollBar) |
|
|
|
{ |
|
|
|
scrollBar.SmallIncrement(); |
|
|
|
} |
|
|
|
|
|
|
|
private Track? Track => this.GetTemplateChildren().OfType<Track>().FirstOrDefault(); |
|
|
|
|
|
|
|
private class SimpleCommand : ICommand |
|
|
|
{ |
|
|
|
private readonly Action _action; |
|
|
|
|
|
|
|
public SimpleCommand(Action action) |
|
|
|
{ |
|
|
|
_action = action; |
|
|
|
} |
|
|
|
|
|
|
|
public event EventHandler? CanExecuteChanged; |
|
|
|
|
|
|
|
public bool CanExecute(object? parameter) => true; |
|
|
|
|
|
|
|
public void Execute(object? parameter) |
|
|
|
{ |
|
|
|
_action(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|