using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Barotrauma.Extensions; namespace Barotrauma { public class GUICanvas : RectTransform { protected GUICanvas() : base(Size, parent: null) { } private static GUICanvas _instance; public static GUICanvas Instance { get { if (_instance == null) { _instance = new GUICanvas(); if (GameMain.Instance != null) { GameMain.Instance.ResolutionChanged += RecalculateSize; } _instance.ChildrenChanged += OnChildrenChanged; } return _instance; } } //GUICanvas stores the children as weak references, to allow elements that we no longer need to get garbage collected private readonly List> childrenWeakRef = new List>(); private static Vector2 Size => new Vector2(GameMain.GraphicsWidth / (float)GUI.UIWidth, 1f); protected override Rectangle NonScaledUIRect => UIRect; private enum ResizeAxis { Both = 0, X = 1, Y = 2 } private static void OnChildrenChanged(RectTransform _) { CrossThread.RequestExecutionOnMainThread(RefreshChildren); } private static void RefreshChildren() { //add weak reference if we don't have one yet foreach (var child in _instance.Children) { if (!_instance.childrenWeakRef.Any(c => c.TryGetTarget(out var existingChild) && existingChild == child)) { _instance.childrenWeakRef.Add(new WeakReference(child)); } } //get rid of strong references _instance.children.Clear(); //remove dead children for (int i = _instance.childrenWeakRef.Count - 1; i >= 0; i--) { if (!_instance.childrenWeakRef[i].TryGetTarget(out var child) || child.Parent != _instance) { _instance.childrenWeakRef.RemoveAt(i); } } } // Turn public, if there is a need to call this manually. private static void RecalculateSize() { Vector2 recalculatedSize = Size; // Scale children that are supposed to encompass the whole screen so that they are properly scaled on ultrawide as well for (int i = 0; i < Instance.childrenWeakRef.Count; i++) { if (!_instance.childrenWeakRef[i].TryGetTarget(out RectTransform target) || target == null) { continue; }; _instance.children.Add(target); if (target.RelativeSize.X < 1 && target.RelativeSize.Y < 1) { continue; } ResizeAxis axis; if (target.RelativeSize.X >= 1 && target.RelativeSize.Y >= 1) { axis = ResizeAxis.Both; } else if (target.RelativeSize.X >= 1) { axis = ResizeAxis.X; } else { axis = ResizeAxis.Y; } switch (axis) { case ResizeAxis.Both: target.RelativeSize = recalculatedSize; break; case ResizeAxis.X: target.RelativeSize = new Vector2(recalculatedSize.X, target.RelativeSize.Y); break; case ResizeAxis.Y: target.RelativeSize = new Vector2(target.RelativeSize.X, recalculatedSize.Y); break; } } Instance.Resize(Size, resizeChildren: true); Instance.GetAllChildren().Select(c => c.GUIComponent as GUITextBlock).ForEach(t => t?.SetTextPos()); _instance.children.Clear(); } } }