Skip to content

Commit

Permalink
Added drag support to the triple-click mouse processor.
Browse files Browse the repository at this point in the history
Also, changed the logic to select lines *including* their line breaks, and put the caret at the beginning of selections (otherwise, the caret draws on the line following the visible selection, and looks awkward).
  • Loading branch information
Noah Richards committed Jan 12, 2010
1 parent d87c98b commit 6df0e36
Showing 1 changed file with 91 additions and 7 deletions.
98 changes: 91 additions & 7 deletions TripleClickMouseProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
/* TripleClickMouseProcessor.cs
* Copyright Noah Richards, licensed under the Ms-PL.
* Check out blogs.msdn.com/noahric for more information about the Visual Studio 2010 editor!
*
* Check out blogs.msdn.com/noahric for more information about the Visual Studio 2010 editor.
*/
using System.ComponentModel.Composition;
using System.Windows;
using System.Windows.Input;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Utilities;
using System;
using Microsoft.VisualStudio.Text.Operations;
using Microsoft.VisualStudio.Text.Formatting;

namespace TripleClick
{
Expand All @@ -27,41 +31,121 @@ public IMouseProcessor GetAssociatedProcessor(IWpfTextView wpfTextView)
internal sealed class TripleClickMouseProcessor : MouseProcessorBase
{
private IWpfTextView _view;
private bool _dragging;
private SnapshotSpan? _originalLine;

public TripleClickMouseProcessor(IWpfTextView view)
{
_view = view;

_view.LayoutChanged += (sender, args) =>
{
if (_dragging && args.OldSnapshot != args.NewSnapshot)
StopDrag();
};
}

#region Overrides

public override void PreprocessMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (e.ClickCount != 3)
return;

Point viewPoint = RelativeToView(e.GetPosition(_view.VisualElement));
var line = GetTextViewLineUnderPoint(e);
if (line == null)
return;

SnapshotSpan extent = line.ExtentIncludingLineBreak;

SelectSpan(extent);
StartDrag(extent);

e.Handled = true;
}

public override void PreprocessMouseMove(MouseEventArgs e)
{
if (!_dragging || e.LeftButton != MouseButtonState.Pressed)
return;

var line = _view.TextViewLines.GetTextViewLineContainingYCoordinate(viewPoint.Y);
// If somehow the drag wasn't set up correctly or the view has changed, stop the drag immediately
if (_originalLine == null || _originalLine.Value.Snapshot != _view.TextSnapshot)
{
StopDrag();
return;
}

var line = GetTextViewLineUnderPoint(e);
if (line == null)
return;

var extent = line.Extent;
SnapshotSpan newExtent = line.ExtentIncludingLineBreak;

// Calculate the new extent, using the original span
int start = Math.Min(newExtent.Start, _originalLine.Value.Start);
int end = Math.Max(newExtent.End, _originalLine.Value.End);

SelectSpan(new SnapshotSpan(_view.TextSnapshot, Span.FromBounds(start, end)));

e.Handled = true;
}

public override void PostprocessMouseLeftButtonUp(MouseButtonEventArgs e)
{
StopDrag();
}

#endregion

#region Helpers

ITextViewLine GetTextViewLineUnderPoint(MouseEventArgs e)
{
Point viewPoint = RelativeToView(e.GetPosition(_view.VisualElement));

return _view.TextViewLines.GetTextViewLineContainingYCoordinate(viewPoint.Y);
}

void SelectSpan(SnapshotSpan extent)
{
if (!extent.IsEmpty)
{
_view.Selection.Select(extent, false);
_view.Selection.Select(extent, true);
_view.Caret.MoveTo(_view.Selection.ActivePoint);
}
else
{
_view.Selection.Clear();
_view.Caret.MoveTo(extent.Start.TranslateTo(_view.TextSnapshot, PointTrackingMode.Negative));
}
}

void StartDrag(SnapshotSpan extent)
{
_dragging = _view.VisualElement.CaptureMouse();

if (_dragging)
{
_originalLine = extent;
}
}

e.Handled = true;
void StopDrag()
{
if (_dragging)
{
_view.VisualElement.ReleaseMouseCapture();
_originalLine = null;
_dragging = false;
}
}

Point RelativeToView(Point position)
{
return new Point(position.X - _view.ViewportLeft, position.Y - _view.ViewportTop);
}

#endregion
}
}
}

0 comments on commit 6df0e36

Please sign in to comment.