Last active
June 21, 2023 17:22
-
-
Save Twilight-Dream-Of-Magic/97787db37a66ab77e74ebc31ad57f745 to your computer and use it in GitHub Desktop.
MiniHyperTextNew
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Unity Component Requirements: HyperText Mini (Candlelight) | |
Summary: | |
This component is based on the Candlelight HyperText version for Unity 2018 or later. | |
It should allow for the creation and management of interactive and non-interactive texts within the Unity engine. | |
The component should not rely on TextMeshPro and should be accessible, usable, or editable within the Unity Inspector through the generated code. | |
Do not use TextMeshPro or any other third-party plugins. | |
Features: | |
1. Text Division: | |
- Normal text: Non-interactive text. | |
- Interactive text: Interactive text that has two sub-categories: | |
a) Directly open the browser or other protocols (http, https, ftp, mailto). | |
b) Trigger events related to the interactive text. | |
2. Interactive Text Style: | |
Interactive text should have a style composed of text mixed with continuous underscores, similar to how websites display hyperlinks. | |
3. Multiple Gradient Colors for Text Renderer: | |
The component should support rendering text with multiple gradient colors. | |
4. Inspector Functionality: | |
The component must provide the following functionality within the Unity editor inspector: | |
1. Interactable : bool | |
2. Link Hitbox Padding : bool | |
Styles: | |
Link Keyword Collections (List is Empty) [+] [-] | |
Tag Keyword Collections (List is Empty) [+] [-] | |
Quad Keyword Collections (List is Empty) [+] [-] | |
Text Box Content Editor: | |
Override Text Source : [ None (Object) ] | |
3. Character: | |
3-1. Font : [ Font (Object) ] | |
3-2. FontStyle : Enum | |
3-3. FontSize : int | |
3-4. LineSpacing | |
3-5. RichText : bool | |
4. Paragraph: | |
4-1. Alignment | |
4-2. HorizontalOverflow : enum | |
4-3. VerticalOverflow : enum | |
4-4. BestFit | |
4-5. Color (Can Gradient) | |
4-6. Material : [ None (Material) ] | |
5. Events: | |
5-1. OnClick (HyperText, LinkInfo) (List is Empty) [+] [-] | |
5-2. OnEnter (HyperText, LinkInfo) (List is Empty) [+] [-] | |
5-3. OnExit (HyperText, LinkInfo) (List is Empty) [+] [-] | |
5-4. OnPress (HyperText, LinkInfo) (List is Empty) [+] [-] | |
5-5. OnRelease (HyperText, LinkInfo) (List is Empty) [+] [-] | |
*/ | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Text.RegularExpressions; | |
using UnityEngine; | |
using UnityEngine.EventSystems; | |
using UnityEngine.Events; | |
using UnityEngine.UI; | |
using TextFormatChecker = Twilight_Dream.Utilities.TextFormatChecker; | |
namespace Twilight_Dream.Utilities.HyperTexts.Version2 | |
{ | |
public enum InteractiveElementType | |
{ | |
None, | |
EventFunction, | |
URL | |
} | |
public class InteractiveElementInfo | |
{ | |
public InteractiveElementType Type { get; private set; } | |
//Starting vertex index of the text in the hyperlink | |
public int VertexStartIndex = 0; | |
//Ending vertex index of the text in the hyperlink | |
public int VertexEndIndex = 0; | |
public string Data { get; private set; } | |
public readonly List<Rect> Boxes = new List<Rect>(); | |
public InteractiveElementInfo(InteractiveElementType type, string data) | |
{ | |
Type = type; | |
Data = data; | |
} | |
} | |
public enum ColorMode | |
{ | |
SingleColor, | |
GradientColor | |
} | |
public class MiniHyperTextNew : UnityEngine.UI.Text, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler | |
{ | |
[Header("Interactable Settings")] | |
public bool interactable = false; | |
public float linkHitboxPaddingFactor = 0.5f; | |
[Header("Font Setting")] | |
public bool freeTextSize = false; | |
[Header("Color Settings")] | |
public ColorMode colorMode = ColorMode.SingleColor; | |
public Gradient colorGradient; | |
[Header("Text Box Content")] | |
public TextAsset overrideTextSource; | |
[System.Serializable] | |
public class LinkClickEvent : UnityEvent<string> | |
{ | |
} | |
[Header("Events")] | |
public LinkClickEvent onEventFunctionClick; // Event for EventFunction clicks | |
/// <summary> | |
/// List of hyperlinked information | |
/// </summary> | |
private readonly List<InteractiveElementInfo> interactiveElementInfos = new List<InteractiveElementInfo>(); | |
private bool TextDirty = false; | |
[TextArea(3, 10), SerializeField] | |
protected string OriginText; | |
public override string text | |
{ | |
get => ParsedTextString; | |
set | |
{ | |
if (string.IsNullOrEmpty(value)) | |
{ | |
if (string.IsNullOrEmpty(ParsedTextString)) | |
{ | |
return; | |
} | |
OriginText = string.Empty; | |
TextDirty = true; | |
SetVerticesDirty(); | |
} | |
else | |
{ | |
if (OriginText == value) | |
{ | |
return; | |
} | |
OriginText = value; | |
TextDirty = true; | |
SetVerticesDirty(); | |
} | |
} | |
} | |
private string ParsedTextString = string.Empty; | |
protected override void Awake() | |
{ | |
base.Awake(); | |
// Load the text from the overrideTextSource if provided | |
if (overrideTextSource != null) | |
{ | |
OriginText = overrideTextSource.text; | |
} | |
} | |
protected override void OnEnable() | |
{ | |
base.OnEnable(); | |
} | |
protected override void OnDisable() | |
{ | |
base.OnDisable(); | |
} | |
public override void SetVerticesDirty() | |
{ | |
base.SetVerticesDirty(); | |
TextDirty = true; | |
// Parse the regex text or text and create the interactive elements | |
ParsedTextString = ParseRegexText(OriginText); | |
} | |
private string ParseRegexText(string OriginText) | |
{ | |
// Check if the text is marked as interactable | |
if (interactable && (OriginText.Contains("<EventFunctionName>") || OriginText.Contains("<URLName>"))) | |
{ | |
interactiveElementInfos.Clear(); | |
if (string.IsNullOrEmpty(OriginText)) | |
return ""; | |
string temporaryText = OriginText; | |
/* | |
Use regex to find matches for event functions and URLs | |
*/ | |
{ | |
/* | |
The offset variable is used to keep track of how many characters are added or removed from the original text. | |
For example, if you have a function name like foo, and you replace it with <u>foo</u>, you are adding 7 characters to the text. | |
This means that the next match will have a different index than the original text. | |
To account for this, you need to add 7 to the offset variable, and use it to adjust the match index and the vertex index. | |
*/ | |
int offset = 0; // this variable will store the number of characters added or removed from the original text | |
MatchCollection FunctionNamePartMatches = Regex.Matches(temporaryText, @"<EventFunctionName>(.*?)</EventFunctionName>", RegexOptions.Singleline); | |
// Process the event function matches and store them in the eventTextData dictionary | |
foreach (Match match in FunctionNamePartMatches) | |
{ | |
// Extract the function name and remove the surrounding tags | |
string functionName = match.Groups[1].Value; | |
if (functionName != null && TextFormatChecker.IsValidFunctionIdentifierName(functionName)) | |
{ | |
var info = new InteractiveElementInfo(InteractiveElementType.EventFunction, functionName); | |
info.VertexStartIndex = (match.Index + offset) * 4; // start vertex index, adjusted by offset | |
info.VertexEndIndex = (match.Index + offset + functionName.Length - 1) * 4 + 3; // end vertex index, adjusted by offset | |
// Add the box area and the function name to the list of interactive elements | |
interactiveElementInfos.Add(info); | |
// Replace the function name in the original text with an underscored version | |
temporaryText = temporaryText.Remove(match.Index, match.Length).Insert(match.Index, $"<u>{functionName}</u>"); | |
offset += 7; // update the offset by adding 7 characters | |
} | |
} | |
} | |
{ | |
int offset = 0; // this variable will store the number of characters added or removed from the original text | |
MatchCollection URLNamePartMatches = Regex.Matches(temporaryText, @"<URLName>(.*?)</URLName>", RegexOptions.Singleline); | |
// Process the URL matches and store them in the linkTextData dictionary | |
foreach (Match match in URLNamePartMatches) | |
{ | |
// Extract the URL and remove the surrounding tags | |
string urlName = match.Groups[1].Value; | |
if (urlName != null && TextFormatChecker.IsValidURL(urlName)) | |
{ | |
temporaryText = urlName; | |
var info = new InteractiveElementInfo(InteractiveElementType.URL, urlName); | |
info.VertexStartIndex = (match.Index + offset) * 4; // start vertex index, adjusted by offset | |
info.VertexEndIndex = (match.Index + offset + urlName.Length - 1) * 4 + 3; // end vertex index, adjusted by offset | |
// Add the box area and the URL to the list of interactive elements | |
interactiveElementInfos.Add(info); | |
// Replace the URL in the original text with an underscored version | |
temporaryText = temporaryText.Remove(match.Index, match.Length).Insert(match.Index, $"<u>{urlName}</u>"); | |
offset += 7; // update the offset by adding 7 characters | |
} | |
} | |
} | |
TextDirty = false; | |
return temporaryText; | |
} | |
return OriginText; | |
} | |
public void OnPointerClick(PointerEventData eventData) | |
{ | |
HandleInteractiveElementAction(eventData); | |
} | |
public void OnPointerEnter(PointerEventData eventData) | |
{ | |
HandleInteractiveElementAction(eventData); | |
} | |
public void OnPointerExit(PointerEventData eventData) | |
{ | |
HandleInteractiveElementAction(eventData); | |
} | |
public void OnPointerDown(PointerEventData eventData) | |
{ | |
HandleInteractiveElementAction(eventData); | |
} | |
public void OnPointerUp(PointerEventData eventData) | |
{ | |
HandleInteractiveElementAction(eventData); | |
} | |
private void HandleInteractiveElementAction(PointerEventData eventData) | |
{ | |
if (interactable && !string.IsNullOrEmpty(OriginText)) | |
{ | |
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out var localPosition); | |
// Iterate through each interactive element | |
foreach (var info in interactiveElementInfos) | |
{ | |
var type = info.Type; | |
var data = info.Data; | |
// Check if the click point lies within this interactive element's box | |
var boxes = info.Boxes; // Assuming Boxes is a List<Rect> property of InteractiveElementInfo | |
for (var i = 0; i < boxes.Count; ++i) | |
{ | |
if (!boxes[i].Contains(localPosition)) continue; | |
// If it does, invoke the corresponding action | |
if (type == InteractiveElementType.EventFunction) | |
{ | |
onEventFunctionClick.Invoke(data); // Assuming onEventFunctionClick is an event for EventFunction clicks | |
} | |
else if (type == InteractiveElementType.URL) | |
{ | |
Application.OpenURL(data); | |
} | |
return; | |
} | |
} | |
} | |
} | |
protected override void OnPopulateMesh(VertexHelper toFill) | |
{ | |
base.m_DisableFontTextureRebuiltCallback = true; | |
// Call the base method to generate the mesh data | |
base.OnPopulateMesh(toFill); | |
UIVertex vertex = new UIVertex(); | |
// Handling hyperlink enclosing boxes | |
foreach (var info in interactiveElementInfos) | |
{ | |
info.Boxes.Clear(); | |
if (info.VertexStartIndex >= toFill.currentVertCount) | |
{ | |
continue; | |
} | |
// Add the index coordinates of the text vertices inside the hyperlink to the enclosing box | |
toFill.PopulateUIVertex(ref vertex, info.VertexStartIndex); | |
var vertexPosition = vertex.position; | |
var bounds = new Bounds(vertexPosition, Vector3.zero); | |
for (int index = info.VertexStartIndex, size = info.VertexEndIndex; index < size; index++) | |
{ | |
if (index >= toFill.currentVertCount) | |
{ | |
break; | |
} | |
toFill.PopulateUIVertex(ref vertex, index); | |
vertexPosition = vertex.position; | |
if (vertexPosition.x < bounds.min.x) // Wrap-around box re-added | |
{ | |
info.Boxes.Add(new Rect(bounds.min, bounds.size)); | |
bounds = new Bounds(vertexPosition, Vector3.zero); | |
} | |
else | |
{ | |
bounds.Encapsulate(vertexPosition); // Extended Wraparound Box | |
} | |
} | |
info.Boxes.Add(new Rect(bounds.min, bounds.size)); | |
} | |
// Call this custom setting apply to base class data | |
ApplyFreeTextSize(); | |
ApplyColorMode(toFill); | |
base.m_DisableFontTextureRebuiltCallback = false; | |
} | |
private void ApplyColorMode(VertexHelper toFill) | |
{ | |
// UGUI的网格处理类 | |
// Get the vertices in the generated mesh | |
List<UIVertex> vertices = new List<UIVertex>(); | |
toFill.GetUIVertexStream(vertices); | |
// Ensure that there are at least three vertices in the mesh | |
if (vertices.Count < 3) | |
{ | |
return; | |
} | |
// Calculate the min and max Y positions of the text (for gradient color mode) | |
float minY = vertices[0].position.y; | |
float maxY = minY; | |
if (colorMode == ColorMode.GradientColor) | |
{ | |
for (int i = 1; i < vertices.Count; i++) | |
{ | |
float y = vertices[i].position.y; | |
if (y < minY) minY = y; | |
if (y > maxY) maxY = y; | |
} | |
} | |
// Apply the selected color mode to the vertices | |
for (int i = 0; i < vertices.Count; i++) | |
{ | |
UIVertex vertex = vertices[i]; | |
if (colorMode == ColorMode.SingleColor) | |
{ | |
vertex.color = base.color; | |
} | |
else if (colorMode == ColorMode.GradientColor) | |
{ | |
//Reset this vertex color is white | |
vertex.color = Color.white; | |
float time = Mathf.InverseLerp(minY, maxY, vertex.position.y); | |
//Overlay color to the current vertex(using multiplication) | |
vertex.color *= colorGradient.Evaluate(time); | |
} | |
vertices[i] = vertex; | |
} | |
// Update the mesh with the modified vertices | |
toFill.Clear(); | |
toFill.AddUIVertexTriangleStream(vertices); | |
} | |
private void ApplyFreeTextSize() | |
{ | |
RectTransform parentRectTransform = transform.parent.GetComponent<RectTransform>(); | |
ContentSizeFitter contentSizeFitter = parentRectTransform.GetComponent<ContentSizeFitter>(); | |
LayoutElement layoutElement = GetComponent<LayoutElement>(); | |
if (freeTextSize) | |
{ | |
if (contentSizeFitter == null) | |
{ | |
contentSizeFitter = parentRectTransform.gameObject.AddComponent<ContentSizeFitter>(); | |
} | |
contentSizeFitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize; | |
contentSizeFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; | |
if (layoutElement == null) | |
{ | |
layoutElement = gameObject.AddComponent<LayoutElement>(); | |
} | |
layoutElement.flexibleWidth = 1; | |
layoutElement.flexibleHeight = 1; | |
} | |
else | |
{ | |
if (contentSizeFitter != null) | |
{ | |
#if UNITY_EDITOR | |
DestroyImmediate(contentSizeFitter); | |
#else | |
Destroy(contentSizeFitter); | |
#endif | |
} | |
if (layoutElement != null) | |
{ | |
#if UNITY_EDITOR | |
DestroyImmediate(layoutElement); | |
#else | |
Destroy(layoutElement); | |
#endif | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Twilight_Dream.Utilities.HyperTexts.Version2; | |
using UnityEditor; | |
using UnityEngine.UI; | |
[CustomEditor(typeof(MiniHyperTextNew))] | |
[CanEditMultipleObjects] | |
public class MiniHyperTextNewEditor : UnityEditor.UI.TextEditor | |
{ | |
private SerializedProperty OriginText; | |
private SerializedProperty FontData; | |
protected override void OnEnable() | |
{ | |
base.OnEnable(); | |
OriginText = serializedObject.FindProperty("OriginText"); | |
FontData = serializedObject.FindProperty("m_FontData"); | |
} | |
public override void OnInspectorGUI() | |
{ | |
serializedObject.Update(); | |
MiniHyperTextNew miniHyperText = (MiniHyperTextNew)target; | |
serializedObject.Update(); | |
EditorGUILayout.PropertyField(OriginText); | |
EditorGUILayout.PropertyField(FontData); | |
// Draw the properties for MiniHyperTextNew | |
SerializedProperty colorMode = serializedObject.FindProperty("colorMode"); | |
EditorGUILayout.PropertyField(colorMode, true); | |
if (miniHyperText.colorMode == ColorMode.GradientColor) | |
{ | |
SerializedProperty colorGradient = serializedObject.FindProperty("colorGradient"); | |
EditorGUILayout.PropertyField(colorGradient, true); | |
} | |
else if (miniHyperText.colorMode == ColorMode.SingleColor) | |
{ | |
SerializedProperty color = serializedObject.FindProperty("m_Color"); | |
EditorGUILayout.PropertyField(color, true); | |
} | |
// Display other properties | |
SerializedProperty interactable = serializedObject.FindProperty("interactable"); | |
EditorGUILayout.PropertyField(interactable, true); | |
SerializedProperty linkHitboxPaddingFactor = serializedObject.FindProperty("linkHitboxPaddingFactor"); | |
EditorGUILayout.PropertyField(linkHitboxPaddingFactor, true); | |
SerializedProperty freeTextSize = serializedObject.FindProperty("freeTextSize"); | |
EditorGUILayout.PropertyField(freeTextSize, true); | |
SerializedProperty overrideTextSource = serializedObject.FindProperty("overrideTextSource"); | |
EditorGUILayout.PropertyField(overrideTextSource, true); | |
SerializedProperty onEventFunctionClick = serializedObject.FindProperty("onEventFunctionClick"); | |
EditorGUILayout.PropertyField(onEventFunctionClick, true); | |
serializedObject.ApplyModifiedProperties(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using System.Threading.Tasks; | |
namespace Twilight_Dream.Utilities | |
{ | |
internal class TextFormatChecker | |
{ | |
private static bool IsValidEmailAddress(string value) | |
{ | |
// Check if the value is null or empty | |
if (string.IsNullOrEmpty(value)) | |
{ | |
return false; | |
} | |
// Split the value by the @ sign | |
string[] parts = value.Split('@'); | |
// Check if there are exactly two parts | |
if (parts.Length != 2) | |
{ | |
return false; | |
} | |
// Check the local part of the email address | |
string local = parts[0]; | |
// Define a regular expression pattern for the local part | |
string localPattern = @"^\w+([-+.']\w+)*$"; | |
// Use the Regex class to match the local part against the pattern | |
if (!Regex.IsMatch(local, localPattern)) | |
{ | |
return false; | |
} | |
// Check the domain part of the email address | |
string domain = parts[1]; | |
// Define a regular expression pattern for the domain part | |
string domainPattern = @"^\w+([-.]\w+)*\.\w+([-.]\w+)*$"; | |
// Use the Regex class to match the domain part against the pattern | |
if (!Regex.IsMatch(domain, domainPattern)) | |
{ | |
return false; | |
} | |
// If all checks passed, return true | |
return true; | |
} | |
public static bool IsValidFTPURL(string value) | |
{ | |
// Check if the value is null or empty | |
if (string.IsNullOrEmpty(value)) | |
{ | |
return false; | |
} | |
// Define a regular expression pattern for the FTP protocol | |
string protocolPattern = @"^ftp://"; | |
// Use the Regex class to match the value against the pattern | |
if (!Regex.IsMatch(value, protocolPattern)) | |
{ | |
return false; | |
} | |
// Remove the protocol part from the value | |
value = value.Substring(protocolPattern.Length); | |
// Split the value by the / sign | |
string[] parts = value.Split('/'); | |
// Check if there is at least one part | |
if (parts.Length < 1) | |
{ | |
return false; | |
} | |
// Check the host part of the FTP URL | |
string host = parts[0]; | |
// Define a regular expression pattern for the host part | |
string hostPattern = @"^\w+([-.]\w+)*\.\w+([-.]\w+)*$"; | |
// Use the Regex class to match the host part against the pattern | |
if (!Regex.IsMatch(host, hostPattern)) | |
{ | |
return false; | |
} | |
// Check the path part of the FTP URL, if any | |
if (parts.Length > 1) | |
{ | |
string path = string.Join("/", parts.Skip(1)); | |
// Define a regular expression pattern for the path part | |
string pathPattern = @"^[\w-./]+$"; | |
// Use the Regex class to match the path part against the pattern | |
if (!Regex.IsMatch(path, pathPattern)) | |
{ | |
return false; | |
} | |
} | |
// If all checks passed, return true | |
return true; | |
} | |
public static bool IsValidURL(string value) | |
{ | |
bool IsMatchPattern = false; | |
if (string.IsNullOrEmpty(value)) | |
return false; | |
else | |
{ | |
//This is HTTP/HTTPS URL pattern | |
Regex pattern = new Regex(@"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$", RegexOptions.Singleline); | |
//Remove prefix white space and suffix white space | |
string ValueNotHaveWhiteSpace = value.Trim(); | |
IsMatchPattern = pattern.IsMatch(ValueNotHaveWhiteSpace) || IsValidFTPURL(ValueNotHaveWhiteSpace); | |
if (!IsMatchPattern) | |
{ | |
if (IsValidEmailAddress(ValueNotHaveWhiteSpace) && !ValueNotHaveWhiteSpace.Contains("mailto:")) | |
{ | |
StringBuilder stringBuilder = new StringBuilder(ValueNotHaveWhiteSpace); | |
stringBuilder.Insert(0, "mailto:"); | |
ValueNotHaveWhiteSpace = stringBuilder.ToString(); | |
} | |
} | |
} | |
return IsMatchPattern; | |
} | |
public static bool IsValidFunctionIdentifierName(string value) | |
{ | |
if (string.IsNullOrEmpty(value)) | |
return false; | |
// Define a regular expression pattern for a valid function name | |
string pattern = @"^[A-Za-z_][A-Za-z0-9_]{0,126}$"; | |
// Use the Regex class to match the value against the pattern | |
return Regex.IsMatch(value, pattern); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I need the interactive text to be interactive or not, and also the graphic layout. And then in because you UNITY top display it | |
I think this requirement is a big headache. Because the interactive text involves events or hyperlinks. Because, you know, I'm going to give a chat system to use. It's not only in the online lobby system of the game that you can see the interactive text with links. | |
Players can also send images and text through the Private chat UI to send pictures and text. And then you can also send a link. | |
That's why I keep mentioning that component at the beginning, because I can't think of a design solution anymore. | |
This means that the chat system acts as a bridge between a hypertext link event and the interface. However hypertext is what is being designed. | |
----------------- | |
I see, you're looking to create a rich chat system that supports text, images, and interactive elements like hyperlinks and events. This is indeed a complex task, but it's definitely achievable with Unity. | |
The MiniHyperTextNew component you've created is a good start. It already supports interactive text and events. Here's how you can extend it to meet your requirements: | |
Images: To support images in chat, you could use Unity's Rich Text system, which allows you to embed images in text using the <quad> tag. You would need to add a Sprite Asset to your project containing the images you want to use, and then you can reference these images by their name in the Sprite Asset. You would also need to extend your MiniHyperTextNew component to parse these <quad> tags and replace them with the appropriate image references. | |
Hyperlinks: Your MiniHyperTextNew component already supports hyperlinks. You just need to make sure that the text of the hyperlink is formatted correctly in the chat message. For example, if you want to create a hyperlink to https://www.example.com, the chat message should include the text <URLName>https://www.example.com</URLName>. | |
Events: Your MiniHyperTextNew component already supports events. You just need to make sure that the event function name is formatted correctly in the chat message. For example, if you want to trigger an event called MyEvent, the chat message should include the text <EventFunctionName>MyEvent</EventFunctionName>. | |
Non-interactive text: Any text that is not enclosed in <URLName> or <EventFunctionName> tags will be treated as non-interactive text. | |
Chat System: The chat system would be responsible for taking input from the user, formatting it correctly (e.g., replacing URLs and event function names with the appropriate tags), and then sending it to the MiniHyperTextNew component to be displayed. It would also need to handle incoming chat messages from other users and display them in the same way. | |
Private Chat UI: The Private Chat UI would be a separate interface that uses the same underlying chat system. It would have its own instance of the MiniHyperTextNew component to display chat messages. | |
This is a high-level overview of how you could design your system. The exact details would depend on your specific requirements and constraints. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment