diff --git a/Branding/SVGs/MiddleMouseButton.svg b/Branding/SVGs/MiddleMouseButton.svg new file mode 100644 index 00000000..c48ff9e6 --- /dev/null +++ b/Branding/SVGs/MiddleMouseButton.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Wabbajack/Resources/ResourceLinks.cs b/Wabbajack/Resources/ResourceLinks.cs index e2a12a5d..f7b54b3d 100644 --- a/Wabbajack/Resources/ResourceLinks.cs +++ b/Wabbajack/Resources/ResourceLinks.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Windows; using System.Windows.Media.Imaging; @@ -18,5 +16,7 @@ namespace Wabbajack UIUtils.BitmapImageFromStream(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/MO2Button.png")).Stream)); public static Lazy VortexButton { get; } = new Lazy(() => UIUtils.BitmapImageFromStream(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/VortexButton.png")).Stream)); + public static Lazy MiddleMouseButton { get; } = new Lazy(() => + UIUtils.BitmapImageFromStream(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/middle_mouse_button.png")).Stream)); } } diff --git a/Wabbajack/Resources/middle_mouse_button.png b/Wabbajack/Resources/middle_mouse_button.png new file mode 100644 index 00000000..cb277a65 Binary files /dev/null and b/Wabbajack/Resources/middle_mouse_button.png differ diff --git a/Wabbajack/View Models/ManifestVM.cs b/Wabbajack/View Models/ManifestVM.cs index 80935f71..19293e04 100644 --- a/Wabbajack/View Models/ManifestVM.cs +++ b/Wabbajack/View Models/ManifestVM.cs @@ -1,4 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; using Wabbajack.Common; using Wabbajack.Lib; @@ -16,9 +21,34 @@ namespace Wabbajack public IEnumerable Archives => Manifest.Archives; + [Reactive] + public string SearchTerm { get; set; } + + private readonly ObservableAsPropertyHelper> _searchResults; + public IEnumerable SearchResults => _searchResults.Value; + public ManifestVM(Manifest manifest) { Manifest = manifest; + + _searchResults = + this.WhenAnyValue(x => x.SearchTerm) + .Throttle(TimeSpan.FromMilliseconds(800)) + .Select(term => term?.Trim()) + .DistinctUntilChanged() + .Select(term => + { + if (string.IsNullOrWhiteSpace(term)) + return Archives; + + return Archives.Where(x => + { + if (term.StartsWith("hash:")) + return x.Hash.StartsWith(term.Replace("hash:", "")); + return x.Name.StartsWith(term); + }); + }) + .ToGuiProperty(this, nameof(SearchResults), Archives); } } } diff --git a/Wabbajack/Views/ManifestView.xaml b/Wabbajack/Views/ManifestView.xaml index e2c9860d..900cf364 100644 --- a/Wabbajack/Views/ManifestView.xaml +++ b/Wabbajack/Views/ManifestView.xaml @@ -7,7 +7,8 @@ xmlns:reactiveUi="http://reactiveui.net" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - + + - + - + - - + + - - - + + + + + @@ -37,7 +42,7 @@ - + @@ -46,15 +51,17 @@ - - + + Link - + + + diff --git a/Wabbajack/Views/ManifestView.xaml.cs b/Wabbajack/Views/ManifestView.xaml.cs index 22ac66ac..c96b4de9 100644 --- a/Wabbajack/Views/ManifestView.xaml.cs +++ b/Wabbajack/Views/ManifestView.xaml.cs @@ -1,9 +1,15 @@ using System; using System.Diagnostics; using System.Reactive.Disposables; +using System.Windows; +using System.Windows.Controls; using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; using System.Windows.Navigation; +using System.Windows.Shapes; using ReactiveUI; +using Wabbajack.Common; using Wabbajack.Lib; namespace Wabbajack @@ -30,12 +36,14 @@ namespace Wabbajack .DisposeWith(disposable); this.OneWayBind(ViewModel, x => x.Description, x => x.Description.Text) .DisposeWith(disposable); - this.OneWayBind(ViewModel, x => x.Archives, x => x.ModsList.ItemsSource) + this.OneWayBind(ViewModel, x => x.SearchResults, x => x.ModsList.ItemsSource) .DisposeWith(disposable); this.OneWayBind(ViewModel, x => x.InstallSize, x => x.InstallSize.Text) .DisposeWith(disposable); this.OneWayBind(ViewModel, x => x.DownloadSize, x => x.DownloadSize.Text) .DisposeWith(disposable); + this.Bind(ViewModel, x => x.SearchTerm, x => x.SearchBar.Text) + .DisposeWith(disposable); }); } @@ -55,5 +63,78 @@ namespace Wabbajack e.Handled = true; } + + //solution from https://stackoverflow.com/questions/5426232/how-can-i-make-wpf-scrollviewer-middle-click-scroll/5446307#5446307 + + private bool _isMoving; //False - ignore mouse movements and don't scroll + private bool _isDeferredMovingStarted; //True - Mouse down -> Mouse up without moving -> Move; False - Mouse down -> Move + private Point? _startPosition; + private const double Slowdown = 10; //smaller = faster + + private void ScrollViewer_MouseDown(object sender, MouseButtonEventArgs e) + { + if (_isMoving) + CancelScrolling(); + else if (e.ChangedButton == MouseButton.Middle && e.ButtonState == MouseButtonState.Pressed) + { + if (_isMoving) return; + + _isMoving = true; + _startPosition = e.GetPosition(sender as IInputElement); + _isDeferredMovingStarted = true; + + AddScrollSign(e.GetPosition(TopLayer).X, e.GetPosition(TopLayer).Y); + } + } + + private void ScrollViewer_MouseUp(object sender, MouseButtonEventArgs e) + { + if(e.ChangedButton == MouseButton.Middle && e.ButtonState == MouseButtonState.Released && _isDeferredMovingStarted != true) + CancelScrolling(); + } + + private void ScrollViewer_MouseMove(object sender, MouseEventArgs e) + { + if (!_isMoving || !(sender is ScrollViewer sv)) + return; + + _isDeferredMovingStarted = false; + + var currentPosition = e.GetPosition(sv); + if (_startPosition == null) + return; + + var offset = currentPosition - _startPosition.Value; + offset.Y /= Slowdown; + offset.X /= Slowdown; + + sv.ScrollToVerticalOffset(sv.VerticalOffset + offset.Y); + sv.ScrollToHorizontalOffset(sv.HorizontalOffset + offset.X); + } + + private void CancelScrolling() + { + _isMoving = false; + _startPosition = null; + _isDeferredMovingStarted = false; + RemoveScrollSign(); + } + + private void AddScrollSign(double x, double y) + { + const double size = 50.0; + var img = ResourceLinks.MiddleMouseButton.Value; + var icon = new Image {Source = img, Width = size, Height = size}; + //var icon = new Ellipse { Stroke = Brushes.Red, StrokeThickness = 2.0, Width = 20, Height = 20 }; + + TopLayer.Children.Add(icon); + Canvas.SetLeft(icon, x - size / 2); + Canvas.SetTop(icon, y - size / 2); + } + + private void RemoveScrollSign() + { + TopLayer.Children.Clear(); + } } } diff --git a/Wabbajack/Wabbajack.csproj b/Wabbajack/Wabbajack.csproj index 8d5ec5a8..d12d402c 100644 --- a/Wabbajack/Wabbajack.csproj +++ b/Wabbajack/Wabbajack.csproj @@ -41,6 +41,7 @@ + @@ -81,6 +82,7 @@ +