diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs index b75abc59dd..3bdcb78d05 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs @@ -176,6 +176,7 @@ namespace ICSharpCode.AvalonEdit.AddIn textEditor.MouseHover += textEditor_MouseHover; textEditor.MouseHoverStopped += textEditor_MouseHoverStopped; textEditor.MouseLeave += textEditor_MouseLeave; + textView.MouseDown += textEditor_MouseDown; textEditor.TextArea.Caret.PositionChanged += caret_PositionChanged; textEditor.TextArea.DefaultInputHandler.CommandBindings.Add( new CommandBinding(CustomCommands.CtrlSpaceCompletion, OnCodeCompletion)); @@ -302,7 +303,6 @@ namespace ICSharpCode.AvalonEdit.AddIn 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)); @@ -321,25 +321,14 @@ namespace ICSharpCode.AvalonEdit.AddIn throw new NotSupportedException("Content to show in Popup must be UIElement: " + args.ContentToShow); } if (popup == null) { - popup = createPopup(true); + popup = createPopup(); } if (tryCloseExistingPopup(false)) { + // when popup content decides to close, close the popup + contentToShowITooltip.Closed += (closedSender, closedArgs) => { popup.IsOpen = 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;*/ } e.Handled = true; } @@ -362,53 +351,21 @@ namespace ICSharpCode.AvalonEdit.AddIn } } - 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; + if (popup != null) { + var popupContentITooltip = popup.Child as ITooltip; + if (popupContentITooltip != null) { + canClose = popupContentITooltip.Close(mouseClick); + } + if (canClose) { + 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); @@ -430,12 +387,12 @@ namespace ICSharpCode.AvalonEdit.AddIn }*/ } - Popup createPopup(bool staysOpenOnFocusLost) + Popup createPopup() { popup = new Popup(); popup.Closed += popup_Closed; popup.Placement = PlacementMode.Absolute; - popup.StaysOpen = staysOpenOnFocusLost; + popup.StaysOpen = true; return popup; } @@ -449,13 +406,17 @@ namespace ICSharpCode.AvalonEdit.AddIn void textEditor_MouseLeave(object sender, MouseEventArgs e) { - if (popup != null) { + if (popup != null && !popup.IsMouseOver) { // do not close popup if mouse moved from editor to popup - if (!popup.IsMouseOver) { - tryCloseExistingPopup(false); - } + tryCloseExistingPopup(false); } } + + void textEditor_MouseDown(object sender, MouseButtonEventArgs e) + { + // close existing popup on text editor mouse down + tryCloseExistingPopup(false); + } void toolTip_Closed(object sender, RoutedEventArgs e) { diff --git a/src/Main/Base/Project/Src/Editor/ITooltip.cs b/src/Main/Base/Project/Src/Editor/ITooltip.cs index 980af0ccf9..906d570e49 100644 --- a/src/Main/Base/Project/Src/Editor/ITooltip.cs +++ b/src/Main/Base/Project/Src/Editor/ITooltip.cs @@ -5,6 +5,7 @@ // $Revision$ // using System; +using System.Windows; namespace ICSharpCode.SharpDevelop.Editor { @@ -21,11 +22,6 @@ namespace ICSharpCode.SharpDevelop.Editor /// bool ShowAsPopup { get; } - /// - /// Indicates whether this tooltip allows to be closed. - /// - bool AllowsClose { get; } - /// /// Closes this tooltip. /// @@ -33,5 +29,10 @@ namespace ICSharpCode.SharpDevelop.Editor /// because of mouse click on some SharpDevelop GUI element. /// True if Close succeeded (that is, can close). False otherwise. bool Close(bool mouseClick); + + /// + /// Occurs when this tooltip decides to close. + /// + event RoutedEventHandler Closed; } } diff --git a/src/Main/Base/Project/Src/Services/Debugger/DebuggerPopup.cs b/src/Main/Base/Project/Src/Services/Debugger/DebuggerPopup.cs index 182d843f90..706bf88f18 100644 --- a/src/Main/Base/Project/Src/Services/Debugger/DebuggerPopup.cs +++ b/src/Main/Base/Project/Src/Services/Debugger/DebuggerPopup.cs @@ -17,20 +17,13 @@ namespace ICSharpCode.SharpDevelop.Debugging public class DebuggerPopup : Popup { private DebuggerTooltipControl contentControl; - //private DebuggerPopup parentPopup; - public DebuggerPopup() + public DebuggerPopup(DebuggerTooltipControl parentControl) { - this.contentControl = new DebuggerTooltipControl(); + this.contentControl = new DebuggerTooltipControl(parentControl); 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"); + this.IsLeaf = false; } public IEnumerable ItemsSource @@ -39,14 +32,35 @@ namespace ICSharpCode.SharpDevelop.Debugging set { this.contentControl.ItemsSource = 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 Close() + public void CloseSelfAndChildren() { - this.contentControl.CloseChildPopup(); + this.contentControl.CloseChildPopups(); 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 71f4a5a1c1..c87c4f8534 100644 --- a/src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml +++ b/src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml @@ -56,8 +56,9 @@ + - + 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 f05d27be5f..4f57f4782a 100644 --- a/src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml.cs +++ b/src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml.cs @@ -39,6 +39,21 @@ namespace ICSharpCode.SharpDevelop.Debugging this.ItemsSource = nodes; } + public DebuggerTooltipControl(DebuggerTooltipControl parentControl) + : this() + { + this.parentControl = parentControl; + } + + public event RoutedEventHandler Closed; + protected void OnClosed() + { + if (this.Closed != null) + { + this.Closed(this, new RoutedEventArgs()); + } + } + private LazyItemsControl lazyGrid; private IEnumerable itemsSource; @@ -53,7 +68,7 @@ namespace ICSharpCode.SharpDevelop.Debugging if (this.lazyGrid.ItemsSourceTotalCount != null) { - // hide up, down buttons if too few items + // hide up/down buttons if too few items btnUp.Visibility = btnDown.Visibility = this.lazyGrid.ItemsSourceTotalCount.Value <= 10 ? Visibility.Collapsed : Visibility.Visible; } @@ -71,30 +86,24 @@ namespace ICSharpCode.SharpDevelop.Debugging } } - /// - /// 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(); + CloseChildPopups(); return true; - } + } else { return false; } } - DebuggerPopup childPopup; + DebuggerPopup childPopup { get; set; } + DebuggerTooltipControl parentControl { get; set; } + internal DebuggerPopup containingPopup { get; set; } + bool isChildExpanded { get { @@ -102,18 +111,50 @@ namespace ICSharpCode.SharpDevelop.Debugging } } + private ToggleButton expandedButton; + /// /// Closes the child popup of this control, if it exists. /// - public void CloseChildPopup() + public void CloseChildPopups() { - if (this.childPopup != null) + if (this.expandedButton != null) { - this.childPopup.Close(); + this.expandedButton.IsChecked = false; + this.expandedButton = null; + // nice simple example of indirect recursion + this.childPopup.CloseSelfAndChildren(); } } - internal Popup containingPopup; + public void CloseOnLostFocus() + { + // when leaf below us closes, we become the 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 chaing + if (this.expandedButton != null) + { + this.expandedButton.IsChecked = false; + this.expandedButton = null; + } + } + } private void btnExpander_Click(object sender, RoutedEventArgs e) { @@ -123,30 +164,28 @@ namespace ICSharpCode.SharpDevelop.Debugging if (clickedButton.IsChecked.GetValueOrDefault(false)) { - CloseChildPopup(); + CloseChildPopups(); + this.expandedButton = clickedButton; // open child Popup if (this.childPopup == null) { - this.childPopup = new DebuggerPopup(); + this.childPopup = new DebuggerPopup(this); this.childPopup.Placement = PlacementMode.Absolute; - this.childPopup.StaysOpen = true; } if (this.containingPopup != null) { - this.containingPopup.StaysOpen = true; + this.containingPopup.IsLeaf = false; } - // last popup is always StaysOpen = false, therefore focused - this.childPopup.StaysOpen = false; + this.childPopup.IsLeaf = true; this.childPopup.HorizontalOffset = buttonPos.X + 15; this.childPopup.VerticalOffset = buttonPos.Y + 15; this.childPopup.ItemsSource = clickedNode.ChildNodes; - this.childPopup.UpdateLayout(); this.childPopup.Open(); } else { - CloseChildPopup(); + CloseChildPopups(); } }