Browse Source

Move tooltip handling from CodeEditor to CodeEditorView.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4848 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 16 years ago
parent
commit
94bf27e8cd
  1. 1
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
  2. 2
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs
  3. 209
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  4. 245
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
  5. 3
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs

1
src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj

@ -68,6 +68,7 @@ @@ -68,6 +68,7 @@
<Compile Include="Src\BracketHighlightRenderer.cs" />
<Compile Include="Src\CodeEditor.cs" />
<Compile Include="Src\CodeEditorAdapter.cs" />
<Compile Include="Src\CodeEditorView.cs" />
<Compile Include="Src\CustomCommands.cs" />
<Compile Include="Src\IconBarManager.cs" />
<Compile Include="Src\IconBarMargin.cs" />

2
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs

@ -225,7 +225,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -225,7 +225,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
public void JumpTo(int line, int column)
{
codeEditor.JumpTo(line, column);
codeEditor.ActiveTextEditor.JumpTo(line, column);
}
#endregion

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

@ -45,9 +45,9 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -45,9 +45,9 @@ namespace ICSharpCode.AvalonEdit.AddIn
const string contextMenuPath = "/SharpDevelop/ViewContent/AvalonEdit/ContextMenu";
QuickClassBrowser quickClassBrowser;
readonly TextEditor primaryTextEditor;
readonly CodeEditorView primaryTextEditor;
readonly CodeEditorAdapter primaryTextEditorAdapter;
TextEditor secondaryTextEditor;
CodeEditorView secondaryTextEditor;
CodeEditorAdapter secondaryTextEditorAdapter;
readonly IconBarManager iconBarManager;
readonly TextMarkerService textMarkerService;
@ -56,11 +56,11 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -56,11 +56,11 @@ namespace ICSharpCode.AvalonEdit.AddIn
BracketHighlightRenderer primaryBracketRenderer;
BracketHighlightRenderer secondaryBracketRenderer;
public TextEditor PrimaryTextEditor {
public CodeEditorView PrimaryTextEditor {
get { return primaryTextEditor; }
}
public TextEditor ActiveTextEditor {
public CodeEditorView ActiveTextEditor {
get { return primaryTextEditor; }
}
@ -98,15 +98,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -98,15 +98,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
}
public ITextEditor ActiveTextEditorAdapter {
get { return GetAdapter(this.ActiveTextEditor); }
}
CodeEditorAdapter GetAdapter(TextEditor editor)
{
if (editor == secondaryTextEditor)
return secondaryTextEditorAdapter;
else
return primaryTextEditorAdapter;
get { return this.ActiveTextEditor.Adapter; }
}
public IconBarManager IconBarManager {
@ -163,10 +155,11 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -163,10 +155,11 @@ namespace ICSharpCode.AvalonEdit.AddIn
this.Children.Add(primaryTextEditor);
}
protected virtual TextEditor CreateTextEditor()
protected virtual CodeEditorView CreateTextEditor()
{
TextEditor textEditor = new TextEditor();
CodeEditorView textEditor = new CodeEditorView();
CodeEditorAdapter adapter = new CodeEditorAdapter(this, textEditor);
textEditor.Adapter = adapter;
TextView textView = textEditor.TextArea.TextView;
textView.Services.AddService(typeof(ITextEditor), adapter);
textView.Services.AddService(typeof(CodeEditor), this);
@ -176,10 +169,6 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -176,10 +169,6 @@ namespace ICSharpCode.AvalonEdit.AddIn
textEditor.FontSize = 13;
textEditor.TextArea.TextEntering += TextAreaTextEntering;
textEditor.TextArea.TextEntered += TextAreaTextEntered;
textEditor.MouseHover += TextEditorMouseHover;
textEditor.MouseHoverStopped += TextEditorMouseHoverStopped;
textEditor.MouseLeave += TextEditorMouseLeave;
textView.MouseDown += TextViewMouseDown;
textEditor.TextArea.Caret.PositionChanged += TextAreaCaretPositionChanged;
textEditor.TextArea.DefaultInputHandler.CommandBindings.Add(
new CommandBinding(CustomCommands.CtrlSpaceCompletion, OnCodeCompletion));
@ -309,178 +298,6 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -309,178 +298,6 @@ namespace ICSharpCode.AvalonEdit.AddIn
CaretPositionChanged.RaiseEvent(this, EventArgs.Empty);
}
public void JumpTo(int line, int column)
{
TryCloseExistingPopup(true);
// the adapter sets the caret position and takes care of scrolling
this.ActiveTextEditorAdapter.JumpTo(line, column);
this.ActiveTextEditor.Focus();
}
ToolTip toolTip;
Popup popup;
void TextEditorMouseHover(object sender, MouseEventArgs e)
{
TextEditor textEditor = (TextEditor)sender;
ToolTipRequestEventArgs args = new ToolTipRequestEventArgs(GetAdapter(textEditor));
var pos = textEditor.GetPositionFromPoint(e.GetPosition(textEditor));
args.InDocument = pos.HasValue;
if (pos.HasValue) {
args.LogicalPosition = AvalonEditDocumentAdapter.ToLocation(pos.Value);
}
if (args.InDocument) {
var markersAtOffset = textMarkerService.GetMarkersAtOffset(args.Editor.Document.PositionToOffset(args.LogicalPosition.Line, args.LogicalPosition.Column));
ITextMarker markerWithToolTip = markersAtOffset.FirstOrDefault(marker => marker.ToolTip != null);
if (markerWithToolTip != null) {
if (toolTip == null) {
toolTip = new ToolTip();
toolTip.Closed += ToolTipClosed;
}
toolTip.Content = markerWithToolTip.ToolTip;
toolTip.IsOpen = true;
e.Handled = true;
return;
}
}
ToolTipRequestService.RequestToolTip(args);
if (args.ContentToShow != null) {
var contentToShowITooltip = args.ContentToShow as ITooltip;
if (contentToShowITooltip != null && contentToShowITooltip.ShowAsPopup) {
if (!(args.ContentToShow is UIElement)) {
throw new NotSupportedException("Content to show in Popup must be UIElement: " + args.ContentToShow);
}
if (popup == null) {
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;
//ICSharpCode.SharpDevelop.Debugging.DebuggerService.CurrentDebugger.IsProcessRunningChanged
SetPopupPosition(popup, textEditor, e);
popup.IsOpen = true;
}
e.Handled = true;
}
else {
if (toolTip == null) {
toolTip = new ToolTip();
toolTip.Closed += ToolTipClosed;
}
toolTip.Content = args.ContentToShow;
toolTip.IsOpen = true;
e.Handled = true;
}
}
else {
// close popup if mouse hovered over empty area
if (popup != null) {
e.Handled = true;
}
TryCloseExistingPopup(false);
}
}
bool TryCloseExistingPopup(bool mouseClick)
{
bool canClose = true;
if (popup != null) {
var popupContentITooltip = popup.Child as ITooltip;
if (popupContentITooltip != null) {
canClose = popupContentITooltip.Close(mouseClick);
}
if (canClose) {
popup.IsOpen = false;
}
}
return canClose;
}
void SetPopupPosition(Popup popup, TextEditor textEditor, MouseEventArgs mouseArgs)
{
var popupPosition = GetPopupPosition(textEditor, mouseArgs);
popup.HorizontalOffset = popupPosition.X;
popup.VerticalOffset = popupPosition.Y;
}
/// <summary> Returns Popup position based on mouse position, in device independent units </summary>
Point GetPopupPosition(TextEditor textEditor, MouseEventArgs mouseArgs)
{
Point mousePos = mouseArgs.GetPosition(textEditor);
Point positionInPixels;
// align Popup with line bottom
TextViewPosition? logicalPos = textEditor.GetPositionFromPoint(mousePos);
if (logicalPos.HasValue) {
var textView = textEditor.TextArea.TextView;
positionInPixels =
textView.PointToScreen(
textView.GetVisualPosition(logicalPos.Value, VisualYPosition.LineBottom) - textView.ScrollOffset);
positionInPixels.X -= 4;
}
else {
positionInPixels = textEditor.PointToScreen(mousePos + new Vector(-4, 6));
}
// use device independent units, because Popup Left/Top are in independent units
return positionInPixels.TransformFromDevice(textEditor);
}
Popup CreatePopup()
{
popup = new Popup();
popup.Closed += PopupClosed;
popup.Placement = PlacementMode.Absolute;
popup.StaysOpen = true;
return popup;
}
void TextEditorMouseHoverStopped(object sender, MouseEventArgs e)
{
if (toolTip != null) {
toolTip.IsOpen = false;
e.Handled = true;
}
}
void TextEditorMouseLeave(object sender, MouseEventArgs e)
{
if (popup != null && !popup.IsMouseOver) {
// do not close popup if mouse moved from editor to popup
TryCloseExistingPopup(false);
}
}
void TextViewMouseDown(object sender, MouseButtonEventArgs e)
{
// close existing popup immediately on text editor mouse down
TryCloseExistingPopup(false);
if (e.ChangedButton == MouseButton.Left && Keyboard.Modifiers == ModifierKeys.Control) {
TextEditor editor = GetTextEditorFromSender(sender);
var position = editor.GetPositionFromPoint(e.GetPosition(editor));
if (position != null) {
GoToDefinition.Run(GetAdapterFromSender(sender), document.GetOffset(position.Value));
e.Handled = true;
}
}
}
void ToolTipClosed(object sender, RoutedEventArgs e)
{
toolTip = null;
}
void PopupClosed(object sender, EventArgs e)
{
popup = null;
}
volatile static ReadOnlyCollection<ICodeCompletionBinding> codeCompletionBindings;
public static ReadOnlyCollection<ICodeCompletionBinding> CodeCompletionBindings {
@ -563,10 +380,10 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -563,10 +380,10 @@ namespace ICSharpCode.AvalonEdit.AddIn
return textEditor;
}
TextEditor GetTextEditorFromSender(object sender)
CodeEditorView GetTextEditorFromSender(object sender)
{
ITextEditorComponent textArea = (ITextEditorComponent)sender;
TextEditor textEditor = (TextEditor)textArea.GetService(typeof(TextEditor));
CodeEditorView textEditor = (CodeEditorView)textArea.GetService(typeof(TextEditor));
if (textEditor == null)
throw new InvalidOperationException("could not find TextEditor service");
return textEditor;
@ -575,9 +392,9 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -575,9 +392,9 @@ namespace ICSharpCode.AvalonEdit.AddIn
void OnCodeCompletion(object sender, ExecutedRoutedEventArgs e)
{
CloseExistingCompletionWindow();
TextEditor textEditor = GetTextEditorFromSender(sender);
CodeEditorView textEditor = GetTextEditorFromSender(sender);
foreach (ICodeCompletionBinding cc in CodeCompletionBindings) {
if (cc.CtrlSpace(GetAdapter(textEditor))) {
if (cc.CtrlSpace(textEditor.Adapter)) {
e.Handled = true;
break;
}
@ -664,7 +481,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -664,7 +481,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
if (quickClassBrowser != null || parseInfo.CompilationUnit.Classes.Count > 0) {
if (quickClassBrowser == null) {
quickClassBrowser = new QuickClassBrowser();
quickClassBrowser.JumpAction = JumpTo;
quickClassBrowser.JumpAction = (line, col) => ActiveTextEditor.JumpTo(line, col);
SetRow(quickClassBrowser, 0);
this.Children.Add(quickClassBrowser);
}

245
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs

@ -0,0 +1,245 @@ @@ -0,0 +1,245 @@
/*
* Created by SharpDevelop.
* User: daniel
* Date: 31.08.2009
* Time: 14:55
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.AvalonEdit;
using ICSharpCode.SharpDevelop.Editor.Commands;
namespace ICSharpCode.AvalonEdit.AddIn
{
/// <summary>
/// The text editor use inside the CodeEditor.
/// There can be two CodeEditorView instances in a single CodeEditor if split-view
/// is enabled.
/// </summary>
public class CodeEditorView : TextEditor
{
public ITextEditor Adapter { get; set; }
public CodeEditorView()
{
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Help, OnHelpExecuted));
this.MouseHover += TextEditorMouseHover;
this.MouseHoverStopped += TextEditorMouseHoverStopped;
this.MouseLeave += TextEditorMouseLeave;
this.TextArea.TextView.MouseDown += TextViewMouseDown;
}
#region Help
void OnHelpExecuted(object sender, ExecutedRoutedEventArgs e)
{
ShowHelp();
}
public void ShowHelp()
{
// Resolve expression at cursor and show help
TextArea textArea = this.TextArea;
IExpressionFinder expressionFinder = ParserService.GetExpressionFinder(this.Adapter.FileName);
if (expressionFinder == null)
return;
string textContent = this.Text;
ExpressionResult expressionResult = expressionFinder.FindFullExpression(textContent, textArea.Caret.Offset);
string expression = expressionResult.Expression;
if (expression != null && expression.Length > 0) {
ResolveResult result = ParserService.Resolve(expressionResult, textArea.Caret.Line, textArea.Caret.Column, this.Adapter.FileName, textContent);
TypeResolveResult trr = result as TypeResolveResult;
if (trr != null) {
HelpProvider.ShowHelp(trr.ResolvedClass);
}
MemberResolveResult mrr = result as MemberResolveResult;
if (mrr != null) {
HelpProvider.ShowHelp(mrr.ResolvedMember);
}
}
}
#endregion
#region Tooltip
ToolTip toolTip;
Popup popup;
void TextEditorMouseHover(object sender, MouseEventArgs e)
{
Debug.Assert(sender == this);
ToolTipRequestEventArgs args = new ToolTipRequestEventArgs(this.Adapter);
var pos = GetPositionFromPoint(e.GetPosition(this));
args.InDocument = pos.HasValue;
if (pos.HasValue) {
args.LogicalPosition = AvalonEditDocumentAdapter.ToLocation(pos.Value);
}
TextMarkerService textMarkerService = this.Adapter.GetService(typeof(ITextMarkerService)) as TextMarkerService;
if (args.InDocument && textMarkerService != null) {
var markersAtOffset = textMarkerService.GetMarkersAtOffset(args.Editor.Document.PositionToOffset(args.LogicalPosition.Line, args.LogicalPosition.Column));
ITextMarker markerWithToolTip = markersAtOffset.FirstOrDefault(marker => marker.ToolTip != null);
if (markerWithToolTip != null) {
args.ShowToolTip(markerWithToolTip.ToolTip);
}
}
if (!args.Handled) {
// if request wasn't handled by a marker, pass it to the ToolTipRequestService
ToolTipRequestService.RequestToolTip(args);
}
if (args.ContentToShow != null) {
var contentToShowITooltip = args.ContentToShow as ITooltip;
if (contentToShowITooltip != null && contentToShowITooltip.ShowAsPopup) {
if (!(args.ContentToShow is UIElement)) {
throw new NotSupportedException("Content to show in Popup must be UIElement: " + args.ContentToShow);
}
if (popup == null) {
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;
//ICSharpCode.SharpDevelop.Debugging.DebuggerService.CurrentDebugger.IsProcessRunningChanged
SetPopupPosition(popup, e);
popup.IsOpen = true;
}
e.Handled = true;
} else {
if (toolTip == null) {
toolTip = new ToolTip();
toolTip.Closed += ToolTipClosed;
}
toolTip.Content = args.ContentToShow;
toolTip.IsOpen = true;
e.Handled = true;
}
} else {
// close popup if mouse hovered over empty area
if (popup != null) {
e.Handled = true;
}
TryCloseExistingPopup(false);
}
}
bool TryCloseExistingPopup(bool mouseClick)
{
bool canClose = true;
if (popup != null) {
var popupContentITooltip = popup.Child as ITooltip;
if (popupContentITooltip != null) {
canClose = popupContentITooltip.Close(mouseClick);
}
if (canClose) {
popup.IsOpen = false;
}
}
return canClose;
}
void SetPopupPosition(Popup popup, MouseEventArgs mouseArgs)
{
var popupPosition = GetPopupPosition(mouseArgs);
popup.HorizontalOffset = popupPosition.X;
popup.VerticalOffset = popupPosition.Y;
}
/// <summary> Returns Popup position based on mouse position, in device independent units </summary>
Point GetPopupPosition(MouseEventArgs mouseArgs)
{
Point mousePos = mouseArgs.GetPosition(this);
Point positionInPixels;
// align Popup with line bottom
TextViewPosition? logicalPos = GetPositionFromPoint(mousePos);
if (logicalPos.HasValue) {
var textView = this.TextArea.TextView;
positionInPixels =
textView.PointToScreen(
textView.GetVisualPosition(logicalPos.Value, VisualYPosition.LineBottom) - textView.ScrollOffset);
positionInPixels.X -= 4;
} else {
positionInPixels = PointToScreen(mousePos + new Vector(-4, 6));
}
// use device independent units, because Popup Left/Top are in independent units
return positionInPixels.TransformFromDevice(this);
}
Popup CreatePopup()
{
popup = new Popup();
popup.Closed += PopupClosed;
popup.Placement = PlacementMode.Absolute;
popup.StaysOpen = true;
return popup;
}
void TextEditorMouseHoverStopped(object sender, MouseEventArgs e)
{
if (toolTip != null) {
toolTip.IsOpen = false;
e.Handled = true;
}
}
void TextEditorMouseLeave(object sender, MouseEventArgs e)
{
if (popup != null && !popup.IsMouseOver) {
// do not close popup if mouse moved from editor to popup
TryCloseExistingPopup(false);
}
}
void ToolTipClosed(object sender, RoutedEventArgs e)
{
toolTip = null;
}
void PopupClosed(object sender, EventArgs e)
{
popup = null;
}
#endregion
#region Ctrl+Click Go To Definition
void TextViewMouseDown(object sender, MouseButtonEventArgs e)
{
// close existing popup immediately on text editor mouse down
TryCloseExistingPopup(false);
if (e.ChangedButton == MouseButton.Left && Keyboard.Modifiers == ModifierKeys.Control) {
var position = GetPositionFromPoint(e.GetPosition(this));
if (position != null) {
GoToDefinition.Run(this.Adapter, this.Document.GetOffset(position.Value));
e.Handled = true;
}
}
}
#endregion
public void JumpTo(int line, int column)
{
TryCloseExistingPopup(true);
// the adapter sets the caret position and takes care of scrolling
this.Adapter.JumpTo(line, column);
this.Focus();
}
}
}

3
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs

@ -216,6 +216,9 @@ namespace ICSharpCode.AvalonEdit.Editing @@ -216,6 +216,9 @@ namespace ICSharpCode.AvalonEdit.Editing
/// <summary>
/// Event raised when the caret position has changed.
/// This event might be raised multiple times during a big update operation.
/// You might want to check TextDocument.IsInUpdate and delay time-consuming
/// actions until the update operation ends.
/// </summary>
public event EventHandler PositionChanged;

Loading…
Cancel
Save