This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Unity.PlasticSCM.Editor.UI;
namespace Unity.PlasticSCM.Editor.Help
{
internal class FormattedHelpLink
{
internal HelpLink Source;
internal int Position;
internal int Length;
}
internal static class BuildFormattedHelp
{
internal static void ForData(
string plainText,
HelpFormat[] formattedBlocks,
HelpLink[] links,
out string formattedHelpText,
out List<FormattedHelpLink> formattedLinks)
{
formattedHelpText = string.Empty;
formattedLinks = new List<FormattedHelpLink>();
List<object> segments = new List<object>();
segments.AddRange(formattedBlocks);
segments.AddRange(links);
var sortedSegments = segments.OrderBy(
n => n is HelpFormat ?
((HelpFormat)n).Position :
((HelpLink)n).Position);
StringBuilder sb = new StringBuilder();
int lastIndex = 0;
foreach (var segment in sortedSegments)
{
Segment.Data segmentData = GetSegmentData(segment);
if (segmentData.Begin > lastIndex)
sb.Append(plainText.Substring(lastIndex, segmentData.Begin - lastIndex));
string plainSegment = plainText.Substring(
segmentData.Begin, segmentData.Length);
if (segment is HelpLink)
{
formattedLinks.Add(new FormattedHelpLink()
{
Source = (HelpLink)segment,
Position = sb.Length,
Length = segmentData.Prefix.Length +
plainSegment.Length +
segmentData.Suffix.Length
});
}
sb.Append(segmentData.Prefix);
sb.Append(plainSegment);
sb.Append(segmentData.Suffix);
lastIndex = segmentData.Begin + segmentData.Length;
}
sb.Append(plainText.Substring(lastIndex));
formattedHelpText = sb.ToString();
}
internal static bool IsLinkMetaChar(
FormattedHelpLink formattedLink,
int charIndex)
{
int prefixEndIndex =
formattedLink.Position +
Segment.LINK_PREFIX.Length - 1;
if (formattedLink.Position <= charIndex &&
charIndex <= prefixEndIndex)
return true;
int suffixStartIndex =
formattedLink.Position +
formattedLink.Length - Segment.LINK_SUFFIX.Length;
if (suffixStartIndex <= charIndex &&
charIndex <= (formattedLink.Position + formattedLink.Length - 1))
return true;
return false;
}
static Segment.Data GetSegmentData(object segment)
{
if (segment is HelpLink)
{
HelpLink link = (HelpLink)segment;
return Segment.BuildForLink(link);
}
HelpFormat format = (HelpFormat)segment;
return Segment.BuildForFormat(format);
}
static class Segment
{
internal class Data
{
internal int Begin;
internal int Length;
internal string Prefix;
internal string Suffix;
}
internal static Data BuildForLink(HelpLink link)
{
return new Data()
{
Begin = link.Position,
Length = link.Length,
Prefix = LINK_PREFIX,
Suffix = LINK_SUFFIX
};
}
internal static Data BuildForFormat(HelpFormat format)
{
switch (format.Type)
{
case HelpFormat.FormatType.Title:
return new Data()
{
Begin = format.Position,
Length = format.Length,
Prefix = TITLE_PREFIX,
Suffix = TITLE_SUFFIX
};
case HelpFormat.FormatType.Bold:
return new Data()
{
Begin = format.Position,
Length = format.Length,
Prefix = BOLD_PREFIX,
Suffix = BOLD_SUFFIX
};
case HelpFormat.FormatType.Underline:
// NOTE(rafa): No support yet for underline, we use italic instead
return new Data()
{
Begin = format.Position,
Length = format.Length,
Prefix = ITALIC_PREFIX,
Suffix = ITALIC_SUFFIX
};
default:
return null;
}
}
internal const string LINK_PREFIX = "<color=\"" + UnityStyles.HexColors.LINK_COLOR + "\">";
internal const string LINK_SUFFIX = "</color>";
const string TITLE_PREFIX = "<size=16>";
const string TITLE_SUFFIX = "</size>";
const string BOLD_PREFIX = "<b>";
const string BOLD_SUFFIX = "</b>";
const string ITALIC_PREFIX = "<i>";
const string ITALIC_SUFFIX = "</i>";
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ed669a00ecaa3ff45b9d6eb52f4a478f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,184 @@
using System.Diagnostics;
using UnityEditor;
using UnityEngine;
using Codice.Client.Common;
using PlasticGui;
using Unity.PlasticSCM.Editor.UI;
namespace Unity.PlasticSCM.Editor.Help
{
internal static class DrawHelpPanel
{
internal static void For(
HelpPanel helpPanel)
{
if (!helpPanel.Visible)
return;
DoHelpPanelToolbar(helpPanel);
GUILayout.Space(10);
DoHelpPanelContent(helpPanel);
}
static void DoHelpPanelToolbar(
HelpPanel helpPanel)
{
Rect rect = GUILayoutUtility.GetLastRect();
rect.y = rect.yMax;
rect.height = 22;
GUILayout.Space(1);
GUIStyle expandableToolbar = new GUIStyle(EditorStyles.toolbar);
expandableToolbar.fixedHeight = 0;
GUI.Label(rect, GUIContent.none, expandableToolbar);
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("<", EditorStyles.miniButtonLeft))
{
// TODO(codice): On Left Clicked
}
if (GUILayout.Button(">", EditorStyles.miniButtonRight))
{
// TODO(codice): On Right Clicked
}
GUILayout.FlexibleSpace();
// TODO(codice): The bool used here must be loaded and persisted by some means
helpPanel.Data.ShouldShowAgain = EditorGUILayout.ToggleLeft(
PlasticLocalization.GetString(PlasticLocalization.Name.DontShowItAgain),
helpPanel.Data.ShouldShowAgain, UnityStyles.MiniToggle);
bool okWasPressed = GUILayout.Button(
PlasticLocalization.GetString(PlasticLocalization.Name.OkButton),
EditorStyles.miniButton);
if (okWasPressed)
{
helpPanel.Hide();
// TODO(codice): Do on helppanel dismiss actions
return;
}
}
}
static void DoHelpPanelContent(
HelpPanel helpPanel)
{
using (new EditorGUILayout.HorizontalScope())
{
using (new EditorGUILayout.VerticalScope())
{
GUIStyle helpParagraph = UnityStyles.Paragraph;
helpPanel.TextScroll = GUILayout.BeginScrollView(helpPanel.TextScroll);
GUILayout.Label(helpPanel.GUIContent, helpParagraph);
if (Event.current.type != EventType.Layout)
DoHelpPanelLinks(helpPanel, helpParagraph);
GUILayout.EndScrollView();
Rect scrollRect = GUILayoutUtility.GetLastRect();
if (Mouse.IsRightMouseButtonPressed(Event.current) &&
scrollRect.Contains(Event.current.mousePosition))
{
GenericMenu contextMenu = BuildHelpPanelMenu(helpPanel.Data.CleanText);
contextMenu.ShowAsContext();
}
}
}
}
static void DoHelpPanelLinks(
HelpPanel helpPanel,
GUIStyle helpParagraph)
{
var lastRect = GUILayoutUtility.GetLastRect();
bool linkWasClicked = false;
GUIContent charContent = new GUIContent();
for (int charIdx = 0; charIdx < helpPanel.GUIContent.text.Length; charIdx++)
{
HelpLink link;
if (!helpPanel.TryGetLinkAtChar(charIdx, out link))
continue;
charContent.text = helpPanel.GUIContent.text[charIdx].ToString();
var pos = helpParagraph.GetCursorPixelPosition(
lastRect, helpPanel.GUIContent, charIdx);
float charWidth = helpParagraph.CalcSize(charContent).x;
Rect charRect = new Rect(pos, new Vector2(
charWidth - 4, helpParagraph.lineHeight));
if (!linkWasClicked &&
Mouse.IsLeftMouseButtonPressed(Event.current) &&
charRect.Contains(Event.current.mousePosition))
{
linkWasClicked = true;
OnHelpLinkClicked(helpPanel, link);
}
// Underline for links
charRect.y = charRect.yMax - 1;
charRect.height = 1;
GUI.DrawTexture(charRect, Images.GetLinkUnderlineImage());
}
}
static void OnHelpLinkClicked(
HelpPanel helpPanel,
HelpLink helpLink)
{
HelpLink.LinkType linkType;
string content;
if (!HelpLinkData.TryGet(helpLink.Link, out linkType, out content))
return;
switch (linkType)
{
case HelpLink.LinkType.Action:
GuiMessage.ShowInformation(
"An ACTION link has been clicked:\n" + content);
break;
case HelpLink.LinkType.Help:
helpPanel.Show(
content == "sample1" ?
TestingHelpData.GetSample1() :
TestingHelpData.GetSample2());
break;
case HelpLink.LinkType.Link:
Process.Start(content);
break;
}
}
static void CopyToClipboard(string data)
{
EditorGUIUtility.systemCopyBuffer = data;
}
static GenericMenu BuildHelpPanelMenu(string cleanText)
{
GenericMenu result = new GenericMenu();
result.AddItem(
new GUIContent(PlasticLocalization.GetString(PlasticLocalization.Name.Copy)),
false,
() => CopyToClipboard(cleanText)
);
return result;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db1d9800cb39bae4299b611ea7d86b2d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
namespace Unity.PlasticSCM.Editor.Help
{
internal class ExternalLink
{
internal string Label;
internal string Url;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8e956137d866cfc4797b1830930ed57e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace Unity.PlasticSCM.Editor.Help
{
internal class HelpData
{
internal List<HelpFormat> FormattedBlocks = new List<HelpFormat>();
internal List<HelpLink> Links = new List<HelpLink>();
internal string CleanText;
internal bool ShouldShowAgain;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 888ac39227622bd40b0b9956dc68b73d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
namespace Unity.PlasticSCM.Editor.Help
{
internal class HelpFormat
{
internal enum FormatType
{
Title,
Bold,
Underline
}
internal int Position;
internal int Length;
internal FormatType Type;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7eede78e1db095499f674a48eac686b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
namespace Unity.PlasticSCM.Editor.Help
{
internal class HelpLink
{
internal enum LinkType
{
Action,
Help,
Link,
}
internal int Position;
internal int Length;
internal string Link;
internal LinkType Type = LinkType.Action;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7613946404a945a43bd9b5142a436f21
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
using System;
using Codice.LogWrapper;
namespace Unity.PlasticSCM.Editor.Help
{
internal static class HelpLinkData
{
internal static bool TryGet(
string link, out HelpLink.LinkType type, out string content)
{
type = HelpLink.LinkType.Link;
content = string.Empty;
int separatorIdx = link.IndexOf(':');
if (separatorIdx == -1)
return false;
string key = link.Substring(0, separatorIdx);
try
{
type = (HelpLink.LinkType)Enum.Parse(
typeof(HelpLink.LinkType), key, true);
}
catch (Exception ex)
{
mLog.ErrorFormat("Unable to get help link data: '{0}': {1}",
key, ex.Message);
mLog.DebugFormat("StackTrace: {0}", ex.StackTrace);
return false;
}
content = link.Substring(separatorIdx + 1);
return true;
}
static readonly ILog mLog = PlasticApp.GetLogger("HelpLinkData");
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1d2cd7920afc0741b8baae80a676934
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,115 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Unity.PlasticSCM.Editor.UI;
namespace Unity.PlasticSCM.Editor.Help
{
internal class HelpPanel
{
internal Vector2 TextScroll;
internal bool Visible { get; private set; }
internal HelpData Data
{
get { return mHelpData; }
}
internal GUIContent GUIContent
{
get { return mHelpGUIContent; }
}
internal HelpPanel(EditorWindow window)
{
mWindow = window;
}
internal void Show(HelpData helpData)
{
ClearData();
UpdateData(helpData);
Visible = true;
mWindow.Repaint();
}
internal void Hide()
{
ClearData();
Visible = false;
mWindow.Repaint();
}
internal bool TryGetLinkAtChar(
int charIndex,
out HelpLink link)
{
link = null;
FormattedHelpLink formattedLink = GetFormattedLinkAtChar(
mFormattedLinks, charIndex);
if (formattedLink == null)
return false;
link = formattedLink.Source;
return !BuildFormattedHelp.IsLinkMetaChar(formattedLink, charIndex);
}
void ClearData()
{
mHelpData = null;
mHelpGUIContent = null;
mFormattedLinks = null;
}
void UpdateData(HelpData helpData)
{
mHelpData = helpData;
string formattedHelpText;
BuildFormattedHelp.ForData(
mHelpData.CleanText,
mHelpData.FormattedBlocks.ToArray(),
mHelpData.Links.ToArray(),
out formattedHelpText,
out mFormattedLinks);
mHelpGUIContent = new GUIContent(formattedHelpText);
}
static FormattedHelpLink GetFormattedLinkAtChar(
List<FormattedHelpLink> formattedLinks, int charIndex)
{
for(int i = 0; i < formattedLinks.Count; i++)
{
FormattedHelpLink link = formattedLinks[i];
if (link.Position <= charIndex &&
charIndex < link.Position + link.Length)
return link;
if (charIndex <= link.Position + link.Length)
return null;
}
return null;
}
HelpData mHelpData;
GUIContent mHelpGUIContent;
List<FormattedHelpLink> mFormattedLinks;
EditorWindow mWindow;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8bbed09c8363d9a4eb93c8bd2afb96eb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,205 @@
using System.Collections.Generic;
namespace Unity.PlasticSCM.Editor.Help
{
internal static class TestingHelpData
{
internal static HelpData GetSample1()
{
HelpData result = new HelpData();
result.CleanText = "There are some .private files" + System.Environment.NewLine + System.Environment.NewLine +
"Do not panic, these are copies Plastic creates to preserve files it can't overwrite." + System.Environment.NewLine + System.Environment.NewLine +
"Suppose you have a private file \"src / foo.c\", then switch your workspace to a branch where someone added \"src / foo.c\". Plastic downloads the new file because it is under source control and yours is not. But, it can't delete yours, so it renames it as .private.0." + System.Environment.NewLine + System.Environment.NewLine +
"Makes sense?" + System.Environment.NewLine + System.Environment.NewLine +
"Learn more:" + System.Environment.NewLine +
"* You have some files ready to be added to version control" + System.Environment.NewLine +
"* Are you missing any changes?" + System.Environment.NewLine +
"* Tips to work with Visual Studio projects." + System.Environment.NewLine + System.Environment.NewLine +
"This is just text after links (like this help link -> content2) to verify that the format is preserved." + System.Environment.NewLine + System.Environment.NewLine +
"And then that another link at the end works, with some bold text at the end, final!!";
// IMPORTANT! We need single EOL chars to calculate the positions,
// otherwise positions are wrong calculated
result.CleanText = result.CleanText.Replace("\r\n", "\n");
result.FormattedBlocks = new List<HelpFormat>();
result.FormattedBlocks.Add(new HelpFormat()
{
Type = HelpFormat.FormatType.Title,
Position = 0,
Length = 29
});
result.FormattedBlocks.Add(new HelpFormat()
{
Type = HelpFormat.FormatType.Bold,
Position = result.CleanText.IndexOf("not panic"),
Length = "not panic".Length
});
result.FormattedBlocks.Add(new HelpFormat()
{
Type = HelpFormat.FormatType.Underline,
Position = result.CleanText.IndexOf("overwrite"),
Length = "overwrite".Length
});
result.FormattedBlocks.Add(new HelpFormat()
{
Type = HelpFormat.FormatType.Bold,
Position = result.CleanText.IndexOf("Makes sense?"),
Length = "Makes sense?".Length
});
result.Links = new List<HelpLink>();
result.Links.Add(new HelpLink()
{
Position = result.CleanText.IndexOf("You have some files ready to be added to version control"),
Length = "You have some files ready to be added to version control".Length,
Link = HelpLinkData.AsString(HelpLink.LinkType.Action, "plasticscm-pendingchanges-filestoadd")
});
result.Links.Add(new HelpLink()
{
Position = result.CleanText.IndexOf("Are you missing any changes?"),
Length = "Are you missing any changes?".Length,
Link = HelpLinkData.AsString(HelpLink.LinkType.Action, "plasticscm-pendingchanges-missingchanges")
});
result.Links.Add(new HelpLink()
{
Position = result.CleanText.IndexOf("Tips to work with Visual Studio projects."),
Length = "Tips to work with Visual Studio projects.".Length,
Link = HelpLinkData.AsString(HelpLink.LinkType.Action, "plasticscm-pendingchanges-visualstudio")
});
result.FormattedBlocks.Add(new HelpFormat()
{
Type = HelpFormat.FormatType.Bold,
Position = result.CleanText.IndexOf("verify that the format is preserved"),
Length = "verify that the format is preserved".Length
});
result.Links.Add(new HelpLink()
{
Position = result.CleanText.IndexOf("this help link"),
Length = "this help link".Length,
Link = HelpLinkData.AsString(HelpLink.LinkType.Help, "sample2")
});
result.Links.Add(new HelpLink()
{
Position = result.CleanText.IndexOf("another link at the end"),
Length = "another link at the end".Length,
Link = HelpLinkData.AsString(HelpLink.LinkType.Link, "https://www.google.com")
});
result.FormattedBlocks.Add(new HelpFormat()
{
Type = HelpFormat.FormatType.Bold,
Position = result.CleanText.IndexOf("bold text at the end"),
Length = "bold text at the end".Length
});
return result;
}
internal static HelpData GetSample2()
{
HelpData result = new HelpData();
result.CleanText = "Alternative title to confirm that all is working" + System.Environment.NewLine + System.Environment.NewLine +
"This is just another help example to ensure that the panel replaces the helps dynamically." + System.Environment.NewLine + System.Environment.NewLine +
"If you're reading this text, means that the help changed its content dynamically, so we can navigate between help tips by clicking hyperlinks" + System.Environment.NewLine + System.Environment.NewLine +
"Makes sense?" + System.Environment.NewLine + System.Environment.NewLine +
"Learn more:" + System.Environment.NewLine +
"* You have some files ready to be added to version control" + System.Environment.NewLine +
"* Are you missing any changes?" + System.Environment.NewLine +
"* Tips to work with Visual Studio projects." + System.Environment.NewLine + System.Environment.NewLine +
"This is just text after links (like this help link -> content1) to verify that the format is preserved.";
// IMPORTANT! We need single EOL chars to calculate the positions,
// otherwise positions are wrong calculated
result.CleanText = result.CleanText.Replace("\r\n", "\n");
result.FormattedBlocks = new List<HelpFormat>();
result.FormattedBlocks.Add(new HelpFormat()
{
Type = HelpFormat.FormatType.Title,
Position = 0,
Length = "Alternative title to confirm that all is working".Length
});
result.FormattedBlocks.Add(new HelpFormat()
{
Type = HelpFormat.FormatType.Bold,
Position = result.CleanText.IndexOf("replaces the helps dynamically"),
Length = "replaces the helps dynamically".Length
});
result.Links = new List<HelpLink>();
result.Links.Add(new HelpLink()
{
Position = result.CleanText.IndexOf("You have some files ready to be added to version control"),
Length = "You have some files ready to be added to version control".Length,
Link = HelpLinkData.AsString(HelpLink.LinkType.Action, "plasticscm-pendingchanges-filestoadd")
});
result.Links.Add(new HelpLink()
{
Position = result.CleanText.IndexOf("Are you missing any changes?"),
Length = "Are you missing any changes?".Length,
Link = HelpLinkData.AsString(HelpLink.LinkType.Action, "plasticscm-pendingchanges-missingchanges")
});
result.Links.Add(new HelpLink()
{
Position = result.CleanText.IndexOf("Tips to work with Visual Studio projects."),
Length = "Tips to work with Visual Studio projects.".Length,
Link = HelpLinkData.AsString(HelpLink.LinkType.Action, "plasticscm-pendingchanges-visualstudio")
});
result.Links.Add(new HelpLink()
{
Position = result.CleanText.IndexOf("this help link"),
Length = "this help link".Length,
Link = HelpLinkData.AsString(HelpLink.LinkType.Help, "sample1")
});
return result;
}
class HelpLinkData
{
internal static string AsString(HelpLink.LinkType linkType, string linkContent)
{
string linkTypeString = string.Empty;
switch (linkType)
{
case HelpLink.LinkType.Action:
linkTypeString = ACTION;
break;
case HelpLink.LinkType.Help:
linkTypeString = HELP;
break;
case HelpLink.LinkType.Link:
linkTypeString = LINK;
break;
}
return string.Concat(linkTypeString, SEPARATOR, linkContent);
}
const string ACTION = "action";
const string HELP = "help";
const string LINK = "link";
const string SEPARATOR = ":";
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fae031d3fccb34244b789b0048117ee7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: