mirror of https://github.com/icsharpcode/ILSpy.git
12 changed files with 1924 additions and 3 deletions
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Windows; |
||||
|
||||
namespace ILSpy.Debugger.AvalonEdit |
||||
{ |
||||
/// <summary>
|
||||
/// Content of text editor tooltip (used as <see cref="ToolTipRequestEventArgs.ContentToShow"/>),
|
||||
/// specifying whether it should be displayed in a WPF Popup.
|
||||
/// </summary>
|
||||
public interface ITooltip |
||||
{ |
||||
/// <summary>
|
||||
/// If true, this ITooltip will be displayed in a WPF Popup.
|
||||
/// Otherwise it will be displayed in a WPF Tooltip.
|
||||
/// WPF Popups are (unlike WPF Tooltips) focusable.
|
||||
/// </summary>
|
||||
bool ShowAsPopup { get; } |
||||
|
||||
/// <summary>
|
||||
/// Closes this tooltip.
|
||||
/// </summary>
|
||||
/// <param name="mouseClick">True if close request is raised
|
||||
/// because of mouse click on some SharpDevelop GUI element.</param>
|
||||
/// <returns>True if Close succeeded (that is, can close). False otherwise.</returns>
|
||||
bool Close(bool mouseClick); |
||||
|
||||
/// <summary>
|
||||
/// Occurs when this tooltip decides to close.
|
||||
/// </summary>
|
||||
event RoutedEventHandler Closed; |
||||
} |
||||
} |
@ -0,0 +1,417 @@
@@ -0,0 +1,417 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections; |
||||
using System.Collections.Generic; |
||||
using System.Collections.ObjectModel; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Windows; |
||||
using System.Windows.Controls; |
||||
using System.Windows.Media; |
||||
using System.Xml; |
||||
using System.Xml.Linq; |
||||
|
||||
namespace ILSpy.Debugger.Services |
||||
{ |
||||
/// <summary>
|
||||
/// Extension methods used in SharpDevelop.
|
||||
/// </summary>
|
||||
public static class ExtensionMethods |
||||
{ |
||||
/// <summary>
|
||||
/// Raises the event.
|
||||
/// Does nothing if eventHandler is null.
|
||||
/// Because the event handler is passed as parameter, it is only fetched from the event field one time.
|
||||
/// This makes
|
||||
/// <code>MyEvent.RaiseEvent(x,y);</code>
|
||||
/// thread-safe
|
||||
/// whereas
|
||||
/// <code>if (MyEvent != null) MyEvent(x,y);</code>
|
||||
/// would not be safe.
|
||||
/// </summary>
|
||||
/// <remarks>Using this method is only thread-safe under the Microsoft .NET memory model,
|
||||
/// not under the less strict memory model in the CLI specification.</remarks>
|
||||
public static void RaiseEvent(this EventHandler eventHandler, object sender, EventArgs e) |
||||
{ |
||||
if (eventHandler != null) { |
||||
eventHandler(sender, e); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Raises the event.
|
||||
/// Does nothing if eventHandler is null.
|
||||
/// Because the event handler is passed as parameter, it is only fetched from the event field one time.
|
||||
/// This makes
|
||||
/// <code>MyEvent.RaiseEvent(x,y);</code>
|
||||
/// thread-safe
|
||||
/// whereas
|
||||
/// <code>if (MyEvent != null) MyEvent(x,y);</code>
|
||||
/// would not be safe.
|
||||
/// </summary>
|
||||
public static void RaiseEvent<T>(this EventHandler<T> eventHandler, object sender, T e) where T : EventArgs |
||||
{ |
||||
if (eventHandler != null) { |
||||
eventHandler(sender, e); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Runs an action for all elements in the input.
|
||||
/// </summary>
|
||||
public static void ForEach<T>(this IEnumerable<T> input, Action<T> action) |
||||
{ |
||||
if (input == null) |
||||
throw new ArgumentNullException("input"); |
||||
foreach (T element in input) { |
||||
action(element); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Adds all <paramref name="elements"/> to <paramref name="list"/>.
|
||||
/// </summary>
|
||||
public static void AddRange<T>(this ICollection<T> list, IEnumerable<T> elements) |
||||
{ |
||||
foreach (T o in elements) |
||||
list.Add(o); |
||||
} |
||||
|
||||
public static ReadOnlyCollection<T> AsReadOnly<T>(this IList<T> arr) |
||||
{ |
||||
return new ReadOnlyCollection<T>(arr); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Converts a recursive data structure into a flat list.
|
||||
/// </summary>
|
||||
/// <param name="input">The root elements of the recursive data structure.</param>
|
||||
/// <param name="recursion">The function that gets the children of an element.</param>
|
||||
/// <returns>Iterator that enumerates the tree structure in preorder.</returns>
|
||||
public static IEnumerable<T> Flatten<T>(this IEnumerable<T> input, Func<T, IEnumerable<T>> recursion) |
||||
{ |
||||
Stack<IEnumerator<T>> stack = new Stack<IEnumerator<T>>(); |
||||
try { |
||||
stack.Push(input.GetEnumerator()); |
||||
while (stack.Count > 0) { |
||||
while (stack.Peek().MoveNext()) { |
||||
T element = stack.Peek().Current; |
||||
yield return element; |
||||
IEnumerable<T> children = recursion(element); |
||||
if (children != null) { |
||||
stack.Push(children.GetEnumerator()); |
||||
} |
||||
} |
||||
stack.Pop().Dispose(); |
||||
} |
||||
} finally { |
||||
while (stack.Count > 0) { |
||||
stack.Pop().Dispose(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates an array containing a part of the array (similar to string.Substring).
|
||||
/// </summary>
|
||||
public static T[] Splice<T>(this T[] array, int startIndex) |
||||
{ |
||||
if (array == null) |
||||
throw new ArgumentNullException("array"); |
||||
return Splice(array, startIndex, array.Length - startIndex); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates an array containing a part of the array (similar to string.Substring).
|
||||
/// </summary>
|
||||
public static T[] Splice<T>(this T[] array, int startIndex, int length) |
||||
{ |
||||
if (array == null) |
||||
throw new ArgumentNullException("array"); |
||||
if (startIndex < 0 || startIndex > array.Length) |
||||
throw new ArgumentOutOfRangeException("startIndex", startIndex, "Value must be between 0 and " + array.Length); |
||||
if (length < 0 || length > array.Length - startIndex) |
||||
throw new ArgumentOutOfRangeException("length", length, "Value must be between 0 and " + (array.Length - startIndex)); |
||||
T[] result = new T[length]; |
||||
Array.Copy(array, startIndex, result, 0, length); |
||||
return result; |
||||
} |
||||
|
||||
#region System.Drawing <-> WPF conversions
|
||||
public static System.Drawing.Point ToSystemDrawing(this Point p) |
||||
{ |
||||
return new System.Drawing.Point((int)p.X, (int)p.Y); |
||||
} |
||||
|
||||
public static System.Drawing.Size ToSystemDrawing(this Size s) |
||||
{ |
||||
return new System.Drawing.Size((int)s.Width, (int)s.Height); |
||||
} |
||||
|
||||
public static System.Drawing.Rectangle ToSystemDrawing(this Rect r) |
||||
{ |
||||
return new System.Drawing.Rectangle(r.TopLeft.ToSystemDrawing(), r.Size.ToSystemDrawing()); |
||||
} |
||||
|
||||
public static System.Drawing.Color ToSystemDrawing(this System.Windows.Media.Color c) |
||||
{ |
||||
return System.Drawing.Color.FromArgb(c.A, c.R, c.G, c.B); |
||||
} |
||||
|
||||
public static Point ToWpf(this System.Drawing.Point p) |
||||
{ |
||||
return new Point(p.X, p.Y); |
||||
} |
||||
|
||||
public static Size ToWpf(this System.Drawing.Size s) |
||||
{ |
||||
return new Size(s.Width, s.Height); |
||||
} |
||||
|
||||
public static Rect ToWpf(this System.Drawing.Rectangle rect) |
||||
{ |
||||
return new Rect(rect.Location.ToWpf(), rect.Size.ToWpf()); |
||||
} |
||||
|
||||
public static System.Windows.Media.Color ToWpf(this System.Drawing.Color c) |
||||
{ |
||||
return System.Windows.Media.Color.FromArgb(c.A, c.R, c.G, c.B); |
||||
} |
||||
#endregion
|
||||
|
||||
#region DPI independence
|
||||
public static Rect TransformToDevice(this Rect rect, Visual visual) |
||||
{ |
||||
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice; |
||||
return Rect.Transform(rect, matrix); |
||||
} |
||||
|
||||
public static Rect TransformFromDevice(this Rect rect, Visual visual) |
||||
{ |
||||
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice; |
||||
return Rect.Transform(rect, matrix); |
||||
} |
||||
|
||||
public static Size TransformToDevice(this Size size, Visual visual) |
||||
{ |
||||
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice; |
||||
return new Size(size.Width * matrix.M11, size.Height * matrix.M22); |
||||
} |
||||
|
||||
public static Size TransformFromDevice(this Size size, Visual visual) |
||||
{ |
||||
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice; |
||||
return new Size(size.Width * matrix.M11, size.Height * matrix.M22); |
||||
} |
||||
|
||||
public static Point TransformToDevice(this Point point, Visual visual) |
||||
{ |
||||
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice; |
||||
return new Point(point.X * matrix.M11, point.Y * matrix.M22); |
||||
} |
||||
|
||||
public static Point TransformFromDevice(this Point point, Visual visual) |
||||
{ |
||||
Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice; |
||||
return new Point(point.X * matrix.M11, point.Y * matrix.M22); |
||||
} |
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Removes <param name="stringToRemove" /> from the start of this string.
|
||||
/// Throws ArgumentException if this string does not start with <param name="stringToRemove" />.
|
||||
/// </summary>
|
||||
public static string RemoveStart(this string s, string stringToRemove) |
||||
{ |
||||
if (s == null) |
||||
return null; |
||||
if (string.IsNullOrEmpty(stringToRemove)) |
||||
return s; |
||||
if (!s.StartsWith(stringToRemove)) |
||||
throw new ArgumentException(string.Format("{0} does not start with {1}", s, stringToRemove)); |
||||
return s.Substring(stringToRemove.Length); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Removes <param name="stringToRemove" /> from the end of this string.
|
||||
/// Throws ArgumentException if this string does not end with <param name="stringToRemove" />.
|
||||
/// </summary>
|
||||
public static string RemoveEnd(this string s, string stringToRemove) |
||||
{ |
||||
if (s == null) |
||||
return null; |
||||
if (string.IsNullOrEmpty(stringToRemove)) |
||||
return s; |
||||
if (!s.EndsWith(stringToRemove)) |
||||
throw new ArgumentException(string.Format("{0} does not end with {1}", s, stringToRemove)); |
||||
return s.Substring(0, s.Length - stringToRemove.Length); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Takes at most <param name="length" /> first characters from string.
|
||||
/// String can be null.
|
||||
/// </summary>
|
||||
public static string TakeStart(this string s, int length) |
||||
{ |
||||
if (string.IsNullOrEmpty(s) || length >= s.Length) |
||||
return s; |
||||
return s.Substring(0, length); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Takes at most <param name="length" /> first characters from string, and appends '...' if string is longer.
|
||||
/// String can be null.
|
||||
/// </summary>
|
||||
public static string TakeStartEllipsis(this string s, int length) |
||||
{ |
||||
if (string.IsNullOrEmpty(s) || length >= s.Length) |
||||
return s; |
||||
return s.Substring(0, length) + "..."; |
||||
} |
||||
|
||||
|
||||
public static string Replace(this string original, string pattern, string replacement, StringComparison comparisonType) |
||||
{ |
||||
if (original == null) |
||||
throw new ArgumentNullException("original"); |
||||
if (pattern == null) |
||||
throw new ArgumentNullException("pattern"); |
||||
if (pattern.Length == 0) |
||||
throw new ArgumentException("String cannot be of zero length.", "pattern"); |
||||
if (comparisonType != StringComparison.Ordinal && comparisonType != StringComparison.OrdinalIgnoreCase) |
||||
throw new NotSupportedException("Currently only ordinal comparisons are implemented."); |
||||
|
||||
StringBuilder result = new StringBuilder(original.Length); |
||||
int currentPos = 0; |
||||
int nextMatch = original.IndexOf(pattern, comparisonType); |
||||
while (nextMatch >= 0) { |
||||
result.Append(original, currentPos, nextMatch - currentPos); |
||||
// The following line restricts this method to ordinal comparisons:
|
||||
// for non-ordinal comparisons, the match length might be different than the pattern length.
|
||||
currentPos = nextMatch + pattern.Length; |
||||
result.Append(replacement); |
||||
|
||||
nextMatch = original.IndexOf(pattern, currentPos, comparisonType); |
||||
} |
||||
|
||||
result.Append(original, currentPos, original.Length - currentPos); |
||||
return result.ToString(); |
||||
} |
||||
|
||||
public static byte[] GetBytesWithPreamble(this Encoding encoding, string text) |
||||
{ |
||||
byte[] encodedText = encoding.GetBytes(text); |
||||
byte[] bom = encoding.GetPreamble(); |
||||
if (bom != null && bom.Length > 0) { |
||||
byte[] result = new byte[bom.Length + encodedText.Length]; |
||||
bom.CopyTo(result, 0); |
||||
encodedText.CopyTo(result, bom.Length); |
||||
return result; |
||||
} else { |
||||
return encodedText; |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Returns the index of the first element for which <paramref name="predicate"/> returns true.
|
||||
/// If none of the items in the list fits the <paramref name="predicate"/>, -1 is returned.
|
||||
/// </summary>
|
||||
public static int FindIndex<T>(this IList<T> list, Func<T, bool> predicate) |
||||
{ |
||||
for (int i = 0; i < list.Count; i++) { |
||||
if (predicate(list[i])) |
||||
return i; |
||||
} |
||||
|
||||
return -1; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Adds item to the list if the item is not null.
|
||||
/// </summary>
|
||||
public static void AddIfNotNull<T>(this IList<T> list, T itemToAdd) |
||||
{ |
||||
if (itemToAdd != null) |
||||
list.Add(itemToAdd); |
||||
} |
||||
|
||||
public static void WriteTo(this Stream sourceStream, Stream targetStream) |
||||
{ |
||||
byte[] buffer = new byte[4096]; |
||||
int bytes; |
||||
while ((bytes = sourceStream.Read(buffer, 0, buffer.Length)) > 0) |
||||
targetStream.Write(buffer, 0, bytes); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Scrolling related helpers.
|
||||
/// </summary>
|
||||
public static class ScrollUtils |
||||
{ |
||||
/// <summary>
|
||||
/// Searches VisualTree of given object for a ScrollViewer.
|
||||
/// </summary>
|
||||
public static ScrollViewer GetScrollViewer(this DependencyObject o) |
||||
{ |
||||
var scrollViewer = o as ScrollViewer; |
||||
if (scrollViewer != null) { |
||||
return scrollViewer; |
||||
} |
||||
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++) |
||||
{ |
||||
var child = VisualTreeHelper.GetChild(o, i); |
||||
var result = GetScrollViewer(child); |
||||
if (result != null) { |
||||
return result; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Scrolls ScrollViewer up by given offset.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset by which to scroll up. Should be positive.</param>
|
||||
public static void ScrollUp(this ScrollViewer scrollViewer, double offset) |
||||
{ |
||||
ScrollUtils.ScrollByVerticalOffset(scrollViewer, -offset); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Scrolls ScrollViewer down by given offset.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset by which to scroll down. Should be positive.</param>
|
||||
public static void ScrollDown(this ScrollViewer scrollViewer, double offset) |
||||
{ |
||||
ScrollUtils.ScrollByVerticalOffset(scrollViewer, offset); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Scrolls ScrollViewer by given vertical offset.
|
||||
/// </summary>
|
||||
public static void ScrollByVerticalOffset(this ScrollViewer scrollViewer, double offset) |
||||
{ |
||||
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + offset); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,98 @@
@@ -0,0 +1,98 @@
|
||||
// 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.Controls.Primitives; |
||||
|
||||
using ICSharpCode.NRefactory.CSharp; |
||||
using ILSpy.Debugger.Models.TreeModel; |
||||
|
||||
namespace ILSpy.Debugger.Tooltips |
||||
{ |
||||
/// <summary>
|
||||
/// Popup containing <see cref="DebuggerTooltipControl"></see>.
|
||||
/// </summary>
|
||||
public class DebuggerPopup : Popup |
||||
{ |
||||
internal DebuggerTooltipControl contentControl; |
||||
|
||||
public DebuggerPopup(DebuggerTooltipControl parentControl, AstLocation logicalPosition, bool showPins = true) |
||||
{ |
||||
this.contentControl = new DebuggerTooltipControl(parentControl, logicalPosition, showPins); |
||||
this.contentControl.containingPopup = this; |
||||
this.Child = this.contentControl; |
||||
this.IsLeaf = false; |
||||
|
||||
//this.KeyDown += new KeyEventHandler(DebuggerPopup_KeyDown);
|
||||
|
||||
//this.contentControl.Focusable = true;
|
||||
//Keyboard.Focus(this.contentControl);
|
||||
//this.AllowsTransparency = true;
|
||||
//this.PopupAnimation = PopupAnimation.Slide;
|
||||
} |
||||
|
||||
// attempt to propagate shortcuts to main windows when Popup is focusable (needed for keyboard scrolling + editing)
|
||||
/*void DebuggerPopup_KeyDown(object sender, KeyEventArgs e) |
||||
{ |
||||
LoggingService.Debug("Unhandled popup key down: " + e.Key); |
||||
RaiseEventPair(WorkbenchSingleton.MainWindow, PreviewKeyDownEvent, KeyDownEvent, |
||||
new KeyEventArgs(e.KeyboardDevice, e.InputSource, e.Timestamp, e.Key)); |
||||
} |
||||
|
||||
// copied from CompletionWindowBase
|
||||
static bool RaiseEventPair(UIElement target, RoutedEvent previewEvent, RoutedEvent @event, RoutedEventArgs args) |
||||
{ |
||||
if (target == null) |
||||
throw new ArgumentNullException("target"); |
||||
if (previewEvent == null) |
||||
throw new ArgumentNullException("previewEvent"); |
||||
if (@event == null) |
||||
throw new ArgumentNullException("event"); |
||||
if (args == null) |
||||
throw new ArgumentNullException("args"); |
||||
args.RoutedEvent = previewEvent; |
||||
target.RaiseEvent(args); |
||||
args.RoutedEvent = @event; |
||||
target.RaiseEvent(args); |
||||
return args.Handled; |
||||
}*/ |
||||
|
||||
public IEnumerable<ITreeNode> ItemsSource |
||||
{ |
||||
get { return this.contentControl.ItemsSource; } |
||||
set { this.contentControl.SetItemsSource(value); } |
||||
} |
||||
|
||||
private bool isLeaf; |
||||
public bool IsLeaf |
||||
{ |
||||
get { return isLeaf; } |
||||
set |
||||
{ |
||||
isLeaf = value; |
||||
// leaf popup closes on lost focus
|
||||
this.StaysOpen = !isLeaf; |
||||
} |
||||
} |
||||
|
||||
protected override void OnClosed(EventArgs e) |
||||
{ |
||||
base.OnClosed(e); |
||||
if (isLeaf) { |
||||
this.contentControl.CloseOnLostFocus(); |
||||
} |
||||
} |
||||
|
||||
public void Open() |
||||
{ |
||||
this.IsOpen = true; |
||||
} |
||||
|
||||
public void CloseSelfAndChildren() |
||||
{ |
||||
this.contentControl.CloseChildPopups(); |
||||
this.IsOpen = false; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,230 @@
@@ -0,0 +1,230 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<UserControl |
||||
x:Class="ILSpy.Debugger.Tooltips.DebuggerTooltipControl" |
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
xmlns:aero="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" |
||||
xmlns:debugging="clr-namespace:ILSpy.Debugger.Tooltips" |
||||
xmlns:localControls="clr-namespace:ILSpy.Debugger.Tooltips" |
||||
Background="Transparent"> |
||||
<UserControl.Resources> |
||||
<ResourceDictionary> |
||||
<ResourceDictionary.MergedDictionaries> |
||||
<ResourceDictionary |
||||
Source="VisualizerPicker.xaml" /> |
||||
<ResourceDictionary |
||||
Source="PinControlsDictionary.xaml" /> |
||||
</ResourceDictionary.MergedDictionaries> |
||||
</ResourceDictionary> |
||||
</UserControl.Resources> |
||||
<StackPanel |
||||
Orientation="Vertical"> |
||||
<RepeatButton |
||||
Name="btnUp" |
||||
Focusable="False" |
||||
Style="{StaticResource upButtonStyle}" |
||||
Content="^" |
||||
Click="BtnUp_Click"></RepeatButton> |
||||
<DataGrid |
||||
VerticalScrollBarVisibility="Disabled" |
||||
HorizontalScrollBarVisibility="Disabled" |
||||
GridLinesVisibility="None" |
||||
RowHeight="18" |
||||
MaxHeight="202" |
||||
SelectionMode="Single" |
||||
SelectionUnit="FullRow" |
||||
ItemsSource="{Binding}" |
||||
Name="dataGrid" |
||||
AutoGenerateColumns="False" |
||||
CanUserAddRows="False" |
||||
HeadersVisibility="None" |
||||
BorderBrush="Gray" |
||||
BorderThickness="1"> |
||||
<DataGrid.Background> |
||||
<!-- Control backgound --> |
||||
<LinearGradientBrush |
||||
StartPoint="0,-0.03" |
||||
EndPoint="0,1"> |
||||
<GradientStop |
||||
Color="White" /> |
||||
<GradientStop |
||||
Color="#FFFAFCFE" |
||||
Offset="0.983" /> |
||||
<GradientStop |
||||
Color="#FFECF7FC" |
||||
Offset="0.07" /> |
||||
<GradientStop |
||||
Color="#FFEEF7FA" |
||||
Offset="0.436" /> |
||||
</LinearGradientBrush> |
||||
</DataGrid.Background> |
||||
<DataGrid.RowStyle> |
||||
<Style |
||||
TargetType="{x:Type DataGridRow}"> |
||||
<Setter |
||||
Property="Background" |
||||
Value="Transparent"></Setter> |
||||
<Style.Triggers> |
||||
<Trigger |
||||
Property="IsMouseOver" |
||||
Value="True"> |
||||
<Setter |
||||
Property="Background" |
||||
Value="#FFE2F6FE" /> |
||||
</Trigger> |
||||
</Style.Triggers> |
||||
</Style> |
||||
</DataGrid.RowStyle> |
||||
<DataGrid.CellStyle> |
||||
<Style |
||||
TargetType="{x:Type DataGridCell}"> |
||||
<Setter |
||||
Property="Focusable" |
||||
Value="false" /> |
||||
<!-- Focusable=true blocks shortcuts if cell is focused --> |
||||
<Setter |
||||
Property="BorderThickness" |
||||
Value="0" /> |
||||
<Style.Triggers> |
||||
<Trigger |
||||
Property="IsSelected" |
||||
Value="True"> |
||||
<!-- disable selection highlight --> |
||||
<Setter |
||||
Property="Foreground" |
||||
Value="Black" /> |
||||
<Setter |
||||
Property="Background" |
||||
Value="{x:Null}" /> |
||||
</Trigger> |
||||
</Style.Triggers> |
||||
</Style> |
||||
</DataGrid.CellStyle> |
||||
<DataGrid.Columns> |
||||
<DataGridTemplateColumn> |
||||
<!-- "Plus" expander --> |
||||
<DataGridTemplateColumn.CellTemplate> |
||||
<DataTemplate> |
||||
<Grid |
||||
Background="White"> |
||||
<StackPanel |
||||
VerticalAlignment="Center"> |
||||
<ToggleButton |
||||
x:Name="btnExpander" |
||||
Style="{StaticResource ExpandCollapseToggleStyle}" |
||||
Click="btnExpander_Click" |
||||
Padding="0" |
||||
Margin="0" /> |
||||
</StackPanel> |
||||
</Grid> |
||||
<DataTemplate.Triggers> |
||||
<DataTrigger |
||||
Binding="{Binding Path=HasChildNodes}" |
||||
Value="False"> |
||||
<Setter |
||||
TargetName="btnExpander" |
||||
Property="Visibility" |
||||
Value="Collapsed" /> |
||||
</DataTrigger> |
||||
</DataTemplate.Triggers> |
||||
</DataTemplate> |
||||
</DataGridTemplateColumn.CellTemplate> |
||||
</DataGridTemplateColumn> |
||||
<!-- Icon --> |
||||
<DataGridTemplateColumn> |
||||
<DataGridTemplateColumn.CellTemplate> |
||||
<DataTemplate> |
||||
<Image |
||||
Source="{Binding ImageSource}"></Image> |
||||
</DataTemplate> |
||||
</DataGridTemplateColumn.CellTemplate> |
||||
</DataGridTemplateColumn> |
||||
<DataGridTemplateColumn |
||||
MinWidth="20" |
||||
Header="Name"> |
||||
<!-- Name --> |
||||
<DataGridTemplateColumn.CellTemplate> |
||||
<DataTemplate> |
||||
<Border |
||||
BorderBrush="#FFDDDDDD" |
||||
BorderThickness="0 0 1 0"> |
||||
<TextBlock |
||||
Style="{StaticResource TextBlockStyle}" |
||||
Text="{Binding Path=Name, Mode=OneWay}" |
||||
VerticalAlignment="Top"></TextBlock> |
||||
</Border> |
||||
</DataTemplate> |
||||
</DataGridTemplateColumn.CellTemplate> |
||||
</DataGridTemplateColumn> |
||||
<!-- Visualizer picker --> |
||||
<!--<DataGridTemplateColumn> |
||||
<DataGridTemplateColumn.CellTemplate> |
||||
<DataTemplate> |
||||
<debugging:VisualizerPicker |
||||
Focusable="False" |
||||
x:Name="visPicker" |
||||
ItemsSource="{Binding Path=VisualizerCommands, Mode=OneWay}" |
||||
Margin="4 0 0 0"></debugging:VisualizerPicker> |
||||
<DataTemplate.Triggers> |
||||
<DataTrigger |
||||
Binding="{Binding Path=HasVisualizerCommands}" |
||||
Value="False"> |
||||
<Setter |
||||
TargetName="visPicker" |
||||
Property="Visibility" |
||||
Value="Collapsed" /> |
||||
</DataTrigger> |
||||
</DataTemplate.Triggers> |
||||
</DataTemplate> |
||||
</DataGridTemplateColumn.CellTemplate> |
||||
</DataGridTemplateColumn>--> |
||||
<DataGridTemplateColumn |
||||
MinWidth="20" |
||||
Header="Text"> |
||||
<!-- Text (value) --> |
||||
<DataGridTemplateColumn.CellTemplate> |
||||
<DataTemplate> |
||||
<TextBox |
||||
Style="{StaticResource TextStyle}" |
||||
IsEnabled="{Binding CanSetText}" |
||||
KeyUp="TextBox_KeyUp" |
||||
Text="{Binding Path=Text}" /> |
||||
</DataTemplate> |
||||
</DataGridTemplateColumn.CellTemplate> |
||||
</DataGridTemplateColumn> |
||||
<DataGridTemplateColumn> |
||||
<!-- Pin --> |
||||
<DataGridTemplateColumn.CellTemplate> |
||||
<DataTemplate> |
||||
<ToggleButton |
||||
IsChecked="{Binding IsPinned}" |
||||
DataContext="{Binding}" |
||||
Visibility="Collapsed" |
||||
Name="PinButton" |
||||
VerticalAlignment="Center" |
||||
Checked="PinButton_Checked" |
||||
Unchecked="PinButton_Unchecked" |
||||
Template="{StaticResource PinTooltipButtonTemplate}" /> |
||||
<DataTemplate.Triggers> |
||||
<DataTrigger |
||||
Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridRow}}" |
||||
Value="True"> |
||||
<Setter |
||||
TargetName="PinButton" |
||||
Property="Visibility" |
||||
Value="Visible" /> |
||||
</DataTrigger> |
||||
</DataTemplate.Triggers> |
||||
</DataTemplate> |
||||
</DataGridTemplateColumn.CellTemplate> |
||||
</DataGridTemplateColumn> |
||||
</DataGrid.Columns> |
||||
</DataGrid> |
||||
<RepeatButton |
||||
Name="btnDown" |
||||
Focusable="False" |
||||
Style="{StaticResource downButtonStyle}" |
||||
Content="v" |
||||
Click="BtnDown_Click"></RepeatButton> |
||||
</StackPanel> |
||||
</UserControl> |
@ -0,0 +1,403 @@
@@ -0,0 +1,403 @@
|
||||
// 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.Globalization; |
||||
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.Shapes; |
||||
|
||||
using ICSharpCode.NRefactory.CSharp; |
||||
using ILSpy.Debugger.AvalonEdit; |
||||
using ILSpy.Debugger.Models.TreeModel; |
||||
using ILSpy.Debugger.Services; |
||||
|
||||
namespace ILSpy.Debugger.Tooltips |
||||
{ |
||||
/// <summary>
|
||||
/// Default Control used as content of SharpDevelop debugger tooltips.
|
||||
/// </summary>
|
||||
public partial class DebuggerTooltipControl : UserControl, ITooltip |
||||
{ |
||||
private const double ChildPopupOpenXOffet = 16; |
||||
private const double ChildPopupOpenYOffet = 15; |
||||
private const int InitialItemsCount = 12; |
||||
private const int VisibleItemsCount = 11; |
||||
|
||||
private bool showPins = true; |
||||
private LazyItemsControl<ITreeNode> lazyGrid; |
||||
private IEnumerable<ITreeNode> itemsSource; |
||||
readonly AstLocation logicalPosition; |
||||
|
||||
public DebuggerTooltipControl(AstLocation logicalPosition) |
||||
{ |
||||
this.logicalPosition = logicalPosition; |
||||
InitializeComponent(); |
||||
|
||||
Loaded += new RoutedEventHandler(OnLoaded); |
||||
} |
||||
|
||||
public DebuggerTooltipControl(AstLocation logicalPosition, ITreeNode node) |
||||
: this(logicalPosition, new ITreeNode[] { node }) |
||||
{ |
||||
|
||||
} |
||||
|
||||
public DebuggerTooltipControl(AstLocation logicalPosition, IEnumerable<ITreeNode> nodes) |
||||
: this(logicalPosition) |
||||
{ |
||||
this.itemsSource = nodes; |
||||
} |
||||
|
||||
public DebuggerTooltipControl(DebuggerTooltipControl parentControl, AstLocation logicalPosition, bool showPins = true) |
||||
: this(logicalPosition) |
||||
{ |
||||
this.parentControl = parentControl; |
||||
this.showPins = showPins; |
||||
} |
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e) |
||||
{ |
||||
if (!showPins) { |
||||
dataGrid.Columns[5].Visibility = Visibility.Collapsed; |
||||
} |
||||
|
||||
SetItemsSource(this.itemsSource); |
||||
} |
||||
|
||||
public event RoutedEventHandler Closed; |
||||
protected void OnClosed() |
||||
{ |
||||
if (this.Closed != null) { |
||||
this.Closed(this, new RoutedEventArgs()); |
||||
} |
||||
} |
||||
|
||||
public IEnumerable<ITreeNode> ItemsSource { |
||||
get { return this.itemsSource; } |
||||
} |
||||
|
||||
public void SetItemsSource(IEnumerable<ITreeNode> value) { |
||||
this.itemsSource = value; |
||||
this.lazyGrid = new LazyItemsControl<ITreeNode>(this.dataGrid, InitialItemsCount); |
||||
|
||||
// // HACK for updating the pins in tooltip
|
||||
// var observable = new List<ITreeNode>();
|
||||
// this.itemsSource.ForEach(item => observable.Add(item));
|
||||
//
|
||||
// // verify if at the line of the root there's a pin bookmark
|
||||
// ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
|
||||
// var editor = provider.TextEditor;
|
||||
// if (editor != null) {
|
||||
// var pin = BookmarkManager.Bookmarks.Find(
|
||||
// b => b is PinBookmark &&
|
||||
// b.Location.Line == logicalPosition.Line &&
|
||||
// b.FileName == editor.FileName) as PinBookmark;
|
||||
//
|
||||
// if (pin != null) {
|
||||
// observable.ForEach(item => { // TODO: find a way not to use "observable"
|
||||
// if (pin.ContainsNode(item))
|
||||
// item.IsPinned = true;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
var source = new VirtualizingIEnumerable<ITreeNode>(value); |
||||
lazyGrid.ItemsSource = source; |
||||
this.dataGrid.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(handleScroll)); |
||||
|
||||
if (this.lazyGrid.ItemsSourceTotalCount != null) { |
||||
// hide up/down buttons if too few items
|
||||
btnUp.Visibility = btnDown.Visibility = |
||||
this.lazyGrid.ItemsSourceTotalCount.Value <= VisibleItemsCount ? Visibility.Collapsed : Visibility.Visible; |
||||
} |
||||
} |
||||
|
||||
//public Location LogicalPosition { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool ShowAsPopup |
||||
{ |
||||
get |
||||
{ |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Close(bool mouseClick) |
||||
{ |
||||
if (mouseClick || (!mouseClick && !isChildExpanded)) { |
||||
CloseChildPopups(); |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
private DebuggerPopup childPopup { get; set; } |
||||
private DebuggerTooltipControl parentControl { get; set; } |
||||
internal DebuggerPopup containingPopup { get; set; } |
||||
|
||||
bool isChildExpanded |
||||
{ |
||||
get |
||||
{ |
||||
return this.childPopup != null && this.childPopup.IsOpen; |
||||
} |
||||
} |
||||
|
||||
private ToggleButton expandedButton; |
||||
|
||||
/// <summary>
|
||||
/// Closes the child popup of this control, if it exists.
|
||||
/// </summary>
|
||||
public void CloseChildPopups() |
||||
{ |
||||
if (this.expandedButton != null) { |
||||
this.expandedButton.IsChecked = false; |
||||
this.expandedButton = null; |
||||
// nice simple example of indirect recursion
|
||||
this.childPopup.CloseSelfAndChildren(); |
||||
} |
||||
} |
||||
|
||||
public void CloseOnLostFocus() |
||||
{ |
||||
// when we close, parent becomes leaf
|
||||
if (this.containingPopup != null) { |
||||
this.containingPopup.IsLeaf = true; |
||||
} |
||||
if (!this.IsMouseOver) { |
||||
if (this.containingPopup != null) { |
||||
this.containingPopup.IsOpen = false; |
||||
this.containingPopup.IsLeaf = false; |
||||
} |
||||
if (this.parentControl != null) { |
||||
this.parentControl.CloseOnLostFocus(); |
||||
} |
||||
OnClosed(); |
||||
} else { |
||||
// leaf closed because of click inside this control - stop the closing chain
|
||||
if (this.expandedButton != null && !this.expandedButton.IsMouseOver) { |
||||
this.expandedButton.IsChecked = false; |
||||
this.expandedButton = null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void btnExpander_Click(object sender, RoutedEventArgs e) |
||||
{ |
||||
var clickedButton = (ToggleButton)e.OriginalSource; |
||||
var clickedNode = (ITreeNode)clickedButton.DataContext; |
||||
// use device independent units, because child popup Left/Top are in independent units
|
||||
Point buttonPos = clickedButton.PointToScreen(new Point(0, 0)).TransformFromDevice(clickedButton); |
||||
|
||||
if (clickedButton.IsChecked.GetValueOrDefault(false)) { |
||||
CloseChildPopups(); |
||||
this.expandedButton = clickedButton; |
||||
|
||||
// open child Popup
|
||||
if (this.childPopup == null) { |
||||
this.childPopup = new DebuggerPopup(this, logicalPosition, false); |
||||
this.childPopup.Placement = PlacementMode.Absolute; |
||||
} |
||||
if (this.containingPopup != null) { |
||||
this.containingPopup.IsLeaf = false; |
||||
} |
||||
this.childPopup.IsLeaf = true; |
||||
this.childPopup.HorizontalOffset = buttonPos.X + ChildPopupOpenXOffet; |
||||
this.childPopup.VerticalOffset = buttonPos.Y + ChildPopupOpenYOffet; |
||||
this.childPopup.ItemsSource = clickedNode.ChildNodes; |
||||
this.childPopup.Open(); |
||||
} else { |
||||
CloseChildPopups(); |
||||
} |
||||
} |
||||
|
||||
private void handleScroll(object sender, ScrollChangedEventArgs e) |
||||
{ |
||||
btnUp.IsEnabled = !this.lazyGrid.IsScrolledToStart; |
||||
btnDown.IsEnabled = !this.lazyGrid.IsScrolledToEnd; |
||||
} |
||||
|
||||
void BtnUp_Click(object sender, RoutedEventArgs e) |
||||
{ |
||||
this.lazyGrid.ScrollViewer.ScrollUp(1); |
||||
} |
||||
|
||||
void BtnDown_Click(object sender, RoutedEventArgs e) |
||||
{ |
||||
this.lazyGrid.ScrollViewer.ScrollDown(1); |
||||
} |
||||
|
||||
#region Edit value in tooltip
|
||||
|
||||
void TextBox_KeyUp(object sender, KeyEventArgs e) |
||||
{ |
||||
if (e.Key == Key.Escape) { |
||||
dataGrid.Focus(); |
||||
return; |
||||
} |
||||
|
||||
if (e.Key == Key.Enter) { |
||||
dataGrid.Focus(); |
||||
// set new value
|
||||
var textBox = (TextBox)e.OriginalSource; |
||||
var newValue = textBox.Text; |
||||
var node = ((FrameworkElement)sender).DataContext as ITreeNode; |
||||
SaveNewValue(node, textBox.Text); |
||||
} |
||||
} |
||||
|
||||
void TextBox_LostFocus(object sender, RoutedEventArgs e) |
||||
{ |
||||
var textBox = (TextBox)e.OriginalSource; |
||||
var newValue = textBox.Text; |
||||
var node = ((FrameworkElement)sender).DataContext as ITreeNode; |
||||
SaveNewValue(node, textBox.Text); |
||||
} |
||||
|
||||
void SaveNewValue(ITreeNode node, string newValue) |
||||
{ |
||||
if(node != null && node.SetText(newValue)) { |
||||
// show adorner
|
||||
var adornerLayer = AdornerLayer.GetAdornerLayer(dataGrid); |
||||
var adorners = adornerLayer.GetAdorners(dataGrid); |
||||
if (adorners != null && adorners.Length != 0) |
||||
adornerLayer.Remove(adorners[0]); |
||||
SavedAdorner adorner = new SavedAdorner(dataGrid); |
||||
adornerLayer.Add(adorner); |
||||
} |
||||
} |
||||
|
||||
#endregion
|
||||
|
||||
#region Pining checked/unchecked
|
||||
|
||||
void PinButton_Checked(object sender, RoutedEventArgs e) |
||||
{ |
||||
// ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
|
||||
// var editor = provider.TextEditor;
|
||||
// if (editor == null) return;
|
||||
// var node = (ITreeNode)(((ToggleButton)(e.OriginalSource)).DataContext);
|
||||
//
|
||||
// if (!string.IsNullOrEmpty(editor.FileName)) {
|
||||
//
|
||||
// // verify if at the line of the root there's a pin bookmark
|
||||
// var pin = BookmarkManager.Bookmarks.Find(
|
||||
// b => b is PinBookmark &&
|
||||
// b.LineNumber == logicalPosition.Line &&
|
||||
// b.FileName == editor.FileName) as PinBookmark;
|
||||
//
|
||||
// if (pin == null) {
|
||||
// pin = new PinBookmark(editor.FileName, logicalPosition);
|
||||
// // show pinned DebuggerPopup
|
||||
// if (pin.Popup == null) {
|
||||
// pin.Popup = new PinDebuggerControl();
|
||||
// pin.Popup.Mark = pin;
|
||||
// Rect rect = new Rect(this.DesiredSize);
|
||||
// var point = this.PointToScreen(rect.TopRight);
|
||||
// pin.Popup.Location = new Point { X = 500, Y = point.Y - 150 };
|
||||
// pin.Nodes.Add(node);
|
||||
// pin.Popup.ItemsSource = pin.Nodes;
|
||||
// }
|
||||
//
|
||||
// // do actions
|
||||
// pin.Popup.Open();
|
||||
// BookmarkManager.AddMark(pin);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (!pin.ContainsNode(node)) {
|
||||
// pin.Nodes.Add(node);
|
||||
// pin.Popup.ItemsSource = pin.Nodes;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
} |
||||
|
||||
void PinButton_Unchecked(object sender, RoutedEventArgs e) |
||||
{ |
||||
// ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
|
||||
// var editor = provider.TextEditor;
|
||||
// if (editor == null) return;
|
||||
//
|
||||
// if (!string.IsNullOrEmpty(editor.FileName)) {
|
||||
// // remove from pinned DebuggerPopup
|
||||
// var pin = BookmarkManager.Bookmarks.Find(
|
||||
// b => b is PinBookmark &&
|
||||
// b.LineNumber == logicalPosition.Line &&
|
||||
// b.FileName == editor.FileName) as PinBookmark;
|
||||
// if (pin == null) return;
|
||||
//
|
||||
// ToggleButton button = (ToggleButton)e.OriginalSource;
|
||||
// pin.RemoveNode((ITreeNode)button.DataContext);
|
||||
// pin.Popup.ItemsSource = pin.Nodes;
|
||||
// // remove if no more data pins are available
|
||||
// if (pin.Nodes.Count == 0) {
|
||||
// pin.Popup.Close();
|
||||
//
|
||||
// BookmarkManager.RemoveMark(pin);
|
||||
// }
|
||||
// }
|
||||
} |
||||
|
||||
#endregion
|
||||
|
||||
#region Saved Adorner
|
||||
|
||||
class SavedAdorner : Adorner |
||||
{ |
||||
public SavedAdorner(UIElement adornedElement) : base(adornedElement) |
||||
{ |
||||
Loaded += delegate { Show(); }; |
||||
} |
||||
|
||||
protected override void OnRender(DrawingContext drawingContext) |
||||
{ |
||||
Rect adornedElementRect = new Rect(this.AdornedElement.DesiredSize); |
||||
|
||||
// Some arbitrary drawing implements.
|
||||
var formatedText = new FormattedText("Saved", |
||||
CultureInfo.CurrentCulture, |
||||
FlowDirection.LeftToRight, |
||||
new Typeface(new FontFamily("Arial"), |
||||
FontStyles.Normal, |
||||
FontWeights.Black, |
||||
FontStretches.Expanded), |
||||
8d, |
||||
Brushes.Black); |
||||
|
||||
|
||||
drawingContext.DrawText(formatedText, |
||||
new Point(adornedElementRect.TopRight.X - formatedText.Width - 2, |
||||
adornedElementRect.TopRight.Y)); |
||||
} |
||||
|
||||
private void Show() |
||||
{ |
||||
DoubleAnimation animation = new DoubleAnimation(); |
||||
animation.From = 1; |
||||
animation.To = 0; |
||||
|
||||
animation.Duration = new Duration(TimeSpan.FromSeconds(2)); |
||||
animation.SetValue(Storyboard.TargetProperty, this); |
||||
animation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath(Rectangle.OpacityProperty)); |
||||
|
||||
Storyboard board = new Storyboard(); |
||||
board.Children.Add(animation); |
||||
|
||||
board.Begin(this); |
||||
} |
||||
} |
||||
|
||||
#endregion
|
||||
} |
||||
} |
@ -0,0 +1,113 @@
@@ -0,0 +1,113 @@
|
||||
// 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.Controls; |
||||
|
||||
using ILSpy.Debugger.Services; |
||||
|
||||
namespace ILSpy.Debugger.Tooltips |
||||
{ |
||||
/// <summary>
|
||||
/// ItemsControl wrapper that takes VirtualizingIEnumerable as source,
|
||||
/// and adds additional items from the source to underlying ItemsControl when scrolled to bottom.
|
||||
/// </summary>
|
||||
public class LazyItemsControl<T> |
||||
{ |
||||
private ItemsControl itemsControl; |
||||
private int initialItemsCount; |
||||
|
||||
/// <summary>
|
||||
/// Creates new instance of LazyItemsControl.
|
||||
/// </summary>
|
||||
/// <param name="wrappedItemsControl">ItemsControl to wrap and add items to it when scrolled to bottom.</param>
|
||||
/// <param name="initialItemsCount">Number of items to be initially displayed in wrapped ItemsControl.</param>
|
||||
public LazyItemsControl(ItemsControl wrappedItemsControl, int initialItemsCount) |
||||
{ |
||||
if (wrappedItemsControl == null) |
||||
throw new ArgumentNullException("wrappedItemsControl"); |
||||
|
||||
this.initialItemsCount = initialItemsCount; |
||||
this.itemsControl = wrappedItemsControl; |
||||
this.itemsControl.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(handleScroll)); |
||||
} |
||||
|
||||
private ScrollViewer scrollViewerCached; |
||||
public ScrollViewer ScrollViewer |
||||
{ |
||||
get |
||||
{ |
||||
if (this.scrollViewerCached == null) |
||||
this.scrollViewerCached = this.itemsControl.GetScrollViewer(); |
||||
return this.scrollViewerCached; |
||||
} |
||||
} |
||||
|
||||
public bool IsScrolledToStart |
||||
{ |
||||
get |
||||
{ |
||||
if (ScrollViewer == null) // Visual tree not initialized yet
|
||||
return false; |
||||
return ScrollViewer.VerticalOffset == 0; |
||||
} |
||||
} |
||||
|
||||
public bool IsScrolledToEnd |
||||
{ |
||||
get |
||||
{ |
||||
if (itemsSourceTotalCount == null) { |
||||
// not scrolled to end of IEnumerable yet
|
||||
return false; |
||||
} |
||||
// already scrolled to end of IEnumerable
|
||||
int totalItems = itemsSourceTotalCount.Value; |
||||
return (ScrollViewer.VerticalOffset >= totalItems - ScrollViewer.ViewportHeight); |
||||
} |
||||
} |
||||
|
||||
private int? itemsSourceTotalCount = null; |
||||
/// <summary> Items count of underlying IEnumerable. Null until scrolled to the end of IEnumerable. </summary>
|
||||
public int? ItemsSourceTotalCount |
||||
{ |
||||
get |
||||
{ |
||||
return this.itemsSourceTotalCount; |
||||
} |
||||
} |
||||
|
||||
private VirtualizingIEnumerable<T> itemsSource; |
||||
/// <summary> The collection that underlying ItemsControl sees. </summary>
|
||||
public VirtualizingIEnumerable<T> ItemsSource |
||||
{ |
||||
get { return itemsSource; } |
||||
set |
||||
{ |
||||
this.itemsSource = value; |
||||
addNextItems(this.itemsSource, initialItemsCount); |
||||
this.itemsControl.ItemsSource = value; |
||||
} |
||||
} |
||||
|
||||
private void addNextItems(VirtualizingIEnumerable<T> sourceToAdd, int nItems) |
||||
{ |
||||
sourceToAdd.AddNextItems(nItems); |
||||
if (!sourceToAdd.HasNext) { |
||||
// all items from IEnumerable have been added
|
||||
this.itemsSourceTotalCount = sourceToAdd.Count; |
||||
} |
||||
} |
||||
|
||||
private void handleScroll(object sender, ScrollChangedEventArgs e) |
||||
{ |
||||
if (e.VerticalChange > 0) { |
||||
// scrolled to bottom
|
||||
if (e.VerticalOffset >= this.itemsSource.Count - e.ViewportHeight) { |
||||
addNextItems(this.itemsSource, (int)e.VerticalChange); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,362 @@
@@ -0,0 +1,362 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
xmlns:local="clr-namespace:ILSpy.Debugger.Tooltips" |
||||
> |
||||
<LinearGradientBrush x:Key="OrangeBrushKey" EndPoint="0,1" StartPoint="0,0"> |
||||
<LinearGradientBrush.GradientStops> |
||||
<GradientStop Offset="0" Color="White" /> |
||||
<GradientStop Offset="0.5" Color="Orange" /> |
||||
<GradientStop Offset="1" Color="Orange" /> |
||||
</LinearGradientBrush.GradientStops> |
||||
</LinearGradientBrush> |
||||
|
||||
<LinearGradientBrush x:Key="OrangePressedBrushKey" EndPoint="0,1" StartPoint="0,0"> |
||||
<LinearGradientBrush.GradientStops> |
||||
<GradientStop Offset="1" Color="White" /> |
||||
<GradientStop Offset="0.5" Color="Orange" /> |
||||
<GradientStop Offset="0" Color="Orange" /> |
||||
</LinearGradientBrush.GradientStops> |
||||
</LinearGradientBrush> |
||||
|
||||
<LinearGradientBrush x:Key="SilverBrushKey" EndPoint="0,1" StartPoint="0,0"> |
||||
<LinearGradientBrush.GradientStops> |
||||
<GradientStop Offset="0" Color="White" /> |
||||
<GradientStop Offset="0.5" Color="LightGray" /> |
||||
<GradientStop Offset="1" Color="LightGray" /> |
||||
</LinearGradientBrush.GradientStops> |
||||
</LinearGradientBrush> |
||||
|
||||
<LinearGradientBrush x:Key="SilverPressedBrushKey" EndPoint="0,1" StartPoint="0,0"> |
||||
<LinearGradientBrush.GradientStops> |
||||
<GradientStop Offset="1" Color="White" /> |
||||
<GradientStop Offset="0.5" Color="LightGray" /> |
||||
<GradientStop Offset="0" Color="LightGray" /> |
||||
</LinearGradientBrush.GradientStops> |
||||
</LinearGradientBrush> |
||||
|
||||
<ControlTemplate x:Key="CloseButtonTemplate" TargetType="Button"> |
||||
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="2,2,0,0" BorderThickness="1" BorderBrush="Black" Background="{StaticResource SilverPressedBrushKey}"> |
||||
<Canvas> |
||||
<Line X1="3.5" X2="10.5" Y1="3.5" Y2="10.5" Stroke="Black" StrokeThickness="2"/> |
||||
<Line X1="3.5" X2="10.5" Y1="10.5" Y2="3.5" Stroke="Black" StrokeThickness="2"/> |
||||
</Canvas> |
||||
</Border> |
||||
<ControlTemplate.Triggers> |
||||
<Trigger Property="UIElement.IsMouseOver" Value="true"> |
||||
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangeBrushKey}"/> |
||||
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/> |
||||
</Trigger> |
||||
<Trigger Property="ButtonBase.IsPressed" Value="True"> |
||||
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangePressedBrushKey}"/> |
||||
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/> |
||||
</Trigger> |
||||
</ControlTemplate.Triggers> |
||||
</ControlTemplate> |
||||
|
||||
<TransformGroup x:Key="Rotate"> |
||||
<RotateTransform Angle="270" CenterX="7" CenterY="7"/> |
||||
</TransformGroup> |
||||
|
||||
<TransformGroup x:Key="RotateUnpin"> |
||||
<RotateTransform Angle="270" CenterX="7" CenterY="7"/> |
||||
<RotateTransform Angle="-90" CenterX="7" CenterY="7"/> |
||||
<ScaleTransform ScaleY="-1" CenterX="7" CenterY="7"/> |
||||
</TransformGroup> |
||||
|
||||
<TransformGroup x:Key="RotatePin"> |
||||
<RotateTransform Angle="-90" CenterX="7" CenterY="7"/> |
||||
</TransformGroup> |
||||
|
||||
<TransformGroup x:Key="FlipComment"> |
||||
<ScaleTransform CenterX="7" CenterY="7" ScaleY="-1"/> |
||||
</TransformGroup> |
||||
|
||||
<ControlTemplate x:Key="PinButtonTemplate" TargetType="ToggleButton"> |
||||
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="0" BorderThickness="1" BorderBrush="Black" Background="{StaticResource SilverPressedBrushKey}"> |
||||
<Canvas Name="TheCanvas"> |
||||
<Line X1="4" X2="10" Y1="2" Y2="2" Stroke="Black" StrokeThickness="1"/> |
||||
<Line X1="9" X2="9" Y1="2" Y2="8" Stroke="Black" StrokeThickness="1"/> |
||||
<Line X1="2" X2="12" Y1="8" Y2="8" Stroke="Black" StrokeThickness="1"/> |
||||
<Rectangle Fill="Black" Width="2" Height="5" Canvas.Left="4" Canvas.Top="3"/> |
||||
<Line X1="7" X2="7" Y1="9" Y2="12" Stroke="Black" StrokeThickness="1"/> |
||||
</Canvas> |
||||
</Border> |
||||
<ControlTemplate.Triggers> |
||||
<Trigger Property="UIElement.IsMouseOver" Value="true"> |
||||
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangeBrushKey}"/> |
||||
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/> |
||||
</Trigger> |
||||
<Trigger Property="ButtonBase.IsPressed" Value="True"> |
||||
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource RotatePin}"/> |
||||
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangePressedBrushKey}"/> |
||||
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/> |
||||
</Trigger> |
||||
<Trigger Property="IsChecked" Value="true"> |
||||
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource RotatePin}"/> |
||||
</Trigger> |
||||
</ControlTemplate.Triggers> |
||||
</ControlTemplate> |
||||
|
||||
<ControlTemplate x:Key="CommentButtonTemplate" TargetType="ToggleButton"> |
||||
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="0,0,2,2" BorderThickness="1" BorderBrush="Black" Background="{StaticResource SilverPressedBrushKey}"> |
||||
<Canvas Name="TheCanvas"> |
||||
<Line X1="3" Y1="3" X2="7" Y2="7.5" Stroke="Black" StrokeThickness="1"/> |
||||
<Line X1="7" Y1="7.4" X2="11" Y2="3" Stroke="Black" StrokeThickness="1"/> |
||||
<Line X1="3" Y1="7.5" X2="7" Y2="12" Stroke="Black" StrokeThickness="1"/> |
||||
<Line X1="7" Y1="12" X2="11" Y2="7.5" Stroke="Black" StrokeThickness="1"/> |
||||
</Canvas> |
||||
</Border> |
||||
<ControlTemplate.Triggers> |
||||
<Trigger Property="UIElement.IsMouseOver" Value="true"> |
||||
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangeBrushKey}"/> |
||||
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/> |
||||
</Trigger> |
||||
<Trigger Property="ButtonBase.IsPressed" Value="True"> |
||||
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource OrangePressedBrushKey}"/> |
||||
<Setter TargetName="TheBorder" Property="BorderBrush" Value="Silver"/> |
||||
</Trigger> |
||||
<Trigger Property="IsChecked" Value="True"> |
||||
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource FlipComment}"/> |
||||
</Trigger> |
||||
</ControlTemplate.Triggers> |
||||
</ControlTemplate> |
||||
|
||||
<SolidColorBrush x:Key="MouseOverPinBrush" Color="Black" /> |
||||
|
||||
<ControlTemplate x:Key="PinTooltipButtonTemplate" TargetType="ToggleButton"> |
||||
<Border Width="16" Height="16" Name="TheBorder" CornerRadius="2" BorderBrush="Transparent" BorderThickness="1" Background="Transparent"> |
||||
<Canvas RenderTransform="{StaticResource Rotate}" Name="TheCanvas"> |
||||
<Line X1="4" X2="10" Y1="2" Y2="2" Stroke="Silver" StrokeThickness="1" Name="Line1"/> |
||||
<Line X1="9" X2="9" Y1="2" Y2="8" Stroke="Silver" StrokeThickness="1" Name="Line2"/> |
||||
<Line X1="2" X2="12" Y1="8" Y2="8" Stroke="Silver" StrokeThickness="1" Name="Line3"/> |
||||
<Rectangle Fill="Silver" Width="2" Height="7" Canvas.Left="4" Canvas.Top="2" Name="Rectangle"/> |
||||
<Line X1="7" X2="7" Y1="9" Y2="12" Stroke="Silver" StrokeThickness="1" Name="Line4"/> |
||||
</Canvas> |
||||
</Border> |
||||
<ControlTemplate.Triggers> |
||||
<Trigger Property="ButtonBase.IsPressed" Value="True"> |
||||
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource RotateUnpin}"/> |
||||
</Trigger> |
||||
<Trigger Property="ButtonBase.IsMouseOver" Value="True"> |
||||
<Setter TargetName="Line1" Property="Stroke" Value="{StaticResource MouseOverPinBrush}"/> |
||||
<Setter TargetName="Line2" Property="Stroke" Value="{StaticResource MouseOverPinBrush}"/> |
||||
<Setter TargetName="Line3" Property="Stroke" Value="{StaticResource MouseOverPinBrush}"/> |
||||
<Setter TargetName="Line4" Property="Stroke" Value="{StaticResource MouseOverPinBrush}"/> |
||||
<Setter TargetName="Rectangle" Property="Fill" Value="{StaticResource MouseOverPinBrush}"/> |
||||
</Trigger> |
||||
<Trigger Property="IsChecked" Value="True"> |
||||
<Setter TargetName="TheCanvas" Property="RenderTransform" Value="{StaticResource RotateUnpin}"/> |
||||
</Trigger> |
||||
</ControlTemplate.Triggers> |
||||
</ControlTemplate> |
||||
|
||||
<Style |
||||
TargetType="{x:Type TextBox}" |
||||
x:Key="TextStyle"> |
||||
<Setter |
||||
Property="OverridesDefaultStyle" |
||||
Value="True" /> |
||||
<Setter |
||||
Property="VerticalAlignment" |
||||
Value="Center" /> |
||||
<Setter |
||||
Property="FontFamily" Value="Khmer UI" /> |
||||
<Setter Property="FontSize" Value="12" /> |
||||
<Setter |
||||
Property="KeyboardNavigation.TabNavigation" |
||||
Value="None" /> |
||||
<Setter |
||||
Property="FocusVisualStyle" |
||||
Value="{x:Null}" /> |
||||
<Setter |
||||
Property="Template"> |
||||
<Setter.Value> |
||||
<ControlTemplate |
||||
TargetType="{x:Type TextBoxBase}"> |
||||
<Border |
||||
Name="Border" |
||||
Background="Transparent" |
||||
BorderBrush="Transparent" |
||||
BorderThickness="0"> |
||||
<ScrollViewer |
||||
Margin="0" |
||||
Name="PART_ContentHost" /> |
||||
</Border> |
||||
</ControlTemplate> |
||||
</Setter.Value> |
||||
</Setter> |
||||
</Style> |
||||
|
||||
<Style TargetType="TextBlock" x:Key="TextBlockStyle"> |
||||
<Setter Property="Margin" Value="4 0" /> |
||||
<Setter |
||||
Property="FontFamily" Value="Khmer UI" /> |
||||
<Setter Property="FontSize" Value="12" /> |
||||
</Style> |
||||
|
||||
<Style x:Key="PinThumbStyle" TargetType="Thumb"> |
||||
<Setter Property="Template"> |
||||
<Setter.Value> |
||||
<ControlTemplate TargetType="Thumb"> |
||||
<StackPanel x:Name="Container"/> |
||||
</ControlTemplate> |
||||
</Setter.Value> |
||||
</Setter> |
||||
</Style> |
||||
|
||||
<Style |
||||
x:Key="ExpandCollapseToggleStyle" |
||||
TargetType="{x:Type ToggleButton}"> |
||||
<Setter |
||||
Property="Focusable" |
||||
Value="False" /> |
||||
<Setter |
||||
Property="Width" |
||||
Value="19" /> |
||||
<Setter |
||||
Property="Height" |
||||
Value="13" /> |
||||
<Setter |
||||
Property="Template"> |
||||
<Setter.Value> |
||||
<ControlTemplate |
||||
TargetType="{x:Type ToggleButton}"> |
||||
<Border |
||||
Width="19" |
||||
Height="13" |
||||
Background="Transparent"> |
||||
<Border |
||||
Width="9" |
||||
Height="9" |
||||
BorderThickness="1" |
||||
BorderBrush="#FF7898B5" |
||||
CornerRadius="1" |
||||
SnapsToDevicePixels="true"> |
||||
<Border.Background> |
||||
<LinearGradientBrush |
||||
StartPoint="0,0" |
||||
EndPoint="1,1"> |
||||
<LinearGradientBrush.GradientStops> |
||||
<GradientStop |
||||
Color="White" |
||||
Offset=".2" /> |
||||
<GradientStop |
||||
Color="#FFC0B7A6" |
||||
Offset="1" /> |
||||
</LinearGradientBrush.GradientStops> |
||||
</LinearGradientBrush> |
||||
</Border.Background> |
||||
<Path |
||||
x:Name="ExpandPath" |
||||
Margin="1,1,1,1" |
||||
Fill="Black" |
||||
Data="M 0 2 L 0 3 L 2 3 L 2 5 L 3 5 L 3 3 L 5 3 L 5 2 L 3 2 L 3 0 L 2 0 L 2 2 Z" /> |
||||
</Border> |
||||
</Border> |
||||
<ControlTemplate.Triggers> |
||||
<Trigger |
||||
Property="IsChecked" |
||||
Value="True"> |
||||
<Setter |
||||
Property="Data" |
||||
TargetName="ExpandPath" |
||||
Value="M 0 2 L 0 3 L 5 3 L 5 2 Z" /> |
||||
</Trigger> |
||||
</ControlTemplate.Triggers> |
||||
</ControlTemplate> |
||||
</Setter.Value> |
||||
</Setter> |
||||
</Style> |
||||
<Style |
||||
x:Key="upDownBorderStyle" |
||||
TargetType="{x:Type Border}"> |
||||
<Setter |
||||
Property="BorderBrush" |
||||
Value="Gray" /> |
||||
<Setter |
||||
Property="HorizontalAlignment" |
||||
Value="Stretch" /> |
||||
<Setter |
||||
Property="Margin" |
||||
Value="0" /> |
||||
<Setter |
||||
Property="Padding" |
||||
Value="0" /> |
||||
<Setter |
||||
Property="Background" |
||||
Value="#FFECF7FC" /> |
||||
<Setter |
||||
Property="Height" |
||||
Value="14" /> |
||||
<Style.Triggers> |
||||
<DataTrigger |
||||
Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled}" |
||||
Value="False"> |
||||
<Setter |
||||
Property="Background" |
||||
Value="#FFE0E0E0"></Setter> |
||||
</DataTrigger> |
||||
</Style.Triggers> |
||||
</Style> |
||||
<Style |
||||
x:Key="upButtonStyle" |
||||
TargetType="{x:Type RepeatButton}"> |
||||
<Setter |
||||
Property="Template"> |
||||
<Setter.Value> |
||||
<ControlTemplate |
||||
TargetType="{x:Type RepeatButton}"> |
||||
<Border |
||||
Style="{StaticResource upDownBorderStyle}" |
||||
BorderThickness="1 1 1 0"> |
||||
<ContentPresenter |
||||
HorizontalAlignment="Center"></ContentPresenter> |
||||
</Border> |
||||
</ControlTemplate> |
||||
</Setter.Value> |
||||
</Setter> |
||||
</Style> |
||||
<Style |
||||
x:Key="downButtonStyle" |
||||
TargetType="{x:Type RepeatButton}"> |
||||
<Setter |
||||
Property="Template"> |
||||
<Setter.Value> |
||||
<ControlTemplate |
||||
TargetType="{x:Type RepeatButton}"> |
||||
<Border |
||||
Style="{StaticResource upDownBorderStyle}" |
||||
BorderThickness="1 0 1 1"> |
||||
<ContentPresenter |
||||
HorizontalAlignment="Center"></ContentPresenter> |
||||
</Border> |
||||
</ControlTemplate> |
||||
</Setter.Value> |
||||
</Setter> |
||||
</Style> |
||||
|
||||
<ControlTemplate x:Key="RefreshButton" TargetType="Button"> |
||||
<Border |
||||
Name="ImageBorder" |
||||
CornerRadius="7" |
||||
BorderBrush="Transparent" |
||||
BorderThickness="1" |
||||
Height="14" |
||||
Width="14"> |
||||
<Image Width="9" Height="9" Margin="2 2" |
||||
x:Name="RefreshContentImage" |
||||
Tag="{Binding}" |
||||
Source="pack://application:,,,/ILSpy;component/Images/Refresh.png"/> |
||||
</Border> |
||||
<ControlTemplate.Triggers> |
||||
<Trigger Property="UIElement.IsMouseOver" Value="true"> |
||||
<Setter TargetName="ImageBorder" Property="Background" Value="{StaticResource SilverBrushKey}"/> |
||||
<Setter TargetName="ImageBorder" Property="BorderBrush" Value="Gray"/> |
||||
</Trigger> |
||||
<Trigger Property="ButtonBase.IsPressed" Value="True"> |
||||
<Setter TargetName="ImageBorder" Property="Background" Value="{StaticResource SilverPressedBrushKey}"/> |
||||
<Setter TargetName="ImageBorder" Property="BorderBrush" Value="Gray"/> |
||||
</Trigger> |
||||
</ControlTemplate.Triggers> |
||||
</ControlTemplate> |
||||
</ResourceDictionary> |
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
// 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.Collections.ObjectModel; |
||||
|
||||
namespace ILSpy.Debugger.Tooltips |
||||
{ |
||||
/// <summary>
|
||||
/// A wrapper around IEnumerable<T> with AddNextItems method for pulling additional items
|
||||
/// from underlying IEnumerable<T>.
|
||||
/// Can be used as source for <see cref="LazyItemsControl" />.
|
||||
/// </summary>
|
||||
public class VirtualizingIEnumerable<T> : ObservableCollection<T> |
||||
{ |
||||
private IEnumerator<T> originalSourceEnumerator; |
||||
|
||||
public VirtualizingIEnumerable(IEnumerable<T> originalSource) |
||||
{ |
||||
if (originalSource == null) |
||||
throw new ArgumentNullException("originalSource"); |
||||
|
||||
this.originalSourceEnumerator = originalSource.GetEnumerator(); |
||||
} |
||||
|
||||
private bool hasNext = true; |
||||
/// <summary>
|
||||
/// False if all items from underlying IEnumerable have already been added.
|
||||
/// </summary>
|
||||
public bool HasNext |
||||
{ |
||||
get |
||||
{ |
||||
return this.hasNext; |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Requests next <paramref name="count"/> items from underlying IEnumerable source and adds them to the collection.
|
||||
/// </summary>
|
||||
public void AddNextItems(int count) |
||||
{ |
||||
for (int i = 0; i < count; i++) { |
||||
if (!originalSourceEnumerator.MoveNext()) { |
||||
this.hasNext = false; |
||||
break; |
||||
} |
||||
this.Add(originalSourceEnumerator.Current); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
// 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.Linq; |
||||
using System.Windows.Controls; |
||||
|
||||
using ILSpy.Debugger.Models.TreeModel; |
||||
|
||||
namespace Debugger.AddIn.Tooltips |
||||
{ |
||||
/// <summary>
|
||||
/// Control displaying and executing <see cref="IVisualizerCommand">visualizer commands</see>.
|
||||
/// </summary>
|
||||
public class VisualizerPicker : ComboBox |
||||
{ |
||||
public VisualizerPicker() |
||||
{ |
||||
this.SelectionChanged += VisualizerPicker_SelectionChanged; |
||||
} |
||||
|
||||
void VisualizerPicker_SelectionChanged(object sender, SelectionChangedEventArgs e) |
||||
{ |
||||
if (this.SelectedItem == null) { |
||||
return; |
||||
} |
||||
var clickedCommand = this.SelectedItem as IVisualizerCommand; |
||||
if (clickedCommand == null) { |
||||
throw new InvalidOperationException( |
||||
string.Format("{0} clicked, only instances of {1} must be present in {2}.", |
||||
this.SelectedItem.GetType().ToString(), typeof(IVisualizerCommand).Name, typeof(VisualizerPicker).Name)); |
||||
} |
||||
|
||||
clickedCommand.Execute(); |
||||
// Make no item selected, so that multiple selections of the same item always execute the command.
|
||||
// This triggers VisualizerPicker_SelectionChanged again, which returns immediately.
|
||||
this.SelectedIndex = -1; |
||||
} |
||||
|
||||
public new IEnumerable<IVisualizerCommand> ItemsSource |
||||
{ |
||||
get { return (IEnumerable<IVisualizerCommand>)base.ItemsSource; } |
||||
set { base.ItemsSource = value; } |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,133 @@
@@ -0,0 +1,133 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" |
||||
xmlns:debugging="clr-namespace:ILSpy.Debugger.Tooltips" |
||||
xmlns:core="http://icsharpcode.net/sharpdevelop/core"> |
||||
<Style x:Key="ComboBoxFocusVisual"> |
||||
<Setter Property="Control.Template"> |
||||
<Setter.Value> |
||||
<ControlTemplate> |
||||
<Rectangle Stroke="Black" StrokeDashArray="1 2" StrokeThickness="1" Margin="4,4,21,4" SnapsToDevicePixels="true"/> |
||||
</ControlTemplate> |
||||
</Setter.Value> |
||||
</Setter> |
||||
</Style> |
||||
<LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0"> |
||||
<GradientStop Color="#F3F3F3" Offset="0"/> |
||||
<GradientStop Color="#EBEBEB" Offset="0.5"/> |
||||
<GradientStop Color="#DDDDDD" Offset="0.5"/> |
||||
<GradientStop Color="#CDCDCD" Offset="1"/> |
||||
</LinearGradientBrush> |
||||
<SolidColorBrush x:Key="ButtonNormalBorder" Color="#FF707070"/> |
||||
<Geometry x:Key="DownArrowGeometry">M 0 0 L 2 3 L 4 0 Z</Geometry> |
||||
<Style x:Key="ComboBoxReadonlyToggleButton" TargetType="{x:Type ToggleButton}"> |
||||
<Setter Property="OverridesDefaultStyle" Value="true"/> |
||||
<Setter Property="IsTabStop" Value="false"/> |
||||
<Setter Property="Focusable" Value="false"/> |
||||
<Setter Property="ClickMode" Value="Press"/> |
||||
<Setter Property="Template"> |
||||
<Setter.Value> |
||||
<ControlTemplate TargetType="{x:Type ToggleButton}"> |
||||
|
||||
<!-- Button face - changed from original ButtonChrome to Border --> |
||||
<Border Name="FaceBorder" Background="Transparent" CornerRadius="2" BorderBrush="Transparent" BorderThickness="1" Padding="1" > |
||||
<Grid> |
||||
<Grid.ColumnDefinitions> |
||||
<ColumnDefinition Width="*"/> |
||||
<ColumnDefinition Width="5"/> |
||||
</Grid.ColumnDefinitions> |
||||
<!-- |
||||
<Rectangle Width="10" Height="10" Grid.Column="0" Fill="Black"></Rectangle> --> |
||||
<!--<Image Grid.Column="0" Width="10" Height="10" Stretch="Fill" Source="{core:GetBitmap Icons.Magnifier}" />--> |
||||
<Path Grid.Column="1" x:Name="Arrow" Fill="Black" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center" Data="{StaticResource DownArrowGeometry}"/> |
||||
</Grid> |
||||
</Border> |
||||
<ControlTemplate.Triggers> |
||||
<Trigger Property="IsChecked" Value="true"> |
||||
<Setter TargetName="FaceBorder" Property="Background" Value="White"/> |
||||
<Setter TargetName="FaceBorder" Property="BorderBrush" Value="#FFE0EDFF"/> |
||||
</Trigger> |
||||
<Trigger Property="IsMouseOver" Value="true"> |
||||
<Setter TargetName="FaceBorder" Property="Background" Value="White"/> |
||||
<Setter TargetName="FaceBorder" Property="BorderBrush" Value="#FFE0EDFF"/> |
||||
<!-- #FFE0EDFF border --> |
||||
<!-- #FFD9D9E9 fill --> |
||||
</Trigger> |
||||
</ControlTemplate.Triggers> |
||||
</ControlTemplate> |
||||
</Setter.Value> |
||||
</Setter> |
||||
</Style> |
||||
<LinearGradientBrush x:Key="TextBoxBorder" EndPoint="0,20" StartPoint="0,0" MappingMode="Absolute"> |
||||
<GradientStop Color="#ABADB3" Offset="0.05"/> |
||||
<GradientStop Color="#E2E3EA" Offset="0.07"/> |
||||
<GradientStop Color="#E3E9EF" Offset="1"/> |
||||
</LinearGradientBrush> |
||||
<!--<Style TargetType="{x:Type debugging:VisualizerPicker}"> |
||||
<Setter Property="FocusVisualStyle" Value="{StaticResource ComboBoxFocusVisual}"/> |
||||
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/> |
||||
<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/> |
||||
<Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/> |
||||
<Setter Property="BorderThickness" Value="1"/> |
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/> |
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/> |
||||
<Setter Property="Padding" Value="4,3"/> |
||||
<Setter Property="Template"> |
||||
<Setter.Value> |
||||
<ControlTemplate TargetType="{x:Type debugging:VisualizerPicker}"> |
||||
|
||||
ComboBox face |
||||
<Grid x:Name="MainGrid" SnapsToDevicePixels="true" Width="18" Height="16"> |
||||
<Grid.ColumnDefinitions> |
||||
<ColumnDefinition Width="*"/> |
||||
<ColumnDefinition Width="5"/> |
||||
</Grid.ColumnDefinitions> |
||||
<Popup x:Name="PART_Popup" Margin="1" AllowsTransparency="true" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="Bottom" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Grid.ColumnSpan="2"> |
||||
<Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid}" Color="Transparent"> |
||||
<Border x:Name="DropDownBorder" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1"> |
||||
<ScrollViewer CanContentScroll="true"> |
||||
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.DirectionalNavigation="Contained"/> |
||||
</ScrollViewer> |
||||
</Border> |
||||
</Microsoft_Windows_Themes:SystemDropShadowChrome> |
||||
</Popup> |
||||
<ToggleButton Style="{StaticResource ComboBoxReadonlyToggleButton}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/> |
||||
|
||||
Content of ComboBox face |
||||
<ContentPresenter |
||||
HorizontalAlignment="Left" |
||||
Margin="0" |
||||
VerticalAlignment="Center" |
||||
IsHitTestVisible="false" |
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" |
||||
ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" |
||||
Content=" " |
||||
|
||||
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" |
||||
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"/> |
||||
</Grid> |
||||
<ControlTemplate.Triggers> |
||||
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true"> |
||||
<Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/> |
||||
<Setter Property="Color" TargetName="Shdw" Value="#71000000"/> |
||||
</Trigger> |
||||
<Trigger Property="HasItems" Value="false"> |
||||
<Setter Property="Height" TargetName="DropDownBorder" Value="95"/> |
||||
</Trigger> |
||||
<Trigger Property="IsEnabled" Value="false"> |
||||
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> |
||||
<Setter Property="Background" Value="#FFF4F4F4"/> |
||||
</Trigger> |
||||
<Trigger Property="IsGrouping" Value="true"> |
||||
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/> |
||||
</Trigger> |
||||
</ControlTemplate.Triggers> |
||||
</ControlTemplate> |
||||
</Setter.Value> |
||||
</Setter> |
||||
<Style.Triggers> |
||||
<Trigger Property="IsEditable" Value="true"> |
||||
</Trigger> |
||||
</Style.Triggers> |
||||
</Style>--> |
||||
</ResourceDictionary> |
Loading…
Reference in new issue