From 1a9088a8017b73320d4fc419259575bff5010988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kon=C3=AD=C4=8Dek?= Date: Fri, 7 Aug 2009 23:10:07 +0000 Subject: [PATCH] Debugger tooltips - expanding, partially working collapsing using StaysOpen = false for leaf Popup git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4615 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../AvalonEdit.AddIn/Src/CodeEditor.cs | 83 +++++++++++++--- .../Project/ICSharpCode.SharpDevelop.csproj | 1 + src/Main/Base/Project/Src/Editor/ITooltip.cs | 15 ++- .../Src/Services/Debugger/DebuggerPopup.cs | 53 ++++++++++ .../Debugger/DebuggerTooltipControl.xaml | 9 +- .../Debugger/DebuggerTooltipControl.xaml.cs | 96 +++++++++++++++---- 6 files changed, 220 insertions(+), 37 deletions(-) create mode 100644 src/Main/Base/Project/Src/Services/Debugger/DebuggerPopup.cs diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs index be620c2e1b..b75abc59dd 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs @@ -298,9 +298,11 @@ namespace ICSharpCode.AvalonEdit.AddIn ToolTip toolTip; Popup popup; + bool settingFocus = false; void textEditor_MouseHover(object sender, MouseEventArgs e) { + LoggingService.Debug("textEditor_MouseHover"); TextEditor textEditor = (TextEditor)sender; ToolTipRequestEventArgs args = new ToolTipRequestEventArgs(GetAdapter(textEditor)); var pos = textEditor.GetPositionFromPoint(e.GetPosition(textEditor)); @@ -319,14 +321,26 @@ namespace ICSharpCode.AvalonEdit.AddIn throw new NotSupportedException("Content to show in Popup must be UIElement: " + args.ContentToShow); } if (popup == null) { - popup = createPopup(); + popup = createPopup(true); + } + if (tryCloseExistingPopup(false)) { + popup.Child = (UIElement)args.ContentToShow; + setPopupPosition(popup, textEditor, e); + LoggingService.Debug("opening new popup"); + popup.IsOpen = true; + /*popup.Child.Focusable = true; + popup.Child.PreviewLostKeyboardFocus += (senderChild, _e) => { + LoggingService.Debug(string.Format("popup focus lost, source {0}, old {1}, new {2}", _e.Source, _e.OldFocus, _e.NewFocus)); + //if (_e.OriginalSource == popup.Child) { + tryCloseExistingPopup(true); + //} + }; + popup.IsOpen = true; + //settingFocus = true; + textEditor.Focus(); // needed so that popup.Child.Focus() succeds + popup.Child.Focus(); + //settingFocus = false;*/ } - // close old popup - popup.IsOpen = false; - popup.Child = (UIElement)args.ContentToShow; - setPopupPosition(popup, textEditor, e); - // open popup at new location - popup.IsOpen = true; e.Handled = true; } else { @@ -342,12 +356,59 @@ namespace ICSharpCode.AvalonEdit.AddIn else { // close popup if mouse hovered over empty area if (popup != null) { - popup.IsOpen = false; e.Handled = true; } + tryCloseExistingPopup(false); + } + } + + ITooltip getExistingITooltipPopupContent() + { + if (popup == null) { + return null; + } + return popup.Child as ITooltip; + } + + bool tryCloseExistingPopup(bool mouseClick) + { + bool canClose = true; + var popupContentITooltip = getExistingITooltipPopupContent(); + if (popupContentITooltip != null) { + canClose = popupContentITooltip.Close(mouseClick); } + if (popup != null && canClose) { + popup.IsOpen = false; + } + return canClose; } + /*bool tryCloseExistingPopup(bool mouseClick) + { + bool canClose = true; + var popupContentITooltip = getExistingITooltipPopupContent(); + if (popupContentITooltip != null) { + canClose = popupContentITooltip.AllowsClose; + } + if (popup != null && canClose) { + LoggingService.Debug("popup.IsOpen = false"); + // popup.IsOpen = false + closing ITooltip in Closed handler + // does not work, because Closed is called asynchronously + closeITooltipContent(popup); + popup.IsOpen = false; + } + return canClose; + } + + void closeITooltipContent(Popup popup) + { + var popupContentITooltip = getExistingITooltipPopupContent(); + if (popupContentITooltip != null) { + LoggingService.Debug("Closing popup children"); + popupContentITooltip.Close(); + } + }*/ + void setPopupPosition(Popup popup, TextEditor textEditor, MouseEventArgs mouseArgs) { var popupPosition = getPopupPosition(textEditor, mouseArgs); @@ -369,12 +430,12 @@ namespace ICSharpCode.AvalonEdit.AddIn }*/ } - Popup createPopup() + Popup createPopup(bool staysOpenOnFocusLost) { popup = new Popup(); popup.Closed += popup_Closed; popup.Placement = PlacementMode.Absolute; - popup.StaysOpen = true; + popup.StaysOpen = staysOpenOnFocusLost; return popup; } @@ -391,7 +452,7 @@ namespace ICSharpCode.AvalonEdit.AddIn if (popup != null) { // do not close popup if mouse moved from editor to popup if (!popup.IsMouseOver) { - popup.IsOpen = false; + tryCloseExistingPopup(false); } } } diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 3a0a78087c..7af59535e2 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -218,6 +218,7 @@ + DebuggerTooltipControl.xaml Code diff --git a/src/Main/Base/Project/Src/Editor/ITooltip.cs b/src/Main/Base/Project/Src/Editor/ITooltip.cs index f4b716ab47..980af0ccf9 100644 --- a/src/Main/Base/Project/Src/Editor/ITooltip.cs +++ b/src/Main/Base/Project/Src/Editor/ITooltip.cs @@ -9,7 +9,7 @@ using System; namespace ICSharpCode.SharpDevelop.Editor { /// - /// Content of text editor tooltip (), + /// Content of text editor tooltip (used as ), /// specifying whether it should be displayed in a WPF Popup. /// public interface ITooltip @@ -22,9 +22,16 @@ namespace ICSharpCode.SharpDevelop.Editor bool ShowAsPopup { get; } /// - /// Closes this debugger tooltip. + /// Indicates whether this tooltip allows to be closed. /// - /// True if Close succeeded, false otherwise. - bool Close(); + bool AllowsClose { get; } + + /// + /// Closes this tooltip. + /// + /// True if close request is raised + /// because of mouse click on some SharpDevelop GUI element. + /// True if Close succeeded (that is, can close). False otherwise. + bool Close(bool mouseClick); } } diff --git a/src/Main/Base/Project/Src/Services/Debugger/DebuggerPopup.cs b/src/Main/Base/Project/Src/Services/Debugger/DebuggerPopup.cs new file mode 100644 index 0000000000..182d843f90 --- /dev/null +++ b/src/Main/Base/Project/Src/Services/Debugger/DebuggerPopup.cs @@ -0,0 +1,53 @@ +// +// +// +// +// $Revision$ +// +using ICSharpCode.Core; +using System; +using System.Collections.Generic; +using System.Windows.Controls.Primitives; + +namespace ICSharpCode.SharpDevelop.Debugging +{ + /// + /// Popup containing . + /// + public class DebuggerPopup : Popup + { + private DebuggerTooltipControl contentControl; + //private DebuggerPopup parentPopup; + + public DebuggerPopup() + { + this.contentControl = new DebuggerTooltipControl(); + this.contentControl.containingPopup = this; + this.Child = this.contentControl; + // to handle closed by lost focus + this.Closed += DebuggerPopup_Closed; + } + + void DebuggerPopup_Closed(object sender, EventArgs e) + { + LoggingService.Debug("DebuggerPopup_Closed"); + } + + public IEnumerable ItemsSource + { + get { return this.contentControl.ItemsSource; } + set { this.contentControl.ItemsSource = value; } + } + + public void Open() + { + this.IsOpen = true; + } + + public void Close() + { + this.contentControl.CloseChildPopup(); + this.IsOpen = false; + } + } +} diff --git a/src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml b/src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml index 378e1d65e9..71f4a5a1c1 100644 --- a/src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml +++ b/src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml @@ -57,6 +57,7 @@ + @@ -122,6 +123,12 @@ @@ -134,7 +141,7 @@ - + diff --git a/src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml.cs b/src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml.cs index 4a5b8cccb7..f05d27be5f 100644 --- a/src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml.cs +++ b/src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml.cs @@ -28,12 +28,12 @@ namespace ICSharpCode.SharpDevelop.Debugging InitializeComponent(); } - public DebuggerTooltipControl(ITreeNode node) + public DebuggerTooltipControl(ITreeNode node) : this(new ITreeNode[] { node }) { } - public DebuggerTooltipControl(IEnumerable nodes) + public DebuggerTooltipControl(IEnumerable nodes) : this() { this.ItemsSource = nodes; @@ -41,24 +41,6 @@ namespace ICSharpCode.SharpDevelop.Debugging private LazyItemsControl lazyGrid; - /// - /// Determines whether DebuggerTooltipControl should be displayed in WPF Popup. - /// - public bool ShowAsPopup { - get { - return true; - } - } - - /// - /// Closes the debugger Popup containing this control. Also closes all child Popups. - /// - /// - public bool Close() - { - throw new NotImplementedException(); - } - private IEnumerable itemsSource; public IEnumerable ItemsSource { @@ -71,6 +53,7 @@ namespace ICSharpCode.SharpDevelop.Debugging if (this.lazyGrid.ItemsSourceTotalCount != null) { + // hide up, down buttons if too few items btnUp.Visibility = btnDown.Visibility = this.lazyGrid.ItemsSourceTotalCount.Value <= 10 ? Visibility.Collapsed : Visibility.Visible; } @@ -81,18 +64,89 @@ namespace ICSharpCode.SharpDevelop.Debugging } } + /// + public bool ShowAsPopup { + get { + return true; + } + } + + /// + /// When child popup is expanded, returns false. Otherwise true. + /// + public bool AllowsClose { + get { + return !isChildExpanded; + } + } + + /// + public bool Close(bool mouseClick) + { + if (mouseClick || (!mouseClick && !isChildExpanded)) + { + CloseChildPopup(); + return true; + } + else + { + return false; + } + } + + DebuggerPopup childPopup; + bool isChildExpanded + { + get { + return this.childPopup != null && this.childPopup.IsOpen; + } + } + + /// + /// Closes the child popup of this control, if it exists. + /// + public void CloseChildPopup() + { + if (this.childPopup != null) + { + this.childPopup.Close(); + } + } + + internal Popup containingPopup; + private void btnExpander_Click(object sender, RoutedEventArgs e) { var clickedButton = (ToggleButton)e.OriginalSource; + var clickedNode = (ITreeNode)clickedButton.DataContext; Point buttonPos = clickedButton.PointToScreen(new Point(0, 0)); if (clickedButton.IsChecked.GetValueOrDefault(false)) { + CloseChildPopup(); + // open child Popup + if (this.childPopup == null) + { + this.childPopup = new DebuggerPopup(); + this.childPopup.Placement = PlacementMode.Absolute; + this.childPopup.StaysOpen = true; + } + if (this.containingPopup != null) + { + this.containingPopup.StaysOpen = true; + } + // last popup is always StaysOpen = false, therefore focused + this.childPopup.StaysOpen = false; + this.childPopup.HorizontalOffset = buttonPos.X + 15; + this.childPopup.VerticalOffset = buttonPos.Y + 15; + this.childPopup.ItemsSource = clickedNode.ChildNodes; + this.childPopup.UpdateLayout(); + this.childPopup.Open(); } else { - // close child Popups + CloseChildPopup(); } }