9 changed files with 345 additions and 34 deletions
@ -0,0 +1,277 @@ |
|||||||
|
// 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 ICSharpCode.SharpDevelop.Editor; |
||||||
|
|
||||||
|
namespace ICSharpCode.AvalonEdit.AddIn |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Scrollbar that shows markers.
|
||||||
|
/// </summary>
|
||||||
|
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); |
||||||
|
Grid.SetColumn(trackBackground, Grid.GetColumn(track)); |
||||||
|
Grid.SetRow(trackBackground, Grid.GetRow(track)); |
||||||
|
Grid.SetColumnSpan(trackBackground, Grid.GetColumnSpan(track)); |
||||||
|
Grid.SetRowSpan(trackBackground, Grid.GetRowSpan(track)); |
||||||
|
Panel.SetZIndex(track, 1); |
||||||
|
grid.Children.Add(trackBackground); |
||||||
|
} |
||||||
|
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(track); |
||||||
|
if (adornerLayer != null) { |
||||||
|
trackAdorner = new TrackAdorner(this, adornerLayer, track); |
||||||
|
} |
||||||
|
} |
||||||
|
#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 : Adorner |
||||||
|
{ |
||||||
|
readonly AdornerLayer adornerLayer; |
||||||
|
readonly TextEditor editor; |
||||||
|
readonly TextMarkerService textMarkerService; |
||||||
|
readonly StreamGeometry triangleGeometry; |
||||||
|
|
||||||
|
public TrackAdorner(EnhancedScrollBar enhanchedScrollBar, AdornerLayer adornerLayer, UIElement adornedElement) |
||||||
|
: base(adornedElement) |
||||||
|
{ |
||||||
|
this.adornerLayer = adornerLayer; |
||||||
|
this.editor = enhanchedScrollBar.editor; |
||||||
|
this.textMarkerService = enhanchedScrollBar.textMarkerService; |
||||||
|
|
||||||
|
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(); |
||||||
|
|
||||||
|
adornerLayer.Add(this); |
||||||
|
textMarkerService.RedrawRequested += textMarkerService_RedrawRequested; |
||||||
|
} |
||||||
|
|
||||||
|
public void Remove() |
||||||
|
{ |
||||||
|
textMarkerService.RedrawRequested -= textMarkerService_RedrawRequested; |
||||||
|
adornerLayer.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.ScrollBarLeftTriangle | TextMarkerTypes.ScrollBarRightTriangle)) == 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); |
||||||
|
|
||||||
|
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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue