Browse Source

Fix initial logical scrollable state (#13066)

* Add failing test for #13064

* Remove Content binding from ScrollViewer template.

Since #10803, the `ScrollContentPresenter.Content` binding is managed internally, not via a binding in the template.

* Assign owner before doing subscriptions.

Fixes #13064.
revert-13066-fixes/13064-logical-scrollable
Steven Kirk 3 years ago
committed by GitHub
parent
commit
bf1be0cd2f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  2. 1
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  3. 1
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
  4. 161
      tests/Avalonia.Controls.UnitTests/ScrollViewerTests_ILogicalScrollable.cs

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

@ -323,6 +323,7 @@ namespace Avalonia.Controls.Presenters
}
_ownerSubscriptions?.Dispose();
_owner = owner;
var subscriptionDisposables = new IDisposable?[]
{
@ -333,7 +334,6 @@ namespace Avalonia.Controls.Presenters
IfUnset(ContentProperty, p => Bind(p, owner.GetBindingObservable(ContentProperty), Data.BindingPriority.Template)),
}.Where(d => d != null).Cast<IDisposable>().ToArray();
_owner = owner;
_ownerSubscriptions = new CompositeDisposable(subscriptionDisposables);
static bool NotDisabled(ScrollBarVisibility v) => v != ScrollBarVisibility.Disabled;

1
tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs

@ -1182,7 +1182,6 @@ namespace Avalonia.Controls.UnitTests
new ScrollContentPresenter
{
Name = "PART_ContentPresenter",
[~ScrollContentPresenter.ContentProperty] = parent.GetObservable(ScrollViewer.ContentProperty).ToBinding(),
}.RegisterInNameScope(scope),
new ScrollBar
{

1
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

@ -1328,7 +1328,6 @@ namespace Avalonia.Controls.UnitTests.Primitives
new ScrollContentPresenter
{
Name = "PART_ContentPresenter",
[~ScrollContentPresenter.ContentProperty] = parent.GetObservable(ScrollViewer.ContentProperty).ToBinding(),
}.RegisterInNameScope(scope),
new ScrollBar
{

161
tests/Avalonia.Controls.UnitTests/ScrollViewerTests_ILogicalScrollable.cs

@ -0,0 +1,161 @@
using System;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Xunit;
#nullable enable
namespace Avalonia.Controls.UnitTests;
public class ScrollViewerTests_ILogicalScrollable
{
[Fact]
public void Extent_Offset_And_Viewport_Should_Be_Read_From_ILogicalScrollable()
{
var scrollable = new TestScrollable
{
Extent = new Size(100, 100),
Offset = new Vector(50, 50),
Viewport = new Size(25, 25),
};
var target = CreateTarget(scrollable);
Assert.Equal(scrollable.Extent, target.Extent);
Assert.Equal(scrollable.Offset, target.Offset);
Assert.Equal(scrollable.Viewport, target.Viewport);
scrollable.Extent = new Size(200, 200);
scrollable.Offset = new Vector(100, 100);
scrollable.Viewport = new Size(50, 50);
Assert.Equal(scrollable.Extent, target.Extent);
Assert.Equal(scrollable.Offset, target.Offset);
Assert.Equal(scrollable.Viewport, target.Viewport);
}
private static ScrollViewer CreateTarget(object? content)
{
var result = new ScrollViewer
{
Content = content,
};
var root = new TestRoot
{
Resources =
{
{ typeof(ScrollViewer), CreateScrollViewerTheme() },
},
Child = result,
};
root.LayoutManager.ExecuteInitialLayoutPass();
return result;
}
private static ControlTheme CreateScrollViewerTheme()
{
return new ControlTheme(typeof(ScrollViewer))
{
Setters =
{
new Setter(TreeView.TemplateProperty, CreateScrollViewerTemplate()),
},
};
}
private static FuncControlTemplate CreateScrollViewerTemplate()
{
return new FuncControlTemplate<ScrollViewer>((parent, scope) =>
new Panel
{
Children =
{
new ScrollContentPresenter
{
Name = "PART_ContentPresenter",
}.RegisterInNameScope(scope),
}
});
}
private class TestScrollable : Control, ILogicalScrollable
{
private Size _extent;
private Vector _offset;
private Size _viewport;
private EventHandler? _scrollInvalidated;
public bool CanHorizontallyScroll { get; set; }
public bool CanVerticallyScroll { get; set; }
public bool IsLogicalScrollEnabled { get; set; } = true;
public Size AvailableSize { get; private set; }
public bool HasScrollInvalidatedSubscriber => _scrollInvalidated != null;
public event EventHandler? ScrollInvalidated
{
add => _scrollInvalidated += value;
remove => _scrollInvalidated -= value;
}
public Size Extent
{
get => _extent;
set
{
_extent = value;
_scrollInvalidated?.Invoke(this, EventArgs.Empty);
}
}
public Vector Offset
{
get => _offset;
set
{
_offset = value;
_scrollInvalidated?.Invoke(this, EventArgs.Empty);
}
}
public Size Viewport
{
get => _viewport;
set
{
_viewport = value;
_scrollInvalidated?.Invoke(this, EventArgs.Empty);
}
}
public Size ScrollSize => new(double.PositiveInfinity, 1);
public Size PageScrollSize => new(double.PositiveInfinity, Viewport.Height);
public bool BringIntoView(Control target, Rect targetRect)
{
throw new NotImplementedException();
}
public void RaiseScrollInvalidated(EventArgs e)
{
_scrollInvalidated?.Invoke(this, e);
}
protected override Size MeasureOverride(Size availableSize)
{
AvailableSize = availableSize;
return new Size(150, 150);
}
public Control? GetControlInDirection(NavigationDirection direction, Control? from)
{
throw new NotImplementedException();
}
}
}
Loading…
Cancel
Save