Browse Source

Merge branch 'master' into dont-build-core-tests

pull/1353/head
Steven Kirk 8 years ago
committed by GitHub
parent
commit
bdb585deef
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      samples/Previewer/MainWindow.xaml.cs
  2. 4
      samples/RemoteTest/Program.cs
  3. 12
      samples/VirtualizationTest/MainWindow.xaml
  4. 18
      samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs
  5. 2
      src/Avalonia.Base/AvaloniaObject.cs
  6. 4
      src/Avalonia.Base/PriorityBindingEntry.cs
  7. 2
      src/Avalonia.Base/Threading/AvaloniaScheduler.cs
  8. 4
      src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
  9. 4
      src/Avalonia.Base/Threading/Dispatcher.cs
  10. 4
      src/Avalonia.Base/Threading/IDispatcher.cs
  11. 12
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  12. 27
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  13. 63
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  14. 2
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  15. 4
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  16. 10
      src/Avalonia.Controls/Primitives/ILogicalScrollable.cs
  17. 1
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  18. 3
      src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs
  19. 2
      src/Avalonia.Controls/Remote/RemoteWidget.cs
  20. 10
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  21. 97
      src/Avalonia.Controls/ScrollViewer.cs
  22. 22
      src/Avalonia.Controls/TextBox.cs
  23. 2
      src/Avalonia.Controls/TreeView.cs
  24. 2
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  25. 2
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  26. 2
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  27. 2
      src/Avalonia.Layout/LayoutManager.cs
  28. 7
      src/Avalonia.Themes.Default/ListBox.xaml
  29. 5
      src/Avalonia.Themes.Default/ScrollViewer.xaml
  30. 3
      src/Avalonia.Themes.Default/TextBox.xaml
  31. 2
      src/Avalonia.Themes.Default/TreeView.xaml
  32. 2
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  33. 9
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  34. 12
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  35. 17
      src/Avalonia.Visuals/Visual.cs
  36. 51
      src/Avalonia.Visuals/VisualTree/BoundsTracker.cs
  37. 5
      src/Avalonia.Visuals/VisualTree/IVisual.cs
  38. 2
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  39. 2
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  40. 2
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  41. 2
      src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs
  42. 2
      src/OSX/Avalonia.MonoMac/TopLevelImpl.cs
  43. 2
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs
  44. 2
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  45. 11
      tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
  46. 46
      tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests_ILogicalScrollable.cs
  47. 30
      tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
  48. 2
      tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
  49. 12
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs
  50. 4
      tests/Avalonia.UnitTests/ImmediateDispatcher.cs
  51. 4
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
  52. 2
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs
  53. 2
      tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs
  54. 5
      tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs

4
samples/Previewer/MainWindow.xaml.cs

@ -39,7 +39,7 @@ namespace Previewer
}));
new BsonTcpTransport().Listen(IPAddress.Loopback, 25000, t =>
{
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
if (_connection != null)
{
@ -61,7 +61,7 @@ namespace Previewer
private void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj)
{
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
if (transport != _connection)
return;

4
samples/RemoteTest/Program.cs

@ -25,7 +25,7 @@ namespace RemoteTest
var transport = new BsonTcpTransport();
transport.Listen(IPAddress.Loopback, port, sc =>
{
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
new RemoteServer(sc).Content = new MainView();
});
@ -34,7 +34,7 @@ namespace RemoteTest
var cts = new CancellationTokenSource();
transport.Connect(IPAddress.Loopback, port).ContinueWith(t =>
{
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
var window = new Window()
{

12
samples/VirtualizationTest/MainWindow.xaml

@ -21,6 +21,12 @@
<TextBox Watermark="Viewport"
UseFloatingWatermark="True"
Text="{Binding #listBox.Scroll.Viewport, Mode=OneWay}"/>
<TextBlock>Horiz. ScrollBar</TextBlock>
<DropDown Items="{Binding ScrollBarVisibilities}"
SelectedItem="{Binding HorizontalScrollBarVisibility}"/>
<TextBlock>Vert. ScrollBar</TextBlock>
<DropDown Items="{Binding ScrollBarVisibilities}"
SelectedItem="{Binding VerticalScrollBarVisibility}"/>
<TextBox Watermark="Item to Create"
UseFloatingWatermark="True"
Text="{Binding NewItemString}"/>
@ -35,7 +41,9 @@
Items="{Binding Items}"
SelectedItems="{Binding SelectedItems}"
SelectionMode="Multiple"
VirtualizationMode="{Binding VirtualizationMode}">
VirtualizationMode="{Binding VirtualizationMode}"
ScrollViewer.HorizontalScrollBarVisibility="{Binding HorizontalScrollBarVisibility, Mode=TwoWay}"
ScrollViewer.VerticalScrollBarVisibility="{Binding VerticalScrollBarVisibility, Mode=TwoWay}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="{Binding Orientation}"/>
@ -43,7 +51,7 @@
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
<TextBlock Text="{Binding Header}" TextWrapping="Wrap"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

18
samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using ReactiveUI;
namespace VirtualizationTest.ViewModels
@ -17,6 +18,8 @@ namespace VirtualizationTest.ViewModels
private int _newItemIndex;
private IReactiveList<ItemViewModel> _items;
private string _prefix = "Item";
private ScrollBarVisibility _horizontalScrollBarVisibility = ScrollBarVisibility.Auto;
private ScrollBarVisibility _verticalScrollBarVisibility = ScrollBarVisibility.Auto;
private Orientation _orientation = Orientation.Vertical;
private ItemVirtualizationMode _virtualizationMode = ItemVirtualizationMode.Simple;
@ -64,6 +67,21 @@ namespace VirtualizationTest.ViewModels
public IEnumerable<Orientation> Orientations =>
Enum.GetValues(typeof(Orientation)).Cast<Orientation>();
public ScrollBarVisibility HorizontalScrollBarVisibility
{
get { return _horizontalScrollBarVisibility; }
set { this.RaiseAndSetIfChanged(ref _horizontalScrollBarVisibility, value); }
}
public ScrollBarVisibility VerticalScrollBarVisibility
{
get { return _verticalScrollBarVisibility; }
set { this.RaiseAndSetIfChanged(ref _verticalScrollBarVisibility, value); }
}
public IEnumerable<ScrollBarVisibility> ScrollBarVisibilities =>
Enum.GetValues(typeof(ScrollBarVisibility)).Cast<ScrollBarVisibility>();
public ItemVirtualizationMode VirtualizationMode
{
get { return _virtualizationMode; }

2
src/Avalonia.Base/AvaloniaObject.cs

@ -774,7 +774,7 @@ namespace Avalonia
}
else
{
Dispatcher.UIThread.InvokeAsync(Set);
Dispatcher.UIThread.Post(Set);
}
}

4
src/Avalonia.Base/PriorityBindingEntry.cs

@ -123,7 +123,7 @@ namespace Avalonia
}
else
{
Dispatcher.UIThread.InvokeAsync(Signal);
Dispatcher.UIThread.Post(Signal);
}
}
@ -135,7 +135,7 @@ namespace Avalonia
}
else
{
Dispatcher.UIThread.InvokeAsync(() => _owner.Completed(this));
Dispatcher.UIThread.Post(() => _owner.Completed(this));
}
}
}

2
src/Avalonia.Base/Threading/AvaloniaScheduler.cs

@ -33,7 +33,7 @@ namespace Avalonia.Threading
if (!Dispatcher.UIThread.CheckAccess())
{
var cancellation = new CancellationDisposable();
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
if (!cancellation.Token.IsCancellationRequested)
{

4
src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs

@ -36,7 +36,7 @@ namespace Avalonia.Threading
/// <inheritdoc/>
public override void Post(SendOrPostCallback d, object state)
{
Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send);
Dispatcher.UIThread.Post(() => d(state), DispatcherPriority.Send);
}
/// <inheritdoc/>
@ -45,7 +45,7 @@ namespace Avalonia.Threading
if (Dispatcher.UIThread.CheckAccess())
d(state);
else
Dispatcher.UIThread.InvokeTaskAsync(() => d(state), DispatcherPriority.Send).Wait();
Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait();
}
}
}

4
src/Avalonia.Base/Threading/Dispatcher.cs

@ -79,13 +79,13 @@ namespace Avalonia.Threading
public void RunJobs(DispatcherPriority minimumPriority) => _jobRunner.RunJobs(minimumPriority);
/// <inheritdoc/>
public Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
return _jobRunner?.InvokeAsync(action, priority);
}
/// <inheritdoc/>
public void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
_jobRunner?.Post(action, priority);
}

4
src/Avalonia.Base/Threading/IDispatcher.cs

@ -25,7 +25,7 @@ namespace Avalonia.Threading
/// <param name="action">The method.</param>
/// <param name="priority">The priority with which to invoke the method.</param>
/// <returns>A task that can be used to track the method's execution.</returns>
void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
/// <summary>
/// Post action that will be invoked on main thread
@ -34,6 +34,6 @@ namespace Avalonia.Threading
/// <param name="priority">The priority with which to invoke the method.</param>
// TODO: The naming of this method is confusing: the Async suffix usually means return a task.
// Remove this and rename InvokeTaskAsync as InvokeAsync. See #816.
Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
}
}

12
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -5,6 +5,7 @@ using System;
using System.Collections;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Utils;
using Avalonia.Input;
using Avalonia.Layout;
@ -97,6 +98,7 @@ namespace Avalonia.Controls.Presenters
/// <inheritdoc/>
public override Size MeasureOverride(Size availableSize)
{
var scrollable = (ILogicalScrollable)Owner;
var visualRoot = Owner.GetVisualRoot();
var maxAvailableSize = (visualRoot as WindowBase)?.PlatformImpl?.MaxClientSize
?? (visualRoot as TopLevel)?.ClientSize;
@ -115,7 +117,10 @@ namespace Avalonia.Controls.Presenters
}
}
availableSize = availableSize.WithWidth(double.PositiveInfinity);
if (scrollable.CanHorizontallyScroll)
{
availableSize = availableSize.WithWidth(double.PositiveInfinity);
}
}
else
{
@ -127,7 +132,10 @@ namespace Avalonia.Controls.Presenters
}
}
availableSize = availableSize.WithHeight(double.PositiveInfinity);
if (scrollable.CanVerticallyScroll)
{
availableSize = availableSize.WithHeight(double.PositiveInfinity);
}
}
Owner.Panel.Measure(availableSize);

27
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

@ -23,6 +23,8 @@ namespace Avalonia.Controls.Presenters
defaultValue: ItemVirtualizationMode.None);
private ItemVirtualizer _virtualizer;
private bool _canHorizontallyScroll;
private bool _canVerticallyScroll;
/// <summary>
/// Initializes static members of the <see cref="ItemsPresenter"/> class.
@ -46,6 +48,31 @@ namespace Avalonia.Controls.Presenters
set { SetValue(VirtualizationModeProperty, value); }
}
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
bool ILogicalScrollable.CanHorizontallyScroll
{
get { return _canHorizontallyScroll; }
set
{
_canHorizontallyScroll = value;
InvalidateMeasure();
}
}
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
bool ILogicalScrollable.CanVerticallyScroll
{
get { return _canVerticallyScroll; }
set
{
_canVerticallyScroll = value;
InvalidateMeasure();
}
}
/// <inheritdoc/>
bool ILogicalScrollable.IsLogicalScrollEnabled
{

63
src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs

@ -17,6 +17,24 @@ namespace Avalonia.Controls.Presenters
/// </summary>
public class ScrollContentPresenter : ContentPresenter, IPresenter, IScrollable
{
/// <summary>
/// Defines the <see cref="CanHorizontallyScroll"/> property.
/// </summary>
public static readonly DirectProperty<ScrollContentPresenter, bool> CanHorizontallyScrollProperty =
AvaloniaProperty.RegisterDirect<ScrollContentPresenter, bool>(
nameof(CanHorizontallyScroll),
o => o.CanHorizontallyScroll,
(o, v) => o.CanHorizontallyScroll = v);
/// <summary>
/// Defines the <see cref="CanVerticallyScroll"/> property.
/// </summary>
public static readonly DirectProperty<ScrollContentPresenter, bool> CanVerticallyScrollProperty =
AvaloniaProperty.RegisterDirect<ScrollContentPresenter, bool>(
nameof(CanVerticallyScroll),
o => o.CanVerticallyScroll,
(o, v) => o.CanVerticallyScroll = v);
/// <summary>
/// Defines the <see cref="Extent"/> property.
/// </summary>
@ -41,12 +59,8 @@ namespace Avalonia.Controls.Presenters
o => o.Viewport,
(o, v) => o.Viewport = v);
/// <summary>
/// Defines the <see cref="CanScrollHorizontally"/> property.
/// </summary>
public static readonly StyledProperty<bool> CanScrollHorizontallyProperty =
ScrollViewer.CanScrollHorizontallyProperty.AddOwner<ScrollContentPresenter>();
private bool _canHorizontallyScroll;
private bool _canVerticallyScroll;
private Size _extent;
private Size _measuredExtent;
private Vector _offset;
@ -73,6 +87,24 @@ namespace Avalonia.Controls.Presenters
this.GetObservable(ChildProperty).Subscribe(UpdateScrollableSubscription);
}
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
public bool CanHorizontallyScroll
{
get { return _canHorizontallyScroll; }
set { SetAndRaise(CanHorizontallyScrollProperty, ref _canHorizontallyScroll, value); }
}
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
public bool CanVerticallyScroll
{
get { return _canVerticallyScroll; }
set { SetAndRaise(CanVerticallyScrollProperty, ref _canVerticallyScroll, value); }
}
/// <summary>
/// Gets the extent of the scrollable content.
/// </summary>
@ -100,11 +132,6 @@ namespace Avalonia.Controls.Presenters
private set { SetAndRaise(ViewportProperty, ref _viewport, value); }
}
/// <summary>
/// Gets a value indicating whether the content can be scrolled horizontally.
/// </summary>
public bool CanScrollHorizontally => GetValue(CanScrollHorizontallyProperty);
/// <summary>
/// Attempts to bring a portion of the target visual into view by scrolling the content.
/// </summary>
@ -182,10 +209,15 @@ namespace Avalonia.Controls.Presenters
{
measureSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
if (!CanScrollHorizontally)
if (!CanHorizontallyScroll)
{
measureSize = measureSize.WithWidth(availableSize.Width);
}
if (!CanVerticallyScroll)
{
measureSize = measureSize.WithHeight(availableSize.Height);
}
}
child.Measure(measureSize);
@ -289,7 +321,12 @@ namespace Avalonia.Controls.Presenters
if (scrollable.IsLogicalScrollEnabled == true)
{
_logicalScrollSubscription = new CompositeDisposable(
this.GetObservable(OffsetProperty).Skip(1).Subscribe(x => scrollable.Offset = x),
this.GetObservable(CanHorizontallyScrollProperty)
.Subscribe(x => scrollable.CanHorizontallyScroll = x),
this.GetObservable(CanVerticallyScrollProperty)
.Subscribe(x => scrollable.CanVerticallyScroll = x),
this.GetObservable(OffsetProperty)
.Skip(1).Subscribe(x => scrollable.Offset = x),
Disposable.Create(() => scrollable.InvalidateScroll = null));
UpdateFromScrollable(scrollable);
}

2
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -191,7 +191,7 @@ namespace Avalonia.Controls.Presenters
// The measure is currently invalid so there's no point trying to bring the
// current char into view until a measure has been carried out as the scroll
// viewer extents may not be up-to-date.
Dispatcher.UIThread.InvokeAsync(
Dispatcher.UIThread.Post(
() =>
{
var rect = FormattedText.HitTestTextPosition(caretIndex);

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

@ -18,8 +18,6 @@ namespace Avalonia.Controls.Primitives
private static readonly AttachedProperty<AdornedElementInfo> s_adornedElementInfoProperty =
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, AdornedElementInfo>("AdornedElementInfo");
private readonly BoundsTracker _tracker = new BoundsTracker();
static AdornerLayer()
{
AdornedElementProperty.Changed.Subscribe(AdornedElementChanged);
@ -118,7 +116,7 @@ namespace Avalonia.Controls.Primitives
adorner.SetValue(s_adornedElementInfoProperty, info);
}
info.Subscription = _tracker.Track(adorned).Subscribe(x =>
info.Subscription = adorned.GetObservable(TransformedBoundsProperty).Subscribe(x =>
{
info.Bounds = x;
InvalidateArrange();

10
src/Avalonia.Controls/Primitives/ILogicalScrollable.cs

@ -19,6 +19,16 @@ namespace Avalonia.Controls.Primitives
/// </remarks>
public interface ILogicalScrollable : IScrollable
{
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
bool CanHorizontallyScroll { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
bool CanVerticallyScroll { get; set; }
/// <summary>
/// Gets a value indicating whether logical scrolling is enabled on the control.
/// </summary>

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

@ -99,6 +99,7 @@ namespace Avalonia.Controls.Primitives
case ScrollBarVisibility.Visible:
return true;
case ScrollBarVisibility.Disabled:
case ScrollBarVisibility.Hidden:
return false;

3
src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs

@ -5,8 +5,9 @@ namespace Avalonia.Controls.Primitives
{
public enum ScrollBarVisibility
{
Disabled,
Auto,
Visible,
Hidden,
Visible,
}
}

2
src/Avalonia.Controls/Remote/RemoteWidget.cs

@ -18,7 +18,7 @@ namespace Avalonia.Controls.Remote
public RemoteWidget(IAvaloniaRemoteTransportConnection connection)
{
_connection = connection;
_connection.OnMessage += (t, msg) => Dispatcher.UIThread.InvokeAsync(() => OnMessage(msg));
_connection.OnMessage += (t, msg) => Dispatcher.UIThread.Post(() => OnMessage(msg));
_connection.Send(new ClientSupportedPixelFormatsMessage
{
Formats = new[]

10
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@ -46,16 +46,16 @@ namespace Avalonia.Controls.Remote.Server
{
_lastReceivedFrame = lastFrame.SequenceId;
}
Dispatcher.UIThread.InvokeAsync(RenderIfNeeded);
Dispatcher.UIThread.Post(RenderIfNeeded);
}
if (obj is ClientSupportedPixelFormatsMessage supportedFormats)
{
lock (_lock)
_supportedFormats = supportedFormats.Formats;
Dispatcher.UIThread.InvokeAsync(RenderIfNeeded);
Dispatcher.UIThread.Post(RenderIfNeeded);
}
if (obj is MeasureViewportMessage measure)
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
var m = Measure(new Size(measure.Width, measure.Height));
_transport.Send(new MeasureViewportMessage
@ -69,7 +69,7 @@ namespace Avalonia.Controls.Remote.Server
lock (_lock)
{
if (_pendingAllocation == null)
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
ClientViewportAllocatedMessage allocation;
lock (_lock)
@ -168,7 +168,7 @@ namespace Avalonia.Controls.Remote.Server
public override void Invalidate(Rect rect)
{
_invalidated = true;
Dispatcher.UIThread.InvokeAsync(RenderIfNeeded);
Dispatcher.UIThread.Post(RenderIfNeeded);
}
public override IMouseDevice MouseDevice { get; } = new MouseDevice();

97
src/Avalonia.Controls/ScrollViewer.cs

@ -2,8 +2,6 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
@ -15,16 +13,34 @@ namespace Avalonia.Controls
public class ScrollViewer : ContentControl, IScrollable
{
/// <summary>
/// Defines the <see cref="CanScrollHorizontally"/> property.
/// Defines the <see cref="CanHorizontallyScroll"/> property.
/// </summary>
public static readonly StyledProperty<bool> CanScrollHorizontallyProperty =
AvaloniaProperty.Register<ScrollViewer, bool>(nameof(CanScrollHorizontally), true);
/// <remarks>
/// There is no public C# accessor for this property as it is intended to be bound to by a
/// <see cref="ScrollContentPresenter"/> in the control's template.
/// </remarks>
public static readonly DirectProperty<ScrollViewer, bool> CanHorizontallyScrollProperty =
AvaloniaProperty.RegisterDirect<ScrollViewer, bool>(
nameof(CanHorizontallyScroll),
o => o.CanHorizontallyScroll);
/// <summary>
/// Defines the <see cref="CanVerticallyScroll"/> property.
/// </summary>
/// <remarks>
/// There is no public C# accessor for this property as it is intended to be bound to by a
/// <see cref="ScrollContentPresenter"/> in the control's template.
/// </remarks>
public static readonly DirectProperty<ScrollViewer, bool> CanVerticallyScrollProperty =
AvaloniaProperty.RegisterDirect<ScrollViewer, bool>(
nameof(CanVerticallyScroll),
o => o.CanVerticallyScroll);
/// <summary>
/// Defines the <see cref="Extent"/> property.
/// </summary>
public static readonly DirectProperty<ScrollViewer, Size> ExtentProperty =
AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Extent),
AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Extent),
o => o.Extent,
(o, v) => o.Extent = v);
@ -41,7 +57,7 @@ namespace Avalonia.Controls
/// Defines the <see cref="Viewport"/> property.
/// </summary>
public static readonly DirectProperty<ScrollViewer, Size> ViewportProperty =
AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Viewport),
AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Viewport),
o => o.Viewport,
(o, v) => o.Viewport = v);
@ -85,14 +101,10 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="HorizontalScrollBarVisibility"/> property.
/// </summary>
/// <remarks>
/// There is no public C# accessor for this property as it is intended to be bound to by a
/// <see cref="ScrollContentPresenter"/> in the control's template.
/// </remarks>
public static readonly AttachedProperty<ScrollBarVisibility> HorizontalScrollBarVisibilityProperty =
AvaloniaProperty.RegisterAttached<ScrollViewer, Control, ScrollBarVisibility>(
nameof(HorizontalScrollBarVisibility),
ScrollBarVisibility.Auto);
ScrollBarVisibility.Hidden);
/// <summary>
/// Defines the VerticalScrollBarMaximum property.
@ -136,7 +148,7 @@ namespace Avalonia.Controls
/// </summary>
public static readonly AttachedProperty<ScrollBarVisibility> VerticalScrollBarVisibilityProperty =
AvaloniaProperty.RegisterAttached<ScrollViewer, Control, ScrollBarVisibility>(
nameof(VerticalScrollBarVisibility),
nameof(VerticalScrollBarVisibility),
ScrollBarVisibility.Auto);
private Size _extent;
@ -150,6 +162,8 @@ namespace Avalonia.Controls
{
AffectsValidation(ExtentProperty, OffsetProperty);
AffectsValidation(ViewportProperty, OffsetProperty);
HorizontalScrollBarVisibilityProperty.Changed.AddClassHandler<ScrollViewer>(x => x.ScrollBarVisibilityChanged);
VerticalScrollBarVisibilityProperty.Changed.AddClassHandler<ScrollViewer>(x => x.ScrollBarVisibilityChanged);
}
/// <summary>
@ -218,15 +232,6 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Gets a value indicating whether the content can be scrolled horizontally.
/// </summary>
public bool CanScrollHorizontally
{
get { return GetValue(CanScrollHorizontallyProperty); }
set { SetValue(CanScrollHorizontallyProperty, value); }
}
/// <summary>
/// Gets or sets the horizontal scrollbar visibility.
/// </summary>
@ -245,6 +250,22 @@ namespace Avalonia.Controls
set { SetValue(VerticalScrollBarVisibilityProperty, value); }
}
/// <summary>
/// Gets a value indicating whether the viewer can scroll horizontally.
/// </summary>
protected bool CanHorizontallyScroll
{
get { return HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled; }
}
/// <summary>
/// Gets a value indicating whether the viewer can scroll vertically.
/// </summary>
protected bool CanVerticallyScroll
{
get { return VerticalScrollBarVisibility != ScrollBarVisibility.Disabled; }
}
/// <summary>
/// Gets the maximum horizontal scrollbar value.
/// </summary>
@ -316,7 +337,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="control">The control to read the value from.</param>
/// <returns>The value of the property.</returns>
public ScrollBarVisibility GetHorizontalScrollBarVisibility(Control control)
public static ScrollBarVisibility GetHorizontalScrollBarVisibility(Control control)
{
return control.GetValue(HorizontalScrollBarVisibilityProperty);
}
@ -326,7 +347,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="control">The control to set the value on.</param>
/// <param name="value">The value of the property.</param>
public void SetHorizontalScrollBarVisibility(Control control, ScrollBarVisibility value)
public static void SetHorizontalScrollBarVisibility(Control control, ScrollBarVisibility value)
{
control.SetValue(HorizontalScrollBarVisibilityProperty, value);
}
@ -336,7 +357,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="control">The control to read the value from.</param>
/// <returns>The value of the property.</returns>
public ScrollBarVisibility GetVerticalScrollBarVisibility(Control control)
public static ScrollBarVisibility GetVerticalScrollBarVisibility(Control control)
{
return control.GetValue(VerticalScrollBarVisibilityProperty);
}
@ -346,7 +367,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="control">The control to set the value on.</param>
/// <param name="value">The value of the property.</param>
public void SetVerticalScrollBarVisibility(Control control, ScrollBarVisibility value)
public static void SetVerticalScrollBarVisibility(Control control, ScrollBarVisibility value)
{
control.SetValue(VerticalScrollBarVisibilityProperty, value);
}
@ -385,6 +406,30 @@ namespace Avalonia.Controls
}
}
private void ScrollBarVisibilityChanged(AvaloniaPropertyChangedEventArgs e)
{
var wasEnabled = !ScrollBarVisibility.Disabled.Equals(e.OldValue);
var isEnabled = !ScrollBarVisibility.Disabled.Equals(e.NewValue);
if (wasEnabled != isEnabled)
{
if (e.Property == HorizontalScrollBarVisibilityProperty)
{
RaisePropertyChanged(
CanHorizontallyScrollProperty,
wasEnabled,
isEnabled);
}
else if (e.Property == VerticalScrollBarVisibilityProperty)
{
RaisePropertyChanged(
CanVerticallyScrollProperty,
wasEnabled,
isEnabled);
}
}
}
private void CalculatedPropertiesChanged()
{
// Pass old values of 0 here because we don't have the old values at this point,

22
src/Avalonia.Controls/TextBox.cs

@ -8,7 +8,6 @@ using System.Linq;
using System.Reactive.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils;
using Avalonia.Input;
using Avalonia.Interactivity;
@ -26,9 +25,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty<bool> AcceptsTabProperty =
AvaloniaProperty.Register<TextBox, bool>(nameof(AcceptsTab));
public static readonly DirectProperty<TextBox, bool> CanScrollHorizontallyProperty =
AvaloniaProperty.RegisterDirect<TextBox, bool>(nameof(CanScrollHorizontally), o => o.CanScrollHorizontally);
public static readonly DirectProperty<TextBox, int> CaretIndexProperty =
AvaloniaProperty.RegisterDirect<TextBox, int>(
nameof(CaretIndex),
@ -92,7 +88,6 @@ namespace Avalonia.Controls
private int _caretIndex;
private int _selectionStart;
private int _selectionEnd;
private bool _canScrollHorizontally;
private TextPresenter _presenter;
private UndoRedoHelper<UndoRedoState> _undoRedoHelper;
private bool _ignoreTextChanges;
@ -106,12 +101,11 @@ namespace Avalonia.Controls
public TextBox()
{
this.GetObservable(TextWrappingProperty)
.Select(x => x == TextWrapping.NoWrap)
.Subscribe(x => CanScrollHorizontally = x);
var horizontalScrollBarVisibility = this.GetObservable(AcceptsReturnProperty)
.Select(x => x ? ScrollBarVisibility.Auto : ScrollBarVisibility.Hidden);
var horizontalScrollBarVisibility = Observable.CombineLatest(
this.GetObservable(AcceptsReturnProperty),
this.GetObservable(TextWrappingProperty),
(acceptsReturn, wrapping) => acceptsReturn && wrapping == TextWrapping.NoWrap ?
ScrollBarVisibility.Auto : ScrollBarVisibility.Disabled);
Bind(
ScrollViewer.HorizontalScrollBarVisibilityProperty,
@ -132,12 +126,6 @@ namespace Avalonia.Controls
set { SetValue(AcceptsTabProperty, value); }
}
public bool CanScrollHorizontally
{
get { return _canScrollHorizontally; }
private set { SetAndRaise(CanScrollHorizontallyProperty, ref _canScrollHorizontally, value); }
}
public int CaretIndex
{
get

2
src/Avalonia.Controls/TreeView.cs

@ -250,7 +250,7 @@ namespace Avalonia.Controls
if (AutoScrollToSelectedItem)
{
Dispatcher.UIThread.InvokeAsync(container.ContainerControl.BringIntoView);
Dispatcher.UIThread.Post(container.ContainerControl.BringIntoView);
}
break;

2
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -3,11 +3,9 @@
using System;
using System.Collections.Specialized;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.VisualTree;
namespace Avalonia.Controls
{

2
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -49,7 +49,7 @@ namespace Avalonia.DesignerSupport.Remote
// In previewer mode we completely ignore client-side viewport size
if (obj is ClientViewportAllocatedMessage alloc)
{
Dispatcher.UIThread.InvokeAsync(() => SetDpi(new Vector(alloc.DpiX, alloc.DpiY)));
Dispatcher.UIThread.Post(() => SetDpi(new Vector(alloc.DpiX, alloc.DpiY)));
return;
}
base.OnMessage(transport, obj);

2
src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs

@ -140,7 +140,7 @@ namespace Avalonia.DesignerSupport.Remote
};
}
private static void OnTransportMessage(IAvaloniaRemoteTransportConnection transport, object obj) => Dispatcher.UIThread.InvokeAsync(() =>
private static void OnTransportMessage(IAvaloniaRemoteTransportConnection transport, object obj) => Dispatcher.UIThread.Post(() =>
{
if (obj is ClientSupportedPixelFormatsMessage formats)
{

2
src/Avalonia.Layout/LayoutManager.cs

@ -203,7 +203,7 @@ namespace Avalonia.Layout
{
if (!_queued && !_running)
{
Dispatcher.UIThread.InvokeAsync(ExecuteLayoutPass, DispatcherPriority.Layout);
Dispatcher.UIThread.Post(ExecuteLayoutPass, DispatcherPriority.Layout);
_queued = true;
}
}

7
src/Avalonia.Themes.Default/ListBox.xaml

@ -3,11 +3,16 @@
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Template">
<ControlTemplate>
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Name="PART_ScrollViewer" Background="{TemplateBinding Background}">
<ScrollViewer Name="PART_ScrollViewer"
Background="{TemplateBinding Background}"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"

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

@ -6,12 +6,13 @@
<Grid ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">
<ScrollContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}"
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}"
Content="{TemplateBinding Content}"
Extent="{TemplateBinding Path=Extent, Mode=TwoWay}"
Margin="{TemplateBinding Padding}"
Offset="{TemplateBinding Path=Offset, Mode=TwoWay}"
Viewport="{TemplateBinding Path=Viewport, Mode=TwoWay}"
CanScrollHorizontally="{TemplateBinding CanScrollHorizontally}"/>
Viewport="{TemplateBinding Path=Viewport, Mode=TwoWay}"/>
<ScrollBar Name="horizontalScrollBar"
Orientation="Horizontal"
Maximum="{TemplateBinding HorizontalScrollBarMaximum}"

3
src/Avalonia.Themes.Default/TextBox.xaml

@ -36,8 +36,7 @@
<Path Data="M14,7 A7,7 0 0,0 0,7 M0,7 A7,7 0 1,0 14,7 M7,3l0,5 M7,9l0,2" Stroke="{DynamicResource ErrorBrush}" StrokeThickness="2"/>
</Canvas>
<ScrollViewer CanScrollHorizontally="{TemplateBinding CanScrollHorizontally}"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
<ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
<Panel>

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

@ -7,7 +7,7 @@
<ControlTemplate>
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer CanScrollHorizontally="True" Background="{TemplateBinding Background}">
<ScrollViewer Background="{TemplateBinding Background}">
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"

2
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -415,7 +415,7 @@ namespace Avalonia.Rendering
if (!_updateQueued && (_dirty == null || _dirty.Count > 0))
{
_updateQueued = true;
_dispatcher.InvokeAsync(UpdateScene, DispatcherPriority.Render);
_dispatcher.Post(UpdateScene, DispatcherPriority.Render);
}
Scene scene = null;

9
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@ -169,7 +169,7 @@ namespace Avalonia.Rendering
{
foreach (var e in visual.GetSelfAndVisualDescendants())
{
BoundsTracker.SetTransformedBounds((Visual)visual, null);
visual.TransformedBounds = null;
}
}
@ -197,7 +197,7 @@ namespace Avalonia.Rendering
if (filter?.Invoke(visual) != false)
{
bool containsPoint = BoundsTracker.GetTransformedBounds((Visual)visual)?.Contains(p) == true;
bool containsPoint = visual.TransformedBounds?.Contains(p) == true;
if ((containsPoint || !visual.ClipToBounds) && visual.VisualChildren.Count > 0)
{
@ -257,10 +257,7 @@ namespace Avalonia.Rendering
new TransformedBounds(bounds, new Rect(), context.CurrentContainerTransform);
#pragma warning restore 0618
if (visual is Visual)
{
BoundsTracker.SetTransformedBounds((Visual)visual, transformed);
}
visual.TransformedBounds = transformed;
foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance))
{

12
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@ -209,11 +209,8 @@ namespace Avalonia.Rendering.SceneGraph
}
catch { }
if (visual is Visual)
{
var transformed = new TransformedBounds(new Rect(visual.Bounds.Size), clip, node.Transform);
BoundsTracker.SetTransformedBounds((Visual)visual, transformed);
}
var transformed = new TransformedBounds(new Rect(visual.Bounds.Size), clip, node.Transform);
visual.TransformedBounds = transformed;
if (forceRecurse)
{
@ -279,10 +276,7 @@ namespace Avalonia.Rendering.SceneGraph
scene.Layers[node.LayerRoot].Dirty.Add(node.Bounds);
if (node.Visual is Visual v)
{
BoundsTracker.SetTransformedBounds(v, null);
}
node.Visual.TransformedBounds = null;
foreach (VisualNode child in node.Children)
{

17
src/Avalonia.Visuals/Visual.cs

@ -32,6 +32,11 @@ namespace Avalonia
public static readonly DirectProperty<Visual, Rect> BoundsProperty =
AvaloniaProperty.RegisterDirect<Visual, Rect>(nameof(Bounds), o => o.Bounds);
public static readonly DirectProperty<Visual, TransformedBounds?> TransformedBoundsProperty =
AvaloniaProperty.RegisterDirect<Visual, TransformedBounds?>(
nameof(TransformedBounds),
o => o.TransformedBounds);
/// <summary>
/// Defines the <see cref="ClipToBounds"/> property.
/// </summary>
@ -87,6 +92,7 @@ namespace Avalonia
AvaloniaProperty.Register<Visual, int>(nameof(ZIndex));
private Rect _bounds;
private TransformedBounds? _transformedBounds;
private IRenderRoot _visualRoot;
private IVisual _visualParent;
@ -135,6 +141,11 @@ namespace Avalonia
protected set { SetAndRaise(BoundsProperty, ref _bounds, value); }
}
/// <summary>
/// Gets the bounds of the control relative to the window, accounting for rendering transforms.
/// </summary>
public TransformedBounds? TransformedBounds => _transformedBounds;
/// <summary>
/// Gets a value indicating whether the control should be clipped to its bounds.
/// </summary>
@ -253,6 +264,12 @@ namespace Avalonia
/// Gets the root of the visual tree, if the control is attached to a visual tree.
/// </summary>
IRenderRoot IVisual.VisualRoot => VisualRoot;
TransformedBounds? IVisual.TransformedBounds
{
get { return _transformedBounds; }
set { SetAndRaise(TransformedBoundsProperty, ref _transformedBounds, value); }
}
/// <summary>
/// Invalidates the visual and queues a repaint.

51
src/Avalonia.Visuals/VisualTree/BoundsTracker.cs

@ -1,51 +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;
namespace Avalonia.VisualTree
{
/// <summary>
/// Tracks the bounds of a control.
/// </summary>
/// <remarks>
/// This class is used to track a controls's bounds for hit testing.
/// TODO: This shouldn't be implemented as an attached property: it would be more performant
/// to just store bounds in some sort of central repository.
/// </remarks>
public class BoundsTracker
{
/// <summary>
/// Defines the TransformedBounds attached property.
/// </summary>
private static AttachedProperty<TransformedBounds?> TransformedBoundsProperty =
AvaloniaProperty.RegisterAttached<BoundsTracker, Visual, TransformedBounds?>("TransformedBounds");
/// <summary>
/// Starts tracking the specified visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>An observable that returns the tracked bounds.</returns>
public IObservable<TransformedBounds?> Track(Visual visual)
{
return visual.GetObservable(TransformedBoundsProperty);
}
/// <summary>
/// Sets the transformed bounds of the visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <param name="value">The transformed bounds.</param>
internal static void SetTransformedBounds(Visual visual, TransformedBounds? value)
{
visual.SetValue(TransformedBoundsProperty, value);
}
/// <summary>
/// Gets the transformed bounds of the visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>The transformed bounds or null if the visual is not visible.</returns>
public static TransformedBounds? GetTransformedBounds(Visual visual) => visual.GetValue(TransformedBoundsProperty);
}
}

5
src/Avalonia.Visuals/VisualTree/IVisual.cs

@ -36,6 +36,11 @@ namespace Avalonia.VisualTree
/// </summary>
Rect Bounds { get; }
/// <summary>
/// Gets the bounds of the control relative to the window, accounting for rendering transforms.
/// </summary>
TransformedBounds? TransformedBounds { get; set; }
/// <summary>
/// Gets a value indicating whether the control should be clipped to its bounds.
/// </summary>

2
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@ -351,7 +351,7 @@ namespace Avalonia.Gtk3
void OnInput(RawInputEventArgs args)
{
Dispatcher.UIThread.InvokeAsync(() => Input?.Invoke(args), DispatcherPriority.Input);
Dispatcher.UIThread.Post(() => Input?.Invoke(args), DispatcherPriority.Input);
}
public Point PointToClient(Point point)

2
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@ -41,7 +41,7 @@ namespace Avalonia.LinuxFramebuffer
if(_renderQueued)
return;
_renderQueued = true;
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
Paint?.Invoke(new Rect(default(Point), ClientSize));
_renderQueued = false;

2
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@ -62,7 +62,7 @@ public static class LinuxFramebufferPlatformExtensions
public TokenClosable(CancellationToken token)
{
token.Register(() => Dispatcher.UIThread.InvokeAsync(() => Closed?.Invoke(this, new EventArgs())));
token.Register(() => Dispatcher.UIThread.Post(() => Closed?.Invoke(this, new EventArgs())));
}
}

2
src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs

@ -104,7 +104,7 @@ namespace Avalonia.Markup.Data.Plugins
protected override void SubscribeCore(IObserver<object> observer)
{
_subscription = Instance.GetWeakObservable(_property).Subscribe(observer);
_subscription = Instance?.GetWeakObservable(_property).Subscribe(observer);
}
}
}

2
src/OSX/Avalonia.MonoMac/TopLevelImpl.cs

@ -107,7 +107,7 @@ namespace Avalonia.MonoMac
if (_nonUiRedrawQueued)
return;
_nonUiRedrawQueued = true;
Dispatcher.UIThread.InvokeAsync(
Dispatcher.UIThread.Post(
() =>
{
lock (SyncRoot)

2
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs

@ -281,6 +281,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
var scroller = new TestScroller
{
CanHorizontallyScroll = false,
CanVerticallyScroll = true,
Content = result = new TestItemsPresenter(useContainers)
{
Items = items,

2
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs

@ -998,6 +998,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
var scroller = new TestScroller
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = result = new TestItemsPresenter(useContainers)
{
Items = items,

11
tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Layout;
using Xunit;
@ -118,6 +119,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
TestControl content;
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = content = new TestControl(),
};
@ -134,6 +137,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
Border content;
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = content = new Border
{
Width = 150,
@ -155,8 +160,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
var child = new TestControl();
var target = new ScrollContentPresenter
{
CanVerticallyScroll = true,
Content = child,
[ScrollContentPresenter.CanScrollHorizontallyProperty] = false,
};
target.UpdateChild();
@ -171,6 +176,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
var child = new TestControl();
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = child,
};
@ -246,6 +253,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
Border border;
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Width = 100,
Height = 100,
Content = new Decorator

46
tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests_ILogicalScrollable.cs

@ -13,7 +13,7 @@ namespace Avalonia.Controls.UnitTests
public class ScrollContentPresenterTests_ILogicalScrollable
{
[Fact]
public void Measure_Should_Pass_Unchanged_Bounds_To_IScrollable()
public void Measure_Should_Pass_Unchanged_Bounds_To_ILogicalScrollable()
{
var scrollable = new TestScrollable();
var target = new ScrollContentPresenter
@ -28,7 +28,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Arrange_Should_Not_Offset_IScrollable_Bounds()
public void Arrange_Should_Not_Offset_ILogicalScrollable_Bounds()
{
var scrollable = new TestScrollable
{
@ -50,7 +50,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Arrange_Should_Offset_IScrollable_Bounds_When_Logical_Scroll_Disabled()
public void Arrange_Should_Offset_ILogicalScrollable_Bounds_When_Logical_Scroll_Disabled()
{
var scrollable = new TestScrollable
{
@ -59,6 +59,8 @@ namespace Avalonia.Controls.UnitTests
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = scrollable,
Offset = new Vector(25, 25),
};
@ -71,7 +73,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Arrange_Should_Not_Set_Viewport_And_Extent_With_IScrollable()
public void Arrange_Should_Not_Set_Viewport_And_Extent_With_ILogicalScrollable()
{
var target = new ScrollContentPresenter
{
@ -122,7 +124,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Extent_Offset_And_Viewport_Should_Be_Read_From_IScrollable()
public void Extent_Offset_And_Viewport_Should_Be_Read_From_ILogicalScrollable()
{
var scrollable = new TestScrollable
{
@ -152,7 +154,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Offset_Should_Be_Written_To_IScrollable()
public void Offset_Should_Be_Written_To_ILogicalScrollable()
{
var scrollable = new TestScrollable
{
@ -172,7 +174,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Offset_Should_Not_Be_Written_To_IScrollable_After_Removal()
public void Offset_Should_Not_Be_Written_To_ILogicalScrollable_After_Removal()
{
var scrollable = new TestScrollable
{
@ -203,6 +205,8 @@ namespace Avalonia.Controls.UnitTests
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = scrollable,
};
@ -253,6 +257,8 @@ namespace Avalonia.Controls.UnitTests
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = logicalScrollable,
};
@ -286,12 +292,38 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Rect(0, 0, 100, 100), logicalScrollable.Bounds);
}
[Fact]
public void Should_Set_ILogicalScrolable_CanHorizontallyScroll()
{
var logicalScrollable = new TestScrollable();
var target = new ScrollContentPresenter { Content = logicalScrollable };
target.UpdateChild();
Assert.False(logicalScrollable.CanHorizontallyScroll);
target.CanHorizontallyScroll = true;
Assert.True(logicalScrollable.CanHorizontallyScroll);
}
[Fact]
public void Should_Set_ILogicalScrolable_CanVerticallyScroll()
{
var logicalScrollable = new TestScrollable();
var target = new ScrollContentPresenter { Content = logicalScrollable };
target.UpdateChild();
Assert.False(logicalScrollable.CanVerticallyScroll);
target.CanVerticallyScroll = true;
Assert.True(logicalScrollable.CanVerticallyScroll);
}
private class TestScrollable : Control, ILogicalScrollable
{
private Size _extent;
private Vector _offset;
private Size _viewport;
public bool CanHorizontallyScroll { get; set; }
public bool CanVerticallyScroll { get; set; }
public bool IsLogicalScrollEnabled { get; set; } = true;
public Size AvailableSize { get; private set; }
public Action InvalidateScroll { get; set; }

30
tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs

@ -1,6 +1,8 @@
// 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.Collections.Generic;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
@ -25,6 +27,32 @@ namespace Avalonia.Controls.UnitTests
Assert.IsType<TextBlock>(target.Presenter.Child);
}
[Fact]
public void CanHorizontallyScroll_Should_Track_HorizontalScrollBarVisibility()
{
var target = new ScrollViewer();
var values = new List<bool>();
target.GetObservable(ScrollViewer.CanHorizontallyScrollProperty).Subscribe(x => values.Add(x));
target.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
target.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
Assert.Equal(new[] { true, false, true }, values);
}
[Fact]
public void CanVerticallyScroll_Should_Track_VerticalScrollBarVisibility()
{
var target = new ScrollViewer();
var values = new List<bool>();
target.GetObservable(ScrollViewer.CanVerticallyScrollProperty).Subscribe(x => values.Add(x));
target.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
target.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
Assert.Equal(new[] { true, false, true }, values);
}
[Fact]
public void Offset_Should_Be_Coerced_To_Viewport()
{
@ -59,7 +87,7 @@ namespace Avalonia.Controls.UnitTests
[~~ScrollContentPresenter.ExtentProperty] = control[~~ScrollViewer.ExtentProperty],
[~~ScrollContentPresenter.OffsetProperty] = control[~~ScrollViewer.OffsetProperty],
[~~ScrollContentPresenter.ViewportProperty] = control[~~ScrollViewer.ViewportProperty],
[~ScrollContentPresenter.CanScrollHorizontallyProperty] = control[~ScrollViewer.CanScrollHorizontallyProperty],
[~ScrollContentPresenter.CanHorizontallyScrollProperty] = control[~ScrollViewer.CanHorizontallyScrollProperty],
},
new ScrollBar
{

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

@ -85,7 +85,7 @@ namespace Avalonia.Layout.UnitTests
{
Width = 200,
Height = 200,
CanScrollHorizontally = true,
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Content = textBlock = new TextBlock

12
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs

@ -103,20 +103,22 @@ namespace Avalonia.Markup.UnitTests.Data
{
using (var sync = UnitTestSynchronizationContext.Begin())
{
var data = new Class1();
var target = new ExpressionObserver(data, "Next^.Foo", true);
var data1 = new Class1();
var data2 = new Class2("foo");
var target = new ExpressionObserver(data1, "Next^.Foo", true);
var result = new List<object>();
var sub = target.Subscribe(x => result.Add(x));
data.Next.OnNext(new Class2("foo"));
data1.Next.OnNext(data2);
sync.ExecutePostedCallbacks();
Assert.Equal(new[] { new BindingNotification("foo") }, result);
sub.Dispose();
Assert.Equal(0, data.PropertyChangedSubscriptionCount);
Assert.Equal(0, data1.PropertyChangedSubscriptionCount);
GC.KeepAlive(data);
GC.KeepAlive(data1);
GC.KeepAlive(data2);
}
}

4
tests/Avalonia.UnitTests/ImmediateDispatcher.cs

@ -14,12 +14,12 @@ namespace Avalonia.UnitTests
return true;
}
public void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
action();
}
public Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
action();
return Task.FromResult<object>(null);

4
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

@ -24,13 +24,13 @@ namespace Avalonia.Visuals.UnitTests.Rendering
var root = new TestRoot();
var dispatcher = new Mock<IDispatcher>();
dispatcher.Setup(x => x.InvokeAsync(It.IsAny<Action>(), DispatcherPriority.Render))
dispatcher.Setup(x => x.Post(It.IsAny<Action>(), DispatcherPriority.Render))
.Callback<Action, DispatcherPriority>((a, p) => a());
CreateTargetAndRunFrame(root, dispatcher: dispatcher.Object);
dispatcher.Verify(x =>
x.InvokeAsync(
x.Post(
It.Is<Action>(a => a.Method.Name == "UpdateScene"),
DispatcherPriority.Render));
}

2
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs

@ -372,6 +372,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering
Margin = new Thickness(0, 100, 0, 0),
Child = scroll = new ScrollContentPresenter()
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = new StackPanel()
{
Children =

2
tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs

@ -357,6 +357,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering
Margin = new Thickness(0, 100, 0, 0),
Child = scroll = new ScrollContentPresenter()
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = new StackPanel()
{
Children =

5
tests/Avalonia.Visuals.UnitTests/VisualTree/BoundsTrackerTests.cs → tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs

@ -17,14 +17,13 @@ using Avalonia.Platform;
namespace Avalonia.Visuals.UnitTests.VisualTree
{
public class BoundsTrackerTests
public class TransformedBoundsTests
{
[Fact]
public void Should_Track_Bounds()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var target = new BoundsTracker();
var control = default(Rectangle);
var tree = new Decorator
{
@ -46,7 +45,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
tree.Arrange(new Rect(0, 0, 100, 100));
ImmediateRenderer.Render(tree, context);
var track = target.Track(control);
var track = control.GetObservable(Visual.TransformedBoundsProperty);
var results = new List<TransformedBounds?>();
track.Subscribe(results.Add);
Loading…
Cancel
Save