From 70b33f5963bd410f89d6c6686253982d1941937a Mon Sep 17 00:00:00 2001 From: Eusebiu Marcu Date: Sun, 14 Nov 2010 00:26:26 +0200 Subject: [PATCH] move tooltips to Debugger.AddIn move pin to Debugger.AddIn --- .../Icons.16x16.Refresh.png | Bin 858 -> 1063 bytes .../Debugger.AddIn/Debugger.AddIn.addin | 9 +- .../Debugger.AddIn/Debugger.AddIn.csproj | 29 +- .../Debugger.AddIn/Pads/LocalVarPad.cs | 2 +- .../Debugger.AddIn/Pads/WatchPadModel.cs | 2 +- .../Debugger.AddIn/Service/WindowsDebugger.cs | 28 +- .../Debugger.AddIn/Tooltips/DebuggerPopup.cs | 101 +++++ .../Tooltips/DebuggerTooltipControl.xaml | 229 ++++++++++ .../Tooltips/DebuggerTooltipControl.xaml.cs | 406 ++++++++++++++++++ .../Tooltips/LazyItemsControl.cs | 113 +++++ .../Tooltips/PinCloseControl.xaml | 29 ++ .../Tooltips/PinCloseControl.xaml.cs | 74 ++++ .../Tooltips/PinControlsDictionary.xaml | 337 +++++++++++++++ .../Tooltips/PinDebuggerControl.xaml | 258 +++++++++++ .../Tooltips/PinDebuggerControl.xaml.cs | 378 ++++++++++++++++ .../Debugger.AddIn/Tooltips/PinLayer.cs | 226 ++++++++++ .../Debugger.AddIn/Tooltips/PinningBinding.cs | 121 ++++++ .../Tooltips/VirtualizingIEnumerable.cs | 53 +++ .../Tooltips/VisualizerPicker.cs | 48 +++ .../Tooltips/VisualizerPicker.xaml | 135 ++++++ .../Debugger.AddIn/Tooltips/magnifier.png | Bin 0 -> 564 bytes .../TreeModel/Adapters/TreeViewVarNode.cs | 2 +- .../TreeModel/ArrayRangeNode.cs | 6 +- .../TreeModel/ChildNodesOfObject.cs | 12 +- .../TreeModel/ExpressionNode.cs | 126 +++--- .../Debugger.AddIn/TreeModel/SavedTreeNode.cs | 30 ++ .../TreeModel/StackFrameNode.cs | 12 +- .../Debugger.AddIn/TreeModel/TreeNode.cs | 21 +- .../Project/ICSharpCode.SharpDevelop.addin | 7 - .../Project/ICSharpCode.SharpDevelop.csproj | 28 +- .../Src/Bookmarks/BookmarkConverter.cs | 2 +- .../Debugger/Tooltips/DebuggerPopup.cs | 2 +- .../Tooltips/DebuggerTooltipControl.xaml.cs | 67 +-- .../Debugger/Tooltips/IPinDebuggerControl.cs | 20 + .../Services/Debugger/Tooltips/ITreeNode.cs | 4 +- .../Services/Debugger/Tooltips/PinBookmark.cs | 8 +- .../Debugger/Tooltips/PinDebuggerControl.xaml | 37 +- .../Tooltips/PinDebuggerControl.xaml.cs | 252 ++++++----- .../Resources/BitmapResources.resources | Bin 603877 -> 660624 bytes 39 files changed, 2960 insertions(+), 254 deletions(-) create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerPopup.cs create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/LazyItemsControl.cs create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinCloseControl.xaml create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinCloseControl.xaml.cs create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinControlsDictionary.xaml create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinDebuggerControl.xaml create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinDebuggerControl.xaml.cs create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinLayer.cs create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinningBinding.cs create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/VirtualizingIEnumerable.cs create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/VisualizerPicker.cs create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/VisualizerPicker.xaml create mode 100644 src/AddIns/Debugger/Debugger.AddIn/Tooltips/magnifier.png create mode 100644 src/AddIns/Debugger/Debugger.AddIn/TreeModel/SavedTreeNode.cs create mode 100644 src/Main/Base/Project/Src/Services/Debugger/Tooltips/IPinDebuggerControl.cs diff --git a/data/resources/image/BitmapResources/BitmapResources-data/Icons.16x16.Refresh.png b/data/resources/image/BitmapResources/BitmapResources-data/Icons.16x16.Refresh.png index 0dbe747a52ecdf7c3597ec418f25207965b4d6df..51c3c653ceb796cb62caa34657cc23e52a97d74f 100644 GIT binary patch delta 1043 zcmV+u1nm3T2B!#+BYy+#Nklrd8IQB*`hMGzQho$a(}XR-CSbyeX*`Q%S20%H{sZL8b7n2U``)2*GY+s%??*|MAM zX6tTv_u9S5larjB^PKlQ=Yg2R8F0LO7*fTPkS?)6MuQcy<$pHdD4f7k&A}P>eO{*h~84WWVS&i1G+-BPruhrq? zw#>>)JH7GJ>r0>*3_-2I7llb$rG0*Yw32)Sx*IP~q^$Ra^70!Qin(aH; za%wTJ)$SE&=6x`Dc=N9a!Q85(eX8*#4xh%}?iZvAHu05_AsmRbh#Rj%L5 zt^{wDcZYflbiNqA+6@AYo8T+$=c;=An?nnaBU8&eXn%V31tM2oAkA2K?h5%eKvu&v zq}5IZFB+DwD#te~FBn!TiaP_4-DCr{#02McE|3|6M2XH}b>PV}_jl3g7e64?NZ8dr zu@1m(rXWj7PIS&ZKG8vLC)OF)62yH$D7|zSavM#rR4@6+!5(mJ{$+izizY(PQLpdD}RPp0E+eVP^flhol)9Eg=&XLsBww}s#y`Y$x@lqFvZA`PBH|oGiLQj*sHs> z>eY;`dX+3rwsP}ToXsGWKwkla{rOsO1qaGEFp#c&v;{rBc3m!H29hz#!_?fAn#i*<9ll1^U~ zA3Jf+m3*xy>O(`gtH!wY38%&Wgk5hQ%B(j5h`T7L@AJ-6SfFValMl#qAA* zZGZf0mdtuPRXia|uO~H_=w7Cb`uS|TFLJ7^kCQBD1K?L*22lsOpdDHKEs1?`Kzhyp zhtBj3sy42nlI}1%*R_NSuq*c*%2C}#g?fJ^xlliw_-+wB;dnkkQc4~(ncr$-#l2ks z0Hk$+6NJ(5x}{L`V6Mco9A{w78YEA9vZi6`?2 zR$S6S03sd}9L6|?@gBx$g2u>Z#=cd`Jec$WBRU}m=ib4112?}$+qa(qGzI|k^&>!M z#R9?Dk2&ZxT(A#=1q3acpfh7|VKj{yeE`QOdQ>a}rwD?+SMvY+5BZLw--5}Y6$}6X N002ovPDHLkV1kzc_?iF! delta 836 zcmV-K1H1gE2-*gaBYyw}VoOIv00000008+zyMF)x010qNS#tmY07w7;07w8v$!k6U z00R0+L_t(I%e9kTXj5kx$N%qp&Pki5IZc~dOx0|PYpYZ!WB7qGy%S`cQwrXQj3Rim zX2F;UiUTJ)7YAIs8$*M)nw}ccR!%1hn+?-i<2aRCD!DcaD*M%% zN_Vg>K9px3XMf;}-3tM~-Or)ktH?J(xwk`;*YbH&I_@{E3^ zl!^aoJ)UsN%?bcO0)S;5TVZonm5FI(ZS1d$M^E>J@5hVzwQshOq%prXmx|wFTKH>o zCrBu3icg*u}>`|5WWMcln^|P#> z{J*pXn}0J8bSMP!O&!5Rk!>sJT0Z|cG^jSe&fDvou`1wGXQMNX@u(K9YiQtS554!F zgRX&bc#xqtC-bY*T;87IbZ3#VR>!EEXj{5WX$h`#xm@lQdcLqT&+LRA3!k^K`UQ!x zBx>Ybb(U4{FIb)f78fcicB#s)-4>VpO=iSyY=5dNud5bD(o84&4*htXeY5xAb3il^ zfl%(0snt13xX6N2l~imu19>MStR1Gznd7xa{AA`kW8%_-pZ49uAHSYl=pgvf&k%M| zrOq246YlxYF3(Ln+=@}57>ZpU^@_PmZteVpg<3l_dSh{k!TrAgL~bLtyU#96T`kOw z3_UBhhm*{#@!Grn{Ga}Iu$Q$E_@D<=QYDm}^^Td=Aw(qc`}Z&X*na^JKXj+YF~raS O0000 - + + + + + 3.0 - - 3.0 - 4.0 @@ -155,7 +152,26 @@ EditBreakpointScriptWindow.xaml Code + + + DebuggerTooltipControl.xaml + + + + PinCloseControl.xaml + + + PinDebuggerControl.xaml + + + + + + VisualizerPicker.xaml + + + @@ -298,6 +314,7 @@ + @@ -347,6 +364,11 @@ + + + + + @@ -368,6 +390,7 @@ False + diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs index 8f90a4ddc7..746108e4d2 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs @@ -7,7 +7,7 @@ using Debugger.AddIn.Pads.Controls; using Debugger.AddIn.TreeModel; using ICSharpCode.Core; using Exception = System.Exception; -using TreeNode = ICSharpCode.SharpDevelop.Debugging.TreeNode; +using TreeNode = Debugger.AddIn.TreeModel.TreeNode; namespace ICSharpCode.SharpDevelop.Gui.Pads { diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadModel.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadModel.cs index 01b41c3f0e..ce3906bf27 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadModel.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadModel.cs @@ -22,7 +22,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads } } - public new bool SetText(string text) + public override bool SetText(string text) { this.Text = text; return true; diff --git a/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs b/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs index 5e62a70437..8b1c08ad75 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs @@ -11,7 +11,7 @@ using System.Text; using System.Windows.Forms; using Debugger; -using Debugger.AddIn; +using Debugger.AddIn.Tooltips; using Debugger.AddIn.TreeModel; using Debugger.Interop.CorPublish; using ICSharpCode.Core; @@ -404,13 +404,37 @@ namespace ICSharpCode.SharpDevelop.Services { try { var tooltipExpression = GetExpression(variableName); - ExpressionNode expressionNode = new ExpressionNode(ExpressionNode.GetImageForLocalVariable(), variableName, tooltipExpression); + string imageName; + var image = ExpressionNode.GetImageForLocalVariable(out imageName); + ExpressionNode expressionNode = new ExpressionNode(image, variableName, tooltipExpression); + expressionNode.ImageName = imageName; return new DebuggerTooltipControl(expressionNode); } catch (GetValueException) { return null; } } + public ITreeNode GetNode(string variable, string currentImageName = null) + { + try { + var expression = GetExpression(variable); + string imageName; + IImage image; + if (string.IsNullOrEmpty(currentImageName)) { + image = ExpressionNode.GetImageForLocalVariable(out imageName); + } + else { + image = new ResourceServiceImage(currentImageName); + imageName = currentImageName; + } + ExpressionNode expressionNode = new ExpressionNode(image, variable, expression); + expressionNode.ImageName = imageName; + return expressionNode; + } catch (GetValueException) { + return null; + } + } + public bool CanSetInstructionPointer(string filename, int line, int column) { if (debuggedProcess != null && debuggedProcess.IsPaused && debuggedProcess.SelectedStackFrame != null) { diff --git a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerPopup.cs b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerPopup.cs new file mode 100644 index 0000000000..17507bde47 --- /dev/null +++ b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerPopup.cs @@ -0,0 +1,101 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls.Primitives; +using System.Windows.Input; + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Debugging; +using ICSharpCode.SharpDevelop.Gui; + +namespace Debugger.AddIn.Tooltips +{ + /// + /// Popup containing . + /// + public class DebuggerPopup : Popup + { + internal DebuggerTooltipControl contentControl; + + public DebuggerPopup(DebuggerTooltipControl parentControl, bool showPins = true) + { + this.contentControl = new DebuggerTooltipControl(parentControl, showPins); + this.contentControl.containingPopup = this; + this.Child = this.contentControl; + this.IsLeaf = false; + + //this.KeyDown += new KeyEventHandler(DebuggerPopup_KeyDown); + + //this.contentControl.Focusable = true; + //Keyboard.Focus(this.contentControl); + //this.AllowsTransparency = true; + //this.PopupAnimation = PopupAnimation.Slide; + } + + // attempt to propagate shortcuts to main windows when Popup is focusable (needed for keyboard scrolling + editing) + /*void DebuggerPopup_KeyDown(object sender, KeyEventArgs e) + { + LoggingService.Debug("Unhandled popup key down: " + e.Key); + RaiseEventPair(WorkbenchSingleton.MainWindow, PreviewKeyDownEvent, KeyDownEvent, + new KeyEventArgs(e.KeyboardDevice, e.InputSource, e.Timestamp, e.Key)); + } + + // copied from CompletionWindowBase + static bool RaiseEventPair(UIElement target, RoutedEvent previewEvent, RoutedEvent @event, RoutedEventArgs args) + { + if (target == null) + throw new ArgumentNullException("target"); + if (previewEvent == null) + throw new ArgumentNullException("previewEvent"); + if (@event == null) + throw new ArgumentNullException("event"); + if (args == null) + throw new ArgumentNullException("args"); + args.RoutedEvent = previewEvent; + target.RaiseEvent(args); + args.RoutedEvent = @event; + target.RaiseEvent(args); + return args.Handled; + }*/ + + public IEnumerable ItemsSource + { + get { return this.contentControl.ItemsSource; } + set { this.contentControl.SetItemsSource(value); } + } + + private bool isLeaf; + public bool IsLeaf + { + get { return isLeaf; } + set + { + isLeaf = value; + // leaf popup closes on lost focus + this.StaysOpen = !isLeaf; + } + } + + protected override void OnClosed(EventArgs e) + { + base.OnClosed(e); + if (isLeaf) { + this.contentControl.CloseOnLostFocus(); + } + } + + public void Open() + { + this.IsOpen = true; + } + + public void CloseSelfAndChildren() + { + this.contentControl.CloseChildPopups(); + this.IsOpen = false; + } + } +} \ No newline at end of file diff --git a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml new file mode 100644 index 0000000000..2d2a44abba --- /dev/null +++ b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs new file mode 100644 index 0000000000..0c53338417 --- /dev/null +++ b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs @@ -0,0 +1,406 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +using ICSharpCode.Core; +using ICSharpCode.NRefactory; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Bookmarks; +using ICSharpCode.SharpDevelop.Debugging; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Gui; +using Services.Debugger.Tooltips; + +namespace Debugger.AddIn.Tooltips +{ + /// + /// Default Control used as content of SharpDevelop debugger tooltips. + /// + public partial class DebuggerTooltipControl : UserControl, ITooltip + { + private const double ChildPopupOpenXOffet = 16; + private const double ChildPopupOpenYOffet = 15; + private const int InitialItemsCount = 12; + private const int VisibleItemsCount = 11; + + private bool showPins = true; + private LazyItemsControl lazyGrid; + private IEnumerable itemsSource; + + public DebuggerTooltipControl() + { + InitializeComponent(); + + Loaded += new RoutedEventHandler(OnLoaded); + } + + public DebuggerTooltipControl(ITreeNode node) + : this(new ITreeNode[] { node }) + { + + } + + public DebuggerTooltipControl(IEnumerable nodes) + : this() + { + this.itemsSource = nodes; + } + + public DebuggerTooltipControl(DebuggerTooltipControl parentControl, bool showPins = true) + : this() + { + this.parentControl = parentControl; + this.showPins = showPins; + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + if (!showPins) { + dataGrid.Columns[5].Visibility = Visibility.Collapsed; + } + + SetItemsSource(this.itemsSource); + } + + public event RoutedEventHandler Closed; + protected void OnClosed() + { + if (this.Closed != null) { + this.Closed(this, new RoutedEventArgs()); + } + } + + public IEnumerable ItemsSource { + get { return this.itemsSource; } + } + + public void SetItemsSource(IEnumerable value) { + this.itemsSource = value; + this.lazyGrid = new LazyItemsControl(this.dataGrid, InitialItemsCount); + + // HACK for updating the pins in tooltip + var observable = new List(); + this.itemsSource.ForEach(item => observable.Add(item)); + + // verify if at the line of the root there's a pin bookmark + ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider; + var editor = provider.TextEditor; + if (editor != null) { + var pin = BookmarkManager.Bookmarks.Find( + b => b is PinBookmark && + b.Location.Line == LogicalPosition.Line && + b.FileName == editor.FileName) as PinBookmark; + + if (pin != null) { + observable.ForEach(item => { // TODO: find a way not to use "observable" + if (pin.ContainsNode(item)) + item.IsPinned = true; + }); + } + } + + var source = new VirtualizingIEnumerable(observable); + lazyGrid.ItemsSource = source; + this.dataGrid.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(handleScroll)); + + if (this.lazyGrid.ItemsSourceTotalCount != null) { + // hide up/down buttons if too few items + btnUp.Visibility = btnDown.Visibility = + this.lazyGrid.ItemsSourceTotalCount.Value <= VisibleItemsCount ? Visibility.Collapsed : Visibility.Visible; + } + } + + public Location LogicalPosition { get; set; } + + /// + public bool ShowAsPopup + { + get + { + return true; + } + } + + /// + public bool Close(bool mouseClick) + { + if (mouseClick || (!mouseClick && !isChildExpanded)) { + CloseChildPopups(); + return true; + } else { + return false; + } + } + + private DebuggerPopup childPopup { get; set; } + private DebuggerTooltipControl parentControl { get; set; } + internal DebuggerPopup containingPopup { get; set; } + + bool isChildExpanded + { + get + { + return this.childPopup != null && this.childPopup.IsOpen; + } + } + + private ToggleButton expandedButton; + + /// + /// Closes the child popup of this control, if it exists. + /// + public void CloseChildPopups() + { + if (this.expandedButton != null) { + this.expandedButton.IsChecked = false; + this.expandedButton = null; + // nice simple example of indirect recursion + this.childPopup.CloseSelfAndChildren(); + } + } + + public void CloseOnLostFocus() + { + // when we close, parent becomes leaf + if (this.containingPopup != null) { + this.containingPopup.IsLeaf = true; + } + if (!this.IsMouseOver) { + if (this.containingPopup != null) { + this.containingPopup.IsOpen = false; + this.containingPopup.IsLeaf = false; + } + if (this.parentControl != null) { + this.parentControl.CloseOnLostFocus(); + } + OnClosed(); + } else { + // leaf closed because of click inside this control - stop the closing chain + if (this.expandedButton != null && !this.expandedButton.IsMouseOver) { + this.expandedButton.IsChecked = false; + this.expandedButton = null; + } + } + } + + private void btnExpander_Click(object sender, RoutedEventArgs e) + { + var clickedButton = (ToggleButton)e.OriginalSource; + var clickedNode = (ITreeNode)clickedButton.DataContext; + // use device independent units, because child popup Left/Top are in independent units + Point buttonPos = clickedButton.PointToScreen(new Point(0, 0)).TransformFromDevice(clickedButton); + + if (clickedButton.IsChecked.GetValueOrDefault(false)) { + CloseChildPopups(); + this.expandedButton = clickedButton; + + // open child Popup + if (this.childPopup == null) { + this.childPopup = new DebuggerPopup(this); + this.childPopup.Placement = PlacementMode.Absolute; + this.childPopup.contentControl.LogicalPosition = LogicalPosition; + } + if (this.containingPopup != null) { + this.containingPopup.IsLeaf = false; + } + this.childPopup.IsLeaf = true; + this.childPopup.HorizontalOffset = buttonPos.X + ChildPopupOpenXOffet; + this.childPopup.VerticalOffset = buttonPos.Y + ChildPopupOpenYOffet; + this.childPopup.ItemsSource = clickedNode.ChildNodes; + this.childPopup.Open(); + } else { + CloseChildPopups(); + } + } + + private void handleScroll(object sender, ScrollChangedEventArgs e) + { + btnUp.IsEnabled = !this.lazyGrid.IsScrolledToStart; + btnDown.IsEnabled = !this.lazyGrid.IsScrolledToEnd; + } + + void BtnUp_Click(object sender, RoutedEventArgs e) + { + this.lazyGrid.ScrollViewer.ScrollUp(1); + } + + void BtnDown_Click(object sender, RoutedEventArgs e) + { + this.lazyGrid.ScrollViewer.ScrollDown(1); + } + + #region Edit value in tooltip + + void TextBox_KeyUp(object sender, KeyEventArgs e) + { + if (e.Key == Key.Escape) { + dataGrid.Focus(); + return; + } + + if (e.Key == Key.Enter) { + dataGrid.Focus(); + // set new value + var textBox = (TextBox)e.OriginalSource; + var newValue = textBox.Text; + var node = ((FrameworkElement)sender).DataContext as ITreeNode; + SaveNewValue(node, textBox.Text); + } + } + + void TextBox_LostFocus(object sender, RoutedEventArgs e) + { + var textBox = (TextBox)e.OriginalSource; + var newValue = textBox.Text; + var node = ((FrameworkElement)sender).DataContext as ITreeNode; + SaveNewValue(node, textBox.Text); + } + + void SaveNewValue(ITreeNode node, string newValue) + { + if(node != null && node.SetText(newValue)) { + // show adorner + var adornerLayer = AdornerLayer.GetAdornerLayer(dataGrid); + var adorners = adornerLayer.GetAdorners(dataGrid); + if (adorners != null && adorners.Length != 0) + adornerLayer.Remove(adorners[0]); + SavedAdorner adorner = new SavedAdorner(dataGrid); + adornerLayer.Add(adorner); + } + } + + #endregion + + #region Pining checked/unchecked + + void PinButton_Checked(object sender, RoutedEventArgs e) + { + ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider; + var editor = provider.TextEditor; + if (editor == null) return; + var node = (ITreeNode)(((ToggleButton)(e.OriginalSource)).DataContext); + + if (!string.IsNullOrEmpty(editor.FileName)) { + + // verify if at the line of the root there's a pin bookmark + var pin = BookmarkManager.Bookmarks.Find( + b => b is PinBookmark && + b.LineNumber == LogicalPosition.Line && + b.FileName == editor.FileName) as PinBookmark; + + if (pin == null) { + pin = new PinBookmark(editor.FileName, LogicalPosition); + // show pinned DebuggerPopup + if (pin.Popup == null) { + pin.Popup = new PinDebuggerControl(); + pin.Popup.Mark = pin; + Rect rect = new Rect(this.DesiredSize); + var point = this.PointToScreen(rect.TopRight); + pin.Popup.Location = new Point { X = 500, Y = point.Y - 150 }; + pin.Nodes.Add(node); + pin.Popup.ItemsSource = pin.Nodes; + } + + // do actions + pin.Popup.Open(); + BookmarkManager.AddMark(pin); + } + else + { + if (!pin.ContainsNode(node)) { + pin.Nodes.Add(node); + pin.Popup.ItemsSource = pin.Nodes; + } + } + } + } + + void PinButton_Unchecked(object sender, RoutedEventArgs e) + { + ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider; + var editor = provider.TextEditor; + if (editor == null) return; + + if (!string.IsNullOrEmpty(editor.FileName)) { + // remove from pinned DebuggerPopup + var pin = BookmarkManager.Bookmarks.Find( + b => b is PinBookmark && + b.LineNumber == LogicalPosition.Line && + b.FileName == editor.FileName) as PinBookmark; + if (pin == null) return; + + ToggleButton button = (ToggleButton)e.OriginalSource; + pin.RemoveNode((ITreeNode)button.DataContext); + pin.Popup.ItemsSource = pin.Nodes; + // remove if no more data pins are available + if (pin.Nodes.Count == 0) { + pin.Popup.Close(); + + BookmarkManager.RemoveMark(pin); + } + } + } + + #endregion + + #region Saved Adorner + + class SavedAdorner : Adorner + { + public SavedAdorner(UIElement adornedElement) : base(adornedElement) + { + Loaded += delegate { Show(); }; + } + + protected override void OnRender(DrawingContext drawingContext) + { + Rect adornedElementRect = new Rect(this.AdornedElement.DesiredSize); + + // Some arbitrary drawing implements. + var formatedText = new FormattedText(StringParser.Parse("${res:ICSharpCode.SharpDevelop.Debugging.SavedString}"), + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + new Typeface(new FontFamily("Arial"), + FontStyles.Normal, + FontWeights.Black, + FontStretches.Expanded), + 8d, + Brushes.Black); + + + drawingContext.DrawText(formatedText, + new Point(adornedElementRect.TopRight.X - formatedText.Width - 2, + adornedElementRect.TopRight.Y)); + } + + private void Show() + { + DoubleAnimation animation = new DoubleAnimation(); + animation.From = 1; + animation.To = 0; + + animation.Duration = new Duration(TimeSpan.FromSeconds(2)); + animation.SetValue(Storyboard.TargetProperty, this); + animation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath(Rectangle.OpacityProperty)); + + Storyboard board = new Storyboard(); + board.Children.Add(animation); + + board.Begin(this); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/LazyItemsControl.cs b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/LazyItemsControl.cs new file mode 100644 index 0000000000..f4af8a0aa7 --- /dev/null +++ b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/LazyItemsControl.cs @@ -0,0 +1,113 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Windows.Controls; + +using ICSharpCode.SharpDevelop; + +namespace Debugger.AddIn.Tooltips +{ + /// + /// ItemsControl wrapper that takes VirtualizingIEnumerable as source, + /// and adds additional items from the source to underlying ItemsControl when scrolled to bottom. + /// + public class LazyItemsControl + { + private ItemsControl itemsControl; + private int initialItemsCount; + + /// + /// Creates new instance of LazyItemsControl. + /// + /// ItemsControl to wrap and add items to it when scrolled to bottom. + /// Number of items to be initially displayed in wrapped ItemsControl. + public LazyItemsControl(ItemsControl wrappedItemsControl, int initialItemsCount) + { + if (wrappedItemsControl == null) + throw new ArgumentNullException("wrappedItemsControl"); + + this.initialItemsCount = initialItemsCount; + this.itemsControl = wrappedItemsControl; + this.itemsControl.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(handleScroll)); + } + + private ScrollViewer scrollViewerCached; + public ScrollViewer ScrollViewer + { + get + { + if (this.scrollViewerCached == null) + this.scrollViewerCached = this.itemsControl.GetScrollViewer(); + return this.scrollViewerCached; + } + } + + public bool IsScrolledToStart + { + get + { + if (ScrollViewer == null) // Visual tree not initialized yet + return false; + return ScrollViewer.VerticalOffset == 0; + } + } + + public bool IsScrolledToEnd + { + get + { + if (itemsSourceTotalCount == null) { + // not scrolled to end of IEnumerable yet + return false; + } + // already scrolled to end of IEnumerable + int totalItems = itemsSourceTotalCount.Value; + return (ScrollViewer.VerticalOffset >= totalItems - ScrollViewer.ViewportHeight); + } + } + + private int? itemsSourceTotalCount = null; + /// Items count of underlying IEnumerable. Null until scrolled to the end of IEnumerable. + public int? ItemsSourceTotalCount + { + get + { + return this.itemsSourceTotalCount; + } + } + + private VirtualizingIEnumerable itemsSource; + /// The collection that underlying ItemsControl sees. + public VirtualizingIEnumerable ItemsSource + { + get { return itemsSource; } + set + { + this.itemsSource = value; + addNextItems(this.itemsSource, initialItemsCount); + this.itemsControl.ItemsSource = value; + } + } + + private void addNextItems(VirtualizingIEnumerable sourceToAdd, int nItems) + { + sourceToAdd.AddNextItems(nItems); + if (!sourceToAdd.HasNext) { + // all items from IEnumerable have been added + this.itemsSourceTotalCount = sourceToAdd.Count; + } + } + + private void handleScroll(object sender, ScrollChangedEventArgs e) + { + if (e.VerticalChange > 0) { + // scrolled to bottom + if (e.VerticalOffset >= this.itemsSource.Count - e.ViewportHeight) { + addNextItems(this.itemsSource, (int)e.VerticalChange); + } + } + } + } +} diff --git a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinCloseControl.xaml b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinCloseControl.xaml new file mode 100644 index 0000000000..c2fc077e1e --- /dev/null +++ b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinCloseControl.xaml @@ -0,0 +1,29 @@ + + + + + + + + + + +