From 995ab902d7fb791f54427b42ddc2756192255147 Mon Sep 17 00:00:00 2001 From: Mathieu Guindon Date: Mon, 26 Apr 2021 01:49:14 -0400 Subject: [PATCH 01/14] prototype view --- Rubberduck.Core/UI/Controls/PeekControl.xaml | 71 +++++++++++++++++++ .../UI/Controls/PeekControl.xaml.cs | 28 ++++++++ .../UI/FindSymbol/FindSymbolControl.xaml | 2 +- 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 Rubberduck.Core/UI/Controls/PeekControl.xaml create mode 100644 Rubberduck.Core/UI/Controls/PeekControl.xaml.cs diff --git a/Rubberduck.Core/UI/Controls/PeekControl.xaml b/Rubberduck.Core/UI/Controls/PeekControl.xaml new file mode 100644 index 0000000000..a5673ed570 --- /dev/null +++ b/Rubberduck.Core/UI/Controls/PeekControl.xaml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Binding PeekBody + + + + + diff --git a/Rubberduck.Core/UI/Controls/PeekControl.xaml.cs b/Rubberduck.Core/UI/Controls/PeekControl.xaml.cs new file mode 100644 index 0000000000..89d1527f72 --- /dev/null +++ b/Rubberduck.Core/UI/Controls/PeekControl.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Rubberduck.UI.Controls +{ + /// + /// Interaction logic for PeekControl.xaml + /// + public partial class PeekControl : UserControl + { + public PeekControl() + { + InitializeComponent(); + } + } +} diff --git a/Rubberduck.Core/UI/FindSymbol/FindSymbolControl.xaml b/Rubberduck.Core/UI/FindSymbol/FindSymbolControl.xaml index 7234df4bb5..b35340cbc3 100644 --- a/Rubberduck.Core/UI/FindSymbol/FindSymbolControl.xaml +++ b/Rubberduck.Core/UI/FindSymbol/FindSymbolControl.xaml @@ -25,7 +25,7 @@ - Date: Tue, 27 Apr 2021 11:55:25 -0400 Subject: [PATCH 02/14] support dragging popup, added inputbinding (ESC), fixed bindings / introduced viewmodel. --- .../CodeExplorer/CodeExplorerViewModel.cs | 53 +++++++ .../UI/CodeExplorer/CodeExplorerControl.xaml | 14 ++ .../CommandBars/RubberduckCommandBar.cs | 2 +- Rubberduck.Core/UI/Controls/CloseButton.xaml | 16 +++ .../UI/Controls/CloseButton.xaml.cs | 28 ++++ .../UI/Controls/DependencyObjectExtensions.cs | 28 ++++ Rubberduck.Core/UI/Controls/PeekControl.xaml | 133 +++++++++++------- .../UI/Controls/PeekControl.xaml.cs | 40 +++++- .../UI/Controls/PeekDefinitionViewModel.cs | 115 +++++++++++++++ Rubberduck.Core/UI/Controls/SearchView.xaml | 16 +-- .../UI/FindSymbol/FindSymbolControl.xaml | 2 +- .../CodeExplorer/CodeExplorerUI.Designer.cs | 9 ++ .../CodeExplorer/CodeExplorerUI.cs.resx | 3 + .../CodeExplorer/CodeExplorerUI.de.resx | 3 + .../CodeExplorer/CodeExplorerUI.es.resx | 3 + .../CodeExplorer/CodeExplorerUI.fr.resx | 3 + .../CodeExplorer/CodeExplorerUI.resx | 3 + Rubberduck.Resources/RubberduckUI.Designer.cs | 18 +++ Rubberduck.Resources/RubberduckUI.fr.resx | 6 + Rubberduck.Resources/RubberduckUI.resx | 6 + 20 files changed, 433 insertions(+), 68 deletions(-) create mode 100644 Rubberduck.Core/UI/Controls/CloseButton.xaml create mode 100644 Rubberduck.Core/UI/Controls/CloseButton.xaml.cs create mode 100644 Rubberduck.Core/UI/Controls/DependencyObjectExtensions.cs create mode 100644 Rubberduck.Core/UI/Controls/PeekDefinitionViewModel.cs diff --git a/Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerViewModel.cs b/Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerViewModel.cs index 8270aa709f..280528a55e 100644 --- a/Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerViewModel.cs +++ b/Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerViewModel.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading.Tasks; using NLog; using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; @@ -19,6 +20,7 @@ using Rubberduck.Templates; using Rubberduck.UI.CodeExplorer.Commands.DragAndDrop; using Rubberduck.UI.Command.ComCommands; +using Rubberduck.UI.Controls; using Rubberduck.UI.UnitTesting.ComCommands; using Rubberduck.VBEditor.SafeComWrappers.Abstract; @@ -86,6 +88,8 @@ public CodeExplorerViewModel( RemoveCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteRemoveCommand, _externalRemoveCommand.CanExecute); } + PeekDefinitionCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecutePeekDefinitionCommand, CanExecutePeekDefinitionCommand); + ClosePeekDefinitionCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteClosePeekDefinitionCommand); OnPropertyChanged(nameof(Projects)); @@ -444,6 +448,55 @@ private void ExecuteRemoveCommand(object param) public CommandBase CollapseAllCommand { get; } public CommandBase ExpandAllCommand { get; } + public CommandBase PeekDefinitionCommand { get; } + public CommandBase ClosePeekDefinitionCommand { get; } + + private bool _showPeekDefinitionPopup; + public bool ShowPeekDefinitionPopup + { + get => _showPeekDefinitionPopup; + set + { + if (value != _showPeekDefinitionPopup) + { + _showPeekDefinitionPopup = value; + OnPropertyChanged(); + } + } + } + + private PeekDefinitionViewModel _peekDefinitionViewModel; + public PeekDefinitionViewModel PeekDefinitionViewModel + { + get => _peekDefinitionViewModel; + private set + { + if (_peekDefinitionViewModel != value) + { + _peekDefinitionViewModel = value; + OnPropertyChanged(); + } + } + } + + private void ExecutePeekDefinitionCommand(object param) + { + if (param is ICodeExplorerNode node) + { + PeekDefinitionViewModel = new PeekDefinitionViewModel(node, this.FindAllReferencesCommand, this.OpenCommand, this.ClosePeekDefinitionCommand); + } + else + { + PeekDefinitionViewModel = null; + } + + + ShowPeekDefinitionPopup = PeekDefinitionViewModel != null; + } + + private void ExecuteClosePeekDefinitionCommand(object param) => ShowPeekDefinitionPopup = false; + + private bool CanExecutePeekDefinitionCommand(object param) => SelectedItem is CodeExplorerMemberViewModel; public ICodeExplorerNode FindVisibleNodeForDeclaration(Declaration declaration) { diff --git a/Rubberduck.Core/UI/CodeExplorer/CodeExplorerControl.xaml b/Rubberduck.Core/UI/CodeExplorer/CodeExplorerControl.xaml index 10700b0d62..147d0634d1 100644 --- a/Rubberduck.Core/UI/CodeExplorer/CodeExplorerControl.xaml +++ b/Rubberduck.Core/UI/CodeExplorer/CodeExplorerControl.xaml @@ -59,6 +59,7 @@ + + @@ -563,6 +565,9 @@ Command="{Binding FindAllImplementationsCommand}" CommandParameter="{Binding SelectedItem, Mode=OneWay}" /> + @@ -625,6 +630,7 @@ + @@ -677,5 +683,13 @@ + + + + \ No newline at end of file diff --git a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs index 5861112924..f803df5899 100644 --- a/Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs +++ b/Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs @@ -79,7 +79,7 @@ private async void OnSelectionChange(object sender, DeclarationChangedEventArgs var source = _tokenSources.GetOrAdd(nameof(OnSelectionChange), k => new CancellationTokenSource()); var token = source.Token; - await Task.Run(async () => + Task.Run(async () => { var caption = await _formatter.FormatAsync(e.Declaration, e.MultipleControlsSelected, token); token.ThrowIfCancellationRequested(); diff --git a/Rubberduck.Core/UI/Controls/CloseButton.xaml b/Rubberduck.Core/UI/Controls/CloseButton.xaml new file mode 100644 index 0000000000..feb6d11a29 --- /dev/null +++ b/Rubberduck.Core/UI/Controls/CloseButton.xaml @@ -0,0 +1,16 @@ + diff --git a/Rubberduck.Core/UI/Controls/CloseButton.xaml.cs b/Rubberduck.Core/UI/Controls/CloseButton.xaml.cs new file mode 100644 index 0000000000..e425d551c7 --- /dev/null +++ b/Rubberduck.Core/UI/Controls/CloseButton.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Rubberduck.UI.Controls +{ + /// + /// Interaction logic for CloseButton.xaml + /// + public partial class CloseButton : Button + { + public CloseButton() + { + InitializeComponent(); + } + } +} diff --git a/Rubberduck.Core/UI/Controls/DependencyObjectExtensions.cs b/Rubberduck.Core/UI/Controls/DependencyObjectExtensions.cs new file mode 100644 index 0000000000..0fa34a20f0 --- /dev/null +++ b/Rubberduck.Core/UI/Controls/DependencyObjectExtensions.cs @@ -0,0 +1,28 @@ +using System.Windows; +using System.Windows.Media; + +namespace Rubberduck.UI.Controls +{ + public static class DependencyObjectExtensions + { + //from https://stackoverflow.com/a/41985834/1188513 + public static T GetAncestor(this DependencyObject child, int maxLevels = 10) where T : DependencyObject + { + var levels = 0; + var parent = child; + do + { + parent = VisualTreeHelper.GetParent(parent); + if (parent is T ancestor) + { + return ancestor; + } + + levels++; + } + while (parent != null && levels <= maxLevels); + return null; + } + + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Controls/PeekControl.xaml b/Rubberduck.Core/UI/Controls/PeekControl.xaml index a5673ed570..2cf866be8b 100644 --- a/Rubberduck.Core/UI/Controls/PeekControl.xaml +++ b/Rubberduck.Core/UI/Controls/PeekControl.xaml @@ -1,13 +1,18 @@  - + x:Name="PopupChildControl" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:Rubberduck.UI.Controls" + xmlns:converters="clr-namespace:Rubberduck.UI.Converters" + d:DataContext="{d:DesignInstance Type=local:PeekDefinitionViewModel}" + mc:Ignorable="d" + MinWidth="350" MaxWidth="550" + MinHeight="140" MaxHeight="450"> + + + @@ -19,53 +24,81 @@ + + - - - - - - - - - - + + + + + + + + + + + + + - - + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - + - - - Binding PeekBody - - - - diff --git a/Rubberduck.Core/UI/Controls/PeekControl.xaml.cs b/Rubberduck.Core/UI/Controls/PeekControl.xaml.cs index 89d1527f72..e92635a610 100644 --- a/Rubberduck.Core/UI/Controls/PeekControl.xaml.cs +++ b/Rubberduck.Core/UI/Controls/PeekControl.xaml.cs @@ -1,17 +1,18 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; -using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using Rubberduck.Parsing.Symbols; +using Rubberduck.UI.Command; namespace Rubberduck.UI.Controls { @@ -23,6 +24,41 @@ public partial class PeekControl : UserControl public PeekControl() { InitializeComponent(); + DragThumb.DragDelta += DragThumb_DragDelta; + } + + private void DragThumb_DragDelta(object sender, DragDeltaEventArgs e) + { + if (this.Parent is Popup parent) + { + parent.HorizontalOffset += e.HorizontalChange; + parent.VerticalOffset += e.VerticalChange; + e.Handled = true; + } + } + + protected override void OnMouseDown(MouseButtonEventArgs e) + { + try + { + if (e.ChangedButton == MouseButton.Left) + { + DragThumb.RaiseEvent(e); + e.Handled = true; + } + } + catch + { + // possible stack overflow exception with rapid double-button clickety-clicking + } + } + + private void LinkButton_OnClick(object sender, RoutedEventArgs e) + { + if (DataContext is PeekDefinitionViewModel vm) + { + vm.CloseCommand.Execute(null); + } } } } diff --git a/Rubberduck.Core/UI/Controls/PeekDefinitionViewModel.cs b/Rubberduck.Core/UI/Controls/PeekDefinitionViewModel.cs new file mode 100644 index 0000000000..ff170ac30e --- /dev/null +++ b/Rubberduck.Core/UI/Controls/PeekDefinitionViewModel.cs @@ -0,0 +1,115 @@ +using System.Linq; +using System.Windows.Input; +using Antlr4.Runtime; +using Rubberduck.Navigation.CodeExplorer; +using Rubberduck.Parsing; +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Symbols; + +namespace Rubberduck.UI.Controls +{ + public class PeekDefinitionViewModel : ViewModelBase + { + internal PeekDefinitionViewModel() + { + // default constructor for xaml designer + } + + public PeekDefinitionViewModel(ICodeExplorerNode node, + ICommand findReferencesCommand, + ICommand navigateCommand, + ICommand closeCommand) + :this(node.Declaration, findReferencesCommand, navigateCommand, closeCommand) + { + NavigateCommandParameter = node; + } + + public PeekDefinitionViewModel(Declaration target, + ICommand findReferencesCommand, + ICommand navigateCommand, + ICommand closeCommand) + { + Target = target; + FindReferencesCommand = findReferencesCommand; + NavigateCommand = navigateCommand; + CloseCommand = closeCommand; + } + + public int MaxLines => 1000; // todo make this configurable? + + private Declaration _target; + public Declaration Target + { + get => _target; + set + { + if (_target != value) + { + _target = value; + OnPropertyChanged(); + SetPeekBody(); + } + } + } + + private void SetPeekBody() + { + if (Target == null) + { + Body = string.Empty; + return; + } + + ParserRuleContext context; + if (Target.Context.Parent is VBAParser.ModuleBodyElementContext member) + { + context = member; + } + else if(Target.Context.TryGetAncestor(out var declaration)) + { + context = declaration; + } + else + { + context = Target.Context; + } + + var body = (context?.GetText() ?? string.Empty).Split('\n'); + var ellipsis = body?.Length > MaxLines ? "\n…" : string.Empty; + + Body = string.Join("\n", body.Take(MaxLines)) + ellipsis; + } + + private string _body; + public string Body + { + get => _body; + set + { + if (_body != value) + { + _body = value; + OnPropertyChanged(); + } + } + } + + public ICommand CloseCommand { get; } + public ICommand FindReferencesCommand { get; } + public ICommand NavigateCommand { get; } + + private object _navigateCommandParameter; + public object NavigateCommandParameter + { + get => _navigateCommandParameter; + set + { + if (_navigateCommandParameter != value) + { + _navigateCommandParameter = value; + OnPropertyChanged(); + } + } + } + } +} \ No newline at end of file diff --git a/Rubberduck.Core/UI/Controls/SearchView.xaml b/Rubberduck.Core/UI/Controls/SearchView.xaml index c9750f04d2..527ff60785 100644 --- a/Rubberduck.Core/UI/Controls/SearchView.xaml +++ b/Rubberduck.Core/UI/Controls/SearchView.xaml @@ -11,17 +11,6 @@ - @@ -30,9 +19,8 @@ -