diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj
index f3c61470202..576bf6f2f13 100644
--- a/Flow.Launcher/Flow.Launcher.csproj
+++ b/Flow.Launcher/Flow.Launcher.csproj
@@ -138,7 +138,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Flow.Launcher/Resources/Controls/CustomScrollViewerEx.cs b/Flow.Launcher/Resources/Controls/CustomScrollViewerEx.cs
new file mode 100644
index 00000000000..78985108ce2
--- /dev/null
+++ b/Flow.Launcher/Resources/Controls/CustomScrollViewerEx.cs
@@ -0,0 +1,253 @@
+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);
+ }
+
+ ///
+ protected override void OnInitialized(EventArgs e)
+ {
+ base.OnInitialized(e);
+
+ if (Style == null && ReadLocalValue(StyleProperty) == DependencyProperty.UnsetValue)
+ {
+ SetResourceReference(StyleProperty, typeof(ScrollViewer));
+ }
+ }
+
+ ///
+ 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;
+ }
+ }
+
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Causes the to load a new view into the viewport using the specified offsets and zoom factor.
+ ///
+ /// A value between 0 and that specifies the distance the content should be scrolled horizontally.
+ /// A value between 0 and that specifies the distance the content should be scrolled vertically.
+ /// A value between MinZoomFactor and MaxZoomFactor that specifies the required target ZoomFactor.
+ /// if the view is changed; otherwise, .
+ public bool ChangeView(double? horizontalOffset, double? verticalOffset, float? zoomFactor)
+ {
+ return ChangeView(horizontalOffset, verticalOffset, zoomFactor, false);
+ }
+
+ ///
+ /// Causes the to load a new view into the viewport using the specified offsets and zoom factor, and optionally disables scrolling animation.
+ ///
+ /// A value between 0 and that specifies the distance the content should be scrolled horizontally.
+ /// A value between 0 and that specifies the distance the content should be scrolled vertically.
+ /// A value between MinZoomFactor and MaxZoomFactor that specifies the required target ZoomFactor.
+ /// to disable zoom/pan animations while changing the view; otherwise, . The default is false.
+ /// if the view is changed; otherwise, .
+ 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);
+ }
+ }
+}
diff --git a/Flow.Launcher/Themes/Base.xaml b/Flow.Launcher/Themes/Base.xaml
index c3831e68f8a..c5b45890bf4 100644
--- a/Flow.Launcher/Themes/Base.xaml
+++ b/Flow.Launcher/Themes/Base.xaml
@@ -252,14 +252,12 @@
-
-
-
-
+
-
+
diff --git a/Flow.Launcher/packages.lock.json b/Flow.Launcher/packages.lock.json
index 8c3a16e5e66..b4b929d1965 100644
--- a/Flow.Launcher/packages.lock.json
+++ b/Flow.Launcher/packages.lock.json
@@ -28,9 +28,9 @@
},
"iNKORE.UI.WPF.Modern": {
"type": "Direct",
- "requested": "[0.10.2.1, )",
- "resolved": "0.10.2.1",
- "contentHash": "nGwuuVul+TcLCTgPmaAZCc0fYFqUpCNZ8PiulVT3gZnsWt/AvxMZ0DSPpuyI/iRPc/NhFIg9lSIR7uaHWV0I/Q==",
+ "requested": "[0.10.1, )",
+ "resolved": "0.10.1",
+ "contentHash": "nRYmBosiL+42eUpLbHeqP7qJqtp5EpzuIMZTpvq4mFV33VB/JjkFg1y82gk50pjkXlAQWDvRyrfSAmPR5AM+3g==",
"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.1.0, )",
+ "Flow.Launcher.Plugin": "[5.0.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.1.0, )",
+ "Flow.Launcher.Plugin": "[5.0.0, )",
"InputSimulator": "[1.0.4, )",
"MemoryPack": "[1.21.4, )",
"Microsoft.VisualStudio.Threading": "[17.14.15, )",