- Home /
How to apply multiple styles to the same line(s) of GUI text
I'd like to have a set of lines of GUI text with multiple styles in it. So long as I use only a single line of text, I can use Horizontal areas and GUILayout.Label to do coloring, but if any piece of the line flows over and "wrap"s (which is set in the style for that piece), the formatting is thrown off.
This happens because GUIlayout.Label's can't be started right after the last character in a textfield, only at the end of the text field, either to the right of it, or below it.
E.g.:
"I am the smartest person that"
[new style] "ever" [/new style]
"walked the earth, so I think."
What I want is to have something like:
"I am the smartest person that" [new style] "ever" [/new style] "walked the earth, so I think."
I could try count the number of characters and offset a manual GUI.Label (Rect(pixelCount, etc...), but that is far too inaccurate. Does anyone have a better way of doing this?
To rephrase my problem: I need a way to apply styles to portions of a string that wraps, similar to the way HTML tags work.
This guy, (http://forum.unity3d.com/viewtopic.php?t=9808), used the approximation technique, with 5 pixels per character and GUILayout.Label's ins$$anonymous$$d of manual GUI.Label's.
Answer by CJCurrie · Mar 03, 2010 at 09:46 PM
After a little bit of work, I have a system that splits a string into a 3D array: the first dimension contains the entire string, split up into one line per element, the second dimension contains each part of line, as split up by arbitrary tags, and the third dimension contains the actual text and a cached ref to the style associated with it.
Anyway, to the example:
var notice = "Hello there. <blu>How are you? <nor>I'm fine, thank you for asking. <blu>How would you like to be my pet? <red>Oh, I'd love that. <nor>I do believe I'm going crazy as all <blu>balls <nor>else.";
var messageToReturn = ParseStyles(notice, null); // We can pass a default style here
function ParseStyles(notice : String, style : GUIStyle) { // <nor> = noticeNormal : GUIStyle // <blu> = noticeBlue : GUIStyle // <red> = noticeRed : GUIStyle
if (style != null) var curStyle = style; else // Start with normal style curStyle = noticeNormal;
var msg : Array = Array(); var leftovers : String = notice; var cont : boolean = true;
while (cont) { var container : Array = Array(); var go : boolean = true;
// My lines will only be ~39 chars long
if (leftovers.length >= 39)
var charsToGo : int = 39;
else
{
charsToGo = leftovers.length-1;
// We've reached the end, so do not continue
cont = false;
}
var text : String = leftovers;
while (go)
{
// --- Find indexes of tags within the first chunk (39 chars) of the string
var normalIndex : int = text.IndexOf("<nor>",0, charsToGo);
if (normalIndex == -1) normalIndex = 100; // Infinity doesn't work, but 100 is guaranteed to be higher than 39, our max index
var blueIndex : int = text.IndexOf("<blu>",0, charsToGo);
if (blueIndex == -1) blueIndex = 100;
var redIndex : int = text.IndexOf("<red>",0, charsToGo);
if (redIndex == -1) redIndex = 100;
var lowest : int = Mathf.Min(normalIndex,blueIndex);
lowest = Mathf.Min(lowest, redIndex);
// ...etc. with all other styles compared to the previous lowest. May the lowest low win
if (lowest > 0 && lowest < 39)
{
// Put what we have so far plus the style into the end of the array
container.Add(Array(text.Substring(0,lowest), curStyle ));
// Remove what we added to container
text = text.Remove(0, lowest);
// Calculate the new style for the next iteration of the while loop. Mine starts at 1 and goes 3 because I'm evaluating what's between the <___>
var sub : String = text.Substring(1, 3);
switch (sub)
{
case "nor":
curStyle = noticeNormal;
break;
case "blu":
curStyle = noticeBlue;
break;
case "red":
curStyle = noticeRed;
break;
}
// Remove the tag characters and subtract from charsToGo. My tags are 5 chars long
print ("before: "+text);
text = text.Remove(0, 5);
charsToGo -= lowest + 5;
}
else
{
// No styles found in this segment, so put the rest of the line in and get ready for tail recursion
if (cont)
// We have more to do, cut at the last space.
var lastSpace = text.LastIndexOf(" ",charsToGo); // cut it off at the last space
else
// We have naught else to do, manually set lastSpace to force the whole thing into container
lastSpace = -1;
// Now check lastSpace
if (lastSpace != -1)
{
container.Add(Array(text.Substring(0,lastSpace), curStyle ));
leftovers = text.Remove(0, lastSpace); // Cuts everything up to lastSpace
}
// There is nothing more to parse
else
{
container.Add(Array(text, curStyle ));
}
msg.Add(container);
go = false;
}
}
} return msg; }
And for the GUI implementation you just need some Layouting:
GUILayout.BeginHorizontal(GUI.skin.box, GUILayout.Height(100), GUILayout.Width(760), GUILayout.ExpandHeight(true));
GUILayout.Space(30); GUILayout.BeginVertical(); GUILayout.Space(10); GUILayout.FlexibleSpace(); for (x in messageToReturn) { GUILayout.BeginHorizontal(); for (n in x) { GUILayout.Space(10); GUILayout.Label(n[0], n[1]); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(1); } GUILayout.Space(10); GUILayout.FlexibleSpace(); GUILayout.EndVertical(); GUILayout.Space(30); GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
All the extra spaces and such I have in there for alignment reasons.
Now, there are some flaws to this system, most noticeably that if style changes font size some clipping may occur, there's nothing to handle words longer than the arbitrary line limit, and tags all have to be the same length (3 characters right now). These can be fixed with a little tweaking, but I figured it was complete enough to share with the community at large now.
Enjoy! If you have questions, feel free to email me at cjcurrie@blackstormsstudios.com
Could you include a Unity 3D Demo. I am new to Unity 3D and could use the help.
Your answer
Follow this Question
Related Questions
button with content in multiple colors 3 Answers
Can you use GUIStyle with GUILayout? 0 Answers
Styling an individual button in SelectionGrid 0 Answers
Why can't I get my tooltip to show only when there is a tooltip set? 2 Answers
GUI.Window error. InvalidOperationException: Hashtable.Enumerator: snapshot out of sync. 0 Answers