3 using Microsoft.Xna.Framework;
4 using Microsoft.Xna.Framework.Graphics;
6 using System.Collections.Generic;
7 using System.Collections.Immutable;
8 using System.Globalization;
10 using System.Xml.Linq;
20 return element.NameAsIdentifier();
23 protected int ParseSize(XElement element,
string attributeName)
25 string valueStr = element.GetAttributeString(attributeName,
string.Empty);
26 bool relativeToWidth = valueStr.EndsWith(
"vw");
27 bool relativeToHeight = valueStr.EndsWith(
"vh");
28 if (relativeToWidth || relativeToHeight)
30 string floatStr = valueStr.Substring(0, valueStr.Length - 2);
31 if (!
float.TryParse(floatStr, NumberStyles.Any, CultureInfo.InvariantCulture, out
float relativeHeight))
33 DebugConsole.ThrowError($
"Error while parsing a {nameof(GUIComponentStyle)}: {valueStr} is not a valid size.");
39 return element.GetAttributeInt(attributeName, 0);
73 if (font is
null) {
return null; }
74 if (specialHandlingFonts is
null) {
return font; }
76 return specialHandlingFonts.TryGetValue(category, out var resultFont)
78 : specialHandlingFonts.TryGetValue(TextManager.SpeciallyHandledCharCategory.CJK, out resultFont)
87 this.element = element;
93 string? fontPath = GetFontFilePath(element);
94 uint size = GetFontSize(element);
95 bool dynamicLoading = GetFontDynamicLoading(element);
96 var shcc = GetShcc(element);
98 specialHandlingFonts?.Values.ForEach(f => f.Dispose());
106 ForceUpperCase = element.GetAttributeBool(
"forceuppercase",
false)
110 foreach (var flag
in TextManager.SpeciallyHandledCharCategories)
112 if (shcc.HasFlag(flag)) {
continue; }
114 var extractedFont = ExtractFont(flag, element);
115 if (extractedFont is
null) {
continue; }
116 fallbackFonts.Add(flag, extractedFont);
118 fallbackFonts.Values.ForEach(ff => ff.ForceUpperCase = font.
ForceUpperCase);
119 specialHandlingFonts = fallbackFonts.ToImmutableDictionary();
120 Language = GameSettings.CurrentConfig.Language;
127 specialHandlingFonts?.Values.ForEach(f => f.Dispose());
128 specialHandlingFonts =
null;
133 foreach (var subElement
in element.
Elements().Reverse())
135 if (subElement.NameAsIdentifier() !=
"override") {
continue; }
136 if (ScalableFont.ExtractShccFromXElement(subElement).HasFlag(flag))
138 uint overrideFontSize = GetFontSize(subElement, defaultSize: font?.Size ?? 14);
139 return new ScalableFont(subElement, overrideFontSize, GameMain.Instance.GraphicsDevice);
143 ScalableFont hardcodedFallback(
string path)
147 GameMain.Instance.GraphicsDevice,
148 dynamicLoading:
true,
149 speciallyHandledCharCategory: flag);
153 TextManager.SpeciallyHandledCharCategory.CJK
154 => hardcodedFallback(
"Content/Fonts/NotoSans/NotoSansCJKsc-Bold.otf"),
155 TextManager.SpeciallyHandledCharCategory.Cyrillic
156 => hardcodedFallback(
"Content/Fonts/Oswald-Bold.ttf"),
161 private string? GetFontFilePath(ContentXElement element)
163 foreach (var subElement
in element.Elements())
165 if (IsValidOverride(subElement))
167 return subElement.GetAttributeContentPath(
"file")?.Value;
170 return element.GetAttributeContentPath(
"file")?.Value;
173 private uint GetFontSize(XElement element, uint defaultSize = 14)
176 foreach (var subElement
in element.Elements())
178 if (IsValidOverride(subElement))
180 uint overrideFontSize = GetFontSize(subElement, 0);
181 if (overrideFontSize > 0) {
return (uint)Math.Round(overrideFontSize * GameSettings.CurrentConfig.Graphics.TextScale); }
185 foreach (var subElement
in element.Elements())
187 if (!subElement.Name.ToString().Equals(
"size", StringComparison.OrdinalIgnoreCase)) {
continue; }
188 Point maxResolution = subElement.GetAttributePoint(
"maxresolution",
new Point(
int.MaxValue,
int.MaxValue));
189 if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y)
191 int rawSize =
ParseSize(subElement,
"size");
192 return (uint)Math.Round(rawSize * GameSettings.CurrentConfig.Graphics.TextScale);
195 return (uint)Math.Round(defaultSize * GameSettings.CurrentConfig.Graphics.TextScale);
198 private bool GetFontDynamicLoading(XElement element)
200 foreach (var subElement
in element.Elements())
202 if (IsValidOverride(subElement))
204 return subElement.GetAttributeBool(
"dynamicloading",
false);
207 return element.GetAttributeBool(
"dynamicloading",
false);
210 private TextManager.SpeciallyHandledCharCategory GetShcc(XElement element)
212 foreach (var subElement
in element.Elements())
214 if (IsValidOverride(subElement))
216 return ScalableFont.ExtractShccFromXElement(subElement);
219 return ScalableFont.ExtractShccFromXElement(element);
222 private bool IsValidOverride(XElement element)
224 if (!element.IsOverride()) {
return false; }
225 var languages = element.GetAttributeIdentifierArray(
"language", Array.Empty<
Identifier>());
226 return languages.Any(l => l.ToLanguageIdentifier() == GameSettings.CurrentConfig.Language);
232 public GUIFont(
string identifier) : base(identifier) { }
242 public uint Size => Value?.Size ?? 0;
247 Prefabs.ActivePrefab?.GetFontForCategory(TextManager.GetSpeciallyHandledCategories(str));
249 public void DrawString(SpriteBatch sb,
LocalizedString text, Vector2 position, Color color,
float rotation, Vector2 origin, Vector2 scale, SpriteEffects spriteEffects,
float layerDepth)
251 DrawString(sb, text.
Value, position, color, rotation, origin, scale, spriteEffects, layerDepth);
254 public void DrawString(SpriteBatch sb,
string text, Vector2 position, Color color,
float rotation, Vector2 origin, Vector2 scale, SpriteEffects spriteEffects,
float layerDepth)
256 GetFontForStr(text)?.DrawString(sb, text, position, color, rotation, origin, scale, spriteEffects, layerDepth);
259 public void DrawString(SpriteBatch sb,
LocalizedString text, Vector2 position, Color color,
float rotation, Vector2 origin,
float scale, SpriteEffects spriteEffects,
float layerDepth, Alignment alignment = Alignment.TopLeft)
261 DrawString(sb, text.
Value, position, color, rotation, origin, scale, spriteEffects, layerDepth, alignment);
264 public void DrawString(SpriteBatch sb,
string text, Vector2 position, Color color,
float rotation, Vector2 origin,
float scale, SpriteEffects spriteEffects,
float layerDepth, Alignment alignment = Alignment.TopLeft,
ForceUpperCase forceUpperCase =
Barotrauma.ForceUpperCase.Inherit)
266 GetFontForStr(text)?.DrawString(sb, text, position, color, rotation, origin, scale, spriteEffects, layerDepth, alignment, forceUpperCase);
271 DrawString(sb, text.
Value, position, color, forceUpperCase, italics);
276 GetFontForStr(text)?.DrawString(sb, text, position, color, forceUpperCase, italics);
279 public void DrawStringWithColors(SpriteBatch sb,
string text, Vector2 position, Color color,
float rotation, Vector2 origin,
float scale, SpriteEffects spriteEffects,
float layerDepth, in ImmutableArray<RichTextData>? richTextData,
int rtdOffset = 0, Alignment alignment = Alignment.TopLeft,
ForceUpperCase forceUpperCase =
Barotrauma.ForceUpperCase.Inherit)
281 GetFontForStr(text)?.DrawStringWithColors(sb, text, position, color, rotation, origin, scale, spriteEffects, layerDepth, richTextData, rtdOffset, alignment, forceUpperCase);
286 return GetFontForStr(str)?.MeasureString(str, removeExtraSpacing) ?? Vector2.Zero;
291 return GetFontForStr($
"{c}")?.MeasureChar(c) ?? Vector2.Zero;
295 => GetFontForStr(text)?.WrapText(text, width) ?? text;
297 public string WrapText(
string text,
float width,
int requestCharPos, out Vector2 requestedCharPos)
299 requestedCharPos =
default;
300 return GetFontForStr(text)?.WrapText(text, width, requestCharPos, out requestedCharPos) ?? text;
303 public string WrapText(
string text,
float width, out Vector2[] allCharPositions)
305 var scalableFont = GetFontForStr(text);
306 if (scalableFont !=
null)
308 return scalableFont.WrapText(text, width, out allCharPositions);
311 allCharPositions = Enumerable.Range(0, text.Length + 1).Select(_ => Vector2.Zero).ToArray();
315 public float LineHeight => Value?.LineHeight ?? 0;
332 private readonly Color fallbackColor;
334 public GUIColor(
string identifier, Color fallbackColor) : base(identifier)
336 this.fallbackColor = fallbackColor;
343 return Prefabs?.ActivePrefab?.Color ?? fallbackColor;
347 public static implicit
operator Color(
GUIColor reference) => reference.Value;
351 return value.
Value * scale;
372 public GUISprite(
string identifier) : base(identifier) { }
378 return Prefabs.ActivePrefab?.
Sprite;
384 public void Draw(SpriteBatch spriteBatch, RectangleF rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None)
386 Value?.
Draw(spriteBatch, rect, color, spriteEffects);
389 public void Draw(SpriteBatch spriteBatch, Rectangle rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None)
391 Value?.Draw(spriteBatch, rect, color, spriteEffects);
422 public int FrameCount => Value?.FrameCount ?? 1;
423 public Point FrameSize => Value?.FrameSize ?? Point.Zero;
425 public void Draw(ISpriteBatch spriteBatch, Vector2 pos,
float rotate = 0,
float scale = 1, SpriteEffects spriteEffects = SpriteEffects.None)
427 Value?.Draw(spriteBatch, pos, rotate, scale, spriteEffects);
430 public void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin,
float rotate = 0,
float scale = 1, SpriteEffects spriteEffects = SpriteEffects.None,
float? depth =
null)
432 Value?.Draw(spriteBatch, pos, color, origin, rotate, scale, spriteEffects, depth);
435 public void Draw(ISpriteBatch spriteBatch,
int spriteIndex, Vector2 pos, Color color, Vector2 origin,
float rotate, Vector2 scale, SpriteEffects spriteEffects = SpriteEffects.None,
float? depth =
null)
437 Value?.Draw(spriteBatch, spriteIndex, pos, color, origin, rotate, scale, spriteEffects, depth);
450 foreach (var subElement
in element.Elements())
453 Sprites[(int)state] =
new Sprite(subElement);
459 foreach (var sprite
in Sprites)
468 public GUICursor(
string identifier) : base(identifier) { }
Color GetAttributeColor(string key, in Color def)
IEnumerable< ContentXElement > Elements()
GUIColor(string identifier, Color fallbackColor)
static Color operator*(GUIColor value, float scale)
GUIColorPrefab(ContentXElement element, UIStyleFile file)
GUICursor(string identifier)
readonly Sprite[] Sprites
GUICursorPrefab(ContentXElement element, UIStyleFile file)
void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects spriteEffects, float layerDepth)
Vector2 MeasureString(LocalizedString str, bool removeExtraSpacing=false)
void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, ForceUpperCase forceUpperCase=Barotrauma.ForceUpperCase.Inherit, bool italics=false)
ScalableFont? GetFontForStr(string str)
string WrapText(string text, float width)
string WrapText(string text, float width, out Vector2[] allCharPositions)
void DrawString(SpriteBatch sb, LocalizedString text, Vector2 position, Color color, ForceUpperCase forceUpperCase=Barotrauma.ForceUpperCase.Inherit, bool italics=false)
string WrapText(string text, float width, int requestCharPos, out Vector2 requestedCharPos)
Vector2 MeasureChar(char c)
void DrawString(SpriteBatch sb, LocalizedString text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects spriteEffects, float layerDepth, Alignment alignment=Alignment.TopLeft)
GUIFont(string identifier)
void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects spriteEffects, float layerDepth, in ImmutableArray< RichTextData >? richTextData, int rtdOffset=0, Alignment alignment=Alignment.TopLeft, ForceUpperCase forceUpperCase=Barotrauma.ForceUpperCase.Inherit)
void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects spriteEffects, float layerDepth, Alignment alignment=Alignment.TopLeft, ForceUpperCase forceUpperCase=Barotrauma.ForceUpperCase.Inherit)
void DrawString(SpriteBatch sb, LocalizedString text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects spriteEffects, float layerDepth)
GUIFontPrefab(ContentXElement element, UIStyleFile file)
ScalableFont? GetFontForCategory(TextManager.SpeciallyHandledCharCategory category)
LanguageIdentifier Language
override Identifier DetermineIdentifier(XElement element)
GUIPrefab(ContentXElement element, UIStyleFile file)
int ParseSize(XElement element, string attributeName)
readonly Identifier Identifier
readonly PrefabSelector< T > Prefabs
GUISelector(string identifier)
void Draw(SpriteBatch spriteBatch, Rectangle rect, Color color, SpriteEffects spriteEffects=SpriteEffects.None)
GUISprite(string identifier)
GUISpritePrefab(ContentXElement element, UIStyleFile file)
void Draw(ISpriteBatch spriteBatch, int spriteIndex, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffects=SpriteEffects.None, float? depth=null)
void Draw(ISpriteBatch spriteBatch, Vector2 pos, float rotate=0, float scale=1, SpriteEffects spriteEffects=SpriteEffects.None)
GUISpriteSheet(string identifier)
void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate=0, float scale=1, SpriteEffects spriteEffects=SpriteEffects.None, float? depth=null)
readonly SpriteSheet SpriteSheet
GUISpriteSheetPrefab(ContentXElement element, UIStyleFile file)
static int GraphicsHeight
readonly Identifier Identifier
TextManager.SpeciallyHandledCharCategory SpeciallyHandledCharCategory
Sprite(ContentXElement element, string path="", string file="", bool lazyLoad=false, float sourceRectScale=1)
SpriteSheet(ContentXElement element, string path="", string file="")
void Draw(SpriteBatch spriteBatch, RectangleF rect, Color color, SpriteEffects spriteEffects=SpriteEffects.None, Vector2? uvOffset=null)