Browse Source

Debugger tooltips - basic version finished, lazy evaluated on scrolling, behavior very similar to SharpDevelop 3. Missing: icons, integration with visualizers.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4628 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Martin Koníček 17 years ago
parent
commit
cff4acbc5f
  1. 69
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  2. 11
      src/Main/Base/Project/Src/Editor/ITooltip.cs
  3. 38
      src/Main/Base/Project/Src/Services/Debugger/DebuggerPopup.cs
  4. 3
      src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml
  5. 87
      src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml.cs

69
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs

@ -176,6 +176,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -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 @@ -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 @@ -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 @@ -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 (popup != null) {
var popupContentITooltip = popup.Child as ITooltip;
if (popupContentITooltip != null) {
canClose = popupContentITooltip.Close(mouseClick);
}
if (popup != null && canClose) {
if (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);
@ -430,12 +387,12 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -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,12 +406,16 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -449,12 +406,16 @@ 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);
}
}
void textEditor_MouseDown(object sender, MouseButtonEventArgs e)
{
// close existing popup on text editor mouse down
tryCloseExistingPopup(false);
}
void toolTip_Closed(object sender, RoutedEventArgs e)

11
src/Main/Base/Project/Src/Editor/ITooltip.cs

@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
// <version>$Revision$</version>
// </file>
using System;
using System.Windows;
namespace ICSharpCode.SharpDevelop.Editor
{
@ -21,11 +22,6 @@ namespace ICSharpCode.SharpDevelop.Editor @@ -21,11 +22,6 @@ namespace ICSharpCode.SharpDevelop.Editor
/// </summary>
bool ShowAsPopup { get; }
/// <summary>
/// Indicates whether this tooltip allows to be closed.
/// </summary>
bool AllowsClose { get; }
/// <summary>
/// Closes this tooltip.
/// </summary>
@ -33,5 +29,10 @@ namespace ICSharpCode.SharpDevelop.Editor @@ -33,5 +29,10 @@ namespace ICSharpCode.SharpDevelop.Editor
/// because of mouse click on some SharpDevelop GUI element.</param>
/// <returns>True if Close succeeded (that is, can close). False otherwise.</returns>
bool Close(bool mouseClick);
/// <summary>
/// Occurs when this tooltip decides to close.
/// </summary>
event RoutedEventHandler Closed;
}
}

38
src/Main/Base/Project/Src/Services/Debugger/DebuggerPopup.cs

@ -17,20 +17,13 @@ namespace ICSharpCode.SharpDevelop.Debugging @@ -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<ITreeNode> ItemsSource
@ -39,14 +32,35 @@ namespace ICSharpCode.SharpDevelop.Debugging @@ -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;
}
}

3
src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml

@ -56,8 +56,9 @@ @@ -56,8 +56,9 @@
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="Background" Value="#FFECF7FC" />
<Setter Property="Height" Value="14" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled}" Value="False">
<Setter Property="Background" Value="#FFE0E0E0"></Setter>

87
src/Main/Base/Project/Src/Services/Debugger/DebuggerTooltipControl.xaml.cs

@ -39,6 +39,21 @@ namespace ICSharpCode.SharpDevelop.Debugging @@ -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<ITreeNode> lazyGrid;
private IEnumerable<ITreeNode> itemsSource;
@ -53,7 +68,7 @@ namespace ICSharpCode.SharpDevelop.Debugging @@ -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,21 +86,12 @@ namespace ICSharpCode.SharpDevelop.Debugging @@ -71,21 +86,12 @@ namespace ICSharpCode.SharpDevelop.Debugging
}
}
/// <summary>
/// When child popup is expanded, returns false. Otherwise true.
/// </summary>
public bool AllowsClose {
get {
return !isChildExpanded;
}
}
/// <inheritdoc/>
public bool Close(bool mouseClick)
{
if (mouseClick || (!mouseClick && !isChildExpanded))
{
CloseChildPopup();
CloseChildPopups();
return true;
}
else
@ -94,7 +100,10 @@ namespace ICSharpCode.SharpDevelop.Debugging @@ -94,7 +100,10 @@ namespace ICSharpCode.SharpDevelop.Debugging
}
}
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 @@ -102,18 +111,50 @@ namespace ICSharpCode.SharpDevelop.Debugging
}
}
private ToggleButton expandedButton;
/// <summary>
/// Closes the child popup of this control, if it exists.
/// </summary>
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 @@ -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();
}
}

Loading…
Cancel
Save