Merge pull request #4118 from Flow-Launcher/UI.WPF.Modern

Upgrade iNKORE.UI.WPF.Modern and refactor scroll logic
This commit is contained in:
Jack Ye 2025-12-16 22:54:53 +08:00 committed by GitHub
commit 20930617bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 13 additions and 264 deletions

View file

@ -138,7 +138,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="iNKORE.UI.WPF.Modern" Version="0.10.1" />
<PackageReference Include="iNKORE.UI.WPF.Modern" Version="0.10.2.1" />
<PackageReference Include="MdXaml" Version="1.27.0" />
<PackageReference Include="MdXaml.AnimatedGif" Version="1.27.0" />
<PackageReference Include="MdXaml.Html" Version="1.27.0" />

View file

@ -1,253 +0,0 @@
using iNKORE.UI.WPF.Modern.Controls;
using iNKORE.UI.WPF.Modern.Controls.Helpers;
using iNKORE.UI.WPF.Modern.Controls.Primitives;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Flow.Launcher.Resources.Controls
{
// TODO: Use IsScrollAnimationEnabled property in future: https://github.com/iNKORE-NET/UI.WPF.Modern/pull/347
public class CustomScrollViewerEx : ScrollViewer
{
private double LastVerticalLocation = 0;
private double LastHorizontalLocation = 0;
public CustomScrollViewerEx()
{
Loaded += OnLoaded;
var valueSource = DependencyPropertyHelper.GetValueSource(this, AutoPanningMode.IsEnabledProperty).BaseValueSource;
if (valueSource == BaseValueSource.Default)
{
AutoPanningMode.SetIsEnabled(this, true);
}
}
#region Orientation
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register(
nameof(Orientation),
typeof(Orientation),
typeof(CustomScrollViewerEx),
new PropertyMetadata(Orientation.Vertical));
public Orientation Orientation
{
get => (Orientation)GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
#endregion
#region AutoHideScrollBars
public static readonly DependencyProperty AutoHideScrollBarsProperty =
ScrollViewerHelper.AutoHideScrollBarsProperty
.AddOwner(
typeof(CustomScrollViewerEx),
new PropertyMetadata(true, OnAutoHideScrollBarsChanged));
public bool AutoHideScrollBars
{
get => (bool)GetValue(AutoHideScrollBarsProperty);
set => SetValue(AutoHideScrollBarsProperty, value);
}
private static void OnAutoHideScrollBarsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CustomScrollViewerEx sv)
{
sv.UpdateVisualState();
}
}
#endregion
private void OnLoaded(object sender, RoutedEventArgs e)
{
LastVerticalLocation = VerticalOffset;
LastHorizontalLocation = HorizontalOffset;
UpdateVisualState(false);
}
/// <inheritdoc/>
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
if (Style == null && ReadLocalValue(StyleProperty) == DependencyProperty.UnsetValue)
{
SetResourceReference(StyleProperty, typeof(ScrollViewer));
}
}
/// <inheritdoc/>
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
var Direction = GetDirection();
ScrollViewerBehavior.SetIsAnimating(this, true);
if (Direction == Orientation.Vertical)
{
if (ScrollableHeight > 0)
{
e.Handled = true;
}
var WheelChange = e.Delta * (ViewportHeight / 1.5) / ActualHeight;
var newOffset = LastVerticalLocation - WheelChange;
if (newOffset < 0)
{
newOffset = 0;
}
if (newOffset > ScrollableHeight)
{
newOffset = ScrollableHeight;
}
if (newOffset == LastVerticalLocation)
{
return;
}
ScrollToVerticalOffset(LastVerticalLocation);
ScrollToValue(newOffset, Direction);
LastVerticalLocation = newOffset;
}
else
{
if (ScrollableWidth > 0)
{
e.Handled = true;
}
var WheelChange = e.Delta * (ViewportWidth / 1.5) / ActualWidth;
var newOffset = LastHorizontalLocation - WheelChange;
if (newOffset < 0)
{
newOffset = 0;
}
if (newOffset > ScrollableWidth)
{
newOffset = ScrollableWidth;
}
if (newOffset == LastHorizontalLocation)
{
return;
}
ScrollToHorizontalOffset(LastHorizontalLocation);
ScrollToValue(newOffset, Direction);
LastHorizontalLocation = newOffset;
}
}
/// <inheritdoc/>
protected override void OnScrollChanged(ScrollChangedEventArgs e)
{
base.OnScrollChanged(e);
if (!ScrollViewerBehavior.GetIsAnimating(this))
{
LastVerticalLocation = VerticalOffset;
LastHorizontalLocation = HorizontalOffset;
}
}
private Orientation GetDirection()
{
var isShiftDown = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
if (Orientation == Orientation.Horizontal)
{
return isShiftDown ? Orientation.Vertical : Orientation.Horizontal;
}
else
{
return isShiftDown ? Orientation.Horizontal : Orientation.Vertical;
}
}
/// <summary>
/// Causes the <see cref="ScrollViewerEx"/> to load a new view into the viewport using the specified offsets and zoom factor.
/// </summary>
/// <param name="horizontalOffset">A value between 0 and <see cref="ScrollViewer.ScrollableWidth"/> that specifies the distance the content should be scrolled horizontally.</param>
/// <param name="verticalOffset">A value between 0 and <see cref="ScrollViewer.ScrollableHeight"/> that specifies the distance the content should be scrolled vertically.</param>
/// <param name="zoomFactor">A value between MinZoomFactor and MaxZoomFactor that specifies the required target ZoomFactor.</param>
/// <returns><see langword="true"/> if the view is changed; otherwise, <see langword="false"/>.</returns>
public bool ChangeView(double? horizontalOffset, double? verticalOffset, float? zoomFactor)
{
return ChangeView(horizontalOffset, verticalOffset, zoomFactor, false);
}
/// <summary>
/// Causes the <see cref="ScrollViewerEx"/> to load a new view into the viewport using the specified offsets and zoom factor, and optionally disables scrolling animation.
/// </summary>
/// <param name="horizontalOffset">A value between 0 and <see cref="ScrollViewer.ScrollableWidth"/> that specifies the distance the content should be scrolled horizontally.</param>
/// <param name="verticalOffset">A value between 0 and <see cref="ScrollViewer.ScrollableHeight"/> that specifies the distance the content should be scrolled vertically.</param>
/// <param name="zoomFactor">A value between MinZoomFactor and MaxZoomFactor that specifies the required target ZoomFactor.</param>
/// <param name="disableAnimation"><see langword="true"/> to disable zoom/pan animations while changing the view; otherwise, <see langword="false"/>. The default is false.</param>
/// <returns><see langword="true"/> if the view is changed; otherwise, <see langword="false"/>.</returns>
public bool ChangeView(double? horizontalOffset, double? verticalOffset, float? zoomFactor, bool disableAnimation)
{
if (disableAnimation)
{
if (horizontalOffset.HasValue)
{
ScrollToHorizontalOffset(horizontalOffset.Value);
}
if (verticalOffset.HasValue)
{
ScrollToVerticalOffset(verticalOffset.Value);
}
}
else
{
if (horizontalOffset.HasValue)
{
ScrollToHorizontalOffset(LastHorizontalLocation);
ScrollToValue(Math.Min(ScrollableWidth, horizontalOffset.Value), Orientation.Horizontal);
LastHorizontalLocation = horizontalOffset.Value;
}
if (verticalOffset.HasValue)
{
ScrollToVerticalOffset(LastVerticalLocation);
ScrollToValue(Math.Min(ScrollableHeight, verticalOffset.Value), Orientation.Vertical);
LastVerticalLocation = verticalOffset.Value;
}
}
return true;
}
private void ScrollToValue(double value, Orientation Direction)
{
if (Direction == Orientation.Vertical)
{
ScrollToVerticalOffset(value);
}
else
{
ScrollToHorizontalOffset(value);
}
ScrollViewerBehavior.SetIsAnimating(this, false);
}
private void UpdateVisualState(bool useTransitions = true)
{
var stateName = AutoHideScrollBars ? "NoIndicator" : "MouseIndicator";
VisualStateManager.GoToState(this, stateName, useTransitions);
}
}
}

View file

@ -252,12 +252,14 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<cc:CustomScrollViewerEx
<ui:ScrollViewerEx
x:Name="ListBoxScrollViewer"
Focusable="False"
IsScrollAnimationEnabled="False"
RewriteWheelChange="True"
Template="{DynamicResource ScrollViewerControlTemplate}">
<cc:CustomScrollViewerEx.Style>
<Style TargetType="cc:CustomScrollViewerEx">
<ui:ScrollViewerEx.Style>
<Style TargetType="ui:ScrollViewerEx">
<Style.Triggers>
<Trigger Property="ComputedVerticalScrollBarVisibility" Value="Visible">
<Setter Property="Margin" Value="0 0 0 0" />
@ -269,9 +271,9 @@
</Trigger>
</Style.Triggers>
</Style>
</cc:CustomScrollViewerEx.Style>
</ui:ScrollViewerEx.Style>
<VirtualizingStackPanel IsItemsHost="True" />
</cc:CustomScrollViewerEx>
</ui:ScrollViewerEx>
</ControlTemplate>
</Setter.Value>
</Setter>

View file

@ -28,9 +28,9 @@
},
"iNKORE.UI.WPF.Modern": {
"type": "Direct",
"requested": "[0.10.1, )",
"resolved": "0.10.1",
"contentHash": "nRYmBosiL+42eUpLbHeqP7qJqtp5EpzuIMZTpvq4mFV33VB/JjkFg1y82gk50pjkXlAQWDvRyrfSAmPR5AM+3g==",
"requested": "[0.10.2.1, )",
"resolved": "0.10.2.1",
"contentHash": "nGwuuVul+TcLCTgPmaAZCc0fYFqUpCNZ8PiulVT3gZnsWt/AvxMZ0DSPpuyI/iRPc/NhFIg9lSIR7uaHWV0I/Q==",
"dependencies": {
"iNKORE.UI.WPF": "1.2.8"
}
@ -1619,7 +1619,7 @@
"FSharp.Core": "[9.0.303, )",
"Flow.Launcher.Infrastructure": "[1.0.0, )",
"Flow.Launcher.Localization": "[0.0.6, )",
"Flow.Launcher.Plugin": "[5.0.0, )",
"Flow.Launcher.Plugin": "[5.1.0, )",
"Meziantou.Framework.Win32.Jobs": "[3.4.5, )",
"Microsoft.IO.RecyclableMemoryStream": "[3.0.1, )",
"SemanticVersioning": "[3.0.0, )",
@ -1634,7 +1634,7 @@
"BitFaster.Caching": "[2.5.4, )",
"CommunityToolkit.Mvvm": "[8.4.0, )",
"Flow.Launcher.Localization": "[0.0.6, )",
"Flow.Launcher.Plugin": "[5.0.0, )",
"Flow.Launcher.Plugin": "[5.1.0, )",
"InputSimulator": "[1.0.4, )",
"MemoryPack": "[1.21.4, )",
"Microsoft.VisualStudio.Threading": "[17.14.15, )",