9 changed files with 345 additions and 34 deletions
@ -0,0 +1,277 @@
@@ -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