using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; using System.Linq; namespace Barotrauma { public class GUIMessageBox : GUIFrame { public static List MessageBoxes = new List(); private static int DefaultWidth { get { return Math.Max(400, (int)(400 * (GameMain.GraphicsWidth / GUI.ReferenceResolution.X))); } } private float inGameCloseTimer = 0.0f; private const float inGameCloseTime = 15f; public enum Type { Default, InGame } public List Buttons { get; private set; } = new List(); //public GUIFrame BackgroundFrame { get; private set; } public GUILayoutGroup Content { get; private set; } public GUIFrame InnerFrame { get; private set; } public GUITextBlock Header { get; private set; } public GUITextBlock Text { get; private set; } public string Tag { get; private set; } public GUIImage Icon { get; private set; } public Color IconColor { get { return Icon == null ? Color.White : Icon.Color; } set { if (Icon == null) { return; } Icon.Color = value; } } private bool alwaysVisible; private float openState; private bool closing; private Type type; public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault(); public GUIMessageBox(string headerText, string text, Vector2? relativeSize = null, Point? minSize = null) : this(headerText, text, new string[] { "OK" }, relativeSize, minSize) { this.Buttons[0].OnClicked = Close; } public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null) : base(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: GUI.Style.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox") { int width = (int)(DefaultWidth * (type == Type.Default ? 1.0f : 1.5f)), height = 0; if (relativeSize.HasValue) { width = (int)(GameMain.GraphicsWidth * relativeSize.Value.X); height = (int)(GameMain.GraphicsHeight * relativeSize.Value.Y); } if (minSize.HasValue) { width = Math.Max(width, minSize.Value.X); if (height > 0) { height = Math.Max(height, minSize.Value.Y); } } InnerFrame = new GUIFrame(new RectTransform(new Point(width, height), RectTransform, type == Type.InGame ? Anchor.TopCenter : Anchor.Center) { IsFixedSize = false }, style: null); GUI.Style.Apply(InnerFrame, "", this); this.type = type; Tag = tag; if (type == Type.Default) { Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 }; Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true); GUI.Style.Apply(Header, "", this); Header.RectTransform.MinSize = new Point(0, Header.Rect.Height); if (!string.IsNullOrWhiteSpace(text)) { Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true); GUI.Style.Apply(Text, "", this); Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize = new Point(Text.Rect.Width, Text.Rect.Height); Text.RectTransform.IsFixedSize = true; } var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), Content.RectTransform, Anchor.BottomCenter), childAnchor: Anchor.TopCenter) { AbsoluteSpacing = 5, IgnoreLayoutGroups = true }; int buttonSize = 35; var buttonStyle = GUI.Style.GetComponentStyle("GUIButton"); if (buttonStyle != null && buttonStyle.Height.HasValue) { buttonSize = buttonStyle.Height.Value; } buttonContainer.RectTransform.NonScaledSize = buttonContainer.RectTransform.MinSize = buttonContainer.RectTransform.MaxSize = new Point(buttonContainer.Rect.Width, (int)((buttonSize + 5) * buttons.Length)); buttonContainer.RectTransform.IsFixedSize = true; if (height == 0) { height += Header.Rect.Height + Content.AbsoluteSpacing; height += (Text == null ? 0 : Text.Rect.Height) + Content.AbsoluteSpacing; height += buttonContainer.Rect.Height + 20; if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); } InnerFrame.RectTransform.NonScaledSize = new Point(InnerFrame.Rect.Width, (int)Math.Max(height / Content.RectTransform.RelativeSize.Y, height + (int)(50 * GUI.yScale))); Content.RectTransform.NonScaledSize = new Point(Content.Rect.Width, height); } Buttons = new List(buttons.Length); for (int i = 0; i < buttons.Length; i++) { var button = new GUIButton(new RectTransform(new Vector2(0.6f, 1.0f / buttons.Length), buttonContainer.RectTransform), buttons[i]); Buttons.Add(button); } } else if (type == Type.InGame) { InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight); alwaysVisible = true; CanBeFocused = false; GUI.Style.Apply(InnerFrame, "", this); var horizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.95f), InnerFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true, RelativeSpacing = 0.02f }; if (icon != null) { Icon = new GUIImage(new RectTransform(new Vector2(0.2f, 0.95f), horizontalLayoutGroup.RectTransform), icon, scaleToFit: true); } Content = new GUILayoutGroup(new RectTransform(new Vector2(icon != null ? 0.65f : 0.85f, 1.0f), horizontalLayoutGroup.RectTransform)); var buttonContainer = new GUIFrame(new RectTransform(new Vector2(0.15f, 1.0f), horizontalLayoutGroup.RectTransform), style: null); Buttons = new List(1) { new GUIButton(new RectTransform(new Vector2(0.5f, 0.5f), buttonContainer.RectTransform, Anchor.Center), style: "GUIButtonHorizontalArrow") { OnClicked = Close } }; Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true); GUI.Style.Apply(Header, "", this); Header.RectTransform.MinSize = new Point(0, Header.Rect.Height); if (!string.IsNullOrWhiteSpace(text)) { Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true); GUI.Style.Apply(Text, "", this); Content.Recalculate(); Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize = new Point(Text.Rect.Width, Text.Rect.Height); Text.RectTransform.IsFixedSize = true; } if (height == 0) { height += Header.Rect.Height + Content.AbsoluteSpacing; height += (Text == null ? 0 : Text.Rect.Height) + Content.AbsoluteSpacing; if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); } InnerFrame.RectTransform.NonScaledSize = new Point(InnerFrame.Rect.Width, (int)Math.Max(height / Content.RectTransform.RelativeSize.Y, height + (int)(50 * GUI.yScale))); Content.RectTransform.NonScaledSize = new Point(Content.Rect.Width, height); } Buttons[0].RectTransform.MaxSize = new Point(Math.Min(Buttons[0].Rect.Width, Buttons[0].Rect.Height)); } MessageBoxes.Add(this); } public static void AddActiveToGUIUpdateList() { for (int i = 0; i < MessageBoxes.Count; i++) { if (MessageBoxes[i] is GUIMessageBox alwaysVisibleMsgBox && alwaysVisibleMsgBox.alwaysVisible) { alwaysVisibleMsgBox.AddToGUIUpdateList(); break; } } for (int i = MessageBoxes.Count - 1; i >= 0; i--) { if (MessageBoxes[i].UserData as string == "verificationprompt" || MessageBoxes[i].UserData as string == "bugreporter") { continue; } if (!(MessageBoxes[i] is GUIMessageBox msgBox) || !msgBox.alwaysVisible) { MessageBoxes[i].AddToGUIUpdateList(); break; } } } protected override void Update(float deltaTime) { if (type == Type.InGame) { Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight); Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale); Vector2 endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y); /*for (int i = MessageBoxes.IndexOf(this); i >= 0; i--) { if (MessageBoxes[i] is GUIMessageBox otherMsgBox && otherMsgBox != this && otherMsgBox.type == type && !otherMsgBox.closing) { defaultPos = new Vector2( Math.Max(otherMsgBox.InnerFrame.RectTransform.AbsoluteOffset.X + 10 * GUI.Scale, defaultPos.X), Math.Max(otherMsgBox.InnerFrame.RectTransform.AbsoluteOffset.Y + 10 * GUI.Scale, defaultPos.Y)); } }*/ if (!closing) { InnerFrame.RectTransform.AbsoluteOffset = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint(); openState = Math.Min(openState + deltaTime * 2.0f, 1.0f); if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn)) { inGameCloseTimer += deltaTime; } if (inGameCloseTimer >= inGameCloseTime) { Close(); } } else { openState += deltaTime * 2.0f; InnerFrame.RectTransform.AbsoluteOffset = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint(); if (openState >= 2.0f) { if (Parent != null) { Parent.RemoveChild(this); } if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); } } } } } public void Close() { if (type == Type.InGame) { closing = true; } else { if (Parent != null) { Parent.RemoveChild(this); } if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); } } } public bool Close(GUIButton button, object obj) { Close(); return true; } public static void CloseAll() { MessageBoxes.Clear(); } /// /// Parent does not matter. It's overridden. /// public void AddButton(RectTransform rectT, string text, GUIButton.OnClickedHandler onClick) { rectT.Parent = RectTransform; Buttons.Add(new GUIButton(rectT, text) { OnClicked = onClick }); } } }