#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

351 lines
11 KiB

// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.AvalonEdit.AddIn
{
/// <summary>
/// Scrollbar that shows markers.
/// </summary>
[TextEditorService]
public class EnhancedScrollBar : IDisposable
{
readonly TextEditor editor;
readonly TextMarkerService textMarkerService;
readonly IChangeWatcher changeWatcher;
TrackBackground trackBackground;
TrackAdorner trackAdorner;
public EnhancedScrollBar(TextEditor editor, TextMarkerService textMarkerService, IChangeWatcher changeWatcher)
{
if (editor == null)
throw new ArgumentNullException("editor");
this.editor = editor;
this.textMarkerService = textMarkerService;
this.changeWatcher = changeWatcher;
editor.Loaded += editor_Loaded;
if (editor.IsLoaded) {
editor_Loaded(null, null);
}
}
public void Dispose()
{
editor.Loaded -= editor_Loaded;
if (trackBackground != null) {
trackBackground.Remove();
trackBackground = null;
}
if (trackAdorner != null) {
trackAdorner.Remove();
trackAdorner = null;
}
}
#region Initialize UI
bool isUIInitialized;
void editor_Loaded(object sender, RoutedEventArgs e)
{
if (isUIInitialized)
return;
isUIInitialized = true;
editor.ApplyTemplate();
var scrollViewer = (ScrollViewer)editor.Template.FindName("PART_ScrollViewer", editor);
if (scrollViewer == null)
return;
scrollViewer.ApplyTemplate();
var vScrollBar = (ScrollBar)scrollViewer.Template.FindName("PART_VerticalScrollBar", scrollViewer);
var hScrollBar = (ScrollBar)scrollViewer.Template.FindName("PART_HorizontalScrollBar", scrollViewer);
// make both scrollbars transparent so that they look consistent
MakeThumbTransparent(vScrollBar);
MakeThumbTransparent(hScrollBar);
if (vScrollBar == null)
return;
Track track = (Track)vScrollBar.Template.FindName("PART_Track", vScrollBar);
if (track == null)
return;
Grid grid = VisualTreeHelper.GetParent(track) as Grid;
if (grid != null) {
trackBackground = new TrackBackground(this);
trackAdorner = new TrackAdorner(this);
Grid.SetColumn(trackBackground, Grid.GetColumn(track));
Grid.SetRow(trackBackground, Grid.GetRow(track));
Grid.SetColumnSpan(trackBackground, Grid.GetColumnSpan(track));
Grid.SetRowSpan(trackBackground, Grid.GetRowSpan(track));
Grid.SetColumn(trackAdorner, Grid.GetColumn(track));
Grid.SetRow(trackAdorner, Grid.GetRow(track));
Grid.SetColumnSpan(trackAdorner, Grid.GetColumnSpan(track));
Grid.SetRowSpan(trackAdorner, Grid.GetRowSpan(track));
Panel.SetZIndex(track, 1);
Panel.SetZIndex(trackAdorner, 2);
grid.Children.Add(trackBackground);
grid.Children.Add(trackAdorner);
}
}
#endregion
#region MakeThumbTransparent
List<Thumb> transparentThumbs = new List<Thumb>();
const double thumbOpacity = 0.7;
static readonly Duration animationDuration = new Duration(TimeSpan.FromSeconds(0.25));
void MakeThumbTransparent(ScrollBar scrollBar)
{
if (scrollBar == null)
return;
Track track = (Track)scrollBar.Template.FindName("PART_Track", scrollBar);
if (track == null)
return;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(track); i++) {
var thumb = VisualTreeHelper.GetChild(track, i) as Thumb;
if (thumb != null) {
thumb.Opacity = thumbOpacity;
thumb.MouseEnter += thumb_MouseEnter;
thumb.MouseLeave += thumb_MouseLeave;
transparentThumbs.Add(thumb);
break;
}
}
}
void ClearTransparencyFromThumbs()
{
foreach (var thumb in transparentThumbs) {
thumb.MouseEnter -= thumb_MouseEnter;
thumb.MouseLeave -= thumb_MouseLeave;
thumb.ClearValue(Thumb.OpacityProperty);
}
}
void thumb_MouseEnter(object sender, MouseEventArgs e)
{
var thumb = (Thumb)sender;
thumb.BeginAnimation(Thumb.OpacityProperty, new DoubleAnimation(1, animationDuration, FillBehavior.HoldEnd));
}
void thumb_MouseLeave(object sender, MouseEventArgs e)
{
var thumb = (Thumb)sender;
thumb.BeginAnimation(Thumb.OpacityProperty, new DoubleAnimation(thumbOpacity, animationDuration, FillBehavior.HoldEnd));
}
#endregion
static Brush GetBrush(Color markerColor)
{
SolidColorBrush brush = new SolidColorBrush(markerColor);
brush.Freeze();
return brush;
}
#region TrackBackground
sealed class TrackBackground : UIElement
{
readonly TextEditor editor;
readonly TextMarkerService textMarkerService;
readonly IChangeWatcher changeWatcher;
public TrackBackground(EnhancedScrollBar enhanchedScrollBar)
{
this.editor = enhanchedScrollBar.editor;
this.textMarkerService = enhanchedScrollBar.textMarkerService;
this.changeWatcher = enhanchedScrollBar.changeWatcher;
textMarkerService.RedrawRequested += textMarkerService_RedrawRequested;
}
public void Remove()
{
textMarkerService.RedrawRequested -= textMarkerService_RedrawRequested;
Grid grid = (Grid)VisualTreeHelper.GetParent(this);
grid.Children.Remove(this);
}
void textMarkerService_RedrawRequested(object sender, EventArgs e)
{
InvalidateVisual();
}
protected override void OnRender(DrawingContext drawingContext)
{
var renderSize = this.RenderSize;
var document = editor.Document;
var textView = editor.TextArea.TextView;
double documentHeight = textView.DocumentHeight;
foreach (var marker in textMarkerService.TextMarkers) {
if ((marker.MarkerTypes & (TextMarkerTypes.LineInScrollBar | TextMarkerTypes.CircleInScrollBar)) == 0)
continue;
var location = document.GetLocation(marker.StartOffset);
double visualTop = textView.GetVisualTopByDocumentLine(location.Line);
double renderPos = visualTop / documentHeight * renderSize.Height;
var brush = GetBrush(marker.MarkerColor);
if ((marker.MarkerTypes & (TextMarkerTypes.LineInScrollBar)) != 0) {
drawingContext.DrawRectangle(brush, null, new Rect(3, renderPos - 1, renderSize.Width - 6, 2));
}
if ((marker.MarkerTypes & (TextMarkerTypes.CircleInScrollBar)) != 0) {
const double radius = 3;
drawingContext.DrawEllipse(brush, null, new Point(renderSize.Width / 2, renderPos), radius, radius);
}
}
}
}
#endregion
#region TrackAdorner
sealed class TrackAdorner : FrameworkElement
{
#region TriangleGeometry
static readonly StreamGeometry triangleGeometry = CreateTriangleGeometry();
static StreamGeometry CreateTriangleGeometry()
{
var triangleGeometry = new StreamGeometry();
using (var ctx = triangleGeometry.Open()) {
const double triangleSize = 6.5;
const double right = (triangleSize * 0.866) / 2;
const double left = -right;
ctx.BeginFigure(new Point(left, triangleSize / 2), true, true);
ctx.LineTo(new Point(left, -triangleSize / 2), true, false);
ctx.LineTo(new Point(right, 0), true, false);
}
triangleGeometry.Freeze();
return triangleGeometry;
}
#endregion
readonly TextEditor editor;
readonly TextMarkerService textMarkerService;
public TrackAdorner(EnhancedScrollBar enhanchedScrollBar)
{
this.editor = enhanchedScrollBar.editor;
this.textMarkerService = enhanchedScrollBar.textMarkerService;
this.Cursor = Cursors.Hand;
this.ToolTip = string.Empty;
textMarkerService.RedrawRequested += textMarkerService_RedrawRequested;
}
public void Remove()
{
textMarkerService.RedrawRequested -= textMarkerService_RedrawRequested;
Grid grid = (Grid)VisualTreeHelper.GetParent(this);
grid.Children.Remove(this);
}
void textMarkerService_RedrawRequested(object sender, EventArgs e)
{
InvalidateVisual();
}
protected override void OnRender(DrawingContext drawingContext)
{
var renderSize = this.RenderSize;
var document = editor.Document;
var textView = editor.TextArea.TextView;
double documentHeight = textView.DocumentHeight;
foreach (var marker in textMarkerService.TextMarkers) {
if (!IsVisibleInAdorner(marker))
continue;
var location = document.GetLocation(marker.StartOffset);
double visualTop = textView.GetVisualTopByDocumentLine(location.Line);
double renderPos = visualTop / documentHeight * renderSize.Height;
var brush = GetBrush(marker.MarkerColor);
var translateTransform = new TranslateTransform(6, renderPos);
translateTransform.Freeze();
drawingContext.PushTransform(translateTransform);
if ((marker.MarkerTypes & (TextMarkerTypes.ScrollBarLeftTriangle)) != 0) {
var scaleTransform = new ScaleTransform(-1, 1);
scaleTransform.Freeze();
drawingContext.PushTransform(scaleTransform);
drawingContext.DrawGeometry(brush, null, triangleGeometry);
drawingContext.Pop();
}
if ((marker.MarkerTypes & (TextMarkerTypes.ScrollBarRightTriangle)) != 0) {
drawingContext.DrawGeometry(brush, null, triangleGeometry);
}
drawingContext.Pop();
}
}
bool IsVisibleInAdorner(ITextMarker marker)
{
return (marker.MarkerTypes & (TextMarkerTypes.ScrollBarLeftTriangle | TextMarkerTypes.ScrollBarRightTriangle)) != 0;
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
var marker = FindNextMarker(e.GetPosition(this));
if (marker != null) {
var location = editor.Document.GetLocation(marker.StartOffset);
// Use JumpTo() if possible
var textEditor = editor.TextArea.GetService(typeof(ITextEditor)) as ITextEditor;
if (textEditor != null)
textEditor.JumpTo(location.Line, location.Column);
else
editor.ScrollTo(location.Line, location.Column);
e.Handled = true;
}
}
ITextMarker FindNextMarker(Point mousePos)
{
var renderSize = this.RenderSize;
var document = editor.Document;
var textView = editor.TextArea.TextView;
double documentHeight = textView.DocumentHeight;
ITextMarker bestMarker = null;
double bestDistance = double.PositiveInfinity;
foreach (var marker in textMarkerService.TextMarkers) {
if (!IsVisibleInAdorner(marker))
continue;
var location = document.GetLocation(marker.StartOffset);
double visualTop = textView.GetVisualTopByDocumentLine(location.Line);
double renderPos = visualTop / documentHeight * renderSize.Height;
double distance = Math.Abs(renderPos - mousePos.Y);
if (distance < bestDistance) {
bestDistance = distance;
bestMarker = marker;
}
}
return bestMarker;
}
protected override void OnToolTipOpening(ToolTipEventArgs e)
{
base.OnToolTipOpening(e);
var marker = FindNextMarker(Mouse.GetPosition(this));
if (marker != null && marker.ToolTip != null) {
this.ToolTip = marker.ToolTip;
} else {
// prevent tooltip from opening
e.Handled = true;
}
}
}
#endregion
}
}