diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/IssueManager.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/IssueManager.cs
index 130ab1d8a7..9546c32a4b 100644
--- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/IssueManager.cs
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/IssueManager.cs
@@ -166,12 +166,14 @@ namespace CSharpBinding.Refactoring
return;
marker = markerService.Create(startOffset, endOffset - startOffset);
marker.ToolTip = this.Description;
+
+ Color color = GetColor(this.Severity);
+ color.A = 186;
+ marker.MarkerColor = color;
+ marker.MarkerTypes = TextMarkerTypes.ScrollBarRightTriangle;
switch (Provider.DefaultMarker) {
case IssueMarker.Underline:
- Color underlineColor = GetColor(this.Severity);
- underlineColor.A = 186;
- marker.MarkerType = TextMarkerType.SquigglyUnderline;
- marker.MarkerColor = underlineColor;
+ marker.MarkerTypes |= TextMarkerTypes.SquigglyUnderline;
break;
case IssueMarker.GrayOut:
marker.ForegroundColor = SystemColors.GrayTextColor;
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
index fad33ab8b8..d0c5359736 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
@@ -98,6 +98,7 @@
+
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
index 5c25dd4acd..cbb134bd4d 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
@@ -213,6 +213,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
if (changeWatcher != null) {
codeEditorView.TextArea.LeftMargins.Add(new ChangeMarkerMargin(changeWatcher));
}
+ textView.Services.AddService(typeof(EnhancedScrollBar), new EnhancedScrollBar(codeEditorView, textMarkerService, changeWatcher));
codeEditorView.TextArea.MouseRightButtonDown += TextAreaMouseRightButtonDown;
codeEditorView.TextArea.ContextMenuOpening += TextAreaContextMenuOpening;
@@ -234,6 +235,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
{
foreach (var d in textEditor.TextArea.LeftMargins.OfType())
d.Dispose();
+ ((EnhancedScrollBar)textEditor.TextArea.GetService(typeof(EnhancedScrollBar))).Dispose();
textEditor.Dispose();
}
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/EnhancedScrollBar.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/EnhancedScrollBar.cs
new file mode 100644
index 0000000000..c57e7fadb5
--- /dev/null
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/EnhancedScrollBar.cs
@@ -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
+{
+ ///
+ /// Scrollbar that shows markers.
+ ///
+ 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 transparentThumbs = new List();
+ 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
+ }
+}
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs
index 42c691d64c..e81846ba08 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs
@@ -118,7 +118,11 @@ namespace ICSharpCode.AvalonEdit.AddIn
internal void Redraw(ISegment segment)
{
codeEditor.Redraw(segment, DispatcherPriority.Normal);
+ if (RedrawRequested != null)
+ RedrawRequested(this, EventArgs.Empty);
}
+
+ public event EventHandler RedrawRequested;
#endregion
#region DocumentColorizingTransformer
@@ -182,30 +186,31 @@ namespace ICSharpCode.AvalonEdit.AddIn
drawingContext.DrawGeometry(brush, null, geometry);
}
}
- if (marker.MarkerType != TextMarkerType.None) {
+ if ((marker.MarkerTypes & (TextMarkerTypes.SquigglyUnderline | TextMarkerTypes.NormalUnderline)) != 0) {
foreach (Rect r in BackgroundGeometryBuilder.GetRectsForSegment(textView, marker)) {
Point startPoint = r.BottomLeft;
Point endPoint = r.BottomRight;
Pen usedPen = new Pen(new SolidColorBrush(marker.MarkerColor), 1);
usedPen.Freeze();
- switch (marker.MarkerType) {
- case TextMarkerType.SquigglyUnderline:
- double offset = 2.5;
-
- int count = Math.Max((int)((endPoint.X - startPoint.X) / offset) + 1, 4);
-
- StreamGeometry geometry = new StreamGeometry();
-
- using (StreamGeometryContext ctx = geometry.Open()) {
- ctx.BeginFigure(startPoint, false, false);
- ctx.PolyLineTo(CreatePoints(startPoint, endPoint, offset, count).ToArray(), true, false);
- }
-
- geometry.Freeze();
-
- drawingContext.DrawGeometry(Brushes.Transparent, usedPen, geometry);
- break;
+ if ((marker.MarkerTypes & TextMarkerTypes.SquigglyUnderline) != 0) {
+ double offset = 2.5;
+
+ int count = Math.Max((int)((endPoint.X - startPoint.X) / offset) + 1, 4);
+
+ StreamGeometry geometry = new StreamGeometry();
+
+ using (StreamGeometryContext ctx = geometry.Open()) {
+ ctx.BeginFigure(startPoint, false, false);
+ ctx.PolyLineTo(CreatePoints(startPoint, endPoint, offset, count).ToArray(), true, false);
+ }
+
+ geometry.Freeze();
+
+ drawingContext.DrawGeometry(Brushes.Transparent, usedPen, geometry);
+ }
+ if ((marker.MarkerTypes & TextMarkerTypes.NormalUnderline) != 0) {
+ drawingContext.DrawLine(usedPen, startPoint, endPoint);
}
}
}
@@ -231,7 +236,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
this.service = service;
this.StartOffset = startOffset;
this.Length = length;
- this.markerType = TextMarkerType.None;
+ this.markerTypes = TextMarkerTypes.None;
}
public event EventHandler Deleted;
@@ -282,13 +287,13 @@ namespace ICSharpCode.AvalonEdit.AddIn
public object Tag { get; set; }
- TextMarkerType markerType;
+ TextMarkerTypes markerTypes;
- public TextMarkerType MarkerType {
- get { return markerType; }
+ public TextMarkerTypes MarkerTypes {
+ get { return markerTypes; }
set {
- if (markerType != value) {
- markerType = value;
+ if (markerTypes != value) {
+ markerTypes = value;
Redraw();
}
}
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/themes/generic.xaml b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/themes/generic.xaml
index 9ea4a275ea..d16f35dace 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/themes/generic.xaml
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/themes/generic.xaml
@@ -27,7 +27,7 @@
BorderThickness="{TemplateBinding BorderThickness}"
TextOptions.TextFormattingMode="{Binding CurrentZoom, ElementName=PART_ScrollViewer, Converter={x:Static local:ZoomLevelToTextFormattingModeConverter.Instance}}">
-
diff --git a/src/Main/Base/Project/Src/Editor/ITextMarker.cs b/src/Main/Base/Project/Src/Editor/ITextMarker.cs
index 286b2c9b5e..5f308934b6 100644
--- a/src/Main/Base/Project/Src/Editor/ITextMarker.cs
+++ b/src/Main/Base/Project/Src/Editor/ITextMarker.cs
@@ -55,7 +55,7 @@ namespace ICSharpCode.SharpDevelop.Editor
///
/// Gets/Sets the type of the marker. Use TextMarkerType.None for normal markers.
///
- TextMarkerType MarkerType { get; set; }
+ TextMarkerTypes MarkerTypes { get; set; }
///
/// Gets/Sets the color of the marker.
@@ -73,16 +73,38 @@ namespace ICSharpCode.SharpDevelop.Editor
object ToolTip { get; set; }
}
- public enum TextMarkerType
+ [Flags]
+ public enum TextMarkerTypes
{
///
/// Use no marker
///
- None,
+ None = 0x0000,
///
/// Use squiggly underline marker
///
- SquigglyUnderline
+ SquigglyUnderline = 0x001,
+ ///
+ /// Normal underline.
+ ///
+ NormalUnderline = 0x002,
+
+ ///
+ /// Horizontal line in the scroll bar.
+ ///
+ LineInScrollBar = 0x0100,
+ ///
+ /// Small triangle in the scroll bar, pointing to the right.
+ ///
+ ScrollBarRightTriangle = 0x0400,
+ ///
+ /// Small triangle in the scroll bar, pointing to the left.
+ ///
+ ScrollBarLeftTriangle = 0x0800,
+ ///
+ /// Small circle in the scroll bar.
+ ///
+ CircleInScrollBar = 0x1000
}
public interface ITextMarkerService
diff --git a/src/Main/Base/Project/Src/Services/Debugger/BreakpointBookmark.cs b/src/Main/Base/Project/Src/Services/Debugger/BreakpointBookmark.cs
index a6237510fe..1e6df4f14e 100644
--- a/src/Main/Base/Project/Src/Services/Debugger/BreakpointBookmark.cs
+++ b/src/Main/Base/Project/Src/Services/Debugger/BreakpointBookmark.cs
@@ -113,6 +113,8 @@ namespace ICSharpCode.SharpDevelop.Debugging
ITextMarker marker = markerService.Create(line.Offset, line.Length);
marker.BackgroundColor = Color.FromRgb(180, 38, 38);
marker.ForegroundColor = Colors.White;
+ marker.MarkerColor = Color.FromRgb(180, 38, 38);
+ marker.MarkerTypes = TextMarkerTypes.CircleInScrollBar;
return marker;
}
diff --git a/src/Main/Base/Project/Src/Services/Tasks/ErrorPainter.cs b/src/Main/Base/Project/Src/Services/Tasks/ErrorPainter.cs
index 8c082cf997..8b0f1e0deb 100644
--- a/src/Main/Base/Project/Src/Services/Tasks/ErrorPainter.cs
+++ b/src/Main/Base/Project/Src/Services/Tasks/ErrorPainter.cs
@@ -160,7 +160,7 @@ namespace ICSharpCode.SharpDevelop
}
marker.MarkerColor = markerColor;
- marker.MarkerType = TextMarkerType.SquigglyUnderline;
+ marker.MarkerTypes = TextMarkerTypes.SquigglyUnderline | TextMarkerTypes.LineInScrollBar;
marker.ToolTip = task.Description;