Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WinUI-only ScrollBarHandler #1671

Merged
merged 2 commits into from
Jul 14, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Controls/src/Core/AppHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ public static class AppHostBuilderExtensions
{ typeof(Layout2.Layout), typeof(LayoutHandler) },
{ typeof(Picker), typeof(PickerHandler) },
{ typeof(ProgressBar), typeof(ProgressBarHandler) },
#if WINDOWS
{ typeof(ScrollView), typeof(ScrollViewHandler) },
#endif
{ typeof(SearchBar), typeof(SearchBarHandler) },
{ typeof(Slider), typeof(SliderHandler) },
{ typeof(Stepper), typeof(StepperHandler) },
Expand Down
33 changes: 33 additions & 0 deletions src/Controls/src/Core/HandlerImpl/ScrollView.Impl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Microsoft.Maui.Controls
{
public partial class ScrollView : IScrollView
{
IView IScrollView.Content => Content;

double IScrollView.HorizontalOffset
{
get => ScrollX;
set
{
if (ScrollX != value)
{
SetScrolledPosition(value, ScrollY);
}
}
}

double IScrollView.VerticalOffset
{
get => ScrollY;
set
{
if (ScrollY != value)
{
SetScrolledPosition(ScrollX, value);
}
}
}

void IScrollView.ScrollFinished() => SendScrollFinished();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ internal static InputScope ToInputScope(this ReturnType returnType)
return inputScope;
}

internal static UwpScrollBarVisibility ToUwpScrollBarVisibility(this Maui.Controls.ScrollBarVisibility visibility)
internal static UwpScrollBarVisibility ToUwpScrollBarVisibility(this ScrollBarVisibility visibility)
{
switch (visibility)
{
case Maui.Controls.ScrollBarVisibility.Always:
case ScrollBarVisibility.Always:
return UwpScrollBarVisibility.Visible;
case Maui.Controls.ScrollBarVisibility.Default:
case ScrollBarVisibility.Default:
return UwpScrollBarVisibility.Auto;
case Maui.Controls.ScrollBarVisibility.Never:
case ScrollBarVisibility.Never:
return UwpScrollBarVisibility.Hidden;
default:
return UwpScrollBarVisibility.Auto;
Expand Down
9 changes: 0 additions & 9 deletions src/Controls/src/Core/ScrollBarVisibility.cs

This file was deleted.

10 changes: 0 additions & 10 deletions src/Controls/src/Core/ScrollOrientation.cs

This file was deleted.

14 changes: 12 additions & 2 deletions src/Controls/src/Core/ScrollView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
namespace Microsoft.Maui.Controls
{
[ContentProperty(nameof(Content))]
public class ScrollView : Layout, IScrollViewController, IElementConfiguration<ScrollView>, IFlowDirectionController
public partial class ScrollView : Layout, IScrollViewController, IElementConfiguration<ScrollView>, IFlowDirectionController
{
#region IScrollViewController

Expand Down Expand Up @@ -274,7 +274,17 @@ protected override SizeRequest OnSizeRequest(double widthConstraint, double heig
break;
}

SizeRequest contentRequest = Content.Measure(widthConstraint, heightConstraint, MeasureFlags.IncludeMargins);
SizeRequest contentRequest;

if (Content is IFrameworkElement fe)
{
contentRequest = fe.Handler.GetDesiredSize(widthConstraint, heightConstraint);
}
else
{
contentRequest = Content.Measure(widthConstraint, heightConstraint, MeasureFlags.IncludeMargins);
}

contentRequest.Minimum = new Size(Math.Min(40, contentRequest.Minimum.Width), Math.Min(40, contentRequest.Minimum.Height));

return contentRequest;
Expand Down
50 changes: 50 additions & 0 deletions src/Core/src/Core/IScrollView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using Microsoft.Maui.Graphics;

namespace Microsoft.Maui
{
public interface IScrollView : IView
{
// TODO ezhart 2021-07-08 It might make sense for IPage and IScrollView to derive from (the not yet created) IContentView

/// <summary>
/// Gets the view that contains the content of the ScrollView.
/// </summary>
public IView Content { get; }

/// <summary>
/// Gets a value indicating the visibility rules for the horizontal scroll bar.
/// </summary>
ScrollBarVisibility HorizontalScrollBarVisibility { get; }

/// <summary>
/// Gets a value indicating the visibility rules for the vertical scroll bar.
/// </summary>
ScrollBarVisibility VerticalScrollBarVisibility { get; }

/// <summary>
/// Gets a value indicating the scroll orientation of the ScrollView.
/// </summary>
ScrollOrientation Orientation { get; }

/// <summary>
/// Gets the size of the scrollable content in the ScrollView.
/// </summary>
Size ContentSize { get; }

/// <summary>
/// Gets the current scroll position of the ScrollView along the horizontal axis.
/// </summary>
double HorizontalOffset { get; set; }

/// <summary>
/// Gets the current scroll position of the ScrollView along the vertical axis.
/// </summary>
double VerticalOffset { get; set; }

/// <summary>
/// Allows the native ScrollView to inform that cross-platform code that a scroll operation has completed.
/// </summary>
void ScrollFinished();
}
}
21 changes: 21 additions & 0 deletions src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
using AndroidX.Core.Widget;

namespace Microsoft.Maui.Handlers
{
public partial class ScrollViewHandler : ViewHandler<IScrollView, NestedScrollView>
{
protected override NestedScrollView CreateNativeView()
{
throw new NotImplementedException();
}

public static void MapContent(ScrollViewHandler handler, IScrollView scrollView) { }
public static void MapHorizontalScrollBarVisibility(ScrollViewHandler handler, IScrollView scrollView) { }
public static void MapVerticalScrollBarVisibility(ScrollViewHandler handler, IScrollView scrollView) { }
public static void MapOrientation(ScrollViewHandler handler, IScrollView scrollView) { }
public static void MapContentSize(ScrollViewHandler handler, IScrollView scrollView) { }
}
}
18 changes: 18 additions & 0 deletions src/Core/src/Handlers/ScrollView/ScrollViewHandler.Standard.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.Maui.Handlers
{
public partial class ScrollViewHandler : ViewHandler<IScrollView, object>
{
protected override object CreateNativeView() => throw new NotImplementedException();

public static void MapContent(IViewHandler handler, IScrollView scrollView) { }
public static void MapHorizontalScrollBarVisibility(IViewHandler handler, IScrollView scrollView) { }
public static void MapVerticalScrollBarVisibility(IViewHandler handler, IScrollView scrollView) { }
public static void MapOrientation(IViewHandler handler, IScrollView scrollView) { }
public static void MapContentSize(IViewHandler handler, IScrollView scrollView) { }
public static void MapRequestScrollTo(ScrollViewHandler handler, IScrollView scrollView, object? args) { }
}
}
65 changes: 65 additions & 0 deletions src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.UI.Xaml.Controls;

namespace Microsoft.Maui.Handlers
{
public partial class ScrollViewHandler : ViewHandler<IScrollView, ScrollViewer>
{
protected override ScrollViewer CreateNativeView()
{
return new ScrollViewer();
}

protected override void ConnectHandler(ScrollViewer nativeView)
{
base.ConnectHandler(nativeView);
nativeView.ViewChanged += ViewChanged;
}

protected override void DisconnectHandler(ScrollViewer nativeView)
{
base.DisconnectHandler(nativeView);
nativeView.ViewChanged -= ViewChanged;
}

public static void MapContent(ScrollViewHandler handler, IScrollView scrollView)
{
if (handler.MauiContext == null)
{
return;
}

handler.NativeView.Content = scrollView.Content.ToNative(handler.MauiContext);
}

public static void MapHorizontalScrollBarVisibility(ScrollViewHandler handler, IScrollView scrollView)
{
handler.NativeView?.UpdateScrollBarVisibility(scrollView.Orientation, scrollView.HorizontalScrollBarVisibility);
}

public static void MapVerticalScrollBarVisibility(ScrollViewHandler handler, IScrollView scrollView)
{
handler.NativeView.VerticalScrollBarVisibility = scrollView.VerticalScrollBarVisibility.ToWindowsScrollBarVisibility();
}

public static void MapOrientation(ScrollViewHandler handler, IScrollView scrollView)
{
handler.NativeView?.UpdateScrollBarVisibility(scrollView.Orientation, scrollView.HorizontalScrollBarVisibility);
}

void ViewChanged(object? sender, ScrollViewerViewChangedEventArgs e)
{
VirtualView.VerticalOffset = NativeView.VerticalOffset;
VirtualView.HorizontalOffset = NativeView.HorizontalOffset;

if (e.IsIntermediate == false)
{
VirtualView.ScrollFinished();
}
}
}
}
21 changes: 21 additions & 0 deletions src/Core/src/Handlers/ScrollView/ScrollViewHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#nullable enable
using System;

namespace Microsoft.Maui.Handlers
{
public partial class ScrollViewHandler
{
public static PropertyMapper<IScrollView, ScrollViewHandler> ScrollViewMapper = new(ViewMapper)
{
[nameof(IScrollView.Content)] = MapContent,
[nameof(IScrollView.HorizontalScrollBarVisibility)] = MapHorizontalScrollBarVisibility,
[nameof(IScrollView.VerticalScrollBarVisibility)] = MapVerticalScrollBarVisibility,
[nameof(IScrollView.Orientation)] = MapOrientation,
};

public ScrollViewHandler() : base(ScrollViewMapper)
{

}
}
}
25 changes: 25 additions & 0 deletions src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;
using UIKit;

namespace Microsoft.Maui.Handlers
{
public partial class ScrollViewHandler : ViewHandler<IScrollView, UIScrollView>
{
public ScrollViewHandler(PropertyMapper mapper) : base(mapper)
{
}

protected override UIScrollView CreateNativeView()
{
throw new NotImplementedException();
}

public static void MapContent(ScrollViewHandler handler, IScrollView scrollView) { }
public static void MapHorizontalScrollBarVisibility(ScrollViewHandler handler, IScrollView scrollView) { }
public static void MapVerticalScrollBarVisibility(ScrollViewHandler handler, IScrollView scrollView) { }
public static void MapOrientation(ScrollViewHandler handler, IScrollView scrollView) { }
public static void MapContentSize(ScrollViewHandler handler, IScrollView scrollView) { }
}
}
3 changes: 3 additions & 0 deletions src/Core/src/Hosting/AppHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ public static partial class AppHostBuilderExtensions
{ typeof(ILayout), typeof(LayoutHandler) },
{ typeof(IPicker), typeof(PickerHandler) },
{ typeof(IProgress), typeof(ProgressBarHandler) },
#if WINDOWS
{ typeof(IScrollView), typeof(ScrollViewHandler) },
#endif
{ typeof(ISearchBar), typeof(SearchBarHandler) },
{ typeof(IShapeView), typeof(ShapeViewHandler) },
{ typeof(ISlider), typeof(SliderHandler) },
Expand Down
53 changes: 53 additions & 0 deletions src/Core/src/Platform/Windows/ScrollViewerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Microsoft.UI.Xaml.Controls;
using WScrollBarVisibility = Microsoft.UI.Xaml.Controls.ScrollBarVisibility;

namespace Microsoft.Maui
{
public static class ScrollViewerExtensions
{
public static WScrollBarVisibility ToWindowsScrollBarVisibility(this ScrollBarVisibility visibility)
{
return visibility switch
{
ScrollBarVisibility.Always => WScrollBarVisibility.Visible,
ScrollBarVisibility.Default => WScrollBarVisibility.Auto,
ScrollBarVisibility.Never => WScrollBarVisibility.Hidden,
_ => WScrollBarVisibility.Auto,
};
}

public static void UpdateScrollBarVisibility(this ScrollViewer scrollViewer, ScrollOrientation orientation,
ScrollBarVisibility horizontalScrollBarVisibility)
{
if (horizontalScrollBarVisibility == ScrollBarVisibility.Default)
{
// If the user has not explicitly set a horizontal scroll bar visibility, then the orientation will
// determine what the horizontal scroll bar does

scrollViewer.HorizontalScrollBarVisibility = orientation switch
{
ScrollOrientation.Horizontal or ScrollOrientation.Both => WScrollBarVisibility.Auto,
_ => WScrollBarVisibility.Disabled,
};

return;
}

// If the user _has_ set a horizontal scroll bar visibility preference, then convert that preference to the native equivalent
// if the orientation allows for it

scrollViewer.HorizontalScrollBarVisibility = orientation switch
{
ScrollOrientation.Horizontal or ScrollOrientation.Both => horizontalScrollBarVisibility.ToWindowsScrollBarVisibility(),
_ => WScrollBarVisibility.Disabled,
};

// TODO ezhart 2021-07-08 RE: the note below - do we actually need to be accounting for Neither in the measurement code?
// Could we just disable the scroll bars entirely?
// Accounting for Neither in the xplat measurement code is a leftover from Forms, it may be easier to do that on the native side here.

// Note that the Orientation setting of "Neither" is covered by the measurement code (the size of the content is limited
// so that no scrolling is possible) and the xplat scrolling code (the ScrollTo methods are disabled when Orientation=Neither)
}
}
}
Loading