Skip to content

Commit

Permalink
! Redesigned Super Quick Info
Browse files Browse the repository at this point in the history
+ Made text in Super Quick Info selectable (right click to copy)
- Fixed potential crash in Quick Info introduced in previous version
  • Loading branch information
wmjordan committed Jan 1, 2019
1 parent b385d51 commit 61b7e0b
Show file tree
Hide file tree
Showing 16 changed files with 540 additions and 191 deletions.
3 changes: 2 additions & 1 deletion Codist/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,8 @@ public enum QuickInfoOptions
OverrideDefaultDocumentation = 1 << 17,
DocumentationFromBaseType = 1 << 18,
DocumentationFromInheritDoc = 1 << 19,
TextOnlyDoc = 1 << 22,
TextOnlyDoc = 1 << 21,
ExceptionDoc = 1 << 22,
ReturnsDoc = 1 << 23,
RemarksDoc = 1 << 24,
AlternativeStyle = 1 << 25,
Expand Down
101 changes: 100 additions & 1 deletion Codist/Controls/ThemedText.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
using System.Windows;
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using Microsoft.VisualStudio.PlatformUI;

namespace Codist.Controls
{
sealed class ThemedTipText : TextBlock
{
static ThemedTipText() {
FocusableProperty.OverrideMetadata(typeof(ThemedTipText), new FrameworkPropertyMetadata(true));
TextEditorWrapper.RegisterCommandHandlers(typeof(ThemedTipText), true, true, true);

// remove the focus rectangle around the control
FocusVisualStyleProperty.OverrideMetadata(typeof(ThemedTipText), new FrameworkPropertyMetadata((object)null));
}
readonly TextEditorWrapper _editor;
public ThemedTipText() {
TextWrapping = TextWrapping.Wrap;
Foreground = ThemeHelper.ToolTipTextBrush;
_editor = TextEditorWrapper.CreateFor(this);
}
public ThemedTipText(string text) : this() {
Inlines.Add(text);
Expand All @@ -27,6 +38,58 @@ public ThemedTipText UnderlineLastRun() {
return this;
}
}
sealed class ThemedTipDocument : StackPanel
{
public ThemedTipDocument Append(ThemedTipParagraph block) {
Children.Add(block);
return this;
}
public ThemedTipDocument AppendTitle(int imageId, string text) {
Children.Add(new ThemedTipParagraph(imageId, new ThemedTipText(text, true)));
return this;
}
public void ApplySizeLimit() {
var w = Config.Instance.QuickInfoMaxWidth - (WpfHelper.IconRightMargin + ThemeHelper.DefaultIconSize + WpfHelper.SmallMarginSize + WpfHelper.SmallMarginSize + 22/*scrollbar width*/);
if (w <= 0) {
return;
}
foreach (var item in Children) {
var r = item as ThemedTipParagraph;
if (r != null) {
r.Content.MaxWidth = w;
}
}
}
public UIElement Host() {
return this;
}
}
sealed class ThemedTipParagraph : StackPanel
{
const int PlaceHolderSize = WpfHelper.IconRightMargin + ThemeHelper.DefaultIconSize;

ThemedTipParagraph() {
Margin = WpfHelper.TinyMargin;
Orientation = Orientation.Horizontal;
}
public ThemedTipParagraph(int iconId, TextBlock content) : this() {
if (iconId == 0) {
Children.Add(new Border { Height = WpfHelper.IconRightMargin, Width = PlaceHolderSize });
}
else {
var icon = ThemeHelper.GetImage(iconId).WrapMargin(WpfHelper.GlyphMargin);
icon.VerticalAlignment = VerticalAlignment.Top;
Children.Add(icon);
}
Children.Add(Content = content ?? new ThemedTipText());
}
public ThemedTipParagraph(int iconId) : this(iconId, null) {
}
public ThemedTipParagraph(TextBlock content) : this(0, content) {
}
public Image Icon => Children[0] as Image;
public TextBlock Content { get; private set; }
}
sealed class ThemedToolBarText : TextBlock
{
public ThemedToolBarText() {
Expand All @@ -48,4 +111,40 @@ public ThemedMenuText(string text, bool bold) : this() {
this.Append(text, bold);
}
}

// https://stackoverflow.com/questions/136435/any-way-to-make-a-wpf-textblock-selectable
sealed class TextEditorWrapper
{
private static readonly Type TextEditorType = Type.GetType("System.Windows.Documents.TextEditor, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
private static readonly PropertyInfo IsReadOnlyProp = TextEditorType.GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly PropertyInfo TextViewProp = TextEditorType.GetProperty("TextView", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly MethodInfo RegisterMethod = TextEditorType.GetMethod("RegisterCommandHandlers",
BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(Type), typeof(bool), typeof(bool), typeof(bool) }, null);

private static readonly Type TextContainerType = Type.GetType("System.Windows.Documents.ITextContainer, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
private static readonly PropertyInfo TextContainerTextViewProp = TextContainerType.GetProperty("TextView");

private static readonly PropertyInfo TextContainerProp = typeof(TextBlock).GetProperty("TextContainer", BindingFlags.Instance | BindingFlags.NonPublic);

public static void RegisterCommandHandlers(Type controlType, bool acceptsRichContent, bool readOnly, bool registerEventListeners) {
RegisterMethod.Invoke(null, new object[] { controlType, acceptsRichContent, readOnly, registerEventListeners });
}

public static TextEditorWrapper CreateFor(TextBlock tb) {
var textContainer = TextContainerProp.GetValue(tb);

var editor = new TextEditorWrapper(textContainer, tb, false);
IsReadOnlyProp.SetValue(editor._editor, true);
TextViewProp.SetValue(editor._editor, TextContainerTextViewProp.GetValue(textContainer));

return editor;
}

private readonly object _editor;

public TextEditorWrapper(object textContainer, FrameworkElement uiScope, bool isUndoEnabled) {
_editor = Activator.CreateInstance(TextEditorType, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.CreateInstance,
null, new[] { textContainer, uiScope, isUndoEnabled }, null);
}
}
}
56 changes: 29 additions & 27 deletions Codist/Helpers/SymbolFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Immutable;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using AppHelpers;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -257,17 +258,17 @@ internal TextBlock ToUIText(TextBlock block, ImmutableArray<SymbolDisplayPart> p
return block;
}

internal void ToUIText(TextBlock block, TypedConstant constant) {
internal void ToUIText(InlineCollection block, TypedConstant constant) {
switch (constant.Kind) {
case TypedConstantKind.Primitive:
if (constant.Value is bool) {
block.Append((bool)constant.Value ? "true" : "false", Keyword);
block.Add(WpfHelper.Render((bool)constant.Value ? "true" : "false", Keyword));
}
else if (constant.Value is string) {
block.Append(constant.ToCSharpString(), Text);
block.Add(WpfHelper.Render(constant.ToCSharpString(), Text));
}
else {
block.Append(constant.ToCSharpString(), Number);
block.Add(WpfHelper.Render(constant.ToCSharpString(), Number));
}
break;
case TypedConstantKind.Enum:
Expand All @@ -283,75 +284,76 @@ internal void ToUIText(TextBlock block, TypedConstant constant) {
var flags = items.ToArray();
for (int i = 0; i < flags.Length; i++) {
if (i > 0) {
block.Append(" | ");
block.Add(" | ");
}
block.Append(constant.Type.Name + "." + flags[i].Name, Enum);
block.Add(WpfHelper.Render(constant.Type.Name + "." + flags[i].Name, Enum));
}
}
else {
block.Append(constant.Type.Name + en.Substring(en.LastIndexOf('.')), Enum);
block.Add(WpfHelper.Render(constant.Type.Name + en.Substring(en.LastIndexOf('.')), Enum));
}
break;
case TypedConstantKind.Type:
block.Append("typeof", Keyword).Append("(")
.AddSymbol(constant.Value as ITypeSymbol, null, this)
.Append(")");
block.Add(WpfHelper.Render("typeof", Keyword));
block.Add("(");
ToUIText(block, constant.Value as ITypeSymbol, null);
block.Add(")");
break;
case TypedConstantKind.Array:
block.Append("{");
block.Add("{");
bool c = false;
foreach (var item in constant.Values) {
if (c == false) {
c = true;
}
else {
block.Append(", ");
block.Add(", ");
}
ToUIText(block, item);
}
block.Append("}");
block.Add("}");
break;
default:
block.Append(constant.ToCSharpString());
block.Add(constant.ToCSharpString());
break;
}
}

internal TextBlock ToUIText(TextBlock block, AttributeData item) {
internal void ToUIText(InlineCollection block, AttributeData item) {
var a = item.AttributeClass.Name;
block.Append("[")
.AddSymbol(item.AttributeConstructor ?? (ISymbol)item.AttributeClass, a.EndsWith("Attribute", StringComparison.Ordinal) ? a.Substring(0, a.Length - 9) : a, Class);
block.Add("[");
block.Add(WpfHelper.Render(item.AttributeConstructor ?? (ISymbol)item.AttributeClass, a.EndsWith("Attribute", StringComparison.Ordinal) ? a.Substring(0, a.Length - 9) : a, Class));
if (item.ConstructorArguments.Length == 0 && item.NamedArguments.Length == 0) {
var node = item.ApplicationSyntaxReference?.GetSyntax() as Microsoft.CodeAnalysis.CSharp.Syntax.AttributeSyntax;
if (node != null && node.ArgumentList?.Arguments.Count > 0) {
block.Append(node.ArgumentList.ToString(), ThemeHelper.SystemGrayTextBrush);
block.Add(WpfHelper.Render(node.ArgumentList.ToString(), ThemeHelper.SystemGrayTextBrush));
}
block.Append("]");
return block;
block.Add("]");
return;
}
block.Append("(");
block.Add("(");
int i = 0;
foreach (var arg in item.ConstructorArguments) {
if (++i > 1) {
block.Append(", ");
block.Add(", ");
}
ToUIText(block, arg);
}
foreach (var arg in item.NamedArguments) {
if (++i > 1) {
block.Append(", ");
block.Add(", ");
}
var attrMember = item.AttributeClass.GetMembers(arg.Key).FirstOrDefault(m => m.Kind == SymbolKind.Field || m.Kind == SymbolKind.Property);
if (attrMember != null) {
block.Append(arg.Key, attrMember.Kind == SymbolKind.Property ? Property : Field);
block.Add(WpfHelper.Render(arg.Key, attrMember.Kind == SymbolKind.Property ? Property : Field));
}
else {
block.Append(arg.Key, false, true, null);
block.Add(WpfHelper.Render(arg.Key, false, true, null));
}
block.Append("=");
block.Add("=");
ToUIText(block, arg.Value);
}
return block.Append(")]");
block.Add(")]");
}

internal void UpdateSyntaxHighlights(IEditorFormatMap formatMap) {
Expand Down
5 changes: 3 additions & 2 deletions Codist/Helpers/ThemeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ namespace Codist
{
static class ThemeHelper
{
internal const int DefaultIconSize = 16;

static readonly Microsoft.VisualStudio.Text.Classification.IClassificationFormatMap __ToolTipFormatMap = ServicesHelper.Instance.ClassificationFormatMap.GetClassificationFormatMap("tooltip");
static ThemeHelper() {
RefreshThemeCache();
Expand Down Expand Up @@ -88,13 +90,12 @@ public static void GetFontSettings(string categoryGuid, out string fontName, out
/// </summary>
/// <param name="imageId">The image id.</param>
public static CrispImage GetImage(int imageId, int size = 0) {
const int DEFAULT_SIZE = 16;
var moniker = new ImageMoniker {
Guid = KnownImageIds.ImageCatalogGuid,
Id = imageId
};
if (size < 1) {
size = DEFAULT_SIZE;
size = DefaultIconSize;
}
var image = new CrispImage {
Moniker = moniker,
Expand Down
2 changes: 1 addition & 1 deletion Codist/Helpers/ToolTipFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static Controls.ThemedToolTip CreateToolTip(ISymbol symbol, Compilation c
content.Append("\nconst: " + f.ConstantValue?.ToString()); // sometimes the const value could be null
}
foreach (var attr in symbol.GetAttributes()) {
SymbolFormatter.Empty.ToUIText(content.AppendLine(), attr);
SymbolFormatter.Empty.ToUIText(content.AppendLine().Inlines, attr);
}
var doc = new XmlDoc(symbol, compilation);
var summary = doc.Summary ?? (Config.Instance.QuickInfoOptions.MatchFlags(QuickInfoOptions.DocumentationFromInheritDoc) ? doc.ExplicitInheritDoc?.Summary : null);
Expand Down
Loading

0 comments on commit 61b7e0b

Please sign in to comment.